本帖最后由 JuncoJet 于 2025-8-6 10:18 编辑
Heaven’s Gate 是一种在 Windows WOW64 架构下使用的段跳技术,它允许一个 32 位程序在运行时切换到 64 位代码段执行。名字听起来像是某种神秘入口——其实它就是:一个程序从 32 位世界“穿越”到 64 位世界的门。
Heaven’s Gate 是什么?
它是一个 远跳指令(jmp far),通常写作:
jmp far 33:xxxx
为什么需要 Heaven’s Gate?
在 WOW64 架构下:
应用是 32 位的(x86)
操作系统是 64 位的(x64)
所以系统调用必须最终进入 64 位内核(Ring 0)
但 32 位程序不能直接执行 syscall 指令(它是 x64 专属),于是就需要 Heaven’s Gate 来:
“跳过 32 位的限制,进入 64 位的 syscall stub”
32-bit App
└──> ntdll32.dll → syscall stub
└──> Heaven’s Gate → jmp far 33:xxxx
└──> wow64cpu.dll → 64-bit stub
└──> ntdll64.dll → syscall
└──> Ring 0 内核
WOW64模块 主要代理对象 职能描述
wow64cpu.dll ntdll.dll 的系统调用部分 负责执行 CPU 指令转换(x86 到 x64),尤其是进入 Ring 0 的 syscall stub 跳转
wow64.dll ntdll.dll 的参数调度 管理调用栈转换,参数封装,执行上下文转移,配合 wow64cpu 做 syscall dispatch
wow64win.dll user32.dll / gdi32.dll 转发窗口管理、输入设备事件、图形绘制等与用户交互相关的调用
早在XP的时代,32位程序是这样的结构
ntdll.dll 中的系统调用封装也改为:
mov eax, 系统调用号
mov edx, 0x7FFE0300
call dword ptr [edx] ; 实际是 KiFastSystemCall
0x7FFE0300 是 _KUSER_SHARED_DATA 结构中的 SystemCall 字段,指向 KiFastSystemCall。
到了64位系统,32位程序则使用了Wow64组件(wow64、wow64cpu、wow64win)来实现了天堂之门,应该说天堂之门只是他的一部分。更多的是抽象层ports了32位到64位内核函数的转换,实现了ntdll、user32、gdi32的翻译。
导读完成,下面实战内容,x32dbg查看wow64的主要结构
以 CloseHandle API为入口
内部是ZwClose
再往下就是一个跳转表结构,指向到 wow64
内部是 Heaven’s Gate 的经典结构 jmp far 0x33
写一个POC,劫持这个代理结构
代码由G++和NASM混合编程,实在受不鸟GAS的变态语法。纯C/C++实现不了,除非写shellcode
可以看到这随便添加的几个WINAPI都被成功的截取了,返回的他的调用栈地址
一次 Hook 全局响应,优雅、上档次,没有多余的代码,实现 Hook 所有的API
完整的测试代码在后面
[C++] 纯文本查看 复制代码 #include <windows.h>
#include <iostream>
#include <set>
#include <Psapi.h>
using namespace std;
extern "C" void hgate();
void *pInfo;
typedef int (*nFn)();
nFn oFn;
set<void*> sp;
HMODULE hMod;
void pinfo(int p){
void *pp=*(void**)(&p+2);
sp.insert(pp);
printf("ADDR %08X\n",pp);
}
int main(int argc, char *argv[]){
//dword ptr fs:[7EFDD0C0]=722E2320
DWORD wow64=(DWORD)LoadLibrary("wow64cpu");
DWORD *p=(DWORD*)0x7EFDD0C0;
if(*p>wow64){
puts("true");
hMod=(HMODULE)LoadLibrary("ntdll");
DWORD *pFn=(DWORD*)&hgate;
cout<<"pFn "<<pFn<<endl;
DWORD oldProtect;
VirtualProtect((void*)*p, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &oldProtect);
puts("set RWX");
cout<<"oVal "<<*p<<endl;
pInfo=(void*)&pinfo;
cout<<"pinfo "<<pInfo<<endl;
oFn=(nFn)*p;
*p=(DWORD)pFn;
cout<<"nVal "<<*p<<endl;
puts("always wait...");
}else{
puts("false");
}
for(;;){
Sleep(1000);
}
}
[Asm] 纯文本查看 复制代码 global _hgate
extern _oFn, _pinfo, _pInfo
section .text
_ret:
retn
_hgate:
call [_oFn]
call [_pInfo]
pop edx
pop edx
push edx
push edx
ret
int3
|