zoukankan      html  css  js  c++  java
  • 【ZCTF】easy reverse 详解

    0x01  前言
        团队逆向牛的解题思路,分享出来~



    0x02  内容

    0. 样本
    bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll
    1. 静态分析
    使用IDAPro逆向分析样本,样本较小,得到方法列表:
    调用关系:
    PS: 开始受调用关系误导,分析DLL入口函数调用的几个方法,浪费了时间。
    查看内部字符串:
    根据yes!定位到方法 get_string
    get_string: 调用关系图,有解密过程调用。
    使用IDAPro F5功能,生成伪码:
    大致过程:
    1) 获取unk_6E282000处存储的0x19长字节流
    2) 用户输入字符串
    3) 使用encrypt_str加密用户输入字符串
    4) 加密结果与1)处0x19字节相同,则输入的串为可能的Flag
    encrypt_str伪码:
    大致过程:
    1) v4基本上为固定值0,原因自己看
    2) 输入串长度大于2, 并且只有偶数个字节参与运算, v7为终止哨兵,其值为 a1+2*result,  a1为串首地址,result为串长度的1/2取整。
    3) v6从首地址开始,每循环一次,跳2个字节。
    encrypt伪码:
            大致过程:
    1) 伪码中参数列表,重点看a3参数,从encrypt_str调用处可看出,a3为指向用户输入串中的某个位置,调用一次,前进2
    2) 重点看:
    此处取了 *a3 *(a3 + 1)的值
    此处回写了 *a3 *(a3 + 1)的值
    这块是暴力破解的关键。
            PS 可以看出,加密过程是通过一系列复杂的运算来实现,一般算法很难从结果逆推出原始串,先从暴力破解入手。
    2. 动态分析
    样本为DLL,导出方法get_string,需要编写Load程序。
    为简化调试过程,Load程序模拟get_string流程,不需scanf控制台输入,主要代码如下:
    typedef int(*encrypt_str)(uint8_t*, uint32_t, int*);
    auto h = LoadLibrary("bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll");
    encrypt_str f = (encrypt_str)((uint8_t*)h + 0x67C + 0xC00);
            int v1 = 0xEFBEADDE;
            char testStr[] = "0123456789abcdefgh";
            f((uint8_t *)testStr, strlen(testStr)+1, &v1);
    注意:v1的值问题,IDA翻译的伪码未识别出v1的类型,从汇编可以看出,v14字节Int0xEFBEADDE对应伪码中 -34.-83,-66.-17
    OD调试分析步骤:
    1) 使用OD启动编译完的程序
    2) bp encrypt_str 下断点
    OD断在
    查看寄存器信息:
    可知 0073FDD0处存储待加密的字符串,OD数据跟随:
    单步步过一次encrypt调用后,数据区:
    前两个字节数据变化,基本肯定,加密方法每次处理2字节数据,且加密后数据长度不发生变化。
    PS上述过程原理上只能得出每次只生成两字节加密数据,不能证明加密过程也只有2字节参与,证明方法:可以在数据区按字节下内存读断点,看在encrypt过程中,是否只命中两个字节的读断点。


    3. 破解方法
    1) 取到dll中加密后的串,即get_stringunk_6E2820000x19 Bytes数据。有效数据长度24
    2) 每两字节一组,穷举0x0-0xFF,共256*256种可能,调用encrypt,结果与unk_6E282000对应位置数据比较,一致时找到2Bytes
    3) 重复过程2) 12次。
    4) 由于算法中,每两字节一组独立运行,暴力枚举量从: 256^24 缩小到 256^2*12 = 786432,运算时间10秒以内。
    核心代码如下:
    uint8_t str[25] = { 0 };[/align]        for (int n = 0; n < 25; n+=2)
          {
                  for (int i = 0; i <= 0xFF; i++)
                          for (int j = 0; j <= 0xFF; j++)
                          {
                                  uint8_t *data = new uint8_t[25];
                                  memcpy(data, str, 25);
                                  *(data + n) = (uint8_t)i;
                                  *(data + n + 1) = (uint8_t)j;
                                  f(data, 25, &v1);
                                  if (data[n] == pstr[n] && data[n+1] == pstr[n+1]) {
                                          str[n] = i;
                                          str[n + 1] = j;
                                          break;
                                  }
                          }
          }
          printf("%s
    ", str);
          f(str, 25, &v1);
          if (memcmp(str, pstr, 25) == 0)
          {
                  printf("It's OK!
    ");
          }
      结果:
    0x01  前言
        团队逆向牛的解题思路,分享出来~



    0x02  内容

    0. 样本
    bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll

    1. 静态分析
    使用IDAPro逆向分析样本,样本较小,得到方法列表:

    调用关系:

    PS: 开始受调用关系误导,分析DLL入口函数调用的几个方法,浪费了时间。

    查看内部字符串:

    根据yes!定位到方法 get_string

    get_string: 调用关系图,有解密过程调用。

    使用IDAPro F5功能,生成伪码:

    大致过程:
    1) 获取unk_6E282000处存储的0x19长字节流
    2) 用户输入字符串
    3) 使用encrypt_str加密用户输入字符串
    4) 加密结果与1)处0x19字节相同,则输入的串为可能的Flag

    encrypt_str伪码:
    大致过程:
    1) v4基本上为固定值0,原因自己看
    2) 输入串长度大于2, 并且只有偶数个字节参与运算, v7为终止哨兵,其值为 a1+2*result,  a1为串首地址,result为串长度的1/2取整。
    3) v6从首地址开始,每循环一次,跳2个字节。

    encrypt伪码:
            大致过程:
    1) 伪码中参数列表,重点看a3参数,从encrypt_str调用处可看出,a3为指向用户输入串中的某个位置,调用一次,前进2
    2) 重点看:
    此处取了 *a3 *(a3 + 1)的值

    此处回写了 *a3 *(a3 + 1)的值
    这块是暴力破解的关键。

            PS 可以看出,加密过程是通过一系列复杂的运算来实现,一般算法很难从结果逆推出原始串,先从暴力破解入手。

    2. 动态分析
    样本为DLL,导出方法get_string,需要编写Load程序。

    为简化调试过程,Load程序模拟get_string流程,不需scanf控制台输入,主要代码如下:
    typedef int(*encrypt_str)(uint8_t*, uint32_t, int*);
    auto h = LoadLibrary("bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll");
    encrypt_str f = (encrypt_str)((uint8_t*)h + 0x67C + 0xC00);
            int v1 = 0xEFBEADDE;
            char testStr[] = "0123456789abcdefgh";
            f((uint8_t *)testStr, strlen(testStr)+1, &v1);

    注意:v1的值问题,IDA翻译的伪码未识别出v1的类型,从汇编可以看出,v14字节Int0xEFBEADDE对应伪码中 -34.-83,-66.-17

    OD调试分析步骤:
    1) 使用OD启动编译完的程序
    2) bp encrypt_str 下断点
    OD断在

    查看寄存器信息:

    可知 0073FDD0处存储待加密的字符串,OD数据跟随:

    单步步过一次encrypt调用后,数据区:

    前两个字节数据变化,基本肯定,加密方法每次处理2字节数据,且加密后数据长度不发生变化。

    PS上述过程原理上只能得出每次只生成两字节加密数据,不能证明加密过程也只有2字节参与,证明方法:可以在数据区按字节下内存读断点,看在encrypt过程中,是否只命中两个字节的读断点。


    3. 破解方法
    1) 取到dll中加密后的串,即get_stringunk_6E2820000x19 Bytes数据。有效数据长度24
    2) 每两字节一组,穷举0x0-0xFF,共256*256种可能,调用encrypt,结果与unk_6E282000对应位置数据比较,一致时找到2Bytes
    3) 重复过程2) 12次。
    4) 由于算法中,每两字节一组独立运行,暴力枚举量从: 256^24 缩小到 256^2*12 = 786432,运算时间10秒以内。

    核心代码如下:
    uint8_t str[25] = { 0 };[/align]        for (int n = 0; n < 25; n+=2)
          {
                  for (int i = 0; i <= 0xFF; i++)
                          for (int j = 0; j <= 0xFF; j++)
                          {
                                  uint8_t *data = new uint8_t[25];
                                  memcpy(data, str, 25);
                                  *(data + n) = (uint8_t)i;
                                  *(data + n + 1) = (uint8_t)j;
                                  f(data, 25, &v1);
                                  if (data[n] == pstr[n] && data[n+1] == pstr[n+1]) {
                                          str[n] = i;
                                          str[n + 1] = j;
                                          break;
                                  }
                          }
          }
          printf("%s
    ", str);
          f(str, 25, &v1);
          if (memcmp(str, pstr, 25) == 0)
          {
                  printf("It's OK!
    ");
          }

    结果:
  • 相关阅读:
    RequireJS 和 Sea.js
    zoom:1
    font-sqirrel
    WEB前端面试题 分别使用2个、3个、5个DIV画出一个大的红十字
    获取表单select域的选择部分的文本
    写一个简单的form表单,当光标离开表单的时候表单的值发送给后台
    Python3基础 e记法示例
    Python3基础 response.read 输出网页的源代码
    Python3基础 访问在线的有道词典
    Python3基础 response.info 服务器返回的header信息
  • 原文地址:https://www.cnblogs.com/ichunqiu/p/6489306.html
Copyright © 2011-2022 走看看