本帖最后由 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:[ebp+0x8]
7C801A30 E8 CFC60000 call kernel32.7C80E104
7C801A35 85C0 test eax,eax
7C801A37 74 1E je Xkernel32.7C801A57
7C801A39 FF75 20 push dword ptr ss:[ebp+0x20]
7C801A3C FF75 1C push dword ptr ss:[ebp+0x1C]
7C801A3F FF75 18 push dword ptr ss:[ebp+0x18]
7C801A42 FF75 14 push dword ptr ss:[ebp+0x14]
7C801A45 FF75 10 push dword ptr ss:[ebp+0x10]
7C801A48 FF75 0C push dword ptr ss:[ebp+0xC]
7C801A4B FF70 04 push dword ptr ds:[eax+0x4]
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,[local.1] ; |
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,[local.148] ; |
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,[local.149] ; |
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; // [sp+0h] [bp-48h]@1
PVOID v6; // [sp+8h] [bp-40h]@10
NTSTATUS v7; // [sp+Ch] [bp-3Ch]@4
UNICODE_STRING DestinationString; // [sp+10h] [bp-38h]@1
PDEVICE_OBJECT DeviceObject; // [sp+18h] [bp-30h]@1
int v10; // [sp+1Ch] [bp-2Ch]@1
int v11; // [sp+20h] [bp-28h]@1
int v12; // [sp+24h] [bp-24h]@1
int v13; // [sp+28h] [bp-20h]@1
int v14; // [sp+2Ch] [bp-1Ch]@1
int v15; // [sp+30h] [bp-18h]@1
int v16; // [sp+34h] [bp-14h]@1
int v17; // [sp+38h] [bp-10h]@1
int v18; // [sp+3Ch] [bp-Ch]@1
int v19; // [sp+40h] [bp-8h]@1
unsigned int i; // [sp+44h] [bp-4h]@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[i] = (PDRIVER_DISPATCH)sub_11250;
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_11220;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_11220;
DriverObject->MajorFunction[14] = (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[i+5]起循环负值,分别为V11[i],因为上面变量依次压栈
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; // [sp+14h] [bp-38h]@1
unsigned int i; // [sp+18h] [bp-34h]@6
PVOID v6; // [sp+1Ch] [bp-30h]@1
signed int v7; // [sp+20h] [bp-2Ch]@1
unsigned int v8; // [sp+24h] [bp-28h]@3
char *v9; // [sp+34h] [bp-18h]@1
char *v10; // [sp+3Ch] [bp-10h]@3
int irpStack; // [sp+40h] [bp-Ch]@1
unsigned int v12; // [sp+44h] [bp-8h]@3
char *v13; // [sp+48h] [bp-4h]@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[i] != (*((_DWORD *)v6 + i + 5) ^ (2 * i + 2) ^ v13[i]) )// 用这个算法推出V13[i]的值
{ // (*((_DWORD *)v6 + i + 5)依次取出V6[i+5]
v8 = 0;
break;
}
v10[i] = v13[i] + 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[9] = {122, 5, 62, 22, 41, 30, 88, 31, 13};
int Strlen_Key = 0, i, j;
Strlen_Key = strlen(key);
char Result[100] = {0};
for ( i = 0; i < Strlen_Key; i++)//驱动层算法
{
Result[i] = key[i] ^ Value[i] ^ (2 *(i + 1));
}
for ( j = 0; j < Strlen_Key; j++)//应用层算法
{
Result[j] = Result[j] ^ (j + 1);
Result[j] = Result[j] - j;
printf ("%c", Result[j]);
}
printf("\n");
return 0;
}
本人很菜啊,第一次在贵宝地发帖,很多分析不到位,还请xiaobang,klxnw1,Peace 等各大牛多多指教
|