当我创建一个处于挂起状态(CREATE_SUSPENDED
)的Windowsx86进程时,其CONTEXT
包含:
Eax
寄存器中入口点的虚拟地址;Ebx
寄存器中Process Environment Block structure的虚拟地址。
但是,当我对x86_64进程执行同样的操作时,CONTEXT
包含:
Rcx
寄存器中入口点的虚拟地址(为什么不是Rax
?)Rdx
寄存器中PEB结构的虚拟地址(为什么不是Rbx
?)
在我看来,用x64中的Rax
代替x86中的Eax
,用x64中的Rbx
代替x86中的Ebx
是合乎逻辑的。
但是,我们看到的不是1 m13n1x → 1 m14n1x和1 m15n1x → 1 m16n1x,而是1 m17n1x → 1 m18n1x和1 m19n1x → 20n1x。
同样,我看到64位的Cheat Engine在打开32位进程时也意识到了这一点(注意值eax
ecx
和ebx
edx
的迁移:
第一次
在64位进程中,从*ax
寄存器迁移到*cx
以及从*bx
迁移到*dx
的原因是什么?
它是否以某种方式连接到calling conventions?
它是否仅与Windows相关,或者其他操作系统是否也有这种寄存器重用?
更新:刚刚创建的处于挂起状态的x64进程的屏幕截图:
1条答案
按热度按时间js5cn81o1#
在我看来,用x64中的Rax代替x86中的Eax,用x64中的Rbx代替x86中的Ebx是合乎逻辑的。
我不明白为什么要这么认为。
即使在MS,他们已经定义了一个内部ABI来记录刚刚创建的32位进程的上下文,64位版本的也会重新设计,因此没有理由假设它继承了旧的32位ABI。
如果Windows使用
sysret
返回到用户空间,则创建的挂起状态进程可能会泄漏rcx
中的目标地址。通过其他机制(例如
iret/retf
)返回,如32位代码的情况,当然会泄漏不同寄存器中的不同数据。您所看到的可能是Windows如何返回到用户模式的一个人工产物。我不知道返回到用户模式的Windows内核代码是什么,但可以合理地假设MS为32位进程保留了相同的接口,并且该接口是在
sysret
被广泛使用之前设计的。请注意,在PE入口点
rcx
包含指向PEB的指针,rdx
包含指向入口点的指针(而不是相反)。前者看起来是an undocumented parameter passed to the entry-point function,后者可能只是如何调用入口点的产物。实际上,32位进程将在堆栈中找到指向PEB的指针,作为PE入口点代码的第一个参数。
对于其他操作系统,任何没有被证明是稳定的东西都可以随时自由改变(包括寄存器中剩下的东西)。
就稳定性而言,从32位过渡到64位实现是一个相当大的进步,同样,没有理由继续使用一个非常旧的接口(但是有更宽的寄存器),而不是用所有最新的知识来改进它。例如,你可以很容易地看到,Linux“重新使用”了64位系统调用ABI中的寄存器。