# 主函数

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
void __cdecl main_main()
{
__int64 v0; // rdx
int v1; // edi
__int64 v2; // rsi
int v3; // edx
int v4; // ecx
__int64 v5; // r8
__int64 v6; // r9
__int64 v7; // r8
__int64 v8; // r9
__int64 v9; // r8
__int64 v10; // r9
int v11; // er8
int v12; // er9
int v13; // edx
__int64 v14; // r8
__int64 v15; // r9
int v16; // edx
int v17; // ecx
__int64 v18; // r8
__int128 v19; // [rsp+58h] [rbp-38h] BYREF
__int128 v20; // [rsp+68h] [rbp-28h] BYREF
void *v21; // [rsp+78h] [rbp-18h] BYREF
void **v22; // [rsp+80h] [rbp-10h] BYREF

if ( &v22 <= *(__readfsqword(0xFFFFFFF8) + 16) )
runtime_morestack_noctxt();
sync___WaitGroup__Add(v1, v2, v0);
runtime_newobject(v1, v2, v3, v4, v5, v6);
v21 = &unk_4B0DA0;
v22 = &off_4E9BB0;
fmt_Fprintln(v1, v2, &v21, &unk_4B0DA0, v7, v8, &go_itab__os_File_io_Writer, os_Stdout, &v21);
*&v20 = &unk_4AE9C0;
*(&v20 + 1) = 1LL;
fmt_Fscanf(
v1,
v2,
&go_itab__os_File_io_Reader,
&v20,
v9,
v10,
&go_itab__os_File_io_Reader,
os_Stdin,
&unk_4C9DA5,
2LL,
&v20,
1LL);
runtime_newproc(v1, v2, &off_4D2310, MEMORY[1], v11, v12, 16, &off_4D2310, MEMORY[1], MEMORY[9]);
*&v19 = &unk_4B0DA0;
*(&v19 + 1) = &off_4E9BC0;
fmt_Fprintln(v1, v2, v13, &go_itab__os_File_io_Writer, v14, v15, &go_itab__os_File_io_Writer, os_Stdout, &v19);
sync___WaitGroup__Wait(v1, v2, v16, v17, v18);
}

其中 runtime_newproc 函数代表启动了另一个线程来执行函数,交叉引用可以发现引用的是 main_checkflag 函数,后面的数组比如 & off_4E9BC0 就是提示语,在 linux 中打开之后输入 flag 会提示’Who am I? where am I? what am I doing?’,不过我们先不用管这个,先看关键函数

# main_checkflag

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
__int64 __fastcall main_checkflag(int a1, __int64 a2, int a3, __int64 a4, int a5, int a6, __int64 a7, __int64 a8)
{
unsigned __int64 v8; // rcx
__int64 v9; // r8
__int64 v10; // r9
int v11; // edx
char v12; // al
__int64 v13; // rdx
int v15; // [rsp+18h] [rbp-70h]
__int64 v16; // [rsp+20h] [rbp-68h]
int v17; // [rsp+28h] [rbp-60h]
char v18[32]; // [rsp+40h] [rbp-48h] BYREF
__int128 v19; // [rsp+60h] [rbp-28h] BYREF
__int128 v20; // [rsp+70h] [rbp-18h] BYREF
__int64 v21; // [rsp+80h] [rbp-8h] BYREF

v8 = __readfsqword(0xFFFFFFF8);
if ( &v21 <= *(v8 + 16) )
runtime_morestack_noctxt();
runtime_stringtoslicebyte(a1, a2, a3, v8, a5, a6, v18, a7, a8);
main_Myencode(a1, a2, v17, v16);
v11 = v15;
if ( v16 == qword_55EA78 )
{
runtime_memequal(a1, a2, v15, main_enc, v9, v10);
v12 = v15;
}
else
{
v12 = 0;
}
if ( v12 )
{
*&v20 = &unk_4B0DA0;
*(&v20 + 1) = &off_4E9B90;
fmt_Fprintln(a1, a2, v11, &go_itab__os_File_io_Writer, v9, v10, &go_itab__os_File_io_Writer, os_Stdout, &v20);
}
else
{
*&v19 = &unk_4B0DA0;
*(&v19 + 1) = &off_4E9BA0;
fmt_Fprintln(a1, a2, v11, &go_itab__os_File_io_Writer, v9, v10, &go_itab__os_File_io_Writer, os_Stdout, &v19);
}
return sync___WaitGroup__Add(a1, a2, v13);
}

这个函数出现了一个 main_Myencode,看名字就觉得是个关键函数,再看后面也有一个类似刚才的数组,然后我们点进去看,发现一个关键信息

说明 flag 是我们输入的字符串的 md5 值,现在看 main_Myencode 函数

# main_Myencode

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
__int64 __fastcall main_Myencode(int a1, int a2, int a3, _DWORD a4, int a5, int a6, __int64 a7, __int64 a8, int a9)
{
unsigned __int64 v9; // rcx
int v10; // er8
int v11; // er9
__int64 v12; // r8
int v13; // er8
int v14; // er9
__int64 v16; // [rsp+18h] [rbp-50h]
int v17; // [rsp+20h] [rbp-48h]
int v18; // [rsp+28h] [rbp-40h]
char v19[32]; // [rsp+38h] [rbp-30h] BYREF
__int64 v20; // [rsp+58h] [rbp-10h]
void *retaddr; // [rsp+68h] [rbp+0h] BYREF

v9 = __readfsqword(0xFFFFFFF8);
if ( &retaddr <= *(v9 + 16) )
runtime_morestack_noctxt();
runtime_makeslice(a1, a2, a3, v9, a5, a6, &unk_4B0EE0, a8, a8);
v20 = v16;
runtime_stringtoslicebyte(a1, a2, qword_55E898, main_enc_key, v10, v11, v19, main_enc_key, qword_55E898);
crypto_rc4_NewCipher(a1, a2, v18, v17, v12);
crypto_rc4___Cipher__XORKeyStream(a1, a2, a9, a8, v13, v14);
return a8;
}

看到了两个 rc4 加密的函数,说明这里是将输入的字符串进行了 rc4 加密,并且可以看到 21 行这里看到有一个 main_enc_key,这应该就是密钥了,可以直接看到它的值 "thisiskkk",然后在这里突然又想起在外面那个函数也看到一个类似这个的东西,就是 main_enc,然后点进去看发现是个数组,这刚好和密钥的名字对应,肯定就是加密后的结果了

1
2
3
4
0xD8, 0xE5, 0x85, 0xBE, 0xE7, 0xF8, 0x58, 0x75, 0x95, 0x65, 
0x85, 0xE3, 0xA6, 0x47, 0x59, 0xB9, 0x14, 0x6F, 0x33, 0xB5,
0xCA, 0x84, 0x0B, 0xE7, 0x92, 0x0E, 0xD2, 0xFD, 0x64, 0x18,
0x96, 0xD0, 0x0F, 0x5E, 0x44, 0x3E

不过这不是最终的加密结果,一开始没有发现,最后这一串还异或了一个 0x23,在 ida 左侧的函数栏里面还有一个带有 main 的函数 main_init_0,在这个里面又将 rc4 后的结果进行了一次异或,所以虽然说名字像不一定是百分百有联系,但还是看一下比较好,官方的 wp 说还可以通过动调来直接找这个最终的密文,一开始也尝试了动调,但我是直接用 ida 的远程调试的,搞了很久没搞出来,可以用 gdb 调试,但 dlv 会更方便(不会 dlv)

# 总体思路

程序将输入的字符串进行一次 rc4 加密再进行一次异或,我们只要先异或再解密就行了,知道了 key 和密文,再 rc4 解密一下就行了,解密脚本用的是一位大佬写的,解密之后得到 56e83694-f976-11eb-b343-faffc201c8e0,在 linux 中运行程序的时候,输入这个字符串,也会弹出提示,最终的 flag 是 DASCTF {md5 (Input)},所以在程序的分析中没有发现这个也是没问题的

得到 flag:DASCTF{9e1963bbbb1285b993c862a5a6f12604}