Was wird hier gecrackt?
- ScareByte's SerialMe 1.0 (http://scared.at/scarebyte)
Was brauche ich alles?
- SoftICE
- eine Programmiersprache
Los gehts!
Vielleicht ist im Vorfeld zu sagen, dass dieses SerialMe mit tE!-lock gepackt wurde (ich hab das nicht selber rausgefunden sondern ScareByte hat's mir gesagt, nachdem ich sein SerialMe gelöst hatte). Dieses machte mir ein Dumpen via ProcDump unmöglich. Des weiteren benutzt tE!-Lock noch SoftICE-Checks (speziell INT3), welche sich aber mit icedump(W9x) bzw. NT_ALL (Win2K) überlisten lassen.
Nun aber zum SerialMe. Da ich ScareByte etwas kenne und daher weiß, dass er nur in Delphi programmiert (die letzten beiden meiner Tutorials deuten darauf hin) und ich auch zudem noch weiß, dass er inzwischen APIs wie "GetWindowTextA" anstelle vom Standard-Delphi "Edit1.Text" benutzt, hab ich diesmal nicht DeDe benutzt sondern ganz normal meinen "bpx GetWindowTextA" gesetzt. Dieses natürlich, nachdem ich meine Serial "11223344" eingegeben hatte. Nach einem Druck auf "Check" und F12 war ich dann auch hier:
:00404368 Call 00403E24 <-- GetWindowTextA :0040436D lea eax, [ebp-04] :00404370 mov edx, ebx <-- edx zeigt auf "11223344" :00404372 call 00402F88 <-- unwichtiger Call :00404377 mov eax, [ebp-04] <-- eax zeigt auf eine andere Stelle, an der jetzt auch "11223344" steht :0040437A call 004040B4 <-- interessanter Call |
Diesen Call hab ich mir dann mal näher angeschaut.
4040B4 push ebp 4040B5 mov ebp, esp 4040B7 add esp, FFFFFEE8 4040BD push ebx 4040BE push esi 4040BF xor edx, edx 4040C1 mov [ebp+FFFFFEE8], edx 4040C7 mov [ebp-14], edx 4040CA mov [ebp-04], eax 4040CD mov eax, [ebp-04] 4040D0 call 00403010 4040D5 xor eax, eax 4040D7 push ebp 4040D8 push 004042E2 4040DD push fs:[eax] 4040E0 mov fs:[eax], esp 4040E3 mov eax, 00406540 4040E8 mov edx, [ebp-04] 4040EB call 00402EB8 4040F0 mov eax, [ebp-04] <-- eax zeigt auf "11223344" 4040F3 call 00402FC4 <-- eax = 8 (Länge von "11223344") 4040F8 imul edx, eax, 0053 <-- edx=eax*53 also 298 4040FB add edx, 09 <-- edx=edx+9 also 2A1 4040FE sub edx, eax <-- edx=edx-eax also 299 404100 sub edx, 08 <-- edx=edx-8 also 291 404103 xor edx, 01B3 <-- edx=edx XOR 1B3 also 322 404109 shl edx, 03 <-- edx=edx*8 (also 1910) 40410C cmp edx, 1A80 <-- ist edx=1A80 404112 jne 004042B9 <-- nein, springe 4042B9 xor eax, eax ... nichts passiert ... 4042BB pop edx 4042BC pop ecx 4042BD pop ecx 4042BE mov fs:[eax], edx 4042C1 push 004042E9 4042C6 lea eax, [ebp+FFFFFEE8] 4042CC call 00402E64 4042D1 lea eax, [ebp-14] 4042D4 call 00402E64 4042D9 lea eax, [ebp-04] 4042DC call 00402E64 4042E1 ret ... Return (nichts ist passiert) ... |
Sieht nach einer kleinen mathematischen Aufwärmübung aus. Wir können uns auch gleich den "SPRUNG ZU 4042B9" als "SPRUNG ZU 'schlechter Cracker' " merken. Wollen wir diese mathematische Spielerei mal auflösen
((Länge*53 + 9 - Länge - 8) XOR 1B3 ) * 8 = 1A80 |Zusammenfassen ; durch 8 (Länge*52 + 1) XOR 1B3 = 350 |XOR 1B3 ; -1 ; durch 52 Länge = 9
Sieht so aus, als bräuchten wir eine Serial, die neun Ziffern lang ist. Deshalb nehme ich jetzt lieber "112233445" und setze das ganze fort:
4040F0 mov eax, [ebp-04] <-- eax zeigt auf "112233445" 4040F3 call 00402FC4 <-- eax = 9 (Länge von "112233445") 4040F8 imul edx, eax, 0053 <-- edx=2EB 4040FB add edx, 09 <-- edx=2F4 4040FE sub edx, eax <-- edx=2EB 404100 sub edx, 08 <-- edx=2E3 404103 xor edx, 01B3 <-- edx=350 404109 shl edx, 03 <-- edx=1A80 40410C cmp edx, 1A80 <-- edx=1A80 ? 404112 jne 004042B9 <-- ja, diesmal schon 404118 lea edx, [ebp-08] 40411B mov eax, [ebp-04] <-- eax zeigt auf "112233445" 40411E call 004025E4 <-- eax= 6B08BE5 also 112233445 in Hexadezimal 404123 mov ebx, eax <-- merk mir eax in ebx 404125 cmp [ebp-08], 0 <-- ja, ist Null 404129 jne 004042B9 <-- springe nicht 40412F mov eax, ebx <-- und wieder zurück... 404131 cdq <-- "convert double to quad" - mache also aus der 32Bit Zahl in eax eine 64Bit Zahl 404132 push edx <-- pushe die ersten 32Bit der neuen Zahl (also 0) 404133 push eax <-- pushe auch die anderen 32Bit (also 6B08BE5) 404134 push 00000000 <-- pushe nochmal 0 (ich nehme an, das HIGH-DWORD zum Wert... 404136 push 126822F5 <-- ... 1268.22F5, welcher hier gepusht wird 40413B push 00000000 <-- schon wieder 0 40413D push 9860AA69 <-- pushe 9860.AA69 404142 call 00403F90 <-- eax = 39EF.4A7A 404147 mov ebx, eax <-- merk mir dieses Resultat in ebx |
Diesen "CALL 403F90" hab ich mir erstmal nur flüchtig angekuckt, da er viel zu umfangreich war. Ich wollte lieber darauf achten, was denn eigentlich für ein Wert aus dem Call rauskommen muss. Deshalb hab ich erst mal weiter getracet
404142 call 00403F90 <-- der Call 404147 mov ebx, eax <-- ebx ist jetzt auch 39EF.4A7A 404149 lea edx, [ebp+FFFFFEEC] 40414F mov eax, ebx 404151 call 004025D8 404156 lea edx, [ebp+FFFFFEEC] <-- edx zeigt mehr oder weniger auf "971983482", also 39EF.4A7A in Dezimal 40415C lea eax, [ebp-14] <-- eax zeigt auf eine leere Stelle 40415F call 00402FB8 <-- an dieser leeren Stelle steht jetzt ein Zeiger auf "971983482" 404164 mov ebx, 00000030 <-- ebx=0x30 404169 xor esi, esi <-- esi=0 40416B mov eax, [ebp-14] <-- eax zeigt auf "971983482" 40416E call 00402FC4 <-- eax=9 (Länge dieses Strings) 404173 test eax, eax <-- ist da überhaupt ein String rausgekommen? 404175 jle 0040418D <-- wenn nicht (unwahrscheinlich), dann springe sonst: 404177 mov edx, 00000001 <-- edx=1 (fange bei der ersten Ziffer an) 40417C mov ecx, [ebp-14] <-- ecx zeigt auf "971983482" 40417F movzx ecx, [ecx+edx-01] <-- ecx=die aktuelle Ziffer des Strings 404184 cmp ebx, ecx <-- aktueller Buchstabe = aktueller Vergleichswert ? (erst 30, dann 31;32;33...) 404186 jne 00404189 <-- wenn nicht, springe 404188 inc esi <-- ansonsten: erhöhe esi 404189 inc edx <-- erhöhe aktuelle Ziffer 40418A dec eax <-- senke Zähler der noch zu verarbeitenden Ziffern 40418B jne 0040417C <-- springe solange noch Ziffern zu verarbeiten sind 40418D dec esi <-- ziehe eins von ESI ab 40418E jg 004042B9 <-- ist ESI > 0 (kam der aktuelle Buchstabe mehr als einmal vor)? wenn ja: springe 404194 inc ebx <-- sonst: erhöhe aktuellen Vergleichswert 404195 cmp ebx, 003A <-- und das solange bis dieser 3A ist (er also alle Ziffern von 0-9 durch hat) 404198 jne 00404169 |
Alles was er hier macht ist zu checken, ob jedes Zeichen aus dem String "971983482" auch wirklich nur einmal vorkommt. Er fängt bei "0" (also 0x30) an und scannt den String nach Vorkommnis aller Ziffern von 0-9 ab. Findet er die aktuelle Ziffer, erhöht er ESI um eins. Ist am Ende ESI>1 springt er zu "BAD". Es darf also maximal jedes Zeichen nur einmal vorkommen. Bevor wir jetzt ausrechnen, wie viele Möglichkeiten uns hier verbleiben, sollten wir uns erstmal die nächsten Bedingungen ankucken.
Da in "971983482" nicht nur die Ziffer 9, sondern auch die 8 zweimal vorkommt, schmeißt er mich natürlich raus. Deshalb nehm ich lieber eine Zahl, bei der das klappt ("123456789"), rechne diese ins hexadezimale um (0x075B.CD15 und plaziere selbige nach dem CALL 403F90, welcher mir ja eine 39EF.4A7A ausspuckt, im EAX Register.
40419A mov eax, [ebp-14] <-- eax zeigt auf "123456789" 40419D mov dl, [eax] <-- dl = erstes Zeichen (0x31) 40419F sub dl, 30 <-- mache aus dem Zeichen eine Ziffer (1) 4041A2 mov eax, [ebp-14] 4041A5 mov al, [eax+01] <-- al= zweites Zeichen 4041A8 sub al, 30 <-- mache aus dem Zeichen eine Ziffer (2) 4041AA mov ecx, [ebp-14] 4041AD mov cl, [ecx+02] <-- mache das auch mit dem dritten Zeichen 4041B0 sub cl, 30 4041B3 mov ebx, [ebp-14] 4041B6 mov bl, [ebx+03] <-- Nummer 4 4041B9 sub bl, 30 4041BC mov [ebp-09], bl 4041BF mov ebx, [ebp-14] 4041C2 mov bl, [ebx+04] <-- Nummer 5 4041C5 sub bl, 30 4041C8 mov [ebp-0A], bl 4041CB mov ebx, [ebp-14] 4041CE mov bl, [ebx+05] <-- Nummer 6 4041D1 sub bl, 30 4041D4 mov [ebp-0B], bl 4041D7 mov ebx, [ebp-14] 4041DA mov bl, [ebx+06] <-- Nummer 7 4041DD sub bl, 30 4041E0 mov [ebp-0C], bl 4041E3 mov ebx, [ebp-14] 4041E6 mov bl, [ebx+07] <-- Nummer 8 4041E9 sub bl, 30 4041EC mov [ebp-0D], bl 4041EF mov ebx, [ebp-14] 4041F2 mov bl, [ebx+08] <-- Nummer 9 4041F5 sub bl, 30 4041F8 mov [ebp-0E], bl 4041FB test dl, dl <-- ist die erste Ziffer eine 0 ? 4041FD jbe 004042B9 <-- nein 404203 test al, al <-- ist vielleicht die zweite Ziffer eine 0? 404205 jbe 004042B9 <-- nein 40420B and edx, 000000FF 404211 imul edx, 00002710 <-- edx=2710 (erste Ziffer mal 0x2710) 404217 xor ebx, ebx 404219 mov bl, cl <-- bl=dritte Ziffer 40421B imul ebx, 000007D0 <-- ebx=1770 404221 add edx, ebx <-- edx=3E80 404223 xor ebx, ebx 404225 mov bl, [ebp-09] <-- bl=vierte Ziffer 404228 imul ebx, 000000C8 <-- mal C8 40422E add edx, ebx <-- addiere Ergebnis zu edx 404230 xor ebx, ebx 404232 mov bl, [ebp-0A] <-- [5] 404235 shl ebx, 02 <-- *2 404238 lea ebx, [ebx+4*ebx] <-- *5 40423B add edx, ebx <-- und dazu addieren 40423D xor ebx, ebx 40423F mov bl, [ebp-0B] <-- [6] 404242 add ebx, ebx <-- addiere 404244 add edx, ebx <-- addiere nochmal 404246 xor ebx, ebx 404248 mov bl, al <-- [2] 40424A imul ebx, 2710 <-- mal 2170 404250 add edx, ebx <-- und drauf addieren 404252 and eax, 00FF <-- al=[2] 404257 imul eax, 000186A0 <-- *186a0 40425D xor ebx, ebx 40425F mov bl, [ebp-0C] <-- bl=[7] 404262 imul ebx, 2710 <-- *2710 404268 add eax, ebx <-- diesmal zu EAX addieren 40426A xor ebx, ebx 40426C mov bl, [ebp-0D] <-- bl=[8] 40426F imul ebx, 03E8 <-- *3E8 404275 add eax, ebx <-- und zu EAX addieren 404277 xor ebx, ebx 404279 mov bl, [ebp-0B] <-- bl=[6] 40427C imul ebx, 00000064 <-- *64 40427F add eax, ebx <-- addiere zu EAX 404281 xor ebx, ebx 404283 mov bl, [ebp-0E] <-- [9] 404286 add ebx, ebx 404288 lea ebx, [ebx+4*ebx] <-- insgesamt *A 40428B add eax, ebx <-- zu EAX 40428D and ecx, 00FF 404293 add eax, ecx 404295 cmp edx, eax <-- ist edx=eax? (9030=440A5?) 404297 jne 004042B9 <-- nein, springe zu "Falsch" sonst: 404299 lea edx, [ebp+FFFFFEE8] <-- edx zeigt auf eine leere Stelle 40429F mov eax, 004042F8 <-- eax zeigt auf "B1 BB C9 FE-FC F2 E8 EF..." 4042A4 call 00403EEC 4042A9 mov edx, [ebp+FFFFFEE8] <-- edx zeigt auf "* Registration Successful *" 4042AF mov eax, 00406540 <-- eax zeigt auf die Adresse des Eingabefeld-Textes 4042B4 call 00402EB8 <-- schreibe Text ins Eingabefeld 4042B9 xor eax, eax 4042BB pop edx 4042BC pop ecx 4042BD pop ecx 4042BE mov fs:[eax], edx 4042C1 push 004042E9 4042C6 lea eax, [ebp+FFFFFEE8] 4042CC call 00402E64 4042D1 lea eax, [ebp-14] 4042D4 call 00402E64 4042D9 lea eax, [ebp-04] 4042DC call 00402E64 4042E1 ret |
Ok, das war die letzte Bedingung. Nicht nur, dass die Zahl, die aus dem CALL rauskommen muss, jede Ziffer nur einmal beinhalten darf (und die zweite ungleich 0 sein muss), sie muss auch noch folgende Bedingungen erfüllen (alle Werte in Dezimal):
[1]*10.000 + [3]*2.000 + [4]*200 + [5]*20 + [6]*2 + [2]*10.000 = [2]*100.000 + [7]*10.000 + [8]*2.000 + [6]*100 + [9]*10 + [3]
Lassen wir uns das mal zusammenfassen: Aus dem Call kann eine beliebige 32Bit-Zahl rauskommen. Das wären also 2^32 Möglichkeiten. Da aber ScareByte's Int2String-Funktion die Zahl als vorzeichenbehaftete Zahl betrachtet, sinkt die Anzahl der Möglichkeiten schon auf 2^31. In diesem Bereich von 0 bis 2,1 Milliarden gibt es ungefähr 10 Millionen Möglichkeiten, bei denen jede Ziffer nur wirklich einmal vorkommt. Da ich keine Lust habe, erst 2 Milliarden Möglichkeiten nach irgendwelchen Werten zu durchforsten, um dann noch mal 10 Millionen zu erhalten welche ich noch mal nach obigen Kriterien durchforsten müsste, habe ich erst die obigen Kriterien rausgefiltert. Ich hab dazu dieses Programm benutzt:
// MS VisualC++ Konsolenanwendung #include "stdafx.h" #include "stdio.h" unsigned int i; //vorzeichenlose 32Bit Ganzzahl unsigned char num[12]={0}; //Datenfeld von 12 vorzeichenlosen Bytes char buffer[20]={0}; //String mit einer Länge von 20 Zeichen int x,y,z; //32Bit Ganzzahlen int main(int argc, char* argv[]) { for (i=1;i<0x80000000;i++) //zähle von 0 bis 0x7FFF.FFFF { sprintf(buffer,"%u",i); //konvertiere i zu einem String for (z=0;z<11;z++) //Fülle das Datenfeld num mit den jeweiligen Ziffern num[z]=buffer[z]-0x30; x=num[0]*10000+num[2]*2000+num[3]*200+num[4]*20+num[5]*2+num[1]*10000; //EDX y=num[1]*100000+num[6]*10000+num[7]*1000+num[5]*100+num[8]*10+num[2]; //EAX if (x==y) //EDX=EAX? printf("%u,",i); //Wenn ja: gib i aus } return 0; } |
Bei meinem Rechner (Athlon 1000) war er dann auch nach ungefähr 90 Minuten soweit und hatte mir 3098 Möglichkeiten ausgespuckt. Das sind mir noch bedeutend zu viele um mit der Hand rauszulesen, bei welcher dieser Möglichkeiten jede Ziffer nur einmal vorkommt. Deswegen hab ich die Bildschirmausgabe lieber in eine Dateiausgabe umgeleitet und diese Daten dann erneut durchforsten lassen, bei welcher Möglichkeit denn jede Ziffer nur einmal vertreten ist. Dazu habe ich dieses Programm benutzt:
// MS VisualC++ Konsolenanwendung #include "stdafx.h" #include "stdio.h" #include "string.h" unsigned int i=0; unsigned char num[12]={0}; int x,y,z=0; bool ok=true; char buffer[20]={0}; unsigned int data[3098]= //hier stehen normalerweise ALLE 3098 Möglichkeiten drin (hab ich hier natürlich gekürzt) { 10004008,10054009,10209112,10259113,10414216,10464217,10619320,10669321,10824424,10874425, ... 2089993790,2089993791,2089993792,2089993793,2089993794,2089993795,2089993796,2089993797,2089993798,2089993799 }; int main(int argc, char* argv[]) { for (i=0;i<3098;i++) //grase die verbleidenden Möglichkeiten durch { sprintf(buffer,"%u",data[i]); //wandle aktuelle Möglichkeit in einen String um for (z=0;z<strlen(buffer);z++) //lese String ins Datenfeld num[z]=buffer[z]-0x30; for(x=0;x<strlen(buffer)-1;x++) //vergleiche alle Zeichen { for (y=x+1;y<strlen(buffer);y++) { if ((num[x]==num[y])) //ist ein Zeichen doppelt: ok=false; // dann setze Variable 'ok' auf FALSCH } } if (ok==true) //ist die Variable 'ok' noch auf WAHR ? printf("%.10u : %.8X\n",data[i],data[i]); //wenn ja: gib aktuelle Möglichkeit aus ok=true; //setze 'ok' zurück auf WAHR } return 0; } |
Dieses Progrämmchen filterte mir folgende Möglichkeiten raus:
Dezimal |
Hexadezimal |
108479265 |
06774321 |
208459361 |
0C6CD661 |
208479365 |
0C6D2485 |
406193528 |
18360578 |
806173924 |
300D3CE4 |
912836057 |
3668C5D9 |
1084792653 |
40A89F4D |
2084593617 |
7C405FD1 |
2084793651 |
7C436D33 |
Das wären schon mal 10 Möglichkeiten. Allerdings dürfen ja weder erste, noch zweite Ziffer gleich Null sein. Hier noch mal zur Erinnerung:
4041FB test dl, dl <-- ist die erste Ziffer eine 0 ? 4041FD jbe 004042B9 <-- nein 404203 test al, al <-- ist vielleicht die zweite Ziffer eine 0? 404205 jbe 004042B9 <-- nein |
Einzigste verbleibende Möglichkeit: 912836057 (bzw: 0x3668.C5D9). Nun müssen wir nur noch herausfinden, welche Zahl (bzw. Seriennummer) in den CALL 403F90 bei 404142 reinmuss, damit besagte 912836057 rauskommt. Hier noch mal die Stelle:
404132 push edx <-- pushe 0 (bei einer 9-stelligen SN nicht anders möglich) 404133 push eax <-- pushe die Seriennummer 404134 push 00000000 <-- pushe 0 404136 push 126822F5 <-- pushe 1268.22F5 40413B push 00000000 <-- pushe 0 40413D push 9860AA69 <-- pushe 9860.AA69 404142 call 00403F90 <-- aus diesem CALL muss 0x3668.C5D9 rauskommen |
Da ich wie gesagt gescheitert bin, mir mal einen W32Dasm-fähigen (und ausdruckbaren) Dump zu machen, und meine Nerven durch das ganze brute-forcing schon etwas strapaziert waren, habe ich mich auch hier entschlossen, lieber zum brute-force zu greifen anstatt mir mal den CALL näher anzukucken. Ich hab mir also kurz vor diesen CALL einen Breakpoint gesetzt und das Programm dann im Speicher so umgeschrieben, dass es alle Möglichkeiten durchtestet. Das sah dann so aus:
:0040412F 33DB xor ebx, ebx <-- fange bei 0 an (besser wäre 100.000.000, passt aber vom Platz nicht) :00404131 90 nop :00404132 52 push edx :00404133 53 push ebx <-- pushe aktuellen Wert :00404134 6A00 push 00000000 :00404136 68F5226812 push 126822F5 :0040413B 6A00 push 00000000 :0040413D 6869AA6098 push 9860AA69 :00404142 E849FEFFFF call 00403F90 <-- verarbeite aktuellen Wert :00404147 3DD9C56836 cmp eax, 3668C5D9 <-- kam aus dem CALL die gewünschte 912836057 ? :0040414C 7409 je 00404157 <-- wenn ja, springe :0040414E 43 inc ebx <-- erhöhe aktuellen Wert :0040414F 81FBFFC99A3B cmp ebx, 3B9AC9FF <-- sind wir schon bei 999.999.999 ? :00404155 75DB jne 00404132 <-- wenn nicht, dann nochmal :00404157 E95D010000 jmp 004042B9 <-- hier einen Breakpoint draufsetzen. |
Die Zeilen 40412F und 404157 hab ich nicht deshalb Türkis gemacht, weil mir die Farbe so gut gefällt sondern eher weil ich andeuten wollte, dass man auf diese Stellen einen Breakpoint setzen sollte. Wenn man beim ersten Breakpoint angelangt ist, sollte man EBX lieber auf 0x5F5.E100 und EDX auf 0 setzen, da wir ja eine 9-stellige Serial haben müssen und deshalb getrost bei 100.000.000 anfangen können.
Der Breakpoint auf 404157 muss deshalb dorthin, damit wir auch merken wenn er eine Möglichkeit gefunden hat. Sollte er dann an dieser Stelle unterbrechen, können wir aus dem EBX Register bequem unsere Serial ablesen. Macht das am besten kurz bevor ihr schlafen geht. Bei mir hat diese Schleife ungefähr 7 Stunden gebraucht, bis ich die gewünschte Seriennummer "872250136" (oder 0x33FD7B18) hatte.
7 Stunden dauert mir zu lange und "Brute Force" kann ich auch nicht leiden! Wie könnte ich sonst noch auf die Lösung kommen?
Die beiden gepushten Konstanten 0x126822F5 und 0x9860AA69 ließen in mir schon einen Verdacht aufsteigen den mir ScareByte später auch bestätigte. Auch ein näheres Untersuchen vom CALL 403F90 bestätigten diese Befürchtungen. ScareByte hatte den sehr populären Verschlüsselungsalgorithmus RSA benutzt. Zu dieser Erkenntnis haben mir neben ScareBytes Bestätigung noch folgende, von mir bereits gelesene, Informationsquellen gedient:
- TSC's RSA24 CrackMe welches ich mal gelöst hatte (http://crackmes.cjb.net)
- ACiD BuRN's Tutorial über dieses CrackMe welches ich anschließend
gelesen hatte um zu bemerken, dass er es auf einen ganz anderen Weg gelöst
hatte
- oVeRFLoW's RSA Tutorial in der Ausgabe 15 des Kräcker Mäg (zu saugen
bei http://www.l2c-board.de/kraecker
)
Das soll hier kein weiteres RSA Tutorial werden. Nur hätte ich mal etwas intensiver gelesen und ein paar Informationen mehr gehabt, dann hätte ich mir viel Zeit und Mühe gespart.
Zuerst hab ich mir (mal wieder) einen BruteForcer in C++ geschrieben, da ich davon ausgegangen bin, dass dieser schneller ist als wenn ich ScareByte's Delphi-SerialMe sich selber brute-forcen lasse. Dieses Progrämmchen macht im Prinzip genau das gleiche wie der CALL 403F90, nur schneller. Ich werde es deshalb nicht noch mal näher erklären:
// BruteForcer zu ScareBytes SerialMe 1 #include "stdafx.h" #include "stdio.h" __int64 div=0x9860AA69; //der eine Wert, der gepusht wird char bin[41]= {1,0,0,1,0,0,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,1}; // 0x126822F5 so zerlegt, wie es der "CALL 403F90" auch macht __int64 mod1=0; //erster Wert __int64 mod2=0; //zweiter Wert __int64 i=0; //aktueller Testwert int z=1; //Zähler int main() { for (i=100000000;i<1000000000;i++) //probiere alle 9-stelligen Möglichkeiten durch { mod1=i; //Setze Werte auf aktuellen Wert mod2=i; for (z=1;z<41;z++) //grase alle Bits durch { if (bin[z]==0) //wenn Bit nicht gesetzt dann: mod1=(__int64)(mod1*mod1)%div; //mod1= mod12 mod 0x9860AA69 else //sonst: mod2=(__int64)(mod2*mod1)%div; //mod2=(mod2*mod1) mod 0x9860AA69 } if (mod2==0x3668C5D9) //kam aus dieser Berechnung der gewünschte Wert 0x3668C5D9 ? { //ja: printf("Bingo: %u",i); //gib diesen Wert aus return 0; //Programmende } } } |
Dieses Progrämmchen brauche dann "nur" noch ungefähr eine Stunde um zum Ergebnis zu kommen bzw 10 Minuten wenn man die Schleife rückwärts laufen lassen würde (also 70 Minuten für ALLE 900.000.000 Möglichkeiten). Eine wesentlich schnellere Möglichkeit wäre, es über die Formeln zu lösen, die die Idee vom RSA-Algo betreffen. Diese wären:
c = ( m ^ e) mod n m = ( c ^ d) mod n
wobei:
c = verschlüsselter Wert m = entschlüsselter Wert n = ÖFFENTLICHER Modulus e = ÖFFENTLICHER Exponent d = privater Exponent
Inwiefern diese Werte jetzt mathematisch zusammenhängen werde ich hier nicht erklären (das steht schon alles im Kräcker-Tut von oVeRFLoW). Wollen wir mal unsere gegebenen Werte nutzen.
Wir haben als Konstanten 0x126822F5 und 0x9860AA69. Da 0x126822F5 kleiner ist als 0x9860AA69 und sich zudem noch in mehr als zwei Primzahlen zerlegen lässt, bin ich davon ausgegangen, dass 0x126822F5 der Exponent und 0x9860AA69 das Modul ist. Des weiteren haben wir noch unseren verschlüsselten Wert 0x3668C5D9 also der, der aus dem CALL rauskommen muss. Wollen wir mal diese Gegebenheiten einsetzen:
0x3668C5D9 = (m ^ 0x126822F5) MOD 0x9860AA69 oder aber m = (0x3668C5D9 ^ d) MOD 0x9860AA69
Ok, entweder wir Bruteforcen "m" so wie ich, oder aber wir finden "d" heraus und können dann "m" ausrechnen. Zum herausfinden von "d" hab ich das RSA-Tool von tHE EGOiSTE genutzt. Hier grob die Vorgehensweise:
1. n und e in den entsprechenden Feldern eintragen (darauf achten, dass E dezimal sein muss!) 2. mit "Factor N" die beiden Primzahlen p und q ausrechnen lassen 3. mir mit "Calc D" d ausrechnen lassen.
Dieser spuckt mir dann auch als d den Wert 0x10001 bzw. 65537(d) aus. Nun haben wir eigentlich alles was wir brauchen:
Serial = (0x3668C5D9 ^ 0x10001) MOD 0x9860AA69
Nun potenziert mal 912836057 mit 65537. Das Ergebnis hätte nicht ganz 200.000 Stellen! Aber zum Glück sandte mir ScareByte auch hier eine Vereinfachung zu, die auf mathematischen Grundlagen beruhte, die sich meiner Erkenntnis entzogen. Der Trick ist nämlich:
a*b MOD n = ((a mod n) * (b mod n)) mod n
Im selben Dokument über die Grundlagen der Zahlenlehre war auch gleich noch ein Beispielprogramm zum "schnellen Potenzieren modulo n" aufgeführt:
function hoch_modulo(t,x,n:positiv):positiv; { Berechnet (t hoch x) mod n } var a,b,v, hilf,hilf1: positiv; begin a:=t mod n; b:=x;v:=1; while b<>0 do begin if (b mod 2)=0 then begin a:=(a*a) mod n; b:=b div 2 end else begin dec(b); v:=(v*a) mod n; end; if (b<0) or (a<0) or (v<0) then begin writeln('Potenzierungs-Fehler!!! V= ',v,' a= ',a); halt(0); end; end; hoch_modulo:=v; end; |
Mit diesem Progrämmchen und dem RSA-Tool von tE! war es mir in
Blitzesschnelle ein leichtes, zur gewünschten Serial zu kommen ohne
erst stundenlang bruteforcen zu müssen.
Nachwort:
Mal wieder ein interessantes SerialMe von ScareByte durch welches ich dazu gelernt habe.
Bei Fragen, Kritik, Bemerkung oder Glückwünschen:
SpaceKeks@hotmail.com
http://fly.to/SpaceKeks