avatar

RCTF WP (week 7)

6.3日更新

5.30号到6.1号都进行了RCTF的比赛,逆向小分队,一共做出5道,差一道题ak,W&M排行第4

cipher

拿到程序后用 ghrdia 反编译一下

image-20200603085224992

看到了随机数的位置,srand那么后面一定就有地方rand取出来,这里对另一个文件的时间戳进行的取的随机数,进行后,进行cipher,我们跟进去看一下

image-20200603085431792

我们看到puParm2这个变量,进行随机数的一个取char类型,也就是1Byte的数据量,那么mips64架构这里是大端序,c语言是小端序,那么假设一个是 0x11 0x22 一个char key[16] ,那么就应该是0x1122000000000000,然后写脚本,这里祥哥帮了帮忙

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

#define ull unsigned long long

ull ror(ull a, int b) {
return (a >> b) + (a << (0x40 - b));
}

ull rol(ull a, int b) {
return (a << b) + (a >> (0x40 - b));
}

void encrypt(ull * output, ull * input, ull * key) {
ull s1, s2, s3, s4;

s1 = key[1];
s2 = key[0];

s4 = input[0];
s3 = input[1];

s3 = ror(s3, 8) + s4 ^ s2;
s4 = rol(s4, 3) ^ s3;

for (int i = 0; i < 0x1f; i++) {
s1 = ror(s1, 8) + s2 ^ (ull)i;
s2 = rol(s2, 3) ^ s1;
s3 = ror(s3, 8) + s4 ^ s2;
s4 = rol(s4, 3) ^ s3;
}
output[0] = s4;
output[1] = s3;
}

void decrypt(ull * msg, ull * cipher, ull * key) {
ull s1, s2, s3, s4;

s4 = cipher[0];
s3 = cipher[1];

s1 = key[1];
s2 = key[0];

for (int i = 0; i < 0x1f; i++) {
s1 = ror(s1, 8) + s2 ^ (ull)i;
s2 = rol(s2, 3) ^ s1;
}

for (int i = 0x1f-1; i >=0; i--) {
s4 = ror(s4 ^ s3, 3);
s3 = rol((s3 ^ s2) - s4, 8);
s2 = ror(s2 ^ s1, 3);
s1 = rol((s1 ^ (ull)i) - s2, 8);
}

s4 = ror(s4 ^ s3, 3);
s3 = rol( (s3 ^ s2) - s4, 8);
msg[0] = s4;
msg[1] = s3;
}

void test() {

ull input[2] = {
11238374476513621386LLu,
11791438470581322078LLu
};

ull key[2] = {
18046502099429878359LLu,
11630413863397426168LLu
};

ull output[2] = {0};
ull decrypted[2] = {0};

encrypt(output, input, key);

printf("%llu\n%llu\n", output[0], output[1]);
// 5456981390149708428 10686201289862117679

decrypt(decrypted, output, key);
printf("%llu\n%llu\n", decrypted[0], decrypted[1]);
// 11238374476513621386 11791438470581322078
} // ok

int main() {

ull msg[2] = {0};
ull key[2] = {0};

ull cipher[3][2] = {
{
0x2a00f82be11d77c1LLu,
0xc3b171fc23d591f4LLu,
},
{
0x30f11e8bc2885957LLu,
0xd594ab77422feb75LLu,
},
{
0xe15d76f0466e98b9LLu,
0xb651fdb55d7736f2LLu,
}
};

for (int k = 0; k < 3; k++) {
for (ull key1 = 0; key1 <= 0xff; key1++)
for (ull key2 = 0; key2 <= 0xff; key2++) {
key[0] = ( key1 << 56) + ( key2 << 48 );
key[1] = 0LLu; // 这里可能也应该调成大端序
decrypt(msg, cipher[k], key);
unsigned char * m = (unsigned char *) msg;
int i;
for (i = 0; i < 0x10; i++) {
if (m[i] < 0x20 || m[i] >= 0x80) // 非可见字符
break;
}
if (i == 0x10) {
printf("%llx\n%llx\n", msg[0], msg[1]);
}
}
}
}

在最后一次的把 m[i] < 0x20取消就好,因为有不可见的字符

go-flag

恢复一下符号表

image-20200603090203444

接受字符串

image-20200603090300288

send 和 recv 这两个函数是想对应的,客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答,不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据

image-20200603090917079

看一下调用的chanrecv1

image-20200603091039196

下断可以看到一个flag,按顺序搜集这些 main_main_func 的 flag,拼接起来即可

play_the_game

是个五子棋的游戏,我刚开始用的改之理

image-20200603091156257

换 jadx 看了下发现关键点

image-20200603091217611

image-20200603091229243

去解一下

image-20200603091559888

得到我们需要赢电脑99次,我们就可以得到flag

image-20200603092018685

调用99次就可以

rust-flag

image-20200603092132613

主要是一个异或:A^B^我们输入的数据

1
2
3
4
5
data1 = [0x6b,0x56,0x76,0xb2,0xee,0x03,0xb1,0xe3,0x62,0x5c,0xe6,0x91,0x1d,0x93,0x27, 0xfc ,0x8d,0xf8,0x53,0x64]
data2 = [0x39,0x15,0x22,0xf4,0x95,0x70,0xe5,0x91,0x07,0x3d,0x8b,0xce,0x78,0xc2,0x52 ,0x9d, 0xe1,0x8b,0x2e,0x6e]

for i in range(len(data1)):
print(chr(data1[i]^data2[i]),end = '')

My Switch game

打开压缩包,有一个 nro 文件和一份 log,下载一份 loader Switch64.dll,把 nro 丢给 IDA。稍微分析一下,可以知道 sub_C20 会初始化部分游戏参数,比如蛇和果实的位置。sub_10D0 则是关键函数,会根据当前蛇的前进方向和吃到的果实数,生成下一位 flag 以及下一颗果实的位置(初始函数解密一位、前 4 个以及第 31 个果实 xor 0x44,解密出"RCTF{}")。部分伪代码如下:

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
 v9 = direct + 4 * index - 1;
qword_B8F98 = v9;
srand(v9);
qword_B8FA0 = sub_63AD0(0LL);
v10 = rand();
food_x = v10 - 77 * ((unsigned __int64)(0x3531DEC1LL * v10 >> 36) - (v10 >> 31));// v10%77
v11 = rand();
v12 = v11;
v13 = index;
v14 = off_B0D98;
v15 = index + 1;
v16 = (unsigned __int64)(0x63E7063FLL * v11 >> 36) - (v11 >> 31);
v17 = off_B0D98;
*((_BYTE *)flag + index) = tmp;
food_y = v12 - 41 * v16; // v12%41
index = v13 + 1;
v18 = len((unsigned __int64)v17) - 1;
if ( v15 < v18 )
{
v19 = (unsigned __int8)v14[v15];
LABEL_28:
result = v19 ^ 0x44u;
tmp = result;
return result;
}
if ( v15 == 0x1F )
{
v19 = (unsigned __int8)v14[v18];
goto LABEL_28;
}
if ( v15 == 0x20 )
{
result = 1LL;
byte_B8C04 = 1;
}
else
{
v20 = rand();
v21 = len((unsigned __int64)&algn_B8B9C[4]);
result = (unsigned __int8)algn_B8B9C[v20 - v20 / v21 * v21 + 4];// strtable[v20%v21+4]
tmp = result;
}
return result;

可以直接抠 rand 的代码用,设置好 seed 就行。问题在于吃到果实的时候蛇的前进方向。代码里没分析出其他信息,猜测是在 log.log 里面。在题目描述里的 github,找到了一个脚本可以解析一部分日志数据。https://github.com/mart1nro/joycontrol/blob/master/scripts/parse_capture.pyInputReport 和 OutputReport 的解析可以看这个https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_notes.md简单解析了一下,发现 InputReport ID 大部分为 0x30,也就是包含了按键信息

image-20200603092339418

OutputReport ID 大部分为 0x10,也就是 Rumble data

image-20200603092358844

推测是吃到果实后,游戏会让手柄振动,那么 Rumble data 应该会大不相同。结合这几点,写个脚本解析 log,主要是解析出方向键数据以及 Rumble data,并且过滤一下没用的 Rumble data(图片中的"0001404000014040"出现得最多)。代码如下:

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
import struct
import binascii
f = open('log.log', 'rb+')
s=f.read()
f.close()

i = 0
t = []

while i < len(s):
[time,l] = struct.unpack_from("di", s, i)
i+=12
dat = s[i:i+l]
if ord(dat[0]) == 0xa1 and ord(dat[1]) == 0x30 and len(dat) > 5:
t.append(dat[1:])
elif ord(dat[0])==0xa2 and ord(dat[1]) == 0x10:
t.append(dat)
i+=l

d = ''

dir = []
for i in t:
if ord(i[0])==0xa2:
if i[3:11].encode('hex').upper() not in '0001404000014040':
d+= '\nRumble :'+i[3:11].encode('hex').upper()+'\n'
continue
state = ord(i[5]) & 0xf
left = int(bool(state & (1<<3)))
right = int(bool(state & (1<<2)))
down = int(bool(state & (1<<0)))
up = int(bool(state & (1<<1)))
if left + right + up + down > 1:
if state == dir[-1]:
d+=d[-1]
continue
else:
state ^= dir[-1]
left = int(bool(state & (1<<3)))
right = int(bool(state & (1<<2)))
down = int(bool(state & (1<<0)))
up = int(bool(state & (1<<1)))
if left:
d+='L'
elif right:
d+='R'
elif down:
d+='D'
elif up:
d+='U'
dir.append(state)


print(d)

运行后,删除掉没有方向数据的 Rumble data 就可以分析了,这里取两条最开始的数据

1
2
3
4
DDDDDDDDDDDDLLLLLLLLL
Rumble :04B4014E04B4014E
UUUUUUUUUUUUUUULLLLLLLLLLLLDDDDDDDDDDDLLLLLLLLLLLLLUUUUUUUUUUUULLLLLLLLLLLUUUUUU
Rumble :04B4014E04B4014E

在 Rumble data 上面的最后一个方向数据,就是吃到果实的时候蛇的方向了。第一条是 L,第二条是 U,以此类推。拿到所有方向数据后,写个脚本直接输出 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
import ctypes
seed = 0
DIR_LEFT = 1
DIR_RIGHT = 2
DIR_UP = 3
DIR_DOWN = 4


def crand():
global seed
cseed = ctypes.c_longlong(seed)
cseed.value = 0x5851F42D4C957F2D * cseed.value + 1
seed = cseed.value
return (cseed.value >> 32) & 0x7FFFFFFF


strtable = '0123456789abcdefghijklmnopqrstuvwxyz_'
out = ''
dirc = [DIR_LEFT, DIR_UP, DIR_UP, DIR_LEFT, DIR_LEFT, DIR_LEFT, DIR_LEFT, DIR_RIGHT, DIR_LEFT, DIR_RIGHT, DIR_LEFT, DIR_LEFT,
DIR_LEFT, DIR_LEFT, DIR_UP, DIR_LEFT, DIR_RIGHT, DIR_LEFT,
DIR_RIGHT, DIR_LEFT, DIR_DOWN, DIR_LEFT, DIR_RIGHT, DIR_LEFT, DIR_UP,
DIR_LEFT, DIR_RIGHT, DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_RIGHT
]
seed = 1
index = 0
food_x = crand() % 77
food_y = crand() % 41
print 'first food:', food_x, food_y
for i in range(len(dirc)):
seed = dirc[i] + 4 * index - 1
index += 1
food_x = crand() % 77
food_y = crand() % 41
print 'next food:', food_x, food_y
out += strtable[crand() % 37]
print 'RCTF{' + out[4:-1] + '}'
Author: L0x1c
Link: https://l0x1c.github.io/2020/06/03/2020-6-03/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶