004011E6 > 5B POP EBX
A 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 :)…