-
-
[原创]看雪CTF2016-4-JoenChen.rar破解说明
-
发表于: 2016-11-9 17:38 3443
-
【文章标题】: 看雪CTF2016-4-JoenChen.rar破解说明
【文章作者】: SilentGamb@pediy
【作者主页】: http://bbs.pediy.com/member.php?u=492631
【软件名称】: 4-JoenChen.rar
【软件大小】: 283 KB (290,304 字节)
【下载地址】: http://ctf.pediy.com/?game-fight-5.htm
【加壳方式】: 无
【保护方式】: WProtect
【使用工具】: OD1.1+IDA6.1
【操作平台】: Win7X64
【软件介绍】: JoenChen为大家制作的cm
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
先说下结果 :
从文件新加节名看, cm用了WProtect, 单步时,也不是编译器直接编译出来的样子.
跟进去转了好久,没看出算法。估计作者也是装了这个口袋让攻击者钻。
单步调试也看出了一些缓冲区操作,但是整体算法没看出,也没搞出算法公式。
但是通过调试,弄清了注册码和注册成功之间的模糊关系(cm的注册函数地址,入口参数,注册码要求)。
已经确定自己不能弄清注册算法,结合调试得到的注册码和注册成功之间的模糊关系,写出了穷举程序,
5秒钟得到了一个注册码。
以下摘自调试记录:
侦查
注册失败没有提示.
MD5 :: 00000B37 :: 00401737
The reference is above.
SHA-512/384 [mixing] :: 00010198 :: 00411998
The reference is above.
.WProtec
加了开源虚拟机壳WProtec
https://github.com/xiaoweime/WProtect
口令比对函数
00401486 |. FF15 04D14000 call dword ptr ds:[<&USER32.GetDlgItemTe>; \GetDlgItemTextA
0040148C |. 83F8 1E cmp eax,0x1E ; 口令位数 = 30个字符
0040148F |. 75 25 jnz short CrackMe.004014B6
00401491 |. 8BD0 mov edx,eax
00401493 |. 8D8D FCFDFFFF lea ecx,dword ptr ss:[ebp-0x204]
00401499 |. E8 62FBFFFF call CrackMe.00401000
0040149E |. 83F8 01 cmp eax,0x1
004014A1 |. 75 13 jnz short CrackMe.004014B6
004014A3 |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL
004014A5 |. 68 80374100 push CrackMe.00413780 ; |Title = "information"
004014AA |. 68 8C374100 push CrackMe.0041378C ; |Text = "注册成功"
004014AF |. 56 push esi ; |hOwner = 00080A2E ('CrackMe by JoenChen',class='#32770')
004014B0 |. FF15 00D14000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
口令要求
30个字符
采用口令
111111111111111111111111111111
走到中间记录一下
0042FCFA C3 retn
栈
0018F394 0042F3A3 CrackMe.0042F3A3
0018F398 0018F8C4 返回到 0018F8C4
0018F39C 00000000
0018F3A0 00000287
0018F3A4 0018F8B0
0018F3A8 0018F770
0018F3AC 00000000
0018F3B0 0018F840 ASCII "1111111"
0018F3B4 0018F4DC
0018F3B8 0018F8D0 ASCII "111111111111111111111111111111"
0018F3BC 0001E240
0018F3C0 0000001E
0018F3C4 76E47DDD 返回到 usp10.76E47DDD
记录一下
0042F72C /0F87 A7050000 ja CrackMe.0042FCD9
0042F623 66:F7D3 not bx
0042F630 66:81EB 85E2 sub bx,0xE285
此时,测试输入还在栈内.
准备对栈内数据下硬件断点了, 看哪用这个数据
0018F3B0 0018F840 ASCII "1111111"
004015A7 |. 88440E 18 |mov byte ptr ds:[esi+ecx+0x18],al
.text:004015A7 mov [esi+ecx+18h], al ; 开始用到测试输入了
004015A4 |> /8A040A /mov al,byte ptr ds:[edx+ecx]
004015A7 |. |88440E 18 |mov byte ptr ds:[esi+ecx+0x18],al
004015AB |. |41 |inc ecx
004015AC |. |3BCB |cmp ecx,ebx ; ebx = 7
004015AE |.^\72 F4 \jb short CrackMe.004015A4
数据被拷贝到新缓冲区
0018F500 31 31 31 31 31 31 31 00 AC 48 D1 01 02 00 00 00 1111111.琀?...
004015B0 |> \5F pop edi ; 0018F8D0
记录一下
0042FCFA C3 retn
edx = 0018F840
0018F840 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 1111111.........
0042F72C /0F87 A7050000 ja CrackMe.0042FCD9 // 跳转成立去0042FCFA上面
0042F732 |8D4F 64 lea ecx,dword ptr ds:[edi+0x64] ; 转完圈了会来这
esi = 0018F140是1111111的0x64长度缓冲区,
拷贝到 edi = 0018F0DC
ecx = edx = 0x64
0042F749 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] ; 拷贝了一次
0042F74B 2BFA sub edi,edx
0042FCFA C3 retn ; 经常会过来
0042F749 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] ; 拷贝了一次
0042F74B 2BFA sub edi,edx
0042F623 66:F7D3 not bx ; 转圈完成后,ret回来
还是在栈内下断
0018EFC8 0018F840 ASCII "1111111"
0018EFCC 0018F4DC
0018EFD0 0018F8D0 ASCII "111111111111111111111111111111"
对0018F840和0018F8D0下满长度的内存访问断点
EIP
0042F623 66:F7D3 not bx ; 转圈完成后,ret回来
并不再访问用户输入...
开始关键运算
004012B5 ? 8B8D 08FCFFFF mov ecx,dword ptr ss:[ebp-0x3F8] ; 来过 0018f4b8
004012BB ? 8D95 90FEFFFF lea edx,dword ptr ss:[ebp-0x170] ; 0018f740
004012C1 ? 8B85 04FCFFFF mov eax,dword ptr ss:[ebp-0x3FC] ; 0018f4b4
004012C7 ? C1E1 04 shl ecx,0x4
004012CA . 03C1 add eax,ecx
004012CC . 03D0 add edx,eax
004012CE > 8995 04FCFFFF mov dword ptr ss:[ebp-0x3FC],edx
004012D4 . 8A8C05 90FCFF>mov cl,byte ptr ss:[ebp+eax-0x370] ; 0xD4怎么来的?
eax 在 ss:[ebp+eax-0x370] 中指向的字节,如果简单运算后,是只要算出不是30, 24,58就成功.
ss:[0018f540]数据从哪来?
0040116E . 0FB741 1B movzx eax,word ptr ds:[ecx+0x1B] ; 拷贝用户输入, 拷贝了用户输入的31个字符中的前7个字符1111111.
0018F840 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 1111111......... // 中间拷贝的前7个,没用到
0018F850 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0018F860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0018F870 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0018F880 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 1111111111111111 // 前16个字符
0018F890 31 31 31 31 31 31 31 76 3A FD 8B 16 FE FF FF FF 1111111v:龐? // 前7个
开始和下面的数据运算
0018F4C0 04 0B D1 01 44 AD 5C CC 12 90 73 8D 47 81 E3 89
?D璡?恠岹併?
0018F4D0 84 9C DF F9 47 6A B6 9E 11 30 27 00 D0 F8 18 00 劀啭Gj稙0'.续.
0018F4E0 3C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <...............
第一个用户输入字符 xor byte[18f4c4]
00401187 . 32840D 14FCFF>xor al,byte ptr ss:[ebp+ecx-0x3EC]
// 异或的结果放到0018f498
0018F490 75 00 00 00 0C 00 00 00 .......
0018F4A0 00 00 00 00 00 00 02 00 B0 15 E0 76 54 0B D1 01 .......?鄓T
?
0018F4B0 B0 99 D1 01 02 00 00 00 00 00 00 00 C8 0B D1 01 皺?.......??
0040119E . C6440D D0 00 mov byte ptr ss:[ebp+ecx-0x30],0x0 ; 清用户输入c1
004011A3 . C6840D 14FCFF>mov byte ptr ss:[ebp+ecx-0x3EC],0x0 ; 清数据k1
// 异或0x17(23)个字符
00401183 > /8A440D D0 mov al,byte ptr ss:[ebp+ecx-0x30] ; Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15 of switch 00401195
00401187 . |32840D 14FCFF>xor al,byte ptr ss:[ebp+ecx-0x3EC]
0040118E . |88840D E8FBFF>mov byte ptr ss:[ebp+ecx-0x418],al
00401195 . |83F9 17 cmp ecx,0x17 ; Switch (cases 0..16)
00401198 . |0F83 68020000 jnb CrackMe.00401406
0040119E . |C6440D D0 00 mov byte ptr ss:[ebp+ecx-0x30],0x0 ; 清用户输入c1
004011A3 . |C6840D 14FCFF>mov byte ptr ss:[ebp+ecx-0x3EC],0x0 ; 清数据k1
004011AB . |41 inc ecx
004011AC . |83F9 17 cmp ecx,0x17
004011AF .^\7C D2 jl short CrackMe.00401183
开始运算了
00401183 > /8A440D D0 mov al,byte ptr ss:[ebp+ecx-0x30] ; Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15 of switch 00401195
00401187 . |32840D 14FCFF>xor al,byte ptr ss:[ebp+ecx-0x3EC]
0040118E . |88840D E8FBFF>mov byte ptr ss:[ebp+ecx-0x418],al
00401195 . |83F9 17 cmp ecx,0x17 ; Switch (cases 0..16)
00401198 . |0F83 68020000 jnb CrackMe.00401406
0040119E . |C6440D D0 00 mov byte ptr ss:[ebp+ecx-0x30],0x0 ; 清用户输入c1
004011A3 . |C6840D 14FCFF>mov byte ptr ss:[ebp+ecx-0x3EC],0x0 ; 清数据k1
004011AB . |41 inc ecx
004011AC . |83F9 17 cmp ecx,0x17
004011AF .^\7C D2 jl short CrackMe.00401183 ; 异或运算23个字符
23个字符运算完成
0018F490 75 9C 6D FD 23 A1 42 BC 渕??
0018F4A0 76 B0 D2 B8 B5 AD EE C8 76 5B 87 AF 20 01 16 v耙傅葀[嚡
开始运算2 - 也是23个字符
EIP
004011C0 > /8A840D 90FDFF>mov al,byte ptr ss:[ebp+ecx-0x270] ; Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15 of switch 004011D2
18f640 与 18f498 异或 送到 0018F880
0018F640 7F A8 28 2A D9 30 47 A4 D6 FE 61 11 C9 3B 30 8A ?*?Gぶ?0?
0018F650 7F A8 28 2A D9 30 47 ?*?G..
0018F490 75 9C 6D FD 23 A1 42 BC 渕??
0018F4A0 76 B0 D2 B8 B5 AD EE v耙傅葀[嚡
al=0A (Line Feed)
堆栈 ss:[0018F880]=00
0018F880在运算前,已经清好了
0018F880 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0018F890 00 00 00 00 00 00 00 .......
算完之后
0018F880 0A 34 45 D7 FA 91 05 18 A0 4E B3 A9 7C 96 DE 42 .4E?燦畅|栟B
0018F890 09 F3 AF 85 F9 31 51 .蟑咘1Qv:
继续计算
004011F1 . B9 40000000 mov ecx,0x40 ;
004011F6 . 8DBD 90FCFFFF lea edi,dword ptr ss:[ebp-0x370] ; 0018f8d0 用户输入的30个字符
ecx = 00000040
esi = 00413798
edi = 0018F540
00413798 6E D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9 n?i_N瑾曵2b?
004137A8 02 18 74 95 1F C2 4D 33 3C F0 3B EC E9 81 4B 9C t?翸3<?扉並?
004137B8 0F 47 5C AD D9 09 B0 87 53 9B F2 E3 0F 01 92 8D
0x40 dword拷贝完成后, 总共0x100字节
0018F540 6E D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9 n?i_N瑾曵2b?
0018F550 02 18 74 95 1F C2 4D 33 3C F0 3B EC E9 81 4B 9C t?翸3<?扉並?
0018F560 0F 47 5C AD D9 09 B0 87 53 9B F2 E3 0F 01 92 8D
继续计算
00401205 . B9 40000000 mov ecx,0x40
0040120A . 8985 30FCFFFF mov dword ptr ss:[ebp-0x3D0],eax
eax=00000000
堆栈 ss:[0018F4E0]=0000003C
[0018F4E0] = 0 // 清0
开始拷贝数据
0040121B . F3:A5 rep movs dword ptr es:[edi],dword ptr ds:[esi]
esi = 00413898
edi = 0018F740
拷贝完成后
0018F740 D8 05 F6 6A E7 A2 0B 9B 54 8C DA 82 BD B6 A8 46 ?鰆纰
汿屭偨定F
0018F750 B1 36 2D 55 F7 81 63 FC 3F 0C FE 0B 4B 50 E2 17 ?-U鱽c?.?KP?
0018F760 F2 E1 27 5B 46 73 1C D0 E5 D7 8D C9 F2 70 94 53 蜥'[Fs绣讔沈p擲
040121F . 898D 2CFCFFFF mov dword ptr ss:[ebp-0x3D4],ecx ; 0x18f4dc用户输入字符串指针清0
00401225 > /8A4C0D D0 mov cl,byte ptr ss:[ebp+ecx-0x30] ; [18f880] => cl
0040122C . C785 0CFCFFFF>mov dword ptr ss:[ebp-0x3F4],0x6 ; dw ss:[18f4bc] = 6
00401236 . 898D 00FCFFFF mov dword ptr ss:[ebp-0x400],ecx ; ss:18f4b0 = a
00401242 . 8985 10FCFFFF mov dword ptr ss:[ebp-0x3F0],eax ; ss:18f4c0 = 0
一段查表跳转,往下跳了一句.
执行eax -=10 or eax += 1, or eax += 10
00401253 . /FF2495 0C1440>jmp dword ptr ds:[edx*4+0x40140C] ; CrackMe.0040125A
0040125A > \83E8 10 sub eax,0x10
0040125D . EB 09 jmp short CrackMe.00401268
0040125F > 40 inc eax
00401260 . EB 06 jmp short CrackMe.00401268
00401262 > 83C0 10 add eax,0x10
00401265 . EB 01 jmp short CrackMe.00401268
00401268 > \8985 30FCFFFF mov dword ptr ss:[ebp-0x3D0],eax
0040128F > \8985 04FCFFFF mov dword ptr ss:[ebp-0x3FC],eax ; ss:[0018f4b4] = 0
00401295 .- E9 DBB10300 jmp CrackMe.0043C475
不断的转圈, 如果不转圈了,进下面3个call
004013E2 . E8 CF090000 call CrackMe.00401DB6
004013E7 . 8BE5 mov esp,ebp
004013E9 . 5D pop ebp
004013EA . 8BE3 mov esp,ebx
004013EC . 5B pop ebx
004013ED . C3 retn
004013EE > 8B4D FC mov ecx,dword ptr ss:[ebp-0x4]
004013F1 . B8 01000000 mov eax,0x1
004013F6 . 5F pop edi
004013F7 . 33CD xor ecx,ebp
004013F9 . 5E pop esi ; CrackMe.00413998
004013FA . E8 B7090000 call CrackMe.00401DB6
004013FF . 8BE5 mov esp,ebp
00401401 . 5D pop ebp
00401402 . 8BE3 mov esp,ebx
00401404 . 5B pop ebx
00401405 . C3 retn
00401406 > E8 DF0A0000 call CrackMe.00401EEA ; Default case of switch 004011D2
来了
004013E2 . E8 CF090000 call CrackMe.00401DB6
004013D9 <CrackMe.failed_004013d9> > \33C0 xor eax,eax ; 失败了来这里
004013DB . 5F pop edi
004013DC . 5E pop esi
004013DD . 8B4D FC mov ecx,dword ptr ss:[ebp-0x4]
004013E0 . 33CD xor ecx,ebp
004013E2 . E8 CF090000 call <CrackMe.fn_anti> ; 没有影响eax
004013E7 . 8BE5 mov esp,ebp
004013E9 . 5D pop ebp
004013EA . 8BE3 mov esp,ebx
004013EC . 5B pop ebx
004013ED . C3 retn
004012EE ecx == 0x30 . 83F9 30 cmp ecx,0x30 ; ecx == 0x30失败
异或之前 ecx = 0x35失败
004012E3 (al >> 2) == 0x35 . C0C8 02 ror al,0x2 ; (al == 0xd4) 失败
0xD4怎么来的?
004012D4 . 8A8C05 90FCFFFF mov cl,byte ptr ss:[ebp+eax-0x370] ; 0xD4怎么来的?
第20次,变成了0xD4
在第19次时,跟一下.
ss:[ebp+eax-0x370] = ss:[0018f541] = 0xd4时失败
如果新打开OD, 运行第4次,就开始算出了错误的ss:[0018f540], 所以第3次,要去看一下.
第4次,就算好了错误值 ss:[0018f540]时,ss:[0018f541]的值已经确定是0xd4. 已经错了.
ss:[0018f541]往下简单移位计算, 只要算出不是30, 24,58就成功.
堆栈 ss:[0018F530]=00
cl=F0
堆栈 ss:[0018F520]=00
cl=E0
堆栈 ss:[0018F530]=00
cl=F0
下面就要算ss:[0018f540], 开始跟一下.
其实,第一次就已经算成了0xD4, 第一次就要跟.
111111111111111111111111111111
算数据的过程
这里的数据就是出去后,要比对的ss:[0018f541] 计算出30, 24,58, 只要不算出计算后的30,24,58,就注册成功了.
0040115E . 8B8D 2CFCFFFF mov ecx,dword ptr ss:[ebp-0x3D4]
00401164 . 8B41 17 mov eax,dword ptr ds:[ecx+0x17]
00401167 . 0F1041 07 movups xmm0,dqword ptr ds:[ecx+0x7]
0040116B . 8945 E0 mov dword ptr ss:[ebp-0x20],eax
0040116E . 0FB741 1B movzx eax,word ptr ds:[ecx+0x1B] ; 拷贝用户输入
00401172 . 66:8945 E4 mov word ptr ss:[ebp-0x1C],ax
00401176 . 0FB641 1D movzx eax,byte ptr ds:[ecx+0x1D]
0040117A . 33C9 xor ecx,ecx
0040117C . 0F1145 D0 movups dqword ptr ss:[ebp-0x30],xmm0
00401180 . 8845 E6 mov byte ptr ss:[ebp-0x1A],al
00401183 > 8A440D D0 mov al,byte ptr ss:[ebp+ecx-0x30] ; Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15 of switch 00401195
00401187 . 32840D 14FCFF>xor al,byte ptr ss:[ebp+ecx-0x3EC]
0040118E . 88840D E8FBFF>mov byte ptr ss:[ebp+ecx-0x418],al
00401195 . 83F9 17 cmp ecx,0x17 ; Switch (cases 0..16)
00401198 . 0F83 68020000 jnb CrackMe.00401406
0040119E . C6440D D0 00 mov byte ptr ss:[ebp+ecx-0x30],0x0 ; 清用户输入c1
004011A3 . C6840D 14FCFF>mov byte ptr ss:[ebp+ecx-0x3EC],0x0 ; 清数据k1
00413798谁算出来的?
00413798 6E D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9 n?i_N瑾曵2b?
004137A8 02 18 74 95 1F C2 4D 33 3C F0 3B EC E9 81 4B 9C t?翸3<?扉並?
004137B8 0F 47 5C AD D9 09 B0 87 53 9B F2 E3 0F 01 92 8D
G\.皣S涷?拲
004011FC . BE 98374100 mov esi,CrackMe.00413798
00413798决定注册是否成功,[00413799]为d4就失败.
00413798 是内置的数组啊
.data:00413798 db 6Eh, 0D4h, 1Bh, 69h, 5Fh, 4Eh, 0E8h, 0AAh, 95h, 0F6h, 0AFh, 0CEh, 32h, 1Ah, 62h, 0D9h; 0
.data:00413798 db 2, 18h, 74h, 95h, 1Fh, 0C2h, 4Dh, 33h, 3Ch, 0F0h, 3Bh, 0ECh, 0E9h, 81h, 4Bh, 9Ch; 16
.data:00413798 db 0Fh, 47h, 5Ch, 0ADh, 0D9h, 9, 0B0h, 87h, 53h, 9Bh, 0F2h, 0E3h, 0Fh, 1, 92h, 8Dh; 32
.data:00413798 db 0C2h, 0F5h, 0Ch, 0DDh, 42h, 0CCh, 0AFh, 0B4h, 0D5h, 0E4h, 86h, 0D3h, 9Ah, 0Bh, 62h, 63h; 48
没招了,要穷举了
再不穷举,就要放弃治疗了...
从前面的调试可以看出, 口令为30个字符, 再没其他限制.
0042FCFA 时,看栈内数据,有前7个字符
0040116E开始,可以看到cm拿用户输入的前7个字符做了任务.
004015AE时,看到前7个字符的拷贝动作
0042F732 时,
esi = 0018F140是1111111的0x64长度缓冲区,
拷贝到 edi = 0018F0DC
ecx = edx = 0x64
0042F623 时,栈内有用户输入的全部注册码,和前7个注册码,而且前7个注册码在前
0040116E时,拷贝了前7个字符
很多操作都进了.WProctect 0x0043xxxx, 对虚拟机不甚了解, 跟进去看了下,无功而返.
只能看0040xxxx的操作了.
但是咋拿前7个字符计算的,就没看到了,可能在.WProctect 0x0043xxxx.
综合调试记录分析,cm使用前7个字符作为注册码来源的可能性很大.
先穷举30个字符中的前7个,如果不行,就全部穷举.
将PE作为DLL载入, 穷举前7个字符,后面的23个字符不变,运行5秒钟后,得到一个注册码. 00000bQ11111111111111111111111, 直接运行cm, 输入注册码成功注册^_^
穷举程序片段
// hw.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <windows.h>
DWORD Base;
DWORD pfn = 0;
// void __declspec(naked) fnCalcRegSn1/*<eax>*/(signed int a1/*<edx>*/, int a2/*<ecx>*/, int a3/*<ebp>*/)
// {
//
// }
typedef void (*PFN_fnCalcRegSn1)/*<eax>*/();
char ucAryPwdCharSet[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
int main(int argc, char* argv[])
{
// v4 和szBuf必须前2个, pfn1要用
int v4; // ebp@0
CHAR szBuf[0x200] = {'\0'}; // [sp+4h] [bp-204h]@7
int iBuf = 0;
PFN_fnCalcRegSn1 pfn1 = NULL;
int iRc = -2;
DWORD dwProt;
int iLenAryPwdCharSet = sizeof(ucAryPwdCharSet);
int iLoop1 = 0;
int iLoop2 = 0;
int iLoop3 = 0;
int iLoop4 = 0;
int iLoop5 = 0;
int iLoop6 = 0;
int iLoop7 = 0;
// 284 KB (290,816 字节) => 290816 => 0x47000
// CrackMe.exe 默认装载地址为0x0040xxxx
// 00401000 > $ 53 push ebx ; 注册码比对
// 程序基地址改为 0x200000
Base = (DWORD)LoadLibraryEx("CrackMe.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
VirtualProtect((LPVOID)Base, 0x47000, PAGE_EXECUTE_READWRITE, &dwProt);
if (Base == 0x400000) {
// 00401000 > $ 53 push ebx ; 注册码比对
pfn = Base + 0x1000;
pfn1 = (PFN_fnCalcRegSn1)pfn;
// 根据调试记录, 前7个字母是此cm的产生数据的原数据
// 穷举前7个字符, 规模不大
for (iLoop1 = 0; iLoop1 < iLenAryPwdCharSet; iLoop1++) {
for (iLoop2 = 0; iLoop2 < iLenAryPwdCharSet; iLoop2++) {
for (iLoop3 = 0; iLoop3 < iLenAryPwdCharSet; iLoop3++) {
for (iLoop4 = 0; iLoop4 < iLenAryPwdCharSet; iLoop4++) {
for (iLoop5 = 0; iLoop5 < iLenAryPwdCharSet; iLoop5++) {
for (iLoop6 = 0; iLoop6 < iLenAryPwdCharSet; iLoop6++) {
for (iLoop7 = 0; iLoop7 < iLenAryPwdCharSet; iLoop7++) {
strcpy(szBuf, "111111111111111111111111111111");
szBuf[0] = ucAryPwdCharSet[iLoop1];
szBuf[1] = ucAryPwdCharSet[iLoop2];
szBuf[2] = ucAryPwdCharSet[iLoop3];
szBuf[3] = ucAryPwdCharSet[iLoop4];
szBuf[4] = ucAryPwdCharSet[iLoop5];
szBuf[5] = ucAryPwdCharSet[iLoop6];
szBuf[6] = ucAryPwdCharSet[iLoop7];
iBuf = (int)szBuf;
__asm {
mov edx, 30
mov ecx, iBuf
};
iRc = 0;
pfn1();
__asm {
mov iRc, eax
};
if (iRc > 0) {
::MessageBox(NULL, szBuf, "找到注册码!", MB_OK);
OutputDebugString("找到注册码!\r\n");
OutputDebugString(szBuf);
OutputDebugString("\r\n");
} else {
OutputDebugString(".");
}
}
}
}
}
}
}
}
} else {
// 再FreeLibrary, 再LoadLibraryEx没用的, 必须重新启动一次
::MessageBox(NULL, "错误", "载入目标程序失败, 请重新运行一次", MB_OK);
}
if (0 != Base) {
FreeLibrary((HMODULE)Base);
Base = 0;
}
::MessageBox(NULL, "END", "穷举结束", MB_OK);
return 0;
}
感谢JoenChen制作的cm^_^
--------------------------------------------------------------------------------
【经验总结】
大家都开始上密码学,虚拟机,以后要针对这方面加强练习.
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2016年11月09日 17:31:12
【文章作者】: SilentGamb@pediy
【作者主页】: http://bbs.pediy.com/member.php?u=492631
【软件名称】: 4-JoenChen.rar
【软件大小】: 283 KB (290,304 字节)
【下载地址】: http://ctf.pediy.com/?game-fight-5.htm
【加壳方式】: 无
【保护方式】: WProtect
【使用工具】: OD1.1+IDA6.1
【操作平台】: Win7X64
【软件介绍】: JoenChen为大家制作的cm
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
先说下结果 :
从文件新加节名看, cm用了WProtect, 单步时,也不是编译器直接编译出来的样子.
跟进去转了好久,没看出算法。估计作者也是装了这个口袋让攻击者钻。
单步调试也看出了一些缓冲区操作,但是整体算法没看出,也没搞出算法公式。
但是通过调试,弄清了注册码和注册成功之间的模糊关系(cm的注册函数地址,入口参数,注册码要求)。
已经确定自己不能弄清注册算法,结合调试得到的注册码和注册成功之间的模糊关系,写出了穷举程序,
5秒钟得到了一个注册码。
以下摘自调试记录:
侦查
注册失败没有提示.
MD5 :: 00000B37 :: 00401737
The reference is above.
SHA-512/384 [mixing] :: 00010198 :: 00411998
The reference is above.
.WProtec
加了开源虚拟机壳WProtec
https://github.com/xiaoweime/WProtect
口令比对函数
00401486 |. FF15 04D14000 call dword ptr ds:[<&USER32.GetDlgItemTe>; \GetDlgItemTextA
0040148C |. 83F8 1E cmp eax,0x1E ; 口令位数 = 30个字符
0040148F |. 75 25 jnz short CrackMe.004014B6
00401491 |. 8BD0 mov edx,eax
00401493 |. 8D8D FCFDFFFF lea ecx,dword ptr ss:[ebp-0x204]
00401499 |. E8 62FBFFFF call CrackMe.00401000
0040149E |. 83F8 01 cmp eax,0x1
004014A1 |. 75 13 jnz short CrackMe.004014B6
004014A3 |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL
004014A5 |. 68 80374100 push CrackMe.00413780 ; |Title = "information"
004014AA |. 68 8C374100 push CrackMe.0041378C ; |Text = "注册成功"
004014AF |. 56 push esi ; |hOwner = 00080A2E ('CrackMe by JoenChen',class='#32770')
004014B0 |. FF15 00D14000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
口令要求
30个字符
采用口令
111111111111111111111111111111
走到中间记录一下
0042FCFA C3 retn
栈
0018F394 0042F3A3 CrackMe.0042F3A3
0018F398 0018F8C4 返回到 0018F8C4
0018F39C 00000000
0018F3A0 00000287
0018F3A4 0018F8B0
0018F3A8 0018F770
0018F3AC 00000000
0018F3B0 0018F840 ASCII "1111111"
0018F3B4 0018F4DC
0018F3B8 0018F8D0 ASCII "111111111111111111111111111111"
0018F3BC 0001E240
0018F3C0 0000001E
0018F3C4 76E47DDD 返回到 usp10.76E47DDD
记录一下
0042F72C /0F87 A7050000 ja CrackMe.0042FCD9
0042F623 66:F7D3 not bx
0042F630 66:81EB 85E2 sub bx,0xE285
此时,测试输入还在栈内.
准备对栈内数据下硬件断点了, 看哪用这个数据
0018F3B0 0018F840 ASCII "1111111"
004015A7 |. 88440E 18 |mov byte ptr ds:[esi+ecx+0x18],al
.text:004015A7 mov [esi+ecx+18h], al ; 开始用到测试输入了
004015A4 |> /8A040A /mov al,byte ptr ds:[edx+ecx]
004015A7 |. |88440E 18 |mov byte ptr ds:[esi+ecx+0x18],al
004015AB |. |41 |inc ecx
004015AC |. |3BCB |cmp ecx,ebx ; ebx = 7
004015AE |.^\72 F4 \jb short CrackMe.004015A4
数据被拷贝到新缓冲区
0018F500 31 31 31 31 31 31 31 00 AC 48 D1 01 02 00 00 00 1111111.琀?...
004015B0 |> \5F pop edi ; 0018F8D0
记录一下
0042FCFA C3 retn
edx = 0018F840
0018F840 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 1111111.........
0042F72C /0F87 A7050000 ja CrackMe.0042FCD9 // 跳转成立去0042FCFA上面
0042F732 |8D4F 64 lea ecx,dword ptr ds:[edi+0x64] ; 转完圈了会来这
esi = 0018F140是1111111的0x64长度缓冲区,
拷贝到 edi = 0018F0DC
ecx = edx = 0x64
0042F749 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] ; 拷贝了一次
0042F74B 2BFA sub edi,edx
0042FCFA C3 retn ; 经常会过来
0042F749 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] ; 拷贝了一次
0042F74B 2BFA sub edi,edx
0042F623 66:F7D3 not bx ; 转圈完成后,ret回来
还是在栈内下断
0018EFC8 0018F840 ASCII "1111111"
0018EFCC 0018F4DC
0018EFD0 0018F8D0 ASCII "111111111111111111111111111111"
对0018F840和0018F8D0下满长度的内存访问断点
EIP
0042F623 66:F7D3 not bx ; 转圈完成后,ret回来
并不再访问用户输入...
开始关键运算
004012B5 ? 8B8D 08FCFFFF mov ecx,dword ptr ss:[ebp-0x3F8] ; 来过 0018f4b8
004012BB ? 8D95 90FEFFFF lea edx,dword ptr ss:[ebp-0x170] ; 0018f740
004012C1 ? 8B85 04FCFFFF mov eax,dword ptr ss:[ebp-0x3FC] ; 0018f4b4
004012C7 ? C1E1 04 shl ecx,0x4
004012CA . 03C1 add eax,ecx
004012CC . 03D0 add edx,eax
004012CE > 8995 04FCFFFF mov dword ptr ss:[ebp-0x3FC],edx
004012D4 . 8A8C05 90FCFF>mov cl,byte ptr ss:[ebp+eax-0x370] ; 0xD4怎么来的?
eax 在 ss:[ebp+eax-0x370] 中指向的字节,如果简单运算后,是只要算出不是30, 24,58就成功.
ss:[0018f540]数据从哪来?
0040116E . 0FB741 1B movzx eax,word ptr ds:[ecx+0x1B] ; 拷贝用户输入, 拷贝了用户输入的31个字符中的前7个字符1111111.
0018F840 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 1111111......... // 中间拷贝的前7个,没用到
0018F850 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0018F860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0018F870 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0018F880 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 1111111111111111 // 前16个字符
0018F890 31 31 31 31 31 31 31 76 3A FD 8B 16 FE FF FF FF 1111111v:龐? // 前7个
开始和下面的数据运算
0018F4C0 04 0B D1 01 44 AD 5C CC 12 90 73 8D 47 81 E3 89
?D璡?恠岹併?
0018F4D0 84 9C DF F9 47 6A B6 9E 11 30 27 00 D0 F8 18 00 劀啭Gj稙0'.续.
0018F4E0 3C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <...............
第一个用户输入字符 xor byte[18f4c4]
00401187 . 32840D 14FCFF>xor al,byte ptr ss:[ebp+ecx-0x3EC]
// 异或的结果放到0018f498
0018F490 75 00 00 00 0C 00 00 00 .......
0018F4A0 00 00 00 00 00 00 02 00 B0 15 E0 76 54 0B D1 01 .......?鄓T
?
0018F4B0 B0 99 D1 01 02 00 00 00 00 00 00 00 C8 0B D1 01 皺?.......??
0040119E . C6440D D0 00 mov byte ptr ss:[ebp+ecx-0x30],0x0 ; 清用户输入c1
004011A3 . C6840D 14FCFF>mov byte ptr ss:[ebp+ecx-0x3EC],0x0 ; 清数据k1
// 异或0x17(23)个字符
00401183 > /8A440D D0 mov al,byte ptr ss:[ebp+ecx-0x30] ; Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15 of switch 00401195
00401187 . |32840D 14FCFF>xor al,byte ptr ss:[ebp+ecx-0x3EC]
0040118E . |88840D E8FBFF>mov byte ptr ss:[ebp+ecx-0x418],al
00401195 . |83F9 17 cmp ecx,0x17 ; Switch (cases 0..16)
00401198 . |0F83 68020000 jnb CrackMe.00401406
0040119E . |C6440D D0 00 mov byte ptr ss:[ebp+ecx-0x30],0x0 ; 清用户输入c1
004011A3 . |C6840D 14FCFF>mov byte ptr ss:[ebp+ecx-0x3EC],0x0 ; 清数据k1
004011AB . |41 inc ecx
004011AC . |83F9 17 cmp ecx,0x17
004011AF .^\7C D2 jl short CrackMe.00401183
开始运算了
00401183 > /8A440D D0 mov al,byte ptr ss:[ebp+ecx-0x30] ; Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15 of switch 00401195
00401187 . |32840D 14FCFF>xor al,byte ptr ss:[ebp+ecx-0x3EC]
0040118E . |88840D E8FBFF>mov byte ptr ss:[ebp+ecx-0x418],al
00401195 . |83F9 17 cmp ecx,0x17 ; Switch (cases 0..16)
00401198 . |0F83 68020000 jnb CrackMe.00401406
0040119E . |C6440D D0 00 mov byte ptr ss:[ebp+ecx-0x30],0x0 ; 清用户输入c1
004011A3 . |C6840D 14FCFF>mov byte ptr ss:[ebp+ecx-0x3EC],0x0 ; 清数据k1
004011AB . |41 inc ecx
004011AC . |83F9 17 cmp ecx,0x17
004011AF .^\7C D2 jl short CrackMe.00401183 ; 异或运算23个字符
23个字符运算完成
0018F490 75 9C 6D FD 23 A1 42 BC 渕??
0018F4A0 76 B0 D2 B8 B5 AD EE C8 76 5B 87 AF 20 01 16 v耙傅葀[嚡
开始运算2 - 也是23个字符
EIP
004011C0 > /8A840D 90FDFF>mov al,byte ptr ss:[ebp+ecx-0x270] ; Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15 of switch 004011D2
18f640 与 18f498 异或 送到 0018F880
0018F640 7F A8 28 2A D9 30 47 A4 D6 FE 61 11 C9 3B 30 8A ?*?Gぶ?0?
0018F650 7F A8 28 2A D9 30 47 ?*?G..
0018F490 75 9C 6D FD 23 A1 42 BC 渕??
0018F4A0 76 B0 D2 B8 B5 AD EE v耙傅葀[嚡
al=0A (Line Feed)
堆栈 ss:[0018F880]=00
0018F880在运算前,已经清好了
0018F880 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0018F890 00 00 00 00 00 00 00 .......
算完之后
0018F880 0A 34 45 D7 FA 91 05 18 A0 4E B3 A9 7C 96 DE 42 .4E?燦畅|栟B
0018F890 09 F3 AF 85 F9 31 51 .蟑咘1Qv:
继续计算
004011F1 . B9 40000000 mov ecx,0x40 ;
004011F6 . 8DBD 90FCFFFF lea edi,dword ptr ss:[ebp-0x370] ; 0018f8d0 用户输入的30个字符
ecx = 00000040
esi = 00413798
edi = 0018F540
00413798 6E D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9 n?i_N瑾曵2b?
004137A8 02 18 74 95 1F C2 4D 33 3C F0 3B EC E9 81 4B 9C t?翸3<?扉並?
004137B8 0F 47 5C AD D9 09 B0 87 53 9B F2 E3 0F 01 92 8D
0x40 dword拷贝完成后, 总共0x100字节
0018F540 6E D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9 n?i_N瑾曵2b?
0018F550 02 18 74 95 1F C2 4D 33 3C F0 3B EC E9 81 4B 9C t?翸3<?扉並?
0018F560 0F 47 5C AD D9 09 B0 87 53 9B F2 E3 0F 01 92 8D
继续计算
00401205 . B9 40000000 mov ecx,0x40
0040120A . 8985 30FCFFFF mov dword ptr ss:[ebp-0x3D0],eax
eax=00000000
堆栈 ss:[0018F4E0]=0000003C
[0018F4E0] = 0 // 清0
开始拷贝数据
0040121B . F3:A5 rep movs dword ptr es:[edi],dword ptr ds:[esi]
esi = 00413898
edi = 0018F740
拷贝完成后
0018F740 D8 05 F6 6A E7 A2 0B 9B 54 8C DA 82 BD B6 A8 46 ?鰆纰
汿屭偨定F
0018F750 B1 36 2D 55 F7 81 63 FC 3F 0C FE 0B 4B 50 E2 17 ?-U鱽c?.?KP?
0018F760 F2 E1 27 5B 46 73 1C D0 E5 D7 8D C9 F2 70 94 53 蜥'[Fs绣讔沈p擲
040121F . 898D 2CFCFFFF mov dword ptr ss:[ebp-0x3D4],ecx ; 0x18f4dc用户输入字符串指针清0
00401225 > /8A4C0D D0 mov cl,byte ptr ss:[ebp+ecx-0x30] ; [18f880] => cl
0040122C . C785 0CFCFFFF>mov dword ptr ss:[ebp-0x3F4],0x6 ; dw ss:[18f4bc] = 6
00401236 . 898D 00FCFFFF mov dword ptr ss:[ebp-0x400],ecx ; ss:18f4b0 = a
00401242 . 8985 10FCFFFF mov dword ptr ss:[ebp-0x3F0],eax ; ss:18f4c0 = 0
一段查表跳转,往下跳了一句.
执行eax -=10 or eax += 1, or eax += 10
00401253 . /FF2495 0C1440>jmp dword ptr ds:[edx*4+0x40140C] ; CrackMe.0040125A
0040125A > \83E8 10 sub eax,0x10
0040125D . EB 09 jmp short CrackMe.00401268
0040125F > 40 inc eax
00401260 . EB 06 jmp short CrackMe.00401268
00401262 > 83C0 10 add eax,0x10
00401265 . EB 01 jmp short CrackMe.00401268
00401268 > \8985 30FCFFFF mov dword ptr ss:[ebp-0x3D0],eax
0040128F > \8985 04FCFFFF mov dword ptr ss:[ebp-0x3FC],eax ; ss:[0018f4b4] = 0
00401295 .- E9 DBB10300 jmp CrackMe.0043C475
不断的转圈, 如果不转圈了,进下面3个call
004013E2 . E8 CF090000 call CrackMe.00401DB6
004013E7 . 8BE5 mov esp,ebp
004013E9 . 5D pop ebp
004013EA . 8BE3 mov esp,ebx
004013EC . 5B pop ebx
004013ED . C3 retn
004013EE > 8B4D FC mov ecx,dword ptr ss:[ebp-0x4]
004013F1 . B8 01000000 mov eax,0x1
004013F6 . 5F pop edi
004013F7 . 33CD xor ecx,ebp
004013F9 . 5E pop esi ; CrackMe.00413998
004013FA . E8 B7090000 call CrackMe.00401DB6
004013FF . 8BE5 mov esp,ebp
00401401 . 5D pop ebp
00401402 . 8BE3 mov esp,ebx
00401404 . 5B pop ebx
00401405 . C3 retn
00401406 > E8 DF0A0000 call CrackMe.00401EEA ; Default case of switch 004011D2
来了
004013E2 . E8 CF090000 call CrackMe.00401DB6
004013D9 <CrackMe.failed_004013d9> > \33C0 xor eax,eax ; 失败了来这里
004013DB . 5F pop edi
004013DC . 5E pop esi
004013DD . 8B4D FC mov ecx,dword ptr ss:[ebp-0x4]
004013E0 . 33CD xor ecx,ebp
004013E2 . E8 CF090000 call <CrackMe.fn_anti> ; 没有影响eax
004013E7 . 8BE5 mov esp,ebp
004013E9 . 5D pop ebp
004013EA . 8BE3 mov esp,ebx
004013EC . 5B pop ebx
004013ED . C3 retn
004012EE ecx == 0x30 . 83F9 30 cmp ecx,0x30 ; ecx == 0x30失败
异或之前 ecx = 0x35失败
004012E3 (al >> 2) == 0x35 . C0C8 02 ror al,0x2 ; (al == 0xd4) 失败
0xD4怎么来的?
004012D4 . 8A8C05 90FCFFFF mov cl,byte ptr ss:[ebp+eax-0x370] ; 0xD4怎么来的?
第20次,变成了0xD4
在第19次时,跟一下.
ss:[ebp+eax-0x370] = ss:[0018f541] = 0xd4时失败
如果新打开OD, 运行第4次,就开始算出了错误的ss:[0018f540], 所以第3次,要去看一下.
第4次,就算好了错误值 ss:[0018f540]时,ss:[0018f541]的值已经确定是0xd4. 已经错了.
ss:[0018f541]往下简单移位计算, 只要算出不是30, 24,58就成功.
堆栈 ss:[0018F530]=00
cl=F0
堆栈 ss:[0018F520]=00
cl=E0
堆栈 ss:[0018F530]=00
cl=F0
下面就要算ss:[0018f540], 开始跟一下.
其实,第一次就已经算成了0xD4, 第一次就要跟.
111111111111111111111111111111
算数据的过程
这里的数据就是出去后,要比对的ss:[0018f541] 计算出30, 24,58, 只要不算出计算后的30,24,58,就注册成功了.
0040115E . 8B8D 2CFCFFFF mov ecx,dword ptr ss:[ebp-0x3D4]
00401164 . 8B41 17 mov eax,dword ptr ds:[ecx+0x17]
00401167 . 0F1041 07 movups xmm0,dqword ptr ds:[ecx+0x7]
0040116B . 8945 E0 mov dword ptr ss:[ebp-0x20],eax
0040116E . 0FB741 1B movzx eax,word ptr ds:[ecx+0x1B] ; 拷贝用户输入
00401172 . 66:8945 E4 mov word ptr ss:[ebp-0x1C],ax
00401176 . 0FB641 1D movzx eax,byte ptr ds:[ecx+0x1D]
0040117A . 33C9 xor ecx,ecx
0040117C . 0F1145 D0 movups dqword ptr ss:[ebp-0x30],xmm0
00401180 . 8845 E6 mov byte ptr ss:[ebp-0x1A],al
00401183 > 8A440D D0 mov al,byte ptr ss:[ebp+ecx-0x30] ; Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15 of switch 00401195
00401187 . 32840D 14FCFF>xor al,byte ptr ss:[ebp+ecx-0x3EC]
0040118E . 88840D E8FBFF>mov byte ptr ss:[ebp+ecx-0x418],al
00401195 . 83F9 17 cmp ecx,0x17 ; Switch (cases 0..16)
00401198 . 0F83 68020000 jnb CrackMe.00401406
0040119E . C6440D D0 00 mov byte ptr ss:[ebp+ecx-0x30],0x0 ; 清用户输入c1
004011A3 . C6840D 14FCFF>mov byte ptr ss:[ebp+ecx-0x3EC],0x0 ; 清数据k1
00413798谁算出来的?
00413798 6E D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9 n?i_N瑾曵2b?
004137A8 02 18 74 95 1F C2 4D 33 3C F0 3B EC E9 81 4B 9C t?翸3<?扉並?
004137B8 0F 47 5C AD D9 09 B0 87 53 9B F2 E3 0F 01 92 8D
G\.皣S涷?拲
004011FC . BE 98374100 mov esi,CrackMe.00413798
00413798决定注册是否成功,[00413799]为d4就失败.
00413798 是内置的数组啊
.data:00413798 db 6Eh, 0D4h, 1Bh, 69h, 5Fh, 4Eh, 0E8h, 0AAh, 95h, 0F6h, 0AFh, 0CEh, 32h, 1Ah, 62h, 0D9h; 0
.data:00413798 db 2, 18h, 74h, 95h, 1Fh, 0C2h, 4Dh, 33h, 3Ch, 0F0h, 3Bh, 0ECh, 0E9h, 81h, 4Bh, 9Ch; 16
.data:00413798 db 0Fh, 47h, 5Ch, 0ADh, 0D9h, 9, 0B0h, 87h, 53h, 9Bh, 0F2h, 0E3h, 0Fh, 1, 92h, 8Dh; 32
.data:00413798 db 0C2h, 0F5h, 0Ch, 0DDh, 42h, 0CCh, 0AFh, 0B4h, 0D5h, 0E4h, 86h, 0D3h, 9Ah, 0Bh, 62h, 63h; 48
没招了,要穷举了
再不穷举,就要放弃治疗了...
从前面的调试可以看出, 口令为30个字符, 再没其他限制.
0042FCFA 时,看栈内数据,有前7个字符
0040116E开始,可以看到cm拿用户输入的前7个字符做了任务.
004015AE时,看到前7个字符的拷贝动作
0042F732 时,
esi = 0018F140是1111111的0x64长度缓冲区,
拷贝到 edi = 0018F0DC
ecx = edx = 0x64
0042F623 时,栈内有用户输入的全部注册码,和前7个注册码,而且前7个注册码在前
0040116E时,拷贝了前7个字符
很多操作都进了.WProctect 0x0043xxxx, 对虚拟机不甚了解, 跟进去看了下,无功而返.
只能看0040xxxx的操作了.
但是咋拿前7个字符计算的,就没看到了,可能在.WProctect 0x0043xxxx.
综合调试记录分析,cm使用前7个字符作为注册码来源的可能性很大.
先穷举30个字符中的前7个,如果不行,就全部穷举.
将PE作为DLL载入, 穷举前7个字符,后面的23个字符不变,运行5秒钟后,得到一个注册码. 00000bQ11111111111111111111111, 直接运行cm, 输入注册码成功注册^_^
穷举程序片段
// hw.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <windows.h>
DWORD Base;
DWORD pfn = 0;
// void __declspec(naked) fnCalcRegSn1/*<eax>*/(signed int a1/*<edx>*/, int a2/*<ecx>*/, int a3/*<ebp>*/)
// {
//
// }
typedef void (*PFN_fnCalcRegSn1)/*<eax>*/();
char ucAryPwdCharSet[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
int main(int argc, char* argv[])
{
// v4 和szBuf必须前2个, pfn1要用
int v4; // ebp@0
CHAR szBuf[0x200] = {'\0'}; // [sp+4h] [bp-204h]@7
int iBuf = 0;
PFN_fnCalcRegSn1 pfn1 = NULL;
int iRc = -2;
DWORD dwProt;
int iLenAryPwdCharSet = sizeof(ucAryPwdCharSet);
int iLoop1 = 0;
int iLoop2 = 0;
int iLoop3 = 0;
int iLoop4 = 0;
int iLoop5 = 0;
int iLoop6 = 0;
int iLoop7 = 0;
// 284 KB (290,816 字节) => 290816 => 0x47000
// CrackMe.exe 默认装载地址为0x0040xxxx
// 00401000 > $ 53 push ebx ; 注册码比对
// 程序基地址改为 0x200000
Base = (DWORD)LoadLibraryEx("CrackMe.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
VirtualProtect((LPVOID)Base, 0x47000, PAGE_EXECUTE_READWRITE, &dwProt);
if (Base == 0x400000) {
// 00401000 > $ 53 push ebx ; 注册码比对
pfn = Base + 0x1000;
pfn1 = (PFN_fnCalcRegSn1)pfn;
// 根据调试记录, 前7个字母是此cm的产生数据的原数据
// 穷举前7个字符, 规模不大
for (iLoop1 = 0; iLoop1 < iLenAryPwdCharSet; iLoop1++) {
for (iLoop2 = 0; iLoop2 < iLenAryPwdCharSet; iLoop2++) {
for (iLoop3 = 0; iLoop3 < iLenAryPwdCharSet; iLoop3++) {
for (iLoop4 = 0; iLoop4 < iLenAryPwdCharSet; iLoop4++) {
for (iLoop5 = 0; iLoop5 < iLenAryPwdCharSet; iLoop5++) {
for (iLoop6 = 0; iLoop6 < iLenAryPwdCharSet; iLoop6++) {
for (iLoop7 = 0; iLoop7 < iLenAryPwdCharSet; iLoop7++) {
strcpy(szBuf, "111111111111111111111111111111");
szBuf[0] = ucAryPwdCharSet[iLoop1];
szBuf[1] = ucAryPwdCharSet[iLoop2];
szBuf[2] = ucAryPwdCharSet[iLoop3];
szBuf[3] = ucAryPwdCharSet[iLoop4];
szBuf[4] = ucAryPwdCharSet[iLoop5];
szBuf[5] = ucAryPwdCharSet[iLoop6];
szBuf[6] = ucAryPwdCharSet[iLoop7];
iBuf = (int)szBuf;
__asm {
mov edx, 30
mov ecx, iBuf
};
iRc = 0;
pfn1();
__asm {
mov iRc, eax
};
if (iRc > 0) {
::MessageBox(NULL, szBuf, "找到注册码!", MB_OK);
OutputDebugString("找到注册码!\r\n");
OutputDebugString(szBuf);
OutputDebugString("\r\n");
} else {
OutputDebugString(".");
}
}
}
}
}
}
}
}
} else {
// 再FreeLibrary, 再LoadLibraryEx没用的, 必须重新启动一次
::MessageBox(NULL, "错误", "载入目标程序失败, 请重新运行一次", MB_OK);
}
if (0 != Base) {
FreeLibrary((HMODULE)Base);
Base = 0;
}
::MessageBox(NULL, "END", "穷举结束", MB_OK);
return 0;
}
感谢JoenChen制作的cm^_^
--------------------------------------------------------------------------------
【经验总结】
大家都开始上密码学,虚拟机,以后要针对这方面加强练习.
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2016年11月09日 17:31:12
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
他的文章
谁下载
看原图
赞赏
雪币:
留言: