jmzhwf一个好玩的CM 算法分析+注册机
本帖最后由 missviola 于 2009-10-20 15:59 编辑【破文标题】jmzhwf一个好玩的CM 算法分析+注册机
【破文作者】missviola
【破解工具】PEID OD
【破解平台】Windows XP
【原版下载】http://www.52pojie.cn/thread-33387-1-1.html
【破解声明】只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
------------------------------------------------------------------------
【破解过程】今天在52pojie下了这个crackme,跟了一下感觉挺不错的,写点心得和大家分享一下吧。首先我们用PEID查壳,发现为
MASM32 / TASM32,由汇编语言编写。用OD载入,在第二个GetDlgItemTextA后面F2下断点,输入name:viola,password:123456,
点击Register,发现没有断下。。。 -_-||| 这个断点可是小菜我在破解那些汇编写的程序时屡试不爽的一个方法啊。看来此路不通
,我们只能想其他方法。我们找到第二个GetDlgItemTextA写入内存的地址,下内存写入断点试试看吧。
004012C6 . FF35 40304000PUSH DWORD PTR DS: ; /Count = 14 (20.)
004012CC . 68 23304000 PUSH 3.00403023 ; |Buffer = 3.00403023
004012D1 . 68 ED030000 PUSH 3ED ; |ControlID = 3ED (1005.)
004012D6 . FF75 08 PUSH DWORD PTR SS: ; |hWnd
004012D9 . E8 BE020000 CALL <JMP.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
我们在00403023这里下内存写入断点,输入刚才的注册信息。点击Register,终于断下来了,我们来到这里:
77D32192 8B4D 08 MOV ECX,DWORD PTR SS:
77D32195 E8 4663FEFF CALL user32.77D184E0
77D3219A 8BF0 MOV ESI,EAX
77D3219C 8975 E4 MOV DWORD PTR SS:,ESI
77D3219F 3BF3 CMP ESI,EBX
77D321A1 0F84 A3F20000 JE user32.77D4144A
77D321A7 56 PUSH ESI
77D321A8 E8 6A6CFFFF CALL user32.77D28E17
77D321AD 6A 01 PUSH 1
77D321AF 57 PUSH EDI
77D321B0 FF75 10 PUSH DWORD PTR SS:
77D321B3 6A 0D PUSH 0D
77D321B5 56 PUSH ESI
77D321B6 85C0 TEST EAX,EAX
77D321B8 0F85 ED8E0000 JNZ user32.77D3B0AB
77D321BE E8 177BFFFF CALL user32.77D29CDA
77D321C3 834D FC FF OR DWORD PTR SS:,FFFFFFFF
77D321C7 E8 3464FEFF CALL user32.77D18600
77D321CC C2 0C00 RETN 0C
我们一路F8单步走回去,来到这里:
004012DE . E8 4F000000 CALL 3.00401332
我们F7跟进去看看:
00401332 $ 33C0 XOR EAX,EAX
00401334 . B9 00000000 MOV ECX,0
00401339 . BE 23304000 MOV ESI,3.00403023 ;ASCII "123456"
0040133E . 8A06 MOV AL,BYTE PTR DS:
00401340 . EB 10 JMP SHORT 3.00401352
00401342 > 0FB6C0 MOVZX EAX,AL 依次取password各位ASCII码
00401345 . 80B8 50314000 >CMP BYTE PTR DS:,2 这里有个小机关进行比较
0040134C . 75 0A JNZ SHORT 3.00401358
0040134E . 41 INC ECX
0040134F . 8A0431 MOV AL,BYTE PTR DS:
00401352 > 3C 00 CMP AL,0 有没有比较完
00401354 .^77 EC JA SHORT 3.00401342 没有跳回去继续比较
这里401345这里是比较password各位的ASCII码值加上固定地址403150以后,该内存地址的值是不是为2,我们d 403150过去看看,发
现如下:
0040315000 09 09 09 09 09 09 09 09 06 05 09 09 05 09 09.............
0040316009 09 09 09 09 09 09 09 09 09 09 09 09 09 09 09................
0040317006 05 05 05 09 05 05 05 05 05 04 04 05 04 05 04.
0040318001 01 01 01 01 01 01 01 01 01 05 05 05 05 05 05
0040319009 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02.
004031A002 02 02 02 02 02 02 02 02 02 02 05 05 05 05 05
从403091到4031AA这里都是2,也就说password各位的ASCII码值范围应该在0x41-0x49之间,也就说password必须为大写字母。
00401356 . EB 07 JMP SHORT 3.0040135F
00401358 > C605 44304000 >MOV BYTE PTR DS:,40
0040135F > BE 00304000 MOV ESI,3.00403000 ;ASCII "viola"
00401364 . 33C9 XOR ECX,ECX
00401366 . B8 01000000 MOV EAX,1
0040136B . 33D2 XOR EDX,EDX
0040136D . C705 45304000 >MOV DWORD PTR DS:,0 =0
00401377 > B9 00000000 MOV ECX,0
0040137C . 8A0C32 MOV CL,BYTE PTR DS: 依次取name各位ASCII码值
0040137F . 80F9 00 CMP CL,0 有没有取完
00401382 . 74 09 JE SHORT 3.0040138D
00401384 . 42 INC EDX 循环次数+1
00401385 . 000D 45304000ADD BYTE PTR DS:,CL 进行累加,初始值为0
0040138B .^EB EA JMP SHORT 3.00401377
上面的这个小循环是分别取name各位的ASCII码值进行累加,取十六进制结果的最后结果进行下一步运算,我们记为zonghe1,接着跟
下去。
0040138D > A1 45304000 MOV EAX,DWORD PTR DS: zonghe1送eax
00401392 . B9 18000000 MOV ECX,18 0x18送ecx
00401397 . 99 CDQ edx清零
00401398 . F7F9 IDIV ECX zonghe1 idiv 0x18
0040139A . 8815 4F304000MOV BYTE PTR DS:,DL 余数送
004013A0 . 8A0D 44304000MOV CL,BYTE PTR DS:
004013A6 . 80F9 40 CMP CL,40 如果刚才password比较的时候出错的话,这里CL就会为0x40,
也就是会跳向失败
004013A9 . 75 05 JNZ SHORT 3.004013B0
004013AB . E9 45010000 JMP 3.004014F5
004013B0 > E9 CB000000 JMP 3.00401480 跳向下一步运算
上面的第二个循环分别取name的各位的ASCII码值,相加以后除以0x18,取余数送,记为yushu,我们继续看~~
00401480 > E8 8B000000 CALL 3.00401510 F7跟进
00401510 $ A0 24304000 MOV AL,BYTE PTR DS: 取password第二位的ASCII码值
00401515 . 3C 45 CMP AL,45 比较是否为0x45(0x45 = E)
00401517 ^75 DC JNZ SHORT 3.004014F5 不相等就跳向失败
00401519 $ BF 96124000 MOV EDI,3.00401296 ;Entry address
0040151E . B9 00010000 MOV ECX,100
00401523 . B0 99 MOV AL,99
00401525 . 34 55 XOR AL,55
00401527 . F2:AE REPNE SCAS BYTE PTR ES:
00401529 . 85C9 TEST ECX,ECX
0040152B . 74 06 JE SHORT 3.00401533
0040152D . 5E POP ESI
0040152E . 33F6 XOR ESI,ESI
00401530 . 57 PUSH EDI
00401531 .^EB C2 JMP SHORT 3.004014F5
00401533 > C3 RETN
从上面这个call我们可以知道password第二位必须为E。
00401485 . 33DB XOR EBX,EBX
00401487 . BF 80144000 MOV EDI,3.00401480
0040148C . 83EF 60 SUB EDI,60
0040148F . B8 DE000000 MOV EAX,0DE
00401494 . 83F0 12 XOR EAX,12
00401497 . B9 59000000 MOV ECX,59
0040149C . F2:AE REPNE SCAS BYTE PTR ES:
0040149E . 85C9 TEST ECX,ECX
004014A0 . 74 06 JE SHORT 3.004014A8
004014A2 . 5E POP ESI
004014A3 . 33F6 XOR ESI,ESI
004014A5 . 57 PUSH EDI
004014A6 . EB 4D JMP SHORT 3.004014F5
004014A8 > C3 RETN
走过上面的retn我们来到这里。
004012E3 . 68 53304000 PUSH 3.00403053 ;zwatrqlcghpsxyenvbjdfkmu
004012E8 . E8 C9000000 CALL 3.004013B6 F7跟进,注意上面的字符串
004013B6 $ 55 PUSH EBP
004013B7 . 8BEC MOV EBP,ESP
004013B9 . 68 23304000 PUSH 3.00403023 ;
004013BE . E8 7D010000 CALL 3.00401540 这个call获取password长度
004013C3 . 83F8 0A CMP EAX,0A 同0xA比较
004013C6 0F85 29010000JNZ 3.004014F5 不相等就跳向失败
到这里我们知道password必须要为10位。
004013CC . BE 23304000 MOV ESI,3.00403023 ;
004013D1 . B8 00000000 MOV EAX,0
004013D6 . BB 00000000 MOV EBX,0
004013DB . 33C9 XOR ECX,ECX
004013DD . EB 06 JMP SHORT 3.004013E5
004013DF > 8A0C30 MOV CL,BYTE PTR DS: 依次取password各位的ASCII码值
004013E2 . 03D9 ADD EBX,ECX 相加,ebx初始值为0
004013E4 . 40 INC EAX 循环次数+1
004013E5 > 83F8 09 CMP EAX,9 同9比较
004013E8 .^72 F5 JB SHORT 3.004013DF 没算完接着跳回去算
004013EA . 8BC3 MOV EAX,EBX 和送eax
004013EC . B9 09000000 MOV ECX,9 ecx=0x9
004013F1 . 99 CDQ
004013F2 . F7F9 IDIV ECX 刚才运算的和 idiv 9
004013F4 . A3 4A304000 MOV DWORD PTR DS:,EAX 除下来的商送
004013F9 . 8B7D 08 MOV EDI,DWORD PTR SS: 字符串 ZWATRQLCGHPSXYENVBJDFKMU 送edi
004013FC . 8A15 4F304000MOV DL,BYTE PTR DS: yushu1送dl
00401402 . 8AC2 MOV AL,DL
00401404 . 3C 18 CMP AL,18 yushu1同0x18比较
00401406 . 76 02 JBE SHORT 3.0040140A 小于等于就跳走
00401408 . 2C 18 SUB AL,18 大于就减去0x18
0040140A > A2 4E304000 MOV BYTE PTR DS:,AL
0040140F . 33C0 XOR EAX,EAX
00401411 . A0 4E304000 MOV AL,BYTE PTR DS:
00401416 . 8A2438 MOV AH,BYTE PTR DS: 根据yushu1的大小取字符串对应位置的字符
00401419 . 8A36 MOV DH,BYTE PTR DS: 取password第一位
0040141B . 38F4 CMP AH,DH 比较
0040141D 0F85 D2000000JNZ 3.004014F5 不相等就跳向失败
这里用yushu1作为下标来到字符串中获取对应的字符。比如我这里的yushu1=3,那么就是取字符串中下标为3的那个字符,也就是“T
”(字符串的下标从0开始)。
00401423 . 80EE 41 SUB DH,41 注册码第一位ASCII码值减去0x41
00401426 . 8AF2 MOV DH,DL
00401428 . B4 00 MOV AH,0
0040142A . A2 4E304000 MOV BYTE PTR DS:,AL
0040142F . 33C0 XOR EAX,EAX
00401431 . A0 4E304000 MOV AL,BYTE PTR DS:
00401436 . 02C2 ADD AL,DL yushu1 = yushu1*2
00401438 . 3C 18 CMP AL,18
0040143A . 76 02 JBE SHORT 3.0040143E 同0x18比较
0040143C . 2C 18 SUB AL,18
0040143E > B9 02000000 MOV ECX,2
00401443 . 8A2438 MOV AH,BYTE PTR DS: 根据新的yushu1来取字符串中的字符
00401446 . 8A3431 MOV DH,BYTE PTR DS: 取password第三位
00401449 . 38F4 CMP AH,DH 比较
0040144B 0F85 A4000000JNZ 3.004014F5 不相等就跳向失败
00401451 . EB 24 JMP SHORT 3.00401477
这里将第一次的yushu1乘以2以后,有了新的下标,去取字符串中的对应字符,作为password的第三位。(之前我们已经知道了
password第二位为E)
00401453 > A2 4E304000 MOV BYTE PTR DS:,AL 新的yushu1送
00401458 . 33C0 XOR EAX,EAX
0040145A . A0 4E304000 MOV AL,BYTE PTR DS:
0040145F . 80EE 41 SUB DH,41 当前位置的password字符的ASCII码值减去0x41,记为A1
00401462 . 8AD6 MOV DL,DH
00401464 . 41 INC ECX 循环次数+1
00401465 . 02C2 ADD AL,DL yushu1 = yushu1 + A1
00401467 . 3C 18 CMP AL,18 同18比较
00401469 . 76 02 JBE SHORT 3.0040146D
0040146B . 2C 18 SUB AL,18
0040146D > 8A2438 MOV AH,BYTE PTR DS: 根据新的下标取字符
00401470 . 8A3431 MOV DH,BYTE PTR DS: 依次取password第四到第九位
00401473 . 38F4 CMP AH,DH 比较
00401475 75 7E JNZ SHORT 3.004014F5 不相等就跳向失败
00401477 > 83F9 08 CMP ECX,8
0040147A .^72 D7 JB SHORT 3.00401453 没算完就跳回去继续算
0040147C . C9 LEAVE
0040147D . C2 0400 RETN 4
上面的这个循环就是将当前位置password的ASCII码值减去0x41,加上yushu1,作为下一位的下标到字符串中去取字符。这个循环算
出了password的第四到第九位。步过retn的后,我们来到这里:
004012ED . E8 DC010000 CALL 3.004014CE 继续跟进
004014CE/$ BE 23304000 MOV ESI,3.00403023
004014D3|. A1 4A304000 MOV EAX,DWORD PTR DS: 取password各位运算以后的商
004014D8|. 8A5E 09 MOV BL,BYTE PTR DS: 取password第十位
004014DB|. 38D8 CMP AL,BL 比较
004014DD 75 16 JNZ SHORT 3.004014F5 不相等就跳向失败
004014DF|. B8 6C304000 MOV EAX,3.0040306C ;great job!
004014E4|. 8BD8 MOV EBX,EAX
004014E6|. 83C3 0B ADD EBX,0B
004014E9|. 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004014EB|. 50 PUSH EAX ; |Title => "Great Job!"
004014EC|. 53 PUSH EBX ; |Text => "You have completed Key Gen Me #1."
004014ED|. 6A 00 PUSH 0 ; |hOwner = NULL
004014EF|. E8 B4000000 CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA
004014F4|. C3 RETN
------------------------------------------------------------------------
【破解总结】这个crackme分析到这里,算法应该很清晰了,我们最后来总结一下:
1.password必须为大写字符,且长度要为10,第二位是E。
2.将name各位的ASCII码值进行累加,取和的十六进制形式的最后两位,除以0x1B以后,取余数,记为yushu1。
3.将yushu1作为下标,到字符串ZWATRQLCGHPSXYENVBJDFKMU中取对应位置的字符,作为password的第一位。
4.将yushu1*2,作为第三位的下标,去字符串中取字符。
5.取当前位置的password字符的ASCII码值,减去0x41以后,再加上yushu1,作为新的下标,依次到字符串中取字符。依次类推,分
别算出password的第四到第九位。期间如果算出新的下标大于0x18的话,减去0x18,再作为新的下标。
6.password最后一位字符为前9位字符的ASCII码值累加,除以9以后的商对应的ASCII码值的字符。
最后送上我用python编写的注册机源代码(在python 2.5+windows xp下编译通过):
import sys
zonghe1 = 0
zifuchuan = 'ZWATRQLCGHPSXYENVBJDFKMU'
zonghe2 = 0
sn = ''
regname = raw_input('Please input youre regname:')
for i in range(0, len(regname)):
zonghe1 = zonghe1 + ord(regname)
zonghe1 = zonghe1 & 0xFF
yushu1 = zonghe1 % 24
try:
sn = zifuchuan + 'E' + zifuchuan
yushu1 = yushu1*2
except:
print 'your regname is invalid!'
print 'press any key to exit'
raw_input()
sys.exit()
for j in range(0, 6):
yushu1 = (ord(zifuchuan)-65) + yushu1
if yushu1 >= 24:
yushu1 = yushu1-24
else:
yushu1 = yushu1
sn = sn + zifuchuan
for k in range(0, len(sn)):
zonghe2 = zonghe2 + ord(sn)
zonghe2 = zonghe2 / 9
print "Your register string is:%s" % (sn + chr(zonghe2))
print 'press any key to exit'
raw_input()
------------------------------------------------------------------------
【版权声明】本文原创于52pojie技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
LZ分析得不错。
我来补充一下:之所以下INT3断点程序会马上弹出失败的消息窗口,是由于程序中有很多检验----多次搜索代码段里的0xCC,如果发现代码段里某个字节为0xCC,就马上弹出失败的消息窗口。这一点仔细跟踪就会发现了。为了能够把程序断下来,并且不被检测出来,可以改下硬件断点。
顺便给出我写的注册机代码:(VC 6 下测试通过)
#include <stdio.h>
#include <string.h>
int main()
{
char table[] = {"ZWATRQLCGHPSXYENVBJDFKMU"},
UserName,
Key;
unsigned index, sum = 0;
printf("UserName:");
scanf("%s", UserName);
for (unsigned i=0; i<strlen(UserName); i++)
{
sum += UserName;
}
sum &= 0xff;
index = sum % 0x18;
Key = table;
Key = 'E';
index *= 2;
if (index > 0x18)
{
index -= 0x18;
}
Key = table;
for (i=3; i<9; i++)
{
index += Key - 'A';
if (index > 0x18)
{
index -= 0x18;
}
Key = table;
}
sum = 0;
for (i=0; i<9; i++)
{
sum += Key;
}
Key = (char)(sum/9);
Key = '\0';
printf("Key :%s\n", Key);
return 0;
} 都是高手啊,华山论剑啊 IFY嘿嘿~~ 都是高人,我也来学习一下 本帖最后由 jmzhwf 于 2009-10-17 23:15 编辑
分析的非常好~~~~~
其实这个CM最精华的就是这一句
REPNE SCAS BYTE PTR ES:
如果在代码中发现了CC既INT 3断点
就会在ECX记录它出现的位置
如果不为0就跳向死亡
页:
[1]