请选择 进入手机版 | 继续访问电脑版
查看: 2839|回复: 9

一个Crackme的逆向分析

[复制链接]
  • TA的每日心情
    奋斗
    2017-1-16 20:03
  • 签到天数: 116 天

    [LV.6]常住居民II

    发表于 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

    回复

    使用道具 举报

  • TA的每日心情
    无聊
    2017-9-11 15:40
  • 签到天数: 425 天

    [LV.9]以坛为家II

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

    点评

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

    使用道具 举报

  • TA的每日心情
    奋斗
    2017-1-16 20:03
  • 签到天数: 116 天

    [LV.6]常住居民II

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

    不能让你那么轻松的拿到

    点评

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

    使用道具 举报

  • TA的每日心情
    开心
    2017-10-1 06:59
  • 签到天数: 308 天

    [LV.8]以坛为家I

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

    点评

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

    使用道具 举报

  • TA的每日心情

    2020-8-11 20:00
  • 签到天数: 688 天

    [LV.9]以坛为家II

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

    我也看不懂
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2019-10-17 06:41
  • 签到天数: 182 天

    [LV.7]常住居民III

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

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

    使用道具 举报

  • TA的每日心情
    擦汗
    2019-10-17 06:41
  • 签到天数: 182 天

    [LV.7]常住居民III

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

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

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

    点评

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

    使用道具 举报

  • TA的每日心情
    擦汗
    2019-10-17 06:41
  • 签到天数: 182 天

    [LV.7]常住居民III

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

    点评

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

    使用道具 举报

  • TA的每日心情
    奋斗
    2017-1-16 20:03
  • 签到天数: 116 天

    [LV.6]常住居民II

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

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

    使用道具 举报

  • TA的每日心情
    奋斗
    2017-1-16 20:03
  • 签到天数: 116 天

    [LV.6]常住居民II

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

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

    使用道具 举报

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

    本版积分规则

    快速回复 返回顶部 返回列表