文章总结: CodeCave是一种隐蔽的PE文件后门植入技术,利用编译器在PE文件中生成的未使用字节区域注入恶意代码而不增加文件体积。文章详细介绍了其核心原理、实现效果及实操步骤,包括确定目标地址、准备Shellcode、寻找CodeCave、跳转到CodeCave、保存上下文、注入Shellcode、恢复上下文、恢复原始命令、注入返回跳转和保存补丁等步骤。这种技术可实现零体积增长、动态触发,具有一定免杀优势。 综合评分: 91 文章分类: 免杀,二进制安全,漏洞分析,红队,实战经验
【免杀研究】PE文件植入后门之CodeCave
原创
红队安全圈
红队安全圈
2025年12月21日 15:12 重庆
Code Cave(代码洞穴)是一种隐蔽的PE文件后门植入技术。它利用编译器在PE文件中生成的未使用字节区域,如对齐填充、节区间隙,注入恶意代码而不增加文件体积,从而某些场景下规避静态检测
一、核心原理
通过分析PE文件的结构,找到合适的Codecave区域,将恶意代码或自定义功能代码注入其中。注入后,通过修改PE文件的控制流(如跳转指令、函数调用等),使程序在运行时能够执行注入的代码,同时尽量保持原程序的正常功能,避免程序崩溃
二、实现效果
- • 零体积增长:后门隐藏于现有节区,文件大小与哈希值不变,规避一些特征码识别
- • 动态触发:程序运行时自动执行恶意载荷(如反向连接、文件操作),而表面功能正常
- • 免杀优势:传统添加新节区方法更容易被检测,CodeCave可一定程度降低发现概率
三、实操步骤
下面手把手演示操作,修改一个大家都很熟悉的 ssh 连接工具 putty.exe,在它里面植入计算器的 shellcode,受害者打开这个 putty.exe 的时候自动弹出计算器,实战中把计算器换成恶意后门的 shellcode
1. 确定目标地址(Hook Point)
在 xdbg 调试工具打开 PE 文件,确定程序中想植入后门逻辑的地址(例如,程序入口点 EP 或某个关键函数开头),观察程序流程,定位到目标指令地址(Hook_Addr),一般x32dbg打开程序会默认在入口点打了断点,在”Breakpoints”页面下直接双击断点就会跳转到”CPU”页面对应的位置,比如下图中的 0x00454AD0
记录这个地址和该地址以及下面几行的内容,Ctrl+C复制暂时保存到文本里,以便后续恢复
2. 准备 Shellcode
CS/MSF等生成payload,用脚本转成shellcode,这里以打开计算器为例
3. 寻找Code Cave(代码洞穴)
有很多小工具可以自动化发现 codecave,这里我为了演示原理,就手动查找
找到一段足够长的连续空闲内存区域来存放Shellcode,一般先在.text段中搜索,可以先通过MemoryMap页面找到程序的.text段,双击进入到CPU页面对应的位置
然后在CPU页面该位置处右键 -> searchfor -> CurrentRegion -> Pattern -> Hex输入框,搜索的很长串
00字节,多按一些00,确保搜到的区域长度大于 shellcode 的长度。或者直接拉到CPU页面的最下面,程序的最后面通常可能会有未使用的空区域点击OK搜索后进入到References页面,可以看到搜索到了全是原程序未使用的代码洞穴区域,双击左侧的地址进入的CPU页面对应的位置
比如下图的 0x0045C961 就是代码洞穴的首行
4. 跳转到Code Cave
在第1步的hook地址那一行0x00454AD0,右键 Assemble (或者空格键) 输入jmp指令跳转到第3步获取到的代码洞穴,假设就跳转到代码洞穴的第一个的内存地址
jmp 0x0045C961
jmp命令需要占用5个字节,也就是原始的 0x00454AD0 到 0x00454AD4 处的内容被覆盖了,后续需要恢复
5. 保存上下文
在执行shellcode之前,先要在 Code Cave 的第一条指令处,右键 Assemble (或者空格键) 输入使用指令保存 CPU 状态,写下面两行,备份通用寄存器和标志寄存器的状态,注意64位的程序稍有不同
# 32位
pushad
pushfd
# 64位
64位需手动push
pushfq
6. 注入 Shellcode
将第2步准备好的 Shellcode 机器码写入CodeCave中,从上一步pushfd的下面一行开始,选中尽可能多的00行,右键 -> Binary -> Edit,把shellcode复制到Hex区域保存,可以加一些断点一步一步运行看看效果
可能出现的情况:
比如shellcode运行后,主程序突然直接结束了,xdebug的界面直接空白,这可能是shellcode结束时调用了类似exit退出的东西,导致整个程序直接结束,而没有继续运行后面的代码。因此需要找到具体是哪一步退出,在shellcode的汇编代码的部分找到所有的call调用,都打上断点,一步一步运行调试,看到哪一步调用导致程序退出
有个取巧的办法,直接跳过这个call调用,跳过让程序导致终止退出的代码。
下面这个例子,从顶部到0x0045CA20都属于刚刚复制进来的shellcode,然而经过测试运行到0x0045CA19这个call调用的时候,程序终止了,因此可以在它的前面,比如0045CA16这一行执行一个jmp语句,跳到下面的00区域:jmp 0x0045CA25,这样就不会执行那个导致程序终止的call,然后再进行后续的操作即可
注意由于强制jmp,属于非正常结束,提前脱离了 Code Cave 的执行流,要隔离后面的代码块,至少隔开1个空行(00),而不是紧接着的0x0045CA23,防止执行错误
为了保证最高的可靠性,一个健壮的 Code Cave 注入结构应该始终包含清晰的阶段划分,这样做的效果是:
- 1. 强制对齐: 确保 Shellcode 结束后,指令指针指向的第一个有效指令是
popfd- 2. 防止截断: 避免 Shellcode 的最后一条机器码不小心“溢出”到
popfd的第一个字节
7. 恢复上下文
在 Shellcode 执行完毕后,要恢复 CPU 的原始状态,紧接第6步在shellcode的下面(Code Cave区域)恢复通用寄存器和标志寄存器的状态,写下面两行
# 32位
popfd
popad
# 64位
popfq
64位需手动pop
8. 恢复原始命令
在shellcode和恢复CPU状态的末尾,也就是第7步的最下面插入原始命令,因为第4步在原Hook_Addr地址插入jmp(5个字节)跳转语句时覆盖了两行内容,现在要还原回去
多选中要修改的空白行,右键Binary -> Edit,在Hex输入框粘贴原始的Hex值,即第1步中备份的内容
9. 注入返回跳转
首先要确定跳回去的内存地址,hook地址从 0x00454AD0 开始jmp(5个字节),返回地址本应该是 0x00454AD5,但原始指令没有 0x00454AD5 和 0x00454AD6,由于指令修复的原因,返回跳转的地址就是 00454AD7
正确的 原始返回地址 = Hook地址 + JMP补丁长度(5字节) + 跳过未使用的字节
所以紧接着第8步,写一条jmp跳转,跳转到原程序流程,将控制权还给原始程序,确保原始程序流程逻辑不被破坏
jmp 0x00454AD7
10. 保存补丁
在CPU页面右键选择 Patchs,点击Select All 全选,点击Patch File 保存为新的 EXE 文件,这样就将所有的修改(Hook_Addr 处的 JMP 和 Cave_Addr 处的代码等等)永久写入磁盘文件
最终输出的新文件就是被注入了 shellcode 的 putty.exe,发给受害者运行,对方打开的时候自动运行恶意后门,同时原 putty 的功能不受影响
获取更多红队实战技巧
欢迎关注红队安全圈👇
如果我的文章对你有帮助,期待一键三连😄
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:红队安全圈 红队安全圈《
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论