首页
社区
课程
招聘
[原创]看雪 2022 KCTF 春季赛 第二题 末日邀请
发表于: 2022-5-12 01:26 15548

[原创]看雪 2022 KCTF 春季赛 第二题 末日邀请

2022-5-12 01:26
15548

直接拖入IDA。
整个程序的对输入的检查分为几个部分。

获取输入长度,然后计算crc32的值(略有修改,标准crc32里的无符号右移在本程序中是有符号右移)
main函数末尾对这个值有检查:

计算3n+1猜想,由于最后必定落入4->2->1->4的循环,因此 v17 = v66[198] | v66[197] | v66[196] 的值一定等于 4 | 2 | 1 = 7
动态调试发现,v72受所有输入字符的影响,导致循环中的v15可能为负数,只能落入-1 -> -2 -> -1的循环,这显然不是正确答案。

确定输入的第3-6个字符是"KCTF"。由第二部分,可以认为在正确的输入下v17是常量7。

迭代输入的第7-15位共九个数字,将其逐渐加位转换为整数,然后验证能被位数整除。由于位数不超过9,所以v23 <= 1262703685的条件永远成立。
这个校验的Python等价代码:

容易发现在数位长度为偶数时除数也是偶数,因此偶数位必须都是偶数才有可能整除。

这里对第7-15个字符做冒泡排序,然后验证值为1-9,因此可确定第7-15个字符是'1'-'9'的全排列

对奇数位的5个数和偶数位的4个数的全排列分别遍历:

得到唯一符合要求的结果:381654729

至此,可确定正确的输入满足正则:???KCTF381654729.*

题目做到这里时先写了爆破crc的程序(见第六部分),但是没爆出结果。后来才发现是因为忘记调用init_crctable()初始化crc table……
痛哭流涕……(此时题目一血还未出现)

(因为前一部分的失误,浪费了不少时间在这里整理sub_4010E1的逻辑,其实这个函数完全无用)

sub_4010E1的核心是8字节到8字节的一个线性变换,Python的等价写法为:

对于它的求解,可以用z3一把梭:(注意z3的BitVec重载的>>是算术右移)

这部分检查的是输入值从第16个字节开始的部分:

asc_4055C0asc_403DC8异或,然后以8字节为单位求解,发现无解

现在只有前三个字节未知。crc是可逆的,不过3个字节还是直接爆破更简单,直接复制IDA反编译的伪代码:

秒出结果:421KCTF381654729,输入程序中验证通过。

...
  scanf("%s", (char)inputvalue);
  printf("\n现在,你就是韩立,韩立就是你,如遇绝境,吼:男人至死是少年!", v33);
  inputvalue[41] = 0;
  inputlen = strlen(inputvalue);
  v5 = 0;
  inputlen2 = inputlen;
  v6 = inputlen;
  v72 = 0;
  if ( inputlen )
  {
    v7 = inputvalue;
    do
    {
      v5 ^= *v7;
      --v6;
      ++v7;
      v8 = 8;
      do
      {
        v9 = 2 * v5;
        v10 = v9 ^ 7;
        if ( v9 >= 0 )
          v10 = v9;
        v5 = v10;
        --v8;
      }
      while ( v8 );
    }
    while ( v6 );
    inputlen = inputlen2;
    v72 = v10;
  }
  init_crctable();
  v11 = -1;
  for ( i = 0; i < inputlen; ++i )
    v11 = crctable[(unsigned __int8)(v11 ^ inputvalue[i])] ^ (v11 >> 8);
  v67 = ~v11;
...
...
  scanf("%s", (char)inputvalue);
  printf("\n现在,你就是韩立,韩立就是你,如遇绝境,吼:男人至死是少年!", v33);
  inputvalue[41] = 0;
  inputlen = strlen(inputvalue);
  v5 = 0;
  inputlen2 = inputlen;
  v6 = inputlen;
  v72 = 0;
  if ( inputlen )
  {
    v7 = inputvalue;
    do
    {
      v5 ^= *v7;
      --v6;
      ++v7;
      v8 = 8;
      do
      {
        v9 = 2 * v5;
        v10 = v9 ^ 7;
        if ( v9 >= 0 )
          v10 = v9;
        v5 = v10;
        --v8;
      }
      while ( v8 );
    }
    while ( v6 );
    inputlen = inputlen2;
    v72 = v10;
  }
  init_crctable();
  v11 = -1;
  for ( i = 0; i < inputlen; ++i )
    v11 = crctable[(unsigned __int8)(v11 ^ inputvalue[i])] ^ (v11 >> 8);
  v67 = ~v11;
...
...
    if ( v67 == 0xF52E0765 )
    {
      printf("\n你一如既往的谨慎,哪怕屠龙在手.........", v61);
      printf(
        "\n"
        "十年后,你终于干掉了赤月恶魔,肃清了赤月邪教,获得了 赤月套装 .\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了.",
        v62);
    }
...
...
    if ( v67 == 0xF52E0765 )
    {
      printf("\n你一如既往的谨慎,哪怕屠龙在手.........", v61);
      printf(
        "\n"
        "十年后,你终于干掉了赤月恶魔,肃清了赤月邪教,获得了 赤月套装 .\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了.",
        v62);
    }
...
...
  hex_to_int(inputvalue, inputlen);
  v13 = v72;
  v69 = 1;
  v68 = v72 + 1;
  v14 = v68;
  do
  {
    v15 = v13;
    for ( j = 1; j < 200; ++j )
    {
      if ( (v15 & 1) != 0 )
        v15 = 3 * v15 + 1;                      // 3n+1
      else
        v15 >>= 1;
      v66[j] = v15;
    }
    ++v13;
  }
  while ( v13 < v14 );
  v17 = v66[198] | v66[197] | v66[196];         // 4 | 2 | 1 == 7
  v18 = inputlen2;
  if ( v17 != (inputvalue[2] ^ inputvalue[1] ^ inputvalue[0]) )
  {
    printf("\n%s", (char)"你深深明白那些裤衩人在做什么,时不我待.");
    printf(
      "\n%s\n%s",
      (char)"你加入了他们,前世作为高知识分子,你深深明白,赤手空拳的你,肯定不是鹿的对手.");
    printf(
      "\n%s",
      (char)"虽然前世没有被酒色掏空了身体(没那个钱财).但是经常加班的你,灵魂里面根本没有动手能力这个概念.");
    printf("\n%s", (char)"大公鸡攻击了你的小公鸡,GAME OVER ...");
    goto LABEL_57;
  }
...
...
  hex_to_int(inputvalue, inputlen);
  v13 = v72;
  v69 = 1;
  v68 = v72 + 1;
  v14 = v68;
  do
  {
    v15 = v13;
    for ( j = 1; j < 200; ++j )
    {
      if ( (v15 & 1) != 0 )
        v15 = 3 * v15 + 1;                      // 3n+1
      else
        v15 >>= 1;
      v66[j] = v15;
    }
    ++v13;
  }
  while ( v13 < v14 );
  v17 = v66[198] | v66[197] | v66[196];         // 4 | 2 | 1 == 7
  v18 = inputlen2;
  if ( v17 != (inputvalue[2] ^ inputvalue[1] ^ inputvalue[0]) )
  {
    printf("\n%s", (char)"你深深明白那些裤衩人在做什么,时不我待.");
    printf(
      "\n%s\n%s",
      (char)"你加入了他们,前世作为高知识分子,你深深明白,赤手空拳的你,肯定不是鹿的对手.");
    printf(
      "\n%s",
      (char)"虽然前世没有被酒色掏空了身体(没那个钱财).但是经常加班的你,灵魂里面根本没有动手能力这个概念.");
    printf("\n%s", (char)"大公鸡攻击了你的小公鸡,GAME OVER ...");
    goto LABEL_57;
  }
...
...
  const_9 = v17 + 2;
  v20 = v18 - const_9 - 7;
  if ( inputvalue[3] != 20 )                    // chr(20+55) = 'K'
  {
...
  if ( inputvalue[4] != 12 )                    // 'C'
  {
...
  if ( inputvalue[5] != 29 )                    // 'T'
  {
...
  if ( inputvalue[6] != 15 )                    // 'F'
  {
...
...
  const_9 = v17 + 2;
  v20 = v18 - const_9 - 7;
  if ( inputvalue[3] != 20 )                    // chr(20+55) = 'K'
  {
...
  if ( inputvalue[4] != 12 )                    // 'C'
  {
...
  if ( inputvalue[5] != 29 )                    // 'T'
  {
...
  if ( inputvalue[6] != 15 )                    // 'F'
  {
...
...
  v21 = 0;
  inputlen2 = 0;
  if ( const_9 > 0 )
  {
    v22 = 1;
    do
    {
      v23 = inputvalue[v22 + 6] + 10 * v70;
      v24 = v23 - 926365495;
      if ( v23 <= 1262703685 )
        v24 = v23;
      v70 = v24;
      if ( v24 % v69 )
        goto LABEL_50;
      v21 = inputlen2 + 1;
      v22 = v69 + 1;
      inputlen2 = v21;
      ++v69;
    }
    while ( v21 < const_9 );
  }
  if ( v21 != const_9 )
  {
...
...
  v21 = 0;
  inputlen2 = 0;
  if ( const_9 > 0 )
  {
    v22 = 1;
    do
    {
      v23 = inputvalue[v22 + 6] + 10 * v70;
      v24 = v23 - 926365495;
      if ( v23 <= 1262703685 )
        v24 = v23;
      v70 = v24;
      if ( v24 % v69 )
        goto LABEL_50;
      v21 = inputlen2 + 1;
      v22 = v69 + 1;
      inputlen2 = v21;
      ++v69;
    }
    while ( v21 < const_9 );
  }
  if ( v21 != const_9 )
  {
...
def checknum(s : str):
    assert len(s) == 9
    for i in range(1, 10):
        n = int(s[:i])
        if n % i:
            return False
    return True
def checknum(s : str):
    assert len(s) == 9
    for i in range(1, 10):
        n = int(s[:i])
        if n % i:
            return False
    return True
...
  v25 = const_9 - 1;
  if ( const_9 - 1 > 0 )
  {
    v26 = const_9 - 1;
    do
    {
      v27 = 0;
      if ( v25 > 0 )
      {
        do
        {
          v28 = inputvalue[v27 + 7];
          v29 = inputvalue[v27 + 8];
          if ( v28 > v29 )
          {
            inputvalue[v27 + 7] = v29;
            inputvalue[v27 + 8] = v28;
          }
          ++v27;
        }
        while ( v27 < v25 );
        v3 = 0;
      }
      --v25;
      --v26;
    }
    while ( v26 );
  }
  hex_to_int("1234567890_ABCDEFGHIJKLMNOPQRSTUVWXYZ", const_9);
  v30 = 0;
  if ( const_9 > 0 )
  {
    while ( a1234567890Abcd[v30] == inputvalue[v30 + 7] )
    {
      if ( ++v30 >= const_9 )
        goto LABEL_41;
    }
    goto LABEL_49;
  }
...
...
  v25 = const_9 - 1;
  if ( const_9 - 1 > 0 )
  {
    v26 = const_9 - 1;
    do
    {
      v27 = 0;
      if ( v25 > 0 )

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-6-26 00:51 被mb_mgodlfyn编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (2)
雪    币: 35
活跃值: (124)
能力值: ( LV2,RANK:16 )
在线值:
发帖
回帖
粉丝
2
第四部分23 <= 1262703685应该是永远成立?
2022-6-25 19:14
0
雪    币: 23637
活跃值: (12165)
能力值: ( LV15,RANK:1744 )
在线值:
发帖
回帖
粉丝
3
笔误,感谢指出
2022-6-26 00:49
0
游客
登录 | 注册 方可回帖
返回
//