SecuritySpy is an application that allows you to remotely monitor cameras. It has motion detection, the ability to record videos and a lot of other features. Cracking the application is rather simple because it relies on a flag named gTrialPeriodExpiredFlag
to hold the information regarding the trial expiration. Similarly, it uses functions to overlay DEMO
messages which are easily circumvented.
====== B E G I N O F P R O C E D U R E ====== ; Basic Block Input Regs: ebp - Killed Regs: eax ecx ebx esi edi _DrawDemoMessage_4edb0: 0004edb0 55 push ebp ; XREF=0x4a191 0004edb1 89E5 mov ebp, esp 0004edb3 53 push ebx 0004edb4 57 push edi 0004edb5 56 push esi 0004edb6 83EC4C sub esp, 0x4C 0004edb9 E800000000 call 0x4EDBE 0004edbe 59 pop ecx ; XREF=0x4edb9 0004edbf 0FBF4508 movsx eax, word [ss:ebp-0x58+arg_0] 0004edc3 69F090170000 imul esi, eax, 0x1790 0004edc9 8BB96E420A00 mov edi, dword [ds:ecx-0x4edbe+0xF302C] 0004edcf 8B5C377C mov ebx, dword [ds:edi+esi+0x7C] 0004edd3 8B8C3794000000 mov ecx, dword [ds:edi+esi+0x94] 0004edda 85C9 test ecx, ecx 0004eddc 751C jne 0x4EDFA ; Basic Block Input Regs: eax - Killed Regs: eax esp 0004edde 0FBFC0 movsx eax, ax 0004ede1 890424 mov dword [ss:esp], eax 0004ede4 E8978D0100 call _GenerateDemoMessageGWorld_67b80 0004ede9 85C0 test eax, eax 0004edeb 0F8569040000 jne 0x4F25A
We turn the jne
to 0x4EDFA
into a jump to 0x4F25A
. This is because the first jne
leads to the code segment responsible with displaying he DEMO
message when SecuritySpy is running in Active mode.
00042f03 0F8497000000 je 0x42FA0 ; Basic Block Input Regs: eax edi - Killed Regs: eax ecx ebx esp ebp esi 00042f09 E8423D0900 call imp___symbol_stub__CFBundleGetMainBundle 00042f0e 8D8B2F6D0B00 lea ecx, dword [ds:ebx-0x42a91+cfstring_DDNS_disabled_because_your_trial_period_has_expired] ; @"DDNS disabled because your trial period has expired"
Simple enough, the je
takes us over the block so we turn the je
into a jmp
to the same address.
There is an additional protection:
0004399f 750D jne 0x439AE 000439a1 E8AA320900 call imp___symbol_stub__CFBundleGetMainBundle 000439a6 8D8B2F6D0B00 lea ecx, dword [ds:ebx+0xB6D2F] ; @"DDNS disabled because your trial period has expired" 000439ac EBA4 jmp 0x43952 000439ae 80F945 cmp cl, 0x45 ; XREF=0x4399f
Easy enough, we eliminate this check by turning the jne
into a jmp
.
So that the trial never expires, we never set it to 0x1
. This is around 0x953f
:
0000953d 7609 jbe 0x9548 ; Basic Block Input Regs: <nothing> - Killed Regs: esi 0000953f C68667580F0001 mov byte [ds:esi-0x5aa1+_gTrialPeriodExpiredFlag], 0x1 ; Basic Block Input Regs: eax - Killed Regs: eax 00009546 31C0 xor eax, eax ; XREF=0x5e2d, 0x6205 ; Basic Block Input Regs: ebp - Killed Regs: ecx 00009548 8B8D9CFDFFFF mov ecx, dword [ss:ebp-0x278+var_20] ; XREF=0x5b60, 0x5e07, 0x953d, 0x5c46, 0x5c7a, 0x60b3, ...
We just set the jbe
to a jmp
in order to avoid setting the gTrialPeriodExpiredFlag
to 0x1
.
Another sequence where the flag is set:
0000ffda 7607 jbe 0xFFE3 ; Basic Block Input Regs: <nothing> - Killed Regs: edi 0000ffdc C687D7B70E0001 mov byte [ds:edi-0xfb31+_gTrialPeriodExpiredFlag], 0x1 ; Basic Block Input Regs: ebp - Killed Regs: eax 0000ffe3 8B85D4FEFFFF mov eax, dword [ss:ebp-0x158+var_44] ; XREF=0xffc3, 0xffda
We turn the jbe
into a jmp
to void setting it.
Now we could stop because the flag is never set. But we can go on and mess with the conditionals where the flag is checked. One of those instances is at 0x11068
:
; Basic Block Input Regs: ebx - Killed Regs: <nothing> 00011068 80BB67A80E0000 cmp byte [ds:ebx-0x10aa1+_gTrialPeriodExpiredFlag], 0x0 0001106f 7405 je 0x11076 ; Basic Block Input Regs: <nothing> - Killed Regs: <nothing> 00011071 E8BA5B0300 call _DisplayTrialPeriodExpiredMessage_46c30 ; Basic Block Input Regs: ebx edi - Killed Regs: ebx edi 00011076 31FF xor edi, edi ; XREF=0x1106f
roughly translates to "if the gTrialPeriodExpiredFlag
is 0x0
then do not display the trial expired message". So we turn the je
into a jmp
. This is not necessary, of course, if we avoid the setting of the flag to 0x1
initially.
We find the same sequence later on:
; Basic Block Input Regs: edi - Killed Regs: <nothing> 0003ebe5 80BF2AC80B0000 cmp byte [ds:edi-0x3eade+_gTrialPeriodExpiredFlag], 0x0 0003ebec 7405 je 0x3EBF3 ; Basic Block Input Regs: <nothing> - Killed Regs: <nothing> 0003ebee E83D800000 call _DisplayTrialPeriodExpiredMessage_46c30 ; Basic Block Input Regs: esi edi - Killed Regs: esi edi 0003ebf3 31F6 xor esi, esi ; XREF=0x3ebec
Again, we turn the je
into a jmp
.
On the same wavelength but with a small difference is:
; Basic Block Input Regs: eax - Killed Regs: <nothing> 000414cf 80B85AA00B0000 cmp byte [ds:eax-0x412ae+_gTrialPeriodExpiredFlag], 0x0 000414d6 750A jne 0x414E2 ; Basic Block Input Regs: eax esi - Killed Regs: esi edi 000414d8 31F6 xor esi, esi 000414da 8BB87E1D0B00 mov edi, dword [ds:eax-0x412ae+0xF302C] 000414e0 EB1E jmp 0x41500 ; Basic Block Input Regs: eax esi - Killed Regs: esi edi 000414e2 89C7 mov edi, eax ; XREF=0x414d6 000414e4 E847570000 call _DisplayTrialPeriodExpiredMessage_46c30 000414e9 31F6 xor esi, esi 000414eb 8BBF7E1D0B00 mov edi, dword [ds:edi+0xB1D7E] 000414f1 6666666666662E0F1F840000000000 nop word [cs:eax+eax-0x412ae+0x412AE] ; Basic Block Input Regs: edi - Killed Regs: <nothing> 00041500 803F00 cmp byte [ds:edi], 0x0 ; XREF=0x414e0, 0x41535 00041503 7422 je 0x41527
Where we nop
the jne
instead of replacing it the jump. The conditional is inverted in this case.
More of turning to jmp
:
; Basic Block Input Regs: ebx esi - Killed Regs: edi 00068443 80BEF730090000 cmp byte [ds:esi-0x68211+_gTrialPeriodExpiredFlag], 0x0 ; XREF=0x68437 0006844a 6689DF mov di, bx 0006844d 0F841F010000 je 0x68572 ; Basic Block Input Regs: eax ebx ebp - Killed Regs: eax ecx ebx esp ebp esi edi 00068453 E8F8E70600 call imp___symbol_stub__CFBundleGetMainBundle 00068458 8D8EDF170900 lea ecx, dword [ds:esi-0x68211+cfstring_TRIAL_PERIOD_EXPIRED] ; @"TRIAL PERIOD EXPIRED"
And again:
00067d35 80B87737090000 cmp byte [ds:eax-0x67b91+_gTrialPeriodExpiredFlag], 0x0 00067d3c 89D9 mov ecx, ebx 00067d3e 740F je 0x67D4F ; Basic Block Input Regs: <nothing> - Killed Regs: ebp 00067d40 66838526FFFFFF28 add word [ss:ebp-0x128+var_78], 0x28 00067d48 66D1A524FFFFFF shl word [ss:ebp-0x128+var_76], 0x1 ; Basic Block Input Regs: eax ecx edi - Killed Regs: eax ebx ebp esi 00067d4f 89C6 mov esi, eax ; XREF=0x67d3e
Finally, we even set it to 0x0
:
0003de3d 7408 je 0x3DE47 ; Basic Block Input Regs: eax - Killed Regs: esp 0003de3f 890424 mov dword [ss:esp], eax 0003de42 E87B8E0900 call imp___symbol_stub__CFRelease ; Basic Block Input Regs: ebp esi - Killed Regs: eax ecx esp edi 0003de47 C68787DB0B0000 mov byte [ds:edi-0x3d781+_gTrialPeriodExpiredFlag], 0x0 ; XREF=0x3de3d
by replacing the je
at 0x3de3d
with a jmp
.