如何从C++源代码中获取可执行字节码用于内存注入
从C++源代码直接生成可内存注入字节码的实现方案
你需要的可注入字节码本质是位置无关代码(PIC, Position-Independent Code),不需要依赖PE加载器处理重定位、导入表就能直接在目标进程内存中运行的原生指令序列,具体实现步骤如下:
1 编写符合Shellcode规范的C++代码
代码必须满足无外部依赖、位置无关的核心要求,需遵循以下规则:
- 不可直接调用系统API,需自行通过PEB遍历获取目标API地址,或将用到的API地址作为参数传入shellcode逻辑
- 禁止使用全局变量、静态变量,所有数据都在栈上分配,或用相对偏移读取内嵌的常量数据
- 不可调用C++标准库、CRT函数,避免引入外部依赖
- 统一使用
__stdcall调用约定,自行控制栈平衡,避免编译器生成冗余指令
示例代码框架:
// 自行声明需要用到的结构体、函数指针类型,不依赖外部头文件 typedef NTSTATUS(WINAPI* PFN_NtAllocateVirtualMemory)( HANDLE ProcessHandle, PVOID* BaseAddress, ULONG ZeroBits, PULONG RegionSize, ULONG AllocationType, ULONG Protect ); // Shellcode入口函数,所有逻辑都写在此处,不要调用外部未定义的函数 VOID ShellcodeMain() { // 业务逻辑实现,例如遍历PEB获取NtAllocateVirtualMemory地址、分配内存等 // 所有字符串都以栈数组形式定义,例如:CHAR kernel32Name[] = {'k','e','r','n','e','l','3','2','.','d','l','l',0}; }
2 调整编译配置
以MSVC编译器为例,需调整以下编译选项:
- 开启
/O1或/Os优化,最小化代码体积 - 开启
/Gy函数级链接选项,方便后续提取代码段 - 关闭
/GS缓冲区安全检查,去除多余的安全检查指令 - 关闭
/RTC运行时错误检查,避免生成CRT检查代码 - 编译架构和待注入的目标进程架构完全一致,x86进程只能注入x86架构的字节码,x64同理
3 提取编译后的字节码
有两种常用提取方式可选择:
方式1:用dumpbin工具提取
编译生成obj目标文件后,使用VS自带的dumpbin工具查看.text段内容:dumpbin /ALL your_source.obj
找到ShellcodeMain函数对应的.text段字节序列,直接拷贝转换为unsigned char数组即可。
方式2:添加边界标记直接读取
在代码中新增空函数标记shellcode的结束边界,运行时直接读取两个函数地址之间的内存即可拿到字节码:
// 放在ShellcodeMain之后的空函数,作为结束边界 VOID ShellcodeEnd() {} // 运行时直接提取字节码 SIZE_T shellcodeSize = (SIZE_T)ShellcodeEnd - (SIZE_T)ShellcodeMain; unsigned char* shellcodeBuffer = (unsigned char*)ShellcodeMain;
注意事项
- 注入字节码前,需要将目标进程中存放shellcode的内存页权限修改为
PAGE_EXECUTE_READWRITE,否则会触发内存访问异常 - 如果shellcode中存在硬编码地址,需要自行处理重定位逻辑,或者全程使用相对偏移访问数据和指令
内容的提问来源于stack exchange,提问作者anasouardini5
相关产品推荐
相关产品推荐

