CreateRemoteThread函式多引數傳入使用方法

NO IMAGE

注意事項:

1.Debug版本編譯的時候使用增量編譯,導致每個函式都是用一個Thunk, 所以請使用Release版本。

2.目標程序非本程序時不能呼叫本程序內的函式或使用本程序內的變數,有時在隱式使用時可能會引起該

問題,容易引起程序崩潰。(例如WriteProcessMemory寫入的函式中呼叫了本程序的全域性變數)

3.多引數使用時請在目標程序中為函式引數分配相應的記憶體空間,因為CreateRemoteThread第5個引數是LPVOID型,

這意味著它只能放一個指標值,而該指標值應該指向分配的相應記憶體空間。

使用例項:

假設我們呼叫的目標程序的主視窗標題為“ImageCall”, 並假設在偏移該程序首地址0x000163D0有一個用於加血的遊戲函式。

於是我們可以如下所示來使用CreateRemoteThread多引數呼叫來在目標程序中呼叫該加血函式。

(以下使用預設在MFC中,CImageBloodDlg為一個基本對話方塊類,

AddBlood_Inject()為對話方塊上某一按鈕按下時的觸發函式。)

//首先定義全域性標題和偏移地址

LPCTSTR gameCaption = _T("ImageCall");
const int ADD_BLOOD_CALL = 0x000163D0;

//之後是上一篇部落格中寫道的獲取程序首地址的函式

//獲取目標程序首地址
BOOL CImageBloodDlg::getProcessAddr(DWORD dwPID, DWORD& baseAddr)
{
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
// 在目標程序中獲取所有程序的snapshot
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (hModuleSnap == INVALID_HANDLE_VALUE)
{
AfxMessageBox(_T("CreateToolhelp32Snapshot (of modules) fail"));
return(FALSE);
}
// 設定MODULEENTRY32資料結構大小欄位
me32.dwSize = sizeof(MODULEENTRY32);
//檢索第一個模組的資訊,不成功則返回
if (!Module32First(hModuleSnap, &me32))
{
AfxMessageBox(_T("Module32First fail")); // 顯示呼叫失敗
CloseHandle(hModuleSnap);    // 清除控制代碼物件
return(FALSE);
}
// 從me32中得到基址
baseAddr = (DWORD)me32.modBaseAddr;
// 別忘了最後清除模組控制代碼物件
CloseHandle(hModuleSnap);
return(TRUE);
}

//然後我們定義一個獲取目標地址空間真實地址的結構體

typedef struct tagTrueAddr
{
DWORD baseAddr;
DWORD Offset;
}TrueAddr, *PTrueAddr;

//這是我們想在目標程序中進行呼叫的函式(使用了內聯彙編)

void __stdcall addBloodCall(TrueAddr* addr)
{
DWORD baseAddr = addr->baseAddr;
DWORD baseOffset = addr->Offset;
_asm
{
mov eax, baseAddr
add eax, baseOffset
call eax
}
}

//最後我們通過遠端注入來進行CreateRemoteThread的多引數呼叫,按下相應按鈕時觸發該函式

void CImageBloodDlg::AddBlood_Inject()
{
// TODO:  在此新增控制元件通知處理程式程式碼
//得到視窗控制代碼
HWND hwnd = ::FindWindow(NULL, gameCaption);
//得到程序ID
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
//獲取程序訪問許可權
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
//目標程序分配函式引數空間
LPVOID paramsCall = VirtualAllocEx(hProcess, NULL, sizeof(TrueAddr), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (paramsCall == NULL)
{
AfxMessageBox(_T("get paramsCall failed"));
return;
}
//目標程序分配函式本體空間
LPVOID baseCall = VirtualAllocEx(hProcess, NULL, 1000, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (baseCall == NULL)
{
AfxMessageBox(_T("get baseCall failed"));
return;
}
//在函式本體空間中寫入addBloodCall函式本體
if (!WriteProcessMemory(hProcess, baseCall, addBloodCall, 1000, NULL))
{
AfxMessageBox(_T("writeProcessMemory fail"));
return;
}
//得到目標程序首地址
DWORD baseAddr;
getProcessAddr(pid, baseAddr);
//設定真實地址(baseAddr首地址, Offset偏移地址量)
TrueAddr trueAddr;
trueAddr.baseAddr = baseAddr;
trueAddr.Offset = ADD_BLOOD_CALL;
if (!WriteProcessMemory(hProcess, paramsCall, (LPCVOID)&trueAddr, sizeof(TrueAddr), NULL))
{
AfxMessageBox(_T("Write trueAddress failed"));
return;
}
//傳入函式引數並建立遠端執行緒函式
PTrueAddr ptAddr = (PTrueAddr)paramsCall;
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)baseCall, (LPVOID)ptAddr, 0,NULL);
if (!hRemoteThread)
{
AfxMessageBox(_T("CreateRemoteThread failed"));
return;
}
}

先定義一個結構體的資料結構型別,並在該結構中包括所有的函式入口引數,

然後為想寫入的函式提供唯一的一個結構體指標的引數,最後在CreateRemoteThread中

提供該唯一的函式引數,併為該函式引數指標所指的結構體在目標程序中開闢相應的記憶體空間,

並使該指標指向它,通過這種方法我們便可以成功使用多引數函式傳入的CreateRemoteThread呼叫了。