FortIDCTF - toilet
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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
unsigned int v3; // ebx
__int64 v4; // rax
__int64 v5; // rax
_QWORD *v6; // rax
unsigned int v7; // eax
__int64 v8; // rax
__int64 v9; // rax
int v10; // r12d
_QWORD *v11; // rax
_QWORD *v12; // rax
unsigned int v14; // [rsp+14h] [rbp-40Ch] BYREF
unsigned __int64 index; // [rsp+18h] [rbp-408h] BYREF
unsigned __int64 mask; // [rsp+20h] [rbp-400h]
__int64 v17; // [rsp+28h] [rbp-3F8h]
char v18[32]; // [rsp+30h] [rbp-3F0h] BYREF
char v19[32]; // [rsp+50h] [rbp-3D0h] BYREF
char v20[400]; // [rsp+70h] [rbp-3B0h] BYREF
char v21[256]; // [rsp+200h] [rbp-220h] BYREF
_QWORD v22[36]; // [rsp+300h] [rbp-120h] BYREF
v22[33] = __readfsqword(0x28u);
if ( a1 == 2 )
{
std::basic_ifstream<char,std::char_traits<char>>::basic_ifstream(v21, a2[1], 8LL);
if ( (unsigned __int8)std::basic_ios<char,std::char_traits<char>>::operator!(v22) )
{
perror("open input");
v3 = 1;
}
else
{
v6 = (_QWORD *)std::istream::operator>>(v21, &v14);
if ( (unsigned __int8)std::basic_ios<char,std::char_traits<char>>::operator!((char *)v6 + *(_QWORD *)(*v6 - 24LL)) )
{
std::operator<<<std::char_traits<char>>(&std::cerr, "Error: could not read brush size\n");
v3 = 1;
}
else if ( v14 && v14 <= 0x3C )
{
mask = (1LL << v14) - 1;
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v18);
std::getline<char,std::char_traits<char>,std::allocator<char>>(v21, v18);
while ( 1 )
{
v12 = (_QWORD *)std::getline<char,std::char_traits<char>,std::allocator<char>>(v21, v18);
if ( !(unsigned __int8)std::basic_ios<char,std::char_traits<char>>::operator bool((char *)v12 + *(_QWORD *)(*v12 - 24LL)) )
break;
if ( (unsigned __int8)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::empty(v18) )
{
std::operator<<<std::char_traits<char>>(&std::cout, "\n");
}
else
{
v7 = sub_2E42(16LL, 8LL);
std::__cxx11::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::basic_stringstream(
v20,
v18,
v7);
while ( 1 )
{
v11 = (_QWORD *)std::istream::operator>>(v20, &index);
if ( !(unsigned __int8)std::basic_ios<char,std::char_traits<char>>::operator bool((char *)v11 + *(_QWORD *)(*v11 - 24LL)) )
break;
if ( mask < index )
{
v8 = std::operator<<<std::char_traits<char>>(&std::cerr, "Error: k = ");
v9 = std::ostream::operator<<(v8, index);
std::operator<<<std::char_traits<char>>(v9, " is too large\n");
v3 = 1;
v10 = 0;
goto LABEL_19;
}
v17 = sub_26DC(index, mask);
sub_288B(v19, v17, v14);
std::operator<<<char,std::char_traits<char>,std::allocator<char>>(&std::cout, v19);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v19);
}
std::operator<<<std::char_traits<char>>(&std::cout, "\n");
v10 = 1;
LABEL_19:
std::__cxx11::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::~basic_stringstream(v20);
if ( v10 != 1 )
goto LABEL_22;
}
}
v3 = 0;
LABEL_22:
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v18);
}
else
{
std::operator<<<std::char_traits<char>>(&std::cerr, "Error: brush size must be between 1 and 60\n");
v3 = 1;
}
}
std::basic_ifstream<char,std::char_traits<char>>::~basic_ifstream(v21);
}
else
{
v4 = std::operator<<<std::char_traits<char>>(&std::cerr, "Usage: ");
v5 = std::operator<<<std::char_traits<char>>(v4, *a2);
std::operator<<<std::char_traits<char>>(v5, " input.txt\n");
return 1;
}
return v3;
}
其中sub_288B函数会将一个int64数字按照0/1比特转换成 ‘ ’ 和 ‘*’相间的字符串 关键函数:
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
__int64 __fastcall sub_26DC(unsigned __int64 a1, __int64 a2)
{
__int64 v2; // rbx
__int64 v4; // [rsp+0h] [rbp-90h] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-88h]
__int64 v6; // [rsp+10h] [rbp-80h] BYREF
unsigned __int64 i; // [rsp+18h] [rbp-78h]
char v8[32]; // [rsp+20h] [rbp-70h] BYREF
char v9[56]; // [rsp+40h] [rbp-50h] BYREF
unsigned __int64 v10; // [rsp+78h] [rbp-18h]
v5 = a1;
v4 = a2;
v10 = __readfsqword(0x28u);
if ( a1 == 1 )
return 1LL;
sub_2F32(v9);
sub_2FB2(v8);
v6 = 1LL;
sub_33BC(v9, &v6);
v6 = 1LL;
sub_3440(v8, &v6);
sub_3476(v9, &v4);
sub_34EE(v8, &v4);
if ( v5 == 2 )
{
v2 = v4;
}
else
{
v6 = 0LL;
for ( i = 3LL; i <= v5; ++i )
{
v6 = sub_2589(v9, v8, v4);
sub_3476(v9, &v6);
sub_34EE(v8, &v6);
}
v2 = v6;
}
sub_3374(v8);
sub_2F52(v9);
return v2;
}
通过动态调试猜测出了其功能,静态分析比较难,不知道有没有什么好的静态方法。 执行逻辑是:传入一个index和mask,其中index为1时返回1,为2时返回mask,其它的情况会遍历所有的点,然后计算和所有已知点的距离,如果是其中最小的就返回。
程序固有的算法效率实在太低,不可能算完。我们很容易找出其中的通项公式,然后用frida hook替换目标函数,改成我们自己的逻辑。
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
function convert(num: bigint, mask: bigint) {
if (num == 0n)
return 0n;
if (num == 1n)
return 1n;
if (num == 2n)
return mask;
if (num == 3n)
return (1n + mask) / 2n;
// find group (2^r+1)
var grp_num: bigint = 0n;
while (1) {
var x = 2n ** grp_num + 1n;
if (num <= x) {
break;
}
grp_num++;
}
grp_num--;
var remain: bigint = num - 2n ** grp_num - 1n;
var mid: bigint = (1n + mask) / 2n;
var cellNum: bigint = 2n ** grp_num;
var cellSize: bigint = (1n + mask) / cellNum;
if (remain == cellNum - 1n) {
return cellSize / 2n;
} else if (remain == cellNum) {
return mid + cellSize / 2n - 1n;
} else if (remain <= (cellNum - 2n) / 2n) {
return cellSize / 2n + cellSize * remain;
} else if (remain <= cellNum - 2n) {
return cellSize / 2n + cellSize * (remain + 1n) - 1n;
} else {
console.log("error", num, remain);
return 1n;
}
}
const proc = Process.findModuleByName("chal");
if (proc) {
const baseAddr = proc.base;
const targetAddr = baseAddr.add(0x26DC)
var cal_result: bigint | undefined;
Interceptor.attach(targetAddr, {
onEnter: function(args) {
const num: bigint = BigInt("0x" + args[0].toString(16));
const mask: bigint = BigInt("0x" + args[1].toString(16));
cal_result = convert(num, mask);
args[0] = ptr(1);
},
onLeave: function(retval) {
if (cal_result) {
retval.replace(ptr("0x" + cal_result.toString(16)));
}
}
});
}
else {
console.log("Cannot find main module.");
}
(中间用字符串来构造ptr,以及提取int64参数,避免默认的number截断)
但是这一题有些数据似乎是超过mask范围的?
[TODO]
This post is licensed under CC BY 4.0 by the author.