Es wird wieder einmal Zeit für einen neuen Artikel. Diesmal habe ich mir ein älteres Spiel ausgesucht und es ist wieder ein Spiel aus dem Hause Blizzard. Heute geht es um Starcraft. Ich versuche herauszufinden, wie der Serial überprüft wird und auf dieser Grundlage versuche ich einen Keygen zu Programmieren. Da ich es im Original habe, ahne ich schon das es vielleicht ein wenig einfacher wird, als die letzten Sachen die ich gecrackt habe. Dies liegt daran dass der Serial nur aus Nummern besteht. Also auf geht es, in eine neue Runde der Zerstörung.
Zunächst einmal habe ich mir mit ImgBurn eine ISO von Starcraft gemacht. Nachdem die ISO fertig erstellt worden ist, habe ich mir einmal die Dateien auf der CD genauer angeschaut. Wieder der übliche Aufbau! Eine Setup.exe die wahrscheinlich nur die Install.exe startet. Also habe ich mir gleich die Install.exe gestartet und geschaut was denn die Installation so macht.
Der Anfang
OK! So sieht das also aus. Nur noch einmal mit PEid analysieren und siehe da:
Es handelt sich um "Microsoft Visual C++ 5.0 [Overlay]"
. Also können wir gleich mit OllyDbg heransetzen und den Kram debuggen. Schnell nach allen intermodular Calls
geschaut und nach Destination Name
geordnet finde ich folgende interessanten Stelle.
Nun habe ich 3 Breakpoints auf die GetDlgItemTextA
Stellen gemacht und hoffe dass die Eingaben einfach abgefangen werden, wenn ich den Serial eingebe. Jetzt starte ich das Programm, Klick auf installieren, bestätige die AGB und gebe dann den Serial ein. Nachdem ich die erste Zahl, in die Serialbox eingegeben habe, werde ich auch gleich in OllyDbg geschmissen.
CPU Disasm Address Hex dump Command Comments 00401A58 |. 8D4424 00 LEA EAX,[LOCAL.1023] ; faengt Zeichen im Serial ab 00401A5C |. 68 00100000 PUSH 1000 ; /MaxCount = 4096. 00401A61 |. 50 PUSH EAX ; |String => OFFSET LOCAL.1023 00401A62 |. 51 PUSH ECX ; |ItemID => [ARG.2] 00401A63 |. 52 PUSH EDX ; |hDialog => [ARG.1] 00401A64 |. FF15 94564400 CALL DWORD PTR DS:[<&USER32.GetDlgItemTe ; \USER32.GetDlgItemTextA 00401A6A |. 85C0 TEST EAX,EAX
Nicht wirklich eine interessante Stelle, also schnell den Breakpoint auf Disable setzten. Nun gebe ich die nächsten Zahlen ein und nachdem die erste Textbox voll ist und ich noch ein Zeichen eingeben will kommt schon der nächste Halt in OllyDbg.
CPU Disasm Address Hex dump Command Comments 004018BC |. 81C6 D7070000 ADD ESI,7D7 ; funktion switcht zur naechsten box 004018C2 |. 56 PUSH ESI ; /ItemID 004018C3 |. 50 PUSH EAX ; |hDialog => [43A658] = 002E061A, class = #32770, text = Starcraft - CD-Code. 004018C4 |. FFD7 CALL EDI ; \USER32.GetDlgItem 004018C6 |. 50 PUSH EAX ; /hWnd 004018C7 |. FF15 9C564400 CALL DWORD PTR DS:[] ; \USER32.SetFocus 004018CD |. 8B0D 58A64300 MOV ECX,DWORD PTR DS:[43A658]
Ah! Hier wird einfach nur der Fokus auf die nächste Textbox gesetzt. Sehr nette Funktion. Nachdem ich nun den gesamten Serial eingegeben habe, kann es auch schon losgehen und mit einem Klick auf OK, ploppt wieder OllyDbg auf.
Da sind wir
CPU Disasm Address Hex dump Command Comments 00401DAC |. 8D4424 18 LEA EAX,[LOCAL.1023] ; Serial ueberpruefung beginnt 00401DB0 |. 68 00100000 PUSH 1000 ; /MaxCount = 4096. 00401DB5 |. 50 PUSH EAX ; |String => OFFSET LOCAL.1023 00401DB6 |. 68 DA070000 PUSH 7DA ; |ItemID = 2010. 00401DBB |. 56 PUSH ESI ; |hDialog => [ARG.1] 00401DBC |. FFD7 CALL EDI ; \USER32.GetDlgItemTextA 00401DBE |. 85C0 TEST EAX,EAX 00401DC0 |. 75 04 JNE SHORT 00401DC6 00401DC2 |. 884424 18 MOV BYTE PTR SS:[LOCAL.1023],AL 00401DC6 |> 8D4C24 18 LEA ECX,[LOCAL.1023] 00401DCA |. 56 PUSH ESI ; /Arg2 00401DCB |. 51 PUSH ECX ; |Arg1 => OFFSET LOCAL.1023 00401DCC |. E8 AF000000 CALL 00401E80 ; \INSTALL.00401E80 00401DD1 |. 83C4 08 ADD ESP,8 00401DD4 |. 85C0 TEST EAX,EAX 00401DD6 |. 0F84 8C000000 JE 00401E68 00401DDC |. 8D5424 18 LEA EDX,[LOCAL.1023] 00401DE0 |. 52 PUSH EDX ; /Src => OFFSET LOCAL.1023 00401DE1 |. 68 38A64300 PUSH OFFSET 0043A638 ; |Dest = "TheVamp" 00401DE6 |. FF15 68554400 CALL DWORD PTR DS:[] ; \KERNEL32.lstrcpy 00401DEC |. 6A 0E PUSH 0E ; /Arg3 = 0E 00401DEE |. 68 28A64300 PUSH OFFSET 0043A628 ; |Arg2 = ASCII "1111111111111" 00401DF3 |. 56 PUSH ESI ; |Arg1 00401DF4 |. E8 B7010000 CALL 00401FB0 ; \INSTALL.00401FB0 00401DF9 |. 83C4 0C ADD ESP,0C 00401DFC |. 56 PUSH ESI ; /Arg2 00401DFD |. 68 28A64300 PUSH OFFSET 0043A628 ; |Arg1 = ASCII "1111111111111" 00401E02 |. E8 09010000 CALL 00401F10 ; \INSTALL.00401F10 00401E07 |. 83C4 08 ADD ESP,8
Die Letzte Funktion ist am interessantesten. Die Funktion 0x00401FB0
aufgerufen wird, handelt es sich nur um eine Kopierfunktion, die den Serial aus den einzelnen Textboxen zusammenbringt. Diese ist somit eher uninteressant und wir gehen sofort zur letzten Funktion (0x00401F10
) über.
Wie immer – Auf die Länge kommt’s an
CPU Disasm Address Hex dump Command Comments 00401F26 |> \56 PUSH ESI ; /String => [ARG.1] 00401F27 |. FF15 64554400 CALL DWORD PTR DS:[] ; \KERNEL32.lstrlenA 00401F2D |. 83F8 0D CMP EAX,0D ; laenge von 13 zeichen sonst ist nicht alles eingegeben 00401F30 |. 74 1C JE SHORT 00401F4E 00401F32 |. 8B4424 10 MOV EAX,DWORD PTR SS:[ARG.2] ; fehlermeldung serial zu kurz 00401F36 |. 50 PUSH EAX ; /Arg3 => [ARG.2] 00401F37 |. 68 5A020000 PUSH 25A ; |Arg2 = 25A 00401F3C |. 68 58020000 PUSH 258 ; |Arg1 = 258 00401F41 |. E8 DA040100 CALL 00412420 ; \INSTALL.00412420 00401F46 |. 83C4 0C ADD ESP,0C
Das erste was uns wieder entgegen grinst ist die Längen-Überprüfung. So wie immer muss erst einmal der Serial die richtige Größe haben. Nachdem diese Prüfung überstanden ist, geht es auch schon zur richtigen Serialüberprüfung.
Zielgerade
CPU Disasm Address Hex dump Command Comments 00401F4E |> \B8 03000000 MOV EAX,3 ; laufvariable 00401F53 |. 33D2 XOR EDX,EDX 00401F55 |> 8A0C32 /MOV CL,BYTE PTR DS:[ESI+EDX] 00401F58 |. 80F9 30 |CMP CL,30 ; ueberpruefe ob zahl 00401F5B |. 7C 2F |JL SHORT 00401F8C 00401F5D |. 80F9 39 |CMP CL,39 00401F60 |. 7F 2A |JG SHORT 00401F8C 00401F62 |. 0FBEC9 |MOVSX ECX,CL ; lese serial zahl ein 00401F65 |. 8D3C00 |LEA EDI,[EAX+EAX] ; verdopple laufvariable temporaer 00401F68 |. 83E9 30 |SUB ECX,30 ; zahl fuer cpu lesbar machen 00401F6B |. 33F9 |XOR EDI,ECX ; serialzahl XOR laufvariable 00401F6D |. 03C7 |ADD EAX,EDI ; fuege XOR Wert zur Laufvariable hinzu 00401F6F |. 42 |INC EDX 00401F70 |. 83FA 0C |CMP EDX,0C ; bei 0xC rausspringen 00401F73 |.^ 72 E0 \JB SHORT 00401F55 00401F75 |. 33D2 XOR EDX,EDX 00401F77 |. B9 0A000000 MOV ECX,0A 00401F7C |. F7F1 DIV ECX ; rest von der Laufvariable / 0xA 00401F7E |. 0FBE46 0C MOVSX EAX,BYTE PTR DS:[ESI+0C] ; letzte stelle vom serial 00401F82 |. 0FBECA MOVSX ECX,DL 00401F85 |. 83C1 30 ADD ECX,30 00401F88 |. 3BC1 CMP EAX,ECX ; vergleich letzte stelle serial mit rest von laufvariable / 0xA 00401F8A |. 74 1C JE SHORT 00401FA8 00401F8C |> 8B5424 10 MOV EDX,DWORD PTR SS:[ARG.2] ; Fehlermeldung Serial ist falsch 00401F90 |. 52 PUSH EDX ; /Arg3 => [ARG.2] 00401F91 |. 68 59020000 PUSH 259 ; |Arg2 = 259 00401F96 |. 68 58020000 PUSH 258 ; |Arg1 = 258 00401F9B |. E8 80040100 CALL 00412420 ; \INSTALL.00412420 00401FA0 |. 83C4 0C ADD ESP,0C
Blizzard setzte also schon damals auf schöne XOR Funktionen. Die Funktion startet mit einer Anfangsvariable, die auf 3 gesetzt ist, danach wird überprüft ob das eingelesene Zeichen eine Zahl ist. Diese eingelesene Zahl wird dann mit dem doppelten der Anfangsvariable gexored. Das Ergebnis aus diesem XOR wird dann auf die Anfangsvariable aufaddiert. Nachdem die ersten 12 Zeichen eingelesen und gexored worden sind, wird die Schleife verlassen und unsere Anfangsvariable ist ziemlich groß geworden (^.^). Die letzte Stelle des Serials wird somit nicht einberechnet. Der Wert der Anfangsvariable wird nun durch 10 geteilt und der Rest davon wird wiederrum mit der letzten Stelle des Serial verglichen. Wenn beide gleich sind haben wir diese Überprüfung überstanden und können das Spiel installieren.
Random Bruteforce FTW!
Da ich mich noch nicht so extrem mit Kryptografie beschäftigt habe, habe ich keinen Plan wie man aus dieser Serialüberprüfung einen passenden KeyGen macht. Also habe ich einfach, wie bei Diablo 2, eine Random Bruteforce Attacke geschrieben. Einfach die Funktion nach coden und schon kann das gebrute losgehen :D.
Das Programm arbeitet mit Threads, deswegen sind die locks dazwischen ;)
private void crack_starcraft_key() { int[] serial = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };//13 stellen lang while (true) { //Random Bruteforce Random r = new Random(); for (int i = 0; i < serial.Length; i++) { serial[i] = r.Next(0, 9); } for (int j = 0; j < 1000; j++) { //Serial Prüfung int laufvariable = 0x3; for (int i = 0; i < 0xC; i++) { laufvariable += (laufvariable*2) ^ serial[i]; } //Vergleich int check = laufvariable % 0xA; if (check == serial[12]) { //serials ausgeben string temp = ""; for (int i = 0; i < serial.Length; i++) { temp += serial[i].ToString(); } txt_ausgabe.Text += temp + "\n"; //Serials abspeichern lock (this) { StreamWriter myFile = new StreamWriter("./starcraft_serials.txt", true); myFile.Write(temp + "\n"); myFile.Close(); } } //Serial hochzählen for (int i = 12; i > 0; i--) { //ersten hochzählen if (i == 12) { serial[i]++; } //wenn über größe dann davor hochzählen if (serial[i] >= 10) { serial[i - 1]++; serial[i] = 0; } } } } }
Ich hoffe es hat euch gefallen und der kleine Ausflug war vielleicht wieder ein wenig lehrreich ;) Ich hau mich jetzt auf’s Ohr und freu mich auf das nächste Spiel oder Programm.
Greetz TheVamp
The includes were iostream, string, stdlib.h, time.h
Here’s some C++ code I made from looking at the executable on my system
/* ———————————————-
Starcraft CD-Key Generator
Algorithm @ 004111A5 in INSTALL.EXE
File Version 1.05
———————————————–*/
#include
#include
#include
#include
int main() {
int s=3; // Initial value
int i=0;
int serial[13];
/* Initialize seed value */
srand(time(NULL));
while (i<12) {
serial[i] = rand() % 10;
s += (s*2)^(serial[i]);
i++;
}
serial[12] = s % 10;
std::cout << "CD-KEY: ";
for (int n = 0; n < 13; n++) {
if ((n == 4)|(n == 9)) {
std::cout << '-';
}
std::cout << serial[n];
}
std::cout << std::endl;
}