精华内容
下载资源
问答
  • vb 程序破解

    千次阅读 2017-09-02 16:44:57
    这里我尽量多和详细地解释了VB程序的破解(其实也可看到反破解)的相关东西,把一个简单的VB计时器程序研究透了,对于VB破解应有较大帮助,如果觉得无用的不要喷,谢谢!可能包含错误认识,如有错误欢迎指出。 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
    展开全文
  • VB程序破解

    千次阅读 2016-04-02 16:03:34
    VB程序的破解思路总结、编译方式vs破解工具 比较 把一个简单的VB计时器程序研究透了,对于VB破解应有较大帮助,如有错误欢迎指出。 VB程序也是一个非常有趣的东西,你去看VB的主程序,是用Microsoft Visual ...

    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.com.cn/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.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


    程序
    ========

     深入了解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程序对图标要求严格,很多体积大,精美的图标用不了,收集了一个VB图标大全。
  • VB 程序检测按键

    千次阅读 2017-07-18 14:43:36
    VB 程序检测按键 VB按键常量

    VB 程序检测按键
    弹出所按下键(使用KeyDown事件)

    语法格式为:
    Sub Object_KeyDown(KeyCode As Integer,Shift As Integer)

    例:

    Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) 
    If KeyCode = vbKeyUp Then 
        msgbox "您按了上键!"
    ElseIf KeyCode = vbKeyDown Then 
        msgbox "您按了下键!" 
    ElseIf KeyCode = vbKeyLeft Then 
        msgbox "您按了左键!" 
    ElseIf KeyCode = vbKeyRight Then 
        msgbox "您按了右键!" 
    End If 
    End Sub 

    依此类推,可以设置为其键值
    下面提供VB中的各个按键常量!
    Keycode 常数
    可在代码中的任何地方用下列常数代替实际值:

    常数         值     描述 
    vbKeyLButton 0x1 鼠标左键 
    vbKeyRButton 0x2 鼠标右键 
    vbKeyCancel 0x3 CANCEL 键 
    vbKeyMButton 0x4 鼠标中键 
    vbKeyBack 0x8 BACKSPACE 键 
    vbKeyTab 0x9 TAB 键 
    vbKeyClear 0xC CLEAR 键 
    vbKeyReturn 0xD ENTER 键 
    vbKeyShift 0x10 SHIFT 键 
    vbKeyControl 0x11 CTRL 键 
    vbKeyMenu 0x12 MENU 键 
    vbKeyPause 0x13 PAUSE 键 
    vbKeyCapital 0x14 CAPS LOCK 键 
    vbKeyEscape 0x1B ESC 键 
    vbKeySpace 0x20 SPACEBAR 键 
    vbKeyPageUp 0x21 PAGE UP 键 
    vbKeyPageDown 0x22 PAGE DOWN 键 
    vbKeyEnd 0x23 END 键 
    vbKeyHome 0x24 HOME 键 
    vbKeyLeft 0x25 LEFT ARROW 键 
    vbKeyUp 0x26 UP ARROW 键 
    vbKeyRight 0x27 RIGHT ARROW 键 
    vbKeyDown 0x28 DOWN ARROW 键 
    vbKeySelect 0x29 SELECT 键 
    vbKeyPrint 0x2A PRINT SCREEN 键 
    vbKeyExecute 0x2B EXECUTE 键 
    vbKeySnapshot 0x2C SNAPSHOT 键 
    vbKeyInsert 0x2D INSERT 键 
    vbKeyDelete 0x2E DELETE 键 
    vbKeyHelp 0x2F HELP 键 
    vbKeyNumlock 0x90 NUM LOCK 键 
    A 至 Z 键与 A – Z 字母的 ASCII 码相同:
    常数 值 描述 
    vbKeyA 65 A 键 
    vbKeyB 66 B 键 
    vbKeyC 67 C 键 
    vbKeyD 68 D 键 
    vbKeyE 69 E 键 
    vbKeyF 70 F 键 
    vbKeyG 71 G 键 
    vbKeyH 72 H 键 
    vbKeyI 73 I 键 
    vbKeyJ 74 J 键 
    vbKeyK 75 K 键 
    vbKeyL 76 L 键 
    vbKeyM 77 M 键 
    vbKeyN 78 N 键 
    vbKeyO 79 O 键 
    vbKeyP 80 P 键 
    vbKeyQ 81 Q 键 
    vbKeyR 82 R 键 
    vbKeyS 83 S 键 
    vbKeyT 84 T 键 
    vbKeyU 85 U 键 
    vbKeyV 86 V 键 
    vbKeyW 87 W 键 
    vbKeyX 88 X 键 
    vbKeyY 89 Y 键 
    vbKeyZ 90 Z 键 
    09 键与数字 09 的 ASCII 码相同:
    常数 值 描述 
    vbKey0 48 0 键 
    vbKey1 49 1 键 
    vbKey2 50 2 键 
    vbKey3 51 3 键 
    vbKey4 52 4 键 
    vbKey5 53 5 键 
    vbKey6 54 6 键 
    vbKey7 55 7 键 
    vbKey8 56 8 键 
    vbKey9 57 9 键 
    下列常数代表数字键盘上的键:
    常数 值 描述 
    vbKeyNumpad0 0x60 0 键 
    vbKeyNumpad1 0x61 1 键 
    vbKeyNumpad2 0x62 2 键 
    vbKeyNumpad3 0x63 3 键 
    vbKeyNumpad4 0x64 4 键 
    vbKeyNumpad5 0x65 5 键 
    vbKeyNumpad6 0x66 6 键 
    vbKeyNumpad7 0x67 7 键 
    vbKeyNumpad8 0x68 8 键 
    vbKeyNumpad9 0x69 9 键 
    vbKeyMultiply 0x6A MULTIPLICATION SIGN (*) 键 
    vbKeyAdd 0x6B PLUS SIGN (+) 键 
    vbKeySeparator 0x6C ENTER 键 
    vbKeySubtract 0x6D MINUS SIGN (-) 键 
    vbKeyDecimal 0x6E DECIMAL POINT (.) 键 
    vbKeyDivide 0x6F DIVISION SIGN (/) 键 
    下列常数代表功能键:
    常数 值 描述 
    vbKeyF1 0x70 F1 键 
    vbKeyF2 0x71 F2 键 
    vbKeyF3 0x72 F3 键 
    vbKeyF4 0x73 F4 键 
    vbKeyF5 0x74 F5 键 
    vbKeyF6 0x75 F6 键 
    vbKeyF7 0x76 F7 键 
    vbKeyF8 0x77 F8 键 
    vbKeyF9 0x78 F9 键 
    vbKeyF10 0x79 F10 键 
    vbKeyF11 0x7A F11 键 
    vbKeyF12 0x7B F12 键 
    vbKeyF13 0x7C F13 键 
    vbKeyF14 0x7D F14 键 
    vbKeyF15 0x7E F15 键 
    vbKeyF16 0x7F F16 键 

    小白资源分享

    忆云竹

    展开全文
  • vb程序设计实例100

    热门讨论 2012-09-05 10:10:51
    100个vb程序设计实例,vb学习从简单开始,
  • VB 程序设计参考

    千次阅读 多人点赞 2012-12-30 15:09:07
    第四章 VB程序的控制结构 第五章 VB中的数组 第六章 VB的过程设计 第七章 VB中的文件操作 第八章 VB中ActiveX控件的使用 第九章 VB的数据库操作 常见错误和难点分析 编码资料大汇总   | VB入门 | VB速...
    展开全文
  • VB讲课笔记02:VB程序开发环境

    千次阅读 2018-03-09 11:24:25
    VB讲课笔记02:VB程序开发环境一、VB的特点和版本1、VB的特点Visual Basic(简称VB)是Microsoft 公司开发的一种通用的基于对象的程序设计语言。“Visual” 指的是开发图形用户界面 (GUI) 的方法——不需编写大量...
  • VB程序调试

    千次阅读 2012-03-06 16:06:50
    一、在VB程序设计中有三类错误: 第一类:语法错误 由于违反了语言 违反有关语句形式或使用规则而产生的错误。系统可以自动检查, 如:输入非法字符,缺少括号等。 第二类:运行错误 由于试图执行一个不可...
  • vb程序设计基础笔记

    千次阅读 2019-08-04 20:32:05
    程序设计基础 变量的作用域,即是变量的“可见性” 定义一个变量后,为了正确的使用变量值,应当明确可以在程序的...Vb程序语言由 三块地方组成, 窗体模块 标准模块 类模块 二级不考,类模块 所以应用程序 由 窗体...
  • VB程序逆向关键点

    2017-06-28 14:36:39
    VB程序逆向关键点 关注加粗的即可!!
  • 简单vb程序

    2013-07-10 11:27:56
    先编写一个bat文件 @echo off echo 开始...... echo 解压文件 winrar x -o+ ./EnvironmentTools.zip C:\ echo 执行exe文件 ...echo 调用vb程序 start ./install_tcl.vbs ping 1
  • VB程序设计入门基础

    千人学习 2017-09-11 21:04:06
    VB入门基础。 清楚易懂,适合初学者 本课程主要从开发环境、窗体、命令按钮、文本框、标签、消息框和输入框、代码基础、选择结构、循环结构、数组、过程、控件等方面进行讲解
  • VB程序的脱壳技巧

    千次阅读 2016-06-06 17:49:54
    VB程序的启动方式:每个VB程序通常都会调用到许多的API,但是其中有一个API是雷 打不动的,这就是我们都知道的ThunRTMain函数。VB程序在运行时,都会首先调用 ThunRTMain函数,ThunRTMain函数将会为程序...
  • 设计VB程序的时候,想更改运行程序图标。谁能告诉我哪些图标能用于VB程序
  • VB程序打包方法

    万次阅读 2013-04-24 21:15:49
    VB学习已经有很长一段时间了,在马上就迎来的作品展中,相信大家的作品也接近了尾声,VB程序的最后一步就是打包发布,对此,我遇到了很多问题,我找到了两个解决的方法。利用VB本身提供的打包程序可以实现打包,但是...
  • VB程序架构分析

    2014-01-30 21:23:48
    在08年,我曾经写过VB程序。前面也将该VB程序的架构剥离出来并发出来共享了(具体见http://www.cnblogs.com/lzhdim/archive/2012/03/08/2385028.html)。这些天快过年了,也没啥事,就想将该架构做个分析,仅做留念...
  • VB程序的调试

    千次阅读 2012-03-05 09:32:06
    3.VB程序的调试 由于VB会把程序编译成解释型(http://zh.wikipedia.org/wiki/VB_%E4%BC%AA%E4%BB%A3%E7%A0%81)或者P-Code(http://en.wikipedia.org/wiki/Microsoft_P-Code),所以它的执行方式有点奇怪,它要通过...
  • vb程序的破解笔记

    2015-12-25 09:40:36
    算法比较简单。总结破解vb程序的思路如下: 工具:VB Decompiler Pro+Ollydbg 1、脱壳后用VB Decompiler Pro找到注册按钮控件的地址。 2、运行OD,在该地址处F2下断就可以分析算法了。
  • 更改VB程序的图标

    千次阅读 2014-10-22 14:22:33
    更改VB程序的图标 这几天,用VB写了一些小程序。除了功能的实现,关注的就是界面的美化了。 而程序图标的是用户运行程序前,最先呈现在屏幕上的事物。所以,我认为程序有必要使用一个有自己特点的图标。  ...
  • VB程序的破解

    千次阅读 2013-02-16 21:22:21
    下面我就来给大家介绍一下VB程序的常见函数 1) 数据类型转换: a) __vbaI2Str 将一个字符串转为8 位(1个字节)的数值形式(范围在 0 至 255 之间) 或2 个字节的数值形式(范围在 -32,768 到 32,767 之间)。 b)__...
  • WINDOWS下,鼠标右键点击程序会出现一个菜单,比如其中有一项是,“在...我想用VB代码实现这样的功能,VB程序让外部程序按照右键菜单里某个选项运行,然后到一定时间,用VB去结束这个第三方程序,请问代码如何实现呢?
  • VB程序的特殊性在于,再用OD调试过程中,会在动态链接库以及程序领空中经常跳跃,这里说一些常见的动态链接库: VB4 链接库为 vb40016.dll ; - 16 位,较少见; VB4 链接库为 vb40032.dll ...
  • VB程序设计实验报告

    千次阅读 2012-03-28 07:07:23
    VB程序设计实验报告 一:试验目的: 1 利用vb程序设计语言制作随机图形 2 利用vb程序设计语言制作动画 二:实验内容: 1先进行界面分布和相应的属性设置 2编写代码如下: 1,Private Sub Command1_Click() ...
  • VB程序中嵌套其它应用程序

    千次阅读 2012-02-26 22:00:12
    本文重点介绍如何将一个已经存在的应用程序嵌套到VB程序中。 新建工程,执行“外接程序”—“外接程序管理器”,选择加载“VB6 资源管理器”并确定。点击工具栏上的“VB资源管理器”图标打开“VB 资源管理器”...
  • 如何优化VB程序

    千次阅读 2009-12-18 14:54:00
    如何去优化你的VB程序帮助 1、如何去优化你的VB程序Visual Basic 作为一种高级编程语言,它也有着不可避免的缺点---开发出的应用程序运行速度慢。如果我们能够程序做一些优化,那么情况将会大大改善。要优化程序...
  • VB程序设计教程(第四版) 龚沛曾

    万次阅读 多人点赞 2019-03-27 13:59:46
    VB程序设计教程(第四版) 龚沛曾 课后习题答案 目录 第一章课后答案 第二章课后答案 第三章课后答案 第四章课后答案 第五章课后答案 第六章课后答案 第七章课后答案 第八章课后答案 .... 第一章课后答案...
  • VB串口通信程序(VB程序

    热门讨论 2009-04-10 16:10:58
    很好用的,VB串口通信,互相采用串口发送文字~

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 33,599
精华内容 13,439
关键字:

vb程序