BUUCTF-[GUET-CTF2019]number_game1

下载题目:下载

下载后发现无壳,64位,拖入IDA。 大致看一下代码,简短感觉很简单,结果还是不会,我是菜鸡... 主要代码为if语句条件判断不为0则输出“TQL!”,就成功。那看一下sub_400917。

 发现这是一个数独:每一行每一列不能有相同的数,并且for语句是0到4,所以是5×5数独。要使if条件不等于0,则需要数独成立,即每一行每一列不能有相同的数。

返回到主函数,看sub_4006D6函数。从这里可以知道输入的字符串长度为10,并且输入的必须是字符0-4,否则会goto LABEL_2,就会输出wrong。

返回主函数进入sub_400758()函数。在这里我就懵了,不知道这是什么,经过查阅资料知道这是二叉树知识点,几乎没有接触过...,查阅:[GUET-CTF2019] number_game_firfis的博客-CSDN博客,该函数作用是建一个二叉树,每个节点申请了0x18位的空间,也就是3个字节。
v6[0]存储节点的值v6[1]存储 左子树的地址v6[2]存储 右子树的地址
树的结构与对应的输入值的关系如下图

 

查阅资料:数据结构之二叉树(C语言附详细代码)_樱桃的崩崩的博客-CSDN博客_二叉树代码,其中前序,中序,后序遍历是蛮重要的应该。

 

一,二叉树的遍历:

    前序遍历:根->左->右;

    以上图为例:从根节点开始->输出A,然后找到左子树->输出B,再以B为子根,找到B的左子树->输出D,再以D为子根,找到D的左子树->输出H,然后找到D的右子树->输出I,同样的输出E;

    然后到根A的右子树C,同样循环上述操作,右侧输出为CFJG;

    总输出顺序为:A-B-D-H-I-E-C-F-J-G;

    中序遍历:左->根->右;

    通过顺序A->B->D->H,找到最左的H,并输出H,然后返回,输出H的子根D,然后找到D的右子树I并输出,然后继续输出D的子根B,找到B的右子树E并输出,然后找到B的根A并输出;

    然后找根A的右子树C,然后通过顺序C->F->J找到J,并对右侧进行相同操作;

    最后输出为:H-D-I-B-E-A-J-F-C-G;

    通过中序遍历可以清楚的得出结点的左右位置;

    后序遍历:左->右->根;

    同样的,通过顺序A->B->D->H,找到最左的H,并输出H,然后返回输出H的同层次同根的结点I并输出,最后输出子根D,继续找D的同层次同子根结点E并输出,最后再输出子根B,然后找到与B同层次同根的结点C,然后通过C->F->J的顺序找到J,并重复上述操作;

    最后的输出顺序:H-I-D-E-B-J-F-G-C-A;


二,代码

   ① 前序遍历二叉树

    首先传入树的首地址,判断是否为空,非空则输出数据,然后循环本函数遍历左子树,根据前序遍历的特点:“根->左->右”,进行遍历输出;
    void PreShowBitree(bitree_t *r)
    {
        if(r == NULL)
        {
            return;
        }
        printf("%c",r->data);
        PreShowBitree(r->lchild);
        PreShowBitree(r->rchild);
    }

    ②中序遍历二叉树

    首先传入树的首地址,判断是否为空,非空则循环本函数遍历左子树,根据中序遍历的特点:“左->根->右”,进行遍历输出;
    void midShowBitree(bitree_t *r)
    {
        if(r == NULL)
        {
            return;
        }
        midShowBitree(r->lchild);
        printf("%c",r->data);
        midShowBitree(r->rchild);
    }

    ③后序遍历二叉树

    首先传入树的首地址,判断是否为空,非空则循环本函数遍历左子树,根据后序遍历的特点:“左->右->根”,进行遍历输出;
    void PostShowBitree(bitree_t *r)
    {
        if(r == NULL)
        {
            return;
        }
        PostShowBitree(r->lchild);
        PostShowBitree(r->rchild);
        printf("%c",r->data);

返回主函数,进入sub_400807()函数。发现很像是中序遍历。则有对应关系,摘自[GUET-CTF2019] number_game_firfis的博客-CSDN博客

再进入sub_400881()函数,

 发现是把变化后的10个字符分别存入地址中,跟进观察:

变化后的数据是未知数#,根据是5×5数独,可以求出#,为0,4,2,1,4,2,1,4,3,0,即输入的flag变化后的数据。再根据中序变化的加密就可以得出flag。

map = [7, 3, 8, 1, 9, 4, 0, 5, 2, 6] # 对应关系
v7 = [48, 52, 50, 49, 52, 50, 49, 52, 51, 48]
flag=[0]*10

for i in range(10):
    flag[map[i]] = chr(v7[i])
flag=''.join(flag)
print('flag{'+flag+'}')
# 1134240024

 

 

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值