【免杀前置课——HOOK】二十四、消息HOOK,各种句柄的区别HMODULE,HANDLE等,IAT HOOK,Inline hook,硬编码是什么?调试dll窍门(附代码)

什么是 HOOK:

Windows操作系统是事件驱动的。事件被包装了消息发送给窗口,比如点击菜单,按钮,移动窗口等等~
消息处理过程:例如:
1、当按下键盘时,会产生一个键盘按下的消息,这个消息首先被加入到系统消息队列2、操作系统从消息队列中取出消息,添加到相应的程序的消息队列中
3、程序自身通过GetMessage获取消息,DispatchMessage分发消息,通过消息回调函数处理消息。
HOOK就是从系统消息队列到应用程序消息队列之前对消息进行处理,处理之后在扔给下一个消息钩子(HOOK)或者扔到应用程序的消息队列中。

在操作系统中,操作系统会从系统消息队列中找到不同程序的消息队列分发消息,hook就是在传递这条线上截取相应的消息,对消息进行处理,处理后按顺序从上到下让hook或者程序消息队列处理。

在这里插入图片描述
在这里插入图片描述

消息hook

编写消息钩子需要将设置钩子的函数写到dll里面,当勾住一个线程后,产生消息时,假如系统发现包含钩子的dll不在本进程当中,系统会将dll强行加载进去,这一是一种dlI注入的手段。

C++中各种句柄的区别

  • HMODULE表示模块句柄
  • Handle 是代表 系统的内核对象,如 文件句柄,线程句柄,进程句柄。
  • HMODULE 是代表应用 程序载入的模块,win32系统下通常是被载入模块的 线性地址。
  • HINSTANCE 表示
    实例句柄。在win32下与HMODULE是相同的东西,在Win32下还存在主要是因为win16程序使用HINSTANCE来区别task。
  • HWND 是 窗口句柄

在头文件中HMODULE定义如下:
typedef HINSTANCE HMODULE;
再看看HINSTANCE定义,typedef HANDLE HINSTANCE;
再看看HANDLE定义,typedef PVOID HANDLE;
再看看PVOID定义,typedef void *PVOID;
其实这些都可以称为句柄,为了表述的方便,所以对于不同类型的句柄都用不同样式的typedef,比如说HINSTANCE表示 实例句柄,HMODULE是 模块句柄,实际上他们本质上都是VOID 指针,是可以指向任何类型的指针。

在虚拟机中运行需要改运行库,因为虚拟机可能有很多文件没有。

在这里插入图片描述
在这里插入图片描述

IAT HOOK

hookmain.cpp

#include"hookMain.h"

int WINAPI HookMessageBoxW(
	HWND    hWnd,
	LPCWSTR lpText,
	LPCWSTR lpCaption,
	UINT    uType
)
{
	int result = MessageBoxA(0, "51HOOK", "提示", MB_OK);
	return result;
}
//安装钩子
BOOL InstallHook()
{
	DWORD dwOldProtect = 0;//修改之前的属性
	VirtualProtect(g_iatAddr, 4, PAGE_EXECUTE_READWRITE, & dwOldProtect);
	*g_iatAddr = (DWORD)HookMessageBoxW;//因为我们HookMessageBoxW函数的文件属性不一定是可写的,所以我们要在上面改可写属性
	VirtualProtect(g_iatAddr, 4, dwOldProtect, &dwOldProtect);
	return TRUE;
}
//卸载钩子
BOOL UnInstallHook()
{
	DWORD dwOldProtect = 0;//修改之前的属性
	VirtualProtect(g_iatAddr, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
	*g_iatAddr = (DWORD)g_unHookAddr;//因为我们HookMessageBoxW函数的文件属性不一定是可写的,所以我们要在上面改可写属性
	VirtualProtect(g_iatAddr, 4, dwOldProtect, &dwOldProtect);
	return TRUE;
}

DWORD* GetIatAddr(const char* dllName,const char* dllFunName) 
{
	//获取当前EXE文件模块句柄
	HMODULE hModule = GetModuleHandleA(0);
	DWORD dwhModule = (DWORD)hModule;
	//获取当前dos头
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwhModule;
	//获取当前NT头
	PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + dwhModule);
	//获取可选PE头
	PIMAGE_OPTIONAL_HEADER pOptionalHeader = &pNtHeaders->OptionalHeader;
	//获取数据目录表
	IMAGE_DATA_DIRECTORY dataDirectory = pOptionalHeader->DataDirectory[1];
	//获取导入表
	PIMAGE_IMPORT_DESCRIPTOR pImageImageTable = (PIMAGE_IMPORT_DESCRIPTOR)(dataDirectory.VirtualAddress + dwhModule);
	//遍历导入表获取符合条件的函数
	while (pImageImageTable->Name)
	{
		char* iatDllName = (char*)(pImageImageTable->Name + dwhModule);
		if (_stricmp(iatDllName, dllName) == 0)//判断dllName是否和iatDllName相同 查网dll遍历与函数原型,这里dll遍历是不区分大小写 不用完全匹配 函数名是区分的 要完全匹配
		{
			//获取导入名称表
			PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)(pImageImageTable->OriginalFirstThunk + dwhModule);
			//获取导入地址表
			PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)(pImageImageTable->FirstThunk + dwhModule);
			while (pINT->u1.Function)//遍历名称表
			{
				if ((pINT->u1.Ordinal & 0x80000000)==0)//确定INT是按名称导入的
				{
					PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)(pINT->u1.Function + dwhModule);
					if (strcmp(pImportName->Name, dllFunName) == 0)//按不区分大小写的方式比较名称 查网dll遍历与函数原型,这里dll遍历是不区分大小写 不用完全匹配 函数名是区分的 要完全匹配
					{
						return (DWORD*)pIAT;//获取到要替换函数的地址
					}
				}
				pINT++;
				pIAT++;
			}
		}
		pImageImageTable++;
	}
}

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD callReason, LPVOID lpReversed)
{
	if (callReason == DLL_PROCESS_ATTACH)
	{
		//获取IAT表
		g_iatAddr = GetIatAddr("user32.dll","MessageBoxW");
		//保护要HOOK的函数地址,最后卸载钩子要能够找到
		g_unHookAddr = (DWORD*)*g_iatAddr;
		//安装钩子
		InstallHook();
	}
	else if (callReason==DLL_PROCESS_DETACH)
	{
		UnInstallHook();
	}
	return TRUE;
}

hookmain.h

#pragma once
#include <Windows.h>
DWORD* g_iatAddr = NULL;
DWORD* g_unHookAddr = NULL;
int WINAPI HookMessageBoxW(
	HWND    hWnd,
	LPCWSTR lpText,
	LPCWSTR lpCaption,
	UINT    uType
);
BOOL InstallHook();
BOOL UnInstallHook();
DWORD* GetIatAddr(const char* dllName, const char* dllFunName);

test main.cpp

#include<Windows.h>

int main()
{

	HMODULE hModule = LoadLibraryA("IATHook.dll");
	if (hModule)
	{
		MessageBoxW(0, L"好喽", L"结束折磨", MB_OK);
	}
	return 0;
}

编写完成后我们通过代码注入器进行注入。
在这里插入图片描述

Inline hook

如果我们想要操控的函数不在IAT表中我们该如何hook?
可以使用内联hook。

思路

1、找到我们要HOOK函数地址
2、保存要HOOK的函数的前5个字节
3、计算目标函数距离jmp指令的一条指令的偏移offset
4、改变函数的前5个字节改成OXE9 offset
正常的API函数调用流程应该是由调用者(进程)通过函数名去调用已加载动态链接库中的导出函数。那么InlineHook的流程一般是在被调用API的头部,插入跳转指令的方式,劫持函数执行流程。

下图就是MessageBoxA的函数实现:
在这里插入图片描述

那么,我们可以在头部做出如下修改,进而实现HOOK:
在这里插入图片描述
上图中的12345678就代指的是我们自己的函数,通过这种方法劫持到我们的流程中执行。下面,我们采用MessageBoxW的Hook作为例子,实际体验一下InlineHook的实现方式。

硬编码

之所以要对前五个字节进行操控,是因为五个字节为jmp命令需要占五个字节,硬编码中jmp指令为 E9 xxxxx
汇编中要jmp跳转的目标地址=xxxxx+5+jmp指令初地址
即上图12345678-= 9C5421A3+5+75E034D0

所以E9后面xxxxx就是跳转目标地址距JMP指令下一个指令的偏移
在这里插入图片描述

main.h

#pragma once
#include<iostream>
#include<Windows.h>
int WINAPI MyMessageBoxW(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType);
BOOL InstallHook();
BOOL UnInstallHook();

main.cpp

#include"main.h"

DWORD g_unhookfun = NULL;
char g_oldcode[5] = { 0 };//函数的前五个字节也就是旧函数的地址
char g_newcode[5] = { 0xE9 };

//思路
//1、找到我们要HOOK函数地址
//2、保存要HOOK的函数的前5个字节
//3、计算目标函数距离jmp指令的一条指令的偏移offset
//4、改变函数的前5个字节改成OXE9 offset

//1、初始化函数:进行hook的初始工作找到hook函数保存函数的前五个字节,计算出偏移值。保存改变后的前5个字节
//2、安装钩子
//3、卸载钩子
//4、自定义函数

int WINAPI MyMessageBoxW(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType)
{
	UnInstallHook();
	int result = MessageBoxW(hWnd, L"51HOOK", lpCaption, uType);
	InstallHook();
	return result;
}

//安装钩子
BOOL InstallHook()
{
	DWORD oldProtect = 0;
	if (g_unhookfun==0)
	{
		return FALSE;
	}
	VirtualProtect((DWORD*)g_unhookfun, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
	memcpy((DWORD*)g_unhookfun, g_newcode, 5);	
	VirtualProtect((DWORD*)g_unhookfun, 5, oldProtect, &oldProtect);
	return TRUE;
}

//卸载钩子
BOOL UnInstallHook()
{
	DWORD oldProtect = 0;
	if (g_unhookfun == 0)
	{
		return FALSE;
	}
	VirtualProtect((DWORD*)g_unhookfun, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
	memcpy((DWORD*)g_unhookfun, g_oldcode, 5);
	VirtualProtect((DWORD*)g_unhookfun, 5, oldProtect, &oldProtect);
	return TRUE;
}

//初始化函数
BOOL InitHook()
{
	//找到要Hook函数的地址
	HMODULE hModule = LoadLibraryA("user32.dll");
	if (hModule == 0)
	{
		return 0;
	}
	g_unhookfun = (DWORD)GetProcAddress(hModule, "MessageBoxW");
	//保存函数的前五个字节(旧函数的地址)
	memcpy(g_oldcode, (char*)g_unhookfun, 5);
	//计算偏移
	DWORD offset = (DWORD)MyMessageBoxW - (g_unhookfun + 5);
	//保存hook后的五个字节(新函数的地址)
	memcpy(&g_newcode[1], &offset, 4);
	return TRUE;
}

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD callReason, LPVOID lpReserved)
{
	if (callReason == DLL_PROCESS_ATTACH)
	{
		InitHook();
		InstallHook();
	}
	else if (callReason == DLL_PROCESS_DETACH)
	{
		UnInstallHook();
	}
	return TRUE;
}

调试DLL

有时候我们没办法调试,不知道dll文件哪里有问题,我们可以在项目属性里,浏览要注入的exe文件,选中后应用确定。直接调试,我们可以看到关联的exe文件也运行了,这时打开dll注入工具注入dll,即可进行调试。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webfker from 0 to 1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值