Table of Contents

Summary

xScope is a screen measuring tool that comes along with different other tools which are useful for anything design related. The application has surprising features, such as being able to measure iOS screens as well as color pickers using a loupe that allows you to select pixels and gives you the numeric representation of the color.

Avast Nag-text!

So we're back on the x64 OSX platform, and looking to demine the application using jumps only. Using a quick search for symbols, we find the method isRegistered which is referenced in several places:

                                            objc_sel_isRegistered:
0000000100093e10                                 dq         0x000000010005d29d            ; XREF=0x100003d0e, 0x100010db2, 0x1000137e5, 0x100013e48, 0x10001e124, 0x10001fe28, ...

which has the following body:

 
====== B E G I N   O F   P R O C E D U R E ======
 
 
                                       ; Basic Block Input Regs: rsp rdi -  Killed Regs: rax rbp
                                            methImpl_RegistrationManager_isRegistered:
000000010001fe06 55                              push       rbp
000000010001fe07 4889E5                          mov        rbp, rsp
000000010001fe0a 488B057F8C0700                  mov        rax, qword [ds:_OBJC_IVAR_$_RegistrationManager.registrationState]
000000010001fe11 833C0702                        cmp        dword [ds:rdi+rax], 0x2
000000010001fe15 0F94C0                          sete       al
000000010001fe18 0FB6C0                          movzx      eax, al
000000010001fe1b 5D                              pop        rbp
000000010001fe1c C3                              ret        
                        ; endp

which returns a value based on a test performed by RegistrationManager. We can go ahead and modify the return so that the test always returns true. However, if we cannot modify the values themselves and want to stick to control flow, we have to go through the isRegistered cases and reason about the jumps. There are exactly 13 calls to isRegistered. Let us take the very first one in the method ALoupeView:

0000000100003d0e 488B35FB000900                  mov        rsi, qword [ds:objc_sel_isRegistered] ; @selector(isRegistered)
0000000100003d15 4889C7                          mov        rdi, rax
0000000100003d18 FFD3                            call       rbx
0000000100003d1a 84C0                            test       al, al
0000000100003d1c 0F8557010000                    jne        0x100003E79
                                       ; Basic Block Input Regs: rbp -  Killed Regs: xmm0 xmm1
0000000100003d22 F20F1005FE580600                movsd      xmm0, qword [ds:0x100069628]
0000000100003d2a F20F108D60FFFFFF                movsd      xmm1, qword [ss:rbp-0xe0+var_64]
0000000100003d32 660F2EC1                        ucomisd    xmm0, xmm1
0000000100003d36 770A                            jnbe       0x100003D42
                                       ; Basic Block Input Regs: <nothing> -  Killed Regs: xmm0
0000000100003d38 F20F1005E0580600                movsd      xmm0, qword [ds:0x100069620]
0000000100003d40 EB1E                            jmp        0x100003D60
                                       ; Basic Block Input Regs: rax xmm1 -  Killed Regs: rax rcx xmm0
0000000100003d42 F20F1005E6580600                movsd      xmm0, qword [ds:0x100069630]  ; XREF=0x100003d36
0000000100003d4a 660F2EC1                        ucomisd    xmm0, xmm1
0000000100003d4e 0F97C0                          setnbe     al
0000000100003d51 0FB6C0                          movzx      eax, al
0000000100003d54 488D0D15590600                  lea        rcx, qword [ds:0x100069670]   ; ""
0000000100003d5b F20F1004C1                      movsd      xmm0, qword [ds:rcx+rax*8]
                                       ; Basic Block Input Regs: rax rbp xmm0 xmm1 xmm2 -  Killed Regs: rax rcx rdx rbx rsp rbp rsi rdi r8 r9 r12 r14 r15 xmm2 xmm3 xmm4 xmm5
 
. . .
 
0000000100003de5 488D0544C80900                  lea        rax, qword [ds:_registrationReminderText]
0000000100003dec 488B18                          mov        rbx, qword [ds:rax]
0000000100003def 488B3532000900                  mov        rsi, qword [ds:objc_sel_sizeWithAttributes_] ; @selector(sizeWithAttributes:)
0000000100003df6 4889DF                          mov        rdi, rbx
0000000100003df9 4C89F2                          mov        rdx, r14

and notice the jump:

0000000100003d1c 0F8557010000                    jne        0x100003E79

If this test succeeds, then the whole block after the jump is skipped. The block being something that leads to:

0000000100003de5 488D0544C80900                  lea        rax, qword [ds:_registrationReminderText]

Since we do not want the nag-text to appear, we replace the jne by a jmp so that the test al, al becomes superfluous. This would have to be repeated 13 times for all the isRegistered calls.

Avast Reminders!

xScope pops up reminders from time to time, another symbol check and we find the method needsLicenseReminder:

 
====== B E G I N   O F   P R O C E D U R E ======
 
 
                                       ; Basic Block Input Regs: rax rcx rdi -  Killed Regs: rcx rsi rdi r14
                                            methImpl_RegistrationManager_needsLicenseReminder:
000000010001feac 55                              push       rbp
000000010001fead 4889E5                          mov        rbp, rsp
000000010001feb0 4157                            push       r15
000000010001feb2 4156                            push       r14
000000010001feb4 53                              push       rbx
000000010001feb5 50                              push       rax
000000010001feb6 4989FE                          mov        r14, rdi
000000010001feb9 488B35503F0700                  mov        rsi, qword [ds:objc_sel_isRegistered] ; @selector(isRegistered)
000000010001fec0 4C89F7                          mov        rdi, r14
000000010001fec3 FF1587D30500                    call       qword [ds:imp___got__objc_msgSend]
000000010001fec9 30C9                            xor        cl, cl
000000010001fecb 84C0                            test       al, al
000000010001fecd 7438                            je         0x10001FF07
                                       ; Basic Block Input Regs: rax rcx r14 -  Killed Regs: rax rcx rbx rsi rdi r15
000000010001fecf 488B3542480700                  mov        rsi, qword [ds:objc_sel_licenseCount] ; @selector(licenseCount)
000000010001fed6 4C8B3D73D30500                  mov        r15, qword [ds:imp___got__objc_msgSend]
000000010001fedd 4C89F7                          mov        rdi, r14
000000010001fee0 41FFD7                          call       r15
000000010001fee3 4889C3                          mov        rbx, rax
000000010001fee6 488B059B8B0700                  mov        rax, qword [ds:_OBJC_IVAR_$_RegistrationManager.serial]
000000010001feed 48FFC3                          inc        rbx
000000010001fef0 498B3C06                        mov        rdi, qword [ds:r14+rax]
000000010001fef4 488B3525480700                  mov        rsi, qword [ds:objc_sel_usersWithLicense] ; @selector(usersWithLicense)
000000010001fefb 41FFD7                          call       r15
000000010001fefe 30C9                            xor        cl, cl
000000010001ff00 4839D8                          cmp        rax, rbx
000000010001ff03 7E02                            jle        0x10001FF07
                                       ; Basic Block Input Regs: <nothing> -  Killed Regs: rcx
000000010001ff05 B101                            mov        cl, 0x1
                                       ; Basic Block Input Regs: rcx -  Killed Regs: rax rbx rsp rbp r14 r15
000000010001ff07 0FB6C1                          movzx      eax, cl                       ; XREF=0x10001fecd, 0x10001ff03
000000010001ff0a 4883C408                        add        rsp, 0x8
000000010001ff0e 5B                              pop        rbx
000000010001ff0f 415E                            pop        r14
000000010001ff11 415F                            pop        r15
000000010001ff13 5D                              pop        rbp
000000010001ff14 C3                              ret        
                        ; endp

and since we do not need reminders, we can short circuit this method as well and step over the isRegistered check by turning the je at 000000010001fecd into a jmp as if we were registered, to 0x10001FF07:

000000010001fecd E935000000                      jmp        0x10001FF07

Another interesting symbol is:

                                            cfstring___Remove_this_notice__purchase_now__:
000000010009d450                                 dq         0x0000000000000000, 0x00000000000007c8, 0x000000010005a385, 0x0000000000000024 ; XREF=0x10003ac8d, 0x100047a3f

which as only two references. Following the first one at 0x10003ac8d, we find:

000000010003ac84 751A                            jne        0x10003ACA0
                                       ; Basic Block Input Regs: rax r14 -  Killed Regs: rdx rsi rdi r14
000000010003ac86 488B35EB930500                  mov        rsi, qword [ds:objc_sel_stringByAppendingString_] ; @selector(stringByAppendingString:)
000000010003ac8d 488D15BC270600                  lea        rdx, qword [ds:cfstring___Remove_this_notice__purchase_now__] ; @" (Remove this notice: purchase now!)"
000000010003ac94 4C89F7                          mov        rdi, r14
000000010003ac97 FF15B3250400                    call       qword [ds:imp___got__objc_msgSend]
000000010003ac9d 4989C6                          mov        r14, rax
                                       ; Basic Block Input Regs: rax rbp r8 r14 -  Killed Regs: rax rcx rdx rbx rsi rdi r8 r15
000000010003aca0 488B3501960500                  mov        rsi, qword [ds:objc_sel_generalPasteboard] ; @selector(generalPasteboard) XREF=0x10003ac84

and since we do not want to purchase it, we remove the nagger by turning the jne into a jmp and paddign accordingly:

000000010003ac84 E917000000                      jmp        0x10003ACA0
000000010003ac89 90                              nop        
000000010003ac8a 90                              nop        
000000010003ac8b 90                              nop        
000000010003ac8c 90                              nop        
000000010003ac8d 488D15BC270600                  lea        rdx, qword [ds:cfstring___Remove_this_notice__purchase_now__] ; @" (Remove this notice: purchase now!)"
000000010003ac94 4C89F7                          mov        rdi, r14
000000010003ac97 FF15B3250400                    call       qword [ds:imp___got__objc_msgSend]
000000010003ac9d 4989C6                          mov        r14, rax
                                       ; Basic Block Input Regs: rax rbp r8 r14 -  Killed Regs: rax rcx rdx rbx rsi rdi r8 r15
000000010003aca0 488B3501960500                  mov        rsi, qword [ds:objc_sel_generalPasteboard] ; @selector(generalPasteboard) XREF=0x10003ac84

We do the same for the other reference at 0x100047a3f.

Fun Stuff

The PurchaseWindowController_registrationStatus checks whether the license is in order:

0000000100044409 488D0DA0510500                  lea        rcx, qword [ds:cfstring_]     ; @"" XREF=0x1000444ef, 0x10004444b
0000000100044410 488B3519050500                  mov        rsi, qword [ds:objc_sel_localizedStringForKey_value_table_] ; @selector(localizedStringForKey:value:table:)
0000000100044417 4889C7                          mov        rdi, rax
000000010004441a 4531C0                          xor        r8d, r8d
000000010004441d 488B052C8E0300                  mov        rax, qword [ds:imp___got__objc_msgSend]
0000000100044424 4883C408                        add        rsp, 0x8
0000000100044428 5B                              pop        rbx
0000000100044429 415E                            pop        r14
000000010004442b 415F                            pop        r15
000000010004442d 5D                              pop        rbp
000000010004442e FFE0                            jmp        rax
0000000100044430 488B35C1030500                  mov        rsi, qword [ds:objc_sel_mainBundle] ; @selector(mainBundle) XREF=0x1000443e7
0000000100044437 488B3D6A220500                  mov        rdi, qword [ds:bind__OBJC_CLASS_$_NSBundle]
000000010004443e FF150C8E0300                    call       qword [ds:imp___got__objc_msgSend]
0000000100044444 488D1565970500                  lea        rdx, qword [ds:cfstring_RegistrationInvalid] ; @"RegistrationInvalid"
000000010004444b EBBC                            jmp        0x100044409

it does that by checking the user and serial text fields and then jumping back until rax contains the needed value to break out of the jmp 0x100044409.

We can avoid this block and jump directly to the valid multiple licenses by changing:

00000001000443ec 745F                            je         0x10004444D

to a jump at address 0x10004444d, where we nop the next jump at 0x100044476 in order to have the program be thankful that we did not purchase any licenses at all:

 You are welcome. . .

That's it. :-)