onmiuncai 发表于 2016-7-23 17:26

XXXX DVD Ripper 算法分析及成品注册机

本帖最后由 姐又寡闻了 于 2019-6-6 22:38 编辑

写一篇自己分析算法的一个过程,不对欢迎指正。


今天的程序是 Ahead DVD Ripper 年代好像有点久远,然而就从简单的算法来分析走起



PEID查壳一个VC++ 6.0,直接拖入OD 随意输入用户名与注册码 点击OK


是个MFC,到MFC42下按钮事件,来到关键算法CALL

00414156   .C74424 14 000>mov dword ptr ss:,0x0
0041415E   .E8 47D70500   call <jmp.&MFC42.#6334>
00414163   .E8 6AD60500   call <jmp.&MFC42.#1168>
00414168   .8B48 04       mov ecx,dword ptr ds:         ;Ahead_DV.00413FB0
0041416B   .E8 0ADA0500   call <jmp.&MFC42.#1669>
00414170   .8B46 64       mov eax,dword ptr ds:
00414173   .8B4E 60       mov ecx,dword ptr ds:
00414176   .50            push eax                                 ;EAX 序列号
00414177   .51            push ecx                                 ;ECX 用户名
00414178   .C64424 18 01mov byte ptr ss:,0x1
0041417D   .E8 EEFCFFFF   call Ahead_DV.00413E70                   ;关键CALL
00414182   .83C4 08       add esp,0x8
00414185   .85C0          test eax,eax                           ;Ahead_DV.00478518
00414187   .75 15         jnz short Ahead_DV.0041419E            ;关键跳。
00414189   .6A 40         push 0x40
0041418B   .68 2C2D4A00   push Ahead_DV.004A2D2C                   ;ASCII "Sorry"
00414190   .68 002D4A00   push Ahead_DV.004A2D00                   ;ASCII "Invalid username or registration code      "
00414195   .8BCE          mov ecx,esi                              ;mfc42.#4234
00414197   .E8 9CD60500   call <jmp.&MFC42.#4224>
0041419C   .EB 54         jmp short Ahead_DV.004141F2
0041419E   >8B46 60       mov eax,dword ptr ds:
004141A1   .8D4C24 04   lea ecx,dword ptr ss:
004141A5   .50            push eax                                 ;Ahead_DV.00478518
004141A6   .68 E42C4A00   push Ahead_DV.004A2CE4                   ;ASCII "License To:%s             "
004141AB   .51            push ecx
004141AC   .E8 57D60500   call <jmp.&MFC42.#2818>
004141B1   .8B5424 10   mov edx,dword ptr ss:          ;mfc42.73D322FD
004141B5   .83C4 0C       add esp,0xC
004141B8   .8BCE          mov ecx,esi                              ;mfc42.#4234
004141BA   .6A 40         push 0x40
004141BC   .68 D82C4A00   push Ahead_DV.004A2CD8                   ;ASCII "Thank you"
004141C1   .52            push edx


F7跟进关键CALL来到了这里:

00413E70/$53            push ebx
00413E71|.55            push ebp
00413E72|.8B6C24 0C   mov ebp,dword ptr ss:         ;用户名
00413E76|.56            push esi                                 ;mfc42.#4234
00413E77|.57            push edi
00413E78|.BE 2C2F4C00   mov esi,Ahead_DV.004C2F2C
00413E7D|.8BC5          mov eax,ebp
00413E7F|>8A10          /mov dl,byte ptr ds:                ;取用户名单字符
00413E81|.8A1E          |mov bl,byte ptr ds:
00413E83|.8ACA          |mov cl,dl                               ;单字符给了CL
00413E85|.3AD3          |cmp dl,bl
00413E87|.75 1E         |jnz short Ahead_DV.00413EA7             ;不为空则跳出
00413E89|.84C9          |test cl,cl
00413E8B|.74 16         |je short Ahead_DV.00413EA3
00413E8D|.8A50 01       |mov dl,byte ptr ds:
00413E90|.8A5E 01       |mov bl,byte ptr ds:
00413E93|.8ACA          |mov cl,dl
00413E95|.3AD3          |cmp dl,bl
00413E97|.75 0E         |jnz short Ahead_DV.00413EA7
00413E99|.83C0 02       |add eax,0x2
00413E9C|.83C6 02       |add esi,0x2
00413E9F|.84C9          |test cl,cl
00413EA1|.^ 75 DC         \jnz short Ahead_DV.00413E7F
00413EA3|>33C0          xor eax,eax                              ;Ahead_DV.00478518
00413EA5|.EB 05         jmp short Ahead_DV.00413EAC
00413EA7|>1BC0          sbb eax,eax                              ;Ahead_DV.00478518
00413EA9|.83D8 FF       sbb eax,-0x1
00413EAC|>85C0          test eax,eax                           ;Ahead_DV.00478518
00413EAE|.74 51         je short Ahead_DV.00413F01


最后的00413EAE 跳到了下面这段代码, 从未进关键CALL之前,可以看到下面EAX为关键标志,如果EAX为0则注册失败,通过对上面汇编代码分析得出,这代码主要判断用户名是否为空

00413F01|> \5F            pop edi                                  ;这里EAX为0,注册失败
00413F02|.5E            pop esi                                  ;mfc42.73D323EB
00413F03|.5D            pop ebp                                  ;mfc42.73D323EB
00413F04|.33C0          xor eax,eax                              ;Ahead_DV.00478518
00413F06|.5B            pop ebx                                  ;mfc42.73D323EB
00413F07\.C3            retn


以此类推,下面是注册码是否为空的判断代码:

00413EB0|.8B7C24 18   mov edi,dword ptr ss:          ;取序列号
00413EB4|.BE 2C2F4C00   mov esi,Ahead_DV.004C2F2C
00413EB9|.8BC7          mov eax,edi
00413EBB|>8A10          /mov dl,byte ptr ds:                ;与上面循环一样
00413EBD|.8A1E          |mov bl,byte ptr ds:
00413EBF|.8ACA          |mov cl,dl
00413EC1|.3AD3          |cmp dl,bl
00413EC3|.75 1E         |jnz short Ahead_DV.00413EE3             ;不为空则跳出
00413EC5|.84C9          |test cl,cl
00413EC7|.74 16         |je short Ahead_DV.00413EDF
00413EC9|.8A50 01       |mov dl,byte ptr ds:
00413ECC|.8A5E 01       |mov bl,byte ptr ds:
00413ECF|.8ACA          |mov cl,dl
00413ED1|.3AD3          |cmp dl,bl
00413ED3|.75 0E         |jnz short Ahead_DV.00413EE3
00413ED5|.83C0 02       |add eax,0x2
00413ED8|.83C6 02       |add esi,0x2
00413EDB|.84C9          |test cl,cl
00413EDD|.^ 75 DC         \jnz short Ahead_DV.00413EBB
00413EDF|>33C0          xor eax,eax                              ;Ahead_DV.00478518
00413EE1|.EB 05         jmp short Ahead_DV.00413EE8
00413EE3|>1BC0          sbb eax,eax                              ;Ahead_DV.00478518
00413EE5|.83D8 FF       sbb eax,-0x1
00413EE8|>85C0          test eax,eax                           ;Ahead_DV.00478518
00413EEA|.74 15         je short Ahead_DV.00413F01




注意这里,用户名和注册码都不为空时,这里又进入下一层CALL,此CALL也必须跟进,因为关键算法就在里面了。


00413C40/$6A FF         push -0x1
00413C42|.68 203B4700   push Ahead_DV.00473B20                   ;SE 处理程序安装
00413C47|.64:A1 0000000>mov eax,dword ptr fs:
00413C4D|.50            push eax                                 ;Ahead_DV.00478518
00413C4E|.64:8925 00000>mov dword ptr fs:,esp
00413C55|.83EC 14       sub esp,0x14
00413C58|.8B4424 24   mov eax,dword ptr ss:          ;用户名
00413C5C|.53            push ebx
00413C5D|.55            push ebp
00413C5E|.56            push esi                                 ;mfc42.#4234
00413C5F|.57            push edi
00413C60|.50            push eax                                 ;Ahead_DV.00478518
00413C61|.8D4C24 18   lea ecx,dword ptr ss:
00413C65|.E8 2EDC0500   call <jmp.&MFC42.#537>                   ;EAX存着用户名指针
00413C6A|.8D4C24 14   lea ecx,dword ptr ss:
00413C6E|.C74424 2C 000>mov dword ptr ss:,0x0
00413C76|.E8 F3DE0500   call <jmp.&MFC42.#6282>
00413C7B|.8D4C24 14   lea ecx,dword ptr ss:
00413C7F|.E8 E4DE0500   call <jmp.&MFC42.#6283>
00413C84|.6A 20         push 0x20
00413C86|.8D4C24 18   lea ecx,dword ptr ss:
00413C8A|.E8 B5DB0500   call <jmp.&MFC42.#2915>
00413C8F|.8B4C24 38   mov ecx,dword ptr ss:
00413C93|.8BD8          mov ebx,eax                              ;Ahead_DV.00478518
00413C95|.51            push ecx
00413C96|.8D4C24 14   lea ecx,dword ptr ss:
00413C9A|.E8 F9DB0500   call <jmp.&MFC42.#537>
00413C9F|.8D4C24 10   lea ecx,dword ptr ss:          ;EAX存着序列号指针
00413CA3|.C64424 2C 01mov byte ptr ss:,0x1
00413CA8|.E8 C1DE0500   call <jmp.&MFC42.#6282>
00413CAD|.8D4C24 10   lea ecx,dword ptr ss:
00413CB1|.E8 B2DE0500   call <jmp.&MFC42.#6283>
00413CB6|.6A 20         push 0x20
00413CB8|.8D4C24 14   lea ecx,dword ptr ss:
00413CBC|.E8 83DB0500   call <jmp.&MFC42.#2915>
00413CC1|.8BD0          mov edx,eax                              ;EDX序列号
00413CC3|.83CE FF       or esi,-0x1
00413CC6|.8BFA          mov edi,edx
00413CC8|.8BCE          mov ecx,esi                              ;mfc42.#4234
00413CCA|.33C0          xor eax,eax                              ;Ahead_DV.00478518
00413CCC|.895424 20   mov dword ptr ss:,edx
00413CD0|.F2:AE         repne scas byte ptr es:
00413CD2|.F7D1          not ecx
00413CD4|.49            dec ecx
00413CD5|.8BFB          mov edi,ebx
00413CD7|.8BE9          mov ebp,ecx
00413CD9|.8BCE          mov ecx,esi                              ;mfc42.#4234
00413CDB|.F2:AE         repne scas byte ptr es:
00413CDD|.F7D1          not ecx
00413CDF|.49            dec ecx
00413CE0|.3BCD          cmp ecx,ebp                              ;比较用户名与序列号的长度
00413CE2|.0F87 54010000 ja Ahead_DV.00413E3C                     ;跳到注册失败


进入这个CALL后,通过上面的分析,可以看到,取了用户名与注册码的长度进行比较,若用户名长度大于注册码的长度则跳到00413E3C



如果跳到 00413E3C 这个位置明显是注册失败,从上图可以看到EAX又被赋值为0,而且可以看到这里有3个跳转来自.所以1处就是刚分析得到的,用户长度不能大于注册码长度,否则注册失败。


接着继续分析下面代码,这里可以看到,用户名的长度与注册码的长度分别做了比较,如果为空则跳到 00413E3C,那么刚才的2处错误跳转就来自于这2个比较。


00413CE8|.8BFB          mov edi,ebx
00413CEA|.8BCE          mov ecx,esi                              ;mfc42.#4234
00413CEC|.F2:AE         repne scas byte ptr es:
00413CEE|.F7D1          not ecx
00413CF0|.49            dec ecx
00413CF1|.0F84 45010000 je Ahead_DV.00413E3C                     ;用户名是否为空,跳到注册失败
00413CF7|.8BFA          mov edi,edx
00413CF9|.8BCE          mov ecx,esi                              ;mfc42.#4234
00413CFB|.F2:AE         repne scas byte ptr es:
00413CFD|.F7D1          not ecx
00413CFF|.49            dec ecx
00413D00|.0F84 36010000 je Ahead_DV.00413E3C                     ;序列号是否为空,跳到注册失败


接着就是关键的一些算法,我们一点一点来看。


00413D06|.894424 38   mov dword ptr ss:,eax          ;Ahead_DV.00478518
00413D0A|>8B5424 38   /mov edx,dword ptr ss:
00413D0E|.8D4C24 34   |lea ecx,dword ptr ss:         ;ECX存放用户名指针
00413D12|.8A82 782C4A00 |mov al,byte ptr ds:       ;EDX计数,常量ZQY
00413D18|.884424 18   |mov byte ptr ss:,al
00413D1C|.E8 93DA0500   |call <jmp.&MFC42.#540>
00413D21|.8BFB          |mov edi,ebx
00413D23|.83C9 FF       |or ecx,-0x1
00413D26|.33C0          |xor eax,eax                           ;Ahead_DV.00478518
00413D28|.33ED          |xor ebp,ebp
00413D2A|.F2:AE         |repne scas byte ptr es:
00413D2C|.F7D1          |not ecx
00413D2E|.49            |dec ecx
00413D2F|.C64424 2C 02|mov byte ptr ss:,0x2
00413D34|.74 4B         |je short Ahead_DV.00413D81            ;用户名长度为8,没有跳,暂时不管
00413D36|>8A042B      |/mov al,byte ptr ds:         ;取用户名1字节
00413D39|.33F6          ||xor esi,esi                            ;mfc42.#4234
00413D3B|>3A0475 102C4A>||/cmp al,byte ptr ds:   ;查表,比较
00413D42|.74 08         |||je short Ahead_DV.00413D4C
00413D44|.46            |||inc esi                               ;mfc42.#4234
00413D45|.83FE 34       |||cmp esi,0x34                        ;比较大小字母 总的52位
00413D48|.^ 7C F1         ||\jl short Ahead_DV.00413D3B
00413D4A|.EB 11         ||jmp short Ahead_DV.00413D5D
00413D4C|>8A0C75 112C4A>||mov cl,byte ptr ds:    ;对应的码表赋值cl
00413D53|.51            ||push ecx
00413D54|.8D4C24 38   ||lea ecx,dword ptr ss:
00413D58|.E8 05DE0500   ||call <jmp.&MFC42.#940>               ;EAX存放着算出来的字符串指针
00413D5D|>83FE 34       ||cmp esi,0x34
00413D60|.75 0E         ||jnz short Ahead_DV.00413D70
00413D62|.8B5424 18   ||mov edx,dword ptr ss:
00413D66|.8D4C24 34   ||lea ecx,dword ptr ss:
00413D6A|.52            ||push edx
00413D6B|.E8 F2DD0500   ||call <jmp.&MFC42.#940>
00413D70|>8BFB          ||mov edi,ebx
00413D72|.83C9 FF       ||or ecx,-0x1
00413D75|.33C0          ||xor eax,eax                            ;Ahead_DV.00478518
00413D77|.45            ||inc ebp
00413D78|.F2:AE         ||repne scas byte ptr es:
00413D7A|.F7D1          ||not ecx
00413D7C|.49            ||dec ecx
00413D7D|.3BE9          ||cmp ebp,ecx                            ;ebp为计数
00413D7F|.^ 72 B5         |\jb short Ahead_DV.00413D36
00413D81|>8B4424 34   |mov eax,dword ptr ss:         ;用户名计算得出的字符串
00413D85|.8B48 F8       |mov ecx,dword ptr ds:          ;Ahead_DV.00411CF0
00413D88|.83F9 10       |cmp ecx,0x10                            ;比较是否大于等于 10h
00413D8B|.7D 3A         |jge short Ahead_DV.00413DC7
00413D8D|.8BC1          |mov eax,ecx
00413D8F|.B9 10000000   |mov ecx,0x10
00413D94|.2BC8          |sub ecx,eax                           ;Ahead_DV.00478518
00413D96|.8D5424 1C   |lea edx,dword ptr ss:
00413D9A|.51            |push ecx
00413D9B|.52            |push edx
00413D9C|.B9 1C044E00   |mov ecx,Ahead_DV.004E041C               ;UNICODE "匀?"
00413DA1|.E8 B6DD0500   |call <jmp.&MFC42.#4129>               ;截取10h-用户名长度字符串 "HLWsCbMErpAQOXWK"
00413DA6|.50            |push eax                              ;Ahead_DV.00478518
00413DA7|.8D4C24 38   |lea ecx,dword ptr ss:
00413DAB|.C64424 30 03|mov byte ptr ss:,0x3
00413DB0|.E8 CBDA0500   |call <jmp.&MFC42.#939>
00413DB5|.8D4C24 1C   |lea ecx,dword ptr ss:         ;计算的字符串+截取字符
00413DB9|.C64424 2C 02|mov byte ptr ss:,0x2          ;ASCII "ZZCKAZBZHLWsCbME"
00413DBE|.E8 EFD80500   |call <jmp.&MFC42.#800>
00413DC3|.8B4424 34   |mov eax,dword ptr ss:
00413DC7|>8B4C24 20   |mov ecx,dword ptr ss:         ;mfc42.73D491F4
00413DCB|.51            |push ecx                              ; /s2 = "匞"
00413DCC|.50            |push eax                              ; |s1 = "?G"
00413DCD|.FF15 6C574700 |call dword ptr ds:[<&MSVCRT._mbscmp>]   ; \_mbscmp
00413DD3|.83C4 08       |add esp,0x8                           ;真假比较
00413DD6|.8D4C24 34   |lea ecx,dword ptr ss:
00413DDA|.85C0          |test eax,eax                            ;Ahead_DV.00478518
00413DDC|.C64424 2C 01|mov byte ptr ss:,0x1
00413DE1|.74 1B         |je short Ahead_DV.00413DFE
00413DE3|.33F6          |xor esi,esi                           ;mfc42.#4234
00413DE5|.E8 C8D80500   |call <jmp.&MFC42.#800>
00413DEA|.8B4424 38   |mov eax,dword ptr ss:
00413DEE|.40            |inc eax                                 ;Ahead_DV.00478518
00413DEF|.83F8 03       |cmp eax,0x3
00413DF2|.894424 38   |mov dword ptr ss:,eax         ;Ahead_DV.00478518
00413DF6|.^ 0F8C 0EFFFFFF \jl Ahead_DV.00413D0A
00413DFC|.EB 0A         jmp short Ahead_DV.00413E08
00413DFE|>BE 01000000   mov esi,0x1                              ;标志位赋值
00413E03|.E8 AAD80500   call <jmp.&MFC42.#800>
00413E08|>8D4C24 10   lea ecx,dword ptr ss:
00413E0C|.C64424 2C 00mov byte ptr ss:,0x0
00413E11|.E8 9CD80500   call <jmp.&MFC42.#800>
00413E16|.8D4C24 14   lea ecx,dword ptr ss:
00413E1A|.C74424 2C FFF>mov dword ptr ss:,-0x1
00413E22|.E8 8BD80500   call <jmp.&MFC42.#800>
00413E27|.8BC6          mov eax,esi                              ;mfc42.#4234
00413E29|.5F            pop edi                                  ;mfc42.73D323EB
00413E2A|.5E            pop esi                                  ;mfc42.73D323EB
00413E2B|.5D            pop ebp                                  ;mfc42.73D323EB
00413E2C|.5B            pop ebx                                  ;mfc42.73D323EB
00413E2D|.8B4C24 14   mov ecx,dword ptr ss:
00413E31|.64:890D 00000>mov dword ptr fs:,ecx
00413E38|.83C4 20       add esp,0x20
00413E3B|.C3            retn


这里有个可疑的常量,我们先记录一下,一会还要用到这个



这里AL为存放用户名的逐个取字符,EBP为计数,与esi*2+0x4A2C10 内存地址进行比较,我们数据跟随,可以看到一张表,34h == 52,这里进行循环比较,从这个内存地址我们可以看出,他就是取这张表来进行比较,总共比的是a-z与A-Z刚好是52个大小写字母




比较完若找到字符,则把esi*2+0x4A2C11 赋值给CL,没有找到就跳过赋值,通过跟踪分析得出,这里如果用户名的字符是字母则把表里的紧跟的字符赋值给了cl,如果不是字母,则利用之前的那个常量,还记得吗,ZQY的一个字符Z来代替,然后依次循环N*NAME长度,这样就获取到了由用户名通过码表计算出来的一串字符串。

00413D81|> \8B4424 34   |mov eax,dword ptr ss:         ;用户名计算得出的字符串
00413D85|.8B48 F8       |mov ecx,dword ptr ds:
00413D88|.83F9 10       |cmp ecx,0x10                            ;比较是否大于等于 10h
00413D8B|.7D 3A         |jge short Ahead_DV.00413DC7
00413D8D|.8BC1          |mov eax,ecx
00413D8F|.B9 10000000   |mov ecx,0x10
00413D94|.2BC8          |sub ecx,eax
00413D96|.8D5424 1C   |lea edx,dword ptr ss:
00413D9A|.51            |push ecx
00413D9B|.52            |push edx
00413D9C|.B9 1C044E00   |mov ecx,Ahead_DV.004E041C               ;UNICODE "匀?"
00413DA1|.E8 B6DD0500   |call <jmp.&MFC42.#4129>               ;截取10h-用户名长度字符串 "HLWsCbMErpAQOXWK"
00413DA6|.50            |push eax
00413DA7|.8D4C24 38   |lea ecx,dword ptr ss:
00413DAB|.C64424 30 03|mov byte ptr ss:,0x3
00413DB0|.E8 CBDA0500   |call <jmp.&MFC42.#939>
00413DB5|.8D4C24 1C   |lea ecx,dword ptr ss:         ;计算的字符串+截取字符


取得计算出来的字符串长度判断是否够16位,若不够,则补到16位,而补的字符串取值来自0x4E041C ASCII ”HLWsCbMErpAQOXWK“,最后组合成新的注册码与我们之前的假码进行比较。




这里看到,当比较结果给了EAX,如果EAX为0则ESI就为1,最后通过ESI给EAX后,注册即可成功。所以大概的流程就是这样,现在我们来大概梳理一下,还有那个常量的问题,再次来分析一下。




从这边看到这里大循环3次,而且和这个常量有关,我们再走一遍分析一下,得出这一次计算出来的结果则是如果名字不是字母则用Q来代替,之前是用Z来代替,那么我们就可以总结出,如果NAME含有不是字母的其他字符,那么他就有3个KEY。


到这里已经很清晰了,通过上面的分析我们就可以来自己写KEYGEN了,首先名字不能为空,其次名字长度不能大于注册码的长度,然后通过码表依次计算得出新的字符串,不到16位则用第2个表的字符串进行补位,最后生成的就是正确的KEY。




用易语言写的KEYGEN,-.-# 凑合看看就好啦~ 附上成品注册机。



onmiuncai 发表于 2016-7-23 20:15

ZMZwise 发表于 2016-7-23 17:49
虽然看不懂,但是还是支持原创作品

可以尝试看看,写得都挺通俗易懂的。哪里不明白也欢迎讨论~

onmiuncai 发表于 2016-7-24 19:41

yiboy 发表于 2016-7-24 11:55
支持楼主,鼓励原创,膜拜大神

跟大家一起起步学习,有什么问题欢迎一起讨论。

二十七秒 发表于 2016-7-23 17:35

唔EAX为0注册失败怎么看出来的

onmiuncai 发表于 2016-7-23 17:41

二十七秒 发表于 2016-7-23 17:35
唔EAX为0注册失败怎么看出来的


ZMZwise 发表于 2016-7-23 17:49

虽然看不懂,但是还是支持原创作品

道不同 发表于 2016-7-23 21:01

谢谢分享!!!虽然我不懂

hongge 发表于 2016-7-24 11:49

我是小白,看不懂{:1_909:}

yiboy 发表于 2016-7-24 11:55

支持楼主,鼓励原创,膜拜大神

Hmily 发表于 2016-7-25 18:23

很详细,加精鼓励,期待更多分享!
页: [1] 2 3 4 5 6
查看完整版本: XXXX DVD Ripper 算法分析及成品注册机