查看: 20573|回复: 9

一个Crackme的逆向分析

[复制链接]
发表于 2016-3-27 02:11:16 | 显示全部楼层 |阅读模式
===================================

          红客联盟&Milw0rm有奖活动

===================================

        看90办个活动不容易,发个帖子表示支持.
        这只是个简单的CrackMe,不加壳,属于那种只要会用OD和IDA就能分析的东西,但是既然把这个过程发出来,表示它还是有一点值得学习的东西在里面.
        废话就不多说了,先运行一遍软件,发现直接弹出个“注册失败”的对话框,然后用IDA加载,发现这个CM代码不多,而“注册失败”字符差串则是保存在Text中,选择Text,在键盘上按下“x”键,在弹出来的交叉参考对话框发现有一处代码修改了这个Text的值,如下图:
1.png
        于是想到这里可能是把“注册失败”改为“注册成功”。先做个测试,用OD加载软件,按下“ctrl”+“G”,输入0x00111045(这个地址要根据实际情况来确定,这里这个CrackMe的基地址是0x00111000),按下回车,来到下图所示的位置:
15.png
        说明:上图是IDA中的位置,在OD中是0x00111045
        这里调用CreateFile函数来打开一个文件,因为目前没有这个文件,所以这个函数注定会失败,所以那个jz就会跳转.于是就在OD中把 2.png (IDA中反汇编出来的是jz,OD中是je)的跳转地址改为0x001111fd,这样当这个函数调用失败后,程序就直来跳转到修改注册结果的地方,如下图所示:
3.png
        上图所示的是程序中“注册失败”改为“注册成功”的地方,在这里我们看到是用edx+eax+0x23e525dc,然后将这个结果写入到字符串中,我们先猜一下这个结果可能就是字符串“成功”的值,通过测试可知“成功”的值为0xa6b9c9b3,我们就先把ecx的值改为0xa6b9c9b3,把edx的值改为4(“注册”占4个字节),然后按F8,发现程序跑到 4.png 这里就跑飞了,说明现在这个内存是不能改写的。不过既然要注册成功,那么这个内存就应该是要能改写的,这就说明前面有地方要修改这个内存地址的属性。
根据IDA中Text的交叉参考可知,除了这里和MessageBox用到了Text之外,还有一处地方用到这个地址,如下图:
5.png
        在这里把Text当成一个参数传进函数中,而这个函数的参数显然不是MessageBox,这时就想到这里把Text的属性改了,这样后面才能改写Text。而能修改内存属性的API,VirtualProtect就跳出来了,而且上面这个函数的参数和VirtualProtect一样,基本上能确定这里就是调用了VirtualProtect。只不过这里是一个函数指针,这个指针由上面得来:
6.png
        这里需要确定这个模块名和函数名的来源,继续向上看发现:
7.png
8.png
        在往上看发现这个buffer中的数据是从一个名为“CrackMeA.key”的文件中读取出来的:
9.png
        这样我们就可以知道,这个CM需要个注册文件,而这个注册文件中的第一个字符串是一个模块名,第二个字符串是一个函数名。然后继续分析接下来需要的一些数据。
10.png
        上图显示的是注册成功要用到的一些数据, 在这里把数据都取出来了之后,接下来就是校验了。先判断生成“成功”的值是否为空,通过上图可知,除了这两个值外,校验中还用到了另外两个值,所以在接下来的程序中,就先要判断这四个值是否为空,若是有一个为空则注册失败,代码如下:
11.png
        上面这里检测完了之后,栈中的数据如下图所示:
12.png
        接下来再进行一次压栈操作,代码如下:
13.png
        这时栈中的数据如下图:
14.png
        然后是注册验证,代码如下:
00E111AE  |.  D9CF          fxch st(7)                               ;  交换st0与st7的值,这时st0为第二组值的长度
00E111B0  |.  DECD          fmulp st(5),st                           ;  fLen1 * fLen2,结果保存在st4
00E111B2  |.  D9CB          fxch st(3)
00E111B4  |.  DECD          fmulp st(5),st                           ;  fStr1 * fStr2,结果保存在st4,之前st4的值上移到st3
00E111B6  |.  D9CB          fxch st(3)
00E111B8  |.  DEC4          faddp st(4),st                           ;  fStr1 * fStr2 + fLen1 * fLen2,结果保存在st3
00E111BA  |.  D9C4          fld st(4)                                ;  压入fStr1
00E111BC  |.  DECD          fmulp st(5),st                           ;  st5中的值也是fStr1,所以这里是fStr1 * fStr1,结果保存在st4
00E111BE  |.  D9C1          fld st(1)                                ;  将fLen1压栈
00E111C0  |.  DECA          fmulp st(2),st                           ;  st2也是fLen1,这里是fLen1 * fLen1,结果保存在st1
00E111C2  |.  D9CC          fxch st(4)
00E111C4  |.  DEC1          faddp st(1),st                           ;  这里是fStr1 * fStr1 + fLen1 * fLen1
00E111C6  |.  D9C1          fld st(1)
00E111C8  |.  DECA          fmulp st(2),st                           ;  这里是fStr2 * fStr2
00E111CA  |.  D9C3          fld st(3)
00E111CC  |.  DECC          fmulp st(4),st                           ;  这里是fLen2 * fLen2
00E111CE  |.  D9C9          fxch st(1)
00E111D0  |.  DEC3          faddp st(3),st                           ;  fStr2 * fStr2 + fLen2 * fLen2
00E111D2  |.  DECA          fmulp st(2),st                           ;  (fStr1 * fStr1 + fLen1 * fLen1) * (fStr2 * fStr2 + fLen2 * fLen2)
00E111D4  |.  DCC8          fmul st,st                               ;  (fStr1 * fStr2 + fLen1 * fLen2) * (fStr1 * fStr2 + fLen1 * fLen2)
00E111D6  |.  DEE9          fsubp st(1),st                           ;  (fStr1 * fStr1 + fLen1 * fLen1) * (fStr2 * fStr2 + fLen2 * fLen2) - (fStr1 * fStr2 + fLen1 * fLen2) * (fStr1 * fStr2 + fLen1 * fLen2)
00E111D8  |.  DC25 C0C2E100 fsub qword ptr ds:[E1C2C0]               ;  将上面得到的结果减去1,这里和接下去的这几行代码是判断上面的结果是否为1
00E111DE  |.  D95C24 10     fstp dword ptr ss:[esp+10]
00E111E2  |.  D94424 10     fld dword ptr ss:[esp+10]
00E111E6  |.  D9E1          fabs
00E111E8  |.  D95C24 10     fstp dword ptr ss:[esp+10]
00E111EC  |.  D94424 10     fld dword ptr ss:[esp+10]
00E111F0  |.  DC1D B8C2E100 fcomp qword ptr ds:[E1C2B8]
00E111F6  |.  DFE0          fstsw ax
00E111F8  |.  F6C4 05       test ah,5
00E111FB  |.  7A 20         jpe short crackmea.00E1121D              ;  结果不为1则跳转
00E111FD  |.  8B55 00       mov edx,dword ptr ss:[ebp]
00E11200  |.  8B07          mov eax,dword ptr ds:[edi]
00E11202  |.  8D8C02 DC25E5>lea ecx,dword ptr ds:[edx+eax+23E525DC]  ;  生成“成功”字符串
00E11209  |.  8B5424 20     mov edx,dword ptr ss:[esp+20]
00E1120D  |.  898A 9CC2E100 mov dword ptr ds:[edx+E1C29C],ecx        ;  用“成功”替换“失败”


        上面这段代码中,设第一个值为发fStr1,其长度为fLen1,第二个值为fStr2,其长度为fLen2。通过上面的计算,我们可以总结出这么一个方程:
(fStr1^2 + fLen1^2)*(fStr2^2 + fLen2^2) – (fStr1*fStr2 + fLen1*fLen2)^2 = 1……(1)
fStr1 + fStr2 = 0xa6b9c9b3 - 0x23e525dc……(2)
        到这里,第一个CrackMe的分析也就结束了,要想做注册机,则在注册算法中解出上面的方程组,然后把得到的数据填入CrackMeA.key文件中的响应位置即可.
        这个CrackMe破解的关键是能不能想到用VirtualProtect这个函数来修改内存属性,从而达到破解的目的.附件中有我逆出来的代码和编写好的注册机代码,以及注册文件和原文件,想练手的同学可以玩玩.
       

CrackMe.zip

37.88 KB, 下载次数: 8, 下载积分: i币 -1

回复

使用道具 举报

发表于 2016-3-27 08:12:57 | 显示全部楼层
发威了,和我抢奖品啊

点评

不能让你那么轻松的拿到  详情 回复 发表于 2016-3-27 09:21
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-3-27 09:21:21 | 显示全部楼层
wuyan 发表于 2016-3-27 08:12
发威了,和我抢奖品啊

不能让你那么轻松的拿到

点评

抢得好!!!  详情 回复 发表于 2016-3-28 10:16
回复 支持 反对

使用道具 举报

发表于 2016-3-27 18:50:04 | 显示全部楼层
虽然我看不懂,但是为了呼应冰琥珀大牛,我决定冒个泡!

点评

我也看不懂  详情 回复 发表于 2016-3-27 21:26
回复 支持 反对

使用道具 举报

发表于 2016-3-27 21:26:56 | 显示全部楼层
sladjfksld 发表于 2016-3-27 18:50
虽然我看不懂,但是为了呼应冰琥珀大牛,我决定冒个泡!

我也看不懂
回复 支持 反对

使用道具 举报

发表于 2016-3-28 10:16:57 | 显示全部楼层
冰琥珀 发表于 2016-3-27 09:21
不能让你那么轻松的拿到

抢得好!!!
回复 支持 反对

使用道具 举报

发表于 2016-3-28 10:30:56 | 显示全部楼层
本帖最后由 小圈圈 于 2016-3-28 22:51 编辑

我想问问怎样才能防止VirtualProtect函数被修改或者删除
我现在有一个思路是母程序释放一个子保护程序互相监视VirtualProtect函数的内存,一个程序进程被结束后立即被另一个程序重启
即所谓的进程守护

//话说你这么破解让我等广大程序猿情何以堪( ⊙ o ⊙ )啊!

点评

这样没用的,VirtualProtect在kernel.dll中,每个程序都会把它加载到自己的进程空间去,你监控的也只是你自己程序进程空间中的VirtualProtect,对其他的进程没影响 要实现监控,只能注入一个线程到目标进程中,然后  详情 回复 发表于 2016-3-28 11:35
回复 支持 反对

使用道具 举报

发表于 2016-3-28 10:33:56 | 显示全部楼层
还有说有些软件会搜索OD的进程防破解,是因为动态反汇编需要运行?
那搜索C32ASM这样的静态反汇编软件是不是没什么意义?

点评

是的,OD就是动态调试,简单点的反调试,你可以对当前进程的父进程进行判断,如果父进程不是explorer.exe,就可以认为是被别的进程加载启动的  详情 回复 发表于 2016-3-28 12:01
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-3-28 11:35:07 | 显示全部楼层
小圈圈 发表于 2016-3-28 10:30
我想问问怎样才能防止VirtualProtect函数被修改或者删除
我现在有一个思路是母程序释放一个子保护程序互相 ...

这样没用的,VirtualProtect在kernel.dll中,每个程序都会把它加载到自己的进程空间去,你监控的也只是你自己程序进程空间中的VirtualProtect,对其他的进程没影响
要实现监控,只能注入一个线程到目标进程中,然后在线程里对VirtualProtect进行监控
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-3-28 12:01:38 | 显示全部楼层
小圈圈 发表于 2016-3-28 10:33
还有说有些软件会搜索OD的进程防破解,是因为动态反汇编需要运行?
那搜索C32ASM这样的静态反汇编软件是不 ...

是的,OD就是动态调试,简单点的反调试,你可以对当前进程的父进程进行判断,如果父进程不是explorer.exe,就可以认为是被别的进程加载启动的
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

旗下站点

邮箱系统

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2025-5-2 04:51 , Processed in 0.081239 second(s), 21 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部