Inline Hook(内联钩子)

原理

API函数保存在操作系统的DLL文件中,在运行程序后程序会将API所在的DLL加载入进程中,当在程序中使用某个API函数时。这样程序就会像调用自己的函数一样调用API,大体过程如图1所示。

imgbed.cn图床

图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了。

imgbed.cn图床

由于这种方法是在程序流程中直接进行嵌入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];//m_bNewBytes[0] = '\xe9',为jmp的机器码,m_bNewBytes为jmp指令

};


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();
}

//本质就是把User32.dll中的MessageBox的地址换成钩子函数地址
//模块名 pszModuleName, 函数名 pszFuncName, 钩子函数 pfnHookFunc
BOOL CILHook::Hook(LPCWSTR pszModuleName, LPCSTR pszFuncName, PROC pfnHookFunc) {
BOOL bRet = FALSE;
//获取指定模块中函数的地址,User32.dll中的MessageBox
m_pfnOrig = GetProcAddress(GetModuleHandle(pszModuleName), pszFuncName);
if (m_pfnOrig != NULL) {
//保存该地址处5个字节的内容到m_bOldBytes
DWORD dwNum = 0;
ReadProcessMemory(GetCurrentProcess(), (LPCVOID)m_pfnOrig, m_bOldBytes, 5, &dwNum);

m_bNewBytes[0] = '\xe9';//jmp的机器码
//pfnhookfunc是HOOK后的目标地址,m_pfnOrig是原来的地址,5是指令长度
*(DWORD*)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5;

WriteProcessMemory(GetCurrentProcess(), (LPVOID)m_pfnOrig, m_bNewBytes, 5, &dwNum);
bRet = TRUE;
}
return bRet;
}

//解除Hook
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;

//钩子函数,弹出内容为HOOK的对话框
int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
{
MsgHook.UnHook();
MessageBox(NULL, _T("Hook"), lpCaption, uType);//钩子函数弹出Hook
MsgHook.ReHook();
return 0;
}

int main()
{
//hook后的MessageBox
MsgHook.Hook(_T("User32.dll"), "MessageBoxA", (FARPROC)MyMessageBoxW);
MessageBox(NULL, _T("test"), _T("test"), MB_OK);//如果成功,弹出的内容不是test而是Hook。
MsgHook.UnHook();
return 0;
}

利用过程

在windows 10中测试失败,仍然执行正常的MessageBox函数,原因是windows 10操作系统中采用ASLR(地址随机化),用公式计算出来的偏移地址是无效的,所以无法jmp到钩子函数执行。而且在多线程系统中,可能有某个线程就在这个时候调用了修改的系统函数,造成无法预期的结果。在windows 10中,可以使用微软的一个轻量级的开源库——detours来完成Hook。

imgbed.cn图床

使用detours进行API Hook

这里可以使用微软的一个轻量级的开源库来完成Hook。Detours是微软提供的一个开发库,使用它可以简单、高效、稳定地实现API HOOK的功能。

安装

在Visual Studio中选择工具->NuGet包管理器->程序包管理器控制台

输入安装命令会自动安装Detours库:

1
Install-Package 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;
}

利用过程

成功

imgbed.cn图床

具体内容和利用可见

渗透技巧——从远程桌面客户端提取明文凭据

Detours InLine Hook


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!