【文章标题】: 无知者无畏----菜菜鸟也找注册码
【文章作者】: AsmDebuger 
【作者QQ号】: 38250367
【软件名称】: xxx电子教程
【下载地址】: 自己搜索下载
【编写语言】: VB
【使用工具】: OD、PEiD、Google
【操作平台】: XP SP2
【软件介绍】: 一本电子书
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
    自走上学习破解之路后,就有了个习惯-看到任何需要注册的软件都要查查、脱脱壳,只是发现
大部分软件脱了壳也不能直接运行,其保护措施比较全面,因此对于我这样的菜菜鸟来说,这些软件
跟起来还很吃力,而有那么几个软件,只要脱了壳就基本上破解成功了,这样的软件,对于我们菜菜
鸟来说,正是练手的好靶子,今天提到的软件即是如此。
    这两天新年刚开工,闲来无事,常在坛子里泡,无意中看到一位仁兄求破解的违规帖,于是下载
来试试手,先用PEiD查壳:ASPack 2.12 -> Alexey Solodovnikov,嗯,不算是猛壳,还记得上次我提
到的超级巡警吧?直接脱之,成功!再查:Microsoft Visual Basic 5.0 / 6.0,怀着忐忑的心情用颤抖的
右手双击了一下图标,哇!能运行,美妙的开始(说实话,最近脱过的软件,能直接运行成功的寥寥
无几)!到此我信心倍增,不过我对VB还停留在无知的阶段,还是那句老话:无知者无畏,下面就
开始寻找注册码。
    软件开始运行的是一个登录窗口,显示机器码为"964446455",需要填入注册码,根据提示说明,
有个试用密码"admin168",但功能有限制, 菜菜鸟也想找注册码。回忆一下,对于VB的程序,看过
一些资料,说有个什么字符串比较的断点,名字不记得了,别急,用OD载入脱壳后的软件,Ctrl+N 
查看所有函数名称:
地址 区段 类型 ( 名称 注释
……
00401030 .text 输入 MSVBVM60.__vbaSetSystemError
0040102C .text 输入 ( MSVBVM60.__vbaStrCat
0040107C .text 输入 MSVBVM60.__vbaStrCmp ;这个看起来应该是了。
0040100C .text 输入 MSVBVM60.__vbaStrI4
    直接F2下断(忽略在数据段下断的提示)。运行,随便输入密码"123".点击"登陆"按钮,程序停
在MSVBVM60的领空,看堆栈:
0012F3BC 004657EC 返回到 unpacked.004657EC 来自 MSVBVM60.__vbaStrCmp
0012F3C0 0040D300 UNICODE "admin168"
0012F3C4 0015E63C UNICODE "123"

猜测是拿我们的假码和试用密码比较,我们到004657EC看看先:

  004657DD  >   \8B45 98       MOV     EAX, SS:[EBP-68]
  004657E0  .  50             PUSH    EAX
  004657E1  .  68   00D34000  PUSH    unpacked.0040D300   ;  UNICODE "admin168"
  004657E6  .  FF15 7C104000  CALL    NEAR DS:[<&MSVBVM60.__vbaStrCmp>];  MSVBVM60.__vbaStrCmp
  004657EC  .    8BF8            MOV     EDI, EAX

    果然如此,不过这个不是我们想要的,我们要的是注册码,继续往上看(往下已经提示出错了), 仍然采用"倒叙法",
    注意程序流程,看是怎样跳到上面那个比较的:

  0046579D       . /E9 90020000       JMP     unpacked.00465A32  ;这里是跳走,
  004657A2       > |8B16              MOV     EDX, DS:[ESI]       ;  unpacked.004C1620
  004657A4       . |56                PUSH    ESI
  004657A5       . |FF92 0C030000     CALL    NEAR DS:[EDX+30C]
;;;;;;;;;;;

提示窗口显示004657A2处的跳转来自 004656A0,继续往前看:

  0046565F       > \8B45 98           MOV     EAX, SS:[EBP-68]
  00465662       .  8D8D 74FFFFFF     LEA     ECX, SS:[EBP-8C]
  00465668       .  8D56 34           LEA     EDX, DS:[ESI+34]
  0046566B       .  51                PUSH    ECX                                   ; /var18 = ntdll.7C93056D
  0046566C       .  52                PUSH    EDX                                   ; |var28 = 00130000
  0046566D       .  C745 98 00000000  MOV     DWORD PTR SS:[EBP-68], 0              ; |
  00465674       .  8985 7CFFFFFF     MOV     SS:[EBP-84], EAX                      ; |
  0046567A       .  C785 74FFFFFF 088>MOV     DWORD PTR SS:[EBP-8C], 8008           ; |
  00465684       .  FF15 84104000     CALL    NEAR DS:[<&MSVBVM60.__vbaVarTstEq>] ; \__vbaVarTstEq 
  0046568A       .  8D4D 84           LEA     ECX, SS:[EBP-7C]
  0046568D       .  8BF8              MOV     EDI, EAX
  0046568F       .  FF15 44114000     CALL    NEAR DS:[<&MSVBVM60.__vbaFreeObj>]  ;  MSVBVM60.__vbaFreeObj
  00465695       .  8D8D 74FFFFFF     LEA     ECX, SS:[EBP-8C]
  0046569B       .  FFD3              CALL    NEAR EBX                            ;  MSVBVM60.__vbaFreeVar
  0046569D       .  66:85FF           TEST    DI, DI
004656A0 . 0F84 FC000000     JE     unpacked.004657A2 ;DI等零则跳到试用码比较;根据vbaVarTstEq的返回参数,
                     ;等0则为不等,等-1则相等,猜测软件先比较注册码了。
PS:
00465684 这行有"Eq"看到它我就有点儿警觉,有可能是EQU的缩写,因此这个CALL有可能是比较
函数,Google搜索该函数提示是变量比较, 疑点!在0046565F直接下断F2.再次重新开始运行,输
入假码,点击"登陆"按钮,中断,

  0046565F       > \8B45 98           MOV     EAX, SS:[EBP-68]
  00465662       .  8D8D 74FFFFFF     LEA     ECX, SS:[EBP-8C]
  00465668       .  8D56 34           LEA     EDX, DS:[ESI+34]
  0046566B       .  51                PUSH    ECX                                          ; /var18 = 0012F47C
  0046566C       .  52                PUSH    EDX                                          ; |var28 = 0015C2AC
  0046566D       .  C745 98 00000000  MOV     DWORD PTR SS:[EBP-68], 0                     ; |
  00465674       .  8985 7CFFFFFF     MOV     SS:[EBP-84], EAX                             ; |
  0046567A       .  C785 74FFFFFF 088>MOV     DWORD PTR SS:[EBP-8C], 8008                  ; |
  00465684       .  FF15 84104000     CALL    NEAR DS:[<&MSVBVM60.__vbaVarTstEq>]          ; \__vbaVarTstEq

运行到此,看两个参数并不能知道注册码,只得F7跟进vbaVarTstEq这个Call:

  7349BBE6 MSVBVM60.__vbaVarTstEq       FF7424 08           PUSH    DWORD PTR SS:[ESP+8]
  7349BBEA                              FF7424 08           PUSH    DWORD PTR SS:[ESP+8]   ;第一个参数压栈;
  7349BBEE                              6A 00               PUSH    0
  7349BBF0                              E8 2254FFFF         CALL    MSVBVM60.73491017   ;继续跟进这个Call。

进入未知函数:

  73491017                55                  PUSH    EBP
  73491018                8BEC                MOV     EBP, ESP
  7349101A                83EC 38             SUB     ESP, 38
  7349101D                8B55 10             MOV     EDX, SS:[EBP+10]                             ; unpacked.0040AD64
  73491020                8B4D 0C             MOV     ECX, SS:[EBP+C]
  ;;;
  73491049                0FB745 08           MOVZX   EAX, WORD PTR SS:[EBP+8]
  7349104D                68 01000300         PUSH    30001
  73491052                50                  PUSH    EAX
  73491053                51                  PUSH    ECX
  73491054                52                  PUSH    EDX
  73491055                FF15 700E4A73       CALL    NEAR DS:[734A0E70]                           ; OLEAUT32.VarCmp

运行到这里,继续跟进OLEAUT32.VarCmp这个Call(Var为一种特殊的结构,现在还没有看到明码):

  77109F38 OLEAUT32.Var>  8BFF                MOV     EDI, EDI
  77109F3A                55                  PUSH    EBP
  77109F3B                8BEC                MOV     EBP, ESP
  ;;;;
  7710C013                6A 05               PUSH    5
  7710C015                FF75 14             PUSH    DWORD PTR SS:[EBP+14]
  7710C018                8BFE                MOV     EDI, ESI
  7710C01A                FF75 10             PUSH    DWORD PTR SS:[EBP+10]
  7710C01D                8BC3                MOV     EAX, EBX
  7710C01F                E8 6AEFFFFF         CALL    OLEAUT32.7710AF8E

继续运行,到了这里,仍然没有出现明码,且没有明显的比较指令、函数,继续:

  7710AF8E                8BFF                MOV     EDI, EDI
  7710AF90                55                  PUSH    EBP
  7710AF91                8BEC                MOV     EBP, ESP
  7710AF93                83EC 10             SUB     ESP, 10
  7710AF96                66:8B0F             MOV     CX, DS:[EDI]
  ;;;;
  7710AFC5                56                  PUSH    ESI
  7710AFC6                8D45 F0             LEA     EAX, SS:[EBP-10]
  7710AFC9                50                  PUSH    EAX
  7710AFCA                E8 1BBBFEFF         CALL    OLEAUT32.VariantChangeTypeEx 
;看名称知道是类型转换,因为前面的变量比较可以比较不同类型的变量,因此我们猜测此处可能是
转换明码或假码的程序,直接F8过;

  7710AFCF                85C0                TEST    EAX, EAX
  7710AFD1                7C 1D               JL      SHORT OLEAUT32.7710AFF0
  7710AFD3                FF75 0C             PUSH    DWORD PTR SS:[EBP+C]
  7710AFD6                FF75 08             PUSH    DWORD PTR SS:[EBP+8]
  7710AFD9                FF77 08             PUSH    DWORD PTR DS:[EDI+8]
  7710AFDC                FF75 F8             PUSH    DWORD PTR SS:[EBP-8]
  7710AFDF                E8 30F8FFFF         CALL    OLEAUT32.VarBstrCmp   
;哇!看名称知道,久违的比较函数到来了!运行到此,看堆栈:
ESP ==> 0012F2CC 0015E63C   UNICODE "1110989163.25" ;明码
ESP+4 0012F2D0 0015E664   UNICODE "123" ;假码

    哈哈,这里就可以看到注册码的明码了,二话不说,记下明码,退出OD,直接运行程序,输入
我们刚才得到的明码(全部输入,包括"."),嗯,无出错提示进入软件运行界面,再点击有注册限制
的功能,已经可以显示了,至此,我们已经找到注册码;
PS:
在此推测vbaVarTstEq函数运行机制:由于变量有可能类型不同,因此该函数会根据第一个参数的类
型对第二个参数进行类型转换,然后调用相应的比较 函数进行判断,本例是UNICODE码和浮点数
进行比较,把第二参数转换成UNICODE后再进行比较,因此如果bpx VarBstrCmp可以成功,但对
于其他的程序,有可能不成功,比较保险的还是下断vbaVarTstEq,再跟进几步就可以看到注册码了。

    最后说说软件的注册码计算方法(通过查浮点数指令集得到):
机器码(本机964446455)做为十进制值,先浮点除以4,再浮点乘以3
然后浮点加387654322.0000000,最后得到的浮点数即是注册码,如果小数点后为0则只需输入整数
即可;
小数点后不为零则需要输入小数点加上小数数位,由于运算过程仅除以4,因此小数点不会超过两位,
即不存在除不尽的情况,以避免无法得到注册码的BUG。

--------------------------------------------------------------------------------
【经验总结】
    这个软件的破解比较容易,首先是脱壳容易,第二是没有复杂的验证方法,之所以写这篇文章,主要是说说菜菜鸟对于
一个毫不了解的语言编写的程序怎样进行破解,其实主要是先多看大牛们的教程,记下各个语言关键下断的地方(比如这个
VB程序,如果在vbaVarTstEq下断的话就更快些),看清楚程序流程,以追溯到关键地址, 遇到不清楚功能的函数直接Google,
很多情况下Google会帮我们链接到看雪的某个帖子,相信会有很大帮助的。菜菜鸟经验不足,有失偏颇之处,敬请见谅!

    正在写破文的过程中接到了从卓越买的《加密与解密》(第三版)的书,质量不错,已经爱不释手了,下一步准备系
统地学习一下书本的内容,以免自己失去方向。