vb 程序破解

WARNING: 此文章非常长,如果你感兴趣但是没耐心看完,可以做个标记然后按浏览器的关闭按钮,等到下次有兴趣了再看。这里我尽量多和详细地解释了VB程序的破解(其实也可看到反破解)的相关东西,把一个简单的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的差不多,上方也是一张表(不过好像更大?),下方也有一段数据,但是再往下拉会发现:

  1. 00408C60   > \55            push ebp
  2. 00408C61   .  8BEC          mov ebp,esp
  3. 00408C63   .  83EC 18       sub esp,18
  4. 00408C66   .  68 26314000   push <jmp.&MSVBVM60.__vbaExceptHandler> ;  SE 句柄安装
  5. 00408C6B   .  64:A1 0000000>mov eax,dword ptr fs:[0]
  6. 00408C71   .  50            push eax
  7. 00408C72   .  64:8925 00000>mov dword ptr fs:[0],esp
  8. 00408C79   .  B8 D0010000   mov eax,1D0
  9. 00408C7E   .  E8 9DA4FFFF   call <jmp.&MSVBVM60.__vbaChkstk>
  10. 00408C83   .  53            push ebx
  11. 00408C84   .  56            push esi
  12. 00408C85   .  57            push edi
  13. 00408C86   .  8965 E8       mov dword ptr ss:[ebp-18],esp
  14. 00408C89   .  C745 EC B8114>mov dword ptr ss:[ebp-14],Timer_nc.0040>
  15. 00408C90   .  8B45 08       mov eax,dword ptr ss:[ebp+8]
  16. 00408C93   .  83E0 01       and eax,1
  17. 00408C96   .  8945 F0       mov dword ptr ss:[ebp-10],eax
  18. 00408C99   .  8B4D 08       mov ecx,dword ptr ss:[ebp+8]
  19. 00408C9C   .  83E1 FE       and ecx,FFFFFFFE
复制代码
这不是我们熟悉的汇编代码了嘛呵呵。
所以说,到目前为止,P-code的防破解作用远远大于Native-code的。
<破解思路1>在API上面下断点跟踪......
这个对于P-code程序是完全无效的了,但是对于Native Code的可以一试。
首先,以他的水平来说,比较注册码肯定是明码比较,但是我没那么无聊去做内存 注册机 ,来爆破试试看。
打开Timer_nc_XXX.exe,在入口点前面的大表这里下断:
  1. 004032E2   $- FF25 B0104000 jmp dword ptr ds:[<&MSVBVM60.__vbaStrCmp>]           ;  MSVBVM60.__vbaStrCmp
复制代码
运行程序,第一次断下先放行,结果窗口就出来了,说明第一次断下就已经到了关键.
重载程序,断下后F8,来到这里:

  1. 734793DA >  FF7424 08       push dword ptr ss:[esp+8]
  2. 734793DE    FF7424 08       push dword ptr ss:[esp+8]
  3. 734793E2    6A 00           push 0
  4. 734793E4    E8 44E6FFFF     call MSVBVM60.__vbaStrComp
  5. 734793E9    C2 0800         retn 8
复制代码
再按Alt+F9来到这里:
  1. 00414BAE   .  50            push eax
  2. 00414BAF   .  68 78734000   push Timer_nc.00407378
  3. 00414BB4   .  E8 29E7FEFF   call <jmp.&MSVBVM60.__vbaStrCmp>
  4. 00414BB9   .  F7D8          neg eax                                              ;  here
  5. 00414BBB   .  1BC0          sbb eax,eax
  6. 00414BBD   .  F7D8          neg eax
  7. 00414BBF   .  F7D8          neg eax
复制代码
这里已经是程序的代码段了,可以更改代码了,继续跟踪看看。
  1. 00414BC1   .  66:8985 68FEF>mov word ptr ss:[ebp-198],ax
  2. 00414BC8   .  8D4D D8       lea ecx,dword ptr ss:[ebp-28]
  3. 00414BCB   .  E8 60E7FEFF   call <jmp.&MSVBVM60.__vbaFreeStr>
  4. 00414BD0   .  8D4D B4       lea ecx,dword ptr ss:[ebp-4C]
  5. 00414BD3   .  E8 B6E6FEFF   call <jmp.&MSVBVM60.__vbaFreeVar>
  6. 00414BD8   .  0FBF85 68FEFF>movsx eax,word ptr ss:[ebp-198]
  7. 00414BDF   .  85C0          test eax,eax
  8. 00414BE1   .  0F84 E60B0000 je Timer_nc.004157CD     ;这里跳了
  9. 00414BE7   .  C745 FC 1F000>mov dword ptr ss:[ebp-4],1F
  10. 00414BEE   .  66:8365 DC 00 and word ptr ss:[ebp-24],0
  11. 00414BF3   .  C745 FC 20000>mov dword ptr ss:[ebp-4],20
  12. 00414BFA   .  68 7C744000   push Timer_nc.0040747C                                 ;  UNICODE "Timer_Regcode.inf"
  13. 00414BFF   .  6A 01         push 1
  14. 00414C01   .  6A FF         push -1
复制代码
可以看到,00414BE1这里跳转跳了,但是应该是不要跳的,因为下面的代码才开始读取注册码. 于是NOP。

  1. 00414C03   .  6A 01         push 1
  2. 00414C05   .  E8 78E6FEFF   call <jmp.&MSVBVM60.__vbaFileOpen>
  3. 00414C0A   >  C745 FC 21000>mov dword ptr ss:[ebp-4],21
  4. 00414C11   .  6A 01         push 1
  5. 00414C13   .  E8 64E6FEFF   call <jmp.&MSVBVM60.#571>
  6. 00414C18   .  0FBFC0        movsx eax,ax
  7. 00414C1B   .  85C0          test eax,eax
  8. 00414C1D   .  0F85 8E000000 jnz Timer_nc.00414CB1
  9. 00414C23   .  C745 FC 22000>mov dword ptr ss:[ebp-4],22
复制代码
但是接下来问题也随之出现,就是说这个文件不存在,却要被打开(00414C05处),会出现异常的,所以这句代码也得NOP。不过这个程序令我惊讶的是,居然自带了异常处理程序!现在不能观摩,否则异常不断产生会导致程序卡死,先要把所有异常都处理好,不过那时候也不能再观摩了.
所以还要NOP:

  1. 00414C05   .  E8 78E6FEFF   call <jmp.&MSVBVM60.__vbaFileOpen>
  2. 00414C13   .  E8 64E6FEFF   call <jmp.&MSVBVM60.#571>
复制代码
另外,为了不浪费程序的感情不断读取文件,我们把00414C1D的jnz改为jmp跳过读取阶段. 然后就到了这里:
  1. 00414CB1   > \C745 FC 2B000>mov dword ptr ss:[ebp-4],2B
  2. 00414CB8   .  6A 01         push 1
  3. 00414CBA   .  E8 ABE5FEFF   call <jmp.&MSVBVM60.__vbaFileClose>
  4. 00414CBF   .  C745 FC 2C000>mov dword ptr ss:[ebp-4],2C
  5. 00414CC6   .  C785 ECFEFFFF>mov dword ptr ss:[ebp-114],Timer_nc.004>
  6. 00414CD0   .  C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],8008
  7. 00414CDA   .  C785 DCFEFFFF>mov dword ptr ss:[ebp-124],Timer_nc.004>
  8. 00414CE4   .  C785 D4FEFFFF>mov dword ptr ss:[ebp-12C],8008
  9. 00414CEE   .  8B45 08       mov eax,dword ptr ss:[ebp+8]
  10. 00414CF1   .  05 A4000000   add eax,0A4
  11. 00414CF6   .  50            push eax
  12. 00414CF7   .  8D85 E4FEFFFF lea eax,dword ptr ss:[ebp-11C]
  13. 00414CFD   .  50            push eax
  14. 00414CFE   .  8D45 B4       lea eax,dword ptr ss:[ebp-4C]
  15. 00414D01   .  50            push eax
  16. 00414D02   .  E8 57E5FEFF   call <jmp.&MSVBVM60.__vbaVarCmpNe>
  17. 00414D07   .  50            push eax
  18. 00414D08   .  8B45 08       mov eax,dword ptr ss:[ebp+8]
  19. 00414D0B   .  05 B4000000   add eax,0B4
  20. 00414D10   .  50            push eax
  21. 00414D11   .  8D85 D4FEFFFF lea eax,dword ptr ss:[ebp-12C]
  22. 00414D17   .  50            push eax
  23. 00414D18   .  8D45 A4       lea eax,dword ptr ss:[ebp-5C]
  24. 00414D1B   .  50            push eax
  25. 00414D1C   .  E8 3DE5FEFF   call <jmp.&MSVBVM60.__vbaVarCmpNe>
  26. 00414D21   .  50            push eax
  27. 00414D22   .  8D45 94       lea eax,dword ptr ss:[ebp-6C]
  28. 00414D25   .  50            push eax
  29. 00414D26   .  E8 39E5FEFF   call <jmp.&MSVBVM60.__vbaVarOr>
  30. 00414D2B   .  50            push eax
  31. 00414D2C   .  E8 6FE5FEFF   call <jmp.&MSVBVM60.__vbaBoolVarNull>
  32. 00414D31   .  0FBFC0        movsx eax,ax
  33. 00414D34   .  85C0          test eax,eax
  34. 00414D36   .  0F84 6D090000 je Timer_nc.004156A9
复制代码
一样的,需要NOP的:
  1. 00414CBA   .  E8 ABE5FEFF   call <jmp.&MSVBVM60.__vbaFileClose>
复制代码
而在00414D36这里,如果跳转就是未注册了. 所以NOP.
继续:

  1. 00414D3C   .  C745 FC 2D000>mov dword ptr ss:[ebp-4],2D
  2. 00414D43   .  C785 ECFEFFFF>mov dword ptr ss:[ebp-114],Timer_nc.004>
  3. 00414D4D   .  C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],8
  4. 00414D57   .  8D95 E4FEFFFF lea edx,dword ptr ss:[ebp-11C]
  5. 00414D5D   .  8B4D 08       mov ecx,dword ptr ss:[ebp+8]
  6. 00414D60   .  83C1 64       add ecx,64
  7. 00414D63   .  E8 08E5FEFF   call <jmp.&MSVBVM60.__vbaVarCopy>
  8. 00414D68   .  C745 FC 2E000>mov dword ptr ss:[ebp-4],2E
  9. 00414D6F   .  C785 ECFEFFFF>mov dword ptr ss:[ebp-114],Timer_nc.004>
  10. 00414D79   .  C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],8
  11. 00414D83   .  8D95 E4FEFFFF lea edx,dword ptr ss:[ebp-11C]
  12. 00414D89   .  8B4D 08       mov ecx,dword ptr ss:[ebp+8]
  13. 00414D8C   .  83C1 74       add ecx,74
  14. 00414D8F   .  E8 DCE4FEFF   call <jmp.&MSVBVM60.__vbaVarCopy>
  15. 00414D94   .  C745 FC 2F000>mov dword ptr ss:[ebp-4],2F
  16. 00414D9B   .  C785 ECFEFFFF>mov dword ptr ss:[ebp-114],Timer_nc.004>
  17. 00414DA5   .  C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],8008
  18. 00414DAF   .  8B45 08       mov eax,dword ptr ss:[ebp+8]
  19. 00414DB2   .  05 A4000000   add eax,0A4
  20. 00414DB7   .  50            push eax
  21. 00414DB8   .  8D85 E4FEFFFF lea eax,dword ptr ss:[ebp-11C]
  22. 00414DBE   .  50            push eax
  23. 00414DBF   .  E8 0CE5FEFF   call <jmp.&MSVBVM60.__vbaVarTstEq>
  24. 00414DC4   .  0FBFC0        movsx eax,ax
  25. 00414DC7   .  85C0          test eax,eax
  26. 00414DC9   .  74 05         je short Timer_nc.00414DD0
  27. 00414DCB   .  E9 4A160000   jmp Timer_nc.0041641A
复制代码
00414DC9这个跳转不跳的话就会产生1个异常,并且计时器的显示不正确,虽然可以正确及时,而且调整窗口是否全屏也会出错,不过现在可以顺便观摩一下异常处理窗口了:

  1. 异常处理程序
  2.    10:48:02:异常发生.已拦截.无需进一步操作. 错误编号:0, 错误描述:, 引起错误在:“计时器”窗口.
  3.    10:48:31:异常发生.已拦截.无需进一步操作. 错误编号:380, 错误描述:无效属性值, 引起错误在:“计时器”窗口.
  4.    10:48:31:异常发生.已拦截.无需进一步操作. 错误编号:380, 错误描述:无效属性值, 引起错误在:“计时器”窗口.
  5.    10:48:31:异常发生.已拦截.无需进一步操作. 错误编号:380, 错误描述:无效属性值, 引起错误在:“计时器”窗口.
  6.    10:48:31:异常发生.已拦截.无需进一步操作. 错误编号:380, 错误描述:无效属性值, 引起错误在:“计时器”窗口.
  7.   程序已经成功处理了异常,这个窗口关闭后也可正常运行,但是一个或多个命令执行失败.
  8.   你可以将本窗口截屏或复制叙述并配以适当文字叙述(比如是在什么情况下引起异常,因为错误对象有时不准确),然后联系作者,会尽快处理问题. 谢谢!
  9.                                                     关闭                                                     复制全部    复制选中    清
复制代码
感觉好高级的。呵呵。扯远了,重载程序,这里需要把00414DC9跳转改成jmp.
继续看:

  1. 00414DD0   > \C745 FC 32000>mov dword ptr ss:[ebp-4],32
  2. 00414DD7   .  C785 ECFEFFFF>mov dword ptr ss:[ebp-114],1
  3. 00414DE1   .  C785 E4FEFFFF>mov dword ptr ss:[ebp-11C],2
  4. 00414DEB   .  C785 DCFEFFFF>mov dword ptr ss:[ebp-124],1
  5. 00414DF5   .  C785 D4FEFFFF>mov dword ptr ss:[ebp-12C],2
  6. 00414DFF   .  8D85 E4FEFFFF lea eax,dword ptr ss:[ebp-11C]
  7. 00414E05   .  50            push eax
  8. 00414E06   .  8B45 08       mov eax,dword ptr ss:[ebp+8]
  9. 00414E09   .  05 A4000000   add eax,0A4
  10. 00414E0E   .  50            push eax
  11. 00414E0F   .  8D45 B4       lea eax,dword ptr ss:[ebp-4C]
  12. 00414E12   .  50            push eax
  13. 00414E13   .  E8 3AE4FEFF   call <jmp.&MSVBVM60.__vbaLenVar>
  14. 00414E18   .  50            push eax
  15. 00414E19   .  8D85 D4FEFFFF lea eax,dword ptr ss:[ebp-12C]
  16. 00414E1F   .  50            push eax
  17. 00414E20   .  8D85 30FEFFFF lea eax,dword ptr ss:[ebp-1D0]
  18. 00414E26   .  50            push eax
  19. 00414E27   .  8D85 40FEFFFF lea eax,dword ptr ss:[ebp-1C0]
  20. 00414E2D   .  50            push eax
  21. 00414E2E   .  8B45 08       mov eax,dword ptr ss:[ebp+8]
  22. 00414E31   .  05 08010000   add eax,108
  23. 00414E36   .  50            push eax
  24. 00414E37   .  E8 1CE4FEFF   call <jmp.&MSVBVM60.__vbaVarForInit>
  25. 00414E3C   .  8985 F8FDFFFF mov dword ptr ss:[ebp-208],eax
  26. 00414E42   .  E9 0B020000   jmp Timer_nc.00415052
  27. ......
  28. 00415052   > \83BD F8FDFFFF>cmp dword ptr ss:[ebp-208],0
  29. 00415059   .^ 0F85 E8FDFFFF jnz Timer_nc.00414E47


复制代码
00415059此处也得NOP。
经过了3段类似代码以后,终于来到关键跳(前面跳转必须全部改对才能执行到这里哦):
  1. 00415316   .  E8 FBDEFEFF   call <jmp.&MSVBVM60.__vbaVarCmpGe>
  2. 0041531B   .  50            push eax
  3. 0041531C   .  8D45 84       lea eax,dword ptr ss:[ebp-7C]
  4. 0041531F   .  50            push eax
  5. 00415320   .  E8 75DFFEFF   call <jmp.&MSVBVM60.__vbaVarAnd>
  6. 00415325   .  50            push eax
  7. 00415326   .  E8 75DFFEFF   call <jmp.&MSVBVM60.__vbaBoolVarNull>
  8. 0041532B   .  0FBFC0        movsx eax,ax
  9. 0041532E   .  85C0          test eax,eax
  10. 00415330   .  0F84 4F020000 je Timer_nc.00415585       ;***
复制代码
00415330直接NOp了,这下后面的代码就是设置成已注册的了!

  1. 00415336   .  C745 FC 3D000>mov dword ptr ss:[ebp-4],3D
  2. 0041533D   .  8B45 08       mov eax,dword ptr ss:[ebp+8]
  3. 00415340   .  8B00          mov eax,dword ptr ds:[eax]
  4. 00415342   .  FF75 08       push dword ptr ss:[ebp+8]
  5. 00415345   .  FF90 24030000 call dword ptr ds:[eax+324]
  6. 0041534B   .  50            push eax
  7. 0041534C   .  8D45 CC       lea eax,dword ptr ss:[ebp-34]
  8. 0041534F   .  50            push eax
  9. 00415350   .  E8 23E0FEFF   call <jmp.&MSVBVM60.__vbaObjSet>
  10. 00415355   .  8985 68FEFFFF mov dword ptr ss:[ebp-198],eax
  11. 0041535B   .  6A 00         push 0
  12. 0041535D   .  8B85 68FEFFFF mov eax,dword ptr ss:[ebp-198]
  13. 00415363   .  8B00          mov eax,dword ptr ds:[eax]
  14. 00415365   .  FFB5 68FEFFFF push dword ptr ss:[ebp-198]
  15. 0041536B   .  FF90 94000000 call dword ptr ds:[eax+94]
  16. 00415371   .  DBE2          fclex
复制代码
很好,并且没有了注册按钮,而且程序开启还会显示已注册!(虽然注册名和注册码都是空的。)
先保存到文件,再来看看关于窗口,反正看了也不会怎么样吧。(因为某些原因,需要把学号和学校代号抹掉,呵呵)
  1. 关于
  2.           计时器 中文版 版本1.7 构建23
  3.            此软件由****开发.
  4.     Copyright (c) 2015 ****. 保留所有权利.
  5.             你正在使用演示版(未注册)
  6.                 本软件尚未注册!

  7.   注册名:未注册
  8.   注册码:为了支持软件开发,请及时注册软件! (暂
  9.   未对软件试用进行时间限制)

  10.                       关闭
复制代码
一连3个未注册提示,额,有一种不祥的预感,不过至少现在没有注册提示框. 并且设置窗口大小、是否置顶都没出现任何问题。
于是,我再手贱地按了一下“倒”按钮(即倒计时)。结果:
  1. 12345678901234567890
  2. 123456789
  3.       
  4.             
  5.     关于 返回
复制代码
额,果然有暗桩。我一开始以为本来功能就没有的,结果我用他给我的注册码一试,发现还是有倒计时功能而且完整的。所以再次拖入OD调试。
不过这个反破解有点坑,虽然不是最坑,但是想退出程序还真难!按“返回”按钮没有任何用处,按了开始计时按钮提示"00:00已用完."(这是正常的,因为我还没有设置过多长时间的倒计时),于是又一次手贱按了“设”按钮(设置),结果倒计时窗体不见了,只有一个设置窗口。再次手贱设置完了倒计时时间,还勾选了"允许超时",最后一次手贱就按了“关”按钮(关闭设置窗口),结果程序一个窗口都不见了......好窘。打开任务管理器,发现还在运行啊:
  1. taskmgr.exe                  Admin...  00     5,476 K
  2. Timer_nc_none.exe            Admin...  00     5,640 K       ;***
  3. NOTEPAD.EXE                  Admin...  00       832 K
复制代码
算了,先结束了进程,再载入OD调试看看:
按了一下“倒”按钮,发现真的又一次读取了注册码:
  1. 00427562   .  F7D8          neg eax
  2. 00427564   .  1BC0          sbb eax,eax
  3. 00427566   .  F7D8          neg eax
  4. 00427568   .  F7D8          neg eax
  5. 0042756A   .  66:8985 68FEF>mov word ptr ss:[ebp-198],ax
  6. 00427571   .  8D4D D8       lea ecx,dword ptr ss:[ebp-28]
  7. 00427574   .  E8 B7BDFDFF   call <jmp.&MSVBVM60.__vbaFreeStr>
  8. 00427579   .  8D4D B4       lea ecx,dword ptr ss:[ebp-4C]
  9. 0042757C   .  E8 0DBDFDFF   call <jmp.&MSVBVM60.__vbaFreeVar>
  10. 00427581   .  0FBF85 68FEFF>movsx eax,word ptr ss:[ebp-198]
  11. 00427588   .  85C0          test eax,eax
  12. 0042758A   .  0F84 0F0D0000 je Timer_nc.0042829F
  13. 00427590   .  C745 FC 07000>mov dword ptr ss:[ebp-4],7
  14. 00427597   .  66:8365 DC 00 and word ptr ss:[ebp-24],0
  15. 0042759C   .  C745 FC 08000>mov dword ptr ss:[ebp-4],8
  16. 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程序试试看:
  1. SmartCheck汉化版
  2.   Timer_p-code.exe 被编到p代码
  3.    SmartCheck 是不能的提供调试工程信息为编了的p代码。
  4.   从 SmartCheck 得到,保证与这些 Visual Basic 编译器背景造这
  5.   个工程:
  6.      - 编到本机代码
  7.      - 没有优化
  8.      - 创造符号调试信息
  9.          继续打开程序( )               不打开程序( )
  10.      不显示出这条消息( )
复制代码
悲剧的是,貌似同学给我的这几个程序都不符合要求:
  1. Timer_nc_none.exe - 程序结果
  2.    类型                              Qty.     合计 (字节)    类型            
  3.                                                                                                                           No details)
  4.                                                                               No events

  5.               Results               Events

  6. No source file
复制代码
于是,我联系同学,让他再给我发个符合要求的......终于来了,Timer_nc_none_dbg.exe就是这个了。(从这里可以看出,SMARTCHECK是为编程程序员设计的,不是为逆向而生......)
试试看......
what??还是没有结果,那这个程序就扔掉了吧。没用啊。
不过,幸好,我在扔掉前想到会不会是因为我用的是 绿色版 的原因,于是我费尽千辛万苦找到6.20 RC2 Retail安装包,安装以后发现要输 序列号 ,不管,先用,结果发现程序没有正确的序列号就不能调试其他程序,显示试用期已过,于是爆破SCShell.dll,可以正常使用了。
结果再测试......发现其实之前的非P-code文件也是可以正常调试的,甚至被优化的也可正常调试,而且P-code程序也可以调试,而且也有有用的东西,只不过一些语句不太明确而已......真是白忙活了,绿色版害死人啊. 
好吧,打开程序,调试,由于Timer事件不断产生,所以我们就点击一次注册按钮,再点击一次“倒”按钮,发现真的好清楚:
  1.     Timer_nc_none.exe - 程序结果
  2.    类型                              Qty.     合计 (字节)    类型            
  3.                                                                                                                           No details)
  4.                                                                               No events
  5.               Results               Events
  6. No source file
  7.                   Thread 0 [thread id:2360 (0x938)]
  8.               Event reporting started: 2015-02-10 12:37:35
  9.                   Form1 (Form) created
  10.                   Form1_Load
  11.                        OnError
  12.                        Form1.hWnd
  13.                        GetSystemMenu returns HMENU:6D058F
  14.                        RemoveMenu returns BOOL:1
  15.                        Form1.hWnd
  16.                        GetSystemMenu returns HMENU:6D058F
  17.                        RemoveMenu returns BOOL:1
  18.                        Form1.hWnd
  19.                        GetSystemMenu returns HMENU:6D058F
  20.                        RemoveMenu returns BOOL:1
  21.                        Form1.hWnd
  22.                        SetWindowPos returns BOOL:1
  23.                        sfd.Text <-- "1" (String)
  24.                        Timer1.Interval <-- 1000 (Long)
  25.                        Form1.Caption <-- "计时器" (String)
  26.                        Label1.Caption <-- "00:00" (String)
  27.                        Command1.Caption <-- "退出" (String)
  28.                        comd2.Caption <-- "√" (String)
  29.                        Command3.Caption <-- "X" (String)
  30.                        Command4.Caption <-- "‖" (String)
  31.                        Command7.Caption <-- "大" (String)
  32.                        Command8.Caption <-- "关于" (String)
  33.                        Command9.Caption <-- "常" (String)
  34.                        Text1.Visible <-- False (Boolean)
  35.                        Text2.Visible <-- False (Boolean)
  36.                        Timer1.Enabled <-- False (Boolean)
  37.                        comd2.Enabled <-- True (Boolean)
  38.                        Command3.Enabled <-- False (Boolean)
  39.                        Command4.Enabled <-- False (Boolean)
  40.                        sfd.Visible <-- False (Boolean)
  41.                        Timer2.Enabled <-- False (Boolean)
复制代码
看了老半天,没发现这些语句如何修改,于是只能做追码工具了......
我们先输入注册名注册码:(此程序貌似支持中文注册名啊......)
  1. 用户名:dsong@吾爱破解论坛 WwW.52PoJie.Cn
  2. 注册码:01234567890ABCDEFabcdef
复制代码
额,然后同一目录下生成了明码的注册码存储......
然后是长长的检验和计算过程:
  1.                       Dir                          //获取目录
  2.                        Open                         //打开
  3.                        EOF                         //是否到最后一行
  4.                        LineInputNum                         //输入
  5.                        EOF                         //是否到最后一行
  6.                        LineInputNum                         //输入
  7.                        EOF                         //....
  8.                        Close                         //关闭
  9.                        Len returns LONG:1243848
  10.                        Mid
  11.                        OnError
  12.                        Asc returns Integer:100
  13.                        Hex
  14.                        Resume
  15.                        Visual Basic Runtime Error 20: 无错误恢复
  16.                        Handling Visual Basic Runtime Error 20
  17.                        Resuming from Visual Basic Runtime Error
  18.                        Resume
  19.                        String ("&H64") --> Long (100)
  20.                        Hex
  21.                        Mid
  22.                        OnError
  23.                        Asc returns Integer:115
  24.                        Hex
  25.                        Resume
  26.                        Visual Basic Runtime Error 20: 无错误恢复
  27.                        Handling Visual Basic Runtime Error 20
  28.                        Resuming from Visual Basic Runtime Error
  29.                        Resume
  30.                        String ("&H73") --> Long (115)
  31.                        Hex
  32.                        Mid
  33.                        OnError
  34.                        Asc returns Integer:111
  35.                        HexResume
  36.                        Visual Basic Runtime Error 20: 无错误恢复
  37. .......
  38.                        Hex
  39.                        String ("&HA0") --> Long (160)
  40.                        String ("&HA0") --> Long (160)
  41.                        Hex
  42.                        String ("&H56") --> Long (86)
  43.                        String ("&H56") --> Long (86)
  44.                        Chr
  45.                        Resume
  46.                        Visual Basic Runtime Error 20: 无错误恢复
  47.                        Handling Visual Basic Runtime Error 20
  48.                        Resuming from Visual Basic Runtime Error
  49.                        Resume
  50.                        Mid
  51.                        OnError
  52.                        String ("&HBE") --> Long (190)
  53.                        String ("&HBE") --> Long (190)
  54.                        String ("&HBE") --> Long (190)
  55.                        Hex
  56.                        String ("&H74") --> Long (116)
  57.                        String ("&H74") --> Long (116)
  58.                        Chr
  59.                        Resume
  60.                        Visual Basic Runtime Error 20: 无错误恢复
  61.                        Handling Visual Basic Runtime Error 20
  62.                        Resuming from Visual Basic Runtime Error
  63.                        Resume
  64.                        Mid
  65.                        OnError
  66.                        String ("&H0A") --> Long (10)
  67.                        String ("&H0A") --> Long (10)
  68.                        Hex
  69.                        String ("&H3A") --> Long (58)
  70.                        String ("&H3A") --> Long (58)
  71.                        String ("&H3A") --> Long (58)
  72.                        Chr
  73. ......
  74.                        Mid
  75.                        OnError
  76.                        Mid
  77.                        OnError
  78.                        Mid
  79.                        OnError
  80.                        Mid
  81.                        OnError
  82.                        Mid
  83.                        OnError
  84.                        Mid
  85.                        OnError
  86.                        Mid
  87.                        OnError
  88.                        Mid
  89.                        OnError
  90.                        Trim               //注册码明码出现
  91.                        Len returns LONG:1243832               //注册码明码出现
  92.                        Timer2.Interval <-- 1000 (Long)               //定时弹出提醒注册窗口,看到这里要往前找
  93.                        Timer2.Enabled <-- True (Boolean)
  94.                        Form1.Height
  95.                        Label1.FontSize
  96.                        Form1.Height
  97.                        Label1.Height
  98.                        Form1.Width
  99.                        Label1.Width
  100.                        Text1.Text <-- "dsong@吾爱破解论坛 WwW.52PoJie.Cn" (String)               //注册名
  101.                        Text2.Text <-- "01234567890ABCDEFabcdef" (String)        //注册码(假)
  102.                        csy.Visible <-- False (Boolean)
  103.                        csy.Text <-- "0" (String)
  104.                        Resume
  105.                        Visual Basic Runtime Error 20: 无错误恢复
  106.                        Handling Visual Basic Runtime Error 20
  107.                        Resuming from Visual Basic Runtime Error
  108.                        Resume
复制代码
在注册码明码出现的时候,看旁边的细节窗口:
string (variant)
      String  .bstrVal = 001559A4
               = "h1141X:if:2Vt:y78OTEiMr34VyX11HaF"
这就是我们的注册码了。使用这个注册码注册一下,然后看看关于窗口:
  1. 关于
  2.           计时器 中文版 版本1.7 构建23
  3.            此软件由****开发.
  4.     Copyright (c) 2015 ****. 保留所有权利.
  5.             你正在使用演示版(已注册)
  6.                 本软件已授权给:

  7.   注册名:dsong@吾爱破解论坛 WwW.52PoJie.Cn
  8.   注册码:h1141X:if:2Vt:y78OTEiMr34VyX11HaF

  9.                       关闭
复制代码

不过只看关于窗口是不够的,因为它这个程序只要检测到输入过注册码,就会显示已注册,所以我们再来按“倒”按钮:<--反破解思路
  1. 倒计时 已注册
  2. 00:00   
  3.             
  4.     关于 返回
复制代码
发现一切正常(这就好啦哈哈).
(其实如果软件设计成注册码分段验证的会更好,这样的话追码是不可能了,算法分析也会有阻碍.)
---拓展:算法分析
它这个算法也不难,不过是单向的,如果反过来就会检验错误。呵呵。所以只能是明码比较。算法贴出来,让他自己去改代码去。
由于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虚拟机的运作的成品啊!
应该说,它可以直接把程序算法给爆出来,如下:
  1.   loc_412296:   If CBool((global_164 <> vbNullString) Or (global_180 <> vbNullString)) Then
  2.    loc_4122A3:     global_100 = vbNullString
  3.    loc_4122B1:     global_116 = vbNullString
  4.    loc_4122C5:     If (global_164 = vbNullString) Then
  5.    loc_4122CA:       Exit Sub
  6.    loc_4122CB:     End If
  7.    loc_4122E2:     For var_108 = 1 To Len(global_164): global_264 = var_108 'Variant
  8.    loc_41231B:       global_244 = ZFto16(CStr(Mid(global_164, CLng(global_264), 1)))
  9.    loc_41235B:       global_244 = CStr((CVar(CLng("&H" & global_244)) + (global_264 * 4)))
  10.    loc_41238E:       global_100 = global_100 & Hex(global_244)
  11.    loc_4123A1:     Next var_108 'Variant
  12.    loc_4123BD:     global_212 = Trim(global_100)
  13.    loc_4123DB:     For var_12C = 1 To Len(global_212): global_264 = var_12C 'Variant
  14.    loc_412421:       var_10C = ZFto16ZF(CStr(Mid(global_212, CLng(((2 * global_264) - 1)), 2)))
  15.    loc_412433:       global_116 = global_116 & CVar(var_10C)
  16.    loc_41244D:     Next var_12C 'Variant
  17.    loc_412469:     global_228 = Trim(global_116)
  18.    loc_41249C:     If CBool((global_180 = global_228) And (Len(global_228) >= 10)) Then
  19.    loc_4124AD:       Me.Command5.Visible = False
  20.    loc_4124BF:       global_296 = 0
  21.    loc_4124D4:       Me.Timer3.Interval = &H7D0
  22.    loc_4124EA:       Me.Timer3.Enabled = True
  23.    loc_412500:       Me.Command6.Enabled = True
  24.    loc_412512:       global_312 = 1
  25.    loc_412519:     Else
  26.    loc_41252C:       Me.Timer2.Interval = &H3E8
  27.    loc_412542:       Me.Timer2.Enabled = True
  28.    loc_412554:       global_312 = 0
  29.    loc_412558:     End If
  30.    loc_41255D:   Else
  31.    loc_412570:     Me.Timer2.Interval = &H3E8
  32.    loc_412586:     Me.Timer2.Enabled = True
  33.    loc_412598:     global_312 = 0
  34.    loc_41259C:   End If
  35.    loc_4125A1: Else
  36.    loc_4125B4:   Me.Timer2.Interval = &H3E8
  37.    loc_4125CA:   Me.Timer2.Enabled = True
  38.    loc_4125DC:   global_312 = 0
  39.    loc_4125E0: End If
复制代码
恩,挺变态的吧,再试试看Native-code的:
  1.   loc_00414BC1: var_198 = (Dir("Timer_Regcode.inf", 7) = vbNullString)
  2.    loc_00414BE1: If var_198 = 0 Then GoTo loc_004157CD
  3.    loc_00414C05: Open "Timer_Regcode.inf" For Input As #1 Len = -1
  4.    loc_00414C0A: 
  5.    loc_00414C1D: If EOF(1) <> 0 Then GoTo loc_00414CB1
  6.    loc_00414C35: Line Input #1, Me
  7.    loc_00414C47: If var_24 <> 0 Then GoTo loc_00414C67
  8.    loc_00414C62: ecx = Me
  9.    loc_00414C67: 'Referenced from: 00414C47
  10.    loc_00414C73: If var_24 <> 1 Then GoTo loc_00414C93
  11.    loc_00414C8E: ecx = Me
  12.    loc_00414C93: 'Referenced from: 00414C73
  13.    loc_00414C9E: var_24 = var_24 + 0001h
  14.    loc_00414CA8: var_24 = var_24
  15.    loc_00414CAC: GoTo loc_00414C0A
  16.    loc_00414CB1: 'Referenced from: 00414C1D
  17.    loc_00414CBA: Close #1
  18.    loc_00414CC6: var_114 = vbNullString
  19.    loc_00414CDA: var_124 = vbNullString
  20.    loc_00414D02: var_ret_1 = (Me <> vbNullString)
  21.    loc_00414D1C: var_ret_2 = (Me <> vbNullString)
  22.    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)
  23.    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
  24.    loc_00414D43: var_114 = vbNullString
  25.    loc_00414D63: ecx = vbNullString
  26.    loc_00414D6F: var_114 = vbNullString
  27.    loc_00414D8F: ecx = vbNullString
  28.    loc_00414D9B: var_114 = vbNullString
  29.    loc_00414DC9: If (Me = vbNullString) = 0 Then GoTo loc_00414DD0
  30.    loc_00414DCB: GoTo loc_0041641A
  31.    loc_00414DD0: 'Referenced from: 00414DC9
  32.    loc_00414E37: For Me = 1 To Len(Me) Step 1
  33.    loc_00414E42: GoTo loc_00415052
  34.    loc_00414E47: 
  35.    loc_00414EA4: var_eax = Timer.1788
  36.    loc_00414EAA: var_198 = Timer.1788
  37.    loc_00414EEC: ecx = var_2C
  38.    loc_00414F3A: var_ret_4 = CLng("&H" & eax+000000F4h)
  39.    loc_00414F3F: var_124 = var_ret_4
  40.    loc_00414F98: var_2C = var_ret_4 + Me * 4
  41.    loc_00414FA8: ecx = var_2C
  42.    loc_00415012: ecx = Me & Hex(Me)
  43.    loc_00415047: Next Me
  44.    loc_0041504C: var_208 = Next Me
  45.    loc_00415052: 'Referenced from: 00414E42
  46.    loc_00415059: If var_208 <> 0 Then GoTo loc_00414E47
  47.    loc_00415082: ecx = Trim(Me)
  48.    loc_004150F6: For Me = 1 To Len(Me) Step 1
  49.    loc_00415101: GoTo loc_00415284
  50.    loc_00415106: 
  51.    loc_004151AD: var_eax = Timer.1792
  52.    loc_004151B3: var_198 = Timer.1792
  53.    loc_004151EC: var_210 = var_2C
  54.    loc_004151FC: var_84 = var_210
  55.    loc_0041522E: ecx = Me & var_210
  56.    loc_00415279: Next Me
  57.    loc_0041527E: var_20C = Next Me
  58.    loc_00415284: 'Referenced from: 00415101
  59.    loc_0041528B: If var_20C <> 0 Then GoTo loc_00415106
  60.    loc_004152B4: ecx = Trim(Me)
  61.    loc_00415320: var_ret_B = (Me = Me) And (Len(Me) >= 10)
  62.    loc_00415330: If CBool(var_ret_B) = 0 Then GoTo loc_00415585
  63.    loc_0041536B: Command5.Visible = False
  64.    loc_00415373: var_19C = eax
  65.    loc_004153DB: ecx = False
  66.    loc_00415418: Timer3.Interval = CInt(2000)
  67.    loc_0041541D: var_19C = eax
  68.    loc_00415490: Timer3.Enabled = True
  69.    loc_00415495: var_19C = eax
  70.    loc_00415508: Command6.Enabled = True
  71.    loc_00415510: var_19C = eax
  72.    loc_0041557B: ecx = CInt(1)
  73.    loc_00415580: GoTo loc_004156A4
  74.    loc_00415585: 'Referenced from: 00415330
  75.    loc_004155BD: Timer2.Interval = CInt(1000)
  76.    loc_004155C2: var_19C = eax
  77.    loc_00415635: Timer2.Enabled = True
  78.    loc_0041563A: var_19C = eax
  79.    loc_0041569F: ecx = False
  80.    loc_004156A4: 'Referenced from: 00415580
  81.    loc_004156A4: GoTo loc_004157C8
  82.    loc_004156A9: 'Referenced from: 00414D36
  83.    loc_004156E1: Timer2.Interval = CInt(1000)
  84.    loc_004156E6: var_19C = eax
  85.    loc_00415759: Timer2.Enabled = True
  86.    loc_0041575E: var_19C = eax
  87.    loc_004157C3: ecx = False
  88.    loc_004157C8: 'Referenced from: 004156A4
  89.    loc_004157C8: GoTo loc_004158EC
  90.    loc_004157CD: 'Referenced from: 00414BE1
  91.    loc_00415805: Timer2.Interval = CInt(1000)
  92.    loc_0041580A: var_19C = eax
  93.    loc_0041587D: Timer2.Enabled = True
  94.    loc_00415882: var_19C = eax
  95.    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的对应,对于修改爆破程序是有必要稍微了解一些的,否则代码你看不懂的话怎么破解呢。
我们先来看帮助文件,我这里直接打开会显示“已取消到该网页的导航”,对于这种问题解决办法:
右键属性,在下部看到一个“安全”,按旁边的“解除锁定”即可。最后记得要按确定.
  1. 安全:       此文件来自其他计算机,可能
  2.              被阻止以帮助保护该计算机。   解除锁定( )
复制代码
然后就正常了。点击目录中“操作码与助记符列表”,然后点击"标准设置",就是长长的一列对照表了。因为P-code与Intel的汇编代码完全不一样,所以得像初学OD那样先掌握一些P-code的含义。
目光聚焦到了这里:
  1. 1Eh Branch 3 
  2. 1Ch BranchF 3 
  3. 5Ch BranchFVar 3 
  4. 1Dh BranchT 3
复制代码
这里可以知道,1c就代表jnz,1d就代表je,1e就代表jmp。(至少我当时是这么稚嫩地认为的。)然后我就到处找,汇编中的NOP在P-code里面该怎么表示呢?我在里面看这张表,找了三遍都没找到相关指令。然后无意中看到了这篇文章: http://blog.sina.com.cn/s/blog_5000f4c901013iiy.html
里面有提到:
  1. ...... 所以解密插入一段代码是基本功,但在p-code里这样做比INTEL难。   看了下面的说明,你就会明白p-code语言插入语句为什么困难,如果你懂INTEL汇编,这事并不太难,加句NOP(90)很easy,但p-code不一样,他的语句大部分都与堆栈有关,比如:p-code里面转向语句一共有三个,
  2. Branch (1E) ---- 无条件跳转
  3. BranchT(1D) ---- 栈顶数据为真则跳转
  4. BranchF(1F) ---- 栈顶数据为假则跳转
  5. 第一次接触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研究最大的成果也就这么几个。扯远了,来看看程序。界面如下:
  1. WKTVBDebugger v1.3 / 汉化:小生我怕怕[LCG]
  2.    -Code 代码源:  Locs. Addr: 0012FA94h                      Proc. Range:   411F14h-  412838h                       堆栈:
  3.   00411F14: 00 LargeBos
  4.   00411F16: 00 LargeBos                                                            0012F8B4: 00 00 00 00 00 00 00 00
  5.                                                                                    0012F8AC: 80 FF 12 00 00 00 00 00                       ESP
  6.   00411F18: 4B OnErrorGoto 004126ECh                                               0012F8A4: 38 28 41 00 6B 3B 0E 66
  7.   00411F1B: 00 LargeBos                                                            0012F89C: EC FA 12 00 B4 F8 12 00                                Byte
  8.   00411F1D: 04 FLdRfVar 0012FA64h                                                  0012F894: 98 DF 15 00 15 1F 41 00
  9.   00411F20: 05 ImpAdLdRf                                                           0012F88C: EC FA 12 00 02 02 00 00                      
  10.                                                                                    0012F884: 00 00 00 00 00 00 00 00
  11.   00411F23: 24 NewIfNullPr Form1 004040FC                                                                                                           Word
  12.                                                                                    0012F87C: 00 00 00 00 00 00 00 00
  13.   00411F26: 0D VCallHresult get__ipropHWNDFORM                                     0012F874: 8C F8 12 00 44 FF 00 10
  14.   00411F2B: F5 LitI4: -> 0h 0                                                      0012F86C: C4 FA 12 00 6C F8 12 00                                Dword
  15.   00411F30: 6C ILdRf 00000000h                                                     0012F864: 0A 00 00 00 44 F8 12 00
  16.                                                                                    0012F85C: F8 FF FF FF B4 F8 12 00
  17.   00411F33: 5E ImpAdCallI4 user32!GetSystemMenu                                    0012F854: 10 00 00 00 58 4D 05 10
  18.   00411F38: 71 FStR4                                                               0012F84C: 00 00 00 00 12 00 12 00                        
  19.   00411F3B: 3C SetLastSystemError                                                  0012F844: FF 00 00 FF 7A 9F 80 7C                                EBP
  20.   00411F3C: F5 LitI4: -> 1000h 4096                                                0012F83C: 9A 9A 83 7C 80 9F 80 7C
  21.   00411F41: F5 LitI4: -> FFFFF060h -4000                                                                                                             ESP
  22.   00411F46: 6C ILdRf 00000000h
  23.   00411F49: 0A ImpAdCallFPR4 user32!RemoveMenu
  24.   00411F4E: 3C SetLastSystemError                                                                                                                    Enable
  25.   00411F4F: 00 LargeBos

  26.                                                                                                                                                                                                                                                                                   EBP
  27.                                                                                                                                                                                        上一页                                         下一页 >
  28.                                                                                                                                                                                                            内存转存 (Ctrl+M)
  29.                                                                                                                                                                                                               加载模块符号
  30.                                                                                                                                                                                                          字符串参考. (Ctrl+S)

  31.   Op: 00 02                                                                 文件偏移:      00011F14                        编辑                              跟踪命令:                                                        断点

  32.   Form1!00411F14
  33.                                                                                                                                                                                        单步跟踪 (F8)                                                API (Ctrl+B)
  34. 堆栈转存是启用的,相对到 ESP<-EBP.
  35.                                                                                                                                                                                      执行到返回 (F12)                                         操作码(Ctrl+O)
  36.                                                                                                                                                                                    跟踪当前指令 (F10)                                     执行断点 (Ctrl+E)
  37.                                                                                                                                                                                             运行 (F5)                                             跟踪X行代码 (F6)
  38.    反汇编信息保存           保存信息                  选项                    管理窗口 (Ctrl+F)                     管理杂项
  39.                                            清除日志                  帮助                    深层信息 (Ctrl+I)                      高级信息
  40.      命令 >
复制代码
比较有用的适合刚接触的就是“管理窗口 (Ctrl+F)”,使用这个,可以轻松地对程序载入、按钮按下等下断点。我们打开它,选择Form1(加载时默认的主窗口,如果不是可以一个一个慢慢试)。然后它会给你地址:
  1. 对象属性
  2.   Form1
  3. 地址: 004040FCh
  4.   Picture
  5.   Label
  6.   TextBox
  7.   Frame
  8.   Command
  9.   CheckBox
  10.   Option
  11.   ComboBox
  12.   ListBox
  13.   HScrollBar
  14.   VScrollBar
  15.   Timer
  16.   Printer
  17.   Screen
  18.   Clipboard
  19.   DriveListBox
  20.   DirListBox
  21.   FileListBox
  22.   Menu
  23.   Shape
  24.   Line
  25.   Image
  26.   PropiertyPage
  27.   UserControl
  28.   Unknow Controls
复制代码
可以说,非常齐全,但是它只能根据控件来进行下断。而我们根据之前的分析,它在Form_Load的时候就检测了注册码。(如果没有重启验证,那么可以在按钮上下断点。)所以我们不得不再想另外的办法。<--反破解思路,做成纯重启验证也有它的好处。
再次打开VB Decompiler,发现Form_Load事件是在412838,于是打开WKTVBDebugger,下断412838,结果发现断不下!再仔细看看,发现这其实并不是代码的地址,而是窗体地址。所以在VB Decompiler反编译结果的窗口中选中第一句的地址411F26,再下断。
测试成功!这时候就可以取消断点,把VB Decompiler拖到注册验证段,开始是4121F0,再下断。
这里的跳转应该是不跳的,现在这里跳了,所以必须改掉。
这里为了验证BranchF/BranchT不能改成Branch,我们先改了试试看。
点击编辑,把目前的1c改成1e,然后运行。发现也没什么问题啊,请问那篇文章的真实性?我不得而知,望大牛来解释一下。
好,重新来一次。到这里以后看看代码:
  1. 004121D2: 3A LitVarStr 'Timer_Regcode.inf'
  2. 004121D7: 4E FStVarCopyObj 0012FA3Ch
  3. 004121DA: 04 FLdRfVar 0012FA3Ch
  4. 004121DD: 0B ImpAdCallI2 rtcDir on address 660D4F71h
  5. 004121E2: 23 FStStrNoPop
  6. 004121E5: 1B LitStr: ''
  7. 004121E8: 3D NeStr
  8. 004121EA: 2F FFree1Str
  9. 004121ED: 35 FFree1Var
  10. 004121F0: 1C BranchF 004125A1 (Jump ?       ;在这
  11. 004121F3: 00 LargeBos
  12. 004121F5: F4 LitI2_Byte: -> 0h 0
  13. 004121F7: 70 FStI2 0012FA36
  14. 004121FA: 00 LargeBos
  15. 004121FC: 1B LitStr: 'Timer_Regcode.inf'
  16. 004121FF: F4 LitI2_Byte: -> 1h 1
  17. 00412201: F4 LitI2_Byte: -> FFh 255
  18. 00412203: FE Lead3/OpenFile
  19. 00412207: 00 LargeBos
复制代码
004121F0的opcode为:1c 8d 06 先改成1c 00 00可以看到:
  1. 004121F0: 1C BranchF 00411F14 (Jump ?
复制代码
而我们的目标为BranchF 00412296(那里开始注册码的校验,由于这里没有NOP指令,只能先跳过读取阶段),而412296-411F14=382,所以我们就可以改为:1c 82 03 。值得注意的是,这里又和Intel的汇编指令不同,汇编指令的跳转是以当前地址为参照,而这里P-code是以起始代码为参照的。这样一改,反而还挺好的,没有注册码就跳过读取阶段,有就不跳过,这样的话可以随意改写注册文件了。
然后就到了验证注册码的第一个环节:
  1. 00412296: 1C BranchF 0041255D (Jump ?             ;在这
  2. 00412299: 00 LargeBos
  3. 0041229B: 3A LitVarStr ''
  4. 004122A0: 08 FLdPr
  5. 004122A3: FD Lead2/MemStVarCopy
  6. 004122A7: 00 LargeBos
  7. 004122A9: 3A LitVarStr ''
  8. 004122AE: 08 FLdPr
  9. 004122B1: FD Lead2/MemStVarCopy
  10. 004122B5: 00 LargeBos
  11. 004122B7: 08 FLdPr
  12. 004122BA: 06 MemLdRfVar
  13. 004122BD: 3A LitVarStr ''
  14. 004122C2: 5D HardType
  15. 004122C3: 33 EqVarBool
  16. 004122C5: 1C BranchF 004122CB ?
  17. 004122C8: 00 LargeBos
  18. 004122CA: 13 ExitProcHresult
  19. 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不太熟悉)。
顺便加个对话:
  1. 我: 各有利弊,.......(上面总结的话)......,你自己看着办吧。
  2. 小A: 哦,那我就用Native Code吧,反正反编译出来的源码就不那么直接清楚了。
  3. 我: 不过你那个程序算法太简单了一点,加个重壳吧。
  4. 小A: 不,加了重壳以后计时的延迟就太大了。
  5. 我: 我已经写出你那个软件的注册机了。
  6. 小A: 啊?......算了,我换一种编程语言写吧。
  7. 我: ......
复制代码
P.S.: 经过我的百般祈求,我的那个小A同学仍然不同意我把他的程序公开。对不起了,大家还是用自己的程序试炼一下吧。恩不过他没说我不能把注册机放出来......呵呵,我相信他不会打我的。

制作总结性的东西总是很累,这个笔记就当大家的参考吧,以后遇到VB程序脱了壳后破解起来应该会挺方便了吧。希望大家支持一下哦!写这篇文章差点写到浏览器卡死...... 下一篇帖子估计要等到一年半以后了。(初二下和初三应该没时间再来研究逆向这种耗费时间的东西了。)

一些提到的软件的链接:(解压密码全部为 dsong@52PoJie.Cn )
1. Numega SmartCheck:
链接:  http://pan.baidu.com/s/1qWzAlqc  密码: 2oh1

2. DotFix VB Decompiler:
链接:  http://pan.baidu.com/s/1i3L3jvR  密码: bf8v

3. WKTVBDebugger:
链接:  http://pan.baidu.com/s/1bnF9KV9  密码: ikce

4. Timer's  KeyGen :
链接:  http://pan.baidu.com/s/1ntolbKl  密码: ilal
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值