Cracking4neWBies - Lekce č.6

Zdroj: SOOM.cz [ISSN 1804-7270]
Autor: DjH
Datum: 15.8.2007
Hodnocení/Hlasovalo: 1.14/7

Cracking pro začátečníky...pošesté :-)

Cracking4neWBies

Lekce č.6

Tajemství výroby ochran


Teď oddech od crackingu. Na chvíli se vrhneme do programování. Měli byste se alespoň trochu orientovat v Delphi (Pascal), a C++.
Vyrobíme si jedno CMe, v Delphi.
Tajemství spočívá, jak jinak, v "šifrách". V Delphi je základ příkaz Ord(‚písmeno‘)… Samozřejmě my budeme chtít náš…třeba Nick…číst z EditBoxu. To uděláme takto: Ord(Edit1.Text[číslo písmena]);
Dost k teorii, jdeme k příkladu. Takto může vypadat s/n algoritmus:

Var
Sn, one:string;
Cykl:integer;
Begin
Sn:=‘‘;
One:=Edit1.Text;
for cykl:=1 to 18 do
begin
sn:=sn+inttostr(ord(one[cykl])+cykl*cykl-2);
end;
end;


Toto je vážně jednoduchý algoritmus. Ještě jsem neřekl jednu podstatnou věc, a to, co to vlastně ono „Ord“ je. Ord(písmeno) převede písmeno na číslo v ASCII tabulce. Tzn.že Ord(‘A‘) se rovná 65. Ord(‘a‘) je 97…atd…Musíme si uvědomit, že ord je integer, a musíme ho vždy převést příkazem IntToStr…
Provedeme si výpočet prvního písmena v zadaném nicku, a převedeme si ho na první číslo v s/n. Dejme tomu že první písmeno bude ‚D‘…
Ord(‘D‘) := 68
Cykl2 := 1
Cykl2 -1 := -1

68+(-1) := 67…

První číslo v s/n bude tedy ‘67‘
Vyrobil jsem pro tento účel CMe. Source najdete v balíku (i s Keygenem).
Nedivte se zpracování :-). Je to plnohodnotný komprimovací program, ale účel je stejný, dojít ke správnému s/n. Taky mě nemusíte říkat, že mám v source bordel :-)…vím to, a zvykejte si:-)…

Najdete tam tuto „šifrovací“ proceduru:

var
one,two,three,seven,sno,snt,snsf,snf :string;
four,five,six,eigth :integer;
begin
randomize;
if length(Edit1.Text) < 5 then
begin
ShowMessage('Nick musí mít aspoň 5 znaků!');
exit; end;
///////////výpočet serialu/////
begin
one:=Edit1.Text;
one:=one+'ReGiStRaTiOn!';
four:=Length(one);
for five:=1 to 18 do
begin
two:=two+inttostr(ord(one[five])+five*five-2);
end;
seven:=inttostr(ord(two[1]));
seven:=seven[2];
eigth:=((strtoint(seven)div 2)+1);
for six:=1 to 40 do
begin
if (six/3) = (round(six/3)) then
begin
three:=three+((two[six]));
if ((six/eigth) = (round(six/eigth))) and (six<26) then
begin
three:=three+char(six+64);
end;
end;
end;
for six:=1 to 5 do
begin
sno:=sno+three[six];
end;
for six:=1 to 5 do
begin
snt:=snt+inttostr(ord(sno[six]));
end;
for six:=1 to 5 do
begin
snsf:=snsf+snt[six];
end;

if Edit2.Text = ('DjH-'+sno+snsf) then begin regist; end;


if Edit2.Text <> ('DjH-'+sno+snsf) then begin
asm
xor eax,eax
xor ebx,ebx
end;
six:=ord(char(round(random(32)+64)));
showmessage('Špatný reg.kód nebo Nick....'); end;

end;
///////////výpočet serialu/////
end;


Zde vidíte pěkný příklad. Ale řekli byste, že i tento kód má ochranu asi tak 2/10 ?
Proč? Protože se vypočítávané číslo ukládá do paměti, a můžete si ho jednoduše přečíst v OllyDbg. Ochranou proti tomu je to, kontrolovat s/n kousek po kousku (třeba písmeno po písmenu) a nejlépe v hex. podobě, aby v Olly nebyl string čitelný……Místo stringu ‘DjH-‘, jsme mohli napsat Char(68) + Char(106) + Char(72) + Char(45)… vyšlo by to nastejno, ale string by nebyl jen tak čitelný.
Instrukce Char je úplný opak Ord. Z ASCII čísla vyrobí znak. Mohli jsme taky napsat:

If (Ord(Edit2.Text[1]) = 68) and (Ord(Edit2.Text[2]) = 106) and (Ord(Edit2.Text[3]) = 72) and (Ord(Edit2.Text[4]) = 45)

……
Mohli jste také vidět: ……then Begin regist; end;…
Jestli je s/n správné, skočí na proceduru Regist, kde je jen zápis hodnot do souboru… Při načítání se tyto hodnoty načtou, a ověří se jejich správnost, jako kdybyste to s/n zadávali v okně. Jestli program vyhodnotí s/n jako správné, pokračuje cestou kde vypíše v okně třeba ‚Registered to:‘ + vaše jméno… jestli vyhodnotí s/n jako špatné (třeba při prvním spuštění…) pokračuje tak, že do okna napíše třeba ‚Unregistered‘… Mohli jste postřehnout také slabost. Když obskočíme hned toto kontrolování, program se může tvářit jako registrovaný pořád. Necháme program načíst jen Nick, a místo čtení s/n (ze souboru), přepíšeme na jump kde se (např.) vypisuje do okna ‚Registered to:‘.
Zkusíme si moje CMe cracknout, nic složitého to není.
Víme, že program je kompilovaný v Delphi, načteme ho tedy do DeDe. Podíváme se do Procedures, Form3. Vidíme zde dvě tlačítka a dvě procedury. Regist a Before a Button1 a 2.Na obou Buttonech je jeden Call a Ret. Tzn, že toho moc nevyčteme. Ale když se pořádně podíváme, vidíme na Button1Click toto:
* Reference to : TForm3.before()
|
00470ECC E893FCFFFF call 00470B64
00470ED1 C3 ret

Reference to TForm3.Before(). Tzn, že tento button volá proceduru ‚before‘, která vše kontroluje…
Při prohlédnutí procedury Before, vám musí být jasné, že se jedná o kontrolu s/n. Procedura regist, pak jen zapisuje do souboru.
Mrkneme se tedy na adresu z tlačítka Button1, a nastavíme v OllyDbg BP na adresu s CALLem. Spustíme program, a klikneme na Nápověda->Registrace. Zadáme nějaké jméno…Třeba DjH… a jako s/n 123456…klikneme na OK. A krokujeme (do callu vstupte pomocí F7).
00470BA1 |. 83F8 05 CMP EAX, 5
00470BA4 |. 7D 0F JGE SHORT Compress.00470BB5
00470BA6 |. B8 580E4700 MOV EAX, Compress.00470E58 ; ASCII "Nick musí mít aspoň 5 znaků!"
00470BAB |. E8 4C87FBFF CALL Compress.004292FC

Vyhodí nám to hlášku o tom, že nick musí mít aspoň 5 znaků. Nastavíme Nick: DjH2oo7 a s/n ponecháme na 123456.
A krokujeme zas…
00470BC6 |. BA 800E4700 MOV EDX, Compress.00470E80 ; ASCII "ReGiStRaTiOn!"
00470BCB |. E8 603BF9FF CALL Compress.00404730
00470BD0 |. 8B45 FC MOV EAX, [LOCAL.1]

Když proženeme CALL, zjistíme, že k Nicku se nám přidá slovo „ReGiStRaTiOn!“. (např. ‚DjH2oo7ReGiStRaTiOn!‘). Dostaneme se sem:
00470BD3 |. E8 503BF9FF CALL Compress.00404728
00470BD8 |. BB 01000000 MOV EBX, 1
00470BDD |> 8B45 FC /MOV EAX, [LOCAL.1]
00470BE0 |. 0FB64418 FF |MOVZX EAX, BYTE PTR DS:[EAX+EBX-1]
00470BE5 |. 8BD3 |MOV EDX, EBX
00470BE7 |. 0FAFD3 |IMUL EDX, EBX
00470BEA |. 03C2 |ADD EAX, EDX
00470BEC |. 83E8 02 |SUB EAX, 2 //eax=eax-2
00470BEF |. 8D55 DC |LEA EDX, [LOCAL.9]
00470BF2 |. E8 4179F9FF |CALL Compress.00408538
00470BF7 |. 8B55 DC |MOV EDX, [LOCAL.9]
00470BFA |. 8D45 F8 |LEA EAX, [LOCAL.2]
00470BFD |. E8 2E3BF9FF |CALL Compress.00404730
00470C02 |. 43 |INC EBX //ebx:=ebx+1
00470C03 |. 83FB 13 |CMP EBX, 13 //dec:19
00470C06 |.^ 75 D5 \JNZ SHORT Compress.00470BDD
00470C08 |. 8D55 F0 LEA EDX, [LOCAL.4]
00470C0B |. 8B45 F8 MOV EAX, [LOCAL.2]
00470C0E |. 0FB600 MOVZX EAX, BYTE PTR DS:[EAX]
00470C11 |. E8 2279F9FF CALL Compress.00408538

Operace mezi adresami 00470BDD až 00470C06 bychom mohli označit jako
for five:=1 to 18 do
begin
two:=two+inttostr(ord(one[five])+five*five-2);
end;

00470C03 |. 83FB 13 |CMP EBX, 13 //dec:19
Zde vidíme důkaz…Proč je zde 13(19) a ne 12(18)? Protože při 19tém čísle je EBX 18, tzn. že 19ctá operace se neprovádí…
Na adrese 00470C0B nám to vyhodí velkou kupu čísel:
671087964134145102144180169224225283276320338392401
Je to vlastně zatím proměná ‚two‘… Uvidíme co se bude dít dál.
Od adresy 00470C3B – 00470CD4 vidíme
for six:=1 to 40 do
begin
if (six/3) = (round(six/3)) then
begin
three:=three+((two[six]));
if ((six/eigth) = (round(six/eigth))) and (six<26) then
begin
three:=three+char(six+64);
end;
end;

A takhle bychom mohli pokračovat. Uvádím to jen pro orientaci, kdybychom crackovali program a neviděli source, asi ztěží bychom rozeznali která procedura je která. Zkušení crackeři to však dokážou. Nakonec na adrese 00470D6F vidíme CALL, ten vystopujem. Jedná se o CALL který všechny ty čísla přeluští a vytvoří správné s/n. Dalo by se říct že je to tento kód(zvýrazněno zelenou…):
if Edit2.Text = ('DjH-'+sno+snsf) then begin regist; end;

potom si už můžeme na adrese 00470D77 přečíst v EDX správné s/n (DjH-1C7F449675). Kontrola mezi správným a zadaným s/n je na adrese 0040487B (po callu z adresy 00470D77).
Nyní zadáme Nick (DjH2oo7) a správné s/n (DjH-1C7F449675) a kliknem na OK, jestli chcete, tak můžete krokovat, nakonec vám vyskočí hláška o úspěšném zaregistrovaní (mj. odblokuje se vám v CMe možnost dekomprimace :-))
Jestli si vzpomínáte, mluvil jsem o chybě. O chybě, že když se načítá program a kontroluje Nick a s/n, můžete nechat načíst jen nick a místo čtení s/n, přepíšete příkaz na jump který odkazuje na GoodBoy. My si nyní (na tom stejném CMe) pokusíme najít takovou chybu, a napíšeme si patch. Nyní už by se tomu dalo říkat Crack:-).
Vymažeme po úspěšné registraci soubor reg.ini(!!!) a spustíme program („jen tak“ – bez debugování). Dole vidíme napsáno „CompressMan v 1.0:neregistrováno…“. Nyní si načteme program do OllyDbgru a klikneme pravým myšítkem do oblasti s ASM kódem a kliknem na „Search for“ -> „All referenced text strings“ (toto lze také obejít přes program DeDe, ale chci vás naučit s Olly:-)). Najdeme si string „neregistrováno“…je úplně dole na adrese
004718A9 . BA 94194700 MOV EDX, Compress.00471994 ; ASCII "CompressMan v 1.0:neregistrováno..."
Půjdeme po stopách programu, ale nahoru (program nezapínejte!!!). Narazíme na adresu 00471867, kde vidíme po CALLu funkci JNZ a skáče na část s textem neregistrováno. Pokud bysme tento příkaz zaměnili za JE, program by skočil na část GoodBoy s každým špatným s/n. Ale pozor, kdybychom zadali správné s/n, program by jej v tom případě vyhodnotil jako špatné! Od tohoto problému by nás oddělil příkaz NOP. Ale zůstaneme u JE:-). Dvakrát poklepeme na příkaz, a zaměníme jen JNZ za JZ (nebo JE). Spustíme program (F9), a … neregistrováno! Jakto? Protože program nenašel žádný ‚reg.ini‘ soubor(protože jsme ho vymazali)! A tak vytvořil nový. Kdybychom spustili program znovu, Napsal by nám, že je registrován na „1“. Podívejte se do souboru „reg.ini“. Vidíme tam Nick=1 a sn=1. Zjistíme tedy, že naše první spuštění programu (po vymazání reg.ini), je jediná možnost kdy se můžeme zaregistrovat na libovolné jméno. Ale musí nám to „sežrat“ okno Registrace… Takže… Stiskneme [Ctrl+F2] aby se program „resetoval“. Vymazal se nám i patch na té adrese který jsme udělali, ale to nevadí, protože když klikneme na tlačítko [/] objeví se nám tam, ale ve stavu removed. Potom ho stačí dát do stavu aktive a bude to :-), nemusíme tedy nic opět hledat. Takže uděláme práci navíc, a tuto stejnou chybu aplikujeme i na tlačítko pro ověření s/n. Pamatujeme si adresu pro Button1Click :00470ECC. Tam vidíme CALL na adresu 00470B64. Najdeme sui tuto adresu (program je stále vyplý!). Tam jsme vystopovali jump na adrese 00470D7D. Je tam příkaz JNZ, my ho zaměníme za příkaz JZ(JE). Je to vlastně JNZ po kontrole správnosti s/n (resp. po callu). Po tomto JNZ probíhá další kontrola. Pokud bysme to nechali tak, jak to teď máme a uložili, klikli bychom na registrovat, tak by nám to vyplivlo hlášku, že jsme to úspěšně registrovali, a po odkliknutí této hlášky by se objevila nová, že jsme to regli špatně. Ale do souboru se to uloží, takže problém je akorát ten, že např. běžný uživatel by byl zmatený. Proto na adrese 00470DB9 vidíme příkaz JE, které po další kontrole s/n, by způsobyl skok na BadBoy. Přepíšeme tento příkaz na JNZ. Já bych si byl jistý, že tyto tři patchlé byty by měli být všechno. Můžeme si tedy náš cracklý soubor uložit. Olly nám přímo umožňuje soubor uložit. Klikneme pravým myšítkem na ASM kód a kliknem na „Copy to executable“ -> „All modifications“. Zeptá se vás to na otázku. Stiskněte „Copy all“. Objeví se nám nové okno. Na to klikneme opět levým myšítkem a stiskneme „Save to file“. Uložíme si to třeba jako „CMe1[cracked].exe“. Až vše uložíme, minimalizujeme si Ollyho, vymažeme soubor „reg.ini“ a spustíme si náš cracklý soubor. Klikneme na Nápověda->registrovat. Zadáme jakýkoliv nick (DjH2oo7:-)) a jako s/n můžete zadat třeba „na_s/n_ti_jebu!!!“ :-), program to vyhodnotí jako správné s/n…


Uph :+)... Zase jednou záhul.. :-)... V příštím, posledním díle, se mrknem, na tajemství výroby patchů a cracků... Popsalo se to sice trochu již v Lekci č.1, ale skusím to popsat lépe :-)

Zatíííím!! :-)
Zdarec!