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 (6039 Downloads)
Greetz TheVamp und viel Spaß mit dem Source Code
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