currwin 发表于 2014-6-13 18:32

[CM分析]新手CM 无壳无花 业界良心 的分析

本帖最后由 currwin 于 2014-6-13 18:31 编辑

【破解作者】currwin(F8LEFT)【联系方式】QQ:不能留Q来着。。。。【作者主页】不好说啊【破解工具】OllyDbg 1.1、IDE【破解声明】师傅写的cm,不敢不玩,不过又不会玩。唉,只能写一点小小的分析了,如果有错的话不要介意啊
【软件名称】新手CM 无壳无花 业界良心【下载地址】http://www.52pojie.cn/forum.php? ... 9&page=1#pid5883935【加壳信息】VC【软件简介】一个很有趣的cm,带了一点掩眼法,需要多加一点心思才能搞定的啊
      一个小小的cm,无壳无花,代码一目了然,只是可以参考的字符串不多,但还是有的,我们右键查找一下ASCII,会找到几条有趣的中文提示:
00401759   .68 F48B4300   push 无壳无花.00438BF4                     ;用户名和序列号不可为空!;判断输入的数据是否为空00401712   .68 D08B4300   push 无壳无花.00438BD0                     ; |c:\reg.ini00401741   .68 DC8B4300   push 无壳无花.00438BDC                     ;验证通过,请重启软件!    ;要求重启程序进行验证
    估计这附近就是关键的比较的地方了,我们先来猜测一下程序的验证流程:输入数据->验证数据->保存数据->重启验证。   应该就是这么几点的,于是我们就来在这一段的段首下段,输入好信息后,慢慢跟踪吧。   段首为 00401610,下F2断点,点注册,然后慢慢跟踪,确定每一个函数的大致作用
00401610   .6A FF         push -0x1
00401612   .68 90264300   push 无壳无花.00432690
         .......            ;此处省略掉无关的代码
00401693   .8D4C24 0C   lea ecx,dword ptr ss:
00401697   .51            push ecx
00401698   .8D8E D0000000 lea ecx,dword ptr ds:
0040169E   .C64424 24 01mov byte ptr ss:,0x1
004016A3   .E8 C6870000   call <无壳无花.Edit_GetText>               ;GetPass
004016A8   .8D5424 10   lea edx,dword ptr ss:
004016AC   .52            push edx
004016AD   .8D8E 78010000 lea ecx,dword ptr ds:
004016B3   .E8 B6870000   call <无壳无花.Edit_GetText>               ;GetName
004016B8   .8B4424 0C   mov eax,dword ptr ss:
004016BC   .68 678F4300   push 无壳无花.00438F67                     ;push NULL
004016C1   .50            push eax                                 ;push Pass
004016C2   .C74424 1C 000>mov dword ptr ss:,0x0
004016CA   .E8 FCDB0100   call <无壳无花.strcmp>                     ;把Pass与空字符串比较,确定Pass是否为空
004016CF   .83C4 08       add esp,0x8
004016D2   .85C0          test eax,eax
004016D4   .0F95C0      setne al
004016D7   .84C0          test al,al
004016D9   .74 7A         je short 无壳无花.00401755                   ;为空的话,跳向错误提示
004016DB   .8B4C24 10   mov ecx,dword ptr ss:
004016DF   .68 678F4300   push 无壳无花.00438F67                     ;PUSH NULL
004016E4   .51            push ecx                                 ;push Name
004016E5   .E8 E1DB0100   call <无壳无花.strcmp>                     ;吧Name与空字符串比较,确定Name是否为空
004016EA   .83C4 08       add esp,0x8
004016ED   .85C0          test eax,eax
004016EF   .0F95C0      setne al
004016F2   .84C0          test al,al
004016F4   .74 5F         je short 无壳无花.00401755                   ;为空的话跳向失败
004016F6   .8B5424 0C   mov edx,dword ptr ss:         ;取Pass的长度
004016FA   .837A F4 14    cmp dword ptr ds:,0x14          ;与0x14比较,如果Pass的长度不够0x14,则跳向失败
004016FE   .75 65         jnz short 无壳无花.00401765                  ;Pass长度为 0x14
00401700   .6A 00         push 0x0                                 ; /hTemplateFile = NULL
00401702   .68 80000000   push 0x80                              ; |Attributes = NORMAL
00401707   .6A 02         push 0x2                                 ; |Mode = CREATE_ALWAYS
00401709   .6A 00         push 0x0                                 ; |pSecurity = NULL
0040170B   .6A 02         push 0x2                                 ; |ShareMode = FILE_SHARE_WRITE
0040170D   .68 00000040   push 0x40000000                        ; |Access = GENERIC_WRITE
00401712   .68 D08B4300   push 无壳无花.00438BD0                     ; |c:\reg.ini
00401717   .FF15 9C324300 call dword ptr ds:[<&KERNEL32.CreateFile>; \CreateFileA
0040171D   .8BF8          mov edi,eax                              ;下面就对文件C:\reg.ini开始写入注册信息。
0040171F   .6A 00         push 0x0                                 ; /pOverlapped = NULL
00401721   .8D4424 18   lea eax,dword ptr ss:          ; |
00401725   .50            push eax                                 ; |pBytesWritten
00401726   .8B4424 14   mov eax,dword ptr ss:          ; |
0040172A   .8B48 F4       mov ecx,dword ptr ds:         ; |
0040172D   .51            push ecx                                 ; |nBytesToWrite
0040172E   .50            push eax                                 ; |Buffer
0040172F   .57            push edi                                 ; |hFile
00401730   .FF15 98324300 call dword ptr ds:[<&KERNEL32.WriteFile>>; \WriteFile
00401736   .57            push edi                                 ; /hObject
00401737   .FF15 94324300 call dword ptr ds:[<&KERNEL32.CloseHandl>; \CloseHandle
0040173D   .6A 00         push 0x0
0040173F   .6A 00         push 0x0
00401741   .68 DC8B4300   push 无壳无花.00438BDC                     ;验证通过,请重启软件!
00401746   .8BCE          mov ecx,esi
00401748   .E8 E3640000   call 无壳无花.00407C30                     ;信息写入后退出程序
0040174D   .6A 00         push 0x0                                 ; /ExitCode = 0x0
0040174F   .FF15 90324300 call dword ptr ds:[<&KERNEL32.ExitProces>; \ExitProcess
00401755   >6A 00         push 0x0                                 ;验证失败则继续循环
00401757   .6A 00         push 0x0
00401759   .68 F48B4300   push 无壳无花.00438BF4                     ;用户名和序列号不可为空!
0040175E   .8BCE          mov ecx,esi
00401760   .E8 CB640000   call 无壳无花.00407C30
00401765   >C64424 20 00mov byte ptr ss:,0x0
0040176A   .8B4424 10   mov eax,dword ptr ss:
         ...............;此处省略掉无关的代码
004017C0   .83C4 18       add esp,0x18
004017C3   .C3            retn


    这样注册的流程就一目了然了,①首先,读取Name与Pass,把他们与Null比较,如果其中有一个为空,则提示失败②接着把Pass的长度与0x14比较,如果不等,则继续要求用户重新输入③最后把读取到的信息保存到文件c:\reg.ini里面,待重启程序后继续重新进行验证。    于是,我们的目标也同样是非常明确的了。①重启程序,并对CreateFileA之类的函数下断点。②找到程序验证序列号的地方,并且找出他验证的方式③爆破掉关键的地方,从而达到破解的目的。
    重启程序,对文件操作一类的函数下断点,然后F9运行。那么,自然的,程序就停下来了。。。。。。。才对的。但是神奇的一幕出现了,到注册框出现以前,程序并没有调用到对文件操作的函数!!!    这下我可郁闷了,没办法,用程序监视一类的软件监视这个软件,果然,还是没有出现对reg.ini这个文件进行操作的记录。    也就是说,这个程序压根就没有想过读取这一个文件!!!!!    我哪个去,这又是为啥啊,的确是挺让人郁闷的,这让小菜该怎么办啊。
    实在是没有思路了,于是我就从程序初始化到程序界面出来为止跟了一下,发现了一个非常有趣的地方。
004012F0   .56            push esi
004012F1   .57            push edi
004012F2   .8B7C24 0C   mov edi,dword ptr ss:         ;初始化各项
004012F6   .8BF1          mov esi,ecx
004012F8   .8D46 78       lea eax,dword ptr ds:
004012FB   .50            push eax
004012FC      68 EC030000   push 0x3EC
00401301   .57            push edi
00401302   .E8 33BE0000   call <无壳无花.CreateControl>                ;注册button
00401307   .8D8E D0000000 lea ecx,dword ptr ds:
0040130D   .51            push ecx
0040130E   .68 EB030000   push 0x3EB
00401313   .57            push edi
00401314   .E8 21BE0000   call <无壳无花.CreateControl>                ;Edit
00401319   .8D96 24010000 lea edx,dword ptr ds:
0040131F   .52            push edx
00401320   .68 ED030000   push 0x3ED
00401325   .57            push edi
00401326   .E8 0FBE0000   call <无壳无花.CreateControl>                ;注册button
0040132B   .8D86 78010000 lea eax,dword ptr ds:
00401331   .50            push eax
00401332   .68 EE030000   push 0x3EE
00401337   .57            push edi
00401338   .E8 FDBD0000   call <无壳无花.CreateControl>                ;Edit
0040133D   .81C6 CC010000 add esi,0x1CC
00401343   .56            push esi
00401344   .6A 02         push 0x2
00401346   .57            push edi
00401347   .E8 EEBD0000   call <无壳无花.CreateControl>                ;static
0040134C   .5F            pop edi
0040134D   .5E            pop esi
0040134E   .C2 0400       retn 0x4



    这一段是程序初始化各个控件的地方。这倒是没有什么关系,不过认真想了一下,就会发现有特别的地方了!
    首先是两个Edit框,这倒是没有问题,一个用来输入Name,一个用来输入Pass。
    问题就在于它创建了两个 按钮 button。
    这不是很奇怪吗,为什么要创建两个呢?现在,我们来做一下假设,在MFC中,假如一个按钮button只能对应一个按钮事件,那么两个button不就对应了两个按钮事件吗?
    假如每一个按钮事件对应了一种验证方式,那么不就有两种不同的验证方式了吗?
    假如我们前面看到的验证方式是其中一种,那么另外一种是什么呢?话说回来,到底是谁规定了前面给出的验证方式就一定是正确的呢?如果前面那个是一个陷阱,那么不就表明另外一个按钮事件才是真正的验证地方吗?
    所以,基于这样一个想法,我就来寻找一种激活另外一个按钮的按钮事件的方法。

    寻找的方法有很多,我就不一一分析了。我说一下我的思路吧:
    程序肯定会有判断倒是是激活哪一个按钮的机制。有可能是时钟,不过这个程序并没有时钟,所以这个Pass。
    有可能是根据Name与Pass的数据来修改,所以我就对读取信息的相关函数下段了,就是前面我下过标签的Edit_GetText函数了,具体的位置在00409E6E,作用就是获取Edit框的文本数据了。
    断点下好后,输入Name,然后切换到Pass的输入时,OD里面停下来了。原来是根据Name_Edit的输入焦点消失的时候才会激活的事件。怪不得程序要使用WH_CBT钩子了。
    我们来看一下那个函数:


004017D0   .6A FF         push -0x1                              ;NameEdit焦点消失
         ...............;继续省略一堆数据
0040181E   .8D4C24 08   lea ecx,dword ptr ss:
00401822   .51            push ecx
00401823   .8D8E 78010000 lea ecx,dword ptr ds:
00401829   .C74424 18 000>mov dword ptr ss:,0x0
00401831   .E8 38860000   call <无壳无花.Edit_GetText>               ;GetName
00401836   .8B5424 08   mov edx,dword ptr ss:
0040183A   .52            push edx
0040183B   .E8 E7D50100   call <无壳无花.strtoxl>                      ;把Name(数字字符串)转换为数字
00401840   .83C4 04       add esp,0x4
00401843   .3D DE070000   cmp eax,0x7DE
00401848   .75 17         jnz short 无壳无花.00401861                  ;如果等于7DE(2014)则不会跳转
0040184A   .6A 01         push 0x1
0040184C   .8D8E 24010000 lea ecx,dword ptr ds:
00401852   .E8 FFA40000   call <无壳无花.ShwoWindow>                   ;使隐藏的按钮显示出来
00401857   .6A 00         push 0x0
00401859   .8D4E 78       lea ecx,dword ptr ds:
0040185C   .E8 F5A40000   call <无壳无花.ShwoWindow>                   ;并且隐藏原来的按钮
00401861   >C74424 14 FFF>mov dword ptr ss:,-0x1
00401869   .8B4424 08   mov eax,dword ptr ss:
      .................;省略一堆代码
00401896   .83C4 10       add esp,0x10
00401899   .C3            retn



    很显然了,当NameEdit控件的焦点消失的时候,读取Name,并且判断Name是否为数字2014,如果是的话,就显示隐藏的按钮,并且隐藏原来的按钮。如果不是的话,就什么都不做。
    这不就是我一直在寻找的地方吗?赶快在Name中输入2014,然后切换代码吧,这样正确的注册按钮与正确的验证机制就会出来了。这样一来,输入完Pass后,点注册,就能来的正确的注册地方了。


004018A0   .6A FF         push -0x1
         ........;看我省略一堆代码
004018F0   .8D4C24 08   lea ecx,dword ptr ss:
004018F4   .51            push ecx
004018F5   .8D8E D0000000 lea ecx,dword ptr ds:
004018FB   .C74424 20 000>mov dword ptr ss:,0x0
00401903   .E8 66850000   call <无壳无花.Edit_GetText>               ;GetPass
00401908   .8B4C24 08   mov ecx,dword ptr ss:
0040190C   .8B41 F4       mov eax,dword ptr ds:         ;取得Pass的长度
0040190F   .83F8 0A       cmp eax,0xA                              ;用长度与0xa(10)比较
00401912   .0F85 84000000 jnz 无壳无花.0040199C                        ;不等于则跳向失败
00401918   .51            push ecx
00401919   .E8 09D50100   call <无壳无花.strtoxl>                      ;把Pass(数字的Str)转换为整形数据
0040191E   .83C4 04       add esp,0x4
00401921   .3D 3F420F00   cmp eax,0xF423F                        ;与0xF423F (999999)比较
00401926   .7E 74         jle short 无壳无花.0040199C                  ;比这个小的话就跳向失败
00401928   .6A 00         push 0x0
0040192A   .6A FF         push -0x1
0040192C   .8BCE          mov ecx,esi
0040192E   .E8 C3A20000   call <无壳无花.GetDlgItem>                   ;获取图标控件的hwnd
00401933   .8BC8          mov ecx,eax
00401935   .E8 1CA40000   call <无壳无花.ShwoWindow>
0040193A   .C74424 10 000>mov dword ptr ss:,0x0
00401942   .C74424 0C 044>mov dword ptr ss:,无壳无花.00434D04 ;0辕
0040194A   .C64424 1C 01mov byte ptr ss:,0x1
0040194F   .E8 21B70000   call 无壳无花.0040D075
00401954   .8B40 0C       mov eax,dword ptr ds:         ;加载正确的图片
00401957   .68 86000000   push 0x86                              ; /RsrcName = 134.
0040195C   .50            push eax                                 ; |hInst
0040195D   .FF15 70344300 call dword ptr ds:[<&USER32.LoadBitmapA>>; \LoadBitmapA
00401963   .50            push eax
00401964   .8D4C24 10   lea ecx,dword ptr ss:
00401968   .E8 20CD0000   call 无壳无花.0040E68D
0040196D   .8B5424 10   mov edx,dword ptr ss:
00401971   .8B86 EC010000 mov eax,dword ptr ds:
00401977   .52            push edx                                 ; /lParam
00401978   .6A 00         push 0x0                                 ; |wParam = 0x0
0040197A   .68 72010000   push 0x172                               ; |Message = STM_SETIMAGE
0040197F   .50            push eax                                 ; |hWnd
00401980   .FF15 5C344300 call dword ptr ds:[<&USER32.SendMessageA>; \SendMessageA
00401986   .C64424 1C 00mov byte ptr ss:,0x0
0040198B   .8D4C24 0C   lea ecx,dword ptr ss:
0040198F   .C74424 0C 044>mov dword ptr ss:,无壳无花.00434D04 ;0辕
00401997   .E8 54010000   call 无壳无花.00401AF0
0040199C   >C74424 1C FFF>mov dword ptr ss:,-0x1         ;失败
      ........;不重要的代码就不贴了
004019D1   .83C4 18       add esp,0x18
004019D4   .C3            retn



      这样,真正的验证段又出现了。
      首先,获取Pass,然后用长度与0xA(10)比较。不等于的话就跳向失败。
      然后,把Pass的字符串数据转换为整形数据,并且与0xF423F(999999)比较,比他小的话就跳向失败。
      所以,输入1000000以上的10位数字就会成功的了,大家玩起吧。我就狠心一点,直接爆菊了。



版权声明:本文由F8LEFT原创与52pojie论坛,转载请保留以上信息

PS:很久没有玩过游戏了,手痒了,赶紧玩去了。各位如果觉得我写的文章有趣的话,请多多回复吧,不要老是潜水了啊。。。。。花费你们几分钟时间打几个字应该不过分吧?这篇破文我可是写了1个小时有多的的呵。

灵光丶Fiycix 发表于 2014-6-13 18:49

本帖最后由 qq516145704 于 2014-6-13 19:45 编辑

我不是这么分析的,但最后一样成功了

大概就是分析到有两个按钮.然后就用彗星小助手显示隐藏控件了.

万年的胡杨 发表于 2014-6-13 18:57

不明觉厉。。。。。。

rain灿 发表于 2014-6-13 19:05

感谢师父

心断空 发表于 2014-6-13 19:30

我F8大大果然屌

lwj一辈子 发表于 2014-6-13 21:42

F8学编程去了,就是牛

wssb999 发表于 2014-6-14 08:34

师傅就是叼 两个按钮都看出了{:1_909:}
页: [1]
查看完整版本: [CM分析]新手CM 无壳无花 业界良心 的分析