【文章标题】: Hide Window HotkeyV2.5注册算法分析
【文章作者】: TyroneKing
【作者邮箱】: tyroneking@126.com
【软件名称】: Hide Window Hotkey
【下载地址】: HWHSetup.rar
【KeyGen】: KeyGen.rar
【加壳方式】: ASPack 2.12 -> Alexey Solodovnikov
【保护方式】: 注册名+序列号
【编写语言】: Borland Delphi 6.0 - 7.0
【使用工具】: OD + PEiD+DeDe + MD5计算器+AspackDie+ IDA (可以不用,加强分析)
【操作平台】: WIN XP +SP3、 VC++6.0
【软件介绍】: 隐藏运行的窗口和应用程序软件。
【作者声明】: 只是为了学习,没有其他目的。失误之处敬请诸位大侠赐教!
【分析过程】
1、用PEiD查壳,为ASPack 2.12 -> Alexey Solodovnikov,用ESP定律很容易就可以脱壳了,不过这里还是用AspackDie,不对如何脱壳详说(我也是新手,脱一些简单的行,强壳,还得继续努力呀,:))。
2、用AspackDie脱壳,用PEiD查为Borland Delphi 6.0 - 7.0编写, KANAL 算法识别插件分析,软件用了MD5(地址004B549A)算法,用,直接运行脱壳后程序没啥反应,应该是自检验了。
3、用OD加载下bp GetFileSize 断点,程序中断在GetFileSize函数中,按Alt+F9返回程序领空 004030A1
4、单步运行过004030C8后,跳出该函数,运行到004C4B8B
5、将4C4B90改为JMP SHORT unpacked.004C4BA7,保存成Crack.exe文件,运行Crack.exe,程序正常运行(注意要先前面调试的OD,因为这个软件只允许一个进程运行)。
6、在Help->Register中打开注册窗口,输入Name和Code,提示错误,退出程序,分别用OD和DeDe加载,下面开始算法分析。
7、在DeDe中查找注册窗口的OK按钮事件

在OD中004B609C 下断点。
8、OD中运行程序,(在第2点中提到004B549A(MD5算法位置)附近 004B5499 下断点,为了判断程序是否用MD5加密)打开注册窗口Name输入 TYRONEKING,Code输入 0123456789,点击OK,OD在004B609C 中断。
9、单步跟进 004B622F |. E8 CC020000 CALL Crack.004B6500 ; 计算Code
进一步分析
10、单步跟进004B659C |. E8 C3000000 CALL Crack.004B6664 ; 第二层运算
进一步分析
11、在004B676B返回后回到 004B65A1
12、返回到 004B6234
14、以上对程序的注册算法以基本分析完成,但要写出注册机,还是分析作者对MD5算法做了什么手脚,导致该程序的MD5加密结果与正常的MD5加密有所不同,经分析(我是通过一个正常的MD5calculator.,反编译后与该程序的MD5算法进行比较,最终发现差别之处,这里不详细说明):
对应为MD5算法里的MD5Update=》MD5Transform函数中 进行4轮变化后结果相加时作了修改
正常MD5算法:
该程序MD5算法:
以上是对该程序的所有注册算法进行分析。
-------------------------------------------------------------------------
【算法总结】
1、 用户名长度不能小于8,不能包含空格,不能为单一字符。
2、 对用户名的第i位与第i+1位(最后一位与第0位)进行OR运算。
3、 对2的结果循环右移一字节
4、 对3的结果进行变形MD5运算(该结果与正常MD5值的前16字节一致,后16字节不同)
5、 取4的结果前10位数字部分
6、 对5的结果循环右移一字节
7、 根据6的结果对应的数字取固定字符串“5497312680”对应位,即为注册码。
这里附上用VC++6.0编写的注册机的原码,注册机是经《加密与解密》第三版 6.1.1 MD5算法的MD5calculator修改而成的。
下面是部分原码:
代码:
MD5_CTX context;
long dtLength;
int i;
int len;
int j;
TCHAR *szKeyStr="5497312680";
TCHAR sTemp=' ';
TCHAR szName[MAXINPUTLEN]={0};
TCHAR szHash[MAXINPUTLEN]={0};
TCHAR szBuffer[MAXINPUTLEN]={0};
dtLength=GetDlgItemText(hWnd, IDC_TXT0, szName, sizeof(szName)/sizeof(TCHAR)+1);
len=strlen(szName);
if (len<=8)//长度判断
{
SetDlgItemText(hWnd, IDC_TXT1, "Name必须大于8位!");
return FALSE;
}
for(i=0;i < len; i++)//是否包含 空格
if(szName[i]==' ')
{
SetDlgItemText(hWnd, IDC_TXT1, "Name不能包含空格!");
return FALSE;
}
sTemp=szName[0];
i=1;
while(i<len)
{
if(sTemp==szName[i])
i++;
else
break;
}
if(i>=len)
{
SetDlgItemText(hWnd, IDC_TXT1, "Name不能为单一字符!");
return FALSE;
}
for(i=0;i < len -1; i++)
szName[i]=szName[i] | szName[i+1];
szName[len-1]=szName[len-1] | sTemp;//各字节进行 OR 运算
sTemp=szName[len-1];
for(i=len-1; i > 0 ;i--)
szName[i]=szName[i-1];
szName[0]=sTemp;//循环右移一字节
MD5Init(&context);
MD5Update(&context, szName, dtLength);//该函数里的MD5Transform函数变形了
MD5Final(szHash, &context);
for(i=0; i < 16; i++) // 将szHash[]中的16进制转换成字符形式显示
{
wsprintf(&szBuffer[i*2], "%02X", *(byte*)(szHash+i));
}//变形MD5
i=0;
len=0;
while(len<10 && i<32)
{
if(szBuffer[i]>='0' && szBuffer[i]<='9')
{
szName[len]=szBuffer[i];
len+=1;
}
i++;
}//取前10位数字
szName[len]='\0';
if(len<1)
{
SetDlgItemText(hWnd, IDC_TXT1,"注册码为空?!");
return TRUE;
}
sTemp=szName[len-1];
for(i=len-1; i > 0 ;i--)
szName[i]=szName[i-1];
szName[0]=sTemp;//循环右移一字节
for(i=0; i < len ;i++)
{
j=szName[i]-'0';
szName[i]=szKeyStr[j];//根据szName[i]值对取应szKeyStr值
}
SetDlgItemText(hWnd, IDC_TXT1,szName);
return TRUE;
【总结】
1、软件虽然经过多次运算,但注册码还是在内存中出现,不分析算法也可以轻易写出内存注册机。
2、不知作者是有意还是无意(为什么只变形后16位,而不32位一起变形呢?)将MD5变形了,这点会加大算法分析者的难度。
3、将注册码计算、注册码判断、错误提示分开,多少会加大破解的难度(至少对于新手,本人一开始为了查找消息处理函数就花了大量时间)。
4、没有很好地加解密提示字符串(或不提示),将提示串放在资源文件也能简单防止通过字符串查找破解。
5、注册码与机器无关,只要有一对正确用户名和注册码就可以任意机器注册。
第一次写破文,不足之处还望大侠多多指教!谢谢!
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2010年04月09日