首页
论坛
课程
招聘
[原创]KCTF2022春季赛第二题 解题过程
2022-5-15 22:37 3210

[原创]KCTF2022春季赛第二题 解题过程

2022-5-15 22:37
3210

算法分析题,有众多的printf打印信息,整个流程比较清晰,有正确的提示。从输入开始进行流程分析。

 

step1:输入ID,最多41个字符,第42个会设为0。

1
2
3
4
5
printf_40100C(Format, (char)byte_4055C0);
scanf_40103A("%s", (char)ID);
printf_40100C(asc_403160, v33);
ID[41] = 0;
iLenOfID = strlen(ID);

step2:计算CRC8(ID)

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
v5 = 0;
t_ = iLenOfID;
v6 = iLenOfID;
ID_CRC8 = 0;
if ( iLenOfID )
{
  v7 = ID;
  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 );
  iLenOfID = t_;
  ID_CRC8 = v10;
}

step3:计算CRC32(ID),不过这里要注意,正常CRC32计算的时候定义为unsigned int类型,这里识别的是int类型,在进行移位的时候如果是负数,则高位添加0xff。

1
2
3
4
5
6
7
CRC32_Init_40106C();
 
ID_CRC32 = 0xFFFFFFFF;
for ( i = 0; i < iLenOfID; ++i )
  ID_CRC32 = dword_405B20[(unsigned __int8)(ID_CRC32 ^ ID[i])] ^ (ID_CRC32 >> 8);
 
ID_CRC32_R = ~ID_CRC32;

step4:将输入的ASCII码类型字符串,转换为int类型。Ascii2Raw_4010B7((int)ID, iLenOfID);

 

step5:直接搜索3*n+1是什么数列,得知为Callztz数列,无论初始输入为多少,最终的结果就是变为1、4、2的循环,猜测经过200次变换,最后3个值为(1 4 2)、(4 2 1)或(2 1 4)的一种。
第一次校验:输入的前3个数的异或值为7。ID[2] ^ ID[1] ^ ID[0] == 7

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
v13 = ID_CRC8;
imod = 1;
v68 = ID_CRC8 + 1;
v14 = v68;
do
{
  v15 = v13;
  for ( j = 1; j < 200; ++j )                 // Callatz
  {
    if ( (v15 & 1) != 0 )
      v15 = 3 * v15 + 1;
    else
      v15 >>= 1;
    CallatzTable[j] = v15;
  }
  ++v13;
}
while ( v13 < v14 );
value_7 = CallatzTable[198] | CallatzTable[197] | CallatzTable[196];
v18 = t_;
if ( value_7 != (ID[2] ^ ID[1] ^ ID[0]) )     // b0111 的组合
{
  printf_40100C("\n%s", (char)&unk_405594);
  printf_40100C("\n%s\n%s", (char)&unk_405678);
  printf_40100C("\n%s", (char)&unk_405118);
  printf_40100C("\n%s", (char)&unk_4053A4);
  goto LABEL_57;
}

step6:判断ID[3:7]是否为KCTF。(下图略掉printf)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if ( ID[3] != 20 )                            // K
{
  //
  goto LABEL_57;
}
  //
if ( ID[4] != 12 )                            // C
{
  //
  goto LABEL_57;
}
  //
if ( ID[5] != 29 )                            // T
{
  //
  goto LABEL_57;
}
  //
if ( ID[6] != 15 )                            // F
{
  //
  goto LABEL_57;
}

step7:mod测试:
ID[7]%1 == 0
(ID[7]*10 + ID[8]) %2 == 0
(ID[7]*100 + ID[8]*10 + ID[9] ) %3 == 0
...
(ID[7]*100000000 + ID[8]*10000000 + ... + ID[15]) % 9 == 0
随后对ID[7:16]共9个数字进行冒泡排序,排序后为123456789,说明ID[7:16]为123456789的组合。

 

编程求解得解为:ID[7:16] == 381654729

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
if ( value_9 > 0 )
{
  istart = 1;
  do
  {
    v23 = ID[istart + 6] + 10 * before;
    before_ = v23 - 926365495;                // junkcode
 
    if ( v23 <= 1262703685 )
      before_ = v23;
 
    before = before_;
    if ( before_ % imod )                     // mod 1 2 3 4 5 6 7 8 9 中间必须是5
      goto LABEL_50;
 
    t = t_ + 1;
    istart = imod + 1;
    t_ = t;
    ++imod;
  }
  while ( t < value_9 );
}
 
value_8 = value_9 - 1;
if ( value_9 - 1 > 0 )
{
  value_8_ = value_9 - 1;
  do
  {
    i_ = 0;
    if ( value_8 > 0 )
    {
      do
      {
        v28 = ID[i_ + 7];                     //  冒泡排序 从小到大 排序后为123456789
        v29 = ID[i_ + 8];
        if ( v28 > v29 )
        {
          ID[i_ + 7] = v29;
          ID[i_ + 8] = v28;
        }
        ++i_;
      }
      while ( i_ < value_8 );
      v3 = 0;
    }
    --value_8;
    --value_8_;
  }
  while ( value_8_ );
}
 
Ascii2Raw_4010B7((int)a1234567890Abcd, value_9);// 123456789
i__ = 0;
if ( value_9 > 0 )
{
  while ( a1234567890Abcd[i__] == ID[i__ + 7] )
  {
    if ( ++i__ >= value_9 )
      goto LABEL_41;
  }
  goto LABEL_49;
}

step8:如果输入字符长度超过16,则对后续的字符判断是否为8字节的倍数,进行变换sub_4010E1,不过简单分析感觉并不可逆。如果长度正好为16则校验ID_CRC32_R == 0xF52E0765。

 

假设输入ID正好为16字符,综合以上分析,只需CRC32(XYZKCTF381654219) = ~0xF52E0765,且X^Y^Z==7。直接编程进行穷尽即可,注意这里的CRC32计算时为int类型。最后得421KCTF381654219。

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
  ID_16 = &ID[value_9 + 7];
  sub_4010E1((int)ID_16, iLenOfID_sub_16);
  if ( iLenOfID_sub_16 <= 0 )
  {
LABEL_45:
    printf_40100C(asc_403C08, v60);
    if ( ID_CRC32_R == 0xF52E0765 )
    {
      printf_40100C(asc_403C50, v61);
      printf_40100C(
        "\n"
        "十年后,你终于干掉了赤月恶魔,肃清了赤月邪教,获得了 赤月套装 .\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了\n"
        "恭喜你,少年,你成功了.",
        v62);
    }
    else
    {
      printf_40100C("\n%s", (char)&unk_4051BC);
      printf_40100C("\n%s", (char)&unk_40524C);
    }
  }
  else
  {
    while ( ((unsigned __int8)byte_4055C0[v3] ^ (unsigned __int8)ID_16[v3]) == byte_403DC8[v3] )
    {
      if ( ++v3 >= iLenOfID_sub_16 )
        goto LABEL_45;
    }
    printf_40100C(asc_403B50, v60);
    printf_40100C(asc_403BC0, v63);
  }

看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

最后于 2022-5-16 20:48 被kanxue编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回