吾爱破解2012CM大赛破文-LittleFater
本帖最后由 willJ 于 2012-5-8 22:57 编辑【文章标题】: 吾爱破解2012CM大赛破文-LittleFater【文章作者】:willJ【难度】: 难 大家都知道这个是基于驱动来验证的。那就直接开始分析吧。这个程序是不能爆破的,因为它的成功字符串是被加密了的,所以理论上只能输入正确的解密因子才能弹出正确来。一:应用层: 用OD载入,直接下段bp CreateFileA,因为要释放驱动,这个目的是为了抓出驱动来。F9运行起来,输入willJ,回车,断了下来:7C801A28 >8BFF mov edi,edi
7C801A2A 55 push ebp
7C801A2B 8BEC mov ebp,esp
7C801A2D FF75 08 push dword ptr ss:
7C801A30 E8 CFC60000 call kernel32.7C80E104
7C801A35 85C0 test eax,eax
7C801A37 74 1E je Xkernel32.7C801A57
7C801A39 FF75 20 push dword ptr ss:
7C801A3C FF75 1C push dword ptr ss:
7C801A3F FF75 18 push dword ptr ss:
7C801A42 FF75 14 push dword ptr ss:
7C801A45 FF75 10 push dword ptr ss:
7C801A48 FF75 0C push dword ptr ss:
7C801A4B FF70 04 push dword ptr ds:
7C801A4E E8 9DED0000 call kernel32.CreateFileW
7C801A53 5D pop ebp
7C801A54 C2 1C00 retn 0x1C
Alt + F9执行到返回,走几步就到释放的地方了:0040279E|.51 push ecx ; /ResourceType
0040279F|.52 push edx ; |ResourceName
004027A0|.6A 00 push 0x0 ; |hModule = NULL
004027A2|.FF15 38945100 call dword ptr ds:[<&KERNEL32.FindResour>; \FindResourceA
004027A8|.8BF8 mov edi,eax
004027AA|.57 push edi ; /hResource
004027AB|.6A 00 push 0x0 ; |hModule = NULL
004027AD|.FF15 5C945100 call dword ptr ds:[<&KERNEL32.LoadResour>; \LoadResource
004027B3|.57 push edi ; /hResource
004027B4|.6A 00 push 0x0 ; |hModule = NULL
004027B6|.8BD8 mov ebx,eax ; |
004027B8|.FF15 64945100 call dword ptr ds:[<&KERNEL32.SizeofReso>; \SizeofResource
004027BE|.6A 00 push 0x0 ; /pOverlapped = NULL
004027C0|.8D4D FC lea ecx, ; |
004027C3|.51 push ecx ; |pBytesWritten
004027C4|.50 push eax ; |nBytesToWrite
004027C5|.53 push ebx ; |Buffer
004027C6|.56 push esi ; |hFile
004027C7|.FF15 34945100 call dword ptr ds:[<&KERNEL32.WriteFile>>; \WriteFile
004027CD|.56 push esi ; /hObject
004027CE|.FF15 48945100 call dword ptr ds:[<&KERNEL32.CloseHandl>; \CloseHandle
运行到4027ce,就可以在C:\windows\system32\drivers得到驱动,名字是CrackMe.sys,得保存下来,因为程序做了删除操作的。继续往下面走就看见了与驱动交互的API了:00402638|.6A 00 push 0x0 ; /pOverlapped = NULL
0040263A|.8D95 B0FDFFFF lea edx, ; |
00402640|.52 push edx ; |pBytesReturned
00402641|.56 push esi ; |OutBufferSize
00402642|.8BF8 mov edi,eax ; |
00402644|.57 push edi ; |OutBuffer
00402645|.56 push esi ; |InBufferSize
00402646|.53 push ebx ; |InBuffer
00402647|.8B9D ACFDFFFF mov ebx, ; |
0040264D|.68 00202200 push 0x222000 ; |IoControlCode = 222000
00402652|.53 push ebx ; |hDevice
00402653|.FF15 40945100 call dword ptr ds:[<&KERNEL32.DeviceIoCo>; \DeviceIoControl
在数据窗口我们跟随下传入的字符串:本来输入的willJ,变成了vhmkK,说明应用层也做了对输入字符串的加密,通过下段 GetWindowTextA可以得到作者即时的对输入的数据进行了inbufer =inbufer + i;然后在后面又对字符窜做了inbufer = inbufer ^ (i+1)的操作才输入驱动层的这里比较简单,有兴趣的朋友可以跟下。二.驱动层:驱动层的主要函数:先进入初始化函数:signed int __stdcall InitDriver(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
signed int result; // eax@5
int v3; // edx@10
int v4; // edx@10
UNICODE_STRING SymbolicLinkName; // @1
PVOID v6; // @10
NTSTATUS v7; // @4
UNICODE_STRING DestinationString; // @1
PDEVICE_OBJECT DeviceObject; // @1
int v10; // @1
int v11; // @1
int v12; // @1
int v13; // @1
int v14; // @1
int v15; // @1
int v16; // @1
int v17; // @1
int v18; // @1
int v19; // @1
unsigned int i; // @1
DeviceObject = 0;
v10 = (int)"52Crackme";
v11 = 122;
v12 = 5;
v13 = 62;
v14 = 22;
v15 = 41;
v16 = 30;
v17 = 88;
v18 = 31;
v19 = 13;
RtlInitUnicodeString(&DestinationString, L"\\Device\\CrackMe0");
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\CrackMe0");
for ( i = 0; i <= 0x1B; ++i )
DriverObject->MajorFunction = (PDRIVER_DISPATCH)sub_11250;
DriverObject->MajorFunction = (PDRIVER_DISPATCH)sub_11220;
DriverObject->MajorFunction = (PDRIVER_DISPATCH)sub_11220;
DriverObject->MajorFunction = (PDRIVER_DISPATCH)DishpathCtrol;
DriverObject->DriverUnload = (PDRIVER_UNLOAD)UnloadDriver;
v7 = IoCreateDevice(DriverObject, 0x38u, &DestinationString, 0x22u, 0, 0, &DeviceObject);
if ( v7 >= 0 )
{
if ( DeviceObject )
{
DeviceObject->Flags |= 4u;
DeviceObject->AlignmentRequirement = 1;
v7 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if ( v7 >= 0 )
{
DeviceObject->Flags &= 0xFFFFFF7Fu;
v6 = DeviceObject->DeviceExtension;
v3 = (int)v6;
*(_DWORD *)v6 = *(_DWORD *)&DestinationString;
*(_DWORD *)(v3 + 4) = DestinationString.Buffer;
v4 = (int)v6;
*((_DWORD *)v6 + 2) = *(_DWORD *)&SymbolicLinkName;
*(_DWORD *)(v4 + 12) = SymbolicLinkName.Buffer;
*((_DWORD *)v6 + 4) = v10; // V6+4出为52Crackme
for ( i = 0; i < 9; ++i )
*((_DWORD *)v6 + i + 5) = *(&v11 + i);// 给V6起循环负值,分别为V11,因为上面变量依次压栈
DbgPrint("Driver Successfully Loaded");
result = 0;
}
else
{
result = v7;
}
}
else
{
result = -1073741591;
}
}
else
{
result = v7;
}
return result;
}
就是驱动相应的一些初始化操作,不过这里有几个关键的地方:v10 =(int)"52Crackme";v11 = 122;v12 = 5;v13 = 62;v14 = 22;v15 = 41;v16 = 30;v17 = 88;v18 = 31;v19 = 13;这个是在算法里会用到的。然后就是进入关键的分发函数(关键的所在):signed int __stdcall DishpathCtrol(PDEVICE_OBJECT a1, int Irp)
{
char v2; // ST13_1@2
int v4; // @1
unsigned int i; // @6
PVOID v6; // @1
signed int v7; // @1
unsigned int v8; // @3
char *v9; // @1
char *v10; // @3
int irpStack; // @1
unsigned int v12; // @3
char *v13; // @3
v7 = 0;
irpStack = sub_113F0(Irp); // PIO_STACK_LOCATION irpStack获取当前IPR设备栈
v6 = a1->DeviceExtension; // 获取设备扩展,在入口函数初始化的
v9 = (char *)*((_DWORD *)v6 + 4); // 获取52Crackme
v4 = *((_DWORD *)v6 + 4);
do
v2 = *(_BYTE *)v4++; // 计算52CracKme的长度
while ( v2 );
v13 = *(char **)(Irp + 12); // buffer
v12 = *(_DWORD *)(irpStack + 8); // inputBufferLength
v10 = *(char **)(Irp + 12); // buffer
v8 = *(_DWORD *)(irpStack + 4); // outBufferLenth
if ( *(_DWORD *)(irpStack + 12) == 0x222000 ) // ioc 控制码
{
if ( v4 - (_DWORD)(v9 + 1) == v12 ) // 判断输入长度
{
for ( i = 0; i < v12; ++i )
{
if ( v9 != (*((_DWORD *)v6 + i + 5) ^ (2 * i + 2) ^ v13) )// 用这个算法推出V13的值
{ // (*((_DWORD *)v6 + i + 5)依次取出V6
v8 = 0;
break;
}
v10 = v13 + 2 * i;
v8 = i;
}
}
}
else
{
v7 = -1073741262;
}
*(_DWORD *)(Irp + 24) = v7; // 返回状态
*(_DWORD *)(Irp + 28) = v8; // inputBufferLength实际返回长度
IofCompleteRequest((PIRP)Irp, 0); // 表示完成
return v7;
}
然后我用C语言还原下KEY吧:#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
char *key = "52Crackme";
int Value = {122, 5, 62, 22, 41, 30, 88, 31, 13};
int Strlen_Key = 0, i, j;
Strlen_Key = strlen(key);
char Result = {0};
for ( i = 0; i < Strlen_Key; i++)//驱动层算法
{
Result = key ^ Value ^ (2 *(i + 1));
}
for ( j = 0; j < Strlen_Key; j++)//应用层算法
{
Result = Result ^ (j + 1);
Result = Result - j;
printf ("%c", Result);
}
printf("\n");
return 0;
}
本人很菜啊,第一次在贵宝地发帖,很多分析不到位,还请xiaobang,klxnw1,Peace 等各大牛多多指教
我是沙发呀,来学习下 {:1_930:}沙发继续膜拜J牛 厉害啊现在都应用到 驱动层了小菜不及感谢楼主详细分解 太深奥了 根本不懂~ xiaobang 发表于 2012-5-8 22:56 static/image/common/back.gif
沙发继续膜拜J牛
我还得多多向bang姐学习{:1_930:} willJ老师果断威武,涉及到驱动了。
分析的太精彩了。 膜拜willJ大大 膜拜J大{:1_931:} J牛,你才把答案放出来。。。。放出来我也看不懂。哈哈