【文章标题】: FaNtOm's Crackme #8 新手算法分析+C注册机
【文章作者】: Vcsoft
【作者邮箱】: [email]vcsoftpro@163.com[/email]
【软件名称】: FaNtOm's Crackme #8
【保护方式】: Serial Numer
【使用工具】: peid/od
【操作平台】: winxp
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
首先用 PEiD 检查,发现用UPX加过壳,类型 UPX 0.89.6 - 1.02 / 1.05 - 1.24 -> Markus & Laszlo,这个壳很老了,
用 PEiD自带的插件 "Unpacker for UPX" 就可脱之。
OD载入脱壳后的程序,用查找字符串的方法找不出错误信息,只好下断点 bp GetDlgItemTextA
运行程序,输入试练码,点击Verify后程序中断在USER32领空,F8单步直到返回程序领空
00401295 6A 64 push 64
00401297 68 74314000 push 00403174 ; ASCII "87654321"
0040129C 68 EA030000 push 3EA
004012A1 FF75 08 push dword ptr [ebp+8]
004012A4 E8 B5000000 call <jmp.&USER32.GetDlgItemTextA>
------------------------------┬---------------------------------------返回到这里
004012A9 6A 64 push 64
004012AB 68 10314000 push 00403110 ; ASCII "vcsoft"
004012B0 68 E9030000 push 3E9
004012B5 FF75 08 push dword ptr [ebp+8]
004012B8 E8 A1000000 call <jmp.&USER32.GetDlgItemTextA>
004012BD 8D05 10314000 lea eax, [403110] ; 传送输入的name地址
004012C3 50 push eax
004012C4 8D05 74314000 lea eax, [403174] ; 传送输入的code地址
004012CA 50 push eax
004012CB FF15 0C314000 call [40310C] ; CM8.dll中的产生真code的函数
---------------┴--------------------关键CALL,跟入
10001287 > 55 push ebp
10001288 8BEC mov ebp, esp
1000128A FF75 0C push dword ptr [ebp+C]
1000128D E8 85000000 call 10001317 ; 通过检查name中的结束符来计算字符串长度
eax = 字符串长度
10001292 83F8 00 cmp eax, 0 ; 是否输入了name
10001295 74 71 je short 10001308
10001297 83F8 64 cmp eax, 64 ; name 长度 >= 100 就失败
1000129A 7D 6C jge short 10001308
1000129C 50 push eax ; name 长度入栈
1000129D FF75 08 push dword ptr [ebp+8]
100012A0 E8 72000000 call 10001317 ; 这次计算code长度,同上一个函数
100012A5 83F8 00 cmp eax, 0 ; 是否输入了 code
100012A8 74 5E je short 10001308
100012AA 83F8 64 cmp eax, 64 ; 长度也不能 >= 100
100012AD 7D 59 jge short 10001308
100012AF 8B75 0C mov esi, [ebp+C] ; esi = name 地址
100012B2 8D3D 24300010 lea edi, [10003024]
100012B8 59 pop ecx ; name 长度出栈 以下len(name)表示
100012B9 41 inc ecx ; j=len(name)+1
100012BA BB 42000000 mov ebx, 42 ; ebx = 0x42
100012BF 03D9 add ebx, ecx ; t1 = 0x42 + len(name) + 1
100012C1 0FB606 movzx eax, byte ptr [esi] ; eax = name(i)
100012C4 46 inc esi ; i++
100012C5 33C3 xor eax, ebx ; name(i)^t1
100012C7 C1C0 05 rol eax, 5 ; 循环左移5位
100012CA C1E8 02 shr eax, 2 ; 再逻辑右移2位
100012CD B3 10 mov bl, 10 ; bl = 0x10
100012CF F6F3 div bl ; t2 / 0x10
100012D1 80C4 30 add ah, 30 ; 无符号除法的余数t2+0x30
100012D4 80FC 40 cmp ah, 40 ; 结果是否<= 0x40
100012D7 7E 03 jle short 100012DC
100012D9 80C4 07 add ah, 7 ; 如果大于 则再加上7
100012DC 8827 mov [edi], ah ; <= 保存
100012DE 47 inc edi ;
100012DF 3C 10 cmp al, 10 ; 无符号除法的商 t3
100012E1 7D 08 jge short 100012EB ; 如果t3 < 0x10
100012E3 66:C1E0 08 shl ax, 8 ; ax 逻辑左移8位
----------------------------------------------------------------------这里左移8位是为了使 ah = al,即t2=t3
100012E7 B0 11 mov al, 11 ; t3= al = 0x11
100012E9 ^\EB E6 jmp short 100012D1 ; 内层循环
100012EB 49 dec ecx ; j--
100012EC 83F9 00 cmp ecx, 0
100012EF 74 02 je short 100012F3 ; j=0 外层循环结束
100012F1 ^ EB C7 jmp short 100012BA ; 外层循环
100012F3 C607 00 mov byte ptr [edi], 0
100012F6 68 24300010 push 10003024 ; 计算后真码
100012FB FF75 08 push dword ptr [ebp+8] ; 输入的假码
100012FE E8 29000000 call 1000132C ; jmp 到 kernel32.lstrcmpA
10001303 83F8 00 cmp eax, 0 ; 两字符串相同 lstrcmp返回0
10001306 74 09 je short 10001311
10001308 B8 01000000 mov eax, 1 字串不同返回1
1000130D C9 leave
1000130E C2 0800 retn 8
10001311 33C0 xor eax, eax 相同则返回0
10001313 C9 leave
10001314 C2 0800 retn 8
-----------------------------------------------------------------------------------------------------------------
004012D1 85C0 test eax, eax 测试返回值是否为0
004012D3 74 08 je short 004012DD
004012D5 8D05 A0304000 lea eax, [4030A0] MessageBox失败字串地址
004012DB EB 06 jmp short 004012E3
004012DD 8D05 63304000 lea eax, [403063] MessageBox成功字串地址
004012E3 6A 40 push 40
004012E5 68 54304000 push 00403054 ; ASCII "Serial Results"
004012EA 50 push eax
004012EB 6A 00 push 0
004012ED E8 8A000000 call <jmp.&USER32.MessageBoxA>
小结:
1、输入的注册名和注册码必须小于100 (谁的名字这么长?)
2、name长度+1做为外层循环的计数器j 再加0x42的值记为t1
name的ASCII码与t1异或,先循环左移5位,再逻辑右移2位,然后除以0x10
余数记为t2,商记为t3
3、t2加0x30,如果>0x40 则再加上7,否则直接保存
4、如果t3<0x10,则t2=t3,t3=0x11, 从第3步循环,直到t3>=0x10跳出内层循环,
j--,判断下一字符
比如: name vcsoft
j=6+1=7
t1 = j+0x42 = 0x49
"v"ASCII为0x76
0x76^0x49=0x3F 0x3F<<5=0x07E0 0x07E0>>2=0x01F8 0x01F8/0x10=0x081F 08为余数记为t2 1F为商记为t3
t2=0x08+0x30=0x38 0x38<0x40 保存结果0x38
t3=0x1F 0x1F>0x10 跳出内层循环 j-1=6 进入下一循环计算第2位
所以注册码第一位为0x38("8")
给出3组序号:
name: vcsoft code:8808808
name: !@#$%^&*() code:0060000;80888
name: www.pediy.com code:80880000888088 做个广告!
--------------------------------------------------------------------------------
注册机C代码:在WinXP-SP2+Win-TC下编译通过
main()
{
short int t1,t2,t3,namelen,i=0,j=0;
char name[100],serial[100];
printf("Please input your name: ");
scanf("%s",&name);
namelen=strlen(name);
for(;namelen>=0;namelen--)
{
t1=(name[i]^(namelen+67))<<3;
i++;
t2=t1%16; t3=t1/16;
loop:
t2+=48;
if(t2>64) t2+=7;
serial[j]=t2;
j++;
while(1)
{
if(t3<16)
{
t2=t3;
t3=17;
goto loop;
}else break;
}
}
printf("\n\n\n\n\nThis is your password: %s",serial);
getch();
return 0;
}
本人是看雪论坛的菜鸟一只,文中如有不对的地方麻烦高手指正,向高手学习。
内附:CRACKME8.EXE FaNtOm's Crackme #8
CM8.DLL CRACKME8的链接库
KeyGen.exe 注册机
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)