Trao đổi với tôi

http://www.buidao.com

12/9/10

[Reverse] Phân Tích RCA Crackme Bằng Olly Và IDA+HexRays

RefLink: http://kienmanowar.wordpress.com/category/my-tutorials/ida-tutorials/

Trên REA lão RCA có code một cái crackme nhỏ và ra đề như sau :

+ Crackme level 1.1 by Rongchaua.
- Level : Very very easy.
- Aim :
. Thực tập cách set breakpoint.
. Thực tập patch.
. Mã Ascii.

Rảnh rỗi ngồi phân tích thử xem thế nào!

Dùng PeID check thử, hú hồn may quá lão Rồng nhà ta không pack, thoát khỏi bước đầu tiên. Đọc Rulz và run thử Crackme thì thấy nút Checkit bị Disable mất tiêu. Soi lại list các hàm API thường dùng cho Window thì thấy có em EnableWindow (đây mới chỉ là nghi thôi)

The EnableWindow function enables or disables mouse and keyboard input to the specified window or control. When input is disabled, the window does not receive input such as mouse clicks and key presses. When input is enabled, the window receives all input.

BOOL EnableWindow(
HWND hWnd, // handle to window
BOOL bEnable // flag for enabling or disabling input
);

Parameters
hWnd
Identifies the window to be enabled or disabled.
bEnable
Specifies whether to enable or disable the window. If this parameter is TRUE, the window is enabled. If the parameter is FALSE, the window is disabled.

Okie đã có thông tin cho sol đầu tiên “Patch to enable the button”, load crackme vào trong Olly. Tại Olly , chuột phải và chọn Search all intermodular calls, ta sẽ thấy được một list các danh sách API mà lão Rồng dùng :

Found intermodular calls
Address Disassembly Destination
00401000 Cr PUSH 0 (Initial CPU selection)
004010FE CALL user32.CreateDialogParamA
00401035 CALL user32.DialogBoxParamA
0040107D CALL user32.EnableWindow
0040108C CALL user32.EnableWindow
004011D5 CALL user32.EnableWindow
004011EF CALL user32.EnableWindow
00401113 CALL user32.EndDialog
00401125 CALL user32.EndDialog
0040123C CALL user32.EndDialog
0040103B CALL kernel32.ExitProcess
0040106C CALL user32.GetDlgItem
004011C8 CALL user32.GetDlgItem
004011E2 CALL user32.GetDlgItem
004010BA CALL user32.GetDlgItemTextA
004010D1 CALL user32.GetDlgItemTextA
00401002 CALL kernel32.GetModuleHandleA
00401017 CALL user32.LoadIconA
0040118C CALL kernel32.lstrcatA
004011A3 CALL kernel32.lstrcmpA
0040115F CALL kernel32.lstrlenA
004011BB CALL user32.MessageBoxA
00401205 CALL user32.MessageBoxA
00401140 CALL ntdll.RtlZeroMemory
0040114F CALL ntdll.RtlZeroMemory
0040105F CALL user32.SendMessageA
0040122A CALL user32.SendMessageA
0040117A CALL user32.wsprintfA

_Ái chà có tới 4 hàm EnableWindow lận, vậy là không còn nghi ngờ gì nữa rồi.Chuột phải vào 1 hàm EnableWindow bất kì và chọn Set BP on every call to EnableWindow. Kết thúc quá trình Set BP, bây h nhấn F9 để Run cracke. Bụp, Olly đã break tại 1 hàm EnableWindow và đây chính là hàm cần Patch. Lý do tại sao tôi khẳng định được là vì khi form crackme của lão Rồng được load lên thì hàm EnableWindow sẽ được load theo để Disable cái nút nhấn Checkit :

00401087 |> \6A 00 PUSH 0 ; /Enable = FALSE <== Patch here
00401089 |. FF75 FC PUSH DWORD PTR SS:[EBP-4] ; |hWnd
0040108C |. E8 D3010000 CALL ; \EnableWindow <== Break here

_Dịch lên 1 chút ta thấy PUSH 0, và thấy Olly comment bên cạnh. Do đó ta sẽ patch lại thành PUSH 1 . (0 và 1 ở đây thực chất là các cờ dùng cho việc set disable hoặc enable)

_Patch xong save lại và Run .. Done nút Checkit đã được Enable lên. Vậy là đã giải quyết được bài tập thứ nhât!

_Tiếp theo là đến bài tập tìm Real Serial. Như trong danh sách các APIs mà lão Rồng dùng ở trên, chúng ta để ý thấy có hàm GetDlgItemTextA. Check lại info về hàm này :

The GetDlgItemText function retrieves the title or text associated with a control in a dialog box.
UINT GetDlgItemText(
HWND hDlg, // handle of dialog box
int nIDDlgItem, // identifier of control
LPTSTR lpString, // address of buffer for text
int nMaxCount // maximum size of string
);

_Okie vậy là ta set 1 BP tại GetDlgItemTextA. Sau đó nhấn F9 để run Crackme và nhập thông tin về UserName và Fake Serial vào. Cuối cùng nhấn CheckIt, ta sẽ break tại đây trong Olly :

004010A8 |. 68 00010000 PUSH 100 ; /Count = 100 (256.)
004010AD |. 68 68304000 PUSH CrackMe.00403068 ; |Buffer = CrackMe.00403068
004010B2 |. 68 EC030000 PUSH 3EC ; |ControlID = 3EC (1004.)
004010B7 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004010BA |. E8 B7010000 CALL ; \GetDlgItemTextA <== Break here

Tại cửa sổ Stack :

0012FB1C 000C019E |hWnd = 000C019E ('CrackMe Level 1.1 by Rongchaua',class='#32770')
0012FB20 000003EC |ControlID = 3EC (1004.)
0012FB24 00403068 |Buffer = CrackMe.00403068
0012FB28 00000100 \Count = 100 (256.)

_UserName nhập vào của chúng ta sẽ được lưu tại Buffer trên. F8 để trace qua hàm , tại cửa sổ Dump ta có :

00403068  6B 69 65 6E 6D 61 6E 6F 77 61 72 00 00 00 00 00  kienmanowar.....

_Tương tự với hàm tiếp theo ta cũng có được kết quả FakeSerial được lưu vào một Buffer khác :

00403368  31 31 31 31 31 39 38 32 00 00 00 00 00 00 00 00  11111982........

_Sau khi Trace xong ta tới đây :


004010D6 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; /Arg1
004010D9 |. E8 52000000 CALL CrackMe.00401130 ; \CrackMe.00401130 <== Trace Into

_ Trace Into vào hàm Call ở trên, ta sẽ tới quá trình tính toán sinh ra Real Serial.

00401130 /$ 55 PUSH EBP
00401131 |. 8BEC MOV EBP,ESP
00401133 |. 83C4 F8 ADD ESP,-8
00401136 |. 68 00010000 PUSH 100 ; /Length = 100 (256.)
0040113B |. 68 68314000 PUSH CrackMe.00403168 ; |Destination = CrackMe.00403168
00401140 |. E8 55010000 CALL ; \RtlZeroMemory
00401145 |. 68 00010000 PUSH 100 ; /Length = 100 (256.)
0040114A |. 68 68324000 PUSH CrackMe.00403268 ; |Destination = CrackMe.00403268
0040114F |. E8 46010000 CALL ; \RtlZeroMemory
00401154 |. BE 68324000 MOV ESI,CrackMe.00403268 ; ASCII "6b"
00401159 |. BF 68304000 MOV EDI,CrackMe.00403068 ; <== FU
0040115E |. 57 PUSH EDI ; /String => "kienmanowar"
0040115F |. E8 48010000 CALL ; \lstrlenA
00401164 |. 8BD8 MOV EBX,EAX ; <== Len = Length(User Name)
00401166 |. 33C0 XOR EAX,EAX ; <== i == 0
00401168 |> 50 /PUSH EAX ; <== i
00401169 |. 53 |PUSH EBX ; <== Len
0040116A |. 57 |PUSH EDI ; <== FU
0040116B |. 0FBE0C38 |MOVSX ECX,BYTE PTR DS:[EAX+EDI] ; <== Temp = FU [i]
0040116F |. 51 |PUSH ECX ; /<%x>
00401170 |. 68 59304000 |PUSH CrackMe.00403059 ; |Format = "%x"
00401175 |. 68 68314000 |PUSH CrackMe.00403168 ; |s = CrackMe.00403168
0040117A |. E8 D3000000 |CALL ; \wsprintfA
0040117F |. 83C4 0C |ADD ESP,0C
00401182 |. 68 68314000 |PUSH CrackMe.00403168 ; /StringToAdd = "69"
00401187 |. 68 68324000 |PUSH CrackMe.00403268 ; |ConcatString = "6b"
0040118C |. E8 0F010000 |CALL ; \lstrcatA
00401191 |. 5F |POP EDI
00401192 |. 5B |POP EBX
00401193 |. 58 |POP EAX
00401194 |. 40 |INC EAX ; <== i++
00401195 |. 3BC3 |CMP EAX,EBX ; <== While i < Len
00401197 |.^ 7C CF \JL SHORT CrackMe.00401168 ; <== Continue
00401199 |. 68 68334000 PUSH CrackMe.00403368 ; /String2 = "11111982"
0040119E |. 68 68324000 PUSH CrackMe.00403268 ; |String1 = "6b"
004011A3 |. E8 FE000000 CALL ; \lstrcmpA
004011A8 |. 0BC0 OR EAX,EAX
004011AA |. 75 4A JNZ SHORT CrackMe.004011F6
004011AC |. 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004011AE |. 68 00304000 PUSH CrackMe.00403000 ; |Title = "Reverse Engineering Association"
004011B3 |. 68 20304000 PUSH CrackMe.00403020 ; |Text = "Congratulation! You've done with it"
004011B8 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
004011BB |. E8 C2000000 CALL ; \MessageBoxA
004011C0 |. 68 EC030000 PUSH 3EC ; /ControlID = 3EC (1004.)
004011C5 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011C8 |. E8 A3000000 CALL ; \GetDlgItem
004011CD |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
004011D0 |. 6A 00 PUSH 0 ; /Enable = FALSE
004011D2 |. FF75 FC PUSH DWORD PTR SS:[EBP-4] ; |hWnd
004011D5 |. E8 8A000000 CALL ; \EnableWindow
004011DA |. 68 ED030000 PUSH 3ED ; /ControlID = 3ED (1005.)
004011DF |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011E2 |. E8 89000000 CALL ; \GetDlgItem
004011E7 |. 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX
004011EA |. 6A 00 PUSH 0 ; /Enable = FALSE
004011EC |. FF75 F8 PUSH DWORD PTR SS:[EBP-8] ; |hWnd
004011EF |. E8 70000000 CALL ; \EnableWindow
004011F4 |. EB 14 JMP SHORT CrackMe.0040120A
004011F6 |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004011F8 |. 68 00304000 PUSH CrackMe.00403000 ; |Title = "Reverse Engineering Association"
004011FD |. 68 44304000 PUSH CrackMe.00403044 ; |Text = "No,no! Try it again!"
00401202 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401205 |. E8 78000000 CALL ; \MessageBoxA
0040120A |> C9 LEAVE
0040120B \. C2 0400 RETN 4

_ Tóm tắt đoạn code này như sau :
Đây là một vòng lặp dùng để chuyển từng kí tự trong chuỗi user name mà chúng ta nhập vào sang dạng mã Hexa của kí tự đó ( ví dụ : k — 0x6b). Sau quá trình trace khỏi vòng lặp ta được kết quả như sau :

EAX 0000000B <== length(FU)
ECX 77E74BAE kernel32.77E74BAE
EDX 00000003
EBX 0000000B
ESP 0012FB18
EBP 0012FB20
ESI 00403268 ASCII "6b69656e6d616e6f776172" <== after converted
EDI 00403068 ASCII "kienmanowar" <== String to convert

_Chuỗi sau khi tính toán sẽ được đem đi so sành với Serial mà chúng ta nhập vào. Đúng thì Ok, còn sai thì đi bụi

00401199 |. 68 68334000 PUSH CrackMe.00403368 ; /String2 = "11111982"
0040119E |. 68 68324000 PUSH CrackMe.00403268 ; |String1 = "6b69656e6d616e6f776172"
004011A3 |. E8 FE000000 CALL ; \<== Compare two Strings

Kết quả cuối cùng :

User Name : kienmanowar Serial    : 6b69656e6d616e6f776172

Phần tiếp theo là dùng IDA+HexRays :

Cái tên HexRays sau khi xuất hiện trên site của lão Ilfak Guilfanov (http://www.hexblog.com/) đã làm cho giới RE kinh thiên động địa mặc dù lúc đó nó mới chỉ là trong ý tưởng.Đến sau này khi lão release sản phầm và public trên một site khác (http://www.hex-rays.com/) càng làm cho “giang hồ” thèm muốn. Nhưng cái giá mà lão đưa ra để có được HexRays thì quá chua chát : “A single user license of the Hex-Rays Decompiler is 2299USD (1500EUR)”. Với số tiến như thế này thì có mà thách đố, chắc đi cày đến hết đời mới mua nổi em nó về dùng. Cũng có một số team có ý định và đã bỏ tiền ra mua HexRays về sử dụng…. nhưng rồi đến một ngày, một ngày mà cả giới RE phải online để download hàng. Đó chỉnh là hôm YAG Team public hexrays cho bà con download….. Hơ hơ nhưng mà tool xịn có trong tay rồi, giờ dùng sao ta ? Không lẽ down về để đó ngó chơi…… hơn 2 ngàn $ chứ có ít đâu . Hôm nay tập dùng HexRays bèn lấy em crackme của lão Rồng ra vọc thử .. Thấy đại ca Còm dùng HexRays hay quá nên ráng đua đòi ..nhưng cũng chỉ dừng ở mức cưỡi ngựa xem hoa chứ chưa dám chạy trước . Và đây là kết quả cuối cùng sau một hồi ngồi phân tích bằng IDA + HexRays :

int __stdcall DialogFunc(HWND a1, UINT a2, WPARAM a3, LPARAM a4)
{
HWND v5; // eax@2
HWND hWnd; // [sp+0h] [bp-4h]@2

switch ( a2 )
{
case 0x110u:
SendMessageA(a1, 0x80u, 1u, lParam);
v5 = GetDlgItem(a1, 1006);
hWnd = v5;
if ( v5 )
EnableWindow(hWnd, FALSE); // disable checkit button
else
EnableWindow(hWnd, TRUE);
break;
case 0x111u:
switch ( a3 )
{
case 0x3EEu:
GetDlgItemTextA(a1, 1004, szName, 256);
GetDlgItemTextA(a1, 1005, szSerial, 256);
convert_szName_to_hex(a1);
break;
case 0x3F0u:
CreateDialogParamA(hInstance, (LPCSTR)0x3F1, a1, sub_40120E, 0);
break;
case 0x3EFu:
EndDialog(a1, 0);
break;
}
break;
case 0x10u:
EndDialog(a1, 0);
break;
}
return 0;
}

void __stdcall convert_szName_to_hex(HWND hDlg)
{
int i; // eax@1
int iLen; // ebx@1
int iIndex; // ST14_4@2
HWND v4; // eax@4
HWND v5; // eax@4

RtlZeroMemory(szBuf, 256); // Set a block of memory with 0's.
RtlZeroMemory(szHex, 256);
iLen = lstrlenA(szName);
i = 0;
do
{
iIndex = i;
wsprintfA(szBuf, "%x", szName[i]); // convert szName[i] to Hexa and store in a szBuf
lstrcatA(szHex, szBuf); // append szBuf to szHex
i = iIndex + 1;
}
while ( iIndex + 1 < iLen );
if ( lstrcmpA(szHex, szSerial) ) // compare szHex with szSerial
{
MessageBoxA(hDlg, "No,no! Try it again!", "Reverse Engineering Association", 0);
}
else
{
MessageBoxA(hDlg, "Congratulation! You've done with it", "Reverse Engineering Association", 0);
v4 = GetDlgItem(hDlg, 1004);
EnableWindow(v4, 0);
v5 = GetDlgItem(hDlg, 1005);
EnableWindow(v5, 0);
}
}