从网上下载了一个DEMO版的《KTV音乐无限点歌台V8.5》的点歌软件,酷爱之,故尝试对其限制功能进行解除,查壳为PECOMPACT 2.X------基本上算最简单的压缩壳,立即脱壳、运行,发现程序能正常启动,就点歌功能不正常(出现Access violation at address 005D2052 in module 'Music_ktv_tk.exe、Query1: Cannot perform this operation on a closed dataset错误信息),最初以为是脱壳不干净,IAT被加密之类的,后来到脱壳区以及UNPACK上面去求助,得到高人指点方知系程序自校验。立即着手去除程序的自校验,由于本人才学粗浅,先潜水学习之,从论坛上看到了密界高人laomms一篇文章《常见自校检分析实例》,受益非浅(在此感谢),随即参照里面的方法进行了尝试:

1、通用对比法-----跟踪了几遍,没发现蛛丝马迹,估计比较调用很深或很远,实在雷人,暂放弃!

2、跟踪退出函数------程序校验失败后并没有退出,所以不能应用该方法。

3、利用第三方软件辅助查找关键的地方------用PEiD辅助工具KANAL查看,发现程序内使用了MD5及DES多处加密,跟踪几次,还是太累,暂放弃!

4、对付校检自身大小的软件的一般方法------使用W32DASM反汇编后,未发现直接进行文件尺寸的比较,放弃!

5、对付自校验的杀手锏 -- 偷天换日-------修改程序代码,让它去验证原程序,无论如何都是OK的,这个方法虽说有效,但是并不利于破解程序的发布,毕竟玩家都不太喜欢程序被强迫安装到固定的目录下,而且还必须与破解前的程序“和平共处”。

首次调试程序时采用了“偷天换日”的方法来去除自校验,总觉得不够完美,后来调试程序的时候还发现一个怪事,程序里面的加密字符串也进行了自校验,一但修改就OVER,郁闷之至!总结上一次追踪“校验比较”失败的经过,基本确定程序的自校验不属于加壳程序(PECOMPACT)所为,校验失败后也不退出程序,只是对程序进行出错处理(完全受作者的摆布),猜测程序自校验与功能限制有着某种联系,好了让我们一起来看看去除双重校验的全过程:

还是从下断“bp CreateFileA”,开始,F9运行,程序断在这里:

7C801A28 k>  8BFF          mov edi,edi              //系统空间,F8单步跟踪几步  
7C801A2A     55            push ebp
7C801A2B     8BEC          mov ebp,esp
7C801A2D     FF75 08       push dword ptr ss:[ebp+8]
7C801A30     E8 CFC60000   call kernel32.7C80E104
7C801A35     85C0          test eax,eax
7C801A37     74 1E         je short kernel32.7C801A57
========
F8跟踪几步(或ALT+F9)后返回程序空间:
005DCFCD     E8 3EA3E2FF   call <jmp.&kernel32.CreateFileA>       //这就是刚才读文件中断所在的系统调用
005DCFD2     8945 F4       mov dword ptr ss:[ebp-C],eax           //返回到这里
005DCFD5     837D F4 FF    cmp dword ptr ss:[ebp-C],-1
005DCFD9     0F84 D6000000 je Music_kt.005DD0B5
005DCFDF     33C0          xor eax,eax
005DCFE1     55            push ebp
005DCFE2     68 AED05D00   push Music_kt.005DD0AE
005DCFE7     64:FF30       push dword ptr fs:[eax]
005DCFEA     64:8920       mov dword ptr fs:[eax],esp
005DCFED     6A 00         push 0
005DCFEF     6A 00         push 0
005DCFF1     6A 00         push 0
005DCFF3     6A 02         push 2
005DCFF5     6A 00         push 0
005DCFF7     8B45 F4       mov eax,dword ptr ss:[ebp-C]
005DCFFA     50            push eax
005DCFFB     E8 18A3E2FF   call <jmp.&kernel32.CreateFileMappingA>   //读取文件在内存中映射,以便计算
005DD000     8945 F0       mov dword ptr ss:[ebp-10],eax
005DD003     837D F0 00    cmp dword ptr ss:[ebp-10],0
005DD007     0F84 8A000000 je Music_kt.005DD097
005DD00D     33C0          xor eax,eax
005DD00F     55            push ebp
005DD010     68 90D05D00   push Music_kt.005DD090
005DD015     64:FF30       push dword ptr fs:[eax]
005DD018     64:8920       mov dword ptr fs:[eax],esp
005DD01B     6A 00         push 0
005DD01D     6A 00         push 0
005DD01F     6A 00         push 0
005DD021     6A 04         push 4
005DD023     8B45 F0       mov eax,dword ptr ss:[ebp-10]
005DD026     50            push eax
005DD027     E8 4CA5E2FF   call <jmp.&kernel32.MapViewOfFile>      //查看文件映射 
005DD02C     8945 EC       mov dword ptr ss:[ebp-14],eax
005DD02F     837D EC 00    cmp dword ptr ss:[ebp-14],0
005DD033     74 44         je short Music_kt.005DD079
005DD035     33C0          xor eax,eax
005DD037     55            push ebp
005DD038     68 72D05D00   push Music_kt.005DD072
005DD03D     64:FF30       push dword ptr fs:[eax]
005DD040     64:8920       mov dword ptr fs:[eax],esp
005DD043     6A 00         push 0
005DD045     8B45 F4       mov eax,dword ptr ss:[ebp-C]
005DD048     50            push eax
005DD049     E8 02A4E2FF   call <jmp.&kernel32.GetFileSize>         //取得文件尺寸
005DD04E     8BC8          mov ecx,eax
005DD050     8D45 94       lea eax,dword ptr ss:[ebp-6C]
005DD053     8B55 EC       mov edx,dword ptr ss:[ebp-14]
005DD056     E8 C5FDFFFF   call Music_kt.005DCE20                                   
005DD05B     33C0          xor eax,eax
005DD05D     5A            pop edx
005DD05E     59            pop ecx
005DD05F     59            pop ecx
005DD060     64:8910       mov dword ptr fs:[eax],edx
005DD063     68 79D05D00   push Music_kt.005DD079
005DD068     8B45 EC       mov eax,dword ptr ss:[ebp-14]
005DD06B     50            push eax
005DD06C     E8 9FA5E2FF   call <jmp.&kernel32.UnmapViewOfFile>     //关闭文件映射
005DD071     C3            retn
========
到这里程序已获取了文件的基本信息,并向上层逐步返回,一路跟踪来到这里:
005DD214     E8 6FFDFFFF   call Music_kt.005DCF88                   //这是刚才中断调用的CALL
005DD219     8D45 EC       lea eax,dword ptr ss:[ebp-14]            //返回到这里
005DD21C     8BD3          mov edx,ebx
005DD21E     E8 C1FEFFFF   call Music_kt.005DD0E4                   //计算文件的MD5值
005DD223     33C0          xor eax,eax
========
这时查看堆栈区:
0012FCBC  |00D82544  ASCII "C:\Program Files\Music_KTV_DJB\Music_ktv_tk-1.exe"     //程序名
0012FCC0  |00D826F0  ASCII "d5ced6890a02d58b280add2bf46eadbc"       //文件MD5加密后的代码
========
算得MD5后继续向上层返回,来到这里:
005DEC42     E8 A5E5FFFF   call Music_kt.005DD1EC                   //这是刚才调用文件校验的主CALL
005DEC47     8D85 BCFEFFFF lea eax,dword ptr ss:[ebp-144]           //返回到这里
005DEC4D     50            push eax
========
到这里发现程序有很长的一系列调用,向上看看程序做了些什么事,我们在005DEBD7处F2下断,重新加载程序,F9:
005DEBD7     50            push eax                                 //断在这里
005DEBD8     8D95 C4FEFFFF lea edx,dword ptr ss:[ebp-13C]
005DEBDE     A1 B46C5F00   mov eax,dword ptr ds:[5F6CB4]
005DEBE3     8B00          mov eax,dword ptr ds:[eax]
005DEBE5     8B80 F4030000 mov eax,dword ptr ds:[eax+3F4]
005DEBEB     8B80 80000000 mov eax,dword ptr ds:[eax+80]
005DEBF1     E8 52ABE2FF   call Music_kt.00409748
005DEBF6     8B85 C4FEFFFF mov eax,dword ptr ss:[ebp-13C]
005DEBFC     8D95 C8FEFFFF lea edx,dword ptr ss:[ebp-138]
005DEC02     E8 89E5FFFF   call Music_kt.005DD190                    //对标识符“DEMO SOFTWARE”进行MD5加密
005DEC07     8B85 C8FEFFFF mov eax,dword ptr ss:[ebp-138]
005DEC0D     B9 08000000   mov ecx,8
005DEC12     BA 08000000   mov edx,8
005DEC17     E8 A861E2FF   call Music_kt.00404DC4
005DEC1C     8D45 F4       lea eax,dword ptr ss:[ebp-C]
005DEC1F     E8 805CE2FF   call Music_kt.004048A4
005DEC24     8D95 B8FEFFFF lea edx,dword ptr ss:[ebp-148]
005DEC2A     A1 5C715F00   mov eax,dword ptr ds:[5F715C]
005DEC2F     8B00          mov eax,dword ptr ds:[eax]
005DEC31     E8 8A8DEAFF   call Music_kt.004879C0
005DEC36     8B85 B8FEFFFF mov eax,dword ptr ss:[ebp-148]
005DEC3C     8D95 BCFEFFFF lea edx,dword ptr ss:[ebp-144]
005DEC42     E8 A5E5FFFF   call Music_kt.005DD1EC                      //调用文件校验的主call,并取得MD5值,也是我们刚才跟到的地方
005DEC47     8D85 BCFEFFFF lea eax,dword ptr ss:[ebp-144]
005DEC4D     50            push eax
005DEC4E     8D95 B0FEFFFF lea edx,dword ptr ss:[ebp-150]
005DEC54     A1 886F5F00   mov eax,dword ptr ds:[5F6F88]
005DEC59     8B00          mov eax,dword ptr ds:[eax]
005DEC5B     E8 E8AAE2FF   call Music_kt.00409748
005DEC60     8B85 B0FEFFFF mov eax,dword ptr ss:[ebp-150]
005DEC66     8D95 B4FEFFFF lea edx,dword ptr ss:[ebp-14C]              //载入程序一个关键代码(该代码系SQL语句经DES加密后的密文)
005DEC6C     E8 1FE5FFFF   call Music_kt.005DD190                      //计算关键代码的MD5值
005DEC71     8B95 B4FEFFFF mov edx,dword ptr ss:[ebp-14C]
005DEC77     58            pop eax
005DEC78     E8 EF5EE2FF   call Music_kt.00404B6C                      //把文件校验码MD5与关键代码MD5加密后的代码进行连接
005DEC7D     8B85 BCFEFFFF mov eax,dword ptr ss:[ebp-144]
005DEC83     8D95 C0FEFFFF lea edx,dword ptr ss:[ebp-140]
005DEC89     E8 02E5FFFF   call Music_kt.005DD190                      //把连接后的代码再进行MD5加密得到“07d2b16ca04461c374f9f104d2167d45”--------------怎么样,这样的做法够BT吧
005DEC8E     8B95 C0FEFFFF mov edx,dword ptr ss:[ebp-140]
005DEC94     A1 B46C5F00   mov eax,dword ptr ds:[5F6CB4]
005DEC99     8B00          mov eax,dword ptr ds:[eax]
......

对堆栈区的观察如下:
0012FCB4   00D82720  ASCII "1432CCA3CF7287C5C2740E93781B7388743406C8BF2F1F8BE56B6C026F21849795AC278931E149FECD888BAF> //关键代码
0012FCB8   00D82A9C  ASCII "895932964c568fb566509ce3c7d51de9"  //“关键代码”MD5加密后得到的代码
0012FCBC   00D82544  ASCII "C:\Program Files\Music_KTV_DJB\Music_ktv_tk-1.exe"  //所调试的程序
0012FCC0   00D82ACC  ASCII "d5ced6890a02d58b280add2bf46eadbc895932964c568fb566509ce3c7d51de9" //前半部分为所调试的程序文件MD5加密码,后半部分是“关键代码”MD5加密后得到的代码,这里进行了连接
0012FCC4   00D82C88  ASCII "07d2b16ca04461c374f9f104d2167d45"  //连接后再次进行MD5编码。
0012FCC8   00D8197C  ASCII "DEMO SoftWare"
0012FCCC   00D82514  ASCII "d824dd50127e1b3e9b8a823b79994994"   //"DEMO SoftWare"加密后的MD5,暂时没发现有何用途
=========
中途总结:以上分析是多次、反复跟踪发现的,并非偶然,最初走入了误区,一直以为是加壳程序进行了自校验,当取得文件信息后为什么始终不进行比较,也找不出在那儿进行的比较,更不理解的是为什么与还与“关键代码”进行整合(后来才发现关键代码为作者实现程序功能限制的重要SQL语句,该语句已经经过了DES加密),可见作者非常谨慎!
=========

好,继续:
分析刚才得到的最终MD5码“07d2b16ca04461c374f9f104d2167d45”,应该最终会参与比对,故迟早会用到的,对此在我们跟踪内存数据区00D82C88,并在这里下DWord的硬件访问断点,F9,断在这里:
00409765     807C1F FF 20  cmp byte ptr ds:[edi+ebx-1],20
0040976A   ^ 76 F4         jbe short Music_kt.00409760        //断点位置,F8继续跟踪
0040976C     3BF3          cmp esi,ebx
0040976E     7D 0A         jge short Music_kt.0040977A
00409770     8BC5          mov eax,ebp
00409772     E8 2DB1FFFF   call Music_kt.004048A4             //再次中断
00409777     EB 17         jmp short Music_kt.00409790        //跳
00409779     4E            dec esi
0040977A     807C37 FF 20  cmp byte ptr ds:[edi+esi-1],20
0040977F   ^ 76 F8         jbe short Music_kt.00409779
00409781     55            push ebp
00409782     8BCE          mov ecx,esi
00409784     2BCB          sub ecx,ebx
00409786     41            inc ecx
00409787     8BD3          mov edx,ebx
00409789     8BC7          mov eax,edi
0040978B     E8 34B6FFFF   call Music_kt.00404DC4
00409790     5D            pop ebp
00409791     5F            pop edi
00409792     5E            pop esi
00409793     5B            pop ebx
00409794     C3            retn                             //返回上层CALL:
========
004049B5     89D8          mov eax,ebx
004049B7     E8 E8FEFFFF   call Music_kt.004048A4
004049BC     893B          mov dword ptr ds:[ebx],edi      //来到这里
004049BE     5F            pop edi
004049BF     5E            pop esi
004049C0     5B            pop ebx
004049C1     C3            retn                            //返回上层CALL
=======
00404DE7     E8 A8FBFFFF   call Music_kt.00404994
00404DEC     EB 11         jmp short Music_kt.00404DFF     //来到这里
00404DEE     31D2          xor edx,edx
00404DF0   ^ EB E5         jmp short Music_kt.00404DD7
00404DF2     89D9          mov ecx,ebx
00404DF4   ^ EB EB         jmp short Music_kt.00404DE1
00404DF6     8B4424 08     mov eax,dword ptr ss:[esp+8]
00404DFA     E8 A5FAFFFF   call Music_kt.004048A4
00404DFF     5B            pop ebx
00404E00     C2 0400       retn 4
00404E03     C3            retn                           //返回上层CALL
========           
0040978B     E8 34B6FFFF   call Music_kt.00404DC4
00409790     5D            pop ebp                        //来到这里                  
00409791     5F            pop edi
00409792     5E            pop esi
00409793     5B            pop ebx
00409794     C3            retn                           //返回上层CALL 
========
005E70BA     E8 8926E2FF   call Music_kt.00409748
005E70BF     8B85 78FFFFFF mov eax,dword ptr ss:[ebp-88]  //返回到这里,看看堆栈区:
005E70C5     50            push eax
========
堆栈 ss:[0012FB24]=00D81FA8, (ASCII "07d2b16ca04461c374f9f104d2167d45")    //这就是刚才最终MD5码(其实在首次硬件中断后,CPU内存中也出现过的)
eax=0012FB24
========
我们往上看几行:
005E70BA     E8 8926E2FF   call Music_kt.00409748           //这行为读取刚才最终MD5值07d2b16ca04461c374f9f104d2167d45
005E70BF     8B85 78FFFFFF mov eax,dword ptr ss:[ebp-88]    
005E70C5     50            push eax
005E70C6     8D95 74FFFFFF lea edx,dword ptr ss:[ebp-8C]
005E70CC     8B45 FC       mov eax,dword ptr ss:[ebp-4]
005E70CF     8B80 04030000 mov eax,dword ptr ds:[eax+304]
005E70D5     8B80 80000000 mov eax,dword ptr ds:[eax+80]    //载入标准的MD5(未破解的MD5值dff845376ac0cf39c12e1519b5385788)
005E70DB     E8 6826E2FF   call Music_kt.00409748
005E70E0     8B95 74FFFFFF mov edx,dword ptr ss:[ebp-8C]
005E70E6     58            pop eax
005E70E7     E8 C4DBE1FF   call Music_kt.00404CB0           //比较MD5
005E70EC     74 2D         je short Music_kt.005E711B       //这就是千辛万苦寻找的关键点,不跳就OVER,把je改成JMP就一切OK
005E70EE     8B45 FC       mov eax,dword ptr ss:[ebp-4]
005E70F1     8B80 F4030000 mov eax,dword ptr ds:[eax+3F4]
005E70F7     05 80000000   add eax,80
005E70FC     BA 88755E00   mov edx,Music_kt.005E7588
005E7101     E8 F2D7E1FF   call Music_kt.004048F8           //这里应该是对程序进行错误处理
005E7106     8B45 FC       mov eax,dword ptr ss:[ebp-4]
005E7109     8B80 74030000 mov eax,dword ptr ds:[eax+374]
005E710F     6950 30 EA490>imul edx,dword ptr ds:[eax+30],849EA
005E7116     E8 1D87E5FF   call Music_kt.0043F838           //这里应该是对程序进行错误处理
005E711B     8B45 FC       mov eax,dword ptr ss:[ebp-4]
......

感兴趣的是我们再看看文件标准的MD5“dff845376ac0cf39c12e1519b5385788”藏在什么地方呢,首先用OD搜索程序里面的参考文本,没有发现。我们再向上看看刚才比较的那段程序,在005E6F77处下断(这个断点是经过反复确定的,后来发现此处有突破点)
005E6F77     8B55 88       mov edx,dword ptr ss:[ebp-78]
005E6F7A     8D4D 8C       lea ecx,dword ptr ss:[ebp-74]    //载入一段经过DES加密后的代码
005E6F7D     B8 F0745E00   mov eax,Music_kt.005E74F0        //ASCII "BAC65E17BF4EC3439371EDB7A7D2CF2B67645C5FFBF3EC34968B8370ACA2E9B74CF6B0BE8A65901221559BDEBF3687146C1C8F66F72029C5"
005E6F82     E8 596DFFFF   call Music_kt.005DDCE0           //调用DES解密
005E6F87     8B45 8C       mov eax,dword ptr ss:[ebp-74]    //得到DES之前的SQL语句:
=======
堆栈 ss:[0012FB38]=00D81EC4, (ASCII "SELECT data1 as ktv1md5,data2 as ht2md5 FROM DWXXB")
eax=00000000
分析看来标准MD5数据存储在*.mdb数据库中的DWXXB表中,于是跟踪数据库的密码(很容易获得为"MMMNNN"),打开该表一看,果然找到了标准的MD5,呵呵,原来这个重要的东东居然藏在这里,至此一切疑惑均已解开(破解自校验也可以修改MD5到数据库中)!
=======
总结:程序自校验有很多都是加壳程序所为,但校验失败后往往都是拒绝运行程序,而本文是作者在编程时融入了程序的自校验,同时还把程序功能限制的SQL语句捆绑在一起,首先将SQL字符串进行DES加密,再把加密后的密文再次进行MD5,最后再与文件校验的MD5进行连接,连接后再进行最终MD5加密,加密后的标准MD5码藏到了MDB数据库中,启动程序时进行读出,然后与当前的MD5进行比对(这个比对的地方与文件校验的地方相差很远,且藏得很隐蔽,对比跟踪法基本没戏),一旦程序发生修改或SQL语句发生变化,这两个条件的任一个成立就OVER。自从2006年V6.2版本遭破解后作者已经把加密提高到了一个空前的高度,呵呵,经过2周的努力,V8.5的程序已经得到完美破解!
====END====