【i春秋】第十届全国大学生信息安全大赛之逆向–溯源

作者: skywilling 分类: PE逆向,逆向 发布时间: 2018-05-29 11:48

转自https://www.52pojie.cn/thread-679776-1-1.html

0x00前言
考研结束了,终于可以有一段闲暇的时间了。在睡了一天后,不忘发个帖子,疏解一下空虚寂寞的心情,再加上今天是圣诞节。这个题目其实在暑假就已经解出来了,是准备等到考完研再发出来的,也就是现在

下面开始切入正题
0x01运行

运行很简单,输入错误即返回fail提示。
0x02静态分析
首先放到IDA中看一下,入口在哪里


三个关键字也很显眼
根据”please input key:”找到输入口,直接F5

[C] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
int sub_4014B0()
{
  int v0; // eax@1
  int v1; // ecx@1
  int v2; // eax@1
  int v3; // ebx@1
  signed int v4; // eax@2
  unsigned int v5; // ecx@3
  int v6; // edx@3
  int v7; // eax@5
  int v8; // esi@5
  int v9; // edx@5
  int v10; // eax@5
  char *v11; // edi@9
  int v12; // ecx@9
  int v13; // esi@9
  void *v14; // esi@11
  int v15; // eax@11
  int v16; // ebx@11
  const char *v17; // edx@14
  int v18; // eax@15
  int v19; // eax@17
  char *v20; // ecx@19
  unsigned int v21; // eax@22
  unsigned int v22; // ecx@24
  signed int v24; // [sp+10h] [bp-1008h]@3
  int v25; // [sp+14h] [bp-1004h]@5
  int v26; // [sp+18h] [bp-1000h]@1
  void *Memory; // [sp+1Ch] [bp-FFCh]@9
  int v28; // [sp+20h] [bp-FF8h]@5
  int v29; // [sp+24h] [bp-FF4h]@5
  int v30; // [sp+28h] [bp-FF0h]@1
  int v31; // [sp+2Ch] [bp-FECh]@2
  void *v32; // [sp+30h] [bp-FE8h]@1
  int v33; // [sp+40h] [bp-FD8h]@1
  unsigned int v34; // [sp+44h] [bp-FD4h]@1
  char v35; // [sp+48h] [bp-FD0h]@1
  __int128 v36; // [sp+FE8h] [bp-30h]@3
  __int128 v37; // [sp+FF8h] [bp-20h]@5
  int v38; // [sp+1014h] [bp-4h]@1
  v0 = sub_401A60(std::cout, "please input key:");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v0, sub_401C90);
  v33 = 0;
  v34 = 15;
  LOBYTE(v32) = 0;
  v38 = 0;
  sub_401DC0(std::cin, (int)&v32);
  sub_401300((unsigned int)&v32, (int)&v35);
  sub_401120((int)&v26, (int)&v35, v1);         // 将堆栈数据复制到数据段
  v2 = 999;
  LOBYTE(v38) = 1;
  v3 = v26;
  v30 = 999;
  while ( 2 )
  {
    v4 = (unsigned __int8)byte_403230[v2];
    v31 = 0;
    do
    {
      v5 = v4 & 3;
      v24 = v4 >> 2;
      v6 = 5 - v5;
      v36 = xmmword_403660;
      if ( v5 < 2 )
        v6 = 1 - v5;
      v37 = xmmword_403620;
      v7 = *((_DWORD *)&v36 + 2 * v6 + 1);
      v8 = v28 + *((_DWORD *)&v36 + 2 * v6);
      v9 = v29;
      v10 = v29 + v7;
      v25 = v8;
      if ( v8 < 0 || v8 >= v3 || v10 < 0 || v10 >= v3 )// v3=0xA
      {
        v19 = sub_401A60(std::cout, "fail");
        std::basic_ostream<char,std::char_traits<char>>::operator<<(v19, sub_401C90);
        v14 = Memory;
        goto LABEL_18;
      }
      v29 = v10;
      v11 = (char *)Memory + 4 * (v9 + v28 * v3);
      v12 = v10 + v8 * v3;
      v4 = v24;
      v13 = *((_DWORD *)Memory + v12);
      *((_DWORD *)Memory + v12) = *(_DWORD *)v11;
      *(_DWORD *)v11 = v13;
      v28 = v25;
      ++v31;
    }
    while ( v31 < 4 );
    v2 = v30-- - 1;
    if ( v30 >= 0 )
      continue;
    break;
  }
  v14 = Memory;
  v15 = 0;
  v16 = v3 * v3;
  if ( v16 <= 0 )
  {
LABEL_14:
    v17 = "success";
  }
  else
  {
    while ( *((_DWORD *)Memory + v15) == v15 )
    {
      if ( ++v15 >= v16 )
        goto LABEL_14;
    }
    v17 = "fail";
  }
  v18 = sub_401A60(std::cout, v17);
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v18, sub_401C90);
LABEL_18:
  j_j_free(v14);
  if ( v34 >= 0x10 )
  {
    v20 = (char *)v32;
    if ( v34 + 1 >= 0x1000 )
    {
      if ( (unsigned __int8)v32 & 0x1F )
        invalid_parameter_noinfo_noreturn(v32);
      v21 = *((_DWORD *)v20 - 1);
      if ( v21 >= (unsigned int)v20 )
        v21 = invalid_parameter_noinfo_noreturn(v20);
      v22 = (unsigned int)&v20[-v21];
      if ( v22 < 4 )
        v21 = invalid_parameter_noinfo_noreturn(v22);
      if ( v22 > 0x23 )
        v21 = invalid_parameter_noinfo_noreturn(v22);
      v20 = (char *)v21;
    }
    j_free(v20);
  }
  return 0;
}


三个关键字都在这里了
逐一分析这段代码功能

这里把输入的数据直接存到v32

然后调用了 sub_401300((unsigned int)&v32, (int)&v35),进去查看,发现函数名是_DWORD *__fastcall sub_401300(unsigned int input, int a2)
这时v32对应到input,v35对应a2,再往下看

这时,a2赋值到temp,input赋值到v3,

这里有个判断是否为200的地方,其实就是看输入的字符长度是否为200

再往下有个循环

这里其实就是i++,再看一下循环结束条件 while ( v14 != v15 ),往上看v14=0,再找v15,

v3=input,算来算去,v15就是200
这时可以还原出C代码:

[C] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
9
for(i=0;i<100;i++){
        temp[i]=i;
    }
    for(i=0;i<200;i++){
        temp[i/2]=temp[i/2]*16+input[i]-65;
        if((i+1)%2==1){
            temp[i/2+1]=0;
        }
    }


注意这里有个65,也就是A,到这里可以确认的输入格式是长度为200的大写英文字母,而这个循环就是要将长度为200的输入缩合为长度为100的数据,格式就是A,A,B,B,C,C转换为00,11,22
再往下看
sub_401120((int)&v26, (int)&v35, v1);         // 将堆栈数据复制到数据段
这里是保存到了数据段v26

这里可以看到一个长度为1000的数组,类似于置换表
再往下看就是关于数据置换的代码了,下面给出还原后的代码

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
while(1) {
        n=table[i];
        num=0;
        do {
            low = n & 3;  //低三位
            high = n >> 2; //高五位
            j = 5 - low;
            if (low < 2) {
                j = 1 - low;
            }
            a = xmm[j * 2] + l;
            b = xmm[j * 2 + 1] + k;
            //printf("j=%X b=%X a=%X low=%X high=%X n=%X k=%X l=%X\n", j,b, a, low, high, n, k, l);
            if (a < 0 || a >= c || b < 0 || b >= c) {
                printf("fail\n");
                return 0;
            }
            //交换
            c = 0xA * l;
            c += k;
            d = temp;
            //printf("c=%X temp[%d]=%X\n",c,c,d);
            temp_ = temp + c;
            c = 0xA * a;
            c += b;
            d_ = temp;
            temp = d;
            //printf("c=%X temp[%d]=%X\n",c,c,d_);
            *temp_ = d_;
            //
            l = a;
            k = b;
            n = high;
            c = 0xA;
            num++;
        } while (num < 4);
        //print(temp,100);
        i--;
        if(i<0){
            break;
        }
    }


由于是直接按照汇编还原,代码实现比较冗长

[C] 纯文本查看 复制代码
1
2
3
4
5
6
7
//判断
    for(i=0;i<100;i++){
        if(temp[i]!=i){
            printf("fail\n");
            return 0;
        }
    }


置换后的数据与0-99比较,如果相等就成功了,否则失败
解密过程我会在附件中给出
0x03结尾
老规矩,题目和c代码会在文章最后的附件中给出,下面献上成功的截图:

附件:https://pan.baidu.com/s/1o8wvQAY 密码:i46l
版权声明:允许转载,但是一定要注明出处

发表评论

电子邮件地址不会被公开。 必填项已用*标注