PE打包器中不增大.text段的控制流混淆:如何识别跳转来源?
解决PE打包器跳转来源识别问题(无.text扩容、无call/异常方案)
核心思路
利用新建的.pack段存储专属stub入口和加密表,为每个被替换的跳转指令分配独立的stub小片段(硬编码来源标识),同时保证.text段仅替换同长度指令,完全不扩容。
可行实现方案
1. 专属Stub入口+硬编码来源地址
这是最直接且可靠的方案:
- 构建阶段:
- 扫描.text段时,为每个跳转指令(如
jb 0x8F在0x6D处)记录三个关键信息:原指令地址src_addr、原指令长度len、原目标地址dest_addr,并将这些存入.pack段的加密表(可按src_addr哈希或直接索引存储)。 - 为每个跳转指令生成一个极小的专属stub片段,硬编码其
src_addr:BITS 64 ; 为0x6D处的跳转生成的专属stub stub_src_0x6D: mov rcx, 0x6D ; 硬编码原跳转指令地址 jmp common_handler ; 跳转到公共处理逻辑 - 将原.text段的跳转指令替换为同长度的条件/无条件跳转,指向对应的专属stub入口(比如原2字节
jb rel8替换为2字节jb stub_src_0x6D,确保字节数完全一致,不扩容.text段)。
- 扫描.text段时,为每个跳转指令(如
- 运行阶段:
- 当条件满足触发跳转进入专属stub后,
rcx已携带原跳转地址。 - 公共处理逻辑用
rcx查询加密表,取出原目标地址dest_addr。 - 直接执行无条件跳转到
dest_addr(因为能进入stub说明原跳转条件已满足)。
- 当条件满足触发跳转进入专属stub后,
2. 反向计算原跳转地址(无需专属Stub入口)
如果想减少stub的数量,可通过CPU的RIP寄存器反向推导原跳转地址:
- 构建阶段:
- 替换跳转指令时,记录原指令地址
src_addr、长度len、原目标dest_addr,存入加密表。 - 替换后的跳转指令为同长度相对跳转,指向公共stub入口。同时,将替换后的跳转指令的相对偏移值(即
stub_addr - next_inst_addr,next_inst_addr是原跳转指令的下一条地址)和原指令长度len关联存储。
- 替换跳转指令时,记录原指令地址
- 运行阶段:
- 进入stub后,通过
lea rax, [rip]获取当前RIP值。 - 计算原跳转指令的下一条地址:
next_inst_addr = RIP - rel_offset(rel_offset是替换后的跳转指令中的相对偏移值,需提前存入加密表)。 - 原跳转地址
src_addr = next_inst_addr - len。 - 用
src_addr查询加密表得到原目标地址,执行跳转。
- 进入stub后,通过
关键注意事项
- 必须严格保证替换后的跳转指令与原指令长度完全一致,避免破坏.text段的字节对齐和后续指令的正确性。
- 条件跳转的处理:只有原条件满足时才会进入stub,因此stub中无需重复判断条件,直接跳转到原目标即可。
- 加密表需做简单混淆(如异或、简单置换),避免静态分析直接获取原目标地址映射。
内容的提问来源于stack exchange,提问作者Alon Alush
相关产品推荐
相关产品推荐

