8 November 2015
As with the previous version, Jitouch 2 consists in two distinct binaries that have to be modified:
Contents/MacOS/Jitouch
. Altering this binary, we can eliminate the register button, turn on an endless demo-mode and clear out any nag screens or artefacts.Contents/Resources/Jitouch.app/Contents/MacOS/Jitouch
. It also carries the same way, so be careful when replacing the altered binaries.
The first step is to remove the license check, simply by shorting the methImpl_JitouchPref_checkLicense
method by jumping directly from 0x1f7e
to 0x212d
, thereby skipping everything that the license check does.
; Basic Block Input Regs: rdi - Killed Regs: rax rdx rbx rbp rsi rdi methImpl_JitouchPref_checkLicense: 0000000000001f6d 55 push rbp 0000000000001f6e 4889E5 mov rbp, rsp 0000000000001f71 4157 push r15 0000000000001f73 4156 push r14 0000000000001f75 4155 push r13 0000000000001f77 4154 push r12 0000000000001f79 53 push rbx 0000000000001f7a 4883EC28 sub rsp, 0x28 0000000000001f7e E9A6010000 jmp 0x2129 ... 0000000000002129 4883C428 add rsp, 0x28 ; XREF=0x1fb0, 0x1f7e 000000000000212d 5B pop rbx 000000000000212e 415C pop r12 0000000000002130 415D pop r13 0000000000002132 415E pop r14 0000000000002134 415F pop r15 0000000000002136 5D pop rbp 0000000000002137 C3 ret
The jitouch
preference pane has a Register
tab that gets removed once the license check is passed. The Register
button is referenced in two places:
cfstring_Register: 000000000002b8f8 dq 0x0000000000000000, 0x00000000000007c8, 0x0000000000022df7, 0x0000000000000008 ; XREF=0x2370, 0x288e
0x2370
is the first reference and is triggered when the user actually registers jitouch
. Since we will not be doing that, we can ignore this reference.0x288e
is the second reference and is to be found in the methImpl_JitouchPref_mainViewDidLoad
, which we do want to attack.
The method that jitouch
uses to hide the Register
button is to first check whether jitouch
is registered (can be observed from 0x285e
to 0x2875
) and if jitouch
is registered, then the function continues down and calls ds:objc_sel_removeTabViewItem_
in order to remove the Register
button. Conversely, if jitouch
is not registered, then jitouch
jumps over that block. Thus, we nop
the jump such that jitouch
does proceed in the block in order to remove the button.
; Basic Block Input Regs: rdi - Killed Regs: rax rcx rdx rbx rsi rdi r8 r9 r12 r14 r15 xmm0 methImpl_JitouchPref_mainViewDidLoad: ... 000000000000285e 488B35F3760200 mov rsi, qword [ds:objc_sel_checkLicense] ; @selector(checkLicense) XREF=0x27fe 0000000000002865 4C89F7 mov rdi, r14 0000000000002868 FF15FA270200 call qword [ds:imp___got__objc_msgSend] 000000000000286e 488B05AB890200 mov rax, qword [ds:_OBJC_IVAR_$_JitouchPref.license] 0000000000002875 41833C0600 cmp dword [ds:r14+rax], 0x0 000000000000287a 90 nop 000000000000287b 90 nop ; Basic Block Input Regs: r14 r15 - Killed Regs: rax rdx rbx rsi rdi 000000000000287c 488B05B5890200 mov rax, qword [ds:_OBJC_IVAR_$_JitouchPref.mainTabView] 0000000000002883 498B1C06 mov rbx, qword [ds:r14+rax] 0000000000002887 488B35FA760200 mov rsi, qword [ds:objc_sel_indexOfTabViewItemWithIdentifier_] ; @selector(indexOfTabViewItemWithIdentifier:) 000000000000288e 488D1563900200 lea rdx, qword [ds:cfstring_Register] ; @"Register" 0000000000002895 4889DF mov rdi, rbx 0000000000002898 41FFD7 call r15 000000000000289b 488B35EE760200 mov rsi, qword [ds:objc_sel_tabViewItemAtIndex_] ; @selector(tabViewItemAtIndex:) 00000000000028a2 4889DF mov rdi, rbx 00000000000028a5 4889C2 mov rdx, rax 00000000000028a8 41FFD7 call r15 00000000000028ab 488B35E6760200 mov rsi, qword [ds:objc_sel_removeTabViewItem_] ; @selector(removeTabViewItem:) 00000000000028b2 4889DF mov rdi, rbx 00000000000028b5 4889C2 mov rdx, rax 00000000000028b8 41FFD7 call r15 ...
The sneaky binary that needs to be edited resides at:
Contents/Resources/Jitouch.app/Contents/MacOS/Jitouch
which is the background application that is launched from the preference pane.
The binary contains the expiration function methImpl_JitouchAppDelegate_expire
. This method is trigged from _increaseLicenseCounter
via obj_sel_expire
and can be short-circuited:
_increaseLicenseCounter: 000000010000dc8c 55 push rbp ; XREF=0x100009869, 0x100004c49, 0x1000071fa, 0x100007f24 000000010000dc8d 4889E5 mov rbp, rsp 000000010000dc90 53 push rbx 000000010000dc91 50 push rax 000000010000dc92 813DAC270100EF8C7915 cmp dword [ds:_license], 0x15798cef 000000010000dc9c E974000000 jmp 0x10000dd15 000000010000dca1 90 nop 000000010000dca2 90 nop 000000010000dca3 90 nop ... 000000010000dd15 4883C408 add rsp, 0x8 ; XREF=0x10000dc9c 000000010000dd19 5B pop rbx 000000010000dd1a 5D pop rbp 000000010000dd1b C3 ret
There may be other elaborate ways to disable the expiration, but this does the job.
The new version for El Capitain masks the expire
method as a delegate as well as adding stack-guard checks. However, the expire
method is called from sub_10000c8b7
such that we can conditionally jump to the end of sub_10000c8b7
without entering the section responsible for calling the expire
method:
sub_10000c8b7: 000000010000c8b7 push rbp ; XREF=sub_100002bfd+7248, sub_100006aba+779, sub_100006aba+4366, sub_100009169+805 000000010000c8b8 mov rbp, rsp 000000010000c8bb push rbx 000000010000c8bc push rax 000000010000c8bd cmp dword [ds:0x100022b48], 0x15798cef ; 0x100022b48 000000010000c8c7 jmp 0x10000c93e 000000010000c8cc nop 000000010000c8cd nop 000000010000c8ce nop ; ... 000000010000c93e add rsp, 0x8 ; XREF=sub_10000c8b7+16 000000010000c942 pop rbx 000000010000c943 pop rbp 000000010000c944 ret ; ... expire method is being called in the next section ...