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.
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.
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:
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.
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.
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…