[原创]crackme第5题
1. 因为题目中释放了驱动文件,所以首先,把驱动文件拿到,在od中对DeleteFile设置断点,在目录下取到驱动文件: vmxdrv.sys
2. 应用程序与驱动通信,在ida中查看应用程序的主要调用逻辑如下(对应地址为sub_401760):
...
...
if ( *(v8 - 8) != 6 || IsDebuggerPresent() )
{
CString::operator=((v1 + 108), byte_431398);
CString::operator=((v1 + 104), byte_431398);
sub_41A4F7(0);
}
else
{
if ( *(v1 + 0x19) )
{
v6 = *(v8 - 8);
v3 = sub_418263(&v8, 0);
CallSysMd5_401D50(v1, v3, v6);
}
else
{
PostMessageA(*(v1 + 7), 0x12u, 0, 0);
}
v6 = &v9;
v5 = &v9;
v11 = &v5;
sub_417D43(&v5, v1 + 0x17);
MD5_401920(v5, v6);
sub_415A78(&v9, &v10, 2, 0xAu);
v14 = 2;
sub_4182FA(&v10);
CString::operator=((v1 + 108), byte_431398);
CString::operator=((v1 + 104), byte_431398);
sub_41A4F7(0);
if ( _mbsicmp(v9, a888aeda4ab) )
{
CString::operator=((v1 + 108), byte_431398);
CString::operator=((v1 + 104), byte_431398);
sub_41A4F7(0);
}
...
...上面的md5计算过程由peid检测后和调试分析得出,同时可见输入长度需要为6。
3. 调试发现程序被反调试检测到,然后退出,接着分析驱动代码,驱动代码放到ida静态分析即可,看到对进程信息的处理过程:
PEPROCESS anti_10486()
{
PEPROCESS result; // eax@1
struct _EPROCESS *v1; // edx@1
result = IoGetCurrentProcess();
v1 = result;
while ( result != (PEPROCESS)dword_114E0 )
{
result = (PEPROCESS)(*((_DWORD *)result + 0x22) - 0x88);
if ( result == v1 )
return result;
}
*((_DWORD *)result + 0x2F) = 0;
return result;
}进一步调试需要对该处代码,屏蔽,但修改是需要注意驱动中存储了状态:
int __stdcall sub_1071A(int a1, PIRP Irp)
{
switch ( *(_DWORD *)(Irp->Tail.Overlay.PacketType + 12) )
{
case 0x222004:
checked = 1;
dword_114E0 = (int)IoGetCurrentProcess();
anti_10486();
break;
case 0x222008:
DbgPrint("%s\n", Irp->AssociatedIrp.IrpCount);
break;
case 0x22200C:
DbgPrint("bye!\n");
break;
default:
DbgPrint("unkonw code!\n");
break;
}
Irp->IoStatus.Status = 0;
Irp->IoStatus.Information = 0;
IofCompleteRequest(Irp, 0);
return 0;
}上面对0x222004的处理过程一定要在r3中调用过,但后面的反调试可以直接去掉,因此拖到16进制编辑器中修改,对anti_10486函数nop掉,然后存储(同时重新生成文件hash,否则驱动会加载失败):
此时,驱动已经拿到,并且去除了反调试,因此,可以进一步调试。
4. 在r3程序启动前,手动加载驱动文件,保证与原始文件名称相同:vmxdrv.sys ,目的是替换应用程序中的驱动的加载;此后配合od的插件可以调试分析程序;
5. 分析驱动调用流程:原理为应用层调用writefile,驱动根据输入计算md5,然后readfile取得md5值,应用层后续转换为文本,然后继续计算hash,该hash被截取(1,5)部分结果与目标值比较,对应的驱动代码:
memcpy(str, Irpa, v3);
if ( checked )
{
sub_104B6(str, (int)hash);
hashed = 1;
}
ExFreePoolWithTag(str, 0);
v2->IoStatus.Status = 0;其中checked由0x222004设置,因此不能从r3屏蔽对应检测线程;
6. 穷举,此时可以对结果进行穷举,中间程序写错了。。。最后正确的是(外层循环省略):
unsigned long *a4 = 0;
char *a1 = 0;
char text[100] = { 0 };
unsigned char res[16] = { 0 };
unsigned char res2[16] = { 0 };
char text2[100] = { 0 };
sprintf(sn, "%c%c%c%c%c%c",
table[i1], table[i2], table[i3], table[i4], table[i5], table[i6]);
func(sn);
MD5_CTX ctx = { 0 };
MD5Init(&ctx);
MD5Update(&ctx, (unsigned char*)sn, strlen(sn));
MD5Final(&ctx, res);
int len = 0;
for (int i = 0; i < 16; i++)
{
len += sprintf(text + len, "%02x", res[i]);
}
memset(&ctx, 0, sizeof(ctx));
MD5Init(&ctx);
MD5Update(&ctx, (unsigned char*)text, 32);
MD5Final(&ctx, res2);
len = 0;
for (int i = 1; i < 6; i++)
{
len += sprintf(text2 + len, "%02x", res2[i]);
}
if (strcmp(text2, "888aeda4ab") == 0)
{
printf("result: %s.\n", sn);
getchar();
}