Table of Contents

Shortnote

The Perfect Photo Suite is an addon for Adobe Photoshop CS6. In demo-mode it runs for 15 days, after which it starts nagging. It seems that the whole nag system is easily dismantled by disabling the showDemoOrActivationIfNecessary in the Perfect Photo Suite application. However that is not clean enough if other applications from the suite are launched individually.

Perfect Photo Suite

Cracking the Perfect Photo Suite is redundant and the best way is to crack the onOneToolbox helper application instead. We have left this section here because it represented the first logical step. You can jump directly to the "Framework Protection" section.

The Perfect Photo Suite application can be easily made to shut-up by short-circuiting the showDemoOrActivationIfNecessary method. This is a self-standing check that is implemented in the Perfect Photo Suite binary. Later on, we will move to the framework checks.

First, since activation and showing demo screens is not really necessary, we go to the showDemoOrActivationIfNecessary method and jmp from the start to the end of the method. This will only ensure that the popups will not show for just the Perfect Photo Suite application which implements its own checks.

====== B E G I N   O F   P R O C E D U R E ======
 
 
                                       ; Basic Block Input Regs: rax rsi rdi -  Killed Regs: rbx rsi rdi
                                            __ZN9PLToolbox31showDemoOrActivationIfNecessaryEv_10004b644:        // PLToolbox::showDemoOrActivationIfNecessary()
000000010004b644 55                              push       rbp                           ; XREF=0x10002294c
000000010004b645 4889E5                          mov        rbp, rsp
000000010004b648 53                              push       rbx
000000010004b649 4883EC08                        sub        rsp, 0x8
000000010004b64d 4889FB                          mov        rbx, rdi
000000010004b650 488B7F10                        mov        rdi, qword [ds:rdi+0x10]
000000010004b654 31F6                            xor        esi, esi
000000010004b656 E8B92E0200                      call       imp___symbol_stub1__onOneActivationToolboxRegister
000000010004b65b 894318                          mov        dword [ds:rbx+0x18], eax
000000010004b65e 3D204E0000                      cmp        eax, 0x4E20
000000010004b663 7410                            je         0x10004B675
                                       ; Basic Block Input Regs: rax -  Killed Regs: <nothing>
000000010004b665 3D224E0000                      cmp        eax, 0x4E22
000000010004b66a 7409                            je         0x10004B675
                                       ; Basic Block Input Regs: rax rbx -  Killed Regs: rbx
000000010004b66c 31DB                            xor        ebx, ebx
000000010004b66e 3D214E0000                      cmp        eax, 0x4E21
000000010004b673 7525                            jne        0x10004B69A
                                       ; Basic Block Input Regs: rax -  Killed Regs: rbx
000000010004b675 89C3                            mov        ebx, eax                      ; XREF=0x10004b663, 0x10004b66a
000000010004b677 85C0                            test       eax, eax
000000010004b679 741F                            je         0x10004B69A
                                       ; Basic Block Input Regs: rax -  Killed Regs: rax rcx rdx rsi rdi
000000010004b67b 488D0D86522300                  lea        rcx, qword [ds:cfstring_PLToolbox__showDemoOrActivationIfNecessary__] ; @"PLToolbox::showDemoOrActivationIfNecessary()"
000000010004b682 89C2                            mov        edx, eax
000000010004b684 488D0505672300                  lea        rax, qword [ds:_kPerfectLayersStr_100281d90] ; ""
000000010004b68b 488B30                          mov        rsi, qword [ds:rax]
000000010004b68e BF01000000                      mov        edi, 0x1
000000010004b693 31C0                            xor        eax, eax
000000010004b695 E842150200                      call       imp___symbol_stub1__ONLogWithParams
                                       ; Basic Block Input Regs: rbx -  Killed Regs: rax rbx rsp
000000010004b69a 89D8                            mov        eax, ebx                      ; XREF=0x10004b673, 0x10004b679
000000010004b69c 4883C408                        add        rsp, 0x8
000000010004b6a0 5B                              pop        rbx
000000010004b6a1 C9                              leave      
000000010004b6a2 C3                              ret        
                        ; endp
000000010004b6a3 90                              nop

We jump directly from 0x10004b64d to the end of the method at 000000010004b69a.

====== B E G I N   O F   P R O C E D U R E ======
 
 
                                       ; Basic Block Input Regs: <nothing> -  Killed Regs: <nothing>
                                            __ZN9PLToolbox31showDemoOrActivationIfNecessaryEv_10004b644:        // PLToolbox::showDemoOrActivationIfNecessary()
000000010004b644 55                              push       rbp                           ; XREF=0x10002294c
000000010004b645 4889E5                          mov        rbp, rsp
000000010004b648 53                              push       rbx
000000010004b649 4883EC08                        sub        rsp, 0x8
000000010004b64d E948000000                      jmp        0x10004B69A
000000010004b652 90                              nop        
000000010004b653 90                              nop        
000000010004b654 31F6                            xor        esi, esi
000000010004b656 E8B92E0200                      call       imp___symbol_stub1__onOneActivationToolboxRegister
000000010004b65b 894318                          mov        dword [ds:rbx+0x18], eax
000000010004b65e 3D204E0000                      cmp        eax, 0x4E20
000000010004b663 7410                            je         0x10004B675
000000010004b665 3D224E0000                      cmp        eax, 0x4E22
000000010004b66a 7409                            je         0x10004B675
000000010004b66c 31DB                            xor        ebx, ebx
000000010004b66e 3D214E0000                      cmp        eax, 0x4E21
000000010004b673 7525                            jne        0x10004B69A
000000010004b675 89C3                            mov        ebx, eax                      ; XREF=0x10004b663, 0x10004b66a
000000010004b677 85C0                            test       eax, eax
000000010004b679 741F                            je         0x10004B69A
000000010004b67b 488D0D86522300                  lea        rcx, qword [ds:cfstring_PLToolbox__showDemoOrActivationIfNecessary__] ; @"PLToolbox::showDemoOrActivationIfNecessary()"
000000010004b682 89C2                            mov        edx, eax
000000010004b684 488D0505672300                  lea        rax, qword [ds:_kPerfectLayersStr_100281d90] ; ""
000000010004b68b 488B30                          mov        rsi, qword [ds:rax]
000000010004b68e BF01000000                      mov        edi, 0x1
000000010004b693 31C0                            xor        eax, eax
000000010004b695 E842150200                      call       imp___symbol_stub1__ONLogWithParams
                                       ; Basic Block Input Regs: rbx -  Killed Regs: rax rbx rsp
000000010004b69a 89D8                            mov        eax, ebx                      ; XREF=0x10004b64d, 0x10004b673, 0x10004b679
000000010004b69c 4883C408                        add        rsp, 0x8
000000010004b6a0 5B                              pop        rbx
000000010004b6a1 C9                              leave      
000000010004b6a2 C3                              ret        
                        ; endp
000000010004b6a3 90                              nop

Framework Protection

Each plugin uses the onOneToolbox binary to show the demo dialog using the method showDemoDialog. There are two onOneToolbox binaries located at:

They are the same file, intuitively it is part of the framework for the application and placed in two different locations so that the suite can run in stand-alone mode by running just an individual application.

Hiding the Demo Window

There are two types of windows that pop-up. One of them is the regular demo window (showDemoDialog) and the other is the demo expired window (showDemoExpiredDialog).

showDemoDialog

A simple one, following the reasoning, we follow the jump and skip over the window display. Here is the original version:

 
====== B E G I N   O F   P R O C E D U R E ======
 
 
                                       ; Basic Block Input Regs: rdi -  Killed Regs: rax r12 r13
                                            methImpl_OTBDemoDialogController_showDemoDialog:
00000000000099d6 55                              push       rbp
00000000000099d7 4889E5                          mov        rbp, rsp
00000000000099da 4155                            push       r13
00000000000099dc 4154                            push       r12
00000000000099de 53                              push       rbx
00000000000099df 4883EC18                        sub        rsp, 0x18
00000000000099e3 4989FC                          mov        r12, rdi
00000000000099e6 4C8D2DB3BC0000                  lea        r13, qword [ds:_OBJC_IVAR_$_OTBDialogController.myWindow]
00000000000099ed 498B4500                        mov        rax, qword [ds:r13+0x0]
00000000000099f1 48833C0700                      cmp        qword [ds:rdi+rax], 0x0
00000000000099f6 754F                            jne        0x9A47
                                       ; Basic Block Input Regs: rdi -  Killed Regs: rax
00000000000099f8 488B0591B80000                  mov        rax, qword [ds:_OBJC_IVAR_$_OTBDemoDialogController.mDemoOnly]
00000000000099ff 803C0700                        cmp        byte [ds:rdi+rax], 0x0
0000000000009a03 7413                            je         0x9A18
                                       ; Basic Block Input Regs: r12 -  Killed Regs: rcx rdx rdi
0000000000009a05 488B3DF4A30000                  mov        rdi, qword [ds:bind__OBJC_CLASS_$_NSBundle]
0000000000009a0c 4C89E1                          mov        rcx, r12
0000000000009a0f 488D1572910000                  lea        rdx, qword [ds:cfstring_DemoOnly] ; @"DemoOnly"
0000000000009a16 EB11                            jmp        0x9A29
                                       ; Basic Block Input Regs: r12 -  Killed Regs: rcx rdx rdi
0000000000009a18 488B3DE1A30000                  mov        rdi, qword [ds:bind__OBJC_CLASS_$_NSBundle] ; XREF=0x9a03
0000000000009a1f 4C89E1                          mov        rcx, r12
0000000000009a22 488D157F910000                  lea        rdx, qword [ds:cfstring_Demo] ; @"Demo"
                                       ; Basic Block Input Regs: rbx r12 r13 -  Killed Regs: rax rbx rsi
0000000000009a29 488D35B89F0000                  lea        rsi, qword [ds:objc_msg_loadNibNamed_owner_] ; @selector(loadNibNamed:owner:) XREF=0x9a16
0000000000009a30 FF15B29F0000                    call       qword [ds:objc_msg_loadNibNamed_owner_] ; @selector(loadNibNamed:owner:)
0000000000009a36 498B4500                        mov        rax, qword [ds:r13+0x0]
0000000000009a3a 31DB                            xor        ebx, ebx
0000000000009a3c 49833C0400                      cmp        qword [ds:r12+rax], 0x0
0000000000009a41 0F84C7000000                    je         0x9B0E
                                       ; Basic Block Input Regs: r12 r13 -  Killed Regs: rax rcx rdx rbx rbp rsi rdi
0000000000009a47 4C8965D0                        mov        qword [ss:rbp-0x30+var_0], r12 ; XREF=0x99f6
0000000000009a4b 488B05FEA40000                  mov        rax, qword [ds:0x13F50]
0000000000009a52 488945D8                        mov        qword [ss:rbp-0x30+var_8], rax
0000000000009a56 488D7DD0                        lea        rdi, qword [ss:rbp-0x30+var_0]
0000000000009a5a 488D35F7A00000                  lea        rsi, qword [ds:objc_msg_setupWindow] ; @selector(setupWindow)

The procedure is as follows:

The result will be:

                                            methImpl_OTBDemoDialogController_showDemoDialog:
00000000000099d6 55                              push       rbp
00000000000099d7 4889E5                          mov        rbp, rsp
00000000000099da 4155                            push       r13
00000000000099dc 4154                            push       r12
00000000000099de 53                              push       rbx
00000000000099df 4883EC18                        sub        rsp, 0x18
00000000000099e3 4989FC                          mov        r12, rdi
00000000000099e6 4C8D2DB3BC0000                  lea        r13, qword [ds:_OBJC_IVAR_$_OTBDialogController.myWindow]
00000000000099ed 498B4500                        mov        rax, qword [ds:r13+0x0]
00000000000099f1 48833C0700                      cmp        qword [ds:rdi+rax], 0x0
00000000000099f6 90                              nop        
00000000000099f7 90                              nop        
00000000000099f8 488B0591B80000                  mov        rax, qword [ds:_OBJC_IVAR_$_OTBDemoDialogController.mDemoOnly]
00000000000099ff 803C0700                        cmp        byte [ds:rdi+rax], 0x0
0000000000009a03 7413                            je         0x9A18
0000000000009a05 488B3DF4A30000                  mov        rdi, qword [ds:bind__OBJC_CLASS_$_NSBundle]
0000000000009a0c 4C89E1                          mov        rcx, r12
0000000000009a0f 488D1572910000                  lea        rdx, qword [ds:cfstring_DemoOnly] ; @"DemoOnly"
0000000000009a16 EB11                            jmp        0x9A29
0000000000009a18 488B3DE1A30000                  mov        rdi, qword [ds:bind__OBJC_CLASS_$_NSBundle] ; XREF=0x9a03
0000000000009a1f 4C89E1                          mov        rcx, r12
0000000000009a22 488D157F910000                  lea        rdx, qword [ds:cfstring_Demo] ; @"Demo"
0000000000009a29 488D35B89F0000                  lea        rsi, qword [ds:objc_msg_loadNibNamed_owner_] ; @selector(loadNibNamed:owner:) XREF=0x9a16
0000000000009a30 FF15B29F0000                    call       qword [ds:objc_msg_loadNibNamed_owner_] ; @selector(loadNibNamed:owner:)
0000000000009a36 498B4500                        mov        rax, qword [ds:r13+0x0]
0000000000009a3a 31DB                            xor        ebx, ebx
0000000000009a3c 49833C0400                      cmp        qword [ds:r12+rax], 0x0
0000000000009a41 E9C8000000                      jmp        0x9B0E

showDemoExpiredDialog

Exactly the same procedure applies to the showDemoExpiredDialog. Fist nop the first jne and at 0x9d69 turn the je into a jmp. The final version is:

                                            methImpl_OTBDemoExpiredDialogController_showDemoExpiredDialog:
0000000000009cf6 55                              push       rbp
0000000000009cf7 4889E5                          mov        rbp, rsp
0000000000009cfa 48895DE8                        mov        qword [ss:rbp+0xFFFFFFFFFFFFFFE8], rbx
0000000000009cfe 4C8965F0                        mov        qword [ss:rbp+0xFFFFFFFFFFFFFFF0], r12
0000000000009d02 4C896DF8                        mov        qword [ss:rbp+0xFFFFFFFFFFFFFFF8], r13
0000000000009d06 4883EC30                        sub        rsp, 0x30
0000000000009d0a 4889FB                          mov        rbx, rdi
0000000000009d0d 4C8D2D8CB90000                  lea        r13, qword [ds:_OBJC_IVAR_$_OTBDialogController.myWindow]
0000000000009d14 498B4500                        mov        rax, qword [ds:r13+0x0]
0000000000009d18 48833C0700                      cmp        qword [ds:rdi+rax], 0x0
0000000000009d1d 90                              nop        
0000000000009d1e 90                              nop        
0000000000009d1f 488B05AAB60000                  mov        rax, qword [ds:_OBJC_IVAR_$_OTBDemoExpiredDialogController.mDemoOnly]
0000000000009d26 803C0700                        cmp        byte [ds:rdi+rax], 0x0
0000000000009d2a 7413                            je         0x9D3F
0000000000009d2c 488B3DCDA00000                  mov        rdi, qword [ds:bind__OBJC_CLASS_$_NSBundle]
0000000000009d33 4889D9                          mov        rcx, rbx
0000000000009d36 488D158B8E0000                  lea        rdx, qword [ds:cfstring_DemoOnlyExpired] ; @"DemoOnlyExpired"
0000000000009d3d EB11                            jmp        0x9D50
0000000000009d3f 488B3DBAA00000                  mov        rdi, qword [ds:bind__OBJC_CLASS_$_NSBundle] ; XREF=0x9d2a
0000000000009d46 4889D9                          mov        rcx, rbx
0000000000009d49 488D15988E0000                  lea        rdx, qword [ds:cfstring_DemoExpired] ; @"DemoExpired"
0000000000009d50 488D35919C0000                  lea        rsi, qword [ds:objc_msg_loadNibNamed_owner_] ; @selector(loadNibNamed:owner:) XREF=0x9d3d
0000000000009d57 FF158B9C0000                    call       qword [ds:objc_msg_loadNibNamed_owner_] ; @selector(loadNibNamed:owner:)
0000000000009d5d 498B4500                        mov        rax, qword [ds:r13+0x0]
0000000000009d61 4531E4                          xor        r12d, r12d
0000000000009d64 48833C0300                      cmp        qword [ds:rbx+rax], 0x0
0000000000009d69 E954000000                      jmp        0x9DC2

Hiding the License Window

After hiding the demo window, the license window will pop up. Just hiding the license window will not cut it. We need to fiddle with the ZN17ActivationToolbox12ActivationUIE11LicenseMode_8982 method in order to remove:

0000000000008a88 E86D1F0000                      call       _doLicenseEntryDialog_a9fa

that is responsible for showing the license window in the first place. We do that by noping the doLicenseEntryDialog call and then turning the je 0x8D24 into a jmp:

0000000000008a73 4C89F7                          mov        rdi, r14                      ; XREF=0x8d04, 0x8b47
0000000000008a76 E817060000                      call       __ZN28ActivationToolboxStringUtils19format18DigitStringERSs_9092 ; ActivationToolboxStringUtils::format18DigitString(std::string&)
0000000000008a7b 4C89F2                          mov        rdx, r14
0000000000008a7e 488BB538FFFFFF                  mov        rsi, qword [ss:rbp+0xFFFFFFFFFFFFFF38]
0000000000008a85 4C89E7                          mov        rdi, r12
0000000000008a88 90                              nop        
0000000000008a89 90                              nop        
0000000000008a8a 90                              nop        
0000000000008a8b 90                              nop        
0000000000008a8c 90                              nop        
0000000000008a8d 89C3                            mov        ebx, eax
0000000000008a8f 83F804                          cmp        eax, 0x4
0000000000008a92 E98D020000                      jmp        0x8D24
0000000000008a97 90                              nop

That is it.