今天实现一波Hook目标进程的MessageBoxA,当然其他的API都是一样的。
Hook API和DLL注入很像,唯一不同点在于DLL内的操作:
// hook_dll.cc
#include <iostream>
#include <windows.h>
#ifndef HOOK_DLL_API
#define HOOK_DLL_API extern "C" __declspec(dllimport)
BYTE originalCode[6] = {0};
LPVOID messageBoxAddress;
int WINAPI HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption,
UINT uType) {
std::cout << "HookedMessageBoxA" << std::endl;
WriteProcessMemory(GetCurrentProcess(), messageBoxAddress, originalCode,
sizeof(originalCode), NULL);
MessageBoxA(hWnd, lpText, lpCaption, uType);
return 0;
}
DWORD WINAPI HookMessageBoxA() {
HINSTANCE library = LoadLibraryA("user32.dll");
// get address of the MessageBox function in memory
messageBoxAddress = (LPVOID)GetProcAddress(library, "MessageBoxA");
memcpy(originalCode, messageBoxAddress, 6);
ReadProcessMemory(GetCurrentProcess(), messageBoxAddress, originalCode, 6,
NULL);
BYTE shellcode[6] = {
0x68, 0x0, 0x0, 0x0, 0x0, // PUSH 0x00000000
0xC3 // RETN
};
void *HookedMessageBoxAAddress = (LPVOID)&HookedMessageBoxA;
std::cout << "HookedMessageBoxA" << (DWORD)&HookedMessageBoxA << std::endl;
memcpy(&shellcode[1], (LPVOID)&HookedMessageBoxAAddress, 4);
WriteProcessMemory(GetCurrentProcess(), messageBoxAddress, shellcode,
sizeof(shellcode), NULL);
for (int i = 0; i < sizeof(shellcode); i++) {
std::cout << std::hex << int(shellcode[i]);
}
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved) // reserved
{
// Perform actions based on the reason for calling.
switch (fdwReason) {
case DLL_PROCESS_ATTACH: {
// Initialize once for each new process.
// Return FALSE to fail DLL load.
HANDLE thread = CreateThread(
NULL, 0, (LPTHREAD_START_ROUTINE)&HookMessageBoxA, NULL, 0, NULL);
CloseHandle(thread);
break;
}
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
if (lpvReserved != nullptr) {
break; // do not do cleanup if process termination scenario
}
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
#endif // HOOK_DLL_API
这里重点关注下HookMessageBoxA这个函数,这里面构造了一个6字节的shellcode,执行了一个push和一个ret,这俩就相当于一个jmp。但我们为啥没直接用jmp呢,因为jmp后面要接一个offset,我们不去算这个offset就直接用一个push+ret代替。
然后我们把push后面的地址换成HookedMessageBoxA函数指针,注意memcpy的时候要对HookedMessageBoxA函数指针再取地址,这样HookedMessageBoxA函数指针就会写入到push后面。我一开始直接把&HookedMessageBoxA作为参数传进了memcpy,然后发现拼接好的shellcode一直不对。
拼接好之后先保存下MessageBoxA开头的6个字节,为了后面自定义操作之后调用MessageBoxA。然后使用MessageBoxA将shellcode写入MessageBoxA。
HookedMessageBoxA很简单,输出一行字符串,把原来MessageBoxA开头6字节写回去然后调用MessageBoxA即可。
// target.cc
#include <iostream>
#include <windows.h>
int main() {
std::cout << "process ID: " << GetCurrentProcessId();
while (true) {
MessageBoxA(NULL, "This is a target client", "Notice", MB_OK);
getchar();
}
}
// client.cc
#include <iostream>
#include <windows.h>
constexpr char kDllPath[] =
"C:\\Users\\chuzhang\\Desktop\\DummyProgram\\hook\\hook_dll.dll";
int main() {
std::cout << "input process id: " << std::endl;
int processId;
std::cin >> processId;
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, false, processId);
if (!process) {
std::cout << "open process failed, erorr: " << GetLastError() << std::endl;
return 1;
}
LPVOID allocatedAddress = VirtualAllocEx(process, NULL, strlen(kDllPath) + 1,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
bool res = WriteProcessMemory(process, allocatedAddress, kDllPath,
strlen(kDllPath) + 1, NULL);
if (!res) {
std::cout << "write memory failed, erorr: " << GetLastError() << std::endl;
return 1;
}
LPVOID loadLibraryAAdress =
(LPVOID)GetProcAddress(GetModuleHandleA("kernel32"), "LoadLibraryA");
if (loadLibraryAAdress == NULL) {
std::cout << "Find function Address failed, erorr: " << GetLastError()
<< std::endl;
return 1;
}
HANDLE thread = CreateRemoteThread(process, NULL, 0,
(LPTHREAD_START_ROUTINE)loadLibraryAAdress,
allocatedAddress, 0, NULL);
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
VirtualFreeEx(process, allocatedAddress, strlen(kDllPath) + 1, MEM_DECOMMIT);
return 0;
}