【文章标题】: WAKU的第二个破文----Rith's CrackMe #1(非常简单)
【文章作者】: WAKU
【作者邮箱】: wakuwakuwawaku@163.com
【作者主页】: www.rzz.cn
【下载地址】: 见附件
【使用工具】: PEID OD
--------------------------------------------------------------------------------
【详细过程】
最近学习脱壳没什么进展,怕自己失去信心在<<加密与解密第二版>>中找了个CrackMe玩玩.
用PEID查了一个,无壳,用VC6编写.
用OD载入运行一下,是NAME/SERIAL型验证方式.乱输了一对,点Check It!.哦耶!没反应-_-!那我也看看有什么字符串,用
"超级字串参考+"看了下,发现了congratulations!well done cracker!嘿嘿,出错不提示成功有提示也行~双击来到代码段,
往上下都翻一翻,在一大段NOP指令和一个REN指令之间,就应该是验证注册码的函数代码了,WAKU把这段代码一段段粘上来,并
加上详细注释: (为了简洁, 注释中的>不是大于的意思,是把什么放到什么的意思)
00401580 . 6A FF PUSH -1
00401582 . 68 B81B4000 PUSH Rith_Cra.00401BB8 ; SE 处理程序安装
00401587 . 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
0040158D . 50 PUSH EAX
0040158E . 64:8925 00000>MOV DWORD PTR FS:[0],ESP
00401595 . 83EC 0C SUB ESP,0C
00401598 . 53 PUSH EBX
00401599 . 55 PUSH EBP
0040159A . 56 PUSH ESI
0040159B . 8BF1 MOV ESI,ECX
0040159D . 57 PUSH EDI
0040159E . 68 48304000 PUSH Rith_Cra.00403048 ; 31415926535897932384 这个字符串很可疑,好像是圆周率呢...
004015A3 . 8D4C24 14 LEA ECX,DWORD PTR SS:[ESP+14]
004015A7 . 897424 1C MOV DWORD PTR SS:[ESP+1C],ESI
004015AB . E8 FA020000 CALL <JMP.&MFC42.#537_??0CString@@QAE@PB>
004015B0 . 6A 01 PUSH 1
004015B2 . 8BCE MOV ECX,ESI
004015B4 . C74424 28 000>MOV DWORD PTR SS:[ESP+28],0
004015BC . E8 E3020000 CALL <JMP.&MFC42.#6334_?UpdateData@CWnd@>
004015C1 . 8B7E 60 MOV EDI,DWORD PTR DS:[ESI+60] ; 用户名 > EDI
004015C4 . 8B5F F8 MOV EBX,DWORD PTR DS:[EDI-8] ; 用户名长度 > EBX
004015C7 . 83FB 05 CMP EBX,5
004015CA . 7C 7E JL SHORT Rith_Cra.0040164A ; 长度小于5就OVER
004015CC . 8B46 64 MOV EAX,DWORD PTR DS:[ESI+64] ; 注册码 > EAX
004015CF . 894424 14 MOV DWORD PTR SS:[ESP+14],EAX
004015D3 . 3958 F8 CMP DWORD PTR DS:[EAX-8],EBX ; 注册码长度如果不和用户名长度相等,也OVER
004015D6 . 75 72 JNZ SHORT Rith_Cra.0040164A
004015D8 . 83FB 14 CMP EBX,14 ; 注册码长度大于0x14也不行,事真多
004015DB . 7F 6D JG SHORT Rith_Cra.0040164A
004015DD . 33C9 XOR ECX,ECX ; ECX归0,为下面的循环做准备
004015DF . 85DB TEST EBX,EBX
004015E1 . 7E 54 JLE SHORT Rith_Cra.00401637 ; 如果注册码全验证成功则弹出正确提示
004015E3 . 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10] ; 把圆周率从栈中取出 > ESI(看执行结果才知道...)
有人问了,你咋知道哪处内存放的就是用户名哪处内存就是注册码呢?嗯,光看代码俺也不知道,那你不会用F8走一次啊?OD
提示信息那么丰富你不用干啥啊?
还有个事,看着有类似CALL <JMP.&MFC42.#537_??0CString@@QAE@PB>这样的CALL没?这样的是调用MFC的函数,你别傻乎乎
的跟进去了啊!
嗯,恢复温柔语气.上面的代码呢基本就是判断用户名和注册的合法性,旁边的注释也是我调试好多次才写出来的.
好,看下面的循环段码
004015E7 > /8A040F MOV AL,BYTE PTR DS:[EDI+ECX] ; 依次取用户名的每位 > AL
004015EA . |0FBE2C31 MOVSX EBP,BYTE PTR DS:[ECX+ESI] ; 取圆周率每一位 > EBP
004015EE . |0FBEC0 MOVSX EAX,AL ; 把AL扩展放到EAX中,看寄存器变化其实就是留下低字节,剩下的清0
004015F1 . |99 CDQ ; CDQ?是什么意思? 查一下,原来是把EAX中的字的符号扩展到EDX中去
004015F2 . |F7FD IDIV EBP ; 除法,EAX/EBP 商回送AX,余数回送DX
004015F4 . |8BC2 MOV EAX,EDX ; 看来商没啥用,人家要的是余数
004015F6 . |D1E0 SHL EAX,1 ; 左移一位,相当于乘2
如果你和俺一样对汇编指令不熟悉,身旁一定要有汇编指令参考之类的资料,遇到陌生的查一下.
上面的指令就是把用户名第n位和圆周率字符串第n位求余,再乘2
004015F8 . 83F8 7B CMP EAX,7B
004015FB . 7E 03 JLE SHORT Rith_Cra.00401600
004015FD . 83E8 1A SUB EAX,1A ; 如果大于z的ASCII码,减去1Ah,即26使其为小写字母
00401600 > 83F8 41 CMP EAX,41 ; 41是大写字母A
00401603 . 7D 09 JGE SHORT Rith_Cra.0040160E
00401605 . BA 82000000 MOV EDX,82
0040160A . 2BD0 SUB EDX,EAX
0040160C . 8BC2 MOV EAX,EDX ; 如小于41则用82减,结果肯定是大于等于41,即为大写字母
0040160E > 83F8 5B CMP EAX,5B ; 5B是[符号,在Z之后
00401611 . 7E 12 JLE SHORT Rith_Cra.00401625 ; 和00401603一起判断是否为大写字母
00401613 . 83F8 61 CMP EAX,61
00401616 . 7D 0D JGE SHORT Rith_Cra.00401625 ; 如果大于等于a则不用处理
00401618 . 99 CDQ ; 如果在Z和a之间,那么下面的处理让它成为数字!
00401619 . BD 0A000000 MOV EBP,0A
0040161E . F7FD IDIV EBP ; EAX除以10,余数放到DX中
00401620 . 83C2 30 ADD EDX,30 ; 加上30h,变成数字的ASCII值
00401623 . 8BC2 MOV EAX,EDX ; 放回EAX中
00401625 > 8B5424 14 MOV EDX,DWORD PTR SS:[ESP+14] ; 注册码取出 > EDX
00401629 . 38040A CMP BYTE PTR DS:[EDX+ECX],AL ; 把AL依次和注册码比,有一位不等就OVER 可知把用户名按照上面过程计算就
可得注册码
0040162C . 75 1C JNZ SHORT Rith_Cra.0040164A ; 有一位不等则死掉
0040162E . 41 INC ECX ; 计算下一位
0040162F . 3BCB CMP ECX,EBX
00401631 .^ 7C B4 JL SHORT Rith_Cra.004015E7 ; 循环,直到ECX等于注册码的长度
这段指令比较麻烦,来回跳转.你一定要查询每个16进制数所代表的字符(一般都是紧挨着A,Z,a,z),猜测作者的意图就是
保证用户名每一次运算后得到的是常规字符(大小写字符,数字等).要多跟踪几次,输入不同的用户名测试流程,耐心一点肯定
会弄明白这段代码的.
总结一下,验证过程非常简单,只经过了一次计算就得出了注册码:
1.取用户名每位和圆周率每位求余,然后乘2,设算出的字符ASCII值为c
2.如果C > 'z',就减去0x1A(这么一减一般就是小写字母了)
3.如果C < 'A',就用0x82减(这么一减一定就是大写字母了)
4.如果c > 'A' 并且 c < 'a',就和10求余,然后加上0x30,成为数字
5.循环步骤1到4,直到用户名都算完,得出的就是注册码了.
下面给出C语言的注册机算法:
#include <stdio.h>
#include <string.h>
int main()
{
char* PI = "31415926535897932384";
char szUserName[80];
int nLen;
int i;
char c;
do
{
printf("请输入用户名:");
scanf("%s", szUserName);
nLen = strlen(szUserName);
if (nLen >= 5 && nLen <= 0x14) break;
} while(true);
printf("注册码:");
for (i = 0; i < nLen; i++)
{
c = szUserName[i] % PI[i];
c <<= 1;
if (c > 'z') c -= 0x1a;
if (c < 'A') c = 0x82 - c;
if (c > 'A' && c < 'a') c = c % 0xA + 0x30;
printf("%c", c);
}
printf("\n");
}
完
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
WAKU 2006年03月23日 21:01:26
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
上传的附件: