实现 x86 到 x64 汇编代码切换

Posted

技术标签:

【中文标题】实现 x86 到 x64 汇编代码切换【英文标题】:Implement x86 to x64 assembly code switch 【发布时间】:2017-01-11 15:47:26 【问题描述】:

我正在研究 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 Remote 调试器,并查找反汇编,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

所以我有几个问题,从附加的 ntdll 远跳转 jmp 0x33:0x7738b5cf 跳转到 x86 进程如何跳转到 x64_NtCreateFile 第一条指令?在这种情况下,从 x86 到 x64 的切换究竟是如何发生的?基本上我可以制作 x86 应用程序,并通过跳转切换段,然后执行其中的 x64 代码,我可以通过像 db (0x00) ; x64 machine code commands 这样的操作来创建,对吗?

【问题讨论】:

在这里阅读有关操作系统引导方式的信息可能对您有所帮助。首先是 16 位 -> 32 位转换,然后是 32 --> 64 位切换。 Unfortunately i cannot debug further, because, ida doesnt follow the jump 因为 ida 是错误的调试器。使用windbg - 它允许你进入跳转 @RbMm 感谢您的建议。我试过了,它不太舒服,但在拆卸方面更好 【参考方案1】:

如果您查看地址 0x7738b5cf 处的字节,您会看到类似

41 FF A7 F8 00 00 00(至少如果您使用的是 Windows 8.1 或更高版本)

对应于单个 x86_64 指令jmp QWORD PTR [r15+0xf8]

在通过远跳转从 32 位代码执行切换到 64 位代码执行之后,R15 寄存器将始终指向 wow64cpu.dll 内的一个特殊跳转表(R15 寄存器设置为指向此表来自在应用程序的 32 位入口点之前执行的 64 位代码)。

[r15+0xf8] 恰好指向wow64cpu.dll 中的CpupReturnFromSimulatedCode 方法,它将设置正确的上下文并使用syscall 指令执行实际的系统调用(在您的情况下为NtCreateFile)。

有关详细说明的一些信息,请参阅:

https://duo.com/assets/pdf/wow-64-and-so-can-you.pdf https://www.malwaretech.com/2015/07/windows-10-system-call-stub-changes.html http://rce.co/knockin-on-heavens-gate-dynamic-processor-mode-switching/

【讨论】:

谢谢,这正是我想要弄清楚的。现在我可以尝试使用 sysenter 直接调用 Nt 函数【参考方案2】:

是的,我可以确认可以在 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, [$]”的地址。

【讨论】:

感谢您的回答,对我很有帮助,我查阅了代码并理解它很好。我也可以问你一些事情,例如,如果我使用页面执行/读写来执行 VirtualAlloc,并从 x86 进程那里写入 x64 指令,我需要执行相同的段切换技巧,对吗? @Vlad 如果要执行 64 位代码,CS 必须加载 64 位(AKA 长模式)代码描述符的选择器。你用远跳、远调用、远返回来改变 CS。

以上是关于实现 x86 到 x64 汇编代码切换的主要内容,如果未能解决你的问题,请参考以下文章

《80X86汇编语言程序设计教程》十 实模式与保护模式的切换实例

《80X86汇编语言程序设计教程》十一 32位代码段和16位代码段切换实例

x86平台转x64平台关于内联汇编不再支持的解决

x64汇编第一讲,Vs系列配置x64环境与x86环境

X86汇编层面的方法调用。

《80X86汇编语言程序设计教程》十二 任务状态段控制门和控制转移