Autor: DjH     | 20.10.2007 | 
004011E6   >  5B            POP     EBXA RETN nás hodí do knihovny  USER32.DLL (můžete vyčíst ze stacku: 
004011E7   . |5F            POP     EDI
004011E8   . |5E            POP     ESI
004011E9   . |C9            LEAVE
004011EA   . |C2 1000       RETN    10
0012F4B8   77AB1A10  RETURN to USER32.77AB1A10
A tam už bysme se asi nevyznali, proto si najdeme adresu, kde už se pracuje se stringem. Popojedeme nahoru a vidíme:
00401223   .  83F8 00       CMP     EAX, 0
00401226   .^ 74 BE         JE      SHORT CRACKME1.004011E6
00401228   .  68 8E214000   PUSH    CRACKME1.0040218E  ;  ASCII "DjH2oo7"
0040122D   .  E8 4C010000   CALL    CRACKME1.0040137E  ;  vypocet cisla z
[name]
00401232   .  50            PUSH    EAX
00401233   .  68 7E214000   PUSH    CRACKME1.0040217E  ;  ASCII "123456"
00401238   .  E8 9B010000   CALL    CRACKME1.004013D8  ;  vypocet cisla z
[sn]
0040123D   .  83C4 04       ADD     ESP, 4
00401240   .  58            POP     EAX
00401241   .  3BC3          CMP     EAX, EBX    ;  hodnoty se porovnaji
nastavíme BP na adresu 00401223 a krokujeme. Call na adrese 0040122D vytracujeme pomocí [F7] a skočíme sem:
0040137E  /$  8B7424 04     MOV     ESI, DWORD PTR SS:[ESP+4]        ;  CRACKME1.0040218E
00401382  |.  56            PUSH    ESI
00401383  |>  8A06          /MOV     AL, BYTE PTR DS:[ESI]            ;  tato procedurka zajisti,
00401385  |.  84C0          |TEST    AL, AL                           ;  ze v editboxu pro
00401387  |.  74 13         |JE      SHORT CRACKME1.0040139C          ;  [name] budou jen velka pismena
00401389  |.  3C 41         |CMP     AL, 41
0040138B  |.  72 1F         |JB      SHORT CRACKME1.004013AC
0040138D  |.  3C 5A         |CMP     AL, 5A
0040138F  |.  73 03         |JNB     SHORT CRACKME1.00401394
00401391  |.  46            |INC     ESI
00401392  |.^ EB EF         |JMP     SHORT CRACKME1.00401383
00401394  |>  E8 39000000   |CALL    CRACKME1.004013D2
00401399  |.  46            |INC     ESI
0040139A  |.^ EB E7         \JMP     SHORT CRACKME1.00401383
0040139C  |>  5E            POP     ESI
0040139D  |.  E8 20000000   CALL    CRACKME1.004013C2                 ;  pokud jsou,
skoc...
004013A2  |.  81F7 78560000 XOR     EDI, 5678
004013A8  |.  8BC7          MOV     EAX, EDI
004013AA  |.  EB 15         JMP     SHORT CRACKME1.004013C1
004013AC  |>  5E            POP     ESI
004013AD  |.  6A 30         PUSH    30                                ; /Style =
MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004013AF  |.  68 60214000   PUSH    CRACKME1.00402160                 ; |Title = "No
luck!"
004013B4  |.  68 69214000   PUSH    CRACKME1.00402169                 ; |Text = "No luck there, mate!"
004013B9  |.  FF75 08       PUSH    [ARG.1]                           ; |hOwner
004013BC  |.  E8 79000000   CALL    <JMP.&USER32.MessageBoxA>         ; \MessageBoxA
004013C1  \>  C3            RETN
Zde jsme zjistili, že v EditBoxu name, musí být jen písmena. Jak jsme k tomu došli?
Takto: AL, tedy aktuální znak, který tam přiřadila funkce MOV AL, BYTE PTR DS:[ESI] (z cyklu) se porovnává s hodnotou 41h, tedy 65 dec. což je v ASCII „A“ a hodnota 5Ah je 90 dec, což je „Z“, a pokud není v tomto rozhraní, CMe skočí sem:
004013D2  /$  2C 20         SUB     AL, 20
004013D4  |.  8806          MOV     BYTE PTR DS:[ESI], AL
004013D6  \.  C3            RETN
Tím se zajistí, že kdyby náhodou přišlo na malý znak, odečtením hodnoty 20h (32 dec) dostaneme znak velký, a podprogram skočí zpět. Tam pokračuje v kontrole, a pokud i po „akci podprogramu“ není znak v tomto rozmezí, CMe skočí na badboi. Tedy číslice nepřipadají v úvahu…
Takže necháme CMe projet, a nastavíme jméno  „DJH“ a s/n necháme na
„123456“. 
Nyní si dáme breakpoint na adresu 0040139D protože už jsme si jistí, že k tomuto skoku dojde. Cyklus necháme proběhnout [F9] a Olly se zastaví na tomto BreakPointu. Ten my vytracujeme [F7] a skočíme sem:
004013C2  /$  33FF          XOR     EDI, EDI                          ;  tato procedurka vypocita
Myslím že kód je dostatečně popsaný. Cykl sečte ordinální hodnoty ASCII znaků. Je to něco jako:
004013C4  |.  33DB          XOR     EBX, EBX                          ;  ordinalni soucet ASCII znaku
004013C6  |>  8A1E  /MOV     BL, BYTE PTR DS:[ESI]            ;  v editu
[name]
004013C8  |.  84DB  |TEST BL, BL
004013CA  |.  74 05 |JE      SHORT CRACKME1.004013D1
004013CC  |.  03FB  |ADD     EDI, EBX
004013CE  |.  46    |INC     ESI
004013CF  |.^ EB F5\JMP     SHORT CRACKME1.004013C6
004013D1  \>  C3            RETN
For i:=1 to Length(Edit1.Text) do
Až cyklus skončí, a dorazí na „RETN“, opíšeme si (třeba do poznámkového bloku) hodnotu z EDI. To je vlastně ta hodnota. Převeďte si ji (třeba pomocí Win kalkulačky) na decimální hodnotu, a tu si taky zapište. Se jménem „DJH“ by v EDI mělo být D6h. Super, vše máme, krokujeme tedy dále:
begin
	soucet := soucet + ord(Edit1.Text[i]);
end;
0040139D  |.  E8 20000000   CALL    CRACKME1.004013C2
                ;  pokud jsou, skoc...//program se nam vlastne
vrati…
004013A2  |.  81F7 78560000 XOR     EDI, 5678                         ;  soucet vyxoruj s 5678h
004013A8  |.  8BC7          MOV     EAX, EDI                          ;  a uloz do EAX
004013AA  |.  EB 15         JMP     SHORT CRACKME1.004013C1
Po xoru EDI s 5678h si EDI znovu někam zapište (v hex. i v dec.). Po skoku se dosaneme zpět sem:
00401232   .  50            PUSH    EAX
00401233   .  68 7E214000   PUSH    CRACKME1.0040217E                 ;  ASCII "123456"
00401238   .  E8 9B010000   CALL    CRACKME1.004013D8                 ;  vypocet cisla z
[sn]
A call na adrese 00401238 vytracujeme a dostaneme se sem:
004013D8  /$  33C0          XOR     EAX, EAX
004013DA  |.  33FF          XOR     EDI, EDI
004013DC  |.  33DB          XOR     EBX, EBX
004013DE  |.  8B7424 04     MOV     ESI, DWORD PTR SS:[ESP+4]
004013E2  |>  B0 0A         /MOV     AL, 0A                           ;  prevedeni cisla v [sn] na hex tvar
004013E4  |.  8A1E          |MOV     BL, BYTE PTR DS:[ESI]            ;  ktery se ulozi do registru EDI
004013E6  |.  84DB          |TEST    BL, BL
004013E8  |.  74 0B         |JE      SHORT CRACKME1.004013F5
004013EA  |.  80EB 30       |SUB     BL, 30
004013ED  |.  0FAFF8        |IMUL    EDI, EAX
004013F0  |.  03FB          |ADD     EDI, EBX
004013F2  |.  46            |INC     ESI
004013F3  |.^ EB ED         \JMP     SHORT CRACKME1.004013E2
004013F5  |>  81F7 34120000 XOR     EDI, 1234                         ;  cislo se vyxoruje s cislem 1234h
004013FB  |.  8BDF          MOV     EBX, EDI                          ;  a presune do EBX
004013FD  \.  C3            RETN
Myslím že není co dodat. Cyklus na adrese <004013E2 až 004013F3> vypočítá ze zadaného SN platné číslo, tedy z „123456“ vytvoří v EDI „1E240h“, což je vlastně oněch 123456 v dec. tvaru. Toto EDI se ovšem zase XORuje, tentokráte s hodnotou 1234h a vyxorovaná hodnota se přesune do EBX. RETN nás vrátí sem:
0040123D   .  83C4 04       ADD     ESP, 4
00401240   .  58            POP     EAX
00401241   .  3BC3          CMP     EAX, EBX                          ;  hodnoty se porovnaji
00401243   .  74 07         JE      SHORT CRACKME1.0040124C
A to už je jasné :). Takže to co nám vzniklo po xoru 5678h se musí rovnat číslu v editboxu serial. Toto číslo ale musí být zase vyxorováno hodnotou 1234h a převedeno na dec. Zní to složitě, ale opět je opak pravdou… Možná to pochopíte takto:
For i:=1 to Length(Edit1.Text) do
Kde Edit1 je EditBox pro jméno a Edit2 pro serial. Vlastně máte před sebou už keygen v Delphi :). Takže když už keygen, tak procedura by po úplném zkrácení měla vypadat takto:
begin
    soucet := soucet + ord(Edit1.Text[i]);
end;
    soucet := soucet xor $5678;
    soucet := soucet xor $1234;
    Edit2.Text := IntToStr(Soucet);
procedure ProcGen;
Takto to je v API, keygen je přiložený i se zdrojáky v Delphi bez VCL. Vzhledem k tomu, že plánuju do budoucna více keygenů, rozhodl jsem se i pro šablonu na keygeny v Delphi. Stačí si jen pozměnit proceduru pro výpočet a nějaké stringy, víceméně podívejte se sami do zdrojových kódů :)
var
    sName:string;
//sName - String Name...
    sz,j:integer;
//sz = soucet ord znaku, i = docasna promenna
begin
    sName := GetName;
    sz := 0;
    for j := 1 to i do
    begin
        sz := sz + ord(sName[j]);
    end;
    //sz := sz xor $5678;
    //sz := sz xor $1234;
    //5678h xor 1234h = 444Ch
    sz := sz xor $444C;
    SetDlgItemText(mWnd,id_serial,pchar(IntToStr(sz)));
end;
	Myslím si, že dnes byl díl jeden z nejzajímavějších, hlavně pro nováčky. Kód, co jsme projížděli v Olly jsem vysvětloval jen jednou protože si myslím, že kód byl pochopitelný velmi snadno. Kdyby náhodou jste pořád něco nechápali, pročtěte si článek ještě jednou, nebo tolikrát než to pochopíte. Vězte, že po delším začtení to pochopit musíte :)…