函数钩子-Dll注入
来源:互联网 发布:数据透视表汇总求和 编辑:程序博客网 时间:2024/05/17 01:12
在应用层可以设置的钩子方法有许多种,其中经典的钩子是消息钩子,消息钩子分为两种,一种是系统级全局钩子,另外一种是线程级局部钩子,它们都是通过下面这一组函数来实现消息勾取,实现相对简单。
设置钩子: SetWindowsHookEx释放钩子: UnhookWindowsHookEx继续钩子: CallNextHookEx
钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。《百度百科》
除此之外,还可以利用修改目标进程空间中PE文件的IAT表中的指定API的地址,使它指向自定的DLL函数的API地址来达到API勾取的目的,在自定的API执行完毕之后,为了保证程序的正确执行,又要把相关的修改的地方还原,其中需要注意的是修改IAT中的地址的时候需要先通过VirtualProtect()函数获得对相关内存的读写权限。
BOOL VirtualProtect(LPVOID lpAddress, // 目标地址起始位置DWORD dwSize, // 大小DWORD flNewProtect, // 请求的保护方式PDWORD lpflOldProtect // 保存老的保护方式);
其中实现的原理如下图(参考逆向工程核心原理332页)
下面直接引用书上的源代码
InjectDll.exe
#include "stdio.h"#include "windows.h"#include "tlhelp32.h"#include "winbase.h"#include "tchar.h"void usage(){ printf("\nInjectDll.exe by ReverseCore\n" "- blog : http://www.reversecore.com\n" "- email : reversecore@gmail.com\n\n" "- USAGE : InjectDll.exe <i|e> <PID> <dll_path>\n\n");}BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName){ HANDLE hProcess, hThread; LPVOID pRemoteBuf; DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR); LPTHREAD_START_ROUTINE pThreadProc; if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { DWORD dwErr = GetLastError(); return FALSE; } pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL); pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); return TRUE;} BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName){ BOOL bMore = FALSE, bFound = FALSE; HANDLE hSnapshot, hProcess, hThread; MODULEENTRY32 me = { sizeof(me) }; LPTHREAD_START_ROUTINE pThreadProc; if( INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) ) return FALSE; bMore = Module32First(hSnapshot, &me); for( ;bMore ;bMore = Module32Next(hSnapshot, &me) ) { if( !_tcsicmp(me.szModule, szDllName) || !_tcsicmp(me.szExePath, szDllName) ) { bFound = TRUE; break; } } if( !bFound ) { CloseHandle(hSnapshot); return FALSE; } if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { CloseHandle(hSnapshot); return FALSE; } pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); CloseHandle(hSnapshot); return TRUE;}DWORD _EnableNTPrivilege(LPCTSTR szPrivilege, DWORD dwState){ DWORD dwRtn = 0; HANDLE hToken; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { LUID luid; if (LookupPrivilegeValue(NULL, szPrivilege, &luid)) { BYTE t1[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; BYTE t2[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; DWORD cbTP = sizeof(TOKEN_PRIVILEGES) + sizeof (LUID_AND_ATTRIBUTES); PTOKEN_PRIVILEGES pTP = (PTOKEN_PRIVILEGES)t1; PTOKEN_PRIVILEGES pPrevTP = (PTOKEN_PRIVILEGES)t2; pTP->PrivilegeCount = 1; pTP->Privileges[0].Luid = luid; pTP->Privileges[0].Attributes = dwState; if (AdjustTokenPrivileges(hToken, FALSE, pTP, cbTP, pPrevTP, &cbTP)) dwRtn = pPrevTP->Privileges[0].Attributes; } CloseHandle(hToken); } return dwRtn;}DLL注入以及提权的代码和普通DLL注入的一样,没有变化,在这里有一个问题,DLL注入之后就可以直接实现运行自定义代码,又何必费力去勾取特定函数?我思考之后发现原因可能有二:1、从恶意代码的角度考虑,它是防止杀软检测的一种手段(具体原因还没有搞清楚)2、从函数监控或者函数补丁的角度来看它显然用处明显int _tmain(int argc, TCHAR* argv[]){ if( argc != 4 ) { usage(); return 1; } // adjust privilege _EnableNTPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED); // InjectDll.exe <i|e> <PID> <dll_path> if( !_tcsicmp(argv[1], L"i") ) InjectDll((DWORD)_tstoi(argv[2]), argv[3]); else if(!_tcsicmp(argv[1], L"e") ) EjectDll((DWORD)_tstoi(argv[2]), argv[3]); return 0;}
hookiat.dll
// include#include "stdio.h"#include "wchar.h"#include "windows.h"// typedeftypedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);// globalsFARPROC g_pOrgFunc = NULL;// 自定用于勾取的替代函数BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString){ wchar_t* pNum = L"零一二三四五六七八九"; wchar_t temp[2] = {0,}; int i = 0, nLen = 0, nIndex = 0; nLen = wcslen(lpString); for(i = 0; i < nLen; i++) { // 将阿拉伯数字转换为中文数字 // lpString 是wide-character (2 byte) 字符串 if( L'0' <= lpString[i] && lpString[i] <= L'9' ) { temp[0] = lpString[i]; nIndex = _wtoi(temp); lpString[i] = pNum[nIndex]; } } // user32!SetWindowTextW() API return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);}// hook_iatBOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew){ HMODULE hMod; LPCSTR szLibName; PIMAGE_IMPORT_DESCRIPTOR pImportDesc; PIMAGE_THUNK_DATA pThunk; DWORD dwOldProtect, dwRVA; PBYTE pAddr; // hMod, pAddr = ImageBase of calc.exe // = VA to MZ signature (IMAGE_DOS_HEADER) hMod = GetModuleHandle(NULL); pAddr = (PBYTE)hMod; // pAddr = VA to PE signature (IMAGE_NT_HEADERS) pAddr += *((DWORD*)&pAddr[0x3C]); // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table dwRVA = *((DWORD*)&pAddr[0x80]); // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA); for( ; pImportDesc->Name; pImportDesc++ ) { // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name); if( !_stricmp(szLibName, szDllName) ) { // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk // = VA to IAT(Import Address Table) pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk); // pThunk->u1.Function = VA to API for( ; pThunk->u1.Function; pThunk++ ) { if( pThunk->u1.Function == (DWORD)pfnOrg ) { // 这里是编程中容易遗忘的部分,它充分的考虑到了内存的权限问题 //更改内存属性 VirtualProtect( (LPVOID)&pThunk>u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect); // IAT值修改 pThunk->u1.Function = (DWORD)pfnNew; // 恢复内存属性 VirtualProtect( (LPVOID)&pThunk->u1.Function, 4, dwOldProtect, &dwOldProtect); return TRUE; } } } } return FALSE;}BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){ switch( fdwReason ) { case DLL_PROCESS_ATTACH : // 保存原API地址 g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), "SetWindowTextW"); // # hook // 用hookiat!MySetWindowText()勾取user32!SetWindowTextW() hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW); break; case DLL_PROCESS_DETACH : // # unhook // 将calc.exe IAT恢复原值 hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc); break; } return TRUE;}
小结:
整个思路不算复杂,但是实际编程中有一些需要注意的细节不能出错,否则就会是函数勾取失败,比如DLL注入中额提权操作一定是不能遗忘,还有在IAT地址替换的过程中通过查找IID结构,再一步一步查找IAT中的函数地址也是需要很熟悉PE文件结构才能准确查找的,对IID查找流程中的编码我现在也不太明白代码如下,是比较通用查找流程
// hMod, pAddr = ImageBase of calc.exe // = VA to MZ signature (IMAGE_DOS_HEADER) hMod = GetModuleHandle(NULL); pAddr = (PBYTE)hMod; // pAddr = VA to PE signature (IMAGE_NT_HEADERS) pAddr += *((DWORD*)&pAddr[0x3C]); // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table dwRVA = *((DWORD*)&pAddr[0x80]); // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA); for( ; pImportDesc->Name; pImportDesc++ ) { // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name); if( !_stricmp(szLibName, szDllName) ) { // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk // = VA to IAT(Import Address Table) pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk); // pThunk->u1.Function = VA to API
找到IID结构体之后,还需要了解INT和IAT指向的IMAGE_THUNK_DATA结构体才能准确查找API地址
typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;
PIMAGE_IMPORT_BY_NAME AddressOfData;
} u1;
} IMAGE_THUNK_DATA32;
ForwarderString 指向一个转向者字符串的RVA;
Function 被输入的函数的内存地址;
Ordinal 被输入的API的序数值
AddressOfData 指向IMAGE_IMPORT_BY_NAME
IID结构的查找在内存中汇编表现为[EDI+3C]、[EDI+EAX+80],只要看到此特征,就能猜测它正在查找IID,整个过程到此结束,对于编程过程中的一些细节以后还需要花时间去整理,对此项技术与其他技术的结合使用也还需要进一步去探索,离找工作时间有限,先了解核心思想为先、、、
- 函数钩子-Dll注入
- 键盘钩子函数注入dll
- 钩子DLL注入
- DLL注入--设置消息钩子
- VC dll 注入之钩子注入
- DLL注入技术之消息钩子注入
- DLL注入技术之消息钩子注入
- DLL注入技术之消息钩子注入
- DLL注入技术之消息钩子注入
- vc++进程注入钩子DLL通用模块
- Dll注入技术之消息钩子
- Dll注入技术之消息钩子
- Windows 钩子,基本的dll注入
- Dll注入技术之消息钩子
- 利用钩子函数来注入DLL的一个具体应用:点击桌面不同图标,播放相应音符
- vc++进程注入钩子DLL通用模块开源
- 接触DLL编写并实现线程注入和全局钩子
- 浅析利用钩子注入DLL的原理与防范
- 【HDU】5742 It's All In The Mind
- 设置windows的默认调试器
- hd 1009 FatMouse' Trade (贪心)
- 互联网金融全面洗礼,民生电商金融基因凸显优势
- C++类
- 函数钩子-Dll注入
- hdu 5744(2016 Multi-University Training Contest 2)
- 谷歌GCM 接入笔记1
- 一些机器学习、推荐系统的学习资源
- make命令进行宏编译
- 命令行方式上传本地项目至Github仓库当中
- Java List与数组之间的转换
- 添物不花钱学JavaEE(基础篇)-JSP
- spring boot应用启动原理分析