ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

010EditorV8.0.1逆向分析

2021-03-21 10:59:34  阅读:208  来源: 互联网

标签:分析 逆向 函数 szPassWord DWORD 跳转 0xFF 010EditorV8.0


1.样本概况

1.1 应用程序信息

  ----------------------

 

应用程序名称:010Editor V8.0.1 32位

MD5值:9288F75678EB40C94398DD9F86E1C378

SHA1值:9B5891A124EF051A1175CB5249633AB956474A39

简单功能介绍:

1. 010 Editor是一个专业的文本编辑器和十六进制编辑器

2. 同时也是强大的二进制编辑工具,能查看和编辑硬盘驱动器上的任何二进制文件(文件大小无限制)和文本文件,包括Unicode文件,C / C ++,XML,PHP等。包括查找,替换,在文件中查找,替换文件,二进制比较,校验和/散列算法,直方图等

3. 独特的二进制模板技术使您可以了解任何二进制文件格式,支持下载共享的二进制模板和脚本,已多种不同格式导入导出二进制文件

4. 可以在不同的进制之前相互转换

1.2 分析环境及工具

系统环境:win7 Professional 32位(15pb实验环境)

工具:

1. 010Editor(十六进制编辑器)

2. DIE(PE文件分析)

3. VS2019(编写测试程序)

4. OllyDbg(动态调试分析)

5. Hash(计算MD5,SHA1)

6. IDA(静态分析)

1.3 分析目标

需要实现的功能:

1. 暴力破解,通过注册

2. 分析算法,完成注册机

3. 分析网络验证

2.具体分析过程

2.1 获取样本信息

2.1.1 DIE查壳

由VS2013编写的使用Qt库开发的32位程序

 

2.2 分析注册响应

2.2.1 进行注册

在帮助中找到关于点击注册

随意输入注册信息查看响应,弹出了一个对话框,提示无效的用户名和密码,并且该对话框阻塞了父窗口的点击,应该为模态对话框

2.2.2 OD中分析

由于使用Qt编写的程序,对Qt的库函数并不熟悉,不过最终调用的还是Windows生成窗口的API,使用OD附加程序,找到user32.dll模块在常见的创建窗口API上下断点(e窗口找到模块进入Ctrl+N查看所有名称,搜索API下断)

点击注册,程序在调用CreateWindowExW断下

在k窗口查看堆栈的调用

主要找到主模块中的调用,依次点击查找是否使用了一些敏感字符串

在地址0x14B5B29的位置找到关键字符串与弹出的窗口提示相对应

找到跳转到此处的关键的判断条件位置

下断点重新运行跟踪,断下最终跳转到输出提示无效密码的字符串的位置,找到的为跳转判断位置

2.2.3 暴力破解

对判断逻辑做基础的分析,找到关键的判断位置,直接修改掉

成功验证跳转到0x01D55920

上一步验证成功之后跳转的位置,理想条件为EDI=0xDB

进入函数0x01389C9B分析,需要的成功返回条件

通过验证显示的提示字符串

将关键跳转处的判断修改,暴力破解,保存新文件

选中修改的指令,右键复制到所执行文件,选择,跳转后再右键保存文件

打开保存的补丁文件,即可通过验证

但是这样修改的每次都会弹出注册页面需要点击按钮才会进入程序

找到关键的判断条件函数,修改,提示包含重定位

使用010Editor修改,关闭随机基址的符号

再次修改函数跳转

修改为

打开保存的文件直接跳过注册进入程序

2.2.4 字符串搜索

还可以通过查找提示窗口的字符串来定位函数调用的地方,对于唯一或只有少数的调用这个方法比较有效,快速定位到关键点,这里的检测许可提示就是唯一引用,跳转可以直接找到关键调用加以分析

2.3 分析算法

2.3.1 分析第一部分过程

找到最开始的返回关键判断条件值的函数开始分析

找到需要分析的关键函数

对代码的初步分析

判断输入是否为空

第一个函数0x00467644,对 p[0]^p[6] 的操作处理,返回结果保存在ECX

第二个函数 0x04083C8,对 ESI = (p[1] ^ p[7]) & 0xFF) * 0x100 + ((p[2] ^ p[5]) & 0xFF) 全零扩展 的处理,返回值在EAX

2.3.2 注册机1.0

实现主要函数中前半部分的跳转,编写初步功能的注册机测试,实现到0x13BDD8B的跳转条件

测试代码如下

  1. char szPassWord[8] = {0};  
  2.    szPassWord[3] = 0x9C;  
  3.      
  4.    while (TRUE)  
  5.    {  
  6.        szPassWord[0] = rand() % 0xFF;  
  7.        szPassWord[6] = rand() % 0xFF;  
  8.        DWORD EAX = ((szPassWord[0] ^ szPassWord[6]) ^ 0x18 + 0x30) ^ 0xA7;  
  9.        if (EAX >= 9)  
  10.        {  
  11.            break;  
  12.        }  
  13.    }  
  14.   
  15.    while (TRUE)  
  16.    {  
  17.        szPassWord[1] = rand() % 0xFF;  
  18.        szPassWord[2] = rand() % 0xFF;  
  19.        szPassWord[5] = rand() % 0xFF;  
  20.        szPassWord[7] = rand() % 0xFF;  
  21.        DWORD ECX = DWORD ECX = (((szPassWord[1] ^ szPassWord[7]) & 0xFF) * 0x100 + ((szPassWord[2] ^ szPassWord[5]) & 0xFF));
  22.        DWORD EAX = (((ECX ^ 0x7892) + 0x4D30) ^ 0x3421) & 0xFFFF;  
  23.        if (EAX % 0xB == 0 && EAX / 0xB <= 0x3E8)  
  24.        {  
  25.            break;  
  26.        }  
  27.    }  
  28.    szPassWord[4] = rand() % 0xFF;  
  29.   
  30.    for (int i = 0; i < 8; i++)  
  31.    {  
  32.        printf("%02X", szPassWord[i] & 0xFF);  
  33.    }  

运行结果:29E2-409C-F4F6-6B4D,输入单步查看判断跳转情况

2.3.3 分析第二部分过程

对上半部分实现成功跳转后的算法分析,完成最后的判断条件的达成,通过验证,主要操作是对用户名字符串格式转换为ascii,然后对其求值,依次与密码数组下标为4,5,6,7的元素进行比较,相等达成条件,最后将前半部分中第一个函数0x00467644的返回值与主函数的第一个参数9进行比较,最后返回值为0x2D

这里的关键函数就是对处理过的字符串进行求值,返回值为一个DWORD类型的值

对这个函数进行简单分析

2.3.4 使用IDA分析

使用IDA跳转到该地址分析0x0013BDDB6

使用IDA跳转到该地址分析0x0013BDDB6

找到该DWORD类型的数组,确实是对一个DWORD数组元素操作

F5转换为C语言查看

  1. int __cdecl sub_13BD120(const char *a1, int a2, char a3, __int16 a4)  
  2. {  
  3.   const char *v4; // edx  
  4.   signed int v5; // esi  
  5.   signed int v6; // edi  
  6.   unsigned __int8 v7; // bl  
  7.   int v8; // eax  
  8.   int v9; // ecx  
  9.   int v10; // ecx  
  10.   int result; // eax  
  11.   unsigned __int8 v12; // [esp+8h] [ebp-10h]  
  12.   unsigned __int8 v13; // [esp+Ch] [ebp-Ch]  
  13.   unsigned __int8 v14; // [esp+10h] [ebp-8h]  
  14.   int v15; // [esp+14h] [ebp-4h]  
  15.   
  16.   v4 = a1;  
  17.   v15 = 0;  
  18.   v5 = strlen(a1);  
  19.   v6 = 0;  
  20.   if ( v5 <= 0 )  
  21.     return 0;  
  22.   v12 = 0;  
  23.   v13 = 0;  
  24.   v7 = 15 * a4;  
  25.   v14 = 17 * a3;  
  26.   do  
  27.   {  
  28.     v8 = toupper((unsigned __int8)v4[v6]);  
  29.     v9 = v15 + dword_2E64148[v8];  
  30.     if ( a2 )  
  31.       v10 = dword_2E64148[v13]  
  32.           + dword_2E64148[v7]  
  33.           + dword_2E64148[v14]  
  34.           + dword_2E64148[(unsigned __int8)(v8 + 47)] * (dword_2E64148[(unsigned __int8)(v8 + 13)] ^ v9);  
  35.     else  
  36.       v10 = dword_2E64148[v12]  
  37.           + dword_2E64148[v7]  
  38.           + dword_2E64148[v14]  
  39.           + dword_2E64148[(unsigned __int8)(v8 + 23)] * (dword_2E64148[(unsigned __int8)(v8 + 63)] ^ v9);  
  40.     result = v10;  

在OD中使用插件取出数组复制到代码中,并将IDA分析的转换用户名字符串代码EncodeNameStr代码取出直接在自己的代码中使用

2.3.5 注册机2.0

编写MFC程序完成注册机的功能,使用到的主要函数就是对两部分的算法分析,达到完成注册认证的判断跳转,其中调用上面复制出来的对用户名字符串的求值函数

通过值来确定密码数组中下标为4,5,6,7的元素的值,再通过两个穷举得到其他三个元素实现达成条件,因为字符串求值函数最后一个参数为第二个判断函数的返回值,这里需要改变条件,写成固定值,下表为3的元素为固定值,这里写为0x9C,整个密码16位

  1.     //密码数组  
  2.     char szPassWord[8] = { 0 };  
  3.     //标识版本?  
  4.     szPassWord[3] = 0x9C;  
  5.   
  6.     //判断条件值  
  7.     DWORD JudgeResult = 0x3E8;  
  8.   
  9.     UpdateData(TRUE);  
  10.     CStringA str;  
  11.     str = m_NameStr;  
  12.   
  13.     //对用户名ascii码字符串求值  
  14.     DWORD result = EncodeNameStr(str, 1, 0, JudgeResult);  
  15.   
  16.     //根据求出字符串的值对部分字节赋值  
  17.     szPassWord[4] = result & 0xff;  
  18.     szPassWord[5] = result >> 0x8 & 0xff;  
  19.     szPassWord[6] = result >> 0x10 & 0xff;  
  20.     szPassWord[7] = result >> 0x18 & 0xff;  
  21.   
  22.     while (TRUE)  
  23.     {  
  24.         szPassWord[0] = rand() % 0xFF;  
  25.         DWORD EAX = ((szPassWord[0] ^ szPassWord[6]) ^ 0x18 + 0x30) ^ 0xA7;  
  26.         if (EAX >= 9)  

             break;  

  1.     }  
  2.   
  3.     while (TRUE)  
  4.     {  
  5.         szPassWord[1] = rand() % 0xFF;  
  6.         szPassWord[2] = rand() % 0xFF;  
  7.         DWORD ECX = (((szPassWord[1] ^ szPassWord[7]) & 0xFF) * 0x100 + ((szPassWord[2] ^ szPassWord[5]) & 0xFF));  
  8.         DWORD EAX = (((ECX ^ 0x7892) + 0x4D30) ^ 0x3421) & 0xFFFF;  
  9.         if (EAX % 0xB == 0 && EAX / 0xB == JudgeResult)  
  10.             break;  
  11.     }  

  

  1.     m_PasswordStr=" ";  
  2.     for (int i = 0; i < 8; i++)  
  3.     {  
  4.         CString s;  
  5.         s.Format(L"%02X", szPassWord[i] & 0xFF);  
  6.         m_PasswordStr += s;  
  7.     }  
  8.     UpdateData(FALSE);

测试注册机,完成功能

2.4 网络验证分析

2.4.1 跳过网络验证

当一段时间会发现注册码失效,原因是一段时间后会向服务器发送注册码验证,自己生成的验证码并未在服务器保存,导致认证失败,这里可以找到网络验证的地方跳过

通过字符串搜索找到引用提示字符串的地址

找到跳转来自的地方,确认网络验证的函数

将网络验证的函数修改,恒定返回值为1,确保跳转

这里推断过段时间[ECX+0x2C]会被设置为非0值,则会进行网络验证,这个函数会被多次调用验证

将这个跳转修改,让他正常跳转下去

保持返回值的正确

将修改的指令复制到可执行文件,保存文件,验证

网络验证成功,跳转

跟随跳转到判断

3.总结

和前两个程序对函数的调用分析和消息的响应分析不同的是,010Editor逆向分析的主要内容是对数据处理的算法分析,从提示窗口入手,向上找到关键字符串的引用,逆向找到分析数据的地方。
    这是基于QT的程序,用的也是面向对象的思想,所有从ECX中的this指针一样可以找到很多信息,通过变量及参数的查看分析到何时对输入数据的处理,做如何处理,分析算法需要耐心,一步步跟入,边分析边记录,最后形成一个整体的数据处理过程。再自己编写代码还原验证。这次运用了IDA的代码还原功能,自己再加以分析修改导出可以节省不少时间。

总的来说算法分析比较枯燥,足够的耐心和细心必不可少。

标签:分析,逆向,函数,szPassWord,DWORD,跳转,0xFF,010EditorV8.0
来源: https://blog.csdn.net/datouyu0824/article/details/115043914

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有