Crackme/Serialme

Ich hoffe, dass dieses Crackme/Serialme nicht so leicht zu lösen ist. Richtige Schlüssel oder die gecrackte version bitte posten.
 
Also, ich hab mich mal ein klein wenig mit dem Programm beschäftigt.

Was ich erkennen kann, ist zu Beginn eine Überprüfung, ob die Serial in der richtigen Form eingegeben wurde, und ob nur erlaubte Zeichen benutzt wurden. Danach wird Zeichen für Zeichen durchgegangen und ein wenig mit den Hexwerten der Zeichen und XOR herumgespielt. Dann eine Division mit 3 und eine Überprüfung. Das ist aber noch nicht alles. Danach wird die Serial nochmal auf etwas überprüft, was ich mir noch nicht angesehen habe.

Meine Frage ist eigentlich: Wie erkenne ich, dass meine Serial gültig ist?? Meines erachtens gibt das Programm weder bei falscher, noch bei richtiger Serial eine Ausgabe.
 
Zuletzt bearbeitet:
zzzzz-zS222-33333-44444
ATH00-HABOO-zzaK6-TDC89
Von:
Code:
004014FC  |. 74 0C          JE SHORT keygenme.0040150A
004014FE  |. E8 C1FEFFFF    CALL keygenme.004013C4
00401503  |. 3D 28134000    CMP EAX,keygenme.00401328
00401508  |. 74 0E          JE SHORT keygenme.00401518


Zu:
Code:
004014FC     90             NOP
004014FD     90             NOP
004014FE     90             NOP
004014FF     90             NOP
00401500     90             NOP
00401501     90             NOP
00401502     90             NOP
00401503     B8 28134000    MOV EAX,keygenme.00401328
00401508     EB 0E          JMP SHORT keygenme.00401518

Um richtige Serials zu finden muss man sich die Routine sub_004013C4 angucken. Die muss nach der Berechnung die Adresse 0x00401328 ergeben.

Edit: Hey, escor. Ist das SerialMe jetzt solved oder verlangst du noch ein Keygen? :)
 
Zuletzt bearbeitet:
Sorry, dass ich erst jetzt schreibe, aber wir haben nunmal zu geilen Schnee, den mußte ich ausnutzen ;)
Keygen brauchste nicht schreiben, wie ich sehe hast du ja herausgefunden wie es abläuft. Ich hoffe, dass es eine kleine Herausforderung war, oder täusch ich mich da?
 
Also das reine analysieren der Disassembly, wie die Bedingungen aussehen müssen, ging recht schnell.
Alle Bedingungen dann gleichzeitig zu erfüllen, ist eine Stufe schwieriger. Bei dem einen oder anderen mag das genau umgekehrt sein.

Hier mal eine Solution zum reinen Analysieren.

Wir werden eine Stelle und zwei Funktionen betrachten, die insgesamt für die Validation der Eingabe erforderlich sind.
Unser Beispiel Key zum reversen ist "11111-22222-33333-44444".

Wir setzen dort an, wo der Schlüssel eingelesen wird. Und zwar ist das bei fscanf der Fall.
Folgendes sehen wir:

Code:
004014B8  /$ 8D4C24 04      LEA ECX,DWORD PTR SS:[ESP+4]
004014BC  |. 83E4 F0        AND ESP,FFFFFFF0
004014BF  |. FF71 FC        PUSH DWORD PTR DS:[ECX-4]
004014C2  |. 55             PUSH EBP
004014C3  |. 89E5           MOV EBP,ESP
004014C5  |. 51             PUSH ECX
004014C6  |. 83EC 04        SUB ESP,4
004014C9  |. E8 A2040000    CALL keygenme.00401970
004014CE  |. 83EC 0C        SUB ESP,0C
004014D1  |. 68 63304000    PUSH keygenme.00403063                   ; /format = "serial>"
004014D6  |. E8 ED040000    CALL <JMP.&msvcrt.printf>                ; \printf
004014DB  |. 83C4 0C        ADD ESP,0C
004014DE  |. 68 00204000    PUSH keygenme.00402000                   ;  ASCII "11111-22222-33333-44444"
004014E3  |. 68 6B304000    PUSH keygenme.0040306B                   ; |format = "%s"
004014E8  |. FF35 DC504000  PUSH DWORD PTR DS:[<&msvcrt._iob>]       ; |stream = OFFSET msvcrt._iob
004014EE  |. E8 DD040000    CALL <JMP.&msvcrt.fscanf>                ; \fscanf
004014F3  |. E8 28FFFFFF    CALL keygenme.00401420                   ;  1. Funktion, die Bedingungen prueft
004014F8  |. 83C4 10        ADD ESP,10
004014FB  |. 40             INC EAX
004014FC  |. 74 0C          JE SHORT keygenme.0040150A
004014FE  |. E8 C1FEFFFF    CALL keygenme.004013C4                   ;  2. Funktion die Bedingungen prueft
00401503  |. 3D 28134000    CMP EAX,keygenme.00401328                ;  Die Berechnung dieser Adresse wird erwartet
00401508  |. 74 0E          JE SHORT keygenme.00401518
0040150A  |> B8 01000000    MOV EAX,1
0040150F  |> 8B4D FC        MOV ECX,DWORD PTR SS:[EBP-4]
00401512  |. C9             LEAVE
00401513  |. 8D61 FC        LEA ESP,DWORD PTR DS:[ECX-4]
00401516  |. C3             RETN
00401517  |  90             NOP
00401518  |> FFD0           CALL EAX                                 ;  Der Aufruf der Erfolgsmeldung
0040151A  |. 31C0           XOR EAX,EAX
0040151C  \.^EB F1          JMP SHORT keygenme.0040150F

Aus diesem Teilstück lernen wir:

- Der Serial muss dem scanf-Muster %s genügen. (also eigentliche keine echte Bedingung) Jede Eingabe führt zum Erfolg.

- Die erste Funktion sub_00401420 darf alles außer -1 bzw. 0xFFFFFFFF ergeben um die erste Hürde zu überwinden. (Der Wert wird inkrementiert und die 0 als Ergebnis führt zum Wegsprung "Jump if Zero" (JZ/JE).

- Die zweite Funktion sub_004013C4 muss als Ergebnis den Wert 0x00401328 zurückgeben (CMP EAX, 0x00401328) , welches gleichzeitig die Funktionsadresse der Ergfolgsmeldung darstellt, die später dann auch aufgerufen wird (CALL EAX)

Schauen wir uns die erste Funktion an:

Code:
00401420  /$ 55             PUSH EBP
00401421  |. 89E5           MOV EBP,ESP
00401423  |. 57             PUSH EDI
00401424  |. 83EC 14        SUB ESP,14
00401427  |. BF 00204000    MOV EDI,keygenme.00402000                ;  ASCII "11111-22222-33333-44444"
0040142C  |. 31C0           XOR EAX,EAX
0040142E  |. B9 FFFFFFFF    MOV ECX,-1
00401433  |. F2:AE          REPNE SCAS BYTE PTR ES:[EDI]
00401435  |. 83F9 E7        CMP ECX,-19
00401438  |. 74 0A          JE SHORT keygenme.00401444               ;  Bedingung: Laenge == 23
0040143A  |> B8 FFFFFFFF    MOV EAX,-1
0040143F  |> 8B7D FC        MOV EDI,DWORD PTR SS:[EBP-4]
00401442  |. C9             LEAVE
00401443  |. C3             RETN
00401444  |> 31FF           XOR EDI,EDI                              ;  EDI ist unser Zeiger auf die aktuelle Stelle im String
00401446  |. 66:90          NOP
00401448  |> 83FF 05        CMP EDI,5                                ;  Befinden wir uns an der 5. Stelle?
0040144B  |. 74 0A          JE SHORT keygenme.00401457
0040144D  |. 83FF 0B        CMP EDI,0B                               ;  oder an der 11.?
00401450  |. 74 05          JE SHORT keygenme.00401457
00401452  |. 83FF 11        CMP EDI,11                               ;  oder an der 17.?
00401455  |. 75 41          JNZ SHORT keygenme.00401498
00401457  |> 80BF 00204000 >CMP BYTE PTR DS:[EDI+402000],2D          ;  Wenn ja: Dann muss hier ein Bindestrich hin '-'
0040145E  |.^75 DA          JNZ SHORT keygenme.0040143A
00401460  |> 47             INC EDI
00401461  |. 83FF 17        CMP EDI,17
00401464  |.^75 E2          JNZ SHORT keygenme.00401448
00401466  |. 31D2           XOR EDX,EDX                              ;  Ab hier haben wir die erste Schleife durch alle Zeichen verlassen
00401468  |. 66:31FF        XOR DI,DI
0040146B  |. 90             NOP
0040146C  |> 8A82 00204000  /MOV AL,BYTE PTR DS:[EDX+402000]
00401472  |. 3C 2D          |CMP AL,2D
00401474  |. 74 08          |JE SHORT keygenme.0040147E
00401476  |. 0FBEC0         |MOVSX EAX,AL
00401479  |. 8D0402         |LEA EAX,DWORD PTR DS:[EDX+EAX]          ;  Stellenwert + Wertigkeit des Zeichens
0040147C  |. 31C7           |XOR EDI,EAX                             ;  Diesen Wert geXORed mit EDI des letzten Durchlaufs
0040147E  |> 42             |INC EDX                                 ;  -> Nächste Stelle
0040147F  |. 83FA 17        |CMP EDX,17                              ;  Haben wir alle Zeichen durch?
00401482  |.^75 E8          \JNZ SHORT keygenme.0040146C
00401484  |. B9 03000000    MOV ECX,3
00401489  |. 89F8           MOV EAX,EDI
0040148B  |. 99             CDQ
0040148C  |. F7F9           IDIV ECX                                 ;  Errechneter Wert ganzzahlig durch 3
0040148E  |. 83FA 02        CMP EDX,2                                ;  Wir wollen: Rest 2
00401491  |.^75 A7          JNZ SHORT keygenme.0040143A
00401493  |. 31C0           XOR EAX,EAX
00401495  |.^EB A8          JMP SHORT keygenme.0040143F
00401497  |  90             NOP
00401498  |> 83EC 08        SUB ESP,8
0040149B  |. 0FBE87 0020400>MOVSX EAX,BYTE PTR DS:[EDI+402000]       ;  Wir laden das Zeichen auf das EDI zeigt
004014A2  |. 50             PUSH EAX                                 ; /c
004014A3  |. 68 24304000    PUSH keygenme.00403024                   ; |s ="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
004014A8  |. E8 2B050000    CALL <JMP.&msvcrt.strchr>                ; \strchr
004014AD  |. 83C4 10        ADD ESP,10                               ;  Nur obiger Zeichensatz ist erlaubt
004014B0  |. 85C0           TEST EAX,EAX
004014B2  |.^75 AC          JNZ SHORT keygenme.00401460
004014B4  \.^EB 84          JMP SHORT keygenme.0040143A

Wir lernen, dass für den Schlüssel folgende Bedingungen gelten müssen:

- Die Länge des Schlüssels muss 23 betragen
- Erlaubte Zeichen: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- Spezialfall: An der 5.,11. und 17. Stelle muss ein Bindestrich sein '-'

Die letzte Bedingung dieser Funktion ist nicht so einfach.
Folgendes wird in der 2. Schleife ab (0040146C) berechnet (Delphi):

Code:
function CalcCondition(str: Pchar): Integer;
var
  i: Integer;
  _EDI: DWORD;
begin
  _EDI := 0;
  for i := 0 to Length(str)-1 do
  begin
    if (not (i in [5,11,17])) then
      begin
        _EDI := _EDI xor (ord(str[i]) + i);
      end;
  end;
  result := _EDI mod 3;
end;

Diese Bedingung erfüllt beispielsweise dieser Key:
00000-00000-00000-00005

Das benutzen wir jetzt für den Rest des Crackmes.
Wir sehen, dass wir damit ganz unbescholten durch die erste Funktion kommen. *yeah* xD

Kommen wir also zur zweiten Funktion:

Code:
004013C4  /$ 55             PUSH EBP
004013C5  |. 89E5           MOV EBP,ESP
004013C7  |. 57             PUSH EDI
004013C8  |. 83EC 04        SUB ESP,4
004013CB  |. BF 00204000    MOV EDI,keygenme.00402000                ;  ASCII "00000-00000-00000-00005"
004013D0  |. 31C0           XOR EAX,EAX
004013D2  |. B9 FFFFFFFF    MOV ECX,-1
004013D7  |. F2:AE          REPNE SCAS BYTE PTR ES:[EDI]
004013D9  |. F7D1           NOT ECX
004013DB  |. 49             DEC ECX                                  ;  Laenge nun in ECX
004013DC  |. 31FF           XOR EDI,EDI
004013DE  |. BA 34000000    MOV EDX,34                               ;  Startwert Laufvariable
004013E3  |. 39F9           CMP ECX,EDI
004013E5  |. 76 20          JBE SHORT keygenme.00401407              ;  Laenge groeszer 0? Ja klar!
004013E7  |. 90             NOP
004013E8  |> 8A87 00204000  MOV AL,BYTE PTR DS:[EDI+402000]          ;  Begin der Schleife, Zeichen holen
004013EE  |. 3C 2D          CMP AL,2D                                ;  Bindestrich?
004013F0  |. 74 10          JE SHORT keygenme.00401402
004013F2  |. F7C7 01000000  TEST EDI,1                               ;  Ungerade Stelle?
004013F8  |. 75 1A          JNZ SHORT keygenme.00401414
004013FA  |. 8D1417         LEA EDX,DWORD PTR DS:[EDI+EDX]           ;  Laufvariable += Stelle
004013FD  |. 0FBEC0         MOVSX EAX,AL
00401400  |. 01C2           ADD EDX,EAX                              ;  Laufvariable += Zeichenwert
00401402  |> 47             INC EDI
00401403  |> 39F9           CMP ECX,EDI
00401405  |.^77 E1          JA SHORT keygenme.004013E8
00401407  |> 8D82 000D4000  LEA EAX,DWORD PTR DS:[EDX+400D00]        ;  Muss spaeter addiert mit EDX 0x401328 ergeben (also 0x628)
0040140D  |. 5A             POP EDX
0040140E  |. 5F             POP EDI
0040140F  |. C9             LEAVE
00401410  |. C3             RETN
00401411  |  8D76 00        LEA ESI,DWORD PTR DS:[ESI]
00401414  |> 0FBEC0         MOVSX EAX,AL                             ;  Bei ungerader Stelle:
00401417  |. 01C2           ADD EDX,EAX                              ;  Laufvariable += Zeichenwert
00401419  |. 29FA           SUB EDX,EDI                              ;  Laufvariable -= Stelle
0040141B  |. 47             INC EDI
0040141C  \.^EB E5          JMP SHORT keygenme.00401403

Die Funktion würde in Delphi in etwa so aussehen:

Code:
function CalcAddress(str: Pchar): DWORD;
var
  i: Integer;
  _EDX: DWORD;
begin
  _EDX := $34;
  for i := 0 to Length(str)-1 do
  begin
    if (not (i in [5,11,17])) then
      begin
        if Odd(i) then
          begin
            _EDX := ord(str[i]) + _EDX - i;
          end else
          begin
            _EDX := ord(str[i]) + _EDX + i;
          end;
      end;
  end;
  result := _EDX + $400D00;
end;

Wir haben wieder so etwas wie eine Laufvariable, die jeden Schleifendurchlauf
ihren Wert ändert.
Bei jedem Schleifendurchlauf wird geprüft ob der Schleifenindex ungerade ist.
Ist dies der Fall wird die Laufvariable mit der Zeichenwertigkeit des aktuellen Zeichens
addiert, der Index selbst jedoch subtrahiert.
Im Falle eines geraden Indexs wird der Index addiert.

Am Ende wird die Adressbasis $400D00 dazuaddiert.
Wie wir aus dem ersten Assembler-Abschnitt wissen, muss die Adresse 0x00401328
herauskommen.
Die Schleife muss demnach 0x628 berechnen.

Nun haben wir alles um uns eine gültige Serial zu "kochen".
Das überlasse ich nun aber euch. ;)
 
Zurück
Oben