首页
社区
课程
招聘
[分享]hysteriaCRKME 破解+算法
发表于: 2007-11-12 13:00 5737

[分享]hysteriaCRKME 破解+算法

2007-11-12 13:00
5737

这个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则跳到错误的地方

到这里我们得到第二个公式,即:

(key[2] - MOD)*(key[3] - XOR) +(key[2] - key[4])*(key[3] - key[5]) = 0;

往下:

00401BB9  |.  8B0D 8C634000 mov     ecx, dword ptr [40638C]          ;  MOD赋值给ecx
00401BBF  |.  A1 6C634000   mov     eax, dword ptr [40636C]          ;  第0组Key值
00401BC4  |.  2BC8          sub     ecx, eax                         ;  MOD 减去 第0组Key值,结果放到ecx中
00401BC6  |.  0FAFC9        imul    ecx, ecx                         ;  结果乘平方
00401BC9  |.  8B15 90634000 mov     edx, dword ptr [406390]          ;  XOR赋值给edx
00401BCF  |.  8B1D 70634000 mov     ebx, dword ptr [406370]          ;  第1组Key值
00401BD5  |.  2BD3          sub     edx, ebx                         ;  XOR 减去 第1组Key值,结果放到edx中
00401BD7  |.  0FAFD2        imul    edx, edx                         ;  结果乘平方
00401BDA  |.  03CA          add     ecx, edx
00401BDC  |.  890D 84634000 mov     dword ptr [406384], ecx          ;  得到的结果暂存
00401BE2  |.  8B0D 8C634000 mov     ecx, dword ptr [40638C]          ;  MOD赋值给ecx
00401BE8  |.  A1 74634000   mov     eax, dword ptr [406374]          ;  第2组Key值
00401BED  |.  2BC8          sub     ecx, eax                         ;  MOD 减去 第2组Key值,结果放到ecx中
00401BEF  |.  0FAFC9        imul    ecx, ecx                         ;  结果乘平方
00401BF2  |.  8B15 90634000 mov     edx, dword ptr [406390]          ;  XOR赋值给edx
00401BF8  |.  8B1D 78634000 mov     ebx, dword ptr [406378]          ;  第3组Key值
00401BFE  |.  2BD3          sub     edx, ebx                         ;  XOR 减去 第3组Key值,结果放到edx中
00401C00  |.  0FAFD2        imul    edx, edx                         ;  结果乘平方
00401C03  |.  03CA          add     ecx, edx
00401C05  |.  8B1D 84634000 mov     ebx, dword ptr [406384]
00401C0B  |.  3BCB          cmp     ecx, ebx
00401C0D  |.  75 11         jnz     short 00401C20                   ;  如果两个表达式不相等就跳到错误的地方
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
00401C1E  |.  EB 11         jmp     short 00401C31
00401C20  |>  68 98614000   push    00406198                         ; /try harder !
00401C25  |.  6A 08         push    8                                ; |ControlID = 8
00401C27  |.  FF75 08       push    dword ptr [ebp+8]                ; |hWnd
00401C2A  |.  E8 69000000   call    <jmp.&user32.SetDlgItemTextA>    ; \SetDlgItemTextA
00401C2F  |.  EB 00         jmp     short 00401C31
00401C31  |>  5B            pop     ebx
00401C32  |.  5E            pop     esi
00401C33  |.  5F            pop     edi
00401C34  |.  C9            leave
00401C35  \.  C2 0400       retn    4

我们可以看到胜利的地方了,这段也能得到一个公式:

(MOD - key[0])*(MOD - key[0]) + (XOR - key[1])*(XOR - key[1]) = (MOD - key[2])*(MOD - key[2]) + (XOR - key[3])*(XOR - key[3])

整理一下,我们需要解个方程组

(key[0] - MOD)*(key[1] - XOR) + (key[0] - key[4])*(key[1] - key[5]) = 0                                                                 (1)
(key[2] - MOD)*(key[3] - XOR) + (key[2] - key[4])*(key[3] - key[5]) = 0                                                                 (2)
(MOD - key[0])*(MOD - key[0]) + (XOR - key[1])*(XOR - key[1]) = (MOD - key[2])*(MOD - key[2]) + (XOR - key[3])*(XOR - key[3])           (3)

初看上去这个方程组很难解开,实际上可以用一些巧的方法,我们如果让

key[0]=key[2]=key[4]=MOD 且 key[1]=key[3]=key[5]=XOR

的话,那么方程组就解开了,哦,不要高兴太早,在00401A4B处还有个规则,答案也不止一个,依照规则我们稍微改变一下就可以满足条件,这里列举了两组满足条件的解:

key[0] = MOD - 1  key[1] = XOR
key[2] = MOD      key[3] = XOR - 1
key[4] = MOD - 1  key[5] = XOR - 1



key[0] = MOD      key[1] = XOR - 1
key[2] = MOD + 1  key[3] = XOR
key[4] = MOD + 1  key[5] = XOR - 1

还有其他多个解,不过这里懒得计算了。

另外在00401968  处有个调用用来计算用户名的CRC32值,把这个函数调用再简单说一下

00401000  /$  55            push    ebp
00401001  |.  8BEC          mov     ebp, esp
00401003  |.  56            push    esi
00401004  |.  51            push    ecx
00401005  |.  52            push    edx
00401006  |.  8D35 50624000 lea     esi, dword ptr [406250]          ;  存储用户名地址
0040100C  |.  33D2          xor     edx, edx                         ;  edx清零
0040100E  |.  83C8 FF       or      eax, FFFFFFFF                    ;  eax初始化0xFFFFFFFF
00401011  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]           ;  ecx循环计数
00401014  |>  8A16          /mov     dl, byte ptr [esi]              ;  取出用户名的每个字符
00401016  |.  32D0          |xor     dl, al                          ;  字符和al相异或
00401018  |.  C1E8 08       |shr     eax, 8                          ;  eax右移8位
0040101B  |.  330495 2F1040>|xor     eax, dword ptr [edx*4+40102F]   ;  查表得值再和eax异或
00401022  |.  46            |inc     esi
00401023  |.  49            |dec     ecx
00401024  |.^ 75 EE         \jnz     short 00401014                  ;  循环
00401026  |.  F7D0          not     eax                              ;  eax取反
00401028  |.  5A            pop     edx
00401029  |.  59            pop     ecx
0040102A  |.  5E            pop     esi
0040102B  |.  C9            leave
0040102C  \.  C2 0400       retn    4

一上来看见初始化0xFFFFFFFF还有最后对eax取反,在单步循环之中每次查询0040102F处的数组来计算每个字符,一下就知道这个是在计算CRC,0xFFFFFFFF和结果求反这两步太明显了。

算法整理出来,写出注册机:

#include <iostream>
#include <string>
using namespace std;

int main(int, char **)
{
        unsigned CRC32[256] = {0};
        unsigned uiResult = 0;

        for (unsigned ii = 0; ii < 256; ++ ii)
        {
                uiResult = ii;
                for (unsigned jj = 0; jj < 8; ++ jj)
                {
                        if (uiResult & 0x01)
                                uiResult = (uiResult >> 1)  ^ 0xEDB88320;
                        else
                                uiResult >>= 1;
                }
                CRC32[ii] = uiResult;
        }

        string strName;
        do
        {
                cout << "please input your name:" << endl;
                cin >> strName ;

                if ((4 <= strName.length()) && (0x18 >= strName.length()))
                {
                        goto KeyGen;
                }
               
        } while(1);

KeyGen:

        unsigned uiCRC = 0xFFFFFFFF;
        unsigned uiNameLength = strName.length();
        for (unsigned ii = 0; ii < uiNameLength; ++ ii)
        {
                uiResult = uiCRC;
                uiResult = (uiResult & 0xFF)^((unsigned)strName[ii]);
                uiCRC >>= 8;
                uiCRC ^= (CRC32[uiResult]);
        }

        uiCRC = ~uiCRC;

        unsigned uiMOD = uiCRC % 0x63;
        unsigned uiXOR = uiMOD ^ 0x2007;

        uiCRC &= 0xFFFFFF00;
        uiCRC += (unsigned)strName[0];

        char buffer[9] = {0};
        sprintf(buffer, "%.8X", uiCRC);

        unsigned Key[6] ={0};

        //这里随意取一组
        Key[0] = uiMOD - 1; Key[1] = uiXOR ;
        Key[2] = uiMOD    ; Key[3] = uiXOR - 1;
        Key[4] = uiMOD - 1; Key[5] = uiXOR - 1;

//         Key[0] = uiMOD    ; Key[1] = uiXOR - 1;
//         Key[2] = uiMOD + 1; Key[3] = uiXOR    ;
//         Key[4] = uiMOD + 1; Key[5] = uiXOR - 1;

        FILE * fp = NULL;
        fp = fopen(buffer, "wb");
        if (NULL == fp)
                return 0;
        fwrite(Key, sizeof(unsigned), 6, fp);
        fclose(fp);
        fp = NULL;

        cout << "KeyFile generated." << endl;

        cout << "all right!" << endl;
        cin.get();
        return 0;
}
  
--------------------------------------------------------------------------------
【经验总结】

        这个CrackMe不是明码比较而且也用了一点点数学方面的知识,虽然仍然能够通过爆破解决,但这个CrackMe是让我比较挠头的了,用了不少时间读程序,在计算公式的时候还把方程式(2)计算错了,费了不少时间却原地踏步,最后整理思路重新来过一遍才整理出来。
        事实证明在大脑一片混乱事情一片狼藉的时候打个小盹呼吸点新鲜空气对自己是非常有帮助的。

光棍节快乐!  
--------------------------------------------------------------------------------
【版权声明】: 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年11月11日 20:57:16


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (3)
雪    币: 47147
活跃值: (20450)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
好文章,麻烦megadeath将CrackMe转份本地,放到首帖。怕时间长了,原链接失效。
2007-11-12 13:02
0
雪    币: 1844
活跃值: (35)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
好详细,需然我不玩了,但还是保存起来,顶
2007-11-12 14:02
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
“事实证明在大脑一片混乱事情一片狼藉的时候打个小盹呼吸点新鲜空气对自己是非常有帮助的。”
事实证明:值得学习。。。。。
  
2007-11-12 15:04
0
游客
登录 | 注册 方可回帖
返回
//