看了deroko 的《Ripping VB code and making keygen out of it》,觉得写得非常好,给写算法注册机带来了不少方便之处。但是作者在最后部分特别是写注册机方面过于烦琐,不直观,尤其是其中引用大部分的宏在TASM中编译的时带来不少麻烦,所以我重新编辑了下,连翻带改写了这篇文章,里面的许多内容及一些处理方法会跟作者的的有所出入,并且添加了许多内容。附上在RADASM中编译通过的MASM32代码。

    一、找到关键算法处
    这是个VB写的CRACKME,先用VB的反编译利器P32Dasm(http://t4c.ic.cz/forum/showthread.php?t=67)反编译这个软件,得到以下信息:

文件: C:\Documents and Settings\ crackme\crackme.exe
P32Dasm v2.3
VB6 编译类型: NCode
frm1
004028F4  1.1 cmdExit.Click()
004029C6  1.2 cmdRegister.Click()       注册按钮处理过程
00402BD3  1.3 Command1.Click()
00402D3D  1.4 Command2.Click()
0040315C  1.5 Form.Load()
00403332  1.6 txtReg.KeyPress(KeyAscii As Integer)
文件反编译结束。

    从这里,我们得到信息,该CRACKME的按钮处理过程在004029C6处。
用OD载入,在004029C6处下断,运行程序后随便输入注册码,按注册后断下。单步跟踪,到下面代码:

00402AB2   .  68 24404000   PUSH crackme.00404024                    ;  00404024  001540A4  UNICODE "Serializer"
00402AB7   .  50            PUSH EAX                                 ;  0012F4EC  0015A04C  UNICODE "6D6074621C09010205737401740207"
00402AB8   .  E8 00090000   CALL crackme.004033BD                    ;  关键算法
00402ABD   .  8BD0          MOV EDX,EAX                              ;  得到注册码
00402ABF   .  8D4D DC       LEA ECX,DWORD PTR SS:[EBP-24]
00402AC2   .  E8 99E7FFFF   CALL <JMP.&msvbvm60.__vbaStrMove>
00402AC7   .  50            PUSH EAX
00402AC8   .  E8 99E7FFFF   CALL <JMP.&msvbvm60.__vbaStrCmp>         ;  对比注册码
00402ACD   .  8BF0          MOV ESI,EAX

    将OD中的内存数据窗口切换到“长型”-“地址”,我们在00402AB2和00402AB7处右击-在数据窗口中跟随,可以看到这两处分别是UNICODE字符串“Serializer”和机器码,00402AB8处是关键算法,00402ABD处得到真正的注册码,所以00402AB8处的CALL非常关键,但是我们现在不想了解其中的算法过程,只想拷贝CALL 004033BD里面的代码做注册机。

    二、截取代码
    启动IDA,反汇编该CRACKME,“跳转到指定地址”,填入004033BD,将004033BD到00403613处代码涂黑(即CALL 004033BD的整个内容),选择菜单“文件”-创建文件-创建ASM文件,我们保存为crackme.asm。
    我们将这段代码随便找个汇编注册机模版放进去,这里将它放到我修改的ARTEAM的一个模版里。在RADASM中编译,让它列出错误。

    三、分析代码
    先讲一些基本的错误:
1、seg000:004033C3                 push    (offset loc_401175+1)
    从IDA中可以看出这是个压入异常句柄,置0即可,我们把它改为:
    push   offset  dummy_ seh
    …
    dummy_seh:      xor     eaxeax
                ret
2、mov  eax, large fs:0
    此处的large是修饰符,直接去掉,“fs:0” MASM也认不出来,我们在.DATA区加入
    assume  fs:nothing
3、seg000:004033DF mov dword ptr [ebp-8], offset dword_401160
    我们在OD中可以看到,dword_401160= 8000Eh
    所以在.DATA处加入:
    dword_401160            dd 8000Eh
4、seg000:00403572 push offset dword_4027BC
   这里其实IDA反汇编错误(因为VB的字符串是以UNICODE形式存在的),dword_4027BC 是字符串 "0" 和 2 ,即:
    dd 2 <-- --- 字符串长度
    string_0 dd 30h <----- 字符串0
    所以在.DATA处加入:
                         dd 2
    dword_4027BC          dd 30h
5、jo  loc_403616
   push  offset loc_403602
   jmp  short loc_4035E9
   这几个可以在IDA中直接跟到代码处拷贝相应的内容:
   loc_403616:        ; CODE XREF: seg000:00403488 j
          ; seg000:004034F2 j ...
    call    __vbaErrorOverflow
   loc_403602:        ; DATA XREF: seg000:004035B9 o
    mov  ecx, [ebp-14h]
    mov  eax, [ebp-20h]
    pop  edi
    pop  esi
    mov  large fs:0, ecx
    pop  ebx
    leave
    retn  8

  loc_4035E9:        ; CODE XREF: seg000:004035BE j
    lea  ecx, [ebp-24h]
    call   __vbaFreeStr
    lea  ecx, [ebp-28h]
    call   __vbaFreeStr
    lea  ecx, [ebp-2Ch]
    call   __vbaFreeStr
    retn

    现在来讲一个重点错误:
    就是所有的VB内部函数,这个在MASM中当然不可能被识别。但是…
    感谢Vortex为我们解决了这个问题,它的Dll2inc(http://www.vortex.masmcode.com/)轻松解决了这个烦恼。Vortex原本是想用来生成可以供MASM调用的C运行库的,可是我拿VB的运行库msvbvm60.dll测试了一下,居然成功了。
    拿msvbvm60.dll经过Dll2inc转换后得到msvbvm60.def、msvbvm60.inc、msvbvm60.lib,这样就可以在MASM32中调用了。他专门定义了个调用这些函数的宏:cinvoke。我们只要在include和includelib中加入这些INC文件和LIB文件,然后将所有的调用VB内部函数的CALL改为cinvoke就可以“光明正大”的用这些VB内部函数了。

    RADASM中编译,可是还是有错误。
    我们用OD来跟一下到底问题出在哪里。RADASM中设置好调试器关联后,选择“构建”-“在调试器中运行”,OD自动反汇编了这个有问题的KEYGEN。我们逐步跟踪,到下面的代码时,出现异常,问题就出在__vbaStrCat中。

004012BC   .  E8 2B020000   CALL <JMP.&msvbvm60.__vbaStrCat>

    我们跟入__vbaStrCat:

660E5F3A >  55              PUSH EBP
660E5F3B    8BEC            MOV EBP,ESP
660E5F3D    8D45 08         LEA EAX,DWORD PTR SS:[EBP+8]
660E5F40    50              PUSH EAX
660E5F41    FF75 08         PUSH DWORD PTR SS:[EBP+8]
660E5F44    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
660E5F47    FF15 18EE1066   CALL DWORD PTR DS:[6610EE18]             ; DS:[6610EE18]=00000000,问题出在这里

    跟到660E5F47时出现异常,我们看到PTR DS:[6610EE18]为0,我们拿原来的CRACKME或者直接拿msvbvm60.dll反汇编,跟入__vbaStrCat:

660E5F47    FF15 18EE1066   CALL DWORD PTR DS:[6610EE18]             ; OLEAUT32.VarBstrCat

   可以看出问题所在:PTR DS:[6610EE18]在CRACKEME中有被初始化成VarBstrCat而在KEYGEN中没有。
   我们用IDA反汇编msvbvm60.dl后,跟踪__vbaStrCat函数:

.text:660E5F40                 push    eax
.text:660E5F41                 push    [ebp+arg_0]
.text:660E5F44                 push    [ebp+arg_4]
.text:660E5F47                 call    dword_6610EE18 
    跟进CALL:
.data:6610EE18 dword_6610EE18  dd 0                    ; DATA XREF: sub_66004F58+46C w
.data:6610EE18                                         ; __vbaStrCat+D r
    继续跟踪:
.text:660053BA                 push    offset aVarbstrcat ; "VarBstrCat"
.text:660053BF                 push    edi
.text:660053C0                 call    esi ; GetProcAddress
.text:660053C2                 test    eaxeax
.text:660053C4                 mov     dword_6610EE18, eax
    我们来看看哪个输出函数能够导出该功能,继续跟踪下去:
.text:66004D81 sub_66004D81    proc near               ; CODE XREF: sub_66004D59+20 p
.text:66004D81                                         ; CreateIExprSrvObj+3E p
    CreateIExprSrvObj觉得可疑,跟进看看:
.text:660EA734                 public CreateIExprSrvObj
.text:660EA734 CreateIExprSrvObj proc near
.text:660EA734
.text:660EA734 var_8           = dword ptr -8
.text:660EA734 var_4           = dword ptr -4
.text:660EA734 arg_0           = dword ptr  8
.text:660EA734 arg_4           = word ptr  0Ch
.text:660EA734 arg_8           = word ptr  10h
.text:660EA734
.text:660EA734                 push    ebp
.text:660EA735                 mov     ebpesp
.text:660EA737                 push    ecx
.text:660EA738                 push    ecx
.text:660EA739                 push    ebx
.text:660EA73A                 xor     ebxebx
.text:660EA73C                 cmp     [ebp+arg_4], 4
.text:660EA741                 push    esi
.text:660EA742                 push    edi
.text:660EA743                 mov     [ebp+var_8], ebx
.text:660EA746                 mov     [ebp+var_4], ebx
.text:660EA749                 jnz     loc_660EA7F6
.text:660EA74F                 cmp     [ebp+arg_8], bx
.text:660EA753                 ja      loc_660EA7F6
.text:660EA759                 call    sub_660CCD82
.text:660EA75E                 test    eaxeax
.text:660EA760                 jnz     short loc_660EA782
.text:660EA762                 push    2
.text:660EA764                 push    ebx
.text:660EA765                 push    ebx
.text:660EA766                 push    9
.text:660EA768                 push    ebx
.text:660EA769                 push    ebx
.text:660EA76A                 push    2636h
.text:660EA76F                 push    ebx
.text:660EA770                 push    6
.text:660EA772                 call    sub_66004D81  <---- 就是这个CALL
    我们需要这个CALL,deroko对CALL进行了修改并使之运行正常:
    push    0
    push    4
    push    0
    call    CreateIExprSrvObj(记得改call为cinvoke)
    我们复制到代码区,RADASM中编译已经通过了。

    四、解决UNICODE字符串
    我们注意到在调用这个算法CALL之前有压入机器码和字符串"Serializer",这些字符串在VB中都是以UNICODE形式存在的,所以在定义字符串"Serializer"时我们必须定义为UNICODE字符格式。
    利用MASM32的宏ucMacros.asm可以轻易做到只要在定义的字符串变量前面加WSTR即可:
    WSTR static_string,     "Serializer"
    我们还需要在代码中加入压入机器码和字符串"Serializer"的代码,然后才能调用CALL。

    五,完善注册机
    如果进一步分析,会发现软件机器码的由来。
    0040322C   .  50            PUSH EAX
    0040322D   .  E8 EEF3FFFF   CALL crackme.00402620                    ;  此CALL跳到GetComputerNameW函数,返回计算机名
    ...
    00403286   .  57            PUSH EDI                                 ;  00404024  0013F63C  UNICODE "Serializer"
    00403287   .  50            PUSH EAX                                 ;  计算机名  UNICODE "OEM-MICRO"
    00403288   .  E8 30010000   CALL crackme.004033BD                    ;  算法CALL
    0040328D   .  8BD0          MOV EDX,EAX                              ;  得到机器码
    也就是说计算机名和字符串"Serializer"经过这个算法CALL后得到机器码。
    在注册机中我们就可以直接用改代码获取机器码。

    好了,主要代码已经贴在下面了,其实关键CALL内的算法只是字符串间简单的异或操作。这里讨论的不是怎么去分析这个算法过程。我主要是想大家了解的是这种注册机写法的思路。若有差错,希望补充。
Generate proc
                        push    0
                        push    4
                        push    0
                        cinvoke    CreateIExprSrvObj  

; -----------------------------------------------------------   计算机器码                                       
                        mov  computer_name_size,sizeof computer_name    
                        invoke   GetComputerNameW,addr computer_name,addr computer_name_size                       
                        xor     eaxeax
                        mov     edioffset computer_name
                        stc
                        sbb     ecxecx
                        cld
                        repnz   scasw
                        not     ecx
                        dec     ecx
                        shl     ecx, 1
                        mov     computer_name_size, ecx
                         
                        mov     eaxoffset static_string
                        mov     ptr1, eax
                        mov     eaxoffset computer_name
                        mov     ptr2, eax
                        push    offset ptr1                    ;压入UNICODE字符串"Serializer"
                        push    offset ptr2                    ;压入计算机名
                        call    loc_4033BD                     ;算法CALL                      
                        mov     esieax                       ;得到机器码
                        mov     edioffset hardware_key
                        xor     ecxecx
                        
; -----------------------------------------------------------   将机器码放到hardware_key中供机器码框调用
copy_hardwarekey:       lodsw
                        test    axax
                        jz      get_hardwarekey
                        add     ecx, 2
                        stosw
                        jmp     copy_hardwarekey

; -----------------------------------------------------------   计算注册码    
get_hardwarekey:        mov     hardware_key_size, ecx                        
                        mov     eaxoffset static_string
                        mov     ptr1, eax
                        mov     eaxoffset hardware_key
                        mov     ptr2, eax                        
                        push    offset ptr1                      ;压入UNICODE字符串"Serializer"
                        push    offset ptr2                      ;压入机器码          
                        call    loc_4033BD                       ;算法CALL                        
                        mov     esieax                         ;得到注册码
                        mov     edioffset SerialBuffer

; -----------------------------------------------------------   将注册码复制到SerialBuffer供注册码框调用   
get_SerialBuffer:       lodsw
                        test    axax
                        jz      exit
                        stosw
                        jmp     get_SerialBuffer
                      
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 以下是算法CALL内容,拷自IDA
loc_4033BD:
    push  ebp
    mov  ebpesp
    sub  esp, 0Ch
    push  offset  dummy_seh
    mov  eax,  fs:0
    push  eax
    mov   fs:0, esp
    sub  esp, 64h
    push  ebx
    push  esi
    push  edi
    mov  [ebp-0Ch], esp
    mov  dword ptr [ebp-8], offset dword_401160
    mov  edi, [ebp+0Ch]
    push  1
    pop  esi
    xor  eaxeax
    push  dword ptr [edi]
    mov  [ebp-20h], eax
    mov  [ebp-24h], eax
    mov  [ebp-28h], eax
    mov  [ebp-2Ch], eax
    mov  [ebp-38h], eax
    mov  [ebp-3Ch], eax
    mov  [ebp-40h], eax
    mov  [ebp-50h], eax
    mov  [ebp-60h], eax
    mov  [ebp-1Ch], esi
    cinvoke  __vbaLenBstr

    push  2
    mov  [ebp-68h], eax
    mov  [ebp-18h], esi
    pop  ebx

; ---------------------------------------------------------------------------
loc_40341C:        ; CODE XREF: seg000:00403491j
    mov  eax, [ebp-18h]
    cmp  eax, [ebp-68h]
    jg  short loc_403493

    push  dword ptr [ebp-24h]
    lea  ecx, [ebp-50h]
    mov  [ebp-48h], esi
    mov  [ebp-50h], ebx
    push  ecx
    push  eax
    push  dword ptr [edi]
    cinvoke  rtcMidCharBstr

    mov  edxeax
    lea  ecx, [ebp-3Ch]
    cinvoke  __vbaStrMove

    push  eax
    cinvoke  rtcAnsiValueBstr

    push  eax
    cinvoke  __vbaStrI2

    mov  edxeax
    lea  ecx, [ebp-40h]
    cinvoke  __vbaStrMove

    push  eax
    cinvoke  __vbaStrCat

    mov  edxeax
    lea  ecx, [ebp-24h]
    cinvoke  __vbaStrMove

    lea  eax, [ebp-40h]
    push  eax
    lea  eax, [ebp-3Ch]
    push  eax
    push  ebx
    cinvoke  __vbaFreeStrList

    add  esp, 0Ch
    lea  ecx, [ebp-50h]
    cinvoke  __vbaFreeVar

    push  1
    pop  eax
    add  eax, [ebp-18h]
    jo  loc_403616

    mov  [ebp-18h], eax
    jmp  short loc_40341C

; ---------------------------------------------------------------------------

loc_403493:        ; CODE XREF: seg000:00403422j
    mov  eax, [ebp+8]
    push  dword ptr [eax]
    cinvoke  __vbaLenBstr

    mov  [ebp-70h], eax
    mov  [ebp-18h], esi

; ---------------------------------------------------------------------------
loc_4034A3:        ; CODE XREF: seg000:004035A9j
    mov  edi, [ebp-18h]
    cmp  edi, [ebp-70h]
    jg  loc_4035AE

    lea  eax, [ebp-50h]
    mov  [ebp-48h], esi
    push  eax
    mov  [ebp-50h], ebx
    push  dword ptr [ebp-1Ch]
    push  dword ptr [ebp-24h]
    cinvoke  rtcMidCharBstr

    mov  edxeax
    lea  ecx, [ebp-3Ch]
    cinvoke  __vbaStrMove

    push  eax
    cinvoke  rtcAnsiValueBstr

    movsx  eaxax
    lea  ecx, [ebp-3Ch]
    mov  [ebp-30h], eax
    cinvoke  __vbaFreeStr

    lea  ecx, [ebp-50h]
    cinvoke  __vbaFreeVar

    mov  eax, [ebp-1Ch]
    push  dword ptr [ebp-24h]
    add  eaxesi
    jo  loc_403616

    mov  [ebp-1Ch], eax
    cinvoke  __vbaLenBstr

    cmp  [ebp-1Ch], eax
    jle  short loc_403508

    mov  [ebp-1Ch], esi

; ---------------------------------------------------------------------------
loc_403508:        ; CODE XREF: seg000:00403503j
    lea  eax, [ebp-50h]
    mov  [ebp-48h], esi
    push  eax
    mov  eax, [ebp+8]
    push  edi
    mov  [ebp-50h], ebx
    push  dword ptr [eax]
    cinvoke  rtcMidCharBstr

    mov  edxeax
    lea  ecx, [ebp-3Ch]
    cinvoke  __vbaStrMove

    push  eax
    cinvoke  rtcAnsiValueBstr

    lea  ecx, [ebp-3Ch]
    movsx  ediax
    cinvoke  __vbaFreeStr

    lea  ecx, [ebp-50h]
    cinvoke  __vbaFreeVar

    xor  edi, [ebp-30h]
    lea  eax, [ebp-38h]
    mov  [ebp-58h], eax
    lea  eax, [ebp-60h]
    push  eax
    mov  [ebp-38h], edi
    mov  dword ptr [ebp-60h], 4003h
    cinvoke  rtcHexBstrFromVar

    mov  edxeax
    lea  ecx, [ebp-28h]
    cinvoke  __vbaStrMove

    push  dword ptr [ebp-28h]
    cinvoke  __vbaLenBstr

    cmp  eaxebx
    jge  short loc_403589

    push  offset dword_4027BC
    push  dword ptr [ebp-28h]
    cinvoke  __vbaStrCat

    mov  edxeax
    lea  ecx, [ebp-28h]
    cinvoke  __vbaStrMove

; ---------------------------------------------------------------------------
loc_403589:        ; CODE XREF: seg000:00403570j
    push  dword ptr [ebp-2Ch]
    push  dword ptr [ebp-28h]
    cinvoke  __vbaStrCat

    mov  edxeax
    lea  ecx, [ebp-2Ch]
    cinvoke  __vbaStrMove

    push  1
    pop  eax
    add  eax, [ebp-18h]
    jo  short loc_403616

    mov  [ebp-18h], eax
    jmp  loc_4034A3

; ---------------------------------------------------------------------------

loc_4035AE:        ; CODE XREF: seg000:004034A9j
    mov  edx, [ebp-2Ch]
    lea  ecx, [ebp-20h]
    cinvoke  __vbaStrCopy

    push  offset loc_403602
    jmp  short loc_4035E9

; ---------------------------------------------------------------------------
    test  byte ptr [ebp-4], 4
    jz  short loc_4035CE

    lea  ecx, [ebp-20h]
    cinvoke  __vbaFreeStr

; ---------------------------------------------------------------------------
loc_4035CE:        ; CODE XREF: seg000:004035C4j
    lea  eax, [ebp-40h]
    push  eax
    lea  eax, [ebp-3Ch]
    push  eax
    push  2
    cinvoke  __vbaFreeStrList

    add  esp, 0Ch
    lea  ecx, [ebp-50h]
    cinvoke  __vbaFreeVar

    retn
    
; ---------------------------------------------------------------------------    
loc_403616:        ; CODE XREF: seg000:00403488j
          ; seg000:004034F2j ...
    cinvoke  __vbaErrorOverflow    
    
; ---------------------------------------------------------------------------        
loc_4035E9:        ; CODE XREF: seg000:004035BEj
    lea  ecx, [ebp-24h]
    cinvoke  __vbaFreeStr
    lea  ecx, [ebp-28h]
    cinvoke  __vbaFreeStr
    lea  ecx, [ebp-2Ch]
    cinvoke  __vbaFreeStr
    retn
    
; ---------------------------------------------------------------------------
loc_403602:        ; DATA XREF: seg000:004035B9o
    mov  ecx, [ebp-14h]
    mov  eax, [ebp-20h]
    pop  edi
    pop  esi
    mov  fs:0, ecx
    pop  ebx
    leave
    retn  8
    
; ---------------------------------------------------------------------------
dummy_seh:      xor     eaxeax
                ret

; ---------------------------------------------------------------------------
exit:                        
                ret                
                      
Generate endp             


完整的代码及相关软件看附件。下篇想写个VC类的,如果有时间。

--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年12月14日