skywilling 发表于 2017-12-25 21:58

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

本帖最后由 skywilling 于 2017-12-25 21:59 编辑

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

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

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


三个关键字也很显眼
根据"please input key:"找到输入口,直接F5
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; // @3
int v25; // @5
int v26; // @1
void *Memory; // @9
int v28; // @5
int v29; // @5
int v30; // @1
int v31; // @2
void *v32; // @1
int v33; // @1
unsigned int v34; // @1
char v35; // @1
__int128 v36; // @3
__int128 v37; // @5
int v38; // @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;
    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代码:
for(i=0;i<100;i++){
      temp=i;
    }
    for(i=0;i<200;i++){
      temp=temp*16+input-65;
      if((i+1)%2==1){
            temp=0;
      }
    }
注意这里有个65,也就是A,到这里可以确认的输入格式是长度为200的大写英文字母,而这个循环就是要将长度为200的输入缩合为长度为100的数据,格式就是A,A,B,B,C,C转换为00,11,22
再往下看
sub_401120((int)&v26, (int)&v35, v1);         // 将堆栈数据复制到数据段
这里是保存到了数据段v26

这里可以看到一个长度为1000的数组,类似于置换表
再往下看就是关于数据置换的代码了,下面给出还原后的代码
while(1) {
      n=table;
      num=0;
      do {
            low = n & 3;//低三位
            high = n >> 2; //高五位
            j = 5 - low;
            if (low < 2) {
                j = 1 - low;
            }
            a = xmm + l;
            b = xmm + 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;
      }
    }
由于是直接按照汇编还原,代码实现比较冗长
//判断
    for(i=0;i<100;i++){
      if(temp!=i){
            printf("fail\n");
            return 0;
      }
    }
置换后的数据与0-99比较,如果相等就成功了,否则失败
解密过程我会在附件中给出
0x03结尾
老规矩,题目和c代码会在文章最后的附件中给出,下面献上成功的截图:

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

skywilling 发表于 2017-12-26 20:48

90182si 发表于 2017-12-26 19:20
我是一个新手,想问这里的的input+16什么意思

input是指针,16是偏移量

诗和远方2018 发表于 2017-12-25 22:22

沙发吗?

zyx1211 发表于 2017-12-25 22:38

学习了!

games 发表于 2017-12-25 22:56

祝考研成功!!!!

wtuaixk 发表于 2017-12-26 09:03

顶一个,路过回复。

90182si 发表于 2017-12-26 19:20

我是一个新手,想问这里的的input+16什么意思

无影寒冬 发表于 2017-12-26 20:18

祝考研成功。

90182si 发表于 2017-12-26 21:10

skywilling 发表于 2017-12-26 20:48
input是指针,16是偏移量

哦,知道了,谢谢

OnlyYu 发表于 2017-12-27 10:48

看都看不懂...
页: [1] 2
查看完整版本: 【i春秋】第十届全国大学生信息安全大赛之逆向--溯源