信息来源:http://www.whitecell.org
原始连接:http://delphi.ktop.com.tw/TOPIC.ASP?TOPIC_ID=50997

內核級HOOK的幾種實現與應用

Author : sinister
Email   : sinister@whitecell.org
HomePage: http://www.whitecell.org


實現內核級 HOOK 對於攔截、分析、跟蹤系統內核起著致關重要的作用。實現的方法不同意味著應用側重點的不同。

如想要攔截 NATIVE API 那麼可能常用的就是 HOOK SERVICE TABLE 的方法。
如果要分析一些系統調用,那麼可能想到用 HOOK INT 2E 中斷來實現。

如果想要攔截或跟蹤其他內核 DRIVER 的調用,那麼就要用到HOOK PE 的方法來實現。

這裏我們更注重的是實現,原理方面已有不少高手在網上發表過文章。

大家可以結合起來讀。
下面以我寫的幾個實例程式來講解一下各種方法的實現。
錯誤之處還望各位指正。


1、HOOK SERVICE TABLE 方法:
  這種方法對於攔截 NATIVE API 來說用的比較多。
原理就是通過替換系統導
出的一個 SERVICE TABLE 中相應的 NATIVE API 的位址來達到攔截的目的。
因為此方法較為簡單,網上也有不少資料來介紹。
所以這裏就不給出實例程式了。SERVICE TABLE 的結構如下:

typedef struct ServiceDescriptorEntry {
  unsigned int *ServiceTableBase;
  unsigned int *ServiceCounterTableBase;
  unsigned int NumberOfServices;
  unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
 

2、HOOK INT 2E 方法:
  這種方法對於跟蹤、分析系統調用來說用的比較多。原理是通過替換 IDT
表中的 INT 2E 中斷,使之指向我們自己的中斷服務處理常式來實現的。
掌握此方法需要你對保護模式有一定的基礎。下面的程式演示了這一過程。


/*****************************************************************
檔案名     : WssHookInt2e.c
描述       : 系統調用跟蹤
作者       : sinister
最後修改日期 : 2002-11-02
*****************************************************************/

#include "ntddk.h"
#include "string.h"

#define DWORD unsigned __int32
#define WORD unsigned __int16
#define BYTE unsigned __int8
#define BOOL __int32

#define LOWORD(l)       ((WORD)(l))
#define HIWORD(l)       ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))
#define LOBYTE(w)       ((BYTE)(w))
#define HIBYTE(w)       ((BYTE)(((WORD)(w) >> 8) & 0xFF))

#define MAKELONG(a, b) ((LONG) (((WORD) (a)) | ((DWORD) ((WORD) (b))) << 16))

#define SYSTEMCALL 0x2e
#define SYSNAME "System"
#define PROCESSNAMELEN 16

#pragma pack(1)

//定義 IDTR
typedef struct tagIDTR {
    WORD IDTLimit;
    WORD LowIDTbase;
    WORD HiIDTbase;
}IDTR, *PIDTR;

//定義 IDT
typedef struct tagIDTENTRY{
  WORD OffsetLow;
  WORD selector;
  BYTE unused_lo;
  unsigned char unused_hi:5;
  unsigned char DPL:2;
  unsigned char P:1;
  WORD OffsetHigh;
} IDTENTRY, *PIDTENTRY;


#pragma pack()

DWORD   OldInt2eService;
ULONG   ProcessNameOffset;
TCHAR   ProcessName[PROCESSNAMELEN];

static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);
ULONG GetProcessNameOffset();
VOID GetProcessName( PCHAR Name );
VOID InstallNewInt2e();
VOID UninstallNewInt2e();

VOID __fastcall NativeApiCall()
{
  KIRQL OldIrql;
 
  DWORD ServiceID;
  DWORD ProcessId;

  __asm mov ServiceID,eax;


  ProcessId = (DWORD)PsGetCurrentProcessId();
  GetProcessName(ProcessName);

  KeRaiseIrql(HIGH_LEVEL, &OldIrql); // 提升當前的 IRQL 級別防止被中斷


  switch ( ServiceID )
  {
        case 0x20:
          DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateFile() \n",ProcessName,ProcessId);
          break;

        case 0x2b:
          DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateSection() \n",ProcessName,ProcessId);          
          break;


        case 0x30:
          DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateToken() \n",ProcessName,ProcessId);          
          break;
         
  }

  KeLowerIrql(OldIrql); //恢復原始 IRQL

}

__declspec(naked) NewInt2eService()
{
  __asm{
    pushad
    pushfd
    push fs
    mov bx,0x30
    mov fs,bx
    push ds
    push es

    sti
    call NativeApiCall; // 調用記錄函數
    cli

    pop es
    pop ds
    pop fs
    popfd
    popad

    jmp   OldInt2eService; //跳到原始 INT 2E 繼續工作
  }
}

VOID InstallNewInt2e()
{

  IDTR       idtr;
  PIDTENTRY   OIdt;
  PIDTENTRY   NIdt;

  //得到 IDTR 中得段界限與基底位址
  __asm {
    sidt idtr;
  }

  //得到IDT基底位址
  OIdt = (PIDTENTRY)MAKELONG(idtr.LowIDTbase,idtr.HiIDTbase);

  //保存原來的 INT 2E 服務常式
  OldInt2eService = MAKELONG(OIdt[SYSTEMCALL].OffsetLow,OIdt[SYSTEMCALL].OffsetHigh);
 
  NIdt = &(OIdt[SYSTEMCALL]);

  __asm {
    cli
    lea eax,NewInt2eService; //得到新的 INT 2E 服務常式偏移
    mov ebx, NIdt;
    mov [ebx],ax;   //INT 2E 服務常式低 16 位元
    shr eax,16     //INT 2E 服務常式高 16 位元
    mov [ebx+6],ax;
    lidt idtr //裝入新的 IDT
    sti
  }

}

VOID UninstallNewInt2e()
{
  IDTR       idtr;
  PIDTENTRY   OIdt;
  PIDTENTRY   NIdt;

  __asm {
    sidt idtr;
  }

  OIdt = (PIDTENTRY)MAKELONG(idtr.LowIDTbase,idtr.HiIDTbase);

  NIdt = &(OIdt[SYSTEMCALL]);

  _asm {
    cli
    lea eax,OldInt2eService;
    mov ebx, NIdt;
    mov [ebx],ax;
    shr eax,16
    mov [ebx+6],ax;
    lidt idtr
    sti
  }

}




// 驅動入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
 
  UNICODE_STRING nameString, linkString;
  PDEVICE_OBJECT deviceObject;
  NTSTATUS     status;
  HANDLE       hHandle;
  int           i;
 

  //卸載驅動
  DriverObject->DriverUnload = DriverUnload;

  //建立設備
  RtlInitUnicodeString( &nameString, L"\\Device\\WssHookInt2e" );
 
  status = IoCreateDevice( DriverObject,
                  0,
                  &nameString,
                  FILE_DEVICE_UNKNOWN,
                  0,
                  TRUE,
                  &deviceObject
                  );
                 

  if (!NT_SUCCESS( status ))
    return status;
 

  RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssHookInt2e" );

  status = IoCreateSymbolicLink (&linkString, &nameString);

  if (!NT_SUCCESS( status ))
  {
    IoDeleteDevice (DriverObject->DeviceObject);
    return status;
  }  
 

  for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)   {

      DriverObject->MajorFunction = MydrvDispatch;
  }

    DriverObject->DriverUnload = DriverUnload;

  ProcessNameOffset = GetProcessNameOffset();
  InstallNewInt2e();

return STATUS_SUCCESS;
}



//處理設備物件操作

static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  Irp->IoStatus.Status = STATUS_SUCCESS;
  Irp->IoStatus.Information = 0L;
  IoCompleteRequest( Irp, 0 );
  return Irp->IoStatus.Status;
 
}



VOID DriverUnload (IN PDRIVER_OBJECT   pDriverObject)
{
  UNICODE_STRING nameString;

  UninstallNewInt2e();
  RtlInitUnicodeString( &nameString, L"\\DosDevices\\WssHookInt2e" );  
  IoDeleteSymbolicLink(&nameString);
  IoDeleteDevice(pDriverObject->DeviceObject);

  return;
}



ULONG GetProcessNameOffset()
{
    PEPROCESS curproc;
    int i;
   
    curproc = PsGetCurrentProcess();

    //
    // Scan for 12KB, hopping the KPEB never grows that big!
    //
    for( i = 0; i < 3*PAGE_SIZE; i++ ) {

        if( !strncmp( SYSNAME, (PCHAR) curproc + i, strlen(SYSNAME) )) {

          return i;
        }
    }

    //
    // Name not found - oh, well
    //
    return 0;
}

VOID GetProcessName( PCHAR Name )
{

    PEPROCESS curproc;
    char *nameptr;
    ULONG i;

    if( ProcessNameOffset ) {

        curproc = PsGetCurrentProcess();
        nameptr = (PCHAR) curproc + ProcessNameOffset;
        strncpy( Name, nameptr, 16 );

    } else {

        strcpy( Name, "???");
    }
}


3、 HOOK PE 方法
這種方法對於攔截、分析其他內核驅動的函數調用來說用的比較多。
原理
是根據替換 PE 格式導出表中的相應函數來實現的。
此方法中需要用到一些小技巧。
如內核模式並沒有直接提供類似應用層的 GetModuleHandl()、GetProcAddress() 等函數來獲得模組的位址。
那麼我們就需要自己來編寫,這裏用到了一個未公開的函數與結構。
ZwQuerySystemInformation 與 SYSTEM_MODULE_INFORMATION 來實現得到模組的基底位址。
這樣我們就可以根據PE 格式來枚舉導出表中的函數來替換了。
但這又引出了一個問題,那就是從WINDOWS 2000 後內核資料的頁屬性都是唯讀的,不能更改。
內核模式也沒有提供類似應用層的 VirtualProtectEx() 等函數來修改頁面屬性。
那麼也需要我們自己來編寫。
因為我們是在內核模式所以我們可以通過修改 cr0 寄存器的的防寫位元來達到我們的目的。
這樣我們所期望的攔截內核模式函數的功能便得以實現。
此方法需要你對 PE 格式有一定的基礎。下面的程式演示了這一過程。



/*****************************************************************
檔案名     : WssHookPE.c
描述       : 攔截內核函數
作者       : sinister
最後修改日期 : 2002-11-02
*****************************************************************/

#include "ntddk.h"
#include "windef.h"


typedef enum _SYSTEM_INFORMATION_CLASS {
  SystemBasicInformation,
  SystemProcessorInformation,
  SystemPerformanceInformation,
  SystemTimeOfDayInformation,
  SystemNotImplemented1,
  SystemProcessesAndThreadsInformation,
  SystemCallCounts,
  SystemConfigurationInformation,
  SystemProcessorTimes,
  SystemGlobalFlag,
  SystemNotImplemented2,
  SystemModuleInformation,
  SystemLockInformation,
  SystemNotImplemented3,
  SystemNotImplemented4,
  SystemNotImplemented5,
  SystemHandleInformation,
  SystemObjectInformation,
  SystemPagefileInformation,
  SystemInstructionEmulationCounts,
  SystemInvalidInfoClass1,
  SystemCacheInformation,
  SystemPoolTagInformation,
  SystemProcessorStatistics,
  SystemDpcInformation,
  SystemNotImplemented6,
  SystemLoadImage,
  SystemUnloadImage,
  SystemTimeAdjustment,
  SystemNotImplemented7,
  SystemNotImplemented8,
  SystemNotImplemented9,
  SystemCrashDumpInformation,
  SystemExceptionInformation,
  SystemCrashDumpStateInformation,
  SystemKernelDebuggerInformation,
  SystemContextSwitchInformation,
  SystemRegistryQuotaInformation,
  SystemLoadAndCallImage,
  SystemPrioritySeparation,
  SystemNotImplemented10,
  SystemNotImplemented11,
  SystemInvalidInfoClass2,
  SystemInvalidInfoClass3,
  SystemTimeZoneInformation,
  SystemLookasideInformation,
  SystemSetTimeSlipEvent,
  SystemCreateSession,
  SystemDeleteSession,
  SystemInvalidInfoClass4,
  SystemRangeStartInformation,
  SystemVerifierInformation,
  SystemAddVerifier,
  SystemSessionProcessesInformation
} SYSTEM_INFORMATION_CLASS;


typedef struct tagSYSTEM_MODULE_INFORMATION {
  ULONG Reserved[2];
  PVOID Base;
  ULONG Size;
  ULONG Flags;
  USHORT Index;
  USHORT Unknown;
  USHORT LoadCount;
  USHORT ModuleNameOffset;
  CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

#define IMAGE_DOS_SIGNATURE     0x5A4D     // MZ
#define IMAGE_NT_SIGNATURE     0x50450000 // PE00
#define IMAGE_NT_SIGNATURE1     0x00004550   // 00EP

typedef struct _IMAGE_DOS_HEADER {     // DOS .EXE header
  WORD   e_magic;               // Magic number
  WORD   e_cblp;               // Bytes on last page of file
  WORD   e_cp;                 // Pages in file
  WORD   e_crlc;               // Relocations
  WORD   e_cparhdr;             // Size of header in paragraphs
  WORD   e_minalloc;             // Minimum extra paragraphs needed
  WORD   e_maxalloc;             // Maximum extra paragraphs needed
  WORD   e_ss;                 // Initial (relative) SS value
  WORD   e_sp;                 // Initial SP value
  WORD   e_csum;               // Checksum
  WORD   e_ip;                 // Initial IP value
  WORD   e_cs;                 // Initial (relative) CS value
  WORD   e_lfarlc;             // File address of relocation table
  WORD   e_ovno;               // Overlay number
  WORD   e_res[4];             // Reserved words
  WORD   e_oemid;               // OEM identifier (for e_oeminfo)
  WORD   e_oeminfo;             // OEM information; e_oemid specific
  WORD   e_res2[10];             // Reserved words
  LONG   e_lfanew;             // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;


typedef struct _IMAGE_FILE_HEADER {
  WORD   Machine;
  WORD   NumberOfSections;
  DWORD   TimeDateStamp;
  DWORD   PointerToSymbolTable;
  DWORD   NumberOfSymbols;
  WORD   SizeOfOptionalHeader;
  WORD   Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

typedef struct _IMAGE_DATA_DIRECTORY {
  DWORD   VirtualAddress;
  DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES   16

//
// Optional header format.
//

typedef struct _IMAGE_OPTIONAL_HEADER {
  //
  // Standard fields.
  //

  WORD   Magic;
  BYTE   MajorLinkerVersion;
  BYTE   MinorLinkerVersion;
  DWORD   SizeOfCode;
  DWORD   SizeOfInitializedData;
  DWORD   SizeOfUninitializedData;
  DWORD   AddressOfEntryPoint;
  DWORD   BaseOfCode;
  DWORD   BaseOfData;

  //
  // NT additional fields.
  //

  DWORD   ImageBase;
  DWORD   SectionAlignment;
  DWORD   FileAlignment;
  WORD   MajorOperatingSystemVersion;
  WORD   MinorOperatingSystemVersion;
  WORD   MajorImageVersion;
  WORD   MinorImageVersion;
  WORD   MajorSubsystemVersion;
  WORD   MinorSubsystemVersion;
  DWORD   Win32VersionValue;
  DWORD   SizeOfImage;
  DWORD   SizeOfHeaders;
  DWORD   CheckSum;
  WORD   Subsystem;
  WORD   DllCharacteristics;
  DWORD   SizeOfStackReserve;
  DWORD   SizeOfStackCommit;
  DWORD   SizeOfHeapReserve;
  DWORD   SizeOfHeapCommit;
  DWORD   LoaderFlags;
  DWORD   NumberOfRvaAndSizes;
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

typedef struct _IMAGE_NT_HEADERS {
  DWORD Signature;
  IMAGE_FILE_HEADER FileHeader;
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef IMAGE_NT_HEADERS32             IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32           PIMAGE_NT_HEADERS;

//
// Section header format.
//

#define IMAGE_SIZEOF_SHORT_NAME         8

typedef struct _IMAGE_SECTION_HEADER {
  BYTE   Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
        DWORD   PhysicalAddress;
        DWORD   VirtualSize;
  } Misc;
  DWORD   VirtualAddress;
  DWORD   SizeOfRawData;
  DWORD   PointerToRawData;
  DWORD   PointerToRelocations;
  DWORD   PointerToLinenumbers;
  WORD   NumberOfRelocations;
  WORD   NumberOfLinenumbers;
  DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

#define IMAGE_SIZEOF_SECTION_HEADER       40
//
// Export Format
//

typedef struct _IMAGE_EXPORT_DIRECTORY {
  DWORD   Characteristics;
  DWORD   TimeDateStamp;
  WORD   MajorVersion;
  WORD   MinorVersion;
  DWORD   Name;
  DWORD   Base;
  DWORD   NumberOfFunctions;
  DWORD   NumberOfNames;
  DWORD   AddressOfFunctions;   // RVA from base of image
  DWORD   AddressOfNames;       // RVA from base of image
  DWORD   AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

#define BASEADDRLEN 10

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


typedef NTSTATUS (* ZWCREATEFILE)(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);

ZWCREATEFILE   OldZwCreateFile;

static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);
VOID DisableWriteProtect( PULONG pOldAttr);
VOID EnableWriteProtect( ULONG ulOldAttr );
FARPROC HookFunction(   PCHAR pModuleBase, PCHAR pHookName, FARPROC pHookFunc );

NTSTATUS
HookNtCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);



PCHAR MyGetModuleBaseAddress( PCHAR pModuleName )
{
  PSYSTEM_MODULE_INFORMATION   pSysModule;  

  ULONG         uReturn;
  ULONG         uCount;
  PCHAR         pBuffer = NULL;
  PCHAR         pName   = NULL;
  NTSTATUS     status;
  UINT         ui;

  CHAR         szBuffer[BASEADDRLEN];
  PCHAR         pBaseAddress;
 
  status = ZwQuerySystemInformation( SystemModuleInformation, szBuffer, BASEADDRLEN, &uReturn );

  pBuffer = ( PCHAR )ExAllocatePool( NonPagedPool, uReturn );

  if ( pBuffer )
  {
    status = ZwQuerySystemInformation( SystemModuleInformation, pBuffer, uReturn, &uReturn );

    if( status == STATUS_SUCCESS )
    {
        uCount = ( ULONG )*( ( ULONG * )pBuffer );
        pSysModule = ( PSYSTEM_MODULE_INFORMATION )( pBuffer + sizeof( ULONG ) );

        for ( ui = 0; ui < uCount; ui++ )
        {
          pName = MyStrchr( pSysModule->ImageName, '\\' );

          if ( !pName )
          {
            pName = pSysModule->ImageName;
          }

          else {
            pName++;
          }

          if( !_stricmp( pName, pModuleName ) )
          {
            pBaseAddress = ( PCHAR )pSysModule->Base;
            ExFreePool( pBuffer );
            return pBaseAddress;
          }

          pSysModule ++;
        }
    }

    ExFreePool( pBuffer );
  }

  return NULL;
}


FARPROC HookFunction( PCHAR pModuleBase, PCHAR HookFunName, FARPROC HookFun )
{
  PIMAGE_DOS_HEADER       pDosHdr;
  PIMAGE_NT_HEADERS       pNtHdr;
  PIMAGE_SECTION_HEADER   pSecHdr;
  PIMAGE_EXPORT_DIRECTORY pExtDir;

  UINT             ui,uj;
  PCHAR             FunName;
  DWORD             *dwAddrName;
  DWORD             *dwAddrFun;
  FARPROC             pOldFun;
  ULONG             uAttrib;


  pDosHdr = ( PIMAGE_DOS_HEADER )pModuleBase;

  if ( IMAGE_DOS_SIGNATURE == pDosHdr->e_magic )
  {
    pNtHdr = ( PIMAGE_NT_HEADERS )( pModuleBase + pDosHdr->e_lfanew );

    if( IMAGE_NT_SIGNATURE == pNtHdr->Signature ||   IMAGE_NT_SIGNATURE1 == pNtHdr->Signature )
    {
        pSecHdr = ( PIMAGE_SECTION_HEADER )( pModuleBase + pDosHdr->e_lfanew + sizeof( IMAGE_NT_HEADERS ) );

        for ( ui = 0; ui < (UINT)pNtHdr->FileHeader.NumberOfSections; ui++ )
        {
          if ( !strcmp( pSecHdr->Name, ".edata" ) )
          {          
            pExtDir = ( PIMAGE_EXPORT_DIRECTORY )( pModuleBase + pSecHdr->VirtualAddress );
            dwAddrName = ( PDWORD )(pModuleBase + pExtDir->AddressOfNames );
            dwAddrFun = ( PDWORD )(pModuleBase + pExtDir->AddressOfFunctions );

            for ( uj = 0; uj < (UINT)pExtDir->NumberOfFunctions; uj++ )
            {
                FunName = pModuleBase + *dwAddrName;

                if( !strcmp( FunName, HookFunName ) )
                {
                  DbgPrint(" HOOK %s()\n",FunName);
                  DisableWriteProtect( &uAttrib );
                  pOldFun = ( FARPROC )( pModuleBase + *dwAddrFun );
                  *dwAddrFun = ( PCHAR )HookFun - pModuleBase;
                  EnableWriteProtect( uAttrib );
                  return pOldFun;
                }

              dwAddrName ++;
              dwAddrFun ++;
            }
          }

          pSecHdr++;
        }
    }
  }

  return NULL;
}


// 驅動入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
 
  UNICODE_STRING nameString, linkString;
  PDEVICE_OBJECT deviceObject;
  NTSTATUS     status;
  HANDLE       hHandle;
  PCHAR         pModuleAddress;
  int           i;
 

  //卸載驅動
  DriverObject->DriverUnload = DriverUnload;

  //建立設備
  RtlInitUnicodeString( &nameString, L"\\Device\\WssHookPE" );
 
  status = IoCreateDevice( DriverObject,
                  0,
                  &nameString,
                  FILE_DEVICE_UNKNOWN,
                  0,
                  TRUE,
                  &deviceObject
                  );
                 

  if (!NT_SUCCESS( status ))
    return status;
 

  RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssHookPE" );

  status = IoCreateSymbolicLink (&linkString, &nameString);

  if (!NT_SUCCESS( status ))
  {
    IoDeleteDevice (DriverObject->DeviceObject);
    return status;
  }  
 
  pModuleAddress = MyGetModuleBaseAddress("ntoskrnl.exe");
  if ( pModuleAddress == NULL)
  {
    DbgPrint(" MyGetModuleBaseAddress()\n");
    return 0;
  }

  OldZwCreateFile = (ZWCREATEFILE)HookFunction( pModuleAddress, "ZwCreateFile",(ZWCREATEFILE)HookNtCreateFile);
  if ( OldZwCreateFile == NULL)
  {
    DbgPrint(" HOOK FAILED\n");
    return 0;
  }

  DbgPrint("HOOK SUCCEED\n");

  for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)   {

      DriverObject->MajorFunction = MydrvDispatch;
  }

    DriverObject->DriverUnload = DriverUnload;
 
return STATUS_SUCCESS;
}



//處理設備物件操作

static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  Irp->IoStatus.Status = STATUS_SUCCESS;
  Irp->IoStatus.Information = 0L;
  IoCompleteRequest( Irp, 0 );
  return Irp->IoStatus.Status;
 
}



VOID DriverUnload (IN PDRIVER_OBJECT   pDriverObject)
{
  UNICODE_STRING nameString;
  PCHAR         pModuleAddress;

  pModuleAddress = MyGetModuleBaseAddress("ntoskrnl.exe");
  if ( pModuleAddress == NULL)
  {
    DbgPrint("MyGetModuleBaseAddress()\n");
    return ;
  }

  OldZwCreateFile = (ZWCREATEFILE)HookFunction( pModuleAddress, "ZwCreateFile",(ZWCREATEFILE)OldZwCreateFile);
  if ( OldZwCreateFile == NULL)
  {
    DbgPrint(" UNHOOK FAILED!\n");
    return ;
  }

  DbgPrint("UNHOOK SUCCEED\n");

  RtlInitUnicodeString( &nameString, L"\\DosDevices\\WssHookPE" );  
  IoDeleteSymbolicLink(&nameString);
  IoDeleteDevice(pDriverObject->DeviceObject);

  return;
}

NTSTATUS
HookNtCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
)
{
  NTSTATUS   status;

  DbgPrint("Hook ZwCreateFile()\n");

  status = ((ZWCREATEFILE)(OldZwCreateFile))(
          FileHandle,
          DesiredAccess,
          ObjectAttributes,
          IoStatusBlock,
          AllocationSize,
          FileAttributes,
          ShareAccess,
          CreateDisposition,
          CreateOptions,
          EaBuffer,
          EaLength
        );

  return status;
}


VOID DisableWriteProtect( PULONG pOldAttr)
{

  ULONG uAttr;

  _asm
  {
      push eax;
      mov eax, cr0;
      mov uAttr, eax;
      and eax, 0FFFEFFFFh; // CR0 16 BIT = 0
      mov cr0, eax;
      pop eax;
  };

  *pOldAttr = uAttr; //保存原有的 CRO 屬性

}

VOID EnableWriteProtect( ULONG uOldAttr )
{

_asm
{
    push eax;
    mov eax, uOldAttr; //恢復原有 CR0 屬性
    mov cr0, eax;
    pop eax;
};

}