x64 JMP指令的汇编解码

3

我很少使用汇编语言,所以需要专家帮助解码我必须处理的一小段代码。

0000:  48 ff 25 61 57 07 00    rex.W jmp QWORD PTR [rip+0x75761]        # 0x75768
0007:  cc                      int3

这是一次内存间接跳转至rip+0x75761处的8字节/64位地址,因此跳转目标的绝对地址从中加载的值为0007 + 0x75761 = 0x75768,对吗?


2
顺便说一下,REX.W是多余的。 - harold
谢谢!不幸的是,代码在客户的x64 Windows 10安装中使用了user32.dll函数。我不知道那段代码是怎么来的,因为它不是标准的微软代码。所以我必须对它进行适应。 - user118708
你确定这是64位机器码吗?如果以32位代码反汇编,0x48 字节将是 decinc。(我想是dec eax)。 - Peter Cordes
@user118708 可以从64位进程调用32位代码,反之亦然,您需要进行远跳转以更改模式,然后才能调用该进程。虽然它不是官方支持的,但您可以这样做。 - fuz
@FUZxxl 我非常感兴趣,希望有可用的示例告诉我如何做。DLL函数的默认调用约定是调用方和被调用方之间强制执行相同的位数。由于DLL加载机制决定是否加载DLL,因此我没有将32位DLL加载到64位进程的选择。对于我的目的,64位DLL提供不了任何优势,如果我有这个选择,我很乐意在64位进程中调用32位函数。 - user118708
显示剩余2条评论
1个回答

3

这是在x86-64上的标准尾递归序列,由Microsoft编译器生成。

是的,正如您所说,它是对64位内存地址0x75768的间接跳转。在执行此代码时,rip等于7,因此rip + 0x75761 == 0x7 + 0x75761 == 0x75768。该代码将无条件地转移控制到地址0x75768处的指令。

随后的int 3只是填充,但它也起到了墙的作用。由于前一个指令中的无条件分支,因此不应该到达此点。如果到达了,CPU会陷入陷阱,因为这是“断点”中断。

关于REX.W前缀,Harold技术上是正确的,它是不必要的,但原因可能与您想象的不同。有些令人惊讶的是,在x86-64上使用寄存器间接跳转时,Windows需要REX.W前缀以确保堆栈展开成功。堆栈展开代码在内部使用此作为信号。Ross Ridge写了一篇优秀答案,介绍了Windows x64中带有REX前缀的JMP指令的目的。

在这种情况下,这并不是严格必要的,因为这是一个带有IP相对操作数的间接跳转,但编译器显然仍在发出它。它处理这个的逻辑可能并不复杂,也许它总是生成这个代码以保持一致性。或者官方文档没有详细说明堆栈展开代码的实现方式。多一点REX.W前缀也没有真正的劣势,所以最好还是安全第一。


REX.W是由Microsoft在Windows 10周年更新中添加到Win32 API中的。即使是可选的,它也是未来操作系统的一部分。 - user118708
我不认为我理解你在说什么,@user。如何将汇编操作码添加到API中?如果您是在说Windows 10附带的代码使用此编码方式,那么肯定是这样的。所有64位Windows版本都是这样的,一直以来都是这样的。包括Windows 10周年更新版的32位版本在内,没有任何32位Windows版本这样做。而且它在这里被称为“可选项”,是因为对于IP相对编码实际上并不必要。您只需要用于间接编码。在Windows 10中,这种情况没有改变。堆栈展开仍然以相同的方式工作。 - Cody Gray
1
如果你说“因为这是一个带有IP相对操作数的间接跳转”,那么可能会更清楚。否则,你似乎在暗示它是一个相对直接跳转。 - Ross Ridge
没错,这就是我想的。现在我明白混淆的原因了。需要使用间接跳转且操作数为寄存器的情况下才需要使用 REX.W。当我写“间接跳转”时,我有这个想法,但没有明确指出。Peter 和 Ross 完全正确,当操作数为 IP 相关时,它仍然是一个间接跳转。不同之处仅在于,根据我所读的文档,在前一种情况下需要 REX.W,而在后一种情况下则不需要。更新了答案中的措辞以反映这一点,希望减轻混淆。 - Cody Gray
1
x86架构的基本规则是指令指针始终指向下一条将要执行的指令。因此,当CPU正在执行“JMP”指令时,指令指针(RIP)指向下一条指令,即在这种情况下是“INT 3”,因此它包含值7。 - Cody Gray
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接