win32注入代码

前置回顾:远程线程原理

CreateRemoteThread 不是在本地开线程,线程实体运行在目标进程

● 发起进程:注入器,负责打开目标进程、分配内存、写入机器码、创建远程线程

● 目标进程:真正执行线程函数代码

● 核心:lpStartAddress 填写目标进程虚拟地址

代码注入整体思路

  1. 在注入器内写好功能函数
  2. 提取该函数机器码,写入目标进程预先分配的内存页
  3. 调用CreateRemoteThread,让目标进程 CPU 直接执行这片机器码
    区别远程线程 DLL 注入:DLL 注入是调用LoadLibrary加载模块;代码注入直接拷贝二进制指令,不存在 DLL 模块,PEB 链表看不到,隐蔽性更强

机器码、汇编、源码的关系

  1. 机器码:CPU 可直接识别的二进制 / 十六进制指令(55 8B EC这类),内存里只保存机器码
  2. 汇编:机器码的可读文本翻译,不是内存中存在的数据
  3. C/C++ 源码:编译后就消失,运行阶段不存在
    ⚠️ 核心坑:绝对地址硬编码 如果机器码里写死固定地址(全局变量、字符串常量、导入函数),复制到目标进程直接崩溃
    示例:mov dword ptr 0x00427c50,1
    0x00427c50是注入器进程内地址,目标进程同地址是无效内存

可拷贝函数(Shellcode)编写 5 条硬性规则

  1. ❌ 禁止全局变量:全局变量地址编译固定,跨进程失效
  2. ❌ 禁止常量字符串:字符串放在只读常量段,地址硬编码
  3. ❌ 不能直接调用 API(不能依赖导入表):导入表函数地址是本进程的
  4. ❌ 不能嵌套调用自定义函数:call 指令会硬编码函数地址
  5. ✅ 所有需要的 API 地址由注入器作为参数传入
    原理:同版本 Windows,系统 dll 内函数基地址全进程共享,注入器本地GetProcAddress拿到的 API 地址,可以直接传给目标进程使用

参数传递方案

线程函数必须符合 DWORD WINAPI ThreadProc(LPVOID lpParam) 标准格式

● 单独在目标进程分配一块内存,存放自定义结构体

● 结构体预先存放:API 函数地址、各类参数、字符串指针

● 字符串也要单独在目标进程分配内存,不能使用注入器进程内字符串指针

示例结构体:

c 复制代码
typedef struct {
    HANDLE (*pCreateFile)(LPCSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE);
    char* szFileName;
}PARAM;

三段内存分配(案例:远程创建文件)

都在目标进程空间分配:

  1. 文件名缓冲区:存放文件名字符串,带\0结束符
  2. Shellcode 内存:存放拷贝的机器码(线程执行函数)
  3. 参数结构体内存:存放函数指针、文件名指针等
    重点:结构体里字符串指针,必须指向目标进程内的文件名缓冲区,不能是本地注入器地址

Debug 版本函数陷阱(E9 跳转)

Debug 模式下,C 函数开头经常是 E9 xx xx xx xx(JMP 跳转指令)

● E9:近跳转,占 5 字节(1 字节 op + 4 字节偏移)

● 计算公式:目标地址 = 当前指令地址 + 5 + 偏移

● 问题:直接复制函数首地址只会复制跳转指令,不是真正业务代码 ✅ 解决:反汇编解析跳转,拿到跳转后的真实入口作为机器码拷贝起点

代码注入优缺点

✅ 优点

  1. 没有 DLL 模块,PEB 链表无记录,普通模块枚举检测不到
  2. 继承目标进程权限,可使用目标进程权限执行操作
    ❌ 缺点
  3. 开发复杂,必须手动处理地址、重定位、参数传递
  4. 调试极难,崩溃直接触发目标进程异常
  5. 内存扫描(PE 特征、内存页扫描)依然可以检测

整体流程总步骤

  1. OpenProcess 获取目标进程句柄
  2. VirtualAllocEx 在目标进程分配 3 块内存(shellcode、参数、字符串)
  3. WriteProcessMemory 写入字符串、参数结构体、功能函数机器码
  4. CreateRemoteThread,传入 shellcode 地址 + 参数结构体地址
  5. WaitForSingleObject 等待线程执行完毕
  6. CloseHandle / VirtualFreeEx 释放资源
c 复制代码
// 自定义参数结构体,注入器填充API地址
typedef struct
{
    HANDLE (__stdcall *pCreateFileA)(LPCSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE);
    char* lpFileName;
}SHELL_PARAM;
// Shellcode函数(必须满足:无全局变量、无常量字符串,仅读取传入参数)
DWORD __stdcall RemoteFunc(SHELL_PARAM* pParam)
{
    HANDLE hFile = pParam->pCreateFileA(pParam->lpFileName,GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile) CloseHandle(hFile);
    return 0;
}

速记

  1. 代码注入拷贝机器码,不是源码 / 汇编;核心难点:硬编码绝对地址
  2. Shellcode 禁止全局变量、常量字符串、直接 call 其他函数
  3. API 地址:注入器本地 GetProcAddress 获取,通过结构体传给远程代码
  4. Debug 函数开头可能存在 E9 JMP,要解析跳转拿到真实入口
  5. 所有指针(字符串、参数)都必须指向目标进程虚拟地址
相关推荐
Sarvartha1 小时前
内部类知识
java·开发语言
Web Security Loop2 小时前
MAC安装最新Burpsuite pro详细步骤教程
web安全·macos·网络安全·安全性测试
FPGA小徐2 小时前
【一生一芯 / PA】异常响应机制:RISC-V 中 ecall → mtvec → mret 的完整代
开发语言·数据库·c#
北京盛世宏博2 小时前
工业环境监测选型:Modbus TCP以太网温湿度传感器部署、调试、避坑全指南
开发语言·网络·php
沫璃染墨2 小时前
《Linux工程实践篇(二):线程池实战——从线程安全单例到完整源码实现》
linux·开发语言·c++·单例模式·设计模式·系统架构
泡海椒3 小时前
JQuick-Excel 多字段 TRANSFORM:让当前行字段、JContext 与展示列各归其位
开发语言·python·excel
JPower_mr.g3 小时前
SmartCall 音色管理技术解析:基于 SPI 的可扩展音色注册架构
java·开发语言·人工智能·ai·架构·开源
源图客3 小时前
浏览器开发者工具使用
开发语言·php
念何架构之路3 小时前
go-grpc核心抽象接口
开发语言·后端·golang