BUUCTF逆向工程-3

[2019红帽杯]childRE

很神奇的一道题,不过为啥红帽杯的题都多多少少有点阴间成分?
首先看到它的验证部分,这个很好逆。

运行如下代码:

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
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
char ans[99] = "1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'ASDFGHJKL:\"ZXCVBNM<>?zxcvbnm,./";
char num[99] = "55565653255552225565565555243466334653663544426565555525555222";
char str[99] = "(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&";
int i, pos;
pos = 0;
do
{
for(i = 32; i < 125; i++)
{
if(ans[i % 23] == str[pos] && ans[i / 23] == num[pos])
{
cout << char(i);
break;
}
}
pos++;
}
while(pos < 62);
return 0;
}

就可以得到
private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
而前文中存在UnDecorateSymbolName()函数。因此我们得到的字符串是某个函数去修饰后的样子。
根据这篇文章可以知道函数去修饰前应该是?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
继续跟踪它与输入的关系可以找到找到这个函数:

不用怀疑,这个b绝对是二叉树。为什么呢?被恶心多了就记住了。看规则是后序遍历,手动算一下遍历顺序,写出代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
char raw[99] = "?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z";
char ans[99];
int order[99] = {15,16,7,17,18,8,3,19,20,9,21,22,10,4,1,23,24,11,25,26,12,5,27,28,13,29,30,14,6,2,0};
int i;
for(i = 0; i < 31; i++)
{
ans[order[i]] = raw[i];
}
for(i = 0; i < 31; i++)
{
cout << ans[i];
}
return 0;
}

运行得到Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP
将其32位小写md5加密后得到flag:63b148e750fed3a33419168ac58083f5

[SWPU2019]ReverseMe

这道题代码写得很神奇,反正我没看得太懂,所以嗯逆非常滴恶心。
下面这一段是本题唯一可以看得懂的东西。其中v29很奇怪,会莫名其妙地变掉从而执行if。

接着是sub_6525C0()函数中加密的部分。

然后是主函数中的对比部分。

它的代码写得非常莫名其妙,所以采用动调回事一种很好的方法。动调的时候一定要对着汇编码,随便输入点啥,然后把跟Str异或之后的东西前几位记一下。接着就不停地找哪个寄存器是这一串东西,它的值什么时候变了,就能找到加密的地方。写出代码:

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
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
char str[20] = "SWPU_2019_CTF";
int Xor[99] = {134, 12, 62, 202, 152, 215, 174, 25, 226, 119,
107, 166, 106, 161, 119, 176, 105, 145, 55, 5,
122, 249, 123, 48, 67, 90, 75, 16, 134, 125,
212, 40, 171, 171, 171, 171, 171, 171, 171, 171,
0, 0, 0, 0, 0, 0, 0, 0, 20, 246,
245, 17, 13, 244, 0, 24};
int input[99] = {179, 55, 15, 248, 188, 188, 174, 93, 186, 90,
77, 134, 68, 151, 98, 211, 79, 186, 36, 22,
11, 159, 114, 26, 101, 104, 109, 38, 186, 107,
200, 103};
int i;
for(i = 0; i < 32; i++)
{
input[i] ^= str[i % strlen(str)];
input[i] ^= Xor[i];
cout << char(input[i]);
}
return 0;
}

得到flag:Y0uaretheB3st!#@_VirtualCC

[羊城杯 2020]login

是一个简单的pyinstaller逆向,下载这个,在命令行里输入
python pyinstxtractor.py attachment.exe
会得到一个文件夹,在文件夹里找到login.pyc,把它传到这里就可以了。
是个简单的异或和z3,得到flag:58964088b637e50d3a22b9510c1d1ef8

[QCTF2018]Xman-babymips

简单的异或加密,注意要取后两位,即x & 0xFF。
得到flag:ReA11y_4_B@89_mlp5_4_XmAn_

[安洵杯 2019]crackMe

很牛逼的一道题。
根据findcrypt插件或者运行后搜索弹出的提示词可以找到:

上面一大段是对base表进行更改,下面一段看不懂。跟进得到:

其中sub_411172()是生成SM4加密所需轮秘钥(RK)。查看RK的交叉引用可以得到SM4的加密过程:

跟进TopLevelExceptionFilter可以找到校验部分:

在base64加密过程中有魔改的地方:

即:base表需要向前移动24个,同时最后的等号由叹号代替。
解密得到flag:SM4foRExcepioN?!
官方文档介绍说这个程序在main函数之前hook了MessageBoxW()函数,之后又调用了这个函数。因此在执行MessageBoxW()前先修改base表并创建VEH向量。执行函数后注册SEH并触发异常。在VEH中构造轮秘钥并注册UnhandledExceptionFilter。在SEH中进行SM4加密。在UnhandledExceptionFilter中修改对比结果并进行变种base64。最后执行比较函数。挺好的,都是中国字==我看懂了。

[UTCTF2020]babymips

没啥技术含量。
得到flag:mips_cpp_gang_5VDm:~`N]ze;\)5%vZ=C’C(r#q=\*efD"ZNY_GX>6&sn.wF8v*mvA@’

[GKCTF 2021]QQQQT

根据字符串找到base58加密
得到flag:12t4tww3r5e77

[NPUCTF2020]你好sao啊

这题有意思,需要对给定的数据进行base64加密,然后才可以得到结果
得到flag:w0w+y0U+cAn+r3lllY+dAnc3

[WUSTCTF2020]funnyre

五个花指令和1000+行的屎山,没什么技术含量
得到flag:1dc20f6e3d497d15cef47d9a66d6f1af

[GUET-CTF2019]encrypt

先魔改RC4,再魔改base64:

魔改RC4部分的Sbox可以通过动调get_key()函数获得,sub_4007DB()函数如下:

这个部分比较简单,逆的时候抄下来跑一边就行。而魔改base64如下:

可以发现它采用的base表是以=开头的,依照ascii码递增的64位字符串,即
=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
可以写出代码:

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
#include <iostream>

using namespace std;

int main()
{
int box[999] = {
176, 49, 117, 112, 248, 223, 7, 60, 120, 113, 80, 41, 44, 22, 105, 18, 200, 43, 59, 127, 178,
231, 75, 104, 140, 197, 166, 21, 3, 88, 71, 4, 19, 141, 135, 38, 9, 237, 23, 138, 194, 242, 67, 192,
172, 89, 151, 245, 63, 103, 94, 57, 134, 213, 114, 97, 218, 247, 1, 5, 139, 195, 177, 119, 175, 29,
48, 198, 69, 14, 95, 238, 174, 240, 40, 206, 205, 167, 155, 42, 25, 72, 8, 68, 32, 254, 109, 181,
46, 106, 241, 52, 188, 30, 62, 204, 65, 146, 216, 189, 165, 232, 77, 10, 73, 13, 162, 250, 98, 116,
212, 131, 150, 148, 61, 203, 24, 99, 153, 70, 202, 183, 142, 207, 251, 163, 108, 126, 81, 39, 96,
154, 17, 243, 92, 110, 186, 66, 118, 47, 239, 191, 33, 170, 228, 214, 27, 85, 125, 190, 234, 211,
16, 244, 199, 74, 35, 121, 132, 164, 28, 171, 20, 219, 76, 58, 184, 82, 236, 55, 56, 182, 210, 160,
90, 91, 152, 102, 84, 158, 78, 79, 180, 196, 201, 208, 37, 156, 128, 222, 45, 6, 34, 11, 145, 107,
159, 246, 230, 226, 193, 15, 147, 144, 123, 157, 143, 221, 229, 101, 53, 173, 169, 220, 130, 187,
0, 83, 209, 168, 51, 233, 64, 26, 255, 161, 149, 54, 217, 235, 137, 227, 124, 115, 133, 136, 122,
224, 253, 100, 12, 87, 50, 179, 185, 31, 215, 252, 129, 225, 2, 249, 93, 86, 111, 36
};
int ans[99] = {118, 53, 253, 245, 125, 71, 254, 149, 19, 122, 38, 89, 63, 255, 49, 161, 133, 124, 99, 2, 110, 189, 147, 106, 62, 77, 141, 215, 39, 115, 45, 94, 204, 98, 242, 223, 229, 210, 0};
int i, j = 0, k = 0;
int v7, v8;
for(i = 0; i < 38; i++)
{
j = (j + 1) & 0xff;
v7 = box[j];
k = (k + v7) & 0xff;
v8 = box[k];
box[j] = v8;
box[k] = v7;
ans[i] ^= (box[(v7 + v8) & 0xff] & 0xff);
printf("%c", ans[i]);
}
return 0;
}

得到flag:e10adc3949ba59abbe56e057f20f883e

[WMCTF2020]easy_re

本题采用没听过的perl语言编写,在通过perlapp编译。此类文件会在运行前先将代码解压,然后再执行。
再解压前会有script关键字,同时解压后的代码会存在rax中。通过x64dbg看到:

复制出来是一段perl的代码:

1
2
3
4
5
6
7
8
9
$flag = "WMCTF{I_WAnt_dynam1c_F1ag}";
print "please input the flag:";
$line = <STDIN>;
chomp($line);
if($line eq $flag){
print "congratulation!"
}else{
print "no,wrong"
}

得到flag:I_WAnt_dynam1c_F1ag

[CISCN2018]2ex

魔改base64,但是题目给的输出多了一个"|",不是很理解。
得到flag:change53233

[RoarCTF2019]polyre

很牛逼的一道题。打开看见ollvm混淆,夹杂了控制流平坦化和虚假控制流。采用脚本去除后看见:
P.S.官方题解给出的debcf脚本在IDA7.7中已无法运行,需要自己更新一下,或者用我的。

可以看见它又一些莫名其妙地语句。整体算法发现是可逆的,如果数据大于0,执行后必定为偶数,否则必定为奇数。可以写出代码:

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
#include <iostream>

using namespace std;

int main()
{
unsigned long long ans[10] = {0xbc8ff26d43536296, 0x520100780530ee16, 0x4dc0b5ea935f08ec, 0x342b90afd853f450, 0x8b250ebcaa2c3681, 0x55759f81a2c68ae4};
int i, j;
for(i = 0; i < 6; i++)
{
for(j = 0; j < 64; j++)
{
if(ans[i] & 1)
{
ans[i] = (ans[i] ^ 0xB0004B7679FA26B3) / 2;
ans[i] |= 0x8000000000000000; //强制给它置为负数
}
else
{
ans[i] /= 2;
}
}
for(j = 0; j < 8; j++)
{
printf("%c", ans[i] & 0xff);
ans[i] >>= 8;
}
}
return 0;
}

运行得到flag:6ff29390-6c20-4c56-ba70-a95758e3d1f8

[watevrCTF 2019]Timeout

通过010 editor或者exeinfoPE可以知道这家伙是一个elf文件。把.com后缀删掉拖到IDA里,然后翻一翻各种函数,可以找到flag。
得到flag:3ncrytion_is_overrated_youtube.com/watch?v=OPf0YbXqDm0

[SCTF2019]babyre

非常傻逼的题目。
首先干掉4个花指令。
然后处理三组加密。第一个走迷宫,第二个硬爆破,第三个嗯逆。

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
//爆破
#include <iostream>

using namespace std;

int main()
{
int str[700] = {
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 62, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 63, 0,
0, 0, 52, 0, 0, 0, 53, 0, 0, 0,
54, 0, 0, 0, 55, 0, 0, 0, 56, 0,
0, 0, 57, 0, 0, 0, 58, 0, 0, 0,
59, 0, 0, 0, 60, 0, 0, 0, 61, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 64, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 2, 0,
0, 0, 3, 0, 0, 0, 4, 0, 0, 0,
5, 0, 0, 0, 6, 0, 0, 0, 7, 0,
0, 0, 8, 0, 0, 0, 9, 0, 0, 0,
10, 0, 0, 0, 11, 0, 0, 0, 12, 0,
0, 0, 13, 0, 0, 0, 14, 0, 0, 0,
15, 0, 0, 0, 16, 0, 0, 0, 17, 0,
0, 0, 18, 0, 0, 0, 19, 0, 0, 0,
20, 0, 0, 0, 21, 0, 0, 0, 22, 0,
0, 0, 23, 0, 0, 0, 24, 0, 0, 0,
25, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 26, 0,
0, 0, 27, 0, 0, 0, 28, 0, 0, 0,
29, 0, 0, 0, 30, 0, 0, 0, 31, 0,
0, 0, 32, 0, 0, 0, 33, 0, 0, 0,
34, 0, 0, 0, 35, 0, 0, 0, 36, 0,
0, 0, 37, 0, 0, 0, 38, 0, 0, 0,
39, 0, 0, 0, 40, 0, 0, 0, 41, 0,
0, 0, 42, 0, 0, 0, 43, 0, 0, 0,
44, 0, 0, 0, 45, 0, 0, 0, 46, 0,
0, 0, 47, 0, 0, 0, 48, 0, 0, 0,
49, 0, 0, 0, 50, 0, 0, 0, 51, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0
};
int i, j, k, p, q, r;
int ans[5] = {0x736374, 0x665f39, 0x313032};
for(i = 0; i < 3; i++)
{
for(j = 32; j < 126; j++)
{
for(k = 32; k < 126; k++)
{
for(p = 32; p < 126; p++)
{
for(q = 32; q < 126; q++)
{
if((((((str[j * 4] & 0x3f) << 6) | (str[k * 4] & 0x3f)) << 6 | (str[p * 4] & 0x3f)) << 6 | (str[q * 4] & 0x3f)) == ans[i])
{
if(str[j * 4] != 64 && str[k * 4] != 64 && str[p * 4] != 64 && str[q * 4] != 64)
{
printf("%c%c%c%c", j, k, p, q);
}
}
}
}
}
}
}
return 0;
}
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
//嗯逆
#include <iostream>
#include <cstdlib>

using namespace std;

int __ROL4__(unsigned int x, int k)
{
return (x << k) | (x >> (32 - k));
}

int __ROR4__(unsigned int x, int k)
{
return (x >> k) | (x << (32 - k));
}

int func(int x)
{
int data[2000] = {
214, 0, 0, 0, 144, 0, 0, 0, 233, 0,
0, 0, 254, 0, 0, 0, 204, 0, 0, 0,
225, 0, 0, 0, 61, 0, 0, 0, 183, 0,
0, 0, 22, 0, 0, 0, 182, 0, 0, 0,
20, 0, 0, 0, 194, 0, 0, 0, 40, 0,
0, 0, 251, 0, 0, 0, 44, 0, 0, 0,
5, 0, 0, 0, 43, 0, 0, 0, 103, 0,
0, 0, 154, 0, 0, 0, 118, 0, 0, 0,
42, 0, 0, 0, 190, 0, 0, 0, 4, 0,
0, 0, 195, 0, 0, 0, 170, 0, 0, 0,
68, 0, 0, 0, 19, 0, 0, 0, 38, 0,
0, 0, 73, 0, 0, 0, 134, 0, 0, 0,
6, 0, 0, 0, 153, 0, 0, 0, 156, 0,
0, 0, 66, 0, 0, 0, 80, 0, 0, 0,
244, 0, 0, 0, 145, 0, 0, 0, 239, 0,
0, 0, 152, 0, 0, 0, 122, 0, 0, 0,
51, 0, 0, 0, 84, 0, 0, 0, 11, 0,
0, 0, 67, 0, 0, 0, 237, 0, 0, 0,
207, 0, 0, 0, 172, 0, 0, 0, 98, 0,
0, 0, 228, 0, 0, 0, 179, 0, 0, 0,
28, 0, 0, 0, 169, 0, 0, 0, 201, 0,
0, 0, 8, 0, 0, 0, 232, 0, 0, 0,
149, 0, 0, 0, 128, 0, 0, 0, 223, 0,
0, 0, 148, 0, 0, 0, 250, 0, 0, 0,
117, 0, 0, 0, 143, 0, 0, 0, 63, 0,
0, 0, 166, 0, 0, 0, 71, 0, 0, 0,
7, 0, 0, 0, 167, 0, 0, 0, 252, 0,
0, 0, 243, 0, 0, 0, 115, 0, 0, 0,
23, 0, 0, 0, 186, 0, 0, 0, 131, 0,
0, 0, 89, 0, 0, 0, 60, 0, 0, 0,
25, 0, 0, 0, 230, 0, 0, 0, 133, 0,
0, 0, 79, 0, 0, 0, 168, 0, 0, 0,
104, 0, 0, 0, 107, 0, 0, 0, 129, 0,
0, 0, 178, 0, 0, 0, 113, 0, 0, 0,
100, 0, 0, 0, 218, 0, 0, 0, 139, 0,
0, 0, 248, 0, 0, 0, 235, 0, 0, 0,
15, 0, 0, 0, 75, 0, 0, 0, 112, 0,
0, 0, 86, 0, 0, 0, 157, 0, 0, 0,
53, 0, 0, 0, 30, 0, 0, 0, 36, 0,
0, 0, 14, 0, 0, 0, 94, 0, 0, 0,
99, 0, 0, 0, 88, 0, 0, 0, 209, 0,
0, 0, 162, 0, 0, 0, 37, 0, 0, 0,
34, 0, 0, 0, 124, 0, 0, 0, 59, 0,
0, 0, 1, 0, 0, 0, 33, 0, 0, 0,
120, 0, 0, 0, 135, 0, 0, 0, 212, 0,
0, 0, 0, 0, 0, 0, 70, 0, 0, 0,
87, 0, 0, 0, 159, 0, 0, 0, 211, 0,
0, 0, 39, 0, 0, 0, 82, 0, 0, 0,
76, 0, 0, 0, 54, 0, 0, 0, 2, 0,
0, 0, 231, 0, 0, 0, 160, 0, 0, 0,
196, 0, 0, 0, 200, 0, 0, 0, 158, 0,
0, 0, 234, 0, 0, 0, 191, 0, 0, 0,
138, 0, 0, 0, 210, 0, 0, 0, 64, 0,
0, 0, 199, 0, 0, 0, 56, 0, 0, 0,
181, 0, 0, 0, 163, 0, 0, 0, 247, 0,
0, 0, 242, 0, 0, 0, 206, 0, 0, 0,
249, 0, 0, 0, 97, 0, 0, 0, 21, 0,
0, 0, 161, 0, 0, 0, 224, 0, 0, 0,
174, 0, 0, 0, 93, 0, 0, 0, 164, 0,
0, 0, 155, 0, 0, 0, 52, 0, 0, 0,
26, 0, 0, 0, 85, 0, 0, 0, 173, 0,
0, 0, 147, 0, 0, 0, 50, 0, 0, 0,
48, 0, 0, 0, 245, 0, 0, 0, 140, 0,
0, 0, 177, 0, 0, 0, 227, 0, 0, 0,
29, 0, 0, 0, 246, 0, 0, 0, 226, 0,
0, 0, 46, 0, 0, 0, 130, 0, 0, 0,
102, 0, 0, 0, 202, 0, 0, 0, 96, 0,
0, 0, 192, 0, 0, 0, 41, 0, 0, 0,
35, 0, 0, 0, 171, 0, 0, 0, 13, 0,
0, 0, 83, 0, 0, 0, 78, 0, 0, 0,
111, 0, 0, 0, 213, 0, 0, 0, 219, 0,
0, 0, 55, 0, 0, 0, 69, 0, 0, 0,
222, 0, 0, 0, 253, 0, 0, 0, 142, 0,
0, 0, 47, 0, 0, 0, 3, 0, 0, 0,
255, 0, 0, 0, 106, 0, 0, 0, 114, 0,
0, 0, 109, 0, 0, 0, 108, 0, 0, 0,
91, 0, 0, 0, 81, 0, 0, 0, 141, 0,
0, 0, 27, 0, 0, 0, 175, 0, 0, 0,
146, 0, 0, 0, 187, 0, 0, 0, 221, 0,
0, 0, 188, 0, 0, 0, 127, 0, 0, 0,
17, 0, 0, 0, 217, 0, 0, 0, 92, 0,
0, 0, 65, 0, 0, 0, 31, 0, 0, 0,
16, 0, 0, 0, 90, 0, 0, 0, 216, 0,
0, 0, 10, 0, 0, 0, 193, 0, 0, 0,
49, 0, 0, 0, 136, 0, 0, 0, 165, 0,
0, 0, 205, 0, 0, 0, 123, 0, 0, 0,
189, 0, 0, 0, 45, 0, 0, 0, 116, 0,
0, 0, 208, 0, 0, 0, 18, 0, 0, 0,
184, 0, 0, 0, 229, 0, 0, 0, 180, 0,
0, 0, 176, 0, 0, 0, 137, 0, 0, 0,
105, 0, 0, 0, 151, 0, 0, 0, 74, 0,
0, 0, 12, 0, 0, 0, 150, 0, 0, 0,
119, 0, 0, 0, 126, 0, 0, 0, 101, 0,
0, 0, 185, 0, 0, 0, 241, 0, 0, 0,
9, 0, 0, 0, 197, 0, 0, 0, 110, 0,
0, 0, 198, 0, 0, 0, 132, 0, 0, 0,
24, 0, 0, 0, 240, 0, 0, 0, 125, 0,
0, 0, 236, 0, 0, 0, 58, 0, 0, 0,
220, 0, 0, 0, 77, 0, 0, 0, 32, 0,
0, 0, 121, 0, 0, 0, 238, 0, 0, 0,
95, 0, 0, 0, 62, 0, 0, 0, 215, 0,
0, 0, 203, 0, 0, 0, 57, 0, 0, 0,
72, 0, 0, 0, 198, 0, 0, 0, 186, 0,
0, 0, 177, 0, 0, 0, 163, 0, 0, 0,
80, 0, 0, 0, 51, 0, 0, 0, 170, 0,
0, 0, 86, 0, 0, 0, 151, 0, 0, 0,
145, 0, 0, 0, 125, 0, 0, 0, 103, 0,
0, 0, 220, 0, 0, 0, 34, 0, 0, 0,
112, 0, 0, 0, 178, 0, 0, 0, 0, 0
};
int v2;
v2 = data[(x & 0xff) * 4];
x >>= 8;
v2 |= data[(x & 0xff) * 4] << 8;
x >>= 8;
v2 |= data[(x & 0xff) * 4] << 16;
x >>= 8;
v2 |= data[(x & 0xff) * 4] << 24;
return __ROL4__(v2, 12) ^ (__ROL4__(v2, 8) ^ __ROR4__(v2, 2)) ^ __ROR4__(v2, 6);
}

int main()
{
int ans[20];
ans[0] = 190;
ans[1] = 4;
ans[2] = 6;
ans[3] = 128;
ans[4] = 197;
ans[5] = 175;
ans[6] = 118;
ans[7] = 71;
ans[8] = 159;
ans[9] = 204;
ans[10] = 64;
ans[11] = 31;
ans[12] = 216;
ans[13] = 191;
ans[14] = 146;
ans[15] = 239;
int loop[50];
int i;
for(i = 0; i < 4; i++)
{
loop[i] = (ans[12 - i * 4] << 24) + (ans[12 - 4 * i + 1] << 16) + (ans[12 - i * 4 + 2] << 8) + ans[12 - i * 4 + 3];
}
for(i = 4; i < 30; i++)
{
loop[i] = loop[i - 4] ^ func(loop[i - 3] ^ loop[i - 2] ^ loop[i - 1]);
}
for (int i = 29; i >= 26; i--)
{
printf("%c%c%c%c", ((char*)&loop[i])[0], ((char*)&loop[i])[1], ((char*)&loop[i])[2], ((char*)&loop[i])[3]);
}
return 0;
}

血的教训:写循环位移要定义成unsigned类型。不unsigned的话,右移时整数补0,负数补1,然后就错啦!
得到flag:ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)

[ACTF新生赛2020]fungame

题出的很好,下次别出了。
见面看到一个加密,这个很简单。

解出来交上去就可以发现错啦!
看看下一个函数:

这个函数很神奇,它把16字节的东西赋值给了12字节的空间,这会造成栈溢出。对,这其实是一个pwn题。
跟踪x可以发现

于是解得flag:Re_1s_So0_funny!=#@a1s0_pWn

[羊城杯 2020]Bytecode

傻逼python字节码,有种硬读汇编的美。抄一个脚本。

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
from z3 import *
en = [3, 37, 72, 9, 6, 132]
output = [101, 96, 23, 68, 112, 42, 107, 62, 96, 53, 176, 179, 98, 53, 67, 29, 41, 120, 60, 106, 51, 101, 178, 189, 101, 48]
flag = ''
s = Solver()
a1 = Int('a1')
a2 = Int('a2')
a3 = Int('a3')
a4 = Int('a4')
a5 = Int('a5')
a6 = Int('a6')
s.add(a1 * 3 + a2 * 2 + a3 * 5 == 1003)
s.add(a1 * 4 + a2 * 7 + a3 * 9 == 2013)
s.add(a1 + a2 * 8 + a3 * 2 == 1109)
s.add(a4 * 3 + a5 * 2 + a6 * 5 == 671)
s.add(a4 * 4 + a5 * 7 + a6 * 9 == 1252)
s.add(a4 + a5 * 8 + a6 * 2 == 644)
if s.check():
print(s.model())
k=0
for i in range(13):
flag += chr(output[k+1] ^ en[i%6])
flag += chr(output[k] ^ en[i%6])
k = k + 2
s = [97, 101, 102, 102, 55, 51]
for i in range(6):
flag += chr(s[i])
print(flag)

得到flag:cfa2b87b3f746a8f0ac5c5963faeff73

[FlareOn2]very_success

打开一看函数这么少还以为它加壳了呢,没想到它就是这么少。

逻辑很简单,通过动调可以发现v11始终为1。写出脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
int Xor[100] = {
170, 236, 164, 186, 175, 174, 170, 138, 192, 167,
176, 188, 154, 186, 165, 165, 186, 175, 184, 157,
184, 249, 174, 157, 171, 180, 188, 182, 179, 144,
154, 168
};
int i, t = 0, last = 0;
for(i = 0; i < 32; i++)
{
printf("%c", (Xor[32 - i - 1] - (1 << (last & 3)) - 1) ^ 0xC7);
last += Xor[32 - i - 1];
}
return 0;
}

得到flag:a_Little_b1t_harder_plez@flare-on.com

[2019红帽杯]Snake

Unity3D的游戏,去找Assembly-CSharp.dll。可以看见他调用了一个外部的Interface.dll。

在Snake_Data/Plugins文件夹下可以找到对应dll。用IDA打开,找到flag关键字的位置可以看见一坨非常抽象的东西,nnd看不懂一点。

去搜的大佬的博客知道这个东西可以爆破。python的ctypes库允许爆破dll文件中的特定函数。

1
2
3
4
5
6
import ctypes

for i in range(100):
dll = ctypes.cdll.LoadLibrary("./Interface.dll")
print(i)
dll.GameObject(i)

在地19次是可以得到flag:Ch4rp_W1th_R$@
从结果来看,这个b用C++写了一个RSA…只能说不愧是红帽杯…

[SCTF2019]Strange apk

安卓的SMC。打开看见程序的入口点应该是sctf.demo.myapplication.t,但是找不到。

看见这个里面存在代码西修改的部分。它将data文件经过__()函数和_()函数解密(都tm什么函数名字)。

有用的解密代码部分:

写脚本运行

1
2
3
4
5
6
7
8
def get_byte(v):
return v.to_bytes(1, byteorder='little')

with open('data', 'rb') as f:
a = f.read()
with open('sctf.apk', 'wb') as g:
for i in range(len(a)):
g.write(get_byte(a[i] ^ ord("syclover"[i % 8])))

打开解密后的文件,这回找得到t部分了,但是看不懂。不过他总共就t、s、f三个有用的,找一找可以才出来加密的流程。下面这一部分在s里。前12个字符由f.sctf()函数加密。

后18个字符由f.encode()函数加密。

后半部分加密后的东西在t中

挺离谱的,我不理解为什么会是这么个执行流程。看大佬的博客说跟安卓的父子组件通信机制有关。
得到flag:W3lc0me~t0_An4r0id-w0rld

[安洵杯 2019]game

OLLVM代码混淆。用脚本除了trace()函数之外都可以反混淆。但是只有三个check函数是有意义的,其余的不用管它。
check1()函数对输入进行了一波操作:

check3()函数就是将输入复制到Dog3[]里再与sudoku[]对比。

因此,我们可以让程序运行起来,下断点之后将sudoku的内容提取出来。

接下来就是写一个脚本的过程:

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

using namespace std;

int main()
{
int sudoku[81] = {1,4,5,3,2,7,6,9,8,8,3,9,6,5,4,1,2,7,6,7,2,8,1,9,5,4,3,4,9,6,1,8,5,3,7,2,2,1,8,4,7,3,9,5,6,7,5,3,2,9,6,4,8,1,3,6,7,5,4,2,8,1,9,9,8,4,7,6,1,2,3,5,5,2,1,9,3,8,7,6,4};
int Dog3[81] = {1,0,5,3,2,7,0,0,8,8,0,9,0,5,0,0,2,0,0,7,0,0,1,0,5,0,3,4,9,0,1,0,0,3,0,0,0,1,0,0,7,0,9,0,6,7,0,3,2,9,0,4,8,0,0,6,0,5,4,0,8,0,9,0,0,4,0,0,1,0,3,0,0,2,1,0,3,0,7,0,4};
int input[81] = {};
int i, cnt = 0, tmp;
for(i = 0; i < 81; i++)
{
if(Dog3[i] == 0)
{
input[cnt] = sudoku[i] + 48;
cnt++;
}
}
for(i = 0; i < cnt; i++)
{
input[i] += 20;
input[i] = (input[i] & 0xF3 | ~input[i] & 0xC);
}
for(i = 0; i < cnt; i += 2)
{
tmp = input[i];
input[i] = input[i+1];
input[i+1] = tmp;
}
cnt = 20;
for(i = 0; i < 20; i++)
{
tmp = input[i];
input[i] = input[cnt];
input[cnt] = tmp;
cnt++;
}
for(i = 0; i < 40; i++)
{
printf("%c", input[i]);
}
return 0;
}

运行脚本得到flag:KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J

[GWCTF 2019]babyvm

最瞧不起这种真flag在本地打不通,但假flag能打通的题目。

传统虚拟机逆向。尽管它在执行VMrun函数的时候会跳转到VMrun_another函数很神奇,但并不影响我瞧不起它。我觉得很奇怪,你明明能做到这个,为什么不将VMrun_another函数改成真正需要选手破解的那一段。然后再把VMcheck执行的时候用同样的技术跳转到VMcheck_another函数。这样不就是一个在本地能打通的题目了???

没什么可说的,解题脚本如下:

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
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
int enc[30] = {105, 69, 42, 55, 9, 23, 197, 11, 92, 114, 51, 118, 51, 33, 116, 49, 95, 51, 115, 114};
int i, j, tmp;
for(i = 13; i <= 15; i++)
{
tmp = enc[i];
enc[i] = enc[32-i];
enc[32-i] = tmp;
}
for(i = 8; i >= 6; i--)
{
for(j = 32; j < 127; j++)
{
if((((j*3 + enc[i+1]*2 + enc[i+2]) * enc[12]) & 0xff) == enc[i])
{
enc[i] = j;
break;
}
}
}
for(i = 5; i >= 0; i--)
{
enc[i] ^= enc[i+1];
}
for(i = 0; i < 20; i++)
{
printf("%c", enc[i]);
}
return 0;
}

运行得到flag:Y0u_hav3_r3v3rs3_1t!

[网鼎杯 2020 青龙组]bang

安卓脱壳。
使用这个脚本查壳,然后使用模拟器和frida脱壳。

脱壳后会生成两个dex文件,使用dex2jar工具将其转为jar文件。再使用jadx-jui打开jar文件。

可以找到flag:borring_things

[SUCTF2018]babyre

这个题不好评价。输入key,正确的key会吐出flag。直接爆破。

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
#include <iostream>

using namespace std;

int main()
{
int v7[240] = {2, 3, 2, 1, 4, 7, 4, 5, 10, 11, 10, 9, 14, 15, 12, 13, 16, 19, 16, 17, 20, 23, 22, 19, 28, 25, 30, 31, 28, 25, 26, 31, 36, 33, 34, 39, 36, 33, 34, 35, 40, 41, 46, 43, 36, 45, 38, 47, 56, 49, 58, 59, 52, 61, 62, 55, 48, 57, 50, 59, 60, 53, 54, 55, 72, 73, 66, 66, 68, 68, 70, 71, 72, 73, 74, 74, 77, 77, 79, 78, 80, 80, 82, 83, 85, 84, 86, 87, 89, 89, 90, 91, 92, 93, 94, 94, 96, 96, 99, 99, 100, 101, 103, 103, 105, 105, 107, 107, 108, 109, 110, 110, 112, 112, 114, 115, 116, 117, 119, 119, 120, 121, 123, 123, 125, 125, 127, 127, -127, -127, -125, -125, -116, -115, -114, -113, -120, -119, -118, -117, -116, -115, -114, -121, -104, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -102, -100, -100, -98, -98, -96, -96, -94, -94, -92, -92, -90, -90, -88, -88, -86, -86, -84, -84, -82, -82, -80, -79, -78, -77};
int key, j, v12, v13;
int v9[60];
for(key = 0; key <= 0x10000; key++)
{
for(j = 0; j < 30; j++)
{
v9[j] = 0;
}
v9[30] = 8;
while(v9[30])
{
--v9[30];
for (j = 22; j; v9[j] = (v9[j] | (v13 << v9[30])) & 0xff)
{
v12 = v7[22 * v9[30] + --j];
v13 = (v12 >> ((key >> (2 * v9[30])) & 3)) & 1;
}
}
if(v9[0] == 'S' && v9[1] == 'U' && v9[2] == 'C' && v9[3] == 'T' && v9[4] == 'F')
{
for(j = 0; j < 30; j++)
{
printf("%c", v9[j]);
}
puts("");
}
}
return 0;
}

运行得到flag:Flag_8i7244980f

[HDCTF2019]MFC

好神奇的东西。看见vmp壳的时候想着可以学学怎么脱了,然后一搜发现都是在教怎么破mfc。没事,反正mfc也没见过。
运行程序看见:

将xspy的放大镜拖到这个框上面可以得到:

发现其中的0x0464这条消息不是系统的,写脚本发送消息。此处要用Visual Studio。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<Windows.h>
#include<iostream>

using namespace std;

int main()
{
HWND h = FindWindow(0, "Flag就在控件里");
if (h != 0)
{
SendMessageA(h, 0x464, 0, 0);
printf("success");
}
else
{
printf("failure");
}
}

之后得到:

将xspy上方的数据拿出来解DES可以得到:

可以得到flag:thIs_Is_real_kEy_hahaaa

拖到jadx中,在r2d2方法里找到base64,解出来转成图片:

用JEB的话它会吞掉一部分字符串,用[...]代替,很傻逼。
得到flag:PUCKMAN

[SCTF2019]creakme

做的时候很痛苦,做完了觉得痛苦的原因应该还是自己太菜了。。。
打开看见:

值得关注的是sub_402320()和sub_4024A0()。首先看sub_402420():

可以发现程序自己使用DebugBreak陷入异常,同时使用SEH执行sub_402450()。
再看sub_4024A0(),这个函数将某一段数据作为代码执行了,可以推测sub_402320()是在做相应的SMC。反正没到输入,修改后的代码在干啥其实不用管。
将这两个函数中的反调试跳转语句稍作修改,再动调并根据FindCrypt插件我们可以发现encrypt()函数中的AES和Base64:

这时我们可以发现原先的对比数据>pvfqYc,4tTc2UxRmlJ,sB{Fh4Ck2:CFOb4ErhtIcoLo
变成了nKnbHsgqD3aNEB91jB3gEzAr+IklQwT1bSs3+bXpeuo=
然后我们就可以请出CyberChef了:

BAKE得到flag:Ae3_C8c_I28_pKcs79ad4

[MRCTF2020]EasyCpp

只能说,不愧是CPP。
整个程序没有添加各种奇奇怪怪的东西,只看main即可。先看这一部分:

总结一下就是它输入了9个int数。这道题它虽然用到了vector,但是并没有做什么有关vector的高级操作,完全可以用数组代替。
然后是接下来的加密部分:

代码挺神奇的,还是第一次知道lambda表达式在程序里面长这个样子。要注意depart()函数拆完后拼接的时,会在每一个质数前加一个空格。这也就是为什么要对比的数据里面有很多等于号。开始做的时候没注意这一点,于是就在不停地想,它到底是怎么把数字给弄成空格的。。。
然后是对比部分,可以动调一下找到:

数据量不大,手搓就行了,然后异或1回去,得到2345 1222 5774 2476 3374 9032 2456 3531 6720,再输入给程序。
运行程序得到flag:4367FB5F42C6E46B2AF79BF409FB84D3

[SCTF2019]Who is he

挺神奇的一道题,不是很理解它怎么做到的,网上也没能搜到相应的解释。
常规地,进入Assembly-CSharp.dll查找相关代码,很简单的base64+DES。
解出He_P1ay_Basketball_Very_We11!Hahahahaha!但提交发现这是错的。
用CheatEngine可以搜索到额外的几组密文:

写一个解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import base64
from Crypto.Cipher import DES

key = b"t\x00e\x00s\x00t\x00"
des = DES.new(key, mode = DES.MODE_CBC, iv = key)
cipher = b"78 00 5A 00 57 00 44 00 5A 00 61 00 4B 00 45 00 68 00 57 00 4E 00 4D 00 43 00 62 00 69 00 47 00 59 00 50 00 42 00 49 00 6C 00 59 00 33 00 2B 00 61 00 72 00 6F 00 7A 00 4F 00 39 00 7A 00 6F 00 6E 00 77 00 72 00 59 00 4C 00 69 00 56 00 4C 00 34 00 6E 00 6A 00 53 00 65 00 7A 00 32 00 52 00 59 00 4D 00 32 00 57 00 77 00 73 00 47 00 6E 00 73 00 6E 00 6A 00 43 00 44 00 6E 00 48 00 73 00 37 00 4E 00 34 00 33 00 61 00 46 00 76 00 4E 00 45 00 35 00 34 00 6E 00 6F 00 53 00 61 00 64 00 50 00 39 00 46 00 38 00 65 00 45 00 70 00 76 00 54 00 73 00 35 00 51 00 50 00 47 00 2B 00 4B 00 4C 00 30 00 54 00 44 00 45 00 2F 00 34 00 30 00 6E 00 62 00 55 00 3D 00"

c = b''
for i in range(0, len(cipher), 6):
if cipher[i] >= 48 and cipher[i] <= 57:
t = (cipher[i] - 48) * 16
else:
t = (cipher[i] - 65 + 10) * 16
if cipher[i+1] >= 48 and cipher[i+1] <= 57:
t += cipher[i+1] - 48
else:
t += cipher[i+1] - 65 + 10
c += chr(t).encode()

cipher = base64.b64decode(c)
plain = des.decrypt(cipher)[0:-6].decode("utf-16")
print(plain)

解得flag:She_P1ay_Black_Hole_Very_Wel1!LOL!XD!
不知道它是怎么把这些东西藏起来的。。。

[NPUCTF2020]Baby Obfuscation

看题目名字我还以为能够用脚本一把梭呢。。。结果是做着自己写的混淆。。。
整个代码分析一遍可以得到下面的样子:

其中A_minus_B()函数非常有意思,~(~a + b)等价于a-b
然后就可以写出解题脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
int data[20] = {0, 7801, 7801, 8501, 5901, 8001, 6401, 11501, 4601, 9801, 9601, 11701, 5301, 9701, 10801, 12501, 0};
int A0X4[5] = {2, 3, 4, 5};
int i;
for(i = 1; i < 16; i++)
{
data[i] = (data[i] - 1) / 10;
}
for(i = 1; i < 16; i++)
{
data[i] /= 10;
data[i] ^= A0X4[(i-1) % 4];
data[i] = ~((~data[i]) - A0X4[(i-1) % 4]);
printf("%c", data[i]);
}
return 0;
}

运行得到flag:0bfu5er

[NPUCTF2020]BasicASM

纯汇编,还是CPP的汇编。。。没什么技术含量。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
char ans[100] = "662e61257b26301d7972751d6b2c6f355f3a38742d74341d61776d7d7d";
int data[50];
int i;
for(i = 0; i < strlen(ans) / 2; i++)
{
data[i] = (ans[i*2] >= '0' && ans[i*2] <= '9') ? ans[i*2] - '0' : ans[i*2] - 'a' + 10;
data[i] = (data[i] * 16) + ((ans[i*2+1] >= '0' && ans[i*2+1] <= '9') ? ans[i*2+1] - '0' : ans[i*2+1] - 'a' + 10);
if(i & 1)
{
data[i] ^= 0x42;
}
printf("%c", data[i]);
}
return 0;
}

运行得到flag:d0_y0u_know_x86-64_a5m?

[ACTF新生赛2020]Splendid_MineCraft

一个劲往下动调就行了。加了有SMC,再加上跳转是依靠jmp寄存器进行的,伪代码可读性其实不高,不如汇编流程图来得实在。可以写出这么个代码:

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 <iostream>
#include <cstring>

using namespace std;

int main()
{
printf("ACTF{");

char v3[10] = "Welcome ";
char v4[10] = "3@1b;b";
int i, j;
for(i = 0; i < 6; i++)
{
printf("%c", (v3[i+1] ^ v4[i]) + 35);
}
printf("_");

int data[0x100] = {246, 163, 91, 157, 224, 149, 152, 104, 140, 101, 187, 118, 137, 212, 9, 253, 243, 92, 60, 76, 54, 142, 77, 196, 128, 68, 214, 169, 1, 50, 119, 41, 144, 188, 192, 168, 216, 249, 225, 29, 228, 103, 125, 42, 44, 89, 158, 61, 122, 52, 17, 67, 116, 209, 98, 96, 2, 75, 174, 153, 87, 198, 115, 176, 51, 24, 43, 254, 185, 133, 182, 217, 222, 123, 207, 79, 179, 213, 8, 124, 10, 113, 18, 6, 55, 255, 127, 183, 70, 66, 37, 201, 208, 80, 82, 206, 189, 108, 229, 111, 165, 21, 237, 100, 240, 35, 53, 231, 12, 97, 164, 215, 81, 117, 154, 242, 30, 235, 88, 241, 148, 195, 47, 86, 247, 230, 134, 71, 251, 131, 94, 204, 33, 74, 36, 7, 28, 138, 90, 23, 27, 218, 236, 56, 14, 126, 180, 72, 136, 244, 184, 39, 145, 0, 19, 151, 190, 83, 194, 232, 234, 26, 233, 45, 20, 11, 191, 181, 64, 121, 210, 62, 25, 93, 248, 105, 57, 95, 219, 250, 178, 139, 110, 162, 223, 22, 226, 99, 177, 32, 203, 186, 238, 141, 170, 200, 199, 197, 5, 102, 109, 58, 69, 114, 13, 202, 132, 78, 245, 49, 107, 146, 220, 221, 156, 63, 85, 150, 161, 159, 205, 155, 227, 160, 167, 252, 193, 120, 16, 46, 130, 143, 48, 84, 4, 172, 65, 147, 211, 59, 239, 3, 129, 112, 166, 31, 34, 38, 40, 106, 171, 135, 173, 73, 15, 175};
int num[10] = {48, 4, 4, 3, 48, 99};
for(i = 0; i < 6; i++)
{
for(j = 0; j < 0x100; j++)
{
if(data[j] == num[i])
{
printf("%c", j ^ (i + 0x83));
}
}
}

printf("_5mcsM<}");
return 0;
}

得到flag:yOu0y*_knowo3_5mcsM<