Autor: DjH | 20.10.2007 |
00401167 . 33C0 XOR EAX, EAX //EAX=0
Oživíme si trochu paměť z první lekce:
00401169 . 33C9 XOR ECX, ECX //ECX=0
0040116B . A1 D3304000 MOV EAX, DWORD PTR DS:[4030D3] //do EAX dej to, co je v EDITu (první 4 znaky)
00401170 . B9 5A415244 MOV ECX, 4452415A //ECX=4452415Ah
00401175 . 33C1 XOR EAX, ECX //Vyxoruj EAX s ECX
00401177 . 33D2 XOR EDX, EDX //vynuluj EDX
00401179 . BB 77770100 MOV EBX, 17777 //EBX=17777h
0040117E . F7F3 DIV EBX //vyděl EBX
00401180 . 35 86140000 XOR EAX, 1486 //Vyxoruj EAX s 1486h
00401185 . /75 1E JNZ SHORT crackme_.004011A5 //Jestli není s/n správné skoč na badboy
00401187 . |6A 00 PUSH 0
00401189 . |68 CC304000 PUSH crackme_.004030CC
0040118E . |68 6A304000 PUSH crackme_.0040306A………
Jak může fungovat funkce DIV jen s jednou zdrojovou proměnnou? Stačí se mrknout do IronScrewa popisu ASM instrukcí (v balíku), a zjistíte, že DIV přesně znamená (a používá se…):
DIV zdroj - AL:=AX div zdroj; AH:=AX mod zdroj
…
kde
- DIV – početní operace; dělení bez lomítka
- MOD – početní operace, která zjistí zbytek po dělení
Tudíž, už v první lekci bylo řečeno:
Libovolné číslo z intervalu <1E19D44A, 1E1B4BC0> stačí xornout hodnotou 4452415Ah a vyjde nám číslo, které převedeme odzadu na ASCII a vyjde nám správný string…
Proč takový interval jsem taky vysvětloval:
1E19D44A – toto je vlastně 1486h * 17777h…
1E1B4BC0 – toto je 1E19D44A + 17776h, takový může být vlastně maximální zbytek v EDX, který s hodnotou 1486h v EAX nemá nic společného.
Stačí tedy deklarovat číslo 1E19D44Ah a k tomu přičíst hodnotu „Random($17777);“ (Delphi - $17776 ne, protože čísla v závorce se stejně nikdy nedosáhne, maximální číslo je tedy $17777 – 1, tedy $17776, mohli bysme tedy napsat $17776 + 1 ale je to zbytečné :)), no a tuto hodnotu xornout 4452415Ah a pomocí vkládaného assembleru vypsat string. Keygen v Delphi by vypadal asi takto:
program Keygen;
uses
Windows,DjH;
var
Serial :int64; //sn musi byt int64, aby mel pro sebe 8bytu (integer ma 4, a nemel by ukonceny string
hexnulou)
PtrSn :pointer;
//Pointer na Serial (push addr serial)
Caption:pchar;
//Caption MsgBoxu
begin
Randomize;
Caption := 'Keygen4CMe1-Programujte.com';
serial := ((($1E19D44A) + Random($17777)) xor $4452415A);
PtrSn := Addr(Serial);
asm
push 0
push caption
push PtrSn
push 0
call messagebox
end;
end.
Po každém spuštění se Vám zobrazí platné sériové číslo. To ale může obsahovat i neplatné znaky, zkusíme si tedy ještě přidat kontrolu každého bajtu:
program Keygen;
uses
Windows,DjH;
var
Serial :int64; //sn musi byt int64, aby mel pro sebe 8bytu (integer ma 4, a nemel by ukonceny string
hexnulou)
PtrSn :pointer;
//Pointer na Serial (push addr serial)
Caption:pchar;
//Caption MsgBoxu
label start; //pozn. autora:
begin //JNGE cíl (je menší)
Randomize; //JNLE cíl (je větší)
start:
Caption := 'Keygen4CMe1-Programujte.com';
serial := ((($1E19D44A) + Random($17777)) xor $4452415A);
PtrSn := Addr(Serial);
asm
push edi
xor edi,edi
//########################################//
@az:
cmp byte ptr ds:[serial+edi], 'a'
jnge @@AZ
@az2:
cmp byte ptr ds:[serial+edi], 'z'
jnge @dale2
//########################################//
@@AZ:
cmp byte ptr ds:[serial+edi], 'A'
jnge @09
@@AZ2:
cmp byte ptr ds:[serial+edi], 'Z'
jnge @dale2
//########################################//
@09:
cmp byte ptr ds:[serial+edi], '0'
jnge start
@092:
cmp byte ptr ds:[serial+edi], '9'
jnge @dale2
//########################################//
pop edi
jmp start
//########################################//
@dale2:
inc edi
cmp edi, 3
jnz @az
pop edi
end;
asm
push 0
push caption
push PtrSn
push 0
call messagebox
end;
end.
Jak vidíte, musíme začít kontrolovat od znaku s největší ASCII hodnotou, a postupně je zmenšovat. Největší ASCII hodnotu mají ‚a‘ – ‚z‘, potom je ‚A‘ – ‚Z‘ a nakonec ‚0‘ – ‚9‘. Pokud je v serialu nalezen byť jeden neplatný ASCII znak, vygeneruje se nové s/n a kontrola se provádí znovu, dokud neprojde v pohodě všemi kontrolami. Podobným způsobem bysme mohli udělat keygen i s oknem a tlačítkem „Generate“, ale myslím, že stačí že je v balíku, je to velmi obdobné a vypisovali bysme pořád dokola ten stejný kód. V balíku je i „Bruteforcer“. Není to sice přesný název, ale dejme tomu… V balíku jsou dva:
1. uloží do souboru VŠECHNY platná s/n (i s netisknutelnými znaky)
2. uloží do souboru jen ty, která mají znaky tisknutelné…
Nyní si uděláme keygen v MASM. V Delphi už jsme „nakousli“ tuto část vkládaným assemblerem. Jestli používáte WinAsm studio (v 12. balíku) vytvořte si nový project s dialogem. Na dialog si dejte jeden button(ID_BTN – id=100) a jeden editbox(ID_EDT – id = 102). Po kliku na ID_BTN bude tato operace:
; --------------------------------------------------
; Po kliku na OK
; --------------------------------------------------
call hash
invoke SetDlgItemText, hWin, ID_EDT , ADDR serial ;nastavi SNtext na SN
Kde hash, je tato funkce:
hash:
;randomize:
ADD ESP, -8
PUSH ESP ;/pPerformanceCount
CALL QueryPerformanceCounter
;\QueryPerformanceCounter
TEST EAX, EAX
JE lbl1
MOV EAX, DWORD PTR SS:[ESP]
MOV DWORD PTR DS:[rndprom], EAX
POP ECX
POP EDX
jmp rnd
lbl1:
CALL GetTickCount ;[GetTickCount
MOV DWORD PTR DS:[rndprom], EAX
POP ECX
POP EDX
rnd:
mov eax, 17777h
;call random:
XOR EBX, EBX
IMUL EDX, DWORD PTR DS:[EBX+rndprom], 8088405h
INC EDX
MOV DWORD PTR DS:[EBX+rndprom], EDX
MUL EDX
MOV EAX, EDX
add eax, 1E19D44Ah
xor eax, 4452415Ah
mov dword ptr ds:[serial], eax
xor edi,edi
;//########################################//
@az:
cmp byte ptr ds:[serial+edi], 'a'
jnge @@AZ
@az2:
cmp byte ptr ds:[serial+edi], 'z'
jnge @dale2
;//########################################//
@@AZ:
cmp byte ptr ds:[serial+edi], 'A'
jnge @09
@@AZ2:
cmp byte ptr ds:[serial+edi], 'Z'
jnge @dale2
;//########################################//
@09:
cmp byte ptr ds:[serial+edi], '0'
jnge rnd
@092:
cmp byte ptr ds:[serial+edi], '9'
jnge @dale2
;//########################################//
jmp rnd
;//########################################//
@dale2:
inc edi
cmp edi, 3
jnz @az
ret
Kompletní kód je samozřejmě v balíku :). Co se týče funkce Randomize a Random(x);. Tyto funkce jsem, jak jinak, zjistil z DeDe, stačí si dekompilovat unitu System.dcu. Pokud chcete mít IntToStr, StrToInt atd. v MASMu, není problém si je tam takto přidat, vždyť je to vlastně assembler, a ten prostě fungovat musí…všude (třeba ‚Randomize‘ z Delphi funguje („dumplá“) v MASM…).
PS: Tento keygen vytvořený v MASM má 3kB (3072b). UPX ho zkomprimovat už nedokáže, ale Upack ano, po zkomprimování Upackem, má program 1,46kB (1500b)!!!
Ještě můžeme do rozpoznávání uMsg dosadit tento řádek:
.elseif uMsg == WM_INITDIALOG ;API - OnCreate ;)
Myslím, že není co dodat k Msg wm_initdialog.
call hash
invoke SetDlgItemText, hWin, ID_EDT , ADDR serial ;hned po startu je v editboxu nahodny serial :)
Po spuštění Bruteru, je jedno, ať už toho, co filtruje printable chars nebo ne, všimněte si jedné věci. Správné sériové číslo je vlastně ve tvaru
„xxHZ“, kde „xx“ jsou úplně libovolné tisknutelné znaky. Tedy například „00HZ“,
„F8HZ“, „4ZHZ“, „ZHHZ“… Funguje i „xxIZ“, ale s čísly je to horší. Ale aspoň platí, že i v tomto případě lze za „x“ dosadit libovolné písmeno. Rázem se dostáváme asi na 1500 řešení (pomocí tisknutelných znaků, malá písmena se CMe automaticky převádějí na velká). Jinak asi na 20‘000 (!) s použitím všech znaků (tedy i netisknutelných).
Myslím si, že tento díl nebyl až tak nezajímavý, jak by se mohlo zprva zdát :)…