SSCTF2017 Re450分析

这是SSCTF2017 Re450魂斗罗的题。
当时做题时,由于后面死扣花指令,时间全浪费在了静态分析上,误入歧途。后面就放了,今天又想起来,里面的花指令不错,决定再看一遍。

初步了解

给的是一个游戏,简单看了下目录内的文件,除了游戏的动态库、声音文件、地图模型文件外,还有个flg的文件,这个文件名就有点可疑。

运行了下游戏,是魂斗罗游戏,开始出个弹窗,显示y must pay for flag,qq:12345678。确认后进入游戏,并弹出另一个弹窗,标题为Cheating Infinite Lives,窗口内容为do not close it!,顺手关了弹窗,直接游戏崩了。将flg文件改名,再次运行游戏,结果一致。
根据提示及运行情况,可以确定:

  • flg文件为程序加密或编码后的文件,为flag所在
  • 第二个弹窗关闭后游戏崩溃,是题人提醒我们的研究重点在外挂上,与提示一致

动态跟踪过程

OD载入后,先去掉反调试。在004274E6处调用的ZwSetInformationThread需NOP掉,注意栈平衡。
然后直接字串搜索do not close it!,定位到函数004273c0,下断,运行,关掉两个弹窗后,来到指定位置。

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
004273C0 /> \55 push ebp
004273C1 |. 8BEC mov ebp,esp
004273C3 |. 83EC 40 sub esp,0x40
004273C6 |. 53 push ebx
004273C7 |. 56 push esi ; kernel32.751783EE
004273C8 |. 57 push edi ; kernel32.750E0000
004273C9 |. 8D7D C0 lea edi,[local.16]
004273CC |. B9 10000000 mov ecx,0x10
004273D1 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
004273D6 |. F3:AB rep stos dword ptr es:[edi]
004273D8 |. 8BF4 mov esi,esp
004273DA |. 6A 00 push 0x0 ; /pThreadId = NULL
004273DC |. 6A 00 push 0x0 ; |CreationFlags = 0
004273DE |. 6A 00 push 0x0 ; |pThreadParm = NULL
004273E0 |. 68 68164000 push Game.00401668 ; |ThreadFunction = Game.00401668
004273E5 |. 6A 00 push 0x0 ; |StackSize = 0x0
004273E7 |. 6A 00 push 0x0 ; |pSecurity = NULL
004273E9 |. FF15 60344A00 call dword ptr ds:[<&KERNEL32.CreateThre>; \CreateThread
004273EF |. 3BF4 cmp esi,esp
004273F1 |. E8 A07A0000 call Game.0042EE96
004273F6 |. 8BF4 mov esi,esp
004273F8 |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL
004273FA |. 68 D0534600 push Game.004653D0 ; |do not close it!
004273FF |. 68 B0534600 push Game.004653B0 ; |Cheating Infinite Lives
00427404 |. 6A 00 push 0x0 ; |hOwner = NULL
00427406 |. FF15 28364A00 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
0042740C |. 3BF4 cmp esi,esp
0042740E |. E8 837A0000 call Game.0042EE96
00427413 |. E8 A9A3FDFF call Game.004017C1
00427418 |. 33C0 xor eax,eax
0042741A |. 5F pop edi ; 0702F024
0042741B |. 5E pop esi ; 0702F024
0042741C |. 5B pop ebx ; 0702F024
0042741D |. 83C4 40 add esp,0x40
00427420 |. 3BEC cmp ebp,esp
00427422 |. E8 6F7A0000 call Game.0042EE96
00427427 |. 8BE5 mov esp,ebp
00427429 |. 5D pop ebp ; 0702F024
0042742A \. C2 0400 retn 0x4

004273E9处为函数00401668创建新线程,此线程为外挂无限命的实现,没什么好说的,然后在00427406处创建弹窗。
弹窗关闭后,是栈保护的检查代码,剩下的只有004017C1调用比较可疑了,跟进,进入425840
此处当时我静态分析了很长时间,其主要工作过程是通过PEB获取kernel32.dll的基址,进而得到APIGetProcAddress的地址,然后通过此API获取LoadLibraryAVirtualProtect地址,然后加载msvcrt.dll,获取其APImemcmpmalloc地址,接着获取CreateThread地址。中间还申请了两个堆大小为0x10的chunk,分别存放刚才获取到的kernel32.dll及msvrt.dll的API地址。
此过程中,需要将00424c5c处的je改为jmp
一路跟进到00425ed6,此处又新建了一个线程,线程对应代码为004269e0,并将存放API的堆地址传入。需要注意下,为了程序不崩溃,要将00425ee1至返回间的代码NOP掉。在新线程代码处下断,F9运行,继续跟进。
新线程大致过程是:获取OpenProcessVirtualAllocExWriteProcessMemoryCreateRemoteThreadCloseHandleCreateToolhelp32SnapshotProcess32FirstProcess32Nextstrcmp的地址;在00426ce7处调用00425ef0explorer进程的进程标识符PID,并使用OpenProcess获取其句柄;explorer进程申请大小为0x1000的内存空间,将00425f80处开始的代码写入目标内存,创建explorer进程的远程线程,从00426040处开始执行代码。
因为在win7以上系统,权限限制及系统保护比较严格,所以目标内存代码似乎是写不了的;再加上远程线程对于调试也不方便。所以可以直接patch程序,不创建远程线程,直接call004260400。此函数比较简短,只有一个调用004260b0,跟进。
此段代码开始与前面一样,还是通过PEB结构获取kernel32.dll模块基址,然后获取GetProcAddressLoadLibraryAVirtualProtect的地址;通过00426050改写0042618700426319范围内的代码,跳到00426187执行代码;获取CreateFileACloseHandlemallocstrlenReadFileWriteFile地址,打开c:\flg文件,获取GetFileSize地址并调用,得到文件大小;申请文件大小的chunk用于存放文件原始内容和3倍于文件大小的chunk用于存放文件内容变换结果;读入文件;调用00426330对文件内容进行变换;关闭文件;打开c: flg文件,将变换结果写入文件;关闭文件,函数返回,线程结束。

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
00426187 > \68 6C654100 push Game.0041656C ; 426050处代码后
0042618C . 68 74654669 push 0x69466574
00426191 . 68 43726561 push 0x61657243
00426196 . 54 push esp
00426197 . FF75 FC push dword ptr ss:[ebp-0x4] ; kernel32.750E0000
0042619A . FF55 F8 call dword ptr ss:[ebp-0x8] ; 获取CreateFileA地址
0042619D . 83C4 0C add esp,0xC
004261A0 . 8945 E8 mov dword ptr ss:[ebp-0x18],eax
004261A3 . 68 646C6500 push 0x656C64
004261A8 . 68 6548616E push 0x6E614865
004261AD . 68 436C6F73 push 0x736F6C43
004261B2 . 54 push esp
004261B3 . FF75 FC push dword ptr ss:[ebp-0x4] ; kernel32.750E0000
004261B6 . FF55 F8 call dword ptr ss:[ebp-0x8] ; 获取CloseHandle地址
004261B9 . 83C4 0C add esp,0xC
004261BC . 8945 E0 mov dword ptr ss:[ebp-0x20],eax
004261BF . E8 33000000 call Game.004261F7
004261C4 . 633A arpl word ptr ds:[edx],di
004261C6 . 5C pop esp ; Game.004261C4
004261C7 . 66:6c ins byte ptr es:[edi],dx
004261C9 . 67:0052 65 add byte ptr ss:[bp+si+0x65],dl
004261CD . 61 popad
004261CE . 64:46 inc esi
004261D0 . 696C65 00 577>imul ebp,dword ptr ss:[ebp],0x74697257
004261D8 . 65:46 inc esi
004261DA . 696C65 00 6D7>imul ebp,dword ptr ss:[ebp],0x6376736D
004261E2 . 72 74 jb short Game.00426258
004261E4 . 2e cs:
004261E5 . 64:6c ins byte ptr es:[edi],dx
004261E7 . 6c ins byte ptr es:[edi],dx
004261E8 . 0073 74 add byte ptr ds:[ebx+0x74],dh
004261EB . 72 6C jb short Game.00426259
004261ED . 65:6e outs dx,byte ptr gs:[esi]
004261EF . 006D 61 add byte ptr ss:[ebp+0x61],ch
004261F2 . 6c ins byte ptr es:[edi],dx
004261F3 . 6c ins byte ptr es:[edi],dx
004261F4 . 6f outs dx,dword ptr ds:[esi]
004261F5 . 6300 arpl word ptr ds:[eax],ax
004261F7 $ 58 pop eax ; Game.004261C4
004261F8 . 8945 B8 mov dword ptr ss:[ebp-0x48],eax
004261FB . 83C0 1A add eax,0x1A
004261FE . 50 push eax
004261FF . FF55 F4 call dword ptr ss:[ebp-0xC] ; kernel32.LoadLibraryA
00426202 . 8945 D8 mov dword ptr ss:[ebp-0x28],eax
00426205 . 8B5D B8 mov ebx,dword ptr ss:[ebp-0x48] ; Game.004261C4
00426208 . 83C3 2B add ebx,0x2B
0042620B . 43 inc ebx ; Game.004261C4
0042620C . 53 push ebx ; Game.004261C4
0042620D . 50 push eax
0042620E . FF55 F8 call dword ptr ss:[ebp-0x8] ; 获取malloc地址
00426211 . 8945 C8 mov dword ptr ss:[ebp-0x38],eax
00426214 . 8B5D B8 mov ebx,dword ptr ss:[ebp-0x48] ; Game.004261C4
00426217 . 83C3 25 add ebx,0x25
0042621A . 53 push ebx ; Game.004261C4
0042621B . FF75 D8 push dword ptr ss:[ebp-0x28] ; msvcrt.749C0000
0042621E . FF55 F8 call dword ptr ss:[ebp-0x8] ; 获取strlen地址
00426221 . 8945 C4 mov dword ptr ss:[ebp-0x3C],eax
00426224 . 8B5D B8 mov ebx,dword ptr ss:[ebp-0x48] ; Game.004261C4
00426227 . 83C3 07 add ebx,0x7
0042622A . 53 push ebx ; Game.004261C4
0042622B . FF75 FC push dword ptr ss:[ebp-0x4] ; kernel32.750E0000
0042622E . FF55 F8 call dword ptr ss:[ebp-0x8] ; 获取ReadFile地址
00426231 . 8945 E4 mov dword ptr ss:[ebp-0x1C],eax
00426234 . 8B5D B8 mov ebx,dword ptr ss:[ebp-0x48] ; Game.004261C4
00426237 . 83C3 10 add ebx,0x10
0042623A . 53 push ebx ; Game.004261C4
0042623B . FF75 FC push dword ptr ss:[ebp-0x4] ; kernel32.750E0000
0042623E . FF55 F8 call dword ptr ss:[ebp-0x8] ; 获取WriteFile地址
00426241 . 8945 DC mov dword ptr ss:[ebp-0x24],eax
00426244 . 6A 00 push 0x0
00426246 . 68 00000008 push 0x8000000
0042624B . 6A 03 push 0x3
0042624D . 6A 00 push 0x0
0042624F . 6A 01 push 0x1
00426251 . B8 00000040 mov eax,0x40000000
00426256 0D 00000080 or eax,0x80000000
0042625B ? 50 push eax
0042625C ? 8B5D B8 mov ebx,dword ptr ss:[ebp-0x48] ; Game.004261C4
0042625F ? 53 push ebx ; Game.004261C4
00426260 ? FF55 E8 call dword ptr ss:[ebp-0x18] ; 打开文件
00426263 . 8945 D4 mov dword ptr ss:[ebp-0x2C],eax
00426266 . 83F8 FF cmp eax,-0x1
00426269 . 0F84 B1000000 je Game.00426320
0042626F . 6A 00 push 0x0
00426271 . 50 push eax
00426272 . 68 697A6500 push 0x657A69
00426277 . 68 696C6553 push 0x53656C69
0042627C . 68 47657446 push 0x46746547
00426281 . 54 push esp
00426282 . FF75 FC push dword ptr ss:[ebp-0x4] ; kernel32.750E0000
00426285 . FF55 F8 call dword ptr ss:[ebp-0x8] ; 获取GetFileSize地址
00426288 . 83C4 0C add esp,0xC
0042628B . FFD0 call eax
0042628D . 8945 CC mov dword ptr ss:[ebp-0x34],eax
00426290 . 83C4 04 add esp,0x4
00426293 . 8D40 08 lea eax,dword ptr ds:[eax+0x8]
00426296 . 50 push eax
00426297 . FF55 C8 call dword ptr ss:[ebp-0x38] ; 申请空间
0042629A . 8945 C0 mov dword ptr ss:[ebp-0x40],eax
0042629D . 33D2 xor edx,edx
0042629F . 8B45 CC mov eax,dword ptr ss:[ebp-0x34]
004262A2 . BB 03000000 mov ebx,0x3
004262A7 . F7E3 mul ebx ; Game.004261C4
004262A9 . 50 push eax
004262AA . FF55 C8 call dword ptr ss:[ebp-0x38] ; 申请空间
004262AD . 83C4 04 add esp,0x4
004262B0 . 8945 BC mov dword ptr ss:[ebp-0x44],eax
004262B3 . 50 push eax
004262B4 . FF75 CC push dword ptr ss:[ebp-0x34]
004262B7 . 6A 00 push 0x0
004262B9 . 8D45 D0 lea eax,dword ptr ss:[ebp-0x30]
004262BC . 50 push eax
004262BD . FF75 CC push dword ptr ss:[ebp-0x34]
004262C0 . 8B45 C0 mov eax,dword ptr ss:[ebp-0x40]
004262C3 . 83C0 04 add eax,0x4
004262C6 . 50 push eax
004262C7 . FF75 D4 push dword ptr ss:[ebp-0x2C] ;
004262CA . FF55 E4 call dword ptr ss:[ebp-0x1C] ; 读文件进内存
004262CD . 8B45 C0 mov eax,dword ptr ss:[ebp-0x40]
004262D0 . 83C0 04 add eax,0x4
004262D3 . 50 push eax
004262D4 . E8 57000000 call Game.00426330 ;关键算法
004262D9 . 58 pop eax ;
004262DA . 58 pop eax ;
004262DB . 58 pop eax ;
004262DC . FF75 D4 push dword ptr ss:[ebp-0x2C] ;
004262DF . FF55 E0 call dword ptr ss:[ebp-0x20] ; 关闭文件
004262E2 . 6A 00 push 0x0
004262E4 . 68 00000008 push 0x8000000
004262E9 . 6A 02 push 0x2
004262EB . 6A 00 push 0x0
004262ED . 6A 01 push 0x1
004262EF . B8 00000040 mov eax,0x40000000
004262F4 . 0D 00000080 or eax,0x80000000
004262F9 . 50 push eax
004262FA . 8B5D B8 mov ebx,dword ptr ss:[ebp-0x48] ;
004262FD . 53 push ebx ;
004262FE . FF55 E8 call dword ptr ss:[ebp-0x18] ; 打开文件
00426301 . 8945 D4 mov dword ptr ss:[ebp-0x2C],eax
00426304 . FF75 BC push dword ptr ss:[ebp-0x44]
00426307 . FF55 C4 call dword ptr ss:[ebp-0x3C] ; 计算结果大小
0042630A . 6A 00 push 0x0
0042630C . 8D5D D0 lea ebx,dword ptr ss:[ebp-0x30]
0042630F . 53 push ebx ;
00426310 . 50 push eax
00426311 . FF75 BC push dword ptr ss:[ebp-0x44]
00426314 . FF75 D4 push dword ptr ss:[ebp-0x2C] ;
00426317 . FF55 DC call dword ptr ss:[ebp-0x24] ; 写入结果
0042631A . FF75 D4 push dword ptr ss:[ebp-0x2C] ;
0042631D . FF55 E0 call dword ptr ss:[ebp-0x20] ; 关闭文件
00426320 > 81C4 FC000000 add esp,0xFC
00426326 . 5D pop ebp ; Game.004261C4
00426327 . 61 popad
00426328 . C3 retn

00426320处的add esp,0xFC要patch成add esp,0x100

关键算法

加密或编码算法只是异或加换位,最后是替换。其伪代码如下:

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
int __cdecl sub_426330(char *ori_data, unsigned int length, char *des_data)
{
char v3; // ST4B_1@9
char v4; // ST4B_1@21
char v5; // ST4B_1@31
char v6; // ST4B_1@34
unsigned int mm; // [sp+8h] [bp-38h]@37
unsigned int ll; // [sp+Ch] [bp-34h]@35
unsigned int m; // [sp+10h] [bp-30h]@16
unsigned int v11; // [sp+14h] [bp-2Ch]@1
unsigned int j; // [sp+18h] [bp-28h]@4
unsigned int i; // [sp+1Ch] [bp-24h]@1
unsigned int ii; // [sp+20h] [bp-20h]@22
unsigned int l; // [sp+24h] [bp-1Ch]@10
unsigned int jj; // [sp+28h] [bp-18h]@29
unsigned int n; // [sp+2Ch] [bp-14h]@19
unsigned int k; // [sp+30h] [bp-10h]@7
unsigned int kk; // [sp+34h] [bp-Ch]@32
unsigned int v20; // [sp+38h] [bp-8h]@1
unsigned __int8 v21; // [sp+3Fh] [bp-1h]@22
char v22; // [sp+3Fh] [bp-1h]@35
v20 = length >> 2;
v11 = length % 4;
for ( i = 0; i < 2 * v20; ++i )
{
ori_data[i] ^= ori_data[i + 1];
*(&ori_data[2 * v20] + i) ^= *(&ori_data[2 * v20 + 1] + i);
}
for ( j = 0; j < 2 * v20 - 1; ++j )
{
ori_data[j] ^= *(&ori_data[2 * v20] + j);
*(&ori_data[2 * v20] + j) ^= ori_data[j];
}
for ( k = 0; k < v20; ++k )
{
v3 = ori_data[k];
ori_data[k] = *(&ori_data[k] + v20);
*(&ori_data[k] + v20) = *(&ori_data[2 * v20] + k);
*(&ori_data[2 * v20] + k) = *(&ori_data[3 * v20] + k);
*(&ori_data[3 * v20] + k) = v3;
*(&ori_data[3 * v20] + k) ^= *(&ori_data[k] + v20);
ori_data[k] ^= *(&ori_data[2 * v20] + k);
}
for ( l = 0; l < length - 2; ++l )
{
if ( l % 2 )
ori_data[l] ^= ori_data[l - 1];
else
ori_data[l] ^= ori_data[l + 2];
}
for ( m = 0; m < length; ++m )
ori_data[m] ^= v20;
for ( n = 0; n < v20; ++n )
{
v4 = ori_data[n];
ori_data[n] = *(&ori_data[2 * v20] + n);
*(&ori_data[2 * v20] + n) = *(&ori_data[v20] + n);
*(&ori_data[v20] + n) = *(&ori_data[3 * v20] + n);
*(&ori_data[3 * v20] + n) = v4;
*(&ori_data[2 * v20] + n) ^= *(&ori_data[n] + v20);
ori_data[n] ^= *(&ori_data[3 * v20] + n);
}
v21 = 0;
for ( ii = 0; ii < 2 * v20; ++ii )
{
ori_data[ii] ^= *(&ori_data[2 * v20] + ii);
if ( v11 )
{
if ( v21 == v11 )
v21 = 0;
*(&ori_data[2 * v20] + ii) ^= *(&ori_data[v21] + length - v11);
ori_data[ii] ^= *(&ori_data[v21++] + length - v11);
}
}
for ( jj = 0; jj < v20; ++jj )
{
v5 = ori_data[jj];
ori_data[jj] = *(&ori_data[3 * v20] + jj);
*(&ori_data[3 * v20] + jj) = *(&ori_data[jj] + v20);
*(&ori_data[jj] + v20) = *(&ori_data[2 * v20] + jj);
*(&ori_data[2 * v20] + jj) = v5;
*(&ori_data[jj] + v20) ^= *(&ori_data[2 * v20] + jj);
ori_data[jj] ^= *(&ori_data[3 * v20] + jj);
}
for ( kk = 0; kk < v20; ++kk )
{
*(&ori_data[3 * v20] + kk) ^= *(&ori_data[kk] + v20);
ori_data[kk] ^= *(&ori_data[2 * v20] + kk);
*(&ori_data[kk] + v20) ^= ori_data[kk];
*(&ori_data[2 * v20] + kk) ^= *(&ori_data[3 * v20] + kk);
v6 = ori_data[kk];
ori_data[kk] = *(&ori_data[v20] + kk);
*(&ori_data[v20] + kk) = *(&ori_data[2 * v20] + kk);
*(&ori_data[2 * v20] + kk) = *(&ori_data[3 * v20] + kk);
*(&ori_data[3 * v20] + kk) = v6;
}
v22 = 0;
for ( ll = 0; ll < length - v11; ++ll )
{
for ( mm = 0; mm < v11; ++mm )
{
v22 += *(&ori_data[mm] + length - v11);
ori_data[ll] ^= v22;
}
}
*((_DWORD *)ori_data - 1) = length;
return ((int (__cdecl *)(char *, char *, unsigned int))
(0x4269B0 - ((char *)&sub_401A2D + 3 - byte_401000)))
(// sub_425F80
des_data,
ori_data - 4,
length + 4);
}
int __cdecl sub_425F80(char *des, char *src, unsigned int lehgth)
{
char v4; // [sp+0h] [bp-2Ch]@1
int v5; // [sp+1h] [bp-2Bh]@1
int v6; // [sp+5h] [bp-27h]@1
int v7; // [sp+9h] [bp-23h]@1
int v8; // [sp+Dh] [bp-1Fh]@1
int v9; // [sp+11h] [bp-1Bh]@1
int v10; // [sp+15h] [bp-17h]@1
int v11; // [sp+19h] [bp-13h]@1
__int16 v12; // [sp+1Dh] [bp-Fh]@1
char v13; // [sp+1Fh] [bp-Dh]@1
int v14; // [sp+20h] [bp-Ch]@4
char *j; // [sp+24h] [bp-8h]@4
int i; // [sp+28h] [bp-4h]@1
v4 = 0;
v5 = 0;
v6 = 0;
v7 = 0;
v8 = 0;
v9 = 0;
v10 = 0;
v11 = 0;
v12 = 0;
v13 = 0;
for ( i = 0; i < 24; ++i )
*(&v4 + i) = i + 16;
v14 = 0;
for ( j = src; ; ++j )
{
v14 = j - src;
if ( j - src >= lehgth )
break;
des[2 * v14] = *(&v4 + ((signed int)(unsigned __int8)*j >> 4));
des[2 * v14 + 1] = *(&v4 + 23 - (*j & 0xF));
}
des[2 * v14 + 1] = 0;
return (int)des;
}

算法过程的变换较多,但并不复杂。sub_426330主要是异或及位置置换计算,不知道有什么出处,没看出来。而sub_425F80则是变形的base24编码,将置换值进行了更改。逆向算法如下:

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
# -*- coding=utf-8 -*-
def b24decode(b24estr , length):
table = []
for i in range(24):
table.append(chr(16+i))
b24dstr = ''
for i in range(length/2):
high = table.index(b24estr[2*i])
low = 23-table.index(b24estr[2*i+1])
b24dstr += chr(high<<4|low)
return b24dstr
def decode_s(str_en):
length = int(str_en[:4][::-1].encode('hex'),16)
if length != len(str_en)-4:
print 'data error!'
return
length_m = length % 4
length_q = length >> 2
str_en = str_en[4:]
xor_n = 0
dlist1_n = list(ord(str_en[i]) for i in range(length))
for i in range(length-length_m):
for j in xrange(length_m):
xor_n += dlist1_n[length-length_m+j]
dlist1_n[i] ^= (xor_n & 0xff)
for i in range(length_q):
tmp = dlist1_n[i]
dlist1_n[i] = dlist1_n[3*length_q+i]
dlist1_n[3*length_q+i] = dlist1_n[2*length_q+i]
dlist1_n[2*length_q+i] = dlist1_n[length_q+i]
dlist1_n[length_q+i] = tmp
dlist1_n[2*length_q+i] ^= dlist1_n[3*length_q+i]
dlist1_n[length_q+i] ^= dlist1_n[i]
dlist1_n[i] ^= dlist1_n[2*length_q+i]
dlist1_n[3*length_q+i] ^= dlist1_n[length_q+i]
for i in range(length_q):
dlist1_n[i] ^= dlist1_n[3*length_q+i]
dlist1_n[length_q+i] ^= dlist1_n[2*length_q+i]
tmp = dlist1_n[i]
dlist1_n[i] = dlist1_n[2*length_q+i]
dlist1_n[2*length_q+i] = dlist1_n[length_q+i]
dlist1_n[length_q+i] = dlist1_n[3*length_q+i]
dlist1_n[3*length_q+i] = tmp
tmp = 0
for i in range(2*length_q):
if length_m != 0:
dlist1_n[i] ^= dlist1_n[(tmp%length_m)+length-length_m]
dlist1_n[2*length_q+i] ^= dlist1_n[(tmp%length_m)+length-length_m]
tmp += 1
dlist1_n[i] ^= dlist1_n[2*length_q+i]
for i in range(length_q):
dlist1_n[i] ^= dlist1_n[3*length_q+i]
dlist1_n[2*length_q+i] ^= dlist1_n[length_q+i]
tmp = dlist1_n[i]
dlist1_n[i] = dlist1_n[3*length_q+i]
dlist1_n[3*length_q+i] = dlist1_n[length_q+i]
dlist1_n[length_q+i] = dlist1_n[2*length_q+i]
dlist1_n[2*length_q+i] =tmp
for i in range(length):
dlist1_n[i] ^= (length_q & 0xff)
for i in range(length-3,-1,-1):
if i%2 :
dlist1_n[i] ^= dlist1_n[i-1]
else:
dlist1_n[i] ^= dlist1_n[i+2]
for i in range(length_q):
dlist1_n[i] ^= dlist1_n[2*length_q+i]
dlist1_n[3*length_q+i] ^= dlist1_n[length_q+i]
tmp = dlist1_n[i]
dlist1_n[i] = dlist1_n[3*length_q+i]
dlist1_n[3*length_q+i] = dlist1_n[2*length_q+i]
dlist1_n[2*length_q+i] = dlist1_n[length_q+i]
dlist1_n[length_q+i] =tmp
for i in range(2*length_q-2,-1,-1):
dlist1_n[2*length_q+i] ^= dlist1_n[i]
dlist1_n[i] ^= dlist1_n[2*length_q+i]
dlist1_n.append(0)
for i in range(2*length_q-1,-1,-1):
dlist1_n[2*length_q+i] ^= dlist1_n[2*length_q+i+1]
dlist1_n[i] ^= dlist1_n[i+1]
return dlist1_n
if __name__ == '__main__':
data = file('flg','rb').read()
data = b24decode(data,len(data))
data = decode_s(data)
fp = open('flg','wb')
data = list(chr(data[i]) for i in range(len(data)))
str = ''.join(data)
print len(str)
fp.write(str[:-1])
fp.close()

反解一次发现文件内容的字节值范围还是在[0x10,0x27]之间,继续解,4次之后,内容正常,研究了下格式,应该是word2003文件,打开看到图片中的flag。

写在最后

本题具体算法还是很好理的,个人觉得亮点在于大量花指令的运用。
此题中的花指令的大概模式有:

1
2
3
4
call loc
db xxxx
loc:
pop eax

1
2
3
4
5
6
7
test ebx,ebx ;ebx不为0
jnz loc2
junk code
...
jnuk code
loc2:
code

在远程线程注入前,花指令非常大量的存在,此处花指令数量要大于正常指令数量。远程线程中的代码则几乎没有。

对于利用PEB数据结构获取API的代码,题中有两段,去除花指令,两段是一样的:

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
004260C5 . 64:8B35 30000>mov esi,dword ptr fs:[0x30] ;PEB
004260CC . 8B76 0C mov esi,dword ptr ds:[esi+0xC] ;PEB_LDR_DATA
004260CF . 8B76 1C mov esi,dword ptr ds:[esi+0x1C] ;InInitializationOrderModuleList
004260D2 > 8B46 08 mov eax,dword ptr ds:[esi+0x8] ;模块基址
004260D5 . 8B7E 20 mov edi,dword ptr ds:[esi+0x20] ;模块名称地址
004260D8 . 8B36 mov esi,dword ptr ds:[esi] ;下一个模块
004260DA . 66:394F 18 cmp word ptr ds:[edi+0x18],cx ;模块名称长度为12
004260DE .^ 75 F2 jnz short Game.004260D2
004260E0 . 5E pop esi
004260E1 . 8945 FC mov dword ptr ss:[ebp-0x4],eax
004260E4 . 8BF8 mov edi,eax
004260E6 . 8B47 3C mov eax,dword ptr ds:[edi+0x3C] ;PE首部
004260E9 . 8B5407 78 mov edx,dword ptr ds:[edi+eax+0x78]
004260ED . 03D7 add edx,edi ;导出表地址
004260EF . 8B4A 18 mov ecx,dword ptr ds:[edx+0x18] ;导出函数数目
004260F2 . 8B5A 20 mov ebx,dword ptr ds:[edx+0x20]
004260F5 . 03DF add ebx,edi ;导出函数名地址
004260F7 > 49 dec ecx
004260F8 . 8B348B mov esi,dword ptr ds:[ebx+ecx*4]
004260FB . 03F7 add esi,edi
004260FD . B8 47657450 mov eax,0x50746547 ;函数名前4为'GetP'
00426102 . 3906 cmp dword ptr ds:[esi],eax
00426104 .^ 75 F1 jnz short Game.004260F7 ;导出函数名5-8为‘rocA
00426106 . B8 726F6341 mov eax,0x41636F72
0042610B . 3946 04 cmp dword ptr ds:[esi+0x4],eax
0042610E .^ 75 E7 jnz short Game.004260F7 ;相等则为GetProcAddress
00426110 . 8B5A 24 mov ebx,dword ptr ds:[edx+0x24]
00426113 . 03DF add ebx,edi ;导出序号数组地址
00426115 . 66:8B0C4B mov cx,word ptr ds:[ebx+ecx*2] ;导出序号值
00426119 . 8B5A 1C mov ebx,dword ptr ds:[edx+0x1C]
0042611C . 03DF add ebx,edi ;导出函数地址数组
0042611E . 8B048B mov eax,dword ptr ds:[ebx+ecx*4]
00426121 . 03C7 add eax,edi ;得到函数地址

以上过程大致是:

  • 通过TEB/PEB枚举模块,并以长度判断条件确定模块并获取kernel32.dll基址
  • 在(基址+0x3c)处获取e_lfanewc即PE标志。
  • 在(基址+e_lfanew+0x78)处获取导出表地址exportAddr
  • 在(基址+exportAddr+0x1c)处获取AddressOfFunctions、AddressOfNames、AddressOfNameOrdinalse。
  • 搜索AddressOfNames,确定“GetProcAddress”所对应的index
  • index_val = AddressOfNameOrdinalse [ index ];
  • 函数地址 = AddressOfFunctions [ index_val ];

题目可能考虑不同windows系统的版本兼容,在获取kernel32.dll基址时用了枚举加长度判断,其实完全没有必要,如下代码就能实现,我在以往题目中也用过:

1
2
3
4
5
6
mov eax, fs:[0x30]
mov eax, [eax + 0x0c]
mov eax, [eax + 0x0c]
mov eax, [eax]
mov eax, [eax]
mov eax, [eax + 18h] ;eax即为kernel32.dll基址

至于为什么,有兴趣的可以用windbg试试。

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 初步了解
  2. 2. 动态跟踪过程
  3. 3. 关键算法
  4. 4. 写在最后
,