Borland's C++ Builder v4 
Emulating inputs on a timebomb protection

Written by Tsehp, January 17th 2000
Inspired by Macilaci


After I received macilaci's essay, I wanted to try my luck on Borland C++, well no surprise, the protection is all the same. I actually work on Windows 2000 and Wisdec doesn't work on this system, so we have to use our brains to find another path. More and more tools like Wisdec and ProcDump are discontinued at this time. Hope we still see such great and useful tools in the future.

Tools required

My system : Windows 2000 Server, this should work on any version of windows.

HEX Editor, SoftICE v4.01, IDA v4.01, IsDCC (InstallShield Decompiler, still working on win2k).

Target's URL/FTP


1). InstallShield crack.

First you've got to install this and provide a password that Borland can give you if you provide your e-mail address. We are not here to talk about anonymisers so lets crack this, located as always inside the script setup.ins. We have already learned on this site how to crack these scripts with a HEX editor (see InstallShield cracking section), I can't use Wisdec on my system, it's not working and this tool is stopped since 98, so forget it.

Setup.ins contains tokens, interpreted as instructions by the running proc _ins5116.exe, with tests, jumps etc., all of this is explained in the related essays. In the decompiled script, you find this :


lNumber1 = 0;
lString0 = "Taj";
lString2 = SUPPORTDIR ^ "cleanup.dll";
lNumber2 = LAST_RESULT;
lNumber3 = LAST_RESULT;
StrLoadString("", "TRIAL_TEXT", lString3); * Loads the trial password entry nag.
AskText(lString3, "", lString0); * password entered in string0.
lNumber4 = LAST_RESULT;
StrCompare(lString1, lString0); * password check.
lNumber5 = LAST_RESULT != 0; * this test fails if last_result is not 0.
if (lNumber5 = 0) then * If test OK, this jumps to GOOD BOY, lets patch this one (1).
goto label945;
lNumber5 = lNumber1 >= 3; * More than three attempts ?.
if (lNumber5 = 0) then goto label944; * jump to bad guy exit.

label943: * bad guy messageboxa if the test fails.

StrLoadString("", "WRONG_PSWD", lString3);
MessageBox(lString3, -65535);
lNumber1 = lNumber1 + 1;
StrLoadString("", "TRIAL_TEXT", lString3);
AskText(lString3, "", lString0);
lNumber4 = LAST_RESULT;
goto label943;

(1) OK, we've got to invert the test (or enter the good password!), it would be easy with Wisdec but we will not use it. Search for TRIAL_TEXT with your HEX Editor inside setup.ins, you find this :-


00012040 0054 5249 414C 5F54 4558 5452 98FF 1E00 .TRIAL_TEXTR....

00012050 6298 FF61 0000 529B FF21 0032 97FF 4200 b..a..R..!.2..B.

00012060 0000 0007 0029 0123 0062 9AFF 629B FF28 .....).#.b..b..(

00012070 0132 96FF 4200 0041 0600 0000 4100 0000 .2..B..A....A...

00012080 0022 0070 B103 9542 96FF 4100 0000 0028 .".p...B..A....(

00012090 0132 96FF 429A FF41 0400 0000 4103 0000 .2..B..A....A...

000120A0 0022 0070 B003 9542 96FF 4100 0000 0059 .".p...B..A....Y

000120B0 0100 0008 003A 0041 0000 0000 1201 6100 .....:.A......a.

000120C0 0061 0A00 5752 4F4E 475F 5053 5744 5298 .a..WRONG_PSWDR.

Well, you see the ASCII occurences, if you learned well from the InstallShield essays, you know that a conditionnal jump is like this 03 95 label0 ff label1 where 95 is jump if result = 0 (96 if different than 0). Labels are two bytes like 42 96, they're at the beginning of primary instructions inside Setup.ins.

We invert the jump at offset 12087, 03 95 42 96 --> 03 96 42 96

As there is a CRC check on Setup.ins, (all the bytes are added to form it), we have to reduce a byte to make the CRC work. We better do that inside some ASCII text, found one at offset 58C6 :-

000058C0 6119 0055 6E61 626C 6520 746F 2063 7265 a..Unable to cre

lets reduce the b from "unable" to a

000058C0 6119 0055 6E61 616C 6520 746F 2063 7265 a..Unaale to cre

Now the CRC check will be OK. Launch it, enter nothing at the password nag and the trial installs itself !.

2). Time trial protection (from macilaci's essay, these parts are the same, except for the addresses).

After I saw what was going on I decided to emulate inputs. The delete approach is effective but not elegant. Stop, I found timefix.exe. Sweet - references to HKLM\SOFTWARE\Ntpad\HELPMENU\tin and some more... Try now running the trial with BoundsChecker. GetLocalTime!! 0x00D217D =0x004D217D. Let's see :-

_0000010:004D6900 SUB ESP, 0CCh
_0000010:004D6906 LEA EAX, [ESP+0CCh+var_BC]
_0000010:004D690A PUSH ESI
_0000010:004D690B PUSH EAX
_0000010:004D690C CALL ds:GetLocalTime
_0000010:004D6912 LEA ECX, [ESP+0D0h+var_CC]
_0000010:004D6916 PUSH ECX
_0000010:004D6917 CALL ds:GetSystemTime
_0000010:004D691D MOV CX, ds:word_0_496D02
_0000010:004D6924 CMP [ESP+0D0h+var_C2], CX
_0000010:004D6929 JNZ short loc_0_4D6967
_0000010:004D692B MOV AX, ds:word_0_496D00
_0000010:004D6931 CMP [ESP+0D0h+var_C4], AX
_0000010:004D6936 JNZ short loc_0_4D6967
_0000010:004D6938 MOV AX, ds:word_0_496CFE
_0000010:004D693E CMP [ESP+0D0h+var_C6], AX
_0000010:004D6943 JNZ short loc_0_4D6967
_0000010:004D6945 MOV AX, ds:word_0_496CFA
_0000010:004D694B CMP [esp+0D0h+var_CA], AX
_0000010:004D6950 JNZ short loc_0_4D6967
_0000010:004D6952 MOV AX, ds:word_0_496CF8
_0000010:004D6958 CMP [ESP+0D0h+var_CC], AX
_0000010:004D695D JNZ short loc_0_4D6967
_0000010:004D695F MOV EDX, ds:dword_0_496CF0
_0000010:004D6965 JMP short loc_0_4D69AD


_0000010:004D69AD MOV EAX, [ESP+0D0h+var_B0]
_0000010:004D69B1 PUSH EDX
_0000010:004D69B2 AND EAX, 0FFFFh
_0000010:004D69B7 PUSH EAX
_0000010:004D69B8 XOR EAX, EAX
_0000010:004D69BA MOV AX, [ESP+0D8h+var_B2]
_0000010:004D69BF PUSH EAX
_0000010:004D69C0 MOV EAX, [ESP+28h]
_0000010:004D69C4 AND EAX, 0FFFFh
_0000010:004D69C9 PUSH EAX
_0000010:004D69CA XOR EAX, EAX
_0000010:004D69CC MOV AX, [ESP+0E0h+var_B6]
_0000010:004D69D1 PUSH EAX
_0000010:004D69D2 XOR EAX, EAX
_0000010:004D69D4 MOV AX, WORD PTR [ESP+0E4h+var_BC+2]
_0000010:004D69D9 PUSH EAX
_0000010:004D69DA MOV EAX, [ESP+0E8h+var_BC]
_0000010:004D69DE AND EAX, 0FFFFh
_0000010:004D69E3 PUSH EAX
_0000010:004D69E4 CALL sub_0_4DBCE0 ; this returns some strange value in EAX and EDX.
_0000010:004D69E9 MOV ECX, [ESP+0ECh+arg_0]
_0000010:004D69F0 ADD ESP, 1Ch
_0000010:004D69F3 TEST ECX, ECX
_0000010:004D69F5 JZ short loc_0_4D69F9
_0000010:004D69F7 MOV [ECX], EAX

_0000010:004D69F9 loc_0_4D69F9: ; CODE XREF: sub_0_4D6900+F5j

_0000010:004D69F9 POP ESI
_0000010:004D69FA ADD ESP, 0CCh
_0000010:004D6A00 RETN

Another encryption? Probably yes. Let's look at the above routine sub_0_4D69F9 :-

_0000010:004DBD63 DEC EBX
_0000010:004DBD64 LEA ESI, [EDX+EDX*2]
_0000010:004DBD67 MOV [ESP+34h+var_14], EBX
_0000010:004DBD6B MOV [ESP+34h+var_1C], ECX
_0000010:004DBD6F LEA EDX, [ESI+ESI*4]
_0000010:004DBD72 ADD EDX, [ESP+34h+arg_14] ; the final EDX value
_0000010:004DBD76 LEA ESI, [EDX+EAX+7C558180h] ;the final EAX=ESI value
_0000010:004DBD7D MOV EAX, [ESP+34h+arg_18]
_0000010:004DBD81 CMP EAX, 1
_0000010:004DBD84 JZ short loc_0_4DBDA5
_0000010:004DBD86 CMP EAX, 0FFFFFFFFh
_0000010:004DBD89 JNZ short loc_0_4DBDAB
_0000010:004DBD8B CMP ds:dword_0_4905E4, 0
_0000010:004DBD92 JZ short loc_0_4DBDAB
_0000010:004DBD94 LEA EAX, [ESP+34h+var_24]
_0000010:004DBD98 PUSH EAX
_0000010:004DBD99 CALL sub_0_4DE680
_0000010:004DBD9E ADD ESP, 4
_0000010:004DBDA1 TEST EAX, EAX
_0000010:004DBDA3 JZ short loc_0_4DBDAB

Write down the EDX and EAX and replace the location 004D747F :-

_0000010:004DBD76 MOV ESI, 38820632h
_0000010:004DBD7B MOV EDX, 0BC2C84B2h
_0000010:004DBD80 NOP

Note from tsehp :- Be sure that further installs at this point will be the same day, otherwise the later date tamper subroutine will detect that and the expire will trigger. With a search and replace utility, look for the occurences of 4DBD76 :- 8D B4 02 80 81 55 7C 8B 44 24 50. You will find the protection in all those files that you must patch.

Search String: \0x8d\0xb4\0x02\0x80\0x81\0x55\0x7c\0x8b\0x44\0x24\0x50

Path: C:\Program Files\Borland\CBuilder4, File Mask: *.*

Search Subdirectories, Search ZIP Files

Processing file : C:\Program Files\Borland\CBuilder4\Bin\bcb.exe

Offset 0xcc776 - ´ U| D$P

Found 1 occurrences.

Processing file : C:\Program Files\Borland\CBuilder4\Bin\bcbcxp40.bpl

Offset 0x108976 - ´ U| D$P

Found 1 occurrences.

Processing file : C:\Program Files\Borland\CBuilder4\Bin\bcbidl40.bpl

Offset 0xb9176 - ´ U| D$P

Found 1 occurrences.

Processing file : C:\Program Files\Borland\CBuilder4\Bin\dbexplor.exe

Offset 0x55776 - ´ U| D$P

Found 1 occurrences.

Processing file : C:\Program Files\Borland\CBuilder4\Bin\dcldss40.bpl

Offset 0x64576 - ´ U| D$P

Found 1 occurrences.

Processing file : C:\Program Files\Borland\CBuilder4\Bin\dclmid40.bpl

Offset 0x61576 - ´ U| D$P

Found 1 occurrences.

Processing file : C:\Program Files\Borland\CBuilder4\Bin\dclnet40.bpl

Offset 0x66d76 - ´ U| D$P

Found 1 occurrences.

Processing file : C:\Program Files\Borland\CBuilder4\Bin\ilink32.exe

Offset 0x93776 - ´ U| D$P

Found 1 occurrences.

Searched 2017 file(s), found 8 occurrences in 8 file(s)

Good idea to place the protection it in all those places, but it would have been better to change it a little no ? :-).

3). Run without activator. Back to our BoundsChecker record. Now look at this when the activator is running.

API reference: WaitForSingleObject and before CreateProcessA in caitf32.dll module :-

00401307 PUSH 0
00401309 CALL j_CreateProcessA ; here start activator
0040130E TEST EAX, EAX
00401310 JZ loc_0_4013C0
00401316 TEST EBX, EBX
00401318 JZ loc_0_4013A9
00401320 MOV ECX, [EBP-14h]
00401323 PUSH ECX
00401324 CALL j_WaitForInputIdle
00401329 MOV [EBP-4], EAX
0040132C CMP DWORD PTR [EBP-4], 0
00401330 JNZ short loc_0_4013A0
00401332 PUSH 64h
00401334 MOV EAX, [EBP-14h]
00401337 PUSH EAX
00401338 CALL j_WaitForSingleObject
0040138B PUSH EDX
0040138C MOV ECX, [EBP-14h]
0040138F PUSH ECX
00401390 CALL j_GetExitCodeProcess ; and get the result
00401395 JMP short loc_0_4013AE

This subroutine is called from this sub :-

004018CC PUSH 1
004018CE MOV EDX, [EBP+arg_0]
004018D1 PUSH EDX
004018D2 CALL sub_0_401288 ; call the activator routine
004018D7 ADD ESP, 14h

Each time we press the TRY button EAX is returned 0x00002716. Other registers don't affect the program. Simply replace :-

004018D2  MOV EAX, 0x00002716 ; now we can run it without activator

And the patch is done. Our trial is running any time we want. No time stamp protection on this one.