GameMaster

C#逆向,用dnspy打开.exe文件,找到入口点main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static void Main(string[] args)
{
ConfigurationManager.AppSettings.Set("microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck", "true");
FileStream fileStream = File.OpenRead("gamemessage");
int num = (int)fileStream.Length;
Program.memory = new byte[num];
fileStream.Position = 0L;
fileStream.Read(Program.memory, 0, num);
Console.Title = "♠ Blackjack Game" + new string(' ', 11) + "...Based on Konstantin Tarkus' code";
Console.BufferWidth = (Console.WindowWidth = 70);
Console.BufferHeight = (Console.WindowHeight = 26);
Console.CursorVisible = false;
ArrayList arrayList = new ArrayList();
Game game = new Game();
game.Player.BalanceChanged += Program.OnBalanceChanged;
game.LastStateChanged += Program.OnLastStateChanged;
game.AllowedActionsChanged += Program.OnAllowedActionsChanged;
game.Dealer.Hand.Changed += Program.OnHandChanged;
game.Player.Hand.Changed += Program.OnHandChanged;
game.Play(20m, 5m);

注意到文件一开始读入的一个文件gamemessage,然后把它存到一个叫Program.memory中,接着往下浏览,发现没有什么关键代码,只是一些纸牌操作,考虑gamemessage的大小为12KB,且不可运行,用010editor打开也是乱码,应该是通过某种加密得到的,这个加密应该是对Program.memory来进行操作的。

往下翻阅main函数代码,进入verifyCode函数

1661213933295

1
2
3
4
5
6
7
8
9
10
private static void verifyCode(ArrayList arrayList, Game game)
{
string str = "";
for (int i = 0; i < arrayList.Count; i++)
{
str += arrayList[i].ToString()[0].ToString();
}
Program.goldFunc(arrayList, game);
arrayList.Clear();
}

发现该函数调用了goldFunc函数,继续跟进,发现一堆if里面有俩疑似加密代码

1661214112385

发现是对Program.memory进行一个异或34的操作。

1661214755365

这玩意有Mode,而且也是对Program.memory进行的操作,应该是AES的ECB加密。考虑写脚本先解密gamemessage

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

file = open("gamemessage", "rb")
data = file.read()
dataArr = [i for i in data]

for i in range(len(dataArr)):
dataArr[i] ^= 34
data = bytes(dataArr)

key = [66, 114, 97, 105, 110, 115, 116, 111, 114, 109, 105, 110, 103, 33, 33, 33]
key = bytes(key)

cipher = AES.new(key, AES.MODE_ECB)

tmp = cipher.decrypt(data)

outfile = open("gamedata", "wb")
outfile.write(tmp)

输出文件gamedata就是解密后的文件,我们再用010editor打开,发现两个熟悉的字符

1661215402291

考虑把前面的内容删除,这还是一个.net文件,接着扔进dnspy打开,找到T1这个类

1661215614862

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
using System;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace T1Class
{
// Token: 0x02000002 RID: 2
public class T1
{
// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
private static void Check1(ulong x, ulong y, ulong z, byte[] KeyStream)
{
int num = -1;
for (int i = 0; i < 320; i++)
{
x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1UL) | x << 1);
y = (((y >> 30 ^ y >> 27) & 1UL) | y << 1);
z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1UL) | z << 1);
bool flag = i % 8 == 0;
if (flag)
{
num++;
}
KeyStream[num] = (byte)((long)((long)KeyStream[num] << 1) | (long)((ulong)((uint)((z >> 32 & 1UL & (x >> 30 & 1UL)) ^ (((z >> 32 & 1UL) ^ 1UL) & (y >> 31 & 1UL))))));
}
}

// Token: 0x06000002 RID: 2 RVA: 0x00002110 File Offset: 0x00000310
private static void ParseKey(ulong[] L, byte[] Key)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
Key[i * 4 + j] = (byte)(L[i] >> j * 8 & 255UL);
}
}
}

// Token: 0x06000003 RID: 3 RVA: 0x0000215C File Offset: 0x0000035C
public T1()
{
try
{
string environmentVariable = Environment.GetEnvironmentVariable("AchivePoint1");
string environmentVariable2 = Environment.GetEnvironmentVariable("AchivePoint2");
string environmentVariable3 = Environment.GetEnvironmentVariable("AchivePoint3");
bool flag = environmentVariable == null || environmentVariable2 == null || environmentVariable3 == null;
if (!flag)
{
ulong num = ulong.Parse(environmentVariable);
ulong num2 = ulong.Parse(environmentVariable2);
ulong num3 = ulong.Parse(environmentVariable3);
ulong[] array = new ulong[3];
byte[] array2 = new byte[40];
byte[] array3 = new byte[40];
byte[] array4 = new byte[12];
byte[] first = new byte[]
{
101,
5,
80,
213,
163,
26,
59,
38,
19,
6,
173,
189,
198,
166,
140,
183,
42,
247,
223,
24,
106,
20,
145,
37,
24,
7,
22,
191,
110,
179,
227,
5,
62,
9,
13,
17,
65,
22,
37,
5
};
byte[] array5 = new byte[]
{
60,
100,
36,
86,
51,
251,
167,
108,
116,
245,
207,
223,
40,
103,
34,
62,
22,
251,
227
};
array[0] = num;
array[1] = num2;
array[2] = num3;
T1.Check1(array[0], array[1], array[2], array2);
bool flag2 = first.SequenceEqual(array2);
if (flag2)
{
T1.ParseKey(array, array4);
for (int i = 0; i < array5.Length; i++)
{
array5[i] ^= array4[i % array4.Length];
}
MessageBox.Show("flag{" + Encoding.Default.GetString(array5) + "}", "Congratulations!", MessageBoxButtons.OK);
}
}
}
catch (Exception)
{
}
}
}
}

发现这个加密函数没法直接逆,考虑到check1中的keystream已经知道了,所以用z3直接解x, y, z,然后再扔到ParseKey函数中即可得到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
from z3 import *
first = [101, 5, 80, 213, 163, 26, 59, 38, 19, 6, 173, 189, 198, 166, 140, 183, 42, 247, 223, 24, 106, 20, 145, 37, 24, 7, 22, 191, 110, 179, 227, 5, 62, 9, 13, 17, 65, 22, 37, 5]
data = [60, 100, 36, 86, 51, 251, 167, 108, 116, 245, 207, 223, 40, 103, 34, 62, 22, 251, 227]
solver = Solver()
xx = BitVec('xx', 64)
yy = BitVec('yy', 64)
zz = BitVec('zz', 64)
x = xx
y = yy
z = zz
keystream = [BitVec('key%d' % i, 8) for i in range(len(first))]
num = -1
for i in range(320):
x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1) | x << 1)
y = (((y >> 30 ^ y >> 27) & 1) | y << 1)
z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1) | z << 1)
if(i % 8 == 0):
num += 1
keystream[num] = ZeroExt(56, keystream[num]) # 由于C#是无符号64位,所以这里需要扩展56位
keystream[num] = ((keystream[num] << 1) | ( (z >> 32 & 1 & (x >> 30 & 1)) ^ (((z >> 32 & 1) ^ 1) & (y >> 31 & 1)))) & 0xFF
for i in range(len(first)):
solver.add(first[i] == keystream[i])
if solver.check() == sat:
m = solver.model()
ans = [m[xx].as_long(), m[yy].as_long(), m[zz].as_long()]

key = [0 for i in range(12)]

for i in range(3):
for j in range(4):
key[i * 4 + j] = (ans[i] >> j * 8 & 255)

for i in range(len(data)):
print(chr(data[i] ^ key[i % len(key)]), end = "")

1661215960473

运行得到flag{Y0u_@re_G3meM3s7er!}

EasyRe

先用finger跑一下恢复符号表,可以看到调用了一个fork函数,这个fork函数应该就是sub_402150中的那个ELF文件,名字叫re3,我们可以dump出来或者直接上动调然后让程序自动生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 __fastcall sub_4021BB(__int64 a1, __int64 a2)
{
__int64 v2; // r8
__int64 v3; // r9
int v4; // er8
int v5; // er9
__int64 v7; // [rsp+0h] [rbp-20h]
unsigned int pid; // [rsp+1Ch] [rbp-4h]

sub_402150();
id = fork(); // 子线程
if ( id )
{
sub_401F2F(id);
remove("re3");
}
else
{
ptrace(0LL, 0LL, 0LL, 0LL, v2, v3, a2); // 和主进程进行通信
sub_44B680((unsigned int)"re3", (unsigned int)"re3", *(_QWORD *)(v7 + 8), 0, v4, v5, v7);
}
return 0LL;
}

fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

(1)在父进程中,fork返回新创建子进程的进程ID;

(2)在子进程中,fork返回0;

(3)如果出现错误,fork返回一个负值。

在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

引用一位网友的话来解释fork函数返回的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的fork函数返回的值指向子进程的进程id, 因为子进程没有子进程,所以其fork函数返回的值为0.

那么main函数的逻辑就是:

fork函数在主进程返回的是子进程的ID,在子进程返回的是0

  • 所以在主进程我们会进入 if 分支
  • 在子进程会进入 else 分支

else分支有一个ptrace )函数,是用于子线程和主线程之间通的通信,其最重要的是第一个参数,我们按M可以找到其枚举PTRACE_TRACEME

主进程内容比较复杂,先看前面的几行代码

1
2
3
4
5
6
7
wait(a1, 0LL, 0);
ptrace(PTRACE_CONT, a1, 0LL, 0LL, v1, v2, v16);
v27 = sub_401A30(a1);
v3 = sub_4017E5();
result = v4;
v20 = v3;
v21 = v4;

发现首先是一个wait,等待子进程发送某信息才开始继续执行下面的内容,然后是sub_4017E5中有一串字符值得注意

1661652891326

1661652905436

这个暂时还不知道有啥用,先放在一边,下面分析子进程。

1661487700392

对这个这个没分析的数据按两下C再按P就能找到start函数,main函数也是一堆未分析的数据1661487772349

对着第一行按个C就能全分析出来了,再按P,得到C代码

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
v14 = 1;
if ( a1 != 2 )
return 0LL;
sub_2490(a2[1]);
((void (__fastcall *)(_BYTE *, char *, char *))loc_21F9)(byte_55C0, v9, v8);
v13 = _setjmp(env);
if ( v13 <= 24 )
{
v11 = _setjmp(v5);
if ( v11 < byte_50A0[25 * v13] )
{
if ( byte_50A0[25 * v13 + 1 + v11] != *((char *)&savedregs + 25 * v13 + v11 - 0x28F) )
v14 = 0;
longjmp(v5, v11 + 1);
}
longjmp(env, v13 + 1);
}
v12 = _setjmp(v6);
if ( v12 <= 24 )
{
v10 = _setjmp(v7);
if ( v10 < byte_5320[25 * v12] )
{
if ( byte_5320[25 * v12 + 1 + v10] != *((char *)&savedregs + 25 * v12 + v10 - 0x50F) )
v14 = 0;
longjmp(v7, v10 + 1);
}
longjmp(v6, v12 + 1);
}
if ( v14 == 1 )
puts("GOOD JOB");
return 0LL;

第一个sub2090可以直接构造函数反编译

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
int __fastcall sub_2490(const char *a1)
{
ssize_t v1; // rax
unsigned __int8 buf; // [rsp+13h] [rbp-Dh] BYREF
int fd; // [rsp+14h] [rbp-Ch]
int j; // [rsp+18h] [rbp-8h]
int i; // [rsp+1Ch] [rbp-4h]

LODWORD(v1) = open(a1, 0);
fd = v1;
if ( (int)v1 >= 0 )
{
for ( i = 0; i <= 24; ++i )
{
for ( j = 0; j <= 24; ++j )
{
v1 = read(fd, &buf, 1uLL);
if ( v1 != 1 )
return v1;
LODWORD(v1) = buf;
if ( (char)buf <= 47 )
return v1;
LODWORD(v1) = buf;
if ( (char)buf > 49 )
return v1;
byte_55C0[25 * i + j] = buf - 48;
}
v1 = read(fd, &buf, 1uLL);
if ( v1 != 1 )
return v1;
}
LODWORD(v1) = close(fd);
}
return v1;
}

发现是一个判断输入(长度为25*25)是否是'0''1',并且把这个输入减去'0'存到byte_55C0里面,即输入一串二进制数存到55C0中。

但是loc_21F9却无法修复应该是一个SMC,但是由于这个dump出来的文件没法直接动调,应该是主进程中的某一步给这个子进程进行了调试然后又用了某些操作把这个SMC解出来的东西又给加密了,但是留意到后面*((char *)&savedregs + 25 * v13 + v11 - 0x28F)*((char *)&savedregs + 25 * v12 + v10 - 0x50F)分别减去了0x28F和0x50F,这里是一个寄存器混淆,由于savedregs的基址是0,我们减去0x28F和0x50F,分别可以算到之前的v8和v9两个地址上。

1661602640215

所以这里的loc_21F9应该是把输入读入进去,出来v8和v9进入后面的check,那么这个函数应该就是关键的加密函数。

1661603695628

可以看到这里有一个int 3,异常断点,同时又发现这个0CAFEB055就是主进程中sub_401F2F的异常值,该处的异常值是第一个if中的值,进入的第一个if语句中的函数。

1661652618325

由于不知道这几个函数在干嘛,我们考虑用动态调试,注意这里动态调试需要设置参数,具体多少随意。

我们一路调到MD5加密函数处,这个v24的值就是我们之前找到的那个字符串的值

1661653981414

后面这个for循环应该就是把加密后的MD5值存到v23数组里

1661654179444

而后面有一个异或运算,显示把v23转化成了一个长整型v27(比如v23为23271ceac9cf88e1,那么v27就是0x23231CEAC9CF88E1),然后和v29异或,然后通过ptrace复制回去

1
2
3
v27 = sub_40B660(v23, 0LL, 16LL);
v26 = v27 ^ v29;
ptrace(4LL, a1, v22, v27 ^ v29, v14, v15, v19);

v29我们可以看出是ptrace通信以后得到的一个返回值,这个返回值就是从re3子线程中提取的数据。

1661654472280

也就是说这个函数的目的就是从子进程中取出数据然后和之前我们存的那个字符串的MD5加密再转长整型得到的一个数进行异或。

因为有很多数据,一个一个手算有点麻烦,我们考虑到这个长整型存在RAX寄存器中,那么我们可以在图示的地方下断点后编辑断点,写脚本dump出每一次循环中RAX的值

1661656014456

得到所有异或值(加上之前的0x23231CEAC9CF88E1,我们取出之后,再对re3中的数据上脚本进行异或解密即可。

1661656087378

1
0x23231CEAC9CF88E10x4d9403a34275494c, 0xf1ac2fea63c94ea9, 0xf32554baaf233dcc, 0xad5ea15de7bcf568, 0xabdfc454b2ec9fd0, 0xa5ee2b4680957b2b, 0xaf42f81128b7fb38, 0xca34bde4268cae3, 0x4ee274bc39f2d547, 0x53458e3ea10ab93b, 0x2e5fb32efac34cff, 0x99f8f6faa7a64aec, 0xef38004300eda44d, 0xee67c44e2bcd18fc, 0x9b1c209768ecb41e, 0xfae74344fcba3cdb, 0x62654e739151118d, 0xbfa53d12825ac60, 0x5fda7e9212d8d034, 0xe8e15b2ffd058214, 0x6258db99ec82ff1f, 0xc1f8d40001b68bf6, 0x6211d421f8ab1d50, 0xd25bc129ebbbd366, 0xaea9e2a30d3fcd24, 0x12e2013bc48da1de, 0x1db06bde7ca30286, 0x226499b91812859b, 0xb2b0d80d0f244ce4, 0xfba26ec5f66ad4a5, 0xef4975489b39baa5, 0x75da0adeb0d03511, 0xcbb9c9ef1c68088d, 0xb707f2ec82b077b8, 0x4989b97aadc513bb, 0x74c613b6d47fcde, 0x1d6396837a7ad9d8, 0x7f1a74782535fe54

同时我们发现之前的那串字符串前面的数据就是对应了re3中的addr

1661652905436

1661656754186

8723的16进制就是2213,写idapython脚本恢复数据

1
2
3
4
5
6
7
8
9
10
Key = {8723: 2533025110152939745, 8739: 5590097037203163468, 8755: 17414346542877855401, 8771: 17520503086133755340, 8787: 12492599841064285544, 8803: 12384833368350302160, 8819: 11956541642520230699, 8835: 12628929057681570616, 8851: 910654967627959011, 8867: 5684234031469876551, 8883: 6000358478182005051, 8899: 3341586462889168127, 8915: 11094889238442167020, 8931: 17237527861538956365, 8947: 17178915143649401084, 8963: 11176844209899222046, 8979: 18079493192679046363, 8995: 7090159446630928781, 9011: 863094436381699168, 9027: 6906972144372600884, 9043: 16780793948225765908, 9059: 7086655467811962655, 9075: 13977154540038163446, 9091: 7066662532691991888, 9107: 15157921356638311270, 9123: 12585839823593393444, 9139: 1360651393631625694, 9155: 2139328426318955142, 9171: 2478274715212481947, 9187: 12876028885252459748, 9203: 18132176846268847269, 9219: 17242441603067001509, 9235: 8492111998925944081, 9251: 14679986489201789069, 9267: 13188777131396593592, 9283: 5298970373130621883, 9299: 525902164359904478, 9315: 2117701741234018776, 9331: 9158760851580517972}

addr = 0x2213

while True:
data = get_qword(addr)
k = Key[addr]
dec = data ^ k
idc.patch_qword(addr, dec)
addr += 16 # 根据前面的地址8723,8739,8755每次加16可知

然后经过修复以后得到关键加密函数(修复的时候可能有花,应该是analyze的时候有的数据分析出锅的导致不能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
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
void __fastcall sub_21F9(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v3; // rax
int v4; // [rsp+20h] [rbp-28h]
int v5; // [rsp+24h] [rbp-24h]
char v6; // [rsp+28h] [rbp-20h]
int v7; // [rsp+2Ch] [rbp-1Ch]
int i; // [rsp+30h] [rbp-18h]
int v9; // [rsp+34h] [rbp-14h]
int v10; // [rsp+38h] [rbp-10h]
char v11; // [rsp+3Ch] [rbp-Ch]
int v12; // [rsp+40h] [rbp-8h]
int v13; // [rsp+44h] [rbp-4h]

*(_BYTE *)(v3 + 16532935) += a3;
while ( v13 <= 24 )
{
v12 = 0;
v11 = 0;
v10 = 0;
v9 = 1;
while ( v12 <= 24 )
{
if ( *(_BYTE *)(25 * v13 + v12 + a1) )
{
++v11;
v10 = 1;
}
else
{
if ( v10 )
{
*(_BYTE *)(a2 + 25LL * v13 + v9) = v11;
v11 = 0;
++v9;
}
v10 = 0;
}
if ( ++v12 == 25 && v10 )
{
*(_BYTE *)(a2 + 25LL * v13 + v9) = v11;
v11 = 0;
++v9;
}
}
*(_BYTE *)(25LL * v13++ + a2) = v9 - 1;
}
for ( i = 0; i <= 24; ++i )
{
v7 = 0;
v6 = 0;
v5 = 0;
v4 = 1;
while ( v7 <= 24 )
{
if ( *(_BYTE *)(25 * v7 + i + a1) )
{
++v6;
v5 = 1;
}
else
{
if ( v5 )
{
*(_BYTE *)(a3 + 25LL * i + v4) = v6;
v6 = 0;
++v4;
}
v5 = 0;
}
if ( ++v7 == 25 )
{
if ( v5 )
{
*(_BYTE *)(a3 + 25LL * i + v4) = v6;
v6 = 0;
++v4;
}
}
}
*(_BYTE *)(25LL * i + a3) = v4 - 1;
}
}

然后你妈这是个数织游戏,属实是给我整不会了。

数织游戏的规则是:有一个n*m的方格,给两个文本,一个存行的信息,一个存列的信息,比如第一行的信息是5,4那么就表示这一行有两串长度为5,4的格子需要被上色,第一列的信息为2,3,1就表示这一列有三串长度为2,3,1的格子需要被上色,那么结合行列的信息,我们就可以得出这个格子到底有哪些格子被上色了。

你以为到这就完了?

1661663947475

这俩byte_50A0和byte_5320也不能直接用,也是在init和preinit中被修改了(乌鸡鲅鱼了),

1661665936467

进入修复函数以后进入这俩函数,里面的数据才是真实数据,我们把它们dump出来,然后按每 25个数据取出非0的数字就可以了(第一个数据是显示当前有多少非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
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[] =
{
0x05, 0x05, 0x05, 0x03, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x01, 0x01, 0x05, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x02, 0x04, 0x05,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x05, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x01, 0x01, 0x01,
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x01, 0x02, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x02, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x02, 0x05, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x05, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x03, 0x05, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x01, 0x01, 0x01, 0x03, 0x05, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x05, 0x01,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x01, 0x02, 0x04,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x01, 0x01, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x01, 0x02, 0x04,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x05, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x05, 0x04, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x05, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x02, 0x01, 0x01,
0x01, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, -1
};

int b[] =
{
0x06, 0x05, 0x01, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x03, 0x01, 0x03, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x01, 0x01, 0x02,
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x01, 0x04, 0x01, 0x03, 0x02, 0x01, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x04, 0x04, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x02, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x01, 0x01, 0x01, 0x01, 0x03, 0x04, 0x03, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x03, 0x01, 0x03, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x01, 0x01, 0x04, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0x01, 0x01, 0x01,
0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x02, 0x02, 0x01, 0x02,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x02, 0x05, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x0C, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x02, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x04, 0x07, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x01, 0x02, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x0C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x04, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, -1
};
int main()
{
for(int i = 0; a[i] != -1; i+= 25)
{
for(int j = i + 1; a[j]; j++)
printf("%d ",a[j]);
cout << endl;
}
cout << endl;
for(int i = 0; b[i] != -1; i+= 25)
{
for(int j = i + 1; b[j]; j++)
printf("%d ",b[j]);
cout << endl;
}
}

找一个在线解密网站 Nonogram (handsomeone.github.io) 解密

1661667681689

然后艰难读出flag{I LOVE PLAY ctf_QWB 2022}😓