头部背景图片
senrenbankaの部落格 |
senrenbankaの部落格 |

某crackme破解

0x00: 前言

前两天看雪CTF大赛开始,然而对于看雪的CM题目完全是一脸懵逼,于是决定进行CM的训练,废话不多说,目标是这家伙:
crackme1

居然还用了diablo的头像hhh,运行界面:
crackme2

0x01: 脱壳

简单说明下,上下两个框都可以输入,如果输入有误,第二个框的值会变成”register unvalid”之类的东西,首先用PEID查下壳:
查壳
yoda’s crypter的壳,那么就来脱掉它吧~

OD载入程序进行异常计数,然后断在最后一次异常中,此时查看堆栈得知SEH的处理Handle0040C70C中,于是跳到异常处理函数中,下面是异常处理函数:

0040C70C    55              push ebp
0040C70D    8BEC            mov ebp,esp
0040C70F    57              push edi
0040C710    8B45 10         mov eax,dword ptr ss:[ebp+0x10]
0040C713    8BB8 C4000000   mov edi,dword ptr ds:[eax+0xC4]
0040C719    FF37            push dword ptr ds:[edi]
0040C71B    33FF            xor edi,edi
0040C71D    64:8F07         pop dword ptr fs:[edi]
0040C720    8380 C4000000 0>add dword ptr ds:[eax+0xC4],0x8
0040C727    8BB8 A4000000   mov edi,dword ptr ds:[eax+0xA4]
0040C72D    C1C7 07         rol edi,0x7
0040C730    89B8 B8000000   mov dword ptr ds:[eax+0xB8],edi
0040C736    B8 00000000     mov eax,0x0
0040C73B    5F              pop edi    这里edi的值为0x40A950,为跳转到OEP的关键
0040C73C    C9              leave
0040C73D    C3              retn

跳到edi指向的地址,然后单步跟踪,很快就找到了OEP了,OEP代码如下:

00401000    6A 00           push 0x0
00401002    E8 71020000     call d2k2_crk.00401278                   ; jmp 到 kernel32.GetModuleHandleA
00401007    A3 40314000     mov dword ptr ds:[0x403140],eax
0040100C    6A 00           push 0x0
0040100E    68 28104000     push d2k2_crk.00401028
00401013    6A 00           push 0x0
00401015    6A 01           push 0x1
00401017    FF35 40314000   push dword ptr ds:[0x403140]
0040101D    E8 62020000     call d2k2_crk.00401284                   ; jmp 到 user32.DialogBoxParamA
00401022    50              push eax
00401023    E8 4A020000     call d2k2_crk.00401272                   ; jmp 到 kernel32.ExitProcess
00401028    55              push ebp
00401029    8BEC            mov ebp,esp
0040102B    817D 0C 1101000>cmp dword ptr ss:[ebp+0xC],0x111

明显的TASM/MASM32程序,于是脱壳,再次查壳:
crackme-查壳(已脱壳)

0x02: 破解

一上来先用很low的中文搜索法,如果不行就打算下DLGITEMTEXT的断点试试,但是没想到中文搜索居然成功了:
crackme-中文搜索

位置都在附近,随便点进一个看看(下面中文的注释是我写的,英文的是原本就有的):

004010C2    E8 C9010000     call <jmp.&user32.GetDlgItemTextA>       ; 这里应该是要求某种格式
004010C7    83F8 10         cmp eax,0x10                             ; 这里的eax是第二个框的值,要求第二个框放16个数
004010CA    75 5D           jnz short d2k2_crk.00401129
004010CC    6A 28           push 0x28
004010CE    68 A0304000     push d2k2_crk.004030A0
004010D3    6A 67           push 0x67
004010D5    FF75 08         push dword ptr ss:[ebp+0x8]
004010D8    E8 B3010000     call <jmp.&user32.GetDlgItemTextA>
004010DD    85C0            test eax,eax                             ; eax是我们的输入值的个数
004010DF    74 20           je short d2k2_crk.00401101               ; 这个是没有输入的跳转
004010E1    83F8 08         cmp eax,0x8
004010E4    7F 2F           jg short d2k2_crk.00401115               ; 输入值不能大于8位
004010E6    83F8 01         cmp eax,0x1                              ; 这里这个判定有些问题
004010E9    7C 02           jl short d2k2_crk.004010ED
004010EB    EB 50           jmp short d2k2_crk.0040113D
004010ED    68 24304000     push d2k2_crk.00403024                   ; Name must be at least 2 chars long!
004010F2    6A 68           push 0x68
004010F4    FF75 08         push dword ptr ss:[ebp+0x8]
004010F7    E8 A0010000     call <jmp.&user32.SetDlgItemTextA>
004010FC    E9 41010000     jmp d2k2_crk.00401242
00401101    68 04304000     push d2k2_crk.00403004                   ; Enter a name!
00401106    6A 68           push 0x68
00401108    FF75 08         push dword ptr ss:[ebp+0x8]
0040110B    E8 8C010000     call <jmp.&user32.SetDlgItemTextA>
00401110    E9 2D010000     jmp d2k2_crk.00401242
00401115    68 12304000     push d2k2_crk.00403012                   ; Name is too long!
0040111A    6A 68           push 0x68
0040111C    FF75 08         push dword ptr ss:[ebp+0x8]
0040111F    E8 78010000     call <jmp.&user32.SetDlgItemTextA>
00401124    E9 19010000     jmp d2k2_crk.00401242
00401129    68 48304000     push d2k2_crk.00403048                   ; Your Registration Code is invalid!
0040112E    6A 68           push 0x68
00401130    FF75 08         push dword ptr ss:[ebp+0x8]
00401133    E8 64010000     call <jmp.&user32.SetDlgItemTextA>
00401138    E9 05010000     jmp d2k2_crk.00401242

“Thank you for your support”在这段程序的下面,挺靠后的,然后中间有一大段程序,上面的代码中有一个跳转是到那一大段程序中的
jmp short d2k2_crk.0040113D,我将下面的程序也给出来:

0040113D    33DB            xor ebx,ebx                              ; 将寄存器都变为0
0040113F    33C9            xor ecx,ecx
00401141    33D2            xor edx,edx
00401143    33FF            xor edi,edi
00401145    33F6            xor esi,esi
00401147    8A99 A0304000   mov bl,byte ptr ds:[ecx+0x4030A0]
0040114D    80FB 41         cmp bl,0x41
00401150    7C 07           jl short d2k2_crk.00401159
00401152    80FB 5A         cmp bl,0x5A
00401155    7F 0E           jg short d2k2_crk.00401165
00401157    EB 1F           jmp short d2k2_crk.00401178
00401159    80C3 20         add bl,0x20                              ; 值在0-20的会被赋予F
0040115C    80FB 41         cmp bl,0x41
0040115F    7D 02           jge short d2k2_crk.00401163
00401161    B3 46           mov bl,0x46                              ; 固定值F
00401163  ^ EB ED           jmp short d2k2_crk.00401152
00401165    80EB 20         sub bl,0x20
00401168    80FB 5A         cmp bl,0x5A
0040116B    7E 02           jle short d2k2_crk.0040116F
0040116D    B3 47           mov bl,0x47
0040116F    80FB 41         cmp bl,0x41
00401172    7D 02           jge short d2k2_crk.00401176
00401174    B3 53           mov bl,0x53
00401176  ^ EB DF           jmp short d2k2_crk.00401157
00401178    889A 87304000   mov byte ptr ds:[edx+0x403087],bl        ; 赋值给固定的十六位值
0040117E    83C2 02         add edx,0x2                              ; 每次循环加2
00401181    41              inc ecx
00401182    3BC8            cmp ecx,eax                              ; 输入的位数等于循环次数
00401184  ^ 75 C1           jnz short d2k2_crk.00401147
00401186    33C9            xor ecx,ecx
00401188    33D2            xor edx,edx
0040118A    33DB            xor ebx,ebx
0040118C    8A99 87304000   mov bl,byte ptr ds:[ecx+0x403087]
00401192    03D3            add edx,ebx                              ; bl将这个数加给edx
00401194    41              inc ecx
00401195    83F9 10         cmp ecx,0x10
00401198  ^ 75 F2           jnz short d2k2_crk.0040118C              ; 循环0x10次,不断加值给edx
0040119A    69C0 FF000000   imul eax,eax,0xFF
004011A0    0FAFD0          imul edx,eax
004011A3    81F2 ABDFEBAC   xor edx,0xACEBDFAB                       ; 关键
004011A9    0FCA            bswap edx
004011AB    52              push edx
004011AC    68 00304000     push d2k2_crk.00403000                   ; %lX
004011B1    68 18314000     push d2k2_crk.00403118
004011B6    E8 C3000000     call <jmp.&user32.wsprintfA>
004011BB    83C4 0C         add esp,0xC
004011BE    33DB            xor ebx,ebx
004011C0    33C9            xor ecx,ecx
004011C2    8A99 18314000   mov bl,byte ptr ds:[ecx+0x403118]        ; 这个就是上面bswap的edx的值
004011C8    80FB 3A         cmp bl,0x3A
004011CB    7C 02           jl short d2k2_crk.004011CF
004011CD    EB 09           jmp short d2k2_crk.004011D8
004011CF    80C3 11         add bl,0x11
004011D2    8899 18314000   mov byte ptr ds:[ecx+0x403118],bl
004011D8    41              inc ecx
004011D9    83F9 08         cmp ecx,0x8
004011DC  ^ 75 E4           jnz short d2k2_crk.004011C2
004011DE    33DB            xor ebx,ebx
004011E0    33C9            xor ecx,ecx
004011E2    33D2            xor edx,edx
004011E4    8A99 18314000   mov bl,byte ptr ds:[ecx+0x403118]        ; 这里是对十六位值得奇数位的改变
004011EA    889A 88304000   mov byte ptr ds:[edx+0x403088],bl        ; 这里完成对第二个比较的赋值
004011F0    83C2 02         add edx,0x2
004011F3    41              inc ecx
004011F4    83FA 10         cmp edx,0x10
004011F7  ^ 75 EB           jnz short d2k2_crk.004011E4
004011F9    33DB            xor ebx,ebx
004011FB    33C9            xor ecx,ecx
004011FD    33D2            xor edx,edx
004011FF    33C0            xor eax,eax
00401201    8A9A C8304000   mov bl,byte ptr ds:[edx+0x4030C8]
00401207    8A82 87304000   mov al,byte ptr ds:[edx+0x403087]        ; 这两个数据值要相等
0040120D    33C3            xor eax,ebx
0040120F  ^ 0F85 14FFFFFF   jnz d2k2_crk.00401129
00401215    8A9A C9304000   mov bl,byte ptr ds:[edx+0x4030C9]
0040121B    8A82 88304000   mov al,byte ptr ds:[edx+0x403088]
00401221    04 05           add al,0x5
00401223    38C3            cmp bl,al                                ; 要满足条件bl-al=5
00401225  ^ 0F85 FEFEFFFF   jnz d2k2_crk.00401129
0040122B    83C2 02         add edx,0x2
0040122E    83FA 10         cmp edx,0x10
00401231  ^ 75 CE           jnz short d2k2_crk.00401201
00401233    68 6B304000     push d2k2_crk.0040306B                   ; Thank you for your support!
00401238    6A 68           push 0x68
0040123A    FF75 08         push dword ptr ss:[ebp+0x8]
0040123D    E8 5A000000     call <jmp.&user32.SetDlgItemTextA>

讲讲这段逻辑的基本思路:

先判断第二个框的值是否是16位的,如果不是,跳转到unvalid
取出第一个框的长度,要求第一个框的长度在2-8个字之间(事实上它那个判断逻辑有点问题,应该1个字节也是可以的)
判断无误后,进入生成注册码算法

# 生成注册码算法:

在ds:[0+0x403087]数据段中存放着一个固定的值"EJDACBBTACGIDFNG",这个值是注册码的雏形

算法首先对我们输入的第一个框的值进行格式化和过滤,规则经分析后总结如下:

       如果值在0-20,那么赋予固定值"F";
       如果值在21-3A,那么这个值加上0x20;
       如果值在3B-40,那么赋予固定值"G";
       如果值在41-5A,那么不用处理;
       如果值在5B-60,那么赋予固定值"S";
       如果值在61-7A,那么这个值减去0x20;
       如果值在7B及以上,那么赋予固定值"G";

然后依次将这些值赋给ds:[0+0x403087]的偶数索引,比如我们输入的值是"AAAAAAAA",则有:
     "EJDACBBTACGIDFNG" => "AJAAABATACAIAFAG"

接下来:

edx = ds:[0+0x403087]的每个字符的ASCII值的总和
eax = 输入的第一个框的值的长度 * 0xFF
edx = eax * edx
edx = edx xor 0xACEBDFAB(这个是固定的)
edx进行bswap,也就是第一个字节跟第四个字节交换,第二个字节和第三个字节交换,比如"AFDAFCBA" => "BAFCDAAF"
检验edx的每个位的ASCII值是否小于0x3A,如果是,加上0x11
然后将edx的每一位替换ds:[0+0x403087]的奇数索引
比如"AJAAABATACAIAFAG" => "ABAAAFACADAAAAAF"
将这个值和第二个框输入的值进行比较,比较规则如下:

       如果是偶数索引,要求相等;
       如果是奇数索引,那么对应的第二个框的那一位的ASCII值要大5;

思路理清了,接下来就是写个注册机了,注册机源代码在这里,用的易语言写的,运行如图:

crackme-注册机

拿这个去尝试:

crackme-注册成功

爆破版的这里就不放了,就是改几个关键跳转的事情

avatar AONOSORA 今生今世轮回尽, 来世愿为幻想乡