You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust内联汇编JMP指令报‘invalid operand for instruction’问题排查

代码注入:Rust内联汇编转写问题解决

1. 搞定JMP指令的报错问题

你碰到的核心问题是对x86跳转指令的语法误解,再加上Rust内联汇编和MSVC汇编的差异:

  • 你C代码里的jmp [jmp_back],在MSVC汇编里是间接跳转——它会读取jmp_back变量所在内存里的数据,再跳转到该数据指向的地址。但你明确要返回至0x401484,说明实际需要的是直接跳转到0x401484,所以C代码的正确写法应该是jmp jmp_back(去掉方括号),带方括号的写法本身有误,只是碰巧在你的环境中能运行。
  • Rust里的报错原因:
    • 写jmp [{}]配合in(reg) jump_back时,Rust会把jump_back的值加载到某个寄存器(比如eax),指令变成jmp [eax],这仍是间接跳转,和你要的直接跳转逻辑不符。
    • 直接写jmp 0x401484报错,是因为x86的jmp指令不支持直接使用绝对立即数——jmp后跟的立即数默认是相对当前指令的偏移,要实现绝对跳转必须通过寄存器或内存中转。

正确的Rust实现

要直接跳转到0x401484,需先把目标地址加载到寄存器,再跳转至该寄存器:

let jump_back: u32 = 0x40147D + 7;

unsafe {
    asm!(
        "add dword ptr [ebp-0x0C], 01",
        "mov eax, [ebp-0x0C]",
        // 将目标地址加载到ecx寄存器
        "mov ecx, {}",
        // 直接跳转到ecx存储的绝对地址
        "jmp ecx",
        // 传入jump_back的值到寄存器
        in(reg) jump_back,
        // 声明eax、ecx被修改,避免编译器依赖原有值
        out("eax") _,
        out("ecx") _,
        // 保留ebp寄存器(因为用到了[ebp-0x0C])
        preserves_flags,
    );
}

2. 寄存器原有值的处理

Rust内联汇编默认不会保存寄存器状态——编译器会假设你的汇编代码会破坏未声明的寄存器。如果你的汇编修改了某个寄存器(比如eax、ecx)但未告知编译器,编译器可能之前把变量或临时值存在这些寄存器中,被修改后会导致后续代码崩溃。

解决方法:

  • 使用out(reg) _声明被修改的寄存器,告诉编译器“这个寄存器的值已被修改,不要依赖原有内容”。
  • 若需保留特定寄存器(比如你用到的ebp),可添加preserves_flags或显式声明保留(不同平台语法略有差异)。

3. 为什么不能在汇编块中直接使用常量?

其实可以用,但必须符合x86汇编的语法规则:

  • 如前所述,x86的jmp指令不支持直接写绝对立即数,必须通过寄存器或内存中转实现绝对跳转。
  • 其他指令使用常量是合法的,比如读取内存常量地址:mov eax, dword ptr [0x123456]。
  • 若硬要在跳转中使用常量,只能写成间接跳转(比如jmp dword ptr [0x401484]),但这是跳转到内存地址0x401484中的内容,而非直接跳转到0x401484本身,不符合你的返回逻辑。

另外,Rust的asm!宏对常量写法要求严格,必须与目标平台的汇编语法一致(比如x86无需加$前缀,部分其他平台需要)。

内容的提问来源于stack exchange,提问作者rick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 18:16:06