原程序下载地址:http://www.crackmes.de/users/ox87k/keygenme1_gas/
使用工具:PEid,OllyDbg,ResHacker
根据作者的简介,程序中使用了密码学保护方法,用PEid看了一下,发现SHA-1
字样。然后用OllyDbg加载。程序中有抗调试代码(主要是IsDebuggerPresent方
法),直接运行会立即终止。用隐藏插件尽量隐藏OD后重新载入方可运行。
随便输入用户名和序列号,点击Check按钮,没有任何反应。查找程序中的有用
字串,找到一个:
ASCII "Very good! Now make ur keygen and tutorial!"
但这次关键跳转并没有如预期的那样出现在引用这个字串前不远处,实际上在此之前
的代码有很多处都直接跳向失败,很难说其中的某一个是关键跳转。将代码向上翻
页,在地址408204处有一段代码:
=========================
00408204 . 8D4424 10 lea eax, [esp+10]
00408208 . 50 push eax
00408209 . 68 F3030000 push 3F3
0040820E . E8 030B0000 call <jmp.&MFC42.#3097_CWnd::GetDlgIt>; 取序列号
=========================
先暂时把40820E设置为断点。3F3这个古怪的数字应该是控件ID,有必要弄清它代表
的是哪一个控件,于是求助于Resource Hacker,发现它是序列号输入框的ID。在这
句函数调用之前不远处又找到一句模样差不多的调用,经过与资源脚本的比对知道那
是在取用户名字串。整段代码如下:
////////// 以下是代码 ///////////
00407FFA . 50 push eax
00407FFB . 68 F1030000 push 3F1
00408000 . E8 110D0000 call <jmp.&MFC42.#3097_CWnd::GetDlgIt>; 取用户名
00408005 . 8B5424 18 mov edx, [esp+18]
00408009 . 83C9 FF or ecx, FFFFFFFF
0040800C . 8BFA mov edi, edx
0040800E . 33C0 xor eax, eax
00408010 . F2:AE repne scas byte ptr es:[edi]
00408012 . F7D1 not ecx
00408014 . 2BF9 sub edi, ecx
00408016 . 8DAC24 940200>lea ebp, [esp+294] ; 堆栈地址=0012EAAC
0040801D . 8BC1 mov eax, ecx
0040801F . 8BF7 mov esi, edi
00408021 . 8BFD mov edi, ebp
00408023 . C1E9 02 shr ecx, 2
00408026 . F3:A5 rep movs dword ptr es:[edi], dword p>
00408028 . 8BC8 mov ecx, eax
0040802A . 33C0 xor eax, eax
0040802C . 83E1 03 and ecx, 3
0040802F . F3:A4 rep movs byte ptr es:[edi], byte ptr>; 在上述地址中存入用户名
00408031 . 8BFA mov edi, edx
00408033 . 83C9 FF or ecx, FFFFFFFF
00408036 . F2:AE repne scas byte ptr es:[edi]
00408038 . F7D1 not ecx
0040803A . 49 dec ecx
0040803B . 8BE9 mov ebp, ecx
0040803D 83FD 03 cmp ebp, 3 ; 用户名必须不少于3个字符
00408040 . 7D 0A jge short 0040804C
00408042 . 68 D8534100 push 004153D8 ; ASCII "Please insert between 4 and 32 chars..."
00408047 . E9 A6030000 jmp 004083F2
0040804C > 6A 01 push 1
0040804E . 8D8C24 480200>lea ecx, [esp+248] ; 堆栈地址=0012EA5C
00408055 . 55 push ebp
00408056 . 8D9424 000100>lea edx, [esp+100] ; 堆栈地址=0012E910(Hash串指针?)
0040805D . 51 push ecx
0040805E . 8D8424 A00200>lea eax, [esp+2A0] ; 堆栈地址=0012EAAC(用户名)
00408065 . 52 push edx
00408066 . 50 push eax
00408067 . 81EC C4000000 sub esp, 0C4
0040806D . B9 31000000 mov ecx, 31
00408072 . 8DB424 500600>lea esi, [esp+650] ; 堆栈地址=0012ED90
00408079 . 8BFC mov edi, esp
0040807B . F3:A5 rep movs dword ptr es:[edi], dword p>; MD5加法常数表?
0040807D . E8 3E040000 call 004084C0
00408082 . 81C4 D8000000 add esp, 0D8
00408088 . 8D8C24 300200>lea ecx, [esp+230]
0040808F . 8D9424 900000>lea edx, [esp+90]
00408096 . 8D8424 F80000>lea eax, [esp+F8] ; 堆栈地址=0012E910
0040809D . 6A 02 push 2
0040809F . 55 push ebp
004080A0 . 51 push ecx
004080A1 . 52 push edx
004080A2 . 50 push eax
004080A3 . B9 31000000 mov ecx, 31
004080A8 . 81EC C4000000 sub esp, 0C4
004080AE . 8DB424 C80400>lea esi, [esp+4C8]
004080B5 . 8BFC mov edi, esp
004080B7 . F3:A5 rep movs dword ptr es:[edi], dword p>
004080B9 . E8 02040000 call 004084C0
004080BE . 81C4 D8000000 add esp, 0D8
004080C4 . 8D8C24 6C0200>lea ecx, [esp+26C]
004080CB . 8D5424 28 lea edx, [esp+28]
004080CF . 8D8424 900000>lea eax, [esp+90]
004080D6 . 6A 04 push 4
004080D8 . 55 push ebp
004080D9 . 51 push ecx
004080DA . 52 push edx
004080DB . 50 push eax
004080DC . B9 31000000 mov ecx, 31
004080E1 . 81EC C4000000 sub esp, 0C4
004080E7 . 8DB424 040400>lea esi, [esp+404] ; 堆栈地址=0012EB44
004080EE . 8BFC mov edi, esp
004080F0 . F3:A5 rep movs dword ptr es:[edi], dword p>
004080F2 . E8 C9030000 call 004084C0
004080F7 . 81C4 D8000000 add esp, 0D8
004080FD . 8D8C24 800200>lea ecx, [esp+280]
00408104 . 8D5424 5C lea edx, [esp+5C]
00408108 . 8D4424 28 lea eax, [esp+28]
0040810C . 6A 08 push 8
0040810E . 55 push ebp
0040810F . 51 push ecx
00408110 . 52 push edx
00408111 . 50 push eax
00408112 . B9 31000000 mov ecx, 31
00408117 . 81EC C4000000 sub esp, 0C4
0040811D . 8DB424 8C0500>lea esi, [esp+58C]
00408124 . 8BFC mov edi, esp
00408126 . F3:A5 rep movs dword ptr es:[edi], dword p>
00408128 . E8 93030000 call 004084C0
0040812D . 81C4 D8000000 add esp, 0D8
00408133 . 8D8C24 580200>lea ecx, [esp+258]
0040813A . 8D9424 C40000>lea edx, [esp+C4]
00408141 . 8D4424 5C lea eax, [esp+5C]
00408145 . 6A 10 push 10
00408147 . 55 push ebp
00408148 . 51 push ecx
00408149 . 52 push edx
0040814A . 50 push eax
0040814B . B9 31000000 mov ecx, 31
00408150 . 81EC C4000000 sub esp, 0C4
00408156 . 8DB424 140700>lea esi, [esp+714]
0040815D . 8BFC mov edi, esp
0040815F . F3:A5 rep movs dword ptr es:[edi], dword p>
00408161 . E8 5A030000 call 004084C0
00408166 . 81C4 D8000000 add esp, 0D8
0040816C . 33C0 xor eax, eax
0040816E . 889C24 000100>mov [esp+100], bl ; 填0(截断字符串)
00408175 . 33C9 xor ecx, ecx
00408177 > 8A9404 980000>mov dl, [esp+eax+98] ; 这里是把要取来组合成序列号的子串移到Hash串首
0040817E . 889404 900000>mov [esp+eax+90], dl ; 当然这同时覆盖了原Hash串首的字符
00408185 . 8A540C 38 mov dl, [esp+ecx+38] ; 注意这里的处理,递增eax而递减ecx
00408189 . 885404 28 mov [esp+eax+28], dl ; 因此所复制的子串也是逆序的
0040818D . 8A5404 74 mov dl, [esp+eax+74]
00408191 . 885404 5C mov [esp+eax+5C], dl
00408195 . 8A940C E40000>mov dl, [esp+ecx+E4]
0040819C . 889404 C40000>mov [esp+eax+C4], dl
004081A3 . 40 inc eax
004081A4 . 49 dec ecx
004081A5 . 83F9 F8 cmp ecx, -8
004081A8 .^ 7F CD jg short 00408177
004081AA . A3 60554100 mov [415560], eax
004081AF . 8D8424 C40000>lea eax, [esp+C4]
004081B6 . 8D4C24 5C lea ecx, [esp+5C]
004081BA . 50 push eax ; /<%s>
004081BB . 8D5424 2C lea edx, [esp+2C] ; |
004081BF . 51 push ecx ; |<%s>
004081C0 . 8D8424 980000>lea eax, [esp+98] ; |
004081C7 . 52 push edx ; |<%s>
004081C8 . 8D8C24 040100>lea ecx, [esp+104] ; |
004081CF . 50 push eax ; |<%s>
004081D0 . 51 push ecx ; |<%s>
004081D1 . 8D9424 DC0200>lea edx, [esp+2DC] ; |
004081D8 . 68 C8534100 push 004153C8 ; |format = "%s-%s-%s-%s-%s"
004081DD . 52 push edx ; |根据分析结果,这里应该是存放真码的缓冲区
004081DE . 889C24 B40000>mov [esp+B4], bl ; |填0(截断字符串,下同)
004081E5 . 885C24 4C mov [esp+4C], bl ; |
004081E9 . 889C24 800000>mov [esp+80], bl ; |
004081F0 . 889C24 E80000>mov [esp+E8], bl ; |
004081F7 . FF15 74A24000 call [<&MSVCRT.sprintf>] ; \sprintf
004081FD . 8B4C24 30 mov ecx, [esp+30]
00408201 . 83C4 1C add esp, 1C
00408204 . 8D4424 10 lea eax, [esp+10]
00408208 . 50 push eax
00408209 . 68 F3030000 push 3F3
0040820E . E8 030B0000 call <jmp.&MFC42.#3097_CWnd::GetDlgIt>; 取序列号
00408213 . 8B5424 10 mov edx, [esp+10] ; 序列号指针
00408217 . 33C0 xor eax, eax
00408219 . A3 64554100 mov [415564], eax
0040821E > 8A0C10 mov cl, [eax+edx]
00408221 . 888C04 940100>mov [esp+eax+194], cl ; 首地址12E9AC
00408228 . 8A4C02 09 mov cl, [edx+eax+9]
0040822C . 888C04 FC0100>mov [esp+eax+1FC], cl ; 首地址12EA14
00408233 . 8A4C02 12 mov cl, [edx+eax+12]
00408237 . 888C04 C80100>mov [esp+eax+1C8], cl ; 首地址12E9E0
0040823E . 8A4C02 1B mov cl, [edx+eax+1B]
00408242 . 888C04 600100>mov [esp+eax+160], cl ; 首地址12E978
00408249 . 8A4C02 24 mov cl, [edx+eax+24]
0040824D . 888C04 2C0100>mov [esp+eax+12C], cl ; 首地址12E944
00408254 . 40 inc eax
00408255 . 83F8 08 cmp eax, 8
00408258 . A3 64554100 mov [415564], eax
0040825D .^ 7C BF jl short 0040821E
0040825F . 8BFA mov edi, edx
00408261 . 83C9 FF or ecx, FFFFFFFF
00408264 . 33C0 xor eax, eax
00408266 . 889C24 9C0100>mov [esp+19C], bl
0040826D . F2:AE repne scas byte ptr es:[edi]
0040826F . F7D1 not ecx
00408271 . 49 dec ecx
00408272 . 889C24 040200>mov [esp+204], bl
00408279 . 83F9 2C cmp ecx, 2C ; 序列号长度必须为2Ch
0040827C . 889C24 D00100>mov [esp+1D0], bl
00408283 . 889C24 680100>mov [esp+168], bl
0040828A . 889C24 340100>mov [esp+134], bl
00408291 . 0F85 69010000 jnz 00408400 ; 跳向失败
00408297 . 8A42 08 mov al, [edx+8]
0040829A . 3C 2D cmp al, 2D ; 第9个字符必须为'-'
0040829C . 0F85 5E010000 jnz 00408400 ; 结论:序列号格式为5小节,每小节8字符
004082A2 . 807A 11 2D cmp byte ptr [edx+11], 2D ; 小节之间用'-'连接
004082A6 . 0F85 54010000 jnz 00408400
004082AC . 807A 1A 2D cmp byte ptr [edx+1A], 2D
004082B0 . 0F85 4A010000 jnz 00408400
004082B6 . 807A 23 2D cmp byte ptr [edx+23], 2D
004082BA . 0F85 40010000 jnz 00408400
004082C0 . 8DB424 F80000>lea esi, [esp+F8] ; 从这里开始用输入的序列号进行比较
004082C7 . 8D8424 940100>lea eax, [esp+194]
004082CE > 8A10 mov dl, [eax]
004082D0 . 8ACA mov cl, dl
004082D2 . 3A16 cmp dl, [esi]
004082D4 . 75 1C jnz short 004082F2 ; 跳(串不相等,下同)则失败
004082D6 . 3ACB cmp cl, bl ; 已达串尾否(bl=0)
004082D8 . 74 14 je short 004082EE
004082DA . 8A50 01 mov dl, [eax+1]
004082DD . 8ACA mov cl, dl
004082DF . 3A56 01 cmp dl, [esi+1]
004082E2 . 75 0E jnz short 004082F2 ; 串不相等
004082E4 . 83C0 02 add eax, 2
004082E7 . 83C6 02 add esi, 2
004082EA . 3ACB cmp cl, bl
004082EC .^ 75 E0 jnz short 004082CE
004082EE > 33C0 xor eax, eax
004082F0 . EB 05 jmp short 004082F7
004082F2 > 1BC0 sbb eax, eax
004082F4 . 83D8 FF sbb eax, -1 ; eax = (-1)CF
004082F7 > 3BC3 cmp eax, ebx
004082F9 . 0F85 01010000 jnz 00408400
004082FF . 8DB424 900000>lea esi, [esp+90]
00408306 . 8D8424 FC0100>lea eax, [esp+1FC]
0040830D > 8A10 mov dl, [eax]
0040830F . 8ACA mov cl, dl
00408311 . 3A16 cmp dl, [esi]
00408313 . 75 1C jnz short 00408331 ; 同上
00408315 . 3ACB cmp cl, bl
00408317 . 74 14 je short 0040832D
00408319 . 8A50 01 mov dl, [eax+1]
0040831C . 8ACA mov cl, dl
0040831E . 3A56 01 cmp dl, [esi+1]
00408321 . 75 0E jnz short 00408331
00408323 . 83C0 02 add eax, 2
00408326 . 83C6 02 add esi, 2
00408329 . 3ACB cmp cl, bl
0040832B .^ 75 E0 jnz short 0040830D
0040832D > 33C0 xor eax, eax
0040832F . EB 05 jmp short 00408336
00408331 > 1BC0 sbb eax, eax
00408333 . 83D8 FF sbb eax, -1
00408336 > 3BC3 cmp eax, ebx
00408338 . 0F85 C2000000 jnz 00408400
0040833E . 8D7424 28 lea esi, [esp+28]
00408342 . 8D8424 C80100>lea eax, [esp+1C8]
00408349 > 8A10 mov dl, [eax]
0040834B . 8ACA mov cl, dl
0040834D . 3A16 cmp dl, [esi]
0040834F . 75 1C jnz short 0040836D
00408351 . 3ACB cmp cl, bl
00408353 . 74 14 je short 00408369
00408355 . 8A50 01 mov dl, [eax+1]
00408358 . 8ACA mov cl, dl
0040835A . 3A56 01 cmp dl, [esi+1]
0040835D . 75 0E jnz short 0040836D
0040835F . 83C0 02 add eax, 2
00408362 . 83C6 02 add esi, 2
00408365 . 3ACB cmp cl, bl
00408367 .^ 75 E0 jnz short 00408349
00408369 > 33C0 xor eax, eax
0040836B . EB 05 jmp short 00408372
0040836D > 1BC0 sbb eax, eax
0040836F . 83D8 FF sbb eax, -1
00408372 > 3BC3 cmp eax, ebx
00408374 . 0F85 86000000 jnz 00408400
0040837A . 8D7424 5C lea esi, [esp+5C]
0040837E . 8D8424 600100>lea eax, [esp+160]
00408385 > 8A10 mov dl, [eax]
00408387 . 8ACA mov cl, dl
00408389 . 3A16 cmp dl, [esi]
0040838B . 75 1C jnz short 004083A9
0040838D . 3ACB cmp cl, bl
0040838F . 74 14 je short 004083A5
00408391 . 8A50 01 mov dl, [eax+1]
00408394 . 8ACA mov cl, dl
00408396 . 3A56 01 cmp dl, [esi+1]
00408399 . 75 0E jnz short 004083A9
0040839B . 83C0 02 add eax, 2
0040839E . 83C6 02 add esi, 2
004083A1 . 3ACB cmp cl, bl
004083A3 .^ 75 E0 jnz short 00408385
004083A5 > 33C0 xor eax, eax
004083A7 . EB 05 jmp short 004083AE
004083A9 > 1BC0 sbb eax, eax
004083AB . 83D8 FF sbb eax, -1
004083AE > 3BC3 cmp eax, ebx
004083B0 . 75 4E jnz short 00408400
004083B2 . 8DB424 C40000>lea esi, [esp+C4]
004083B9 . 8D8424 2C0100>lea eax, [esp+12C]
004083C0 > 8A10 mov dl, [eax]
004083C2 . 8ACA mov cl, dl
004083C4 . 3A16 cmp dl, [esi]
004083C6 . 75 1C jnz short 004083E4
004083C8 . 3ACB cmp cl, bl
004083CA . 74 14 je short 004083E0
004083CC . 8A50 01 mov dl, [eax+1]
004083CF . 8ACA mov cl, dl
004083D1 . 3A56 01 cmp dl, [esi+1]
004083D4 . 75 0E jnz short 004083E4
004083D6 . 83C0 02 add eax, 2
004083D9 . 83C6 02 add esi, 2
004083DC . 3ACB cmp cl, bl
004083DE .^ 75 E0 jnz short 004083C0
004083E0 > 33C0 xor eax, eax
004083E2 . EB 05 jmp short 004083E9
004083E4 > 1BC0 sbb eax, eax
004083E6 . 83D8 FF sbb eax, -1
004083E9 > 3BC3 cmp eax, ebx
004083EB . 75 13 jnz short 00408400
004083ED . 68 9C534100 push 0041539C ; ASCII "Very good! Now make ur keygen and tutorial!"
004083F2 > 8B4C24 18 mov ecx, [esp+18]
004083F6 . 68 F3030000 push 3F3
004083FB . E8 10090000 call <jmp.&MFC42.#5953_CWnd::SetDlgIt>
////////// 以上是代码 ///////////
从取得用户名字串的地方开始跟踪,当经过408096一句时,下方的状态框中出现:
堆栈地址=0012E910, (ASCII "A360DF687F69642CD40B89E51587AE4C3C3E018C")
这很象是一个Hash值啊。数一下位数,40位16进数,正好是160位二进数。联系用
PEid检查的结果,猜测这可能就是SHA-1算法。不过现在只是粗跟踪,暂时不去管
它。接下来在4080CF、408108、408141处也出现了相似的情形,提示指令中引用
的堆栈地址是一个Hash字串的指针。
接着跟到获取并处理序列号的部分,最初的时候用的假序列号是任意输的,
跟踪过程中发现序列号的格式有要求,因而重新输入符合格式的序列号。在
4082C0处开始用输入序列号的第一小节做串比较,用于比较的另一字串就是第一
次见到的Hash串的第一小节(每8字符一小节,下同)。由于输入过于任意,在第
一节的比较中就被踢到失败的分支里去了,无法继续跟踪下去,只好通过修改内
存值的方法使得流程进入下一节的比较,往后几节的比较也是如此。跟踪的结果
表明:
输入码的第一小节与第一次出现的Hash串第一小节比较
输入码的第二小节与第二次出现的Hash串第二小节比较
输入码的第三小节与第三次出现的Hash串第??小节比较
输入码的第四小节与第四次出现的Hash串第四小节比较
输入码的第五小节与第五次出现的Hash串第??小节比较
当然现在只是粗跟踪一遍后得到的结论,然而这起码说明了输入的序列号仅仅是
用来比较而不进行其他变换,那就表明应该可以通过程序自身的运算而得到真的
序列号。
既然真的序列号是上述Hash串的某些子串,那就有必要弄清这些Hash值是怎
么算出来的。观察40807D处对过程4084C0的调用,在调用前推入的参数有用户名
指针及长度等信息,调用完以后在408096处就出现了一个Hash串,那4084C0这个
过程应该就是计算Hash值的。用Hash计算器计算"PEDIY"的SHA-1值,怎么跟程序
里出现的值不一样!跟进过程4084C0看一看:
////////// 以下是代码 ///////////
004084C0 /$ 64:A1 0000000>mov eax, fs:[0]
004084C6 |. 6A FF push -1
004084C8 |. 68 78904000 push 00409078
004084CD |. 50 push eax
004084CE |. 64:8925 00000>mov fs:[0], esp
004084D5 |. 53 push ebx
004084D6 |. 56 push esi
004084D7 |. 57 push edi
004084D8 |. 8D4C24 1C lea ecx, [esp+1C] ; MD5 T1
004084DC |. C74424 14 000>mov dword ptr [esp+14], 0
004084E4 |. E8 27D3FFFF call 00405810 ; SHA初始化
004084E9 |. 8BB424 F00000>mov esi, [esp+F0]
004084F0 |. 83FE 01 cmp esi, 1 ; 参数5(子串重复次数)
004084F3 |. 75 1B jnz short 00408510
004084F5 |. 8B8424 EC0000>mov eax, [esp+EC] ; 参数4(用户名长)
004084FC |. 8B8C24 E00000>mov ecx, [esp+E0] ; 参数3(用户名指针)
00408503 |. 50 push eax
00408504 |. 51 push ecx
00408505 |. 8D4C24 24 lea ecx, [esp+24]
00408509 |. E8 C2EAFFFF call 00406FD0 ; 字符串连接
0040850E |. EB 36 jmp short 00408546
00408510 |> 85F6 test esi, esi
00408512 |. C705 60554100>mov dword ptr [415560], 0
0040851C |. 7E 28 jle short 00408546
0040851E |. 8BBC24 EC0000>mov edi, [esp+EC]
00408525 |. 8B9C24 E00000>mov ebx, [esp+E0]
0040852C |> 57 /push edi
0040852D |. 53 |push ebx
0040852E |. 8D4C24 24 |lea ecx, [esp+24]
00408532 |. E8 99EAFFFF |call 00406FD0
00408537 |. A1 60554100 |mov eax, [415560]
0040853C |. 40 |inc eax
0040853D |. 3BC6 |cmp eax, esi
0040853F |. A3 60554100 |mov [415560], eax
00408544 |.^ 7C E6 \jl short 0040852C
00408546 |> 8D4C24 1C lea ecx, [esp+1C]
0040854A |. E8 41EBFFFF call 00407090 ; Hash值计算过程
0040854F |. 8B9424 E40000>mov edx, [esp+E4]
00408556 |. 6A 00 push 0 ; /Arg2 = 00000000
00408558 |. 52 push edx ; |存放Hash值的指针
00408559 |. 8D4C24 24 lea ecx, [esp+24] ; |
0040855D |. E8 0EECFFFF call 00407170 ; \+gAs+.00407170
00408562 |. 8D4C24 1C lea ecx, [esp+1C]
00408566 |. C74424 14 FFF>mov dword ptr [esp+14], -1
0040856E |. E8 8DD2FFFF call 00405800
00408573 |. 8B4C24 0C mov ecx, [esp+C]
00408577 |. 5F pop edi
00408578 |. 5E pop esi
00408579 |. 64:890D 00000>mov fs:[0], ecx
00408580 |. 5B pop ebx
00408581 |. 83C4 0C add esp, 0C
00408584 \. C3 retn
////////// 以上是代码 ///////////
其中查看一下过程405810的内容:
////////// 以下是代码 ///////////
00405810 |$ 33C0 xor eax, eax
00405812 |. C701 78A46AD7 mov dword ptr [ecx], D76AA478
00405818 |. C741 04 56B7C>mov dword ptr [ecx+4], E8C7B756
0040581F |. C741 08 DB702>mov dword ptr [ecx+8], 242070DB
00405826 |. C741 0C EECEB>mov dword ptr [ecx+C], C1BDCEEE
0040582D |. C741 10 AF0F7>mov dword ptr [ecx+10], F57C0FAF
00405834 |. 8941 14 mov [ecx+14], eax
00405837 |. 8941 18 mov [ecx+18], eax
0040583A \. C3 retn
////////// 以上是代码 ///////////
这不是MD5的加法常数吗,难道用的是MD5算法?可是为什么又只用5个值呢,MD5一共
有64个加法常数呀!由于“先入为主”倾向于认为还是SHA-1算法,联想到SHA-1初始
化的时候用了5个双字整数。那这应该是用MD5的前5个Ti做SHA-1的初始化值!SHA-1
计算程序可谓多矣,可是无一例外的是用67452301h之类的“传统数值”初始化,能
自选初始化数值的却没有。不得已之下通过修改exe把初始化数值改成这里所需要的,
再来计算"PEDIY",果然出现了"A360DF687F69642CD40B89E51587AE4C3C3E018C"!
第一次出现的Hash值是用户名的SHA-1,那第二次、第三次……这些Hash值,又
是谁的SHA-1呢?回到主调程序中,代替用户名的指针,4080B9处第二次调用4084C0
过程使用的就是上面这个Hash串的指针。难道是这个串的Hash值?计算一下,发现不
对。又想到在传递给4084C0的参数中还有一个串长(即ebp的值),第二次调用时这
个串长的值并未改变,还是5。那就取第一次的Hash串前5个字符组成的子串,即
"A360D",算一算看看怎么样呢?算了一下,还是不对!
怎么搞的啊。再进入4084C0看一下,在4084F0处用到了调用此过程的第5个参数
(最先入栈参数),根据这个参数的值决定重复调用过程406FD0多少次。然而根据监
视相关内存单元的结果,对Hash值的计算及写入是在调用407090中完成的,那406FD0
这个过程究竟是干什么的呢?这个过程的两个参数分别是父过程传递进来的串长和串
指针,跟进这个过程可知它把串指针起始处的串长个字符复制到某个地方,而关键在
于如果在40852C到408544的循环中调用此过程,则每一次复制字符串的目的指针都是
上一次复制的串尾,也就是说,这本质上相当于字符串连接!这样的话,第二次传递
给过程4084C0的参数5的值是2,上述循环执行2遍,得到的字符串应该是
"A360DA360D",算一下它的SHA-1,与结果相符!
这样的话,主调程序中每次调用4084C0的参数3都是上一次Hash值的串指针,而
究竟要计算哪个串的SHA-1,只需要看调用它的参数5就行了。
如果只是简单地从每一次的Hash串中抽取一个子串来组合成序列号,那到这里就
没有什么更多的事了。问题是,序列号的第3、5小节在对应的Hash串中找不到。第3
次和第5次的Hash串分别是:
"C4D6F82E3150BCB1C2AEAD7A7D47BAC1DF10C8C3"
"C965332E4655754B5F089182C9E49DD9C0E1993B"
而对应序列号的第3小节是:
xxxxxxxx-xxxxxxxx-C1BCB051-xxxxxxxx-C9DD94E9
注意到如果把第10到17个字符组成的子串,即"150BCB1C",反过来读正好是
"C1BCB051",而第五次的Hash串第26到33个字符也存在同样的对应关系。这又是哪里
出了问题呢?回到程序中看一下,在408177处有一个移动8个字符小节的动作,对于
第2、4次的Hash串,只是简单地把要组合的小节移到串首;而第3、5次Hash串,由于
源指针是递减的,相当于移动的同时完成反序。
至此可以把算法总结如下:
以输入用户名"PEDIY"为例
(1)取用户名串长,记为N。N必须大于或等于3。在本例中,N的值为5。
(2)计算用户名的SHA-1字串,记为s1。这里的SHA-1用的初始化数据是MD5的
前5个加法常数,下同。
"PEDIY"的SHA-1为:
"A360DF687F69642CD40B89E51587AE4C3C3E018C"
(3)取s1的前N个字符(十六进制字母必须采用大写形式,下同),重复2遍后
计算该串的SHA-1字串,记为s2。
"A360DA360D"的SHA-1为:
"EBA0E37BE9B7BD512AF60ABED40D9DA9EEACD579"
(4)取s2的前N个字符,重复4遍后计算该串的SHA-1字串,记为s3。
"EBA0EEBA0EEBA0EEBA0E"的SHA-1为:
"C4D6F82E3150BCB1C2AEAD7A7D47BAC1DF10C8C3"
(5)取s3的前N个字符,重复8遍后计算该串的SHA-1字串,记为s4。
"C4D6F...C4D6F"(共8个C4D6F)的SHA-1为:
"86DC3621A675D80043C6675A19E29FD9A03242AE"
(6)取s4的前N个字符,重复16遍后计算该串的SHA-1字串,记为s5。
"86DC3...86DC3"(共16个86DC3)的SHA-1为:
"C965332E4655754B5F089182C9E49DD9C0E1993B"
(7)取s1的第1到8个字符"A360DF68"、s2的第9到16个字符"E9B7BD51"、s3的
第10到17个字符"150BCB1C"、s4的第25到32个字符"19E29FD9"、s5的第
26到33个字符"9E49DD9C",并将第3、5次取的子串反序,即"C1BCB051"
和"C9DD94E9",最后用连接符"-"依次组合而得序列号:
A360DF68-E9B7BD51-C1BCB051-19E29FD9-C9DD94E9
再考虑一种可能,SHA-1只有40个字符,万一N大于40会发生什么事呢?然而在
Name框中尝试一下就可以知道只能输32个字符,原来在407828处有调用:
==============================
00407818 . 6A 00 push 0
0040781A . 6A 20 push 20
0040781C . 68 C5000000 push 0C5 ; EM_LIMITTEXT
00407821 . 68 F1030000 push 3F1 ; 用户名输入框ID
00407826 . 8BCE mov ecx, esi
00407828 . E8 9B140000 call <jmp.&MFC42.#5802_CWnd::SendDlgI>
==============================
正是这里限定了用户名最多为32个字符,所以没有上述可能性。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课