zoukankan      html  css  js  c++  java
  • 通用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 原函数

    保存原函数返回值。调用SetLastCallResult32SetLastCallResult32就得到了原函数的返回值了。

    再重新设置返回值到寄存器,然后通过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: 
     module_index [%u] %s fun_index [%u] %s 
    _stack [%08x] ret_ip: 0x%08x 
    ",
    		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'
    ') {
    	}
    	*++p = L'
    ';
    	*++p = L'';
    
    	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'
    ') {
    	}
    	*++p = L'
    ';
    	*++p = L'';
    
    	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: 
     module_index [%u] %s fun_index [%u] %s 
    _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:xxDebugmyapp.exe
    0x77E20000 ntdll.dll C:WindowsSYSTEM32
    tdll.dll
    0x77430000 KERNEL32.DLL C:WindowsSystem32KERNEL32.DLL
    0x75CD0000 KERNELBASE.dll C:WindowsSystem32KERNELBASE.dll
    0x76CF0000 USER32.dll C:WindowsSystem32USER32.dll
    0x77B70000 win32u.dll C:WindowsSystem32win32u.dll
    0x77260000 GDI32.dll C:WindowsSystem32GDI32.dll
    0x77180000 gdi32full.dll C:WindowsSystem32gdi32full.dll
    0x779F0000 msvcp_win.dll C:WindowsSystem32msvcp_win.dll
    0x77880000 ucrtbase.dll C:WindowsSystem32ucrtbase.dll
    0x77810000 WS2_32.dll C:WindowsSystem32WS2_32.dll
    0x77730000 RPCRT4.dll C:WindowsSystem32RPCRT4.dll
    0x79890000 MSVCP140D.dll C:WindowsSYSTEM32MSVCP140D.dll
    0x796F0000 VCRUNTIME140D.dll C:WindowsSYSTEM32VCRUNTIME140D.dll
    0x79710000 ucrtbased.dll C:WindowsSYSTEM32ucrtbased.dll
    0x77700000 IMM32.DLL C:WindowsSystem32IMM32.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:xxxin_1103dmgamedatafontsyaheib17-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)

  • 相关阅读:
    关于白盒测试的心得
    基于Java的闰年测试
    等价类划分练习的代码实现
    软件测试中的等价类划分练习
    关于软件测试的初学小结
    现代软件工程作业第十二题(原十四题)
    好像木有白盒测试实验的报告,补一个~
    给大家推荐一本书啊啊~
    关于【做一名软件测试工程师,需要具备什么】的我的看法
    关于考试的笔记整理
  • 原文地址:https://www.cnblogs.com/luconsole/p/14813580.html
Copyright © 2011-2022 走看看