首页
社区
课程
招聘
[原创]KCTF2023第二题 CN星际基地
2023-9-5 09:02 8285

[原创]KCTF2023第二题 CN星际基地

2023-9-5 09:02
8285
  1. 先确定input长度为156
1
2
3
4
5
6
7
  if ( input_len != 156 )                       // 输入长度156
  {
LABEL_16:
    v12 = fun_printf(std::cout, "fail");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v12, sub_1400030E0);
    goto LABEL_233;
  }
  1. 然后将156分成4行39列,每一列从上到下识别为一个整数
1
2
3
4
5
6
7
v20 = errno();
v21 = v20;
v22 = (const char *)&Str;
if ( v149 >= 0x10 )
  v22 = Str;
*v20 = 0;
n4First = strtol(v22, &EndPtr, 10);
  1. 如果数据中不存在2,并且数值为1111就fail
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
    if ( !MaxCount || (v27 = memchr(v24, '2', MaxCount)) == 0i64 || v27 - (_BYTE *)v24 == -1 )
    {
      v28 = errno();
      v29 = v28;
      v30 = (const char *)&Str;
      if ( v149 >= 0x10 )
        v30 = Str;
      *v28 = 0;
      v31 = strtol(v30, &v146, 2);              //
      if ( v30 == v146 )
        goto LABEL_245;
      if ( *v29 == 34 )
      {
        std::_Xout_of_range("stoi argument out of range");
        __debugbreak();
LABEL_245:
        std::_Xinvalid_argument("invalid stoi argument");
        __debugbreak();
LABEL_246:
        std::_Xout_of_range("stoi argument out of range");
        __debugbreak();
LABEL_247:
        std::_Xinvalid_argument("invalid stoi argument");
        __debugbreak();
LABEL_248:
        sub_1400012C0(0x80004005);
      }
      if ( v31 == 15 )
      {
        v126 = fun_printf(std::cout, "Fail");
        std::basic_ostream<char,std::char_traits<char>>::operator<<(v126, sub_1400030E0);
        goto LABEL_228;
      }
      v26 = v149;
      v25 = Str;
    }
  1. 如果数值中存在2,但是数值是2222就fail
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
v41 = memchr(str4First_1, '2', MaxCount); // 又查找2。。。
      if ( v41 )
      {
        if ( v41 - (_BYTE *)str4First_1 != -1 )
        {
          v42 = 0;
          v43 = 0;
          v44 = *((signed int *)Unicode4First - 4);
          LODWORD(v45) = 0;
          if ( (signed int)v44 <= 0 )
          {
            v13 = v134;
          }
          else
          {
            do
            {
              v46 = (signed int)v45;
              if ( Unicode4First[v46] == '2' )
              {
                if ( !v43 )
                {
                  v43 = 1;
                  if ( ((1 - *((_DWORD *)Unicode4First - 2)) | (*((_DWORD *)Unicode4First - 3) - (signed int)v44)) < 0 )
                  {
                    sub_140002D70(&v150, v44);
                    Unicode4First = v150;
                  }
                }
                Unicode4First[v46] = '1';
                ++v42;
              }
              v45 = (v46 * 2 + 2) >> 1;
            }
            while ( (signed int)v45 < (signed int)v44 );
            n4First = v138;
            if ( v43 )
            {
              if ( (signed int)v44 > *((_DWORD *)Unicode4First - 3) )
                sub_1400012C0(0x80070057);
              *((_DWORD *)Unicode4First - 4) = v44;
              Unicode4First[v44] = 0;
            }
            if ( v42 == 4 )
            {
              v62 = fun_printf(std::cout, "Fail");
              std::basic_ostream<char,std::char_traits<char>>::operator<<(v62, sub_1400030E0);
              goto LABEL_225;
            }
            v13 = v134;
          }
        }
  1. 并且39个数中,后面的数不能小于等于前面的数
1
2
if ( n4First <= n4FirstLast )
  break;
  1. 然后是限制输入只能是0 1 2
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
if ( ++v0 >= 39 )                           // v0进来是0,所以说实质上,是遍历39轮,每轮处理4字节
 {
   v57 = 0;
   if ( input_len_1 )
   {
     v58 = 0i64;
     do
     {
       v59 = v57 / 39;                       // 0 1 2 3
       v60 = v57 % 39;                       // 0 1 2 ... 38
       v61 = input_2;
       if ( v153 >= 0x10 )
         v61 = (void **)input_2[0];
       switch ( *((_BYTE *)v61 + v58) )
       {
         case 0x30:
           *(_DWORD *)(*(_QWORD *)(qword_1400098A8 + 8i64 * v59) + 4i64 * v60) = 0;
           break;
         case 0x31:
           *(_DWORD *)(*(_QWORD *)(qword_1400098A8 + 8i64 * v59) + 4i64 * v60) = 1;
           break;
         case 0x32:
           *(_DWORD *)(*(_QWORD *)(qword_1400098A8 + 8i64 * v59) + 4i64 * v60) = -1;
           break;
         default:
           goto LABEL_16;
       }
       ++v57;
       ++v58;
     }
     while ( v57 < input_len_1 );
   }
  1. 限制每行1和2的数量必须相同
1
2
3
4
5
6
7
8
9
10
11
12
13
if ( v65 == v66 )                       // 他们俩为啥都是0
{
  **(_DWORD **)(v64 + qword_1400098B8) = 0;
}
else
{
  v96 = -1;
  if ( v65 < v66 )
    v96 = 1;
  **(_DWORD **)(v64 + qword_1400098B8) = v96;
}
if ( v67 )                              // 这里还是要求每行 12的数量相等
  goto LABEL_16;
  1. 然后就是计算MD5,转化为小写字符校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
          v139 = 0;                             // 直接从这里开始分析!
          v108 = 0i64;
          v144 = 0i64;
          v140 = 0x67452301;
          v141 = 0xEFCDAB89;
          v142 = 0x98BADCFE;
          v143 = 0x10325476;
          v109 = (char *)input_2;
          if ( v153 >= 0x10 )                   // 0x9F
            v109 = (char *)input_2[0];
          fun_md5init((__int64)&v139, v109, input_len_1);
          plain_md5 = fun_md5update((__int64)&v139);
.
.
.
          v121 = *(_QWORD *)v136 != 32i64 || memcmp(v118, "aac82b7ad77ab00dcef90ac079c9490d", 0x20ui64);
  1. 然后就是写脚本瞎跑,1天也没跑出来。然后看到网页里有提示。。
    [作者提示:序列号转换后的数值数组:
    1、列向量组里不存在相反值,如[1,-1,1,0]与[-1,1,-1,0]不能同时存在
    2、每个行向量里的 0 的数量 占 1/3
    2023/9/3 20:55
    ]

  2. 根据提示重新写脚本跑数据

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//标记39层,每层选取的元素是谁
unsigned int id02[39] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 };
 
//判断差值是否合规 1=合规 0=错误
int judge(int value)
{
    int precision = 9;
    if ((value < 0) && (0 - value > precision))
        return 0;
    if ((value > 0) && (value > precision))
        return 0;
    return 1;
}
 
//depth表示当前层级,一共遍历39
void search02(unsigned int *arr, int depth, int qian, int bai, int shi, int ge)
{
    if (depth == 39)
    {
        if (qian == 0 && bai == 0 && shi == 0 && ge == 0)
        {
            unsigned int final39[39] = { 0 };
            for (int i = 0; i < 39; i++)
                final39[i] = *(arr + 2 * i + id02[i]);  //获取当前数值
             
            Bubble_sort(final39, 39);   //对final39数组排序
 
            //构造156位字符串,计算md5
            unsigned char *input = (unsigned char *)malloc(4 * 39 + 1);
            memset(input, '0', 4 * 39);
            input[4 * 39] = 0;
             
            for (int i = 0; i < 39; i++)
            {
                int value = final39[i]; //从对元素中取一个
 
                int bit0 = value / 1000;        //千位 第一行
                int bit1 = value % 1000 / 100//百位 第二行
                int bit2 = value % 100 / 10;    //
                int bit3 = value % 10;
                 
                input[i] = bit0 + '0';
                input[i + 39] = bit1 + '0';
                input[i + 39 * 2] = bit2 + '0';
                input[i + 39 * 3] = bit3 + '0';
            }
             
            char* res = getMd5(input);
            if (strcmp(res, "aac82b7ad77ab00dcef90ac079c9490d") == 0)
            {
                printf("%s\n", input);
                printf("%s\n", res);
                free(input);
                free(res);
                exit(0);
            }
            free(input);
            free(res);
        }
    }
 
    for (int i = 0; i < 2; i++)
    {
        int value = *(arr + 2 * depth + i); //从对元素中取一个
 
        int bit0 = value / 1000;        //千位 第一行
        int bit1 = value % 1000 / 100//百位 第二行
        int bit2 = value % 100 / 10;    //
        int bit3 = value % 10;
 
        if (bit0 == 2) bit0 = -1;
        if (bit1 == 2) bit1 = -1;
        if (bit2 == 2) bit2 = -1;
        if (bit3 == 2) bit3 = -1;
 
        if (judge(qian + bit0) && judge(bai + bit1) && judge(shi + bit2) && judge(ge + bit3))
        {
            id02[depth] = i;
            search02(arr, depth + 1, qian + bit0, bai + bit1, shi + bit2, ge + bit3);
            id02[depth] = -1;
        }
    }
    return;
}
 
void ctf02()
{
    //准备对立数组
    unsigned int arr[39][2] = {
        {1,2},{10,20},{11,22},{12,21},{100,200},{101,202},{102,201},{110,220},{111,222},{112,221},
        {120,210},{121,212},{122,211},{1000,2000},{1001,2002},{1002,2001},{1010,2020},{1011,2022},{1012,2021},{1020,2010},
        {1021,2012},{1022,2011},{1100,2200},{1101,2202},{1102,2201},{1110,2220},{1112,2221},{1120,2210},{1121,2212},{1122,2211},
        {1200,2100},{1201,2102},{1202,2101},{1210,2120},{1211,2122},{1212,2121},{1220,2110},{1221,2112},{1222,2111}
    };
 
    search02((unsigned int *)arr, 0, 0, 0, 0, 0);
}
  1. 拿到flag
    000000000000011111111111112222222222222000011111111100001122222220000011222222011100011122200120200112220112202001122101201201201212001212020120121202010201

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回