原理
API函数保存在操作系统的DLL文件中,在运行程序后程序会将API所在的DLL加载入进程中,当在程序中使用某个API函数时。这样程序就会像调用自己的函数一样调用API,大体过程如图1所示。
图1 调用API函数的大体过程
从图1中可以看出,在进程中当EXE模块调用CreateFile()函数的时候,会去调用kernel32.dll模块中的CreateFile()函数,因为真正的CreateFile()函数的实现在kernel32.dll模块中。
CreateFile()是API函数,API函数也是由人编写的代码再编译而成的,也有其对应的二进制代码。既然是代码,那么就可以被修改。通过一种“野蛮”的方法来直接修改API函数在内存中的映像,从而对API函数进行HOOK。使用的方法是,直接使用汇编指令的jmp指令将其代码执行流程改变,进而执行我们的代码,这样就使原来的函数的流程改变了。执行完我们的流程以后,可以选择性地执行原来的函数,也可以不继续执行原来的函数。
假设要对某进程的kernel32.dll的CreateFile()函数进行HOOK,首先需要在指定进程中的内存中找到CreateFile()函数的地址,然后修改CreateFile()函数的首地址的代码为jmpMyProc的指令。这样,当指定的进程调用CreateFile()函数时,就会首先跳转到我们的函数当中去执行流程,这样就完成了我们的HOOK了。
由于这种方法是在程序流程中直接进行嵌入jmp指令来改变流程的,所以就把它叫做Inline Hook。
实现
Inline Hook是在程序中嵌入了jmp指令然后跳转到我们的程序流程中继续执行的,jmp指令的用法是jmp 目的地址,减5是因为jmp xxxx指令机器码长5个字节
jmp后的偏移量 = 目的地址 - 源地址 - 5
目的地址:钩子函数地址 源地址:要HOOK的函数地址
梳理一下我们内联钩子的流程:
- 构造jmp指令
- 在内存中找到想要HOOK的函数地址,并保存为HOOK位置处的前5个字节
- 将构造的跳转指令写入需要HOOK的位置
- 当HOOK位置被执行时会跳转到我们的执行流程
- 如果要继续原来的流程,那么取消HOOK,也就是还原被修改的字节
- 执行原来的流程
- 继续HOOK住原来的位置
代码
先封装一个CILHook类,
头文件CILHook.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #pragma once #include <Windows.h> #include <tchar.h> class CILHook { public: CILHook(); ~CILHook();
BOOL Hook(LPCTSTR pszModuleName, LPCSTR pszFuncName, PROC pfnHookFunc); void UnHook(); BOOL ReHook();
private: FARPROC m_pfnOrig; BYTE m_bOldBytes[5]; BYTE m_bNewBytes[5];
};
|
CILHook.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include "CILHook.h"
CILHook::CILHook() { m_pfnOrig = NULL; ZeroMemory(m_bNewBytes, 5); ZeroMemory(m_bOldBytes, 5);
}
CILHook::~CILHook() { UnHook(); }
BOOL CILHook::Hook(LPCWSTR pszModuleName, LPCSTR pszFuncName, PROC pfnHookFunc) { BOOL bRet = FALSE; m_pfnOrig = GetProcAddress(GetModuleHandle(pszModuleName), pszFuncName); if (m_pfnOrig != NULL) { DWORD dwNum = 0; ReadProcessMemory(GetCurrentProcess(), (LPCVOID)m_pfnOrig, m_bOldBytes, 5, &dwNum);
m_bNewBytes[0] = '\xe9'; *(DWORD*)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5;
WriteProcessMemory(GetCurrentProcess(), (LPVOID)m_pfnOrig, m_bNewBytes, 5, &dwNum); bRet = TRUE; } return bRet; }
void CILHook::UnHook() { BOOL bRet = FALSE; if (!m_pfnOrig != 0) { DWORD dwNum = 0; WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum); bRet = TRUE; } } BOOL CILHook::ReHook() { BOOL bRet = FALSE; if (m_pfnOrig != 0) { DWORD dwNum = 0; WriteProcessMemory(GetCurrentProcess(), (LPVOID)m_pfnOrig, m_bNewBytes, 5, &dwNum); bRet = TRUE; } return bRet; }
|
主程序代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> #include "CILHook.h"
CILHook MsgHook;
int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType) { MsgHook.UnHook(); MessageBox(NULL, _T("Hook"), lpCaption, uType); MsgHook.ReHook(); return 0; }
int main() { MsgHook.Hook(_T("User32.dll"), "MessageBoxA", (FARPROC)MyMessageBoxW); MessageBox(NULL, _T("test"), _T("test"), MB_OK); MsgHook.UnHook(); return 0; }
|
利用过程
在windows 10中测试失败,仍然执行正常的MessageBox函数,原因是windows 10操作系统中采用ASLR(地址随机化),用公式计算出来的偏移地址是无效的,所以无法jmp到钩子函数执行。而且在多线程系统中,可能有某个线程就在这个时候调用了修改的系统函数,造成无法预期的结果。在windows 10中,可以使用微软的一个轻量级的开源库——detours来完成Hook。
使用detours进行API Hook
这里可以使用微软的一个轻量级的开源库来完成Hook。Detours是微软提供的一个开发库,使用它可以简单、高效、稳定地实现API HOOK的功能。
安装
在Visual Studio中选择工具
->NuGet包管理器
->程序包管理器控制台
输入安装命令会自动安装Detours库:
导入代码如下:
1 2
| #include <detours.h> #pragma comment (lib,"detours.lib")
|
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <Windows.h> #include <detours.h> #pragma comment (lib,"detours.lib") static int (WINAPI* OldMesssageBoxA) ( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType ) = MessageBoxA;
int WINAPI MyFunction0(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { return OldMesssageBoxA(NULL, "Hooked", "Warning", MB_OKCANCEL); }
int main() { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)OldMesssageBoxA, MyFunction0); DetourTransactionCommit();
MessageBox(NULL, _T("test"), _T("test"), MB_OK);
return 0; }
|
利用过程
成功
具体内容和利用可见
渗透技巧——从远程桌面客户端提取明文凭据
Detours InLine Hook