64 位跳转、RIP 相对寻址与寄存器

64 位跳转、RIP 相对寻址与寄存器

在 64 位进程中复用 32 位注入习惯容易产生错误。本文说明跳转距离、RIP 相对操作数和寄存器保护,帮助读者检查 CE 自动生成的模板。

本章需要 64 位目标。若 CE 安装中提供 Tutorial-x86_64.exe,请启动并附加该进程,进入第 7 关观察实际生成的模板;Tutorial-i386.exe 是 32 位程序,无法展示 64 位寄存器及 RIP 相对寻址。具体文件名以本机进程列表为准,缺少 64 位 Tutorial 时先将本章作为原理阅读。

不要预设跳转一定是五字节

近距离相对 jmp 常见编码为五字节,但能否使用取决于目的地与跳转点之间的距离。x64 地址空间很大,如果分配到的代码区过远,汇编器可能选择更长的跳转形式;具体长度必须看实际生成的机器码。alloc(name,size,nearAddress) 可以请求靠近某地址分配,但不是成功保证。分配失败或跳转编码改变时,必须重新核对覆盖长度。

从跳转起始地址开始逐条累加完整指令长度,直到容纳实际编码。切勿在指令中间停止,也要检查是否有其他路径跳入这片区域。剩余字节通常可用 NOP 填充,但填充数量必须由实际编码和覆盖区长度算出。

RIP 相对地址为什么会变

x64 指令可能用 RIP 相对方式访问全局数据:有效地址由该指令结束后的地址与编码中的有符号位移共同决定。如果把原始字节直接复制到其他位置,RIP 已变化,即使机器码不变也可能访问另一个地址。正确处理要先确定原目标,再在新位置重新组装等价操作并检查反汇编结果;相对 call、跳转也有类似的位置依赖。

用 CE 内存浏览器对照原始与重组指令的目标地址,不只比对文本。若无法可靠重定位,就选择别的注入点或停止,不要以“看起来能执行”作为充分验证。

寄存器、标志与栈

新代码使用了原流程需要的寄存器、向量寄存器或标志时,应确保原有值仍符合后续代码的预期;恢复时还要考虑保存动作本身对栈及对齐的影响。向函数发起调用需要满足目标平台的调用约定与栈布局。写 EAX 会清零 RAX 上半部,不能当成只改低 32 位而保留上半部。

验收时分别检查跳转编码长度、覆盖末端、重定位目标、返回地址和禁用后的原字节,并在不同启动中重复。以上是分析清单,不构成某个版本可直接复制的注入脚本。

排错时先让新路径只重放原始行为,再逐项添加其他计算。每次改变后都重新查看生成的字节;即便汇编文本只改了一行,也可能改变指令长度和后续标签位置,不能沿用之前的检查结果。

参考资料