Simple protection or break TurboLaunch 5.1.3 in three ways (patch, sniffer, keygen).
Purpose : TurboLaunch 5.1.3
Tools : OllyDbg 1.10, Dup2 (for creating a patch), CodeRipper plugin for OllyDbg, Delphi 7 (for writing keygen)
It was a boring evening, I was looking for a program that I would like to “pick it up”. First, by editing one byte, I removed the trial from AWBackuper 4.0, it seemed very uninteresting, then I remembered the program, which had been on my computer for a long time (year 4) - this is Savard Software's TurboLaunch. Keygens exist to it (probably about 10), but, in no one I saw one small nuance (although by and large it doesn’t practically play a significant role), but about it a bit later.
')
And so, let's begin ...
Patching
The first way I chose to patch, because for beginners it is the easiest.
We ship the victim in OllyDbg (further just olya, olly). Run and see the nag-window, click "Enter My Registration Code"

Enter your name (DimitarSerg) and “honestly purchased” code 1234567890
Very strange, but we see the following message:

Look for the text of the message in the text lines. PKM-> Search for -> All referenced text strings.
Find:

Just above we see
Jump from 00529CD3 , that is, we came from there.
Ok, let's see what we have there:
"Classic":
CALL TurboLau.0053AEB0
TEST AL,AL
JE @TurboLau_00529D99
So, there is a check and in accordance with the result of the jump check.
Just below are the lines:
00529D1A |. BA F49D5200 MOV EDX,TurboLau.00529DF4 ; ASCII "REGISTERED TO: "
And this:
00529D3B |. 68 0C9E5200 PUSH TurboLau.00529E0C ; ASCII "Thank you for registering! Be sure to check out our web site for updated versions of TurboLaunch and other programs written by "
Well then, let's look at 00529CCC |. E8 DF110100 CALL TurboLau.0053AEB0
there is another call, and inside something similar to the registration procedure. Well, why do we need a verification procedure, if you can do without it ?!
And so in the beginning of the registration procedure at 540628 we are doing a “classic” patch.
xor eax,eax // EAX
inc eax // EAX = EAX +1
Retn // return
Save the changes.
PCM-> Copy to executable -> All modifications -> Save file .
Save it under a new name, for example, TurboLaunch1.exe
We want to rejoice, but we get back this:

Ah ah ah. Integrity check Well, God bless her:
Overload the program in the debugger, then set the breakpoint to the function call
MessageBoxAbp MessageBoxA
In the window “Call Stack of main thread” we look where we came from:

Make PCM-> Show Call and find ourselves at
00450CA4 |. E8 8F75FBFF CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA
Scroll above and see:
00450C7B |> \84DB TEST BL,BL
00450C7D |. 74 60 JE SHORT TurboLau.00450CDF
Again check.
Change the conditional transition to unconditional (JE -> JMP), save the changes and start. Hurray, the program starts.
Windows with a request to buy are no longer observable, in the About window we see “Registered to”.
In general, for someone registered, that's good.
Making a patch in
Dup2 or
uPPP is not a problem.
Search serial number.
As you noted above, the message on the correct / incorrect serial number is formed depending on the result.
00529CD1 |. 84C0 TEST AL,AL
And the line above shows:
00529CCC |. E8 DF110100 CALL TurboLau.0053AEB0
It is logical that the whole generation procedure takes place here ...
We look:
0053AEB0 /$ 8B90 68010000 MOV EDX,DWORD PTR DS:[EAX+168]
0053AEB6 |. 8B80 64010000 MOV EAX,DWORD PTR DS:[EAX+164]
0053AEBC |. E8 67570000 CALL TurboLau.00540628
0053AEC1 \. C3 RETN
We set a breakpoint at 0053AEB0 and when entering the name / registration number we stop here and see that the input data is being read, which means that the most interesting is in CALL TurboLau.00540628.
We trace on F7, we see reading, some transformations, other operations, etc. etc., as a result, if you give this procedure a couple of minutes of attention and patience, then during the execution of the instruction
00540758 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
We'll see:
Stack SS: [0012F1AC] = 00C66E8C, (ASCII "D1F74F-5L3GRT-3WDULJ")
Hmm, is the correct serial number really ?! We try: the name DimitarSerg and serial number D1F74F-5L3GRT-3WDULJ, the program congratulates us with the purchase.
Since the serial number is visible in clear text, the sniffer series is written with a bang, you just need to look at the EAX register at the address that goes right after 00540758, that is, 0054075B.
I will not describe the method of writing a series of sniffer, but I would say that the AT4RE team has a tool like Serial Sniffer Creator, you can use it, it should look something like this:

But I do not recommend it, since it does not sniff serials in Unicode and it has frequent glitches with the interface.
There are several examples (templates) for creating sniffers in public, so the assembler or Delphi is in the hands and “finished”.
Keygen
Well, the most interesting is, of course, the spelling of keygen.
As I described above, the whole generation procedure starts at 00540628
Enter any registration data, trace. Pay attention to this place:

Take a look at this call. We see some operations performed on the name, calculations. I will say in advance that the first part of the calculations takes place here, the result of which is used a little bit later. I am not a “great spec” on assembler, so I use the CodeRipper plugin.
I will show two ways of keygen: inline assembly code on Delphi and fully translated Pascal code:
Copy Source | Copy HTML
- procedure GenClick ( Dummy : Pointer; Sender: PControl);
- var NameBuffer, SerNum: String;
- len, sn_tmp: integer ;
- begin
- if Nm.Text <> '' then begin
- NameBuffer: = Nm.Text;
- len: = Length (Nm.Text);
- ASM
- Pushhad
- mov ECX, NameBuffer
- mov EBX, len
- @ TurboLau_0053F42E:
- XOR EAX, EAX
- MOV AL, BYTE PTR DS: [ECX]
- SHL EAX, 8
- XOR EDX, EAX
- MOV EAX, 8
- @ TurboLau_0053F43C:
- TEST DH, 080h
- JE @ TurboLau_0053F44B
- ADD EDX, EDX
- XOR EDX, 01021h
- JMP @ TurboLau_0053F44D
- @ TurboLau_0053F44B:
- ADD EDX, EDX
- @ TurboLau_0053F44D:
- DEC EAX
- JNZ @ TurboLau_0053F43C
- INC ECX
- DEC EBX
- JNZ @ TurboLau_0053F42E
- MOV EAX, EDX
- AND EAX, 0FFFFh
- mov sn_tmp, eax
- Popad
- END ;
- Edit.text: = Int2Hex ((sn_tmp), 4);
- end
- else
- Edit.Text: = 'Enter Your Name' ;
- End ;
Why did I give the procedure separately? It was easier for me, I immediately see the correctness of the ripped code. For example, for the name DimitarSerg sn_tmp = E330
Well, and then again the trick: we use the CodeRipper plug-in, starting from the address 5406BC to 540752 (we see that this is a big generation cycle). What I want to draw attention to is some calls. CodeRipper writes “; <= Jump / Call Address Not Resolved”, most of them are not needed and can be removed, but
005406EE |. E8 F52DECFF || CALL TurboLau.004034E8
In no case should this call be deleted, as inside:
Copy Source | Copy HTML
- @ TurboLau_004034E8:
- Push ebx
- XOR EBX, EBX
- IMUL EDX, DWORD PTR DS: [EBX + 0542008h], 08088405h
- INC EDX
- MOV DWORD PTR DS: [EBX + 0542008h], EDX
- MUL EDX
- MOV EAX, EDX
- POP EBX
- RETN
This, as I was told (at that time I did not know) that this is a standard Random, and DWORD PTR DS: [EBX + 0542008h] (I also have sn_tmp) is RandomSeed.
And in the ripped code, it is no longer to call it as a separate procedure, but simply to copy to the place where it was called, here is a fragment:
Copy Source | Copy HTML
- @ TurboLau_005406E9:
- MOV EAX, 021h
- // -> CALL @ TurboLau_004034E8; <= Jump / Call Address Not Resolved
- // call content
- Push ebx
- XOR EBX, EBX
- IMUL EDX, sn_tmp, 08088405h
- INC EDX
- MOV sn_tmp, edx
- MUL EDX
- MOV EAX, EDX
- POP EBX
- MOV EBX, EAX
- INC EBX
- MOV AL, BYTE PTR SS: [EBP-0Dh]
- XOR AL, 0FFh
- AND EAX, 0FFh
- ADD EBX, EAX
- DEC ESI
- JNZ @ TurboLau_005406E9
I also want to draw attention to the line
00540715 |. BA 1C085400 | MOV EDX, TurboLau.0054081C; ASCII "GF2DSA38HJKL7M4NZXCV5BY9UPT6R1EWQ40I1CP7Z7GOEPQLZ"
It can be called “key”, since the registration code directly depends on it. The edx register is written to a string at 0054081C.
The complete source code of keygen on Delphi with Asmov's inserts and attached on pure Pascal, nothing complicated there.
Well, then, from what I started the story: after all these generations, we DO NOT STOP, but we stamp further and see some names / nicks, etc.
Naturally, this is a primitive blacklist:
Copy Source | Copy HTML
- @ TurboLau_005407C4:
- MOV EAX, DWORD PTR SS: [EBP-4]; Blacklist checkout cycle
- MOV EDX, DWORD PTR DS: [ESI]
- CALL @ TurboLau_00405030
- JNZ @ TurboLau_005407D4
- XOR EBX, EBX
- JMP @ TurboLau_005407DA
- @ TurboLau_005407D4:
- ADD ESI, 4
- DEC EDI
- JNZ @ TurboLau_005407C4
Such interesting nicknames like Nitrogen / TSRh TeaM, REVENGE Crew, FiGHTiNG FOR FUN, TEAM VIRILITY ... etc. skip there. I think you got the hint.
Total 44 names. Not a problem to copy manually, to hammer in a string array of type
BlackList: array [0..43] of string = (...), and when generating, see if the name you want is in the blacklist. (I also attach the file BlackList.txt).
For “complete happiness,” we translate a code with asm-inserts in Pascal:
Copy Source | Copy HTML
- // ...
- var
- Black List: array [ 0 .. 43 ] of string = ( 'zircon / pc97', 'freeware', 'registered user', <br/> 'NuZ' 'c97', 'Registered', 'kOUGER! [CB4] ',' Cosmo Cramer 1997 ',' Cosmo Cramer MJ13 ', <br/> ' MJ13 Forever ',' cH / Phrozen Crew ',' Everybody ',' iCEMAN [uCF] ',' pank ',' Henry Pan ', <br/> 'iTR [CORE]', 'mpbaer', 'CORE / JES', 'Chen Borchang', 'n03l', 'ODIN 97', 'lgb / cORE''97', <br/> 'MCC ',' blastsoft ',' CORE / DrRhui ',' Vizion / CORE ',' TEAM ViRiLiTY ',' Nambulu ',' NuZPc97 ', <br/> ' Weazel ',' Phrozen Crew ',' TEAM VIRILITY ',' x3u ',' Reg Name ',' FiGHTiNG FOR FUN ',' RaSCaL [TMG] ', <br/> ' Nitros ^ 21 ',' TEAM TSRH ',' ttdown.com ',' Nitrogen / TSRh TeaM ',' Free Program ',' REVENGE Crew ', <br/> ' Vladimir Kasho ',' Alexej Melnikov ',' Seth W. Hinshaw ' );
- // ...
- procedure generate;
- Var
- NameBuffer, SerNum: String ;
- EDX, EAX, len, i , a , b , tmp1: integer ;
- Textname: PChar ;
- begin
- len: = GetWindowTextLengthA ( TxtNameHwnd );
- if len> 1 then
- begin
- {Get text from name input }
- GetMem ( Textname, len + 1 );
- GetWindowTextA ( TxtNameHwnd, PAnsiChar (Textname ), len + 1 );
- {Generate Serial}
- KeyStr: = 'GF2DSA38HJKL7M4NZXCV5BY9UPT6R1EWQ40I1CP' ;
- NameBuffer: = String ( Textname );
- SerNum: = " ;
- Randomize;
- EDX: = 0 ;
- for i : = 1 to len do
- begin
- EDX: = EDX xor ( ord (NameBuffer [i] ) shl 8 );
- EAX: = 8 ;
- while EAX <> 0 do
- begin;
- if ( EDX shr 8 and $ 0FF )> = $ 80
- then
- EDX: = EDX shl 1 xor $ 1021
- else
- EDX: = EDX shl 1 ;
- dec ( eax );
- end;
- end;
- RandSeed: = EDX and $ 0FFFF;
- i : = 1 ;
- while i <> $ 13 do
- begin
- a : = 0 ;
- b : = $ 13 - i ;
- if b > 0 then
- begin
- while b > 0 do
- begin
- a : = Random ( 33 );
- inc ( a );
- if i > len then
- tmp1: = ord ( NameBuffer [i mod len] )
- else
- tmp1: = ord ( NameBuffer [i] );
- tmp1: = tmp1 xor $ 0FF and $ 0FF;
- a : = a + tmp1;
- dec ( b );
- end;
- end;
- while a > $ 21 do
- begin
- a : = a - $ 21 ;
- end;
- SerNum: = SerNum + KeyStr [ a ];
- inc ( i );
- end;
- Insert ( '-', SerNum, 7 );
- Insert ( '-', SerNum, 14 );
- For i : = 0 to 43 do
- begin
- if NameBuffer = Black List [ i ] then
- begin
- SerNum: = 'BLACKLISTED NAME' ;
- Break;
- end else;
- end;
- { Display The Results}
- SetWindowTextA ( TxtSerialHwnd, PChar (SerNum ));
- FreeMem ( Textname, len + 1 );
- end
- Else
- { Display Error}
- SetWindowText ( TxtSerialHwnd, 'Not Enough Characters ..' );
- end;
As a result, I got this keygen:

Size 36.5 Kb (unpacked). Such a small keygen size is achieved by using KOL (Key Objects Library) - a library for Delphi. You can also do it using WinApi, paste your logo, add xm / v2m track (so I’m for public keygens did), but my advice is do not use VCL if you write keygens on Delphi ... 400kb and more - this is not the size for keygens!
Who likes more clean assembler - Masm (Fasm, Tasm) in hand.
At this my story comes to an end, I hope someone will learn something from this article.