实现 x86 到 x64 汇编代码切换
Implement x86 to x64 assembly code switch
我正在研究 NtDll
如何用于 x86 进程,并且我使用 IDA PRO 调试了函数 NtCreateFile
。它的代码如下:
mov eax, 55h ; NtCreateFile
mov edx, offset _Wow64SystemServiceCall@0 ;
call edx ; Wow64SystemServiceCall() ;
retn 2Ch
和 Wow64SystemServiceCall()
:
mov edx, large fs:30h
mov edx, [edx+464h]
test edx, 2
jz short loc_7738B5C8
int 2Eh ; DOS 2+ internal - EXECUTE COMMAND
; DS:SI -> counted CR-terminated command string
retn
loc_7738B5C8: ; CODE XREF:
jmp far ptr byte_7738B8FF
我查找了 jmp far ptr byte_7738B8FF
的命令代码
它是 EA CF B5 38 77 33 00
,它跳转到另一个段 0x33 jmp 0x33:0x7738b5cf
。所以根据我在互联网上读到的内容,这是 64 位系统上进程的 x64 段基础,对吗?不幸的是我无法进一步调试,因为 ida 没有跟随跳跃。但是我制作了另一个为 x64 编译的简单 C 应用程序,并调用了 CreateFile
,附加了 x64 IDA PRO 远程调试器,并查找了反汇编,NtCreateFile
看起来像这样:
x64_NtCreateFile proc near
mov r10, rcx
mov eax, 55h
test byte ptr ds:7FFE0308h, 1
jnz short loc_7FFED6695B85
syscall
retn
loc_7FFED6695B85:
int 2Eh ; DOS 2+ internal - EXECUTE COMMAND
; DS:SI -> counted CR-terminated command string
retn
所以我有几个问题,x86 进程附加的 ntdll 远跳转 jmp 0x33:0x7738b5cf
是如何进入 x64_NtCreateFile
第一条指令的?在这种情况下,从 x86 到 x64 的切换究竟是如何发生的?基本上我可以制作 x86 应用程序,并使用跳转切换段,然后执行其中的 x64 代码,我可以通过像 db (0x00) ; x64 machine code commands
这样的操作来创建它,对吗?
是的,我可以确认可以在 64 位 windows 上的 32 位 Windows 应用 运行 中执行 64 位代码。
Mixing x86 with x64 code 提供了说明和操作示例。
这是我尝试过的(使用我的 Smaller C 编译器进行改编和编译):
#define EM(a) asm("db " #a);
#define X64_Start_with_CS(_cs) \
{ \
EM(0x6A) EM(_cs) /* push _cs */ \
EM(0xE8) EM(0) EM(0) EM(0) EM(0) /* call $+5 */ \
EM(0x83) EM(4) EM(0x24) EM(5) /* add dword [esp], 5 */ \
EM(0xCB) /* retf */ \
}
#define X64_End_with_CS(_cs) \
{ \
EM(0xE8) EM(0) EM(0) EM(0) EM(0) /* call $+5 */ \
EM(0xC7) EM(0x44) EM(0x24) EM(4) /* */ \
EM(_cs) EM(0) EM(0) EM(0) /* mov dword [rsp + 4], _cs */ \
EM(0x83) EM(4) EM(0x24) EM(0xD) /* add dword [rsp], 0xD */ \
EM(0xCB) /* retf */ \
}
#define X64_Start() X64_Start_with_CS(0x33)
#define X64_End() X64_End_with_CS(0x23)
#define __break() asm("int3")
int main(void)
{
__break();
X64_Start();
EM(0x48) EM(0x8D) EM(0x05) EM(0xF9) EM(0xFF) EM(0xFF) EM(0xFF) // lea rax, [$] ; rip-relative
X64_End();
__break();
}
然后我在调试器下运行它并注意到 eax 包含 64 位指令的地址 "lea rax, [$]" 当第二个断点被击中时。
如果您查看地址 0x7738b5cf
处的字节,您会看到类似
的内容
41 FF A7 F8 00 00 00
(至少如果您使用的是 Windows 8.1 或更新版本)
对应于单个 x86_64 指令 jmp QWORD PTR [r15+0xf8]
.
在通过远跳转从 32 位代码执行切换到 64 位代码执行后,R15
寄存器将始终指向 wow64cpu.dll
内的特殊跳转 table( R15
寄存器被设置为从在应用程序的 32 位入口点之前执行的 64 位代码指向此 table。
[r15+0xf8]
恰好指向 wow64cpu.dll
中的 CpupReturnFromSimulatedCode
方法,它将设置正确的上下文并执行实际的系统调用(在您的情况下为 NtCreateFile
) 使用 syscall
指令。
有关对此进行详细说明的一些信息,请参阅:
我正在研究 NtDll
如何用于 x86 进程,并且我使用 IDA PRO 调试了函数 NtCreateFile
。它的代码如下:
mov eax, 55h ; NtCreateFile
mov edx, offset _Wow64SystemServiceCall@0 ;
call edx ; Wow64SystemServiceCall() ;
retn 2Ch
和 Wow64SystemServiceCall()
:
mov edx, large fs:30h
mov edx, [edx+464h]
test edx, 2
jz short loc_7738B5C8
int 2Eh ; DOS 2+ internal - EXECUTE COMMAND
; DS:SI -> counted CR-terminated command string
retn
loc_7738B5C8: ; CODE XREF:
jmp far ptr byte_7738B8FF
我查找了 jmp far ptr byte_7738B8FF
的命令代码
它是 EA CF B5 38 77 33 00
,它跳转到另一个段 0x33 jmp 0x33:0x7738b5cf
。所以根据我在互联网上读到的内容,这是 64 位系统上进程的 x64 段基础,对吗?不幸的是我无法进一步调试,因为 ida 没有跟随跳跃。但是我制作了另一个为 x64 编译的简单 C 应用程序,并调用了 CreateFile
,附加了 x64 IDA PRO 远程调试器,并查找了反汇编,NtCreateFile
看起来像这样:
x64_NtCreateFile proc near
mov r10, rcx
mov eax, 55h
test byte ptr ds:7FFE0308h, 1
jnz short loc_7FFED6695B85
syscall
retn
loc_7FFED6695B85:
int 2Eh ; DOS 2+ internal - EXECUTE COMMAND
; DS:SI -> counted CR-terminated command string
retn
所以我有几个问题,x86 进程附加的 ntdll 远跳转 jmp 0x33:0x7738b5cf
是如何进入 x64_NtCreateFile
第一条指令的?在这种情况下,从 x86 到 x64 的切换究竟是如何发生的?基本上我可以制作 x86 应用程序,并使用跳转切换段,然后执行其中的 x64 代码,我可以通过像 db (0x00) ; x64 machine code commands
这样的操作来创建它,对吗?
是的,我可以确认可以在 64 位 windows 上的 32 位 Windows 应用 运行 中执行 64 位代码。
Mixing x86 with x64 code 提供了说明和操作示例。
这是我尝试过的(使用我的 Smaller C 编译器进行改编和编译):
#define EM(a) asm("db " #a);
#define X64_Start_with_CS(_cs) \
{ \
EM(0x6A) EM(_cs) /* push _cs */ \
EM(0xE8) EM(0) EM(0) EM(0) EM(0) /* call $+5 */ \
EM(0x83) EM(4) EM(0x24) EM(5) /* add dword [esp], 5 */ \
EM(0xCB) /* retf */ \
}
#define X64_End_with_CS(_cs) \
{ \
EM(0xE8) EM(0) EM(0) EM(0) EM(0) /* call $+5 */ \
EM(0xC7) EM(0x44) EM(0x24) EM(4) /* */ \
EM(_cs) EM(0) EM(0) EM(0) /* mov dword [rsp + 4], _cs */ \
EM(0x83) EM(4) EM(0x24) EM(0xD) /* add dword [rsp], 0xD */ \
EM(0xCB) /* retf */ \
}
#define X64_Start() X64_Start_with_CS(0x33)
#define X64_End() X64_End_with_CS(0x23)
#define __break() asm("int3")
int main(void)
{
__break();
X64_Start();
EM(0x48) EM(0x8D) EM(0x05) EM(0xF9) EM(0xFF) EM(0xFF) EM(0xFF) // lea rax, [$] ; rip-relative
X64_End();
__break();
}
然后我在调试器下运行它并注意到 eax 包含 64 位指令的地址 "lea rax, [$]" 当第二个断点被击中时。
如果您查看地址 0x7738b5cf
处的字节,您会看到类似
41 FF A7 F8 00 00 00
(至少如果您使用的是 Windows 8.1 或更新版本)
对应于单个 x86_64 指令 jmp QWORD PTR [r15+0xf8]
.
在通过远跳转从 32 位代码执行切换到 64 位代码执行后,R15
寄存器将始终指向 wow64cpu.dll
内的特殊跳转 table( R15
寄存器被设置为从在应用程序的 32 位入口点之前执行的 64 位代码指向此 table。
[r15+0xf8]
恰好指向 wow64cpu.dll
中的 CpupReturnFromSimulatedCode
方法,它将设置正确的上下文并执行实际的系统调用(在您的情况下为 NtCreateFile
) 使用 syscall
指令。
有关对此进行详细说明的一些信息,请参阅: