天堂之门

在32位进程中,调用32位系统函数的操作是通过WOW64实现的。WOW64在内部转换为64位,然后进行64位DLL的调用,从而实现了在64位机上兼容运行32位的操作。

在长模式下,段寄存器用作权限和标志的区分
使用WinDbg进行调试:

CS:23->gdt偏移:4*8 = 20
CS:33->gdt偏移:6*8 = 30

image-20241229225553947

可以看出23指向的段描述符的Long位为NL,而33指向的为Lo,说明33指向的是64位区域,23指向的是32位区域

对于不同的段选择子,CPU会选择对应的机器码解释方式去翻译机器码。
在软件上,有对应的32位函数实现32到64的转换然后进入64位的操作系统内核,因为(64位的操作系统一定是64位的,要执行内核代码必须先转为64位执行)

WOW64

基本调用流程:
32位进程调用32位系统API,其中系统API调用dword ptr fs:[000000C0],即TEB表偏移C0的WOW32Reserved,第一行代码为jmp 0033:77877009,表明先切换为64位,然后跳转到77877009,位于wow64cpu.dll中,首先保存了环境堆栈,然后切换为64为的堆栈
最后通过jmp qword ptr [r15+rcx*8]执行函数调用,r15为函数表,rcx为偏移

[分享][原创]汇编里看Wow64的原理(浅谈32位程序是怎样在windows 64上运行的?)-软件逆向-看雪-安全社区|安全招聘|kanxue.com

作用

反调试:几乎所有调试器均不能正常调试切换后的代码
防钩子:如果杀软只对32位DLL执行了Hook,那么这个方法可以直接绕过32位入口直接调用64位函数。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#define SWITCH_TO_X64  \
do { \
__asm push 0x33 \
__asm _emit 0xE8 \
__asm _emit 0x00 \
__asm _emit 0x00 \
__asm _emit 0x00 \
__asm _emit 0x00 \
__asm add dword ptr [esp], 5 \
__asm retf \
} while (0)


#define SWITCH_TO_X86 \
do { \
__asm _emit 0xE8 /* call $+5 */ \
__asm _emit 0x00 \
__asm _emit 0x00 \
__asm _emit 0x00 \
__asm _emit 0x00 \
__asm _emit 0xC7 /* mov dword [rsp+4], 0x23 */ \
__asm _emit 0x44 \
__asm _emit 0x24 \
__asm _emit 0x04 \
__asm _emit 0x23 \
__asm _emit 0x00 \
__asm _emit 0x00 \
__asm _emit 0x00 \
__asm _emit 0x83 /* add dword [rsp], 0xD */ \
__asm _emit 0x04 \
__asm _emit 0x24 \
__asm _emit 0x0D \
__asm _emit 0xCB /* retf */ \
} while (0)

切换为64位时,需要使用64位汇编的立即数,我感觉可以创建几个辅助的64位DLL帮我执行那些函数

对于反调试的作用,自然是已经实现了,如果要实现不使用WOW64去执行函数,需要以下操作:

  • 切换进程到64位
  • 获取gs:[60h]得到PEB
  • 遍历ldr的InMemoryOrderModuleList找到Ntdll64的模块地址
  • 在Ntdll中获取需要的函数进行操作

如果不是Ntdll中的函数,需要找到Ntdll中的LdrLoadDll函数,在64位中执行加载库函数操作,然后取函数地址并执行。

这个部分汇编写得太恶心了,之后再实现吧QAQ