SubEthaEdit is a collaborative coding utility for the Mac, allowing people to code in real-time by inviting them from the iChat contact list or via bonjour.
The download is a 30 day, no-limitation trial that can be patched efficiently to defeat all the protections. The application is definitely not coded with copy-protection in mind since most of the protections can be defeated by just circumventing control-flow over the isValidSerial
procedures.
The isValidSerial
procedure is triggered at various, yet limited points around the code. It is sufficient to attack just the first few of them to defeat numerous trial and registration checks.
objc_msg_isValidSerial: 001064c0 dd 0x000e4bde ; XREF=0x507f4, 0x5e3a7, 0x5e91a, 0x5ed2a, 0x5edf4, 0x5f06f
The first candidate point referred by 0x507f4
making an isValidSerial
call has to do with the trial. It is a simple check, if the serial is valid, then there are no additional trial operations performed (measuring the elapsed time, etc…) and the program loads without prompting for registration.
000507f4 A1C0641000 mov eax, dword [ds:objc_msg_isValidSerial] ; @selector(isValidSerial) 000507f9 893C24 mov dword [ss:esp], edi 000507fc 89442404 mov dword [ss:esp+0x4], eax 00050800 E83CC20C00 call imp___jump_table__objc_msgSend 00050805 84C0 test al, al 00050807 90 nop 00050808 90 nop 00050809 8B4508 mov eax, dword [ss:ebp+0x8] 0005080c 8B5038 mov edx, dword [ds:eax+0x38] 0005080f A1885E1000 mov eax, dword [ds:objc_msg_setHidden_] ; @selector(setHidden:) 00050814 C744240800000000 mov dword [ss:esp+0x8], 0x0 0005081c 891424 mov dword [ss:esp], edx 0005081f 89442404 mov dword [ss:esp+0x4], eax 00050823 E819C20C00 call imp___jump_table__objc_msgSend 00050828 8B4508 mov eax, dword [ss:ebp+0x8] 0005082b 8B503C mov edx, dword [ds:eax+0x3c] 0005082e A1C4641000 mov eax, dword [ds:objc_msg_setObjectValue_] ; @selector(setObjectValue:) 00050833 89742408 mov dword [ss:esp+0x8], esi 00050837 891424 mov dword [ss:esp], edx 0005083a 89442404 mov dword [ss:esp+0x4], eax 0005083e E8FEC10C00 call imp___jump_table__objc_msgSend 00050843 8B4508 mov eax, dword [ss:ebp+0x8] 00050846 8B5040 mov edx, dword [ds:eax+0x40] 00050849 895C2408 mov dword [ss:esp+0x8], ebx 0005084d E91B010000 jmp 0x5096d 00050852 8B4508 mov eax, dword [ss:ebp+0x8] ; XREF=0x507f2, 0x50807 00050855 8B5038 mov edx, dword [ds:eax+0x38] 00050858 A1885E1000 mov eax, dword [ds:objc_msg_setHidden_] ; @selector(setHidden:) 0005085d C744240801000000 mov dword [ss:esp+0x8], 0x1 00050865 891424 mov dword [ss:esp], edx 00050868 89442404 mov dword [ss:esp+0x4], eax 0005086c E8D0C10C00 call imp___jump_table__objc_msgSend 00050871 A1C8641000 mov eax, dword [ds:objc_msg_daysLeft] ; @selector(daysLeft)
This is performed by nop
ing the jump around 0x00050807
.
The second candidate point, referenced by 0x5e3a7
also makes an isValidSerial
call and is also related to trial procedures.
005e3a7 A1C0641000 mov eax, dword [ds:objc_msg_isValidSerial] ; @selector(isValidSerial) 0005e3ac 891C24 mov dword [ss:esp], ebx 0005e3af 89442404 mov dword [ss:esp+0x4], eax 0005e3b3 E889E60B00 call imp___jump_table__objc_msgSend 0005e3b8 31D2 xor edx, edx 0005e3ba 84C0 test al, al 0005e3bc E98F030000 jmp 0x5e750 0005e3c1 90 nop 0005e3c2 A14C501000 mov eax, dword [ds:objc_msg_alloc] ; @selector(alloc) XREF=0x5e3a5 0005e3c7 89442404 mov dword [ss:esp+0x4], eax 0005e3cb A1B47F1000 mov eax, dword [ds:cls_NSDate] 0005e3d0 890424 mov dword [ss:esp], eax 0005e3d3 E869E60B00 call imp___jump_table__objc_msgSend 0005e3d8 8B1578681000 mov edx, dword [ds:objc_msg_initWithTimeIntervalSinceNow_] ; @selector(initWithTimeIntervalSinceNow:) ... 0005e750 83C43C add esp, 0x3c ; XREF=0x5e3bc 0005e753 89D0 mov eax, edx 0005e755 5B pop ebx 0005e756 5E pop esi 0005e757 5F pop edi 0005e758 C9 leave 0005e759 C3 ret
In order to jump over the time interval checks, we jump at 0x0005e3bc
to 0x5e750
as if the serial is valid.
The popup appears (conveniently) in the applicationDidFinishLaunching
method and is easily circumvented by jumping at 0x0005231a
to 0x5275a
which effectively jumps over all the popups and into the start-up part of the code.
meth_AppController_applicationDidFinishLaunching_: 000522f6 55 push ebp 000522f7 89E5 mov ebp, esp 000522f9 57 push edi 000522fa 56 push esi 000522fb 53 push ebx 000522fc 83EC3C sub esp, 0x3c 000522ff A164651000 mov eax, dword [ds:objc_msg_shouldRun] ; @selector(shouldRun) 00052304 8B7D08 mov edi, dword [ss:ebp+0x8] 00052307 89442404 mov dword [ss:esp+0x4], eax 0005230b A184811000 mov eax, dword [ds:cls_LicenseController] 00052310 890424 mov dword [ss:esp], eax 00052313 E829A70C00 call imp___jump_table__objc_msgSend 00052318 84C0 test al, al 0005231a E93B040000 jmp 0x5275a ... 0005275a A178501000 mov eax, dword [ds:objc_msg_class] ; @selector(class) XREF=0x5231a, 0x526ed, 0x5270b, 0x52713 0005275f 8B1DD07F1000 mov ebx, dword [ds:cls_TCMBEEPChannel] 00052765 89442404 mov dword [ss:esp+0x4], eax 00052769 A1A8811000 mov eax, dword [ds:cls_HandshakeProfile] 0005276e 890424 mov dword [ss:esp], eax 00052771 E8CBA20C00 call imp___jump_table__objc_msgSend 00052776 C744240CA4E00F00 mov dword [ss:esp+0xc], 0xfe0a4 ; @"http://www.codingmonkeys.de/BEEP/SubEthaEditHandshake"
The purchase and registration menu items are shown conditionally depending on whether the application is registered or not. In order to get rid of them we attack the validateMenuItem
procedure and nop
the jump at 0x0005546b
:
meth_AppController_validateMenuItem_: 0005534d 55 push ebp 0005534e 89E5 mov ebp, esp 00055350 83EC38 sub esp, 0x38 00055353 895DF4 mov dword [ss:ebp+0xfffffff4], ebx 00055356 8975F8 mov dword [ss:ebp+0xfffffff8], esi 00055359 8B7510 mov esi, dword [ss:ebp+0x10] 0005535c 897DFC mov dword [ss:ebp+0xfffffffc], edi 0005535f A13C661000 mov eax, dword [ds:objc_msg_action] ; @selector(action) 00055364 893424 mov dword [ss:esp], esi 00055367 89442404 mov dword [ss:esp+0x4], eax 0005536b E8D1760C00 call imp___jump_table__objc_msgSend ... 0005545e A148661000 mov eax, dword [ds:objc_msg_canUndo] ; @selector(canUndo) 00055463 EB7B jmp 0x554e0 00055465 3B1D2C661000 cmp ebx, dword [ds:objc_msg_redo_] ; @selector(redo:) XREF=0x553f2 0005546b 90 nop 0005546c 90 nop 0005546d 90 nop 0005546e 90 nop 0005546f 90 nop 00055470 90 nop ... 000554f5 3B1D54661000 cmp ebx, dword [ds:objc_msg_enterSerialNumber_] ; @selector(enterSerialNumber:) XREF=0x5546b 000554fb 7408 je 0x55505 000554fd 3B1D58661000 cmp ebx, dword [ds:objc_msg_purchaseSubEthaEdit_] ; @selector(purchaseSubEthaEdit:) 00055503 7512 jne 0x55517 00055505 A164651000 mov eax, dword [ds:objc_msg_shouldRun] ; @selector(shouldRun) XREF=0x554fb 0005550a 89450C mov dword [ss:ebp+0xc], eax 0005550d A184811000 mov eax, dword [ds:cls_LicenseController] 00055512 894508 mov dword [ss:ebp+0x8], eax 00055515 EBCF jmp 0x554e6
This effectively avoids the enterSerialNumber
and purchaseSubEthaEdit
messages and thereby disable the registration and purchase menu.
That is it for version 2.1.3.