首页
社区
课程
招聘
[讨论]关于010Editor的分析
发表于: 2021-12-2 18:39 15014

[讨论]关于010Editor的分析

2021-12-2 18:39
15014

1、常规扫描收集信息

image-20211130143455109

image-20211130143528481

image-20211130143623852

2、资源查看

image-20211130143756407

3、spy++查看其窗口回调函数地址

image-20211130145447740

4、OD转到地址分析函数

image-20211130145704008

image-20211130145819123

image-20211130190410355

4、查看调用 (To Be Continued…)

​ 发现来自主模块的调用很多,这里只能一个一个尝试,看哪里有有用的信息。

image-20211130190735345

5、在尝试输入用户名和密码失败后,会弹出一个对话框告诉你密码错误的提示语

​ 其实这个字符串也是个很好的切入点,我们可以在IDA和OD中寻找有关此字符串的信息。

image-20211130210858513

6、OD和IDA寻找关键函数

image-20211130211124067 双击它进入到.rdata段发现函数本地,然后Ctrl+x查看交叉引用发现只有一个地方调用了它,很好我们跟进查看

image-20211130211343354

image-20211130211546700然后IDA最经典的流程图来了,有了它能对我们分析起到帮助。但是一眼看上去好复杂,但是不用怕,不需要每一个框都分析,只用顺着箭头向上找,找到是从哪里跳转过来的就能找到切入点。

cmp ebx,93h =====>这里不能相等

jnz xxxxx

image-20211130211822058

cmp edi,20h=====>这里也不能等

jz xxxxxxx

image-20211130212011162

cmp edi,0EDh=====>还是不能等

jz xxxxxxxx

image-20211130212102673

cmp edi,0DBh=====>继续不等

jnz xxxxxxx

image-20211130212153118

今天继续,从昨天找到的比较edi和0xDB这里继续查看,我们打开OD,定位到这个验证的地方看,但是怎么定位到这里呢,我们可以先搜一下字符串,找到Invalid name。。。那一段,然后双击到代码段。然后你就能发现这儿有个跳转,我们可以过去看看能不能找到上一跳

image-20211203095326575

image-20211203095507979

当然也可以试试下面这个比较不相等的时候会跳到哪里去,我们跟过去看看。

image-20211203095739795

相似的,我把能找到提示的几条跳转的结果都找了一下

image-20211203100312748

然后敲黑板了,重点来了

image-20211203100532960

我们找到它的上一跳发现如果这个标胶不相等就会跳到错误的区间,否则就正确了

image-20211203100638241

继续找上一跳

image-20211203101109377

image-20211203101159166

image-20211203101508292

image-20211203101920735

image-20211203102613725

然后就可以在这个函数上打个断点观察一下参数和返回值了

刚开是可以先去看看这个 函数的返回值是怎样的,先不具体去分析,就粗略的看一下发现有三种。

image-20211203103802789

最上面的返回值是ESI,但是ESI又是啥呢,别急,我们往上翻一下

image-20211203103929479

发现也有几种情况这个我们先放着吧,看看下面那个函数有什么说法。

image-20211203104957387

image-20211203105136583

跟着跳下来发现分别对应着三种返回这,我们要的就是DB,所以传进来的EAX应该要是2D

image-20211203105251699

于是现在有一个结论:上面这个函数的返回值应该是2D才能得到正确的结果。

image-20211203105603940

所以最简单粗暴的破解方法来了,就是把关键跳转那个地方给破坏掉,它无法验证DB那个地方了也就不起作用了。

image-20211203110208740

image-20211203112329674

这个爆破有点low,重新打开程序还是会弹出Register窗口,我们要点击一下Check License按钮还会弹出一个接受密码的对话框,然后点击OK才能打开软件,太麻烦了。因为我们刚刚只是把最外层的关键跳转给改了,里面的密码验证环节特别是关键验证没有变动,所以很麻烦。接下来尝试一下更方便的破解。

再次定位到关键函数,这里这里:

image-20211203114717456

image-20211203140249279

估计很多朋友都忘了随即地址在哪改了吧,我们复习一下PE文件的内容,先找到扩展头(OptionHeader)

image-20211203140735637

然后找到DLL_Characteristics字段里的Dynamic_base,1代表启用,改成0就把他禁止了。

image-20211203140815933

改好了之后再次打开OD回到刚刚定位的地方把前两条语句修改掉,如图

image-20211203141439943

然后保存到文件

image-20211203144937460

image-20211203144955514

然后重新打开,就成了!!!

image-20211203145015999

还得先在OD里定位到两个重要函数的地址。

image-20211203150900172

先看看这个函数的参数,一个ECX和两个立即数,这看起来就是C++的thiscall了,ECX是this指针,这样我们先在数据窗口看一下这个指针指向的数据是啥。

image-20211203151450763

这是this指针下的第一个偏移是我们输入的用户名

image-20211203151858715

第二个偏移是我们输入的密码

image-20211203152057631

第三个偏移

image-20211203152149066

真正的的有用的语句在这些下面

image-20211203154102308

我们看到下面有个OD分析出来的函数,应该是判断是否为空的,为空返回值就是1,否则就是0,为空就直接JNZ跳转走了

image-20211203154522961

image-20211203154640052

继续往下走,第二个遇到的函数就很好说了,看参数把第二个成员给了ecx然后再次检查是否为空,显然就是检查我们输入的序列号了。

image-20211203154826723

在每次检查完是否为空之后就会有个比较和跳转,如果为空直接就跳走了,那么正确的执行流程应该是直接往下走的,跟进代码看看后面会不会对用户名和序列号进行访问或者修改的操作。

image-20211203155641942

call完之后发现局部变量果然被修改了,可以和上图对比着看一下

image-20211203155912007

可见这里应该是把我们输入的密码改成了一个16进制的字节数据

image-20211203160135625

接下来再看这个ESi里面保存的全局变量是什么值,观察所知是一个999字符串,不知道干嘛的,继续往下分析

image-20211203160439492

两个参数,一个999字符串的地址,一个用户名的地址

image-20211203161146821

无事发生,在用户名、序列号、“999”这三个地址的值都未发生改变,推测是创建了一个QString字符串,暂时没什么影响,继续往下走。

image-20211203161523237

是0到F的ASCII码

image-20211203161548259

发现这里是个循环

image-20211203161941694

继续往下看,发现了一个局部变量给BL赋值,跟随数据发现就是我们写入的序列号,它正在取低字节,如果将12这个地址看作数组首地址的话,那么78这里就是偏移了三个单位。

这里我们可以假设此时BL存放的是k[3]

image-20211203162245702

同理,下面BH存放的就是k[5]。

友情提示,即将进入最关键的序列号计算环节了,请打起精神啊~

第一次比较:把BL和0x9C进行对比,不相等的话跳转

image-20211203164855498

然后跟0xFC对比,不相等则跳转

image-20211203165023203

然后跟0xAC对比,不相等则跳转

image-20211203165102659

再然后就进入default处理然后直接结束了

image-20211203165146440

所以拿小本本记下来,k[3]可能的值有三个0x9C、0xFC、0xAC

每一步给AL赋值的语句我们都要数据跟随一下,查看当前局部变量的值是什么,然后转换成序列号数组看看是怎么算的。

image-20211203171158761

image-20211203171259240

image-20211203181611918

image-20211203181811345

image-20211203182032472

我觉得有必要解释一下这一步的过程,首先查看MOVZX的作用

image-20211203182615877

image-20211203182925368

打个比方,假设AL此时的值是0x00AC,那么经过了MOVZX的操作之后,ECX变成了0x0000 00AC。但是把它转换成C语言怎么办呢?就有了按位与上一个0xFF的操作,可以将高位清0。接下来还是一些赋值的操作,看图

image-20211203183316160

image-20211203184100400

image-20211203184124212

回头去看一下这个函数有几个参数?只有一个PUSH

image-20211203184314635

其实这个函数就是把传进来的值进行了一系列计算而已,那么可以小记一下这个call就是在处理k[0]^k[6]的数据

image-20211203184551269

下面也有一个call,参数是很长的一串

image-20211203185938962

然后进去发现这个函数过程也不复杂可以分析一下

image-20211203201340011

接着就是下面的分析了,这个没啥技巧(当然可能是我技艺不精,有更好的方法欢迎留言~)

image-20211203192054983

到这里我们可以写一下代码来验证一下到目前为止所分析的内容。先说一下思路哈:

首先我们生成一下两个随机数,如果在一个字节所能表示的数范围内满足公式:

( ( k[0]\^ k[6]\^0x18 + 0x3D)\^0xA7) & 0xFF > 0

那么就将这两个数填充到数组下标为0和6两个元素处。同理如果满足公式

那么就将这两个随机数填充到数组下标1、2、5、7处

生成了一个试试看,能不能绕过那几个跳转

image-20211203200138104

image-20211203200333201

image-20211203200506227

昨天只是根据假定k[3]的值为0x9C,然后根据这个大前提来通过生成随机数来穷举符合k[0]和k[6],k[1]、k[2]、k[5]、k[7]之间关系的字符序列,从而能够通过一些列验证。

今天我们继续完成关键算法的分析并能实现生成能用的用户名和序列号!

image-20211204084728367

接着往下走,发现有一个局部变量传递给了寄存器并且入栈了,下面就是一个字符串转换的函数,那么就可以认为这个函数的传入和传出参数是同一个,我们可以观察一下这个寄存器的值的变化。而且紧跟着ECX保存的是之前一直没有出现的用户名的地址,这个函数就是将用户名转换一下吗?我们看一下:

image-20211204085523325

image-20211204085606805

image-20211204085700708

就是我们输入的用户名,可是第一个字符出现的地方和0x559B878多了个0x10啊,而且前面多的这些数字是干什么用的?或者说有作用吗?我们慢慢分析。只知道现在我们的用户名在这里被改成了ASCII字符串。

image-20211204085809795

image-20211204090235303

image-20211204090259369

image-20211204090501757

可见这个[EDI+0x20]原封不动的传递了下来,现在要将它入栈准备给下一个call调用了,接着看下面的语句

image-20211204090858588

image-20211204091119931

然后就碰到了一个没见过的指令,SETNE是干啥的?别慌我们查查:

image-20211204091232497

还是不太懂,算了我们看寄存器的变化吧

image-20211204091441283

看起来好像是把EAX从0变成了1

image-20211204091531981

还是不懂这条指令是干什么的,百度,百度,百度!!!重要的事情说三遍:

image-20211204093001095

看起来似乎是将ZF的值经过什么判断后赋给寄存器,我们写个小demo调试一下看看吧:

先是看看在ZF为1的情况下,SETcc咋用:关注EAX的变化!!!

image-20211204093324827

image-20211204093524119

image-20211204093618497

image-20211204093756598

image-20211204093901481

image-20211204093950896

总结一下:

回到我们的分析流上来:

image-20211204094635631

image-20211204094744865

然后紧接着就把用户名字符串给入栈然后调用下一个call,莫非这个call对用户名做了什么处理?老样子我们先不进去看它的处理过程,我们观察返回值,也就是EAX是什么:

可是这不是一个地址并不知道这是个啥,那没事了,我们继续往下走吧,先标注一下这个函数是对用户名做了什么处理就行。

image-20211204095257976

接下来把EDX低4个字节和[EBP-0x20]进行比较,我们可以看到数据窗口中显示这个局部变量的值此时是12

image-20211204100512287

也就是序列号下表为4的那个元素,小记一下:CMP k[4],DL

image-20211204100650234

BH是序列号下标为5的那个元素,也就是CMP k[5],CL

image-20211204101217768

同样的套路,下面几个比较都是把密码不同地方和那个函数返回值的不同字节进行比较,相同则往下继续比,不相同则跳走:

image-20211204110341551

总结一下对应关系:

也就是说k4~k7拼出了计算用户名的运算结果

继续往下看,

image-20211204104141303

image-20211204110408424

image-20211204104358848

image-20211204104611496

image-20211204110435928

还记得我们前面写的验证代码吗,函数的返回值是>0的,这里进一步缩小范围了,应该是大于或等于9,我们去把它改了:

image-20211204105814810

然后分析到这里可以发现其实用户名和序列号之间只有一个对应关系的,k4、k5、k6、k7是用户名的ASCII数组经过计算得出来的一串数据,所以在编写注册机的时候要想办法把这个对应关系给用上。

所以我们也要还原一下计算用户名的那个函数,然后分别给序列号的后四个元素赋值就行。(当然还要满足前面的条件)

我们来到计算用户名这个函数

image-20211204111138959

然后去IDA找一下,然后F5,然后COPY一下不就好了吗?

我觉得还行~

image-20211204112132916

不过还有个问题哈,就是这里频繁使用到了一个数组dword_2E64148,我们过去把它也拷贝一份:

image-20211204112440023

image-20211204112719394

当然最关键的算法可不能落下啊:

image-20211204112808131

image-20211204112925594

要想使用这个函数,光有名字可不行啊,还得知道有什么参数,这里我们去OD再看看PUSH了啥

image-20211204113214310

找一下这个ESI是什么

image-20211204113455199

image-20211204113517635

找到了,所以这个参数是0,然后后面的EAX的值是1,最后一个参数是这一长串的商

image-20211204113832186

在前面验证的过程中,我们是怎么得到这个商的呢,通过生成随机数来穷举符合公式的小于等于0x3EB的值,如图

image-20211204114007620

有个小细节哈,在IDA的翻译成C的过程中,将这个函数的第四个参数变成了char型,但是它是一个商,应该是一个数字才对

image-20211204114610807

而它这里是把结果AX(2字节)扩展成的EAX(4字节)

image-20211204114723463

这里我们不妨把第四个参数大胆改成2字节的变量

image-20211204115202524

然后就可以去编码调用一下这个函数了

image-20211204120454243

然后因为6号元素在这里已经有了确定的值,下面就不需要再去穷举算它的值了。

image-20211204120702846

还有下面的5号元素、7号元素都不用再枚举了,直接由上面计算出来就行

image-20211204120812081

image-20211204121005640

image-20211204121353598

image-20211204122232449

我们跟踪一下EAX的值的变化,到这里EAX都还是0x2D,是我们想要的值

image-20211204123431289

可是经过了call之后发现EAX突变了,变成了0x113,说明这个call里面对EAX的值进行了修改或验证

image-20211204123555524

所以我们这里要跟进这个函数看一下里面做了什么操作:

image-20211204124152686

image-20211204124313850

我们继续往下步过看看能不能通过验证:

image-20211204125015431

image-20211204124557649

很可惜还是不能,继续往下分析吧:

image-20211204125132607

image-20211204125240775

image-20211204125332185

所以这个地方有两个思路:

image-20211204130100278

image-20211204130206664

然后下面一条语句是测试EAX的值

image-20211204130332074

image-20211204130415288

如果无符号就跳走

image-20211204130537466

所以我们可以修改一下这个函数的返回值,这里我们先手动修改一下EAX位1试试,看能不能跳走

image-20211204130825804

image-20211204130943739

image-20211204131123331

image-20211204131209301

小记一下要想过掉网络验证有两个关键:

image-20211204131445652

image-20211204131516051

image-20211204131615713

image-20211204131757999

还没完,得满足堆栈平衡,我们往下翻,看看它的ret是多少:

拖了半天才找到,网络验证真复杂~

image-20211204131852786

image-20211204131944995

这样就应该没毛病了,保存修改到文件:

image-20211204132133123

我这里重新算一组新的试试看:

image-20211204132417736

image-20211204132518950

然后就能愉快的玩耍了。

然后就是将用户名转化成ascii字符串

通过一些列计算将用户名计算出一串字符,分别对应byteCode[4~7]这四个字节

调用了402E50这个函数,有四个参数,分别是用户名字符串、1、0、4083C8函数的返回值

它的返回值是4字节的一串字符UNEncode

这一串字符与序列号数组有着对应关系:

byteCode[4] = UNEncode & 0xFF

byteCode[5] = UNEncode >> 8 & 0xFF

byteCode[6] = UNEncode >> 16 & 0xFF

byteCode[7] = UNEncode >> 24 & 0xFF

判断函数00407644的返回值是否大于等于9

[ ] 当然这个软件的分析细节我还远远没有分析完,这是我一路分析下来的思路,可能有点乱,但是已经达到了我的目的,完成了破解,接下来就是注册机的编写。欢迎各路大神带着新鲜的思路前来指点。稍后奉上注册机和源码。

还是从传入的四个参数说起:[ARG.1]->用户名[ARG.2]->1[ARG.3]->0[ARG.4]->0x3E8

image-20211204162644211

image-20211204162752960

image-20211204163031533

image-20211204163058761

这个循环取出用户名每一个字节,直到0截断停止,猜测可能会有个操作时拿到长度,不然这个循环就没有意义

image-20211204163445163

果然在后面就有个检测

image-20211204164434719

image-20211204165256076

下面紧跟又一个循环

image-20211204165731474

这里有一个比较和跳转,我们跟下去看一下这个跳转是干啥的

image-20211205123449572

image-20211205123546909

这里有一些列的操作,我们留意一下这个跳转:

image-20211205123758227

注意,这里有个小细节

image-20211205130514510

然后我们往下走,分析一轮看看对这个元素做了什么操作

image-20211205131835972

image-20211205132020585

注意:第一轮的数据保存在EAX和ECX中

image-20211205132105350

我们继续跟着跳,但是到这一句的时候我就产生了不好的预感:这里我们上一轮计算的数据已经被新的元素ASCII码覆盖掉了,也就是说之前算的东西还真没有用?

image-20211205132340295

但是突然又想到部队的地方,除了这里是计算的数据之外,还有一个地方我们刚刚忽略掉了,就是这里:

image-20211205132626158

分析到这里,我们就能编码了,把这套逻辑搬运到我们的注册机里,就能完成用户名和对应序列号下标的值的运算了:
image-20211205135803396

这里有个大坑!!!

这里的寄存器乘以4的操作是因为再汇编代码里,所有的数据都是以字节为单位的,所以BYTE长度乘以4就是4字节的DWORD长度,我们在用高级语言编码的时候就不用乘以这个4了

image-20211205164040439

image-20211205164428234

这里附上代码~

image-20211205130322040

可见无论[ARG.2]是1是0,都会执行跳转下来后的这些语句,并且执行循环,最后ret出去

image-20211205123905512

至此,它的分析算是完成了~

今天在找字符串的时候留了个心眼,突然感觉没必要在验证失败这里找东西,真正的玄机不应该是在验证成功之前吗?于是我就顺着验证失败的字符串往上下看了一下,果不其然找到了一个订阅成功的字符串提示:

image-20211203092410307

然后我们双击跟进看一下,老样子还是看一下交叉引用,也是只有一个地方引用了,离真相不远了!

image-20211203092512189

来到了代码段,我们点击F5将其转化成源码(伪码)来看看这里究竟是怎么验证的。

image-20211203092655458

image-20211203092940993

image-20211203093005948

还行,看来懂点英文还是好的

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-12-5 17:02 被JokerMss编辑 ,原因: 内容更新
上传的附件:
收藏
免费 4
支持
分享
最新回复 (6)
雪    币: 2903
活跃值: (2839)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
牛掰,明天继续期待楼主的大作
2021-12-2 18:53
0
雪    币: 544
活跃值: (796)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
向大佬学习
2021-12-2 21:12
0
雪    币: 48
活跃值: (75)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
打卡
2021-12-3 08:57
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
最后那个0xdb是必须等吧
2021-12-3 10:44
0
雪    币: 957
活跃值: (2023)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
mb_lptgynwu 最后那个0xdb是必须等吧
哈哈哈,是的,昨天没有继续往下看了,今天刚刚总结了一下
2021-12-3 20:24
0
雪    币: 661
活跃值: (1193)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
我直接用的暴力破解,直接把0xDB赋值给EAX了,哈哈,毕竟我是个菜鸟
2022-1-21 11:29
0
游客
登录 | 注册 方可回帖
返回
//