执行x86到x64汇编代码开关

问题描述:

我在看如何NtDll适用于x86进程,我使用IDA PRO调试功能NtCreateFile。它的代码如下:执行x86到x64汇编代码开关

mov  eax, 55h  ; NtCreateFile 
mov  edx, offset [email protected] ; 
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没有跟上跳跃。但我做到了编译为64另一个简单的C程序,并呼吁CreateFile,连接64 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这样的操作来创建x64代码,是吗?

+0

约的方式阅读机,一个OS自举可能对你有利这里。首先是16位 - > 32位转换,然后是32位 - > 64位转换。 – enhzflep

+0

'不幸的是我不能进一步调试,因为ida不遵循跳转,因为ida是错误的调试器。使用windbg - 它允许你进入跳转 – RbMm

+0

@RbMm感谢您的建议。我试了一下,它不太舒服,但在拆卸方面更好 – Vlad

如果你看一下在地址0x7738b5cf字节,你会看到类似

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

相当于一个x86_64的指令jmp QWORD PTR [r15+0xf8]

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

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

对于基于这一点,阐述了一些信息,请参阅:

+0

谢谢,这正是我想要弄清楚的。现在我可以尝试使用sysenter直接调用Nt函数 – Vlad

是的,我可以确认可以在运行于64位窗口的32位Windows应用程序中执行64位代码。

Mixing x86 with x64 code提供了一个解释和如何做到这一点的例子。

这是我试了一下(适合和我的小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,[$]“当第二个断点被击中时。

+0

感谢您的回答,非常帮助我,我查了一下代码,并了解它非常好。我还可以问你一些问题,例如,如果我使用页面执行/读取写入'VirtualAlloc',并从x86进程写入x64指令,我需要执行相同的段切换技巧,对吧? – Vlad

+0

@Vlad如果你想执行64位代码,CS必须加载一个用于64位(AKA长模式)代码描述符的选择器。你可以用更远的跳转,更远的呼叫,更远的回报来改变CS。 –