010Editor逆向分析

article/2025/9/19 17:45:34

主要内容:

010Editor介绍

16进制编辑器:16进制修改、文本修改、模板解析各种文件格式、对比文件

010暴力破解分析

1、找到注册的窗口

2、测试注册窗口的反应

3、根据反应做出下一步分析

猜测API,API下断点动态调试

敏感字符串,程序中搜索

4、动态分析,定位关键跳转,修改代码

5、动态分析,定位关键CALL,修改代码

010算法分析

关键函数

一步步分析,找到访问用户名和密码的代码

边分析边重写C语言

测试

1样本概况

1.1 应用程序信息

应用程序名称:010Editor 8.0.1

MD5值:7D5D95F0E3DC24AC30133A596D9904AD

SHA1值:4D7089541DB5BFF56CB87F41ECAC2AC3844B2BCA

简单功能介绍:16进制修改、解析各种文件、对比文件

1.2 分析环境及工具

系统环境:win7 32位

工具:Ollydbg、IDA、010Editor、PEiD、Exeinfo、Vistual Studio

1.3 分析目标

1、010Editor暴力破解分析

2、010Editor算法分析

3、010Editor网络验证分析

2.具体分析过程

2.1 分析过程

2.1.1 010Editor暴力破解分析

使用Exeinfo先看一下程序信息,发现该程序是Qt框架下写的,连接器版本是12.0,对应的编译器是VS2013

该程序是收费的,运行之后需要填写用户名和密码,当我们点击Check License时会弹出一个窗口:

因此我们有两种思路进行分析,第一种是搜索创建窗口的函数,第二种是搜索弹出窗口中的字符串,这里第一种进行分析。

使用OD进行分析,在主模块中右键,查找当前模块的名称,发现很多Qt5的函数,输入Create查找,也没有定位到API

使用IDA对程序进行分析,查看其导入表,使用的是Qt的库,对Qt不熟悉,也没有发现创建窗口的函数,因此继续使用OD进行分析。

在模块窗口中找到user32.dll这个模块,这个模块和窗口API有关,进入该模块后,按:Ctrl+n,然后搜索和创建窗口有关的API,如:CreateWindowExA/W、MessageBox、DialogBox、CreateDialog等,Qt程序底层调用的创建窗口的API是CreateWindowA/W,我们搜索该API下断点,然后运行测试:

发现其在CreateWindowExW处断了下来,我们在K窗口中看栈回溯,对主模块进行分析,找到下面几个调用函数:

每个都点进去往上追踪,看有没有关键信息,其中018CB29点进去之后,发现了有用的字符串信息:

其中一个是无效的用户和密码,就是我们输入时弹出的字符串,我们点击该行,会提示我们是从哪个地方跳过来的,我们右键转到该地址:

我们对上面两个对EDI判断的地方下断点运行分析,发现语句执行了,说明如果EBX不等于0x93,EDI不等于0xED、0x20C,就会跳转到刚才提示失败的地方

继续向上追踪,看EBX和EDI分别表示什么意思:

追踪上去发现,如果EDI不等于0xDB就跳转到刚才的地方,对上面的代码下断点运行之后,发现没有断下来,说明上面这一段代码没有执行,EDI的值是上面跳转过来的。

CMP语句的跳转来自两个地方,是两个相邻的地方,我们追踪过去:

发现是判断EBX的值,如果EBX等于0xE7就跳转到刚才的位置,而EBX又是EAX赋值得到的,所以就是判断EAX是否等于0xE7。而又已经知道程序是VS2013的程序,则ECX是函数的返回值,所以我们找到了关键函数。就是当CALL1函数执行后,返回值如果等于0xE7,下面就会跳转到判断EDI的地方。如果EDI不等于0xDB,就会跳转提示错误。继续往下找,看能不能找到提示正确的地方。

在这里发现了提示正确的字符串,又发现其在关键跳转的下面。说明EDI等于0xDB才能继续往下执行提示正确,而EDI又来自上面的EAX,当CALL2函数执行后,返回值EAX等于0xDB,才能继续往下执行提示正确。进入该函数进行分析,发现里面又调用了一次CALL1函数:

分析之后发现当CALL1函数返回值是0x2D的时候会跳转到EAX等于0xDB,说明要想CALL2的返回值等于0xDB,CALL1的返回值必须是0x2D。

暴力破解的话,我们只需要将EDI是否等于0xDB下面的跳转nop掉,然后保存就行,再打开输入就可以通过验证了。

如果想要运行程序之后,直接进入而不需要我们再点Check License,我们要让CALL2的返回值是0xDB,进入函数进行修改,修改后之直接返回:

修改后保存,再打开就是直接进入了。

也可以用弹出窗口中的字符串,来进行搜索。右键查找字符串,右键输入查找的文本也能找到关键位置:

2.1.2 010Editor算法分析   

破解完程序之后,我们需要对关键函数进行分析,来写一个注册机,自动生成序列号。进入关键函数:0040A826。因为是C++程序,需ECX传递this指针,我们需要关注ECX的值,数据窗口中跟随

每个都右键跟随进去看看,数据窗口跟随:0x05153DF0,发现了我们输入的用户名:

数据窗口跟随:0x05129678,发现了我们输入的密码:

发现了带有isEmpty、String的字符串,猜测是判断用户名和密码是否为空的函数:

进去函数进行分析,确定是判断用户名和密码是否为空的函数:

继续往下分析,EAX被赋值为:0x0012D08C,数据窗口跟随:0x05010C3F0,再数据窗口跟随:

发现了输入的密码:

继续向下分析,跟随ECX,ECX+4上保存的是用户名的信息:

下一个函数将带有密码信息的EAX传进去了,不知道其具体目的,进去之后发现过程很长。先数据窗口跟随EAX,然后运行程序,看其数据是怎么变化:

运行前:

运行后:

根据结果显示,上面函数的作用应该是将密码字符串转为了16进制的字节数据。继续向下分析,下面函数传入了一个ASCII是999的值,根据名字猜测是字符串操作,但是具体作用还不知道:

运行之后,返回值是0,没有发现其他变化,先不管其作用,继续往下分析:

发现ESI指向了0123456789ABCDEF,然后是循环。这一块是将999传入进行了操作,没有涉及到用户名和密码,先不去分析其作用,继续向下分析:

从这里开始应该是算法的地方,一步步对算法进行分析:

K[3]如果不等于9C、FC、AC,就会跳转到EAX=0xE7,返回时就会出错。所以K[3]应该是9C、FC、AC的其中一个。如果密码是个数组,初步判断K[0]和K[6]有联系,K[1]和K[7]、K[2]和K[5]有联系。其中K[0]^K[6]后传入了函数,进入函数进行分析:

该函数对K[0]和K[6]进行了一系列的操作,然后返回EAX,再继续分析下一个函数:

经过分析得知:ECX不能为0,即:((K[0]^K[6])^0x18+0x3D)^0xA7不等于0,EAX也不能为0,且小于等于0x3E8。而EAX的值又取决于上面函数的返回值,函数中分析后可知,EAX的值取决于函数中EAX除以ECX的余数,如果余数EDX不为0,则EAX被清空为0,因此,要想EAX不为0,EAX处于ECX的余数要为0。由上面的分析可以得出密码数组中一些元素的关系,可以根据上面的算法编写代码进行测试。

继续向下分析:

运行到这里之后向下跳转到:013BDD88

有一个函数由toUtf8String的字样,应该是和字符串有关的操作,跟随push进去的EAX,看数据的变化,运行前:

该函数运行后:

所以该函数作用是将用户名字符串转化为ASCII。下面又有一个函数,看名字应该是和数组有关的,运行之后看返回值:

运行之后发现其返回值是用户名,继续向下分析,有一个函数传入了四个参数,具体作用不知道,先运行看数据变化:

运行之后,EAX不确定是什么值,可能是哈希值。继续向下分析:

当0x00407644函数的返回值即:((K[0]^K[6])^0x18+0x3D)^0xA7大于等于9的时候才会跳转到成功的地方。经过分析得知:0x00402E50这个函数对ASCII码版的用户名字符串进行了一个计算,计算后返回的结果,分别与密码的K[4]、K[5]、K[6]、K[7]进行了比较,都相等的话,才能成功。

至于函数对用户名计算的这个函数,可以一步步进行分析,也可以Ctrl+X复制函数的地址,然后再IDA中按G键找到该地址:

按F5进行分析:

OD中将0x2E64148上的数据复制下来,放到代码中:

再将IDA中的代码复制下来,放到VS代码中,将参数进行修改

运行之后,得到密码:

2.1.3 010Editor网络验证分析

生成的序列号过一段时间后,再次注册会将序列号发给官网进行验证,如果官网没有保存这个序列号就会失效,我们需要让其通过网络验证。

使用OD继续分析:

运行之后,发现函数返回了0x113,进入0x00409C9B处的函数分析:

发现里面有判断,如果[ESI+0x2C]上的值不为0,就会返回0x113,我们可以将跳转改为无条件跳转:JMP short 0x013BE4F6,然后保存,继续往下运行观察:

此时,没有跳转,继续向下执行,进行网络验证,修改代码,让其一直返回1:

保存之后,就成功绕过了网络验证。

2.2 测试代码(如有代码验证贴出关键代码

用户名加密函数:

参数1:用户名、参数2:BL等于FC, 就是0,不等就是1、参数3:1、参数4:0x40083C8的返回值,<=0x3E8

int __cdecl EncodeUsername(const char* pUsername, int a2, char a3, unsigned __int16 a4)

{

    const char* v4; // edx@1

    signed int v5; // esi@1

    signed int v6; // edi@1

    unsigned __int8 v7; // bl@2

    int v8; // eax@3

    int v9; // ecx@3

    int v10; // ecx@4

    int result; // eax@4

    int v12; // ecx@5

    unsigned __int8 v13; // [sp+8h] [bp-10h]@2

    unsigned __int8 v14; // [sp+Ch] [bp-Ch]@2

    unsigned __int8 v15; // [sp+10h] [bp-8h]@2

    int v16; // [sp+14h] [bp-4h]@1

    v4 = pUsername;

    v16 = 0;

    v5 = strlen(pUsername);

    v6 = 0;

    if (v5 <= 0)

    {

        result = 0;

    }

    else

    {

        v13 = 0;

        v14 = 0;

        v7 = 15 * a4;

        v15 = 17 * a3;

        do

        {

            v8 = toupper(v4[v6]);

            v9 = v16 + g_EcodeArray[v8];

            if (a2)

            {

                v10 = g_EcodeArray[v7]

                    + g_EcodeArray[v15]

                    + g_EcodeArray[(unsigned __int8)(v8 + 47)] * (g_EcodeArray[(unsigned __int8)(v8 + 13)] ^ v9);

                result = g_EcodeArray[v14] + v10;

                v16 = g_EcodeArray[v14] + v10;

            }

            else

            {

                v12 = g_EcodeArray[v7]

                    + g_EcodeArray[v15]

                    + g_EcodeArray[(unsigned __int8)(v8 + 23)] * (g_EcodeArray[(unsigned __int8)(v8 + 63)] ^ v9);

                result = g_EcodeArray[v13] + v12;

                v16 = g_EcodeArray[v13] + v12;

            }

            v14 += 19;

            ++v6;

            v15 += 9;

            v7 += 13;

            v13 += 7;

            v4 = pUsername;

        } while (v6 < v5);

    }

    return result;

}

    主函数:

int main()

{

    srand(time(NULL));

    //0、初始化

    int dwRet = rand() % 0x3E8;

    byte K[10] = { 0x11,0x22,0x33,0x9C,0x55,0x66,0x77,0x88,0x99,0xaa };

    //1、用户名,经过函数加密,由返回值确定K[4]、K[5]、K[6]、K[7]

    char szName[100] = { 0 };

    printf("please input name: ");

    scanf_s("%s", szName, 100);

    DWORD dwKey = EncodeUsername(szName, 1, 0, dwRet);

    K[4] = dwKey & 0xFF;

    K[5] = dwKey >> 8 & 0xFF;

    K[6] = dwKey >> 16 & 0xFF;

    K[7] = dwKey >> 24 & 0xFF;

    //2、穷举

    while (true)

    {

        byte K0 = rand() % 0xFF;

        byte K6 = K[6];

        //AL=((K[0]^K[6])^0x18+0x3D)^0xA7

        //ECX=(((K[0]^K[6])^0x18+0x3D)^0xA7)&0xFF

        byte al = ((K0 ^ K6) ^ 0x18 + 0x3D) ^ 0xA7;

        if (al >= 9)

        {

            K[0] = K0;

            break;

        }

    }

    while (true)

    {

        byte K1 = rand() % 0xFF;

        byte K2 = rand() % 0xFF;

        byte K5 = K[5];

        byte K7 = K[7];

        //ESI=(((K[1]^K[7])&0xFF)*0x100+((K[2]^K[5])&0xFF))&0xFFFF

        //EAX=(((ESI^0x7892+0x4D30)^0x3421)&0xFFFF)

        //余数为0,返回商;且返回的商小于等于0x3E8

        DWORD ESI = (((K1 ^ K7) & 0xFF) * 0x100 + ((K2 ^ K5) & 0xFF)) & 0xFFFF;

        DWORD EAX = (((ESI ^ 0x7892) + 0x4D30) ^ 0x3421) & 0xFFFF;

        int a = EAX % 0xB;

        if (EAX%0xB==0 && EAX/0xB== dwRet)

        {

            K[1] = K1;

            K[2] = K2;

            K[5] = K5;

            K[7] = K7;

            break;

        }

    }

    printf("%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X", K[0], K[1], K[2], K[3], K[4], K[5], K[6], K[7], K[8], K[9]);

    getchar();

    return 0;

}

3.总结

在对010Editor分析过程中,发现其先对用户名和密码判断是否为空,然后对密码字符串转成16进制字节数据,对数据进行验证。再将用户名转成ASCII字符串,通过一个函数返回一个值,这个值会和密码的K[4]、K[5]、K[6]、K[7]有关系。编写注册机代码思路是:先指定用户名,用加密函数算出一个值,然后根据这个值确定K[4]、K[5]、K[6]、K[7],再用分析出来的的算法将剩下不确定的计算出来。

在对算法进行逆向分析的过程,要一步步跟踪解析算法,需要有耐心,并且要多写注释,便于思路的梳理。另外也可以配合IDA的使用,自动分析一些函数。


http://chatgpt.dhexx.cn/article/rTq2n8b0.shtml

相关文章

Web前端——CSS伪类和伪元素

CSS伪类&#xff1a; 1.伪类的概念&#xff1a; 可以理解为描述元素的某种状态&#xff0c;用于当已有元素处于的某个状态时&#xff0c;为其添加对应的样式&#xff0c;这个状态是根据用户行为而动态变化的。 2.伪类的语法&#xff1a; 标签:伪类{设置的样式&#xff0c;伪类…

CSS伪类

CSS中伪选择器有两种分别是伪元素选择器和伪类选择器。为了向后续版本兼容&#xff0c;伪元素选择器常用"::"开头&#xff0c;而伪类选择器用“:”开头。本篇主要讲解以下伪类选择器&#xff1a; :first-child:last-childonly-childonly-of-type:nth-child(n)nth-la…

【JavaScript 逆向】AST 技术反混淆

前言 通过浏览器工具可以清楚的看到网站正在运行的 HTML 和 JavaScript 代码&#xff0c;所以对 JavaScript 代码进行混淆处理是一些网站常用的反爬措施&#xff0c;例如下文介绍到的字符串混淆、控制流平坦化等&#xff0c;这使得 JavaScript 的可读性变得很差&#xff0c;难以…

Css预编译神器

最近&#xff0c;有靓仔吐槽在编译css代码时&#xff0c;每次写选择器都会变成CV大神&#xff0c;虽说有CV加持但是呢依然会觉得很麻烦&#xff0c;毕竟手速不像年轻时候那样为所欲为 在这里呢给推荐大家用一款神级插件&#xff0c;也是小编参与完成的轻量级插件–sass&#x…

CSS 伪类

CSS 伪类 CSS 伪类是添加到选择器的关键字&#xff0c;用于指定所选元素的特殊状态。例如&#xff0c;伪类 :hover 可以用于选择一个按钮&#xff0c;当用户的指针悬停在按钮上时&#xff0c;设置此按钮的样式。 举例说明: button:hover {color: blue; }伪类由冒号&#xff…

逆向分析并修改Hello World程序《逆向工程核心原理》《软件逆向工程原理与实践》

文章目录 OllyDbg窗口及快捷键步骤1&#xff1a;VS生成需逆向的文件步骤2&#xff1a;OllyDbg中打开该程序的exe文件&#xff0c;找到需修改的位置步骤3&#xff1a;修改修改1&#xff1a;修改指令修改2&#xff1a;修改字符串修改3&#xff1a;输出任意英文 软件逆向工程原理与…

js逆向案例-css字体反爬

目录 一、反爬点二、反爬分析1、js逆向解密响应参数2、css字体伪元素分析一、反爬点 案例网站响应参数js加密, css字体伪元素隐藏,以及style取值等逻辑判断 二、反爬分析 1、js逆

SQL 结构化查询语言

导读 MySql是我们常用的数据库,javaEE常用几款(Oracle,PostgreSQL,DB2或IBM),SQLite是用于嵌入式设备里的小型数据库,例如Android或IOS,而掌握SQL语句,就相当于掌握了所有的常见关系化数据库,需要同学们重点掌握以及经常复习 MySQL数据库服务器、数据库和表的关系 一般一个项…

《数据库系统》(三) 结构化查询语言

hello大家好,今天我们来学习结构化查询语言。教妹学数据库,没见过这么酷炫的标题吧?“语不惊人死不休”,没错,标题就是这么酷炫。 我的妹妹小埋18岁,校园中女神一般的存在,成绩优异体育万能,个性温柔正直善良。然而,只有我知道,众人眼中光芒万丈的小埋,在过去是一个…

mysql 结构化数据库_【MySQL】——MySQL数据库和SQL结构化查询语言概述

【MySQL】——MySQL数据库和SQL结构化查询语言概述 【MySQL】——MySQL数据库和SQL结构化查询语言概述 文章目录数据库和SQL语言【1】数据库概述 【2】SQL语言 【3】MySQL数据库 【4】启动/停止MySQL服务 【1】数据库概述 数据的传输&#xff1a;数据库 —> Web服务器 —>…

MySQL结构化查询语言

结构化查询语言sql包含以下四部分&#xff1a; 1.DDL //数据定义语言&#xff0c;create,drop,alter 2.DML //数据操作语言&#xff0c;insert,update,delete 3.DQL //数据查询语言&#xff0c;select 4.DCL //数据控制语言&#xff0c;grant,commit,rollback 以下就增删查…

结构化查询语言SQL基本功能及其概念

SQL语法 可以把SQL分为两部分数据操作语言DML和数据定义语言DDL。 SQL&#xff08;结构化查询语言&#xff09;有用于执行查询、更新、删除、插入记录的语法。 SQL的DML部分&#xff1a; select-从数据库表中获取数据。insert into-向数据库表中插入数据update-更新数据库表中…

Rasa中文聊天机器人开发指南(3):Core篇

文章目录 1. 对话管理1.1 多轮对话1.2 对话管理 2. Rasa Core2.1 Stories2.2 Domain2.3 Responses2.4 Actions2.5 Policies2.6 Slots2.6.1 Slots Type2.6.2 Slots Set2.6.3 Slots Get 2.7 Form2.8 Interactive Learning 3. 改进ChitChatAssistant项目3.1 config.yml3.2 weather…

Rasa开发使用 Rasa_NLU及Rasa_Core模型训练与测试

文章目录 Rasa术语 Rasa_NLU1. Pipeline2. 准备工作&#xff1a;训练MITIE模型文件3. rasa_nlu 语料4. 训练模型5. 测试验证 Rasa Core1. Stories可视化stories 2. Domain3. 训练对话模型测试对话模型 测试聊天机器人 Rasa Rasa是一个开源机器学习框架&#xff0c;用于构建上下…

浅读Rasa3.2.5源码(rasa train、rasa shell)

目录 浅读Rasa3.2.5源码&#xff08;rasa train、rasa shell&#xff09;一、 分析 __main__.py&#xff08;1&#xff09;. 解析main.py的部分代码&#xff08;2&#xff09;. rasa常用命令 二、 训练阶段&#xff08;1&#xff09;. 准备训练数据&#xff08;2&#xff09;. …

2.rasa架构

rasa架构 消息处理 此图显示了使用Rasa构建的助手如何响应消息的基本步骤&#xff1a; 这些步骤分别是&#xff1a; 1. 收到消息并将其传递给解释器(Interpreter)&#xff0c;解释器将其转换为包含原始文本&#xff0c;意图和找到的任何实体的字典。这部分由NLU处理。 2. 跟踪…

Rasa -流程

Rasa入门笔记1 -流程 一、Rasa是什么二、Rasa工作流程 一、Rasa是什么 Rasa是一个nlp开源机器学习框架&#xff0c;用于构建问答与多轮对话机器人。 二、Rasa工作流程 Rasa分为两个模块NLU模块与Core模块&#xff0c;NLU 一>用于提取意图与实体&#xff0c;Core一>用于…

Rasa-X 部署

Rasa-X 部署(docker版) 版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明&#xff0c;违反必究。 本文链接&#xff1a;https://blog.csdn.net/junxing2018_wu/article/details/106638599 说明: do…

Rasa系列教程(二) -- Rasa NLU基础

目录 前言 一、训练数据 1.1 意图字段&#xff08;intent&#xff09; 1.2 同义词字段&#xff08;synonym&#xff09; 1.3 查找表字段&#xff08;lookup&#xff09; 1.4 正则表达式字段&#xff08;regex&#xff09; 1.5 查找表和正则表达式的使用 二、组件 2.1 …

RASA框架介绍

关于RASA一个机器学习框架&#xff0c;用于构建基于文本和语音的聊天机器人。框架完整&#xff0c;可扩展性&#xff0c;易用性高&#xff0c;高效灵活。2.0新版本统一训练数据格式&#xff0c;配置文件和模型处理方式。 在最新版的rasa里已经集成了bert模型和xlnet&#xff0c…