通用hook拦截所有API的实现
Posted `
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通用hook拦截所有API的实现相关的知识,希望对你有一定的参考价值。
通用hook拦截所有API的实现
实现一个通用hook,可以对当前进程的所有函数调用进行拦截查看和统计。 类似程序:API Monitor
。
可以对某一进程的所有api调用进行拦截,可以获得的调用函数返回值
,获得详细的参数值
。
现在尝试在windows 32位下实现一个类似API Monitor
的程序。
当要hook一个函数,通常我们会创建一个参数相符的detour fake函数,当然还有跳板函数,但这样有一个问题,程序内部的api几百几千,甚至可能上万,不可能每一个函数都去写一个对应的参数相同调用方式相同的detour函数,那样做太蠢了。
但可以尝试实现一个通用hook,这个通用hook关键在于实现一个通用的detour函数,所有的api函数的跳转都指向这个通用的detour函数,通用的detour函数既能获取到所有参数,也能去调用原函数,同时还能获取到原调用函数的返回值。
话不多说,直接上核心代码。
当任意hook目标的函数执行跳转到这个`通用detour function`函数。
=========通用 detour function================
pushad//pushad压入当前8个寄存器值到栈 EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI
//寻找ip 这个ip是call压栈的ip,就是pushad上一条指令,所以栈esp+32
mov eax, dword ptr[esp + 0x20]//0x20 =》32
//开始调用GetCallParameter32
push eax//|GetCallParameter32 参数4
push esp//这时候esp已近加了个8个压栈 所以栈顶是esp +32 |GetCallParameter32 参数3
push 0xff //fun_index |GetCallParameter32 参数2
push 0xff //module_index |GetCallParameter32 参数1
call GetCallParameter32//e8 4b bc fd ff
popad//从栈中还原由pushad压入的8个寄存器,这时候寄存器与原调用函数时候无异
add esp, 4//类似 pop ip,pop ip出去 抵消一个call,直接用原来的push参数
call trampoline//此时再调用跳板函数 执行 原函数
push eax//将原函数return值得到的结果压栈一份
push edx//将原函数return值压栈一份 32位下的64位返回值也要支持,所以需要edx
sub esp, 8
fstp qword ptr[esp] //将浮点数寄存器压栈一份 因为有可能原函数返回浮点数 SetLastCallResult32参数3
push eax//再将得到的结果传参 SetLastCallResult32参数2
push edx//再将得到的结果传参 SetLastCallResult32参数1
call SetLastCallResult32//调用SetLastCallResult32 获取函数执行返回值
mov ecx, eax//SetLastCallResult325返回值是32位跳转地址,存放在ecx
//恢复目标函数的参数返回
sub esp, 8//尝试用原来压栈的浮点数??
fld qword ptr[esp]//取出浮点数
add esp, 8
pop edx //获取压栈的eax
pop eax //获取压栈的eax
push ecx //返回的是ret ip 还原到原来的ret
ret//ret 会跳转到刚存的ecx地址
这个通用的detour函数
在内存中动态创建,这样就能指定fun_index
module_index
trampoline
的任意值,还需要两个c++函数GetCallParameter32
SetLastCallResult32
去辅助完成功能。
通用detour逻辑流程梳理===
pushad
保存当前主要寄存器。寻找ret ip
。
调用GetCallParameter32
这个函数,GetCallParameter32
会去通过esp获取原函数调用的所有参数。
通过popad
恢复寄存器,再恢复栈到参数水平,call 原函数
。
保存原函数返回值
。调用SetLastCallResult32
,SetLastCallResult32
就得到了原函数的返回值了。
再重新设置返回值到寄存器,然后通过ret ip
回到原调用地址。
struct CallContext
{
UINT_PTR ret_ip;
HookEntryInfo* hook_entry = NULL;
};
//使用thread_local是为了存储调用对象,GetCallParameter32开始SetLastCallResult32结束,
//使用stack是为了某些函数在互相调用,用stack可以保证顺序
thread_local std::stack<CallContext*> hook_stack_list;
//在这里获取目标函数的调用信息,可以从module_index和fun_index,确切知道是那个函数在调用,
//可以从_stack这个esp栈指针,获得函数调用的参数信息
void __stdcall GetCallParameter32(uint32_t module_index, uint32_t fun_index, void* _stack, UINT_PTR ret_ip)
{
unsigned char* esp_stack = (unsigned char*)_stack;
ModuleInfo* minfo = module_list[module_index];
HookEntryInfo* einfo = minfo->entry_list[fun_index];
log_printA("GetCallParameter32: \\n module_index [%u] %s fun_index [%u] %s \\n_stack [%08x] ret_ip: 0x%08x \\n",
module_index, minfo->dll_name.c_str(),
fun_index, einfo->func_name.c_str(),
_stack,
ret_ip);
CallContext* tls_hook_context = new CallContext();
tls_hook_context->ret_ip = ret_ip;
tls_hook_context->hook_entry = einfo;
hook_stack_list.push(tls_hook_context);
return;
}
//目标函数执行完会执行这个函数,可以获取函数执行返回值eax,当返回int64edx也有值,当返回浮点数,值就在return_sto
UINT_PTR __stdcall SetLastCallResult32(int edx, int eax, double return_sto)
{
CallContext* tls_hook_context = hook_stack_list.top();
hook_stack_list.pop();
log_printA("SetLastCallResult32: %s eax: %d edx: %d sto: %f",
tls_hook_context->hook_entry->func_name.c_str(),
eax, edx, return_sto);
UINT_PTR ret_ip = tls_hook_context->ret_ip;
delete tls_hook_context;
return ret_ip;
}
不多说,直接上完整的核心代码:=
<此程序只支持32位>
#include "ZEncode.h"//wchar char转换
#include "MinHook.h"
#include <TlHelp32.h>
#include <stack>
#include <unordered_map>
#if defined _M_X64
#elif defined _M_IX86
#pragma comment(lib, "libMinHook.x86.lib")
#endif
void log_printW(const wchar_t* format, ...) {
const int logsize = 2 * 1024;
wchar_t buffer[logsize], * p = buffer;
va_list args;
va_start(args, format);
p += _vsnwprintf_s(buffer, logsize, format, args);
va_end(args);
while (*--p == L\'\\n\') {
}
*++p = L\'\\n\';
*++p = L\'\\0\';
fputws(buffer, stdout);
}
void log_printA(const char* format, ...) {
const int logsize = 2 * 1024;
char buffer[logsize], * p = buffer;
va_list args;
va_start(args, format);
p += _vsnprintf_s(buffer, logsize, format, args);
va_end(args);
while (*--p == L\'\\n\') {
}
*++p = L\'\\n\';
*++p = L\'\\0\';
puts(buffer);
}
class QPCtime {
public:
QPCtime() {
if (!QueryPerformanceFrequency(&Frequency)) {
log_printA("QueryPerformanceFrequency false");
}
}
~QPCtime() {
}
int64_t now_us() {
return now_ns() / 1000;
}
int64_t now_ms() {
return now_ns() / 1000000;
}
int64_t now_ns() {
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
const long long _Whole = (li.QuadPart / Frequency.QuadPart) * 1000000000;
const long long _Part = (li.QuadPart % Frequency.QuadPart) * 1000000000 / Frequency.QuadPart;
return _Whole + _Part;
}
private:
LARGE_INTEGER Frequency;
};
void __stdcall GetCallParameter32(uint32_t module_index, uint32_t fun_index, void* _stack, UINT_PTR ret_ip);
UINT_PTR __stdcall SetLastCallResult32(int edx, int eax, double return_sto);
struct HookAsmCodeSell
{
unsigned char* asm_code = NULL;
int asm_code_size = 0;
void SetValue(uint32_t module_index, uint32_t fun_index, UINT_PTR TrampolineCall)
{
if (asm_code == NULL) {
log_printA("HookAsmCodeSell SetValue hook_shell_code==null");
return;
}
uint32_t _getcallparameter = (uint32_t)&GetCallParameter32;
uint32_t _setlastcallresult32 = (uint32_t)&SetLastCallResult32;
//偏移地址 = 目的地址 - 跳转基地址(的下一条指令的地址)
*(uint32_t*)(asm_code + 8) = fun_index;
*(uint32_t*)(asm_code + 13) = module_index;
*(uint32_t*)(asm_code + 18) =
_getcallparameter - (uint32_t)(asm_code + 18 + 4);//GetCallParameter
*(uint32_t*)(asm_code + 27) =
TrampolineCall - (uint32_t)(asm_code + 27 + 4);;//old fun func_ttttt
*(uint32_t*)(asm_code + 42) =
_setlastcallresult32 - (uint32_t)(asm_code + 42 + 4);//SetLastCallResult32
}
bool AllocShell()
{
unsigned char shellcode_32[] =
{
0x60, // pushad
0x8B, 0x44, 0x24 ,0x20, // mov eax,dword ptr [esp+20h]
0x50, // push eax 这是ret ip
0x54, // push esp
0x68, 0x00, 0x00, 0x00, 0x00, // push fun_index
0x68, 0x00, 0x00, 0x00, 0x00, // push module_index
0xE8, 0x00, 0x00, 0x00, 0x00, // call GetCallParameter (04F2CBAh)
0x61, // popad
//
0x83, 0xC4 ,0x04 , // add esp,4 抵消一个call,直接用原来的push参数
0xE8, 0x00, 0x00, 0x00, 0x00, // call func_ttttt (04F2D55h)
//保存返回值到栈
0x50, // push eax
0x52, // push edx
0x83, 0xEC ,0x08, // sub esp,8
0xDD, 0x1C ,0x24, // fstp qword ptr [esp]
0x50, // push eax
0x52, // push edx
0xE8, 0x00, 0x00, 0x00, 0x00, // call SetLastCallResult32 (04F2D19h)
0x8B, 0xC8 , // mov ecx,eax
//尝试获取原来压栈的8字节浮点数
0x83, 0xEC, 0x08 , // sub esp,8
0xDD, 0x04, 0x24 , // fld qword ptr [esp]
0x83, 0xC4 ,0x08 , // add esp,8
//原来的eax edx返回值
0x5A, // pop edx
0x58, // pop eax
//原来的返回ip
0x51, // push ecx
0xC3, // ret
};
*(uint32_t*)(&shellcode_32[8]) = 0x0;
*(uint32_t*)(&shellcode_32[13]) = 0x0;
*(uint32_t*)(&shellcode_32[18]) = 0xffffffff;//GetCallParameter
*(uint32_t*)(&shellcode_32[27]) = 0xffffffff;//old fun func_ttttt
*(uint32_t*)(&shellcode_32[42]) = 0xffffffff;//SetLastCallResult32
asm_code_size = sizeof(shellcode_32);
asm_code = (unsigned char*)VirtualAlloc(NULL, asm_code_size + 4,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (asm_code == NULL) {
log_printA("VirtualAlloc shellcode_32 NULL!!");
return false;
}
memcpy(asm_code, shellcode_32, asm_code_size);
return true;
}
void Free()
{
if (asm_code != NULL)
VirtualFree(asm_code, 0, MEM_RELEASE);
asm_code = NULL;
}
};
struct HookEntryInfo
{
int index = 0;
int module_index = 0;
std::string func_name;
UINT_PTR func_addr = 0;
UINT_PTR rva_addr = 0;
UINT_PTR trampoline = 0;
bool enable_hook = false;
HookAsmCodeSell hook_shell;
};
struct ModuleInfo
{
int index = 0;
UINT_PTR base_addr = 0;
std::string dll_name;
std::string dll_name_path;
std::vector<HookEntryInfo*> entry_list;
std::unordered_map<std::string, HookEntryInfo*> entry_list_dic;//存一份dic 加速查找
};
std::vector<ModuleInfo*> module_list;//存储所有module信息
std::unordered_map<std::string, ModuleInfo*> module_list_dic;//为了加速查找的
struct CallContext
{
UINT_PTR ret_ip;
HookEntryInfo* hook_entry = NULL;
int64_t start_time;
int64_t end_time;
};
QPCtime qpc;
//使用thread_local是为了存储调用对象,GetCallParameter32开始SetLastCallResult32结束,
//使用stack是为了某些函数在互相调用,用stack可以保证顺序
thread_local std::stack<CallContext*> hook_stack_list;
//在这里获取目标函数的调用信息,可以从module_index和fun_index,确切知道是那个函数在调用,
//可以从_stack这个esp栈指针,获得函数调用的参数信息
void __stdcall GetCallParameter32(uint32_t module_index, uint32_t fun_index, void* _stack, UINT_PTR ret_ip)
{
unsigned char* esp_stack = (unsigned char*)_stack;
esp_stack = esp_stack + 8 * 4 + 4 + 4;//pushad +push [ret_ip] and call
ModuleInfo* minfo = module_list[module_index];
HookEntryInfo* einfo = minfo->entry_list[fun_index];
log_printA("GetCallParameter32: \\n module_index [%u] %s fun_index [%u] %s \\n_stack [%08x] ret_ip: 0x%08x ",
module_index, minfo->dll_name.c_str(),
fun_index, einfo->func_name.c_str(),
_stack,
ret_ip);
//通过esp获取参数的demo
if (einfo->func_name.compare("sendto") == 0) {
/*int PASCAL FAR sendto (
_In_ SOCKET s,
_In_reads_bytes_(len) const char FAR * buf,
_In_ int len,
_In_ int flags,
_In_reads_bytes_opt_(tolen) const struct sockaddr FAR *to,
_In_ int tolen);*/
//sendto是__stdcall,从右到左压栈,所以,栈顶是最左边的参数,为顺序
char* send_data = *(char**)(esp_stack + 1 * 4);//顺数第2个参数
int send_data_len = *(int*)(esp_stack + 2 * 4);//顺数第3个参数
std::string send_data_str(send_data, send_data_len);
log_printA("sendto parameter: send data:%s data len:%d", send_data_str.c_str(), send_data_len);
}
//通过esp获取参数的demo
if (einfo->func_name.compare("MessageBoxW") == 0) {
/*int
WINAPI
MessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType);*/
//MessageBoxW是__stdcall,从右到左压栈,所以,栈顶是最左边的参数,为顺序
wchar_t* lpText = *(wchar_t**)(esp_stack + 1 * 4);//顺数第2个参数
wchar_t* lpCaption = *(wchar_t**)(esp_stack + 2 * 4);//顺数第3个参数
log_printW(L"MessageBoxW parameter: lpText:%s lpCaption:%s", lpText, lpCaption);
lpCaption[0] = L\'A\';
lpCaption[1] = L\'B\';
lpCaption[2] = L\'C\';
lpText[0] = L\'=\';
lpText[1] = L\'=\';
lpText[2] = L\'=\';
}
CallContext* tls_hook_context = new CallContext();
tls_hook_context->ret_ip = ret_ip;
tls_hook_context->hook_entry = einfo;
hook_stack_list.push(tls_hook_context);
tls_hook_context->start_time = qpc.now_ns();
return;
}
//目标函数执行完会执行这个函数,可以获取函数执行返回值eax,当返回int64edx也有值,当返回浮点数,值就在return_sto
UINT_PTR __stdcall SetLastCallResult32(int edx, int eax, double return_sto)
{
CallContext* tls_hook_context = hook_stack_list.top();
hook_stack_list.pop();
tls_hook_context->end_time = qpc.now_ns();
log_printA("SetLastCallResult32: %s eax: %d edx: %d sto: %f use time: %I64d ns",
tls_hook_context->hook_entry->func_name.c_str(),
eax, edx, return_sto, tls_hook_context->end_time - tls_hook_context->start_time);
UINT_PTR ret_ip = tls_hook_context->ret_ip;
delete tls_hook_context;
return ret_ip;
}
//加载模块的所有导出函数
void LoadModuleExportFuncs(ModuleInfo* dll_module)
{
const char* lpImage = (const char*)dll_module->base_addr;
PIMAGE_DOS_HEADER imDH = (PIMAGE_DOS_HEADER)lpImage;
PIMAGE_NT_HEADERS imNH = (PIMAGE_NT_HEADERS)((char*)lpImage + imDH->e_lfanew);
DWORD exportRVA = imNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
PIMAGE_EXPORT_DIRECTORY imED = (PIMAGE_EXPORT_DIRECTORY)(lpImage + exportRVA);
//long pExportSize = imNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
PWORD lpOrdinals = imED->AddressOfNameOrdinals ? (PWORD)(lpImage + imED->AddressOfNameOrdinals) : 0;
PDWORD lpNames = imED->AddressOfNames ? (PDWORD)(lpImage + imED->AddressOfNames) : 0;
PDWORD lpRvas = (PDWORD)(lpImage + imED->AddressOfFunctions);
PIMAGE_SECTION_HEADER ish = (PIMAGE_SECTION_HEADER)(imNH + 1);
int nsec = imNH->FileHeader.NumberOfSections;
for (DWORD i = 0; i < imED->NumberOfFunctions; ++i)
{
DWORD rvafunc = lpRvas[i];
DWORD oftName = 0;
if (lpNames && lpOrdinals)
{
for (DWORD k = 0; k < imED->NumberOfNames; ++k)
{
if (lpOrdinals[k] == i)
{
oftName = lpNames[k];
break;
}
}
}
std::string fun_name;
UINT_PTR fun_addr = (UINT_PTR)lpImage + rvafunc;
if (oftName) {
fun_name = (char*)(lpImage + oftName);
}
else {
//没名字的不要了
continue;
}
HookEntryInfo* hook_entry = new HookEntryInfo();
hook_entry->func_name = fun_name;
hook_entry->func_addr = fun_addr;
hook_entry->rva_addr = rvafunc;
dll_module->entry_list.push_back(hook_entry);
hook_entry->index = dll_module->entry_list.size() - 1;
hook_entry->module_index = dll_module->index;
dll_module->entry_list_dic[hook_entry->func_name] = hook_entry;
}
}
//加载模块
void LoadModule(UINT_PTR base_addr, WCHAR* dll_name, WCHAR* dll_path)
{
std::wstring w_dll_name = dll_name;
std::wstring w_dll_path = dll_path;
for (auto& str : w_dll_name) {
str = ::towlower(str);
}
for (auto& str : w_dll_path) {
str = ::towlower(str);
}
ModuleInfo* add_module_info = new ModuleInfo();
add_module_info->base_addr = base_addr;
add_module_info->dll_name = WCHAR_TO_ANSI(w_dll_name);
add_module_info->dll_name_path = WCHAR_TO_ANSI(w_dll_path);
module_list.push_back(add_module_info);
add_module_info->index = module_list.size() - 1;
module_list_dic[add_module_info->dll_name] = add_module_info;
LoadModuleExportFuncs(add_module_info);
}
//扫描进程现在所有的模块
void ScanModule()
{
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
if (hModuleSnap == INVALID_HANDLE_VALUE) {
log_printA("CreateToolhelp32Snapshot failed.");
return;
}
MODULEENTRY32 me32;
me32.dwSize = sizeof(MODULEENTRY32);
if (!Module32First(hModuleSnap, &me32)) {
log_printA("Module32First failed.");
return;
}
do
{
LoadModule((UINT_PTR)me32.hModule, me32.szModule, me32.szExePath);
log_printW(L"0x%08lX %s %s", me32.hModule, me32.szModule, me32.szExePath);
} while (Module32Next(hModuleSnap, &me32));
CloseHandle(hModuleSnap);
}
void MonitorAPI(const char* _dll_name, const char* func_name)
{
std::string dll_name = _dll_name;
for (auto& str : dll_name) {
str = ::tolower(str);
}
auto find_module_iter = module_list_dic.find(dll_name);
if (find_module_iter == module_list_dic.end()) {
log_printA("not find dll %s", dll_name);
return;
}
ModuleInfo* minfo = find_module_iter->second;
auto find_api_iter = minfo->entry_list_dic.find(func_name);
if (find_api_iter == minfo->entry_list_dic.end()) {
log_printA("not find func_name %s", func_name);
return;
}
HookEntryInfo* einfo = find_api_iter->second;
einfo->hook_shell.AllocShell();
if (MH_CreateHook((LPVOID)einfo->func_addr, einfo->hook_shell.asm_code,
reinterpret_cast<LPVOID*>(&einfo->trampoline)) != MH_OK) {
log_printA("MH_CreateHook false");
return;
}
einfo->hook_shell.SetValue(einfo->module_index,
einfo->index,
(UINT_PTR)einfo->trampoline);
einfo->enable_hook = true;
log_printA("MonitorAPI %s success", func_name);
}
//测试用
extern "C" int64_t _declspec(dllexport) int64test(int64_t a, int64_t b)
{
return a + b;
};
extern "C" float _declspec(dllexport) floattest(float a, float b)
{
return a + b;
};
extern "C" double _declspec(dllexport) doubletest(double a, double b)
{
return a + b;
};
int main()
{
ScanModule();
int value = 0;
if (MH_Initialize() != MH_OK) {
log_printA("MH_Initialize false");
return 0;
}
//====随便添加要监控的任意函数============
MonitorAPI("user32.dll", "MessageBoxW");
MonitorAPI("kernel32.dll", "CreateFileA");
MonitorAPI("kernel32.dll", "WriteFile");
MonitorAPI("ws2_32.dll", "socket");
MonitorAPI("ws2_32.dll", "sendto");
MonitorAPI("ws2_32.dll", "closesocket");
//myapp.exe是当前程序名称,这里测试上面那几个导出函数
MonitorAPI("myapp.exe", "int64test");
MonitorAPI("myapp.exe", "floattest");
MonitorAPI("myapp.exe", "doubletest");
//启用所有hook
if (MH_EnableHook(MH_ALL_HOOKS) != MH_OK) {
log_printA("MH_EnableHook(MH_ALL_HOOKS) false");
return 1;
}
//==========开始测试,执行下面的函数,都会被拦截监控到===============
wchar_t lpText[50] = L"888--this messagebox text.";
wchar_t lpCaption[30] = L"XXX-caption";
MessageBoxW(NULL, lpText, lpCaption, MB_OK);
HANDLE hFile = CreateFileA("one.txt",
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE) {
char buff[256] = "this WriteFile context.";
DWORD dwWrite;
WriteFile(hFile, &buff, strlen(buff), &dwWrite, NULL);
CloseHandle(hFile);
}
#pragma comment(lib,"ws2_32.lib")
SOCKET m_sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
char sendstr[256] = "this sendto data.";
sendto(m_sock, sendstr, strlen(sendstr), 0, (struct sockaddr*)&addr, sizeof(addr));
closesocket(m_sock);
int64_t i64v = int64test(UINT32_MAX, 123);
float fv = floattest(1.123, 2.00);
double dv = doubletest(1.123, 2.00);
//=======结束,移除所有hook========
MH_DisableHook(MH_ALL_HOOKS);
MH_Uninitialize();
}
输出:
0x00520000 myapp.exe D:\\Arma3_Read\\Debug\\myapp.exe
0x77E20000 ntdll.dll C:\\Windows\\SYSTEM32\\ntdll.dll
0x77430000 KERNEL32.DLL C:\\Windows\\System32\\KERNEL32.DLL
0x75CD0000 KERNELBASE.dll C:\\Windows\\System32\\KERNELBASE.dll
0x76CF0000 USER32.dll C:\\Windows\\System32\\USER32.dll
0x77B70000 win32u.dll C:\\Windows\\System32\\win32u.dll
0x77260000 GDI32.dll C:\\Windows\\System32\\GDI32.dll
0x77180000 gdi32full.dll C:\\Windows\\System32\\gdi32full.dll
0x779F0000 msvcp_win.dll C:\\Windows\\System32\\msvcp_win.dll
0x77880000 ucrtbase.dll C:\\Windows\\System32\\ucrtbase.dll
0x77810000 WS2_32.dll C:\\Windows\\System32\\WS2_32.dll
0x77730000 RPCRT4.dll C:\\Windows\\System32\\RPCRT4.dll
0x79890000 MSVCP140D.dll C:\\Windows\\SYSTEM32\\MSVCP140D.dll
0x796F0000 VCRUNTIME140D.dll C:\\Windows\\SYSTEM32\\VCRUNTIME140D.dll
0x79710000 ucrtbased.dll C:\\Windows\\SYSTEM32\\ucrtbased.dll
0x77700000 IMM32.DLL C:\\Windows\\System32\\IMM32.DLL
MonitorAPI MessageBoxW success
MonitorAPI CreateFileA success
MonitorAPI WriteFile success
MonitorAPI socket success
MonitorAPI sendto success
MonitorAPI closesocket success
MonitorAPI int64test success
MonitorAPI floattest success
MonitorAPI doubletest success
GetCallParameter32:
module_index [4] user32.dll fun_index [645] MessageBoxW
_stack [003ef3e4] ret_ip: 0x00553b65
MessageBoxW parameter: lpText:888--this messagebox text. lpCaption:XXX-caption
GetCallParameter32:
module_index [2] kernel32.dll fun_index [1559] WriteFile
_stack [0683ef3c] ret_ip: 0x101266ae
SetLastCallResult32: WriteFile eax: 1 edx: 109309756 sto: -nan(ind) use time: 407400 ns
GetCallParameter32:
module_index [2] kernel32.dll fun_index [1559] WriteFile
_stack [0683ef3c] ret_ip: 0x101266ae
SetLastCallResult32: WriteFile eax: 1 edx: 109309756 sto: -nan(ind) use time: 417800 ns
GetCallParameter32:
module_index [2] kernel32.dll fun_index [1559] WriteFile
_stack [0659ef0c] ret_ip: 0x101266ae
SetLastCallResult32: WriteFile eax: 1 edx: 106557196 sto: -nan(ind) use time: 349500 ns
GetCallParameter32:
module_index [2] kernel32.dll fun_index [1559] WriteFile
_stack [0659ef0c] ret_ip: 0x101266ae
SetLastCallResult32: WriteFile eax: 1 edx: 106557196 sto: -nan(ind) use time: 139900 ns
SetLastCallResult32: MessageBoxW eax: 1 edx: 10420224 sto: -nan(ind) use time: 3345317900 ns
GetCallParameter32:
module_index [2] kernel32.dll fun_index [200] CreateFileA
_stack [003ef3d8] ret_ip: 0x00553b8b
SetLastCallResult32: CreateFileA eax: -1 edx: 10420224 sto: -nan(ind) use time: 515400 ns
GetCallParameter32:
module_index [10] ws2_32.dll fun_index [22] socket
_stack [003ef3e8] ret_ip: 0x00553c58
SetLastCallResult32: socket eax: -1 edx: 0 sto: -nan(ind) use time: 1633900 ns
GetCallParameter32:
module_index [10] ws2_32.dll fun_index [19] sendto
_stack [003ef3dc] ret_ip: 0x00553cf1
sendto parameter: send data:this sendto data. data len:17
SetLastCallResult32: sendto eax: -1 edx: 0 sto: -nan(ind) use time: 490700 ns
GetCallParameter32:
module_index [10] ws2_32.dll fun_index [2] closesocket
_stack [003ef3f0] ret_ip: 0x00553cfd
SetLastCallResult32: closesocket eax: -1 edx: 0 sto: -nan(ind) use time: 29600 ns
GetCallParameter32:
module_index [0] myapp.exe fun_index [2] int64test
_stack [003ef3e4] ret_ip: 0x00553d0a
SetLastCallResult32: int64test eax: 122 edx: 1 sto: -nan(ind) use time: 13100 ns
GetCallParameter32:
module_index [0] myapp.exe fun_index [1] floattest
_stack [003ef3ec] ret_ip: 0x00553d3a
SetLastCallResult32: floattest eax: 5677115 edx: 1 sto: 3.123000 use time: 12300 ns
GetCallParameter32:
module_index [0] myapp.exe fun_index [0] doubletest
_stack [003ef3e4] ret_ip: 0x00553d68
SetLastCallResult32: doubletest eax: 5677115 edx: 1 sto: 3.123000 use time: 11100 ns
** 很可能你很快发现了问题,[GetCallParameter32 MessageBoxW]之后隔了好几个WriteFile,然后才出现,[SetLastCallResult32: MessageBoxW],这是正常的,因为MessageBoxW内部就在WriteFile,所以就体现了,thread_local std::stack<CallContext*> hook_stack_list;
的std::stack
必要性。 **
函数的参数通过esp栈指针去动态获取。函数的返回值通过eax edx 和浮点数sto去获取。此外我们还可以得到目标函数的调用整个纳秒时间。
关于参数获取
esp_stack = esp_stack + 8 * 4 + 4 + 4;//pushad +push [ret_ip] and call
//通过esp获取参数的demo
if (einfo->func_name.compare("sendto") == 0) {
/*int PASCAL FAR sendto (
_In_ SOCKET s,
_In_reads_bytes_(len) const char FAR * buf,
_In_ int len,
_In_ int flags,
_In_reads_bytes_opt_(tolen) const struct sockaddr FAR *to,
_In_ int tolen);*/
//sendto是__stdcall,从右到左压栈,所以,栈顶是最左边的参数,为顺序
char* send_data = *(char**)(esp_stack + 1 * 4);//顺数第2个参数
int send_data_len = *(int*)(esp_stack + 2 * 4);//顺数第3个参数
std::string send_data_str(send_data, send_data_len);
log_printA("sendto parameter: send data:%s data len:%d", send_data_str.c_str(), send_data_len);
}
参数获取,必须要知道传参的具体类型,上面的sendto函数,每个参数都是4字节,如果遇到int64的参数,那么一个参数就是8字节,就需要注意偏移。
问题来了,如何才能像API Monitor
那样去获取任何api的完整的参数?就需要准备一个函数的资料库,这个资料里面有函数的每个参数类型,参数名称,返回值类型,结构体类型。
API Monitor
有准备好了,参考API Monitor
文件夹里面的API
文件夹,里面的xml文件都是windows主要api的参数和返回值类型,很多类型是别名,需要继续去寻找最根本的类型,这样就能获取到大小了。
下面是我通过自己的程序和API Monitor
的xml文件去配合完成的参数读取结果。
module_name:kernel32.dll
function name:CreateFileW
function addr: 0x6f800558
call addr:0x1039a95
description:文件操作
[return]: 0xe70([FILE_HANDLE])
param:
[0] lpFileName LPCTSTR
value: 0x0753F644 (wstring)\\\\?\\D:\\xxx\\bin_110\\3dmgame\\data\\fonts\\yaheib17-01.paa
[1] dwDesiredAccess [FILE_ACCESS_MASK]
value: 0x80000000 ( GENERIC_READ )
[2] dwShareMode [FILE_SHARE_MODE]
value: 0x1 ( FILE_SHARE_READ )
[3] lpSecurityAttributes LPSECURITY_ATTRIBUTES
value: 0x0753F650 (struct)SECURITY_ATTRIBUTES||[DWORD nLength] 0x3||[PSECURITY_DESCRIPTOR lpSecurityDescriptor] 0x€||[BOOL bInheritHandle] 0x0
[4] dwCreationDisposition [CreationDisposition]
value: 0x3 (OPEN_EXISTING)
[5] dwFlagsAndAttributes [FlagsAndAttributes]
value: 0x60000080 ( FILE_ATTRIBUTE_NORMAL FILE_FLAG_NO_BUFFERING FILE_FLAG_OVERLAPPED )
[6] hTemplateFile HANDLE
value: 0x0 (NULL)
module_name:ws2_32.dll
function name:recv
function addr: 0x6f8001f8
call addr:0x7432c157
description:网络访问
[return]: 0x1([SocketCode-int])
param:
[0] s SOCKET
value: 0x1218(peer:213.192.54.31:443)(local:22.62.0.114:13560)
[1] buf [LPVOID|char*]
value: 0x02E0FCA4
[2] len int
value: 0x1
[3] flags [SendRecvFlags]
value: 0x2 ( MSG_PEEK )
32位的核心代码逻辑已给出了。朋友们可以去自己发挥了。关于64位的是同样的道理,但是64位传参主要都是通过寄存器来的了,所以很多不一样的处理,需要的就自己去研究吧。
<完> 2021年5月26日 qq: base64(MTcxMjgzNjQ0)
以上是关于通用hook拦截所有API的实现的主要内容,如果未能解决你的问题,请参考以下文章