这个CrackMe不是很难,但花去我不少时间,贴在这里和大家分享~
【文章标题】: hysteriaCRKME 破解+算法
【文章作者】: megadeath
【作者邮箱】: massacre@163.com
【作者QQ号】: 84227716
【软件名称】: hysteria_CRKME
【软件大小】: 266,240
【下载地址】: http://crackmes.de/users/haiklr/hysteria_crackme/
【加壳方式】: 无
【保护方式】: 无
【编写语言】: asm
【使用工具】: OllyICE
【操作平台】: WinXPsp2
【软件介绍】: 用户名+Keyfile
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
拿到CrackMe后,执行后只有一个文本框,在里面输入任意的用户名,然后点击Check,在文本框中提示Try harder !,显然注册失败,OK,PEiD检查没有壳没有保护,直接用OD加载,来到程序的入口点:
0040142F >/$ 6A 00 push 0 ; /pModule = NULL <--程序开始入口
00401431 |. E8 86080000 call <jmp.&kernel32.GetModuleHandleA> ; \GetModuleHandleA
00401436 |. A3 9C634000 mov dword ptr [40639C], eax
0040143B |. 6A 03 push 3 ; /RsrcName = 3.
0040143D |. 50 push eax ; |hInst
0040143E |. E8 37080000 call <jmp.&user32.LoadIconA> ; \LoadIconA
00401443 |. A3 A0634000 mov dword ptr [4063A0], eax
我们直接用串式参考搜索,发现有“congratz ! send me your keygen”字样,双击来到程序代码中:
00401C0F |. 68 A5614000 push 004061A5 ; /congratz ! send me your keygen
00401C14 |. 6A 08 push 8 ; |ControlID = 8
00401C16 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
00401C19 |. E8 7A000000 call <jmp.&user32.SetDlgItemTextA> ; \SetDlgItemTextA
往上搜寻,这个函数很长一直到这个函数的开头部分:
00401925 /$ 55 push ebp
00401926 |. 8BEC mov ebp, esp
00401928 |. 57 push edi
00401929 |. 56 push esi
0040192A |. 53 push ebx
0040192B |. 90 nop
00401925 在两个地方被调用,00401736 和 00401698 ,在这两个地方下断点,然后F9运行,这时OD停在了00401736处,先不要进入,再F9运行,这时程序完全启动起来,显示出界面,如果这个时候点击 Check后,OD停在了00401698 处。嗯,我们可以估计到在程序启动的时候加载了一次,以后每次点击Chcek按钮就都会调用一次00401925,我们再看一下在00401698 附近的代码:
0040166D . 837D 10 05 cmp dword ptr [ebp+10], 5
00401671 . 75 1C jnz short 0040168F
00401673 . 6A 00 push 0 ; /lParam = NULL
00401675 . 68 91174000 push 00401791 ; |DlgProc = Hysteria.00401791
0040167A . FF75 08 push dword ptr [ebp+8] ; |hOwner
0040167D . 6A 02 push 2 ; |pTemplate = 2
0040167F . FF35 9C634000 push dword ptr [40639C] ; |hInst = 00400000
00401685 . E8 BA050000 call <jmp.&user32.DialogBoxParamA> ; \DialogBoxParamA
0040168A . E9 C5000000 jmp 00401754
0040168F > 837D 10 06 cmp dword ptr [ebp+10], 6
00401693 . 75 0D jnz short 004016A2
00401695 . FF75 08 push dword ptr [ebp+8]
00401698 . E8 88020000 call 00401925
0040169D . E9 B2000000 jmp 00401754
004016A2 > 837D 10 07 cmp dword ptr [ebp+10], 7
004016A6 . 0F85 A8000000 jnz 00401754
004016AC . 6A 00 push 0 ; /lParam = 0
004016AE . 6A 00 push 0 ; |wParam = 0
004016B0 . 6A 10 push 10 ; |Message = WM_CLOSE
004016B2 . FF75 08 push dword ptr [ebp+8] ; |hWnd
004016B5 . E8 D2050000 call <jmp.&user32.SendMessageA> ; \SendMessageA
004016BA . E9 95000000 jmp 00401754
004016BF > 817D 0C 10010>cmp dword ptr [ebp+C], 110
004016C6 . 75 75 jnz short 0040173D
注意0040166D 、0040168F、004016A2这几行,在和5,6,7来比较,到这里能够看出一些端倪,其实这个是Windows回调函数中的对控件消息处理的 switch语句,因此在00401698 调用也就是点击Check按钮的一个响应。我们在00401698 处下断点,在文本框中输入用户名,然后点击check按钮,这时OD拦截,进入00401698 函数,我们一点一点往下看:
00401925 /$ 55 push ebp
00401926 |. 8BEC mov ebp, esp
00401928 |. 57 push edi
00401929 |. 56 push esi
0040192A |. 53 push ebx
0040192B |. 90 nop
0040192C |. 90 nop
0040192D |. 90 nop
0040192E |. 90 nop
0040192F |. 90 nop
00401930 |. 90 nop
00401931 |. 90 nop
00401932 |. 90 nop
00401933 |. 6A 1E push 1E ; /Count = 1E (30.)
00401935 |. 68 50624000 push 00406250 ; |Buffer = Hysteria.00406250
0040193A |. 6A 08 push 8 ; |ControlID = 8
0040193C |. FF75 08 push dword ptr [ebp+8] ; |hWnd
0040193F |. E8 24030000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
00401944 |. 83F8 04 cmp eax, 4
00401947 |. 0F8C D3020000 jl 00401C20 ; 小于4就跳到错误处理
0040194D |. 83F8 18 cmp eax, 18
00401950 |. 0F8F CA020000 jg 00401C20 ; 大于0x18就跳到错误处理
00401956 |. 90 nop
00401957 |. 90 nop
00401958 |. 90 nop
00401959 |. 90 nop
0040195A |. 90 nop
0040195B |. 90 nop
0040195C |. 90 nop
0040195D |. A3 88634000 mov dword ptr [406388], eax
00401962 |. FF35 88634000 push dword ptr [406388] ; 将字符串长度压栈
00401968 |. E8 93F6FFFF call 00401000 ; 计算用户名的CRC32值
0040196D |. A3 69624000 mov dword ptr [406269], eax ; 将用户名的CRC32值放到[00426269]中
00401972 |. A0 50624000 mov al, byte ptr [406250] ; 取得用户名第一个字符放在CRC32的低8位中其余位不变
00401977 |. 50 push eax ; /<%x>
00401978 |. 68 00604000 push 00406000 ; |%x 拼接的字符串放在[00406000]中
0040197D |. 68 03604000 push 00406003 ; |s = Hysteria.00406003
00401982 |. E8 B1020000 call <jmp.&user32.wsprintfA> ; \wsprintfA
00401987 |. 83C4 0C add esp, 0C
0040198A |. 6A 00 push 0 ; /hTemplateFile = NULL
0040198C |. 6A 00 push 0 ; |Attributes = 0
0040198E |. 6A 03 push 3 ; |Mode = OPEN_EXISTING
00401990 |. 6A 00 push 0 ; |pSecurity = NULL
00401992 |. 6A 01 push 1 ; |ShareMode = FILE_SHARE_READ
00401994 |. 68 00000080 push 80000000 ; |Access = GENERIC_READ
00401999 |. 68 03604000 push 00406003 ; |FileName = "7ff2506d"
0040199E |. E8 07030000 call <jmp.&kernel32.CreateFileA> ; \CreateFileA
004019A3 |. 83F8 FF cmp eax, -1 ; 判断文件是否正常打开了
004019A6 |. 0F84 74020000 je 00401C20 ; 如果没有打开文件就跳转到00401c20处,即失败
004019AC |. A3 98634000 mov dword ptr [406398], eax
004019B1 |. 6A 00 push 0 ; /pOverlapped = NULL
004019B3 |. 68 94634000 push 00406394 ; |pBytesRead = Hysteria.00406394
004019B8 |. 68 FF000000 push 0FF ; |BytesToRead = FF (255.)
004019BD |. 68 6D624000 push 0040626D ; |Buffer = Hysteria.0040626D
004019C2 |. FF35 98634000 push dword ptr [406398] ; |hFile = FFFFFFFF
004019C8 |. E8 0D030000 call <jmp.&kernel32.ReadFile> ; \ReadFile
函数很长我们先看到这里,在00401968 处调用了在00401000的函数,这个函数调用其实是计算用户名的CRC32值,我们稍后再详细介绍这个函数,计算好的CRC32值在eax中返回,放到了[00426269]中,一会儿我们还要用到它,随后把用户名的第一个字符放在了CRC32值的低8位中,用这个数值的字符串作为文件名读取文件,这时我们知道这个CrackMe的注册需要Keyfile,并且Keyfile的名称我们已经能够构造出来了。读取文件的内容我们还不知道,明确知道的是文件读取的内容放在了0040626D,并且最长为0xFF个字符。我们停下OD,构造一个文件在CrackMe的目录下,填入足够长的测试数据,然后重新来过一次,断点定在004019CD 处。
004019CD |. 33D2 xor edx, edx
004019CF |. 33F6 xor esi, esi
004019D1 |. BB 01000000 mov ebx, 1
004019D6 |. A1 6D624000 mov eax, dword ptr [40626D] ; Keyfile中的字符串地址
004019DB |. A3 6C634000 mov dword ptr [40636C], eax ; 将Keyfile中的前0-3字符拷贝到[0040636c]中
004019E0 |. 83F8 00 cmp eax, 0 ; 是否没有字符串
004019E3 |. 0F84 37020000 je 00401C20 ; 如果为0的话就跳到错误提示
004019E9 |. A1 71624000 mov eax, dword ptr [406271]
004019EE |. A3 70634000 mov dword ptr [406370], eax
004019F3 |. 83F8 00 cmp eax, 0
004019F6 |. 0F84 24020000 je 00401C20
004019FC |. A1 75624000 mov eax, dword ptr [406275]
00401A01 |. A3 74634000 mov dword ptr [406374], eax
00401A06 |. 83F8 00 cmp eax, 0
00401A09 |. 0F84 11020000 je 00401C20
00401A0F |. A1 79624000 mov eax, dword ptr [406279]
00401A14 |. A3 78634000 mov dword ptr [406378], eax
00401A19 |. 83F8 00 cmp eax, 0
00401A1C |. 0F84 FE010000 je 00401C20
00401A22 |. A1 7D624000 mov eax, dword ptr [40627D]
00401A27 |. A3 7C634000 mov dword ptr [40637C], eax
00401A2C |. 83F8 00 cmp eax, 0
00401A2F |. 0F84 EB010000 je 00401C20
00401A35 |. A1 81624000 mov eax, dword ptr [406281]
00401A3A |. A3 80634000 mov dword ptr [406380], eax
00401A3F |. 83F8 00 cmp eax, 0
00401A42 |. 0F84 D8010000 je 00401C20 ; 从KeyFile字符串中拷贝了前24个字符
这一段是将Keyfile中的字符拷贝24个放到了 [0040636C]中,拷贝了24个字符,因此Keyfile中最多出现的注册码也就是24个字符。我们把这个24个字符分为4个字符一组,共6组,如果每组的4个字符都转换为数值的话一组就是一个32位数,为什么要这么做,这个其实是到后面才知道的,这里先写出来,为了后面看得更清楚。记住这6组数值是从[0040636C]开始的,往下看:
00401A48 |. 90 nop
00401A49 |. 90 nop
00401A4A |. 90 nop
00401A4B |. A1 6C634000 mov eax, dword ptr [40636C]
00401A50 |. 3B05 74634000 cmp eax, dword ptr [406374]
00401A56 |. 75 11 jnz short 00401A69 ; 第0组Key值和第2组Key值不能相同
00401A58 |. A1 70634000 mov eax, dword ptr [406370]
00401A5D |. 3B05 78634000 cmp eax, dword ptr [406378]
00401A63 |. 0F84 B7010000 je 00401C20 ; 如果第0组Key值和第2组Key值相同了,那么第1组Key值和第3组Key值不能相同
00401A69 |> A1 6C634000 mov eax, dword ptr [40636C]
00401A6E |. 3B05 7C634000 cmp eax, dword ptr [40637C]
00401A74 |. 75 11 jnz short 00401A87 ; 第0组Key值和第4组Key值不能相同
00401A76 |. A1 70634000 mov eax, dword ptr [406370]
00401A7B |. 3B05 80634000 cmp eax, dword ptr [406380]
00401A81 |. 0F84 99010000 je 00401C20 ; 如果第0组Key值和第4组Key值相同了,那么第1组Key值和第5组Key值不能相同
00401A87 |> A1 74634000 mov eax, dword ptr [406374]
00401A8C |. 3B05 7C634000 cmp eax, dword ptr [40637C]
00401A92 |. 75 11 jnz short 00401AA5 ; 第2组Key值和第4组Key值不能相同
00401A94 |. A1 78634000 mov eax, dword ptr [406378]
00401A99 |. 3B05 80634000 cmp eax, dword ptr [406380]
00401A9F |. 0F84 7B010000 je 00401C20 ; 如果第2组Key值和第4组Key值相同了,那么第3组Key值和第5组Key值不能相同
00401AA5 |> A1 70634000 mov eax, dword ptr [406370]
00401AAA |. 3B05 78634000 cmp eax, dword ptr [406378]
00401AB0 |. 75 11 jnz short 00401AC3 ; 第1组Key值和第3组Key值不能相同
00401AB2 |. A1 6C634000 mov eax, dword ptr [40636C]
00401AB7 |. 3B05 74634000 cmp eax, dword ptr [406374]
00401ABD |. 0F84 5D010000 je 00401C20 ; 如果第1组Key值和第3组Key值相同了,那么第0组Key值和第2组Key值不能相同
00401AC3 |> A1 70634000 mov eax, dword ptr [406370]
00401AC8 |. 3B05 80634000 cmp eax, dword ptr [406380]
00401ACE |. 75 11 jnz short 00401AE1 ; 第1组Key值和第5组Key值不能相同
00401AD0 |. A1 6C634000 mov eax, dword ptr [40636C]
00401AD5 |. 3B05 7C634000 cmp eax, dword ptr [40637C]
00401ADB |. 0F84 3F010000 je 00401C20 ; 如果第1组Key值和第5组Key值相同了,那么第0组Key值和第4组Key值不能相同
00401AE1 |> A1 78634000 mov eax, dword ptr [406378]
00401AE6 |. 3B05 80634000 cmp eax, dword ptr [406380]
00401AEC |. 75 11 jnz short 00401AFF ; 第3组Key值和第5组Key值不能相同
00401AEE |. A1 74634000 mov eax, dword ptr [406374]
00401AF3 |. 3B05 7C634000 cmp eax, dword ptr [40637C]
00401AF9 |. 0F84 21010000 je 00401C20 ; 如果第3组Key值和第5组Key值相同了,那么第2组Key值和第4组Key值不能相同
这一段是比较复杂的一段,主要是Keyfile取得的6组数值的规则,花了好大一阵功夫写出来,再往下
00401AFF |> \A1 69624000 mov eax, dword ptr [406269] ; 用户名的CRC32值放到eax中
00401B04 |. B9 63000000 mov ecx, 63
00401B09 |. F7F9 idiv ecx ; 除以0x63以求余数
00401B0B |. 8915 8C634000 mov dword ptr [40638C], edx ; 余数放到[0040638c]中,记作MOD
00401B11 |. 81F2 07200000 xor edx, 2007
00401B17 |. 8915 90634000 mov dword ptr [406390], edx ; 异或0x2007后放到[00406390]中,记作XOR
没有什么好说的,继续
00401B1D |. 90 nop
00401B1E |. 90 nop
00401B1F |. 8B0D 8C634000 mov ecx, dword ptr [40638C] ; 刚才的MOD放到ecx中
00401B25 |. A1 6C634000 mov eax, dword ptr [40636C] ; 第1组Key值
00401B2A |. 2BC1 sub eax, ecx ; 用第1组Key值减去MOD,得到的值放到eax中
00401B2C |. 8B1D 70634000 mov ebx, dword ptr [406370] ; 第2组Key值
00401B32 |. 8B15 90634000 mov edx, dword ptr [406390] ; XOR赋值给edx
00401B38 |. 2BDA sub ebx, edx ; 用第二组值减去XOR,结果放到ebx中
00401B3A |. 0FAFC3 imul eax, ebx
00401B3D |. A3 84634000 mov dword ptr [406384], eax ; 把结果暂存到[00406384]中
00401B42 |. A1 6C634000 mov eax, dword ptr [40636C] ; 第0组Key值
00401B47 |. 8B0D 7C634000 mov ecx, dword ptr [40637C] ; 第4组Key值
00401B4D |. 2BC1 sub eax, ecx ; 第0组Key值 减去 第4组Key值,结果放到eax中
00401B4F |. 8B1D 70634000 mov ebx, dword ptr [406370] ; 第1组Key值
00401B55 |. 8B15 80634000 mov edx, dword ptr [406380] ; 第5组Key值
00401B5B |. 2BDA sub ebx, edx ; 第1组Key值 减去 第5组Key值,结果放到ebx中
00401B5D |. 0FAFC3 imul eax, ebx
00401B60 |. 0305 84634000 add eax, dword ptr [406384] ; 和刚才的结果相加
00401B66 |. 85C0 test eax, eax
00401B68 |. 0F85 B2000000 jnz 00401C20 ; 如果之和不为0则跳到错误的地方
到这里我们得到第一个公式,即:
(key[0] - MOD)*(key[1] - XOR) + (key[0] - key[4])*(key[1] - key[5]) = 0
往下看
00401B6E |. 8B0D 8C634000 mov ecx, dword ptr [40638C] ; MOD赋值给ecx
00401B74 |. A1 74634000 mov eax, dword ptr [406374] ; 第2组Key值
00401B79 |. 2BC1 sub eax, ecx ; 第2组Key值 减去 MOD,结果放到eax中
00401B7B |. 8B1D 78634000 mov ebx, dword ptr [406378] ; 第3组Key值
00401B81 |. 8B15 90634000 mov edx, dword ptr [406390] ; XOR赋值给edx
00401B87 |. 2BDA sub ebx, edx ; 第3组Key值 减去 XOR,结果放到ebx中
00401B89 |. 0FAFC3 imul eax, ebx
00401B8C |. A3 84634000 mov dword ptr [406384], eax ; 把结果暂存到[00406384]中
00401B91 |. A1 74634000 mov eax, dword ptr [406374] ; 第2组Key值
00401B96 |. 8B0D 7C634000 mov ecx, dword ptr [40637C] ; 第4组Key值
00401B9C |. 2BC1 sub eax, ecx ; 第2组Key值 减去 第4组Key值,结果放到eax中
00401B9E |. 8B1D 78634000 mov ebx, dword ptr [406378] ; 第3组Key值
00401BA4 |. 8B15 80634000 mov edx, dword ptr [406380] ; 第5组Key值
00401BAA |. 2BDA sub ebx, edx ; 第3组Key值 减去 第5组Key值,结果放到ebx中
00401BAC |. 0FAFC3 imul eax, ebx
00401BAF |. 0305 84634000 add eax, dword ptr [406384] ; 和刚才的结果相加
00401BB5 |. 85C0 test eax, eax
00401BB7 |. 75 67 jnz short 00401C20 ; 如果之和不为0则跳到错误的地方
到这里我们得到第二个公式,即:
0
顶一下0
埋一下引用地址:



