这一题卡了很久,主要是在递归上面,对树这个数据结构不是很敏感。还有,这递归实在不太符合人的思维方式 😦
程序流程大概就是通过建立一个二叉树,用中序遍历对输入进行了一个加密(应该算是吧),最后用一个5*5的二维数组数独检验flag是否正确
函数分析
main函数
unsigned __int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v4; // [rsp+8h] [rbp-38h]
__int64 v5; // [rsp+10h] [rbp-30h] BYREF
__int16 v6; // [rsp+18h] [rbp-28h]
__int64 v7; // [rsp+20h] [rbp-20h] BYREF
__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);
if ( (unsigned int)sub_4006D6((const char *)&v5) )
{
v4 = sub_400758(&v5, 0LL, 10LL);
sub_400807(v4, &v7);
v9 = 0;
sub_400881(&v7);
if ( (unsigned int)sub_400917() )
{
puts("TQL!");
printf("flag{");
printf("%s", (const char *)&v5);
puts("}");
}
else
{
puts("your are cxk!!");
}
}
return __readfsqword(0x28u) ^ v10;
}
sub_4006D6
__int64 __fastcall sub_4006D6(const char *a1)
{
int i; // [rsp+1Ch] [rbp-4h]
if ( strlen(a1) == 10 )
{
for ( i = 0; i <= 9; ++i )
{
if ( a1[i] > '4' || a1[i] <= 47 )
goto LABEL_2;
}
return 1LL;
}
else
{
LABEL_2:
puts("Wrong!");
return 0LL;
}
}
要求输入的字符串长度为10
,字符范围为'0'-'4'
sub_400758
_QWORD *__fastcall sub_400758(__int64 a1, int a2, unsigned int a3)
{
char v5; // [rsp+1Fh] [rbp-11h]
_QWORD *v6; // [rsp+28h] [rbp-8h]
v5 = *(_BYTE *)(a2 + a1);
if ( v5 == ' ' || v5 == '\n' || a2 >= (int)a3 )
return 0LL;
v6 = malloc(0x18uLL);
*(_BYTE *)v6 = v5;
v6[1] = sub_400758(a1, 2 * a2 + 1, a3);
v6[2] = sub_400758(a1, 2 * (a2 + 1), a3);
return v6;
}
该函数作用是建一个二叉树,每个节点申请了0x18
位的空间,也就是3
个字节。
v6[0]
存储节点的值,v6[1]
存储 左子树的地址,v6[2]
存储 右子树的地址
树的结构与对应的输入值的关系如下图
sub_400807
__int64 __fastcall sub_400807(__int64 v4, __int64 v7)
{
__int64 result; // rax
result = v4;
if ( v4 )
{
sub_400807(*(_QWORD *)(v4 + 8), v7);
*(_BYTE *)(v7 + dword_601080++) = *(_BYTE *)v4;
return sub_400807(*(_QWORD *)(v4 + 16), v7);
}
return result;
}
可知v7
是这棵树的中序遍历, 则v7
与input
之间的对应关系如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
v7 | Input[7] | Input[3] | Input[8] | Input[1] | Input[9] | Input[4] | Input[0] | Input[5] | Input[2] | Input[6] |
sub_400881
__int64 __fastcall sub_400881(char *a1)
{
__int64 result; // rax
byte_601062 = *a1;
byte_601067 = a1[1];
byte_601069 = a1[2];
byte_60106B = a1[3];
byte_60106E = a1[4];
byte_60106F = a1[5];
byte_601071 = a1[6];
byte_601072 = a1[7];
byte_601076 = a1[8];
result = (unsigned __int8)a1[9];
byte_601077 = a1[9];
return result;
}
作用是将v7
数组放入相应的内存单元中
sub_400917
__int64 sub_400917()
{
unsigned int v1; // [rsp+0h] [rbp-10h]
int i; // [rsp+4h] [rbp-Ch]
int j; // [rsp+8h] [rbp-8h]
int k; // [rsp+Ch] [rbp-4h]
v1 = 1;
for ( i = 0; i <= 4; ++i )
{
for ( j = 0; j <= 4; ++j )
{
for ( k = j + 1; k <= 4; ++k )
{
if ( *((_BYTE *)&unk_601060 + 5 * i + j) == *((_BYTE *)&unk_601060 + 5 * i + k) )
v1 = 0;
if ( *((_BYTE *)&unk_601060 + 5 * j + i) == *((_BYTE *)&unk_601060 + 5 * k + i) )
v1 = 0;
}
}
}
return v1;
}
作用是判断首地址为60106
的内存单元看作5行5列的二维数组,并且要求每行每列不能出现相同的值,可以看成是一个简易版的数独。
将数据取出,并稍作处理
手动解密,得到 v7 = ['0', '4', '2', '1', '4', '2', '1', '4', '3', '0']
根据上述的v7
与input
之间的对应关系,将v7
映射到input
中,脚本如下:
map = [7, 3, 8, 1, 9, 4, 0, 5, 2, 6] # 对应关系
v7 = ['0', '4', '2', '1', '4', '2', '1', '4', '3', '0']
flag = [0]*10
for i in range(10):
flag[map[i]] = v7[i]
print(''.join(str(i) for i in flag))
# 1134240024
当然手动对应也可以,但感觉代码写出来会更加明白一点
程序验证
flag{1134240024}
没有问题