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

cub*_*l42 3 x86 assembly nasm

$ cat call.s
call dword 0x12345678
$ nasm call.s
$ ndisasm call
00000000  66E872563412      call dword 0x12345678
Run Code Online (Sandbox Code Playgroud)

为什么结果[66,E8,72,56,34,12]不是[66,E8,78,56,34,12]?为什么0x78改变成0x72

Dan*_*zar 6

正在调用该指令的绝对地址,但它似乎并不那么明显乍一看.让我们一步一步地完成装配过程.

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

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

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

mov eax, somecode
jmp eax

somecode:
    int 3
Run Code Online (Sandbox Code Playgroud)

汇总到以下内容:

00000000  66B809000000      mov eax,0x9
00000006  66FFE0            jmp eax
00000009  CD03              int 0x3
Run Code Online (Sandbox Code Playgroud)

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

  • 在这种情况下,通过寄存器进行间接寻址是您正在寻找的.如果您想要阅读更多有关此实际用法的内容,请查看"跳转表". (3认同)