本帖最后由 Sandman 于 2013-12-14 12:00 编辑
【文章标题】 【吾爱2013CM大赛解答】----驱动crackme -- 网际座山雕 CM解题过程+KEYGEN源码
【文章作者】 Sandman
【作者邮箱】
【作者主页】 无~
【软件名称】 CrackMe
【下载地址】 http://www.52pojie.cn/thread-228427-1-1.html
【加壳方式】 无~
【保护方式】 无~
【编写语言】 VC
【使用工具】 OD+IDA
【操作平台】 XPSP3
【软件介绍】 吾爱2013CM大赛题目
由于本题目涉及到RING3程序与RING0程序通信,考虑到常见的应用程序与驱动程序通信方式为CreateFileA+DeviceIoControl方式,使用OD将程序载入后,在CreateFileA与DeviceIoControl函数上下断点。F9运行后,在序列号输入Edit输入任意字符串后,回车,程序在CreateFileA和DeviceIoControl函数上断下。
由OD断点可知,驱动程序创建一个\\.\crackme的符号链接用于与应用程序通信。其中DeviceIoControl函数对驱动程序的控制码为0x222004,查看InBuffer内容,可知程序送往RING0的Buffer内容为机器码+用户设置的序列号,Buffer长度为0x103字节。
将DeviceIoControl函数执行到返回,查看返回Buffer,发现返回Buffer前10字节发生了变化,肯定是驱动程序对这个Buffer做了什么。
将SYS拖到IDA反编译,发现只有6个函数,分别为驱动程序的各个派遣函数,其中sub_10748为驱动程序的DeviceIoControl的处理例程,选择此函数进行F5,找到对驱动控制码0x222004相应的部分,结果如下:
由上述伪代码可知,注册码长度为9位,返回应用程序的格式为: pIoBuffer = RegCode - MachineCode + 1; 现在继续回到OD,在DeviceIoControl的OutputBuffer上下硬件访问断点,长度为1。因为后面的注册码验证部分肯定是要读取这部分数据的。F9后,断下。进入如下循环体
其中ECX控制循环计数。 下面的代码实际上是EAX = EAX * ECX 其中EAX为结果的低32位,EDX为结果的高32位。 00401B7B . B8 398EE338 mov eax, 38E38E39 00401B80 . F7E1 mul ecx 00401B82 . D1EA shr edx, 1 EDX左移一位,总体上来看,实际是ECX * 0x38E38E39 的结果右移了33位: EDX = (ECX * 0x38E38E39) shr 33
M= (ECX * 0x38E38E39) / 2n 其中n=33 ECX > 0 M>0 且ECX和M均为整数 因此,EDX = ECX / 9
上述代码段从0x4438F8处读取1字节的数据,与0xE0比较,如果小于等于则与驱动返回数据中的一个字节做减法,这个字节是谁取决于ECX除以9的结果(并且9次一个循环),并将结果保存至0x446780处,一共循环0xA3次。 上述代码对应的C语言表示为:
[C] 纯文本查看 复制代码 [/align]
[align=left]VOID DecryptCode(PUCHAR SrcBuffer,PUCHAR KeyBuffer)
{
int i;
unsigned char j;
for(i=0;i<0xA3;i++)
{
if(Buffer[i]<=0xE0)
{
j = i / 9;
j += j*8;
j = i -j;
DstBuffer[i] = SrcBuffer[i] - KeyBuffer[j];
}
}
}[/i][/i][/align][i][i]
[align=left][i]
待上述代码解密完成后,继续跟。
其中有2段代码引起注意: cmp byte ptr [446822],0x21.............................1 还有 mov eax,00406780...........................................2 call eax
其中代码1判断0x00406780 + 0xA2的位置是否为0x21 如果不是则跳转到弹出错误的窗口。 如果是则执行代码2.代码2是将解密后的数据作为代码进行调用。程序根据代码的调用结果来判断是否注册成功的。这就引来一个问题:如果代码解密不正确的话,这些代码是不能被CPU成功执行的,将会触发异常。因此解密的关键在于那个KeyBuffer的数据。 仔细整理一下已有的思路: 原始数据地址 = 0x004438F8 解密后数据地址 = 0x00406780 成功解密的一个前提条件是0x00406780 +0xA2处的数据是0x21。而0x004438F8 + 0xA2= 0x22,而解密算法是由原始数据做减法得出的,由DecryptCode函数可知: DstBuffer[0xA2] = SrcBuffer[0xA2] -KeyBuffer[0] 可以推出KeyBuffer[0] = 1。第一个值我们算出来了。 那么后面的8个值怎么办呢? 综合上面的结果可以知道,驱动程序做的只是做一个减法而已,根据不同电脑的分析结果发现,原始数据是不变的,与机器码无关,而解密后的代码具有可执行的属性,也就是说解密后的代码也是不变的,具有唯一性。此时可以得到一个结论:注册码 - 机器码是一个固定值,并且 注册码第一位 - 机器码第一位 + 1的结果是1. 此时我做了一个大胆的推测,那个KeyBuffer的内容可能是KeyBuffer[9] = {1,2,3,4,5,6,7,8}; 因为上面的解密循环是9次一个循环。所以我写了一段注册机代码: [C] 纯文本查看 复制代码 #include <stdio.h>
void KeyGen(unsigned char *MachineID)
{
unsigned int i;
unsigned char Key[10] = {0};
for(i=0;i<9;i++)
{
Key = MachineID + i;
}
printf("KeyCode:%s\n",Key);
}
void main()
{
unsigned char MachineID[10] = {0};
scanf("%s",&MachineID[0]);
KeyGen(MachineID);
}
经过验证,上面的注册算法是可用的。结果如下图:
这个分析过程最后有点不太完美,不是说完整分析出来的,有猜的成分在里面。希望有人能有更好的解决方法贴出来。是怎么得出那个KeyBuffer = {1,2,3,4,5,6,7,8},这个只是我的一个猜测,最终得到了验证而已。
这个弄完实在太晚了,第一次写,时间匆忙,写的不是很好。并且后面的KEYBuffer是我猜出来的,如果论坛有人有更好的解法,希望贴出来一起交流。有错误之处,还请各位高手指正。
|