The crack procedure for Folx 4.0.10913
involves convincing the application that it is purchased. Secondly, Folx seems to conditionally show the preference settings as locked. Since we are using jumps, we are first going to attack the features themselves and then bother with the locked preferences.
Unlocking all the features involves scanning the code for obj_sel_purchased
call and then changing either je
or jne
. Scanning for obj_sel_purchased reveals the following code-patterns.
For je
type jumps, we find the following pattern:
000000010013298a 488B356F588F00 mov rsi, qword [ds:objc_sel_purchased] ; @selector(purchased) 0000000100132991 FF15F12D8900 call qword [ds:imp___got__objc_msgSend] 0000000100132997 84C0 test al, al 0000000100132999 0F842E030000 je 0x100132ccd
at which point je
occurs in case the application is not purchased.
Thus, we nop
the je
.
For jne
type jumps, we find the following pattern:
0000000100105edc 488B351D239200 mov rsi, qword [ds:objc_sel_purchased] ; @selector(purchased) 0000000100105ee3 4889DF mov rdi, rbx 0000000100105ee6 41FFD7 call r15 0000000100105ee9 84C0 test al, al 0000000100105eeb E91D000000 jne 0x100105f0d
at which point the je
occurs in case the application is purchased.
Thus, we turn the jne
into a jmp
.
This procedure should be applied to the rest of the code whenever this sort of pattern occurs. The rest of the tutorial assumes that all the changes to the code have been made and we proceed to unlocking the preferences.
Unlocking the preferences, involves jerry-rigging the objc_sel_setShowOffer_
call to point to methImpl_AppDelegate_enterActivationKey_
instead of methImpl_AppDelegate_setShowOffer_
and then we manipulate methImpl_AppDelegate_enterActivationKey_
in order to set the application as purchased and unlock the settings.
In other words, objc_sel_setShowOffer_
is supposed to show special offers after the application has finished loading. Since, at this point we do not care, we make it point to methImpl_AppDelegate_enterActivationKey_
which is the method that is responsible for showing the registration. The methImpl_AppDelegate_enterActivationKey_
method, in case the registration is successful, contains the necessary code to unlock the preferences as well as setting the application as a purchased version.
With no further ado, locate objc_sel_setShowOffer_
in methImpl_AppDelegate_applicationWillFinishLaunching_
. If you have followed the previous step correctly, you will see the following code in methImpl_AppDelegate_applicationWillFinishLaunching_
:
0000000100020014 90 nop 0000000100020015 90 nop 0000000100020016 488B35AB90A000 mov rsi, qword [ds:objc_sel_setShowOffer_] ; @selector(setShowOffer:) 000000010002001d 31D2 xor edx, edx 000000010002001f 4C89E7 mov rdi, r12 0000000100020022 FF1560579A00 call qword [ds:imp___got__objc_msgSend] 0000000100020028 EB1F jmp 0x100020049
Now objc_sel_setShowOffer_
after imp_gotobjc_msgSend
will call methImpl_AppDelegate_setShowOffer_
. So, we go to the methImpl_AppDelegate_setShowOffer_
method:
; Basic Block Input Regs: rdx rsp - Killed Regs: rax rbp rdi methImpl_AppDelegate_setShowOffer_: 0000000100039f89 55 push rbp 0000000100039f8a 4889E5 mov rbp, rsp 0000000100039f8d 488B05AC3F9F00 mov rax, qword [ds:_OBJC_IVAR_$_AppDelegate._showOffer] 0000000100039f94 881407 mov byte [ds:rdi+rax], dl 0000000100039f97 5D pop rbp 0000000100039f98 C3 ret
and we replace the very first instruction with an unconditional jmp
(a long jump essentially) to methImpl_AppDelegate_enterActivationKey_
:
methImpl_AppDelegate_setShowOffer_: 0000000100039f89 E98942FFFF jmp methImpl_AppDelegate_enterActivationKey_ 0000000100039f8e 90 nop 0000000100039f8f 90 nop 0000000100039f90 90 nop 0000000100039f91 90 nop 0000000100039f92 90 nop 0000000100039f93 90 nop 0000000100039f94 881407 mov byte [ds:rdi+rax], dl 0000000100039f97 5D pop rbp 0000000100039f98 C3 ret
and now we can go to methImpl_AppDelegate_enterActivationKey_
.
The methImpl_AppDelegate_enterActivationKey_
method creates a modal window, in case the registration is successful it sets the application as purchased, unlocks the preference controls, and after that we do not really care what it does because it returns:
; Basic Block Input Regs: rax rdx rdi - Killed Regs: rdx rbx rsi rdi r12 r14 r15 methImpl_AppDelegate_enterActivationKey_: 000000010002e217 55 push rbp ; XREF=0x100039f89 000000010002e218 4889E5 mov rbp, rsp 000000010002e21b 4157 push r15 000000010002e21d 4156 push r14 000000010002e21f 4154 push r12 000000010002e221 53 push rbx 000000010002e222 4989FE mov r14, rdi 000000010002e225 488B3504AC9F00 mov rsi, qword [ds:objc_sel_switchToFullWindowView_] ; @selector(switchToFullWindowView:) 000000010002e22c 4C8B2555759900 mov r12, qword [ds:imp___got__objc_msgSend] 000000010002e233 31D2 xor edx, edx 000000010002e235 41FFD4 call r12 ; build the modal window to enter the license key, etc... ; we need to NOP this, because 000000010002e342 756F jne 0x10002e3b3 ; the next set of instructions after this are: ; Basic Block Input Regs: rax r12 r14 - Killed Regs: rdx rsi rdi 000000010002e344 488B3525A29F00 mov rsi, qword [ds:objc_sel_setPurchased_] ; @selector(setPurchased:) 000000010002e34b BA01000000 mov edx, 0x1 000000010002e350 4C89F7 mov rdi, r14 000000010002e353 41FFD4 call r12 000000010002e356 488B358BB49F00 mov rsi, qword [ds:objc_sel_unlockControls] ; @selector(unlockControls) 000000010002e35d 4C89F7 mov rdi, r14 000000010002e360 41FFD4 call r12 000000010002e363 488B351E9F9F00 mov rsi, qword [ds:objc_sel_needSendStatistic] ; @selector(needSendStatistic) 000000010002e36a 4C89F7 mov rdi, r14 000000010002e36d 41FFD4 call r12 000000010002e370 84C0 test al, al ; and we need to turn the je into a jmp because we really do not care about the rest 000000010002e372 743F je 0x10002e3b3 ; ...
Here is the resulting methImpl_AppDelegate_enterActivationKey_
method after the replacements have been successful, following the control-flow for brevity:
methImpl_AppDelegate_enterActivationKey_: 000000010002e217 55 push rbp ; XREF=0x100039f89 000000010002e218 4889E5 mov rbp, rsp 000000010002e21b 4157 push r15 000000010002e21d 4156 push r14 000000010002e21f 4154 push r12 000000010002e221 53 push rbx 000000010002e222 4989FE mov r14, rdi 000000010002e225 488B3504AC9F00 mov rsi, qword [ds:objc_sel_switchToFullWindowView_] ; @selector(switchToFullWindowView:) 000000010002e22c 4C8B2555759900 mov r12, qword [ds:imp___got__objc_msgSend] 000000010002e233 31D2 xor edx, edx 000000010002e235 41FFD4 call r12 000000010002e238 E907010000 jmp 0x10002e344 000000010002e23d 90 nop 000000010002e23e 90 nop ;... 000000010002e344 488B3525A29F00 mov rsi, qword [ds:objc_sel_setPurchased_] ; @selector(setPurchased:) XREF=0x10002e238 000000010002e34b BA01000000 mov edx, 0x1 000000010002e350 4C89F7 mov rdi, r14 000000010002e353 41FFD4 call r12 000000010002e356 488B358BB49F00 mov rsi, qword [ds:objc_sel_unlockControls] ; @selector(unlockControls) 000000010002e35d 4C89F7 mov rdi, r14 000000010002e360 41FFD4 call r12 000000010002e363 488B351E9F9F00 mov rsi, qword [ds:objc_sel_needSendStatistic] ; @selector(needSendStatistic) 000000010002e36a 4C89F7 mov rdi, r14 000000010002e36d 41FFD4 call r12 000000010002e370 84C0 test al, al 000000010002e372 E93C000000 jmp 0x10002e3b3 ; XREF=0x10002e372 000000010002e377 90 nop 000000010002e378 90 nop 000000010002e379 90 nop 000000010002e37a 90 nop ;... 000000010002e3b3 5B pop rbx ; XREF=0x10002e342, 0x10002e372, 0x10002e392 000000010002e3b4 415C pop r12 000000010002e3b6 415E pop r14 000000010002e3b8 415F pop r15 000000010002e3ba 5D pop rbp 000000010002e3bb C3 ret
The result, after restarting Folx, is that the preferences are unlocked: