Inline Hook

一、 什么是 inline hook
inline hook 就是在运行的流程中插入跳转指令(call/jmp)来抢夺程序运行流程的一个方法。
好了那么问题就来了:
1. 插入怎么样的跳转指令
    一般为 相对JMP(0xE9) + 4字节地址,方便计算 
    公式:源地址 + 相对偏移 = 目的地址
    公式:目的地址 - 源地址 = 相对偏移
2. 被跳转指令覆盖的原始指令应该被保存起来,找适当的时机再执行
3. 需要对应的 jmpIn 和 jmpOut 跳转。
4. inline hook 的一般流程:

源程序流 -> jmpIn -> 保存寄存器 -> 具体处理 -> 恢复寄存器 -> jmpOut -> 源程序流被跳转指令覆盖的代码可以在 jmpIn之后 或者 jmpOut之前


二、 被 inline hook 的位置
1. 指令或者指令之和大于等于5,因为我们要塞进去5个字节(0xE9 + Address)。只要你处理跳转指令覆盖的指令,理论上可以 inline hook 函数中的任意位置。
2. 判断被 inline hook 位置的指令可以使用反汇编引擎判断指令长度。
3. 很多系统调用的开头都是,能很方便的 inline hook
代码:
mov    edi, edi
push    ebp
mov    ebp, esp

三、 可以写 hookProxy 函数,保存和恢复寄存器,统一调用逻辑。

完整代码逻辑:

//.H
#pragma once
__declspec(naked) int Add(int a, int b)
{
	_asm
	{
		mov edi, edi
		push ebp
		mov ebp, esp

		sub esp, 8
		mov eax, [ebp+0x08]
		mov [ebp-0x08], eax
		mov eax, [ebp+0xc]
		mov [ebp-0x04], eax

		mov eax, [ebp-0x08]
		add eax, [ebp-0x04]

		mov esp, ebp
		pop ebp

		ret
	}
}

__declspec(naked) void HookProxy()
{
	// moved bytes
	_asm
	{
		nop
		nop
		nop
		nop
		nop
	}

	// save registers
	//_asm
	//{
	//	pushad
	//	pushfd
	//}

	__asm
	{
		add [esp+0x08], 1
		add [esp+0x0c], 1
	}

	// restore registers
	//_asm
	//{
	//	popad
	//	popfd
	//}

	// use to jump source func back code
	_asm
	{
		nop
		nop
		nop
		nop
		nop
	}
}

void InlineHook()
{
	// move old bytes
	memcpy((BYTE*)HookProxy, (BYTE*)Add, 5);

	// rewrite Add the first of 5 bytes to jmp xxxx
	BYTE jmpInBuffer[5];
	jmpInBuffer[0] = 0xe9;

	DWORD* pJmpInOffset = (DWORD*)&jmpInBuffer[1];
	*pJmpInOffset = (DWORD)HookProxy - ((DWORD)Add + 5);

	memcpy(Add, jmpInBuffer, 5);

	// rewrite HookProc the end 0f 5 bytes to jmp xxxx
	BYTE jmpOutBuffer[5];

	jmpOutBuffer[0] = 0xe9;

	DWORD* pJmpOutOffset = (DWORD*)&jmpOutBuffer[1];
	BYTE* pJmpBackCode = &((BYTE*)HookProxy)[0xF]; // the pos that the end of 5 bytes
	*pJmpOutOffset = ((DWORD)Add + 5) - ((DWORD)pJmpBackCode + 5);

	memcpy(pJmpBackCode, jmpOutBuffer, 5);
}

void UnInlineHook()
{
	// write back Add the first of 5 bytes
	BYTE restoreBuffer[5] = { 0 };
	memcpy(restoreBuffer, HookProxy, 5);
	memcpy(Add, restoreBuffer, 5);
}

//.CPP
DWORD dwNewProtect = PAGE_EXECUTE_READWRITE;
	DWORD oldProtect1;
	DWORD oldProtect2;

	VirtualProtect((LPVOID)Add, 4096, dwNewProtect, &oldProtect1);
	VirtualProtect((LPVOID)HookProxy, 4096, dwNewProtect, &oldProtect2);

	InlineHook();
	printf("After Hook Result = %d \n", Add(1, 2));

	UnInlineHook();
	printf("After UnHook Result = %d \n", Add(1, 2));

	VirtualProtect((LPVOID)Add, 4096, oldProtect1, &dwNewProtect);
	VirtualProtect((LPVOID)HookProxy, 4096, oldProtect2, &dwNewProtect);
四、解析例子
1. 把Add函数的前5个字节搬到了 hookProxy的前5个字节。
2. 然后再 Add函数的前5个字节填充跳转到 hookProxy 的跳转指令
3. 在hookProxy的末尾5个字节填充跳转回 Add函数 + 5 的跳转指令
4. 原本 Add 函数的结构是3,被 hook 之后会变成 5

栗子2:

//.H
#pragma pack(1)
typedef struct _JMPCODE
{
	BYTE jmp;
	DWORD addr;
}JMPCODE, *PJMPCODE;

typedef int (WINAPI *MessageBox_type) (
		__in_opt HWND hWnd,
		__in_opt LPCSTR lpText,
		__in_opt LPCSTR lpCaption,
		__in UINT uType);

MessageBox_type RealMessageBox = NULL;

_declspec(naked) int WINAPI MyMessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
	_asm
	{
		push ebp
		mov ebp, esp
	}

	printf("hwnd:%p lpText:%s lpCaption:%s,uType:%X\n", hWnd, lpText, lpCaption, uType);

	_asm
	{
		mov ebx, RealMessageBox
		add ebx, 5
		jmp ebx
	}
}

void HookMessageBox()
{
	RealMessageBox = MessageBoxA;

	JMPCODE jumpCode;
	jumpCode.jmp = 0xe9;
	jumpCode.addr = (DWORD)MyMessageBox - ((DWORD)RealMessageBox + 5);

	DWORD dwNewProtect = PAGE_EXECUTE_READWRITE;
	MEMORY_BASIC_INFORMATION mbi;
	VirtualQuery(MessageBoxA, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
	
	VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
	WriteProcessMemory(GetCurrentProcess(), MessageBoxA, &jumpCode, sizeof(jumpCode), NULL);
	VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwNewProtect);
}

void UnHookMessageBox()
{
	// 也可以在Hook的时候保存MessageboxA的前5个字节
	BYTE backCode[5] = { 0 };
	backCode[0] = 0x8B;   // mov edi, edi
	backCode[1] = 0xFF;
	backCode[2] = 0x55;   // push ebp
	backCode[3] = 0x8B;   // mov ebp, esp
	backCode[4] = 0xEC;

	DWORD dwNewProtect = PAGE_EXECUTE_READWRITE;
	MEMORY_BASIC_INFORMATION mbi;
	VirtualQuery(MessageBoxA, &mbi, sizeof(MEMORY_BASIC_INFORMATION));

	VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
	WriteProcessMemory(GetCurrentProcess(), MessageBoxA, backCode, sizeof(backCode) / sizeof(backCode[0]), NULL);
	VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwNewProtect);
}

//.CPP
HookMessageBox();
	::MessageBoxA(NULL, "HELLO", "WORLD", MB_OK);

	UnHookMessageBox();
	::MessageBoxA(NULL, "HELLO2", "WORLD2", MB_OK);
执行步骤:

1.把MessageBoxA的前5个字节改写为:jmp xxx(地址是MyMessageBoxA),跳转的指令格式:jmp 相对地址

2.在MessageBoxA中,重新执行原来的汇编指令:(mov edi, edi可忽略)

push ebp
mov ebp, esp

然后执行自己的逻辑

最后再跳转到MessageBoxA中第5个字节的位置继续执行,这里的跳转的指令格式:jmp 16位寄存器

参考:

http://bbs.pediy.com/showthread.php?p=1337589

你可能感兴趣的:(Windows编程)