Need for Speed Underground 2 – Let’s make a real Keygen

Heute ist es mal wieder soweit und wir knöpfen uns diesmal Need For Speed Underground 2 (NFSU2) vor. Diesmal schauen wir, wie der Serial Generator funktioniert. Warum gerade dieses Spiel? Erst einmal ist es ein verdammt geiles Spiel! Außerdem habe ich von TSRH den 20+ Langames Keygen und unter anderem ist auch NFSU2 dabei. Nun ja und da es von diesem Spiel einen richtigen Keygen gibt, dachte ich mir, ich kann diesmal auch einen echten Keygen schreiben und muss nicht wieder Serial Bruteforcing betreiben. So kam es dazu dass ich dieses Spiel auswählte, doch das erste Problem kam schon auf mich zu. WO VERDAMMT NOCH EINMAL IST DIE CD?!? Irgendwo zwischen dem Chaos musste sie sein.


Nunja nach circa einer halben Stunde hatte ich endlich die CD. Also hab ich mir schnell die Images von der CD geladen und dann konnte der Spaß schon losgehen.

Standard

Also beginnt wieder die Standard Prozedur. Was haben wir auf der CD? Was ist für uns relevant?

Ich denke mal das uns die setup.exe am meisten interessiert und mal schauen wie der Generator dort drinnen funktioniert. Doch bevor ich die Setup.exe in OllyDbg auseinander pflücke, schaue ich mir ersteinmal an, wie der Vorgang ohne debugger aussieht.

So nun kann es fast losgehen. Setup.exe erst einmal in PEid laden und wieder haben wir Glück: „Microsoft Visual C++ 7.0“ strahlt uns entgegen und so müssen wir die Datei nicht entpacken. Zuerst werden wieder die Strings und intermodularen Calls nach Hinweisen durchforstet. Ich habe dann speziell nach der Fehlermeldung gesucht, doch überhaupt nichts brauchbares gefunden. Irgendwie bin ich dann auf die Idee gekommen mir die Verzeichnisse auf der CD anzuschauen. Vielleicht befindet sich dort ja ein Dokument, mit den Fehlermeldungen. In dem Support Ordner fand ich zuerst einige Wörterbücher, die für die Übersetzungen zuständig sind, aber auch hier Fehlanzeige von der Fehlermeldung. Sehr lustig war dann die Datei „Localization.ini“

 // Note:
 // 1 - Please enter the entire code found in the white box at the lower left of the back of the DVD/CD case.
 // 2 - Please enter the entire code found in the white box at the lower left of the back of the manual.
 // 3 - Please enter the entire code found in the white box inside the DVD/CD case.
 //
 // The first message will be used by default if a language entry is missing

[CDKEY]
 cs=2
 da=2
 de=2
 el=2
 en-uk=2
 en-us=1
 es=2
 fi=2
 fr-fr=2
 he=2
 it=2
 ja=1
 ko=1
 nl=2
 no=2
 pl=2
 pt-br=1
 pt-pt=2
 ru=2
 sv=2
 th=1
 zh-cn=1
 zh-tw=1

Mal ist der Serial auf dem Handbuch und mal in der Verpackung, aber eins fällt auf: Warum gibt es die Dritte Fehlermeldung, wenn sie nie benutzt wird? :D Naja sei es drum. In dem selben Ordner fiel mir dann eine Datei speziell auf: „Need for Speed Underground 2_code.exe„. Klingt interessant, mal schauen was sie so macht, wenn man sie ausführt.

Wir nähern uns dem Ziel

Na dann ist es ja klar, warum ich die Fehlermeldungen nie finden konnte. Also wieder von vorne Anfangen und mit PEid analysieren: „Microsoft Visual C++ 7.0„. Im nächsten Schritt suchen wir wieder nach passenden Strings und Calls, setzen einige BreakPoints und hoffen, dass das Spiel an der passenden Stelle stoppt. Die erste stoppende Stelle war nicht sehr hilfreich. Gefunden habe ich sie durch die intermodularen Calls (sortiert nach Dest. Name) unter GetDlgItem. Die Stelle liest einfach nur ständig die Key-Felder aus:

CPU Disasm
Address   Hex dump          Command                          Comments
0041A9BA  /$  8379 48 00    CMP DWORD PTR DS:[ECX+48],0      ; liest staendig die textfelder aus
0041A9BE  |.  75 16         JNE SHORT 0041A9D6
0041A9C0  |.  FF7424 04     PUSH DWORD PTR SS:[ARG.1]        ; /ItemID
0041A9C4  |.  FF71 1C       PUSH DWORD PTR DS:[ECX+1C]       ; |hDialog
0041A9C7  |.  FF15 28244200 CALL DWORD PTR DS:[]             ; \USER32.GetDlgItem
0041A9CD  |.  8B4C24 08     MOV ECX,DWORD PTR SS:[ARG.2]
0041A9D1  |.  8901          MOV DWORD PTR DS:[ECX],EAX
0041A9D3  |.  C2 0800       RETN 8
0041A9D6  |>  8B49 48       MOV ECX,DWORD PTR DS:[ECX+48]
0041A9D9  |.  8B01          MOV EAX,DWORD PTR DS:[ECX]
0041A9DB  \.  FF60 70       JMP DWORD PTR DS:[EAX+70]

Und gleich die nächste Stelle die mir dann auch wieder nichts nützte, aber wir sind schon einmal nah bei den Textfeldern dran.

CPU Disasm
Address   Hex dump          Command                          Comments
0041D6C5  /$  55            PUSH EBP                         ; verwandelt kleinbuchstabe in grosse
0041D6C6  |.  8BEC          MOV EBP,ESP
0041D6C8  |.  81EC 04010000 SUB ESP,104
0041D6CE  |.  A1 60E64200   MOV EAX,DWORD PTR DS:[42E660]
0041D6D3  |.  56            PUSH ESI
0041D6D4  |.  FF75 0C       PUSH DWORD PTR SS:[ARG.2]        ; /String => [ARG.2]
0041D6D7  |.  8945 FC       MOV DWORD PTR SS:[LOCAL.1],EAX   ; |
0041D6DA  |.  FF15 68224200 CALL DWORD PTR DS:[]             ; \KERNEL32.lstrlenA
0041D6E0  |.  8BF0          MOV ESI,EAX
0041D6E2  |.  B8 00010000   MOV EAX,100
0041D6E7  |.  3BF0          CMP ESI,EAX
0041D6E9  |.  77 29         JA SHORT 0041D714
0041D6EB  |.  50            PUSH EAX                         ; /MaxCount => 256.
0041D6EC  |.  8D85 FCFEFFFF LEA EAX,[LOCAL.65]               ; |
0041D6F2  |.  50            PUSH EAX                         ; |String => OFFSET LOCAL.65
0041D6F3  |.  FF75 08       PUSH DWORD PTR SS:[ARG.1]        ; |hWnd => [ARG.1]
0041D6F6  |.  FF15 54244200 CALL DWORD PTR DS:[]             ; \USER32.GetWindowTextA
0041D6FC  |.  3BC6          CMP EAX,ESI
0041D6FE  |.  75 14         JNE SHORT 0041D714
0041D700  |.  FF75 0C       PUSH DWORD PTR SS:[ARG.2]        ; /String2 => [ARG.2]
0041D703  |.  8D85 FCFEFFFF LEA EAX,[LOCAL.65]               ; |
0041D709  |.  50            PUSH EAX                         ; |String1 => OFFSET LOCAL.65
0041D70A  |.  FF15 AC214200 CALL DWORD PTR DS:[]             ; \KERNEL32.lstrcmp
0041D710  |.  85C0          TEST EAX,EAX
0041D712  |.  74 0C         JE SHORT 0041D720
0041D714  |>  FF75 0C       PUSH DWORD PTR SS:[ARG.2]        ; /Text => [ARG.2]
0041D717  |.  FF75 08       PUSH DWORD PTR SS:[ARG.1]        ; |hWnd => [ARG.1]
0041D71A  |.  FF15 34234200 CALL DWORD PTR DS:[]             ; \USER32.SetWindowTextA
0041D720  |>  8B4D FC       MOV ECX,DWORD PTR SS:[LOCAL.1]
0041D723  |.  5E            POP ESI
0041D724  |.  E8 659EFEFF   CALL 0040758E
0041D729  |.  C9            LEAVE
0041D72A  \.  C2 0800       RETN 8

Das wars?

Nach einer Weile hab ich dann endlich den Einstieg gefunden. Hätte man eigentlich auch eher drauf kommen können. Es gibt 5 Textfelder also können sie mit %s%s%s%s%s zusammengesetzt werden.

CPU Disasm
Address   Hex dump          Command                             Comments
004029EE  |.  8B86 04010000 MOV EAX,DWORD PTR DS:[ESI+104]      ; perfekt die serial funktion
004029F4  |.  51            PUSH ECX                            ; /
004029F5  |.  8B8E 00010000 MOV ECX,DWORD PTR DS:[ESI+100]      ; |
004029FB  |.  52            PUSH EDX                            ; |
004029FC  |.  8B96 FC000000 MOV EDX,DWORD PTR DS:[ESI+0FC]      ; |
00402A02  |.  50            PUSH EAX                            ; |
00402A03  |.  51            PUSH ECX                            ; |
00402A04  |.  52            PUSH EDX                            ; |
00402A05  |.  8D4424 20     LEA EAX,[ESP+20]                    ; |
00402A09  |.  68 70274200   PUSH OFFSET 00422770                ; |Format = "%s%s%s%s%s"
00402A0E  |.  50            PUSH EAX                            ; |Arg1
00402A0F  |.  E8 29570000   CALL 0040813D                       ; \Need_for_Speed_Underground_2_co.0040813D
00402A14  |.  83C4 1C       ADD ESP,1C
00402A17  |.  8D4C24 0C     LEA ECX,[ESP+0C]                    ; serialpruefer
00402A1B  |.  51            PUSH ECX                            ; /Arg1
00402A1C  |.  8D4C24 08     LEA ECX,[ESP+8]                     ; |
00402A20  |.  E8 8B320000   CALL 00405CB0                       ; \Need_for_Speed_Underground_2_co.00405CB0
00402A25  |.  84C0          TEST AL,AL

Jetzt können wir erst einmal alles schnell durchlaufen und schauen, ob er vielleicht irgendwie etwas vergleicht. Nach dem ich am Ende des Serialprüfers angekommen bin, erblickten meine Augen im Register Bereich etwas schönes und doch schreckliches zugleich. Aber seht nur selbst, denn jetzt können wir an dieser Stelle aufhören!

CPU - main thread, module Need_for_Speed_Underground_2_co

EAX 0012E8B4 ASCII "083BP3ORA6RL3SR5ASFA"
ECX 0012E8B7 ASCII "BP3ORA6RL3SR5ASFA"
EDX 00000041
EBX 00000111
ESP 0012E888
EBP 0012EE14
ESI 0012E8B4 ASCII "083BP3ORA6RL3SR5ASFA"
EDI 0012E8F4 ASCII "0000OHOIA6RLDSR5ASFA"
EIP 00405C25 Need_for_Speed_Underground_2_co.00405C25

Wie man hier in dem kleinen Ausschnitt sieht, generiert der Serialprüfer, auf Basis des eingegebenen Serials, selbst einen Serial und vergleicht dann diese miteinander. In EDI steht meine Eingabe und in ESI wie der Serial aussehen sollte. Also Kopieren wir den Key von ESI und geben Ihn ein. Damit sind wir nun fertig und wir können das Spiel installieren, aber eines fällt auf! Vergleicht man die letzten sieben Stellen, sind diese genau gleich. Es scheint als sei es eine Prüfziffer oder ein Index für den Serial. Wir werden sehen, denn ich möchte es wieder einmal genauer wissen.

Durchmischtes Kalkulieren

Kommen wir zur ersten Funktion. Nach dem der Serial eingelesen wurde, wird er erst einmal durcheinander gebracht. Die Kommentare im ASM Code sollten deutlich machen, was passiert.

CPU Disasm
Address   Hex dump          Command                          Comments
004059D0  /$  8B4424 04     MOV EAX,DWORD PTR SS:[ARG.1]     ; z'B --> Variable? --> Mischfunktion
004059D4  |.  8B4C24 08     MOV ECX,DWORD PTR SS:[ARG.2]     ; Serial
004059D8  |.  53            PUSH EBX
004059D9  |.  56            PUSH ESI
004059DA  |.  8BF0          MOV ESI,EAX
004059DC  |.  2BF1          SUB ESI,ECX
004059DE  |.  8BFF          MOV EDI,EDI                      ; einlesen des serial
004059E0  |>  8A11          /MOV DL,BYTE PTR DS:[ECX]
004059E2  |.  88140E        |MOV BYTE PTR DS:[ECX+ESI],DL
004059E5  |.  41            |INC ECX
004059E6  |.  84D2          |TEST DL,DL
004059E8  |.^ 75 F6         \JNE SHORT 004059E0
004059EA  |.  0FBE0D D8DC42 MOVSX ECX,BYTE PTR DS:[42DCD8]   ; tausche array [2] mit [13]
004059F1  |.  8A1401        MOV DL,BYTE PTR DS:[EAX+ECX]
004059F4  |.  8A58 0D       MOV BL,BYTE PTR DS:[EAX+0D]
004059F7  |.  881C01        MOV BYTE PTR DS:[EAX+ECX],BL
004059FA  |.  8A58 0E       MOV BL,BYTE PTR DS:[EAX+0E]
004059FD  |.  8850 0D       MOV BYTE PTR DS:[EAX+0D],DL
00405A00  |.  0FBE15 D9DC42 MOVSX EDX,BYTE PTR DS:[42DCD9]   ; neuen startwert einlesen fuer das array
00405A07  |.  03C8          ADD ECX,EAX                      ; in diesem falle 4
00405A09  |.  8D0C02        LEA ECX,[EAX+EDX]
00405A0C  |.  8A11          MOV DL,BYTE PTR DS:[ECX]         ; tausche [4] mit [14]
00405A0E  |.  8819          MOV BYTE PTR DS:[ECX],BL
00405A10  |.  8A58 0F       MOV BL,BYTE PTR DS:[EAX+0F]
00405A13  |.  8850 0E       MOV BYTE PTR DS:[EAX+0E],DL
00405A16  |.  0FBE0D DADC42 MOVSX ECX,BYTE PTR DS:[42DCDA]   ; tausche [5] mit [15] (next please :D)
00405A1D  |.  8A1401        MOV DL,BYTE PTR DS:[EAX+ECX]
00405A20  |.  881C01        MOV BYTE PTR DS:[EAX+ECX],BL
00405A23  |.  8A58 10       MOV BL,BYTE PTR DS:[EAX+10]
00405A26  |.  8850 0F       MOV BYTE PTR DS:[EAX+0F],DL
00405A29  |.  0FBE15 DBDC42 MOVSX EDX,BYTE PTR DS:[42DCDB]   ; neuer startwert = 7
00405A30  |.  03C8          ADD ECX,EAX
00405A32  |.  8D0C02        LEA ECX,[EAX+EDX]
00405A35  |.  8A11          MOV DL,BYTE PTR DS:[ECX]         ; tausche [7] mit [16]
00405A37  |.  8819          MOV BYTE PTR DS:[ECX],BL
00405A39  |.  8A58 11       MOV BL,BYTE PTR DS:[EAX+11]
00405A3C  |.  8850 10       MOV BYTE PTR DS:[EAX+10],DL
00405A3F  |.  0FBE0D DCDC42 MOVSX ECX,BYTE PTR DS:[42DCDC]   ; startwert = 12
00405A46  |.  8A1401        MOV DL,BYTE PTR DS:[EAX+ECX]     ; tausche [12] mit [17]
00405A49  |.  881C01        MOV BYTE PTR DS:[EAX+ECX],BL
00405A4C  |.  8A58 12       MOV BL,BYTE PTR DS:[EAX+12]
00405A4F  |.  03C8          ADD ECX,EAX
00405A51  |.  8850 11       MOV BYTE PTR DS:[EAX+11],DL
00405A54  |.  0FBE15 DDDC42 MOVSX EDX,BYTE PTR DS:[42DCDD]   ; startwert = 1
00405A5B  |.  8D0C02        LEA ECX,[EAX+EDX]
00405A5E  |.  8A11          MOV DL,BYTE PTR DS:[ECX]         ; tausche [1] mit [18]
00405A60  |.  8819          MOV BYTE PTR DS:[ECX],BL
00405A62  |.  8A58 13       MOV BL,BYTE PTR DS:[EAX+13]      ; 0x13 = 18 ;p
00405A65  |.  8850 12       MOV BYTE PTR DS:[EAX+12],DL
00405A68  |.  0FBE0D DEDC42 MOVSX ECX,BYTE PTR DS:[42DCDE]   ; startwert = 3
00405A6F  |.  8A1401        MOV DL,BYTE PTR DS:[EAX+ECX]     ; tausche [3] mit dem letzten ([19])
00405A72  |.  03C8          ADD ECX,EAX
00405A74  |.  8819          MOV BYTE PTR DS:[ECX],BL
00405A76  |.  5E            POP ESI
00405A77  |.  8850 13       MOV BYTE PTR DS:[EAX+13],DL
00405A7A  |.  C640 14 00    MOV BYTE PTR DS:[EAX+14],0
00405A7E  |.  5B            POP EBX
00405A7F  \.  C3            RETN

Kommen wir nun auch schon zur nächsten Funktion. Im nächsten Schritt wird nun ein wenig gerechnet. Unser durchmischter Serial hat 20 Stellen und von diesem werden nur die ersten 13 Stellen für die nächste Rechnung benötigt. Die letzten sieben Stellen haben vielleicht eine andere Bedeutung, wie ich es schon erwähnt habe.

CPU Disasm
Address   Hex dump          Command                          Comments
00405780  /$  53            PUSH EBX
00405781  |.  8B5C24 0C     MOV EBX,DWORD PTR SS:[ARG.2]     ; 13 Stellen vom Array
00405785  |.  56            PUSH ESI
00405786  |.  57            PUSH EDI
00405787  |.  33FF          XOR EDI,EDI                      ; buff = 0
00405789  |.  33F6          XOR ESI,ESI                      ; zaehler = 0
0040578B  |.  85DB          TEST EBX,EBX
0040578D  |.  B9 01000000   MOV ECX,1                        ; vorrestwert = 1
00405792  |.  7E 31         JLE SHORT 004057C5
00405794  |.  55            PUSH EBP
00405795  |.  8B6C24 14     MOV EBP,DWORD PTR SS:[ARG.1]     ; gekuerzter array
00405799  |.  8DA424 000000 LEA ESP,[LOCAL.3]                ; stop_zaehler = 14
004057A0  |>  0FBE042E      /MOVSX EAX,BYTE PTR DS:[EBP+ESI] ; rechnung
004057A4  |.  03C1          |ADD EAX,ECX                     ; array[i]-ascii-wert + vorrestwert = vorwert
004057A6  |.  33D2          |XOR EDX,EDX
004057A8  |.  B9 F1FF0000   |MOV ECX,0FFF1
004057AD  |.  F7F1          |DIV ECX                         ; vorwert % 0xfff1 = vorrestwert
004057AF  |.  8BCA          |MOV ECX,EDX
004057B1  |.  8D040F        |LEA EAX,[ECX+EDI]               ; sum_reste = vorrestwert + buff
004057B4  |.  33D2          |XOR EDX,EDX
004057B6  |.  BF F1FF0000   |MOV EDI,0FFF1
004057BB  |.  F7F7          |DIV EDI                         ; sum_reste % 0xfff1 = buff
004057BD  |.  46            |INC ESI
004057BE  |.  3BF3          |CMP ESI,EBX
004057C0  |.  8BFA          |MOV EDI,EDX
004057C2  |.^ 7C DC         \JL SHORT 004057A0
004057C4  |.  5D            POP EBP
004057C5  |>  8BC7          MOV EAX,EDI
004057C7  |.  5F            POP EDI
004057C8  |.  C1E0 10       SHL EAX,10                       ; buff shl vorwert --> macht in hex: XXXXYYYY wobei für  XXXX das Ergebnis von Buff ist und YYYY von vorwert
004057CB  |.  5E            POP ESI
004057CC  |.  03C1          ADD EAX,ECX
004057CE  |.  5B            POP EBX
004057CF  \.  C3            RETN

Ja… Nun ja… Ich kann nur sagen er rechnet da irgendwas  mit den ASCII Werten des gekürzten Serial zusammen. Dieses Ergebnis was denn da rauskommt, wird später noch weiter verwendet. Am interessantesten finde ich den Befehl SHL am Ende des ASM Codes. SHL verschiebt den Wert um X bits nach Links. So wird bei SHL 0b0001, 2 der neue Wert zu 0b0100. Wie man sieht, verschiebt er sich um 2 bit nach links. Damit kann man super schnell größere Zahlen generieren/berechnen. Das gute ist, die CPU wird auch durch diesen Befehl entlastet, da die CPU diesen Befehl schneller abarbeiten kann. Von nun an nur noch mit >> (SHR) oder << (SHL) arbeiten :D
Schauen wir uns den dritten großen Schritt an:

CPU Disasm
Address   Hex dump          Command                          Comments
00405A80  /$  83EC 40       SUB ESP,40
00405A83  |.  53            PUSH EBX
00405A84  |.  55            PUSH EBP
00405A85  |.  56            PUSH ESI
00405A86  |.  8B7424 54     MOV ESI,DWORD PTR SS:[ARG.2]
00405A8A  |.  57            PUSH EDI
00405A8B  |.  6A 0D         PUSH 0D                          ; von 0x00 bis 0x0d serial uebernehmen
00405A8D  |.  56            PUSH ESI
00405A8E  |.  E8 EDFCFFFF   CALL 00405780                    ; rechnerei kram
00405A93  |.  8B5C24 64     MOV EBX,DWORD PTR SS:[ARG.3]     ; 0x1d8df
00405A97  |.  33C3          XOR EAX,EBX                      ; ergebnis xor 0x1d8df
00405A99  |.  50            PUSH EAX
00405A9A  |.  8D4424 3C     LEA EAX,[LOCAL.7]
00405A9E  |.  50            PUSH EAX
00405A9F  |.  E8 ACFDFFFF   CALL 00405850                    ; dritte große Funktion
00405AA4  |.  8D6C24 20     LEA EBP,[LOCAL.15]
00405AA8  |.  83C4 10       ADD ESP,10
00405AAB  |.  8BC6          MOV EAX,ESI
00405AAD  |.  2BEE          SUB EBP,ESI

Unser Ergebnis wird mit 0x1D8DF gexored und wird dann an die Funktion in CALL 00405850 übergeben.

CPU Disasm
Address   Hex dump          Command                               Comments
00405850  /$  8B4424 04     MOV EAX,DWORD PTR SS:[ARG.1]          ; 0x12ebe0
00405854  |.  8B4C24 08     MOV ECX,DWORD PTR SS:[ARG.2]          ; Ergebnis
00405858  |.  8AD1          MOV DL,CL
0040585A  |.  80E2 1F       AND DL,1F                             ; Ergebnis & 0x1f = buchstabe_6
0040585D  |.  8850 06       MOV BYTE PTR DS:[EAX+6],DL
00405860  |.  8BD1          MOV EDX,ECX
00405862  |.  C1EA 05       SHR EDX,5                             ; Ergebnis * 2^5 = wert
00405865  |.  80E2 1F       AND DL,1F                             ; wert & 0x1f = buchstabe_5
00405868  |.  8850 05       MOV BYTE PTR DS:[EAX+5],DL
0040586B  |.  8BD1          MOV EDX,ECX
0040586D  |.  C1EA 0A       SHR EDX,0A                            ; ergebnis * 2^0xA = wert
00405870  |.  80E2 1F       AND DL,1F                             ; wert & 0x1f = buchstabe 4
00405873  |.  8850 04       MOV BYTE PTR DS:[EAX+4],DL
00405876  |.  8BD1          MOV EDX,ECX
00405878  |.  C1EA 0F       SHR EDX,0F                            ; ergebnis * 2^0xf = wert
0040587B  |.  80E2 1F       AND DL,1F                             ; wert & 0x1f = buchstabe 3
0040587E  |.  8850 03       MOV BYTE PTR DS:[EAX+3],DL
00405881  |.  8BD1          MOV EDX,ECX
00405883  |.  C1EA 14       SHR EDX,14                            ; ergebnis * 2^0x14 = wert
00405886  |.  80E2 1F       AND DL,1F                             ; wert & 0x1f = buchstabe 2
00405889  |.  8850 02       MOV BYTE PTR DS:[EAX+2],DL
0040588C  |.  8BD1          MOV EDX,ECX
0040588E  |.  C1E9 1E       SHR ECX,1E                            ; ergebnis * 2^0x1E = wert1
00405891  |.  C1EA 19       SHR EDX,19                            ; ergebnis * 2^0x19 = wert2
00405894  |.  80E1 1F       AND CL,1F                             ; wert1 & 0x1f = buchstabe 0
00405897  |.  8808          MOV BYTE PTR DS:[EAX],CL
00405899  |.  80E2 1F       AND DL,1F                             ; wert2 & 0x1f = buchstabe 1
0040589C  |.  8850 01       MOV BYTE PTR DS:[EAX+1],DL
0040589F  |.  8A48 06       MOV CL,BYTE PTR DS:[EAX+6]            ; Buchstabe[0] | ((Buchstabe[6] & 0x07) << 0x02) = Buchstabe 0
004058A2  |.  8A10          MOV DL,BYTE PTR DS:[EAX]
004058A4  |.  80E1 07       AND CL,07
004058A7  |.  C0E1 02       SHL CL,2
004058AA  |.  0AD1          OR DL,CL
004058AC  |.  8810          MOV BYTE PTR DS:[EAX],DL
004058AE  |.  0FBED2        MOVSX EDX,DL
004058B1  |.  8A8A B8DC4200 MOV CL,BYTE PTR DS:[EDX+42DCB8]       ; Liste: 64382957JKLMNPQRSTUVWXYZABCDEFGH
004058B7  |.  0FBE50 01     MOVSX EDX,BYTE PTR DS:[EAX+1]
004058BB  |.  8808          MOV BYTE PTR DS:[EAX],CL
004058BD  |.  8A8A B8DC4200 MOV CL,BYTE PTR DS:[EDX+42DCB8]
004058C3  |.  0FBE50 02     MOVSX EDX,BYTE PTR DS:[EAX+2]
004058C7  |.  8848 01       MOV BYTE PTR DS:[EAX+1],CL
004058CA  |.  8A8A B8DC4200 MOV CL,BYTE PTR DS:[EDX+42DCB8]
004058D0  |.  0FBE50 03     MOVSX EDX,BYTE PTR DS:[EAX+3]
004058D4  |.  8848 02       MOV BYTE PTR DS:[EAX+2],CL
004058D7  |.  8A8A B8DC4200 MOV CL,BYTE PTR DS:[EDX+42DCB8]
004058DD  |.  0FBE50 04     MOVSX EDX,BYTE PTR DS:[EAX+4]
004058E1  |.  8848 03       MOV BYTE PTR DS:[EAX+3],CL
004058E4  |.  8A8A B8DC4200 MOV CL,BYTE PTR DS:[EDX+42DCB8]
004058EA  |.  0FBE50 05     MOVSX EDX,BYTE PTR DS:[EAX+5]
004058EE  |.  8848 04       MOV BYTE PTR DS:[EAX+4],CL
004058F1  |.  8A8A B8DC4200 MOV CL,BYTE PTR DS:[EDX+42DCB8]
004058F7  |.  0FBE50 06     MOVSX EDX,BYTE PTR DS:[EAX+6]
004058FB  |.  8848 05       MOV BYTE PTR DS:[EAX+5],CL
004058FE  |.  8A8A B8DC4200 MOV CL,BYTE PTR DS:[EDX+42DCB8]
00405904  |.  8848 06       MOV BYTE PTR DS:[EAX+6],CL
00405907  |.  C640 07 00    MOV BYTE PTR DS:[EAX+7],0
0040590B  \.  C3            RETN 

Aus unserem neuen Ergebnis werden nun verschiedene Indizes gebildet. Dieser Index wird dann zwischengespeichert und am Ende wird der Buchstabe dann aus einer Liste gelesen. Wenn Buchstabe 6 den Index 1 hat bekommen wir als ASCII Zeichen die „6“ zurückgegeben. Der neue String der jetzt gebildet wird, ist 7 Zeichen lang. Es ist genau das Teil was uns gefehlt hat. Im nächsten Schritt wird nun der 13 stellige Serial mit diesem neuen siebenstelligen Serial verknüpft.

 CPU Disasm Address   Hex dump          Command           Comments
00405A80  /$  83EC 40       SUB ESP,40
00405A83  |.  53            PUSH EBX
00405A84  |.  55            PUSH EBP
00405A85  |.  56            PUSH ESI
00405A86  |.  8B7424 54     MOV ESI,DWORD PTR SS:[ARG.2]
00405A8A  |.  57            PUSH EDI
00405A8B  |.  6A 0D         PUSH 0D                            ; von 0x00 bis 0x0d serial uebernehmen
00405A8D  |.  56            PUSH ESI
00405A8E  |.  E8 EDFCFFFF   CALL 00405780                      ; rechnerei kram
00405A93  |.  8B5C24 64     MOV EBX,DWORD PTR SS:[ARG.3]       ; 0x1d8df
00405A97  |.  33C3          XOR EAX,EBX                        ; ergebnis xor 0x111
00405A99  |.  50            PUSH EAX
00405A9A  |.  8D4424 3C     LEA EAX,[LOCAL.7]
00405A9E  |.  50            PUSH EAX
00405A9F  |.  E8 ACFDFFFF   CALL 00405850                      ; String aus Rechnung generieren
00405AA4  |.  8D6C24 20     LEA EBP,[LOCAL.15]
00405AA8  |.  83C4 10       ADD ESP,10
00405AAB  |.  8BC6          MOV EAX,ESI
00405AAD  |.  2BEE          SUB EBP,ESI
00405AAF  |.  90            NOP                                ; strings zusammenfügen
00405AB0  |>  8A08          /MOV CL,BYTE PTR DS:[EAX]
00405AB2  |.  880C28        |MOV BYTE PTR DS:[EBP+EAX],CL
00405AB5  |.  40            |INC EAX
00405AB6  |.  84C9          |TEST CL,CL
00405AB8  |.^ 75 F6         \JNE SHORT 00405AB0
00405ABA  |.  8D4424 30     LEA EAX,[LOCAL.7]
00405ABE  |.  8BF0          MOV ESI,EAX
00405AC0  |>  8A08          /MOV CL,BYTE PTR DS:[EAX]
00405AC2  |.  40            |INC EAX
00405AC3  |.  84C9          |TEST CL,CL
00405AC5  |.^ 75 F9         \JNE SHORT 00405AC0
00405AC7  |.  8D7C24 10     LEA EDI,[LOCAL.15]
00405ACB  |.  2BC6          SUB EAX,ESI
00405ACD  |.  4F            DEC EDI
00405ACE  |.  8BFF          MOV EDI,EDI
00405AD0  |>  8A4F 01       /MOV CL,BYTE PTR DS:[EDI+1]
00405AD3  |.  47            |INC EDI
00405AD4  |.  84C9          |TEST CL,CL
00405AD6  |.^ 75 F8         \JNE SHORT 00405AD0
00405AD8  |.  8BC8          MOV ECX,EAX

An dem jetzigen Zeitpunkt habe ich schon die Programmierung des Keygens begonnen. Erst im Laufe der Programmierung sind mir bestimmte Funktionen klarer geworden und erst dann Verstand ich den Sinn dahinter. Deswegen sind die nächsten Funktionen auch mit besseren Kommentaren ausgestattet. Der Source Code für den Keygen folgt wie immer zum Schluss.

Schlag doch in der Tabelle nach

Jetzt kommt schon die spannenste Stelle im Serialteil. Hier wird wieder gerechnet was das Zeug hält und es kommt wieder ein Index raus. Diesmal steht im Arbeitsspeicher eine Art Tabelle mit vorgefertigten Ergebnissen, die dann nach und nach ausgelesen werden.

CPU Disasm
Address   Hex dump          Command                                          Comments
00405AD8  |.  8BC8          MOV ECX,EAX
00405ADA  |.  C1E9 02       SHR ECX,2
00405ADD  |.  F3:A5         REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
00405ADF  |.  8BC8          MOV ECX,EAX
00405AE1  |.  83E1 03       AND ECX,00000003
00405AE4  |.  F3:A4         REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00405AE6  |.  8BC3          MOV EAX,EBX                                      ; v = 0x0001D8DF
00405AE8  |.  33C9          XOR ECX,ECX
00405AEA  |.  8D9B 00000000 LEA EBX,[EBX]                                    ; Neue Rechnung ueber Serial (array[19])
00405AF0  |>  33D2          /XOR EDX,EDX
00405AF2  |.  8A540C 10     |MOV DL,BYTE PTR SS:[ECX+ESP+10]                 ; Buchstabe[i*5] auslesen
00405AF6  |.  83C1 05       |ADD ECX,5                                       ; schleifen zaehler
00405AF9  |.  33D0          |XOR EDX,EAX
00405AFB  |.  81E2 FF000000 |AND EDX,000000FF                                ;(buchstabe-ascii-wert ^ v) & 0xFF = index
00405B01  |.  8B1C95 B8FA42 |MOV EBX,DWORD PTR DS:[EDX*4+42FAB8]             ; Tabelle[index] = tablewert
00405B08  |.  33D2          |XOR EDX,EDX
00405B0A  |.  8A540C 0C     |MOV DL,BYTE PTR SS:[ECX+ESP+0C]                 ; Buchstabe[i*5 + 1] auslesen
00405B0E  |.  C1E8 08       |SHR EAX,8                                       ; v= (v >> 8) ^ tablewert
00405B11  |.  33C3          |XOR EAX,EBX
00405B13  |.  33D0          |XOR EDX,EAX                                     ;(buchstabe ^ v) & 0xff = index
00405B15  |.  81E2 FF000000 |AND EDX,000000FF
00405B1B  |.  8B3C95 B8FA42 |MOV EDI,DWORD PTR DS:[EDX*4+42FAB8]             ; Tabelle[index] = tablewert
00405B22  |.  33D2          |XOR EDX,EDX
00405B24  |.  8A540C 0D     |MOV DL,BYTE PTR SS:[ECX+ESP+0D]                 ; Buchstabe[i*5 + 2] auslesen
00405B28  |.  C1E8 08       |SHR EAX,8
00405B2B  |.  33C7          |XOR EAX,EDI                                     ; v= (v >> 8) ^ tablewert
00405B2D  |.  33D0          |XOR EDX,EAX
00405B2F  |.  81E2 FF000000 |AND EDX,000000FF                                ;(buchstabe ^ v) & 0xff = index
00405B35  |.  8B3495 B8FA42 |MOV ESI,DWORD PTR DS:[EDX*4+42FAB8]             ; Tabelle[index]
00405B3C  |.  33D2          |XOR EDX,EDX
00405B3E  |.  8A540C 0E     |MOV DL,BYTE PTR SS:[ECX+ESP+0E]                 ; Buchstabe[i*5 + 3] auslesen
00405B42  |.  C1E8 08       |SHR EAX,8
00405B45  |.  33C6          |XOR EAX,ESI                                     ;v = (v >> 8) ^ tablewert
00405B47  |.  33D0          |XOR EDX,EAX
00405B49  |.  81E2 FF000000 |AND EDX,000000FF                                ;(buchstabe ^ v) & 0xff = index
00405B4F  |.  8B1C95 B8FA42 |MOV EBX,DWORD PTR DS:[EDX*4+42FAB8]             ; Tabelle[index]
00405B56  |.  33D2          |XOR EDX,EDX
00405B58  |.  8A540C 0F     |MOV DL,BYTE PTR SS:[ECX+ESP+0F]                 ; Buchstabe[i*5 + 4] auslesen
00405B5C  |.  C1E8 08       |SHR EAX,8
00405B5F  |.  33C3          |XOR EAX,EBX                                     ; wieder wie oben v = ... dann buchstabe ^ v ...
00405B61  |.  33D0          |XOR EDX,EAX
00405B63  |.  81E2 FF000000 |AND EDX,000000FF
00405B69  |.  8B3C95 B8FA42 |MOV EDI,DWORD PTR DS:[EDX*4+42FAB8]             ; Tabelle[index]
00405B70  |.  C1E8 08       |SHR EAX,8
00405B73  |.  33C7          |XOR EAX,EDI                                     ; v= (v >> 8) ^ tablewert
00405B75  |.  83F9 14       |CMP ECX,14
00405B78  |.^ 0F8C 72FFFFFF \JL 00405AF0
00405B7E  |.  50            PUSH EAX                                         ; Endergebnis = v
00405B7F  |.  8D4424 34     LEA EAX,[LOCAL.7]                                ; rechnungsstring
00405B83  |.  50            PUSH EAX
00405B84  |.  E8 87FDFFFF   CALL 00405910                                    ;nächste Funktion :)
00405B89  |.  8B4424 60     MOV EAX,DWORD PTR SS:[ARG.2]

EA hat ja echt nen Rad ab. Die haben doch tatsächlich so eine riesen Ergebnistabelle in den Arbeitsspeicher gefeuert. Wie soll ich das denn nachprogrammieren. Ich weiß was EA wollte. Faule Leute die es nachprogrammieren wollen, werden vielleicht so abgeschreckt. Ein Glück kann man im Arbeitspeicher-Bereich in OllyDbg mit einem Rechtklick die Werte ändern. Von nun an wird mir der Arbeitsspeicher in „Integer –> long Hex“ angezeigt. Vorteil, ich kann auch schnell einen Dump machen und es relativ schnell in meinen Source Code einpflegen. Aus der Tabelle werden nur die ersten 255 Einträge gebraucht, das hängt mit der bitweisen AND Operation zusammen. Hier könnt ihr euch die Werte Tabelle anschauen:

CPU Dump
Address   32-bit hex dump
0042FAB8       00000000      77073096      EE0E612C      990951BA
0042FAC8       076DC419      706AF48F      E963A535      9E6495A3
0042FAD8       0EDB8832      79DCB8A4      E0D5E91E      97D2D988
0042FAE8       09B64C2B      7EB17CBD      E7B82D07      90BF1D91
0042FAF8       1DB71064      6AB020F2      F3B97148      84BE41DE
0042FB08       1ADAD47D      6DDDE4EB      F4D4B551      83D385C7
0042FB18       136C9856      646BA8C0      FD62F97A      8A65C9EC
0042FB28       14015C4F      63066CD9      FA0F3D63      8D080DF5
0042FB38       3B6E20C8      4C69105E      D56041E4      A2677172
0042FB48       3C03E4D1      4B04D447      D20D85FD      A50AB56B
0042FB58       35B5A8FA      42B2986C      DBBBC9D6      ACBCF940
0042FB68       32D86CE3      45DF5C75      DCD60DCF      ABD13D59
0042FB78       26D930AC      51DE003A      C8D75180      BFD06116
0042FB88       21B4F4B5      56B3C423      CFBA9599      B8BDA50F
0042FB98       2802B89E      5F058808      C60CD9B2      B10BE924
0042FBA8       2F6F7C87      58684C11      C1611DAB      B6662D3D
0042FBB8       76DC4190      01DB7106      98D220BC      EFD5102A
0042FBC8       71B18589      06B6B51F      9FBFE4A5      E8B8D433
0042FBD8       7807C9A2      0F00F934      9609A88E      E10E9818
0042FBE8       7F6A0DBB      086D3D2D      91646C97      E6635C01
0042FBF8       6B6B51F4      1C6C6162      856530D8      F262004E
0042FC08       6C0695ED      1B01A57B      8208F4C1      F50FC457
0042FC18       65B0D9C6      12B7E950      8BBEB8EA      FCB9887C
0042FC28       62DD1DDF      15DA2D49      8CD37CF3      FBD44C65
0042FC38       4DB26158      3AB551CE      A3BC0074      D4BB30E2
0042FC48       4ADFA541      3DD895D7      A4D1C46D      D3D6F4FB
0042FC58       4369E96A      346ED9FC      AD678846      DA60B8D0
0042FC68       44042D73      33031DE5      AA0A4C5F      DD0D7CC9
0042FC78       5005713C      270241AA      BE0B1010      C90C2086
0042FC88       5768B525      206F85B3      B966D409      CE61E49F
0042FC98       5EDEF90E      29D9C998      B0D09822      C7D7A8B4
0042FCA8       59B33D17      2EB40D81      B7BD5C3B      C0BA6CAD
0042FCB8       EDB88320      9ABFB3B6      03B6E20C      74B1D29A
0042FCC8       EAD54739      9DD277AF      04DB2615      73DC1683
0042FCD8       E3630B12      94643B84      0D6D6A3E      7A6A5AA8
0042FCE8       E40ECF0B      9309FF9D      0A00AE27      7D079EB1
0042FCF8       F00F9344      8708A3D2      1E01F268      6906C2FE
0042FD08       F762575D      806567CB      196C3671      6E6B06E7
0042FD18       FED41B76      89D32BE0      10DA7A5A      67DD4ACC
0042FD28       F9B9DF6F      8EBEEFF9      17B7BE43      60B08ED5
0042FD38       D6D6A3E8      A1D1937E      38D8C2C4      4FDFF252
0042FD48       D1BB67F1      A6BC5767      3FB506DD      48B2364B
0042FD58       D80D2BDA      AF0A1B4C      36034AF6      41047A60
0042FD68       DF60EFC3      A867DF55      316E8EEF      4669BE79
0042FD78       CB61B38C      BC66831A      256FD2A0      5268E236
0042FD88       CC0C7795      BB0B4703      220216B9      5505262F
0042FD98       C5BA3BBE      B2BD0B28      2BB45A92      5CB36A04
0042FDA8       C2D7FFA7      B5D0CF31      2CD99E8B      5BDEAE1D
0042FDB8       9B64C2B0      EC63F226      756AA39C      026D930A
0042FDC8       9C0906A9      EB0E363F      72076785      05005713
0042FDD8       95BF4A82      E2B87A14      7BB12BAE      0CB61B38
0042FDE8       92D28E9B      E5D5BE0D      7CDCEFB7      0BDBDF21
0042FDF8       86D3D2D4      F1D4E242      68DDB3F8      1FDA836E
0042FE08       81BE16CD      F6B9265B      6FB077E1      18B74777
0042FE18       88085AE6      FF0F6A70      66063BCA      11010B5C
0042FE28       8F659EFF      F862AE69      616BFFD3      166CCF45
0042FE38       A00AE278      D70DD2EE      4E048354      3903B3C2
0042FE48       A7672661      D06016F7      4969474D      3E6E77DB
0042FE58       AED16A4A      D9D65ADC      40DF0B66      37D83BF0
0042FE68       A9BCAE53      DEBB9EC5      47B2CF7F      30B5FFE9
0042FE78       BDBDF21C      CABAC28A      53B39330      24B4A3A6
0042FE88       BAD03605      CDD70693      54DE5729      23D967BF
0042FE98       B3667A2E      C4614AB8      5D681B02      2A6F2B94
0042FEA8       B40BBE37      C30C8EA1      5A05DF1B      2D02EF8D
0042FEB8       00000000      00000000      00423084      00000000

Kalkuliertes Mischen

Kommen wir zur nächsten Funktion. Diesmal heißt es kalkuliertes Mischen, statt durchmischtes kalkulieren. Erst wird ein String gebildet/errechnet und dann wird gemischt. Kommen wir erst einmal zur Generierung des neuen Strings:

CPU Disasm
Address   Hex dump          Command                                 Comments
00405910  /$  8B4424 04     MOV EAX,DWORD PTR SS:[ARG.1]
00405914  |.  8B4C24 08     MOV ECX,DWORD PTR SS:[ARG.2]            ; Endergebnis
00405918  |.  8AD1          MOV DL,CL                               ; Kleine Register werden angesprochen --> ECX % 0x100 nach EDX kopieren
0040591A  |.  80E2 03       AND DL,03                               ; ((Endergebnis % 0x100) & 0x03) << 3 = Buchstabe 6
0040591D  |.  C0E2 03       SHL DL,3
00405920  |.  8850 06       MOV BYTE PTR DS:[EAX+6],DL
00405923  |.  C1E9 02       SHR ECX,2                               ; Endergebnis = Endergebnis >> 2
00405926  |.  8AD1          MOV DL,CL                               ; (Endergebnis % 0x100) & 0x1f = Buchstabe 5
00405928  |.  80E2 1F       AND DL,1F
0040592B  |.  8850 05       MOV BYTE PTR DS:[EAX+5],DL
0040592E  |.  8BD1          MOV EDX,ECX
00405930  |.  C1EA 05       SHR EDX,5                               ; (Endergebnis >> 5) & 0x1f = Buchstabe 4
00405933  |.  80E2 1F       AND DL,1F
00405936  |.  8850 04       MOV BYTE PTR DS:[EAX+4],DL
00405939  |.  8BD1          MOV EDX,ECX
0040593B  |.  C1EA 0A       SHR EDX,0A                              ; (Endergebnis >> 0x0A) & 0x1f = Buchstabe 3
0040593E  |.  80E2 1F       AND DL,1F
00405941  |.  8850 03       MOV BYTE PTR DS:[EAX+3],DL
00405944  |.  8BD1          MOV EDX,ECX
00405946  |.  C1EA 0F       SHR EDX,0F                              ; (Endergebnis >> 0x0F) & 0x1f = Buchstabe 2
00405949  |.  80E2 1F       AND DL,1F
0040594C  |.  8850 02       MOV BYTE PTR DS:[EAX+2],DL
0040594F  |.  8BD1          MOV EDX,ECX
00405951  |.  C1EA 14       SHR EDX,14                              ; (Endergebnis >> 0x14) & 0x1f = Buchstabe 1
00405954  |.  C1E9 19       SHR ECX,19                              ; (Endergebnis >> 0x19) & 0x1f = Buchstabe 0
00405957  |.  80E2 1F       AND DL,1F
0040595A  |.  8850 01       MOV BYTE PTR DS:[EAX+1],DL
0040595D  |.  80E1 1F       AND CL,1F
00405960  |.  8808          MOV BYTE PTR DS:[EAX],CL
00405962  |.  8A50 06       MOV DL,BYTE PTR DS:[EAX+6]
00405965  |.  80E1 07       AND CL,07                               ; Buchstabe[6] | (Buchstabe[0] & 0x07) = Buchstabe 6
00405968  |.  0AD1          OR DL,CL
0040596A  |.  8850 06       MOV BYTE PTR DS:[EAX+6],DL
0040596D  |.  0FBE10        MOVSX EDX,BYTE PTR DS:[EAX]
00405970  |.  8A8A 98DC4200 MOV CL,BYTE PTR DS:[EDX+42DC98]         ; ASCII 32,"3456789ABCDEFGHJKLMNPQRSTUVWXYZ64382957JKLMNPQRSTU"
00405976  |.  0FBE50 01     MOVSX EDX,BYTE PTR DS:[EAX+1]
0040597A  |.  8808          MOV BYTE PTR DS:[EAX],CL
0040597C  |.  8A8A 98DC4200 MOV CL,BYTE PTR DS:[EDX+42DC98]         ; ASCII 32,"3456789ABCDEFGHJKLMNPQRSTUVWXYZ64382957JKLMNPQRSTU"
00405982  |.  0FBE50 02     MOVSX EDX,BYTE PTR DS:[EAX+2]
00405986  |.  8848 01       MOV BYTE PTR DS:[EAX+1],CL
00405989  |.  8A8A 98DC4200 MOV CL,BYTE PTR DS:[EDX+42DC98]         ; ASCII 32,"3456789ABCDEFGHJKLMNPQRSTUVWXYZ64382957JKLMNPQRSTU"
0040598F  |.  0FBE50 03     MOVSX EDX,BYTE PTR DS:[EAX+3]
00405993  |.  8848 02       MOV BYTE PTR DS:[EAX+2],CL
00405996  |.  8A8A 98DC4200 MOV CL,BYTE PTR DS:[EDX+42DC98]         ; ASCII 32,"3456789ABCDEFGHJKLMNPQRSTUVWXYZ64382957JKLMNPQRSTU"
0040599C  |.  0FBE50 04     MOVSX EDX,BYTE PTR DS:[EAX+4]
004059A0  |.  8848 03       MOV BYTE PTR DS:[EAX+3],CL
004059A3  |.  8A8A 98DC4200 MOV CL,BYTE PTR DS:[EDX+42DC98]         ; ASCII 32,"3456789ABCDEFGHJKLMNPQRSTUVWXYZ64382957JKLMNPQRSTU"
004059A9  |.  0FBE50 05     MOVSX EDX,BYTE PTR DS:[EAX+5]
004059AD  |.  8848 04       MOV BYTE PTR DS:[EAX+4],CL
004059B0  |.  8A8A 98DC4200 MOV CL,BYTE PTR DS:[EDX+42DC98]         ; ASCII 32,"3456789ABCDEFGHJKLMNPQRSTUVWXYZ64382957JKLMNPQRSTU"
004059B6  |.  0FBE50 06     MOVSX EDX,BYTE PTR DS:[EAX+6]
004059BA  |.  8848 05       MOV BYTE PTR DS:[EAX+5],CL
004059BD  |.  8A8A 98DC4200 MOV CL,BYTE PTR DS:[EDX+42DC98]         ; ASCII 32,"3456789ABCDEFGHJKLMNPQRSTUVWXYZ64382957JKLMNPQRSTU"
004059C3  |.  8848 06       MOV BYTE PTR DS:[EAX+6],CL
004059C6  |.  C640 07 00    MOV BYTE PTR DS:[EAX+7],0
004059CA  \.  C3            RETN

Diese Funktion ist ein wenig anders als unsere erste String Generierungsfunktion. Auf den ersten Blick sieht sie genauso aus, bei genauerer Betrachtung erkennt man aber wesentliche Unterschiede. Zum einen werden am Anfang die ersten Indizes anders berechnet und zum anderen wird ganz am Ende der Index für den Buchstaben 6 berechnet, statt für den Buchstaben 0. Das nächste was auffällt ist, das wir eine andere Liste mit Buchstaben zur Verfügung haben. Wenn man beide Funktionen in einen Zusammenhang sieht, kann man die erste Funktion als Verschlüsselungsfunktion ansehen und die jetzige Funktion als Entschlüsselungsfunktion, aber kommen wir nun zum letzten Schritt des Serial. Lasst uns die Karten… Ich meine: Lasst uns die Buchstaben neu mischen!

CPU Disasm
Address   Hex dump          Command                                          Comments
00405B7E  |.  50            PUSH EAX
00405B7F  |.  8D4424 34     LEA EAX,[LOCAL.7]                                ; Berechne neuen String (Entschlüsselung)
00405B83  |.  50            PUSH EAX
00405B84  |.  E8 87FDFFFF   CALL 00405910                                    ; String neu Berechnen
00405B89  |.  8B4424 60     MOV EAX,DWORD PTR SS:[ARG.2]                     ; Strings zusammen matschen :D
00405B8D  |.  83C4 08       ADD ESP,8
00405B90  |>  8A08          /MOV CL,BYTE PTR DS:[EAX]
00405B92  |.  880C28        |MOV BYTE PTR DS:[EBP+EAX],CL
00405B95  |.  40            |INC EAX
00405B96  |.  84C9          |TEST CL,CL
00405B98  |.^ 75 F6         \JNE SHORT 00405B90
00405B9A  |.  8D4424 30     LEA EAX,[LOCAL.7]
00405B9E  |.  8BD0          MOV EDX,EAX
00405BA0  |>  8A08          /MOV CL,BYTE PTR DS:[EAX]
00405BA2  |.  40            |INC EAX
00405BA3  |.  84C9          |TEST CL,CL
00405BA5  |.^ 75 F9         \JNE SHORT 00405BA0
00405BA7  |.  8D7C24 10     LEA EDI,[LOCAL.15]
00405BAB  |.  2BC2          SUB EAX,EDX
00405BAD  |.  4F            DEC EDI
00405BAE  |.  8BFF          MOV EDI,EDI
00405BB0  |>  8A4F 01       /MOV CL,BYTE PTR DS:[EDI+1]
00405BB3  |.  47            |INC EDI
00405BB4  |.  84C9          |TEST CL,CL
00405BB6  |.^ 75 F8         \JNE SHORT 00405BB0
00405BB8  |.  8BC8          MOV ECX,EAX
00405BBA  |.  C1E9 02       SHR ECX,2
00405BBD  |.  8BF2          MOV ESI,EDX
00405BBF  |.  8B5424 54     MOV EDX,DWORD PTR SS:[ARG.1]
00405BC3  |.  F3:A5         REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
00405BC5  |.  8BC8          MOV ECX,EAX
00405BC7  |.  83E1 03       AND ECX,00000003
00405BCA  |.  F3:A4         REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00405BCC  |.  8D4C24 10     LEA ECX,[LOCAL.15]
00405BD0  |.  51            PUSH ECX
00405BD1  |.  52            PUSH EDX
00405BD2  |.  E8 F9FDFFFF   CALL 004059D0                                    ; Misch noch mal
00405BD7  |.  83C4 08       ADD ESP,8
00405BDA  |.  5F            POP EDI
00405BDB  |.  5E            POP ESI
00405BDC  |.  5D            POP EBP
00405BDD  |.  5B            POP EBX
00405BDE  |.  83C4 40       ADD ESP,40
00405BE1  \.  C3            RETN

Es ist wieder selbige Mischfunktion, wie zum Anfang. Dadurch werden auch die sieben Buchstaben die Ursprünglich dort standen, wieder auf ihren alten Platz gesetzt.

CPU Disasm
Address   Hex dump          Command                                          Comments
004059D0  /$  8B4424 04     MOV EAX,DWORD PTR SS:[ARG.1]                     ; z'B --> Variable? --> Mischfunktion
004059D4  |.  8B4C24 08     MOV ECX,DWORD PTR SS:[ARG.2]                     ; Serial
004059D8  |.  53            PUSH EBX
004059D9  |.  56            PUSH ESI
004059DA  |.  8BF0          MOV ESI,EAX
004059DC  |.  2BF1          SUB ESI,ECX
004059DE  |.  8BFF          MOV EDI,EDI                                      ; einlesen des serial
004059E0  |>  8A11          /MOV DL,BYTE PTR DS:[ECX]
004059E2  |.  88140E        |MOV BYTE PTR DS:[ECX+ESI],DL
004059E5  |.  41            |INC ECX
004059E6  |.  84D2          |TEST DL,DL
004059E8  |.^ 75 F6         \JNE SHORT 004059E0
004059EA  |.  0FBE0D D8DC42 MOVSX ECX,BYTE PTR DS:[42DCD8]                   ; tausche array [2] mit [13]
004059F1  |.  8A1401        MOV DL,BYTE PTR DS:[EAX+ECX]
004059F4  |.  8A58 0D       MOV BL,BYTE PTR DS:[EAX+0D]
004059F7  |.  881C01        MOV BYTE PTR DS:[EAX+ECX],BL
004059FA  |.  8A58 0E       MOV BL,BYTE PTR DS:[EAX+0E]
004059FD  |.  8850 0D       MOV BYTE PTR DS:[EAX+0D],DL
00405A00  |.  0FBE15 D9DC42 MOVSX EDX,BYTE PTR DS:[42DCD9]                   ; neuen startwert einlesen fuer das array
00405A07  |.  03C8          ADD ECX,EAX                                      ; in diesem falle 4
00405A09  |.  8D0C02        LEA ECX,[EAX+EDX]
00405A0C  |.  8A11          MOV DL,BYTE PTR DS:[ECX]                         ; tausche [4] mit [14]
00405A0E  |.  8819          MOV BYTE PTR DS:[ECX],BL
00405A10  |.  8A58 0F       MOV BL,BYTE PTR DS:[EAX+0F]
00405A13  |.  8850 0E       MOV BYTE PTR DS:[EAX+0E],DL
00405A16  |.  0FBE0D DADC42 MOVSX ECX,BYTE PTR DS:[42DCDA]                   ; tausche [5] mit [15] (next please :D)
00405A1D  |.  8A1401        MOV DL,BYTE PTR DS:[EAX+ECX]
00405A20  |.  881C01        MOV BYTE PTR DS:[EAX+ECX],BL
00405A23  |.  8A58 10       MOV BL,BYTE PTR DS:[EAX+10]
00405A26  |.  8850 0F       MOV BYTE PTR DS:[EAX+0F],DL
00405A29  |.  0FBE15 DBDC42 MOVSX EDX,BYTE PTR DS:[42DCDB]                   ; neuer startwert = 7
00405A30  |.  03C8          ADD ECX,EAX
00405A32  |.  8D0C02        LEA ECX,[EAX+EDX]
00405A35  |.  8A11          MOV DL,BYTE PTR DS:[ECX]                         ; tausche [7] mit [16]
00405A37  |.  8819          MOV BYTE PTR DS:[ECX],BL
00405A39  |.  8A58 11       MOV BL,BYTE PTR DS:[EAX+11]
00405A3C  |.  8850 10       MOV BYTE PTR DS:[EAX+10],DL
00405A3F  |.  0FBE0D DCDC42 MOVSX ECX,BYTE PTR DS:[42DCDC]                   ; startwert = 12
00405A46  |.  8A1401        MOV DL,BYTE PTR DS:[EAX+ECX]                     ; tausche [12] mit [17]
00405A49  |.  881C01        MOV BYTE PTR DS:[EAX+ECX],BL
00405A4C  |.  8A58 12       MOV BL,BYTE PTR DS:[EAX+12]
00405A4F  |.  03C8          ADD ECX,EAX
00405A51  |.  8850 11       MOV BYTE PTR DS:[EAX+11],DL
00405A54  |.  0FBE15 DDDC42 MOVSX EDX,BYTE PTR DS:[42DCDD]                   ; startwert = 1
00405A5B  |.  8D0C02        LEA ECX,[EAX+EDX]
00405A5E  |.  8A11          MOV DL,BYTE PTR DS:[ECX]                         ; tausche [1] mit [18]
00405A60  |.  8819          MOV BYTE PTR DS:[ECX],BL
00405A62  |.  8A58 13       MOV BL,BYTE PTR DS:[EAX+13]
00405A65  |.  8850 12       MOV BYTE PTR DS:[EAX+12],DL
00405A68  |.  0FBE0D DEDC42 MOVSX ECX,BYTE PTR DS:[42DCDE]                   ; startwert = 3
00405A6F  |.  8A1401        MOV DL,BYTE PTR DS:[EAX+ECX]                     ; tausche [3] mit dem letzten ([19])
00405A72  |.  03C8          ADD ECX,EAX
00405A74  |.  8819          MOV BYTE PTR DS:[ECX],BL
00405A76  |.  5E            POP ESI
00405A77  |.  8850 13       MOV BYTE PTR DS:[EAX+13],DL
00405A7A  |.  C640 14 00    MOV BYTE PTR DS:[EAX+14],0
00405A7E  |.  5B            POP EBX
00405A7F  \.  C3            RETN

Der String der jetzt heraus kommt, ist unser Serial, den wir eingeben müssten, damit das Spiel es als richtigen Serial erkennt. Im letzten Schritt muss man nun noch diese ganzen Erkenntnisse in Code verwandeln und damit hätten wir einen super KeyGen.

Zusammenfassung und Fazit

Noch einmal kurz zusammengefasst. Der Serialprüfer liest unseren 20 stelligen Serial aus. Dieser wird dann erst einmal gemischt, sodass wir einen neuen Serial bekommen. Von diesem Serial werden die ersten 13 Stellen genommen und es wird aus diesen ein bestimmter Wert errechnet. Dieser Wert wird dann durch eine Funktion gejagt, die uns einen 7 stelligen String ausgibt. Nun werden beide Strings zusammengefügt (XXXXXXXXXXXXXYYYYYYY) und dieser neue große Serial wird wieder durch eine Funktion getrieben, die dann ein bestimmtes Ergebnis am Schluss anhand einer Rechnungstabelle uns zurückgibt. Dieses neue Ergebnis wird wieder in eine Art String Generator gegeben, dieser gibt uns wieder einen 7 stelligen String zurück. Dieser wird wieder mit unserem alten 13 stelligen Teilserial zusammengefügt (XXXXXXXXXXXXXYYYYYYY). Zum Schluss werden wieder die Buchstaben wieder getauscht, sodass die letzten 7 Stellen den Ursprunglichen Wert wieder inne haben.

Fazit: Es war zuerst ernüchternd als ich feststellte, dass man schon aus den Registern den Serial ablesen kann. Das ist dann ein sehr niedriger Schutz. Ein weiterer Nachteil eines solchen Serialprüfers ist, dass er als Self-Keygen benutzt werden kann. Sprich man schreibt, statt die Strings zu vergleichen, ihn genau an die Stelle des Arbeitsspeichers, wo auch der selbst eingegeben Serial ist. Außerdem kann man den ASM Code schnell kopieren und selbst in sein eigenen ASM-Keygen einfügen. Das hätte mir eine Menge Arbeit erspart, aber ich wüsste dann immer noch nicht wie dieser Prüfer funktioniert. Mir hat es mal wieder Spaß gemacht unter die Haube zu schauen und den ASM Code in C# zu exportieren. Außerdem habe ich neu die Shift Left (SHL) und Shift Right (SHR) Befehle gelernt, und das sie sehr schnell sind. Wenn man eine Multiplikation (MUL) verwendet ist diese im Worst-Case bis zu fünfmal langsamer, im Best-Case ist sie zehnmal schneller. Wer sich mehr für diese Performance Programmierung interessiert sollte einen Blick auf Seite 3 dieses Dokumentes werfen: http://gmplib.org/~tege/x86-timing.pdf Dort sind alle Timings aufgeführt und der kann MUL mit SHL bzw. SHR vergleichen.

Ich hoffe es hat euch auch wieder Spaß gemacht meinen Artikel zulesen. Wir sehen uns beim nächsten Spiel oder Programm. Ich hatte wieder eine Menge Spaß.

Greetz TheVamp

Und nun zum krönenden Abschluss der Source Code des KeyGen:

namespace NFSU2_Keygen
{
public partial class Form1 : Form
{

public Form1()
{
InitializeComponent();
}

Random r = new Random();

private void btn_generate_Click(object sender, EventArgs e)
{
//0-9 und A-Z Randomserial eingabe simulieren
string random = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char[] serial = new char[20];
for (int i = 0; i < 20; i++)
{
serial[i] = random[r.Next(0, random.Length)];
}

char[] mixserial = mixing(serial); //mischen
uint mixergebnis = RechnerEi(mixserial); //kalkulieren
mixergebnis = mixergebnis ^ 0x1D8DF;
string mix_erg_serial = string_rechnung_gen(mixergebnis); //string generieren

for (int i = 0; i < mix_erg_serial.Length; i++) //String zusammenfügen
{
mixserial[i + 13] = mix_erg_serial[i];
}

mixergebnis = rechner_kalk(mixserial); //kalkulieren
mix_erg_serial = string_rechnung_gen2(mixergebnis); //dann string holen

for (int i = 0; i < mix_erg_serial.Length; i++)//wieder zusammen matschen :D
{
mixserial[i + 13] = mix_erg_serial[i];
}

mixserial = mixing(mixserial); //und zum schluss mixen

txt_key1.Text = "";
txt_key2.Text = "";
txt_key3.Text = "";
txt_key4.Text = "";
txt_key5.Text = "";

for (int i = 0; i < 4; i++)
{
txt_key1.Text += mixserial[i];
txt_key2.Text += mixserial[i+4];
txt_key3.Text += mixserial[i+8];
txt_key4.Text += mixserial[i+12];
txt_key5.Text += mixserial[i+16];

}
}

//Tauschfunktion
private char[] mixing(char[] serial)
{
char buff = serial[2];
serial[2] = serial[13];
serial[13] = buff;

buff = serial[4];
serial[4] = serial[14];
serial[14] = buff;

buff = serial[5];
serial[5] = serial[15];
serial[15] = buff;

buff = serial[7];
serial[7] = serial[16];
serial[16] = buff;

buff = serial[12];
serial[12] = serial[17];
serial[17] = buff;

buff = serial[1];
serial[1] = serial[18];
serial[18] = buff;

buff = serial[3];
serial[3] = serial[19];
serial[19] = buff;

return serial;
}

//Rechnung bringt ein Ergebnis das in den Generator kommt und die 7 stelligen String ausliest
private uint RechnerEi(char[] mixserial)
{
uint buff = 0;
uint vorrestwert = 1;
uint vorwert = 0;

for (int i = 0; i < 13; i++)
{
vorwert = mixserial[i] + vorrestwert;
vorrestwert = vorwert % 0xfff1;
uint sum_reste = vorrestwert + buff;
buff = sum_reste % 0xfff1;

}

uint ergebnis = buff<<0x10; // SHL (shift left) in c#, COOL!!!
ergebnis += vorwert;

return ergebnis;
}

//Rechnung 1 Verschlüsselungsfunktion --> generiert siebenstelligen String
private string string_rechnung_gen(uint ergebnis)
{
string Serial_gen = "64382957JKLMNPQRSTUVWXYZABCDEFGH";
int[] buff = new int[7];
buff[6] = (int) ergebnis & 0x1f;
buff[5] = (int)(ergebnis >> 0x5) & 0x1f;
buff[4] = (int)(ergebnis >> 0x0A) & 0x1f;
buff[3] = (int)(ergebnis >> 0x0F) & 0x1f;
buff[2] = (int)(ergebnis >> 0x14) & 0x1f;
buff[1] = (int)(ergebnis >> 0x19) & 0x1f;
buff[0] = (int)(ergebnis >> 0x1E) & 0x1f;
buff[0] = ((buff[6] & 0x07) << 0x2);

return Serial_gen[buff[0]].ToString() + Serial_gen[buff[1]] + Serial_gen[buff[2]] + Serial_gen[buff[3]] + Serial_gen[buff[4]] + Serial_gen[buff[5]] + Serial_gen[buff[6]];
}

//Rechnung 2 Entschlüsselungsfuntkion --> generiert siebenstelligen String
private string string_rechnung_gen2(uint ergebnis)
{
string Serial_gen = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ64382957JKLMNPQRSTU"; // zu lang geht aber sowieso nur bis 0x1f :P
int[] buff = new int[7];
buff[6] = (int) ((ergebnis % 0x100) & 0x03) << 3;
ergebnis = ergebnis >> 0x2;
buff[5] = (int)(ergebnis % 0x100) & 0x1f;
buff[4] = (int)(ergebnis >> 0x05) & 0x1f;
buff[3] = (int)(ergebnis >> 0x0A) & 0x1f;
buff[2] = (int)(ergebnis >> 0x0F) & 0x1f;
buff[1] = (int)(ergebnis >> 0x14) & 0x1f;
buff[0] = (int)(ergebnis >> 0x19) & 0x1f;
buff[6] = (buff[6] | (buff[0] & 0x07) );

return Serial_gen[buff[0]].ToString() + Serial_gen[buff[1]] + Serial_gen[buff[2]] + Serial_gen[buff[3]] + Serial_gen[buff[4]] + Serial_gen[buff[5]] + Serial_gen[buff[6]];
}

uint[] rechnungstabelle = {
0x00000000,     0x77073096,     0xEE0E612C,     0x990951BA,
0x076DC419,     0x706AF48F,     0xE963A535,     0x9E6495A3,
0x0EDB8832,     0x79DCB8A4,     0xE0D5E91E,     0x97D2D988,
0x09B64C2B,     0x7EB17CBD,     0xE7B82D07,     0x90BF1D91,
0x1DB71064,     0x6AB020F2,     0xF3B97148,     0x84BE41DE,
0x1ADAD47D,     0x6DDDE4EB,     0xF4D4B551,     0x83D385C7,
0x136C9856,     0x646BA8C0,     0xFD62F97A,     0x8A65C9EC,
0x14015C4F,     0x63066CD9,     0xFA0F3D63,     0x8D080DF5,
0x3B6E20C8,     0x4C69105E,     0xD56041E4,     0xA2677172,
0x3C03E4D1,     0x4B04D447,     0xD20D85FD,     0xA50AB56B,
0x35B5A8FA,     0x42B2986C,     0xDBBBC9D6,     0xACBCF940,
0x32D86CE3,     0x45DF5C75,     0xDCD60DCF,     0xABD13D59,
0x26D930AC,     0x51DE003A,     0xC8D75180,     0xBFD06116,
0x21B4F4B5,     0x56B3C423,     0xCFBA9599,     0xB8BDA50F,
0x2802B89E,     0x5F058808,     0xC60CD9B2,     0xB10BE924,
0x2F6F7C87,     0x58684C11,     0xC1611DAB,     0xB6662D3D,
0x76DC4190,     0x01DB7106,     0x98D220BC,     0xEFD5102A,
0x71B18589,     0x06B6B51F,     0x9FBFE4A5,     0xE8B8D433,
0x7807C9A2,     0x0F00F934,     0x9609A88E,     0xE10E9818,
0x7F6A0DBB,     0x086D3D2D,     0x91646C97,     0xE6635C01,
0x6B6B51F4,     0x1C6C6162,     0x856530D8,     0xF262004E,
0x6C0695ED,     0x1B01A57B,     0x8208F4C1,     0xF50FC457,
0x65B0D9C6,     0x12B7E950,     0x8BBEB8EA,     0xFCB9887C,
0x62DD1DDF,     0x15DA2D49,     0x8CD37CF3,     0xFBD44C65,
0x4DB26158,     0x3AB551CE,     0xA3BC0074,     0xD4BB30E2,
0x4ADFA541,     0x3DD895D7,     0xA4D1C46D,     0xD3D6F4FB,
0x4369E96A,     0x346ED9FC,     0xAD678846,     0xDA60B8D0,
0x44042D73,     0x33031DE5,     0xAA0A4C5F,     0xDD0D7CC9,
0x5005713C,     0x270241AA,     0xBE0B1010,     0xC90C2086,
0x5768B525,     0x206F85B3,     0xB966D409,     0xCE61E49F,
0x5EDEF90E,     0x29D9C998,     0xB0D09822,     0xC7D7A8B4,
0x59B33D17,     0x2EB40D81,     0xB7BD5C3B,     0xC0BA6CAD,
0xEDB88320,     0x9ABFB3B6,     0x03B6E20C,     0x74B1D29A,
0xEAD54739,     0x9DD277AF,     0x04DB2615,     0x73DC1683,
0xE3630B12,     0x94643B84,     0x0D6D6A3E,     0x7A6A5AA8,
0xE40ECF0B,     0x9309FF9D,     0x0A00AE27,     0x7D079EB1,
0xF00F9344,     0x8708A3D2,     0x1E01F268,     0x6906C2FE,
0xF762575D,     0x806567CB,     0x196C3671,     0x6E6B06E7,
0xFED41B76,     0x89D32BE0,     0x10DA7A5A,     0x67DD4ACC,
0xF9B9DF6F,     0x8EBEEFF9,     0x17B7BE43,     0x60B08ED5,
0xD6D6A3E8,     0xA1D1937E,     0x38D8C2C4,     0x4FDFF252,
0xD1BB67F1,     0xA6BC5767,     0x3FB506DD,     0x48B2364B,
0xD80D2BDA,     0xAF0A1B4C,     0x36034AF6,     0x41047A60,
0xDF60EFC3,     0xA867DF55,     0x316E8EEF,     0x4669BE79,
0xCB61B38C,     0xBC66831A,     0x256FD2A0,     0x5268E236,
0xCC0C7795,     0xBB0B4703,     0x220216B9,     0x5505262F,
0xC5BA3BBE,     0xB2BD0B28,     0x2BB45A92,     0x5CB36A04,
0xC2D7FFA7,     0xB5D0CF31,     0x2CD99E8B,     0x5BDEAE1D,
0x9B64C2B0,     0xEC63F226,     0x756AA39C,     0x026D930A,
0x9C0906A9,     0xEB0E363F,     0x72076785,     0x05005713,
0x95BF4A82,     0xE2B87A14,     0x7BB12BAE,     0x0CB61B38,
0x92D28E9B,     0xE5D5BE0D,     0x7CDCEFB7,     0x0BDBDF21,
0x86D3D2D4,     0xF1D4E242,     0x68DDB3F8,     0x1FDA836E,
0x81BE16CD,     0xF6B9265B,     0x6FB077E1,     0x18B74777,
0x88085AE6,     0xFF0F6A70,     0x66063BCA,     0x11010B5C,
0x8F659EFF,     0xF862AE69,     0x616BFFD3,     0x166CCF45,
0xA00AE278,     0xD70DD2EE,     0x4E048354,     0x3903B3C2,
0xA7672661,     0xD06016F7,     0x4969474D,     0x3E6E77DB,
0xAED16A4A,     0xD9D65ADC,     0x40DF0B66,     0x37D83BF0,
0xA9BCAE53,     0xDEBB9EC5,     0x47B2CF7F,     0x30B5FFE9,
0xBDBDF21C,     0xCABAC28A,     0x53B39330,     0x24B4A3A6,
0xBAD03605,     0xCDD70693,     0x54DE5729,     0x23D967BF,
0xB3667A2E,     0xC4614AB8,     0x5D681B02,     0x2A6F2B94,
0xB40BBE37,     0xC30C8EA1,     0x5A05DF1B,     0x2D02EF8D,
0x00000000,     0x00000000,     0x00423084,     0x00000000,
};

//die große Rechnung
private uint rechner_kalk(char[] mixserial)
{
uint eax = 0x1d8df;
uint edx = 0x0;
uint ebx = 0x0;

//kein Bock noch zu entschlüsseln, einfach nur abschreiben
for (int i = 0; i < 4; i++)
{
edx = mixserial[5 * i];
edx = (edx ^ eax) & 0xff;
ebx = rechnungstabelle[edx];

edx = mixserial[(5 * i) + 1];
eax = (eax >> 0x8) ^ ebx;
edx = (edx ^ eax) & 0xff;
ebx = rechnungstabelle[edx];

edx = mixserial[(5 * i) + 2];
eax = (eax >> 0x8) ^ ebx;
edx = (edx ^ eax) & 0xff;
ebx = rechnungstabelle[edx];

edx = mixserial[(5 * i) + 3];
eax = (eax >> 0x8) ^ ebx;
edx = (edx ^ eax) & 0xff;
ebx = rechnungstabelle[edx];

edx = mixserial[(5 * i) + 4];
eax = (eax >> 8) ^ ebx;
edx = (edx ^ eax) & 0xff;
ebx = rechnungstabelle[edx];

eax = (eax >> 8) ^ ebx;
}

return eax;
}

}
}
NFSU2 KeyGen - Source (546 Downloads)

Greetz TheVamp und viel Spaß mit dem Source Code

One thought on “Need for Speed Underground 2 – Let’s make a real Keygen

  1. Hi mate,

    echt cool zu sehen, wie man sich weiterentwickeln kann. Ich habe nun gerade erst angefangen mich mit RE zu beschäftigen, aber Du bist ja schon ein Profi.

    Zur Zeit hänge ich auch an einem Prog mit Anforderungs- und Freischaltcode über einen ausgelagerten Security Client. Echt Hammer schwer für mich, da hier noch eine DLL benutzt wird. A lot of call and so on…

    Also RESPEKT

    Regards
    masterparts

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.