hflywolf 发表于 2009-9-8 09:03:56

PEDIYCrackme竞赛2009-(Atom队-CrackMe)爆破分析

【标题】:PEDIYCrackme竞赛2009-(Atom队-CrackMe)爆破分析
【作者】:hflywolf
【下载】:http://bbs.pediy.com/showthread.php?t=97183
【工具】:SOD+0D
【声明】:纯属技术学习,水平有限,如有错误之处,烦请指出!

    CM 有anti,SOD直接无视(膜拜ing 海风大大)

第一部份:权

(一)、关键代码如下:


00401FF0/.55            push    ebp                                       ;
................................................................................................................
00402068|.E8 930B0000   call    00402C00                                    ; 取当前时间的运算值
0040206D|.8945 E0       mov   dword ptr , eax                     ; 保存运算值(作为校验代码运行时间的初始值)
00402070|.33C0          xor   eax, eax


取name和Serial


0040208F|.6A 01         push    1                                           ;bSaveAndValidate = TRUE(获取数据)
0040209C|.E8 1D180000   call    <jmp.&MFC42.#CWnd::UpdateData_6334>         ;取name和Serial有关数据(长度和字符)


校验name和Serial的合法性


004020A1|.8B4B 60       mov   ecx, dword ptr
004020A4|.8B41 F8       mov   eax, dword ptr
004020A7      83F8 06       cmp   eax, 6                                    ;这里改cmp eax,0
004020AA|.0F8C 49020000 jl      004022F9                                    ;name长度要大于等于6
004020B0|.83F8 20       cmp   eax, 20
004020B3|.0F8F 40020000 jg      004022F9                                    ;name长度要小于等于32(0x20)

................................................................................................................

004020B9|.8B53 64       mov   edx, dword ptr
004020BC|.8B52 F8       mov   edx, dword ptr
004020BF      83FA 06       cmp   edx, 6                                    ;这里改cmp edx,0
004020C2|.0F8C 31020000 jl      004022F9                                    ;Serial长度要大于等于6
004020C8|.81FA A0000000 cmp   edx, 0A0                                    
004020CE|.0F8F 25020000 jg      004022F9                                    ;Serial长度要大于等于160(0xA0)

................................................................................................................

004020D4|.8B35 E8414000 mov   esi, dword ptr [<&MSVCRT.strncpy>]          ;msvcrt.strncpy
004020DA|.50            push    eax                                       ; /maxlen
004020DB|.51            push    ecx                                       ; |src
004020DC|.68 34534000   push    00405334                                    ; |dest = CrackMe1.00405334
004020E1|.FFD6          call    esi                                       ; \strncpy
                                                                              ; 将name字符串复制到405335地址中去

................................................................................................................

004020E3|.8B43 64       mov   eax, dword ptr
004020E6|.8B48 F8       mov   ecx, dword ptr
004020E9|.51            push    ecx
004020EA|.50            push    eax
004020EB|.68 68534000   push    00405368
004020F0|.FFD6          call    esi                                       ; 将Serial字符串复制到405368地址中去

................................................................................................................

0040210A|>8A82 34534000 /mov   al, byte ptr
00402110|.3C 30         |cmp   al, 30
00402112|.7C 04         |jl      short 00402118
00402114|.3C 39         |cmp   al, 39
00402116|.7E 10         |jle   short 00402128
00402118|>3C 61         |cmp   al, 61
0040211A|.7C 04         |jl      short 00402120
0040211C|.3C 7A         |cmp   al, 7A
0040211E|.7E 08         |jle   short 00402128
00402120|>3C 41         |cmp   al, 41
00402122|.7C 1A         |jl      short 0040213E
00402124|.3C 5A         |cmp   al, 5A
00402126|.7F 16         |jg      short 0040213E
00402128|>BF 34534000   |mov   edi, 00405334
0040212D|.83C9 FF       |or      ecx, FFFFFFFF
00402130|.33C0          |xor   eax, eax
00402132|.42            |inc   edx
00402133|.F2:AE         |repne   scas byte ptr es:
00402135|.F7D1          |not   ecx
00402137|.49            |dec   ecx
00402138|.3BD1          |cmp   edx, ecx
0040213A|.^ 75 CE         \jnz   short 0040210A                           ;校验name是否由0-9,a-z,A-Z间的字符组成的
0040213C|.EB 05         jmp   short 00402143                              
0040213E      BE 01000000   mov   esi, 1                                    ;这里改成mov esi,0(name含不合法字符的标志)

................................................................................................................

00402156|>8A82 68534000 /mov   al, byte ptr
0040215C|.3C 30         |cmp   al, 30
0040215E|.7C 04         |jl      short 00402164
00402160|.3C 39         |cmp   al, 39
00402162|.7E 10         |jle   short 00402174
00402164|>3C 61         |cmp   al, 61
00402166|.7C 04         |jl      short 0040216C
00402168|.3C 7A         |cmp   al, 7A
0040216A|.7E 08         |jle   short 00402174
0040216C|>3C 41         |cmp   al, 41
0040216E|.7C 1D         |jl      short 0040218D
00402170|.3C 5A         |cmp   al, 5A
00402172|.7F 19         |jg      short 0040218D
00402174|>BF 68534000   |mov   edi, 00405368
00402179|.83C9 FF       |or      ecx, FFFFFFFF
0040217C|.33C0          |xor   eax, eax
0040217E|.42            |inc   edx
0040217F|.F2:AE         |repne   scas byte ptr es:
00402181|.F7D1          |not   ecx
00402183|.49            |dec   ecx
00402184|.3BD1          |cmp   edx, ecx
00402186|.^ 75 CE         \jnz   short 00402156                           ;校验Serial是否由0-9,a-z,A-Z间的字符组成的
00402188|>83FE 01       cmp   esi, 1                                    ;判断name是否含不合法字符
0040218B|.75 0B         jnz   short 00402198                              ;name合法跳
0040218D|>8B45 F0       mov   eax, dword ptr
00402190|.6A 00         push    0
00402192|.50            push    eax
00402193|.E9 64010000   jmp   004022FC                                    ;name不合法跳


校验代码运行时间(代码中有多处校验,调用的都是同一个CALL 00402C00)


00402198|>B9 58544000   mov   ecx, 00405458
0040219D|.E8 5E0A0000   call    00402C00                                    ;取当前时间的运算值
004021A2|.8B75 E0       mov   esi, dword ptr                      ;取上面保存的初始时间的运算值
004021A5|.2BC6          sub   eax, esi                                    ;计算两个运算值的差值
004021A7|.83F8 02       cmp   eax, 2                                    ;判断差值是否小于等于2
004021AA|.7E 07         jle   short 004021B3                              ;是就跳走(OD中调试要注意这里必须跳)
004021AC|.8BCB          mov   ecx, ebx
004021AE|.E8 0F160000   call    <jmp.&MFC42.#CDialog::OnCancel_4376>      ;大于2就关闭对话框


Serial长度是否不小于32


004021B3|>BF 68534000   mov   edi, 00405368
004021B8|.83C9 FF       or      ecx, FFFFFFFF
004021BB|.33C0          xor   eax, eax
004021BD|.F2:AE         repne   scas byte ptr es:
004021BF|.F7D1          not   ecx
004021C1|.49            dec   ecx
004021C2      83F9 20       cmp   ecx, 20                                     ;这里改成cmp ecx,0
004021C5|.73 0A         jnb   short 004021D1                              ;Serial长度是否不小于32(0x20),不小于跳走
004021C7|.8B4D F0       mov   ecx, dword ptr
004021CA|.50            push    eax
004021CB|.51            push    ecx
004021CC|.E9 2B010000   jmp   004022FC                                    ;小于跳走


加密name和Serial的字符串


004021D1|>8B53 64       mov   edx, dword ptr
004021D4|.8D4D E4       lea   ecx, dword ptr
004021D7|.8B42 F8       mov   eax, dword ptr
004021DA|.50            push    eax
004021DB|.68 68534000   push    00405368
004021E0|.E8 4B110000   call    00403330                                    ; 这个CALL跟Serial有关(时间关系不看了)
004021E5|.B9 58544000   mov   ecx, 00405458

................................................................................................................

004021FD|>68 BC514000   push    004051BC                                    ; /Arg3 = 004051BC ASCII "Name"
00402202|.6A 20         push    20                                          ; |Arg2 = 00000020
00402204|.68 34534000   push    00405334                                    ; |Arg1 = 00405334
00402209|.8D4D E4       lea   ecx, dword ptr                      ; |
0040220C|.E8 2F0F0000   call    00403140                                    ; \CrackMe1.00403140
                                                                              ; 将name字符串加密
................................................................................................................

00402211|.68 B4514000   push    004051B4                                    ; /Arg3 = 004051B4 ASCII "Serial"
00402216|.6A 20         push    20                                          ; |Arg2 = 00000020
00402218|.68 68534000   push    00405368                                    ; |Arg1 = 00405368
0040221D|.8D4D E4       lea   ecx, dword ptr                      ; |
00402220|.E8 1B0F0000   call    00403140                                    ; \CrackMe1.00403140
                                                                              ; 将Serial字符串加密



校验name和Serial加密后的字符串


00402225|.0FBE05 695340>movsx   eax, byte ptr                       ;EAX=Serial加密后第二个字符的ASCII值
0040222C|.0FBE0D 685340>movsx   ecx, byte ptr                       ;ECX=Serial加密后第一个字符的ASCII值
00402233|.0FBE15 355340>movsx   edx, byte ptr                       ;EDX=name加密后第二个字符的ASCII值
0040223A|.50            push    eax                                       ;保存EAX
0040223B|.51            push    ecx                                       ;保存ECX
0040223C|.0FBE05 345340>movsx   eax, byte ptr                       ;EAX=name加密后第一个字符的ASCII值
00402243|.52            push    edx                                       ;保存EDX
00402244|.50            push    eax                                       ;保存EAX
00402245|.6A 01         push    1                                           ;Arg1 = 00000001
00402247|.8D4D A8       lea   ecx, dword ptr                      ;
0040224A|.E8 21F1FFFF   call    00401370                                    ;关键CALL(时间关系不看了)

................................................................................................................

0040227D|.0FBE0D 6A5340>movsx   ecx, byte ptr                       ;ECX=Serial加密后第三个字符的ASCII值
00402284|.0FBE15 375340>movsx   edx, byte ptr                       ;EDX=name加密后第四个字符的ASCII值
0040228B|.0FBE05 365340>movsx   eax, byte ptr                       ;EAX=name加密后第三个字符的ASCII值
00402292|.6A 00         push    0                                           ;Arg6 = 00000000
00402294|.51            push    ecx                                       ;保存ECX
00402295|.0FBE0D 005340>movsx   ecx, byte ptr                       ;ECX=byte ptr (这个405300地址是个关键)
0040229C|.52            push    edx                                       ;保存EDX
0040229D|.50            push    eax                                       ;保存EAX
0040229E|.51            push    ecx                                       ;保存ECX
0040229F|.6A 02         push    2                                           ;Arg1 = 00000002
004022A1|.8D4D A8       lea   ecx, dword ptr                      ;
004022A4|.E8 17F2FFFF   call    004014C0                                    ;关键CALL(时间关系不看了)




(二)、关键代码如下:


004022F2|.E8 19030000   call    00402610                                    ;进这个CALL



402610 代码如下:(注意的是:程序的"关于"也调用这个CALL)


00402610/$56            push    esi                                       ;
00402611|.8BF1          mov   esi, ecx
00402613|.6A 03         push    3
00402615|.E8 66FEFFFF   call    00402480                                    ;关键CALL(时间关系不看了)返回的eax必须为1
0040261A      83F8 01       cmp   eax, 1                                    ;
0040261D      74 04         je      short 00402623                              ;eax为1跳,所以这里改成jmp 00402623
0040261F|.33C0          xor   eax, eax
00402621|.5E            pop   esi
00402622|.C3            retn
00402623|>8BCE          mov   ecx, esi                                    ;跳到这里来
00402625|.E8 8E120000   call    <jmp.&MFC42.#CDialog::DoModal_2514>         ;调用CDialog::DoModal()函数
0040262A|.5E            pop   esi
0040262B\.C3            retn


4025E0 代码如下:


004025E0   .56            push    esi
004025E1   .8BF1          mov   esi, ecx
004025E3   .6A 07         push    7
004025E5   .E8 66FDFFFF   call    00402350                                    ;进入CALL(校验是否注册失败)返回的exe为1
004025EA   .85C0          test    eax, eax                                    
004025EC   .8BCE          mov   ecx, esi
004025EE      75 09         jnz   short 004025F9                              ;必须跳,改成jmp 004025f9
004025F0   .E8 CD110000   call    <jmp.&MFC42.#CDialog::OnCancel_4>
004025F5   .33C0          xor   eax, eax
004025F7   .5E            pop   esi
004025F8   .C3            retn
004025F9   >E8 B4120000   call    <jmp.&MFC42.#CDialog::OnInitDial>         
004025FE   .B8 01000000   mov   eax, 1
00402603   .5E            pop   esi
00402604   .C3            retn                                                ;



402350 代码如下:(其实是用加密后的name和Serial的某个位字符的ASCII来运算,将得到的值来判断是否显示"注册失败"这个Static Text控件)


00402350      8B4424 04   mov   eax, dword ptr
00402354      56            push    esi
00402355      0FBE90 685340>movsx   edx, byte ptr
0040235C|.0FBEB0 345340>movsx   esi, byte ptr
00402363|.2BF2          sub   esi, edx
00402365|.8D56 02       lea   edx, dword ptr
00402368|.83FA 0A       cmp   edx, 0A
0040236B      0F87 92000000 ja      00402403                                    ;这里直接改成jmp 0040240E
00402371|>FF2495 4C2440>/jmp   dword ptr                    ;以下循环时间关系不分析了
004023FD|.^ 0F86 6EFFFFFF \jbe   00402371
00402403      33C0          xor   eax, eax
00402405|.5E            pop   esi
00402406|.C2 0400       retn    4

................................................................................................................

0040240E|.0FBE05 875340>movsx   eax, byte ptr                       ;上面跳来这里         
00402415|.0FBE15 535340>movsx   edx, byte ptr
0040241C      2BD0          sub   edx, eax                                    ;这里改成xor edx,edx(为啥这么改,后面会说明)
0040241E      52            push    edx                                       ;保存edx,作为CWnd::ShowWindow的nCmdShow参数
0040241F|.68 ED030000   push    3ED                                       ;CWnd::GetDlgItem的控件ID参数(记下0x3ED,后面要用)
00402424|.E8 A1140000   call    <jmp.&MFC42.#CWnd::GetDlgItem_30>         ;调用CWnd::GetDlgItem,取得Static Text控件(ID为Ox3ED)的指针
00402429|.8BC8          mov   ecx, eax
0040242B|.E8 94140000   call    <jmp.&MFC42.#CWnd::ShowWindow_62>         ;调用CWnd::ShowWindow
00402430|.0FBE05 415340>movsx   eax, byte ptr
00402437|.0FBE0D 755340>movsx   ecx, byte ptr
0040243E|.2BC1          sub   eax, ecx
00402440|.5E            pop   esi
00402441|.F7D8          neg   eax
00402443      1BC0          sbb   eax, eax
00402445|.40            inc   eax                                       
00402446\.C2 0400       retn    4                                           ;返回的eax


402630 代码如下:(其实是用加密后的name和Serial的某个位字符的ASCII来运算,将得到的值来判断是否显示"注册成功"这个Static Text控件)


00402630   .6A FF         push    -1
00402632   .68 C83A4000   push    00403AC8                                     ;SE handler installation
00402637   .64:A1 0000000>mov   eax, dword ptr fs:
0040263D   .50            push    eax
0040263E   .64:8925 00000>mov   dword ptr fs:, esp
00402645   .83EC 54       sub   esp, 54
00402648   .56            push    esi
00402649   .8BF1          mov   esi, ecx
0040264B   .56            push    esi
0040264C   .8D4C24 08   lea   ecx, dword ptr
00402650   .E8 4B120000   call    <jmp.&MFC42.#CPaintDC::CPaintDC_>
00402655   .A0 00534000   mov   al, byte ptr                         ;al=(这个405300地址是个关键)
0040265A   .C74424 60 000>mov   dword ptr , 0
00402662   .3C 61         cmp   al, 61                           
00402664      75 29         jnz   short 0040268F                               ;这里不能跳,NOP掉
00402666   .0FBE05 865340>movsx   eax, byte ptr
0040266D   .0FBE0D 525340>movsx   ecx, byte ptr
00402674   .2BC8          sub   ecx, eax
00402676   .F7D9          neg   ecx
00402678      1BC9          sbb   ecx, ecx                                     ;这里改xor ecx,ecx(为啥这么做,后面说明)
0040267A   .41            inc   ecx
0040267B   .51            push    ecx                                          ;保存ecx,作为CWnd::ShowWindow的nCmdShow参数
0040267C   .68 EB030000   push    3EB                                          ;CWnd::GetDlgItem的控件ID参数(记下0x3EB,后面要用)
00402681   .8BCE          mov   ecx, esi
00402683   .E8 42120000   call    <jmp.&MFC42.#CWnd::GetDlgItem_30>            ;调用CWnd::GetDlgItem,取得Static Text控件(ID为Ox3EB)的指针
00402688   .8BC8          mov   ecx, eax
0040268A   .E8 35120000   call    <jmp.&MFC42.#CWnd::ShowWindow_62>            ;调用CWnd::ShowWindow
0040268F   >8D4C24 04   lea   ecx, dword ptr
00402693   .C74424 60 FFF>mov   dword ptr , -1
0040269B   .E8 FA110000   call    <jmp.&MFC42.#CPaintDC::~CPaintDC>
004026A0   .8B4C24 58   mov   ecx, dword ptr
004026A4   .5E            pop   esi
004026A5   .64:890D 00000>mov   dword ptr fs:, ecx
004026AC   .83C4 60       add   esp, 60
004026AF   .C3            retn


这里我们用资源工具PE Explorer打开CM来看看!我们里可以发现CM资源里有ID为100的对话框。

而这个对话框中有两个ID分别为1005(Ox3ED)和1003(Ox3EB)的Static Text控件

如下:


100 DIALOGEX 0, 0, 167, 42, 0
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP
EXSTYLE WS_EX_STATICEDGE
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
FONT 9, "宋体"
{
ICON   128, -1, 67, 11, 20, 20
LTEXT   "CrackMe 1.0 版", -1, 95, 7, 65, 8, SS_NOPREFIX
DEFPUSHBUTTON   "确定", 1, 95, 21, 50, 14, WS_GROUP
LTEXT   "注册失败", 1005, 20, 18, 37, 11, 0, WS_EX_TRANSPARENT
LTEXT   "注册成功", 1003, 20, 18, 37, 11, 0, WS_EX_TRANSPARENT
}



我们再来看看CWnd::ShowWindow的nCmdShow参数(指定了窗口显示的状态)


当nCmdShow=0 时 状态值为SW_HIDE(隐藏窗口并激活另一窗口)
当nCmdShow=1 时 状态值为SW_SHOWNORMAL(激活并显示一个窗口。若窗口是最小化或最大化,则恢复到其原来的大小和位置)


到这里我们就知道,只要不让ID为1005(Ox3ED)的Static Text控件显示,而让ID为1003(Ox3EB)的Static Text控件显示,这样就可以达到爆破了。

所以我们回过头看call 00402350 和call 00402630 的代码,


0040240E|.0FBE05 875340>movsx   eax, byte ptr                         ;eax=
00402415|.0FBE15 535340>movsx   edx, byte ptr                         ;edx=
0040241C      2BD0          sub   edx, eax                                        ;edx=edx-eax
0040241E      52            push    edx                                             ;这个edx要为0(现在明白上面的改法作用了吧)
0040241F|.68 ED030000   push    3ED                                             ;ID为1005(Ox3ED)的Static Text控件
00402424|.E8 A1140000   call    <jmp.&MFC42.#CWnd::GetDlgItem_30>
00402429|.8BC8          mov   ecx, eax
0040242B|.E8 94140000   call    <jmp.&MFC42.#CWnd::ShowWindow_62>
................................................................................................................
00402666   .0FBE05 865340>movsx   eax, byte ptr                         ;eax=
0040266D   .0FBE0D 525340>movsx   ecx, byte ptr                         ;ecx=
00402674   .2BC8          sub   ecx, eax                                        ;ecx=ecx-eax
00402676   .F7D9          neg   ecx                                             ;ecx=neg ecx
00402678      1BC9          sbb   ecx, ecx                                        ;ecx=ecx sbb ecx
0040267A   .41            inc   ecx                                             ;ecx=ecx+1
0040267B   .51            push    ecx                                             ;这个ecx要为1(现在明白上面的改法作用了吧)
0040267C   .68 EB030000   push    3EB                                             ;ID为1003(Ox3ED)的Static Text控件
00402681   .8BCE          mov   ecx, esi
00402683   .E8 42120000   call    <jmp.&MFC42.#CWnd::GetDlgItem_30>
00402688   .8BC8          mov   ecx, eax
0040268A   .E8 35120000   call    <jmp.&MFC42.#CWnd::ShowWindow_62>


至此CM的爆破分析第一部份就完成了.经下面的方法改动后,保存为CrackMe_CR.exe文件后,继续第二部份.


004020A7      83F8 06       cmp   eax, 6                                    ;这里改cmp eax,0
004020BF      83FA 06       cmp   edx, 6                                    ;这里改cmp edx,0
0040213E      BE 01000000   mov   esi, 1                                    ;这里改mov esi,0
004021C2      83F9 20       cmp   ecx, 20                                     ;这里改cmp ecx,0
0040261D      74 04         je      short 00402623                              ;这里改jmp short 00402623
004025EE   /75 09         jnz   short 004025F9                              ;这里改jmp short 004025F9
0040236B   /0F87 92000000 ja      00402403                                    ;这里改jmp 0040240E
0040241C      2BD0          sub   edx, eax                                    ;这里改xor edx,edx
00402664   /75 29         jnz   short 0040268F                              ;这里改nop 掉
00402678      1BC9          sbb   ecx, ecx                                    ;这里改xor ecx,ecx



第二部份:

CM要校验自身程序代码完整性!校验的HASH值作为二进制值存放到CrackMe.Atom中.
我们上面已经将程序代码改动了,这样CM的HASH值就发生了变化,所以我们必须将改
动后的HASH值替换CrackMe.Atom原来的值.那怎么做呢?

OD载入CrackMe_CR.exe文件.

CTRL+G 输入401C60 ,回车后来到这里.(


00401C60   .64:A1 0000000>mov   eax, dword ptr fs:                     ;校验代码HASH值开始(此处的原理我就不分析了)
00401DDF   .E8 2C140000   call    00403210                                    ;这个就是计算CM程序代码HASH值的CALL
00401DE4   .8D7C24 30   lea   edi, dword ptr                      ;下断,F9执行后。查看一下13f5e0-13f5f0地址里的值


以下的二进制就是CrackMe_CR.exe文件代码的HASH值

0013F5E0AF FB 35 57 9F 37 8E B6 1B B6 5E 06 2C 90 82 1C?5W?幎禴,悅
0013F5F000                                             .


以下的二进制就是CrackMe.Atom文件里存放的HASH值
                                 .
0013F6001F F5 35 57 9A 9E 8E B6 03 BC 5E E0 4B 77 F8 1B    ?W殲幎糬郖w?
0013F61000                                             .



将13f5f0-13f5f0间的值替换CrackMe.Atom文件的值,保存即可. 到此此CM的爆破分析就完成了。谢谢观看!

[ 本帖最后由 hflywolf 于 2009-9-10 02:40 编辑 ]

孤漂江湖狼 发表于 2009-9-8 09:12:18

占沙发学习

MOV 发表于 2009-9-9 22:42:56

学习了 /:good

cjteam 发表于 2009-9-9 23:23:29

太强大了,这狼

冬天的雷雨 发表于 2009-9-10 09:22:28

膜拜老兄~~

bhcjl 发表于 2009-9-10 11:23:27

强人啊,支持一下

MyCracker 发表于 2009-9-11 15:10:17

来看看,学习下,谢谢分享

火凤凰 发表于 2009-9-11 23:22:28

:loveliness: 进来学习了,厉害

HDd1145 发表于 2009-9-12 01:21:24

又一个属牛的狼

老海 发表于 2009-9-16 06:31:48

这个暴破点是够多的,狼真有耐心。
页: [1] 2
查看完整版本: PEDIYCrackme竞赛2009-(Atom队-CrackMe)爆破分析