WEB

WEB安全

漏洞复现

CTF

常用工具

实战

代码审计

Javaweb

后渗透

内网渗透

免杀

进程注入

权限提升

漏洞复现

靶机

vulnstack

vulnhub

Root-Me

编程语言

java

逆向

PE

逆向学习

HEVD

PWN

CTF

heap

其它

关于博客

面试

杂谈

InlineHook

最近学了一下InlineHook,中文名称是内联钩子

其实就是将函数开头的几个汇编指令修改成跳转,然后跳到要执行的地方

B站:https://www.bilibili.com/video/BV1Ap4y1h72r

0x01 手动Hook

首先写一个程序

1
2
3
4
5
6
7
8
9
10
11
#include<Windows.h>
#include<stdio.h>

void HelloWorld() {
printf("HelloWorld");
}
int main() {
MessageBoxA(NULL, "text", "title", MB_OK);
HelloWorld();
return 0;
}

这个程序很简单就是弹框然后打印HelloWorld

现在编译一下丢到x32dbg

可以看到printf函数的地址,还有MessageBoxA,直接搜索MessageBoxA,找到这个函数地址

前五个字节码修改为jmp printf地址,也就是jmp 0x009D1054

现在把这个程序执行完,会打印两个HelloWorld

这样就是Hook了MessageBoxA这个API

0x02 代码实现(32位)

写一个dll,将dll注入到要Hook的进程,修改函数前五位字节码实现Hook

MyHook.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma once
#include<Windows.h>

class CMTHook
{
public:
CMTHook(void); //构造函数
~CMTHook(void); //析构函数
BOOL Hook(LPSTR pzModuleName, LPSTR pzFunctionName, FARPROC newFunction); //Hook函数,参数为dll名称,函数名称,Hook后要执行函数名称
VOID unHook(); //卸载Hook
BOOL ReHook(); //重装Hook
//这两个按照教程写了偷懒了所以就不用了
private:
FARPROC m_FunctionAddress; //要Hook的函数地址
BYTE oldBytes[5]; //保存原来的五个字节用于卸载Hook后写回去
BYTE newBytes[5]; //要写入的新的五个字节
};

MyHook.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
#include "Myhook.h"
#include<Windows.h>

CMTHook::CMTHook()
{
m_FunctionAddress = NULL;
memset(oldBytes, 0, 5);
memset(newBytes, 0, 5);
}

CMTHook::~CMTHook()
{
unHook();
m_FunctionAddress = NULL;
memset(oldBytes, 0, 5);
memset(newBytes, 0, 5);
}

BOOL CMTHook::Hook(LPSTR pzModuleName, LPSTR pzFunctionName, FARPROC newFunction)
{
m_FunctionAddress = (FARPROC)GetProcAddress(GetModuleHandleA(pzModuleName), pzFunctionName);
//获取要修改的dll的基址
if (m_FunctionAddress == NULL) {
return false;
}
//获取值为NULL返回false
DWORD dwRet = 0; //接收实际读取或写入大小
ReadProcessMemory(GetCurrentProcess(), m_FunctionAddress, oldBytes, 5, &dwRet);
//读取旧的五个字节保存到数组中
newBytes[0] = '\xe9'; //段间转移jmp的机器码对应e9
*(DWORD *)(newBytes + 1) = (DWORD)newFunction - (DWORD)m_FunctionAddress - 5;
//获取函数之间的偏移,-5是减去跳转的机器码,转成dword是因为刚好对应4个字节的地址
BOOL ret = WriteProcessMemory(GetCurrentProcess(), m_FunctionAddress, newBytes, 5, &dwRet); //最后把跳转指令写入
return true;
}

VOID CMTHook::unHook()
{
if (m_FunctionAddress != 0) {
DWORD dwRet;
WriteProcessMemory(GetCurrentProcess(), m_FunctionAddress, oldBytes, 5, &dwRet); //把旧的指令写回去就是unHook
}
return VOID();
}

BOOL CMTHook::ReHook()
{
if (m_FunctionAddress != 0) {
DWORD dwRet;

WriteProcessMemory(GetCurrentProcess(), m_FunctionAddress, newBytes, 5, &dwRet); //重新把新指令写入
}
return 0;
}

主要还是要看一下

*(DWORD *)(newBytes + 1) = (DWORD)newFunction - (DWORD)m_FunctionAddress - 5;

后加载的dll地址肯定要高于先加载的,所以是新函数-要Hook的函数,jmp需要五个字节所以要-5

不然jmp的偏移会出错

dllmain.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
#include "Myhook.h"
#include<Windows.h>
#include<stdio.h>
int WINAPI HookMessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType) {
system("powershell.exe")
return 0;
};

CMTHook m_MyHook;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
char dll[] = "user32.dll";
char func[] = "MessageBoxA";
m_MyHook.Hook(dll, func, (FARPROC)HookMessageBoxA);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

这个就是dll的主函数了,如果要HookMessageBoxA,必须先写一个参数数量相同的函数,这样Hook函数执行完后不会出现堆栈不平衡

这里的话是Hook MessageBoxA这个函数,每次执行这个函数就会弹出powershell

这样的话整个dll就已经写好了,剩下的就是注入到进程内,dll注入之前文章写过了,直接去看就可以

这里用CrakeMe演示一下,把dll注入到CrakeMe中,然后点击注册触发MessageBoxA,跳到执行powershell

0x03 代码实现(64位)

32位只需要五个字节就可以跳转

64位需要十二个字节才可以跳转

1
2
mov rax, 地址
jmp rax

没啥区别的,保存12个字节,然后把函数地址放到rax里面最后jmp rax(FFE0)

都不用去算偏移

下面贴一下文件

MyHook.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma once
#include<Windows.h>

class CMTHook
{
public:
CMTHook(void);
~CMTHook(void);
BOOL Hook(LPSTR pzModuleName, LPSTR pzFunctionName, FARPROC newFunction);
VOID unHook();
BOOL ReHook();

private:
FARPROC m_FunctionAddress;
BYTE oldBytes[12];
BYTE newBytes[12];
};

MyHook.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
#include "Myhook.h"
#include<Windows.h>

CMTHook::CMTHook()
{
m_FunctionAddress = NULL;
memset(oldBytes, 0, 12);
memset(newBytes, 0, 12);
}

CMTHook::~CMTHook()
{
unHook();
m_FunctionAddress = NULL;
memset(oldBytes, 0, 12);
memset(newBytes, 0, 12);
}

BOOL CMTHook::Hook(LPSTR pzModuleName, LPSTR pzFunctionName, FARPROC newFunction)
{
m_FunctionAddress = (FARPROC)GetProcAddress(GetModuleHandleA(pzModuleName), pzFunctionName);
if (m_FunctionAddress == NULL) {
return false;
}
SIZE_T dwRet = 0;
ReadProcessMemory(GetCurrentProcess(), m_FunctionAddress, oldBytes, 12, &dwRet);
newBytes[0] = '\x48';
newBytes[1] = '\xb8';
newBytes[10] = '\xff';
newBytes[11] = '\xe0';
*(__int64*)(newBytes + 2) = (__int64)newFunction;
BOOL ret = WriteProcessMemory(GetCurrentProcess(), m_FunctionAddress, newBytes, 12, &dwRet);
return true;
}

VOID CMTHook::unHook()
{
if (m_FunctionAddress != 0) {
SIZE_T dwRet;
WriteProcessMemory(GetCurrentProcess(), m_FunctionAddress, oldBytes, 12, &dwRet);
}
return VOID();
}

BOOL CMTHook::ReHook()
{
if (m_FunctionAddress != 0) {
SIZE_T dwRet;

WriteProcessMemory(GetCurrentProcess(), m_FunctionAddress, newBytes, 12, &dwRet);
}
return 0;
}

dllmain.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
#include "Myhook.h"
#include<Windows.h>
#include<stdio.h>

BOOL
WINAPI
HookCreateProcessA(
_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
) {
MessageBoxA(NULL, lpCommandLine, "", MB_OK);
return 0;
};


CMTHook m_MyHook;
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
char dll[] = "user32.dll";
char func[] = "CreateProcessA";
m_MyHook.Hook(dll, func, (FARPROC)HookCreateProcessA);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

主要还是MyHook.cpp的区别,这次Hook CreateProcessA这个函数

写一个程序进行Hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<Windows.h>
#include<stdio.h>

int main() {
MessageBoxA(NULL, "text", "title", MB_OK);
ULONG len = 0;
STARTUPINFOA si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
CreateProcessA(NULL, (LPSTR)"powershell", NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
CreateProcessA(NULL, (LPSTR)"cmd", NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);

return 0;
}

这段代码会启动三个进程,用Hook让进程不启动,弹框弹出要启动进程的名字

弹框是为了让程序先停住,然后有注入的时间

注入好之后点击确定就可以看到进程名弹框