[免杀]天堂之门

admin 2026-01-28 06:48:38 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档解析天堂之门技术,利用段寄存器CS从0x23切换至0x33,在32位进程中执行64位代码以绕过EDRHook并阻碍逆向分析。文章结合Wow64机制与GDT原理,提供Minhook与wow64pp库的实战代码,并指出该技术存在调用限制及易被内核层检测的缺陷。 综合评分: 85 文章分类: 免杀,二进制安全,逆向分析,红队


cover_image

[免杀] 天堂之门

原创

joe1sn joe1sn

不止Sec

2026年1月27日 20:30 重庆

这里是两段汇编代码

section .data    msg db "Hello, World!", 10    len equ $ - msg
section .text    global _start
_start:    ; ssize_t write(int fd, const void *buf, size_t count)    mov eax, 4          ; sys_write    mov ebx, 1          ; stdout    mov ecx, msg    mov edx, len    int 0x80
    ; void exit(int status)    mov eax, 1          ; sys_exit    xor ebx, ebx    int 0x80

使用int 0x80系统调用完成输出

section .data    msg db "Hello, World!", 10    len equ $ - msg
section .text    global _start
_start:    ; ssize_t write(int fd, const void *buf, size_t count)    mov rax, 1          ; sys_write    mov rdi, 1          ; stdout    lea rsi, [rel msg]    mov rdx, len    syscall
    ; void exit(int status)    mov rax, 60         ; sys_exit    xor rdi, rdi    syscall

这里使用的是syscall完成输出

或许在之前你已经了解过DLL注入的相关篇章

https://mp.weixin.qq.com/s/qYO0Cf5MRT4vKCT5WYz1KQ

不禁让人产生疑问:32位和64位程序使用的指令集不同,那么64位系统如何兼容运行32位软件呢?本篇文章在windows上作为探索。

Wow64分析

32位程序在64位windows上的运行时通过wow64模拟器实现的

顺带提一嘴,这里根据《深入解析windows操作系统》3.6 CreateProcess 的创建进程中相关步骤

  1. 首先是转换并验证参数:主要调度优先级,是否调试,分析参数

  2. 打开要执行的映像:主要是创建对应的windows映像

  3. 创建Windows进程执行体对象:主要是设置EPROCESS对象,其中

  • 如果处于Wow64下检查是否使用大页面
  • Wow64下则随后分配辅助结构EWOW64PROCESS
  • 在映射Ntdll.dll到进程中,对于Wow64进程还需要映射32位的Ntdll.dll

操作系统相关知识

在OS混沌未开之际,便有几位老祖

  • CS (Code Segment Register):代码段的段基址
  • DS(Data Segment Register):数据段的段基址
  • ES(Extra Segment Register):其值为附加数据段的段基值,称为“附加”是因为此段寄存器用途不像其他 sreg 那样固定,可以额外做他用。
  • FS(Extra Segment Register):其值为附加数据段的段基值
  • GS:同上
  • SS(Stack Segment Register):堆栈段寄存器

其存储着os的本源灵气(物理地址)

再后来一生二、二生三,老祖集天地之造化,创结界(保护模式),著天书(全局描述符GDT(Global Descriptor Table)),生韵韵众生于虚幻。老祖(段寄存器)在虚幻中的像,幻化众生心中便为段选择子


说人话:

段寄存器(CS / DS / SS …)里放的 不是地址,而是:一个 索引 + 权限声明的小结构。

在windows中

| CS | 含义 | | — | — | | 0x23 | 32 位用户代码段 | | 0x33 | 64 位用户代码段 | | 0x10 | 内核代码段 |

  • 怎么做权限隔离?
  • 怎么防止用户程序乱访问内核?
  • 怎么同时跑不同“模式”的代码?

Intel 的答案:把“段的定义”集中放在一张表里,让 CPU 强制检查

段选择子一般长这样(冒号后面可以理解为二进制的长度(bit长度),注意是小端序)

typedef struct selector{    unsigned char RPL :2;    unsigned char TI :1;    //local descriptor table    unsigned short index :13;} __attribute__((packed)) selector;
//15           3 2  1 0//+--------------+--+--+//|   Index      |TI|RPL|//+--------------+--+--+

那么当CS=0x33=0b110011时,按照段选择子的结构体解析:

  • RPL = 11 b = 2
  • TI = 0 = 0
  • index = 110 = 6

那么CPU就回去GDT[6]看这是一个 64 位 ring3 代码段

天堂之门 Heaven’s Gate

利用Wow64机制,就可以在32位程序中运行64位代码,这样EDR 常 hook 32 位 ntdll.dll的时候就可以绕过检测。同时分析32位的程序发现了64位的指令一般是反汇编不出的,例如使用32位的ida分析64位的程序。

这种方式的特征就是使用长跳,类似如下代码:

; 32-bit contextfar_jump CS=0x33, RIP=entry64
; ===== 64-bit context =====entry64:    ; 64-bit instructions    ; e.g. call 64-bit ntdll stub    far_return CS=0x23

现在结合Minhook编写一段hook ntdll.dll中 测试代码

关于minhook的使用可以参考:https://mp.weixin.qq.com/s/Po_t-JGj0e3dMBKDd9i8cw

为了省去LDR这种通用过程的代码,我使用了 https://github.com/JustasMasiulis/wow64pp

这个框架的长跳部分实现如下:

其中的push 0x33只是为“远返回 / 远跳转”准备一个新的段选择子

框架是header-only的,只有一个.h文件,非常轻松的就能使用,但是这个框架目前存在两个问题:

  • 只能使用ntdll.dll中的函数。怎么说呢,根据一些看雪老哥的说法是

另外无法加载kerner32.dll,和user32.dll是操作系统设计限制,就算你加载成功并调用了里面的函数,之后程序也会问题,因为程序已经进入一个混乱的状态了(同时拥有了32位和64位的窗口态等),会让系统分不清楚你这个进程到底是64位的还是32位的

  • 多参数传参问题。最开始我使用的是NtCreateFile,但是返回的结果是传参错误,换了NtQuerySystemTime就可了。这种保持堆栈平衡之类的感觉很难做到十全十美。
#define&nbsp;NOMINMAX#include&nbsp;"extern/minhook/minhook.h"#include&nbsp;"extern/wow64pp/wow64pp.hpp"#include&nbsp;<iostream>#include&nbsp;<Windows.h>#include&nbsp;<winternl.h>typedef&nbsp;NTSTATUS(NTAPI* NtQuerySystemTime_t)(&nbsp; &nbsp; PLARGE_INTEGER SystemTime&nbsp; &nbsp; );NtQuerySystemTime_t fpNtQuerySystemTime =&nbsp;nullptr;NTSTATUS NTAPI&nbsp;HookedNtQuerySystemTime(&nbsp; &nbsp; PLARGE_INTEGER SystemTime){&nbsp; &nbsp; std::cout <<&nbsp;"[hook] NtQuerySystemTime called (x86 stub), ";&nbsp; &nbsp; NTSTATUS status =&nbsp;fpNtQuerySystemTime(SystemTime);&nbsp; &nbsp;&nbsp;if&nbsp;(NT_SUCCESS(status))&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; std::cout <<&nbsp;"[hook] SystemTime = "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; << SystemTime->QuadPart << std::endl;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;return&nbsp;status;}int&nbsp;main()&nbsp;{&nbsp; &nbsp; LARGE_INTEGER systemTime = {&nbsp;0&nbsp;};&nbsp; &nbsp;&nbsp;NtQuerySystemTime(&systemTime);&nbsp; &nbsp;&nbsp;if&nbsp;(MH_Initialize() != MH_OK)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; std::cerr <<&nbsp;"[x] init hook failed\n";&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp; LPVOID pTarget =&nbsp;GetProcAddress(GetModuleHandleW(L"ntdll.dll"),&nbsp;"NtQuerySystemTime");&nbsp; &nbsp; std::cout <<&nbsp;"[*] now hook NtQuerySystemTime: 0x"&nbsp;<< std::hex << pTarget << std::endl;&nbsp; &nbsp;&nbsp;if&nbsp;(MH_CreateHook(pTarget, &HookedNtQuerySystemTime,&nbsp;reinterpret_cast<LPVOID*>(&fpNtQuerySystemTime)) != MH_OK)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; std::cerr <<&nbsp;"[x] create hook failed\n";&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;MH_EnableHook(pTarget);&nbsp; &nbsp;&nbsp;NtQuerySystemTime(&systemTime);&nbsp; &nbsp;&nbsp;auto&nbsp;ntdllHandle = wow64pp::module_handle("ntdll.dll");&nbsp; &nbsp; std::cout <<&nbsp;"[*] found ntdll (x64): 0x"&nbsp;<< std::hex << ntdllHandle <<&nbsp;"\n";&nbsp; &nbsp;&nbsp;auto&nbsp;NtQuerySystemTime64 = wow64pp::import(ntdllHandle,&nbsp;"NtQuerySystemTime");&nbsp; &nbsp; std::cout <<&nbsp;"[*] found NtQuerySystemTime (x64): 0x"&nbsp;<< std::hex << NtQuerySystemTime64 <<&nbsp;"\n";&nbsp; &nbsp;&nbsp;auto&nbsp;status = wow64pp::call_function(&nbsp; &nbsp; &nbsp; &nbsp; NtQuerySystemTime64,&nbsp; &nbsp; &nbsp; &nbsp; &systemTime &nbsp; &nbsp; &nbsp; &nbsp;// PLARGE_INTEGER&nbsp; &nbsp; );&nbsp; &nbsp; std::cout <<&nbsp;"[wow64] NtQuerySystemTime status: 0x"&nbsp;<< std::hex << status <<&nbsp;"\n";&nbsp; &nbsp; std::cout <<&nbsp;"[wow64] SystemTime (100ns since 1601): "&nbsp;<< systemTime.QuadPart <<&nbsp;"\n";&nbsp; &nbsp;&nbsp;MH_DisableHook(pTarget);&nbsp; &nbsp;&nbsp;return&nbsp;0;}
  1. 正常调用NtQuerySystemTime
  2. Hook NtQuerySystemTime并调用
  3. 使用Wow64进行64位的NtQuerySystemTime调用

后记

看似很nb但是现在这种技巧已经不怎么行了

比如上面的绕过hook,这种应用层的很明显是无法绕过内核/etw hook的,同时有明显的shellcode特征。不过可以恶心一手逆向的人,因为这样可以向32位的程序狠狠塞64位的shellcode,造成反汇编指令识别的错误,但也只能恶心了。

关于项目结构这里给出来便于复现。

extern\minhook\CMakeLists.txt

cmake_minimum_required(VERSION 3.11)project(minhook)
set(MINHOOK_SOURCES&nbsp; &nbsp; buffer.c&nbsp; &nbsp; hook.c&nbsp; &nbsp; trampoline.c&nbsp; &nbsp; hde/hde32.c&nbsp; &nbsp; hde/hde64.c&nbsp; &nbsp; )
set(MINHOOK_INCLUDE&nbsp; &nbsp; buffer.h&nbsp; &nbsp; trampoline.h&nbsp; &nbsp; hde/hde32.h&nbsp; &nbsp; hde/hde64.h&nbsp; &nbsp; hde/pstdint.h&nbsp; &nbsp; hde/table32.h&nbsp; &nbsp; hde/table64.h&nbsp; &nbsp; )
add_library(${PROJECT_NAME}&nbsp; STATIC&nbsp; &nbsp;&nbsp;${MINHOOK_SOURCES}&nbsp; &nbsp;&nbsp;${MINHOOK_INCLUDE})
target_include_directories(${PROJECT_NAME}&nbsp; PUBLIC&nbsp; &nbsp;&nbsp;${CMAKE_CURRENT_SOURCE_DIR})

extern\wow64pp\CMakeLists.txt

cmake_minimum_required(VERSION 3.11)project(wow64pp LANGUAGES CXX)
# 创建一个 interface 库add_library(${PROJECT_NAME}&nbsp;INTERFACE)
# 添加 include 目录target_include_directories(${PROJECT_NAME}&nbsp;INTERFACE&nbsp; &nbsp;&nbsp;${CMAKE_CURRENT_SOURCE_DIR})

CMakeLists.txt

cmake_minimum_required(VERSION&nbsp;3.11)project(gate_example LANGUAGES CXX)set(CMAKE_INCLUDE_CURRENT_DIR ON)set(CMAKE_CXX_STANDARD&nbsp;14)set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(extern/minhook)add_subdirectory(extern/wow64pp)
set(PROJECT_INCLUDE&nbsp; &nbsp; include/HeavensGate.hpp)set(PROJECT_SOURCE&nbsp; &nbsp; src/main.cpp)
# Specify MSVC UTF-8 encodingadd_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
add_executable(${PROJECT_NAME}&nbsp;${PROJECT_INCLUDE}&nbsp;${PROJECT_SOURCE})target_link_libraries(${PROJECT_NAME} PRIVATE minhook wow64pp)target_compile_definitions(${PROJECT_NAME} PRIVATE UNICODE _UNICODE)

引用

https://learn.microsoft.com/zh-cn/windows/win32/winprog64/wow64-implementation-details

https://bbs.kanxue.com/thread-270153.htm

Mixing x86 with x64 code


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:不止Sec joe1sn joe1sn《[免杀] 天堂之门》

[免杀]天堂之门 网络安全文章

[免杀]天堂之门

文章总结: 文档解析天堂之门技术,利用段寄存器CS从0x23切换至0x33,在32位进程中执行64位代码以绕过EDRHook并阻碍逆向分析。文章结合Wow64机
评论:0   参与:  0