📜 ⬆️ ⬇️

Study TurboLaunch 5.1.3 protection program

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"
image

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

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

Find:
image

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:
image

Ah ah ah. Integrity check Well, God bless her:
Overload the program in the debugger, then set the breakpoint to the function call MessageBoxA
bp MessageBoxA

In the window “Call Stack of main thread” we look where we came from:
image

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:
image

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:
image

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
  1. procedure GenClick ( Dummy : Pointer; Sender: PControl);
  2. var NameBuffer, SerNum: String;
  3. len, sn_tmp: integer ;
  4. begin
  5. if Nm.Text <> '' then begin
  6. NameBuffer: = Nm.Text;
  7. len: = Length (Nm.Text);
  8. ASM
  9. Pushhad
  10. mov ECX, NameBuffer
  11. mov EBX, len
  12. @ TurboLau_0053F42E:
  13. XOR EAX, EAX
  14. MOV AL, BYTE PTR DS: [ECX]
  15. SHL EAX, 8
  16. XOR EDX, EAX
  17. MOV EAX, 8
  18. @ TurboLau_0053F43C:
  19. TEST DH, 080h
  20. JE @ TurboLau_0053F44B
  21. ADD EDX, EDX
  22. XOR EDX, 01021h
  23. JMP @ TurboLau_0053F44D
  24. @ TurboLau_0053F44B:
  25. ADD EDX, EDX
  26. @ TurboLau_0053F44D:
  27. DEC EAX
  28. JNZ @ TurboLau_0053F43C
  29. INC ECX
  30. DEC EBX
  31. JNZ @ TurboLau_0053F42E
  32. MOV EAX, EDX
  33. AND EAX, 0FFFFh
  34. mov sn_tmp, eax
  35. Popad
  36. END ;
  37. Edit.text: = Int2Hex ((sn_tmp), 4);
  38. end
  39. else
  40. Edit.Text: = 'Enter Your Name' ;
  41. 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
  1. @ TurboLau_004034E8:
  2. Push ebx
  3. XOR EBX, EBX
  4. IMUL EDX, DWORD PTR DS: [EBX + 0542008h], 08088405h
  5. INC EDX
  6. MOV DWORD PTR DS: [EBX + 0542008h], EDX
  7. MUL EDX
  8. MOV EAX, EDX
  9. POP EBX
  10. 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
  1. @ TurboLau_005406E9:
  2. MOV EAX, 021h
  3. // -> CALL @ TurboLau_004034E8; <= Jump / Call Address Not Resolved
  4. // call content
  5. Push ebx
  6. XOR EBX, EBX
  7. IMUL EDX, sn_tmp, 08088405h
  8. INC EDX
  9. MOV sn_tmp, edx
  10. MUL EDX
  11. MOV EAX, EDX
  12. POP EBX
  13. MOV EBX, EAX
  14. INC EBX
  15. MOV AL, BYTE PTR SS: [EBP-0Dh]
  16. XOR AL, 0FFh
  17. AND EAX, 0FFh
  18. ADD EBX, EAX
  19. DEC ESI
  20. 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
  1. @ TurboLau_005407C4:
  2. MOV EAX, DWORD PTR SS: [EBP-4]; Blacklist checkout cycle
  3. MOV EDX, DWORD PTR DS: [ESI]
  4. CALL @ TurboLau_00405030
  5. JNZ @ TurboLau_005407D4
  6. XOR EBX, EBX
  7. JMP @ TurboLau_005407DA
  8. @ TurboLau_005407D4:
  9. ADD ESI, 4
  10. DEC EDI
  11. 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
  1. // ...
  2. var
  3. 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 ' );
  4. // ...
  5. procedure generate;
  6. Var
  7. NameBuffer, SerNum: String ;
  8. EDX, EAX, len, i , a , b , tmp1: integer ;
  9. Textname: PChar ;
  10. begin
  11. len: = GetWindowTextLengthA ( TxtNameHwnd );
  12. if len> 1 then
  13. begin
  14. {Get text from name input }
  15. GetMem ( Textname, len + 1 );
  16. GetWindowTextA ( TxtNameHwnd, PAnsiChar (Textname ), len + 1 );
  17. {Generate Serial}
  18. KeyStr: = 'GF2DSA38HJKL7M4NZXCV5BY9UPT6R1EWQ40I1CP' ;
  19. NameBuffer: = String ( Textname );
  20. SerNum: = " ;
  21. Randomize;
  22. EDX: = 0 ;
  23. for i : = 1 to len do
  24. begin
  25. EDX: = EDX xor ( ord (NameBuffer [i] ) shl 8 );
  26. EAX: = 8 ;
  27. while EAX <> 0 do
  28. begin;
  29. if ( EDX shr 8 and $ 0FF )> = $ 80
  30. then
  31. EDX: = EDX shl 1 xor $ 1021
  32. else
  33. EDX: = EDX shl 1 ;
  34. dec ( eax );
  35. end;
  36. end;
  37. RandSeed: = EDX and $ 0FFFF;
  38. i : = 1 ;
  39. while i <> $ 13 do
  40. begin
  41. a : = 0 ;
  42. b : = $ 13 - i ;
  43. if b > 0 then
  44. begin
  45. while b > 0 do
  46. begin
  47. a : = Random ( 33 );
  48. inc ( a );
  49. if i > len then
  50. tmp1: = ord ( NameBuffer [i mod len] )
  51. else
  52. tmp1: = ord ( NameBuffer [i] );
  53. tmp1: = tmp1 xor $ 0FF and $ 0FF;
  54. a : = a + tmp1;
  55. dec ( b );
  56. end;
  57. end;
  58. while a > $ 21 do
  59. begin
  60. a : = a - $ 21 ;
  61. end;
  62. SerNum: = SerNum + KeyStr [ a ];
  63. inc ( i );
  64. end;
  65. Insert ( '-', SerNum, 7 );
  66. Insert ( '-', SerNum, 14 );
  67. For i : = 0 to 43 do
  68. begin
  69. if NameBuffer = Black List [ i ] then
  70. begin
  71. SerNum: = 'BLACKLISTED NAME' ;
  72. Break;
  73. end else;
  74. end;
  75. { Display The Results}
  76. SetWindowTextA ( TxtSerialHwnd, PChar (SerNum ));
  77. FreeMem ( Textname, len + 1 );
  78. end
  79. Else
  80. { Display Error}
  81. SetWindowText ( TxtSerialHwnd, 'Not Enough Characters ..' );
  82. end;

As a result, I got this keygen:
image

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.

Source: https://habr.com/ru/post/132843/


All Articles