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 35.Cracking Tutorial (29.12.1999) XX Cracking GPPFast 4.2 I. Introduction I.1 The tools II. The essay II.1 First look at the target II.2 The serial II.3 The keygen III. BTW I. Introduction Greetings dear reader. Although the algo here is really simple I wrote this tutorial, because the algo is quite interesting. It calculates the correct serial from the serial that is entered and that provided some fun ;) I.1 The tools SoftIce (I have 3.25) II. The essay When you start the main EXE of GPPFast, you will be confronted by a nagscreen, but this is of no interest, because we will keygen this little sucker ;) So go straight to the About window and enter a code. I will choose the name "LaZaRuS" and the serial 666999. Enter SICE and set a bpx on hmemcpy. Then hit the OK button and when SICE breaks, leave SICE and it will break once again. The first time it breaks, the name is read, the second time the prog reads the serial we entered. Now hit F12 until you are at "useful" code, that is: When the address looks like XXXX:XX4XXXXX and no "ret" appears in the next few lines. The "useful" code is this one: :0043C41F 8B9500FCFFFF mov edx, dword ptr [ebp+FFFFFC00] ;; SERIAL :0043C425 8D8504FDFFFF lea eax, dword ptr [ebp+FFFFFD04] :0043C42B B9FF000000 mov ecx, 000000FF :0043C430 E86B75FCFF call 004039A0 ;; THIS CALL GETS THE LENGTH OF SERIAL :0043C435 0FB6BD04FDFFFF movzx edi, byte ptr [ebp+FFFFFD04] ;; EDI = LENGTH :0043C43C 85FF test edi, edi ;; IS LENGTH = 0 ? :0043C43E 7E26 jle 0043C466 ;; IF SO, THEN "WRONG SERIAL" The next few lines get the amount of "-" inside the serial: :0043C440 8D8505FDFFFF lea eax, dword ptr [ebp+FFFFFD05] ;; SERIAL * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C464(C) | :0043C446 8A10 mov dl, byte ptr [eax] ;; GET CHAR FROM SERIAL :0043C448 80FA30 cmp dl, 30 ;; COMPARE CHAR WITH 30h :0043C44B 7205 jb 0043C452 ;; JUMP, IF BELOW :0043C44D 80FA39 cmp dl, 39 ;; COMPARE CHAR WITH 39h :0043C450 760A jbe 0043C45C ;; JUMP, IF BELOW OR EQUAL * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C44B(C) | :0043C452 80FA2D cmp dl, 2D ;; COMPARE CHAR WITH "-" :0043C455 7405 je 0043C45C ;; JUMP, IF EQUAL :0043C457 BB01000000 mov ebx, 00000001 ;; SET "WRONG"-FLAG * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0043C450(C), :0043C455(C) | :0043C45C 80FA2D cmp dl, 2D ;; COMPARE CHAR WITH "-" :0043C45F 7501 jne 0043C462 ;; JUMP, IF NOT EQUAL :0043C461 46 inc esi ;; COUNTER OF "-" * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C45F(C) | :0043C462 40 inc eax ;; NEXT CHAR :0043C463 4F dec edi :0043C464 75E0 jne 0043C446 ;; IF THERE ARE CHARS TO TEST LEFT * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C43E(C) | :0043C466 83FE02 cmp esi, 00000002 ;; COMPARE "-" COUNTER WITH 2 :0043C469 7D05 jge 0043C470 ;; JUMP, IF GREATER OR EQUAL :0043C46B BB01000000 mov ebx, 00000001 ;; SET "WRONG" FLAG * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C469(C) | :0043C470 85DB test ebx, ebx ;; IS EBX=0 :0043C472 0F85B6040000 jne 0043C92E ;; JUMP, IF NOT EQUAL So, here we learned that the serial can only contain digits and "-" where the minimum of "-" is 2. So, the next serial I tried was: 1234-5678-90 Using this serial, we come here: A loop which gets the digits in front of the first "-" :0043C490 8B9500FCFFFF mov edx, dword ptr [ebp+FFFFFC00] ;; SERIAL :0043C496 8D8504FDFFFF lea eax, dword ptr [ebp+FFFFFD04] :0043C49C B9FF000000 mov ecx, 000000FF :0043C4A1 E8FA74FCFF call 004039A0 ;; GET LENGTH OF SERIAL * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C4C8(C) | :0043C4A6 43 inc ebx :0043C4A7 46 inc esi :0043C4A8 8A941D04FDFFFF mov dl, byte ptr [ebp+ebx-000002FC] ;; GET DIGIT :0043C4AF 8BC2 mov eax, edx :0043C4B1 3C2D cmp al, 2D ;; COMPARE DIGIT WITH "-" :0043C4B3 7409 je 0043C4BE ;; IF EQUAL, THEN JUMP :0043C4B5 88943504FCFFFF mov byte ptr [ebp+esi-000003FC], dl ;;SAVE DIGIT SOMEWHERE :0043C4BC EB08 jmp 0043C4C6 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C4B3(C) | :0043C4BE C6843504FCFFFF00 mov byte ptr [ebp+esi-000003FC], 00 ;; TERMINATING ZERO * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C4BC(U) | :0043C4C6 3C2D cmp al, 2D ;; IS DIGIT = "-"? :0043C4C8 75DC jne 0043C4A6 ;; JUMP IF NOT EQUAL After we have 1234 isolated from "1234-5678-90" we come here. A loop which adds all digits of 1234. :0043C4CA 33C0 xor eax, eax ;; SET EAX TO 0 :0043C4CC 8945F8 mov dword ptr [ebp-08], eax ;; SET BUFFER TO 0 :0043C4CF 8BFB mov edi, ebx :0043C4D1 4F dec edi :0043C4D2 85FF test edi, edi :0043C4D4 7E17 jle 0043C4ED :0043C4D6 8D8505FCFFFF lea eax, dword ptr [ebp+FFFFFC05] ;; EAX = "1234" * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C4EB(C) | :0043C4DC 33D2 xor edx, edx :0043C4DE 8A10 mov dl, byte ptr [eax] ;; GET DIGIT FROM "1234" :0043C4E0 0355F8 add edx, dword ptr [ebp-08] ;; ADD BUFFER TO DIGIT :0043C4E3 83EA30 sub edx, 00000030 ;; SUBTRACT 30 FROM EDX :0043C4E6 8955F8 mov dword ptr [ebp-08], edx ;; WRITE EDX (VALUE1) TO BUFFER :0043C4E9 40 inc eax :0043C4EA 4F dec edi :0043C4EB 75EF jne 0043C4DC ;; IF CHARS LEFT, THEN JUMP Also this looked a little complicated at first, it just adds every digit of the digits in front of the first ""-". For example, for 1234 the result is 1+2+3+4=10=0Ah, for 67321 it is 6+7+3+2+1 = 19 = 13h. The next few lines do nothing more than getting the digits between the first and the second "-". I won't comment, because it is completely the same as above. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C4D4(C) | :0043C4ED 33F6 xor esi, esi * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C516(C) | :0043C4EF 43 inc ebx :0043C4F0 46 inc esi :0043C4F1 8A841D04FDFFFF mov al, byte ptr [ebp+ebx-000002FC] :0043C4F8 3C2D cmp al, 2D :0043C4FA 7410 je 0043C50C :0043C4FC 8A941D04FDFFFF mov dl, byte ptr [ebp+ebx-000002FC] :0043C503 88943504FCFFFF mov byte ptr [ebp+esi-000003FC], dl :0043C50A EB08 jmp 0043C514 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C4FA(C) | :0043C50C C6843504FCFFFF00 mov byte ptr [ebp+esi-000003FC], 00 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043C50A(U) | :0043C514 3C2D cmp al, 2D :0043C516 75D7 jne 0043C4EF The next few lines I won't even paste, because they just test, if all chars from the string between the first and the second "-" are digits. The next important things come here: :0043C529 8B8500FCFFFF mov eax, dword ptr [ebp+FFFFFC00] ;; "5678" :0043C52F E8CCA6FCFF call 00406C00 ;; THIS CALL LOADS THE NUMBER IN EAX :0043C534 8945F4 mov dword ptr [ebp-0C], eax ;; SAVE VALUE2 IN [ebp-0C] The above loop converts the number 5678 in hex (=162E). I strongly believe that this routine has a bug. *Sometimes* it justs loads the first two digits in eax. This causes that sometimes a serial that most time works, does not work. Just re-enter the serial and hit OK, once again to "correct" it. The next lines get the digits after the second "-" and test, if they are all digits, so I won't paste. The next important lines are here: :0043C57F 8B8500FCFFFF mov eax, dword ptr [ebp+FFFFFC00] ;; DIGITS AFTER 2nd "-" :0043C585 E876A6FCFF call 00406C00 ;; LOADS HEXVALUE IN EAX :0043C58A 8BD8 mov ebx, eax ;; MOVE VALUE3 TO EBX :0043C58C 837DF800 cmp dword ptr [ebp-08], 00000000 ;; COMPARES THE FIRST ;; CALCULATE VALUE WITH 0 :0043C590 0F8483030000 je 0043C919 ;; IF EQUAL, THEN "WRONG SERIAL" Now we can enter the main core of the serial algo: :0043C596 6B75F825 imul esi, dword ptr [ebp-08], 00000025 ;; ESI = VALUE1*25h :0043C59A 8BC6 mov eax, esi ;; EAX = ESI = VALUE1*25h :0043C59C 8BD3 mov edx, ebx ;; EDX = VALUE3 :0043C59E C1E204 shl edx, 04 ;; EDX = EDX * 16 :0043C5A1 03D3 add edx, ebx ;; EDX = EDX + EBX :0043C5A3 2BC2 sub eax, edx ;; VALUE1*25h - VALUE3 * 17d :0043C5A5 3B45F4 cmp eax, dword ptr [ebp-0C] ;; COMPARE EAX WITH VALUE2 :0043C5A8 0F8554030000 jne 0043C902 To recollect, the definitions of the values: VALUE1: All digits of the first value added VALUE2: Second part of serial VALUE3: Third part of the serial We gotta solve the following equation to get a valid serial: 37*VALUE1 - 17* VALUE3 = VALUE2 III. BTW Greetings go to: +Sandman, Acid Burn, alpine, Blind Angel, Borna Janes, Carpathia, CrazyKnight, DEATH, DEZM, dimwitz, DnNuke, duelist, Eternal Bliss, Fravia+, Iczelion, Jordan, KnowledgeIsPower, Knotty, Kwazy Webbit, Lucifer48, MisterE, Neural Noise, noos, Prof.X, R!SC, rubor, Shadow, SiG, tC, The AntiXryst, The Hobgoblin, TORN@DO, ultraschall, viny, Volatility, wAj WarezPup, _y and all the guys I forget and I'll add next time.