【问题标题】:Inline asm jmp - 'invalid operand for instruction'内联 asm jmp - \'指令操作数无效\'
【发布时间】:2022-09-27 15:29:00
【问题描述】:

我正在做一些代码注入,我发现很难将代码从 C 转录到 Rust。

情况如下,在相对地址0x40147D我们跳转到了dll注入指令的地址,这是通过手动插入字节来完成的。

let rel = target_address - to_hook - 5;

let bytes: [u8; 4] =  transmute(rel.to_be());

buf[0] = 0xE9;

for x in 1..5 {
    buf[x] = bytes[4-x]
}

所以我们在 asm 中运行任何代码,最后它应该返回到 0x401484,即指令 0x40147D + 7。 在 C 中这可以按预期工作,但在 Rust 中却没有。

C:

DWORD jmp_back = 0x40147D + 7;

__asm {
    add dword ptr[ebp - 0x0C], 03
    mov eax, [ebp - 0x0C]
    jmp [jmp_back]
}

Rust(我贴了一个标签以免程序崩溃):

let jump_back: u32 = 0x40147D + 7;

unsafe {
    asm!(
        \"2:\",
        \"add dword ptr [ebp-0x0C],01\",
        \"mov eax,[ebp-0x0C]\",
        // \"jmp 2b\", //back to label 2
        \"jmp [{}]\", in(reg) jump_back,

        // \'invalid operand for instruction\' 
        //\"jmp 0x401484\" 

    );
 }

什么会被忽视?

编辑

正如@prl 评论的那样,删除方括号会导致:

我正在研究以了解 eax 寄存器中的实际内容。 但是通过这种修改,程序崩溃并关闭。

OBS为了测试,我删除了之前的 jmp 指令 (\"jmp 5D7B145D\")

编辑 2

Inline Asm 所述,显然是为了在指令中使用值作为操作数,Rust 将值加载到块开头的寄存器中。

所以也许这就是我的指令也使用相同寄存器的问题。

我稍后会尝试,但我可能不得不更改 Rust 使用的寄存器。

理想的情况是不必使用寄存器来执行此类操作。

编辑 3

到目前为止,我将我的答案作为这个问题的解决方案,但它仍然存在问题。

Rust 作为 asm 块的第一条指令将您要使用的值移动到寄存器中,有几点:

  • 如果寄存器中包含值?
  • 为什么不在块 asm 中使用常量?
  • @prl我进行了更改,在后期编辑中我显示了图像,但是程序现在崩溃并关闭
  • 我对帖子进行了第二次编辑,确实在 Rust 中使用操作数导致将它们加载到寄存器中。我的问题是,是否有可能解决这个问题?

标签: assembly rust x86 inline-assembly


【解决方案1】:

使用jmp {} 而不是jmp [{}]。 Rust 将操作数放在寄存器中,而不是像 C 代码那样放在内存中。 jmp [{}] 会导致它尝试从寄存器中指定的内存位置读取目标地址。 C 版本需要括号,因为目标地址尚未加载到寄存器中。

您还需要让编译器知道内联汇编代码使用 eax,因此编译器不会将 eax 用于操作数。要指示 eax 被破坏,请添加另一个输出操作数 out("eax") __ 表示可以丢弃该值。

asm!(
    "add dword ptr [ebp-0x0C],01",
    "mov eax,[ebp-0x0C]",
    "jmp {}", in(reg) jump_back, out("eax") _
);

这样做的缺点是它使用编译器选择的寄存器,该寄存器可能已经在代码注入的位置使用。

如果编译器默认选择一个不可用的寄存器,但另一个寄存器可用,则可以指定要使用的寄存器。例如,in("edx") jump_back 会告诉编译器使用 edx。

【讨论】:

    【解决方案2】:

    这是一个仅使用 eax 的可能解决方案:

    asm!(
        "push eax",
        "add dword ptr [ebp-0x0C],01",
        "mov eax,[ebp-0x0C]",
        "ret", in("eax") jump_back,
    );
    

    push 将要跳转到的位置放入堆栈,ret 在加载 eax 后跳转到该位置,避免了需要单独的寄存器来保存地址。

    【讨论】:

    • 谢谢,但这在 Rust“in(eax)”中不起作用。实际上 out("eax") _ 是解决方案,使注入的汇编代码不使用寄存器。另一点是“ret”在上下文中没有意义。这段代码 sn-p 的执行不是通过调用指令,而是通过 jmp。
    • 而“push eax”指令可能会导致问题。
    • 感谢您的解释,我能够在这里将其可视化,它有效。但它仍然提出了一个问题,如果使用 eax 寄存器,Rust 在作用域的开头添加指令“mov eax,”variable”。导致以前的信息丢失。没有办法避免它,因为它是第一条指令。
    • 我认为这个代码注入的全部意义在于修改 eax。如果您打算做其他事情,则需要不同的解决方案。
    【解决方案3】:

    这可能是一个临时解决方案,但正如帖子中提到的,它可能会导致未来的问题。

    正如prl 所评论的,sn-p "out("eax") _ 使生成的 asm 不使用该寄存器。

    unsafe {
        asm!(
            "add dword ptr [ebp-0x0C],01",
            "mov eax,[ebp-0x0C]",
            "jmp {}", in(reg) 0x401484u32,
            out("eax") _
         );
    }
    

    生成的程序集

    突出显示的是可能导致未来问题的原因。

    【讨论】:

      猜你喜欢
      • 2014-04-15
      • 1970-01-01
      • 2014-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多