天堂之门——WoW64学习

天堂之门——WoW64学习

前述:

之前打比赛做题的时候遇到过天堂之门的题目,当时做完感觉云里雾里的,刚好前段时间遇到队友分享天堂之门,所以打算写篇博客来记录一下

具体的参考链接:天堂之门(WoW64技术)总结及CTF中的分析-CTF对抗-看雪-安全社区|安全招聘|kanxue.com

[原创]天堂之门-调试器的末路-软件逆向-看雪-安全社区|安全招聘|kanxue.com

天堂之门概念:

首先,我们需要了解什么是天堂之门。

其实天堂之门是指在计算机体系结构中实现从一种模式切换到另一种模式。例如,在早期的操作系统中,从实模式(real mode)切换到保护模式(protected mode),或者在某些嵌入式系统中从用户模式切换到内核模式。这种切换通常涉及到处理器状态的改变,例如段寄存器(Segment Registers)的修改等。

而现在我们涉及到的天堂之门往往指从32位模式到64位模式,比如32位的程序在64位操作系统上运行。

会被叫作天堂之门的原因可能是这种技术现了从一种模式切换到另外一种模式,比如从一个较低的位数模式(32位)到较高位数模式(64位)的跨越,仿佛是从一个较旧的环境进入到一个更为先进的环境,故称其为“天堂之门”。

然后接下来谈谈什么是WOW64。

WoW64概念:

WoW64是“Windows(32) on Windows 64”的简称,在《逆向工程核心原理》第36章中对于WOW64的定义是:一种在64位操作系统中支持与运行现有的32位应用程序的机制。

64位Windows中,32位应用程序与65位应用程序都可以正常运行。这是为什么呢?

我们需要了解其原理。

原理:

在64位Windows中,32位应用程序会加载kernel64.dll(64位)与ntdll.dll(64位),而32位程序则会加载kernel32.dll以及ntdll.dll(32位)。

而WoW64起到的作用则是在中间将ntdll.dll(32位)的请求(API调用)重定向到ntdll.dll(64位)。

对于重定向这一个板块,在《windows内核编程》的第五章中,也有涉及到对于在64位操作系统中运行32位程序的原理的提及。

64位跳转32位:
1
jmp far 33:地址

或者上面的指令的另一种实现方式

1
2
3
4
6A 33                    ;	push 0x33 栈中压入0x33,作为CS寄存器的值
E8 00 00 00 00 ; call $+5 下一条指令的地址入栈,并继续执行下一条add指令
83 04 24 05 ; add dword [esp], 5 栈顶的返回地址加5,指向retf的下一条指令
CB ; retf 返回到下一条指令继续执行,同时会pop ip和pop cs

这段指令结合机器码看会方便很多

这个地方其实就是直接先把0x33压入栈中,然后执行call $5,将add指令的地址(刚好是当前的地址加5)放入栈中,然后这个时候执行add dword [esp], 5,刚好sp到retf下一条指令处,这个时候再执行retf,相当于pop 了retf下一条指令的值到sp,再pop 0x33的值到cs寄存器

然后继续正常执行下面的指令,这样就实现了从64位程序到执行64位程序的切换

32位跳转64位:
1
2
3
4
5

E8 00 00 00 00 ; call $+5 ; 将下一条指令的地址(即当前 RIP+5)压入栈中,并继续执行下一条指令
C7 44 24 04 23 00 00 00 ; mov dword [rsp + 4], 0x23 ; 将栈中返回地址的高 32 位修改为 0x23,指示程序返回时切换到 32 位模式
83 04 24 0D ; add dword [rsp], 0xD ; 修改栈顶的返回地址,将其增加 0xD,指向 `retf` 之后的下一条指令
CB ; retf ; 通过 `retf`,程序跳转回 32 位模式,并继续执行跳转后的代码

例题讲解:

例题1:ctfshow 月饼杯2 Easytea

题目下载地址:ctf.show

接着我们就直接从题目入手,打开main函数看到爆红,有点像花指令

image-20240927162526895

直接看汇编

image-20240927163052843

在0040124E处我们可以看到

1
EA    58 12 40 00     33 00                    jmp     far ptr loc_401587+1

我们需要了解一下jmp和jmp far的区别

jmp far会在jmp的基础上(修改ip的值),多一个修改cs的值的操作,修改的值就是在far后的数字,所以在上方的操作码中,在jmp一个地址(jmp 0x00401258)的基础上,将CS寄存器的寄存器的值修改为0x33

1
2
3
32位CS寄存器的值:0x23

64位CS寄存器的值:0x33

结合上面所提及到的32位跳转到64位的指令格式,所以这个地方就是跳转到0x401258处

可以找到下图中的位置

image-20240927201130996

这个地方和上面我们讲到的从32位到64位是刚好符合的

在0x401255处我们可以看到call了[ebp+var_28]

然后往上翻可以看到[ebp+var_28]来自unk_427A50

image-20240928210324763

所以我们选择在unk_427A50创建函数,得到

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
int __cdecl sub_427A50(int a1, int a2, int a3, int *a4)
{
int v4; // eax
__int16 v5; // ax
int v9; // ecx
int v11; // ecx
int v12; // edx
int v14; // edx
int v16[107]; // [esp+0h] [ebp-20h] BYREF

memset(v16, 0xCCu, 0x1A8u);
v16[10] = 102;
v16[11] = 108;
v16[12] = 97;
v16[13] = 103;
v16[14] = 105;
v16[15] = 115;
v16[16] = 109;
v16[17] = 101;
v4 = a4[5];
v16[25] = v4;
v16[33] = 0;
for ( v16[41] = 0; v16[41] < 32; v4 = ++v16[41] )
{
v5 = LOWORD(v16[33]) + 4421;
v16[33] -= 2008542907;
for ( v16[49] = 0; v16[49] < 7; ++v16[49] )
{
_AX = v5 - 1;
__asm { arpl word ptr [ebp+18Ch+var_E8], ax }
_ECX = v16[49] + 1;
__asm { arpl cx, cx }
_EDX = v16[49] + 1;
__asm { arpl dx, dx }
v9 = (a4[_EDX] >> 5) ^ (16 * a4[_ECX] + 1);
_EDX = v16[49] + 1;
__asm { arpl dx, dx }
v11 = a4[_EDX] + v9 + 1 + 1;
v12 = v16[(v16[33] & 7) + 10] + v16[33];
_ECX = a4[v12 - 1] + (v12 ^ v11);
__asm { arpl word ptr [ebp+18Ch+var_E8], cx }
a4[_ECX] = _ECX - 2;
v5 = LOWORD(v16[49]) + 1;
}
v14 = v16[(v16[33] & 7) + 10] + v16[33];
a4[7] = *(a4 + v14 - 1) + (v14 ^ (*a4 + ((*a4 >> 5) ^ (16 * *a4 + 1)) + 1 + 1)) - 2;
}
return v4 - 2;
}

所以应该是把这些数据去创建一个64位的程序然后再进行反编译的

然后我们继续往下看,call后这个地方下来刚好是执行0x40126D处

我们在0x40126D创建函数

image-20240928205651192

在这里dword_427A30处我们可以获取接下来需要解密的数据

image-20240928205818317

所以我们选择自己随便写一个C的程序

然后通过

1
gcc -o out test.c

生成对应的64位程序

然后再在这个程序的data段使用Idapython进行将数据源给patch上去

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
dump_data = [0x48, 0x89, 0x4C, 0x24, 0x08, 0x55, 0x57, 0x48, 0x81, 0xEC,
0xA8, 0x01, 0x00, 0x00, 0x48, 0x8D, 0x6C, 0x24, 0x20, 0x48,
0x8B, 0xFC, 0xB9, 0x6A, 0x00, 0x00, 0x00, 0xB8, 0xCC, 0xCC,
0xCC, 0xCC, 0xF3, 0xAB, 0x48, 0x8B, 0x8C, 0x24, 0xC8, 0x01,
0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0xC7, 0x45, 0x08, 0x66, 0x00, 0x00,
0x00, 0xC7, 0x45, 0x0C, 0x6C, 0x00, 0x00, 0x00, 0xC7, 0x45,
0x10, 0x61, 0x00, 0x00, 0x00, 0xC7, 0x45, 0x14, 0x67, 0x00,
0x00, 0x00, 0xC7, 0x45, 0x18, 0x69, 0x00, 0x00, 0x00, 0xC7,
0x45, 0x1C, 0x73, 0x00, 0x00, 0x00, 0xC7, 0x45, 0x20, 0x6D,
0x00, 0x00, 0x00, 0xC7, 0x45, 0x24, 0x65, 0x00, 0x00, 0x00,
0xB8, 0x04, 0x00, 0x00, 0x00, 0x48, 0x6B, 0xC0, 0x07, 0x48,
0x8B, 0x8D, 0xA0, 0x01, 0x00, 0x00, 0x8B, 0x04, 0x01, 0x89,
0x45, 0x44, 0xC7, 0x45, 0x64, 0x00, 0x00, 0x00, 0x00, 0xC7,
0x85, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB,
0x0E, 0x8B, 0x85, 0x84, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x89,
0x85, 0x84, 0x00, 0x00, 0x00, 0x83, 0xBD, 0x84, 0x00, 0x00,
0x00, 0x20, 0x0F, 0x8D, 0x44, 0x01, 0x00, 0x00, 0x8B, 0x45,
0x64, 0x05, 0x45, 0x11, 0x48, 0x88, 0x89, 0x45, 0x64, 0xC7,
0x85, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB,
0x0E, 0x8B, 0x85, 0xA4, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x89,
0x85, 0xA4, 0x00, 0x00, 0x00, 0x83, 0xBD, 0xA4, 0x00, 0x00,
0x00, 0x07, 0x0F, 0x8D, 0x8A, 0x00, 0x00, 0x00, 0x48, 0x63,
0x85, 0xA4, 0x00, 0x00, 0x00, 0x8B, 0x8D, 0xA4, 0x00, 0x00,
0x00, 0xFF, 0xC1, 0x48, 0x63, 0xC9, 0x48, 0x8B, 0x95, 0xA0,
0x01, 0x00, 0x00, 0x8B, 0x0C, 0x8A, 0xC1, 0xE1, 0x04, 0x8B,
0x95, 0xA4, 0x00, 0x00, 0x00, 0xFF, 0xC2, 0x48, 0x63, 0xD2,
0x4C, 0x8B, 0x85, 0xA0, 0x01, 0x00, 0x00, 0x41, 0x8B, 0x14,
0x90, 0xC1, 0xFA, 0x05, 0x33, 0xCA, 0x8B, 0x95, 0xA4, 0x00,
0x00, 0x00, 0xFF, 0xC2, 0x48, 0x63, 0xD2, 0x4C, 0x8B, 0x85,
0xA0, 0x01, 0x00, 0x00, 0x41, 0x03, 0x0C, 0x90, 0x8B, 0x55,
0x64, 0x83, 0xE2, 0x07, 0x8B, 0xD2, 0x8B, 0x54, 0x95, 0x08,
0x44, 0x8B, 0x45, 0x64, 0x44, 0x03, 0xC2, 0x41, 0x8B, 0xD0,
0x33, 0xCA, 0x48, 0x8B, 0x95, 0xA0, 0x01, 0x00, 0x00, 0x03,
0x0C, 0x82, 0x8B, 0xC1, 0x48, 0x63, 0x8D, 0xA4, 0x00, 0x00,
0x00, 0x48, 0x8B, 0x95, 0xA0, 0x01, 0x00, 0x00, 0x89, 0x04,
0x8A, 0xE9, 0x5B, 0xFF, 0xFF, 0xFF, 0xB8, 0x04, 0x00, 0x00,
0x00, 0x48, 0x6B, 0xC0, 0x07, 0xB9, 0x04, 0x00, 0x00, 0x00,
0x48, 0x6B, 0xC9, 0x00, 0x48, 0x8B, 0x95, 0xA0, 0x01, 0x00,
0x00, 0x8B, 0x0C, 0x0A, 0xC1, 0xE1, 0x04, 0xBA, 0x04, 0x00,
0x00, 0x00, 0x48, 0x6B, 0xD2, 0x00, 0x4C, 0x8B, 0x85, 0xA0,
0x01, 0x00, 0x00, 0x41, 0x8B, 0x14, 0x10, 0xC1, 0xFA, 0x05,
0x33, 0xCA, 0xBA, 0x04, 0x00, 0x00, 0x00, 0x48, 0x6B, 0xD2,
0x00, 0x4C, 0x8B, 0x85, 0xA0, 0x01, 0x00, 0x00, 0x41, 0x03,
0x0C, 0x10, 0x8B, 0x55, 0x64, 0x83, 0xE2, 0x07, 0x8B, 0xD2,
0x8B, 0x54, 0x95, 0x08, 0x44, 0x8B, 0x45, 0x64, 0x44, 0x03,
0xC2, 0x41, 0x8B, 0xD0, 0x33, 0xCA, 0x48, 0x8B, 0x95, 0xA0,
0x01, 0x00, 0x00, 0x03, 0x0C, 0x02, 0x8B, 0xC1, 0xB9, 0x04,
0x00, 0x00, 0x00, 0x48, 0x6B, 0xC9, 0x07, 0x48, 0x8B, 0x95,
0xA0, 0x01, 0x00, 0x00, 0x89, 0x04, 0x0A, 0xE9, 0xA1, 0xFE,
0xFF, 0xFF, 0x48, 0x8D, 0x4D, 0xE0, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x48, 0x8D,
0xA5, 0x88, 0x01, 0x00, 0x00, 0x5F, 0x5D, 0xC3]
start_addr = 0x1400060C0
end_addr = start_addr + len(dump_data)
for i in range(start_addr, end_addr):
idc.patch_byte(i, dump_data[i - start_addr])

最后复原出来的加密逻辑:

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
__int64 __fastcall _data_end__(int *a1)
{
char *v1; // rdi
__int64 i; // rcx
__int64 result; // rax
char v4; // [rsp+0h] [rbp-20h] BYREF
int v5[23]; // [rsp+28h] [rbp+8h]
int v6; // [rsp+84h] [rbp+64h]
int j; // [rsp+A4h] [rbp+84h]
int k; // [rsp+C4h] [rbp+A4h]

v1 = &v4;
for ( i = 106i64; i; --i )
{
*(_DWORD *)v1 = -858993460;
v1 += 4;
}
v5[0] = 102;
v5[1] = 108;
v5[2] = 97;
v5[3] = 103;
v5[4] = 105;
v5[5] = 115;
v5[6] = 109;
v5[7] = 101;
result = (unsigned int)a1[7];
v5[15] = a1[7];
v6 = 0;
for ( j = 0; j < 32; ++j )
{
v6 -= 2008542907;
for ( k = 0; k < 7; ++k )
a1[k] += (v5[v6 & 7] + v6) ^ (a1[k + 1] + ((a1[k + 1] >> 5) ^ (16 * a1[k + 1])));
a1[7] += (v5[v6 & 7] + v6) ^ (*a1 + ((*a1 >> 5) ^ (16 * *a1)));
result = (unsigned int)(j + 1);
}
return result;
}

解密脚本:

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
#include<stdio.h>
int main()
{
int v5[8];
v5[0] = 102;
v5[1] = 108;
v5[2] = 97;
v5[3] = 103;
v5[4] = 105;
v5[5] = 115;
v5[6] = 109;
v5[7] = 101;
int a1[8] = {
0xB5ABA743, 0x4C5B3EE0, 0xB70AEB14, 0x6946BC13, 0x906089C4, 0x5B9F98F0, 0x0964B652, 0x78920976
};
int sum=-32*0x77B7EEBB;
int i,k;
for(i=0;i<32;i++)
{

a1[7] -= (v5[sum & 7] + sum) ^ (*a1 + ((*a1 >> 5) ^ (16 * *a1)));

for(k=6;k>=0;k--)
{
a1[k] -= (v5[sum & 7] + sum) ^ (a1[k + 1] + ((a1[k + 1] >> 5) ^ (16 * a1[k + 1])));
}
sum += 0x77B7EEBB;

}
int j;
printf("%s",a1);

return 0;
}

最后获得flag

1
Tea_12345_12345_yes_flag_is_easy

天堂之门——WoW64学习
http://example.com/2024/09/28/天堂之门学习/
作者
John Doe
发布于
2024年9月28日
许可协议