Assembly 为什么'call dword 0x12345678'编译为[66,E8,72,56,34,12]?
为什么结果[66,E8,72,56,34,12]不是[66,E8,78,56,34,12]?为什么Assembly 为什么'call dword 0x12345678'编译为[66,E8,72,56,34,12]?,assembly,x86,nasm,Assembly,X86,Nasm,为什么结果[66,E8,72,56,34,12]不是[66,E8,78,56,34,12]?为什么0x78变为0x72?您使用此指令调用的是一个绝对地址,尽管乍一看可能并不明显。让我们一步一步地完成装配过程 首先,您可以看到您的指令已组装到66 E866是操作数大小前缀-稍后我将解释它为什么会出现E8指向预期的调用指令,以下字节-72563412-是在调用指令之后相对于指令的32位位移的小端表示。这实际上是x86中所有相对跳转和调用的编码方式:因为CPU总是知道指令指针的当前值,所以它可以将指令
0x78
变为0x72
?您使用此指令调用的是一个绝对地址,尽管乍一看可能并不明显。让我们一步一步地完成装配过程
首先,您可以看到您的指令已组装到66 E8
66
是操作数大小前缀-稍后我将解释它为什么会出现E8
指向预期的调用
指令,以下字节-72563412
-是在调用
指令之后相对于指令的32位位移的小端表示。这实际上是x86中所有相对跳转和调用的编码方式:因为CPU总是知道指令指针的当前值,所以它可以将指令指定的值添加到该值,然后执行调用/跳转
其次,有一个问题是,为什么汇编程序选择以这种方式汇编指令。如果实际运行的nasm
没有任何标志,则默认为bin
输出模式,该模式仅将原始操作码输出到指定的文件中。此外,它默认为在该输出模式下以16位模式组装指令。这就是为什么有66
前缀的原因:如果操作码前面有前缀,就可以在16位模式下使用32位操作数执行指令。另外,bin
输出模式中的nasm
假设生成的二进制文件从地址0
开始:这就是为什么您的呼叫的“偏移量”是72563412
-如果IP从0开始,那么在处理呼叫后它将是6,0x6+0x12345672
为您提供0x12345678
,那是你想打电话的地址。您可以通过ORG
(origin)指令更改加载程序的偏移量
如果不想使用带相对寻址的跳转/调用,则必须将要跳转到/调用的代码的地址放入寄存器中,然后以寄存器作为操作数执行指令。大概是这样的:
$ cat call.s
call dword 0x12345678
$ nasm call.s
$ ndisasm call
00000000 66E872563412 call dword 0x12345678
组装到以下位置:
mov eax, somecode
jmp eax
somecode:
int 3
您可以看到跳转的绝对地址直接移动到寄存器中。然而,如果您只是执行对标签的跳转/调用,我想不出任何不使用相对寻址的原因。CPU总是知道它的当前IP,你的汇编程序也应该知道。因为这条指令有6字节长。@DanielKamilKozar抱歉,我是asm的新手。。。无法在机器代码级别调用绝对地址吗?调用地址编码为应用于EIP寄存器的偏移量。加载CALL指令后,其值将不是0x00000000,而是0x000000006。所以它是0x00000006+0x12345672==0x12345678我想使用绝对地址,因为我想动态更改地址。谢谢你令人印象深刻的回答!在这种情况下,通过寄存器的间接寻址就是您要寻找的。如果你想了解更多关于这个的实际用法,请查阅“跳转表”。
00000000 66B809000000 mov eax,0x9
00000006 66FFE0 jmp eax
00000009 CD03 int 0x3