2019暑假任务———CTF_GAME

CTF_GAME

运行程序可以看见是一个类似夹娃娃机的程序

分析

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
void __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // rdx
unsigned int seed; // eax
__int64 seed_1; // rdi
__int64 v6; // rdx
__int64 input_1; // rdx
__int64 v8; // rdx
__int64 v9; // rdx
int v10; // ST14_4
int v11; // [rsp+10h] [rbp-30h]
char input; // [rsp+3Bh] [rbp-5h]
__int64 retaddr; // [rsp+48h] [rbp+8h]

_tsan_func_entry(retaddr, a2, a3);
welcome(retaddr, a2, v3);
seed = time(0LL);
seed_1 = seed;
srand(seed);
init_game(seed_1, a2, v6);
while ( 1 )
{
do
{
input = getchar();
input_1 = input;
}
while ( input_1 == '\n' );
switch ( input )
{
case 'a':
left(seed_1, a2, input_1);
break;
case 'd':
right(seed_1, a2, input_1);
break;
case 's':
catch(seed_1, a2, input_1);
break;
case 'v':
_tsan_read1(&isdebug);
if ( isdebug & 1 )
{
debugmode(&isdebug, a2, v8);
}
else
{
printf("not debug mode\n");
sleep(1LL);
init_game(1LL, a2, v9);
}
break;
default:
break;
}
_tsan_read4(&life);
if ( life <= 0 )
{
printf("you are out of life, please restart the game\n");
exit(0);
}
v10 = input_num + 1;
_tsan_write4(&input_num);
input_num = v10;
_tsan_read4(&input_num);
seed_1 = &level;
v11 = input_num;
_tsan_read4(&level);
if ( v11 > 5 - level )
refresh_flag_position(&level, a2, v11);
}
}

在welcome函数中有开启三个线程来检查程序是否被更改

1
2
3
pthread_create(&v5, 0LL, check_if_debug_is_be_hack, 0LL);
pthread_create(&v6, 0LL, check_if_level_is_be_hack, 0LL);
pthread_create(&v7, 0LL, check_if_life_is_be_hack, 0LL);

check_if_debug_is_be_hack函数检查debug是否开启
check_if_level_is_be_hack函数检查level是否大于5,与一个level增加会减少的值是否正常
check_if_life_is_be_hack函数life是否大于6小于0
如果在一个线程中发现异常则程序退出

输入’a’,’d’指令后抓钩会相应移动,输入’v’可进入debug模式

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
switch ( input )
{
case 'a':
left(seed_1, a2, input_1);
break;
case 'd':
right(seed_1, a2, input_1);
break;
case 's':
catch(seed_1, a2, input_1);
break;
case 'v':
_tsan_read1(&isdebug);
if ( isdebug & 1 )
{
debugmode(&isdebug, a2, v10);
}
else
{
printf("not debug mode\n");
sleep(1LL);
init_game(1LL, a2, v11);
}
break;
default:
break;
}

由于debug模式未开启isdebug为0,不能进入debugmode

开始破解

用ida-patch将程序逻辑更改如下,直接进入debugmode

1
2
3
case 'v':
debugmode(&isdebug, a2, v10);
break;

并且更改check_if_debug_is_be_hack逻辑

1
2
3
4
while(1)
{
;
}

并且进入debugmode后,可以看见里面还有一层检测,更改如下

1
2
3
4
if ( 1 )
{
...
}

程序运行后键入v进入debugmode

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
__int64 __fastcall debugmode(__int64 a1, __int64 a2, __int64 a3)
{
char *v3; // rdi
__int64 v4; // rdx
__int64 v5; // rcx
__int64 v6; // r8
__int64 v7; // r9
__int64 v8; // rdx
char v10; // [rsp+3Fh] [rbp-1h]
void *retaddr; // [rsp+48h] [rbp+8h]

_tsan_func_entry(retaddr, a2, a3);
v3 = &isdebug;
_tsan_read1(&isdebug);
if ( isdebug & 1 )
{
sub_55CD48C27F20(&isdebug, a2, v4);
printf("warning:only for debug\n");
printf("1 Add life\n");
printf("2 Level up\n");
v3 = "3 Debug on\n";
printf("3 Debug on\n");
do
{
v10 = getchar();
v8 = v10;
}
while ( v8 == 10 );
switch ( v10 )
{
case '1':
add_life("3 Debug on\n", a2, v8);
break;
case '2':
level_up("3 Debug on\n", a2, v8);
break;
case '3':
debug_on("3 Debug on\n", a2, v8);
break;
}
init_game("3 Debug on\n", a2, v8);
}
return _tsan_func_exit(v3, a2, v4, v5, v6, v7);
}

选择Debug on即可开启debug模式,然后选择Level up,最多可将level加至4

更改level_up中的代码

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
__int64 __fastcall level_up(__int64 a1, signed __int64 a2, __int64 a3)
{
char *v3; // rdi
__int64 v4; // rdx
__int64 v5; // rcx
__int64 v6; // r8
__int64 v7; // r9
int v8; // ST14_4
int v9; // ST10_4
__int64 v10; // rdx
void *retaddr; // [rsp+28h] [rbp+8h]

_tsan_func_entry(retaddr, a2, a3);
v3 = &isdebug;
_tsan_read1(&isdebug);
if ( isdebug & 1 )
{
pthread_mutex_lock(&unk_55CD49909A50);
_tsan_read4(&safe_code);
if ( safe_code > 0 )
{
v8 = level + 1;
_tsan_write4(&level);
level = v8;
v9 = safe_code - 1;
_tsan_write4(&safe_code);
safe_code = v9;
}
else
{
printf("Out of scopee\n");
}
v3 = &unk_55CD49909A50;
pthread_mutex_unlock(&unk_55CD49909A50);
init_game(&unk_55CD49909A50, a2, v10);
}
return _tsan_func_exit(v3, a2, v4, v5, v6, v7);
}

改为

1
2
3
4
5
6
7
8
9
if ( safe_code >= 0 )
{
v8 = level + 1;
_tsan_write4(&level);
level = v8;
v9 = safe_code - 1;
_tsan_write4(&safe_code);
safe_code = v9;
}

经测试无用,于是最后一个level尝试手动通过

删除main函数中刷新flag位置的函数

1
2
if ( v11 > 5 - level )
refresh_flag_position(&level, a2, v11);

更改catch函数中对于抓取的判定

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
__int64 __fastcall catch(__int64 a1, __int64 a2, __int64 a3)
{
int v3; // ST7C_4
__int64 v4; // rsi
__int64 v5; // rdx
int v6; // ST78_4
__int64 v7; // rdx
int v8; // ST70_4
int v9; // ST6C_4
int v10; // ST68_4
int *v11; // rdi
__int64 v12; // rdx
int v13; // ST64_4
int v14; // eax
__int64 v15; // rdx
int v16; // ST5C_4
int v17; // ST50_4
int v18; // ST4C_4
__int64 v19; // ST38_8
__int64 v20; // rdx
__int64 v21; // rdx
__int64 v22; // rcx
__int64 v23; // r8
__int64 v24; // r9
int v25; // ST18_4
__int64 v26; // rdx
char v28; // [rsp+83h] [rbp-Dh]
int i; // [rsp+88h] [rbp-8h]
int j; // [rsp+88h] [rbp-8h]
int v31; // [rsp+8Ch] [rbp-4h]
void *retaddr; // [rsp+98h] [rbp+8h]

_tsan_func_entry(retaddr, a2, a3);
_tsan_read4(&position_x);
v3 = position_x;
_tsan_read4(&flagx);
v4 = v3 == flagx;
v31 = 6 - v4;
for ( i = 0; i < v31 - 1; ++i ) // 抓取动画_下
{
_tsan_read4(&position_y);
if ( position_y < 5 )
{
v6 = position_y + 1;
_tsan_write4(&position_y);
position_y = v6;
}
sub_55CD48C27F20(&position_y, v4, v5);
draw(&position_y, v4, v7);
usleep(100000LL);
}
if ( v31 == 6 ) // x不同
goto LABEL_30;
v28 = 1;
for ( j = 0; j < v31 - 1; ++j ) // //抓取动画_上
{
_tsan_read4(&position_y);
if ( position_y > 0 )
{
v8 = position_y - 1;
_tsan_write4(&position_y);
position_y = v8;
}
if ( v28 & 1 ) // flag抓取
{
_tsan_read4(&flagy);
if ( flagy > 1 )
{
v9 = flagy - 1;
_tsan_write4(&flagy);
flagy = v9;
}
}
else // flag掉落
{
_tsan_read4(&flagy);
if ( flagy < 5 )
{
v10 = flagy + 1;
_tsan_write4(&flagy);
flagy = v10;
}
}
v11 = &position_y;
_tsan_read4(&position_y);
if ( position_y == 2 ) // F到第三排时
{
v11 = &safe_code;
_tsan_read4(&safe_code);
v13 = safe_code;
v14 = rand();
v12 = (v14 >> 31);
LODWORD(v12) = v14 % 6;
if ( v13 < v14 % 6 ) // 随机数大于safecode就未成功抓取
v28 = 0;
}
sub_55CD48C27F20(v11, v4, v12);
draw(v11, v4, v15);
usleep(500000LL);
}
_tsan_read4(&flagy);
if ( flagy != 1 || (_tsan_read4(&position_x), v16 = position_x, _tsan_read4(&flagx), v16 != flagx) )
{
LABEL_30:
pthread_mutex_lock(&unk_55CD49909A78);
v25 = life - 1;
_tsan_write4(&life);
life = v25;
pthread_mutex_unlock(&unk_55CD49909A78);
printf("sorry you miss it\n");
sleep(1LL);
init_game(1LL, v4, v26);
}
else
{
sleep(1LL);
pthread_mutex_lock(&unk_55CD49909A50);
v17 = safe_code - 1;
_tsan_write4(&safe_code);
safe_code = v17;
v18 = level + 1;
_tsan_write4(&level);
level = v18;
printf("you get one!\n");
printf("level UP!\n");
v19 = X0Y0 + 23;
_tsan_write8(&X0Y0);
X0Y0 = v19;
_tsan_read4(&level);
if ( level == 5 )
printf("You win!\n");
pthread_mutex_unlock(&unk_55CD49909A50);
sleep(1LL);
init_game(1LL, v4, v20);
}
return _tsan_func_exit(1LL, v4, v21, v22, v23, v24);
}

1
2
if ( v13 < v14 % 6 )                      // 随机数大于safecode就未成功抓取
v28 = 0;

改为

1
2
if ( v13 < v14 % 6 )                      // 随机数大于safecode就未成功抓取
v28 = 1;

更改完毕后按以上思路操作,未出flag,于是尝试手动通过5关,弹出一半flag
image

根据弹出的提示,搜索字符串find a race