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
相关产品推荐
相关产品推荐

