首页
社区
课程
招聘
[原创]EditPlus V4.1 注册算法分析
发表于: 2016-11-2 04:39 12658

[原创]EditPlus V4.1 注册算法分析

2016-11-2 04:39
12658
[软件名称及版本]:EditPlus Text Editor 4.1(32位)。
[官网下载地址]:https://www.editplus.com/。
[软件介绍]:由于官网已经对该软件的功能及特点有了权威而详细的介绍,故不再描述。
[分析环境]:Windos XP SP3。
[分析工具]:OLLYDBG V1.10、IDA V6.8。
[本文目的]:本文针对目前官网最新版的 EditPlus 分析了其注册算法,也是一次学习过程,在这里将分析过程写出来,希望能与大家多多交流,互相学习。本次分析过中有些地方分析的不是很透彻,但是关键点都力所能及的分析了,如有错误和疏漏之处,还请大家多多批评指正。

注:本次分析以32位版本为对象,关于64位版本,本文结尾处有提及。在分析过程中以
  UserName:"LocalHostUser", RegCode:"63AC4-6654R-QWED5"做为参数进行分析。

***********************************************************************************************************************************

[正文]:
软件安装完毕,直接运行软件并输入注册码,报错误提示,弹出一个对话框。上OD调试启动,并在MessageBox相关函数处下断点,在MessageBoxW处断下来后,回溯即可找到关键函数。回溯到函数 sub_004D8D80 处:
.text:004D8D80 ; int __thiscall Fun1(CDialog *this)
.text:004D8D80 Fun1        proc near         ; DATA XREF: 
.rdata:00598BA4o
.text:004D8D80
.text:004D8D80 var_8   = dword ptr -8
.text:004D8D80 var_4   = dword ptr -4
.text:004D8D80
.text:004D8D80      sub      esp, 8
.text:004D8D83      push    esi
.text:004D8D84      lea       eax, [esp+0Ch+var_8]
.text:004D8D88      push    eax
.text:004D8D89      mov     esi, ecx
.text:004D8D8B      mov     [esp+10h+var_8], 0
.text:004D8D93      call       Fun_KeyFun      ; 按照 Unicode 格式的数据做校验
.text:004D8D98      test      eax, eax
.text:004D8D9A      jnz       short loc_4D8DD9
.text:004D8D9C      lea      ecx, [esp+0Ch+var_4]
.text:004D8DA0      push    ecx
.text:004D8DA1      mov     ecx, esi
.text:004D8DA3      mov     [esp+10h+var_4], eax
.text:004D8DA7      call       Fun_KeyFun1     ; 按照 ASCII 格式的数据做校验
.text:004D8DAC      mov     ecx, [esp+0Ch+var_4]
.text:004D8DB0      mov     edx, [esp+0Ch+var_8]
.text:004D8DB4      test     ecx, ecx
.text:004D8DB6      jbe      short loc_4D8DC2
.text:004D8DB8      cmp    edx, 4E7Fh
.text:004D8DBE      jz        short loc_4D8DC2
.text:004D8DC0      mov    edx, ecx
.text:004D8DC2
.text:004D8DC2 loc_4D8DC2:      ; CODE XREF: Fun1+36j
.text:004D8DC2                        ; Fun1+3Ej
.text:004D8DC2      test    eax, eax
.text:004D8DC4      jnz     short loc_4D8DD9
.text:004D8DC6      test    edx, edx
.text:004D8DC8      jz        short loc_4D8DD4
.text:004D8DCA      push   0FFFFFFFFh      ; unsigned int
.text:004D8DCC      push   10h             ; uType
.text:004D8DCE      push   edx             ; unsigned int
.text:004D8DCF      call      ?AfxMessageBox@@YGHIII@Z ; AfxMessageBox(uint,uint,uint)
.text:004D8DD4 loc_4D8DD4:                   ; CODE XREF: Fun1+48j
.text:004D8DD4      pop     esi
.text:004D8DD5      add     esp, 8
.text:004D8DD8      retn
.text:004D8DD9 ; 
---------------------------------------------------------------------------
.text:004D8DD9
.text:004D8DD9 loc_4D8DD9:      ; CODE XREF: Fun1+1Aj
.text:004D8DD9                        ; Fun1+44j
.text:004D8DD9      mov     edx, [esi+134h]
.text:004D8DDF      mov     ecx, esi        ; this
.text:004D8DE1      mov     dword ptr [edx], 0
.text:004D8DE7      pop     esi
.text:004D8DE8      add     esp, 8
.text:004D8DEB      jmp     ?OnOK@CDialog@@MAEXXZ ; CDialog::OnOK(void)
.text:004D8DEB Fun1   endp[/COLOR]

由以上代码及后续分析可得出, Fun_KeyFun 会对输入的 UserName 和 RegCode 按照 Unicode 格式的字符串进行校验,如果校验失败会调用 Fun_KeyFun1 再次进行校验,而 Fun_KeyFun1 是对输入的 UserName 和 RegCode 按照 ASCII 格式的字符串做校验,两个函数的内部校验流程是一样的,所以仅对 Fun_KeyFun 进行较为详细的分析。Fun_KeyFun 和 Fun_KeyFun1 如果有任何一个校验成功,即提示注册成功并要求重启程序,否则就会弹出对话框提示注册码错误,应此重点分析 Fun_KeyFun 校验成功的条件。

下面详细看:Fun_KeyFun
.text:004D8AF0 Fun_KeyFun      proc near               ; CODE XREF: 
Fun1+13p
.text:004D8AF0
.text:004D8AF0 var_45C = dword ptr -45Ch
.text:004D8AF0 var_458 = dword ptr -458h
.text:004D8AF0 var_454 = dword ptr -454h
.text:004D8AF0 var_450 = word ptr -450h
.text:004D8AF0 Dst       = word ptr -3ECh
.text:004D8AF0 var_4    = dword ptr -4
.text:004D8AF0 arg_0    = dword ptr  4
.text:004D8AF0
.text:004D8AF0         sub     esp, 45Ch
.text:004D8AF6         mov    eax, dword_5CCE34
.text:004D8AFB         xor     eax, esp
.text:004D8AFD         mov    [esp+45Ch+var_4], eax
.text:004D8B04         mov    eax, [esp+45Ch+arg_0]
.text:004D8B0B         push    ebx
.text:004D8B0C         push    ebp
.text:004D8B0D         mov    ebp, ecx
.text:004D8B0F         lea      ecx, [esp+464h+var_45C]
.text:004D8B13         push    ecx                ; int
.text:004D8B14         push    1F4h               ; SizeInWords
.text:004D8B19         lea     edx, [esp+46Ch+Dst]
.text:004D8B20         mov     [esp+46Ch+var_458], eax
.text:004D8B24         push    edx                ; Dst
.text:004D8B25         lea     eax, [ebp+0C8h]
.text:004D8B2B         push    eax                ; int
.text:004D8B2C         call    Fun_GetInputString ; eax = UserName(Unicode)
.text:004D8B31         add     esp, 10h
.text:004D8B34         cmp     [esp+464h+var_45C], 0
.text:004D8B39         mov     ebx, eax
.text:004D8B3B         mov     [esp+464h+var_454], ebx
.text:004D8B3F         jnz     short loc_4D8B48
.text:004D8B41         xor     eax, eax
.text:004D8B43         jmp     loc_4D8C17
.text:004D8B48 ; 
---------------------------------------------------------------------------
.text:004D8B48
.text:004D8B48 loc_4D8B48:     ; CODE XREF: Fun_KeyFun+4Fj
.text:004D8B48         push    edi
.text:004D8B49         lea     ecx, [esp+468h+var_45C]
.text:004D8B4D         push    ecx             ; int
.text:004D8B4E         push    32h             ; SizeInWords
.text:004D8B50         lea     edx, [esp+470h+var_450]
.text:004D8B54         push    edx             ; Dst
.text:004D8B55         lea     eax, [ebp+74h]
.text:004D8B58         push    eax             ; int
.text:004D8B59         call    Fun_GetInputString ; eax = RegCode(Unicode)
.text:004D8B5E         mov     edi, eax
.text:004D8B60         mov     eax, [esp+478h+var_45C]
.text:004D8B64         add     esp, 10h
.text:004D8B67         test     eax, eax
.text:004D8B69         jz       short loc_4D8BE9
.text:004D8B6B         push    esi
.text:004D8B6C         xor     esi, esi
.text:004D8B6E         test     eax, eax
.text:004D8B70         jle      short loc_4D8BB6 ; 密钥字符串(Unicode)
.text:004D8B72
.text:004D8B72 loc_4D8B72:     ; CODE XREF: Fun_KeyFun+C0j
.text:004D8B72         movzx   ebx, word ptr [edi+esi*2]
.text:004D8B76         mov     ecx, 100h
.text:004D8B7B         cmp     bx, cx
.text:004D8B7E         jnb      short loc_4D8B8D
.text:004D8B80         movzx   edx, bl
.text:004D8B83         movzx   eax, word_5F2890[edx*2]
.text:004D8B8B         jmp     short loc_4D8BA7
.text:004D8B8D ; 
---------------------------------------------------------------------------
.text:004D8B8D
.text:004D8B8D loc_4D8B8D:     ; CODE XREF: Fun_KeyFun+8Ej
.text:004D8B8D         movzx   eax, bx
.text:004D8B90         push    eax             ; lpsz
.text:004D8B91         call     ds:CharUpperW
.text:004D8B97         movzx   eax, ax
.text:004D8B9A         test     ax, ax
.text:004D8B9D         jnz     short loc_4D8BA4
.text:004D8B9F         movzx   eax, bx
.text:004D8BA2         jmp     short loc_4D8BA7
.text:004D8BA4 ; 
---------------------------------------------------------------------------
.text:004D8BA4
.text:004D8BA4 loc_4D8BA4:     ; CODE XREF: Fun_KeyFun+ADj
.text:004D8BA4         movzx   eax, ax
.text:004D8BA7
.text:004D8BA7 loc_4D8BA7:     ; CODE XREF: Fun_KeyFun+9Bj
.text:004D8BA7                ; Fun_KeyFun+B2j
.text:004D8BA7         mov    [edi+esi*2], ax
.text:004D8BAB         inc     esi
.text:004D8BAC         cmp    esi, [esp+46Ch+var_45C]
.text:004D8BB0         jl      short loc_4D8B72 ; 将密钥中的小写字母转换为大写
.text:004D8BB2         mov    ebx, [esp+46Ch+var_454]
.text:004D8BB6
.text:004D8BB6 loc_4D8BB6:     ; CODE XREF: Fun_KeyFun+80j
.text:004D8BB6         push    edi             ; 密钥字符串(Unicode)
.text:004D8BB7         push    ebx             ; 用户名字符串(Unicode)
.text:004D8BB8         call    Fun2_UserName_RegCode ; 参数(UserName, RegCode),对UserName 和 RegCode 进行校验
.text:004D8BBD         add     esp, 8
.text:004D8BC0         pop     esi
.text:004D8BC1         test     eax, eax        ;  校验成功时这里返回1
.text:004D8BC3         jnz      short loc_4D8BD1
.text:004D8BC5         mov     ecx, [esp+468h+var_458] ; 校验成功时这里不执行
.text:004D8BC9         mov     dword ptr [ecx], 5F81h
.text:004D8BCF         jmp     short loc_4D8C16 ; 校验失败时跳转
.text:004D8BD1 ; 
---------------------------------------------------------------------------
.text:004D8BD1
.text:004D8BD1 loc_4D8BD1:     ; CODE XREF: Fun_KeyFun+D3j
.text:004D8BD1         push    edi
.text:004D8BD2         push    ebx
.text:004D8BD3         call    Fun_WriteInfo2RegAndIniFile ; 校验成功之后执行这里,参数仍然为(UserName, RegCode), 向注册表和ini文件中写入一些信息
.text:004D8BD8         add     esp, 8
.text:004D8BDB         test    eax, eax
.text:004D8BDD         jnz     short loc_4D8BED
.text:004D8BDF         mov     edx, [esp+468h+var_458]
.text:004D8BE3         mov     dword ptr [edx], 4E7Fh
.text:004D8BE9
.text:004D8BE9 loc_4D8BE9:     ; CODE XREF: Fun_KeyFun+79j
.text:004D8BE9        xor     eax, eax
.text:004D8BEB        jmp     short loc_4D8C16
.text:004D8BED ; 
---------------------------------------------------------------------------
.text:004D8BED
.text:004D8BED loc_4D8BED:    ; CODE XREF: Fun_KeyFun+EDj
.text:004D8BED        mov     eax, [ebp+120h]
.text:004D8BF3        push    ebx             ; Src
.text:004D8BF4        push    1F4h            ; SizeInWords
.text:004D8BF9        push    eax             ; Dst
.text:004D8BFA        call       _wcscpy_s
.text:004D8BFF        mov      ecx, [ebp+124h]
.text:004D8C05        push    edi             ; Src
.text:004D8C06        push    32h             ; SizeInWords
.text:004D8C08        push    ecx             ; Dst
.text:004D8C09        call      _wcscpy_s
.text:004D8C0E        add     esp, 18h
.text:004D8C11        mov     eax, 1
.text:004D8C16
.text:004D8C16 loc_4D8C16:     ; CODE XREF: Fun_KeyFun+DFj
.text:004D8C16                ; Fun_KeyFun+FBj
.text:004D8C16        pop     edi
.text:004D8C17
.text:004D8C17 loc_4D8C17:     ; CODE XREF: Fun_KeyFun+53j
.text:004D8C17        mov     ecx, [esp+464h+var_4]
.text:004D8C1E        pop     ebp
.text:004D8C1F        pop     ebx
.text:004D8C20        xor      ecx, esp
.text:004D8C22        call      Fun_UnKnow  ;未总结功能
.text:004D8C27        add     esp, 45Ch
.text:004D8C2D        retn    4
.text:004D8C2D Fun_KeyFun      endp
由以上代码及后续分析可得出,Fun_KeyFun 中做了以下几件事:   
    1:获取 Unicode 格式的 UserName 和 RegCode;
    2:将 RegCode 中的小写字母转换为大写字母;
    3:将 UserName 和 RegCode 做为参数传递到函数 Fun2_UserName_RegCode 中进行校验;
    4:如果 Fun2_UserName_RegCode 的校验成功,返回值为1,然后继续调用 Fun_WriteInfo2RegAndIniFile,Fun_WriteInfo2RegAndIniFile  利用 UserName 和 RegCode 以及当前的磁盘信息生成一些加密信息,写入注册表和文件(.ini文件)。


以上流程中1和2不用详细解释,下面重点看 Fun2_UserName_RegCode 利用 UserName 和 RegCode 进行校验的过程,重点看如何才能使该函数返回1
Fun2_UserName_RegCode
.text:004D8250 Fun2_UserName_RegCode proc near    ; CODE XREF: 
sub_460B80+17Ep
.text:004D8250                                    ; Fun_KeyFun+C8p
.text:004D8250
.text:004D8250 Dst     = word ptr -0BD0h
.text:004D8250 var_BCE = word ptr -0BCEh
.text:004D8250 var_BBC = byte ptr -0BBCh
.text:004D8250 var_4   = dword ptr -4
.text:004D8250 arg_0   = dword ptr  4
.text:004D8250 arg_4   = dword ptr  8
.text:004D8250
.text:004D8250         sub     esp, 0BD0h
.text:004D8256         mov     eax, dword_5CCE34
.text:004D825B         xor     eax, esp
.text:004D825D         mov    [esp+0BD0h+var_4], eax
.text:004D8264         push    ebx
.text:004D8265         push    ebp
.text:004D8266         mov    ebp, [esp+0BD8h+arg_0] ; ebp = UserName
.text:004D826D         push   esi
.text:004D826E         mov    esi, [esp+0BDCh+arg_4] ; esi = RegCode
.text:004D8275         mov    eax, ebp
.text:004D8277         push    edi
.text:004D8278         lea     edx, [eax+2]
.text:004D827B         jmp    short loc_4D8280
.text:004D827B ; 
---------------------------------------------------------------------------
.text:004D827D                 align 10h
.text:004D8280
.text:004D8280 loc_4D8280:     ; CODE XREF: Fun2_UserName_RegCode+2Bj
.text:004D8280                       ; Fun2_UserName_RegCode+39j
.text:004D8280         mov    cx, [eax]
.text:004D8283         add    eax, 2
.text:004D8286         test    cx, cx
.text:004D8289         jnz     short loc_4D8280
.text:004D828B         sub    eax, edx
.text:004D828D         sar    eax, 1        ; eax = UserName 长度
.text:004D828F         mov   ebx, eax
.text:004D8291         mov   eax, esi
.text:004D8293         lea    edx, [eax+2]
.text:004D8296
.text:004D8296 loc_4D8296:     ; CODE XREF: Fun2_UserName_RegCode+4Fj
.text:004D8296        mov     cx, [eax]
.text:004D8299        add     eax, 2
.text:004D829C        test    cx, cx
.text:004D829F        jnz      short loc_4D8296
.text:004D82A1        sub     eax, edx
.text:004D82A3        sar      eax, 1          ; eax = RegCode 长度
.text:004D82A5        mov     edi, eax
.text:004D82A7        call      Fun_CreateGlobalTable_005F38A0_ ; 在地址0X005F38A0处生成一个0X200大小的数组
.text:004D82AC        push    0BB8h           ; Arg4 常量0x0BB8
.text:004D82B1        lea       eax, [esp+0BE4h+var_BBC]
.text:004D82B5        push    eax   ; Arg3 栈地址,用于保存格式化后数据的地址
.text:004D82B6        push    ebx   ; Arg2 Unicode 字符串长度(此处为UserName长度)
.text:004D82B7        push    ebp   ; Arg1 Unicode 字符串(此处为 UserName)
.text:004D82B8        call       Fun_FroamtString ; 对Unicode字符串进行格式化,对每个Unicode字符按照 %04X进行格式化
.text:004D82BD        push    eax             ; Arg3 格式化后字符串的长度
.text:004D82BE        lea       ecx, [esp+0BF4h+var_BBC]
.text:004D82C2        push    ecx             ; Arg2 格式化后的字符串
.text:004D82C3        push    0               ; Arg1 常量 0
.text:004D82C5        call        Fun_CalcStringyValue ; 对Unicode字符串(这里是 UserName) 进行计算,得出一个值,在计算的过程中使用了之前的全局表数据
.text:004D82CA        movzx   edx, ax         ; 计算的结果为 WORD 类型
.text:004D82CD        push    edx             ; ArgList
.text:004D82CE        lea       eax, [esp+0C00h+Dst]
.text:004D82D2        push    offset a04x     ; "%04X"
.text:004D82D7        push    eax             ; Dst
.text:004D82D8        call      Fun_FormatCalcRes ; 将计算的结果格式化为字符串
.text:004D82DD        mov     cx, [esi+4]
.text:004D82E1        lea       eax, [esi+4]
.text:004D82E4        add     esp, 28h
.text:004D82E7        cmp     cx, [esp+0BE0h+Dst] ; 格式化后的字符串的第一个字符和 RegCode的第三个字符比较
.text:004D82EC        jz      short loc_4D82F2
.text:004D82EE
.text:004D82EE loc_4D82EE:   ; CODE XREF: Fun2_UserName_RegCode+ABj
.text:004D82EE        xor     eax, eax
.text:004D82F0        jmp     short loc_4D834C
.text:004D82F2 ; 
---------------------------------------------------------------------------
.text:004D82F2
.text:004D82F2
 loc_4D82F2:; CODE XREF: Fun2_UserName_RegCode+9Cj
.text:004D82F2        mov     dx, [esi+6]
.text:004D82F6        cmp     dx, [esp+0BE0h+var_BCE] ; 格式化后的字符串的第二个字符和RegCode的四个字符比较
.text:004D82FB        jnz     short loc_4D82EE
.text:004D82FD        push    0BB8h
.text:004D8302        lea     ecx, [esp+0BE4h+var_BBC]
.text:004D8306        push    ecx
.text:004D8307        add     edi, -2         ; RegCode 长度减2
.text:004D830A        push    edi
.text:004D830B        push    eax
.text:004D830C        call    Fun_FroamtString ; 将RegCode从第三个字符开始,按照 %4X 的格式对每个字符进行格式化
.text:004D8311        push    eax
.text:004D8312        lea     edx, [esp+0BF4h+var_BBC]
.text:004D8316        push    edx
.text:004D8317        push    0
.text:004D8319        call    Fun_CalcStringyValue ; 对Unicode字符串(这里是从RegCode 的第三个字符开始)进行计算,得出一个值,在计算的过程中使用了之前的全局表的数据
.text:004D831E        movzx   eax, ax         ; 计算结果为 DWORD 类型
.text:004D8321        push    eax             ; ArgList
.text:004D8322        lea     ecx, [esp+0C00h+Dst]
.text:004D8326        push   offset a04x     ; "%04X"
.text:004D832B        push   ecx             ; Dst
.text:004D832C        call    Fun_FormatCalcRes ; 格式化计算的结果
.text:004D8331        mov     dx, [esi]       ; 取出 RegCode 第一位字符串
.text:004D8334        add     esp, 28h
.text:004D8337        xor     eax, eax
.text:004D8339       cmp     dx, [esp+0BE0h+Dst] ; 格式化字符串的第一个和 RegCode 的第一个字符比较
.text:004D833E        jnz     short loc_4D834C
.text:004D8340        mov     cx, [esi+2]     ; 取出 RegCode 第二位字符串
.text:004D8344        cmp     cx, [esp+0BE0h+var_BCE] ; 格式化字符串的第二个和 RegCode 的第二个字符比较
.text:004D8349        setz    al
.text:004D834C
.text:004D834C loc_4D834C:     ; CODE XREF: Fun2_UserName_RegCode+A0j
.text:004D834C                 ; Fun2_UserName_RegCode+EEj
.text:004D834C        mov     ecx, [esp+0BE0h+var_4]
.text:004D8353        pop     edi
.text:004D8354        pop     esi
.text:004D8355        pop     ebp
.text:004D8356        pop     ebx
.text:004D8357        xor     ecx, esp
.text:004D8359        call    Fun_UnKnow      ; 暂时没看出来该函数的功能
.text:004D835E        add     esp, 0BD0h
.text:004D8364        retn
.text:004D8364 Fun2_UserName_RegCode endp
由以上代码及后续分析可得出, 校验过程如下:
1.通过函数 Fun_CreateGlobalTable_005F38A0 在全局地址 0x005F38A0 处生成了一张大小为 0x200 且数据固定的表;
2.通过函数 Fun_FroamtString 对 UserName 进行格式化;
3.通过函数 Fun_CalcStringyValue 对格式化后的 UserName 数据进行计算,在计算的过程中使用到了之前生成的全局表数据;
4.对3中计算得出的 DWORD 数据值按照"%04X"的格式进行格式化得出一个字符串,其中字符串的前两位需要与 RegCode 的第3、4位字符相同;
5.取 RegCode 的第 3-结束 部分作为新的字符串通过 3、4 的步骤也计算出一个 DWORD 值并按照 "%04X" 的格式进行格式化得出一个字符串,并且该字符串的前两位需要与 RegCode 的前 2 位字符相等。以上执行过程中如果比较条件都能满足, 则 Fun2_UserName_RegCode 的校验就是成功的,返回1,否则返回0。

举个例子说明上述过程:采用 UserName:"LocalHostUser", RegCode:"63AC4-6654R-QWED5"时,由于
"LocalHostUser"通过 Fun_FroamtString 后得出的内存数据值如下:
0012E4E8  30 30 34 43 30 30 36 46 30 30 36 33 30 30 36 31  004C006F00630061
0012E4F8  30 30 36 43 30 30 34 38 30 30 36 46 30 30 37 33  006C0048006F0073
0012E508  30 30 37 34 30 30 35 35 30 30 37 33 30 30 36 35  0074005500730065
0012E518  30 30 37 32 00                        0072.

对上述内存数据通过 Fun_CalcStringyValue 得出的DWORD值是:0xDBAA;对该DWORD值调用 Fun_FroamtString 得到的字符串是:"DBAA", 所以 RegCode 的第3、4位字符需要是'D'和'B', 因此手动修改 RegCode 为"63DB4-6654R-QWED5"。

取 RegCode 的第 3-结尾 字符作为一个新的字符串 "DB4-6654R-QWED5",调用 Fun_FroamtString 得出的内存数据如下:
0012E4E8  30 30 34 34 30 30 34 32 30 30 33 34 30 30 32 44  004400420034002D
0012E4F8  30 30 33 36 30 30 33 36 30 30 33 35 30 30 33 34  0036003600350034
0012E508  30 30 35 32 30 30 32 44 30 30 35 31 30 30 35 37  0052002D00510057
0012E518  30 30 34 35 30 30 34 34 30 30 33 35 00        004500440035.

对上述内存数据通过 Fun_CalcStringyValue 得出的 DWORD值是:0x8122;对该DWORD值调用 Fun_FroamtString 得到的字符串是:"8122", RegCode的第1、2位需要是'8'、'1',因此手动修改 RegCode 为"81DB4-6654R-QWED5", 至此验证函数 Fun2_UserName_RegCode 即可返回1。

下面关键看验证过程中的几个算法和关键函数:
算法1: Fun_CreateGlobalTable_005F38A0 在全局地址 0x005F38A0 处生成了一张大小为 0x200 且数据固定的表:
.text:004D7560 Fun_CreateGlobalTable_005F38A0_ proc near
.text:004D7560        ; CODE XREF: Fun_GetVolumeSerialAndCalc+D6p
.text:004D7560        ; Fun2_UserName_RegCode+57p ...
.text:004D7560        push    200h            ; size_t
.text:004D7565        push    0               ; int
.text:004D7567        push    offset word_5F38A0 ; void *
.text:004D756C        call    _memset
.text:004D7571        add     esp, 0Ch
.text:004D7574        xor     edx, edx
.text:004D7576        jmp     short loc_4D7580
.text:004D7576 ; 
---------------------------------------------------------------------------
.text:004D7578                 align 10h
.text:004D7580
.text:004D7580 loc_4D7580:    ; CODE XREF: Fun_CreateGlobalTable_005F38A0_+16j
.text:004D7580                ; Fun_CreateGlobalTable_005F38A0_+57j
.text:004D7580        mov     eax, 0C0C1h
.text:004D7585        mov     ecx, 1
.text:004D758A        lea     ebx, [ebx+0]
.text:004D7590
.text:004D7590 loc_4D7590:    ; CODE XREF: Fun_CreateGlobalTable_005F38A0_+4Ej
.text:004D7590        test    edx, ecx
.text:004D7592        jz      short loc_4D759C
.text:004D7594        xor     word_5F38A0[edx*2], ax
.text:004D759C
.text:004D759C loc_4D759C:    ; CODE XREF: Fun_CreateGlobalTable_005F38A0_+32j
.text:004D759C        add     eax, eax
.text:004D759E        xor     eax, 4003h
.text:004D75A3        add     ecx, ecx
.text:004D75A5        cmp     ecx, 100h
.text:004D75AB        movzx   eax, ax
.text:004D75AE        jl      short loc_4D7590
.text:004D75B0        inc     edx
.text:004D75B1        cmp     edx, 100h
.text:004D75B7        jl      short loc_4D7580
.text:004D75B9        retn
.text:004D75B9 Fun_CreateGlobalTable_005F38A0_ endp

将上述代码直接还原为 C 代码如下:
/********************************************
函数功能:
         在固定地址处 g_dwAddr(0x005F38A0) 处
         依据函数内的算法生成数据, 生成的数据
         是固定的, 大小为 g_dwSize(0X200)
**********************************************/
void Fun_CreateGlobalTable_005F38A0()
{
    //空间置0
    memset((void*)g_dwAddr, 0, g_dwSize);
    
    DWORD dwEDX = 0;
    DWORD dwEAX;
    DWORD dwECX;
    DWORD dwEBX;
    
    do
    {
        dwEAX = 0xC0C1;
        dwECX = 0x01;
        dwEBX = 0x0A;
        
        do
        {
            if(0 != (dwEDX&dwECX))
            {
                *(WORD*)((char*)g_dwAddr + dwEDX*2) ^= (WORD)(dwEAX&0x0000FFFF);
            }
            
            dwEAX *= 2;
            dwEAX ^= 0x4003;
            dwECX *= 2;
            dwEAX &= 0x0000FFFF;
        }while(dwECX < 0x100);
        
        dwEDX++;
    }while(dwEDX < 0x100);    
}

字符串格式化函数 Fun_FroamtString:
.text:004D7600 Fun_FroamtString proc near ; CODE XREF: 
Fun2_UserName_RegCode+68p
.text:004D7600                            ; Fun2_UserName_RegCode+BCp
.text:004D7600
.text:004D7600 arg_0 = dword ptr  4
.text:004D7600 arg_4 = dword ptr  8
.text:004D7600 arg_8 = dword ptr  0Ch
.text:004D7600 arg_C = dword ptr  10h
.text:004D7600
.text:004D7600       push    ebp
.text:004D7601       mov     ebp, [esp+4+arg_4]
.text:004D7605       push    esi
.text:004D7606       push    edi
.text:004D7607       xor     esi, esi
.text:004D7609       xor     edi, edi
.text:004D760B       test    ebp, ebp
.text:004D760D       jle     short loc_4D7648
.text:004D760F       push    ebx
.text:004D7610       mov     ebx, [esp+10h+arg_8]
.text:004D7614
.text:004D7614 loc_4D7614: ; CODE XREF: Fun_FroamtString+3Bj
.text:004D7614       mov     eax, [esp+10h+arg_0]
.text:004D7618       movzx   ecx, word ptr [eax+edi*2]
.text:004D761C       mov     edx, [esp+10h+arg_C]
.text:004D7620       push    ecx
.text:004D7621       push    offset a04x_0   ; "%04X"
.text:004D7626       sub     edx, esi
.text:004D7628       push    edx             ; SizeInBytes
.text:004D7629       lea     eax, [esi+ebx]
.text:004D762C       push    eax             ; DstBuf
.text:004D762D       call    _sprintf_s
.text:004D7632       inc     edi
.text:004D7633       add     esp, 10h
.text:004D7636       add     esi, 4
.text:004D7639       cmp     edi, ebp
.text:004D763B       jl      short loc_4D7614
.text:004D763D       mov     byte ptr [esi+ebx], 0
.text:004D7641       pop     ebx
.text:004D7642       pop     edi
.text:004D7643       mov     eax, esi
.text:004D7645       pop     esi
.text:004D7646       pop     ebp
.text:004D7647       retn
.text:004D7648 ; 
---------------------------------------------------------------------------
.text:004D7648
.text:004D7648
 loc_4D7648:     ; CODE XREF: Fun_FroamtString+Dj
.text:004D7648       mov     ecx, [esp+0Ch+arg_8]
.text:004D764C       pop     edi
.text:004D764D       mov     byte ptr [esi+ecx], 0
.text:004D7651       mov     eax, esi
.text:004D7653       pop     esi
.text:004D7654       pop     ebp
.text:004D7655       retn
.text:004D7655 Fun_FroamtString endp


将上述代码直接还原为 C 代码如下:
/****************************************************************************

参数意义:
            Arg1: IN  g_wChar Unicode 字符串首地址            
            Arg2: IN  g_dwAddrFormatDataBuff 接收格式化数据的地址
            Arg3: IN  dwLength 接收格式化数据的缓冲区长度
            Arg4: IN  dwConstValue 常量 0xBB8

函数功能:   格式化 Unicode字符串,格式化后的数据存放到 g_dwAddrFormatDataBuff

函数返回值: 返回格式化后缓冲区的字节数
****************************************************************************/
DWORD Fun_FroamtString(IN WCHAR *g_wChar, IN DWORD g_dwAddrFormatDataBuff, IN DWORD dwLength, IN DWORD dwConstValue)
{
  DWORD dwRet = 0;
  WORD  wValue;
  for(int iIndex = 0; iIndex < dwLength; iIndex++)
  {
    wValue = *(WORD*)((DWORD)g_wChar + iIndex*2);  //取出每个Unicode字符的值
    ::sprintf((char*)(g_dwAddrFormatDataBuff + dwRet), "%04X", wValue);
    dwRet += 4;
  }

  *(char*)(g_dwAddrFormatDataBuff + dwRet) = 0;

  return dwRet;
}


算法2:Fun_CalcStringyValue,利用格式化后的内存字符串数据和之前生成的全局表数据来计算出一个DWORD值:
.text:004D75C0 Fun_CalcStringyValue proc near ; CODE XREF: Fun_GetVolumeSerialAndCalc+E4p
.text:004D75C0                                ; Fun2_UserName_RegCode+75p ...
.text:004D75C0
.text:004D75C0 arg_0 = dword ptr  4
.text:004D75C0 arg_4 = dword ptr  8
.text:004D75C0 arg_8 = dword ptr  0Ch
.text:004D75C0
.text:004D75C0       mov     ecx, [esp+arg_4]
.text:004D75C4       mov     eax, [esp+arg_8]
.text:004D75C8       lea     edx, [ecx+eax]
.text:004D75CB       cmp     ecx, edx
.text:004D75CD       jnb     short loc_4D75F4
.text:004D75CF       mov     eax, [esp+arg_0]
.text:004D75D3       push    esi
.text:004D75D4       push    edi
.text:004D75D5
.text:004D75D5 loc_4D75D5:     ; CODE XREF: Fun_CalcStringyValue+2Fj
.text:004D75D5       movzx   edi, byte ptr [ecx]
.text:004D75D8       movzx   esi, al
.text:004D75DB       shr     ax, 8
.text:004D75DF       xor     esi, edi
.text:004D75E1       xor     ax, word_5F38A0[esi*2]
.text:004D75E9       inc     ecx
.text:004D75EA       movzx   eax, ax
.text:004D75ED       cmp     ecx, edx
.text:004D75EF       jb      short loc_4D75D5
.text:004D75F1       pop     edi
.text:004D75F2       pop     esi
.text:004D75F3       retn
.text:004D75F4 ; 
---------------------------------------------------------------------------
.text:004D75F4
.text:004D75F4 loc_4D75F4:     ; CODE XREF: Fun_CalcStringyValue+Dj
.text:004D75F4       mov     ax, word ptr [esp+arg_0]
.text:004D75F9       retn
.text:004D75F9 Fun_CalcStringyValue endp


将上述代码直接还原为 C 代码如下:
/***************************************************************************************

参数意义:
            Arg1: IN  dwArg1 传入的值为 0, 内部并没有使用
            Arg2: IN  传入的是对输入的数据进行格式化之后的内存数据首地址
            Arg3: IN  传入的是 Arg2 指向的 ASCII 字符串的长度

函数功能:   对 Arg2 处的数据进行计算, 计算的过程中使用到了之前生成的 0x005F38A0 处的数据

函数返回值: 返回计算的结果 DWORD值
***************************************************************************************/
DWORD Fun_004D75C0(IN DWORD dwArg1, IN char *lpszAddr, IN DWORD dwLength)
{
  DWORD dwStartAddr = (DWORD)lpszAddr;
  DWORD dwESI = 0; 
  DWORD dwEDI = 0;
  DWORD dwEAX = 0;
  WORD  wAX   = 0;
  WORD  wValue = 0;

  //比较字符串首位地址高低
  if((dwStartAddr + dwLength) <= (DWORD)lpszAddr)
  {
    return dwArg1;
  }

  for(int iIndex = 0; iIndex < dwLength; iIndex++)
  {
    dwEDI = *(lpszAddr + iIndex);
    dwESI = dwEAX&0x000000FF;
    wAX   = dwEAX&0x0000FFFF;
    wAX   = wAX >> 8;
    dwESI = dwESI^dwEDI;

    wValue = *(WORD*)(g_dwAddr + dwESI*2);
    wAX  = wAX^wValue;
    dwEAX = wAX;
  }

  return dwEAX; 
}


至此 Fun2_UserName_RegCode 函数中的关键算法和函数都已还原完。现在函数流程回到 Fun_KeyFun 中,如果校验成功,则会调用 Fun_WriteInfo2RegAndIniFile 利用能够校验成功的 UserName 和 RegCode 以及硬盘信息生成一些加密后的数据写到注册表以及程序安装目录下的 "reg_u.ini" 文件中。由于在整个注册算法分析的过程中该部分并不是很关键的部分,并且这部分内容实在过多,所以如果有兴趣的朋友可以参见idb文件中的注释内容,这里就不再详细描述。

在这个地方如果你在OD里面直接F9运行,会弹出 UserName 和 RegCode 注册成功的对话框,但是如果你以为到此整个注册过程就分析完了,那后面就会把自己坑死,因为还有一个巨坑。
注意看 Fun_KeyFun 的 Fun_WriteInfo2RegAndIniFile 之后, 还有两个 _wcscpy_s 函数,而拷贝的内容一个是 UserName 一个是 RegCode, 如果注册算法已经还原完毕,那这里还要拷贝 UserName 和 RegCode 做什么呢?所以坑就在后面。

在OD中注意观察拷贝过程


拷贝 UserName


拷贝 RegCode


所以在内存00CBA120和00CBA508处都下上Byte类型的硬件访问断点,然后直接F9运行,看看后续哪里还在使用 UserName 和RegCode。通过硬件断点可以发现后续第一次使用 UserName 的地方位于 sub_004D7FE0 函数内部,代码如下:
Fun_ReChecRegCodekBit5:
.text:004D7FE0 Fun_ReChecRegCodekBit5 proc near  ; CODE XREF: 
sub_4D8850+3p
.text:004D7FE0
.text:004D7FE0 var_2C = dword ptr -2Ch
.text:004D7FE0 var_28 = dword ptr -28h
.text:004D7FE0 Dst    = word ptr -24h
.text:004D7FE0 var_10 = dword ptr -10h
.text:004D7FE0 var_C  = dword ptr -0Ch
.text:004D7FE0 var_4  = dword ptr -4
.text:004D7FE0
.text:004D7FE0        push    0FFFFFFFFh
.text:004D7FE2        push    offset loc_577E73
.text:004D7FE7        mov     eax, large fs:0
.text:004D7FED        push    eax
.text:004D7FEE        sub     esp, 20h
.text:004D7FF1        mov     eax, dword_5CCE34
.text:004D7FF6        xor     eax, esp
.text:004D7FF8        mov     [esp+2Ch+var_10], eax
.text:004D7FFC        push    ebx
.text:004D7FFD        push    ebp
.text:004D7FFE        push    esi
.text:004D7FFF        push    edi
.text:004D8000        mov     eax, dword_5CCE34
.text:004D8005        xor     eax, esp
.text:004D8007        push    eax
.text:004D8008        lea     eax, [esp+40h+var_C]
.text:004D800C        mov     large fs:0, eax
.text:004D8012        mov     ebp, ecx
.text:004D8014        mov     [esp+40h+var_28], ebp
.text:004D8018        mov     dword ptr [ebp+0], offset off_598A4C
.text:004D801F        mov     edx, [ebp+120h] ; edx = UserName(Unicode)首地址
.text:004D8025        mov     eax, edx        ; eax = edx = UserName(Unicode)首地址
.text:004D8027        mov     [esp+40h+var_4], 1 ; Var4 = 1
.text:004D802F        lea     esi, [eax+2]    ; esi 指向 UserName 第二个字符
.text:004D8032
.text:004D8032 loc_4D8032:    ; CODE XREF: Fun_ReChecRegCodekBit5+5Bj
.text:004D8032        mov     cx, [eax]       ; 取出 UserName 中的当前字符
.text:004D8035        add     eax, 2
.text:004D8038        test    cx, cx
.text:004D803B        jnz     short loc_4D8032 ; 计算UserName的长度
.text:004D803D        sub     eax, esi
.text:004D803F        sar     eax, 1
.text:004D8041        mov     ebx, eax        ; eax = ebx = UserName 长度
.text:004D8043        mov     [esp+14h], ebx  ; 保存UserName长度
.text:004D8047        test    ebx, ebx
.text:004D8049        jle     loc_4D8104      ; UserName 长度 <= 0 跳转
.text:004D804F        xor     esi, esi        ; esi = 0
.text:004D8051        xor     edi, edi        ; edi = 0
.text:004D8053        xor     eax, eax        ; eax = 0
.text:004D8055        cmp     ebx, 2
.text:004D8058        lea     ecx, [esi+1]    ; ecx = 1
.text:004D805B        jl      short loc_4D807C ; UserName 长度 < 2 时跳转
.text:004D805D        dec     ebx             ; ebx = UserName 长度 - 1
.text:004D805E        mov     edi, edi
.text:004D8060
.text:004D8060 loc_4D8060:    ; CODE XREF: Fun_ReChecRegCodekBit5+92j
.text:004D8060        movzx   ebp, word ptr [edx+eax*2] ; ebp = UserName 中的第奇数个字符
.text:004D8064        add     esi, ebp        ; esi = UserName 中的第奇数个字符的累加和(不包含最后一个字符(如果最后一个字符是第奇数个字符))
.text:004D8066        movzx   ebp, word ptr [edx+eax*2+2] ; ebp = UserName 中的第偶数个字符
.text:004D806B        add     eax, 2
.text:004D806E        add     edi, ebp        ; edi = UserName 中的第偶数个字符的累加和
.text:004D8070        cmp     eax, ebx
.text:004D8072        jl      short loc_4D8060 ; ebp = UserName 中的第奇数个字符
.text:004D8074        mov     ebx, [esp+40h+var_2C] ; ebx = UserName 长度
.text:004D8078        mov     ebp, [esp+40h+var_28]
.text:004D807C
.text:004D807C loc_4D807C:    ; CODE XREF: Fun_ReChecRegCodekBit5+7Bj
.text:004D807C       cmp     eax, ebx
.text:004D807E       jge     short loc_4D8085 ; esi = UserName 处最后一个第奇数个字符外其他字符的累加和
.text:004D8080       movzx   ecx, word ptr [edx+eax*2] ; ecx = UserName 最后一个字符
.text:004D8084       inc     ecx             ; 最后一个字符累加 1
.text:004D8085
.text:004D8085 loc_4D8085:     ; CODE XREF: Fun_ReChecRegCodekBit5+9Ej
.text:004D8085       add     esi, edi        ; esi = UserName 处最后一个第奇数个字符外其他字符的累加和
.text:004D8087       add     ecx, esi        ; ecx = UserName 所有字母累加和 + 1
.text:004D8089       lea     ecx, [ecx+ecx*8+0Ah] ; ecx = ecx*9 + 0A
.text:004D808D       mov     eax, 55555556h
.text:004D8092       imul    ecx
.text:004D8094       mov     eax, edx
.text:004D8096       shr     eax, 1Fh
.text:004D8099       lea     ecx, [edx+eax+24h] ; ecx = ecx/3 + 0x24
.text:004D809D       and     ecx, 8000000Fh  ; ecx = ecx&0x8000000F
.text:004D80A3       jns     short loc_4D80AA ; ecx为正数时跳转
.text:004D80A5       dec     ecx
.text:004D80A6       or      ecx, 0FFFFFFF0h
.text:004D80A9       inc     ecx             ; ecx 为负数时 ecx =((ecx - 1)|0XFFFFFFF0) + 1
.text:004D80AA
.text:004D80AA loc_4D80AA:     ; CODE XREF: Fun_ReChecRegCodekBit5+C3j
.text:004D80AA       push    ecx             ; ArgList
.text:004D80AB       lea     edx, [esp+44h+Dst]
.text:004D80AF       push    offset a1x      ; "%1X"
.text:004D80B4       push    edx             ; Dst
.text:004D80B5       call    Fun_FormatCalcRes ; 按照"%1x"的格式格式化计算结果
.text:004D80BA       mov     edx, [ebp+124h] ; edx 指向 RegCode
.text:004D80C0       mov     eax, edx        ; eax = edx
.text:004D80C2       add     esp, 0Ch
.text:004D80C5       lea     esi, [eax+2]    ; esi 指向 RegCode 的第二个字符
.text:004D80C8       jmp     short loc_4D80D0
.text:004D80C8 ; 
---------------------------------------------------------------------------
.text:004D80CA       align 10h
.text:004D80D0
.text:004D80D0 loc_4D80D0:    ; CODE XREF: Fun_ReChecRegCodekBit5+E8j
.text:004D80D0                ; Fun_ReChecRegCodekBit5+F9j
.text:004D80D0       mov     cx, [eax]
.text:004D80D3       add     eax, 2
.text:004D80D6       test    cx, cx
.text:004D80D9       jnz     short loc_4D80D0
.text:004D80DB       sub     eax, esi
.text:004D80DD       sar     eax, 1          ; eax 得出的是 RegCode 长度
.text:004D80DF       cmp     eax, 5          ; RegCode 长度小于 5 时跳转
.text:004D80E2       jb      short loc_4D80F3
.text:004D80E4       mov     ax, [edx+8]     ; ax = RegCode 的第五个字符
.text:004D80E8       cmp     ax, [esp+40h+Dst] ; 比较 RegCode 的第五个字符和之前格式化得出的一个字符
.text:004D80ED       jz      loc_4D81CF      ; 相等时跳转
.text:004D80F3
.text:004D80F3 loc_4D80F3:   ; CODE XREF: Fun_ReChecRegCodekBit5+102j
.text:004D80F3       mov     ecx, [ebp+134h]
.text:004D80F9       mov     dword ptr [ecx], 1 ; 标志位置1, 表示 RegCode 长度 < 5 或者 RegCode 的第五位没有校验成功
.text:004D80FF       jmp     loc_4D81CF
.text:004D8104 ; 
---------------------------------------------------------------------------
.text:004D8104
.text:004D8104
 loc_4D8104:   ; CODE XREF: Fun_ReChecRegCodekBit5+69j
.text:004D8104       mov     edi, [ebp+128h]
.text:004D810A       mov     eax, edi
.text:004D810C       lea     edx, [eax+1]
.text:004D810F       nop
.text:004D8110
.text:004D8110 loc_4D8110:    ; CODE XREF: Fun_ReChecRegCodekBit5+135j
.text:004D8110       mov     cl, [eax]
.text:004D8112       inc     eax
.text:004D8113       test    cl, cl
.text:004D8115       jnz     short loc_4D8110
.text:004D8117       sub     eax, edx
.text:004D8119       mov     ebx, eax
.text:004D811B       mov     [esp+40h+var_2C], ebx
.text:004D811F       test    ebx, ebx
.text:004D8121       jle     loc_4D81CF
.text:004D8127       xor     edx, edx
.text:004D8129       xor     esi, esi
.text:004D812B       xor     eax, eax
.text:004D812D       cmp     ebx, 2
.text:004D8130       lea     ecx, [edx+1]
.text:004D8133       jl      short loc_4D815C
.text:004D8135       dec     ebx
.text:004D8136       jmp     short loc_4D8140
.text:004D8136 ; 
---------------------------------------------------------------------------
.text:004D8138       align 10h
.text:004D8140
.text:004D8140 loc_4D8140:   ; CODE XREF: Fun_ReChecRegCodekBit5+156j
.text:004D8140                ; Fun_ReChecRegCodekBit5+172j
.text:004D8140      movzx   ebp, byte ptr [edi+eax]
.text:004D8144      add     edx, ebp
.text:004D8146      movzx   ebp, byte ptr [edi+eax+1]
.text:004D814B      add     eax, 2
.text:004D814E      add     esi, ebp
.text:004D8150      cmp     eax, ebx
.text:004D8152      jl      short loc_4D8140
.text:004D8154      mov     ebp, [esp+40h+var_28]
.text:004D8158      mov     ebx, [esp+40h+var_2C]
.text:004D815C
.text:004D815C loc_4D815C:     ; CODE XREF: Fun_ReChecRegCodekBit5+153j
.text:004D815C     cmp     eax, ebx
.text:004D815E     jge     short loc_4D8165
.text:004D8160     movzx   ecx, byte ptr [eax+edi]
.text:004D8164     inc     ecx
.text:004D8165
.text:004D8165 loc_4D8165:    ; CODE XREF: Fun_ReChecRegCodekBit5+17Ej
.text:004D8165     add     esi, edx
.text:004D8167     add     ecx, esi
.text:004D8169     lea     ecx, [ecx+ecx*8+0Ah]
.text:004D816D     mov     eax, 55555556h
.text:004D8172     imul    ecx
.text:004D8174     mov     eax, edx
.text:004D8176     shr     eax, 1Fh
.text:004D8179     lea     ecx, [edx+eax+24h]
.text:004D817D     and     ecx, 8000000Fh
.text:004D8183     jns     short loc_4D818A
.text:004D8185     dec     ecx
.text:004D8186     or      ecx, 0FFFFFFF0h
.text:004D8189     inc     ecx
.text:004D818A
.text:004D818A loc_4D818A:     ; CODE XREF: Fun_ReChecRegCodekBit5+1A3j
.text:004D818A     push    ecx             ; ArgList
.text:004D818B     lea     edx, [esp+44h+Dst]
.text:004D818F     push    offset off_589878 ; Format
.text:004D8194     push    edx             ; DstBuf
.text:004D8195     call    sub_4D7840
.text:004D819A     mov     ecx, [ebp+12Ch]
.text:004D81A0     mov     eax, ecx
.text:004D81A2     add     esp, 0Ch
.text:004D81A5     lea     esi, [eax+1]
.text:004D81A8
.text:004D81A8 loc_4D81A8:    ; CODE XREF: Fun_ReChecRegCodekBit5+1CDj
.text:004D81A8     mov     dl, [eax]
.text:004D81AA     inc     eax
.text:004D81AB     test    dl, dl
.text:004D81AD     jnz     short loc_4D81A8
.text:004D81AF     sub     eax, esi
.text:004D81B1     cmp     eax, 5
.text:004D81B4     jb      short loc_4D81C3
.text:004D81B6     movzx   eax, byte ptr [ecx+4]
.text:004D81BA     movsx   ecx, byte ptr [esp+40h+Dst]
.text:004D81BF     cmp     eax, ecx
.text:004D81C1     jz      short loc_4D81CF
.text:004D81C3
.text:004D81C3 loc_4D81C3:     ; CODE XREF: Fun_ReChecRegCodekBit5+1D4j
.text:004D81C3     mov     edx, [ebp+134h]
.text:004D81C9     mov     dword ptr [edx], 1
.text:004D81CF
.text:004D81CF loc_4D81CF:     ; CODE XREF: Fun_ReChecRegCodekBit5+10Dj
.text:004D81CF                 ; Fun_ReChecRegCodekBit5+11Fj ...
.text:004D81CF     lea     ecx, [ebp+0C8h]
.text:004D81D5     call    sub_518CCA
.text:004D81DA     lea     ecx, [ebp+74h]
.text:004D81DD     mov     byte ptr [esp+40h+var_4], 0
.text:004D81E2     call    sub_518CCA
.text:004D81E7     mov     ecx, ebp
.text:004D81E9     mov     [esp+40h+var_4], 0FFFFFFFFh
.text:004D81F1     mov     dword ptr [ebp+0], offset off_580E24
.text:004D81F8     call    sub_5108ED
.text:004D81FD     mov     ecx, [esp+40h+var_C]
.text:004D8201     mov     large fs:0, ecx
.text:004D8208     pop     ecx
.text:004D8209     pop     edi
.text:004D820A     pop     esi
.text:004D820B     pop     ebp
.text:004D820C     pop     ebx
.text:004D820D     mov     ecx, [esp+2Ch+var_10]
.text:004D8211     xor     ecx, esp
.text:004D8213     call    Fun_UnKnow
.text:004D8218     add     esp, 2Ch
.text:004D821B     retn
.text:004D821B Fun_ReChecRegCodekBit5 endp  


由以上代码及后续的分析可得出:
Fun_ReChecRegCodekBit5 函数内部对UserName长度和RegCode的长度都做了检查,如果 UserName长度 < 2 或 RegCode的长度 < 5 均直接校验失败,在校验过程中使用了一段算法对 UserName 重新计算得出一个DWORD数据,然后对这个DWORD数据按照"%1X"进行格式化得到一个字符,如果该字符和 RegCode 的第5位字符不匹配,会执行
.text:004D80F3 loc_4D80F3:   ; CODE XREF: Fun_ReChecRegCodekBit5+102j
.text:004D80F3       mov     ecx, [ebp+134h]
.text:004D80F9       mov     dword ptr [ecx], 1 ; 标志位置1, 表示 RegCode 长度 < 5 或者 RegCode 的第五位没有校验成功
.text:004D80FF       jmp     loc_4D81CF

这3句。通过后面的分析得知,其实这个函数的功能就在于执行不执行这3句上,因为 004D80F9 实际上是一个置标志位为1的动作,该位为1表示RegCode的第5位校验失败,该位为0表示RegCode的第5位校验成功。

下面把利用 UserName 计算 RegCode 第5位的算法还原为 C 代码,如下:
/*****************************************************************
参数意义:
            无。

函数功能:   对 UserName 再次进行计算,得出的结果格式化为 1 个字符。

函数返回值: 返回格式化得出的字符。
******************************************************************/
char Fun_GetBit5ByUserName(const char *lpUserName)
{
  char szBuff[4];
  int iRes = 0;
  for(int iIndex = 0; iIndex < strlen(lpUserName); iIndex++)
  {
    iRes += *(lpUserName + iIndex);
  }

  iRes++;
  iRes = iRes*9 + 0xA;
  iRes = iRes/3 + 0x24;
  iRes &= 0X8000000F;

  if(iRes < 0)
  {
    iRes = ((iRes - 1) | 0XFFFFFFF0) + 1;
  }  
  ::wsprintf(szBuff, "%1X", iRes);
  return szBuff[0];
}

利用该算法对 UserName:"LocalHostUser"得出的字符是'2'。所以 RegCode 的第5位需要是 '2'。该算法可称为算法3。
上面的内容分析完毕之后直接F9,会发现弹出一个对话提示重新启动程序,关闭弹出的对话框,并且在关闭程序主窗口界面之后,会有断点断到对 RegCode 的使用上,可以发现断点位于函数 sub_00469210 内:
.text:00469210 ; int __thiscall Fun_ModifyRegCodeByFlag(CFrameWnd *this)
.text:00469210 Fun_ModifyRegCodeByFlag proc near  ; CODE XREF: 
sub_46A9E0+3p
.text:00469210
.text:00469210 var_10 = dword ptr -10h
.text:00469210 var_C  = dword ptr -0Ch
.text:00469210 var_4  = dword ptr -4
.text:00469210
.text:00469210        push    0FFFFFFFFh
.text:00469212        push    offset loc_573A82
.text:00469217        mov     eax, large fs:0
.text:0046921D        push    eax
.text:0046921E        push    ecx
.text:0046921F        push    esi
.text:00469220        mov     eax, dword_5CCE34
.text:00469225        xor     eax, esp
.text:00469227        push    eax
.text:00469228        lea     eax, [esp+18h+var_C]
.text:0046922C        mov     large fs:0, eax
.text:00469232        mov     esi, ecx
.text:00469234        mov     [esp+18h+var_10], esi
.text:00469238        mov     dword ptr [esi], offset off_58F584
.text:0046923E        cmp     dword ptr [esi+1EECh], 0 ; 比较标志位,如果该位为 0 表示之前的 RegCode 的第 5 位验证成功,否者表示之前的验证失败
.text:00469245        mov     [esp+18h+var_4], 0Bh
.text:0046924D        jz      short loc_4692A1
.text:0046924F        cmp     byte ptr [esi+1EA8h], 30h
.text:00469256        lea     eax, [esi+1EA8h]
.text:0046925C        setz    cl
.text:0046925F        push    eax             ; void *
.text:00469260        lea     edx, [esi+1CB4h]
.text:00469266        add     cl, 30h
.text:00469269        push    edx             ; int
.text:0046926A        mov     [eax], cl
.text:0046926C        call    sub_4D7970
.text:00469271        lea     eax, [esi+1C50h]
.text:00469277        mov     ecx, 30h
.text:0046927C        add     esp, 8
.text:0046927F        cmp     cx, [eax]       ; 判断 RegCode 的第一个字符是否为 '0'
.text:00469282        jnz     short loc_46928E ; RegCode 的第一个字符不为 '0'时跳转
.text:00469284        mov     edx, 31h
.text:00469289        mov     [eax], dx
.text:0046928C        jmp     short loc_469291
.text:0046928E ; 
---------------------------------------------------------------------------
.text:0046928E
.text:0046928E
 loc_46928E:     ; CODE XREF: Fun_ModifyRegCodeByFlag+72j
.text:0046928E       mov     [eax], cx       ; RegCode 的第一个字符置 '0'
.text:00469291
.text:00469291 loc_469291:     ; CODE XREF: Fun_ModifyRegCodeByFlag+7Cj
.text:00469291      push    eax
.text:00469292      lea     edx, [esi+1868h]
.text:00469298      push    edx
.text:00469299      call    Fun_WriteInfo2RegAndIniFile ; 修改 RegCode 之后重新加密,生成新的信息,写入注册表和文件
.text:0046929E      add     esp, 8
.text:004692A1
.text:004692A1 loc_4692A1:     ; CODE XREF: Fun_ModifyRegCodeByFlag+3Dj
.text:004692A1      mov     eax, [esi+1F44h]
.text:004692A7      mov     byte ptr [esp+18h+var_4], 0Ah
.text:004692AC      test    eax, eax
.text:004692AE      jz      short loc_4692B8
.text:004692B0      mov     ecx, [eax]
.text:004692B2      mov     edx, [ecx+8]
.text:004692B5      push    eax
.text:004692B6      call    edx
.text:004692B8
.text:004692B8 loc_4692B8:     ; CODE XREF: Fun_ModifyRegCodeByFlag+9Ej
.text:004692B8      mov     eax, [esi+1864h]
.text:004692BE      sub     eax, 10h
.text:004692C1      mov     byte ptr [esp+18h+var_4], 9
.text:004692C6      lea     ecx, [eax+0Ch]
.text:004692C9      or      edx, 0FFFFFFFFh
.text:004692CC      lock xadd [ecx], edx
.text:004692D0      dec     edx
.text:004692D1      test    edx, edx
.text:004692D3      jg      short loc_4692DF
.text:004692D5      mov     ecx, [eax]
.text:004692D7      mov     edx, [ecx]
.text:004692D9      push    eax
.text:004692DA      mov     eax, [edx+4]
.text:004692DD      call    eax
.text:004692DF
.text:004692DF loc_4692DF:    ; CODE XREF: Fun_ModifyRegCodeByFlag+C3j
.text:004692DF      lea     ecx, [esi+1794h]
.text:004692E5      mov     byte ptr [esp+18h+var_4], 8
.text:004692EA      call    sub_40EE30
.text:004692EF      mov     eax, [esi+176Ch]
.text:004692F5      mov     dword ptr [esi+1768h], offset off_58E7EC
.text:004692FF      test    eax, eax
.text:00469301      jz      short loc_46930C
.text:00469303      push    eax             ; void *
.text:00469304      call    ??3@YAXPAX@Z    ; operator delete(void *)
.text:00469309      add     esp, 4
.text:0046930C
.text:0046930C loc_46930C:     ; CODE XREF: Fun_ModifyRegCodeByFlag+F1j
.text:0046930C      lea     ecx, [esi+15DCh] ; this
.text:00469312      mov     byte ptr [esp+18h+var_4], 6
.text:00469317      call    sub_469180
.text:0046931C      lea     ecx, [esi+0C2Ch]
.text:00469322      mov     byte ptr [esp+18h+var_4], 5
.text:00469327      call    sub_4EADC0
.text:0046932C      lea     ecx, [esi+5FCh]
.text:00469332      mov     byte ptr [esp+18h+var_4], 4
.text:00469337      call    sub_4C0ED0
.text:0046933C      lea     ecx, [esi+480h] ; this
.text:00469342      mov     byte ptr [esp+18h+var_4], 3
.text:00469347      call    sub_4690D0
.text:0046934C      lea     ecx, [esi+304h] ; this
.text:00469352      mov     byte ptr [esp+18h+var_4], 2
.text:00469357      call    sub_4690D0
.text:0046935C      lea     ecx, [esi+188h] ; this
.text:00469362      mov     byte ptr [esp+18h+var_4], 1
.text:00469367      call    sub_4690D0
.text:0046936C      lea     ecx, [esi+0ECh] ; this
.text:00469372      mov     byte ptr [esp+18h+var_4], 0
.text:00469377      call    ??1CStatusBar@@UAE@XZ ; CStatusBar::~CStatusBar(void)
.text:0046937C      mov     ecx, esi        ; this
.text:0046937E      mov     [esp+18h+var_4], 0FFFFFFFFh
.text:00469386      call    ??1CFrameWnd@@UAE@XZ ; CFrameWnd::~CFrameWnd(void)
.text:0046938B      mov     ecx, [esp+18h+var_C]
.text:0046938F      mov     large fs:0, ecx
.text:00469396      pop     ecx
.text:00469397      pop     esi
.text:00469398      add     esp, 10h
.text:0046939B      retn
.text:0046939B Fun_ModifyRegCodeByFlag endp


通过对上述代码的分析可以发现,ModifyRegCodeByFlag 函数会根据由之前对 RegCode 第5位的校验结果而置的标志位的情况来选择操作,如果之前校验成功,这里就什么都不做,如果校验失败,该函数会修改 RegCode的第一个字符为'0', 并且依据UserName 和修改后的 RegCode 再次调用 Fun_WriteInfo2RegAndIniFile 函数来生成相关信息并写入注册表和文件。

到此,整个注册过程才算分析完毕。
下面总结一下整体的注册流程:
1.利用算法1在全局数据区生成一个固定大小为 0X200,且数据固定的表;
2.利用算法2对 UserName 计算出结果,利用计算结果对 RegCode 的第3、4位进行校验;
3.利用算法2对 RegCode 的3-结束 字符串计算出结果,利用计算结果对 RegCode 的第1、2位进行验证;
4.利用算法3对 UserName 计算出一个结果,利用该结果对 RegCode 的第5位的校验情况置一个标志位;
5.在关闭弹出的重启验证对话框,并且关闭程序主界面之后,程序利用步骤4中所置的标志位来决定是否修改 RegCode 并重新写注册信息到注册表和文件(修改后的 UserName 和 RegCode 肯定是过不了3步的验证的)。


依据上述的注册过程,经过再次的调试和修改,可以得出一组可用的UserName和RegCode如下:
UserName:"LocalHostUser"  
RegCode:"6FDB2-6654R-QWED5"。

由于在整个分析过程中并没有注意到 RegCode 的严格的格式要求,所以这里就随便采用了一种格式。

关于该软件的ASCII字符串验证过程:由于该软件默认的先直接采用Unicode字符串进行校验,所以分析过程以Unicode格式的字符串进行,对于ASCII格式的校验过程在IDA中看了一下流程和调用的函数,发现和Unicode格式校验过程调用的一样,所以就没有详细分析。

关于该软件的64位版本:利用上面的UserName和RegCode注册 Win7 SP1 下的64位4.1版本的软件时能够直接成功,同时用
IDA查看了64位程序的情况,发现注册流程和算法与32位版本相似,所以这里也不再分析。

最后注册机我就不放出来了(其实如果你仔细看完此文你已经可以自己写了),上面给出了一组 UserName 和 RegCode 大家可以试一下。

[课程]Linux pwn 探索篇!

上传的附件:
收藏
免费 2
支持
分享
最新回复 (20)
雪    币: 99
活跃值: (110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
mark.!!!!
2016-11-2 06:59
0
雪    币: 114
活跃值: (180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
厉害,谢谢分享
2016-11-2 09:13
0
雪    币: 21
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
mark,分析过程很详细,如果把注册机附上就更完美了!
2016-11-2 09:16
0
雪    币: 5511
活跃值: (2072)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
很久没看到这么详细的教程了,谢谢分享!
2016-11-2 12:28
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
不错,这么详实细致的内容,值得好好学习一下。
不知道楼主能不能看下我发的求助帖,帮我分析一下呢?
2016-11-4 06:27
0
雪    币: 131
活跃值: (98)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
算法好文越来越少了………………
支持楼主!~~~~
2016-11-4 13:18
0
雪    币: 144
活跃值: (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
2016-11-5 09:40
0
雪    币: 267
活跃值: (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
楼主好耐心,是一篇不错的学习案例。崇拜楼主
2016-11-5 11:23
0
雪    币: 470
活跃值: (662)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
谢谢震得不错
2016-11-5 18:13
0
雪    币: 26
活跃值: (79)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
11
Mark一下,好帖子越来越少,不得不顶
2016-11-6 13:45
0
雪    币: 8548
活跃值: (2797)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
在学习中进步
2016-11-6 16:41
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
学习学习一下咯
2016-11-6 17:38
0
雪    币: 367
活跃值: (178)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
哈哈, 咱们一起,好好学习。
2016-11-8 16:44
0
雪    币: 367
活跃值: (178)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
谢谢,相互交流,共同进步。
2016-11-8 16:46
0
雪    币: 92
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
过来支持一下虎哥!新版本验证算法基本没什么修改。
2016-11-12 11:24
0
雪    币: 289
活跃值: (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
好帖,顶,学习学习。
2016-11-12 16:31
0
雪    币: 367
活跃值: (178)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
18
看来你以前也分析过,哈哈。
2016-11-14 13:29
0
雪    币: 94
活跃值: (29)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
学习学习,带个注册机就完美了
2016-11-14 13:36
0
雪    币: 23
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
楼主写的不错。make下
2016-11-15 14:59
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
进来学习一下。
2016-12-6 20:47
0
游客
登录 | 注册 方可回帖
返回
//