基础概念
- 注入定义:在第三方进程不知情 / 未授权的前提下,把代码 / 模块写入目标进程虚拟地址空间并执行
安全领域:注入与反注入持续对抗
常见注入:远程线程注入、APC 注入、钩子注入、输入法注入、导入表注入 - 远程线程注入核心思路 不直接在目标进程执行自定义函数,远程线程入口设为LoadLibraryA; 在目标进程内存存放 DLL 路径字符串,将字符串地址作为LoadLibraryA参数
原因:LoadLibraryA原型刚好匹配线程函数格式:HMODULE LoadLibraryA(LPCTSTR lpFileName),1 个 4 字节参数、返回 4 字节返回值,满足CreateRemoteThread入口要求
关键点:所有进程的kernel32.dll基地址一致,LoadLibraryA地址在各个进程相同
完整注入步骤(重点)
- OpenProcess:打开目标进程,获取进程句柄,需要PROCESS_ALL_ACCESS(或对应分项权限),传入目标 PID
- VirtualAllocEx:在目标进程虚拟空间分配内存,PAGE_READWRITE,用来存放 DLL 完整路径(内存大小 = strlen+1,预留\0结束符)
- WriteProcessMemory:跨进程写内存,把 DLL 路径字符串写入刚才分配的远程内存
- GetModuleHandleA(“kernel32.dll”) + GetProcAddress:拿到LoadLibraryA地址
- CreateRemoteThread:创建远程线程
○ 入口地址:LoadLibraryA
○ 线程参数:远程内存中 DLL 路径字符串的地址 - 远程线程执行LoadLibraryA → 目标进程加载 DLL,触发DllMain
- 资源释放:关闭线程句柄、进程句柄
DLL 端逻辑(DllMain)
BOOL APIENTRYDllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved){switch(ul_reason_for_call){caseDLL_PROCESS_ATTACH://DLL被加载进来,新建线程持续执行代码CreateThread(NULL,0,ThreadFunc,NULL,0,NULL);break;caseDLL_THREAD_ATTACH:break;caseDLL_THREAD_DETACH:break;caseDLL_PROCESS_DETACH:break;}returnTRUE;}重要:CreateRemoteThread创建的远程线程执行完 LoadLibrary 就直接结束,如果要长时间运行代码,不能在 DllMain 里面写死循环!
规范做法:DLL_PROCESS_ATTACH 里创建新线程,业务代码放到新线程
用到的核心 API 清单
| API | 作用 |
|---|---|
| OpenProcess | 打开目标进程,拿到 hProcess |
| VirtualAllocEx | 在别的进程分配虚拟内存 (VirtualAlloc:仅本进程) |
| WriteProcessMemory | 向目标进程内存写入数据 |
| GetProcAddress | 获取 LoadLibraryA 函数地址 |
| CreateRemoteThread | 在目标进程创建远程线程 |
易错点 & 限制
- DLL 路径:必须使用完整绝对路径;相对路径容易加载失败
- 位数匹配:32 位注入器只能注入 32 位目标进程;64 位注入器只能注入 64 位目标,32/64 不能交叉注入
- 权限:OpenProcess 权限不足直接失败;高版本 Windows 有注入防护
- 内存属性:VirtualAllocEx 分配 PAGE_READWRITE;如果后续需要执行代码才改成 PAGE_EXECUTE_READWRITE(本案例只存字符串,读写即可)
- DllMain禁止写死循环代码,会导致进程卡死,要新建子线程
最简注入器代码骨架
#include<windows.h>#include<stdio.h>intInjectDll(DWORD dwPID,constchar*szDllPath){//1.打开目标进程HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPID);if(hProcess==NULL){printf("OpenProcess fail\n");return0;}//2.远程分配内存LPVOID pRemoteMem=VirtualAllocEx(hProcess,NULL,strlen(szDllPath)+1,MEM_COMMIT,PAGE_READWRITE);//3.写入DLL路径字符串WriteProcessMemory(hProcess,pRemoteMem,szDllPath,strlen(szDllPath)+1,NULL);//4.获取LoadLibraryA地址LPVOID pLoadLib=(LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"),"LoadLibraryA");//5.创建远程线程HANDLE hRemoteThd=CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)pLoadLib,pRemoteMem,0,NULL);WaitForSingleObject(hRemoteThd,INFINITE);//资源释放CloseHandle(hRemoteThd);CloseHandle(hProcess);return1;}intmain(){InjectDll(1048,"C:\\TestDll.dll");getchar();return0;}总结
- 远程线程注入本质:CreateRemoteThread在目标进程调用LoadLibraryA加载 DLL
- 4 个核心跨进程 API:OpenProcess、VirtualAllocEx、WriteProcessMemory、CreateRemoteThread
- LoadLibrary 地址所有进程相同,所以不用在目标进程内解析
- 远程线程只负责加载 DLL;DLL 要持续运行代码,必须在 DllMain 里新建线程
- 注入前置条件:目标进程正在运行,注入程序拥有足够权限,位数一致