admin 管理员组文章数量: 887021
VB程序的破解思路总结、编译方式vs破解工具 比较
把一个简单的VB计时器程序研究透了,对于VB破解应有较大帮助,如有错误欢迎指出。
VB程序也是一个非常有趣的东西,你去看VB的主程序,是用Microsoft Visual C++ 5.0编写的,也就是说,其实里面调用的各种命令和控件,都是一段VC++的代码。还有VB运行时必须的“库”(虚拟机):msvbvm50, msvbvm60, 都是Microsoft Visual C++ DLL,所以非常明显的,只要研究透了VB的这个VM虚拟机DLL,完全可以反编译出P-code的程序的源码。著名的程序就有比如VB Decompiler,它对于P-code的效果大家可以在下面的分析中看得出来,当然它对于编译成Native Code的程序效果也不错,只不过名称什么的都不太清楚。所以说,VB的P-code程序(其实Native Code程序也差不了多少,只不过虚拟机里的代码一部分都插入了程序里而已)和Flash的SWF还是有异曲同工之妙的:都可以反编译(可能可以得到源码),原因都在于它们用了比较“通用”的虚拟机。vb的就是vbvmXX.dll,flash的就是Flash Player XX。
为了更好地研究VB程序的破解,我特意使用了一个小程序来做演示。
另外,由于正向和逆向是有联系的,所以我这里写正向的时候可以类推到逆向,写逆向的时候可以类推到正向。
这个程序是某个同学给我的,听说我学破解,就想让我来看看他的程序编译到底是编译成P-code好还是编译成native code好,如果编译成native code又要不要优化一下呢?
我打算顺便骗到源码,但是居然不给我!不过他倒是送我一对注册名和注册码。拿到程序一看关于窗口还写着“演示版”,不就是传说中的Demo吗!算了,不管这些细节了。
我拿到的程序有以下几个:Timer_nc_speed.exe(256KB)[Native-Code并进行代码速度优化]、Timer_p-code.exe(80KB)[P-code程序]、Timer_nc_length.exe(240KB)[Native-Code并进行代码长度优化]、Timer_nc_none.exe(240KB)[Native-code无优化]。可以看到,P-code程序非常小,但是它运行必须要VB的VM DLL。
首先来看看这个程序是如何进行授权及验证的:
打开后主窗口:
上面有一个注册按钮正好遮掉计时器的“分”的十位(也就是说你计时不能超过10分钟,否则你自己都不知道过了多久),然后会过随机的时间跳出“请尽快注册”的窗口,输入了注册码之后会提示“注册码已存储,请立即重启程序,如果正确下次将不再有限制。”看来是纯重启验证类型的。不过貌似还有个突破点,就是按“倒”按钮(即倒计时)会提示“未注册版本不支持倒计时,是否立即注册?y/n”。也许可以从这个对话框入手哦!
另外,这个软件还有个版本更新历史(我去,都更新到构造23了),里面有一句话引起我的注意:
更改 VB 内部 MSGBOX 成为 user32.dll 的 API 里的 "MessageBoxA".
额,这么说,所有对话框在入口点前的API表(后面会提到)里下断点都是无效的了。
现在,拿出几个神器(网上有提到的): SMARTCHECK,WKTVBDebugger,VB Decompiler。当然OD也用来看看效果。
首先是P-code程序载入OD:入口点的特征就是push XXXXXXXX,Call XXXXXXXX。入口点上方是各种VB虚拟机DLL里面的API,下面就是各种OD识别不出的东西。(不是汇编代码当然识别不出啦)
然后是Native-code程序载入OD:其实做各种优化的都差不多,首先入口点特征与P-code的差不多,上方也是一张表(不过好像更大?),下方也有一段数据,但是再往下拉会发现:
00408C60 > \55 push ebp
00408C61 . 8BEC mov ebp,esp
00408C63 . 83EC 18 sub esp,18
00408C66 . 68 26314000 push <jmp.&MSVBVM60.__vbaExceptHandler> ; SE 句柄安装
00408C6B . 64:A1 0000000>mov eax,dword ptr fs:[0]
00408C71 . 50 push eax
00408C72 . 64:8925 00000>mov dword ptr fs:[0],esp
00408C79 . B8 D0010000 mov eax,1D0
00408C7E . E8 9DA4FFFF call <jmp.&MSVBVM60.__vbaChkstk>
00408C83 . 53 push ebx
00408C84 . 56 push esi
00408C85 . 57 push edi
00408C86 . 8965 E8 mov dword ptr ss:[ebp-18],esp
00408C89 . C745 EC B8114>mov dword ptr ss:[ebp-14],Timer_nc.0040>
00408C90 . 8B45 08 mov eax,dword ptr ss:[ebp+8]
00408C93 . 83E0 01 and eax,1
00408C96 . 8945 F0 mov dword ptr ss:[ebp-10],eax
00408C99 . 8B4D 08 mov ecx,dword ptr ss:[ebp+8]
00408C9C . 83E1 FE and ecx,FFFFFFFE
复制代码
这不是我们熟悉的汇编代码了嘛呵呵。
所以说,到目前为止,P-code的防破解作用远远大于Native-code的。
<破解思路1>在API上面下断点跟踪......
这个对于P-code程序是完全无效的了,但是对于Native Code的可以一试。
首先,以他的水平来说,比较注册码肯定是明码比较,但是我没那么无聊去做内存注册机,来爆破试试看。
打开Timer_nc_XXX.exe,在入口点前面的大表这里下断:
004032E2 $- FF25 B0104000 jmp dword ptr ds:[<&MSVBVM60.__vbaStrCmp>] ; MSVBVM60.__vbaStrCmp
复制代码
运行程序,第一次断下先放行,结果窗口就出来了,说明第一次断下就已经到了关键.
重载程序,断下后F8,来到这里:
734793DA > FF7424 08 push dword ptr ss:[esp+8]
734793DE FF7424 08 push dword ptr ss:[esp+8]
734793E2 6A 00 push 0
734793E4 E8 44E6FFFF call MSVBVM60.__vbaStrComp
734793E9 C2 0800 retn 8
复制代码
再按Alt+F9来到这里:
00414BAE . 50 push eax
00414BAF . 68 78734000 push Timer_nc.00407378
00414BB4 . E8 29E7FEFF call <jmp.&MSVBVM60.__vbaStrCmp>
00414BB9 . F7D8 neg eax ; here
00414BBB . 1BC0 sbb eax,eax
00414BBD . F7D8 neg eax
00414BBF . F7D8 neg eax
复制代码
这里已经是程序的代码段了,可以更改代码了,继续跟踪看看。
00414BC1 . 66:8985 68FEF>mov word ptr ss:[ebp-198],ax
00414BC8 . 8D4D D8 lea ecx,dword ptr ss:[ebp-28]
00414BCB . E8 60E7FEFF call <jmp.&MSVBVM60.__vbaFreeStr>
00414BD0 . 8D4D B4 lea ecx,dword ptr ss:[ebp-4C]
00414BD3 . E8 B6E6FEFF call <jmp.&MSVBVM60.__vbaFreeVar>
00414BD8 . 0FBF85 68FEFF>movsx eax,word ptr ss:[ebp-198]
00414BDF . 85C0 test eax,eax
00414BE1 . 0F84 E60B0000 je Timer_nc.004157CD ;这里跳了
00414BE7 . C745 FC 1F000>mov dword ptr ss:[ebp-4],1F
00414BEE . 66:8365 DC 00 and word ptr ss:[ebp-24],0
00414BF3 . C745 FC 20000>mov dword ptr ss:[ebp-4],20
00414BFA . 68 7C744000 push Timer_nc.0040747C ; UNICODE "Timer_Regcode.inf"
00414BFF . 6A 01 push 1
00414C01 . 6A FF push -1
复制代码
可以看到,00414BE1这里跳转跳了,但是应该是不要跳的,因为下面的代码才开始读取注册码. 于是NOP。
00414C03 . 6A 01 push 1
00414C05 . E8 78E6FEFF call <jmp.&MSVBVM60.__vbaFileOpen>
00414C0A > C745 FC 21000>mov dword ptr ss:[ebp-4],21
00414C11 . 6A 01 push 1
00414C13 . E8 64E6FEFF call <jmp.&MSVBVM60.#571>
00414C18 . 0FBFC0 movsx eax,ax
00414C1B . 85C0 test eax,eax
00414C1D . 0F85 8E000000 jnz Timer_nc.00414CB1
00414C23 . C745 FC 22000>mov dword ptr ss:[ebp-4],22
复制代码
但是接下来问题也随之出现,就是说这个文件不存在,却要被打开(00414C05处),会出现异常的,所以这句代码也得NOP。不过这个程序令我惊讶的是,居然自带了异常处理程序!现在不能观摩,否则异常不断产生会导致程序卡死,先要把所有异常都处理好,不过那时候也不能再观摩了.
所以还要NOP:
00414C05 . E8 78E6FEFF call <jmp.&MSVBVM60.__vbaFileOpen>
00414C13 . E8 64E6FEFF call <jmp.&MSVBVM60.#571>
复制代码
另外,为了不浪费程序的感情不断读取文件,我们把00414C1D的jnz改为jmp跳过读取阶段. 然后就到了这里:
00414CB1 > \C745 FC 2B000>mov dword ptr ss:[ebp-4],2B
00414CB8 . 6A 01 push 1
00414CBA . E8 ABE5FEFF call <jmp.&MSVBVM60.__vbaFileClose>
00414CBF . C745 FC 2C000>mov dword ptr ss:[ebp-4],2C
00414CC6 . C785 ECFEFFFF>mov dword ptr ss:[ebp-114],Timer_nc.004>
00414CD0 . C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],8008
00414CDA . C785 DCFEFFFF>mov dword ptr ss:[ebp-124],Timer_nc.004>
00414CE4 . C785 D4FEFFFF>mov dword ptr ss:[ebp-12C],8008
00414CEE . 8B45 08 mov eax,dword ptr ss:[ebp+8]
00414CF1 . 05 A4000000 add eax,0A4
00414CF6 . 50 push eax
00414CF7 . 8D85 E4FEFFFF lea eax,dword ptr ss:[ebp-11C]
00414CFD . 50 push eax
00414CFE . 8D45 B4 lea eax,dword ptr ss:[ebp-4C]
00414D01 . 50 push eax
00414D02 . E8 57E5FEFF call <jmp.&MSVBVM60.__vbaVarCmpNe>
00414D07 . 50 push eax
00414D08 . 8B45 08 mov eax,dword ptr ss:[ebp+8]
00414D0B . 05 B4000000 add eax,0B4
00414D10 . 50 push eax
00414D11 . 8D85 D4FEFFFF lea eax,dword ptr ss:[ebp-12C]
00414D17 . 50 push eax
00414D18 . 8D45 A4 lea eax,dword ptr ss:[ebp-5C]
00414D1B . 50 push eax
00414D1C . E8 3DE5FEFF call <jmp.&MSVBVM60.__vbaVarCmpNe>
00414D21 . 50 push eax
00414D22 . 8D45 94 lea eax,dword ptr ss:[ebp-6C]
00414D25 . 50 push eax
00414D26 . E8 39E5FEFF call <jmp.&MSVBVM60.__vbaVarOr>
00414D2B . 50 push eax
00414D2C . E8 6FE5FEFF call <jmp.&MSVBVM60.__vbaBoolVarNull>
00414D31 . 0FBFC0 movsx eax,ax
00414D34 . 85C0 test eax,eax
00414D36 . 0F84 6D090000 je Timer_nc.004156A9
复制代码
一样的,需要NOP的:
00414CBA . E8 ABE5FEFF call <jmp.&MSVBVM60.__vbaFileClose>
复制代码
而在00414D36这里,如果跳转就是未注册了. 所以NOP.
继续:
00414D3C . C745 FC 2D000>mov dword ptr ss:[ebp-4],2D
00414D43 . C785 ECFEFFFF>mov dword ptr ss:[ebp-114],Timer_nc.004>
00414D4D . C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],8
00414D57 . 8D95 E4FEFFFF lea edx,dword ptr ss:[ebp-11C]
00414D5D . 8B4D 08 mov ecx,dword ptr ss:[ebp+8]
00414D60 . 83C1 64 add ecx,64
00414D63 . E8 08E5FEFF call <jmp.&MSVBVM60.__vbaVarCopy>
00414D68 . C745 FC 2E000>mov dword ptr ss:[ebp-4],2E
00414D6F . C785 ECFEFFFF>mov dword ptr ss:[ebp-114],Timer_nc.004>
00414D79 . C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],8
00414D83 . 8D95 E4FEFFFF lea edx,dword ptr ss:[ebp-11C]
00414D89 . 8B4D 08 mov ecx,dword ptr ss:[ebp+8]
00414D8C . 83C1 74 add ecx,74
00414D8F . E8 DCE4FEFF call <jmp.&MSVBVM60.__vbaVarCopy>
00414D94 . C745 FC 2F000>mov dword ptr ss:[ebp-4],2F
00414D9B . C785 ECFEFFFF>mov dword ptr ss:[ebp-114],Timer_nc.004>
00414DA5 . C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],8008
00414DAF . 8B45 08 mov eax,dword ptr ss:[ebp+8]
00414DB2 . 05 A4000000 add eax,0A4
00414DB7 . 50 push eax
00414DB8 . 8D85 E4FEFFFF lea eax,dword ptr ss:[ebp-11C]
00414DBE . 50 push eax
00414DBF . E8 0CE5FEFF call <jmp.&MSVBVM60.__vbaVarTstEq>
00414DC4 . 0FBFC0 movsx eax,ax
00414DC7 . 85C0 test eax,eax
00414DC9 . 74 05 je short Timer_nc.00414DD0
00414DCB . E9 4A160000 jmp Timer_nc.0041641A
复制代码
00414DC9这个跳转不跳的话就会产生1个异常,并且计时器的显示不正确,虽然可以正确及时,而且调整窗口是否全屏也会出错,不过现在可以顺便观摩一下异常处理窗口了:
异常处理程序
10:48:02:异常发生.已拦截.无需进一步操作. 错误编号:0, 错误描述:, 引起错误在:“计时器”窗口.
10:48:31:异常发生.已拦截.无需进一步操作. 错误编号:380, 错误描述:无效属性值, 引起错误在:“计时器”窗口.
10:48:31:异常发生.已拦截.无需进一步操作. 错误编号:380, 错误描述:无效属性值, 引起错误在:“计时器”窗口.
10:48:31:异常发生.已拦截.无需进一步操作. 错误编号:380, 错误描述:无效属性值, 引起错误在:“计时器”窗口.
10:48:31:异常发生.已拦截.无需进一步操作. 错误编号:380, 错误描述:无效属性值, 引起错误在:“计时器”窗口.
程序已经成功处理了异常,这个窗口关闭后也可正常运行,但是一个或多个命令执行失败.
你可以将本窗口截屏或复制叙述并配以适当文字叙述(比如是在什么情况下引起异常,因为错误对象有时不准确),然后联系作者,会尽快处理问题. 谢谢!
感觉好高级的。呵呵。扯远了,重载程序,这里需要把00414DC9跳转改成jmp.
继续看:
00414DD0 > \C745 FC 32000>mov dword ptr ss:[ebp-4],32
00414DD7 . C785 ECFEFFFF>mov dword ptr ss:[ebp-114],1
00414DE1 . C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],2
00414DEB . C785 DCFEFFFF>mov dword ptr ss:[ebp-124],1
00414DF5 . C785 D4FEFFFF>mov dword ptr ss:[ebp-12C],2
00414DFF . 8D85 E4FEFFFF lea eax,dword ptr ss:[ebp-11C]
00414E05 . 50 push eax
00414E06 . 8B45 08 mov eax,dword ptr ss:[ebp+8]
00414E09 . 05 A4000000 add eax,0A4
00414E0E . 50 push eax
00414E0F . 8D45 B4 lea eax,dword ptr ss:[ebp-4C]
00414E12 . 50 push eax
00414E13 . E8 3AE4FEFF call <jmp.&MSVBVM60.__vbaLenVar>
00414E18 . 50 push eax
00414E19 . 8D85 D4FEFFFF lea eax,dword ptr ss:[ebp-12C]
00414E1F . 50 push eax
00414E20 . 8D85 30FEFFFF lea eax,dword ptr ss:[ebp-1D0]
00414E26 . 50 push eax
00414E27 . 8D85 40FEFFFF lea eax,dword ptr ss:[ebp-1C0]
00414E2D . 50 push eax
00414E2E . 8B45 08 mov eax,dword ptr ss:[ebp+8]
00414E31 . 05 08010000 add eax,108
00414E36 . 50 push eax
00414E37 . E8 1CE4FEFF call <jmp.&MSVBVM60.__vbaVarForInit>
00414E3C . 8985 F8FDFFFF mov dword ptr ss:[ebp-208],eax
00414E42 . E9 0B020000 jmp Timer_nc.00415052
......
00415052 > \83BD F8FDFFFF>cmp dword ptr ss:[ebp-208],0
00415059 .^ 0F85 E8FDFFFF jnz Timer_nc.00414E47
复制代码
00415059此处也得NOP。
经过了3段类似代码以后,终于来到关键跳(前面跳转必须全部改对才能执行到这里哦):
00415316 . E8 FBDEFEFF call <jmp.&MSVBVM60.__vbaVarCmpGe>
0041531B . 50 push eax
0041531C . 8D45 84 lea eax,dword ptr ss:[ebp-7C]
0041531F . 50 push eax
00415320 . E8 75DFFEFF call <jmp.&MSVBVM60.__vbaVarAnd>
00415325 . 50 push eax
00415326 . E8 75DFFEFF call <jmp.&MSVBVM60.__vbaBoolVarNull>
0041532B . 0FBFC0 movsx eax,ax
0041532E . 85C0 test eax,eax
00415330 . 0F84 4F020000 je Timer_nc.00415585 ;***
复制代码
00415330直接NOp了,这下后面的代码就是设置成已注册的了!
00415336 . C745 FC 3D000>mov dword ptr ss:[ebp-4],3D
0041533D . 8B45 08 mov eax,dword ptr ss:[ebp+8]
00415340 . 8B00 mov eax,dword ptr ds:[eax]
00415342 . FF75 08 push dword ptr ss:[ebp+8]
00415345 . FF90 24030000 call dword ptr ds:[eax+324]
0041534B . 50 push eax
0041534C . 8D45 CC lea eax,dword ptr ss:[ebp-34]
0041534F . 50 push eax
00415350 . E8 23E0FEFF call <jmp.&MSVBVM60.__vbaObjSet>
00415355 . 8985 68FEFFFF mov dword ptr ss:[ebp-198],eax
0041535B . 6A 00 push 0
0041535D . 8B85 68FEFFFF mov eax,dword ptr ss:[ebp-198]
00415363 . 8B00 mov eax,dword ptr ds:[eax]
00415365 . FFB5 68FEFFFF push dword ptr ss:[ebp-198]
0041536B . FF90 94000000 call dword ptr ds:[eax+94]
00415371 . DBE2 fclex
复制代码
很好,并且没有了注册按钮,而且程序开启还会显示已注册!(虽然注册名和注册码都是空的。)
先保存到文件,再来看看关于窗口,反正看了也不会怎么样吧。(因为某些原因,需要把学号和学校代号抹掉,呵呵)
关于
计时器 中文版 版本1.7 构建23
此软件由****开发.
Copyright (c) 2015 ****. 保留所有权利.
你正在使用演示版(未注册)
本软件尚未注册!
注册名:未注册
注册码:为了支持软件开发,请及时注册软件! (暂
未对软件试用进行时间限制)
一连3个未注册提示,额,有一种不祥的预感,不过至少现在没有注册提示框. 并且设置窗口大小、是否置顶都没出现任何问题。
于是,我再手贱地按了一下“倒”按钮(即倒计时)。结果:
12345678901234567890
123456789
额,果然有暗桩。我一开始以为本来功能就没有的,结果我用他给我的注册码一试,发现还是有倒计时功能而且完整的。所以再次拖入OD调试。
不过这个反破解有点坑,虽然不是最坑,但是想退出程序还真难!按“返回”按钮没有任何用处,按了开始计时按钮提示"00:00已用完."(这是正常的,因为我还没有设置过多长时间的倒计时),于是又一次手贱按了“设”按钮(设置),结果倒计时窗体不见了,只有一个设置窗口。再次手贱设置完了倒计时时间,还勾选了"允许超时",最后一次手贱就按了“关”按钮(关闭设置窗口),结果程序一个窗口都不见了......好窘。打开任务管理器,发现还在运行啊:
taskmgr.exe Admin... 00 5,476 K
Timer_nc_none.exe Admin... 00 5,640 K ;***
NOTEPAD.EXE Admin... 00 832 K
算了,先结束了进程,再载入OD调试看看:
按了一下“倒”按钮,发现真的又一次读取了注册码:
00427562 . F7D8 neg eax
00427564 . 1BC0 sbb eax,eax
00427566 . F7D8 neg eax
00427568 . F7D8 neg eax
0042756A . 66:8985 68FEF>mov word ptr ss:[ebp-198],ax
00427571 . 8D4D D8 lea ecx,dword ptr ss:[ebp-28]
00427574 . E8 B7BDFDFF call <jmp.&MSVBVM60.__vbaFreeStr>
00427579 . 8D4D B4 lea ecx,dword ptr ss:[ebp-4C]
0042757C . E8 0DBDFDFF call <jmp.&MSVBVM60.__vbaFreeVar>
00427581 . 0FBF85 68FEFF>movsx eax,word ptr ss:[ebp-198]
00427588 . 85C0 test eax,eax
0042758A . 0F84 0F0D0000 je Timer_nc.0042829F
00427590 . C745 FC 07000>mov dword ptr ss:[ebp-4],7
00427597 . 66:8365 DC 00 and word ptr ss:[ebp-24],0
0042759C . C745 FC 08000>mov dword ptr ss:[ebp-4],8
004275A3 . 68 7C744000 push Timer_nc.0040747C ; UNICODE "Timer_Regcode.inf"
复制代码
于是我们也有思路了,只要搜索push 0040747C即可,把所有相关的都改掉. (改的东西都基本一模一样的,因为是单独的再次读取和验证,所以不可能改一处就完美,得一个一个改.......蛋疼啊。)<--此处反破解思路
总共有6处。都改完了以后......终于是完美的了。
<破解思路2>使用动态调试程序SMARTCHECK快速定位
SMARTCHECK是NUMEGA公司推出的一款调试VB程序的程序。我找到的最新版是6.20(Build1286) RC2的,1999年发布的......老古董了。网上有几篇文章的,貌似很好用,其实限制也有,不信打开一个P-code程序试试看:
SmartCheck汉化版
Timer_p-code.exe 被编到p代码
SmartCheck 是不能的提供调试工程信息为编了的p代码。
从 SmartCheck 得到,保证与这些 Visual Basic 编译器背景造这
个工程:
- 编到本机代码
- 没有优化
- 创造符号调试信息
继续打开程序( ) 不打开程序( )
不显示出这条消息( )
复制代码
悲剧的是,貌似同学给我的这几个程序都不符合要求:
Timer_nc_none.exe - 程序结果
类型 Qty. 合计 (字节) 类型
No details)
No events
Results Events
No source file
于是,我联系同学,让他再给我发个符合要求的......终于来了,Timer_nc_none_dbg.exe就是这个了。(从这里可以看出,SMARTCHECK是为编程程序员设计的,不是为逆向而生......)
试试看......
what??还是没有结果,那这个程序就扔掉了吧。没用啊。
不过,幸好,我在扔掉前想到会不会是因为我用的是绿色版的原因,于是我费尽千辛万苦找到6.20 RC2 Retail安装包,安装以后发现要输序列号,不管,先用,结果发现程序没有正确的序列号就不能调试其他程序,显示试用期已过,于是爆破SCShell.dll,可以正常使用了。
结果再测试......发现其实之前的非P-code文件也是可以正常调试的,甚至被优化的也可正常调试,而且P-code程序也可以调试,而且也有有用的东西,只不过一些语句不太明确而已......真是白忙活了,绿色版害死人啊.
好吧,打开程序,调试,由于Timer事件不断产生,所以我们就点击一次注册按钮,再点击一次“倒”按钮,发现真的好清楚:
Timer_nc_none.exe - 程序结果
类型 Qty. 合计 (字节) 类型
No details)
No events
Results Events
No source file
Thread 0 [thread id:2360 (0x938)]
Event reporting started: 2015-02-10 12:37:35
Form1 (Form) created
Form1_Load
OnError
Form1.hWnd
GetSystemMenu returns HMENU:6D058F
RemoveMenu returns BOOL:1
Form1.hWnd
GetSystemMenu returns HMENU:6D058F
RemoveMenu returns BOOL:1
Form1.hWnd
GetSystemMenu returns HMENU:6D058F
RemoveMenu returns BOOL:1
Form1.hWnd
SetWindowPos returns BOOL:1
sfd.Text <-- "1" (String)
Timer1.Interval <-- 1000 (Long)
Form1.Caption <-- "计时器" (String)
Label1.Caption <-- "00:00" (String)
Command1.Caption <-- "退出" (String)
comd2.Caption <-- "√" (String)
Command3.Caption <-- "X" (String)
Command4.Caption <-- "‖" (String)
Command7.Caption <-- "大" (String)
Command8.Caption <-- "关于" (String)
Command9.Caption <-- "常" (String)
Text1.Visible <-- False (Boolean)
Text2.Visible <-- False (Boolean)
Timer1.Enabled <-- False (Boolean)
comd2.Enabled <-- True (Boolean)
Command3.Enabled <-- False (Boolean)
Command4.Enabled <-- False (Boolean)
sfd.Visible <-- False (Boolean)
Timer2.Enabled <-- False (Boolean)
复制代码
看了老半天,没发现这些语句如何修改,于是只能做追码工具了......
我们先输入注册名注册码:(此程序貌似支持中文注册名啊......)
用户名:dsong@吾爱破解论坛 WwW.52PoJie.Cn
注册码:01234567890ABCDEFabcdef
额,然后同一目录下生成了明码的注册码存储......
然后是长长的检验和计算过程:
Dir //获取目录
Open //打开
EOF //是否到最后一行
LineInputNum //输入
EOF //是否到最后一行
LineInputNum //输入
EOF //....
Close //关闭
Len returns LONG:1243848
Mid
OnError
Asc returns Integer:100
Hex
Resume
Visual Basic Runtime Error 20: 无错误恢复
Handling Visual Basic Runtime Error 20
Resuming from Visual Basic Runtime Error
Resume
String ("&H64") --> Long (100)
Hex
Mid
OnError
Asc returns Integer:115
Hex
Resume
Visual Basic Runtime Error 20: 无错误恢复
Handling Visual Basic Runtime Error 20
Resuming from Visual Basic Runtime Error
Resume
String ("&H73") --> Long (115)
Hex
Mid
OnError
Asc returns Integer:111
HexResume
Visual Basic Runtime Error 20: 无错误恢复
.......
Hex
String ("&HA0") --> Long (160)
String ("&HA0") --> Long (160)
Hex
String ("&H56") --> Long (86)
String ("&H56") --> Long (86)
Chr
Resume
Visual Basic Runtime Error 20: 无错误恢复
Handling Visual Basic Runtime Error 20
Resuming from Visual Basic Runtime Error
Resume
Mid
OnError
String ("&HBE") --> Long (190)
String ("&HBE") --> Long (190)
String ("&HBE") --> Long (190)
Hex
String ("&H74") --> Long (116)
String ("&H74") --> Long (116)
Chr
Resume
Visual Basic Runtime Error 20: 无错误恢复
Handling Visual Basic Runtime Error 20
Resuming from Visual Basic Runtime Error
Resume
Mid
OnError
String ("&H0A") --> Long (10)
String ("&H0A") --> Long (10)
Hex
String ("&H3A") --> Long (58)
String ("&H3A") --> Long (58)
String ("&H3A") --> Long (58)
Chr
......
Mid
OnError
Mid
OnError
Mid
OnError
Mid
OnError
Mid
OnError
Mid
OnError
Mid
OnError
Mid
OnError
Trim //注册码明码出现
Len returns LONG:1243832 //注册码明码出现
Timer2.Interval <-- 1000 (Long) //定时弹出提醒注册窗口,看到这里要往前找
Timer2.Enabled <-- True (Boolean)
Form1.Height
Label1.FontSize
Form1.Height
Label1.Height
Form1.Width
Label1.Width
Text1.Text <-- "dsong@吾爱破解论坛 WwW.52PoJie.Cn" (String) //注册名
Text2.Text <-- "01234567890ABCDEFabcdef" (String) //注册码(假)
csy.Visible <-- False (Boolean)
csy.Text <-- "0" (String)
Resume
Visual Basic Runtime Error 20: 无错误恢复
Handling Visual Basic Runtime Error 20
Resuming from Visual Basic Runtime Error
Resume
在注册码明码出现的时候,看旁边的细节窗口:
string (variant)
String .bstrVal = 001559A4
= "h1141X:if:2Vt:y78OTEiMr34VyX11HaF"
这就是我们的注册码了。使用这个注册码注册一下,然后看看关于窗口:
关于
计时器 中文版 版本1.7 构建23
此软件由****开发.
Copyright (c) 2015 ****. 保留所有权利.
你正在使用演示版(已注册)
本软件已授权给:
注册名:dsong@吾爱破解论坛 WwW.52PoJie.Cn
注册码:h1141X:if:2Vt:y78OTEiMr34VyX11HaF
关闭
不过只看关于窗口是不够的,因为它这个程序只要检测到输入过注册码,就会显示已注册,所以我们再来按“倒”按钮:<--反破解思路
倒计时 已注册
00:00
关于 返回
发现一切正常(这就好啦哈哈).
(其实如果软件设计成注册码分段验证的会更好,这样的话追码是不可能了,算法分析也会有阻碍.)
---拓展:算法分析
它这个算法也不难,不过是单向的,如果反过来就会检验错误。呵呵。所以只能是明码比较。算法贴出来,让他自己去改代码去。
由于SMARTCHECK代码太长,我这里来说一下它的大致算法。
首先把你的用户名逐位转换成HEX,然后把第一个HEX+4(第一个Hex指用户名第一位的HEX值,后面类推),第二个HEX+8,第三个HEX+C,第四个HEX+10,以此类推,然后取最小为0的hex即48(0x30),最大为z(区分大小写)的hex即122(0x7A),如果过小就+48(0x30),过大就-74(0x4A),然后最后组合起来就是注册码. 既然它程序使用VB写,那么我们也可以用VB来写写看。最后成品放在附件上了。
<3>使用静态反编译工具VB Decompiler快速找到爆破点并更清晰地分析算法
VB Decompiler 是神器了。用它作用非常的大,可以快速找到Native-Code编译的程序爆破点,而不像SMARTCHECK那样不能用来爆破程序。
并且,它不像SMARTCHECK一样渴望是Native Code的程序,它对于P-code的支持反而大于Native Code。看来真是分析了VB虚拟机的运作的成品啊!
应该说,它可以直接把程序算法给爆出来,如下:
loc_412296: If CBool((global_164 <> vbNullString) Or (global_180 <> vbNullString)) Then
loc_4122A3: global_100 = vbNullString
loc_4122B1: global_116 = vbNullString
loc_4122C5: If (global_164 = vbNullString) Then
loc_4122CA: Exit Sub
loc_4122CB: End If
loc_4122E2: For var_108 = 1 To Len(global_164): global_264 = var_108 'Variant
loc_41231B: global_244 = ZFto16(CStr(Mid(global_164, CLng(global_264), 1)))
loc_41235B: global_244 = CStr((CVar(CLng("&H" & global_244)) + (global_264 * 4)))
loc_41238E: global_100 = global_100 & Hex(global_244)
loc_4123A1: Next var_108 'Variant
loc_4123BD: global_212 = Trim(global_100)
loc_4123DB: For var_12C = 1 To Len(global_212): global_264 = var_12C 'Variant
loc_412421: var_10C = ZFto16ZF(CStr(Mid(global_212, CLng(((2 * global_264) - 1)), 2)))
loc_412433: global_116 = global_116 & CVar(var_10C)
loc_41244D: Next var_12C 'Variant
loc_412469: global_228 = Trim(global_116)
loc_41249C: If CBool((global_180 = global_228) And (Len(global_228) >= 10)) Then
loc_4124AD: Me.Command5.Visible = False
loc_4124BF: global_296 = 0
loc_4124D4: Me.Timer3.Interval = &H7D0
loc_4124EA: Me.Timer3.Enabled = True
loc_412500: Me.Command6.Enabled = True
loc_412512: global_312 = 1
loc_412519: Else
loc_41252C: Me.Timer2.Interval = &H3E8
loc_412542: Me.Timer2.Enabled = True
loc_412554: global_312 = 0
loc_412558: End If
loc_41255D: Else
loc_412570: Me.Timer2.Interval = &H3E8
loc_412586: Me.Timer2.Enabled = True
loc_412598: global_312 = 0
loc_41259C: End If
loc_4125A1: Else
loc_4125B4: Me.Timer2.Interval = &H3E8
loc_4125CA: Me.Timer2.Enabled = True
loc_4125DC: global_312 = 0
loc_4125E0: End If
恩,挺变态的吧,再试试看Native-code的:
loc_00414BC1: var_198 = (Dir("Timer_Regcode.inf", 7) = vbNullString)
loc_00414BE1: If var_198 = 0 Then GoTo loc_004157CD
loc_00414C05: Open "Timer_Regcode.inf" For Input As #1 Len = -1
loc_00414C0A:
loc_00414C1D: If EOF(1) <> 0 Then GoTo loc_00414CB1
loc_00414C35: Line Input #1, Me
loc_00414C47: If var_24 <> 0 Then GoTo loc_00414C67
loc_00414C62: ecx = Me
loc_00414C67: 'Referenced from: 00414C47
loc_00414C73: If var_24 <> 1 Then GoTo loc_00414C93
loc_00414C8E: ecx = Me
loc_00414C93: 'Referenced from: 00414C73
loc_00414C9E: var_24 = var_24 + 0001h
loc_00414CA8: var_24 = var_24
loc_00414CAC: GoTo loc_00414C0A
loc_00414CB1: 'Referenced from: 00414C1D
loc_00414CBA: Close #1
loc_00414CC6: var_114 = vbNullString
loc_00414CDA: var_124 = vbNullString
loc_00414D02: var_ret_1 = (Me <> vbNullString)
loc_00414D1C: var_ret_2 = (Me <> vbNullString)
loc_00414D26: call Or(var_6C, var_ret_2, var_ret_1, var_34, var_6C, Me, var_34, var_6C, Me, var_34, var_6C, Me, var_34, var_6C, Me)
loc_00414D36: If CBool(Or(var_6C, var_ret_2, var_ret_1, var_34, var_6C, Me, var_34, var_6C, Me, var_34, var_6C, Me, var_34, var_6C, Me)) = 0 Then GoTo loc_004156A9
loc_00414D43: var_114 = vbNullString
loc_00414D63: ecx = vbNullString
loc_00414D6F: var_114 = vbNullString
loc_00414D8F: ecx = vbNullString
loc_00414D9B: var_114 = vbNullString
loc_00414DC9: If (Me = vbNullString) = 0 Then GoTo loc_00414DD0
loc_00414DCB: GoTo loc_0041641A
loc_00414DD0: 'Referenced from: 00414DC9
loc_00414E37: For Me = 1 To Len(Me) Step 1
loc_00414E42: GoTo loc_00415052
loc_00414E47:
loc_00414EA4: var_eax = Timer.1788
loc_00414EAA: var_198 = Timer.1788
loc_00414EEC: ecx = var_2C
loc_00414F3A: var_ret_4 = CLng("&H" & eax+000000F4h)
loc_00414F3F: var_124 = var_ret_4
loc_00414F98: var_2C = var_ret_4 + Me * 4
loc_00414FA8: ecx = var_2C
loc_00415012: ecx = Me & Hex(Me)
loc_00415047: Next Me
loc_0041504C: var_208 = Next Me
loc_00415052: 'Referenced from: 00414E42
loc_00415059: If var_208 <> 0 Then GoTo loc_00414E47
loc_00415082: ecx = Trim(Me)
loc_004150F6: For Me = 1 To Len(Me) Step 1
loc_00415101: GoTo loc_00415284
loc_00415106:
loc_004151AD: var_eax = Timer.1792
loc_004151B3: var_198 = Timer.1792
loc_004151EC: var_210 = var_2C
loc_004151FC: var_84 = var_210
loc_0041522E: ecx = Me & var_210
loc_00415279: Next Me
loc_0041527E: var_20C = Next Me
loc_00415284: 'Referenced from: 00415101
loc_0041528B: If var_20C <> 0 Then GoTo loc_00415106
loc_004152B4: ecx = Trim(Me)
loc_00415320: var_ret_B = (Me = Me) And (Len(Me) >= 10)
loc_00415330: If CBool(var_ret_B) = 0 Then GoTo loc_00415585
loc_0041536B: Command5.Visible = False
loc_00415373: var_19C = eax
loc_004153DB: ecx = False
loc_00415418: Timer3.Interval = CInt(2000)
loc_0041541D: var_19C = eax
loc_00415490: Timer3.Enabled = True
loc_00415495: var_19C = eax
loc_00415508: Command6.Enabled = True
loc_00415510: var_19C = eax
loc_0041557B: ecx = CInt(1)
loc_00415580: GoTo loc_004156A4
loc_00415585: 'Referenced from: 00415330
loc_004155BD: Timer2.Interval = CInt(1000)
loc_004155C2: var_19C = eax
loc_00415635: Timer2.Enabled = True
loc_0041563A: var_19C = eax
loc_0041569F: ecx = False
loc_004156A4: 'Referenced from: 00415580
loc_004156A4: GoTo loc_004157C8
loc_004156A9: 'Referenced from: 00414D36
loc_004156E1: Timer2.Interval = CInt(1000)
loc_004156E6: var_19C = eax
loc_00415759: Timer2.Enabled = True
loc_0041575E: var_19C = eax
loc_004157C3: ecx = False
loc_004157C8: 'Referenced from: 004156A4
loc_004157C8: GoTo loc_004158EC
loc_004157CD: 'Referenced from: 00414BE1
loc_00415805: Timer2.Interval = CInt(1000)
loc_0041580A: var_19C = eax
loc_0041587D: Timer2.Enabled = True
loc_00415882: var_19C = eax
loc_004158E7: ecx = False
恩,代码明显差很多,但是对于调试的帮助还是有挺多的,比如上面可以得出415330的跳转应该不跳,然后到OD里面前前后后看看即可爆破了,不需要再费尽心思猜API来入手了。
这里我也不多说明,但是很明显的,编译成P-code的程序在遇到VB Decompiler的时候马上就跪了,之前的硬壳直接被看穿。
<破解思路3>利用动态调试器WKTVBDebugger来动态调试与修改P-code的VB程序
之前的VB Decompiler对于P-code的程序支持很好,但是并没有很明显的线索去修改VB程序。这时使用WKTVBDebugger就可以按照它里面的帮助文件进行修改跳转等东西了。
注意事项:
1. 此程序只能用于动态调试VB的P-code程序,对于Native-Code程序没有任何用处。
2. XP下使用时为了不出错,要把要调试的程序放在WKTVBDebugger同一目录下。(XP以前的系统应该没有这个问题,以后的没测试)
还有,帮助文件也很重要,里面有P-code和opcode的对应,对于修改爆破程序是有必要稍微了解一些的,否则代码你看不懂的话怎么破解呢。
我们先来看帮助文件,我这里直接打开会显示“已取消到该网页的导航”,对于这种问题解决办法:
右键属性,在下部看到一个“安全”,按旁边的“解除锁定”即可。最后记得要按确定.
安全: 此文件来自其他计算机,可能
被阻止以帮助保护该计算机。 解除锁定( )
复制代码
然后就正常了。点击目录中“操作码与助记符列表”,然后点击"标准设置",就是长长的一列对照表了。因为P-code与Intel的汇编代码完全不一样,所以得像初学OD那样先掌握一些P-code的含义。
目光聚焦到了这里:
1Eh Branch 3
1Ch BranchF 3
5Ch BranchFVar 3
1Dh BranchT 3
复制代码
这里可以知道,1c就代表jnz,1d就代表je,1e就代表jmp。(至少我当时是这么稚嫩地认为的。)然后我就到处找,汇编中的NOP在P-code里面该怎么表示呢?我在里面看这张表,找了三遍都没找到相关指令。然后无意中看到了这篇文章:http://blog.sina/s/blog_5000f4c901013iiy.html
里面有提到:
...... 所以解密插入一段代码是基本功,但在p-code里这样做比INTEL难。 看了下面的说明,你就会明白p-code语言插入语句为什么困难,如果你懂INTEL汇编,这事并不太难,加句NOP(90)很easy,但p-code不一样,他的语句大部分都与堆栈有关,比如:p-code里面转向语句一共有三个,
Branch (1E) ---- 无条件跳转
BranchT(1D) ---- 栈顶数据为真则跳转
BranchF(1F) ---- 栈顶数据为假则跳转
第一次接触p-code的人会想当然的认为,解密时可以把条件跳转,换成无条件跳转,比如BranchT(1D)换成Branch(1E)但这是错误的,我想这也是解密者犯的第一个错误,因为BranchTbranchF都有一个退栈动作,而Branch与堆栈无关,所以只能用BranchT与branchF互换,下面给出一段我想像的程序,看看他们与通常的机器语言有什么不同,假设一个过程,被调入的基址为00004000,我不知道怎么用p-code中标准术语描述,就称之为一个过程吧。在解密时,你不要想插入所谓空指令,因为栈会乱,改动程序要当心。 ......
复制代码
这一来,我才明白,原来P-code的vb程序不能像Intel程序一样,随便把je,jnz改成jmp,也没有NOP指令给你用了,必须要另辟途径。
好了,到这里准备工作也做得差不多了,把程序载入WKTVBDebugger。看一下关于窗口,发现是2001年的作品,说明最近十几年除了VB Decompiler在不断更新以外,其他的VB工具基本都停留在了10几年以前,这跟微软的保密策略肯定有着非常大的关系(未公开关于P-code的详细信息,不信你去百度或Google搜索,没有什么特别有价值的信息)。对于P-code研究最大的成果也就这么几个。扯远了,来看看程序。界面如下:
WKTVBDebugger v1.3 / 汉化:小生我怕怕[LCG]
-Code 代码源: Locs. Addr: 0012FA94h Proc. Range: 411F14h- 412838h 堆栈:
00411F14: 00 LargeBos
00411F16: 00 LargeBos 0012F8B4: 00 00 00 00 00 00 00 00
0012F8AC: 80 FF 12 00 00 00 00 00 ESP
00411F18: 4B OnErrorGoto 004126ECh 0012F8A4: 38 28 41 00 6B 3B 0E 66
00411F1B: 00 LargeBos 0012F89C: EC FA 12 00 B4 F8 12 00 Byte
00411F1D: 04 FLdRfVar 0012FA64h 0012F894: 98 DF 15 00 15 1F 41 00
00411F20: 05 ImpAdLdRf 0012F88C: EC FA 12 00 02 02 00 00
0012F884: 00 00 00 00 00 00 00 00
00411F23: 24 NewIfNullPr Form1 004040FC Word
0012F87C: 00 00 00 00 00 00 00 00
00411F26: 0D VCallHresult get__ipropHWNDFORM 0012F874: 8C F8 12 00 44 FF 00 10
00411F2B: F5 LitI4: -> 0h 0 0012F86C: C4 FA 12 00 6C F8 12 00 Dword
00411F30: 6C ILdRf 00000000h 0012F864: 0A 00 00 00 44 F8 12 00
0012F85C: F8 FF FF FF B4 F8 12 00
00411F33: 5E ImpAdCallI4 user32!GetSystemMenu 0012F854: 10 00 00 00 58 4D 05 10
00411F38: 71 FStR4 0012F84C: 00 00 00 00 12 00 12 00
00411F3B: 3C SetLastSystemError 0012F844: FF 00 00 FF 7A 9F 80 7C EBP
00411F3C: F5 LitI4: -> 1000h 4096 0012F83C: 9A 9A 83 7C 80 9F 80 7C
00411F41: F5 LitI4: -> FFFFF060h -4000 ESP
00411F46: 6C ILdRf 00000000h
00411F49: 0A ImpAdCallFPR4 user32!RemoveMenu
00411F4E: 3C SetLastSystemError Enable
00411F4F: 00 LargeBos
EBP
内存转存 (Ctrl+M)
加载模块符号
字符串参考. (Ctrl+S)
Op: 00 02 文件偏移: 00011F14 编辑 跟踪命令: 断点
Form1!00411F14
单步跟踪 (F8) API (Ctrl+B)
堆栈转存是启用的,相对到 ESP<-EBP.
执行到返回 (F12) 操作码(Ctrl+O)
跟踪当前指令 (F10) 执行断点 (Ctrl+E)
运行 (F5) 跟踪X行代码 (F6)
反汇编信息保存 保存信息 选项 管理窗口 (Ctrl+F) 管理杂项
清除日志 帮助 深层信息 (Ctrl+I) 高级信息
命令 >
比较有用的适合刚接触的就是“管理窗口 (Ctrl+F)”,使用这个,可以轻松地对程序载入、按钮按下等下断点。我们打开它,选择Form1(加载时默认的主窗口,如果不是可以一个一个慢慢试)。然后它会给你地址:
对象属性
Form1
地址: 004040FCh
Picture
Label
TextBox
Frame
Command
CheckBox
Option
ComboBox
ListBox
HScrollBar
VScrollBar
Timer
Printer
Screen
Clipboard
DriveListBox
DirListBox
FileListBox
Menu
Shape
Line
Image
PropiertyPage
UserControl
Unknow Controls
可以说,非常齐全,但是它只能根据控件来进行下断。而我们根据之前的分析,它在Form_Load的时候就检测了注册码。(如果没有重启验证,那么可以在按钮上下断点。)所以我们不得不再想另外的办法。<--反破解思路,做成纯重启验证也有它的好处。
再次打开VB Decompiler,发现Form_Load事件是在412838,于是打开WKTVBDebugger,下断412838,结果发现断不下!再仔细看看,发现这其实并不是代码的地址,而是窗体地址。所以在VB Decompiler反编译结果的窗口中选中第一句的地址411F26,再下断。
测试成功!这时候就可以取消断点,把VB Decompiler拖到注册验证段,开始是4121F0,再下断。
这里的跳转应该是不跳的,现在这里跳了,所以必须改掉。
这里为了验证BranchF/BranchT不能改成Branch,我们先改了试试看。
点击编辑,把目前的1c改成1e,然后运行。发现也没什么问题啊,请问那篇文章的真实性?我不得而知,望大牛来解释一下。
好,重新来一次。到这里以后看看代码:
004121D2: 3A LitVarStr 'Timer_Regcode.inf'
004121D7: 4E FStVarCopyObj 0012FA3Ch
004121DA: 04 FLdRfVar 0012FA3Ch
004121DD: 0B ImpAdCallI2 rtcDir on address 660D4F71h
004121E2: 23 FStStrNoPop
004121E5: 1B LitStr: ''
004121E8: 3D NeStr
004121EA: 2F FFree1Str
004121ED: 35 FFree1Var
004121F0: 1C BranchF 004125A1 (Jump ? ;在这
004121F3: 00 LargeBos
004121F5: F4 LitI2_Byte: -> 0h 0
004121F7: 70 FStI2 0012FA36
004121FA: 00 LargeBos
004121FC: 1B LitStr: 'Timer_Regcode.inf'
004121FF: F4 LitI2_Byte: -> 1h 1
00412201: F4 LitI2_Byte: -> FFh 255
00412203: FE Lead3/OpenFile
00412207: 00 LargeBos
复制代码
004121F0的opcode为:1c 8d 06 先改成1c 00 00可以看到:
004121F0: 1C BranchF 00411F14 (Jump ?
复制代码
而我们的目标为BranchF 00412296(那里开始注册码的校验,由于这里没有NOP指令,只能先跳过读取阶段),而412296-411F14=382,所以我们就可以改为:1c 82 03 。值得注意的是,这里又和Intel的汇编指令不同,汇编指令的跳转是以当前地址为参照,而这里P-code是以起始代码为参照的。这样一改,反而还挺好的,没有注册码就跳过读取阶段,有就不跳过,这样的话可以随意改写注册文件了。
然后就到了验证注册码的第一个环节:
00412296: 1C BranchF 0041255D (Jump ? ;在这
00412299: 00 LargeBos
0041229B: 3A LitVarStr ''
004122A0: 08 FLdPr
004122A3: FD Lead2/MemStVarCopy
004122A7: 00 LargeBos
004122A9: 3A LitVarStr ''
004122AE: 08 FLdPr
004122B1: FD Lead2/MemStVarCopy
004122B5: 00 LargeBos
004122B7: 08 FLdPr
004122BA: 06 MemLdRfVar
004122BD: 3A LitVarStr ''
004122C2: 5D HardType
004122C3: 33 EqVarBool
004122C5: 1C BranchF 004122CB ?
004122C8: 00 LargeBos
004122CA: 13 ExitProcHresult
004122CB: 00 LargeBos
复制代码
依葫芦画瓢,后面的跳转也可这样改,比如00412296这里要跳到412299即下一行,于是412299-411F14=385,于是改成1c 85 03即可。后面的修改也都是差不多的,参照VB Decompiler里面的指令修改起来会很准确很清楚。(不知道如果我使用WKTVBDebugger像OD一样熟练还会不会要VB Decompiler)
Native Code和P-code程序的比较和各种破解VB程序的工具的比较就到这里告一个段落了,如果你能看完整篇文章,那我承认你对于它是极度感兴趣了,呵呵。
最后做个总结: VB的Native Code程序爆破起来比较简单(主要是OD用熟练了),分析算法的话用VB Decompiler就要多花点心思。而P-code的程序追码和分析算法都非常简单,而爆破就要多花一些时间了(主要是WKTVBDebugger不太熟悉)。
顺便加个对话:
我: 各有利弊,.......(上面总结的话)......,你自己看着办吧。
小A: 哦,那我就用Native Code吧,反正反编译出来的源码就不那么直接清楚了。
我: 不过你那个程序算法太简单了一点,加个重壳吧。
小A: 不,加了重壳以后计时的延迟就太大了。
我: 我已经写出你那个软件的注册机了。
小A: 啊?......算了,我换一种编程语言写吧。
我: ......
复制代码
P.S.: 经过我的百般祈求,我的那个小A同学仍然不同意我把他的程序公开。对不起了,大家还是用自己的程序试炼一下吧。恩不过他没说我不能把注册机放出来......呵呵,我相信他不会打我的。
制作总结性的东西总是很累,这个笔记就当大家的参考吧,以后遇到VB程序脱了壳后破解起来应该会挺方便了吧。希望大家支持一下哦!写这篇文章差点写到浏览器卡死...... 下一篇帖子估计要等到一年半以后了。(初二下和初三应该没时间再来研究逆向这种耗费时间的东西了。)
一些提到的软件的链接:(解压密码全部为dsong@52PoJie.Cn)
1. Numega SmartCheck:
链接: http://pan.baidu/s/1qWzAlqc 密码: 2oh1
2. DotFix VB Decompiler:
链接: http://pan.baidu/s/1i3L3jvR 密码: bf8v
3. WKTVBDebugger:
链接: http://pan.baidu/s/1bnF9KV9 密码: ikce
4. Timer's KeyGen:
链接: http://pan.baidu/s/1ntolbKl 密码: ilal
程序
========
深入了解VB程序注册破解机制(一)
前段时间在csdn论坛上的vb区看见了一篇有关反破解的文章,文章介绍了一些代码。这些代码是通过一些api的调用比如isdebuggerpresent,获取父进程等手段,来防止程序被调试器调试和破解的。如果是在几年前,我说不定会惊为天人,然后赶紧把代码copy到vb里试验一下。但是,由于本人对反汇编和程序破解在这几年间有了一定的认识和了解,所以我清楚的知道这些反调试手段对于真正的cracker来说,实在是不值得一提。简单的来说,如果vb是通过调用api来完成反调试的话,那么只要hook住那个api,然后修改这个api的返回值就可以轻易的使得vb上当受骗。而目前常用的这些调试工具都有这样的功能,如olldbg工具有一个hideod的工具就可以hook住isdebuggerpresent,ntgloblflags,hepflags,process32next,……等等十数个api。而对于获得父进程,更加简单了,因为检测父进程是不是explorer的话,只要把调试器或者装载器的文件名改成explorer.exe就可以了。
其实在早期的软件破解过程中,很多人都害怕调试vb的程序。vb的机制和vc或者dehpi不同,他的功能都是在msvbvm60.dll里完成的,通常的软件跟踪办法很容易会在msvbvm60.dll里面打转,而找不出关键点,更不要说人人都害怕的p-code了。虽然如此,但本人在实际的软件跟踪破解过程中,并没有vb比其他语言的程序更难破解的感觉,也许是由于原先一直用vb所以对vb了解更多一点的原因吧。而在另一方面,也说明了现在的vb软件自身防护意识的淡薄和手段的匮乏。
我就实际剖析两个vb编写的软件,让大家了解一些有关vb注册验证和破解的知识。
【目标软件】: QQ聊天记录器(qq-msg)V3.0 2009升级版
【作者】: aspower
【编写语言】: VB5.0-6.0
【软件介绍】: QQ聊天记录器能完整的记录下你电脑上的所有的QQ聊天信息,不用密码,不用登录QQ窗口,你即能看到本机上所有QQ号的聊天记录(包括聊后立即删除的记录)。本软件主要功能是提供保存自已的聊天记录,或及时了解你孩子、家人的聊天内容,防止孩子交上不良网友。本软件几乎支持目前所有的QQ版本。特别忠告:本软件不得用于监视别人的计算机,请合法应用本软件。
【使用工具】:PEID,regmon,olldbg ,smartcheck
【作者声明】: 只是出于研究,没有其他目的。失误之处敬请大家见谅。
用peid查看软件,是aspack2.1的壳,普通的压缩壳,直接使用脱壳工具脱掉壳,再查看软件,得知软件是Microsoft Visual Basic 5.0 / 6.0,vb写的。
打开软件,标题栏显示qqmsg未注册字样,未注册版本有时间限制,而无法查看qq记录。打开注册窗口,输入注册名aspower,随便假注册码9876543210按下现在注册。当的一声,弹出个窗口,说是需要重新启动。很正常的过程,重启验证。
了解注册机制的人就会知道,大部分重启验证一般是通过检测注册表,或者检测某个key文件来判断注册码的。相信这个也不例外,好,让我们祭起regmon的大旗监视注册表。因为regmon默认的设置会把所有系统的注册表格信息都显示出来,那样的话很小一会就会出现大量的注册表信息。所以我们打开regmon后,先设置过滤条件为qq-msg.exe。这样的话就光显示这个软件对注册表的读写了。现在打开软件,我们就发现regmon里出现了以下键值HKEY_CURRENT_USER\Software\VB and VBA Program Settings\clongxue\clongxue,打开注册表定位到该键值,我们就会发现下面有zcm和zcmm两个dword值,一个是注册名,一个是注册码。
好现在用olldbg加载软件,如何下断点就成了关键。我们知道,通常在vb下下api断点是无法断下api的调用的,因为vb的api调用是通过msvbvm60.dll里DllFunctionCall函数来完成的,直接在vb程序里下断是无效的。同样,等窗体启动以后再下断也是没有效果的,因为这个时候验证过程已经过去。
很多人说vb下断点难,就是因为不了解vb。当碰见这个情况的时候,很多人就不知道如何着手了。再有的人就开始一步一步跟,事实上,vb的断点有vb的规律,我们可以自己考虑下,如果自己来些个注册程序,我们会怎么样呢?最可能是就是这样了,先算出一个注册码,然后跟用户输入的注册码比较。vb很可能是这样写的,
if (string1=string2) then
注册成功
else
注册失败
end if
那么string1=string2在汇编环境下是怎么样的呢?
可能是这样的,
0041BA42 . 50 push eax 压入第一个字符串
0041BA43 . 68 20804000 push 00408020 压入第二个字符串
0041BA48 . FF15 C4104000 call dword ptr [<&MSVBVM60.__vbaStrCm>; MSVBVM60.__vbaStrCmp 比较函数
MOV EDi,EAX ;结果同时在eax中返回
那么我们就可以在od里用MSVBVM60.__vbaStrCmp下断,这样的话,我们就可以在所有的字符串比较函数位置使程序停下来了。我们命令行里输入bpx MSVBVM60.__vbaStrCmp,然后按下f9让程序继续运行。再断下n次以后,发现已经进入程序,依然显示未注册,而根本没有看见注册字符入栈,就是说vbaStrCmp前的push eax 和 push 00408020都没有出现注册码的身影。那么这个程序是怎么进行注册验证的呢?
没关系,调试vb软件还有很多强大的软件,现在让我们启动令vb程序心惊胆颤的smartcheck来看下这个vb程序究竟在哪里搞了鬼.smartcheck
是一个非常强大的vb调试工具,在smartcheck下,你会看到非常详细的vb程序信息,甚至会被反汇编到可读的代码级别.
用smartcheck启动这个软件,你会看见软件启动的整个过程,你会看见,一开始软件启动了一个form1,就是输入密码的窗口,然后开始接受keypress,之后form2.load,form2.load后面有一个加号,那么让我们来看看form2.load里有什么猫腻.果然在form2.caption显示未注册的标题前面有两个可疑的InStr过程,instr大家都知道,在一个字符串里查找另一个字符串的函数,难道这个软件是用instr来从注册码里找字符串的?我们把里面的内容复制出来到文本里详细看看.
InStr(long:1, String:"98765432...", String:"leiw3-mb...", Integer:0) returns LONG:0
Arguments
--------------------
Long start = 1 0x00000001
String string1 = 00150ED4
= "987654321"
String string2 = 00408088
= "leiw3-mbodr-9ewto-nmbio"
Integer compare = 0 0x0000
Instr(long:1, VARIANT:String:"aspower", VARIANT:String:"xue", Integer:0)
Arguments
--------------------
Long start = 1 0x00000001
string1 (variant)
String .bstrVal = 001638B4
= "aspower"
string2 (variant)
String .bstrVal = 0040807C
= "xue"
Integer compare = 0 0x0000
Form2.Caption <-- "qq-msg 3.0 2009(未注册)" (String)
String "qq-msg 3.0 2009(未注册)"
当然我们不知道是不是这样,因为注册码和用户名都进入了查找,我们再用OllyDbg跟一下.现在有smartcheck里的分析,我们可以很容易的下一个有用的断点MSVBVM60.__vbaInStr,果然,很快我们就到了一个可疑的地方.
前面正好有明白的注册码入栈的行为,
00415EE5 . 68 107B4000 push 00407B10 ; /szKey = "zcmm"<--注册表里注册码的位置
00415EEA . 68 D47F4000 push 00407FD4 ; |Section = "clongxue"
00415EEF . 68 D47F4000 push 00407FD4 ; |AppName = "clongxue"
00415EF4 . FF15 BC114000 call dword ptr [<&MSVBVM60.#689>] ; \rtcGetSetting
显然这里就应该是正式的关键位了。
我们看
00415EFC . 8D4D 90 lea ecx, dword ptr [ebp-70]
00415EFF . FF15 EC114000 call dword ptr [<&MSVBVM60.__vbaStrMo>; MSVBVM60.__vbaStrMove 字符串移动
00415F05 . C745 FC 19000>mov dword ptr [ebp-4], 19
00415F0C . C785 30FEFFFF>mov dword ptr [ebp-1D0], 0040807C ; UNICODE "xue"压入用户名比较字符
00415F16 . C785 28FEFFFF>mov dword ptr [ebp-1D8], 8
00415F20 . 6A 01 push 1
00415F22 . 8B55 90 mov edx, dword ptr [ebp-70]
00415F25 . 52 push edx <----这里是我们输入的注册码"123456789"
00415F26 . 68 88804000 push 00408088 ; UNICODE "leiw3-mbodr-9ewto-nmbio"压入注册码比较字符
00415F2B . 6A 00 push 0
00415F2D . FF15 84114000 call dword ptr [<&MSVBVM60.__vbaInStr>; MSVBVM60.__vbaInStr
00415F33 . 8985 20FEFFFF mov dword ptr [ebp-1E0], eax
00415F39 . C785 18FEFFFF>mov dword ptr [ebp-1E8], 3
00415F43 . 6A 01 push 1
00415F45 . 8D45 98 lea eax, dword ptr [ebp-68]
00415F48 . 50 push eax
00415F49 . 8D8D 28FEFFFF lea ecx, dword ptr [ebp-1D8]
00415F4F . 51 push ecx <----这里是我们输入的用户名"aspower"
00415F50 . 6A 00 push 0
00415F52 . 8D95 FCFEFFFF lea edx, dword ptr [ebp-104]
00415F58 . 52 push edx
00415F59 . FF15 5C114000 call dword ptr [<&MSVBVM60.__vbaInStr>; MSVBVM60.__vbaInStrVar
00415F5F . 50 push eax
00415F60 . 8D85 18FEFFFF lea eax, dword ptr [ebp-1E8]
00415F66 . 50 push eax
00415F67 . 8D8D ECFEFFFF lea ecx, dword ptr [ebp-114]
00415F6D . 51 push ecx
00415F6E . FF15 1C114000 call dword ptr [<&MSVBVM60.__vbaVarAn>; MSVBVM60.__vbaVarAnd
00415F74 . 50 push eax
00415F75 . FF15 A8104000 call dword ptr [<&MSVBVM60.__vbaBoolV>; MSVBVM60.__vbaBoolVarNull
00415F7B . 66:8985 C4FDF>mov word ptr [ebp-23C], ax
00415F82 . 8D8D FCFEFFFF lea ecx, dword ptr [ebp-104]
00415F88 . FF15 1C104000 call dword ptr [<&MSVBVM60.__vbaFreeV>; MSVBVM60.__vbaFreeVar
00415F8E . 0FBF95 C4FDFF>movsx edx, word ptr [ebp-23C]
00415F95 . 85D2 test edx, edx
00415F97 0F84 9E000000 je 0041603B <----------------关键一跳,跳了鸭子就飞了!
到这里,真相大白了,原来这个软件就是用instr来查找一下用户名和注册码,而他查找的字符完全是固定值.这样的话,事实上所有的用户名和注册码都必须包含"xue"和"leiw3-mbodr-9ewto-nmbio"字符串,而且必须是连起来的.那么让我们来试一下,输入"xue[boom]aspower"和"leiw3-mbodr-9ewto-nmbio[goodjob]"重新启动,果然,caption后已经没有了未注册的内容,功能也完全没有限制了。
在这里,大家如果有兴趣的话,可以跟进00415F2D . FF15 84114000 call dword ptr [<&MSVBVM60.__vbaInStr>,详细看一下vb是如何进行字符串比较的,相信看了以后对你一定会有很大的帮助的.
看完我的破解过程,你也许觉得这个软件的注册码也太简单了吧,这不等于是白送的么? 我可以告诉你,如今世面上的vb软件10个最少有7个是明码比较的,当然,也许不是完全固定的字符串,但是只要一用到if (string1=string2) then 这样的检验格式,那不出5分钟,你这个软件就可以被破解。而令人惊讶的是,这个软件的版本3.0了,而且在skycn上它的下载量更是超过了10万,如果加上其他软件站的下载,这个软件很可能有超过几十万的用户,即使是这样的一个软件,对于自己的注册码保护措施还是这样的低能,其他的软件可想而知了。
好,在这里讲两句题外的话,在上次的帖子里,有位朋友说了句这样的话。他说我的软件被高手破解的那个功夫也许他自己都能写一个了。说实话,这样想就不太对了,就拿我才分析的这个软件来说,事实上技术含量并不高。你不要被他那广告词所吓倒。你如果认真的分析了整个软件的内容,你就会发现,其实他所谓的监视太简单不过了。他在启动的过程中,会在系统的目录里释放然后启动一个sevesys.exe文件,而且修改启动注册表,让自己自动启动。更加可笑的是,这个文件连自我隐藏都没有做,你只要启动任务管理器,你就可以在里面看见这个文件。详细跟踪这个sevesys.exe文件,你就发现,其实他只是调用了findwindow定时查找窗体,然后利用getwidowtext复制出qq对话框里的内容。
在他的说明里,有个地方这样说,本软件登录时需要密码,您的原始密码是:888,您可以点击“设置”按钮修改密码。没有密码就不能登录本软件,并且本软件对记录下的数据做了很好的加密处理。你可以打开注册表中HKEY_CURRENT_USER\Software\VB and VBA Program Settings\clongxue\clongxue下有个setupmm键值,下面的值就是密码,而且是明码。另外一个地方这样说明6、当去掉“启动QQ实时监控”前的打钩时,本软件即停止监视QQ聊天窗口功能,此时手工删除本软件所在文件夹,即可彻底删除本软件。事实上,在删除掉这个文件夹后,sevesys.exe文件并没有在系统中删除,难道是为了防止这个机密被人发现不删除,还是为了留个后门?非常值得怀疑啊!其实一个软件的技术不是最关键的,倒不一定是最火的软件就是最有技术含量的。同样,你自己觉得别人高手可能不屑破解你的软件,可是你自己的软件连个菜鸟都能对付的话,那又怎么能保证这个菜鸟不给你破解出来呢?
【目标软件】: XX网络电视 8.29
【作者】: aspower
【作者邮箱】: 不能给,怕垃圾邮件啊!
【编写语言】: VB5.0-6.0
【软件介绍】:一个在线收看网络电视的软件,内置100多个电视台,还有大量广播频道。
【使用工具】:peid,olldbg ,smartcheck,filemoniter
【作者声明】: 只是出于研究,没有其他目的。失误之处敬请大家见谅。
这个软件我隐去了软件名,如果需要实例,可以下载我提供的文件包。毕竟大家本是同根生,相煎何太急啊。前面那个软件要不是因为他窥探隐私和类似窃听的功能,我也不能明着来,主要一想到刚上网那会,我注册的n个qq就被这样的所谓o(∩_∩)o技术给弄跑了,想起来就眼泪哗哗的啊。
不多废话,分析软件。该软件什么壳都没有加,不知道是对自己的注册技术很自信呢,还是别的原因。咱们不管了,撸起袖子上吧!
照样操起smartcheck,准备将软件大卸八块。加载软件后,软件显示只能使用45次。好我们进入注册画面,可以看见有三个输入栏,一个是机器码,一个是用户名,一个是输入的注册码。我们随便输入用户名为aspower,注册码是987123456,按下注册,ok弹出一个注册码错误的窗口。有门,这说明是直接验证不重启的。看看smartcheck里面,cmdok_click的行为里出了一长串的字符。我们来仔细看看,里面有没有什么可疑的信息,很快一个string值引起了关注,复制到文本里看看。
Val(String:"5933") returns double:5933 (displayed as single-precision floating point)
Arguments
--------------------
String string1 = 001CA00C
= "5933"
Double (5933) --> Long (5933)
Double 5933
Long 5933
难道又是一个明码的?输入5933到注册码拦里,当,还是一个错误框。看来这个软件用smartcheck是对付不了了。想想也是,要是smartcheck可以随便对付那个vb软件的话,那vb软件早绝种了。好,我们用一个新的工具来对这个软件进行监视吧,filemoniter是用来监视软件读写文件的工具。你问我为什么不用regmon了?嘿嘿,你用用试试,这个软件在启动的时候对注册表有将近5000次的读写,而且全部和注册码无关。所以,我判断这个软件应该是读某个key文件来验证的。
果然,跟踪发现,超级电视启动的时候读取了d盘的Iotmrd.sys文件,看来这个文件里有猫腻。我们随便用个文本工具打开这个文件就会发现,其实里面就是普通文档。
[MyApp]
pt1=5933
pt2=V
Form1Top= 1065
Form1Left= 2625
Form1Height= 8520
Form1Width= 11520 请注意,由于机器的差异上面的内容可能不同,但是意思都是一样的。pt1就是我们刚才看见的5933,而pt2则是个英文字母,是什么意思呢?你可以再打开软件一次,退出再看这个文件。你就会发现,其他都没有变,就这个pt2变了,变成了w,你明白了么?想想看,w不是在v的前面么?这个pt2就记录了你使用的次数,当pt2变为)的时候,你就不能继续使用了。那么你就可以先用到45次,然后来修改这个值,你就又可以使用这个软件了,这里你最多可以用ue或者winhex把这里的acii码值改为ff,那样的话你就可以连续使用212次才过期。
但是我们craker可不会止步于此!下面就带你进入周瑜的思考领域!哦,不好意思说串了,火凤燎原看多了啊!下面就带你进入破解者的思考领域,我们注意到,其实这个文件在最开始的时候并没有安装这个文件,那么肯定是后来生成的,那么我们是不是可以先删除它,然后让他再生成一次呢?说干就干,先删除掉Iotmrd.sys,然后启动ollydbg,用超级字符串查找unicode值,Iotmrd.sys,找到一处,再哪里下段。然后f9开始运行程序,果然,不一会儿,程序在Iotmrd.sys的地方断了下来。我们f8单步行进,并且随时注意d盘下生成没有生成Iotmrd.sys文件,走了大概两百多里地后走到了这里(当然没有那么夸张拉:),
00428EAE . FF15 34424800 CALL DWORD PTR DS:[<&MSVBVM50.#594>] MSVBVM50.rtcRandomize
省略部分代码
00428F5A . FF15 28424800 CALL DWORD PTR DS:[<&MSVBVM50.#593>] MSVBVM50.rtcRandomNext
00428F60 . D99D 04FFFFFF FSTP DWORD PTR SS:[EBP-FC]
00428F66 . D985 04FFFFFF FLD DWORD PTR SS:[EBP-FC]
00428F6C . D80D 28104000 FMUL DWORD PTR DS:[401028]
00428F72 . DFE0 FSTSW AX
00428F74 . A8 0D TEST AL,0D
00428F76 . 0F85 1D4E0000 JNZ supernet.0042DD99
00428F7C . FF15 58434800 CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8IntI4>] MSVBVM50.__vbaR8IntI4
00428F82 . 8D8D 6CFFFFFF LEA ECX,DWORD PTR SS:[EBP-94]
00428F88 . 8BD8 MOV EBX,EAX
00428F8A . FF15 E4414800 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeVar>] MSVBVM50.__vbaFreeVar
00428F90 . 8BCB MOV ECX,EBX
00428F92 . 8B16 MOV EDX,DWORD PTR DS:[ESI]
00428F94 . 0FAFCB IMUL ECX,EBX
00428F97 . 0F80 014E0000 JO supernet.0042DD9E
00428F9D . 81C1 40420F00 ADD ECX,0F4240
很显然了,MSVBVM50.rtcRandomize和 MSVBVM50.rtcRandomNext就是用vb生成了两个随机数,而当代码跑到IMUL ECX,EBX,你会发现ecx和ebx的值是一样的,我这里都是1D54,十进制就是7508。那么IMUL ECX,EBX就是将ecx乘方,然后ADD ECX,0F4240 也就是1D54^2+0f4240。最后结果转换为十进制就是57370064。果然,进入软件后,注册拦的序列号变成了这里算出的57370064,而Iotmrd.sys中的pt1也变成了7508。但是我们这里虽然知道了序列号是怎么算出的,但是离算出注册码还是有很远的距离。
那么怎么办呢?现在我们对msgbox下手,如果这个程序不是调用messagebox这个api来弹出错误的话,那么vb的msg函数通常是MSVBVM50.rtcMsgBox,我们对所有的MSVBVM50.rtcMsgBox下段,然后再次点下注册,ok果然在下面这行断了下来。
004802C0 . FF15 3C424800 CALL DWORD PTR DS:[<&MSVBVM50.#595>] ; MSVBVM50.rtcMsgBox
好现在就来看看要怎么样才能避开这个错误窗口。果然,不远的地方有这样一个跳转能够躲开这个错误提示走到下面去,
0048021D . /0F8D FD030000 JGE supernet.00480620
ok,我们用jmp替换了jge(jge是条件跳转也就是说根据条件跳到某个位置,而jmp则是强迫跳。)再看看可以不可以通过,结果再次输入用户名和注册码,还是弹出了错误窗口。这说明什么?这说明程序根本没有走到这里,只能继续向上找了,向上找了很远很远,(真的很远,翻了六七页代码)终于找到了罪魁祸首,
0047FE3B . 8B06 MOV EAX,DWORD PTR DS:[ESI]
0047FE3D . FF90 A0000000 CALL DWORD PTR DS:[EAX+A0]
0047FE43 . 85C0 TEST EAX,EAX
0047FE45 . 7D 12 JGE SHORT supernet.0047FE59
0047FE47 . 68 A0000000 PUSH 0A0
0047FE4C . 68 C08D4000 PUSH supernet.00408DC0
0047FE51 . 56 PUSH ESI
0047FE52 . 50 PUSH EAX
0047FE53 . FF15 1C424800 CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheckObj>] ; MSVBVM50.__vbaHresultCheckObj
0047FE59 > 8B55 E0 MOV EDX,DWORD PTR SS:[EBP-20]
0047FE5C . 52 PUSH EDX
0047FE5D . FF15 80434800 CALL DWORD PTR DS:[<&MSVBVM50.#581>] ; MSVBVM50.rtcR8ValFromBstr
0047FE63 . FF15 70424800 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFpR8>] ; MSVBVM50.__vbaFpR8
0047FE69 . DB85 40FFFFFF FILD DWORD PTR SS:[EBP-C0]
0047FE6F . DD9D 04FFFFFF FSTP QWORD PTR SS:[EBP-FC]
0047FE75 . DC9D 04FFFFFF FCOMP QWORD PTR SS:[EBP-FC] ; (初始 cpu 选择)
0047FE7B . DFE0 FSTSW AX
0047FE7D . F6C4 40 TEST AH,40
0047FE80 74 07 JE SHORT supernet.0047FE89
0047FE82 . BE 01000000 MOV ESI,1
0047FE87 . EB 02 JMP SHORT supernet.0047FE8B
0047FE89 > 33F6 XOR ESI,ESI
0047FE8B > 8D4D E0 LEA ECX,DWORD PTR SS:[EBP-20]
0047FE8E . FF15 7C434800 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeStr>] ; MSVBVM50.__vbaFreeStr
0047FE94 . 8D4D C8 LEA ECX,DWORD PTR SS:[EBP-38]
0047FE97 . FF15 78434800 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeObj>] ; MSVBVM50.__vbaFreeObj
0047FE9D . F7DE NEG ESI
0047FE9F . 66:85F6 TEST SI,SI
0047FEA2 . 0F84 92030000 JE supernet.0048023A <------------------这里一跳,老母鸡就变鸭了!
只要改为 0f85 92030000 jnz
好,错误框没有弹出了,但是标题拦依然显示为未注册。难道这个只是跳过了错误窗口,而没有使破解成功么?当然有的软件是有多处检测点叫做暗桩,这个软件呢?
我们还是看下d盘的那个Iotmrd.sys文件吧,结果发现里面已经添加了以上内容
pt3=258358
pt4=aaa
这里的pt4正是我输入的注册用户名,而pt3不是我输入的注册码,经过验证发现这个正是正确的注册码。
事实上这个点只是判断是否写入的,结果他写入的时候直接写的程序自身算出来的正确注册码,于是…………庐山瀑布汗啊!
好了,我们现在就可以有好几种完全破解这个软件的方法了,一是用ue或者winhex之类的16进制编辑工具,打开exe文件修改0047FEA2位置的0f84为0f85,然后随意输入用户名和注册码,软件就会自己给你注册了。二就是用内存补丁方式,动态修改0047FE80位置的值。既然是vb程序版,我们当然最好使用程序的方法了。
下面是vb的源码,调试的时候注意,因为系统的不同,可能不一定是每个系统里这个软件的exe名字都是supernettv.exe小写的!大家自行修改!
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare Function OpenProcess Lib "Kernel32" (ByVal dwDesiredAccess As Long, ByValbInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function ReadProcessMemory Lib "Kernel32" (ByVal hProcess As Long, ByVallpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
'定义访问权限常量
Private Const PROCESS_ALL_ACCESS = &H1F0FFF
Private Declare Function WriteProcessMemory Lib "Kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Const TH32CS_SNAPHEAPLIST = &H1
Const TH32CS_SNAPPROCESS = &H2
Const TH32CS_SNAPTHREAD = &H4
Const TH32CS_SNAPMODULE = &H8
Const TH32CS_SNAPALL = (TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE)
Const TH32CS_INHERIT = &H80000000
Const MAX_PATH As Integer = 260
Private Type PROCESSENTRY32
dwSize As Long
cntUsage As Long
th32ProcessID As Long
th32DefaultHeapID As Long
th32ModuleID As Long
cntThreads As Long
th32ParentProcessID As Long
pcPriClassBase As Long
dwFlags As Long
szExeFile As String * MAX_PATH
End Type
Private Declare Function CreateToolhelp32Snapshot Lib "Kernel32" (ByVal lFlags As Long, ByVal lProcessID As Long) As Long
Private Declare Function Process32First Lib "Kernel32" (ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "Kernel32" (ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long
Private Declare Sub CloseHandle Lib "Kernel32" (ByVal hPass As Long)
Dim tvexe As Long
Private Sub Command1_Click()
Dim hWnd As Long, hProcess As Long, pid As Long, address As Long, readBuf As Long
Dim i As Integer
findtv '查找程序是否运行
If tvexe = 0 Then
MsgBox "请先运行超级电视再运行本程序", vbCritical, "失败 "
Else
hProcess = OpenProcess(PROCESS_ALL_ACCESS, False, tvexe) '打开进程
If hProcess = 0 Then
MsgBox "打开进程失败"
Exit Sub
End If
res = "u" '修改数据.把&H47FE80处修改成75H
WriteProcessMemory hProcess, &H47FE80, res, 1, ByVal 0&
End If
End Sub
Private Sub findtv()
Dim hSnapShot As Long, uProcess As PROCESSENTRY32
'Takes a snapshot of the processes and the heaps, modules, and threads used by the processes
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0&)
'set the length of our ProcessEntry-type
uProcess.dwSize = Len(uProcess)
'Retrieve information about the first process encountered in our system snapshot
r = Process32First(hSnapShot, uProcess)
'set graphics mode to persistent
Do While r
t = InStr(1, uProcess.szExeFile, "supernettv.exe")
If t > 0 Then
tvexe = uProcess.th32ProcessID
Exit Sub
End If
'Retrieve information about the next process recorded in our system snapshot
r = Process32Next(hSnapShot, uProcess)
Loop
tvexe = 0
'close our snapshot handle
CloseHandle hSnapShot
End Sub
最后由于这个程序的开始老是弹出网页,跟一下开始就可以发现弹出窗口是使用一个call来完成的 nop掉
8b8504ffffff
50
nop掉弹出网页的call
0042A384 e85fe3fdff
改为nop 9090909090
后记,可能有的朋友已经看的晕头转向了,其实如果要实际讲注册码的计算流程,我还可以继续讲下去,但是我觉得,我要说明的内容已经说到了。如果对算法有更进一步兴趣的朋友可以下载我上传的包来获得更详细的注册吗算法过程。提醒一下,这个程序是用浮点算法来计算的。
从我分析这两个软件的注册算法后,我希望大家了解到以下两点,
第一,破解其实并不象你想象的那么难,因此,如果你用明码比较这样的算法来制作共享软件的注册的话,那肯定是等于完全白送给别人。
第二,一个软件的流行于否,和技术含量的关系并不大。有很多技术含量并不高的软件都有很大的用户量,所以你也一样可能用vb软件开发出来一个流行的软件,重要的是找准目标。
那么,如果你要制作一个共享软件,怎么样能防止别人对你软件的破解呢?
首先,你可以给自己的软件加一个稍微强大一点的壳,像upx或者 aspack这样的壳基本上就跟(***)没什么两样,当然如果有钱购买一些商用的壳,肯定是要比免费能弄到的壳好上很多。
其次,即使是有了一个好壳,也很难抵挡真正高手的破解。我这里可以提供给大家一个简单实用的办法,现在很多软件也在采用,那就是把部分注册用户才可以使用的功能完全屏蔽掉,也就是说在汇编领域把这些代码全部nop掉。这样的话,事实上试用版本完全没有收费版本的功能代码,这样的话,即使被破解这个程序也无法正常试用注册功能。除非破解者能把这些代码自己给补上,但是一般情况是不会有人这样做的。
再次,如果你的程序需要提供全功能试用,或者你代码量和功能本身就不多,那么就应该用其他的办法来保护你的程序,比如利用一些程序smc技术或者利用网络验证等办法,这就是我下两篇文章将给大家介绍和讲解的。
最后,祝各位vb程序员的作品大卖,做员工的人人加工资,做老板的人人发大财。
程序包说明,两个程序的原文件下载地址,第2个软件的详细算法,以及一个vc版本的注册机以及源代码,有需要的可以下载。
========
OD工具破解VB程序的常用断点
bpx hmemcpy 破解万能断点,拦截内存拷贝动作 (注意:Win9x专用断点,XP无效)
bpx Lockmytask 当你用其它断点都无效时可以试一下,这个断点拦截按键的动作
实在找不到断点可以试下面的方法:
bmsg handle wm_gettext 拦截注册码(handle为对应窗口的句柄)
bmsg handle wm_command 拦截OK按钮(handle为对应窗口的句柄)
拦截窗口:
bpx CreateWindow 创建窗口
bpx CreateWindowEx(A/W) 创建窗口
bpx ShowWindow 显示窗口
bpx UpdateWindow 更新窗口
bpx GetWindowText(A/W) 获取窗口文本
拦截消息框:
bpx MessageBox(A) 创建消息框
bpx MessageBoxExA 创建消息框
bpx MessageBoxIndirect(A) 创建定制消息框
拦截警告声:
bpx MessageBeep 发出系统警告声(如果没有声卡就直接驱动系统喇叭发声)
拦截对话框:
bpx DialogBox 创建模态对话框
bpx DialogBoxParam(A/W) 创建模态对话框
bpx DialogBoxIndirect 创建模态对话框
bpx DialogBoxIndirectParam(A/W) 创建模态对话框
bpx CreateDialog 创建非模态对话框
bpx CreateDialogParam(A) 创建非模态对话框
bpx CreateDialogIndirect 创建非模态对话框
bpx CreateDialogIndirectParam(A/W) 创建非模态对话框
bpx GetDlgItemText(A/W) 获取对话框文本
bpx GetDlgItemInt 获取对话框整数值
拦截剪贴板:
bpx GetClipboardData 获取剪贴板数据
拦截注册表:
bpx RegOpenKey(A) 打开子健 ( 例:bpx RegOpenKey(A) if *(esp+8)=='****' )
bpx RegOpenKeyEx 打开子健 ( 例:bpx RegOpenKeyEx if *(esp+8)=='****' )
bpx RegQueryValue(A) 查找子健 ( 例:bpx RegQueryValue(A) if *(esp+8)=='****' )
bpx RegQueryValueEx 查找子健 ( 例:bpx RegQueryValueEx if *(esp+8)=='****' )
bpx RegSetValue(A) 设置子健 ( 例:bpx RegSetValue(A) if *(esp+8)=='****' )
bpx RegSetValueEx(A) 设置子健 ( 例:bpx RegSetValueEx(A) if *(esp+8)=='****' )
注意:“****”为指定子键名的前4个字符,如子键为“Regcode”,则“****”= “Regc”
==================
功能限制拦截断点:
bpx EnableMenuItem 禁止或允许菜单项
bpx EnableWindow 禁止或允许窗口
bmsg hMenu wm_command 拦截菜单按键事件,其中hMenu为菜单句柄
bpx K32Thk1632Prolog 配合bmsg hMenu wm_command使用,可以通过这个断点进入菜单处理程序
应用示例:
CALL [KERNEL32!K32Thk1632Prolog]
CALL [......] <-- 由此跟踪进入菜单处理程序
CALL [KERNEL32!K32Thk1632Epilog]
======================
拦截时间:
bpx GetLocalTime 获取本地时间
bpx GetSystemTime 获取系统时间
bpx GetFileTime 获取文件时间
bpx GetTickCount 获得自系统成功启动以来所经历的毫秒数
bpx GetCurrentTime 获取当前时间(16位)
bpx SetTimer 创建定时器
bpx TimerProc 定时器超时回调函数
拦截文件:
bpx CreateFileA 创建或打开文件 (32位)
bpx OpenFile 打开文件 (32位)
bpx ReadFile 读文件 (32位)
bpx WriteFile 写文件 (32位)
bpx _lcreat 创建或打开文件 (16位)
bpx _lopen 打开文件 (16位)
bpx _lread 读文件 (16位)
bpx _lwrite 写文件 (16位)
bpx _hread 读文件 (16位)
bpx _hwrite 写文件 (16位)
拦截驱动器:
bpx GetDrivetype(A/W) 获取磁盘驱动器类型
bpx GetLogicalDrives 获取逻辑驱动器符号
bpx GetLogicalDriveStringsA(W) 获取当前所有逻辑驱动器的根驱动器路径
拦截狗:
bpio -h 378(或278、3BC) R 378、278、3BC是并行打印端口
bpio -h 3F8(或2F8、3E8、2E8) R 3F8、2F8、3E8、2E8是串行端口
+++++++++++VB程序专用断点:++++++++++
bp__vbaFreeStr 偶发现了VB杀手断点.不管是重起验证.还是有错误提示的VB..下这个断点通杀
bpx msvbvm50!__vbaStrCmp 比较字符串是否相等
bpx msvbvm50!__vbaStrComp 比较字符串是否相等
bpx msvbvm50!__vbaVarTstNe 比较变量是否不相等
bpx msvbvm50!__vbaVarTstEq 比较变量是否相等
bpx msvbvm50!__vbaStrCopy 复制字符串
bpx msvbvm50!__vbaStrMove 移动字符串
bpx MultiByteToWideChar ANSI字符串转换成Unicode字符串
bpx WideCharToMultiByte Unicode字符串转换成ANSI字符串
上面的断点对应VB5程序,如果是VB6程序则将msvbvm50改成msvbvm60即可
VB程序的破解
VB程序使很多朋友感到头痛,主要是VB程序反编译时产生大量的顶!代码,而且也找不到有
用的信息,在动态调试过程中,顶!代码太多,往往迷失于冗余的代码中,找不到方向。 记住VB常用的一些函数:
MultiByteToWideChar 将ANSI字符串转换成UNICODE字符
WideCHatToMultiByte 将UNICODE字符转换成ANSI字符
rtcT8ValFromBstr 把字符转换成浮点数
vbaStrCmp 比较字符串(常用断点)
vbaStrComp 字符串比较(常用断点)
vbaStrCopy 复制字符串
StrConv 转换字符串
vbaStrMove 移动字符串
__vbaVarCat 连接字符串
rtcMidCharVar 在字符串中取字符或者字符串!
__vbaLenBstr 取字符串的长度
vbaVarTstNe 变量比较
vbaVarTstEq 变量比较
rtcMsgBox 显示对话框
VarBstrCmp 比较字符串
VarCyCmp 比较字符串
用OD载入脱壳后的程序,在命令行输入:bpx hmemcpy,然后回车,会弹出程序运行调用的所有的函数,在每个函数上设置好断点!说明:我破VB程序喜欢用这个断点设置方法,通过一步步跟踪,基本可以把握程序保护的思路,所以我破VB程序基本用这个断点,当然你可以用其它的断点,只要能找到关键,任何断点都是用意义的。
关于VB的程序,注册没有提示的二个办法:
第一(提示错误):用GetVBRes来替换里面的提示串,一般是以‘111111’,‘222222’之类的替换
因为:VB,用的字来存放提示还有加了点东东,我们用的工具一般是字节分析。换成‘22222’之类的就是字节了,用静态分析,就有你该的串了。GetVBRes(网上很多,自己下吧)
第二(没有提示):用vbde这个工具(不知道,有没有用过DEDE,是一样思路),主要是找出破解的按钮窗口的位置,来进行跟踪。
先给出修改能正确反编译VB程序的W32DASM的地址:
======================
offsets 0x16B6C-0x16B6D
修改机器码为: 98 F4
======================
VB程序的跟踪断点:
============
MultiByteToWideChar,
rtcR8ValFromBstr,
WideCharToMultiByte,
__vbaStrCmp
__vbaStrComp
__vbaStrCopy
__vbaStrMove
__vbaVarTstNe
rtcBeep
rtcGetPresentDate (时间API)
rtcMsgBox
=========
时间限制断点:
================
CompareFileTime
GetLocalTime
GetSystemTime
GetTimeZoneInformation
msvcrt.diffTime()
msvcrt.Time()
================
VB断点查找方法
1,VB6.0编写,OD载入程序调出注册窗口,alt+e调出可执行模块窗口找到X:\WINDOWS\system32\MSVBVM60.DLL
双击,在ctrl+n调出窗口找到,名称XXXXXXE区段=ENGINE 导出__vbaVarMove双击来到下面地址(可以直接在命令行 bp __vbaVarMove)
回到程序注册窗口点注册被拦断在刚才下断的地址,断后在ctrl+F9,F8回
2,OD载入程序,命令行下断点。
bp rtcMsgBox
堆栈友好提示
确定注册失败按钮返回。接着向上找出点注册按钮执行的代码第一句,可以吗?当然行,根据我们知道程序员写一个事件执行的代码是如这种,
各种语言都差不多。
3,OD载入程序,命令行下断点。
bp rtcMsgBox
任意填入伪注册码 9999999999999999999
确定后中断
堆栈友好提示
确定注册失败按钮返回。
W32Dasm反汇编程序,Shiht+F12
4,VB中的messagebox是一个消息框,汇编中用rtcMsgBox下断点.用olldbg载入程序,Alt+e,在可执行文件模块中找到Msvbvm60.dll,双击它,
在代码窗口点右键-搜索-当前模块中的名称中的rtcMsgBox函数,双击它,在6A362F29 55 PUSH EBP这一句双击下断点,关掉多余的窗口,只留下
cpu调试主窗口,F9运行程序,点?号按钮,随便输入987654321后,回车后立即中断,然后Ctrt+f9执行到返回地址,因为这是msvbvm60的领空,
我们要回到程序领空.秘密记事本弹出message错误提示信息,点确定,向上看 ,再按F8就回到
5,为Microsoft Visual Basic 6.0。先用SmartCheck找到程序比较注册码点,
6,用vb常用比较断点
vbastrcmp
vbastrcomp
vbavartsteq
在od中设断点找注册码
7,用Od载入程序,运行,填入上面的注册码和顺序号。在Od中下断点,Alt+E,双击Msvbvm60运行库,右键-搜索当前模块中的名称,找到Vbastrcmp,双击下断点。
adfafasfasf
========
版权声明:本文标题:VB程序破解 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1726380105h948846.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论