Introduction

ShareMouse is an alternative to mac Teleport or Synergy - it is sort of an UDP KVM allow you to move your mouse between different screens so you can use multiple computers on your desk without needing several controllers. Their app is priced at 24 dalla and if you get some extra spamware, you can even pay 95 dalla, just in case you have nothing else better to do with your money.

ShareMouse comes in Windows and Mac versions - as suggested by Jean, this is the crack for the OSX / Mac version of ShareMouse - not the Windows version.

Required Tools

  • Disassembler, many are available, even for the Mac. You can get IDAQ which is probably the best choice but you can do fine with Hopper.

The Rundown

How does this cracking business work anyway? The disassembler takes a binary and interprets the data as a series of assembler opcodes. By looking at the assembler code, you reason about the flow of the program. Essentially, a program is no more than a series of jumps and function calls based on tests. If you do not look at the internals of a program and ignore all the crud except if-clauses and return statements you will notice that the whole program can be reduced to a series of instructions and jumps.

Let's Go!

We disassemble the application and look for the usual known ASCII strings: “DEMO”, “Register”, “Buy”, etc... We find at the very bottom, the string isRegistered along with a reference to where it is used:

0000000100047d5f                                 db         "v20@0:8S16", 0
0000000100047d6a                                 db         "isRegistered", 0             ; XREF=0x10006a440 # <--- referenced at this location
0000000100047d77                                 db         "setNeedSN:", 0               ; XREF=0x10006a418
0000000100047d82                                 db         "setDemoExpired:", 0          ; XREF=0x10006a410

Now, we go to that location, either via a menu option, or by clicking on the address, or one can look on the left hand side and scroll the screen till you reach the 000000010006a440 address. When we get there we get a full list of locations where the string is used.

                                            objc_sel_isRegistered:
000000010006a440                                 dq         0x0000000100047d6a            ; XREF=0x100004977, 0x100005386, ... <--- locations where the isRegistered shows up

Like the last time, we now have to explore the addresses:

  • 0000000100004977
  • 0000000100005386
  • any others...

To see what the program does with isRegistered. If we try the first address, 0000000100004977 we get to a procedure (function, method, etc...) called onDemoTimer. Now we know by running the application in demo mode that after some random amount of time, the application disables its connection to the network and then prompts the user saying that the demo version only runs for a certain amount of time.

This is obj-c and like any other OO programming language, it relies on event callbacks to process asynchronous events. So, most likely, the security mechanism of the application registers a timer callback which will be triggered after a random amount of time. We can even spot the on keyword in onDemoTimer which makes it obvious that we have just found the callback method. In assembly it looks like this (without the extra annotations and comments from the disassembler):

====== B E G I N   O F   P R O C E D U R E ======
 
 
                                       ; Basic Block Input Regs: rax rdi
                                            methImpl_AppController_onDemoTimer_:
000000010000496b 55                              push       rbp
000000010000496c 4889E5                          mov        rbp, rsp
000000010000496f 53                              push       rbx
0000000100004970 4883EC08                        sub        rsp, 0x8
0000000100004974 4889FB                          mov        rbx, rdi
0000000100004977 488B35C25A0600                  mov        rsi, qword [ds:objc_sel_isRegistered] ; @selector(isRegistered)
000000010000497e E865FF0300                      call       imp___symbol_stub1__objc_msgSend
0000000100004983 84C0                            test       al, al
0000000100004985 752C                            jne        0x1000049B3
                                       ; Basic Block Input Regs: rax rbx
0000000100004987 488B35D2580600                  mov        rsi, qword [ds:objc_sel_isCommercialUse] ; @selector(isCommercialUse)
000000010000498e 4889DF                          mov        rdi, rbx
0000000100004991 E852FF0300                      call       imp___symbol_stub1__objc_msgSend
0000000100004996 0FBED0                          movsx      edx, al
0000000100004999 488B35705A0600                  mov        rsi, qword [ds:objc_sel_setDemoExpired_] ; @selector(setDemoExpired:)
00000001000049a0 4889DF                          mov        rdi, rbx
00000001000049a3 E840FF0300                      call       imp___symbol_stub1__objc_msgSend
00000001000049a8 488B0529A70600                  mov        rax, qword [ds:_OBJC_IVAR_$_AppController.demoDlgShown_10006f0d8]
00000001000049af C6040300                        mov        byte [ds:rbx+rax], 0x0
                                       ; Basic Block Input Regs: <nothing>
00000001000049b3 4883C408                        add        rsp, 0x8                      ; XREF=0x100004985
00000001000049b7 5B                              pop        rbx
00000001000049b8 C9                              leave      
00000001000049b9 C3                              ret        
                        ; endp

Now, let's us step over the assembly code and interpret it:

====== B E G I N   O F   P R O C E D U R E ======
 
 
                                       ; Basic Block Input Regs: rax rdi
                                            methImpl_AppController_onDemoTimer_:
000000010000496b 55                              push       rbp                                                             <-----+ entry point
000000010000496c 4889E5                          mov        rbp, rsp
000000010000496f 53                              push       rbx
0000000100004970 4883EC08                        sub        rsp, 0x8
0000000100004974 4889FB                          mov        rbx, rdi
0000000100004977 488B35C25A0600                  mov        rsi, qword [ds:objc_sel_isRegistered] ; @selector(isRegistered) <-----+ remember this?
000000010000497e E865FF0300                      call       imp___symbol_stub1__objc_msgSend
0000000100004983 84C0                            test       al, al                                                          <-----+ a test condition
0000000100004985 752C                            jne        0x1000049B3                                                     <-----+ jump if the register contents are not equal to 0x1000049B3

Looking at the 0000000100004977, 0000000100004983 and 0000000100004985 we can observe that the program makes a decision based on the outcome of the test involving isRegistered. Based on the outcome of the test, the program jumps to 00000001000049B3. Now, if we look in the same procedure at the address 00000001000049b3, we have:

00000001000049b3 4883C408                        add        rsp, 0x8                      ; XREF=0x100004985 <--- jumped from 0x100004985
00000001000049b7 5B                              pop        rbx
00000001000049b8 C9                              leave      
00000001000049b9 C3                              ret                                                         <--- callback returns.  
                        ; endp

Which means that if the register contents checked by the test al, al line are not equal, then the procedure jumps over everything in between, right to 00000001000049b3 and then the callback returns.

Thinking about it, if that test fails, then the timer callback just exits without doing anything. We also know that when we ran the application in demo mode, after some time we would get prompted with a nag screen and the application would disable itself. Thus, we know that the timer callback must at least do something. At this point we already know what to do, we need to turn that jne opcode so that regardless of the outcome of the test, the callback should jump to 00000001000049b3 and just leave without doing anything.

For shits and giggles, let's see what the part between the jne and the last part of the code is:

                                       ; Basic Block Input Regs: rax rbx
0000000100004987 488B35D2580600                  mov        rsi, qword [ds:objc_sel_isCommercialUse] ; @selector(isCommercialUse)
000000010000498e 4889DF                          mov        rdi, rbx
0000000100004991 E852FF0300                      call       imp___symbol_stub1__objc_msgSend
0000000100004996 0FBED0                          movsx      edx, al
0000000100004999 488B35705A0600                  mov        rsi, qword [ds:objc_sel_setDemoExpired_] ; @selector(setDemoExpired:)    <---- aha...
00000001000049a0 4889DF                          mov        rdi, rbx
00000001000049a3 E840FF0300                      call       imp___symbol_stub1__objc_msgSend
00000001000049a8 488B0529A70600                  mov        rax, qword [ds:_OBJC_IVAR_$_AppController.demoDlgShown_10006f0d8]        <---- a dialog, aaa...
00000001000049af C6040300                        mov        byte [ds:rbx+rax], 0x0

Looking at the part in between, we see that it sets the demo as being expired setDemoExpired which makes sense because that is what the application behaves like in demo mode. After that, the application additionally manipulates a dialog parameter demoDlgShown. Great, that is all what we need for a quick solution.

We now have to change the jne to a jmp. Here is a short table of instructions:

opcode value meaning
jne 0x74 jump if not equal
je 0x0F jump if equal
jmp 0xE9 jump
nop 0x90 no operation (skip)

which will ignore the test and prevent the application from setting of the demo as being expired every time that the callback is entered. Additionally, we can nop the block after the jmp. Either the disassembler allows you to change the instructions, or you use a hex editor and load the application binary, searching for the address 0000000100004985 in order to change the jne to a jmp, the final result will look like this:

                                            methImpl_AppController_onDemoTimer_:
000000010000496b 55                              push       rbp
000000010000496c 4889E5                          mov        rbp, rsp
000000010000496f 53                              push       rbx
0000000100004970 4883EC08                        sub        rsp, 0x8
0000000100004974 4889FB                          mov        rbx, rdi
0000000100004977 488B35C25A0600                  mov        rsi, qword [ds:objc_sel_isRegistered] ; @selector(isRegistered)
000000010000497e E865FF0300                      call       imp___symbol_stub1__objc_msgSend
0000000100004983 84C0                            test       al, al
0000000100004985 E929000000                      jmp        0x1000049B3
000000010000498a 90                              nop        
000000010000498b 90                              nop        
000000010000498c 90                              nop        
000000010000498d 90                              nop        
000000010000498e 4889DF                          mov        rdi, rbx
0000000100004991 E852FF0300                      call       imp___symbol_stub1__objc_msgSend
0000000100004996 0FBED0                          movsx      edx, al
0000000100004999 488B35705A0600                  mov        rsi, qword [ds:objc_sel_setDemoExpired_] ; @selector(setDemoExpired:)
00000001000049a0 4889DF                          mov        rdi, rbx
00000001000049a3 E840FF0300                      call       imp___symbol_stub1__objc_msgSend
00000001000049a8 488B0529A70600                  mov        rax, qword [ds:_OBJC_IVAR_$_AppController.demoDlgShown_10006f0d8]
00000001000049af C6040300                        mov        byte [ds:rbx+rax], 0x0
00000001000049b3 4883C408                        add        rsp, 0x8                      ; XREF=0x100004985
00000001000049b7 5B                              pop        rbx
00000001000049b8 C9                              leave      
00000001000049b9 C3                              ret        

The final step is to have the disassembler create the binary, or just save the file if you used a hex editor and you are done. The application will never expire. :-)

Removing the Register Menu Item

The register menu item is encoded inside the NIB file MainMenu.nib in the Resouces/English.lproj subdirectory. We use NibUnlocker to decompile the MainMenu.nib. Then, in the resulting XIB file, we knock out the following tags:

<object class="NSMenuItem" id="235">
    <int key="NSMnemonicLoc">2147483647</int>
    <reference key="NSMenu" ref="231"/>
    <string key="NSKeyEquiv"></string>
    <string key="NSTitle">Register</string>
    <reference key="NSOnImage" ref="496"/>
    <reference key="NSMixedImage" ref="497"/>
</object>
 
<object class="IBObjectRecord">
    <int key="objectID">966</int>
    <reference key="object" ref="235"/>
    <reference key="parent" ref="231"/>
    <string key="objectName">Menu Item (Register)</string>
</object>
 
<object class="IBConnectionRecord">
    <object class="IBOutletConnection" key="connection">
        <string key="label">regItem</string>
        <reference key="source" ref="337"/>
        <reference key="destination" ref="235"/>
    </object>
    <int key="connectionID">968</int>
</object>
 
<object class="IBConnectionRecord">
    <object class="IBActionConnection" key="connection">
        <string key="label">registerMenuClick:</string>
        <reference key="source" ref="337"/>
        <reference key="destination" ref="235"/>
    </object>
    <int key="connectionID">967</int>
</object>
 
<reference ref="235"/>

which is responsible for displaying the Register menu item, along with all its references. After that, we compile back the xib to a nib by using:

ibtool --errors --warnings --notices --output-format human-readable-text --compile MainMenu.nib MainMenu.xib

and then replace the original file with the modified one. The result is shown below: no register menu.

No register menu item after reverse-engineering NIB.

The same procedure can be used to reverse-engineer the German menu file. That is it for ShareMouse 1.0.92. Sadly, ShareMouse doesn't have relative mouse movement which makes playing games impossible. Enjoy, none the less... :-)


cracks/sharemouse/1.x.txt · Last modified: 2017/02/22 18:30 (external edit)

Access website using Tor


For the copyright, license, warranty and privacy terms for the usage of this website please see the license and privacy pages.