【原创】OverNimble LocPlus 1.05注册算法全程详解(写给学VB算法者)
                                  ————手把手系列之四
【破解作者】 jackily
【作者主页】 http://estudy.ys168.com
【使用工具】 ollydbg、 untelock和偶的一双手(手动脱壳)
【破解平台】 Win9x/NT/2000/XP
【软件名称】 OverNimble LocPlus 1.05
【软件简介】 OverNimble LocPlus主要用于非资源格式的本地化,支持的种类包括非资源格式的 C 编译的程序中的 ASCII 字符串和 UniCode 字符串、非资源格式的 Delphi(C++ Builder)编译的程序的字符串、VB 编译的程序的字符串、文本格式的字符串等的提取及替换。未注册版本有一些功能(“VA英文和符号”、“VA非控制符”、“VA中文(GBK)”、“VA中文(BIG5)”、“VA完全中文”、“VA非保留区”、“VA非限制查找”方式的 ASCII 及 uniCode 查找)不能使用。 
【加壳方式】tElock 0.98b1、PEBundle 2.0b5 - 3.0x 双层壳
【破解声明】 本破解纯以学习和交流为目的,偶得一点心得,愿与大家分享:)
--------------------------------------------------------------------------------

提起OverNimble LocPlus,大家可能很陌生,至少在真正接触它以前,我一直以为它是国外产品,但提到点睛字符串替换器,汉化界的朋友就非常熟悉了。其实二者是一回事。和狂风汉化百宝箱相类似,它最大的长处就是用于非资源格式字符串的本地化,而LocPlus做得更胜一筹,提供了更多的功能。在看雪精华4、5中侦探柯南、八鸟@和leeyam分别对其1.02版和1.04版做了分析,但采取的都是爆破方法,并没有深入到注册算法,而且他们对于关键代码的分析也存在着一些小BUG,因此本文一并指正出来,以免给初学者带来误导。
1.05版不仅增加了新功能,还在验证注册上稍做了改动,具体见分析过程。因此建议,在读本文前,先读一下侦探柯南、八鸟@和leeyam对1.02版和1.04版做的分析(参见看雪五周年收藏版),以便更好地理解本文。
和以往不同,这次倒过来写,先文字详述分析思路、过程和总结,代码分析放到最后的附录中,大家以交流心得为主,谁都不愿意,也没时间一句句地去读汇编代码,而且能读懂的也真不多,有兴趣的自己慢慢读吧。
读懂本文所必备的知识:
   1. 汇编基础知识;
   2. 听说过VB的浮点运算;
   3. 听说过VB的数据在内存中的存放方式;
   4. 了解如下VB内部函数的功能和参数调用(程序中将用到,先自行预习一下,代码分析中有讲解);
     4.1 _vbavaradd 
     4.2 _vbavarsub
     4.3 _vbaVarForInit、_vbaVarForNext
     4.4 _vbaLenBstr
     4.5 _vbaVarTstLe
     4.6 rtcR8ValFromBstr
     4.7 rtcVarBstrFromAnsi
     4.8 rtcIsNumeric
  
这些天伏案成疾,背疼,找了个郎中,也偷学了一些疗法,这回随我出趟诊吧,医不好,不收费的。:)

第一步:出诊(查壳)
首先,当然是用PEID查壳了,发现tElock 0.98b1,用untelock很轻松地去掉第一层外衣。本想再用PEID查一下是什么程序编写的(本人习惯:脱完壳后,再查一次),嗯?!怎么出来个PEBundle 2.0b5 - 3.0x呢?!嘿,我的爆脾气又来了,第一次听说啊?用google一搜索,明白了,与其说PEBundle是一个壳,不如说是EXE压缩器。于是乎找遍了大江南北,楞没找到相应的脱壳机(本人一般很懒,要是有脱壳机,决不自己动手)。这家伙,象大姑娘上花轿似的,扭扭捏捏地,还穿了里三层外三层的?!没办法,用我的手来脱吧。只说一下要点,脱壳部分码见附录一。
这壳特简单。用ollydbg载入,忽略警告,进入后发现入口处的两个命令pushfd(9c)、pushad(60)非常典型,那我们就寻找popad和popfd,连在一起是619d。Ctrl+B搜索619d...,找到后,在紧接着的RETN 处下断。F9运行,被拦,F8单步跟过,又是60(pushad),此时地址是004473D0,顺着代码,一行行地快速下查,在0044753B处找到一个61(popad),紧接着是jmp 004027fc,下断。再来个F8跟过,此时看到了什么?典型的VB程序入口。用插件中的ollydump脱壳,轻松搞掂。

第二步:诊脉(查看注册校验方式)
打开脱壳后的locplus,点注册,弹出对话框,要求输入用户名和注册码?分别输入jackily和123456点确定,要求重启验证。这是1.05与以往版本不同之处。1.04以前版本有出错信息, 在ollydbg中可以查找到其地址,可以下断,因此其在输入注册码时,会直接检验一次,此时会有机会跟踪注册码。当然,每次重启时,它也都会重新校验注册码。而1.05不直接检验注册码,只在重启时检验,这给跟踪增加了一定的难度,是1.05的一大改进。

第三步:B超检查(动态调试)
既然是重启校验的方式,仔细想一下,无碍乎就是读key文件和读注册表。重新载入程序,点右键—“搜索”—“字符参考”。咦,只有稀稀啦啦的几个注册表项目树,并没有发现1.02和1.04版中出现的\Software\OverNimble\LocPlusRegUserName和RegCode项(注:在以前的版本中直接可查到具体的注册个子项),看来作者在反破解上下了点功夫。不过这点已经够了,说明注册码保存在注册表中了。那么如何下断呢?就断在HKEY_CURRENT_USER的地址上。为什么?我试出来的呗!以下过程请对照附录二的代码分析阅读。在00431a43下断,一步一步向下,发现经过计算,\Software\OverNimble\LocPlusRegUserName和RegCode项被解码出来,原来作者对这两个注册表项加密了。再经过N步跟踪,发现了一个重要信息,在00431a43和00431a71处的两个语句mov edx,eax中,eax存放着用户名和注册码的地址(个人认为,长时间的跟踪能力是大部分cracker所欠缺的,这是crack成功与否的关键)。再向下跟,就和1.04版以前的注册验证部分代码相似了。00431a81的call是关键算法,其中对输入的假码同“ONLocPlus"进行了循环计算(注意大小写),并得出新值,子调用中的算法详见附录二,在此不再述赘;00431a91是检验新值的长度,而非侦探柯南和leeyam所说的注册码长度,实际的注册码长度是26位,而这里是0xc,即12位,也就是说,通过重新计算,26位的注册码被转成12位新值;00431aad是检验新值是否为浮点数值,是就通过,否就失败;00431ac8是检验浮点数值的HEX值,也就是要求这个数值是大于0的,也非侦探柯南和leeyam所说的比较注册码;00431ad9是浮点数比较,[402050]中是程序既定值“9900000000.00000000”,因此,新算出的浮点值是“99.后接18个0”。这个写注册机时要用到。以上两点更正是需向读者说明的,以免造成学习过程中的误解。

第四步:开药方(编写注册机)
【破解总结】
一、用户名不参加注册计算,校验注册条件有三:
1、注册码运算后为12位数,即0xC;换句话说,注册码应该为26位。
2、注册码运算后应为数字,分析中会指出判断函数。
3、运算后的数字的HEX值应大于1 。
二、算法思路:
算法属于较特殊的变量比较一种,注册码第1、2位需取00,第3位开始,取12轮,每次取两个位(不用转换,已经认为是HEX值),和“ONLocPlus”的第一位开始(循环取值),每次取1个字母,进行异或。异或值是否小于前一对注册码值,如果是,用0xff减去异或值,再加上前一对注册码值,反之,用异或值减去前一对注册码值。如果前10轮结果值都等于0x30,并且11、12轮结果等于0x39,那么12轮结果转十进制并相连便是00000000099,此时注册码成功。(解释的我都有点糊涂了,能看懂吗?)
算法的数学表达式:
设x为注册码,y为符合条件值,a和b为变量,a=f1(x),b=f2(y),如果f3(a)=b,x为正确的。也就是说如果等式f3(f1(x))=f2(y)成立,x合法有效。
【算法注册机】
/* OverNimble LocPlus c语言注册机 */
/* 本注册机对以前版本也适用 */
/* 在Turboc 2.0 下调试通过  */
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
main()
{
char name[100]={0} ,s[]="ONLocPlus";
int i,j;
long unsigned c,code,k=0,m,t=0;
  printf("OverNimble LocPlusB KeyGen by jackily 2005-1-18\n");
  printf("Email:jackily_zhang@msn.com or jackily_zhang@yahoo.com.cn\n");
  printf("please input name:");
  scanf("%s",name);
  printf("\nYour serial number is:00");
    for (i=0;i<0xc;i++)
 { j=i;
  for (k=0;k<0xff;k++)
  {
  code=k;
  if (j>0x8) j-=0x9;
  m=k^s[j];
  if (t<=m) c=m-t;
  else {c=0xff-t;c+=m;}
  if (i<=0xa&&c==0x30) break;
  if (i>0xa&&c==0x39) break;
  }
 t=code;                        
  if (code<=0xf) printf("0%x",code);

  else printf("%x",code);
  }
}
--------------------------------------------------------------------------------
【内存注册机】
非明码比较,无法实现

【爆破地址】
小孩爬窗户的行为,都有钥匙了,还用吗?

【用户名、密码】
用户名:jackily
SN    :007FE15EE171F14E0B4837292E

--------------------------------------------------------------------------------

附录一:脱壳分析(结合第一步阅读)

步骤一:
0044E000 L>  9C           pushfd                 ; 程序进入点
0044E001     60           pushad
0044E002     E8 02000000  call LocPlus-.0044E009
0044E007     33C0         xor eax,eax
0044E009     8BC4         mov eax,esp
0044E00B     83C0 04      add eax,4
0044E00E     93           xchg eax,ebx

.........中间省略

0044E195     8D9D 0923400>lea ebx,dword ptr ss:[ebp+402309]
0044E19B     53           push ebx
0044E19C     6A 00        push 0
0044E19E     FFD0         call eax
0044E1A0     FFA5 D628400>jmp dword ptr ss:[ebp+4028D6]
0044E1A6     61           popad                     ; 搜索到这里
0044E1A7     9D           popfd
0044E1A8     68 D0734400  push LocPlus-.004473D0
0044E1AD     C3           retn                      ; 下断

步骤二:
004473D0     60           pushad                     ;由44E1AD的retn而来
004473D1     BE 15604300  mov esi,LocPlus-.00436015
004473D6     8DBE EBAFFCF>lea edi,dword ptr ds:[esi+FFFCAFEB]
004473DC     57           push edi
004473DD     83CD FF      or ebp,FFFFFFFF
004473E0     EB 10        jmp short LocPlus-.004473F2
004473E2     90           nop

...........中间省略

0044752E     8903         mov dword ptr ds:[ebx],eax
00447530     83C3 04      add ebx,4
00447533   ^ EB D8        jmp short LocPlus-.0044750D
00447535     FF96 447D040>call dword ptr ds:[esi+47D44]
0044753B     61           popad
0044753C   - E9 BBB2FBFF  jmp LocPlus-.004027FC     ;下第二个断点

真正的程序入口:

004027FC     68 FC594000  push LocPlus-.004059FC        ;oep,在此dump...
00402801     E8 EEFFFFFF  call LocPlus-.004027F4        ;jmp to MSVBVM50.ThunRTMain
00402806     0000         add byte ptr ds:[eax],al
00402808     0000         add byte ptr ds:[eax],al
0040280A     0000         add byte ptr ds:[eax],al
0040280C     3000         xor byte ptr ds:[eax],al
0040280E     0000         add byte ptr ds:[eax],al
00402810     48           dec eax
00402811     0000         add byte ptr ds:[eax],al
00402813     0000         add byte ptr ds:[eax],al
00402815     0000         add byte ptr ds:[eax],al
00402817     0053 4A      add byte ptr ds:[ebx+4A],dl
0040281A   - E9 0B725CCE  jmp CE9C9A2A
........以下代码省略

-------------------------------------------------------------------------------
附录二:代码分析(结合第三步阅读)
004319E0     55           push ebp
004319E1     8BEC         mov ebp,esp
004319E3     83EC 14      sub esp,14
004319E6     68 C6224000  push <jmp.&MSVBVM50.__vbaExceptHandler>
004319EB     64:A1 000000>mov eax,dword ptr fs:[0]
004319F1     50           push eax
004319F2     64:8925 0000>mov dword ptr fs:[0],esp
004319F9     83EC 40      sub esp,40
004319FC     53           push ebx
004319FD     56           push esi
004319FE     57           push edi
004319FF     8965 EC      mov dword ptr ss:[ebp-14],esp
00431A02     C745 F0 7820>mov dword ptr ss:[ebp-10],locplus-.004020>
00431A09     33F6         xor esi,esi
00431A0B     8975 F4      mov dword ptr ss:[ebp-C],esi
00431A0E     8975 F8      mov dword ptr ss:[ebp-8],esi
00431A11     8975 DC      mov dword ptr ss:[ebp-24],esi
00431A14     8975 CC      mov dword ptr ss:[ebp-34],esi
00431A17     8975 BC      mov dword ptr ss:[ebp-44],esi
00431A1A     6A 01        push 1
00431A1C     FF15 F8C4430>call dword ptr ds:[<&MSVBVM50.__vbaOnErro>; MSVBVM50.__vbaOnError
00431A22     66:8935 2C91>mov word ptr ds:[43912C],si
00431A29     68 38914300  push locplus-.00439138
00431A2E     8D45 CC      lea eax,dword ptr ss:[ebp-34]
00431A31     50           push eax
00431A32     E8 F90E0000  call locplus-.00432930
00431A37     8D4D CC      lea ecx,dword ptr ss:[ebp-34]
00431A3A     51           push ecx
00431A3B     8B3D 38C4430>mov edi,dword ptr ds:[<&MSVBVM50.__vbaStr>; MSVBVM50.__vbaStrVarMove
00431A41     FFD7         call edi
00431A43     8BD0         mov edx,eax                     ;eax存放着用户名地址
00431A45     B9 28914300  mov ecx,locplus-.00439128
00431A4A     8B35 18C7430>mov esi,dword ptr ds:[<&MSVBVM50.__vbaStr>; MSVBVM50.__vbaStrMove
00431A50     FFD6         call esi
00431A52     8D4D CC      lea ecx,dword ptr ss:[ebp-34]
00431A55     8B1D 2CC4430>mov ebx,dword ptr ds:[<&MSVBVM50.__vbaFre>; MSVBVM50.__vbaFreeVar
00431A5B     FFD3         call ebx
00431A5D     68 3C914300  push locplus-.0043913C
00431A62     8D55 CC      lea edx,dword ptr ss:[ebp-34]
00431A65     52           push edx
00431A66     E8 C50E0000  call locplus-.00432930
00431A6B     8D45 CC      lea eax,dword ptr ss:[ebp-34]
00431A6E     50           push eax
00431A6F     FFD7         call edi
00431A71     8BD0         mov edx,eax                      ;eax存放着注册码地址
00431A73     8D4D DC      lea ecx,dword ptr ss:[ebp-24]
00431A76     FFD6         call esi
00431A78     8D4D CC      lea ecx,dword ptr ss:[ebp-34]
00431A7B     FFD3         call ebx
00431A7D     8D4D DC      lea ecx,dword ptr ss:[ebp-24]
00431A80     51           push ecx
00431A81     E8 0AFBFFFF  call locplus-.00431590           ;关键算法call,必须跟进
00431A86     8BD0         mov edx,eax                      ;eax为新数值的地址
00431A88     8D4D DC      lea ecx,dword ptr ss:[ebp-24]
00431A8B     FFD6         call esi
00431A8D     8B55 DC      mov edx,dword ptr ss:[ebp-24]
00431A90     52           push edx
00431A91     FF15 30C4430>call dword ptr ds:[<&MSVBVM50.__vbaLenBstr> ; 取新值的长度
00431A97     83F8 0C      cmp eax,0C                             ;检验新值的长度是否为12位
00431A9A     75 6A        jnz short locplus-.00431B06              ;跳走即失败
00431A9C     8D45 DC      lea eax,dword ptr ss:[ebp-24]
00431A9F     8945 C4      mov dword ptr ss:[ebp-3C],eax
00431AA2     C745 BC 0840>mov dword ptr ss:[ebp-44],4008
00431AA9     8D4D BC      lea ecx,dword ptr ss:[ebp-44]
00431AAC     51           push ecx
00431AAD     FF15 8CC5430>call dword ptr ds:[<&MSVBVM50.rtcIsNumeric> ; 检验新值是否为浮点数值
00431AB3     66:85C0      test ax,ax                                  ;是就通过
00431AB6     74 4E        je short locplus-.00431B06                  ;跳走即失败
00431AB8     8B55 DC      mov edx,dword ptr ss:[ebp-24]
00431ABB     52           push edx
00431ABC     FF15 58C7430>call dword ptr ds:[<&MSVBVM50.rtcR8ValFromBstr>];
00431AC2     FF15 00C7430>call dword ptr ds:[<&MSVBVM50.__vbaFpI4>] ; MSVBVM50.__vbaFpI4
00431AC8     8945 E0      mov dword ptr ss:[ebp-20],eax
00431ACB     83F8 01      cmp eax,1                                ; 检验浮点数值的HEX值
00431ACE     7C 36        jl short locplus-.00431B06                 ;跳走即失败
00431AD0     DB45 E0      fild dword ptr ss:[ebp-20]
00431AD3     DD5D A4      fstp qword ptr ss:[ebp-5C]
00431AD6     DD45 A4      fld qword ptr ss:[ebp-5C]
00431AD9     DC1D 5020400>fcomp qword ptr ds:[402050]   ; 浮点数比较
00431ADF     DFE0         fstsw ax                      ;[402050] 既定值“9900000000.00000000”
00431AE1     F6C4 41      test ah,41
00431AE4     74 20        je short locplus-.00431B06
00431AE6     66:C705 2C91>mov word ptr ds:[43912C],0FFFF
00431AEF     FF15 DCC4430>call dword ptr ds:[<&MSVBVM50.__vbaExitPr>; MSVBVM50.__vbaExitProc
00431AF5     9B           wait
00431AF6     68 281B4300  push locplus-.00431B28
00431AFB     EB 21        jmp short locplus-.00431B1E
00431AFD     66:C705 2C91>mov word ptr ds:[43912C],0
00431B06     FF15 DCC4430>call dword ptr ds:[<&MSVBVM50.__vbaExitPr>; MSVBVM50.__vbaExitProc
00431B0C     9B           wait
00431B0D     68 281B4300  push locplus-.00431B28
00431B12     EB 0A        jmp short locplus-.00431B1E
00431B14     8D4D CC      lea ecx,dword ptr ss:[ebp-34]
00431B17     FF15 2CC4430>call dword ptr ds:[<&MSVBVM50.__vbaFreeVa>; MSVBVM50.__vbaFreeVar
00431B1D     C3           retn
00431B1E     8D4D DC      lea ecx,dword ptr ss:[ebp-24]
00431B21   - FF25 54C7430>jmp dword ptr ds:[<&MSVBVM50.__vbaFreeStr>; MSVBVM50.__vbaFreeStr
00431B27     C3           retn
00431B28     8B4D E4      mov ecx,dword ptr ss:[ebp-1C]
00431B2B     64:890D 0000>mov dword ptr fs:[0],ecx
00431B32     5F           pop edi
00431B33     5E           pop esi
00431B34     5B           pop ebx
00431B35     8BE5         mov esp,ebp
00431B37     5D           pop ebp
00431B38     C3           retn

--------------------------------------------------------
关键算法,由00431a81跟进。
00431590     55     push ebp
00431591     8BEC   mov ebp,esp
00431593     83EC 0>sub esp,0C
00431596     68 C62>push <jmp.&MSVBVM50.__vbaExceptHandler>
0043159B     64:A1 >mov eax,dword ptr fs:[0]
004315A1     50     push eax
004315A2     64:892>mov dword ptr fs:[0],esp
004315A9     81EC E>sub esp,0E0
004315AF     A1 589>mov eax,dword ptr ds:[439158]
004315B4     53     push ebx
004315B5     56     push esi
004315B6     57     push edi
004315B7     8965 F>mov dword ptr ss:[ebp-C],esp
004315BA     33FF   xor edi,edi
004315BC     50     push eax                        ; eax=001a7514,unicode "ONLocPlus"
004315BD     C745 F>mov dword ptr ss:[ebp-8],locplus-.00402068
004315C4     897D D>mov dword ptr ss:[ebp-24],edi
004315C7     897D D>mov dword ptr ss:[ebp-2C],edi
004315CA     897D C>mov dword ptr ss:[ebp-3C],edi
004315CD     897D C>mov dword ptr ss:[ebp-40],edi
004315D0     897D B>mov dword ptr ss:[ebp-48],edi
004315D3     897D A>mov dword ptr ss:[ebp-54],edi
004315D6     897D A>mov dword ptr ss:[ebp-58],edi
004315D9     897D 9>mov dword ptr ss:[ebp-68],edi
004315DC     897D 8>mov dword ptr ss:[ebp-78],edi
004315DF     89BD 7>mov dword ptr ss:[ebp-88],edi
004315E5     89BD 6>mov dword ptr ss:[ebp-98],edi
004315EB     89BD 5>mov dword ptr ss:[ebp-A8],edi
004315F1     89BD 4>mov dword ptr ss:[ebp-B8],edi
004315F7     89BD 3>mov dword ptr ss:[ebp-C8],edi
004315FD     89BD 2>mov dword ptr ss:[ebp-D8],edi                 
00431603     89BD 1>mov dword ptr ss:[ebp-E8],edi
00431609     FF15 3>call dword ptr ds:[<&MSVBVM50.__vbaLenBstr>] ; 取"ONLocPlus"的长度放eax,为9
0043160F     8BC8   mov ecx,eax
00431611     FF15 9>call dword ptr ds:[<&MSVBVM50.__vbaI2I4>]     ; MSVBVM50.__vbaI2I4
00431617     8B4D 0>mov ecx,dword ptr ss:[ebp+8]
0043161A     BE 020>mov esi,2
0043161F     68 00D>push locplus-.0040D900                  ; UNICODE "&H"
00431624     56     push esi
00431625     8B11   mov edx,dword ptr ds:[ecx]
00431627     8945 B>mov dword ptr ss:[ebp-4C],eax        ; 把长度值9放入ebp-4c,在00431786会用到
0043162A     52     push edx                             ; 注册码地址
0043162B     FF15 F>call dword ptr ds:[<&MSVBVM50.rtcLeftCharBstr>  ; 取左边两个字符,
00431631     8B1D 1>mov ebx,dword ptr ds:[<&MSVBVM50.__vbaStrMove>; MSVBVM50.__vbaStrMove
00431637     8BD0   mov edx,eax
00431639     8D4D A>lea ecx,dword ptr ss:[ebp-54]
0043163C     FFD3   call ebx                           ; 把宽型第1、2字符的地址放入ebp-54中       0043163E     50     push eax
0043163F     FF15 8>call dword ptr ds:[<&MSVBVM50.__vbaStrCat>] ; &H和第1、2字符连在一起
00431645     8BD0   mov edx,eax
00431647     8D4D A>lea ecx,dword ptr ss:[ebp-58]
0043164A     FFD3   call ebx                             ; 把1a76ac,&H和第1、2字符地址放入ebp-58
0043164C     50     push eax
0043164D     FF15 5>call dword ptr ds:[<&MSVBVM50.rtcR8ValFromBstr> ; &&H和第1、2字符换成浮点数00431653     FF15 F>call dword ptr ds:[<&MSVBVM50.__vbaFpI2>]     ; MSVBVM50.__vbaFpI2
00431659     8BD8   mov ebx,eax
0043165B     8D45 A>lea eax,dword ptr ss:[ebp-58]
0043165E     8D4D A>lea ecx,dword ptr ss:[ebp-54]
00431661     50     push eax
00431662     51     push ecx
00431663     56     push esi
00431664     FF15 A>call dword ptr ds:[<&MSVBVM50.__vbaFreeStrLis>; MSVBVM50.__vbaFreeStrList
0043166A     8B55 0>mov edx,dword ptr ss:[ebp+8]                            ; 注册码存放地址
0043166D     83C4 0>add esp,0C
00431670     89B5 6>mov dword ptr ss:[ebp-A0],esi                          ; ESI=0x2
00431676     89B5 5>mov dword ptr ss:[ebp-A8],esi
0043167C     8B02   mov eax,dword ptr ds:[edx]
0043167E     50     push eax                                        ; 注册码存放地址给EAX
0043167F     FF15 3>call dword ptr ds:[<&MSVBVM50.__vbaLenBstr>]  ; 算注册码长度
00431685     8985 5>mov dword ptr ss:[ebp-B0],eax                 ; 长度应为0x1a,放入ebp-b0
0043168B     B8 030>mov eax,3
00431690     8D8D 5>lea ecx,dword ptr ss:[ebp-A8]
00431696     8985 4>mov dword ptr ss:[ebp-B8],eax                 ; eax=0x3
0043169C     8985 4>mov dword ptr ss:[ebp-C0],eax
004316A2     8D95 4>lea edx,dword ptr ss:[ebp-B8]
004316A8     51     push ecx
004316A9     8D85 3>lea eax,dword ptr ss:[ebp-C8]
004316AF     52     push edx
004316B0     8D8D 1>lea ecx,dword ptr ss:[ebp-E8]
004316B6     50     push eax
004316B7     8D95 2>lea edx,dword ptr ss:[ebp-D8]
004316BD     51     push ecx
004316BE     8D45 D>lea eax,dword ptr ss:[ebp-24]
004316C1     52     push edx
004316C2     50     push eax
004316C3     89B5 3>mov dword ptr ss:[ebp-C8],esi                              
004316C9     FF15 E>call dword ptr ds:[<&MSVBVM50.__vbaVarForInit>; 和00431925中的call联合使用,相当于for循环语句
004316CF     3BC7   cmp eax,edi                         ;条件符合的话,eax=0,否=1 
004316D1     0F84 5>je locplus-.00431932
004316D7     8D4D 9>lea ecx,dword ptr ss:[ebp-68]                        
004316DA     8D55 D>lea edx,dword ptr ss:[ebp-24]                                 
004316DD     BF 080>mov edi,8
004316E2     51     push ecx
004316E3     52     push edx
004316E4     C785 5>mov dword ptr ss:[ebp-B0],locplus-.0040D900   ; UNICODE "&H"
004316EE     89BD 4>mov dword ptr ss:[ebp-B8],edi                  ; edi=0x8
004316F4     8975 A>mov dword ptr ss:[ebp-60],esi                 ; esi=0x2
004316F7     8975 9>mov dword ptr ss:[ebp-68],esi
004316FA     FF15 C>call dword ptr ds:[<&MSVBVM50.__vbaI4Var>]    ; 转长整型
00431700     50     push eax                                      ; 此处通过计算,每一循环取两个字符,下次是5,7,以此类推
00431701     8B45 0>mov eax,dword ptr ss:[ebp+8]
00431704     8B08   mov ecx,dword ptr ds:[eax]
00431706     51     push ecx                         ; 注册码地址
00431707     FF15 4>call dword ptr ds:[<&MSVBVM50.rtcMidCharBstr>>; 从EDX值处(=3、5、7等)取两个字符。
0043170D     8945 9>mov dword ptr ss:[ebp-70],eax                ; 地址放入ebp-70
00431710     8D55 8>lea edx,dword ptr ss:[ebp-78]                ; 内容值为0,下同
00431713     8D85 7>lea eax,dword ptr ss:[ebp-88]
00431719     52     push edx
0043171A     50     push eax
0043171B     897D 8>mov dword ptr ss:[ebp-78],edi                ; edi=0x8
0043171E     FF15 2>call dword ptr ds:[<&MSVBVM50.rtcTrimVar>]    ; MSVBVM50.rtcTrimVar
00431724     8D8D 4>lea ecx,dword ptr ss:[ebp-B8]                  
0043172A     8D95 7>lea edx,dword ptr ss:[ebp-88]
00431730     51     push ecx                          ; ebp-b8 [ecx]=8
00431731     8D85 6>lea eax,dword ptr ss:[ebp-98]
00431737     52     push edx
00431738     50     push eax
00431739     FF15 D>call dword ptr ds:[<&MSVBVM50.__vbaVarAdd>]   ; MSVBVM50.__vbaVarAdd
0043173F     8D4D A>lea ecx,dword ptr ss:[ebp-54]
00431742     50     push eax
00431743     51     push ecx
00431744     FF15 4>call dword ptr ds:[<&MSVBVM50.__vbaStrVarVal>>; 把&H和注册码连在一起
0043174A     50     push eax      
0043174B     FF15 5>call dword ptr ds:[<&MSVBVM50.rtcR8ValFromBstr>; 转浮点数
00431751     FF15 F>call dword ptr ds:[<&MSVBVM50.__vbaFpI2>]     ; MSVBVM50.__vbaFpI2
00431757     8D4D A>lea ecx,dword ptr ss:[ebp-54]            ; call 后ax随注册码在变
0043175A     8BF8   mov edi,eax                              ;VB够笨的,以上实际就是把假码从第3位开始,每次取2个字符,循环取完

0043175C     FF15 5>call dword ptr ds:[<&MSVBVM50.__vbaFreeStr>]  ; MSVBVM50.__vbaFreeStr
00431762     8D95 6>lea edx,dword ptr ss:[ebp-98]
00431768     8D85 7>lea eax,dword ptr ss:[ebp-88]
0043176E     52     push edx
0043176F     8D4D 8>lea ecx,dword ptr ss:[ebp-78]
00431772     50     push eax
00431773     8D55 9>lea edx,dword ptr ss:[ebp-68]
00431776     51     push ecx
00431777     52     push edx
00431778     6A 04  push 4
0043177A     FF15 4>call dword ptr ds:[<&MSVBVM50.__vbaFreeVarLis>; 清空变量
00431780     8B45 C>mov eax,dword ptr ss:[ebp-40]
00431783     83C4 1>add esp,14
00431786     66:3B4>cmp ax,word ptr ss:[ebp-4C]       ; 最早"ONLocPlus"的长度
0043178A     7D 0D  jge short locplus-.00431799
0043178C     66:40  inc ax                                ; 计数器
0043178E     0F80 3>jo locplus-.004319D3
00431794     8945 C>mov dword ptr ss:[ebp-40],eax
00431797     EB 07  jmp short locplus-.004317A0
00431799     C745 C>mov dword ptr ss:[ebp-40],1
004317A0     0FBF4D>movsx ecx,word ptr ss:[ebp-40]
004317A4     8B15 5>mov edx,dword ptr ds:[439158]          ; 地址01a7514,unicode "ONLocPlus"
004317AA     8D45 9>lea eax,dword ptr ss:[ebp-68]
004317AD     50     push eax
004317AE     51     push ecx                       ; ecx=1,2,3等,类推,
004317AF     52     push edx
004317B0     C745 A>mov dword ptr ss:[ebp-60],1
004317B7     8975 9>mov dword ptr ss:[ebp-68],esi     ; esi=2
004317BA     FF15 4>call dword ptr ds:[<&MSVBVM50.rtcMidCharBstr>>; 取"ONLocPlus”取ecx位值,循环
004317C0     8BD0   mov edx,eax
004317C2     8D4D A>lea ecx,dword ptr ss:[ebp-54]
004317C5     FF15 1>call dword ptr ds:[<&MSVBVM50.__vbaStrMove>]  ; 地址放入ebp-54,0012f8dc
004317CB     50     push eax
004317CC     FF15 6>call dword ptr ds:[<&MSVBVM50.rtcAnsiValueBst>; 分别转ascii,如"O"转换成HEX值,eax=0x4F
004317D2     33C7   xor eax,edi                              ;假码与取的值异或          
004317D4     8D95 4>lea edx,dword ptr ss:[ebp-B8]
004317DA     8D4D C>lea ecx,dword ptr ss:[ebp-3C]              ; 地址12f8f4
004317DD     66:898>mov word ptr ss:[ebp-B0],ax              ; ax=异或后的值, 放入[ebp-b0]
004317E4     89B5 4>mov dword ptr ss:[ebp-B8],esi
004317EA     FF15 1>call dword ptr ds:[<&MSVBVM50.__vbaVarMove>]  ; MSVBVM50.__vbaVarMove
004317F0     8D4D A>lea ecx,dword ptr ss:[ebp-54]
004317F3     FF15 5>call dword ptr ds:[<&MSVBVM50.__vbaFreeStr>]  ; MSVBVM50.__vbaFreeStr
004317F9     8D4D 9>lea ecx,dword ptr ss:[ebp-68]
004317FC     FF15 2>call dword ptr ds:[<&MSVBVM50.__vbaFreeVar>]  ; MSVBVM50.__vbaFreeVar
00431802     8D45 C>lea eax,dword ptr ss:[ebp-3C]
00431805     8D8D 5>lea ecx,dword ptr ss:[ebp-A8]
0043180B     50     push eax
0043180C     51     push ecx
0043180D     66:899>mov word ptr ss:[ebp-A0],bx          ; bx=假码的第1位开始取值,每次2个
00431814     C785 5>mov dword ptr ss:[ebp-A8],8002
0043181E     FF15 B>call dword ptr ds:[<&MSVBVM50.__vbaVarTstLe>] ; 比较是否ax<=bx
00431824     66:85C>test ax,ax                                 ; 小于eax=0,否=FFFF
00431827     74 5A  je short locplus-.00431883                 ;小于跳,不小于不跳
00431829     8D95 5>lea edx,dword ptr ss:[ebp-A8]
0043182F     8D45 C>lea eax,dword ptr ss:[ebp-3C]
00431832     52     push edx
00431833     8D4D 9>lea ecx,dword ptr ss:[ebp-68]
00431836     50     push eax
00431837     51     push ecx
00431838     C785 6>mov dword ptr ss:[ebp-A0],0FF                     ; 赋值,0x0ff
00431842     89B5 5>mov dword ptr ss:[ebp-A8],esi
00431848     66:899>mov word ptr ss:[ebp-B0],bx
0043184F     89B5 4>mov dword ptr ss:[ebp-B8],esi
00431855     FF15 D>call dword ptr ds:[<&MSVBVM50.__vbaVarAdd>]   ; MSVBVM50.__vbaVarAdd
0043185B     50     push eax
0043185C     8D95 4>lea edx,dword ptr ss:[ebp-B8]
00431862     8D45 8>lea eax,dword ptr ss:[ebp-78]
00431865     52     push edx
00431866     50     push eax
00431867     FF15 0>call dword ptr ds:[<&MSVBVM50.__vbaVarSub>]   ; MSVBVM50.__vbaVarSub
0043186D     8BD0   mov edx,eax                                                            
0043186F     8D4D C>lea ecx,dword ptr ss:[ebp-3C]
00431872     FF15 1>call dword ptr ds:[<&MSVBVM50.__vbaVarMove>]  ; MSVBVM50.__vbaVarMove
00431878     8D4D 9>lea ecx,dword ptr ss:[ebp-68]
0043187B     FF15 2>call dword ptr ds:[<&MSVBVM50.__vbaFreeVar>]  ; MSVBVM50.__vbaFreeVar
00431881     EB 2D  jmp short locplus-.004318B0            ; 以上为不小于的话,ax+0xff-bx
00431883     8D4D C>lea ecx,dword ptr ss:[ebp-3C]            ; 由431827跳来,
00431886     8D95 5>lea edx,dword ptr ss:[ebp-A8]
0043188C     51     push ecx
0043188D     8D45 9>lea eax,dword ptr ss:[ebp-68]
00431890     52     push edx
00431891     50     push eax
00431892     66:899>mov word ptr ss:[ebp-A0],bx      
00431899     89B5 5>mov dword ptr ss:[ebp-A8],esi     ; esi=0x2
0043189F     FF15 0>call dword ptr ds:[<&MSVBVM50.__vbaVarSub>]   ; MSVBVM50.__vbaVarSub
004318A5     8BD0   mov edx,eax                                    ;小于则 bx-ax
004318A7     8D4D C>lea ecx,dword ptr ss:[ebp-3C]
004318AA     FF15 1>call dword ptr ds:[<&MSVBVM50.__vbaVarMove>]  ; MSVBVM50.__vbaVarMove
004318B0     8B4D B>mov ecx,dword ptr ss:[ebp-48]
004318B3     8D55 C>lea edx,dword ptr ss:[ebp-3C]
004318B6     52     push edx
004318B7     898D 6>mov dword ptr ss:[ebp-A0],ecx
004318BD     C785 5>mov dword ptr ss:[ebp-A8],8
004318C7     FF15 C>call dword ptr ds:[<&MSVBVM50.__vbaI4Var>]    ; MSVBVM50.__vbaI4Var
004318CD     50     push eax
004318CE     8D45 9>lea eax,dword ptr ss:[ebp-68]
004318D1     50     push eax
004318D2     FF15 2>call dword ptr ds:[<&MSVBVM50.rtcVarBstrFromA>; MSVBVM50.rtcVarBstrFromAnsi
004318D8     8D8D 5>lea ecx,dword ptr ss:[ebp-A8]
004318DE     8D55 9>lea edx,dword ptr ss:[ebp-68]
004318E1     51     push ecx
004318E2     8D45 8>lea eax,dword ptr ss:[ebp-78]
004318E5     52     push edx
004318E6     50     push eax
004318E7     FF15 D>call dword ptr ds:[<&MSVBVM50.__vbaVarAdd>]   ; 此处需仔细研究
004318ED     50     push eax
004318EE     FF15 3>call dword ptr ds:[<&MSVBVM50.__vbaStrVarMove>; MSVBVM50.__vbaStrVarMove
004318F4     8BD0   mov edx,eax                  
004318F6     8D4D B>lea ecx,dword ptr ss:[ebp-48]          ; 得出数值放入ebp-48,最后连在一起需得000000000099,才正确
004318F9     FF15 1>call dword ptr ds:[<&MSVBVM50.__vbaStrMove>]  
004318FF     8D4D 8>lea ecx,dword ptr ss:[ebp-78]
00431902     8D55 9>lea edx,dword ptr ss:[ebp-68]
00431905     51     push ecx
00431906     52     push edx
00431907     56     push esi
00431908     FF15 4>call dword ptr ds:[<&MSVBVM50.__vbaFreeVarLis>; MSVBVM50.__vbaFreeVarList
0043190E     83C4 0>add esp,0C
00431911     8D85 1>lea eax,dword ptr ss:[ebp-E8]
00431917     8D8D 2>lea ecx,dword ptr ss:[ebp-D8]
0043191D     8D55 D>lea edx,dword ptr ss:[ebp-24]
00431920     50     push eax
00431921     51     push ecx
00431922     52     push edx
00431923     8BDF   mov ebx,edi
00431925     FF15 4>call dword ptr ds:[<&MSVBVM50.__vbaVarForNext>; 为下一循环作准备
0043192B     33FF   xor edi,edi
0043192D   ^ E9 9DF>jmp locplus-.004316CF
00431932     8B55 B>mov edx,dword ptr ss:[ebp-48]
00431935     8D4D D>lea ecx,dword ptr ss:[ebp-2C]
00431938     FF15 9>call dword ptr ds:[<&MSVBVM50.__vbaStrCopy>]  ; MSVBVM50.__vbaStrCopy
0043193E     9B     wait
0043193F     68 BD1>push locplus-.004319BD
00431944     EB 44  jmp short locplus-.0043198A
00431946     F645 F>test byte ptr ss:[ebp-4],4
0043194A     74 09  je short locplus-.00431955
0043194C     8D4D D>lea ecx,dword ptr ss:[ebp-2C]
0043194F     FF15 5>call dword ptr ds:[<&MSVBVM50.__vbaFreeStr>]  ; MSVBVM50.__vbaFreeStr
00431955     8D45 A>lea eax,dword ptr ss:[ebp-58]
00431958     8D4D A>lea ecx,dword ptr ss:[ebp-54]
0043195B     50     push eax
0043195C     51     push ecx
0043195D     6A 02  push 2
0043195F     FF15 A>call dword ptr ds:[<&MSVBVM50.__vbaFreeStrLis>; MSVBVM50.__vbaFreeStrList
00431965     83C4 0>add esp,0C
00431968     8D95 6>lea edx,dword ptr ss:[ebp-98]
0043196E     8D85 7>lea eax,dword ptr ss:[ebp-88]
00431974     8D4D 8>lea ecx,dword ptr ss:[ebp-78]
00431977     52     push edx
00431978     50     push eax
00431979     8D55 9>lea edx,dword ptr ss:[ebp-68]
0043197C     51     push ecx
0043197D     52     push edx
0043197E     6A 04  push 4
00431980     FF15 4>call dword ptr ds:[<&MSVBVM50.__vbaFreeVarLis>; MSVBVM50.__vbaFreeVarList
00431986     83C4 1>add esp,14
00431989     C3     retn
0043198A     8D85 1>lea eax,dword ptr ss:[ebp-E8]
00431990     8D8D 2>lea ecx,dword ptr ss:[ebp-D8]
00431996     50     push eax
00431997     51     push ecx
00431998     6A 02  push 2
0043199A     FF15 4>call dword ptr ds:[<&MSVBVM50.__vbaFreeVarLis>; MSVBVM50.__vbaFreeVarList
004319A0     8B35 2>mov esi,dword ptr ds:[<&MSVBVM50.__vbaFreeVar>; MSVBVM50.__vbaFreeVar
004319A6     83C4 0>add esp,0C
004319A9     8D4D D>lea ecx,dword ptr ss:[ebp-24]
004319AC     FFD6   call esi
004319AE     8D4D C>lea ecx,dword ptr ss:[ebp-3C]
004319B1     FFD6   call esi
004319B3     8D4D B>lea ecx,dword ptr ss:[ebp-48]
004319B6   - FF25 5>jmp dword ptr ds:[<&MSVBVM50.__vbaFreeStr>]   ; MSVBVM50.__vbaFreeStr
004319BC     C3     retn
004319BD     8B4D E>mov ecx,dword ptr ss:[ebp-14]
004319C0     8B45 D>mov eax,dword ptr ss:[ebp-2C]
004319C3     5F     pop edi
004319C4     5E     pop esi
004319C5     64:890>mov dword ptr fs:[0],ecx
004319CC     5B     pop ebx
004319CD     8BE5   mov esp,ebp
004319CF     5D     pop ebp
004319D0     C2 040>retn 4                                    ; 跳回 00431a86 
     
--------------------------------------------------------------------------------
为了更好地让大家了解和掌握VB注册算法,针对本文讲述的关键部分和最终注册机源码,将用几个问题来结束本文,算是读后思考题吧。回答问题请跟帖,希望管理员将此帖置顶,三天后将公布答案。

【思考题】

1.脱壳时,第一个断点后,F8单步跟过,又是60(pushad),为什么要顺着代码,一行行地下查,而不能用Ctrl+B搜索61(popad)?
2.004316c9、00431925中的_vbaVarForInit和_vbaVarForNext起何作用,是如何实现的?
3.0043181E_vbaVarTstLe在代码中起什么作用?
4.算法注册机第21行的(if (j>0x8) j-=0x9;)语句有何意义?28行的(t=code;)语句代表什么? 
5.算法注册机第29行的(if (code<=0xf) printf("0%x",code);)与30行(else printf("%x",code);)语句有何区别?                        

后记:
1.05于2003年3月推出,增加了专用的对照文件编辑器、工具“偏移量转换器”、“文本编码查询”和“剪贴板繁简转换”,给用户提供了很大的便利条件。但至今本人未见该软件的注册机面世,不知是各位高手不屑一破,还是说其在算法上有一定的难度。因为要试用其全部功能,于是便尝试着分析了一下,本注册机向下兼容,也适合以前版本。
另外,再重申一次,本破解纯粹是技术交流。不为别的,因为这是我目前所见过的VB程序中,注册算法最隐蔽、最有特点的程序之一(也许我太孤陋寡闻),我对该软件的作者表示崇高的敬意。
同时,在此也感谢我的妻子emily长时间来对我的关心和理解,是她能容忍我不去写我的硕士毕业论文,却要和解密熬夜较劲的这种“不务正业”(引用她的口头禅)的行为。不要误会,我可不是学计算机的;本科是英语,硕士读经济,对电脑纯是业余爱好,but I like this game!
                                  二零零五年元月二十三日

【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!