Archive for the ‘Phân tích ASM và code Keygen’ Category


Phân tích ASM và code Keygen

Lời nói đầu :

Sau khi có được cái Keygen Form như trong bài viết trước đã hướng dẫn xây dựng. Trong bài viết tiếp theo này chúng ta sẽ tiến hành việc phân tích ASM của một soft để từ đó tiến hành công việc code keygen :”đầy gian nan nhưng cũng vô cùng thú vị” . Trong bài viết này, chúng ta sẽ sử dụng soft Wallpaper v1.2 mà chú đèn đã viết trong box Newbie (http://www.reaonline.net/forum/showthread.php?t=884) để phục vụ cho việc Keygen. Bài viết này sẽ đối chiếu từng đoạn code tương ứng được nói trong bài viết của chú đèn để mọi người tiện theo dõi. Oki , giờ chúng ta bắt tay vào làm việc.

1. Phân tích ASM của soft trong Ollydbg :

– Để có thể phân tích được ASM của một soft chúng ta cần một chương trình Debug mà cụ thể ở đây là Ollydbg. Chúng ta mở Olly lên và load file Wallpaper.exe vào trong Olly. Tìm và set BP như trong bài viết của chú đèn đã nói. Sau khi đã có được điểm đặt BP tại 00404A35 , chúng ta Run chương trình bằng cách nhấn F9. Tiếp theo chúng tiến hành công việc nhập FU và FS vào trong màn hình Register của chương trình. Ở đây mình nhập như sau : FU : kienmanowar và FS : 11111982. Sau đó nhấn OK , chúng ta sẽ Ice tại điểm mà chúng ta đã set BP. Bước tiếp theo đây sẽ liên quan chặt chẽ tới bài của chú đèn. Chúng ta chú ý tới đoạn code sau :


00404A35 . 8B3B MOV EDI,DWORD PTR DS:[EBX] ; <== FU
00404A37 . B9 FFFFFFFF MOV ECX,-1
00404A3C . 2BC0 SUB EAX,EAX
00404A3E . F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00404A40 . F7D1 NOT ECX
00404A42 . 49 DEC ECX ; <== Length(FU)
00404A43 . 0F84 08010000 JE Wallpape.00404B51 ; <== If Length (FU) = 0 then Jump to Nag

/* Đoạn code này sẽ kiểm tra xem chúng ta có nhập Username không, nếu như không nhập (tức là Length = 0) thì sẽ nhảy tới Nag */

– Đoạn code tiếp theo :

00404A49 . 8B7E 5C MOV EDI,DWORD PTR DS:[ESI+5C] ; <== FS
00404A4C . B9 FFFFFFFF MOV ECX,-1
00404A51 . 2BC0 SUB EAX,EAX
00404A53 . F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00404A55 . F7D1 NOT ECX
00404A57 . 49 DEC ECX ; <== Length (FS)
00404A58 . 0F84 F3000000 JE Wallpape.00404B51 ; <== If Length(FS) = 0 then Jump to Nag

/* Cũng tương tự đoạn code trên , đoạn này dùng để kiểm tra xem chúng ta có nhập Serial vào không */

– Oki , chúng ta đã nhập FU và FS vào rùi nên chúng ta sẽ vượt qua được hai đoạn kiểm tra này. Chúng ta sẽ đến một đoạn code hết sức quan trọng . Đó chính là đoạn Calculation :

00404A5E . 6A 00 PUSH 0 ; /Arg1 = 00000000
00404A60 . 8BCB MOV ECX,EBX ; |
00404A62 . E8 24D40200 CALL Wallpape.00431E8B ; \Wallpape.00431E8B
00404A67 . 50 PUSH EAX ; /Arg1
00404A68 . 8BCE MOV ECX,ESI ; |
00404A6A . E8 21010000 CALL Wallpape.00404B90 ; \Wallpape.00404B90 <==Calculation(trace into)

– Sau khi Trace Into vào trong hàm trên chúng ta sẽ găp một đoạn code như sau :


00404B90 /$ 83EC 2C SUB ESP,2C
00404B93 |. C74424 00 050>MOV DWORD PTR SS:[ESP],5 ; <== Default1 = 5
00404B9B |. C74424 04 030>MOV DWORD PTR SS:[ESP+4],3 ; <== Default2 = 3
00404BA3 |. C74424 08 070>MOV DWORD PTR SS:[ESP+8],7 ; <== Default3 = 7
00404BAB |. C74424 0C 010>MOV DWORD PTR SS:[ESP+C],1 ; <== Default4 = 1
00404BB3 |. 53 PUSH EBX
00404BB4 |. 56 PUSH ESI
00404BB5 |. 57 PUSH EDI
00404BB6 |. BB 88583422 MOV EBX,22345888 ; <== Temp = 0x22345888
00404BBB |. C74424 20 020>MOV DWORD PTR SS:[ESP+20],2 ; <== Default5 = 2
00404BC3 |. C74424 24 060>MOV DWORD PTR SS:[ESP+24],6 ; <== Default6 = 6
00404BCB |. 55 PUSH EBP
00404BCC |. 6A 09 PUSH 9
00404BCE |. C74424 30 040>MOV DWORD PTR SS:[ESP+30],4 ; <== Default7 = 4
00404BD6 |. 33ED XOR EBP,EBP ; <== i = 0
00404BD8 |. 896C24 24 MOV DWORD PTR SS:[ESP+24],EBP ; <== Default8 = 0

/* Chúng ta thấy rằng đây chính là một quá trình khởi tạo giá trị ban đầu sẽ sử dụng để tính toán về sau. Việc đầu tiên nó sẽ khởi tạo các giá trị mặc định là 5 , 3, 7, 1, 0, 2, 6, 4 vào trong một mảng. Để cho tiện chúng ta sẽ gọi mảng chứa những phần tử này là reaDefault [COLOR=Red](int reaDefault[9] = {0x5, 0x3, 0x7, 0x1, 0x0, 0x2, 0x6, 0x4}; ). Tiếp theo chúng ta sẽ thấy chương trình khởi tạo một giá trị khác là 0x22345888 được cất giữ trong thanh ghi EBX , vậy chúng ta gọi một biến là Temp sẽ chứa giá trị này (unsigned long int Temp = 0x22345888smilie . Cuối cùng chúng ta để ý thấy có một lệnh XOR EBP, EBP , lệnh này làm cho giá trị của thanh ghi EBP = 0x0, cho nên ta khai báo một biến i và khởi gán là 0 (i = 0) . Việc tại sao lại đặt tên biến là i sẽ nói ngay dưới đây thôi */[/COLOR]

– Sau khi trace qua đoạn code này, chúng ta sẽ đến một đoạn code tiếp theo:

00404BE1 |. 8B5424 44 MOV EDX,DWORD PTR SS:[ESP+44] ; <== FU
00404BE5 |. 83C4 04 ADD ESP,4
00404BE8 |. 8BF0 MOV ESI,EAX
00404BEA |. 8BFA MOV EDI,EDX ; <== FU
00404BEC |. B9 FFFFFFFF MOV ECX,-1
00404BF1 |. 2BC0 SUB EAX,EAX
00404BF3 |. F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00404BF5 |. F7D1 NOT ECX
00404BF7 |. 49 DEC ECX ; <== Length (FU)
00404BF8 |. 74 1A JE SHORT Wallpape.00404C14

/* Đoạn code này chỉ là việc tính toán lại chiều dài của chuỗi FU mà chúng nhập vào , Length(FU) này sẽ được sử dụng trong quá trình lặp để tính toán tiếp phía dưới */

– Qua đoạn tính toán Length ở trên chúng ta sẽ đến đoạn code tính toán như sau :

00404BFA |> /45 /INC EBP ; <== i = i + 1
00404BFB |. |8BFA |MOV EDI,EDX ; <== FU
00404BFD |. |0FBE442A FF |MOVSX EAX,BYTE PTR DS:[EDX+EBP-1] ; <== FU[i]
00404C02 |. |03D8 |ADD EBX,EAX ; <== Temp = Temp + Fu[i]
00404C04 |. |B9 FFFFFFFF |MOV ECX,-1
00404C09 |. |2BC0 |SUB EAX,EAX
00404C0B |. |F2:AE |REPNE SCAS BYTE PTR ES:[EDI]
00404C0D |. |F7D1 |NOT ECX
00404C0F |. |49 |DEC ECX ; <== Length (FU)
00404C10 |. |3BCD |CMP ECX,EBP ; <== While (Length(FU) > i)
00404C12 |.^\77 E6 \JA SHORT Wallpape.00404BFA ; <== Continue Loop

/* Tại đoạn code trên, chúng ta quan sát thấy quá trình tăng dần của thanh ghi EBP , sau đó giá trị này sẽ được đem đi so sánh với với chiều dài của FU đã đựơc cất trong ECX trong đoạn code tính Length ở trên.Đó chính là lí do tại sao chúng ta lại khai báo một biến [COLOR=Red]i . Vòng lặp này sẽ thực hiện công việc như sau , nó sẽ lấy từng kí tự trong FU sau đó thực hiện một phép cộng dồn với biến Temp (Biến Temp lúc này đang lưu giá trị là 0x22345888). Vòng lặp này sẽ thực hiện bao nhiêu lần là tùy thuộc vào vào chiều dài của chuỗi FU. Kết quả sau khi tính toán được sẽ được biến Temp lưu giữ */

Trong VC++ vòng lặp tính toán cộng dồn trên được viết như sau :

//Caculation
i = 0;
while ( i < LenUser )
{
Temp = Temp + (reaName[i] & 0xFF);
i++;
}

– Sau khi qua khỏi vòng lăp này chúng ta sẽ đến đoạn code sau :


00404C18 |. 53 PUSH EBX ; <== Temp
00404C19 |. 68 D0044400 PUSH Wallpape.004404D0 ; ASCII "%08X"
00404C1E |. 50 PUSH EAX
00404C1F |. E8 AC6B0100 CALL Wallpape.0041B7D0 ; <== TempString = Convert(Temp) to String

/* Đoạn code trên đây làm công việc chuyển đổi giá trị chứa trong biến Temp . Cụ thể là nó sẽ chuyển giá trị trong biến Temp sang chuỗi ở dạng Hexa như định dạng mà nó đã đưa ra trong lệnh [COLOR=Red]PUSH (ASCII “%08X”) . Ví dụ như sau : với chuỗi FU là kienmanowar , sau vòng lặp cộng dồn chúng ta có được biến Temp chứa giá trị sau : 0x22345D24 , sau quá trình chuyển đổi này ta sẽ có một chuỗi là “22345D24”. Chuỗi này chúng ta lưu trữ trong một biến là TempString (char reaTempString[64] = {0}; ) */

Đoạn code chuyển đổi được viết lại trong VC++ như sau :
//Convert Temp value to Hex String
wsprintf(reaTempString, "%08X",Temp);

– Trace tiếp chúng ta sẽ đến đoạn code dưới đây :


00404C27 |. 33C0 XOR EAX,EAX ; <== i = 0
00404C29 |> 8B4C84 10 /MOV ECX,DWORD PTR SS:[ESP+EAX*4+10]; <== j = Default [i]
00404C2D |. 40 |INC EAX ; <== i = i + 1
00404C2E |. 83F8 08 |CMP EAX,8
00404C31 |. 8A4C0C 30 |MOV CL,BYTE PTR SS:[ESP+ECX+30] ; <== TempString[j]
00404C35 |. 884C06 FF |MOV BYTE PTR DS:[ESI+EAX-1],CL ; <== reaTempSerial[i] = TempString[j]
00404C39 |.^ 72 EE \JB SHORT Wallpape.00404C29

/* Trong đoạn code trên chúng ta nhận thấy lại có quá trình tăng dần của EAX cho nên ta lại khởi gán biến i = 0 . Vòng lặp này làm nhiệm vụ như sau, nó sẽ lấy các giá trị trong biến reaTempString theo những vị trị đã được khai báo trong biến reaDefault. Mà để lấy được các giá trị trong biến reaDefault chúng cần một biến j . Do đó ta khai báo thêm một biến j . Vòng lặp này sẽ được thực hiện [COLOR=Red]8 lần , để cho ra một chuỗi chứa trong reaTempSerial , mà thực chất chuỗi này chỉ là xáo trộn các vị trí trong reaTempString mà thôi. Thực ra sau quá trình này thì reaTempSerial chính là Real Serial , nhưng để dễ hiểu ta cứ gọi là reaTempSerial */

Đoạn code tương ứng được viết lại trong VC++ :

//Creat reaTempSerial
i = 0;
while (i < 8)
{
j = reaDefault[i];
reaTempSerial[i] = reaTempString[j];
i++;
}

– Cuối cùng sau khi chúng ta qua khỏi vòng lặp này chúng ta sẽ đi đến đích cuối cùng :


00404C3B |. 8BC6 MOV EAX,ESI ; <== Right Serial

/* Chúng ta sẽ thấy được Right Serial ngay tại câu lệnh trên */

Đoạn code tương ứng trong VC++ :

wsprintf(reaSerial,reaTempSerial);

2. Code Keygen trong VC++ :

Dựa vào tất cả những gì đã phân tích rất chi tiết ở trên , chúng ta chỉ việc ghép lại để có một đoạn code Keygen hoàn chỉnh như sau :


void CKEYGENDlg::OnGenerate()
{
// TODO: Add your control notification handler code here

char reaName[64]={0};
char reaSerial[64]={0};
char reaTempString[64] = {0};
char reaTempSerial[64] = {0};

unsigned long int Temp = 0x22345888;
int reaDefault[9] = {0x5, 0x3, 0x7, 0x1, 0x0, 0x2, 0x6, 0x4};
int i=0,j=0, LenUser=0;

LenUser=GetDlgItemText(IDC_NAME,reaName,128);
if (LenUser < 1 || LenUser > 64)
{
MessageBox(” ———-===== Your name atleast 1 chart =====———- \n\n———-===== But not over than 64 charts =====———- “,”Hey !! Please input your name again !! “);
}
else
{

// Calculation
i = 0;
while ( i < LenUser )
{
Temp = Temp + (reaName[i] & 0xFF);
i++;
}

//Convert Temp value to Hex String
wsprintf(reaTempString, “%08X”,Temp);

//Creat reaTempSerial
i = 0;
while (i < 8)
{
j = reaDefault[i];
reaTempSerial[i] = reaTempString[j];
i++;
}

wsprintf(reaSerial,reaTempSerial);
}

SetDlgItemText(IDC_SERIAL,reaSerial);

}

Trên đây một trong số các cách thức để một cracker thực hiện công việc code keygen . Sau khi bạn đọc xong bài viết này , bạn có cảm thấy dễ dàng hơn không? smilie . Thực sự Keygen nhiều lúc không đơn giản , nó đòi hỏi cả một nghệ thuật để làm sao đoạn code ngắn gọn , trong sáng nhất. Trong 4room này thì anh Moon là người có khả năng đó (và còn nhiều người khác nữa trong đội ngũ BQT của REA :wub: ). Em chỉ là Super Newbie học đòi theo anh Moon mà thôi. Hi vọng với bài viết với tinh thần truyền tải những gì hết sức basic như trên , các bạn sẽ định hình được phần nào công việc code Keygen của mình.

03/03/2005

Best Regards

kienmanowar