在解决一个错误时,我遇到了两个Win64 DLL的导入跳转表之间的区别。 64位版本的kernel32.dll
在其导入跳转表中使用普通FF25
jmp 指令。另一方面,advapi32.dll
的64位版本使用48FF25
,在 jmp 操作码之前指示REX.w=1
前缀。但是,两者似乎都有32位操作数指定RIP +偏移地址。
此特定操作码上的REX.w前缀是否有任何意义?
我经常不使用机器代码,所以请原谅任何实际错误。
答案 0 :(得分:9)
忽略REX.W前缀。在64位模式下,FF /4
操作码始终具有64位操作数(JMP r / m64 ),因此操作数大小更改前缀(REX.W,66)无效。
此REX.W前缀存在的原因可能是符合Microsoft的x64调用约定关于展开的规则。跳转导入存根实际上是一个指令函数,并且由于Windows上的异常是异步的,它们可以随时发生,因此在执行此函数时可能会生成异常。微软放置了许多restrictions on instructions used at the start and end of functions。特别是该函数必须以仅包含某些指令的结尾结束。根据MSDN上的Kevin Frei's blog,如果最后一条指令是间接跳转,它必须使用REX.W前缀:
另一个注意事项:如果最终的jmp不是ip-relative jmp,而是间接的 jmp,它必须以REX前缀开头,以指示操作系统放松 例程,跳转是在函数之外,否则, 操作系统假设它跳转到同一功能内的不同位置。
使用REX.W之间的不一致可能是因为上述规则与Microsoft官方文档对最终JMP指令的要求并不完全一致:
在epilog中只允许jmp语句的一部分。这些是 仅限于具有ModRM内存引用的jmp类,其中ModRM mod 字段值00.在epilog中使用带有ModRM mod字段值01或的字符串 10是被禁止的。
请注意,由于这会排除不使用ModR / M编码的相对JMP指令,这是最常见的一种JMP结束功能,所以我倾向于相信它['这里的官方文档有误。
不一致的其他可能原因是Microsoft的unwinder特别处理导入跳转存根或没有REX.W前缀的跳转存根是一个错误,并且会导致程序在非常不可能的情况下终止异常发生在他们被执行时。