buu-re-54

Article Directory

number_game

这道题是一道二叉树遍历的逆向,数据结构刚学完2个月,看的时候完全没想起来树的相关知识,搁那跟踪递归,头都快跟晕了。还有一些动调的问题,一并在这里记一下。

首先,文件无壳,64位elf,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
unsigned __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // ST08_8
__int64 v5; // [rsp+10h] [rbp-30h]
__int16 v6; // [rsp+18h] [rbp-28h]
__int64 v7; // [rsp+20h] [rbp-20h]
__int16 v8; // [rsp+28h] [rbp-18h]
char v9; // [rsp+2Ah] [rbp-16h]
unsigned __int64 v10; // [rsp+38h] [rbp-8h]

v10 = __readfsqword(0x28u);
v5 = 0LL;
v6 = 0;
v7 = 0LL;
v8 = 0;
v9 = 0;
__isoc99_scanf("%s", &v5, a3);
if ( (unsigned int)sub_4006D6(&v5) ) //检测输入的长度和内容是否合理(10个数,每个都要是0~4)
{
v3 = sub_400758(&v5, 0LL, 10LL); //这里是个先序遍历创建树
sub_400807(v3, &v7); //中序遍历
v9 = 0;
sub_400881(&v7); //将中序遍历的结果填入数独表的'#'中
if ( (unsigned int)sub_400917() ) //5*5数独检测
{
puts("TQL!");
printf("flag{");
printf("%s", &v5);
puts("}");
}
else
{
puts("your are cxk!!");
}
}
return __readfsqword(0x28u) ^ v10;
}

先看看先序遍历sub_400758

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
_QWORD *__fastcall sub_400758(__int64 a1, int a2, int a3)
{
_QWORD *v4; // rax
_QWORD *v5; // ST28_8
unsigned int v6; // [rsp+0h] [rbp-30h]
char v7; // [rsp+1Fh] [rbp-11h]

v6 = a3;
v7 = *(_BYTE *)(a2 + a1);
if ( v7 == 32 || v7 == 10 || a2 >= a3 )
return 0LL;
v4 = malloc(0x18uLL); //创建节点
v5 = v4;
//树的顺序存储
*(_BYTE *)v4 = v7; //根节点赋值
v4[1] = sub_400758(a1, (unsigned int)(2 * a2 + 1), v6); //左子树(i*2+1)
v5[2] = sub_400758(a1, (unsigned int)(2 * (a2 + 1)), v6); //右子树(i*(2+1))
return v5;
}

画个图吧,以输入序列:0123456789来看

1

先序遍历赋值完后v3就是上面这棵树

然后sub_400807就是将中序遍历v3的结果赋给v7,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall sub_400807(__int64 a1, __int64 a2)
{
__int64 result; // rax

result = a1;
if ( a1 )
{
sub_400807(*(_QWORD *)(a1 + 8), a2); //遍历左子树
*(_BYTE *)(a2 + dword_601080++) = *(_BYTE *)a1; //根节点
result = sub_400807(*(_QWORD *)(a1 + 16), a2); //遍历右子树
} //数据结构朗朗上口了hhh
return result;
}

中序遍历的结果就是7,3,8,1,9,4,0,5,2,6

解题脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
text = [0x31, 0x34, 0x23, 0x32, 0x33, 0x33, 0x30, 0x23, 0x31, 0x23,
0x30, 0x23, 0x32, 0x33, 0x23, 0x23, 0x33, 0x23, 0x23, 0x30,
0x34, 0x32, 0x23, 0x23, 0x31]

for i in range(len(text)):
if i%5==0 :print()
print(chr(text[i]),end="")
print()
flagen=[0,4,2,1,4,2,1,4,3,0]
order=[7,3,8,1,9,4,0,5,2,6]
flag=[0,0,0,0,0,0,0,0,0,0]
for i in range(len(flagen)):
flag[order[i]]=flagen[i]
print(flag)

flag{1134240024}

动调

一开始开动调一步一步跟着递归,实在是有点费脑子,后来就想着干脆直接绕过输入合理性检测,输入个0123456789看最终的v7序列得了,
便做了以下尝试
2
3
patch完后发现动调始终达不到目的,依旧会进入checkenable,输入不合理的时候依然会给 eax 赋0,不知道咋回事,害

后来看别的师傅的wp的时候发现ida还可以在调试状态下动态修改标志寄存器。。(不好意思,wtcl)

这样的话,就在会检测到不合理的地方下断点
4
每次跳转前修改一下ZF让他不跳转,然后往下执行,就可以得到想要的v7了!

Comments