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)+, &v1);
注意:v1的值问题,IDA翻译的伪码未识别出v1的类型,从汇编可以看出,v1是4字节Int,0xEFBEADDE对应伪码中 -34.-83,-66.-17
OD调试分析步骤:
1) 使用OD启动编译完的程序
2) bp encrypt_str 下断点
OD断在

查看寄存器信息:

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

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

前两个字节数据变化,基本肯定,加密方法每次处理2字节数据,且加密后数据长度不发生变化。
PS:上述过程原理上只能得出每次只生成两字节加密数据,不能证明加密过程也只有2字节参与,证明方法:可以在数据区按字节下内存读断点,看在encrypt过程中,是否只命中两个字节的读断点。
3. 破解方法
1) 取到dll中加密后的串,即get_string中unk_6E282000处0x19 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[] = {  };[/align]        for (int n = ; n < ; n+=)
{
for (int i = ; i <= 0xFF; i++)
for (int j = ; j <= 0xFF; j++)
{
uint8_t *data = new uint8_t[];
memcpy(data, str, );
*(data + n) = (uint8_t)i;
*(data + n + ) = (uint8_t)j;
f(data, , &v1);
if (data[n] == pstr[n] && data[n+] == pstr[n+]) {
str[n] = i;
str[n + ] = j;
break;
}
}
}
printf("%s\n", str);
f(str, , &v1);
if (memcmp(str, pstr, ) == )
{
printf("It's OK!\n");
}
  结果:
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)+, &v1);
注意:v1的值问题,IDA翻译的伪码未识别出v1的类型,从汇编可以看出,v1是4字节Int,0xEFBEADDE对应伪码中 -34.-83,-66.-17
OD调试分析步骤:
1) 使用OD启动编译完的程序
2) bp encrypt_str 下断点
OD断在

查看寄存器信息:

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

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

前两个字节数据变化,基本肯定,加密方法每次处理2字节数据,且加密后数据长度不发生变化。
PS:上述过程原理上只能得出每次只生成两字节加密数据,不能证明加密过程也只有2字节参与,证明方法:可以在数据区按字节下内存读断点,看在encrypt过程中,是否只命中两个字节的读断点。
3. 破解方法
1) 取到dll中加密后的串,即get_string中unk_6E282000处0x19 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[] = {  };[/align]        for (int n = ; n < ; n+=)
{
for (int i = ; i <= 0xFF; i++)
for (int j = ; j <= 0xFF; j++)
{
uint8_t *data = new uint8_t[];
memcpy(data, str, );
*(data + n) = (uint8_t)i;
*(data + n + ) = (uint8_t)j;
f(data, , &v1);
if (data[n] == pstr[n] && data[n+] == pstr[n+]) {
str[n] = i;
str[n + ] = j;
break;
}
}
}
printf("%s\n", str);
f(str, , &v1);
if (memcmp(str, pstr, ) == )
{
printf("It's OK!\n");
}

结果:

【ZCTF】easy reverse 详解的更多相关文章

  1. Android APK反编译easy 详解

    在学习Android开发的过程你,你往往会去借鉴别人的应用是怎么开发的,那些漂亮的动画和精致的布局可能会让你爱不释手,作为一个开发者,你可能会很想知道这些效果界面是怎么去实现的,这时,你便可以对改应用 ...

  2. Redis:默认配置文件redis.conf详解

    转: Redis:默认配置文件redis.conf详解 # Redis配置文件样例 # Note on units: when memory size is needed, it is possibl ...

  3. Redis配置文件redis.conf详解

    一.Redis配置文件redis.conf详解 # Note on units: when memory size is needed, it is possible to specifiy # it ...

  4. Android Animation动画详解(二): 组合动画特效

    前言 上一篇博客Android Animation动画详解(一): 补间动画 我已经为大家介绍了Android补间动画的四种形式,相信读过该博客的兄弟们一起都了解了.如果你还不了解,那点链接过去研读一 ...

  5. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  6. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  7. Quartz.net开源作业调度框架使用详解

    前言 quartz.net作业调度框架是伟大组织OpenSymphony开发的quartz scheduler项目的.net延伸移植版本.支持 cron-like表达式,集群,数据库.功能性能强大更不 ...

  8. Nginx 反向代理、负载均衡、页面缓存、URL重写及读写分离详解

    转载:http://freeloda.blog.51cto.com/2033581/1288553 大纲 一.前言 二.环境准备 三.安装与配置Nginx 四.Nginx之反向代理 五.Nginx之负 ...

  9. linux查看端口及端口详解

    今天现场查看了TCP端口的占用情况,如下图   红色部分是IP,现场那边问我是不是我的程序占用了tcp的链接,,我远程登陆现场查看了一下,这种类型的tcp链接占用了400多个,,后边查了一下资料,说E ...

随机推荐

  1. Android 8.0+ 更新安装apk失败的问题

    最近做项目发现Android 8.0+ 更新安装apk时 出现安装失败的情况  总结原因是 缺少安装的权限 Android 8.0 (Android O)为了针对一些流氓软件引导用户安装其他无关应用. ...

  2. 使用xhprof对php7程序进行性能分析

    Xhprof是facebook开源出来的一个php轻量级的性能分析工具,跟Xdebug类似,但性能开销更低,还可以用在生产环境中,也可以由程序开关来控制是否进行profile. 对于还在使用php5的 ...

  3. C# 小数点后保留两位小数,四舍五入的函数及使用方法

    1 Math.Round(45.367,2) //Returns 45.37 2 Math.Round(45.365,2) //Returns 45.36 C#中的Round()不是我们中国人理解的四 ...

  4. 《笨方法学Python》加分题16

    基础部分 # 载入 sys.argv 模块,以获取脚本运行参数. from sys import argv # 将 argv 解包,并将脚本名赋值给变量 script :将参数赋值给变量 filena ...

  5. 教程:使用cPanel管理域名和数据库

    cPanel是一个基于web的基于web的控制面板,它简化了许多常见的系统管理任务,如网站创建.数据库部署和管理等.本指南向您展示了如何使用cPanel用户帐户管理域和数据库.所有这些指令都与位于端口 ...

  6. python 练习3

    简单计算器的实现: 计算:1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))' #!usr/bin/en ...

  7. 学习python importlib的导入机制

    1. Importer协议 协议涉及两个对象: Finder 和 loader 1. Finder 实现了方法: finder.find_module(fullname, path=None) 返回一 ...

  8. 上传本地文件到github

    第一步:创建新的仓库 勾选Initialize this repository with a README选项,自动创建REAMDE.md文件. 第二步: $ git config --global ...

  9. 批量运维SQl生成,可以用EXCEl,也可以SQL查询生成

    select 'insert into Base_VehiclesInformation (ID,CarModelID,FistRegTime,ScrappageDate, Creator,Creat ...

  10. 在香港用什么软件可以唱歌?香港K歌app推荐

    KTV的源头来自于日本,KTV是Karaok TV的缩写.随着互联网时代越来越发达,手机K歌成了很多人会选择的方式,那么在香港有什么好用的K歌软件呢?这里qt6小编给大家推荐几款好用的,让你足不出户即 ...