好友
阅读权限 10
听众
最后登录 1970-1-1
本帖最后由 kll3199 于 2012-3-31 13:34 编辑
对于破解 我也是一时来了兴趣才开始学的,发这篇文章以期对跟我一样有兴趣莫有老师全靠百度自学的苦逼孩子共勉。我读的是石油工程专业,大学学的是VB,泪奔啊。后面选修了c++,选修啊选修,你懂得的。c++我也是全自学的,到今天为了写算法,将字符串转换数字,将数字转换字符串,10进制转16进制,总之各种转换,就为了这个我乱78糟的点我的c++6.0上的debug,突然就学会了调试,前面专门为学这个debug下了很多教程,居然看一点就放下完全没懂,所以建议跟我一样的娃娃,对一个陌生的东西千万不要畏难,没有行动就因为困难而根本不动手,其实动起手来才发现,it's so eassy!
这样吧,我把我今天理解的些东西也贴出来,高手无视不带前缀时c++默认的是10进制数,
16进制数表示方法
int k1;
k1=0x7a;
异或的原理 不管是10进制数还是N进制数 首先转换成2进制也就是010101数位,然后相同置0不同置1,得到新的数字。
下面写个例子
CString str1,str2; //str1、str2对应的分别是edit1、edit2映射。
int k1=55;
int k2=0x55;
str1=k1;str2=k2;
结果是edit1显示为7,怎么得来的呢 编译器是先将10进制数55转换为16进制数也就是ACSII码,然后edit1根据acsii码显示相应的字符。
edit2显示为U 因为k2本身就是16进制数 直接acsii码转换。
再看如下代码
CString K3="ABCDE";
int k1,k2=0x55;
k1=K3.GetAt(2);
打开调试在即时窗口里发现k1=67,k2=85;
k1取字符串K3的第三个字母C 其ACSII码即为43,16进制43转换为10进制数为67。由此可知字符强制转换为int型时 会先取字符ACSII码再16进制转换为10进制数。
再看这点
name[j]=name[j]^dx; //name[]为int型数组
str1=name[j]; //str1为CString,我这里干什么呢,其实你调试下就发现是将一个int数
直接转换成ACSII相应的字符
strSerial+=str1;
前面废话,那就转入正题
首先OD打开crackme,大概跟我一样的菜鸟接下来的第一步都是搜索ACSII了,如此,找到这句One of the Details you entered was wrong 点击进入,那么现在就看这句是哪里跳来的呢,往上点两下,发现004015C3 |> \6A 00 push 0x0 这处有个大的跳转,如此甚好,往上找到源头401503,那么会跳呢,肯定是有判断条件,不对就跳至出错地方,废话废话。这样上面就发现不远处就是段首,如是段首F2,然后运行name、serial分别输入几个字符kkk ,111。运行到断点处再单步F8跟着走,发现到刚刚那个jmp出直接跳至error,为什么这样就跳走了呢,那后面算法还没计算呢。我跟着从上面断点处一步一步全部走下去,分析得出原来该处是判断name和serial的位数,少于6位就跳错。原谅我不会发图,只有复制代码来了
004014CE |. 894D E0 mov [local.8],ecx ; 该段作用为判断name位数
004014D1 |. 8D4D E4 lea ecx,[local.7]
004014D4 |. E8 83030000 call <jmp.&MFC42.#540>
004014D9 |. C745 FC 00000>mov [local.1],0x0
004014E0 |. 8D4D F0 lea ecx,[local.4]
004014E3 |. E8 74030000 call <jmp.&MFC42.#540>
004014E8 |. C645 FC 01 mov byte ptr ss:[ebp-0x4],0x1
004014EC |. 8B4D E0 mov ecx,[local.8]
004014EF |. 81C1 A0000000 add ecx,0xA0
004014F5 |. E8 AA030000 call <jmp.&MFC42.#3876>
004014FA |. 8945 EC mov [local.5],eax
004014FD |. 837D EC 05 cmp [local.5],0x5 ; 最少需要输入6个字符
00401501 |. 7F 05 jg XCrackMe.00401508
00401503 |. E9 BB000000 jmp CrackMe.004015C3
00401508 |> 8B4D E0 mov ecx,[local.8] ; 该段作用判断serial位数
0040150B |. 83C1 60 add ecx,0x60
0040150E |. E8 91030000 call <jmp.&MFC42.#3876>
00401513 |. 8945 E8 mov [local.6],eax
00401516 |. 837D E8 05 cmp [local.6],0x5 ; 最少也需要6位
0040151A |. 7F 05 jg XCrackMe.00401521
0040151C |. E9 A2000000 jmp CrackMe.004015C3
上面判断跳错,那么既然写着算法,算法在哪里呢,不着急,且听我道来。
0040157D |. 8B45 E4 mov eax,[local.7]
00401580 |> 8A18 /mov bl,byte ptr ds:[eax] ; 对name进行计算
00401582 |. 32D9 |xor bl,cl /这段计算是将name 每个字符的acsii码依次与ecx异或计算,
00401584 |. 8818 |mov byte ptr ds:[eax],bl /并存在原来对应的地址,ecx初始值是1,计算后ecx++
00401586 |. 41 |inc ecx
00401587 |. 40 |inc eax
00401588 |. 8038 00 |cmp byte ptr ds:[eax],0x0 ; 判断name结尾
0040158B |.^ 75 F3 \jnz XCrackMe.00401580
0040158D |. 33C0 xor eax,eax
0040158F |. 33DB xor ebx,ebx
00401591 |. 33C9 xor ecx,ecx
00401593 |. B9 0A000000 mov ecx,0xA
00401598 |. 33D2 xor edx,edx
0040159A |. 8B45 F0 mov eax,[local.4]
0040159D |> 8A18 /mov bl,byte ptr ds:[eax] ; 对serial进行计算
0040159F |. 32D9 |xor bl,cl /这里的计算跟name方法一样,不信你看代码,都是一个模子吧。只是将ecx初始为0x0A而已
004015A1 |. 8818 |mov byte ptr ds:[eax],bl
004015A3 |. 41 |inc ecx
004015A4 |. 40 |inc eax ; 判断serial结尾
004015A5 |. 8038 00 |cmp byte ptr ds:[eax],0x0
004015A8 |.^ 75 F3 \jnz XCrackMe.0040159D
那么是不是得问,怎么发现上面是算法的呢,我是这么弄出来的,单步F8时,我将跳错的跳转全部不跳,这样F8时就会发现自己输入的ACSII在寄存器里出现,如此而已。
再看刚刚下面这块代码
004015AA |. 8B45 E4 mov eax,[local.7] //该处是将处理后的name传给eax
004015AD |. 8B55 F0 mov edx,[local.4] //该处是将处理后的serial传给edx
004015B0 |> 33C9 /xor ecx,ecx
004015B2 |. 8A18 |mov bl,byte ptr ds:[eax] ;
004015B4 |. 8A0A |mov cl,byte ptr ds:[edx] ; Incorrect try again!!
004015B6 |. 3AD9 |cmp bl,cl
004015B8 |. 75 09 |jnz XCrackMe.004015C3
004015BA |. 40 |inc eax
004015BB |. 42 |inc edx
004015BC |. 8038 00 |cmp byte ptr ds:[eax],0x0 //这中间的嘛,显然就是分别对eax,ecx每个字母一次进行比较,发现不同就跳错,相同的话,就是正确的serial啦
004015BF |.^ 75 EF \jnz XCrackMe.004015B0
004015C1 |. EB 16 jmp XCrackMe.004015D9
至此算法分析完毕,爆破的话很简单,将上面两个判断name和serial位数的jmp nop掉,然后在这行 004015B8 |. /75 09 |jnz XCrackMe.004015C3 也nop掉就OK。
由上面分析的算法,完全可以由name推到出serial,怎么推呢。那么我们来 由异或原理 A xor B=C 是不是可以得出 C xor B=A ,对头就是这样推出来的。
设AX为name的一个字符的ACSII其实就是16进制数,CX初始为01,DX初始为0xA
AX=AX xor CX;
AX=AX xor DX;
CX++,DX++;
再依此将AX连接成字符串就是serial啦,是不是很简单。
看我的c++代码:
void CMyDlg::OnJs()
{
long ax,bx,dx,cx=0,y=10;
int i,j;
char str;
CString strName,strSerial,str1;
int name[18];
UpdateData(TRUE);
i=m_id.GetLength();
j=m_psw.GetLength();
if (i<6)
{
AfxMessageBox(_T("name请不要少于6位!否则则按“nameless”计算"));
m_id="nameless";
}
for (j=0;j<i;j++)
{
//将name字符串每个字母依次进行运算,结果按10进制存放到整形数组name[]中
cx=cx+1;
str=m_id.GetAt(j); //取第m个字符
bx=str; //将字符转换为10进制数
bx=bx^cx; //异或运算
name[j]=bx;
//根据算法,由name倒推serial
//原理其实就是异或运算,A XOR B=C,那么 C XOR B=A。如此而已
dx=cx+9;
name[j]=name[j]^dx;
str1=name[j];
strSerial+=str1;
}
m_psw=strSerial;
UpdateData(FALSE);
}
至此结束。
我先发在看雪上面的见源地址http://bbs.pediy.com/showthread.php?t=148634
附件为 crackme和注册机。
crackme.rar
(8.01 KB, 下载次数: 15)
免费评分
查看全部评分