强网杯青少年专项选拔赛 2024 Reverse方向 WriteUp

731 words

EnterGame:

反编译观察main函数:

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 main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v5[10]; // [rsp+8h] [rbp-138h] BYREF
__int64 s2[2]; // [rsp+30h] [rbp-110h] BYREF
__int64 v7[4]; // [rsp+40h] [rbp-100h] BYREF
char s[112]; // [rsp+60h] [rbp-E0h] BYREF
char s1[104]; // [rsp+D0h] [rbp-70h] BYREF
unsigned __int64 v10; // [rsp+138h] [rbp-8h]

v10 = __readfsqword(0x28u);
puts("Welcome to the Youth Cybersecurity Challenge!");
s2[0] = 0x7A2B7587D3AA135ELL;
s2[1] = 0xD21D7E49A304161BLL;
qmemcpy(v7, "k]X@^DcYHQ\rT^XUX瓊", 17);
*(__int64 *)((char *)&v7[2] + 2) = 0xC1CE5D58ABE7DCAFLL;
puts("Please enter the password to start the game:");
fgets(s, 100, _bss_start);
s[strcspn(s, "\n")] = 0;
BYTE1(v5[9]) = 0;
HIWORD(v5[9]) = 0;
strcpy((char *)v5, "01234567Youth Strengthens the Nation");
v3 = strlen(s);
chacha20_encrypt(&v5[2], v5, (__int64)s, (__int64)s1, v3); //加密算法为chacha20
if ( !memcmp(s1, s2, 0x2AuLL) ) //比较42位
{
puts("Password correct, you may start the game.");
puts("With my strength, I secure the cyber frontier!");
}
else
{
puts("Incorrect password, cannot start the game.");
}
return 0;
}

经过动态调试找到s2(加密的flag)为[0x5e, 0x13, 0xaa, 0xd3, 0x87, 0x75, 0x2b, 0x7a, 0x1b, 0x16, 0x04, 0xa3, 0x49, 0x7e, 0x1d, 0xd2, 0x6b, 0x5d, 0x58, 0x40, 0x5e, 0x44, 0x63, 0x59, 0x48, 0x51, 0x0d, 0x54, 0x5e, 0x58, 0x55, 0x58, 0xad, 0x82, 0xaf, 0xdc, 0xe7, 0xab, 0x58, 0x5d, 0xce, 0xc1],观察加密函数发现为修改的chacha20加密,但chacha20是对称加密,所以在动态调试的时候输入42个随机字符,并在加密前patch为密文,加密过程其实也就是解密过程了,得到flag:flag{385915ad-8f32-49d0-94c3-0067f1dad1bd}.

Flip_over:

jadx反编译查看mainactivityflipgameactivity,发现是要在main中先输入一次账户密码过了才能进入获取flag的flipactivity部分,而两个的具体验证方法均在so文件中,于是反编译so文件,发现第一层输入账户密码与flag并没有关系,直接查看第二层的validateAndEncrypt,发现要输入的是一个42长度的以flag{开头,}结尾的字符串,然后会先对v16那个字符串进行以flag为key的rc4加密,再进行以flagflag为key的des_ecb加密,输出的结果存储在v15,然后将输入的字符串和0x21再和v15逐个异或,再与v11比较,首先将v16(a4c3f8927d9b8e6d6e483fa2cd0193b0a6e2f19c8b47d5a8f3c7a91e8d4b9f67)进行rc4加密得到
8ec4b73aadde72028d3dec3a48eade0afff5125de25cc7da484902f82f6aa39b0387f09442b6c1de39f5568e3e6804ab764adf01bca851051a48781747f262d0,再进行des_ecb加密得到5915c13f409a7ae7a68bb6e3ee0d196db76dcae6da5f0d4bd60ab2deadaa95eb85b777c310bbcfcef7d222c9c4f9fcfbab32ea318155af79528e80afc3186050f1d840bbfc1b89a6,查看v11发现只有两个xmmword(32字节),再仔细观察发现其下游还有v12(8字节)和v13(4字节),补全后用python脚本解得flag:flag{b92d40df-840a-43a8-bdb4-5de79eca13f4}.


Python脚本:

1
2
3
4
5
6
7
v15 = [0x59, 0x15, 0xc1, 0x3f, 0x40, 0x9a, 0x7a, 0xe7, 0xa6, 0x8b, 0xb6, 0xe3, 0xee, 0x0d, 0x19, 0x6d, 0xb7, 0x6d, 0xca, 0xe6, 0xda, 0x5f, 0x0d, 0x4b, 0xd6, 0x0a, 0xb2, 0xde, 0xad, 0xaa, 0x95, 0xeb, 0x85, 0xb7, 0x77, 0xc3, 0x10, 0xbb, 0xcf, 0xce, 0xf7, 0xd2, 0x22, 0xc9, 0xc4, 0xf9, 0xfc, 0xfb, 0xab, 0x32, 0xea, 0x31, 0x81, 0x55, 0xaf, 0x79, 0x52, 0x8e, 0x80, 0xaf, 0xc3, 0x18, 0x60, 0x50, 0xf1, 0xd8, 0x40, 0xbb, 0xfc, 0x1b, 0x89, 0xa6]
v11 = [0x1E, 0x58, 0x81, 0x79, 0x1A, 0xD9, 0x62, 0xF4, 0xE3, 0x9E, 0xA7, 0xA6, 0xA9, 0x01, 0x00, 0x78, 0xA6, 0x2D, 0xC6, 0xF3, 0xC8, 0x1F, 0x14, 0x47, 0x95, 0x4F, 0xF1, 0xCB, 0xA1, 0xBE, 0xD0, 0xAF, 0x93, 0xAF, 0x33, 0x81, 0x50, 0xAB, 0xDD, 0x89, 0xE2, 0x8E]
flag = [None]*42
for i in range(42):
flag[i] = v11[i] ^ v15[i] ^ 0x21
hex_flag = [hex(x) for x in flag]
print(hex_flag)