前言

比赛时间:

Reverse

SafeProgram

程序提取NCTF{}包裹的32位flag,分别对前16位和后16位加密(同一算法)。程序中存在很多阻碍Debug的东西,现在加密函数已经逆向了。尽管相关的变量都在动态调试的时候验证过,但是解密却失败了。
推测原因:在正常运行时,调用了某些函数,对加密用到的全局变量进行了修改,而在Debug的时候却把他们绕过了。

这个函数还是很好找的,就这一个函数改了变量,看来出题人是个好人。

解密脚本:

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BYTE1(x) ((unsigned __int8)((x) >> 8))
#define BYTE2(x) ((unsigned __int8)((x) >> 16))
#define HIBYTE(x) ((unsigned __int8)((x) >> 24))
#define _DWORD unsigned int
#define _BYTE unsigned char

_DWORD dword_7FF69B54A028[4] = {0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC};
_DWORD dword_7FF69B54A040[32] = {
0x70E15, 0x1C232A31, 0x383F464D, 0x545B6269,
0x70777E85, 0x8C939AA1, 0x0A8AFB6BD, 0x0C4CBD2D9,
0x0E0E7EEF5, 0x0FC030A11, 0x181F262D, 0x343B4249,
0x50575E65, 0x6C737A81, 0x888F969D, 0x0A4ABB2B9,
0x0C0C7CED5, 0x0DCE3EAF1, 0x0F8FF060D, 0x141B2229,
0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
0x0A0A7AEB5, 0x0BCC3CAD1, 0x0D8DFE6ED, 0x0F4FB0209,
0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279};
_BYTE S_box[256] = {
0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87,
0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48};
_BYTE byte_7FF69B54A0C0[16] = {0xDF, 0xD2, 0xC5, 0xD7, 0xA3, 0xA5, 0xFF, 0xF2, 0xE5, 0xF7};

unsigned __int64 __fastcall encrypt(__int64 a1, __int64 a2, __int64 a3)
{
unsigned __int64 result; // rax
int i; // [rsp+0h] [rbp-178h]
int j; // [rsp+4h] [rbp-174h]
int m; // [rsp+8h] [rbp-170h]
int ii; // [rsp+Ch] [rbp-16Ch]
int k; // [rsp+10h] [rbp-168h]
int n; // [rsp+14h] [rbp-164h]
int v10; // [rsp+18h] [rbp-160h]
int v11; // [rsp+1Ch] [rbp-15Ch]
_DWORD v12[4]; // [rsp+30h] [rbp-148h] BYREF
_DWORD v13[36]; // [rsp+40h] [rbp-138h] BYREF
_DWORD v14[36]; // [rsp+D0h] [rbp-A8h] BYREF

memset(v14, 0, sizeof(v14));
memset(v13, 0, sizeof(v13));
result = (unsigned __int64)v12;
for (i = 0; i < 4; ++i)
{
// 4个字节为一组逆序存入v13前4个字节
v10 = *(_DWORD *)(a1 + 4LL * i);
v13[i] = (BYTE1(v10) << 16) | ((unsigned __int8)v10 << 24);
v13[i] |= HIBYTE(v10) | (BYTE2(v10) << 8);
v14[i] = dword_7FF69B54A028[i] ^ ((unsigned __int8)HIBYTE(*(_DWORD *)(a2 + 4LL * i)) | ((unsigned __int8)BYTE2(*(_DWORD *)(a2 + 4LL * i)) << 8) | ((unsigned __int8)BYTE1(*(_DWORD *)(a2 + 4LL * i)) << 16) | ((unsigned __int8)*(_DWORD *)(a2 + 4LL * i) << 24));
result = (unsigned int)(i + 1);
}
// 拓展v14至36个字节
for (j = 0; j < 32; ++j)
{
v12[0] = dword_7FF69B54A040[j] ^ v14[j + 3] ^ v14[j + 2] ^ v14[j + 1];
for (k = 0; k < 4; ++k)
*((_BYTE *)v12 + k) = S_box[*((unsigned __int8 *)v12 + k)];
v14[j + 4] = ((v12[0] >> 9) | (v12[0] << 23)) ^ ((v12[0] >> 19) | (v12[0] << 13)) ^ v12[0] ^ v14[j];
result = (unsigned int)(j + 1);
}
for (m = 0; m < 32; ++m)
{
v12[0] = v14[m + 4] ^ v13[m + 3] ^ v13[m + 2] ^ v13[m + 1];
for (n = 0; n < 4; ++n)
*((_BYTE *)v12 + n) = S_box[*((unsigned __int8 *)v12 + n)];
v13[m + 4] = ((v12[0] >> 8) | (v12[0] << 24)) ^ ((v12[0] >> 14) | (v12[0] << 18)) ^ ((v12[0] >> 22) | (v12[0] << 10)) ^ ((v12[0] >> 30) | (v12[0] << 2)) ^ v12[0] ^ v13[m];
result = (unsigned int)(m + 1);
}
for (ii = 0; ii < 4; ++ii)
{
v11 = v13[35 - ii];
*(_DWORD *)(a3 + 4LL * ii) = (BYTE1(v11) << 16) | ((unsigned __int8)v11 << 24);
*(_DWORD *)(a3 + 4LL * ii) |= HIBYTE(v11) | (BYTE2(v11) << 8);
result = (unsigned int)(ii + 1);
}

// 过程量验证

// printf("v13:\n");
// for (int i = 0; i < 36; i++)
// {
// printf("%x ", v13[i]);
// }
// printf("\n");

// printf("v14:\n");
// for (int i = 0; i < 36; i++)
// {
// printf("%x ", v14[i]);
// }
// printf("\n");

return result;
}

unsigned __int64 __fastcall decrypt(__int64 a1, __int64 a2, __int64 a3)
{
unsigned __int64 result; // rax
int i; // [rsp+0h] [rbp-178h]
int j; // [rsp+4h] [rbp-174h]
int m; // [rsp+8h] [rbp-170h]
int ii; // [rsp+Ch] [rbp-16Ch]
int k; // [rsp+10h] [rbp-168h]
int n; // [rsp+14h] [rbp-164h]
int v10; // [rsp+18h] [rbp-160h]
int v11; // [rsp+1Ch] [rbp-15Ch]
_DWORD v12[4]; // [rsp+30h] [rbp-148h] BYREF
_DWORD v13[36]; // [rsp+40h] [rbp-138h] BYREF
_DWORD v14[36]; // [rsp+D0h] [rbp-A8h] BYREF

memset(v14, 0, sizeof(v14));
memset(v13, 0, sizeof(v13));

for (ii = 0; ii < 4; ++ii)
{
v11 = *(_DWORD *)(a3 + 4LL * ii);
v13[35 - ii] = (BYTE1(v11) << 16) | ((unsigned __int8)v11 << 24);
v13[35 - ii] |= HIBYTE(v11) | (BYTE2(v11) << 8);
v14[ii] = dword_7FF69B54A028[ii] ^ ((unsigned __int8)HIBYTE(*(_DWORD *)(a2 + 4LL * ii)) | ((unsigned __int8)BYTE2(*(_DWORD *)(a2 + 4LL * ii)) << 8) | ((unsigned __int8)BYTE1(*(_DWORD *)(a2 + 4LL * ii)) << 16) | ((unsigned __int8)*(_DWORD *)(a2 + 4LL * ii) << 24));
}

// 拓展v14至36个字节
for (j = 0; j < 32; ++j)
{
v12[0] = dword_7FF69B54A040[j] ^ v14[j + 3] ^ v14[j + 2] ^ v14[j + 1];
for (k = 0; k < 4; ++k)
*((_BYTE *)v12 + k) = S_box[*((unsigned __int8 *)v12 + k)];
v14[j + 4] = ((v12[0] >> 9) | (v12[0] << 23)) ^ ((v12[0] >> 19) | (v12[0] << 13)) ^ v12[0] ^ v14[j];
}

for (m = 31; m >= 0; --m)
{
v12[0] = v14[m + 4] ^ v13[m + 3] ^ v13[m + 2] ^ v13[m + 1];
for (n = 0; n < 4; ++n)
*((_BYTE *)v12 + n) = S_box[*((unsigned __int8 *)v12 + n)];
v13[m] = ((v12[0] >> 8) | (v12[0] << 24)) ^ ((v12[0] >> 14) | (v12[0] << 18)) ^ ((v12[0] >> 22) | (v12[0] << 10)) ^ ((v12[0] >> 30) | (v12[0] << 2)) ^ v12[0] ^ v13[m + 4];
}

for (i = 0; i < 4; ++i)
{
v10 = (BYTE1(v13[i]) << 16) | ((unsigned __int8)v13[i] << 24);
v10 |= HIBYTE(v13[i]) | (BYTE2(v13[i]) << 8);
*(_DWORD *)(a1 + 4LL * i) = v10;
}

// 过程量验证

// printf("v13:\n");
// for (int i = 0; i < 36; i++)
// {
// printf("%x ", v13[i]);
// }
// printf("\n");

// printf("v14:\n");
// for (int i = 0; i < 36; i++)
// {
// printf("%x ", v14[i]);
// }
// printf("\n");
}
char *__fastcall sub_7FF69B521E80(char *a1, char *a2)
{
char *result; // rax
char v3; // [rsp+0h] [rbp-18h]

v3 = *a1;
*a1 = *a2;
result = a2;
*a2 = v3;
return result;
}
__int64 sub_7FF69B521480()
{
__int64 result; // rax
int i; // [rsp+20h] [rbp-18h]
int j; // [rsp+24h] [rbp-14h]

for (i = 0; i < 10; ++i)
{
byte_7FF69B54A0C0[i] ^= 0x91u;
result = (unsigned int)(i + 1);
}
for (j = 0; j < 10; ++j)
{
sub_7FF69B521E80(S_box, &S_box[byte_7FF69B54A0C0[j]]);
result = (unsigned int)(j + 1);
}
return result;
}

int main()
{
_BYTE v4[16];
_BYTE Buf[16];

sub_7FF69B521480();
memcpy(v4, &byte_7FF69B54A0C0, 10);
memcpy(&v4[10], &byte_7FF69B54A0C0, 6);

// 测试解密函数
// _BYTE test_flag[17] = "1111111111111111";
// printf("test_flag: %s\n", test_flag);
// encrypt(test_flag, v4, Buf);
// decrypt(Buf, v4, Buf);
// printf("test_flag: %s\n", test_flag);

_BYTE flag[33] = {
0xFB, 0x97, 0x3C, 0x3B, 0xF1, 0x99, 0x12, 0xDF,
0x13, 0x30, 0xF7, 0xD8, 0x7F, 0xEB, 0xA0, 0x6C,
0x14, 0x5B, 0xA6, 0x2A, 0xA8, 0x05, 0xA5, 0xF3,
0x76, 0xBE, 0xC9, 0x01, 0xF9, 0x36, 0x7B, 0x46, 0x00};
decrypt(flag, v4, flag);
decrypt(&flag[16], v4, &flag[16]);
printf("NCTF{%s}", flag);
// NCTF{58cb925e0cd823c0d0b54fd06b820b7e}
return 0;
}

ezDOS

打开程序,修复花指令。分析ASM发现是变种RC4,我先编写了一个理应等效的加密方式,然后编写了解密函数。但是解密没解出来,检查了几遍都没找到问题,如果能动态调试就好了。

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
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _DWORD unsigned int
#define _BYTE unsigned char

_BYTE enc[] = {
0x7C, 0x3E, 0x0D, 0x3C, 0x88, 0x54, 0x83, 0x0E,
0x3B, 0xB8, 0x99, 0x1B, 0x9B, 0xE5, 0x23, 0x43,
0xC5, 0x80, 0x45, 0x5B, 0x9A, 0x29, 0x24, 0x38,
0xA9, 0x5C, 0xCB, 0x7A, 0xE5, 0x93, 0x73, 0x0E,
0x70, 0x6D, 0x7C, 0x31, 0x2B, 0x8C};

_BYTE S_Box[256];

// 定义S盒数据
unsigned char S_Box[256];
unsigned char S_Box_[256] = {
0x8E, 0x76, 0xD7, 0xDE, 0x1C, 0xA1, 0xB8, 0x7E, 0x15, 0x80, 0xDF, 0x45, 0xFF, 0xD4, 0xCE, 0x1E,
0xA2, 0x9B, 0x38, 0x34, 0xA6, 0xC1, 0x0F, 0x59, 0xBE, 0xAB, 0x3B, 0xBA, 0x97, 0x4D, 0xDB, 0x47,
0xC7, 0x4A, 0x7C, 0xCC, 0xE8, 0x27, 0xEB, 0x1F, 0x13, 0x62, 0x56, 0xC3, 0x61, 0x90, 0xFB, 0x66,
0xAC, 0x41, 0x7F, 0x79, 0xB5, 0x11, 0x46, 0x70, 0x6B, 0x39, 0x71, 0xB6, 0xF9, 0xA4, 0x8B, 0xD8,
0xEE, 0x9D, 0xBB, 0x44, 0xF7, 0x3F, 0xB0, 0x17, 0x1A, 0x00, 0xA9, 0xF2, 0xF8, 0x4B, 0xDD, 0x49,
0xCB, 0x86, 0x01, 0xA3, 0xE4, 0xFC, 0x99, 0x9C, 0x64, 0xB2, 0x07, 0xB4, 0xF6, 0xF1, 0x74, 0x9E,
0x8C, 0x88, 0x16, 0xD0, 0x43, 0xFE, 0xDA, 0xD2, 0x32, 0x8D, 0x57, 0x75, 0x8A, 0xA8, 0x77, 0xF5,
0x73, 0xE0, 0x83, 0x91, 0x42, 0x8F, 0x89, 0x0B, 0x85, 0x6A, 0x2C, 0xB7, 0x94, 0x29, 0xE6, 0x03,
0x04, 0x05, 0x98, 0x3C, 0xC4, 0xC2, 0x96, 0x21, 0xC8, 0x7D, 0x93, 0xE1, 0xCA, 0x4F, 0x7A, 0xF4,
0xA7, 0xDC, 0x5F, 0xD3, 0x3A, 0x2E, 0x6F, 0x2B, 0x51, 0x84, 0x19, 0xAF, 0x1D, 0xBC, 0x9A, 0x40,
0x23, 0x58, 0x25, 0x28, 0x5A, 0xEC, 0x1B, 0xFD, 0x55, 0x3D, 0x2F, 0x67, 0x30, 0x2D, 0xD6, 0x54,
0x5E, 0x5B, 0xEA, 0x6E, 0xD5, 0x5C, 0xD9, 0x10, 0xBF, 0x18, 0x65, 0x2A, 0x37, 0xB3, 0xA0, 0xC0,
0x87, 0x08, 0xC6, 0x82, 0x06, 0xEF, 0xCF, 0xB1, 0x69, 0xD1, 0x4C, 0x81, 0xB9, 0x3E, 0x7B, 0x33,
0x48, 0x60, 0xE9, 0x4E, 0x78, 0x5D, 0x26, 0x6D, 0x95, 0xE5, 0xBD, 0x0C, 0xE2, 0xAD, 0x68, 0x72,
0xC9, 0xED, 0x0D, 0x0A, 0x6C, 0x22, 0xE3, 0xCD, 0xAA, 0x9F, 0x14, 0x02, 0xE7, 0x36, 0x63, 0x20,
0x35, 0x24, 0x50, 0xAE, 0xF3, 0x92, 0xC5, 0x53, 0xFA, 0xA5, 0x12, 0xF0, 0x09, 0x52, 0x0E, 0x31};

int S_init()
{
_DWORD a = 0, j = 0, i;
_BYTE key[12] = "NCTf2024nctF";
for (int i = 0; i < 256; i++)
{
S_Box[i] = i;
}
for (int i = 0; i < 12; i++)
{
key[i] = (((key[i] + 6) >> 6) | ((key[i] << 6) + 2)) & 0xFF;
printf("%02x ", key[i]);
}
printf("\n");

for (int i = 0; i < 256; i++)
{

j = (j + S_Box[i] + key[i % 12]) % 256;

int temp = S_Box[i];
S_Box[i] = S_Box[j];
S_Box[j] = temp;
}

// for (i = 0; i < 256; i++)
// {
// S_Box[i] = S_Box_[i];
// }

return 0;
}

int encrypt(_BYTE flag[], int len)
{
int i = 0, j = 0;
for (int ii = 0; ii < len; ii++)
{
i = (i + 1) % 256;
j = (j + S_Box[i]) % 256;

int temp = S_Box[j];
S_Box[j] = S_Box[i];
S_Box[i] = temp;

flag[ii] ^= S_Box[(S_Box[i] + S_Box[j]) % 256] + 2;
j = (j + 0) % 256;
}
}

int decrypt(_BYTE flag[], int len)
{
unsigned int i = 0, j = 0;
for (int ii = 0; ii < len; ii++)
{
i = (i + 1) % 256;
j = (j + S_Box[i]) % 256;

int temp = S_Box[j];
S_Box[j] = S_Box[i];
S_Box[i] = temp;
flag[ii] ^= S_Box[(S_Box[i] + S_Box[j]) % 256] + 1;
}
}

int main()
{
S_init();
decrypt(enc, 0x26);
printf("%s\n", enc);
}
// https://www.bmhysw.com/article/22749.html

后来,在DOSBOX里面动态调试了,S盒的结果和我算的完全不一样,我就直接用跑出来的了。解密函数也有改动

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
int decrypt(_BYTE flag[], int len)
{
unsigned int i = 0, j = 0;
for (int ii = 0; ii < len; ii++)
{
i = (i + 1) % 256;
j = (j + S_Box[i]) % 256;

int temp = S_Box[j];
S_Box[j] = S_Box[i];
S_Box[i] = temp;
}

for (int ii = len - 1; ii >= 0; ii--)
{
flag[ii] ^= S_Box[(S_Box[i] + S_Box[j]) % 256] + 1;

int temp = S_Box[j];
S_Box[j] = S_Box[i];
S_Box[i] = temp;

j = (j - S_Box[i]) % 256;
i = (i - 1) % 256;
}
}

NCTF{Y0u_4r3_Assemb1y_M4st3r_5d0b497e}