远程线程注入

基础概念

  1. 注入定义:在第三方进程不知情 / 未授权的前提下,把代码 / 模块写入目标进程虚拟地址空间并执行
    安全领域:注入与反注入持续对抗
    常见注入:远程线程注入、APC 注入、钩子注入、输入法注入、导入表注入
  2. 远程线程注入核心思路 不直接在目标进程执行自定义函数,远程线程入口设为LoadLibraryA; 在目标进程内存存放 DLL 路径字符串,将字符串地址作为LoadLibraryA参数
    原因:LoadLibraryA原型刚好匹配线程函数格式:HMODULE LoadLibraryA(LPCTSTR lpFileName),1 个 4 字节参数、返回 4 字节返回值,满足CreateRemoteThread入口要求
    关键点:所有进程的kernel32.dll基地址一致,LoadLibraryA地址在各个进程相同

完整注入步骤(重点)

  1. OpenProcess:打开目标进程,获取进程句柄,需要PROCESS_ALL_ACCESS(或对应分项权限),传入目标 PID
  2. VirtualAllocEx:在目标进程虚拟空间分配内存,PAGE_READWRITE,用来存放 DLL 完整路径(内存大小 = strlen+1,预留\0结束符)
  3. WriteProcessMemory:跨进程写内存,把 DLL 路径字符串写入刚才分配的远程内存
  4. GetModuleHandleA("kernel32.dll") + GetProcAddress:拿到LoadLibraryA地址
  5. CreateRemoteThread:创建远程线程
    ○ 入口地址:LoadLibraryA
    ○ 线程参数:远程内存中 DLL 路径字符串的地址
  6. 远程线程执行LoadLibraryA → 目标进程加载 DLL,触发DllMain
  7. 资源释放:关闭线程句柄、进程句柄

DLL 端逻辑(DllMain)

c 复制代码
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
        {
            case DLL_PROCESS_ATTACH:
                //DLL被加载进来,新建线程持续执行代码
                CreateThread(NULL,0,ThreadFunc,NULL,0,NULL);
                break;
            case DLL_THREAD_ATTACH:
                break;
            case DLL_THREAD_DETACH:
                break;
            case DLL_PROCESS_DETACH:
                break;
        }
    return TRUE;
}

重要:CreateRemoteThread创建的远程线程执行完 LoadLibrary 就直接结束,如果要长时间运行代码,不能在 DllMain 里面写死循环!

规范做法:DLL_PROCESS_ATTACH 里创建新线程,业务代码放到新线程

用到的核心 API 清单

API 作用
OpenProcess 打开目标进程,拿到 hProcess
VirtualAllocEx 在别的进程分配虚拟内存 (VirtualAlloc:仅本进程)
WriteProcessMemory 向目标进程内存写入数据
GetProcAddress 获取 LoadLibraryA 函数地址
CreateRemoteThread 在目标进程创建远程线程

易错点 & 限制

  1. DLL 路径:必须使用完整绝对路径;相对路径容易加载失败
  2. 位数匹配:32 位注入器只能注入 32 位目标进程;64 位注入器只能注入 64 位目标,32/64 不能交叉注入
  3. 权限:OpenProcess 权限不足直接失败;高版本 Windows 有注入防护
  4. 内存属性:VirtualAllocEx 分配 PAGE_READWRITE;如果后续需要执行代码才改成 PAGE_EXECUTE_READWRITE(本案例只存字符串,读写即可)
  5. DllMain禁止写死循环代码,会导致进程卡死,要新建子线程

最简注入器代码骨架

c 复制代码
#include <windows.h>
#include <stdio.h>
int InjectDll(DWORD dwPID, const char* szDllPath)
{
    //1.打开目标进程
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
    if (hProcess == NULL)
    {
        printf("OpenProcess fail\n");
        return 0;
    }
    //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);
    return 1;
}
int main()
{
    InjectDll(1048, "C:\\TestDll.dll");
    getchar();
    return 0;
}

总结

  1. 远程线程注入本质:CreateRemoteThread在目标进程调用LoadLibraryA加载 DLL
  2. 4 个核心跨进程 API:OpenProcess、VirtualAllocEx、WriteProcessMemory、CreateRemoteThread
  3. LoadLibrary 地址所有进程相同,所以不用在目标进程内解析
  4. 远程线程只负责加载 DLL;DLL 要持续运行代码,必须在 DllMain 里新建线程
  5. 注入前置条件:目标进程正在运行,注入程序拥有足够权限,位数一致
相关推荐
Mikko742 分钟前
JVM 线上排查实战(七):jps 看不到进程、jstack 报不允许的操作怎么办?attach 失败的六种情况实测
java·运维·jvm·后端
阡陌数智1 小时前
RAG 系统的召回退化:向量库、分块、重排全链路问题排查与优化实践
开发语言·人工智能·语言模型·自然语言处理·推荐算法
geovindu1 小时前
rust: Flyweight Pattern
开发语言·后端·设计模式·rust·享元模式·结构型模式
Escalating_xu1 小时前
【C 语言】字符函数和字符串函数:从 ctype 到 strtok、strerror 全面解析
java·c语言·开发语言
在猴站学算法2 小时前
(实战)PHP文件包含漏洞
网络安全·php
且从容.2 小时前
JS篇-----
java·javascript·jvm
郝学胜-神的一滴2 小时前
C++ Templates 07:编译报错和调试手段与实战技巧
开发语言·c++·后端·软件工程·visual studio
害人终害己2 小时前
Redisson 连接 Redis 失败 报错解决方案
java·开发语言
jimy12 小时前
c++的静态类型和动态类型(二)
开发语言·c++