L ZZZZZZ RRRRR SSSSS L Z R R S L aaa Z aaa R R u u S L a Z a RRRRR u u SSSSS XX L aaaa Z aaaa R R u u S XXXX L a a Z a a R R u u S XXXXXX LLLLLLL aaaaa ZZZZZZZ aaaaa R R uuuuu SSSSSS XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXX XXXX proudly presents his 30.Cracking Tutorial (13.10.1999) XX My solution of the 2nd Kraecker mAG Project I. Introduction I.1 The tools II. The essay II.1 The CrackMe II.2 Clonk Planet III. BTW I. Introduction I participated (again) at the Cracking project of the German Scene EZine Kraecker. The targets to crack were "projekt A" by cg! and tCH and the game Clonk Planet 4.50 - The better you crack them, the more points you achieve. I have to explain the solutions shortly, but as I don't like short solutions, I can write a tut about it. I.1 The tools For the CrackMe: SoftIce (I have v3.23) MASM (to compile the KeyGen) For Clonk Planet: SoftIce MASM (to compile the KeyGen) Intellectual support: The CD "Follow the Blind" from Blind Guardian II. The essay II.1 The CrackMe A small look at the exe with an hexeditor reveals that it is packed with Neolite (see the section that is called .neolit ?). OK, the first rule was "No patching" anyway. So start the CrackMe and enter any name/serial combination. The CrackMe terminates and we have to restart it again. This time we set a breakpoint on hmemcpy (bpx hmemcpy) before push the "oK!" button. When SICE breaks, we push F12 until we reach 32bit code. You recognize 32bit code when the memory address has the following form: XXXX:XXXXXXXX When we reached it, we press F10 until you reach code where there's no "ret" in the next 5 lines. Now we have reached the part where it gets interesting. When we start tracing through the code (F10) we soon find a loop where a value gets calculated from our name. This is a fake calculation. Trace on until you reach this. -- THIS LOOP ADDS EVERY CHAR OF THE NAME AND ADDS 0F FOR EVERY CHAR :00431D1F mov eax, [ebp-08] ;; EAX = NAME :00431D22 movxz eax, byte ptr [edi+eax-01] ;; EAX = i. CHAR :00431D27 add [ebp-0C], eax ;; ADD EAX TO DWORD PTR [EBP-0C] :00431D2A mov ebx, [ebp-0C] ;; LOAD DWORD PTR [EBP-0C] IN EBX :00431D2D add ebx, 0F :00431D33 inc edi :00431D34 dec esi :00431D35 jne 431D1F -- END OF FIRST LOOP :00431D37 lea edx, [ebp-20] :00431D3A mov eax, [ebp-04] :00431D1D mov eax, [eax+01E4] :00431D43 call 41ACB0 :00431D48 mov eax, [ebp-20] ;; EAX POINTS TO SERIAL :00431D4B call 406794 ;; HERE THE SERIAL IS LOADED INTO EAX :00431D50 mov esi, eax ;; MOVE SERIAL INTO ESI :00431D52 sub esi, 05C4E51F ;; SUBTRACT 05C4E51F FROM SERIAL :00431D58 xor esi, 09 ;; XOR SERIAL WITH 9 :00431D5B mov eax, [ebp-08] ;; EAX POINTS TO NAME :00431D5E call 40390C ;; THIS CALL GETS LENGTH OF NAME :00431D63 sub esi, eax ;; SUBTRACTS LENGTH FROM SERIAL :00431D65 cmp ebx, esi ;; COMPARES VALUE FROM FIRST LOOP WITH SERIAL :00431D67 jne 431DDF ;; IF NOT SAME, THEN JUMP So - after we have the value from the first loop - we have to solve the following equation: Serial = ((LoopValue + LengthOfName) XOR 9) + 05C4E51F You will find the KeyGen algo (MASM and C) in the zipfile. A process patcher which will patch byte 431D68 to 00 is included, too. Addition to the keygen: Actually the name must be bigger than 5 chars to be accepted by the CrackMe. I didn't include this in my keygen, as if you have *really* quick eyes (or you slow down your machine ;) you can see that the "Cracked" message will appear before it closes, even if you have the correct serial for a 1-char-name. II.2 Clonk Planet This program is packed, too. But this time we have to deal with ASPack and not with Neolite. But that doesn't really matter, as we will keygen it. Start Clonk Planet (btw: a *really* sucking game ;) and enter a name/serial. Then we use GetWindowTextA as breakpoint and hit the OK button. I will choose LaZaRuS as name and 666999 as serial. I would suggest to take the same values to understand the essay. When SICE breaks hit F12 and you will be back in 32bit code. Now look at eax. Does it have the value 2 or 7? If it has 2, the button-text (OK) has been read, if it has 7 the name (LaZaRuS) has been read. If the buttontext has been read press CTRL-D. SICE will break again. F12 again and set a breakpoint on the call GetWindowTextA (45DF22) Now hit CTRL-D again and SICE will break, when the serial is read. Disable the "bpx GetWindowTextA". If you consult your API reference you will see, that eax is used as pointer to the buffer where the name and serial should be stored. Get out of SICE and re-enter your name/serial. Hit "OK" again and it will break at 45DF22. Now enter "d eax" and hit F10. You will see your name stored in memory (if name is read). Now set a bpm on the first char of your name. In my case it is "bpm B6633C rw" - the memory address changes every time you try to register. Now disable all bpx except the bpm with "bd" and hit CTRL-D. The first two times SICE breaks on memory access are useless for calculation and you can easily find out what's checked on your own. The first important operation is at instruction 436659. You will see this: :00000000 8A16 mov dl, byte ptr [esi] ;; DL = 1st CHAR OF NAME :00000002 84D2 test dl, dl ;; IS DL == 0? :00000004 7415 je 0000001B ;; IF SO, JUMP :00000006 57 push edi :00000007 0FBED2 movsx edx, dl ;; EDX = DL :0000000A 8BF9 mov edi, ecx :0000000C 0FAFFA imul edi, edx ;; EDI = EDI * EDX :0000000F 8A543101 mov dl, byte ptr [ecx+esi+01] ;; DL = NEXT CHAR :00000013 03C7 add eax, edi ;; EAX SAVES THE RESULT :00000015 41 inc ecx ;; NEXT CHAR :00000016 84D2 test dl, dl ;; IF DL = 0 (NO MORE CHAR) :00000018 75ED jne 00000007 ;; IF STILL CHARS LEFT, THEN JUMP After this loop we have a "magic" value in eax which is used later. For "LaZaRuS" it is 7BB. When you go on, you will come here: :0000007A 8D4605 lea eax, dword ptr [esi+05] ;; EAX POINTS TO NAME :0000007D 99 cdq :0000007E F7FB idiv ebx ;; DIVIDE BY THE LENGTH OF THE NAME :00000080 46 inc esi :00000081 49 dec ecx :00000082 83F9F6 cmp ecx, FFFFFFF6 ;; DID WE LOOP TEN TIMES? :00000085 8A043A mov al, byte ptr [edx+edi] ;; GET A CHAR FROM THE NAME :00000088 88811A894900 mov byte ptr [ecx+0049891A], al ;; SAVE THE CHAR ELSEWHERE :0000008E 7FEA jg 0000007A ;; IF NOT LOOPED TEN TIMES, THEN JUMP This loop converts the name into a 10-char-long string. Look at the algo and you will find out in which way. I won't explain, as it doesn't matter. We just need the converted string. Little later you will find that the name is converted again: :00000094 8BC6 mov eax, esi ;; EAX AND ESI CONTAIN THE "MAGIC VALUE" :00000096 8A9910894900 mov bl, byte ptr [ecx+00498910] ;; BL GETS i. CHAR :0000009C 99 cdq :0000009D 33C2 xor eax, edx ;; EAX = EAX XOR EDX :0000009F 83C62C add esi, 0000002C ;; ESI = ESI + 2C :000000A2 2BC2 sub eax, edx ;; EAX = EAX - EDX :000000A4 25FF000000 and eax, 000000FF ;; ONLY AL IS USED FROM EAX :000000A9 33C2 xor eax, edx ;; EAX = EAX XOR EDX :000000AB 2BC2 sub eax, edx ;; EAX = EAX - EDX :000000AD 32D8 xor bl, al ;; BL = BL XOR AL :000000AF 889910894900 mov byte ptr [ecx+00498910], bl ;; NEW CHAR :000000B5 41 inc ecx :000000B6 83F90A cmp ecx, 0000000A ;; TRANSFORMED ALL 10 CHARS? :000000B9 7CD9 jl 00000094 ;; IF NOT, THEN JUMP Here the 10-char-string gets once again transformed and overwritten. :000000BD 8BC1 mov eax, ecx :000000BF 8A9910894900 mov bl, byte ptr [ecx+00498910] ;; GET CHAR :000000C5 99 cdq :000000C6 F77C2414 idiv [esp+14] ;; DIVIDE BY 1C :000000CA 8B442418 mov eax, dword ptr [esp+18] ;; SEE BELOW :000000CE 8A1402 mov dl, byte ptr [edx+eax] ;; GET A CHAR FROM STRING :000000D1 32DA xor bl, dl ;; XOR CHAR OF NAME WITH CHAR OF STRING :000000D3 889910894900 mov byte ptr [ecx+00498910], bl ;; CHAR = NEW VALUE :000000D9 41 inc ecx :000000DA 83F90A cmp ecx, 0000000A ;; TRANSFORMED ALL 10 CHARS? :000000DD 7CDE jl 000000BD ;; IF NOT, THEN JUMP Above, the 10-char-name string gets transformed once again. This time it gets XORed with value from the string "-2..4nsw.-dfe./jkslm0qqmndfe" (w/o quotation marks). So, finally we reached the end: The next loop transforms the string into a valid serial :) :000000E1 0FBE8110894900 movsx eax, byte ptr [ecx+00498910] ;; EAX = CHAR :000000E8 85C0 test eax, eax ;; EAX = 0? :000000EA 7D02 jge 000000EE ;; JUMP IF EAX > 0 :000000EC F7D8 neg eax ;; EAX = EAX * (-1) :000000EE 99 cdq :000000EF BE0A000000 mov esi, 0000000A :000000F4 F7FE idiv esi ;; DIVIDE BY 10 (0Ah) :000000F6 80C230 add dl, 30 ;; CONVERT TO DIGIT BY ADDING 30h :000000F9 889110894900 mov byte ptr [ecx+00498910], dl ;; WRITE SERIAL TO MEM :000000FF 41 inc ecx :00000100 3BCE cmp ecx, esi ;; ESI = 10 :00000102 7CDD jl 000000E1 ;; JUMP IF < 10 DIGITS CREATED So, for the very end another transformation: :00000104 803D1089490030 cmp byte ptr [00498910], 30 :0000010B 7507 jne 00000114 :0000010D C6051089490033 mov byte ptr [00498910], 33 :00000114 B810894900 mov eax, 00498910 If the serial starts with "0", it will be replaced with "3". Trace out of the call and you will end in front of the comparison of the good and bad serial: :00000000 50 push eax ;; EAX = REAL SERIAL :00000001 56 push esi ;; ESI = ENTERED SERIAL :00000002 E8ABF1FFFF call FFFFF1B2 ;; COMPARE THEM :00000007 83C408 add esp, 00000008 ;; FIX THE STACK :0000000A 84C0 test al, al ;; IS AL = 0 :0000000C 0F8407010000 je 00000119 ;; IF SO, THEN JUMP :00000012 53 push ebx ;; EBX POINTS TO YOUR NAME :00000013 6A20 push 00000020 ;; 20h = " " :00000015 E8C8F4FFFF call FFFFF4E2 ;; CHECKS IF " " IS IN YOUR NAME :0000001A 83C408 add esp, 00000008 :0000001D 85C0 test eax, eax ;; EAX = 0 (NO " ") ? :0000001F 0F84F4000000 je 00000119 ;; IF SO, THEN JUMP :00000025 8D85BF990000 lea eax, dword ptr [ebp+000099BF] ;; SEE BELOW :0000002B 50 push eax ;; PUSH STRING :0000002C 53 push ebx ;; PUSH NAME :0000002D E8D0F4FFFF call FFFFF502 ;; DOES NAME CONTAIN INVALID CHARS :00000032 83C408 add esp, 00000008 :00000035 85C0 test eax, eax ;; IS EAX = 0? (NO INV. CHARS) :00000037 0F85DC000000 jne 00000119 ;; IF NOT, THEN JUMP So, here we see that (1) our name has to contain a space (00000015) and it musn't contain any of the following chars: !.§$%&/()=?{[]}#*+ (00000025). Later it blacklists the following names: Animalo, Mad Tulip, MasterDeveloperCode, Matthes Bender, Markus Wichitill and RedWolf Design. btw: Dunno, why they blacklisted "Animalo" and "MasterDeveloperCode" - It wouldn't be possible to register with those names as they don't contain a " " ;) So, that's all. You will find the source of a keygen (MASM) inside the zip file. III. BTW To make sure nobody pisses me off for anything: The process patcher source is 90% from Natzgul and the skeleton of the keygen was borrowed from rubor. visit hello.to/lazarus and hellforge.tsx.org mail: lazarus_hf@hotmail.com Greetings go to: +Sandman, Acid Burn, alpine, Borna Janes, Carpathia, CrazyKnight, DEATH, DEZM, dimwitz, DnNuke, duelist, Eternal Bliss, Fravia+, Iczelion, Jordan, KnowledgeIsPower, Knotty, Lucifer48, MisterE, Neural Noise, noos, Prof.X, R!SC, rubor, Shadow, SiG, tC, The AntiXryst, The Hobgoblin, TORN@DO, viny, Volatility, wAj, _y and all the guys I forget and I'll add next time.