精华内容
下载资源
问答
  • VB大全(精华)源代码 简单实用,初学者可以看看
  • [基于VB.NET的数据库应用程序设计].李永森.文字
  • 从主机下载数据后,用PDA或PDA手机进行资产盘点,可接条码扫描,盘点结果数据返回到主机。方便快捷。
  • 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 程序破解

    千次阅读 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
    展开全文
  • 本DataGridView打印控件和.NET打印控件5.6(含报表模板设计组件)2014年6月22日修改完成,完全免费,在.NET2.0及以上环境下都可以使用(VB打印、C#打印都是可以的),有帮助文档与使用实例。 与上一版本的5.5...
  • 这是一个围棋对弈免费软件,具有人机对弈,双人对弈、对局演示等功能,人工智能水平相当于围棋入门者。
  • VB设计软件封面

    2007-01-12 15:08:00
    对于Windows环境下的应用软件,一个漂亮醒目而且富有立体感变化的封面总会使你的软件更加充满魅力,从而提高软件的商品化程度,增强对用户的吸引力。本文将介绍在Windows环境下使用Visual Basic制作应用软件封面的...
    对于 Windows环境下的 应用软件,一个漂亮醒目而且富有立体感变化的封面总会使你的软件更加充满魅力,从而提高软件的商品化程度,增强对用户的吸引力。本文将介绍在Windows环境下使用Visual Basic制作应用软件封面的技术。

      一、封面 设计技术

      1.封面的制作

      开发者可以选择一些已有的图像文件,或从其它 图像处理过程中截取比较满意的图案,也可以制作具有个人风格的封面。在 VB中制作封面时,是在一个窗体上使用VB的绘图工具Line和Shape绘制出图形,也可以在窗体内载入一个Picture Box或Image Control,利用Picture Box或Image Control载入自己喜欢的图片来制作封面,同时在图形之上,可以使用Text Box或Label来显示版本信息。

      为了使设计的窗体更加象一个封面,可以去除窗体的控制框、标题栏和最大最小化按钮,窗体的大小不允许用户改变。

      在封面窗体的属性中,属性Control Box应设为False,Caption应设为空,MaxButton和MinButton应设为False,BorderStyler属性设为1,即固定的单线边界,这样就设计出了一个标准的封面。

      2. 浮动窗体

      主窗体载入后,激发封面事件(event),使封面窗体成为当前活动窗体,必须保证封面总是浮在主窗体的上面,在VB中可以通过调用Windows的API函数SetWindowpos来实现。SetWindowpos函数的声明如下: 

      Declare Sub SetWindowPos Lib ″User″ (ByVal hWnd As Integer, ByVal hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer)

      其中:hWnd是作为封面窗体的句柄;

      hWndInsertAfter 指示为封面窗体前载入的窗体;

      X为封?娲疤遄笊辖堑腦坐标;

      Y为封面窗体左上角的Y坐标;

      cx为窗体新宽度;

      cy为窗体新高度;

      wFlags影响窗体大小和位置的16位值。

      其中,hWndInsertAfter设为HWND—TOPMOST,wFlags设为SWP—NOMOVE (不移动窗体,即忽略cx和cy)或SWP—NOSIZE(不改变窗体大小,即忽略X和Y),就可以实现将HWND标识的窗体置于主窗体之上。

      3. 封面卸载

      保证主窗体载入完毕后,封面窗体自动卸出。在主窗体中可使用定时器控件,在激活主窗体的Activate事件时激活定时器,设置定时器活动时间属性,在定时器控件的事件Timer中,关闭定时器,卸出封面,释放 存储空间,提高软件运行速度。

      二、封面设计程序

      假设封面窗体名为frmface,主窗体名为frmfirst,其中封面窗体可以用绘图指令绘出。程序中给出了绘制三维窗体的方法,在封面窗体载入过程中,窗体上可根据用户需要绘制三维面板或图片框等。

      Sub Form—Load ()

      Dim fWidth As Integer

      Dim i As Integer, t As Integer, l As Integer

      Dim w As Integer, h As Integer

      frmface.BackColor=&HC0C0C0

      frmface.Left=(screen.Width-Width) / 2

      frmface.Top=(screen.Height-Height) / 2

      frmface.ScaleMode=3: fWidth=4

      frmface.AutoRedraw=-1

      For i=1 To fWidth

      t=i

      l=i

      h=scaleheight

      w=scalewidth

      Line (l, t)-(l, h-i), RGB(255, 255, 255)

      Line (l, t)-(w-i, t), RGB(255, 255, 255)

      Line (w-l, t)-(w-l, h-t), RGB(64, 64, 64)

      Line (l, h-l)-(w-l, h-l), RGB(64, 64, 64)

      Next i

      End Sub  

      在主窗体的激活过程中激活定时器,并设置定时器的Interval属性,程序如下:

      Sub Form—Activate ()

      Timer1.Interval=3000

      ′封面存在时间为3秒

      Timer1.Enabled=True

      ′打开定时器

      End Sub

      载入封面的代码放在主窗体的Load事件中,可以改变鼠标形状以示用户等待。首先在通用过程中声明常量:

      Const HWND—TOPMOST=-1

      Const SWP—NOMOVE=&H10

      Const SWP—NOSIZE=&H40

      Const flags=SWP—NOMOVE Or SWP—NOSIZE

      使用SetWindowPos函数实现浮动 效果

      Sub Form—Load ()

      screen.MousePointer=11

      ′鼠标呈沙漏状

      frmface.Show

      ′显示封面

      frmface.Refresh

      SetWindowPos frmface.hWnd, HWND—TOPMOST, 0, 0, 0, 0, flags

      End Sub

      定时器时间到后关闭定时器,同时卸载封面窗体,使用户可以继续操作主窗体。程序如下:

      Sub Timer1—Timer ()

      frmfirst.Timer1.Enabled=False

      ′关闭定时器

      Unload frmface

      ′卸载封面

      screen.MousePointer=0

      ′鼠标恢复原样

      End Sub

    转载于:https://www.cnblogs.com/tanghuawei/archive/2007/01/12/618867.html

    展开全文
  • 本DataGridView打印控件和.NET打印控件5.5(含报表模板设计组件)2014年2月8日修改完成,完全免费,在.NET2.0及以上环境下都可以使用(VB打印、C#打印都是可以的),有帮助文档与使用实例。 与上一版本的5.4...
  • VB 共享软件防破解设计技术初探(三) ×××××××××××××××××××××××××××××××××××××××××××××× 其他文章快速链接: VB 共享软件防破解设计技术初探(一) ...
    VB 共享软件防破解设计技术初探(三)
    

    ××××××××××××××××××××××××××××××××××××××××××××××

    其他文章快速链接:

    VB 共享软件防破解设计技术初探(一)
    http://bbs.pediy.com/showthread.php?t=72050

    VB 共享软件防破解设计技术初探(二)
    http://bbs.pediy.com/showthread.php?t=72204


    ××××××××××××××××××××××××××××××××××××××××××××××

    作者:爱琴海[SCG] 2008/09/10 (转载请保留该信息)

    第一篇我粗略的讲了以下几个内容:

    1、        文件完整性,防止被非法修改
    2、        运行时的校验,防止被LOADER
    3、        反调试,防止动态跟踪和挂接
    4、        防静态反汇编分析
    5、        注册码系统(算法部分,核心内容)
    6、        加壳防脱壳
    7、        隐蔽性设计
    8、        另辟蹊径

    第二篇我详细得讲解了以下内容:

    1、设计思想:水桶原理
    2、完整性校验,包括VB-CRC32注射器和主体的编写;文件修改时间自校验;文件大小自校验等
    3、防LOADER设计,包括查找标题,经典时值,还有重点的“金蝉脱壳”反LOADER

    上节课我忘讲了种常见的反LOADER的方法:释放程序法(借鸡生蛋)

    这里赶紧补上:

    释放程序法(借鸡生蛋)

    原理:讲需要保护的程序作为自定义资源添加到新的VB程序里,新程序一运行就自动释放内部包含的程序到特定位置,然后通过SHELL调用,自身则结束运行,怀疑就是“借鸡生蛋”,也是常用的木马病毒免杀技术。

    打开VB6.0 新建工程,然后单击“外接程序”――“外接程序管理器”――“VB 6 资源编辑器”,在右下角选项里选择“加载/卸载”,然后单击“确定”

    这时,在VB6.0主程序窗口上方的工具条最后,就会出现一个跟注册表编辑程序挺像的绿色图标,单击它。

    如图:



    选择自定义添加图标,然后在出现的窗口里,选择你要保护的EXE程序。

    如图:



    添加完资源后,按下“保存”按钮

    这样,资源就被添加为代号为101的数据,同理,就绪添加,就从102开始,一直增长上去,你也可以自己修改代号,方便记忆。

    下面我们来写一个自动释放子体的过程SUB

    我是代码启示线————————————————————————————————

    Private Sub Shifang()

    On Error GoTo TakeError

    Dim Lujing As String
    Lujing = "C:\WINDOWS\system32\缓存.exe"
    '定义缓存路径

    If Dir(Lujing) = "" Then
    '检测子体是否已经存在
    '不是的话就直接释放一个即可
    Dim Shuzu() As Byte
    '定义一个数组储存数据
    Shuzu() = LoadResData(101, "CUSTOM")
    '加载数据,101代号指的就是我们添加的EXE子体
    Open Lujing For Binary As #1
    '定义一个缓存路径
    Put #1, , Shuzu()
    '开始写入
    Close #1
    '关闭通道

    Else

    Kill Lujing
    '发现已经存在就删除它,然后就重新创建;目的是为了防止有人故意同名替换
    '定义一个数组储存数据
    Shuzu() = LoadResData(101, "CUSTOM")
    '加载数据,101代号指的就是我们添加的EXE子体
    Open Lujing For Binary As #1
    '定义一个缓存路径
    Put #1, , Shuzu()
    '开始写入
    Close #1
    '关闭通道
    End If

    Shell Lujing, vbNormalFocus
    '释放好后就SHELL使其运行,接下去就结束掉自己
    End

    TakeError:
    '一般发生错误是因为子体正在运行,无法删除或者覆盖,或者是因为OD的HIDEOD插件引起SHELL错误
    MsgBox "请检查我是否正在运行?或者,是因为调试器?请检查", , "发现问题了"
    End

    End Sub

    我是代码终止线————————————————————————————————

    调用的话,在FORM的LOAD事件里,或者其他启动事件里即可,如下:

    Private Sub Form_Load()
    Shifang
    End Sub

    看看效果吧!

    正常运行,跟运行一个程序感觉上没有什么差别

    如果加载OD调试器的话,如果OD刚好加载HIDEOD插件的话,就会提示错误,发现调试器:

    如图:



    如果关闭HIDEOD插件,就会自动借鸡生蛋,使调试器无法加载正确的子体程序

    如图:



    需要注意一点,只要够小心的人,一定会发现真正的程序所在,所以子体程序最好加上自删除代码,也就是检测到UNLOAD事件后,自动删除自己,或者调用批处理,隐藏删除自己。

    我碰到过的一些用VB写的外挂也就是用这种技术来逃避调试器的。

    这种技术就讲解到这里,我们开始新的篇章喽

    第三篇我将具体介绍 1、VB反调试反跟踪 2、防静态反汇编分析

    设计加密解密不久,能力有限,见识短浅,请各位高手见谅,有错误和不足之处敬请原谅,并请你阐述你自己的见解,共同完成《VB 共享软件防破解设计技术初探》系列文章,您的参与和支持是我的荣幸和骄傲。

    这篇开篇前我把自己想到的VB程序防破解设计图展示下,看看大家想到了什么?自由发挥



    我心中总有个声音:你设计的只是针对技术层面,无法真的长久的保证你的软件不被破解

    实际生活中,我们需要八卦一样的“阴阳鱼”,一切造化的根本。(我在故弄玄虚吧?)
    一个是阳刚的“法律”保障,一个是怀柔的“道义”安抚。

    在这里引述一篇文章里的片段,具体作者是谁,大家自己查
    原文是《如何让你的共享软件在国内赚到每月超过万元》

    引用从这里开始————————————————————————————————
    四、面对盗版不惊慌
            当用户找到了你的软件,下载安装并试用之后,发现使用简单,符合自己的需求,很想要,如果你是用户,显然,你就会去找盗版,如何找?两条途径吗
    第一:到搜索引擎中去搜索某某软件后面加一个破解,注册机之类的。结果呢?人家找到了,也就不会买了。另外,还会去盗版碟市场去找,看看有没有破解版的碟。呵呵,第一种情况好对付,你不是在搜索引擎中找吗?作为作者,你也可以用你的正版软件冒充盗版软件到处去发布呀,比如:你可以发布某某软件的破解版,让你的假破解版充斥了整个搜索引擎的结果。当用户下载了10个假破解之后,也自然会想到,这个软件没有破解版了。哦,对了,如果有真破解出现在搜索结果的前几位,也不用着急,首先,打电话给你所在地的公证处,花300元,把你所找到的页面做个公证。(我保证你这300元能够赚回来),然后,想办法找到这个网站的站长,不要管这个网站是个人的还是企业的。联系这个站长,把公证书的复印件,你软件版权的复印件传真给他,限定他在7日内与你进行协商解决。过期,你将在你所在地的人民法院起诉(诉讼费50元),什么?诉状不会写?找我呀,100元,帮你写一份。如何计算赔偿金额?哦,很简单,一般下载站都有提供下载数统计的,用下载量乘以你的软件的价格就是他该赔的金额,(呵呵,算起来很吓人的)。如果你很忙,找个律师是值得的。

        第二、如果在盗版碟市场出现了你的软件,那我告诉你,是喜也是忧,喜的是,你的软件很有名吗,忧的是,只有升级,做更好的软件了。辛苦你了。为了避免这种情况的出现,在你的软件中,加上网上认证的加密办法是可行的。加密点千万不要搞在一个地方,另外,为了防止跟踪破解,一个简单的办法,就是认证通过之后,不要弹出对话框之类的,默默的就让用户开始用吧。千万不要弹出什么:恭喜您,您注册成功了,恭喜您,您登陆成功了的对话框。人家找你的加密点,一跟踪这个对话框就行了。呵呵,这只是防破解的小技巧。其他的防破解方法,你可以查阅相关资料。一句话,与破解做斗争,不该妥协。哪怕花费比所得还要高,你也要与破解盗版做斗争,如同抗日一样,和平时期,也要抗日。要用抗日的精神来抵抗破解盗版。什么?你的软件也是用的盗版VC写的?这也没关系,那是另外一回事情,微软没找你麻烦,那是微软的事。并不妨碍你找破解者的麻烦。总不能因为唐朝的时候咋们侵略过别国,就不抵抗日本侵略了吧?

    引用到此结束—————————————————————————————————

    文中作者告诉我们的是:

    1、        定时关注互联网搜索引擎关于你的软件破解信息
    2、        到盗版碟市场看看有没有你的软件破解注册版
    3、        冒充破解版在网上大量发布,覆盖大部分搜索出来的破解信息资源
    4、        利用法律武器,作公证、取证,警告发布破解信息的站点或者论坛或者其他发布者
    5、        聘请律师,委托代办,发律师信,甚至对簿公堂。
    6、        发现破解版大量存在,那么只能更新软件,加强加密强度。
    (如硬件加密狗,反正羊毛出在羊身上…..)
    7、        隐藏注册结果信息,让用户用着吧,让破解者徘徊迷路吧
    8、        要用抗日的精神来抵抗破解盗版

    相信作者能靠共享软件月入万金,在这里由衷的恭喜他发财。

    这个讲的就是我要说的法律武器,阳刚十足,捍卫自己的利益。但有时,刚性太强,反而容易折断,高碳钢硬度很大,但是脆;低碳钢硬度比较小,但是柔韧。

    这里重点聊聊怀柔“道义”,你的软件的质量,软件的功能要对得起你收的注册费,这个就是道义;
    你的售后服务,服务的态度和服务水平要对得起你的承诺,这个就是道义;
    你对未注册版的限制要合理,不要给用户带来很不良好的体验和心理感觉,这个就是道义;不能强迫用户注册,动不动就跳到注册网页去了,这个就是道义;
    你对某些支持过你,给你提好意见的用户,免费送注册码,这个也是道义;
    定期给某些特定用户群发放免费注册码,或者在特定的时候发布特定的纪念版免费回馈社会,这个也是道义;
    你的防破解设计里不应该有刺激破解者的语言和指令,如关机、格盘、删除系统文件、摧毁数据等,这个更是道义了。

    有这样的软件,它在收取少量的注册费后,将部分收入以公开名义捐赠给四川灾区,这样的软件在道义上他是高尚的,在道义上便已经让很多有良知的高手罢手了。破解者破解了它,发布了它,他将遭到道义上的谴责和唾弃。

    《道德经》讲“道可道非常道”,这“道”便是一切,道义也是“道”的范畴。你的软件连道义都没有,先不说会不会激怒破解者和一些不搞破解的逆向分析师来破解你的软件(无他,只因不爽)单单说有没有用户用你的破解版都还是个问题呢。

    比如说:江民炸弹

    引用从此开始—————————————————————————————————

    简介
    1997年6月24日王江民先生在其主页上发布了kv300l++版,内含逻辑炸弹。
    凡是在mk300v4制作的仿真盘(盗版盘)上执行kv300l++的用户硬盘数据均被破坏,
    同时硬盘被锁,软硬盘皆不能启动。
    Kv300逻辑炸弹可以造成电脑软硬盘都不能启动的现象,
    当时在电脑界引起轰动。这是用常规原理不能解释的现象。

    kv300逻辑炸弹表现
    1.先破坏文件分配表,然后修改分区表造成硬盘被锁。 
    2.不做任何备份 
    3.没有任何提示 
    4.在特定条件下激发(盗版盘) 
    5.王江民始终没有公开提供恢复程序; 
    6.如果用一般常用的修复磁盘工具,如NORTON,会造成不可逆转的损失; 
    7.对其的恢复类似于cih破坏数据的恢复,因为需要重建分区表。 
    8.在win95环境下执行同样会造成文件分配表被破坏,数据损失。

    处理

    1997年7月23日,国内5家反病毒软件公司(以下简称五厂商)在北京联合举行新闻发布会,一起谴责国内另一家著名的反病毒软件公司—北京江民新技术有限责任公司(以下简称江民公司)。在发布会上,五厂商向多家新闻机构发放了一份“联合声明”,称江民公司6月下旬发布的KV300L+ +版反病毒软件(网络下载版)(以下简称KV300,本文无特殊说明,所指的均是这一特定下载版本)中含有“逻辑炸弹”,“在特定条件下对计算机实施破坏,其结果与某些计算机病毒的破坏作用相似……” —7月24日,江民公司对五厂商的“联合声明”做出了强烈反应,认为这时“不正当的侵权行为”,并多次在各专业计算机所刊上登载“严正声明”,江民公司对此的解释是:江民公司并未在KV300中安放任何破坏性程序。五厂商所称的“逻辑炸弹”,其实是江民公司为打击日益猖獗的盗版软件行为而在软件中编制的“逻辑锁”,这一“逻辑锁”首先不可能对任何购买正版产品的用户造成任何影和损失,其次对部分盗版用户也只是起到暂时锁住机器的作用。江民公司特别强调,KV300中的“逻辑锁”与病毒没有关系,因为病毒是具有自我复制和传染性的破坏性程序,而“逻辑锁”却不会对用户数据造成任何伤害 1997年9月8日,公安部门认定kv300L++事件违反计算机安全保护条例之23条,属于故意输入有害数据,危害计算机信息 系统安全的行为,对其做出罚款3000元的决定

    关于KV300 '逻辑锁' 的看法
            王江民口口声声说 KV300 L++ 里面没有逻辑炸弹, 只有所谓 '主动式逻辑锁'. 似乎这样讲就不需要承担责任了!
            的确, 在中国有关计算机安全和保护软件消费者的法律还不健全, 王江民的类似行为暂时还无法受到法律的制裁. 但我们需要进行这方面的讨论. 以了解我们消费者的权利和可以采取的措施.
            从道义上和法律意义上讲, 王江民的这种做法已经侵犯了公民的权利. 王江民认为盗版应视同小偷, 可以用锁把他锁起来. 但是, 我们应该清楚, 中国法律只付予了警察等执法机关有执法的权利, 你王江民有什么权利执法? 你用逻辑锁把你的软件锁起来, 是你的权利. 但王江民没有权利锁别人的门!   即使, 经过国家有关部门的批准, 王江民有这样的执法权. 王江民也必须经过广告等形式公之于众.
            从计算机安全角度讲. 王江民至今没能确认正版用户不会受到意外损害, 他也没有通过适当渠道告知正版用户避免意外损害的方法, 他只是说明受到损害时由他解锁. 这使KV300 的正版用户会因此存在资料失密和损失的危险. 对此, 如何赔偿??
            由于, KV 300 内含有可能危害消费者权利和计算机安全的'逻辑锁'. 在目前, 国家没有相关法律的情况下. 我们消费者有权利相互告知并通知 KV300 的潜在用户, 选择其他的产品, 放弃 KV 300.

    引用到此结束—————————————————————————————————

    就像坛子里很多人讲的一样“先别急着想着防破解,要把软件功能和质量放在第一位”
    呵呵,还好,扯得不是很远,可以把大家的思路引向更广阔的防破解设计上去,我很荣幸。

    回到技术层面,下面我们开始讲解:1、VB反调试反跟踪 2、防静态反汇编分析

    第三章        第一讲

    备注:这一讲将是比较长的,请准备好开水茶叶或者咖啡。

    VB的反调试设计

    这里要讲解的VB反调试有以下几种:

    1、        检测父进程反RING3调试器
    2、        FindWindow检测敏感窗口
    3、        EnumChildWindows枚举所有窗体,检测敏感字符
    4、        检测进程名字来排雷
    5、        利用IsDebuggerPresent检测调试器
    6、        加密字符串,错误提示诱导陷阱
    7、        利用SEH进行反跟踪
    8、        加VM或者P-CODE编译
    9、        直接反汇编修改代码(破解者用的招数,我们也可以用来防守)
    10、        隐蔽性设计
    11、        步长、时值反调试(包含启动时间检测)
    12、        检测内存分配大小反调试
    13、        窗口置顶反调试
    14、        检测按键,一般是F2(断点)、F7(单步步进)、F8(逐过程步过)、F9(运行)
    15、        检测前台窗口反调试
    16、        SeDebugPrivilege检测调试器
    17、        关键部分算法的时候锁定鼠标干扰调试分析
    18、        GetStartupInfo反调试

    其实方法很多很多,建议大家购买《加密与解密第三版》里面有些很好的方法,奈何能力有限,经验不足,我还在琢磨中……与君共勉……

    第一、        检测父进程反RING3调试器

    简要介绍下原理:一般用户都是在WINDOWS桌面下打开我们写的程序,而不是在什么调试器下的,也就是说,调用我们程序的是explorer.exe程序,而不是A.exe也不是B.exe,它们都对应有个ID,我们只需要检测其父亲ID跟explorer.exe的ID是否一致,就说明是否正常打开还是被调试了。

    提示,OD的HIDEOD插件里,如果挑上“Process32NEXT”的话,此方法无效。

    请打开VB6.0 新建工程

    在窗体通用里,复制以下API和其他参数声明

    我是代码启始线————————————————————————————————

    Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long
    Private Declare Function Process32First Lib "kernel32" (ByVal hSnapShot As Long, lppe As PROCESSENTRY32) As Long
    Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapShot As Long, lppe As PROCESSENTRY32) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Long, ByVal dwAppProcessId As Long) As Long
    Private Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
    Const MAX_PATH As Integer = 260
    Const TH32CS_SNAPPROCESS As Long = 2&
    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 * 1024
    End Type

    我是代码终止线————————————————————————————————

    然后我们在窗体里写个SUB过程,以后直接调用即可。

    我是代码起始线————————————————————————————————

    Private Sub Fujincheng()

    '这个过程是检测父进程的父进程是否是EXPLORE的父进程
    Dim Process As PROCESSENTRY32
    Dim hSnapShot As Long
    Dim XNN As Long
    Dim flag As Boolean
    Dim mName As String
    Dim i As Integer
    Dim pid As Long, explorer As Long '注意这2个变量就用来存放2个ID

    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0&) '建立进程快照
    '搜索explorer.exe进程,并获得其ID
      If hSnapShot Then
        Process.dwSize = 1060
        If (Process32First(hSnapShot, Process)) Then '遍历第一个进程,获得PROCESSENTRY32结构
          Do
            i = InStr(1, Process.szExeFile, Chr(0))       '获得映像名称
            mName = LCase(Left(Process.szExeFile, i - 1)) '并转换成小写
           
            If mName = "explorer.exe" Then      '是不是explorer.exe
            explorer = Process.th32ProcessID
            ElseIf mName = LCase(App.EXEName & ".exe") Then  '是不是自己
                 pid = Process.th32ParentProcessID   '获得父进程ID
            Else
                 flag = False
            End If
          Loop Until (Process32Next(hSnapShot, Process) < 1) '遍历所有进程直到返回值为False
        End If
        XNN = CloseHandle(hSnapShot)
        End If

    Dim Openit As Long

    Openit = OpenProcess(1&, -1&, pid)
        
    If pid <> explorer Then MsgBox "发现父进程调试", , "警告": TerminateProcess Openit, 0
    '如果发现父进程不对,就结束掉父进程,对使用HIDEOD中的Process32NEXT的OD无效

    End Sub

    我是代码终止线————————————————————————————————

    我们设计的代码先找出explorer.exe进程的ID号,然后找出本程序的父进程,两者对比,发现不一样就说明很有可能被调试,那么就用OpenProcess打开该进程,用TerminateProcess结束掉该可以进程。

    使用方法如下:

    Private Sub Form_Load()
    Fujincheng
    End Sub

    效果如图:





    说明该方法对待通用RING3级调试器应该都是有效果的

    第二、        FindWindow检测敏感窗口

    原理简介:一般程序都有标题栏,也就是程序最上面的那一栏,该栏显示的就是FindWindow可以查到的字符。我们以此来检测黑名单即可简单检测到调试,继而作出响应。

    请打开VB6.0 新建工程

    在窗体通用里,复制以下API声明

    Private Declare Function FINDWINDOW Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

    然后在窗体代码处,我们写一个过程即可实现调用:

    我是代码起始线————————————————————————————————

    Private Sub GuanbiFindwindow(Mingzi As String)
    winHwnd = FINDWINDOW(vbNullString, Mingzi)
    '获得窗口句柄
    If winHwnd <> 0 Then
    AppActivate Mingzi
    '激活窗体为活动
    SendKeys "%{f4}", True
    'ALT+F4 结束掉
    SendKeys "%y", True
    Else
    End If
    End Sub

    我是代码终止线————————————————————————————————

    如何调用?请看:

    Private Sub Form_Load()

    GuanbiFindwindow ("计算器")
    '关闭计算器是为了方便大家验证

    GuanbiFindwindow ("Numega SmartCheck")

    '这样调用即可,不要放到按钮事件,如果不小心激活了我们自己的窗体,那就结束不掉调试进程了
    End Sub

    实验效果表明,可以结束掉我们规定好的窗口,效果不错。

    第三、        EnumChildWindows枚举所有窗体,检测敏感字符

    简要原理:

    可以用EnumChildWindows来枚举窗体,检测到所有相关敏感字眼,即可判断为调试,继而作出响应。为什么要用这个呢?你有没有发现OD调试器版本真的很多,尤其是修改版的,基本上,高手都喜欢收藏些特别点的调试器,标题栏都不一样,如果用FINDWINDOW来查找的话,还不郁闷死啊?

    但是你又发现了没?基本OD打开后,或者开始调试后,标题栏里总有“CPU”这三个字符,
    而SMARTCHECK大都包含smart字样?所以我们可以用EnumChildWindows来枚举所有窗口标题栏来检测它们中有没有包含CPU三个字,如果有,宁可错杀,也不放过。

    请打开VB6.0 新建工程 添加个TIMER1 定时500 建立个模块

    在模块中添加:

    我是代码起始线————————————————————————————————

    Declare Function GetDesktopWindow Lib "user32" () As Long
    Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
    Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
    Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long

    Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long

    '针对EnumChildProc写的自动处理函数
        Dim sSave As String
        sSave = Space$(GetWindowTextLength(hwnd) + 1)
        GetWindowText hwnd, sSave, Len(sSave)
        sSave = Left$(sSave, Len(sSave) - 1)
        sSave = Trim(sSave)
        If JianCPU(sSave) = True Then MsgBox "发现调试器,请关闭", , "警告"
        EnumChildProc = 1
    End Function

    Function JianCPU(abcdef As String) As Boolean
    '检测字符集里有没有cpu或者smart字样,返回TRUE表示包含
    JianCPU = False
    Dim nnn As Long
    For nnn = 1 To Len(abcdef) - 2
    If LCase(Mid(abcdef, nnn, 3)) = "cpu" Then
    JianCPU = True
    Exit For
    End If
    Next nnn
    For nnn = 1 To Len(abcdef) - 4
    If LCase(Mid(abcdef, nnn, 5)) = "smart" Then
    JianCPU = True
    Exit For
    End If
    Next nnn
    End Function

    Public Sub MeiJu()
    '具体整合成为反调试利器
    On Error Resume Next
    EnumChildWindows GetDesktopWindow, AddressOf EnumChildProc, ByVal 0&
    End Sub

    我是代码终止线————————————————————————————————

    然后在窗体代码中,怎样调用呢?如下:

    Private Sub Form_Load()
    '具体使用
    MeiJu
    End Sub

    Private Sub Timer1_Timer()
    '具体使用
    MeiJu
    End Sub

    即可,怎么样简单吧?

    为什么要搞个定时器呢?那当然,因为我们要时刻检测全部窗体的敏感字符,我认为有些网吧封锁迅雷和“破解”关键字眼,凡是发现有包含的,就直接关闭窗口,也应该是采用类似的技巧的吧?

    看效果吧:



    第四、        检测进程名字来排雷

    不好意思,凌晨3点多,有些打盹了。尽量把这些提到的讲完,没有提到的请大家专研解决。

    简要原理:同样是检测进程,只是比较黑名单而已,不是很简单吗?

    打开VB6.0 新建工程

    在通用部分复制以下变量声明和参数:

    我是代码起始线————————————————————————————————

    Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long
    Private Declare Function Process32First Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long
    Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    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 * 1024
    End Type
    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

    我是代码终止线————————————————————————————————

    然后写个函数:

    我是代码起始线————————————————————————————————

    Private Function Jincheng(namex As String) As Boolean
    '编写个函数方便调用,namex是要检测的程序名,小写,返回TRUE表示发现

    Dim my As PROCESSENTRY32
    Dim l  As Long
    Dim l1 As Long
    Dim mName As String
    Dim i As Integer
    l = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
          If l Then
              my.dwSize = 1060
              If (Process32First(l, my)) Then         '遍历第一个进程
                  Do
                                i = InStr(1, my.szExeFile, Chr(0))
                                mName = LCase(Left(my.szExeFile, i - 1))
                      If mName = namex Then
                                Jincheng = True
                                Exit Function
                        Else
                                Jincheng = False
                        End If
                  Loop Until (Process32Next(l, my) < 1)             '遍历所有进程知道返回值为False
              End If
              l1 = CloseHandle(l)
    End If

    End Function

    我是代码终止线————————————————————————————————

    怎样调用?请看:

    我是代码起始线————————————————————————————————

    Private Sub Form_Load()

    If Jincheng("ollydbg.exe") = True Then MsgBox "发现OLLYDBG调试器,请关闭", , "进程检测"
    '如此调用即可

    End Sub

    我是代码终止线————————————————————————————————

    效果如图:

     

    第五、        利用IsDebuggerPresent检测调试器

    这个算很老的东西了,拿出来给初学者看看

    打开VB6.0 新建工程

    在通用部分写:

    我是代码起始线————————————————————————————————

    Private Declare Function IsDebuggerPresent Lib "kernel32" () As Long

    我是代码终止线————————————————————————————————

    调用:

    Private Sub Form_Load()
    If IsDebuggerPresent Then MsgBox "发现调试器了,好累啊", , "凌晨3:36"
    End Sub

    效果见图:(对SMARTCHECK还有效果,对OD基本无效了)



    第六、        加密字符串,错误提示诱导陷阱

    原理:破解者在破解VB软件的时候,有的高手会查找宽字的十六进制码,然后在OD中直接搜索如“注册成功”对应的编码,然后上下翻找,爆破什么的都来了,噩梦啊。

    所以为了反调试,你最好不要提示注册成功之类的,如果有什么字符容易泄露你的算法信息,那你必须把它隐藏,可以用Chrw线转成数字形式,然后用的时候直接Ascw转回字符即可实现隐藏,但是还是可以被人找到,最好的办法是实现动态解码。

    我只简单的举个例子:字母F的ASCII码(十进制)为70,我用ASC(70)比直接用F好,但是ASC(70)也容易被查到,我建议是通过动态计算得出:A = 34 B =36 C = Chr(A+B)
    这样就安全多了,但是设计Chr 和 Chrw 的函数已经被人所知,也容易被人断点。但是我们还是要尽最大努力去阻止破解者。

    我们随便设计一个实验下:

    Private Function ZiFujiemi(anum As Long, bnum As Long) As String
    '字符解密函数,用这个函数之前可以用相反的逆算来得到加密后的形式
    ZiFujiemi = ChrW(anum Xor bnum)
    End Function

    Private Sub Command1_Click()
    MsgBox ZiFujiemi(64, 22)
    '大写字母V的ASCII为86,可拆成64 XOR 22,我的意思你能明白就好了
    End Sub

    可见只要是计算的,都比原字符要保险,中间进行转换即可。加密后,用VBExplorer就分析不出我们的原始字符串了。

    那错误陷阱呢?恰恰相反,我们要设计一个捕捉CRACKER虫的全套,比方说引诱破解者进入你设计好的,可以注册成功,但是是假成功,而且尽量不给真正的提示,那如何提高陷阱的捕捉率呢?

    可以主动设置好字符串提示,如:“注册码错误”,“注册码正确”,“恭喜你”,“已注册”,“未注册”等等。讲破解着的眼球吸引过来,进入一个貌似算法的,而且算法也挺逼真的地方,注册完后也在标题栏或者什么地方显示个“已注册”字样,其实功能未解禁,^_^

    打开VB6.0 新建工程 添加一个名为“注册”的按钮,该按钮就是用来迷惑破解者的。

    编写以下一个SUB

    我是代码起始线————————————————————————————————

    Private Sub Jiazhuce()
    '这是一个假注册陷阱
    Dim aJia As String
    Dim bJia As String
    Dim cJia As String
    Dim dJia As Double
    Dim nxunhuan As Integer
    aJia = Text1.Text
    For nxunhuan = 1 To Len(Text1.Text)
    aJia = aJia & Asc(Mid(aJia, nxunhuan, 1))
    dJia = Val(aJia)
    Next nxunhuan

    If Val(dijia) = 57894321649498# Then
    cJia = "注册码正确,恭喜你"
    MsgBox cJia, , "感谢"
    Else
    bJia = "注册码错误,请重新输入"
    MsgBox bJia, , "错误"
    End If
    End Sub

    我是代码终止线————————————————————————————————

    真正的注册过程在这里:

    我是代码起始线————————————————————————————————

    Private Sub Form_Unload(Cancel As Integer)
    ‘注册过程设计在关闭程序的瞬间,或者重新启动的时候验证,这里注册码是123456
    If Val(Text1.Text) = 123456 Then MsgBox ChrW(30495) & ChrW(27491) & ChrW(25104) & ChrW(21151), , ChrW(25552) & ChrW(31034)
    End Sub

    我是代码终止线————————————————————————————————

    按钮的代码调用:

    Private Sub Command1_Click()
    Jiazhuce
    '这就是个典型的假圈套,用字符串吸引破解者注意
    '其实真正的注册过程在UNLOAD事件中
    End Sub

    看看效果图吧:



    字符查看软件显示了我们故意释放出来的假象

     

     

    这个例子很简单,但是原理希望初学者能专研下哦

    第七、        利用SEH 反调试

    简介:SEH 是什么?你就简单得理解为异常处理机制好了,可以这样认为:没有调试器存在的时候,我们实现设计好的处理错误的程序代码就携带者注册算法什么的正常运行;如果有调试器开着,那么,就自动把异常错误递交给调试器来处理,也就不运行我们事先安排好的处理代码了,这样就起到反调试作用。

    更多信息请自行网络搜索

    打开VB6.0 新建工程,再添加一个FORM2,新建模块

    在模块中添加:

    我是代码起始线————————————————————————————————
    Private Type EXCEPTION_POINTERS
        pExceptionRecord As Long
        ContextRecord As Long
    End Type

    Public Declare Function SetUnhandledExceptionFilter Lib "kernel32" (ByVal lpTopLevelExceptionFilter As Long) As Long

    Public Function NewExceptionHandler(ByRef lpExceptionPointers As EXCEPTION_POINTERS) As Long
    '在这里可以添加一些关键算法
    '一些关键步骤都可以放这里
    If Val(Form1.Text1.Text) = 123456 Then
    MsgBox "注册码真正正确", , "恭喜": Form2.Label1.Caption = "注册成功": Form1.Hide: Form2.Show
    Else
    MsgBox "真实比较,注册码错误", , "错误"
    End If
    Resume Next
    End Function

    我是代码终止线————————————————————————————————

    在FORM2中建立一个LABEL1,添加代码:

    Private Sub Form_Unload(Cancel As Integer)
    Unload Form1
    End Sub

    在FORM1中添加一个TEXT1和一个COMMAND1

    添加如下代码:

    我是代码起始线————————————————————————————————

    Private Declare Sub DebugBreak Lib "kernel32" ()

    Private Sub Command1_Click()
    On Error GoTo BACK

    DebugBreak
    '产生异常的INT指令

    '凡是流程顺这下来的都是错误的,不管怎样都是假注册圈套
    '然后恰恰是调试器接手了这块,于是就变成了往下运行

    Dim a As Long
    Dim b As Long

    a = 123456 + Val(Text1.Text)
    b = a Xor 6543215
    b = b + a

    If b = 0 Then
    '凡是流程顺着来到这里的都死错误的,不管怎样都是假注册圈套
    MsgBox "虚假比较,注册码错误", , "提示"
    Else
    MsgBox "虚假比较,注册码错误", , "错误"
    End If

    BACK:

    End Sub

    Private Sub Form_Load()
    SetUnhandledExceptionFilter AddressOf NewExceptionHandler
    End Sub

    Private Sub Form_Unload(Cancel As Integer)
    SetUnhandledExceptionFilter 0&
    Unload Form2
    End Sub

    我是代码终止线————————————————————————————————

    这样我们就简单的完成了一个VB编写的SEH反调试程序

    一些关键的解密算法最好放到异常中去执行,然后判断等,而有调试器的状态下,会被调试器捕获异常,进而改变流程。起到了一定的反调试作用。

    我们来尝试测试一下:

    正常情况,输入注册码错误:



    正常情况,注册成功

     

     

    调试器状态下:





    注意要这个SEH的效果的话,OD调试设置里不能选中“忽略INT3中断”。

    从上述测试中我们看到:有调试器调试的时候,破解者被引到了我们设计好的假冒的算法部分,而真实的算法则因SEH作用而不再执行。

    当然,你也可以自己精心设计一个异常,让调试器去捕获,让破解者中招。

    第八、加VM或者P-CODE编译

    加VM的话,其原理是虚拟执行加密的代码,模拟了一个新的环境,让破解者云里雾里,而且一句简单的代码可以发胖好多倍,一句话:累死你。

    VMProtect是当前一款很强的虚拟机保护软件,经过VMProtect处理的软件,能大大提高破解者的分析成本,是目前一种比较理想的保护方式。VMProtect关键是用好,一定要将程序关键代码进行处理,必要时用OllyDBG检查一下被处理的代码。

    另外,经虚拟机处理代码效率会降低,因此一些对效率要求比较高的代码就不适合用VMProtect进行处理。

    关于VM怎么用,我只简单的讲了:用VMProtect打开你的VB程序,设置好要加密的地址简单加密即可,VMProtect 1.2版以后支持SDK,我们在Vb里可以这样标记保护的开始地址和结束地址:



    标记开始:
    Call VarPtr(“VMProtect begin”)

    标记结束:
    Call VarPtr(“VMProtect end”)

    这样能更有效得定位和保护我们的算法部分。具体使用教学请参考:
    http://bbs.pediy.com/printthread.php?t=49979

    实际生活中,VB程序虚拟环境用的最多的是P-CODE编译模式,准确的讲是解析执行。

     

    在VB6.0里按照上述的图片设置编译方式即可,编译出来的VB程序基本上是在VB的DLL里穿来穿去,云里雾里,如果再配合异常处理,陷阱,反调试和修改程序代码的话,真的很难破解了。

    第九、直接反汇编修改代码(破解者用的招数,我们也可以用来防守)

    编译好一个程序后,为了防止VBExplorer等静态分析工具查看到我们的具体事件地址等,有必要修改下VB程序开始的代码,利用的就是平衡堆栈和JMP等指令,加上点经典的时值反调试,效果就出来了,你甚至可以利用这个技术,把自己关键的算法也给搬家了,东一个家,西一个家,让破解者东奔西走,疲惫不堪。

    004018CC >/$  68 7C1D4000   PUSH 1112.00401D7C
    004018D1  |.  E8 F0FFFFFF   CALL <JMP.&MSVBVM60.#100> 
    这个明显透露了VB程序,我们要做的是搬走这个位置
    换句话说是把这个CALL移走,移到天南海角去……
    004018D6  |.  0000          ADD BYTE PTR DS:[EAX],AL
    004018D8  |.  0000          ADD BYTE PTR DS:[EAX],AL
    004018DA  |.  0000          ADD BYTE PTR DS:[EAX],AL
    004018DC  |.  3000          XOR BYTE PTR DS:[EAX],AL
    004018DE  |.  0000          ADD BYTE PTR DS:[EAX],AL
    004018E0  |.  3800          CMP BYTE PTR DS:[EAX],AL
    004018E2  |.  0000          ADD BYTE PTR DS:[EAX],AL
    004018E4  |.  0000          ADD BYTE PTR DS:[EAX],AL
    004018E6  |.  0000          ADD BYTE PTR DS:[EAX],AL
    004018E8  |.  3A28          CMP CH,BYTE PTR DS:[EAX]
    004018EA  |.  8A00          MOV AL,BYTE PTR DS:[EAX]
    004018EC  \.  CF            IRETD
    修改后:―――――――――――――――――――――――》》》
            004018CC > $ /E9 7FA50100   JMP 了凡第一.0041BE50
    004018D1     |90            NOP
    004018D2     |90            NOP
    004018D3     |90            NOP
    004018D4     |90            NOP
    004018D5     |90            NOP
    0041BE50    60              PUSHAD
    0041BE51    0F31            RDTSC
    经典时值,单步跟,不小心的话就会进入到错误地址
    0041BE53    8BC8            MOV ECX,EAX
    0041BE55    0F31            RDTSC
    0041BE57    2BC1            SUB EAX,ECX
    0041BE59    3D 00050000     CMP EAX,500
    0041BE5E  ^ 0F8F BAF9FFFF   JG 了凡第一.0041B81E
    0041BE64    83F8 00         CMP EAX,0
    0041BE67  ^ 0F8C C7F9FFFF   JL 了凡第一.0041B834
    0041BE6D    61              POPAD
    0041BE6E    68 68504000     PUSH 了凡第一.00405068
    0041BE73    60              PUSHAD
    0041BE74    0F31            RDTSC
    0041BE76    8BC8            MOV ECX,EAX
    0041BE78    0F31            RDTSC
    0041BE7A    2BC1            SUB EAX,ECX
    0041BE7C    3D 00050000     CMP EAX,500
    0041BE81  ^ 0F8F ADF9FFFF   JG 了凡第一.0041B834
    0041BE87    83F8 00         CMP EAX,0
    0041BE8A  ^ 0F8C 8EF9FFFF   JL 了凡第一.0041B81E
    0041BE90    61              POPAD
    0041BE91    E8 305AFEFF     CALL <JMP.&MSVBVM60.#100>
    搬到这里来了
    0041BE96  ^ 0F85 3A5AFEFF   JNZ 了凡第一.004018D6
    0041BE9C  ^ 0F84 345AFEFF   JE 了凡第一.004018D6

    效果:





    RDTSC指令是什么意思?当然是检测时间用的,不少壳代码使用RDTSC来检测运行时间,如果在OD里调试,时间必定要延长,这样壳利用两次RDTSC时间差就可发现被调试器。

    如果有时间,你可以自己设计得更恐怖点,东一句西一句,让别人无法还原

    第十、        隐蔽性设计

    调试者如果已经分析到了你的算法部分,你怎么办?
    所以事先还得有准备,也就是隐蔽性设计。

    一段算法,在一般情况下,不执行正确的算法验证,除非已经悄悄得验证了注册码前面几位或者后面几位正确的时候,才悄悄得执行正确的算法验证。

    这样可以在一定程度上让破解者感到郁闷,和无所适从。

    打开VB6.0 新建工程 新建 TEXT1和TEXT2 还有添加一个COMMAND1

    在代码窗口我们编写如下注册过程SUB

    我是代码起始线————————————————————————————————

    Sub ZhuCe(name As String, code As String)
    '设计一个隐蔽算法的反调试注册过程

    '可用真正注册码:
    'name: maplescg
    'code: 844123456

    '假注册码:
    'name: maplescg
    'code: 14245066571

    Dim Nxunhuan As Integer
    Dim Yinbi As Double
    Dim Zhongzhuan As String

    Dim Zhang As Double
    Dim Mihuo As String

    On Error GoTo Yes

    If Len(name) < 5 Then MsgBox "用户名长度不能小于5位", , "提示": Exit Sub

    For Nxunhuan = 1 To Len(name)
    Yinbi = Yinbi + Asc(Mid(name, Nxunhuan, 1))
    Next Nxunhuan
    '循环取用户名所有字母的ASC值之和

    Zhongzhuan = Yinbi

    If Len(code) <= Len((Zhongzhuan)) Then MsgBox "注册码长度不够", , "提示": Exit Sub

    Zhang = Yinbi Xor Val(Left(code, Len(Zhongzhuan)))
    '将上述值跟输入注册码左边开始,相同长度的数值XOR

    Zhang = Log(Zhang)
    '对XOR值进行LOG运算,注意LOG有个特点是什么?
    '当ZHANG为零的时候会出现一个数值为5的异常
    '我们这里就是利用这个特点来设计隐蔽的注册过程来
    '反跟踪。
    '请问,破解者跟到这里了,他怎么会知道这里必须
    '产生一个异常值为5的异常才能跟到正确的注册过程,
    '如果不出现5异常的话,也没什么现象可以给他分析的
    '他只能继续分析下去,就像走一条大路,他根本就没发现
    '必须走小路才有收获,而一直走下去,貌似正确,实际被耍了

    Mihuo = Right(Zhang, 8)
    If Right(code, Len(code) - Len(Zhongzhuan)) = Mihuo Then
    MsgBox "假注册成功,无语了吧?", , "^_^"
    Else
    MsgBox "假注册码错误,落入陷阱", , "^_^"

    End If
    Exit Sub

    Yes:
    If Err.Number = 5 Then
    '检测异常类型是否为5,若为5说明是LOG异常了,表示可以进行正确的注册算法过程了
    '如果不是5,那就处理一般性的异常处理事务。
    '__________
    '这里开始可以嵌入我们真正的算法了

    If Right(code, Len(code) - Len(Zhongzhuan)) = "123456" Then
    MsgBox "真正注册成功", , "学会了吧?"
    End If
    '真正算法里面最好不要出现太明显的提示信息,所以这里我把注册失败给提示给去掉了
    '__________

    Else
    MsgBox "不好意思,请检测输入是否有问题", , "出错了"
    End If
    End Sub

    我是代码终止线————————————————————————————————

    调用如下:

    我是代码起始线————————————————————————————————

    Private Sub Command1_Click()
    ZhuCe Text1.Text, Text2.Text
    '可用真正注册码:
    'name: maplescg
    'code: 844123456

    '假注册码:
    'name: maplescg
    'code: 14245066571
    End Sub

    我是代码终止线————————————————————————————————

    看明白了吧?原来是这样的注册反调试过程:

    如图:



    正常状况下,维持左边正常假注册过程,只有在三叉路口,实现了代号为5的异常的时候,才执行右边正确的算法过程,而破解者基本看不明白在哪里有跳转,因为没有出现JMP这类的代码,而关键的LOG也被认为是计算注册码的必要函数,其实我们只是拿它来实现特定条件下的跳转工作。

    我们把这个决定分支启动的关键点称为“门”。“门”要么很坚固,进不去;要么很隐蔽,找不到。小说里,电视里常常出现这样的“门”,为了找什么秘籍或者宝物,总是要找到很隐蔽或者很坚固的门才行。

    该方法胜在隐蔽,而不是强大上。抗破解能力不是很强,但是绝对会让别人惊喜一下(假注册成功)配合别的技巧,当真是有用武之地的,望君勉之。

    看效果图:





    真正注册成功:

     

    第十一、步长、时值反调试(包含启动时间检测)

    除了前面我们讲过的RDTSC是经典的时值反调试指令可以被直接反汇编来写进我们的程序外,我们也可以用VB自身携带的一些时值检函数如:TIMER(精确到毫秒);也可以用API里的GetTickCount(精确到毫秒)

    甚至可以用API: GetProcessTimes 来进行加载检测,以起到反调试作用。

    这些技术,接下去,我们都要学,而且都要学会,一点都不难。

    先看RDTSC指令:

    0041BE74    0F31            RDTSC
    ‘获得时间
    0041BE76    8BC8            MOV ECX,EAX
    ‘传值到ECX
    0041BE78    0F31            RDTSC
    ‘再次获得时间
    0041BE7A    2BC1            SUB EAX,ECX
    ‘用这个值减去刚才我们储存到ECX的值,也就得到了时间差
    0041BE7C    3D 00050000     CMP EAX,500        
    ‘比较这个时间差是否大于500(H),若是大了说明存在调试
    0041BE81  ^ 0F8F ADF9FFFF   JG 了凡第一.0041B834
    ‘然后就随机跳到坟墓
    0041BE87    83F8 00         CMP EAX,0
    ‘比较这个时间差是否小于0(H),想想怎么可能小于零?光速也不行。
    0041BE8A  ^ 0F8C 8EF9FFFF   JL 了凡第一.0041B81E
            ‘如果小于零说明调试时间太长了,以至轮回到了负数

    在VB里该指令我也不知道怎么编译,干脆大家也自己通过反汇编后来汇编添加吧

    下面再讲:GetTickCount (返回系统启动以来经过的时间:毫秒)

    同样也是利用时间差来计算一段或者一句指令执行的时间差,这个值不应该超过我们设置好的一个值。

    打开VB6.0 新建工程,添加一个COMAND1

    在通用部分,添加GetTickCount声明:

    我是代码起始线————————————————————————————————

    Private Declare Function GetTickCount Lib "kernel32" Alias "GetTickCount" () As Long

    我是代码终止线————————————————————————————————
    在COMMAND1事件里添加以下代码:(实际编写中可以不拘一格)

    我是代码起始线————————————————————————————————

    Private Sub Command1_Click()

    '假设这里是我们的注册过程,我们隔三差五随意将以下代码复制粘帖
    '——————————————————————————————
    Dim atime As Long
    Dim btime As Long
    atime = GetTickCount
    btime = GetTickCount
    If btime - atime = 0 Then
    MsgBox btime - atime, , "正常运行,经历时间:"
    '实际软件中,应该彻底隐蔽这些提示消息
    Else
    MsgBox btime - atime, , "发现调试器,经历时间:"
    '实际软件中,应该彻底隐蔽这些提示消息,直接引入错误的分支去执行乱七八糟的代码
    End If
    '——————————————————————————————
    '算法部分......

    End Sub

    我是代码终止线————————————————————————————————

    效果如图:

     

    发现调试工作:



    再讲系统自带的TIMER函数

    打开VB6.0 新建工程 添加COMMAND1

    在它的事件代码里编写:

    我是代码起始线————————————————————————————————

    Private Sub Command1_Click()

    '假设这里是我们的注册过程,我们隔三差五随意将以下代码复制粘帖
    '------------------------------
    Dim ctime As Double
    Dim dtime As Double
    ctime = Timer
    dtime = Timer
    If dtime - ctime = 0 Then
    MsgBox dtime - ctime, , "正常运行,经历时间:"
    '实际软件中,应该彻底隐蔽这些提示消息
    Else
    MsgBox dtime - ctime, , "发现调试器,经历时间:"
    '实际软件中,应该彻底隐蔽这些提示消息,直接引入错误的分支去执行乱七八糟的代码
    End If

    End Sub

    我是代码终止线————————————————————————————————

    可见TIMER跟GetTickCount有差不多的应用,可以扩展下思维吧。

    关于时间类反调试,我讲最后一个,一是重点介绍的一个:

    API: GetProcessTimes

    我们先来看看它的作用和使用规范:

    Private Declare Function GetProcessTimes Lib "kernel32" Alias "GetProcessTimes" (ByVal hProcess As Long, lpCreationTime As FILETIME, lpExitTime As FILETIME, lpKernelTime As FILETIME, lpUserTime As FILETIME) As Long

    获取与一个进程的经过时间有关的信息

    Long,非零表示成功,零表示失败。会设置GetLastError

    hProcess -------  Long,一个进程句柄

    lpCreationTime -  FILETIME,指定一个FILETIME结构,在其中装载进程的创建时间

    lpExitTime -----  FILETIME,指定一个FILETIME结构,在其中装载进程的中止时间

    lpKernelTime ---  FILETIME,指定一个FILETIME结构,在其中装载进程花在内核模式上的总时间

    lpUserTime -----  FILETIME,指定一个FILETIME结构,在其中装载进程花在用户模式上的总时间
      
    适用平台
    Windows NT

    可见可以用它来检测加载时候的创建时间,一般在OD加载它,但是还没按F9或者点运行,但是系统早已自动帮我们记录了它的“启动”时间,我们只要到程序获得自主权的时候,检测这个“启动”到底花了多少时间就可以发现调试器,进而反调试了。

    跟我一起来:

    打开VB6.0 新建工程 添加一个LABEL1

    在通用部分写下以下代码:

    我是代码起始线————————————————————————————————

    Option Explicit
    Private Declare Sub GetLocalTime Lib "kernel32" (ntt As SystemTime)
    Private Declare Sub GetProcessTimes Lib "kernel32" (ByVal hProcess As Long, lpCreationTime As FILETIME, lpExitTime As FILETIME, lpKernelTime As FILETIME, lpUserTime As FILETIME)
    Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
    Private Declare Sub FileTimeToSystemTime Lib "kernel32" (lpFileTime As FILETIME, lpSystemTime As SystemTime)
    Private Type SystemTime
        wYear As Integer
        wMonth As Integer
        wDayOfWeek As Integer
        wDay As Integer
        wHour As Integer
        wMinute As Integer
        wSecond As Integer
        wMilliseconds As Integer
    End Type
    Private Type FILETIME
        dwLowDateTime As Long
        dwHighDateTime As Long
    End Type
    Dim id As Long
    Dim ctt As FILETIME
    Dim ett As FILETIME
    Dim ktt As FILETIME
    Dim utt As FILETIME
    Dim stt As SystemTime
    Dim ntt As SystemTime
    Dim qtt As Double

    我是代码终止线————————————————————————————————

    下面开始编写一个函数,可以方便复制和移植。

    我是代码起始线————————————————————————————————

    Private Function Antitime() As Boolean
    '自定义了一个利用GetProcessTimes自动进行检测调试器的函数
    GetLocalTime ntt
    '获得现在系统时间
    id = GetCurrentProcess
    '获得本进程ID
    GetProcessTimes id, ctt, ett, ktt, utt
    '获得本进程ID对应的信息
    FileTimeToSystemTime ctt, stt
    '转换时间格式
    qtt = Val(ntt.wSecond) * 1000 + ntt.wMilliseconds - Val(stt.wSecond) * 1000 - stt.wMilliseconds
    '计算时间差
    If qtt > 80 Or qtt < 0 Then
    '如果时间差大于80微秒或者小于0就认为发现了调试,这个值大家可以自行修改
    Antitime = True
    Else
    Antitime = False
    End If
    End Function

    我是代码终止线————————————————————————————————

    如何引用?必须放到窗口的LOAD事件或者Initialize 事件里,因为这连个事件在程序启动时加载,刚好可以给我们限定启动时间。请看代码:

    我是代码起始线————————————————————————————————

    Private Sub Form_Initialize()
    '这样调用即可
    If Antitime = True Then
    MsgBox "发现调试器了", , "提醒"
    Label1.ForeColor = &HFF&
    Label1.Caption = "发现调试器"
    Else
    Label1.ForeColor = &H80000012
    Label1.Caption = "没有发现调试"
    End If
    End Sub

    我是代码终止线————————————————————————————————

    请看效果图:

    没有调试时:



    OD装载调试:





    你可以测试别的调试器,结果基本一样,可靠性还是比较高的。

    请合理利用,比较点等要设计的比较隐蔽才可以。

    第十二、检测内存分配大小反调试

    有没有发现?当程序被OD带HIDEOD插件调试的时候,其分配的内存底线和上线跟正常的情况是不一致的。利用这点,我们可以设计一个反OD的函数。(其实其作用就是检测HIDEOD插件用的,只要开启该插件,就能被发现)