【破文标题】HAGGAR的KeyMe no.4分析
【破文作者】foria
【破解工具】OD peid
【破解平台】WINXP
【下载地址】www.crackmes.de(由于没有上传权限,在首页上找找吧,很容易的)
【破解声明】初学Crack,只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
根据creakme的作者的声明,这个crack Me的加密方式跟很多游戏差不多,我们下面就来研究一下这个crack me。
第一步还是拿peid看一下,看是那什么语言写的,Nothing found *,先不管它,运行一下再说。
程序非常简单,就一个输入cd-key的地方,然后就是next和cancel。
好了,现在拿OD载入。Ctrl+N,看一下有什么好用的api可以设断的,GetDlgItemTextA,MessageBoxA,在这几个api上设好断点。
按F9运行,随便输入假的序列号。程序断在了下面的位置。
00401123 . E8 72090000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
00401128 . 83F8 04 cmp eax, 4 ; 如果输入的不是4个字符
0040112B . 74 09 je short 00401136 ; 跳走则注册失败
F9,继续运行
004012E1 ? 58 pop eax
004012E2 ? 6A 30 push 30
004012E4 ? 68 00304000 push 00403000 ; ASCII "Information"
004012E9 /> 68 0C304000 push 0040300C ; |Text = "The entered CD key appears to be invalid. Setup cannot continue without valid key. Please try again."
004012EE |. 6A 00 push 0 ; |hOwner = NULL
004012F0 |. E8 B1070000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
004012F5 |. C3 retn
由于代码中夹杂了一些花指令,程序断下来后,用Ctrl+方向箭头上下调整一下,就看到了上面的结果,这里是错误的提示信息,将鼠标滚轮向上翻滚,可以看到还有一个MessageBox:
0040125E . E8 93000000 call 004012F6 ; 关键的call
00401263 . 83F8 00 cmp eax, 0 ; 呵呵,这里就是比较了。。。
00401266 . 74 19 je short 00401281 ; 跳走就完蛋
00401268 . 6A 40 push 40
0040126A . 68 00304000 push 00403000 ; ASCII "Information"
0040126F . 68 71304000 push 00403071 ; ASCII "The entered key appears to be valid. You will be never asked for that key by our company. Please, do not give that key to anybody. Thank you."
00401274 > 6A 00 push 0 ; |hOwner = NULL
00401276 . E8 2B080000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
如果是爆破的话,将上面的je改为jne就够了,但是作者是要我们做出注册机,所以上面的call用F7跟进去。
004012F6 |$ 50 push eax
004012F7 |. E8 03000000 call 004012FF ; call的地址很近,很有可能是替代jmp的,F7跟进去
004012FC \.^ EB EB jmp short 004012E9
004012FE 02 db 02
004012FF $^ EB FC jmp short 004012FD
00401301 . 3E:8B0424 mov eax, ds:[esp]
00401305 . 83C0 09 add eax, 9
00401308 > EB 02 jmp short 0040130C
0040130A . EB 06 jmp short 00401312
0040130C > FFE0 jmp eax
0040130E . 58 pop eax
0040130F . 83C0 19 add eax, 19
00401312 >^ EB F4 jmp short 00401308
这里上面是一段花指令(整个程序用得非常多),作用是来替代jmp的,不要被call所迷惑了,用F7一直按下去,就可以到达这一段代码的出口。
0040131C ? 33F6 xor esi, esi
0040131E ? B9 74314000 mov ecx, 00403174
00401323 8B db 8B
00401324 C6 db C6
到这里之后,拿OD的花指令去除插件用一下,就可以看到代码了,后面遇到类似的情况也同样处理。下面一段代码的跟踪的过程就不细说了(这一段代码没什么用)。跟踪到这里:
0040153F 8A81 14374000 mov al, [ecx+403714] ; 复制注册码
00401545 8881 34374000 mov [ecx+403734], al ; 从内存403714的复制到
0040154B 41 inc ecx
0040154C 83F9 14 cmp ecx, 14 ; 逐位复制,总共14次
0040154F ^ 72 EE jb short 0040153F
00401571 8D05 14374000 lea eax, [403714] ; eax指向刚才复制的注册码的位置
00401577 33D2 xor edx, edx ; 置换,关键位置
00401579 33DB xor ebx, ebx
0040157B B9 02000000 mov ecx, 2
00401580 8A1408 mov dl, [eax+ecx]
00401583 8A58 0D mov bl, [eax+D] ; eax[2]与eax[D]置换
00401586 881C08 mov [eax+ecx], bl
00401589 8A58 0E mov bl, [eax+E]
0040158C 8850 0D mov [eax+D], dl
0040158F BA 04000000 mov edx, 4
00401594 03C8 add ecx, eax
00401596 8D0C10 lea ecx, [eax+edx]
00401599 8A11 mov dl, [ecx]
0040159B 8819 mov [ecx], bl
0040159D 8A58 0F mov bl, [eax+F]
004015A0 8850 0E mov [eax+E], dl ; 4与e置换
004015A3 B9 05000000 mov ecx, 5
004015A8 8A1408 mov dl, [eax+ecx]
004015AB 881C08 mov [eax+ecx], bl
004015AE 8A58 10 mov bl, [eax+10]
004015B1 8850 0F mov [eax+F], dl ; 5与f置换,下面的类似,省略
004015B4 BA 07000000 mov edx, 7
004015B9 03C8 add ecx, eax
004015BB 8D0C10 lea ecx, [eax+edx]
004015BE 8A11 mov dl, [ecx]
004015C0 8819 mov [ecx], bl
004015C2 8A58 11 mov bl, [eax+11]
004015C5 8850 10 mov [eax+10], dl
004015C8 B9 0C000000 mov ecx, 0C
004015CD 8A1408 mov dl, [eax+ecx]
004015D0 881C08 mov [eax+ecx], bl
004015D3 8A58 12 mov bl, [eax+12]
004015D6 03C8 add ecx, eax
004015D8 8850 11 mov [eax+11], dl
004015DB BA 01000000 mov edx, 1
004015E0 8D0C10 lea ecx, [eax+edx] ; 最后的置换表:
004015E3 8A11 mov dl, [ecx] ; 2<->D
004015E5 8819 mov [ecx], bl ; 4<->E
004015E7 8A58 13 mov bl, [eax+13] ; 5<->F
004015EA 8850 12 mov [eax+12], dl ; 7<->G
004015ED B9 03000000 mov ecx, 3 ; C<->H
004015F2 8A1408 mov dl, [eax+ecx] ; 1<->I
004015F5 03C8 add ecx, eax ; 3<->J
004015F7 8819 mov [ecx], bl
004015F9 8850 13 mov [eax+13], dl ; 置换完毕
004015FC C640 14 00 mov byte ptr [eax+14], 0 ; 尾部加零
如果你输入的注册码是0123-4567-89AB-CDEF-GHIJ,置换后就是0IDJ-EF6G-89AB-H245-7C13。
00401688 8D05 55374000 lea eax, [403755] ; 计算magic number
0040168E 3E:8B0C24 mov ecx, ds:[esp]
00401692 8AD1 mov dl, cl
00401694 80E2 1F and dl, 1F
00401697 8850 06 mov [eax+6], dl
0040169A 8BD1 mov edx, ecx
0040169C C1EA 05 shr edx, 5
0040169F 80E2 1F and dl, 1F
004016A2 8850 05 mov [eax+5], dl
004016A5 8BD1 mov edx, ecx
004016A7 C1EA 0A shr edx, 0A
004016AA 80E2 1F and dl, 1F
004016AD 8850 04 mov [eax+4], dl
004016B0 8BD1 mov edx, ecx
004016B2 C1EA 0F shr edx, 0F
004016B5 80E2 1F and dl, 1F
004016B8 8850 03 mov [eax+3], dl
......
计算一个magic number,放在内存403755的位置,用OD察看一下,9HYJ4LB,好奇怪的一个数字...
0040176B 8A81 55374000 mov al, [ecx+403755] ; 将magic number复制到注册码的尾部
00401771 8881 21374000 mov [ecx+403721], al
00401777 41 inc ecx
00401778 83F9 07 cmp ecx, 7
0040177B ^ 72 EE jb short 0040176B
继续跟踪:
0040185B 8D05 55374000 lea eax, [403755] ; 再次计算magic number
00401861 8AD1 mov dl, cl
00401863 80E2 03 and dl, 3
00401866 C0E2 03 shl dl, 3
00401869 8850 06 mov [eax+6], dl
0040186C C1E9 02 shr ecx, 2
0040186F 8AD1 mov dl, cl
00401871 80E2 1F and dl, 1F
00401874 8850 05 mov [eax+5], dl
00401877 8BD1 mov edx, ecx
00401879 C1EA 05 shr edx, 5
0040187C 80E2 1F and dl, 1F
0040187F 8850 04 mov [eax+4], dl
00401937 /EB 0D jmp short 00401946
00401939 |8A81 55374000 mov al, [ecx+403755]
0040193F |8881 21374000 mov [ecx+403721], al
00401945 |41 inc ecx
00401946 \83F9 07 cmp ecx, 7
00401949 ^ 72 EE jb short 00401939
同样是计算magic number和复制,跟到了这里,我们会发现前面相同功能的一段代码没有什么用,第二次的结果完全把第一次的结果覆盖了。
现在的magic number是222227J,注册码则变为了0IDJ-EF6G-89AB-H222-227J。
0040196B 8D05 14374000 lea eax, [403714]
00401971 33D2 xor edx, edx
00401973 33DB xor ebx, ebx
00401975 B9 02000000 mov ecx, 2 ; 第二次置换
.......
004019F3 8850 13 mov [eax+13], dl ; 置换表与第一次完全一样
004019F6 C640 14 00 mov byte ptr [eax+14], 0 ; 第二次置换完毕
经过第二轮置换后,注册码现在变为了072J-2262-89AB-2DEF-GHIJ。
00401A1E 33C9 xor ecx, ecx
00401A20 EB 14 jmp short 00401A36
00401A22 8A99 14374000 mov bl, [ecx+403714] ; 置换过的注册码
00401A28 8AB9 34374000 mov bh, [ecx+403734] ; 原始的注册码
00401A2E 38FB cmp bl, bh ; 逐位比较
00401A30 74 03 je short 00401A35
00401A32 33C0 xor eax, eax ; eax清零
00401A34 C3 retn ; 到这就注册失败
00401A35 41 inc ecx
00401A36 83F9 14 cmp ecx, 14 ; 比较14次
00401A39 ^ 72 E7 jb short 00401A22
00401A3B 50 push eax ; 到这就成功了
这一段就是最后的比较部分了,push后面还有一段代码,作用是置eax为1,返回,注册成功。
现在我们对程序的注册方式就了解了,cdkey的认证采用自校验的方式,先将用户的注册码做一次置换,将尾部七个字符修改为magic number,再做一次相同的置换,再将现在变换过的注册码与最原始的相比,相同则注册成功。
变换过的注册码在1,2,3,4,5,7,C这几个位置变为了magic number,而别的位置都不变。
最后的注册算法如下:
072J-2202-0000-2000-0000,将0的位置替换为任意的字符(例如R72J-22M2-B3RB-2222-227J),就可以得到任意的注册码了(因为0所在的位置在变换后是不变的,而非0的地方正是magic number所在的地方)。到了这里注册机就很容易写出了。
等等,还没完,我们上面分析中还少了一点,其实注册码中是不能含有某些字符的,看下面:
0040136A C705 74364000>mov dword ptr [403674], 1010101 ; 将掩码写入内存,后面要用
00401374 C705 78364000>mov dword ptr [403678], 1010101
0040137E C705 7C364000>mov dword ptr [40367C], 1010101
00401388 C705 80364000>mov dword ptr [403680], 1010101
00401392 C705 84364000>mov dword ptr [403684], 1010101
0040139C C705 88364000>mov dword ptr [403688], 1010100
004013A6 C705 8C364000>mov dword ptr [40368C], 1000001
004013B0 C705 90364000>mov dword ptr [403690], 1010101
004013BA C705 94364000>mov dword ptr [403694], 1000101
004013C4 C705 98364000>mov dword ptr [403698], 1010101
这里是前面一段比较注册码的地方,主要是看注册码是否合理,外层循环采用查表的的方法,确定是哪一个字符,
内层在跟掩码比较,如果恰好遇到掩码是0的话,程序就跳走了,注册失败。
我们来计算一下那几个字符被掩掉了:
004030FF 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 0123456789ABCDEF
0040310F 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 GHIJKLMNOPQRSTUV
0040311F 57 5A 59 58 00 36 34 33 38 32 39 35 37 4A 4B 4C WZYX.64382957JKL
这就是程序中用到的表。
00403674 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
00403684 01 01 01 01 00 01 01 01 01 00 00 01 01 01 01 01 ...
00403694 01 01 00 01 01 01 01 01 00 00 00 00 00 00 00 00 .........
这是掩码。
掩码为零的地方恰好是K,P,Q,Y这几个字符,所以在制作注册机的时候要把这几个字符过滤掉。
004014F5 /EB 18 jmp short 0040150F
004014F7 |3A81 FF304000 cmp al, [ecx+4030FF]
004014FD |75 0F jnz short 0040150E
004014FF |80B9 74364000>cmp byte ptr [ecx+403674], 1 ; 内层循环跟掩码比较
00401506 |74 06 je short 0040150E ; 相等则注册失败
00401508 |83C4 04 add esp, 4
0040150B |33C0 xor eax, eax
0040150D |C3 retn
0040150E |41 inc ecx
0040150F \83F9 24 cmp ecx, 24
00401512 ^ 72 E3 jb short 004014F7 ; 外层循环先确定是哪一个字母
总结一下,注册码由数字和大写字母组成,但是大写字母不包括K、P、Q、Y四个字母。072J-2202-0000-2000-0000,将0的位置替换为任意的字符,就可以得到任意的注册码了。
[注意]APP应用上架合规检测服务,协助应用顺利上架!