Inline Hook

inline hook

说明

IAT hook 还是需要有先行条件的,就是导入表里面得有所使用函数的地址。

导入表里面没有被调函数的地址情况:

  • 被调函数与调用函数在同一模块
  • 直接自己使用LoadLibrary,GetProcAddress来加载函数

对于这种无法用IAT hook 的情况,我们可以使用inline hook。

inline hook 本质:就是jmp到我们需要执行的代码,执行完后再jmp回来。
Inline Hook
需要注意的地方:注意保存通用寄存器标志寄存器、然后在跳回去前复原被jmp指令代替的指令

在我们执行的代码中,若对栈有操作,得注意堆栈平衡。对堆栈里面的值谨慎进行修改

步骤

1、安装Inline Hook

  • 参数校验
  • 判断要替换的硬编码长度是否合适
  • 将要Hook的内存修改为可写:VirtualProtectEx或VirtualProtect
  • 创建内存空间,存储替换前的硬编码
  • 得到要跳转的值
    • 要跳转的地址 = E9的地址 + 5 +真正跳转的地址
    • –>E9后面的地址 = 要跳转的地址 - E9的地址 - 5
  • 将要Hook的内存全部初始化成NOP (长度>5 )
  • 修改要Hook的硬编码
  • 修改hook状态

2、使用被Hook的函数

  • 这时使用的就是钩子函数
  • 钩子函数得是裸函数
    • 保存寄存器
    • 获取数据
    • 恢复寄存器
    • 执行被覆盖的硬编码
    • 执行完毕 跳回Hook地址

3、卸载Inline Hook

  • 判断是否需要卸载,没Hook当然就不需要卸载
  • 修改内存为可写:VirtualProtectEx或VirtualProtect
  • 恢复原来的硬编码
  • 修改hook状态

Inline Hook 最需要注意的就是自己更改的硬编码需要将其还原,不能多加也不能少添。

#include <iostream>
#include <windows.h>
#define REPLACESIZE 5		//最少需要5字节替换,jmp 0x412345
using namespace std;

//最好全局变量 因为无需使用推栈  不担心栈平衡
DWORD dwParaX; 
DWORD dwParaY;
char szBuffer[20] = { 0 };

//全局变量 
DWORD g_RetAddr;
DWORD g_HookFlag;
LPVOID lpCodeBuffer;
DWORD g_JmpAddr;
DWORD g_FunAddr;
DWORD g_HookProc;
DWORD g_CodeSize;
DWORD g_Protect;

//Hook函数 裸函数
extern "C" _declspec(naked) void HookProc()
{
	//保存寄存器的值
	__asm
	{
		pushad
		pushfd
	}

	//获取数据
	__asm
	{
		mov eax, DWORD PTR SS : [esp + 0x28]
		mov dwParaX, eax
		mov eax, DWORD PTR SS : [esp + 0x2C]
		mov dwParaY, eax
	}
	//数字转字符
	sprintf_s(szBuffer, "参数X: %d,参数Y: %d", dwParaX, dwParaY);
	MessageBoxA(NULL, szBuffer, "Hook", MB_OK);

	//恢复寄存器
	__asm
	{
		popfd
		popad
	}
	
	//执行被覆盖的硬编码  被覆盖的函数开始的硬编码  这里调用函数用的jmp作了一个中转,没有到函数里面
	//__asm
	//{
	//	push  ebp
	//	mov   ebp, esp
	//	sub   esp, 0C0h
	//}

	//跳回去
	__asm
	{
		jmp g_RetAddr
	}
}

//原来的函数
DWORD Plus(DWORD x, DWORD y)
{
	return x + y;
}

//安装Inline Hook
void SetInlineHook(DWORD FunAddr, DWORD HookProc, DWORD CodeSize)
{
	//参数校验
	if (FunAddr == 0 || HookProc == 0)
	{
		cout << "地址为0" << endl;
		return;
	}
	
	//判断要替换的硬编码长度是否合适
	if (CodeSize < REPLACESIZE)
	{
		cout << "CodeSize < 5" << endl;
		return;
	}

	//将要Hook的内存修改为可写
	DWORD i_OldProtect;
	//第一种:VirtualProtectEx(GetCurrentProcess(), (LPVOID)FunAddr, CodeSize, PAGE_EXECUTE_READWRITE, &OldProtect);
	//自己进程不需要VirtualProtectEx
	VirtualProtect((LPVOID)FunAddr, CodeSize, PAGE_EXECUTE_READWRITE, &i_OldProtect);

	//创建内存空间,存储替换前的硬编码
	lpCodeBuffer = malloc(CodeSize);
	if (lpCodeBuffer == NULL)
	{
		cout << "创建内存空间错误" << endl;
		return;
	}
	memcpy(lpCodeBuffer, (LPVOID)FunAddr, CodeSize);

	//得到要跳转的值  E9后面的地址 =  要跳转的地址 -  E9的地址 - 5 
	g_JmpAddr = HookProc - FunAddr - 5;

	//将要Hook的内存全部初始化成NOP
	memset((LPVOID)FunAddr, 0x90, CodeSize);

	//修改要Hook的硬编码 
	*(PBYTE)FunAddr = 0xE9;
	*(PDWORD)((PBYTE)FunAddr + 1) = g_JmpAddr;

	//修改hook状态
	g_HookFlag = 1;
	g_RetAddr = FunAddr + CodeSize;
	g_FunAddr = FunAddr;
	g_HookProc = HookProc;
	g_CodeSize = CodeSize;
	g_Protect = i_OldProtect;

	//还原属性
	VirtualProtect((LPVOID)FunAddr, CodeSize, i_OldProtect, &i_OldProtect);
	
}

void UnInlineHook()
{
	//判断是否需要卸载
	if (!g_HookFlag)
	{
		cout << "没安装,不需要卸载" << endl;
		return;
	}
	//内存修改为可写
	DWORD u_OldProtect;
	VirtualProtect((LPVOID)g_FunAddr, g_CodeSize, PAGE_EXECUTE_READWRITE, &u_OldProtect);

	//还原
	memcpy((LPVOID)g_FunAddr, lpCodeBuffer, g_CodeSize);
	VirtualProtect((LPVOID)g_FunAddr, g_CodeSize, g_Protect, &g_Protect);

	//修改hook状态
	g_HookFlag = 0;
	g_RetAddr = 0;
	g_FunAddr = 0;
	g_HookProc = 0;
}

//主函数
int main()
{

	//安装Inline Hook
	SetInlineHook((DWORD)Plus, (DWORD)HookProc, 5);

	//执行函数
	system("pause");
	Plus(1, 3);

	//卸载Inline Hook
	UnInlineHook();

	//执行函数
	system("pause");
	Plus(1, 3);
}

实验说明:
在调用函数时会先跳转到我hook的代码,然后再跳回来。CE软件里面有个注入代码模板使用的就是Inline Hook
Inline Hook

上一篇:C++将一个数(整数)正逆序打印以及挨个数字提取出来的方法(不会用到字符串)


下一篇:Js基础知识(js逆向第一步)