米兰电竞游戏欢迎您!
400-888-8888
当前位置: 首页 > 院校新闻 >> 正文

HOOK技术揭秘:消息钩子原理及恶意代码利用方式

时间: 2026-02-14 | 作者: 小编

在代码所构成的世界当中,始终是存在着一些希冀投机取巧之人的。一些带有恶意性质的代码借助 Hook 以及进程注入方面的技术手段,悄然不动声色地隐匿于你的个人电脑内部,它不但能够成功躲避开杀毒软件的仔细扫描,而且还能够长时间驻留在计算机内存当中,趁机窃取数据信息,甚至还能够获取到整台系统给予的最高权限。这样一种关于防范与攻击的对抗情况,每一天都在持续不断地上演。

HHOOK  WINAPI SetWindowsHookExA(
	int       idHook,    //钩取的消息的类型
	HOOKPROC  lpfn,      //指向钩子过程的指针,即回调函数地址
	HINSTANCE hmod,      //包含lpfn过程的dll实例句柄
	DWORD     dwThreadId //线程ID
);

钩子链条的暗黑艺术

LRESULT CALLBACK HookProc(
	int nCode,//钩子代码,表示如何处理消息;
    //wParam和lParam表示消息,根据消息的类型不同具有不同的含义;
	WPARAM wParam,
	LPARAM lParam
){
	// process event
	...
		return CallNextHookEx(NULL, nCode, wParam, lParam);
}

Window的消息钩子机制的原本意图是使得开发者能够对系统消息进行监控或者处理,像键盘输入、鼠标动作这类消息。攻击者能够借助SetWindowsHookEx函数来安装钩子,一旦消息被触发,他们所编写的代码便能够在受害进程里执行。当同时设置多个钩子时,它们会构建成一个链条,消息会按照顺次经过每一个钩子函数,这为攻击者提供了层层拦截的契机。

LRESULT WINAPI CallNextHookEx(
	_In_opt_ HHOOK hhk,//钩子的句柄
	_In_ int nCode, 
	_In_ WPARAM wParam,
	_In_ LPARAM lParam);

	BOOL WINAPI UnhookWindowsHookEx(
	_In_  HHOOK hhk //SetWindowsHookEx的返回值
);

关键在于全局钩子的参数设置,线程ID参数设为0时,此钩子会对系统里所有正在运行的进程产生影响,攻击范围瞬间得到扩大,然而钩子回调函数当中必须调用CallNextHookEx,不然消息会在此处卡住,致使系统界面出现卡顿甚至崩溃,这种异常反倒会引起用户的警觉。

HMODULE h = LoadLibraryA(hook_dll_path);
HOOKPROC f = (HOOKPROC)GetProcAddress(h, "GetMsgProc"); // 获取钩子函数地址,只要GetMessage或PeekMessage函数从应用程序消息队列中检索到消息,系统就会调用此函数。
SetWindowsHookExA(WH_GETMESSAGE, f, h, thread_id); //给线程安装钩子
PostThreadMessage(thread_id, WM_NULL, NULL, NULL); // 触发钩子

HWINEVENTHOOK WINAPI SetWinEventHook(
	// SetWinEventHook的第1,2个参数可以标识一个范围,表示截获哪个范围类的事件,EVENT_MIN-EVENT_MAX:0x00000001 - 0x7FFFFFFF
	__in  UINT eventMin,//指定钩子函数处理的事件范围中最低事件值的事件常量(超链接:https://docs.microsoft.com/zh-cn/windows/win32/winauto/event-constants)。
	__in  UINT eventMax,//指定钩子函数处理的事件范围中的最高事件值的事件常量。
	__in  HMODULE hmodWinEventProc,//如果在dwFlags参数中指定了WINEVENT_INCONTEXT标志,则指向包含lpfnWinEventProc中的钩子函数的DLL;若指定了WINEVENT_OUTOFCONTEXT标志,则此参数为NULL。
	__in  WINEVENTPROC lpfnWinEventProc, //指向事件挂钩函数的指针。
	__in  DWORD idProcess,//指定钩子函数从中接收事件的进程的ID。指定零(0)以从当前桌面上的所有进程接收事件。
	__in  DWORD idThread,//指定钩子函数从中接收事件的线程的ID。如果此参数为零,则钩子函数与当前桌面上的所有现有线程相关联。
	__in  UINT dwflags//标记值,指定挂钩函数的位置和要跳过的事件的位置。
);

IAT Hook的地址篡改术

typedef void (CALLBACK *WINEVENTPROC)(
	HWINEVENTHOOK hWinEventHook,//SetWinEventHook返回值,钩子函数句柄
	DWORD         event,//指定发生的事件(事件常量)
	HWND          hwnd,//生成事件的窗口,如果没有窗口与事件关联,则NULL; 
	LONG          idObject,//对象标识符,表示窗口某个部分
	LONG          idChild,//如果此值为CHILDID_SELF,则事件由对象触发; 如果此值是子ID,则该事件由子元素触发。
	DWORD         dwEventThread,//标识生成事件的线程或拥有当前窗口的线程
	DWORD         dwmsEventTime//指定生成事件的时间
	);

// Global variable.
HWINEVENTHOOK g_hook;
// Initializes COM and sets up the event hook.
void InitializeMSAA(){
	CoInitialize(NULL);
	g_hook = SetWinEventHook(
		EVENT_SYSTEM_MENUSTART, EVENT_SYSTEM_MENUEND,  // Range of events (4 to 5).
		NULL,                                          // Handle to DLL.
		HandleWinEvent,                                // The callback.
		0, 0,              // Process and thread IDs of interest (0 = all)
		WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
}
// Unhooks the event and shuts down COM.
void ShutdownMSAA(){
	UnhookWinEvent(g_hook);
	CoUninitialize();
}
// Callback function that handles events.
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
	LONG idObject, LONG idChild,
	DWORD dwEventThread, DWORD dwmsEventTime){
	IAccessible* pAcc = NULL;
	VARIANT varChild;
	HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild);
	if ((hr == S_OK) && (pAcc != NULL)){
		BSTR bstrName;
		pAcc->get_accName(varChild, &bstrName);
		if (event == EVENT_SYSTEM_MENUSTART){
			printf("Begin: ");
		}
		else if (event == EVENT_SYSTEM_MENUEND){
			printf("End:   ");
		}
		printf("%S\n", bstrName);
		SysFreeString(bstrName);
		pAcc->Release();
	}
}

每一个Windows程序均具备导入地址表,于其中所存放的是其打算调用的系统API函数地址。IAT Hook的原理是十分简单的,也就是寻找到这个表,将某个API的地址变更为恶意代码的地址。举例而言,程序原本计划调用MessageBox,然而却一头扎入了攻击者所编写的弹窗代码之中,用户根本无法察觉到异常。

达成这个技术要对PE文件结构予以解析,攻击者需读取目标程序的头部信息,遍历导入表,寻觅目标API的条目,这种变动极为隐蔽,鉴于程序的执行流程未发生改变,仅从中途转而弯折,平常的文件完整性核验很难察觉此处的异常。

五字节跳转的内存手术

HOOK方式里,最直接的是API函数开头修改,攻击者将函数前5个字节改成JMP指令,以此直接跳转到自己的代码,不过这么一改,那原来的5个字节就被覆盖了,要是恢复不及时,会导致系统功能异常,在Windows里,很多API开头都有MOV EDI, EDI这两个看似无用的字节。

机灵的攻击者会借助这两个字节来施行短跳转,跳转至函数上方的闲置区域。在那个地方存在着充足的空间用以写入跳转代码,既达成了 HOOK,又使原始指令得以留存,以这样的如同温水煮青蛙般的方式更不容易被察觉。恰似于你家门前挖掘了一条地道,然而门牌号依旧悬挂着。

AppInit_DLLs的注册表注入

有一个名为AppInit_DLLs的注册表项是Windows所提供的,任何加载user32.dll的进程,都会自动加载在此处指定的DLL,只要攻击者把写入恶意DLL的绝对路径,并且将LoadAppInit_DLLs值设为1,便能实现大规模注入,从WinXP到Win10都对该功能予以支持。

不过在默认情形之下,这个注册表项是不存在的,攻击者得自己去创建。借助这个机制,在系统启动之际,或者新进程创建之时,恶意代码会静悄悄地寄生进去。这种注入方式借助了系统自身的设计,杀毒软件很难分辨这到底是合法程序呢,还是恶意行为。

APC的异步执行劫持

Windows的并发机制包含异步过程调用,该机制能够让线程在正常执行路径之前先去运行一些代码。若有攻击者,会将恶意函数安插到目标线程的APC队列,待线程进入要警告才能等待的状态之际,那些代码便可以被执行。当创建进程CreateProcess时,只要设置挂起这一标志,就能够在这个新进程的线程方面动手脚。

在实际进行实施对应攻击时,那位实施攻击方的人员,会首先去寻觅找寻到目标进程所拥有有的线程,接着调用QueueUserAPC这个操作来将恶意代码予以插入进去。当线程归属因为SleepEx、WaitForMultipleObjects等函数从而进入处于一种等待的状态之时,便会对APC队列进行对应的处理操作行为情况呈现此方式并不需要去从事创建远程线程的相关事宜,其显得更为隐蔽,特别适合于将其注入到那些会频繁出现等待情况的系统服务之中。

LPVOID WINAPI VirtualAllocEx(
	__in      HANDLE hProcess,   //需要在其中分配空间的进程的句柄.
	__in_opt  LPVOID lpAddress,  //想要获取的地址区域..
	__in      SIZE_T dwSize,      //要分配的内存大小.
	__in      DWORD flAllocationType, //内存分配的类型
	__in      DWORD flProtect        //内存页保护.
);
BOOL WriteProcessMemory(
	HANDLE hProcess, // 进程的句柄
	LPVOID lpBaseAddress, // 要写入的起始地址
	LPVOID lpBuffer, // 写入的缓存区
	DWORD nSize, // 要写入缓存区的大小
	LPDWORD lpNumberOfBytesWritten // 是返回实际写入的字节。
);

傀儡进程的内存偷换

//打开进程
HANDLE h = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE,process_id);
//申请内存
LPVOID target_payload = VirtualAllocEx(h, NULL, sizeof(payload), MEM_COMMIT |MEM_RESERVE, PAGE_EXECUTE_READWRITE); 
//写内存
WriteProcessMemory(h, target_payload, payload, sizeof(payload), NULL);

进程挖空方面将合法程序代码予以完整替换成为恶意程序,攻击者凭借挂起状态去创建目标进程,像记事本这类,接着通过ZwUnmapViewOfSection卸载其内存,再借助VirtualAllocEx重新进行分配,最终依赖WriteProcessMemory写入恶意代码,等到时候恢复进程运行,表面看上去是记事本,可实际上运行的却是黑客程序。

处理PE文件内存对齐是此项技术最难之处,写入的数据得如同系统加载器那般,将各个节区按照内存对齐方式展开,若只是单纯复制文件内容,程序一旦运行便会崩溃,攻击者必须模拟Windows的加载过程,保证导入表、重定位表均正确设置,这可是个技术活。

你曾借助哪些工具去检测或者防御此类内存层面的攻击呢,欢迎于评论区域分享你的经验,点赞并且转发以使更多人知晓这些隐蔽的威胁。

ATOM WINAPI GlobalAddAtom(//向全局原子表添加一个字符串,并返回这个字符串的原子
	_In_ LPCTSTR lpString//原子名字符串
);
UINT GlobalGetAtomNameA(//从表中获取全局原子名字符串,存储在缓冲区中
	ATOM  nAtom,//原子
	LPSTR lpBuffer,//缓冲区
	int   nSize//缓冲区大小
);

免费留学规划
快捷咨询
资深顾问一对一为您解答留学问题
电话
咨询服务电话
400-888-8888
微信
二维码
关注了解更多留学信息
QQ
推荐院校
澳洲莫纳什大学马来西亚分校 澳洲八大名校之一的莫纳什大学,其蒸蒸日上的国际声誉使它在澳洲率先成为一所国际化大学。马来西亚MONASH大学是澳洲莫纳什(才)大学的第七所分校。在澳洲维多利亚州
诺丁汉大学马来西亚分校 马来西亚国际伊大简介   马来西亚国际伊斯兰大学(International Islamic University Malaysia),简称IIUM,由马来西亚
马来西亚玛尼帕尔国际大学 马来西亚国际伊斯兰大学(International Islamic University Malaysia),简称IIUM,由马来西亚政府于1983年倡议和主办
马来西亚拉曼大学 马来西亚国际伊大简介   马来西亚国际伊斯兰大学(International Islamic University Malaysia),简称IIUM,由马来西亚
热门推荐
10天时间完成开工前准备工作,50天时间完成A厂区改造、装修工程,3月29日试产,4月2日已进行开工量产。南宁高新区推进项目的效率如此高,让富士康集团南宁生产基...
山西中阳山体滑坡事故中被埋23人全部遇难
 教育部和微软马来西亚分公司联合利用STEM4ALL(旨在改变马来西亚的教育产业的一项倡议)在马来西亚推进STEM教育。以下是留学信息小编整理的详细内容。 
 由于大马是新兴的留学国家,不少家长和同学对留学大马存在很多困惑,当然还有很多急需要了解的问题。今天留学信息小编总结了其中*常见的10条,同学们记得了解下哟!
 首先,英语在泰国的普及率并没有马来西亚高,虽然泰国也算是一个全英文的学习环境,但是在马来西亚,不仅是学习,还有生活中都是英语环境,对英语口语的锻炼是非常好的