-
-
[原创]crackme第5题
-
2017-6-10 16:26 2743
-
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(); }
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
赞赏
看原图