天堂之门

天堂之门(Heaven’s Gate)是一种在32为程序中执行64为函数、调用64为windows API的技术。可以用来防止动态调试、绕过对32为API的hook等。

原理

32位程序执行流程

64位的windows会为32位程序创建一个模拟环境——WoW64。这使得32为程序可以在64位机器上运行。当32位程序要调用WIN32 API时,以ntdll.dll中的ZwOpenProcess函数为例,步骤如下:

  • 进程调用32位ntdll.dll中的目标API。
  • 32位ntdll.dll调用wow64cpu.dll中的X86SwitchTo64BitMode函数。将进程从32位转为64位。
  • wow64.dll将系统调用由32位转化为64位。
  • 调用64位ntdll.dll中的目标API。
  • 切换到内核进行系统调用。

天堂之门执行流程

手动将程序从32位切换到64位,绕过WoW64机制。

  • cs段寄存器设置为0x33,转换为64位。
  • gs:0x60读取64位PEB。
  • 从64位PEB中定位64位ntdll.dll基址。
  • 遍历64位ntdll.dll导出表,读取目标API的函数地址。
  • 构造64位函数调用。

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

  • 遍历64位ntdll.dll导出表,读取LdrLoadDll函数地址。
  • 调用LdrLoadDll("kernel32.dll")加载64位kernel32.dll
  • 从64位的kernel32.dll中读取GetProcAddress等函数,获取CreateFile函数地址。
  • 调用CreateFile函数。

代码实现

环境准备

使用Visual Studio 2022,x86 release模式,禁用优化,多线程(/MT)。
总计实现以下几个函数:

  • memcpy64:在64位地址之间拷贝数据
  • GetPEB64:获取64位的PEB地址
  • GetModuleHandle64:获取64位的模块基址
  • GetProcAddress64:获取64位模块中的函数地址
  • X64Call:调用64位函数
  • MakeUTFStr:构造UNICODE_STRING结构体
  • GetKernel32:加载64位kernel32.dll及其依赖kernelbase.dll
  • LoadLibrary64:在加载kernel32.dll后用于加载user32.dll

需要借助这个网站实现汇编和字节码的转换。

memcpy64

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void memcpy64(uint64_t dst, uint64_t src, uint64_t sz)
{
uint8_t code[] = {
/*
[bits 32]
push 0x33
push _next_x64_code
retf 返回至栈顶地址,同时将栈中第二个数传给cs段寄存器
*/
0x6A, 0x33,
0x68, 0x78, 0x56, 0x34, 0x12,
0xCB,
/*
[bits 64]
mov rsi, src
mov rdi, dst
mov rcx, sz
rep movsb
*/
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,
/*
[bits 64]
push 0x23
push _next_x86_code
retfq
*/
0x6A, 0x23,
0x68, 0x78, 0x56, 0x34, 0x12,
0x48, 0xCB,
/*
[bits 32]
ret
*/
0xC3
};
uint32_t ptr = NULL;
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 + 10) = src;
*(uint64_t*)(ptr + 20) = dst;
*(uint64_t*)(ptr + 30) = sz;
*(uint32_t*)(ptr + 43) = ptr + 49;
((void(*)())ptr)();
}

GetPEB64

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
36
37
38
39
40
41
42
43
44
45
void GetPEB64(void* peb64)
{
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] 获取PEB
mov [esi], rax 传给peb64
*/
0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00,
0x67, 0x48, 0x89, 0x06,
/*
[bits 64]
push 0x23
push _next_x86_code
retfq
*/
0x6A, 0x23,
0x68, 0x78, 0x56, 0x34, 0x12,
0x48, 0xCB,
/*
[bits 32]
ret
*/
0xC3
};
uint32_t ptr = NULL;
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 + 29) = ptr + 35;
((void(*)())ptr)();
}

GetModuleHandle64

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
uint64_t GetModuleHandle64(const WCHAR *moduleName)
{
uint64_t peb64;
uint64_t Ldr;
uint64_t head;
uint64_t pNode;
GetPEB64(&peb64);
memcpy64((uint64_t)&Ldr, peb64 + 0x18, 8); //PEB+0x18处为Ldr
head = Ldr + 0x10; //Ldr+0x10处为InLoadOrderModuleList
memcpy64((uint64_t)&pNode, head, 8);

while (pNode != head)
{
uint64_t buffer;
memcpy64((uint64_t)(&buffer), pNode + 0x58 + 0x8, 8); //pNode->BaseDllName->Buffer
if (buffer)
{
WCHAR curModuleName[32] = L"";
memcpy64((uint64_t)curModuleName, buffer, 60);
if (!lstrcmpiW(moduleName, curModuleName))
{
uint64_t base;
memcpy64((uint64_t)&base, pNode + 0x30, 8); //pNode->DllBase
return base;
}
}
memcpy64((uint64_t)&pNode, pNode, 8);
}
return 0;
}

MyGetProcAddress

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
uint64_t MyGetProcAddress(uint64_t hModule, const char* func)
{
IMAGE_DOS_HEADER dos;
memcpy64((uint64_t)&dos, hModule, sizeof(dos));
IMAGE_NT_HEADERS64 nt;
memcpy64((uint64_t)&nt, hModule + dos.e_lfanew, sizeof(nt));
IMAGE_EXPORT_DIRECTORY expo;
memcpy64((uint64_t)&expo, hModule + nt.OptionalHeader.DataDirectory[0].VirtualAddress, sizeof(expo));

for (uint64_t i = 0; i < expo.NumberOfNames; i++) {
DWORD pName = 0;
memcpy64((uint64_t)&pName, hModule + expo.AddressOfNames + (4 * i), 4);
char name[64] = "";
memcpy64((uint64_t)name, hModule + pName, 64);
if (!lstrcmpA(name, func)) {
WORD ord;
memcpy64((uint64_t)&ord, hModule + expo.AddressOfNameOrdinals + (2 * i), 2);
uint32_t addr;
memcpy64((uint64_t)&addr, hModule + expo.AddressOfFunctions + (4 * ord), 4);
return hModule + addr;
}
}
return 0;
}

X64Call

64位中WINAPI调用的传参方式:

  • 前四个参数从左往右依次存放到rcx, rdx, r8, r9寄存器中
  • 后面的参数从右往左依次入栈
  • rsp与最后一个参数之间直接需要保留大小为0x20字节的空间,被调函数可能会使用
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
uint64_t X64Call(uint64_t proc, uint32_t argc, ...)
{
uint64_t* args = (uint64_t*)(&argc + 1);
uint64_t ret = 0;
uint8_t code[] = {
/*
[bits 32]
push ebx
mov ebx, esp
and esp, 0xFFFFFFF8 64位汇编的栈要求与8对齐

push 0x33
push _next_x64_code
retf
*/
0x53,
0x89, 0xE3,
0x83, 0xE4, 0xF8,
0x6A, 0x33,
0x68, 0x78, 0x56, 0x34, 0x12,
0xCB,
/*
[bits 64]
mov rsi, args
mov rcx, [rsi]
mov rdx, [rsi+8]
mov r8, [rsi+16]
mov r9, [rsi+24]

mov rax, argc
start:
cmp rax, 4
jle end

mov rdi, [rsi+8*rax-8]
push rdi
dec rax
jmp start
end:

mov rax, proc
sub rsp, 32
call rax
mov rdi, &ret
mov [rdi], rax
*/
0x48, 0xBE, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x8B, 0x0E,
0x48, 0x8B, 0x56, 0x08,
0x4C, 0x8B, 0x46, 0x10,
0x4C, 0x8B, 0x4E, 0x18,

0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x83, 0xF8, 0x04,
0x7E, 0x0B,
0x48, 0x8B, 0x7C, 0xC6, 0xF8,
0x57,
0x48, 0xFF, 0xC8,
0xEB, 0xEF,

0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x83, 0xEC, 0x20,
0xFF, 0xD0,
0x48, 0xBF, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x89, 0x07,
/*
[bits 64]
push 0x23
push _next_x86_code
retfq
[bits 32]
mov esp, ebx
pop ebx
ret
*/
0x6A, 0x23,
0x68, 0x78, 0x56, 0x34, 0x12,
0x48, 0xCB,
0x89, 0xDC,
0x5B,
0xC3
};
uint32_t ptr = NULL;
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 + 9) = ptr + 14;
*(uint64_t*)(ptr + 16) = (uint64_t)args;
*(uint64_t*)(ptr + 41) = (uint64_t)argc;
*(uint64_t*)(ptr + 68) = proc;
*(uint64_t*)(ptr + 84) = (uint64_t)&ret;
*(uint32_t*)(ptr + 98) = ptr + 104;
((void(*)())ptr)();
return ret;
}

MakeUTFStr

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

1
2
3
4
5
6
7
8
9
10
11
12
char* MakeUTFStr(const char* str)
{
uint32_t len = lstrlenA(str);
char* out = (char*)malloc(16 + (len + 1) * 2);
*(uint16_t*)(out) = (uint16_t)(len * 2); //Length
*(uint16_t*)(out + 2) = (uint16_t)((len + 1) * 2); //Max Length

uint16_t* outstr = (uint16_t*)(out + 16);
for (uint32_t i = 0; i <= len; i++) outstr[i] = str[i];
*(uint64_t*)(out + 8) = (uint64_t)(out + 16);
return out;
}

GetKernel32

加载kernel32.dllkernelbase.dll

1
2
3
4
5
6
7
8
9
10
uint64_t GetKernel32()
{
uint64_t kernel32 = 0;

uint64_t ntdll = GetModuleHandle64(L"ntdll.dll");
uint64_t LdrLoadDll = MyGetProcAddress(ntdll, "LdrLoadDll");
char* str = MakeUTFStr("kernel32.dll");
X64Call(LdrLoadDll, 4, (uint64_t)0, (uint64_t)0, (uint64_t)str, (uint64_t)(&kernel32));
return kernel32;
}

GetProcAddress64

1
2
3
4
5
6
7
uint64_t GetProcAddress64(uint64_t module, const char* func);
{
uint64_t K32GetProcAddress = 0;
K32GetProcAddress = MyGetProcAddress(GetKernel32(), "GetProcAddress");

return X64Call(K32GetProcAddress, 2, module, (uint64_t)func);
}

LoadLibrary64

1
2
3
4
5
6
7
uint64_t LoadLibrary64(const char* name)
{
uint64_t LoadLibraryA = 0;
LoadLibraryA = GetProcAddress64(GetKernel32(), "LoadLibraryA");

return X64Call(LoadLibraryA, 1, (uint64_t)name);
}

测试用例

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#include <Windows.h>
#include <iostream>

uint64_t X64Call(uint64_t proc, uint32_t argc, ...) {
uint64_t* args = (uint64_t*)(&argc + 1);
uint64_t ret = 0;
uint8_t code[] = {
/*
[bits 32]
push ebx
mov ebx, esp
and esp, 0xFFFFFFF8 64位汇编的栈要求与8对齐

push 0x33
push _next_x64_code
retf
*/
0x53,
0x89, 0xE3,
0x83, 0xE4, 0xF8,
0x6A, 0x33,
0x68, 0x78, 0x56, 0x34, 0x12,
0xCB,
/*
[bits 64]
mov rsi, args
mov rcx, [rsi]
mov rdx, [rsi+8]
mov r8, [rsi+16]
mov r9, [rsi+24]

mov rax, argc
start:
cmp rax, 4
jle end

mov rdi, [rsi+8*rax-8]
push rdi
dec rax
jmp start
end:

mov rax, proc
sub rsp, 32
call rax
mov rdi, &ret
mov [rdi], rax
*/
0x48, 0xBE, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x8B, 0x0E,
0x48, 0x8B, 0x56, 0x08,
0x4C, 0x8B, 0x46, 0x10,
0x4C, 0x8B, 0x4E, 0x18,

0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x83, 0xF8, 0x04,
0x7E, 0x0B,
0x48, 0x8B, 0x7C, 0xC6, 0xF8,
0x57,
0x48, 0xFF, 0xC8,
0xEB, 0xEF,

0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x83, 0xEC, 0x20,
0xFF, 0xD0,
0x48, 0xBF, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0x89, 0x07,
/*
[bits 64]
push 0x23
push _next_x86_code
retfq
[bits 32]
mov esp, ebx
pop ebx
ret
*/
0x6A, 0x23,
0x68, 0x78, 0x56, 0x34, 0x12,
0x48, 0xCB,
0x89, 0xDC,
0x5B,
0xC3
};
uint32_t ptr = NULL;
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 + 9) = ptr + 14;
*(uint64_t*)(ptr + 16) = (uint64_t)args;
*(uint64_t*)(ptr + 41) = (uint64_t)argc;
*(uint64_t*)(ptr + 68) = proc;
*(uint64_t*)(ptr + 84) = (uint64_t)&ret;
*(uint32_t*)(ptr + 98) = ptr + 104;
((void(*)())ptr)();
return ret;
}

void GetPEB64(void* peb64) {
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, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00,
0x67, 0x48, 0x89, 0x06,
/*
[bits 64]
push 0x23
push _next_x86_code
retfq
*/
0x6A, 0x23,
0x68, 0x78, 0x56, 0x34, 0x12,
0x48, 0xCB,
/*
[bits 32]
ret
*/
0xC3
};
uint32_t ptr = NULL;
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 + 29) = ptr + 35;
((void(*)())ptr)();
}

void memcpy64(uint64_t dst, uint64_t src, uint64_t sz) {
uint8_t code[] = {
/*
[bits 32]
push 0x33
push _next_x64_code
retf
*/
0x6A, 0x33,
0x68, 0x78, 0x56, 0x34, 0x12,
0xCB,
/*
[bits 64]
mov rsi, src
mov rdi, dst
mov rcx, sz
rep movsb
*/
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,
/*
[bits 64]
push 0x23
push _next_x86_code
retfq
*/
0x6A, 0x23,
0x68, 0x78, 0x56, 0x34, 0x12,
0x48, 0xCB,
/*
[bits 32]
ret
*/
0xC3
};
uint32_t ptr = NULL;
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 + 10) = src;
*(uint64_t*)(ptr + 20) = dst;
*(uint64_t*)(ptr + 30) = sz;
*(uint32_t*)(ptr + 43) = ptr + 49;
((void(*)())ptr)();
}

uint64_t GetModuleHandle64(const WCHAR* moduleName) {
uint64_t peb64;
uint64_t Ldr;
uint64_t head;
uint64_t pNode;
GetPEB64(&peb64);
memcpy64((uint64_t)&Ldr, peb64 + 0x18, 8); //PEB+0x18处为Ldr
head = Ldr + 0x10; //Ldr+0x10处为InLoadOrderModuleList
memcpy64((uint64_t)&pNode, head, 8);

while (pNode != head)
{
uint64_t buffer;
memcpy64((uint64_t)(&buffer), pNode + 0x58 + 0x8, 8); //pNode->BaseDllName->Buffer
if (buffer)
{
WCHAR curModuleName[32] = L"";
memcpy64((uint64_t)curModuleName, buffer, 60);
if (!lstrcmpiW(moduleName, curModuleName))
{
uint64_t base;
memcpy64((uint64_t)&base, pNode + 0x30, 8); //pNode->DllBase
return base;
}
}
memcpy64((uint64_t)&pNode, pNode, 8);
}
return 0;
}

uint64_t MyGetProcAddress(uint64_t hModule, const char* func) {
IMAGE_DOS_HEADER dos;
memcpy64((uint64_t)&dos, hModule, sizeof(dos));
IMAGE_NT_HEADERS64 nt;
memcpy64((uint64_t)&nt, hModule + dos.e_lfanew, sizeof(nt));
IMAGE_EXPORT_DIRECTORY expo;
memcpy64((uint64_t)&expo, hModule + nt.OptionalHeader.DataDirectory[0].VirtualAddress, sizeof(expo));

for (uint64_t i = 0; i < expo.NumberOfNames; i++) {
DWORD pName = 0;
memcpy64((uint64_t)&pName, hModule + expo.AddressOfNames + (4 * i), 4);
char name[64] = "";
memcpy64((uint64_t)name, hModule + pName, 64);
if (!lstrcmpA(name, func)) {
WORD ord;
memcpy64((uint64_t)&ord, hModule + expo.AddressOfNameOrdinals + (2 * i), 2);
uint32_t addr;
memcpy64((uint64_t)&addr, hModule + expo.AddressOfFunctions + (4 * ord), 4);
return hModule + addr;
}
}
return 0;
}

char* MakeUTFStr(const char* str) {
uint32_t len = lstrlenA(str);
char* out = (char*)malloc(16 + (len + 1) * 2);
*(uint16_t*)(out) = (uint16_t)(len * 2); //Length
*(uint16_t*)(out + 2) = (uint16_t)((len + 1) * 2); //Max Length

uint16_t* outstr = (uint16_t*)(out + 16);
for (uint32_t i = 0; i <= len; i++) outstr[i] = str[i];
*(uint64_t*)(out + 8) = (uint64_t)(out + 16);
return out;
}

uint64_t GetKernel32() {
uint64_t kernel32 = 0;

uint64_t ntdll = GetModuleHandle64(L"ntdll.dll");
uint64_t LdrLoadDll = MyGetProcAddress(ntdll, "LdrLoadDll");
char* str = MakeUTFStr("kernel32.dll");
X64Call(LdrLoadDll, 4, (uint64_t)0, (uint64_t)0, (uint64_t)str, (uint64_t)(&kernel32));
return kernel32;
}

uint64_t GetProcAddress64(uint64_t module, const char* func) {
uint64_t K32GetProcAddress = 0;
K32GetProcAddress = MyGetProcAddress(GetKernel32(), "GetProcAddress");

return X64Call(K32GetProcAddress, 2, module, (uint64_t)func);
}

uint64_t LoadLibrary64(const char* name)
{
uint64_t LoadLibraryA = 0;
LoadLibraryA = GetProcAddress64(GetKernel32(), "LoadLibraryA");

return X64Call(LoadLibraryA, 1, (uint64_t)name);
}

int main()
{
char path[MAX_PATH] = "C:\\ProgramData\\test.txt";
uint64_t hFile;
uint64_t kernel32 = GetKernel32();
uint64_t CreateFile64 = GetProcAddress64(kernel32, "CreateFileA");
uint64_t CloseHandle64 = GetProcAddress64(kernel32, "CloseHandle");

printf("%s\n", path);
hFile = X64Call(CreateFile64, 7, (uint64_t)path, (uint64_t)GENERIC_WRITE, (uint64_t)NULL, (uint64_t)NULL, (uint64_t)CREATE_NEW, (uint64_t)FILE_ATTRIBUTE_NORMAL, (uint64_t)NULL);
X64Call(CloseHandle64, 1, hFile);

system("PAUSE");
return 0;
}

参考

天堂之门
Rebuild The Heaven’s Gate: from 32-bit Hell back to 64-bit Wonderland
Knockin’ on Heaven’s Gate – Dynamic Processor Mode Switching