You are on page 1of 9

Inject code cave - Benina 1

Inject Code Cave

Tác giả: BENINA

(REA TEAM)

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .


Inject code cave - Benina 2

Inject Code Cave

Author : Benina (REA team) - 2010


Blog: Rootbiez.tk

Hi các bạn!

Hôm nay tôi sẽ mô tả kỹ thuật inject code cave ñể bổ sung cho bài viết của bác
meoconlongvang về “Kỹ thuật Inject Code trong Windows”. Trong bài viết của bác
meoconlongvang không nói ñến pp này. Bài viết này ko có gì cao siêu cả, nó chỉ ñể cho tôi lưu trữ
mà thôi.

Phương pháp này ñã ñược Darawk mô tả trong bài viết “Dll Injection Tutorial” (link
http://www.edgeofnowhere.cc/viewtopic.php?p=2483118) . Nhưng trong bài viết trên,
không biết vô tình hay cố ý, tác giả coding ví dụ về phương pháp này bị sai nên code không chạy
ñược. Vì vậy có rất nhiều câu hỏi trên google về phương pháp này. Nhưng tôi search không thấy
câu trả lời nào làm cho code này chạy ñúng. Hôm nay mạo mụi share với các bạn source code
valid.

OK, nói thêm một chút về source trong tut này. Cũng như tất cả các bài viết của tôi. Source code
ñính kèm ñều không chuẩn mực. Một mặt là do lười biếng viết chuẩn xác, một mặt là do kiến thức
bản thân có hạn. Nhưng ñều quan trọng là code trong các ví dụ ñều run tốt trong hoàn cảnh thực
nghiệm như hệ ñiều hành, những giả ñịnh ban ñầu. Mong các bạn bỏ qua phê bình mảng này nhe.

Yêu cầu kiến thức khi ñọc bài viết này:

-Vì là pp này có một chút asm. Nên các bạn cần kiến thức cơ bản về asm là ñược.
-Biết lập trình trong VC++ 6.0, level newbies như tôi chẳng hạn là ñược. Không cần phải pro như
mấy ñại ca trong congdongcviet ñâu.
-Kiên trì ñọc code. Hehe. Tôi tập một thói quen là cố gắng ñọc các ñoạn code như là ñọc tiếng
Việt. Nếu ñọc không hiểu thì tra tự ñiển “google” là hiểu thôi à.

Một số ứng dụng pp này trong thực tế:

-Viết virus (bậy bạ, ở TÙ như chơi)


-Cheat game (nhà nước VN cấm, coi chừng bị phạt)
-v..v…

Ưu ñiểm của pp này:

Theo Darawk pp này là ñỉnh nhất trong các pp inject vì: PP này sẽ làm việc trên bất kỳ version of
windows, và sẽ ít có khả năng gây ra cảnh báo của bất kỳ A/V nào hay làm cho chương trình trục
trặc. Nếu bạn hiểu nó và thực hiện nó một cách ñúng ñắn, pp này dứt khoát là tốt nhất trong 3
methods ñã ñề cập.

A.Lý thuyết và code:

Tôi xin trích (lại copy hehe) ñoạn tut của Darawk, nhưng có thay ñổi một ít code khi tham khảo qua google.

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .


Inject code cave - Benina 3

Thay vì lợi dụng hàm API của Windows ñể ép process load Dll của chúng ta, lần này chúng ta sẽ cấp phát
một chunk (ñoạn) nhỏ memory bên trong ứng dụng target, và inject một ít stub (ñoạn mã nhỏ) mà nó sẽ
load dll của chúng ta. Thuận lợi của kỹ thuật này là nó làm việc trên tất cả các version of windows, và nó
cũng ít bị nhận diện bởi bất kỳ phương pháp nào dò tìm inject code cho ñến thời ñiểm hiện nay (thời ñiểm
mà Darawk viết bài viết này). Stub của chúng ta sẽ như sau:

Code:

__declspec(naked) loadDll(void)
{
_asm{
// Placeholder for the return address
push 0xDEADBEEF
// Save the flags and registers
pushfd
pushad
// Placeholder for the string address and LoadLibrary
push 0xDEADBEEF
mov eax, 0xDEADBEEF
// Call LoadLibrary with the string parameter
call eax
// Restore the registers and flags
popad
popfd

// Return control to the hijacked thread


ret
}
}

0xDEADBEEF là các bytes làm dấu cho các ñịa chỉ mà chúng ta chưa biết chính xác, và chúng ta phải
patching nó lúc runtime. OK chúng ta hảy tạo trình tự thực hiện pp này như sau:

- Cấp phát vùng nhớ cho stub


- Cấp phát vùng nhớ chứa name of dll
- Suspend (tạm dừng) thread chính of target mà chúng ta muốn inject
- Lấy ñịa chỉ của chỉ thị kế tiếp ñược thực thi khi ta suspend thread (cần ñiều này ñể cho bước kế
tiếp)
- Patch ñịa chỉ thích hợp lấy ở bước trên ñể stub return ñiều khiển cho target sau khi inject xong
(patching ñoạn stub)
- Patch ñịa chỉ address of dll name (patching ñoạn stub)
- Patch dịa chỉ address of hàm LoadLibrary (patching ñoạn stub)
- Thiết lập address of chỉ thị kế tiếp ñược thực thi trong thread của target khi ta resume nó, ñó là
address of ñiểm bắt ñầu stub của chúng ta
- Resume target's thread

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .


Inject code cave - Benina 4
Cấp phát vùng nhớ bên trong target, chúng ta sẽ sử dụng hàm VirtualAllocEx(). Chúng ta cần open một
handle cho process với ñặc quyền chỉ ñịnh là VM_OPERATION. Đối với việc cấp phát vùng nhớ chứa dllName
string, chúng ta chỉ cần có các ñặc quyền read và write priveleges . Tuy nhiên ñối với stub , chúng ta cần các
ñặc quyền là read, write, và execute priveleges. Rồi chúng ta sẽ viết dllName string vào vùng nhớ vừa cấp
phát dành cho nó, làm như vậy chúng ta có thể tham chiếu nó từ stub (do stub và dllname cùng trong vùng
không gian ñịa chỉ của target).

Code:

void *dllString, *stub;


unsigned long wowID;
HANDLE hProcess
//See Appendix A for
//this function
wowID = GetTargetProcessIdFromProcname(PROC_NAME);
hProcess = OpenProcess((PROCESS_VM_WRITE | PROCESS_VM_OPERATION), false, wowID);

dllString = VirtualAllocEx(hProcess, NULL, (strlen(DLL_NAME) + 1), MEM_COMMIT,


PAGE_READWRITE);
stub = VirtualAllocEx(hProcess, NULL, stubLen, MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, dllString, DLL_NAME, strlen(DLL_NAME), NULL);

Để hoàn thành các chức năng kế tiếp của kỹ thuật này, chúng ta cần handle của một trong các threads của
target. Chúng ta có thể sử dụng hàm trong phụ lục B (Appendix B). (Tôi Benina xin ghi chú ở ñây một chút,
hàm GetTargetIdFromProcname trong bài viết gốc bị sai, nên tôi có tham khảo trên gooagle và viết lại hàm
này thành hàm GetMainThreadId ).

Lấy ñược ID of một thread như vậy rồi, chúng ta sử dụng hàm OpenThread API ñể lấy handle của nó. Chúng
ta cần phải có quyền get và set context, cũng như suspend và resume thread.

Code:

unsigned long threadID;


HANDLE hThread;
threadID = GetMainThreadId(wowID);
hThread = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT |
THREAD_SUSPEND_RESUME), false, threadID);

Bây giờ chúng ta cần pause thread ñể lấy "context" của nó. Context of một thread là trạng thái hiện hành
của tất cả các thanh ghi của nó, cũng như những thông tin ngoại vi khác. Tuy nhiên, chúng ta phần lớn là
quan tâm ñến thanh ghi EIP register, mà nó trỏ ñến chỉ thị kế tiếp cần thực thi. Vì vậy, nếu chúng ta ko
suspend (tạm dừng) thread trước khi nhận các thông tin context của nó, thì nó sẽ tiếp tục thực thi và khi
chúng ta sử dụng thông tin lấy ñược thì sẽ bị sai (invalid). Một khi chúng ta ñã paused thread lại rồi, chúng
ta sẽ nhận thông tin context bằng cách sử dụng hàm GetThreadContext(). Chúng ta sẽ túm lấy giá trị của
chỉ thị kế tiếp hiện hành ñể thực thi (EIP reg), do ñó chúng ta biết ñược nơi mà stub của chúng ta sẽ return
lại. Bây giờ ñến phần chính là patching stub ñể có ñược tất cả các con trỏ phù hợp, và ta sẽ ép thread thực
thi nó:

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .


Inject code cave - Benina 5

Code:

SuspendThread(hThread);
ctx.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread, &ctx);
oldIP = ctx.Eip;
//Set the EIP of the context to the address of our stub
ctx.Eip = (DWORD)stub;
ctx.ContextFlags = CONTEXT_CONTROL;
//Right now loadDll is code, which isn't writable. We need
//to change that.
VirtualProtect(loadDll, stubLen, PAGE_EXECUTE_READWRITE, &oldprot);
//Patch the first push instruction
memcpy((void *)((unsigned long)loadDll + 1), &oldIP, 4);
//Patch the 2nd push instruction
memcpy((void *)((unsigned long)loadDll + 8), &dllString, 4);
//Patch the mov eax, 0xDEADBEEF to mov eax, LoadLibrary
memcpy((void *)((unsigned long)loadDll + 13), &loadLibAddy, 4);
WriteProcessMemory(hProcess, stub, loadDll, stubLen, NULL); //Write the stub
into the target
//Set the new context of the target's thread
SetThreadContext(hThread, &ctx);
//Let the target thread continue execution, starting at our stub
ResumeThread(hThread);

Bây giờ tất cả các vùng nhớ ñã cấp phát cho target (stub và dllname) ñể inject code không cần thiết nữa.
Chúng ta cần xóa bỏ các tài nguyên này. Nhưng trước khi chúng ta là ñiều ñó, chúng ta sẽ pause chương
trình injector của chúng ta 1 chút, mục ñích là ñể chắc chắn rằng target có thời gian thực thi stub của chúng
ta . Chúng ta sử dụng hàm Sleep() ñể pause 4 giây trước khi unmapping memory mà chúng ta ñã cấp phát,
và thoát injector.

Code:

Sleep(8000);
VirtualFreeEx(hProcess, dllString, strlen(DLL_NAME), MEM_DECOMMIT);
VirtualFreeEx(hProcess, stub, stubLen, MEM_DECOMMIT);
CloseHandle(hProcess);
CloseHandle(hThread);

PP này sẽ làm việc trên bất kỳ version of windows, và sẽ ít có khả năng gây ra cảnh báo của bất kỳ A/V nào
hay làm cho chương trình trục trặc. Nếu bạn hiểu nó và thực hiện nó một cách ñúng ñắn, pp này dứt khoát
là tốt nhất trong 3 methods ñã ñề cập.

Sau ñây là toàn bộ ñoạn code của pp này

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .


Inject code cave - Benina 6

Code:

#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
#define PROC_NAME "hookk.exe"
#define DLL_NAME "IAT1.dll"
unsigned long GetTargetProcessIdFromProcname(char *procName);
unsigned long GetTargetThreadIdFromProcname(char *procName);
DWORD GetMainThreadId( DWORD process_id );
__declspec(naked) loadDll(void)
{
_asm{
// Placeholder for the return address
push 0xDEADBEEF
// Save the flags and registers
pushfd
pushad
// Placeholder for the string address and LoadLibrary
push 0xDEADBEEF
mov eax, 0xDEADBEEF
// Call LoadLibrary with the string parameter
call eax

// Restore the registers and flags


popad
popfd
// Return control to the hijacked thread
ret
}
}

__declspec(naked) loadDll_end(void)
{
_asm{ret}
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int


nCmdShow)
{
void *dllString;
void *stub;
unsigned long wowID, threadID, stubLen, oldIP, oldprot, loadLibAddy;
HANDLE hProcess, hThread;
CONTEXT ctx;
stubLen = (unsigned long)loadDll_end - (unsigned long)loadDll;

loadLibAddy = (unsigned long)GetProcAddress(GetModuleHandle("kernel32.dll"),


"LoadLibraryA");
wowID = GetTargetProcessIdFromProcname(PROC_NAME);

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .


Inject code cave - Benina 7

//////////////////////////////////////
char temp1[256];
wsprintf(temp1,"ProcessID: %x",wowID);
MessageBox(0,temp1,"ProcessID",MB_OK);
///////////////////////////////////////
hProcess = OpenProcess((PROCESS_VM_WRITE | PROCESS_VM_OPERATION), false, wowID);
dllString = VirtualAllocEx(hProcess, NULL, (strlen(DLL_NAME) + 1), MEM_COMMIT,
PAGE_READWRITE);
stub = VirtualAllocEx(hProcess, NULL, stubLen, MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, dllString, DLL_NAME, strlen(DLL_NAME), NULL);
threadID=GetMainThreadId(wowID);
//////////////////////////////////////////
char temp[256];
wsprintf(temp,"threadID: %x",threadID);
MessageBox(0,temp,"threadID",MB_OK);
//////////////////////////////////////////

hThread = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT |


THREAD_SUSPEND_RESUME), false, threadID);
SuspendThread(hThread);
ctx.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread, &ctx);
oldIP = ctx.Eip;
ctx.Eip = (DWORD)stub;
ctx.ContextFlags = CONTEXT_CONTROL;

VirtualProtect(loadDll, stubLen, PAGE_EXECUTE_READWRITE, &oldprot);


memcpy((void *)((unsigned long)loadDll + 1), &oldIP, 4);
memcpy((void *)((unsigned long)loadDll + 8), &dllString, 4);
memcpy((void *)((unsigned long)loadDll + 13), &loadLibAddy, 4);
WriteProcessMemory(hProcess, stub, loadDll, stubLen, NULL);
SetThreadContext(hThread, &ctx);

ResumeThread(hThread);
Sleep(4000);
VirtualFreeEx(hProcess, dllString, strlen(DLL_NAME), MEM_DECOMMIT);
VirtualFreeEx(hProcess, stub, stubLen, MEM_DECOMMIT);
CloseHandle(hProcess);
CloseHandle(hThread);

return 0;
}

unsigned long GetTargetProcessIdFromProcname(char *procName)


{
PROCESSENTRY32 pe;
HANDLE thSnapshot;
BOOL retval, ProcFound = false;

thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);


if(thSnapshot == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Error: unable to create toolhelp snapshot", "Loader", NULL);
return false;
}

pe.dwSize = sizeof(PROCESSENTRY32);

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .


Inject code cave - Benina 8

retval = Process32First(thSnapshot, &pe);


while(retval)
{
if(StrStrI(pe.szExeFile, procName) )
{
ProcFound = true;
break;
}
retval = Process32Next(thSnapshot,&pe);
pe.dwSize = sizeof(PROCESSENTRY32);
}
CloseHandle(thSnapshot);
if(!ProcFound) return 0;
return pe.th32ProcessID;
}

DWORD GetMainThreadId( DWORD process_id )


{
HANDLE hThreadSnap;
THREADENTRY32 th32;
BOOL return_function;
DWORD thread_id;

hThreadSnap = INVALID_HANDLE_VALUE;
return_function = FALSE;
hThreadSnap = \
CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, process_id );
if( hThreadSnap == INVALID_HANDLE_VALUE )
{
MessageBox(NULL, "Error: unable to create toolhelp snapshot", "Loader",
NULL);
return FALSE;
}
th32.dwSize = sizeof( THREADENTRY32 );
if( !Thread32First( hThreadSnap, & th32 ) )
MessageBox(0,"Thread32First error","GetMainThreadId",MB_OK);

do
{
if ( th32.th32OwnerProcessID == process_id )
{
thread_id = th32.th32ThreadID;
return_function = TRUE;
}

}
while
(
Thread32Next( hThreadSnap, & th32 ) && return_function != TRUE
);
CloseHandle( hThreadSnap );
return thread_id;
}

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .


Inject code cave - Benina 9

B.Thực nghiệm :

Dùng trình biên dịch VC++ 6.0 biên dịch ñoạn code trên thành file codecave.exe (chính là trình
injector của chúng ta).

Tôi sử dụng lại 2 file trong tut “IAT Hooking” của tôi làm mô tả cho pp này. (các bạn xem lại bài
tut ñó nhé, tôi sẽ o giải thích nhiều). Và tôi có ñính kèm 2 file ñó theo tut này ñó là : hookk.exe
và IAT1.dll

Một ñiều chú ý ở ñây là : bắt buộc file hookk.exe và IAT1.dll phải nằm cùng một thư mục. Còn file
Codecave.exe nằm ở ñâu cũng ñược. Giải thích ñiều này chính là do sau khi inject stub vào target
và thực thi ñoạn code stub ñể load dll lên memory. Vì stub nằm trong process target nên khi run
thì nó phải lấy dll nằm trong cùng thư mục với nó hoặc thư mục system.

Bây giờ ta chạy hookk.exe ñầu tiên, sẽ hiện ra hộp thoại

Sau ñó ta chạy file codecave.exe ñể inject IAT1.dll vào hookk.exe. Sau khi thực thi xong
codecave.exe. Ta quay trở lại target và bấm button OK ta sẽ thấy target bị hook như sau:

Vậy là hêt tut. Chúc các bạn vui vẽ.

Benina 2010

Tuts fashion 2010 by Rootbiez.tk ver 1.0 .