今天找到了这样一个程序,拿到OD里初看觉得太简单了,关键的汇编代码就那么几十行,就当是练手吧。后来分析了一阵子发现里面另有玄机,看似短短的几十行汇编代码,里面却有几个地方,稍不留神,就无法写出完整的注册机。
闲话就不多说了,直接进入主题吧。
这是一个控制台的程序,所用的编程语言我也没见到过,既然无法从API上下断,也无法从入口点下手,那就查找字符串吧。。经过一小步,就找到了这个关键代码处,由于代码比较少,我就详细的解释下,有不对的地方,恳请各位大大多多指教。:o:
这段代码看似简单,就是一个while循环的特征。但是仔细分析,发现里面涉及到4个变量的使用,不认真分析还是比较模糊的。
结合上面的分析,整理一下思路:
1、 用一个变量做为循环,记为变量i,循环次数为10次
2、 依次取各字符的值,放入变量a
3、 依次累加各字符的值,即name[0]+name[1]+name[2]…记为变量b.
4、 用一个常量0x8099,依次加上变量b的值,结果存入变量c
容易忽略的地方:
1、 当字符串长度不足10个字节时,还是得循环10 次,那剩下的次数都在做什么呢。
2、 当字符串长度超过10个字符时,多余的部分又怎么算呢。
分析发现:
1、 当字符串不足8位时,用0补足8位,再在后面加上两个字节,值分别是0xb0,0xff
2、 当字符串长度为8位时,第9位补0,第10位为0xff.
3、 当字符串长度为9位时,第10位补0 。
4、 当字符串长度为10位时,则按字符串的值进行累加,不添加其它值。
经过上面的分析,注册机也就能完整的写出来了。
注册机代码如下:
总结:
1、 这个程序关键代码不长,但是要完全分析还是要点时间,因为里面有几个细节点。
2、 最后算出的结果是负数的时候,之前我认为这样的name值是没有key的,经过认真调试才发现后面还有一段代码是将负数变成正数,加上这段代码,注册机就更完整了。
3、 如果注册name超过10个字符,虽然用同样的key可以注册成功,但是会出现一个缓冲区溢出的错误。
4、 通过这个程序的分析,发现有时候简单的事,做起来不一定简单。
可用的注册名如:name: thankyou
The key is : 801777276
name: helloworld
The key is : 1900304355
闲话就不多说了,直接进入主题吧。
这是一个控制台的程序,所用的编程语言我也没见到过,既然无法从API上下断,也无法从入口点下手,那就查找字符串吧。。经过一小步,就找到了这个关键代码处,由于代码比较少,我就详细的解释下,有不对的地方,恳请各位大大多多指教。:o:
代码:
00401478 |> /837D E0 09 /CMP DWORD PTR SS:[EBP-20],9 ; 判断是否大于9
0040147C |. |77 26 |JA SHORT Keygen_#.004014A4 ; 大于则跳转
0040147E |. |89E8 |MOV EAX,EBP
00401480 |. |0345 E0 |ADD EAX,DWORD PTR SS:[EBP-20] ; 基址加上偏移
00401483 |. |83E8 08 |SUB EAX,8 ; 减8定位到字符串
00401486 |. |0FB600 |MOVZX EAX,BYTE PTR DS:[EAX] ; 依次取字符串的各个字符
00401489 |. |8845 EF |MOV BYTE PTR SS:[EBP-11],AL ; 将取到的字符存入变量,记为变量a
0040148C |. |0FBE55 EF |MOVSX EDX,BYTE PTR SS:[EBP-11] ; 将变量a的值传给edx
00401490 |. |8D45 F0 |LEA EAX,DWORD PTR SS:[EBP-10] ; eax=ebp-0x10,即让eax指向这个地址
00401493 |. |0110 |ADD DWORD PTR DS:[EAX],EDX ; eax所指向的变量b,加上edx的值
00401495 |. |8B55 F0 |MOV EDX,DWORD PTR SS:[EBP-10] ; 将b的值传给edx
00401498 |. |8D45 E8 |LEA EAX,DWORD PTR SS:[EBP-18] ; eax=ebp-0x18,即让eax指向这个地址
0040149B |. |0110 |ADD DWORD PTR DS:[EAX],EDX ; 将eax指向的变量c,加上edx的值
0040149D |. |8D45 E0 |LEA EAX,DWORD PTR SS:[EBP-20] ; eax=ebp-0x20,即让eax指向变量i
004014A0 |. |FF00 |INC DWORD PTR DS:[EAX] ; 变量i加1,为下一轮循环做准备
004014A2 |.^\EB D4 \JMP SHORT Keygen_#.00401478
结合上面的分析,整理一下思路:
1、 用一个变量做为循环,记为变量i,循环次数为10次
2、 依次取各字符的值,放入变量a
3、 依次累加各字符的值,即name[0]+name[1]+name[2]…记为变量b.
4、 用一个常量0x8099,依次加上变量b的值,结果存入变量c
容易忽略的地方:
1、 当字符串长度不足10个字节时,还是得循环10 次,那剩下的次数都在做什么呢。
2、 当字符串长度超过10个字符时,多余的部分又怎么算呢。
分析发现:
1、 当字符串不足8位时,用0补足8位,再在后面加上两个字节,值分别是0xb0,0xff
2、 当字符串长度为8位时,第9位补0,第10位为0xff.
3、 当字符串长度为9位时,第10位补0 。
4、 当字符串长度为10位时,则按字符串的值进行累加,不添加其它值。
代码:
004014A4 |> \8B45 F0 MOV EAX,DWORD PTR SS:[EBP-10] ; 取变量b的值
004014A7 |. 8B55 E8 MOV EDX,DWORD PTR SS:[EBP-18] ; 取变量c的值
004014AA |. 01C2 ADD EDX,EAX ; c=c+b
004014AC |. 8B45 E4 MOV EAX,DWORD PTR SS:[EBP-1C] ; 取变量d,d的初始值为7
004014AF |. 0FAFC2 IMUL EAX,EDX ; d=d*c
004014B2 |. 8945 E4 MOV DWORD PTR SS:[EBP-1C],EAX
004014B5 |. 8B55 F0 MOV EDX,DWORD PTR SS:[EBP-10] ; 取变量b的值
004014B8 |. 8B45 E4 MOV EAX,DWORD PTR SS:[EBP-1C] ; 取变量 d的值
004014BB |. 89C1 MOV ECX,EAX
004014BD |. 29D1 SUB ECX,EDX ; d=d-b
004014BF |. 8B55 E8 MOV EDX,DWORD PTR SS:[EBP-18] ; 取变量c 的值
004014C2 |. 89D0 MOV EAX,EDX
004014C4 |. C1F8 1F SAR EAX,1F ; 算术右移,最高位不变
004014C7 |. C1E8 1F SHR EAX,1F ; 逻辑右移,用0补空位
004014CA |. 8D0402 LEA EAX,DWORD PTR DS:[EDX+EAX] ; eax=edx+eax
004014CD |. 89C2 MOV EDX,EAX
004014CF |. D1FA SAR EDX,1 ; 算术右移一位,相当于除2
004014D1 |. 89D0 MOV EAX,EDX
004014D3 |. 01C0 ADD EAX,EAX ; 相当于eax*2
004014D5 |. 01D0 ADD EAX,EDX ; eax=eax+edx
004014D7 |. C1E0 02 SHL EAX,2 ; eax=eax*4
004014DA |. 01D0 ADD EAX,EDX ; eax=eax+edx
004014DC |. 8D1401 LEA EDX,DWORD PTR DS:[ECX+EAX] ; edx=ecx+eax
004014DF |. 8B45 E4 MOV EAX,DWORD PTR SS:[EBP-1C] ; 取变量d的值
004014E2 |. 0FAFC2 IMUL EAX,EDX ; 做乘法
004014E5 |. 8945 E4 MOV DWORD PTR SS:[EBP-1C],EAX
004014E8 |. 837D E4 00 CMP DWORD PTR SS:[EBP-1C],0 ; 若算出来的数是负数,则进行处理
004014EC |. 79 0F JNS SHORT Keygen_#.004014FD
004014EE |. 8B45 E4 MOV EAX,DWORD PTR SS:[EBP-1C]
004014F1 |. BA 00000000 MOV EDX,0
004014F6 |. 29C2 SUB EDX,EAX ; 用0来减去这个负数,等价于把负号去掉.
004014F8 |. 89D0 MOV EAX,EDX
004014FA |. 8945 E4 MOV DWORD PTR SS:[EBP-1C],EAX ; 得到key
004014FD |> 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
00401500 |. 3B45 E4 CMP EAX,DWORD PTR SS:[EBP-1C]
00401503 |. 75 16 JNZ SHORT Keygen_#.0040151B
00401505 |. C74424 04 1C0>MOV DWORD PTR SS:[ESP+4],Keygen_#.004401>; Congrats, now write me keygen!\n\n
0040150D |. C70424 C03344>MOV DWORD PTR SS:[ESP],Keygen_#.004433C0
00401514 |. E8 9FAC0300 CALL Keygen_#.0043C1B8
00401519 |. EB 14 JMP SHORT Keygen_#.0040152F
0040151B |> C74424 04 3D0>MOV DWORD PTR SS:[ESP+4],Keygen_#.004401>; Sorry, try again!\n\n
00401523 |. C70424 C03344>MOV DWORD PTR SS:[ESP],Keygen_#.004433C0
0040152A |. E8 89AC0300 CALL Keygen_#.0043C1B8
0040152F |> C70424 510144>MOV DWORD PTR SS:[ESP],Keygen_#.00440151 ; |pause
00401536 |. E8 95F20000 CALL <JMP.&msvcrt.system> ; \system
注册机代码如下:
代码:
#include <stdio.h>
#include <string.h>
main()
{
char name[20];
int i,a,b=0,c=0x80899,d=7,len,z,c1,d1,t;
printf("Please input a name:\t");
scanf("%s",name);
len=strlen(name);
if(len>=10)//当字符串长度大于10时,则取前面10个字符,依次累加。
{
for(i=0;i<=9;i++)
{
b=name[i]+b;
c=c+b;
}
}
if(len<8)//当字符串不足8位时,用0补足8位,再在后面加上两个字节,值分别是0xb0,0xff
{
z=8-len;
for(i=0;i<len;i++)
{
b=name[i]+b;
c=c+b;
}
while(z!=0)
{
c=c+b;
z--;
}
b=b-80;
c=c+b;
b=b-1;
c=c+b;
}
if(len==8)//当字符串长度为8位时,第9位补0,第10位为0xff.
{
for(i=0;i<len;i++)
{
b=name[i]+b;
c=c+b;
}
c=c+b;
b=b-1;
c=c+b;
}
if(len==9)//当字符串长度为9位时,第10位补0 。
{
for(i=0;i<len;i++)
{
b=name[i]+b;
c=c+b;
}
c=c+b;
}
c1=c+b;
d=d*c1;
d1=d-b;
t=d1;
c1=c;
c1=c1>>1;
d1=c1;
c1=c1+c1;
c1=c1+d1;
c1=c1<<2;
c1=c1+d1;
c1=c1+t;
c1=c1*d;
if(c1&0x800000000)//若算出来是一个负数,则用0来减去它。
{
c1=0-c1;
}
printf("The key is :\t%d\n",c1);
}
1、 这个程序关键代码不长,但是要完全分析还是要点时间,因为里面有几个细节点。
2、 最后算出的结果是负数的时候,之前我认为这样的name值是没有key的,经过认真调试才发现后面还有一段代码是将负数变成正数,加上这段代码,注册机就更完整了。
3、 如果注册name超过10个字符,虽然用同样的key可以注册成功,但是会出现一个缓冲区溢出的错误。
4、 通过这个程序的分析,发现有时候简单的事,做起来不一定简单。
可用的注册名如:name: thankyou
The key is : 801777276
name: helloworld
The key is : 1900304355