2012年7月22日 星期日

Hit 2012 binary 1 答案

感想:這題只有韓國隊PLUSX有解出來,讓我有點意外,其實題目設計不是為了難倒人,都是Windows Driver的基礎概念而已,下面解密一下讓許多人苦惱2天的題目....@_@



題目:Kenny意外地從探險家手上獲得了一張海外的藏寶圖,但看起來似乎失去了下半部分,你能幫助他找到寶藏嗎??

連結:http://dl.dropbox.com/u/19611440/binary1.zip

開啟Keyexe.jpge後會在Temp資料夾產生3個檔案



1  



解法一:



這題主要考的是Window Kernel Ioctl的概念,先用工具加載SYS驅動檔案後



透過發送0x6666Ioctl後,用Debugview可以看到關鍵的檔案名稱,

2  



下圖 0是加載驅動後提示,1是發送錯誤的Ioctl2是發送正確的Ioctl



3  

若發送正確的Ioctl 會顯示Boracay.exe

關鍵在於,必須把原始的.exe 名稱改成Boracay.exe

 4

重新執行後Keyà “hey it nice“就會透過DebugVIew (呼應OutputdebugString)噴出來。 PS:一定要Boracay.exe ---->爆破無效…XD

 5  



附上Code:




#include "stdafx.h"
#include "windows.h"
#define Symblo_NAME L"\\\\.\\Kenny"
#ifndef CTL_CODE
    #include 
#endif
                                                                                                                       
#define Pass_CTL_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x6666,METHOD_BUFFERED,FILE_ANY_ACCESS)  

int _tmain(int argc, _TCHAR* argv[])

{
           HANDLE hDevice = CreateFile(Symblo_NAME,GENERIC_READ | GENERIC_WRITE,0,   NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL );
           UCHAR InputBuffer[10];
           UCHAR OutputBuffer[10];
           DWORD dwOutput;
           DeviceIoControl(hDevice, Pass_CTL_CODE, InputBuffer, 10, &OutputBuffer, 10, &dwOutput, NULL);
           return 0;
}



解法二(暴力法)



即使完全不懂Driver,用IDA也可以解,拖SYS檔案進IDA,找到

6  



手動算一下Xor 0x66 ,共11Byte,也是可以算出Boracay.exe



最後也必須把原始的.exe 名稱改成Boracay.exe,重新執行後Key就會噴出來。

7  









2012年5月4日 星期五

總結一下得到內核模組位址的方法

網上說的比較常見的4種方法:

1、通過DriverEntry傳入的DriverObject參數的DriverSection成員指向LDR_DATA_TABLE_ENTRY結構,通過遍歷這張表得到ntoskrnl的基址和大小

2ZwQuerySystemInformation大法

3、搜索記憶體 

4、利用KPCR結構

存在的問題:

1、第1種方法和第4種方法得到的結果比ZwQuerySystemInformation少一個

2、第1種方法如果輸出BaseDllNamentoskrnl.exe,如果輸出FullDllName則是:\WINDOWS\system32\ntkrnlpa.exe,位址都是:804d8000,不明白為何

環境:虛擬機器VMWareWIN XP SP3  + WDK ---- WINXP Check方式編譯


#include <ntddk.h>
//---------------------------------//
//下面的結構包含了一些重要資訊。如:PsLoadedModuleList ,它是Windows載入的所有內核模組構成的鏈表的表頭。
//PsLoadedModuleList就是如下這個結構體中InLoadOrderLinks。即為LDR_DATA_TABLE_ENTRY結構的第一項。
#pragma pack(push)//結構定義
#pragma pack(1)                  
typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY         InLoadOrderLinks;
    LIST_ENTRY         InMemoryOrderLinks;
    LIST_ENTRY         InInitializationOrderLinks;
    PVOID              DllBase;
    PVOID              EntryPoint;
    ULONG              SizeOfImage;
    UNICODE_STRING     FullDllName;
    UNICODE_STRING     BaseDllName;
    ULONG              Flags;
    USHORT             LoadCount;
    USHORT             TlsIndex;
    union
    {
        LIST_ENTRY     HashLinks;
        struct
        {
            PVOID      SectionPointer;
            ULONG      CheckSum;
        };
    };
    union
    {
        ULONG           TimeDateStamp;
        PVOID           LoadedImports;
    };
    PVOID               EntryPointActivationContext;
    PVOID               PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
#pragma pack(pop)
//---------------------------------------------------------------------------------------------------//函式宣告
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath);
NTSTATUS DriverUnload();
//Method3用到,指定當前執行緒運行在那個處理器
NTKERNELAPI VOID KeSetSystemAffinityThread ( KAFFINITY Affinity );
NTKERNELAPI VOID KeRevertToUserAffinityThread ( VOID );

NTKERNELAPI NTSTATUS ZwQuerySystemInformation(
                                            IN ULONG SystemInformationClass,
                                              IN OUT PVOID SystemInformation,
                                              IN ULONG SystemInformationLength,
                                              IN PULONG ReturnLength OPTIONAL 
                        );

#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, DriverUnload)
//---------------------------------------------------------------------------------------------------//變數、常量、結構定義
UNICODE_STRING BaseName;

#define SystemModuleInformation 11  //Method2要用到11功能號

typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY
{
  ULONG   Unknow1;
  ULONG   Unknow2;
  #ifdef  _WIN64
  ULONG   Unknow3;
  ULONG   Unknow4:
  #endif
  PVOID   Base;
  ULONG   Size;
  ULONG   Flags;
  USHORT  Index;
  USHORT  NameLength;
  USHORT  LoadCount;
  USHORT  ModuleNameOffset;
  char    ImageName[256];
}SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;

typedef struct _SYSTEM_MODULE_INFORMATION
{
   ULONG Count;//內核中以載入的模組的個數
   SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
}SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;


//---------------------------------------------------------------------------------------------------//
/*
    用到了DriverObject域的InLoadOrderLinks鏈表

注意:
   下面的代碼會用到一個宏:
---------------------------------------------------------------------------------------------------------------------
CONTAINING_RECORD 這樣的一個宏,它的定義如下:
#define CONTAINING_RECORD(address, type, field) ((type *)( (PCHAR)(address) - (ULONG_PTR)(&((type*)0)->field)))

根據網上資料:就是address -(fieldtype中的偏移)
----------------------------------------------------------------------------------------------------------------------
*/
VOID Method1(IN PDRIVER_OBJECT DriverObject)//遍歷鏈表
{
  ULONG Base=0;//模組基底位址
  LDR_DATA_TABLE_ENTRY* SectionBase=NULL;
  LIST_ENTRY* Entry=NULL;
    LIST_ENTRY InLoadOrderLinks;
    ULONG num=0;
  Entry=((LIST_ENTRY*)DriverObject->DriverSection)->Flink;

  do
  {
    SectionBase=CONTAINING_RECORD(Entry,LDR_DATA_TABLE_ENTRY,InLoadOrderLinks);//得到這個Entry所屬的Section的地址,此方法經過驗證可行

      if (SectionBase->EntryPoint && 
            SectionBase->BaseDllName.Buffer &&
            SectionBase->FullDllName.Buffer && 
            SectionBase->LoadCount
            )
    {
      DbgPrint("方法一遍歷模組名稱:%wZ,地址:%x\n",&(SectionBase->FullDllName),SectionBase->DllBase);
      //DbgPrint("方法一遍歷模組名稱:%wZ,地址:%8X\n",&(SectionBase->BaseDllName),SectionBase->DllBase);
      num++;
      /*if(!RtlCompareUnicodeString(&(SectionBase->BaseDllName),&BaseName,FALSE))
      {
        DbgPrint("方法一模組名稱:%wZ,地址:%x\n",&(SectionBase->BaseDllName),SectionBase->DllBase);
      }*/
    }
    Entry=Entry->Flink;
    
  }while(Entry!=((LIST_ENTRY*)DriverObject->DriverSection)->Flink);//直到遍歷回來
  DbgPrint("方法一得到模組總數:%d\n",num);
}

void Method2()//ZwQuerySystemInformation大法
{
  PVOID pBuffer=0;//緩衝區
  NTSTATUS Result;//查詢結果
  ULONG NeedSize;
  PSYSTEM_MODULE_INFORMATION pSystemModuleInformation;//將結果強制轉換為該類型
  ULONG BufferSize = 0x5000;//初始分配記憶體大小,沒有採用查詢再分配的迴圈方法
  ULONG ModuleCount;//模組總數
  ULONG i;
  do
  {
    pBuffer=ExAllocatePool(NonPagedPool,BufferSize);
    if(pBuffer==NULL)
    {
      DbgPrint("分配記憶體失敗!\n");
      return FALSE;
    }
    Result=ZwQuerySystemInformation(SystemModuleInformation,pBuffer,BufferSize,&NeedSize);
    if(Result==STATUS_INFO_LENGTH_MISMATCH )//分配不夠
    {
      ExFreePool(pBuffer);
      //大小乘以2,重新分配
      BufferSize*=2;
    }
    else if(!NT_SUCCESS(Result))//失敗,放棄吧
    {
      DbgPrint( "查詢失敗,錯誤碼:%8X\n", Result );
      ExFreePool(pBuffer);
      return FALSE;
    }
   }while( Result == STATUS_INFO_LENGTH_MISMATCH );

  pSystemModuleInformation = (PSYSTEM_MODULE_INFORMATION)pBuffer;//類型轉換
  ModuleCount=pSystemModuleInformation->Count;//模組總數
  for(i=0;i<ModuleCount;i++)
  {
    DbgPrint( "方法二遍歷模組名稱:%s,地址:%8X\n", pSystemModuleInformation->Module[i].ImageName, pSystemModuleInformation->Module[i].Base );
  }
  DbgPrint("方法二得到模組總數:%d\n",ModuleCount);
  ExFreePool(pBuffer);
  return TRUE;
}
VOID Method3(ULONG Base)//搜索記憶體,從0x80000000-----0xa0000000
{
  ;
}
//內核中FS寄存器指向KPCR結構,每個處理器都有一個,使用第一個處理器即可其中比較重要的是KdVersionBlock這個指標它指向一個DBGKD_GET_VERSION64這個結構.

//這個結構體裡面包含了一些重要資訊。如:PsLoadedModuleList ,它是Windows載入的所有內核模組構成的鏈表的表頭

//兩個處理器對應的KPCR結構是有區別的只有第一個處理器的KPCRKdVersionBlock才指向DBGKD_GET_VERSION64這個結構.

//-------------------------------------仔細觀察定義會發現,這個跟使用DriverObject方法達到的鏈表示一樣的!
void Method4()                              
{
  ULONG Addr;//內核地址

  LIST_ENTRY* Entry=NULL;
    LIST_ENTRY InLoadOrderLinks;
  LDR_DATA_TABLE_ENTRY* SectionBase=NULL;//LdrData->DllBase,LdrData->FullDllNme

    ULONG num=0;
    //-----------------------------------------------------------------------------//在莫灰灰基礎上修改一小部分
    KeSetSystemAffinityThread(1);//使當前執行緒運行在第一個處理器上
  _asm
  {
    push  eax
    mov   eax,FS:[0x34]                     ;指向KdVersionBlock的指標
    add   eax,18h                           ;得到指向PsLoadedModuleList的地址,即該指標的地址,指針裡存有PsLoadedModuleList的地址
    mov   eax,[eax]                         ;得到PsLoadedModuleList的地址
        mov   eax,[eax]                         ;得到PsLoadedModuleList的內容
    //mov   eax,[eax+18h]                     ;取出DllBase, ntoskrnl.exe的基底位址
        mov   Addr,eax
        pop  eax
  }
  
  KeRevertToUserAffinityThread();//恢復執行緒運行的處理器
  //----------------------------------------------------------------------// 以下跟方法一重複

    Entry=(LIST_ENTRY*)Addr;

  do
  {
    SectionBase=CONTAINING_RECORD(Entry,LDR_DATA_TABLE_ENTRY,InLoadOrderLinks);//得到這個Entry所屬的Section的地址,此方法經過驗證可行

      if (SectionBase->EntryPoint && 
            SectionBase->BaseDllName.Buffer &&
            SectionBase->FullDllName.Buffer &&
            SectionBase->LoadCount
            )
    {
      DbgPrint("方法四遍歷模組名稱:%wZ,地址:%8X\n",&(SectionBase->FullDllName),SectionBase->DllBase);
      num++;
    }
    Entry=Entry->Flink;
    
  }while(Entry!=(LIST_ENTRY*)Addr);//直到遍歷回來
  DbgPrint("方法四得到模組總數:%d\n",num);
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
{
  ULONG EntryAddr;
  _asm
  {
    push ecx;
    lea ecx,[ebp][4];//得到DriverEntry返回地址
    mov EntryAddr,ecx;
    pop ecx;
  }
  EntryAddr=*(ULONG*)EntryAddr;
  DbgPrint("驅動返回地址:%8X\n",EntryAddr);
  RtlInitUnicodeString(&BaseName,L"ntoskrnl.exe");
  DbgPrint("驅動載入成功!\n");
  //-------------------------------//
  Method1(pDriverObject);
  //-------------------------------//
  Method2();
       //-------------------------------//
  Method3();
        //-------------------------------//
  Method4();
  //-------------------------------//
  pDriverObject->DriverUnload=DriverUnload;
  return STATUS_SUCCESS;
}
NTSTATUS DriverUnload()
{
  DbgPrint("驅動卸載成功\n");
}