$ 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
?
答案 0 :(得分:6)
使用此指令调用绝对地址,尽管乍一看似乎不太明显。让我们一步一步地完成装配过程。
首先,您可以看到您的指示已汇总到66 E8
。 66
是操作数大小前缀 - 我将解释为什么它稍后到达。 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 - 你的汇编程序也应该知道。