ImTOO DVD Audio Ripper 4.0.50.0522

旧版本已经有人分析过了,新版的算法差不多,只是分析的更细,供新手学习MD5。

Microsoft Visual C++ 7.0 Method2 [Debug]

1.断点:
bp MessageBoxExA :
00123F9C   77E180BC    /CALL 到 MessageBoxExA 来自 USER32.77E180B7
00123FA0   00100122    |hOwner = 00100122 ('注册',class='#32770',parent=000302DE)
00123FA4   01069858    |Text = "注册信息不正确!"
00123FA8   0106FE20    |Title = "DVD Audio Ripper"
00123FAC   00000030    |Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
00123FB0   00000000    \LanguageID = 0 (LANG_NEUTRAL)

返回到UILib71.dll的领空:
1001B773      E8 4C820000       call <jmp.&MFC71.#1123>
1001B778      8D4C24 04         lea ecx,dword ptr ss:[esp+4]             ; 返回处

往上找到关键:
1001B738      56                push esi
1001B739      8BF1              mov esi,ecx
1001B73B      E8 D0F4FFFF       call UILib71.ImRegDlg::SaveRegInfo
1001B740      E8 BBF8FFFF       call UILib71.ImRegDlg::IsValidRegInfo
1001B745      85C0              test eax,eax
1001B747      75 49             jnz short UILib71.1001B792

2.其中call UILib71.ImRegDlg::SaveRegInfo,将注册信息写入到注册表:
先加密:1001AD2E      E8 2D7A0000    call UILib71.10022760    
1002279E      885424 18       mov byte ptr ss:[esp+18],dl
100227A2      FF50 10         call dword ptr ds:[eax+10]         ; 加密注册码
是这样加密的:先将用户名的ASCII码作为初始值进行繁琐的计算,得到值为al,然后与注册码异或:
1002274B      32D0           xor dl,al   
根据注册码长度多少位进行循环,如果用户名为cyto,al依次为:
17,61,C9,CF,34,8D,0D,21,D4,16,8B,0D,22,96,09,7A,DB,3D,27,19,79,C0,14,3C,2B,10,C0,35,CC,08,43,02,12,1A,E8,3A,16,0A,0A...

然后转换:1001AD4F      E8 DCF8FFFF    call UILib71.ImRegDlg::String2HexA

再然后保存:
1001AD9F      8B35 18700210     mov esi,dword ptr ds:[<&ADVAPI32.RegSetValueExA>]    
HKCU\Software\ImTOO\DVD Audio Ripper 4\RegInfo\Name  SUCCESS  "cyto"  
HKCU\Software\ImTOO\DVD Audio Ripper 4\RegInfo\Serial  SUCCESS  ""  
HKCU\Software\ImTOO\DVD Audio Ripper 4\RegInfo\Code  SUCCESS  "2F 56 FF FA 34 BE 3F 10 E5 24 B8 39 17 A0 3E 42 "  

3.其中call UILib71.ImRegDlg::IsValidRegInfo是判断注册码的地方:
从注册表取值,转换:
1001B15C      E8 CFF3FFFF    call UILib71.ImRegDlg::Hex2StringA

解密注册码:1001B198      E8 C3750000    call UILib71.10022760
1002279E      885424 18       mov byte ptr ss:[esp+18],dl
100227A2      FF50 10         call dword ptr ds:[eax+10]         ; 解密注册码
解密刚好与加密一样,al值没变。

取前20位:
1001B1DA      6A 14             push 14
1001B1DC      8D4424 0C         lea eax,dword ptr ss:[esp+C]
1001B1E0      50                push eax
1001B1E1      8D4C24 1C         lea ecx,dword ptr ss:[esp+1C]
1001B1E5      FF15 8C770210     call dword ptr ds:[<&MFC71.#3997>]          ; MFC71.7C188E36
00124054   01A6BA38    ASCII "87654321123456788765"

对注册码的要求:
1001B234      8D4C24 14         lea ecx,dword ptr ss:[esp+14]
1001B238      FF15 C8770210     call dword ptr ds:[<&MFC71.#2902>]         ; MFC71.7C146AB0
1001B23E      83F8 27           cmp eax,27
1001B241      0F85 AE030000     jnz UILib71.1001B5F5
注册码要为39位。

取内置的字符串:
1001B257      E8 04A5FEFF    call UILib71.ImAppPref::GetAppInfo         ; ?
1001B25C      83C0 38        add eax,38
1001B25F      50             push eax
1001B260      8D4C24 14      lea ecx,dword ptr ss:[esp+14]
1001B264      FF15 70770210  call dword ptr ds:[<&MFC71.#781>]          ; ?
堆栈
0012405C   01065F00    ASCII "ImTOOdvdaudioripper4"

根据要求取单数:
1001B290      8BCE             mov ecx,esi
1001B292      81E1 01000080    and ecx,80000001                       ; 取值的条件
1001B298      79 05            jns short UILib71.1001B29F
1001B29A      49               dec ecx
1001B29B      83C9 FE          or ecx,FFFFFFFE
1001B29E      41               inc ecx
1001B29F      75 38            jnz short UILib71.1001B2D9
1001B2A1      56               push esi                               ; 循环指针
1001B2A2      8D4C24 14        lea ecx,dword ptr ss:[esp+14]          ; 内置字符串?
1001B2A6      FF15 B8770210    call dword ptr ds:[<&MFC71.#865>]      ; 取值
1001B2AC      8D4C24 0C        lea ecx,dword ptr ss:[esp+C]
1001B2B0      50               push eax
1001B2B1      FF15 6C750210    call dword ptr ds:[<&MFC71.#908>]      ; 保存取值
1001B2B7      8D46 01          lea eax,dword ptr ds:[esi+1]           ; 指针+1
1001B2BA      99               cdq
1001B2BB      B9 FF000000      mov ecx,0FF
1001B2C0      F7F9             idiv ecx
1001B2C2      84D2             test dl,dl
1001B2C4      885424 08        mov byte ptr ss:[esp+8],dl
1001B2C8      74 0F            je short UILib71.1001B2D9
1001B2CA      8B5424 08        mov edx,dword ptr ss:[esp+8]
1001B2CE      52               push edx
1001B2CF      8D4C24 10        lea ecx,dword ptr ss:[esp+10]
1001B2D3      FF15 6C750210    call dword ptr ds:[<&MFC71.#908>]      ; 保存指针+1
1001B2D9      8D4C24 10        lea ecx,dword ptr ss:[esp+10]
1001B2DD      46               inc esi
1001B2DE      FF15 C8770210    call dword ptr ds:[<&MFC71.#2902>]     ; MFC71.7C146AB0
1001B2E4      3BF0             cmp esi,eax
1001B2E6    ^ 7C A8            jl short UILib71.1001B290

参数:ImTOOdvdaudioripper4
根据循环指针and 80000001的值决定取值,也就是取单数并指针保存,最后值保存在edx:
01A6C888  49 01 54 03 4F 05 76 07  ITOv
01A6C890  61 09 64 0B 6F 0D 69 0F  a.do.i
01A6C898  70 11 72 13 00           pr.

再同样的取双数并指针保存到后面:
01A6C888  49 01 54 03 4F 05 76 07  ITOv
01A6C890  61 09 64 0B 6F 0D 69 0F  a.do.i
01A6C898  70 11 72 13 6D 02 4F 04  prmO
01A6C8A0  64 06 64 08 75 0A 69 0C  ddu.i.
01A6C8A8  72 0E 70 10 65 12 34 14  rpe4

然后在最前面加了一个字节31:
1001B38F      FF15 84750210    call dword ptr ds:[<&MFC71.#3850>]    ; MFC71.7C189CDC
D ECX:
01A6C530  31 49 01 54 03 4F 05 76  1ITOv
01A6C538  07 61 09 64 0B 6F 0D 69  a.d o.i
01A6C540  0F 70 11 72 13 6D 02 4F  prmO
01A6C548  04 64 06 64 08 75 0A 69  ddu.i
01A6C550  0C 72 0E 70 10 65 12 34  .rpe4
01A6C558  14 00                    .

第一次连接:1001B47D      E8 4E6DFEFF      call UILib71.100021D0
10002236      FF15 D0770210    call dword ptr ds:[<&MFC71.#1489>]     ; MFC71.7C15A7BD
堆栈:
00124014   01A6B630    ASCII "87654321123456788765"
0012401C   01065F00    ASCII "ImTOOdvdaudioripper4"
0012402C   01A6C970    ASCII "87654321123456788765ImTOOdvdaudioripper4"
就是将输入的注册码前20位与内置的字符串ImTOOdvdaudioripper4连接。

第二次连接:
1001B492      FF15 80770210    call dword ptr ds:[<&MFC71.#907>]     ; MFC71.7C14E599
D EDX;
01A6CB00  31 49 01 54 03 4F 05 76 07 61 09 64 0B 6F 0D 69  1ITOva.d o.i
01A6CB10  0F 70 11 72 13 6D 02 4F 04 64 06 64 08 75 0A 69  prmOddu.i
01A6CB20  0C 72 0E 70 10 65 12 34 14 30 30 38 37 36 35 34  .rpe40087654
01A6CB30  33 32 31 31 32 33 34 35 36 37 38 38 37 36 35 49  321123456788765I
01A6CB40  6D 54 4F 4F 64 76 64 61 75 64 69 6F 72 69 70 70  mTOOdvdaudioripp
01A6CB50  65 72 34                                         er4
这次连接把对内置字符串的2次取值结果与第一次连接值连接起来,得到了值作为MD5的待加密值。

MD5加密:     
1001B4B3      50               push eax                              ; 待加密值?
1001B4B4      8D4C24 70        lea ecx,dword ptr ss:[esp+70]
1001B4B8      E8 03740000      call UILib71.100228C0                 ; MD5?

因为待加密值太大,分批加密:
10023327      E8 D4FEFFFF      call UILib71.10023200                 ; MD5
10023334      E8 C7FEFFFF      call UILib71.10023200                 ; MD5
常数:
1002295D      8B70 04        mov esi,dword ptr ds:[eax+4]      ; EFCDAB89
10022960      8B78 08        mov edi,dword ptr ds:[eax+8]      ; 98BADCFE
10022963      8B50 0C        mov edx,dword ptr ds:[eax+C]      ; 10325476
10022966      8B00           mov eax,dword ptr ds:[eax]        ; 67452301

待加密值:
第一批:
01A6CB00  31 49 01 54 03 4F 05 76 07 61 09 64 0B 6F 0D 69  1ITOva.d o.i
01A6CB10  0F 70 11 72 13 6D 02 4F 04 64 06 64 08 75 0A 69  prmOddu.i
01A6CB20  0C 72 0E 70 10 65 12 34 14 30 30 38 37 36 35 34  .rpe40087654
01A6CB30  33 32 31 31 32 33 34 35 36 37 38 38 37 36 35 49  321123456788765I
第二批:
01A6CB40  6D 54 4F 4F 64 76 64 61 75 64 69 6F 72 69 70 70  mTOOdvdaudioripp
01A6CB50  65 72 34                                         er4

加密值:
1001B4C9      E8 32730000        call UILib71.10022800         ; 转换为字符
eax=001240CD, (ASCII "4ad70c3c17240215f69ec551e6f4c020")

对加密值的取值:
1001B4F0      56                 push esi
1001B4F1      8D4C24 34          lea ecx,dword ptr ss:[esp+34]
1001B4F5      FF15 B8770210      call dword ptr ds:[<&MFC71.#865>]           ; MFC71.7C1894E7
1001B4FB      8D4C24 0C          lea ecx,dword ptr ss:[esp+C]
1001B4FF      50                 push eax
1001B500      FF15 6C750210      call dword ptr ds:[<&MFC71.#908>]           ; MFC71.7C18B24E
1001B506      8BC6               mov eax,esi
1001B508      D1E8               shr eax,1
1001B50A      40                 inc eax
1001B50B      25 03000080        and eax,80000003
1001B510      79 05              jns short UILib71.1001B517
1001B512      48                 dec eax
1001B513      83C8 FC            or eax,FFFFFFFC
1001B516      40                 inc eax
1001B517      75 0F              jnz short UILib71.1001B528
1001B519      68 40E10210        push UILib71.1002E140
1001B51E      8D4C24 10          lea ecx,dword ptr ss:[esp+10]
1001B522      FF15 C4730210      call dword ptr ds:[<&MFC71.#911>]           ; MFC71.7C14E587
1001B528      83C6 02            add esi,2
1001B52B      83FE 20            cmp esi,20
1001B52E    ^ 7C C0              jl short UILib71.1001B4F0

取单数,并4位4位用“-”号连接:
00124058   01A6C9C0    ASCII "4d03-1201-f9c5-efc2-"

转成大写:
1001B530      8D4C24 0C          lea ecx,dword ptr ss:[esp+C]                ; 值
1001B534      FF15 80750210      call dword ptr ds:[<&MFC71.#4085>]          ; 转成大写
00124058   01A6C9C0    ASCII "4D03-1201-F9C5-EFC2-"

去除最后一个“-”号,变成19位:
1001B546      48                 dec eax                                     ; 19位
1001B547      50                 push eax
1001B548      8D4C24 14          lea ecx,dword ptr ss:[esp+14]
1001B54C      FF15 7C750210      call dword ptr ds:[<&MFC71.#1916>]          ; MFC71.7C189568

00124058   01A6C9C0    ASCII "4D03-1201-F9C5-EFC2"

连接注册码的前20位:
1001B55C      50                 push eax
1001B55D      6A 00              push 0
1001B55F      8D4C24 14          lea ecx,dword ptr ss:[esp+14]
1001B563      FF15 84750210      call dword ptr ds:[<&MFC71.#3850>]          ; MFC71.7C189CDC
00124058   01A6C9C0    ASCII "876543211234567887654D03-1201-F9C5-EFC2"

然后比较:
1001B573      50                 push eax                                    ; 原输入的注册码
1001B574      8D4C24 10          lea ecx,dword ptr ss:[esp+10]               ; 计算后的连接值
1001B578      FF15 4C740210      call dword ptr ds:[<&MFC71.#1482>]          ; MFC71.7C144DAE
1001B57E      F7D8               neg eax
1001B580      1AC0               sbb al,al
1001B582      FEC0               inc al
1001B584      8D4C24 30          lea ecx,dword ptr ss:[esp+30]
1001B588      0FB6F0             movzx esi,al
原输入的注册码:00124048   01A6C888    ASCII "876543211234567887654321123456787654321"
计算后的连接值:00124058   01A6C9C0    ASCII "876543211234567887654D03-1201-F9C5-EFC2"
比较值返回到eax,最后作为判断指针返回结果。

然后返回到:
1001B740      E8 BBF8FFFF       call UILib71.ImRegDlg::IsValidRegInfo
1001B745      85C0              test eax,eax                             ; 这个就是返回值
1001B747      75 49             jnz short UILib71.1001B792

4.算法总结:
程序用到的字符串: ASCII "ImTOOdvdaudioripper4"
输入的注册码(39位):ASCII "876543211234567887654321123456787654321"

取注册码前20位,连接字符串得到参数之一:ASCII "87654321123456788765ImTOOdvdaudioripper4"
取字符串单数并序号,再取字符串双数并序号,连接,前面补一个字节31,后面补2个字节30 30,得到参数之一:
01A6CB00  31 49 01 54 03 4F 05 76 07 61 09 64 0B 6F 0D 69  1ITOva.d o.i
01A6CB10  0F 70 11 72 13 6D 02 4F 04 64 06 64 08 75 0A 69  prmOddu.i
01A6CB20  0C 72 0E 70 10 65 12 34 14 30 30                 .rpe400

两个参数连接起来作为待加密值:
01A6CB00  31 49 01 54 03 4F 05 76 07 61 09 64 0B 6F 0D 69  1ITOva.d o.i
01A6CB10  0F 70 11 72 13 6D 02 4F 04 64 06 64 08 75 0A 69  prmOddu.i
01A6CB20  0C 72 0E 70 10 65 12 34 14 30 30 38 37 36 35 34  .rpe40087654
01A6CB30  33 32 31 31 32 33 34 35 36 37 38 38 37 36 35 49  321123456788765I
01A6CB40  6D 54 4F 4F 64 76 64 61 75 64 69 6F 72 69 70 70  mTOOdvdaudioripp
01A6CB50  65 72 34                                         er4

MD5加密得到:(ASCII "4ad70c3c17240215f69ec551e6f4c020"),转换成大写,并取单数,4位4位用“-”号连接,去除最后一个“-”号得到: ASCII "4D03-1201-F9C5-EFC2"
然后与输入的注册码的后19位比较,相等就ok。

用户名的用途:用来加解密注册码,保存到注册表。

注册信息:
用户名:cyto
注册码:876543211234567887654D03-1201-F9C5-EFC2