首页
论坛
课程
招聘
雪    币: 1412
活跃值: 活跃值 (11)
能力值: ( LV7,RANK:108 )
在线值:
发帖
回帖
粉丝

[原创] 半加器解题过程

2018-12-4 14:19 927

[原创] 半加器解题过程

2018-12-4 14:19
927

0x00 分析工具

IDA 7.0
Stud_PE
x64dbg
010editor

0x01 解题过程

这道题的程序是使用vs2017社区版生成的debug版的32位控制台应用程序。首先拿到这道题后我还是像第一题一样修改PE文件头来关闭重定位功能,具体方法我在第一题的writeup中有写。

 

当第一次把程序载入IDA后,我发现有很多API没有解析出来,这里我是通过设置IDA中的签名模块(具体我不知道该怎么叫)来识别在初始化中没有识别出来的函数。首先使用快捷键shift+F5,然后右键选择“Apply new signature...”。在IDA7.0中有一个签名模块叫做vc32ucrt(我虚拟机中的IDA6.8没有这个签名,可能是7.0中新加入的签名模块),倒入这个签名后IDA很快有识别出来2258个函数。然后我们我们就可以继续分析这个程序了。

 

运行一次程序,然后在IDA中搜索命令行搜索出来的字符串"Please Input:"。然后定位到程序获取输入的位置F5查看伪C代码。然后根据每个函数的功能以及调用的参数格式猜测具体调用了哪个函数,这个就是靠的编程经验以及逆向经验了。

/*
    j__开头的函数都是在之前倒入了签名模块后自动分析出来的函数。
*/

int main()
{
  signed int v1; // [esp+D0h] [ebp-8h]

  sub_48D7B4(&unk_5F6007);// 编译器自动添加的代码,对于解题没什么用
  sub_48CD46(std::cout, "Please Input:");//std::cout<<"Please Input:";
  scanf_s("%s", Src, 30);
  v1 = j__strlen(Src);
  if ( v1 > 30 || v1 < 10 )
  {
    printf("输入错误;");
    j___exit_0(0);
  }
  j__strcpy_s(Dst, 0x1Eu, Src);
  if ( Dst[7] != 'A' )
  {
    printf("输入错误;");
    j___exit_0(0);
  }
  xor_key(Dst);
  return sub_48D935();// 编译器自动添加的代码,对于解题没什么用
}

从上述代码中我们可以看出来程序首先获取命令行输入然后判断输入的字符串长度是否在10到30之间。然后判断字符串中的第八位字符是否为'A'。然后在xor_key中对输入内容进行进一步转换。

int __cdecl xor_key(char *a1)
{
  size_t i; // [esp+D0h] [ebp-8h]

  sub_48D7B4(&unk_5F6007);// 编译器自动添加的代码,对于解题没什么用
  a1[7] = '#';
  for ( i = 0; i < j__strlen(a1); ++i )
    a1[i] ^= 0x1Fu;
  return sub_48D935();// 编译器自动添加的代码,对于解题没什么用
}

这段代码的主要逻辑就是程序首先将输入的字符串的第八位字符变换为'#',然后将每一位字符与0x1F进行异或。在这之后xor_key正常返回到main中之后程序就正常退出了。到了这里我就很费解,验证输入的代码怎么看不到?这到底是什么操作。到了这一步我卡了很久,甚至一度以为这道题的flag是靠脑洞猜的。。。

 

在想了很久后实在没有什么思路后我就睡觉去了,第二天睡醒以后突然脑中灵光一闪。程序在获取到输入后为什么要做一次字符串拷贝,在main函数中并没有调用malloc之类的函数,那么main函数中的Dst的空间是从哪里来的呢。在这一切背后究竟有着怎样的真相。在调试过程中可以看出Dst的位置并不是栈上的局部变量,那么一定在程序的什么位置有调用malloc。在大胆假设后,我在IDA中寻找Dst这个变量还在哪里调用过,然后我找到了如下两段代码。

/*
    然后我在调试器中在这个位置设置断点后发现确实是malloc的功能。
*/
int sub_495810()
{
  sub_48D7B4(&unk_5F6007);// 编译器自动添加的代码,对于解题没什么用
  Dst = (char *)sub_48B0B3(30);//这个格式很像Dst=(char *)malloc(30);
  return sub_48D935();// 编译器自动添加的代码,对于解题没什么用
}
int __stdcall sub_49DC80(char *a1)
{
  size_t i; // [esp+E8h] [ebp-14h]

  sub_48D7B4(&unk_5F6007);// 编译器自动添加的代码,对于解题没什么用
  if ( a1 )
  {
    for ( i = 0; i < j__strlen(a1); ++i )
      a1[i] ^= 0x1Cu;
    if ( !j__strcmp(a1, Dst) )
    {
      sub_48B4AA(std::cout, 'o');//std::cout<<"o";
      sub_48B4AA(std::cout, 'k');//std::cout<<"k";
    }
  }
  return sub_48D935();// 编译器自动添加的代码,对于解题没什么用
}

看到这里后我hin是兴奋,终于看到验证的位置了。strcmp的两个参数分别是a1和Dst,这里Dst应该就是经过异或处理后的我们输入的key了。那么a1是什么呢?在IDA中不停的回溯后找到了这段函数。

int sub_49CEB0()
{
  sub_48D7B4(&unk_5F6007);
  sub_48DACA(aInvalidArgumen_1);// aInvalidArgumen_1 = "invalid argument"
  return sub_48D935();
}

到了这里我们将已知的东西进行整合,拼凑出这个程序的代码逻辑了。程序在获取输入内容后,首先判断字符串的长度是否大于10并且小于30,判断字符串第8位字符是否为大写字母A。然后将我们输入的字符串的第8位变换位'#',再将每一个字符与0x1F异或。最后将这个字符串和"invalid argument"与0x1c异或后的结果进行比较。

 

到了这里我们就可以进行逆向运算算出正确的结果,这里我利用了010editor的异或功能进行计算的。

    invalid argument  =>  
        urj}pux<}n{iqyrh  =>  
            jmubojg#bqdvnfmw  =>  
                jmubojgAbqdvnfmw

0x02 代码还原

在分析结束后我尝试了还原这个程序的源代码,大体上实现了和原题相同的逻辑结构,具体代码如下所示:

/*使用vs2017创建控制台程序,然后使用静态编译。然后你会发现生成的程序和这道题非常相似。^_^*/
#include <iostream>
#include <stdlib.h>

using namespace std;

void check();
void xor_key(char * key);

char *userKey = (char *)malloc(30);
char rightkey[30] = "invalid argument";

int code = atexit(check);

int main()
{
    int length = 0;
    char * input = (char *)malloc(30);
    cout << "Please Input:";
    scanf_s("%s", input, 30);
    length = strlen(input);
    if (length > 30 || length < 10)
    {
        printf("Error\n");
        exit(0);
    }
    if (input[7] != 'A')
    {
        printf("Error\n");
        exit(0);
    }

    strcpy_s(userKey, 30, input);

    xor_key(userKey);

    return 0;
}

void xor_key(char * key)
{
    key[7] = '#';
    for (unsigned int i = 0; i < strlen(key); i++)
    {
        key[i] ^= 0x1F;
    }
}


void check()
{
    for (unsigned int i = 0; i < strlen(rightkey); i++)
    {
        rightkey[i] ^= 0x1C;
    }
    if (!strcmp(rightkey, userKey))
    {
        cout << "o";
        cout << "k";
    }
}

[公告]SDC2020 看雪安全者开发者峰会10月23日将在上海举行!欢迎参加!

最后于 2018-12-4 14:20 被DickBoomSky编辑 ,原因:
最新回复 (0)
游客
登录 | 注册 方可回帖
返回