willJ 发表于 2012-5-8 22:51

吾爱破解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 等各大牛多多指教

linwenlin 发表于 2012-5-8 22:55

我是沙发呀,来学习下

Rookietp 发表于 2012-5-8 22:56

{:1_930:}沙发继续膜拜J牛

ainddky 发表于 2012-5-8 22:56

厉害啊现在都应用到 驱动层了小菜不及感谢楼主详细分解

statlove 发表于 2012-5-8 22:58

太深奥了 根本不懂~

willJ 发表于 2012-5-8 22:58

xiaobang 发表于 2012-5-8 22:56 static/image/common/back.gif
沙发继续膜拜J牛

我还得多多向bang姐学习{:1_930:}

Chief 发表于 2012-5-8 23:04

willJ老师果断威武,涉及到驱动了。
分析的太精彩了。

19nuclear91 发表于 2012-5-8 23:25

膜拜willJ大大

HadgeROL 发表于 2012-5-8 23:31

膜拜J大{:1_931:}

Kavia 发表于 2012-5-8 23:45

J牛,你才把答案放出来。。。。放出来我也看不懂。哈哈
页: [1] 2 3
查看完整版本: 吾爱破解2012CM大赛破文-LittleFater