首页
社区
课程
招聘
[原创]天堂之门 (Heaven's Gate) C语言实现
发表于: 2021-11-6 15:23 34237

[原创]天堂之门 (Heaven's Gate) C语言实现

2021-11-6 15:23
34237

天堂之门 (Heaven's Gate) 是一种在32位WoW64进程中执行64位代码,以及直接调用64位WIN32 API函数的技术。从安全角度看,天堂之门可以作为一种软件保护技术,用于防止静态分析以及跨进程的API Hook;从恶意代码角度看,该技术可以绕过沙盒对WIN32 API调用的检测。

本文介绍了天堂之门技术的原理及C语言实现。

天堂之门技术的最早应用已经不可考究,我找到的最早的一篇详解天堂之门的文章是2012年的Knockin’ on Heaven’s Gate – Dynamic Processor Mode Switching,内容非常详细,目前能找到的有关天堂之门的文章大多都引用了这篇。比较新的一个是Rebuild The Heaven's Gate: from 32-bit Hell back to 64-bit Wonderland,貌似是一个台湾人(《惡意程式前線戰術指南》作者馬聖豪)的讲座PPT,比较简短。

简单来说WoW64是Windows x64提供的一种兼容机制,可以认为WoW64是64位Windows系统创建的一个32位的模拟环境,使得32位可执行程序能够在64位的操作系统上正常运行。

推荐一篇讲解WoW64的深度好文WoW64 internals,这里就不再赘述了。

下图(来自Rebuild The Heaven's Gate: from 32-bit Hell back to 64-bit Wonderland)展示了正常情况下32位进程通过WoW64机制调用WIN32 API的过程。以ZwOpenProcess函数的调用为例:

image-20211106105559585

下图展示了通过天堂之门技术调用WIN32 API的过程。这里我们通过一些操作绕过了WoW64机制,手动切换到64位模式并调用64位下的ZwOpenProcess函数,大致流程如下(和图中不太一样):

如果需要调用的是ntdll之外的函数,以kernel32.dll中的CreateFile函数为例,还需要:

image-20211106111544450

从上述过程中我们可以发现通过天堂之门的API调用并没有调用ntdll32中的函数,而目前大多数沙箱在检测32位程序时仅仅会对32位函数进行Hook,通过天堂之门,我们成功绕过了沙箱的API检测:

image-20211106114019275

GitHub仓库:bluesadi/Heavens-Gate

实现天堂之门大概需要实现到以下几个函数:

MakeUTFStr:构造UNICODE_STRING结构体

GetKernel32:加载64位kernel32.dll及其依赖kernelbase.dll

接下来我们将一一讲解这些函数的实现。

VS项目属性中选择"Release", "Win32",切记关闭优化(不关优化会出现玄学错误!!!):

image-20211106104714162

C/C++->代码生成->运行库改为“多线程(/MT)”,即静态编译:

image-20211106132721846

函数声明:

该函数的作用是将64位地址src的内容拷贝到dst,拷贝sz个字节,因为我们操作的地址是64位的,所以我们必须切换到64位模式用64位的汇编实现

由32位切换到64位的代码如下,[bits 32]表示接下来的汇编要以32位模式编译,_next_x64_code为64位汇编代码的地址:

retf表示远返回,该指令会从栈顶取出一个返回地址,再取出一个cs段选择子,在上述代码中,retf指令会跳转到0x33:_next_x64_code,并将cs段寄存器置为0x33,此时程序切换到64位模式(Windows下cs段寄存器为0x23则以32位模式执行指令,为0x33则以64位模式执行指令)。

随后执行64位汇编指令,将src的数据拷贝到dst中,这里不再做解释:

执行完64位代码后,我们需要切回32位模式并返回。retfq中的q表示qword,即返回到64位的地址:

汇编的编译我们可以用Python的keystone模块实现:

输出得到shellcode,其中0x12345678我们要替换成_next_x64_code,也就是下一段64位汇编指令的地址:

完整的shellcode:

要执行这段shellcode,我们需要在堆中开辟新的空间,属性为PAGE_EXECUTE_READWRITE,即可读可写可执行,将shellcode拷贝到这块区域,替换_next_x64_codesrcdst_next_x86_code的地址后执行:

完整代码:

函数声明:

该函数的作用是获取PEB64的地址。

64位中gs:[0x30]指向TEB,gs:[0x60]指向PEB,获取PEB64的地址很简单,只需要将gs:[0x60]拷贝到rax作为返回值即可:

完整代码:

函数声明:

该函数的作用是获取名为moduleName的模块的基址。

实现步骤如下:

img

在WinDbg中使用dt指令查看结构体,可以看到Ldr的地址在PEB中的偏移为0x018:

用之前实现的memcpy64函数拷贝Ldr的地址:

打印_PEB_LDR_DATA结构体,可以看到InLoadOrderModuleList在Ldr中的偏移为0x10:

拷贝InLoadOrderModuleList的地址:

InLoadOrderModuleList的实际类型为_LDR_DATA_TABLE_ENTRYBaseDllName中存储了DLL的名称,类型为_UNICODE_STRING

所以Buffer的偏移量在_LDR_DATA_TABLE_ENTRY中的偏移为0x58+0x08,即96。

遍历链表的代码如下:

完整代码如下:

函数声明:

通过GetModuleHandle64获取模块地址后,此时还无法通过kernel32.dll中的GetProcAddress函数获取模块中函数的地址,可以通过遍历模块的导出表获取函数地址作为过渡方案。

img

首先获取导出表地址,这部分涉及PE文件结构,不再赘述了:

随后遍历导出表,从导出表中读取函数的名称和地址,将函数名称与func进行比对,比对成功则返回函数地址:

完整代码如下:

函数声明:

由于32位与64位的函数调用的传参方式不同,以及在上一步中我们通过MyGetProcAddress函数获取的函数地址为64位,肯定不能直接转化为函数指针调用,所以我们需要用64位汇编实现一个64位函数的调用。

首先来简单了解一下64位中WINAPI调用的传参方式:

image-20211106141230284

构造shellcode,因为接下来我们需要对栈指针进行操作,所以首先将esp保存到ebx中,shellcode执行完毕后需要复原。and esp, 0xFFFFFFF8的作用是使rsp与8对齐,这是64位汇编的栈对齐要求,否则在执行某些系统调用时可能会出错:

切换到64位后按照64位WINAPI调用协定传参,调用函数之前保留32字节的空间,将函数返回值保存到rax。在shellcode执行前后保存和复原rsirdi

最后切换回32位模式,并还原espebx

完整代码如下:

函数原型如下:

构造一个_UNICODE_STRING结构体并返回64位的地址。代码实现如下:

函数声明:

加载kernel32.dll以及kernelbase.dll

在上面提到的Knockin’ on Heaven’s Gate – Dynamic Processor Mode Switching这篇文章中对这一部分有很复杂的叙述。kernel32.dll在Windows中的加载地址是固定的,并且只能被加载到那个地址。在该作者测试的环境下64位kernel32.dll的加载地址所在的空间已经被分配并且被映射为私有的了,会导致调用LdrLoadDll函数加载kernel32.dll时失败并返回0xC0000018 ( STATUS_CONFLICTING_ADDRESSES )。

Any attempts to load kernel32.dll using the LdrLoadDll function would result to the error code 0xC0000018 ( STATUS_CONFLICTING_ADDRESSES ). This is due to the fact that the default memory location of kernel32 is already mapped as private.

解决的思路非常简单:即调用NtFreeVirtualMemory函数将这块已经分配的空间释放掉,再用LdrLoadDll重新加载。但代码写起来非常复杂,可以参考dadas190/Heavens-Gate-2.0的实现。

但是在我的操作系统上(Windows 10 x64 20H2),并没有找到作者提到的分配和映射过程,并且直接调用LdrLoadDll函数也能正常加载kernel32.dll,可能是在某个Windows版本中被移除了吧。

所以加载kernel64这部分的代码就变得非常简单了:

函数原型:

获取了kernel64的地址后我们就能直接通过GetProcAddress函数获取模块中函数的地址了。代码实现如下:

函数原型:

调用kernel64的LoadLibraryA函数加载其他DLL,如user32.dll等等。

代码实现如下:

测试一下典中典之MessageBox弹窗:

运行效果:

image-20211106145257165

首先测试一段正常的文件读写代码:

检测到了文件释放:

image-20211106145521139

再测试一下用天堂之门实现的同样功能的代码:

没有检测到文件释放:

image-20211106145630433

直接崩了,无语:

image-20211106150139913

最终没能逃出火绒剑的魔爪:

image-20211106150000934

目前还有一些玄学Bug,原因不明:

最后,由于我在Windows和恶意代码这块还是新手,难免有理解不当的地方,如果文章内容有什么问题欢迎各位师傅指正!

Heavens-Gate-2.0 by sdadas190

天堂之门技术 by Tardis

Rebuild The Heaven's Gate: from 32-bit Hell back to 64-bit Wonderland by Sheng-Hao Ma

通过PEB结构遍历进程模块

PE基础2-导出表-导入表

 
 
 
 
 
 
 
 
void memcpy64(uint64_t dst, uint64_t src, uint64_t sz);
void memcpy64(uint64_t dst, uint64_t src, uint64_t sz);
 
[bits 32]
push 0x33
push _next_x64_code
retf
[bits 32]
push 0x33
push _next_x64_code
retf
 
[bits 64]
push rsi
push rdi
mov rsi, src
mov rdi, dst
mov rcx, sz
rep movsb
pop rsi
pop rdi
[bits 64]
push rsi
push rdi
mov rsi, src
mov rdi, dst
mov rcx, sz
rep movsb
pop rsi
pop rdi
[bits 64]
push 0x23
push _next_x86_code
retfq
[bits 32]
ret
[bits 64]
push 0x23
push _next_x86_code
retfq
[bits 32]
ret
from keystone import *
 
code = '''
push 0x33
push 0x12345678
retf
'''
 
ks = Ks(KS_ARCH_X86, KS_MODE_32)
asm, cnt = ks.asm(code)
print(code)
for b in asm:
    print('0x' + hex(b)[2:].upper(), end=', ')
from keystone import *
 
code = '''
push 0x33
push 0x12345678
retf
'''
 
ks = Ks(KS_ARCH_X86, KS_MODE_32)
asm, cnt = ks.asm(code)
print(code)
for b in asm:
    print('0x' + hex(b)[2:].upper(), end=', ')
push 0x33     
push 0x12345678
retf
 
0x6A, 0x33, 0x68, 0x78, 0x56, 0x34, 0x12, 0xCB,
push 0x33     
push 0x12345678
retf
 
0x6A, 0x33, 0x68, 0x78, 0x56, 0x34, 0x12, 0xCB,
static uint8_t code[] = {
    /*    [bits 32]
        push 0x33
        push _next_x64_code
        retf
    */
    0x6A, 0x33, 0x68, 0x78, 0x56, 0x34, 0x12, 0xCB,
    /*    [bits 64]
        push rsi
        push rdi
        mov rsi, src
        mov rdi, dst
        mov rcx, sz
        rep movsb
        pop rsi
        pop rdi
    */
    0x56, 0x57,
    0x48, 0xBE, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
    0x48, 0xBF, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
    0x48, 0xB9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
    0xF3, 0xA4,
    0x5E, 0x5F,
    /*    [bits 64]
        push 0x23
        push _next_x86_code
        retfq
    */
    0x6A, 0x23, 0x68, 0x78, 0x56, 0x34, 0x12, 0x48, 0xCB,
    /*    [bits 32]
        ret
    */
    0xC3
};
static uint8_t code[] = {
    /*    [bits 32]
        push 0x33
        push _next_x64_code
        retf
    */
    0x6A, 0x33, 0x68, 0x78, 0x56, 0x34, 0x12, 0xCB,
    /*    [bits 64]
        push rsi
        push rdi
        mov rsi, src
        mov rdi, dst
        mov rcx, sz
        rep movsb
        pop rsi
        pop rdi
    */
    0x56, 0x57,
    0x48, 0xBE, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
    0x48, 0xBF, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
    0x48, 0xB9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
    0xF3, 0xA4,
    0x5E, 0x5F,
    /*    [bits 64]
        push 0x23
        push _next_x86_code
        retfq
    */
    0x6A, 0x23, 0x68, 0x78, 0x56, 0x34, 0x12, 0x48, 0xCB,
    /*    [bits 32]
        ret
    */
    0xC3
};
static uint32_t ptr = NULL;
if (!ptr) {
    ptr = (uint32_t)VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    for (int i = 0; i < sizeof(code); i++) ((PBYTE)ptr)[i] = code[i];
}
*(uint32_t*)(ptr + 3) = ptr + 8;
*(uint64_t*)(ptr + 12) = src;
*(uint64_t*)(ptr + 22) = dst;
*(uint64_t*)(ptr + 32) = sz;
*(uint32_t*)(ptr + 47) = ptr + 53;
((void(*)())ptr)();
static uint32_t ptr = NULL;
if (!ptr) {
    ptr = (uint32_t)VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    for (int i = 0; i < sizeof(code); i++) ((PBYTE)ptr)[i] = code[i];
}
*(uint32_t*)(ptr + 3) = ptr + 8;
*(uint64_t*)(ptr + 12) = src;
*(uint64_t*)(ptr + 22) = dst;
*(uint64_t*)(ptr + 32) = sz;
*(uint32_t*)(ptr + 47) = ptr + 53;
((void(*)())ptr)();
void memcpy64(uint64_t dst, uint64_t src, uint64_t sz) {
    static uint8_t code[] = {
        /*    [bits 32]
            push 0x33
            push _next_x64_code
            retf
        */
        0x6A, 0x33, 0x68, 0x78, 0x56, 0x34, 0x12, 0xCB,
        /*    [bits 64]
            push rsi
            push rdi
            mov rsi, src
            mov rdi, dst
            mov rcx, sz
            rep movsb
            pop rsi
            pop rdi
        */
        0x56, 0x57,
        0x48, 0xBE, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
        0x48, 0xBF, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
        0x48, 0xB9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
        0xF3, 0xA4,
        0x5E, 0x5F,
        /*    [bits 64]
            push 0x23
            push _next_x86_code
            retfq
        */
        0x6A, 0x23, 0x68, 0x78, 0x56, 0x34, 0x12, 0x48, 0xCB,
        /*    [bits 32]
            ret
        */
        0xC3
    };
 
    static uint32_t ptr = NULL;
    if (!ptr) {
        ptr = (uint32_t)VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        for (int i = 0; i < sizeof(code); i++) ((PBYTE)ptr)[i] = code[i];
    }
    *(uint32_t*)(ptr + 3) = ptr + 8;
    *(uint64_t*)(ptr + 12) = src;
    *(uint64_t*)(ptr + 22) = dst;
    *(uint64_t*)(ptr + 32) = sz;
    *(uint32_t*)(ptr + 47) = ptr + 53;
    ((void(*)())ptr)();
}
void memcpy64(uint64_t dst, uint64_t src, uint64_t sz) {
    static uint8_t code[] = {
        /*    [bits 32]
            push 0x33
            push _next_x64_code
            retf
        */
        0x6A, 0x33, 0x68, 0x78, 0x56, 0x34, 0x12, 0xCB,
        /*    [bits 64]
            push rsi
            push rdi
            mov rsi, src
            mov rdi, dst
            mov rcx, sz
            rep movsb
            pop rsi
            pop rdi
        */
        0x56, 0x57,
        0x48, 0xBE, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
        0x48, 0xBF, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
        0x48, 0xB9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
        0xF3, 0xA4,
        0x5E, 0x5F,
        /*    [bits 64]
            push 0x23
            push _next_x86_code
            retfq
        */
        0x6A, 0x23, 0x68, 0x78, 0x56, 0x34, 0x12, 0x48, 0xCB,
        /*    [bits 32]
            ret
        */
        0xC3
    };
 
    static uint32_t ptr = NULL;
    if (!ptr) {
        ptr = (uint32_t)VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        for (int i = 0; i < sizeof(code); i++) ((PBYTE)ptr)[i] = code[i];
    }
    *(uint32_t*)(ptr + 3) = ptr + 8;
    *(uint64_t*)(ptr + 12) = src;
    *(uint64_t*)(ptr + 22) = dst;
    *(uint64_t*)(ptr + 32) = sz;
    *(uint32_t*)(ptr + 47) = ptr + 53;
    ((void(*)())ptr)();
}
void GetPEB64(void* peb64);
void GetPEB64(void* peb64);
 
[bits 64]
mov rax, gs:[0x60]
mov [esi], rax
[bits 64]
mov rax, gs:[0x60]
mov [esi], rax
void GetPEB64(void *peb64) {
    static uint8_t code[] = {
        /*    [bits 32]
            mov esi, peb64
            push 0x33
            push _next_x64_code
            retf
        */
        0xBE, 0x78, 0x56, 0x34, 0x12, 0x6A, 0x33, 0x68, 0x78, 0x56, 0x34, 0x12, 0xCB,
        /*    [bits 64]
            mov rax, gs:[0x60]
            mov [esi], rax
        */
        0x65, 0x48, 0xA1, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x48, 0x89, 0x6,
        /*    [bits 64]
            push 0x23
            push _next_x86_code
            retfq
        */
        0x6A, 0x23, 0x68, 0x78, 0x56, 0x34, 0x12, 0x48, 0xCB,
        /*    [bits 32]
            ret
        */
        0xC3
    };
 
    static uint32_t ptr = NULL;
    if (!ptr) {
        ptr = (uint32_t)VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        for (int i = 0; i < sizeof(code); i++) ((PBYTE)ptr)[i] = code[i];
    }
    *(uint32_t*)(ptr + 1) = (uint32_t)peb64;
    *(uint32_t*)(ptr + 8) = ptr + 13;
    *(uint32_t*)(ptr + 31) = ptr + 37;
    ((void(*)())ptr)();
}
void GetPEB64(void *peb64) {
    static uint8_t code[] = {
        /*    [bits 32]
            mov esi, peb64
            push 0x33
            push _next_x64_code
            retf
        */
        0xBE, 0x78, 0x56, 0x34, 0x12, 0x6A, 0x33, 0x68, 0x78, 0x56, 0x34, 0x12, 0xCB,
        /*    [bits 64]
            mov rax, gs:[0x60]
            mov [esi], rax
        */
        0x65, 0x48, 0xA1, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x48, 0x89, 0x6,
        /*    [bits 64]
            push 0x23
            push _next_x86_code
            retfq
        */
        0x6A, 0x23, 0x68, 0x78, 0x56, 0x34, 0x12, 0x48, 0xCB,
        /*    [bits 32]
            ret
        */
        0xC3
    };
 
    static uint32_t ptr = NULL;
    if (!ptr) {
        ptr = (uint32_t)VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        for (int i = 0; i < sizeof(code); i++) ((PBYTE)ptr)[i] = code[i];
    }
    *(uint32_t*)(ptr + 1) = (uint32_t)peb64;
    *(uint32_t*)(ptr + 8) = ptr + 13;
    *(uint32_t*)(ptr + 31) = ptr + 37;
    ((void(*)())ptr)();
}
uint64_t GetModuleHandle64(const WCHAR *moduleName);
uint64_t GetModuleHandle64(const WCHAR *moduleName);
 
 
0:000> dt _PEB
ntdll!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 2, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 3, 1 Bit
   +0x003 IsPackagedProcess : Pos 4, 1 Bit
   +0x003 IsAppContainer   : Pos 5, 1 Bit
   +0x003 IsProtectedProcessLight : Pos 6, 1 Bit
   +0x003 IsLongPathAwareProcess : Pos 7, 1 Bit
   +0x004 Padding0         : [4] UChar
   +0x008 Mutant           : Ptr64 Void
   +0x010 ImageBaseAddress : Ptr64 Void
   +0x018 Ldr              : Ptr64 _PEB_LDR_DATA
0:000> dt _PEB
ntdll!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 2, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 3, 1 Bit
   +0x003 IsPackagedProcess : Pos 4, 1 Bit
   +0x003 IsAppContainer   : Pos 5, 1 Bit
   +0x003 IsProtectedProcessLight : Pos 6, 1 Bit
   +0x003 IsLongPathAwareProcess : Pos 7, 1 Bit
   +0x004 Padding0         : [4] UChar
   +0x008 Mutant           : Ptr64 Void
   +0x010 ImageBaseAddress : Ptr64 Void
   +0x018 Ldr              : Ptr64 _PEB_LDR_DATA
uint64_t peb64;
uint64_t ldrData;
 
GetPEB64(&peb64);
memcpy64((uint64_t)&ldrData, peb64 + 0x18, 8);
uint64_t peb64;
uint64_t ldrData;
 
GetPEB64(&peb64);
memcpy64((uint64_t)&ldrData, peb64 + 0x18, 8);
0:000> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr64 Void
   +0x010 InLoadOrderModuleList : _LIST_ENTRY
   +0x020 InMemoryOrderModuleList : _LIST_ENTRY
   +0x030 InInitializationOrderModuleList : _LIST_ENTRY
   +0x040 EntryInProgress  : Ptr64 Void
   +0x048 ShutdownInProgress : UChar
   +0x050 ShutdownThreadId : Ptr64 Void
0:000> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr64 Void
   +0x010 InLoadOrderModuleList : _LIST_ENTRY
   +0x020 InMemoryOrderModuleList : _LIST_ENTRY
   +0x030 InInitializationOrderModuleList : _LIST_ENTRY
   +0x040 EntryInProgress  : Ptr64 Void
   +0x048 ShutdownInProgress : UChar
   +0x050 ShutdownThreadId : Ptr64 Void
uint64_t head;
uint64_t pNode;
 
head = ldrData + 0x10;
memcpy64((uint64_t)&pNode, head, 8);
uint64_t head;
uint64_t pNode;
 
head = ldrData + 0x10;
memcpy64((uint64_t)&pNode, head, 8);
0:000> dt _LDR_DATA_TABLE_ENTRY
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x010 InMemoryOrderLinks : _LIST_ENTRY
   +0x020 InInitializationOrderLinks : _LIST_ENTRY
   +0x030 DllBase          : Ptr64 Void
   +0x038 EntryPoint       : Ptr64 Void
   +0x040 SizeOfImage      : Uint4B
   +0x048 FullDllName      : _UNICODE_STRING
   +0x058 BaseDllName      : _UNICODE_STRING
 
0:000> dt _UNICODE_STRING
ntdll!_UNICODE_STRING
   +0x000 Length           : Uint2B
   +0x002 MaximumLength    : Uint2B
   +0x008 Buffer           : Ptr64 Wchar
0:000> dt _LDR_DATA_TABLE_ENTRY
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x010 InMemoryOrderLinks : _LIST_ENTRY
   +0x020 InInitializationOrderLinks : _LIST_ENTRY
   +0x030 DllBase          : Ptr64 Void
   +0x038 EntryPoint       : Ptr64 Void
   +0x040 SizeOfImage      : Uint4B
   +0x048 FullDllName      : _UNICODE_STRING
   +0x058 BaseDllName      : _UNICODE_STRING
 
0:000> dt _UNICODE_STRING
ntdll!_UNICODE_STRING
   +0x000 Length           : Uint2B
   +0x002 MaximumLength    : Uint2B
   +0x008 Buffer           : Ptr64 Wchar
 
while (pNode != head) {
    uint64_t buffer;
    memcpy64((uint64_t)(unsigned)(&buffer), pNode + 96, 8);    // tmp = pNode->BaseDllName->Buffer
    if (buffer) {
        WCHAR curModuleName[32] = {0};
        memcpy64((uint64_t)curModuleName, buffer, 60);
        if (!lstrcmpiW(moduleName, curModuleName)) {
            uint64_t base;
            memcpy64((uint64_t)&base, pNode + 48, 8);
            return base;
        }
    }
    memcpy64((uint64_t)&pNode, pNode, 8);    // pNode = pNode->Flink
}
while (pNode != head) {
    uint64_t buffer;
    memcpy64((uint64_t)(unsigned)(&buffer), pNode + 96, 8);    // tmp = pNode->BaseDllName->Buffer
    if (buffer) {
        WCHAR curModuleName[32] = {0};
        memcpy64((uint64_t)curModuleName, buffer, 60);
        if (!lstrcmpiW(moduleName, curModuleName)) {
            uint64_t base;
            memcpy64((uint64_t)&base, pNode + 48, 8);
            return base;
        }
    }
    memcpy64((uint64_t)&pNode, pNode, 8);    // pNode = pNode->Flink
}
uint64_t GetModuleHandle64(const WCHAR *moduleName) {
    uint64_t peb64;
    /*    nt!_PEB_LDR_DATA
       +0x000 Length           : Uint4B
       +0x004 Initialized      : UChar
       +0x008 SsHandle         : Ptr64 Void
       +0x010 InLoadOrderModuleList : _LIST_ENTRY
    */
    uint64_t ldrData;
    /*
        ptr to InLoadOrderModuleList
    */
    uint64_t head;
    /*
        typedef struct _LDR_MODULE {
          +0x000 LIST_ENTRY              InLoadOrderModuleList;
          +0x010 LIST_ENTRY              InMemoryOrderModuleList;
          +0x020 LIST_ENTRY              InInitializationOrderModuleList;
          +0x030 PVOID                   BaseAddress;
          +0x038 PVOID                   EntryPoint;
          +0x040 ULONG                   SizeOfImage;
          +0x048 UNICODE_STRING          FullDllName;
          +0x058 UNICODE_STRING          BaseDllName;
          ...
        } LDR_MODULE, *PLDR_MODULE;
    */
    uint64_t pNode;
    GetPEB64(&peb64);
    memcpy64((uint64_t)&ldrData, peb64 + 0x18, 8);
    head = ldrData + 0x10;
    memcpy64((uint64_t)&pNode, head, 8);
    while (pNode != head) {
        uint64_t buffer;
        memcpy64((uint64_t)(unsigned)(&buffer), pNode + 96, 8);    // tmp = pNode->BaseDllName->Buffer
        if (buffer) {
            WCHAR curModuleName[32] = {0};
            memcpy64((uint64_t)curModuleName, buffer, 60);
            if (!lstrcmpiW(moduleName, curModuleName)) {
                uint64_t base;
                memcpy64((uint64_t)&base, pNode + 48, 8);
                return base;
            }
        }
        memcpy64((uint64_t)&pNode, pNode, 8);    // pNode = pNode->Flink
    }
    return NULL;
}
uint64_t GetModuleHandle64(const WCHAR *moduleName) {
    uint64_t peb64;
    /*    nt!_PEB_LDR_DATA
       +0x000 Length           : Uint4B
       +0x004 Initialized      : UChar
       +0x008 SsHandle         : Ptr64 Void
       +0x010 InLoadOrderModuleList : _LIST_ENTRY
    */
    uint64_t ldrData;
    /*
        ptr to InLoadOrderModuleList
    */
    uint64_t head;
    /*
        typedef struct _LDR_MODULE {
          +0x000 LIST_ENTRY              InLoadOrderModuleList;
          +0x010 LIST_ENTRY              InMemoryOrderModuleList;
          +0x020 LIST_ENTRY              InInitializationOrderModuleList;
          +0x030 PVOID                   BaseAddress;
          +0x038 PVOID                   EntryPoint;
          +0x040 ULONG                   SizeOfImage;
          +0x048 UNICODE_STRING          FullDllName;
          +0x058 UNICODE_STRING          BaseDllName;
          ...
        } LDR_MODULE, *PLDR_MODULE;
    */
    uint64_t pNode;
    GetPEB64(&peb64);
    memcpy64((uint64_t)&ldrData, peb64 + 0x18, 8);
    head = ldrData + 0x10;
    memcpy64((uint64_t)&pNode, head, 8);

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-11-6 15:41 被34r7hm4n编辑 ,原因:
收藏
免费 33
支持
分享
最新回复 (23)
雪    币: 4769
活跃值: (3702)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
谢谢分享 要些时间才可以消化
2021-11-6 15:58
0
雪    币: 3022
活跃值: (6771)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
不错赞一个
2021-11-6 16:33
0
雪    币: 8452
活跃值: (5046)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
4
地球人,我的超人
2021-11-6 18:53
0
雪    币: 14303
活跃值: (10786)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
5
v0id_ 地球人,我的超人
void orzorzorz
2021-11-6 18:55
0
雪    币: 1935
活跃值: (4180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
感谢分享..
2021-11-6 20:28
0
雪    币: 137
活跃值: (1420)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
很好的文章 
2021-11-6 20:57
0
雪    币: 2428
活跃值: (2597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
天堂之门似乎只能应付一些纯应用层检测,但现在大部分都是火绒剑这样的文件过滤驱动或者sysmon/etw之类的
2021-11-8 13:52
0
雪    币: 14303
活跃值: (10786)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
9
dearfuture 天堂之门似乎只能应付一些纯应用层检测,但现在大部分都是火绒剑这样的文件过滤驱动或者sysmon/etw之类的
是的,毕竟是非常老的技术了,学习一下还是可以,但是实战可能没什么作用了
2021-11-8 13:57
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
棒子的ngs就是用的这招
2021-11-24 07:46
0
雪    币: 244
活跃值: (935)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
dearfuture 天堂之门似乎只能应付一些纯应用层检测,但现在大部分都是火绒剑这样的文件过滤驱动或者sysmon/etw之类的
我觉得,现在很多不是专业的二进制样本分析人员,都喜欢用微步、哈勃这些产品扫扫,所以能过微步这些的检测,还是有一定意义的。
2021-11-26 17:50
0
雪    币: 243
活跃值: (458)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
12
精品文章, vmp里面曾见过用到这种技术
2021-11-26 18:03
0
雪    币: 50161
活跃值: (20620)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
13
精彩文章!感谢分享!
2021-11-26 18:34
0
雪    币: 300
活跃值: (2532)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
mark
2021-11-28 13:24
0
雪    币: 569
活跃值: (292)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
mark
2022-4-18 11:45
0
雪    币: 1935
活跃值: (4180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
文章写的很工整
2022-4-19 09:50
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
https://github.com/rwfpl/rewolf-wow64ext
2022-4-26 01:34
0
雪    币: 3772
活跃值: (2402)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
mark
2022-9-26 10:25
0
雪    币: 197
活跃值: (1400)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
mark
2022-10-24 17:27
0
雪    币: 3668
活跃值: (9375)
能力值: ( LV9,RANK:319 )
在线值:
发帖
回帖
粉丝
20

只能生成后双击运行其实导致马子受到的限制比较多

只能生成后双击运行的原因:https://github.com/dosbox-staging/dosbox-staging/issues/1379和https://asprosys.blogspot.com/2009/03/perils-and-pitfalls-of-launching.html这个文中有提到,大概意思是启动时使用的用户没有访问 Window Station 和桌面的权限,并且您的进程正在其中启动,因此它无法初始化,去调整 Window Station 和桌面的安全描述符,以向正在启动进程的用户授予AllAccess权限的话太过麻烦,源码cpp最开始加一句

#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

生成一个不带控制台窗口的 Windows 应用程序

然后可以加载成功

2024-3-21 12:06
2
雪    币: 1568
活跃值: (2154)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
21
骚操作
2024-3-26 14:28
0
雪    币: 3194
活跃值: (5181)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
22
SYJ-Re 只能生成后双击运行其实导致马子受到的限制比较多只能生成后双击运行的原因:https://github.com/dosbox-staging/dosbox-staging/issues/1379和htt ...
貌似这玩意不能直接通过x64call的方式调用cs或者msf生成的shellcode,我也尝试过从kernel32中获取CreateThread,再通过CreateThread创建线程传入shellcode的方式运行 也没有成功 不知道是为什么
2024-4-16 06:41
0
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
收藏学习一下,外行人的我居然看懂,orzorzorzorzorzorz!膜拜学习。其中get到的新知识是WoW64层的那张图。
2024-4-29 11:23
0
雪    币: 4648
活跃值: (5281)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
24

目前市面上这些都无法兼容ARM64等新WOW64平台,我这个可以
https://github.com/sonyps5201314/wow64ext
另外无法加载kerner32.dll,和user32.dll是操作系统设计限制,就算你加载成功并调用了里面的函数,之后程序也会问题,因为程序已经进入一个混乱的状态了(同时拥有了32位和64位的窗口态等),会让系统分不清楚你这个进程到底是64位的还是32位的,比如你显示了窗口后,那些第三方的64位的窗口钩子DLL也会出现在你的进程中,而工作异常。所以这项技术就不是拿来加载64位dll用的,加载dll还是老实通过新建原生进程吧,如rundll32.exe。这个只能实现读写64位内存,注入远程线程级别的操作。

最后于 2024-4-29 21:32 被sonyps编辑 ,原因:
2024-4-29 15:18
1
游客
登录 | 注册 方可回帖
返回
//