为什么`调用dword 0x12345678`编译为[66,E8,72,56,34,12]?

时间:2014-05-29 18:24:37

标签: assembly x86 nasm

$ cat call.s
call dword 0x12345678
$ nasm call.s
$ ndisasm call
00000000  66E872563412      call dword 0x12345678

为什么结果[66,E8, 72 ,53,54,12]不是[66,E8, 78 ,53,54,12]?为什么0x78会更改为0x72

1 个答案:

答案 0 :(得分:6)

使用此指令调用绝对地址,尽管乍一看似乎不太明显。让我们一步一步地完成装配过程。

首先,您可以看到您的指示已汇总到66 E866是操作数大小前缀 - 我将解释为什么它稍后到达。 E8指向预期的CALL指令,后面的字节 - 72563412 - 是相对于指令的32位位移的小端表示 CALL指令。这实际上是所有相对跳转和调用都是如何在x86中编码的:因为CPU总是知道指令指针的当前值,它只需将指令指定的值添加到该值并执行调用/跳转。

其次,有一个问题为什么汇编程序选择以这种方式组装指令。如果您实际上在没有任何标志的情况下运行nasm,那么它默认为bin输出模式,它只是将原始操作码输出到您指定的文件中。此外,它默认在该输出模式下以16位模式组装指令。这就是66前缀存在的原因:只要在操作码前面存在此前缀,就可以在16位模式下执行32位操作数的指令。此外,nasm输出模式中的bin假定生成的二进制文件从地址0开始:这就是"偏移"你的电话是72563412 - 如果IP从0开始,那么在处理CALL后它将是6,而0x6 + 0x12345672会给你0x12345678,这就是你的地址想打电话。您可以通过ORG(原点)指令更改程序加载的偏移量。

如果您不想使用具有相对寻址的跳转/调用,则必须将要跳转/调用的代码的地址放在寄存器中,然后执行指令注册为操作数。像这样:

mov eax, somecode
jmp eax

somecode:
    int 3

汇总到以下内容:

00000000  66B809000000      mov eax,0x9
00000006  66FFE0            jmp eax
00000009  CD03              int 0x3

您可以看到跳转的绝对地址直接移动到寄存器中。但是,如果您只是简单地执行标签的跳转/调用,我就无法想出您不会使用相对寻址的任何原因。 CPU总是知道它当前的IP - 你的汇编程序也应该知道。