思路 这个题就是把输入的字符串先去掉ZCTF{}后,把括号里的字符串替换,然后进行了浮点运算,运算后的结果和0x19异或,得到的数值就是 . 7 , ) ! . ( , , ) . , , /
具体分析 载入IDA能看出flag格式为:
小于36字符
格式为ZCTF{}
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
signed int __u sercall start@<eax>(int a1@<esi>, _B YTE *a2@<ebx>)
{
signed int result;
char v3;
char Str;
int v5;
int v6;
int v7;
int v8;
int v9;
int v10;
int v11;
char v12;
char *v13;
Str = 0 ;
v5 = 0 ;
v6 = 0 ;
v7 = 0 ;
v8 = 0 ;
v9 = 0 ;
v10 = 0 ;
v11 = 0 ;
v12 = 0 ;
printf ("Please input the flag : " );
scanf ("%s" , &Str);
if ( strlen (&Str) < 0x1E )
{
if ( SBYTE3(v5) == '{' && *(&v3 + strlen (&Str)) == '}' )
{
*(&v3 + strlen (&Str)) = 0 ;
BYTE3(v5) = 0 ;
if ( !stricmp(&Str, "ZCTF" ) )
{
v13 = &v6;
if ( sub_E912E0(a1, a2, &v6) )
printf ("Congratulations!\n" );
else
printf ("Sorry! Flag error!\n" );
result = 0 ;
}
else
{
printf ("Sorry! Flag error!\n" );
result = 1 ;
}
}
else
{
printf ("Sorry! Flag error!\n" );
result = 1 ;
}
}
else
{
printf ("Sorry! Flag error!\n" );
result = 1 ;
}
return result;
}
用OD进行动态调试,关键位置下好断点,来到如下位置 跟到上面的位置,发现大括号里面长度为17,然后取每一字符减去0x30再查询一个偏移表,然后通过跳转表跳转到指定位置初始化一个全局变量。 跳转表如下: 偏移表如下:
其中偏移表中填充了大量0x0B,查询到这个偏移后通过跳转表会直接跳转到函数结束的位置。这里要注意这两个表的跳转关系,输入的字符每一个减去0x30后,先和0x3F比较(偏移表的长度为0x3F,不能跑到这个表外面去),然后再加偏移表的首地址得到一个地址,里面的值就是偏移量,如果值是0,就对应分支表的第一个跳转,如果值是1就对应分支表的第二个跳转,依次类推。跳转到相对应的位置进行替换。 通过偏移表中对应的值的地址-偏移表起始地址+0x30反推输入与全局变量的关系如下:1
2
3
4
5
6
7
8
9
10
11
0 .
o 0
e 1
T 2
H 3
U 4
_ 5
S 6
8 7
W 8
R 9
当然这种对应关系看IDA会更加清楚一点
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
char *__u sercall sub_E91000@<eax>(int a1@<ebp>)
{
size_t v1;
char *result;
v1 = strlen (*(a1 + 8 ));
if ( v1 == 17 )
{
*(a1 - 4 ) = 0 ;
*(a1 - 5 ) = *(*(a1 - 4 ) + *(a1 + 8 ));
while ( 2 )
{
*(a1 - 12 ) = *(a1 - 5 );
*(a1 - 12 ) -= 0x30 ;
switch ( *(a1 - 12 ) )
{
case 0 :
*(&Str + *(a1 - 4 )) = 0x2E ;
goto LABEL_19;
case 0x3F :
*(&Str + *(a1 - 4 )) = 0x30 ;
goto LABEL_19;
case 0x35 :
*(&Str + *(a1 - 4 )) = 0x31 ;
goto LABEL_19;
case 0x24 :
*(&Str + *(a1 - 4 )) = 0x32 ;
goto LABEL_19;
case 0x18 :
*(&Str + *(a1 - 4 )) = 0x33 ;
goto LABEL_19;
case 0x25 :
*(&Str + *(a1 - 4 )) = 0x34 ;
goto LABEL_19;
case 0x2F :
*(&Str + *(a1 - 4 )) = 0x35 ;
goto LABEL_19;
case 0x23 :
*(&Str + *(a1 - 4 )) = 0x36 ;
goto LABEL_19;
case 8 :
*(&Str + *(a1 - 4 )) = 0x37 ;
goto LABEL_19;
case 0x27 :
*(&Str + *(a1 - 4 )) = 56 ;
goto LABEL_19;
case 0x22 :
*(&Str + *(a1 - 4 )) = 57 ;
LABEL_19:
*(a1 - 5 ) = *(++*(a1 - 4 ) + *(a1 + 8 ));
if ( *(a1 - 5 ) )
continue ;
result = &Str;
break ;
default :
result = 0 ;
break ;
}
break ;
}
}
else
{
result = 0 ;
}
return result;
}
然后将这个数值在00c61323这个函数里做浮点运算
运算后的结果和0x19异或后和字符串. 7 , ) ! . ( , , ) . , , / 进行比较,当然我们用0x19和字符串. 7 , ) ! . ( , , ) . , , / 异或后会得到正确的flag的浮点运算结果,然后逆推。 对应关系如下:1
2
.7,)*!.(,,).,,*/**
7.5038715507553633
所以重点在浮点运算,我们看一下浮点运算函数的汇编代码
F7跟入函数
根据0xF13348处的浮点数一共算出四个结果,分析过程如上图。得到一元二次方程,我们解这个方程得到浮点运算前的数值,也就是刚开始时替换后的数值:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from sympy import *
"""
res1 = (Str - 1.864832709999999) * (6.51951822361*Str)
res2 = (Str - 5.778432978588) * (Str + 3.1415926) * 7.861418532
res3 = (Str*Str - 0.8521405969999999) * (8.7356198421365 - 2.0000)
res4 = (Str*Str * 4.93287651872 - Str * 9.542697368122542) * 2.681794561929999
result = res1 + res2 - res3 + res4 + 60.0000
"""
Str = symbols('Str' )
jie = solve([-7.5038715507553633 +(Str - 1.864832709999999 ) * (6.51951822361 *Str)
+(Str - 5.778432978588 ) * (Str + 3.1415926 ) * 7.861418532
-(Str*Str - 0.8521405969999999 ) * (8.7356198421365 - 2.0000 )
+ (Str*Str * 4.93287651872 - Str * 9.542697368122542 ) * 2.681794561929999
+ 60.0000 ],[Str])
print jie
结果如下:1
2
3
4
D:\Python27\python.exe C:/Users/Desktop/Math.py
[(-1.05058442661615,), (3.85205471562591,)]
Process finished with exit code 0
有两个解,[(-1.05058442661615,), (3.85205471562591,)] 上面的result,也就是7.5038715507553633是根据判断处的数据和0x19异或得到的。 解出来以后通过查上面的对应关系能得到FLAG。负解舍弃,保留正解3.85205471562591,但由于精度不够,最后一位字符没有对照,但完全可以猜出来,就是那17个字符8 , H , R , S , T , U , W , _ , e , o , 0 中的一个。最后flag为ZCTF{H0W_To_U8e_ST_Re8}