[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个小时有多的的呵。
本帖最后由 qq516145704 于 2014-6-13 19:45 编辑
我不是这么分析的,但最后一样成功了
大概就是分析到有两个按钮.然后就用彗星小助手显示隐藏控件了.
不明觉厉。。。。。。 感谢师父
我F8大大果然屌 F8学编程去了,就是牛 师傅就是叼 两个按钮都看出了{:1_909:}
页:
[1]