HookImportsOfImage函數解析
以下是_IMAGE_DOS_HEADER的結構,是在winnt.h標頭檔下的,指的是DOS.exe檔案的頭
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;
PIMAGE_NT_HEADERS結構如下所示,作為PE檔案的頭,包含了三部分內容。
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
PIMAGE_IMPORT_DESCRIPTOR之前介紹過,在winnt.h標頭檔中的定義如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
};
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
PIMAGE_IMPORT_BY_NAME的結構如下所示:
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
以上四個定義的結構已經完成,注意變數pc_dlltar要hook的模組名稱,pc_fnctar為要hook的目標函數。
PMDL的結構如下:
// MDL references defined in ntddk.h
typedef struct _MDL {
struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPROCESS *Process;
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;
// MDL Flags
MDL(Memory Descriptor List)記憶體描述表,用來描述一塊記憶體地區,其中包含了該記憶體地區的起始地址、擁有者進程、位元組資料以及標誌。
具體函數的執行如下,每個關鍵語句都有中文注釋
NTSTATUS HookImportsOfImage ( PIMAGE_DOS_HEADER image_addr, HANDLE h_proc) { PIMAGE_DOS_HEADER dosHeader; PIMAGE_NT_HEADERS pNTHeader; PIMAGE_IMPORT_DESCRIPTOR importDesc; PIMAGE_IMPORT_BY_NAME p_ibn; DWORD importsStartRVA; PWORD pd_IAT, pd_INTO; int count, index; char *dll_name = NULL; char *pc_dlltar = "kernel32.dll"; char *pc_fnctar = "GetProcAddress"; PMDL p_mdl; PDWORD MappedImTable; // 匯入的是IMAGE_INFO結構中的 PVOID ImageBase變數 dosHeader = (PIMAGE_DOS_HEADER) image_addr; // 宏,對指標的操作 pNTHeader = MakePtr ( PIMAGE_NT_HEADERS, dosHeader, dosHeader->e_lfanew ); // 通過PE檔案的Signature欄位來判斷該檔案是否是標準的PE檔案 if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE) return STATUS_INVALID_IMAGE_FORMAT; // 匯入段的RVA importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; if ( !importsStartRVA ) return STATUS_INVALID_IMAGE_FORMAT; // 匯入段的RVA與模組在記憶體中的起始地址(dosHeader)相加,得到 // 指向第一個IMAGE_IMPORT_DESCRIPTOR的指標 importDesc = ( PIMAGE_IMPORT_DESCRIPTOR ) (importsStartRVA + (DWORD)dosHeader); // 過濾每個IMAGE_IMPORT_DESCRIPTOR for(count = 0; importDesc[count].Characteristics != 0; count++) { // 得到匯入模組的dll的名稱 dll_name = (char*)(importDesc[count].Name + (DWORD)dosHeader); // 得到IAT pd_IAT = (PDWORD)(((DWORD)dosHeader) + (DWORD)importDesc[count].FirstThunk); // 得到指向IMAGE_IMPORT_BY_NAME結構的指標數組 pd_INTO = (PDWORD)(((DWORD)dosHeader) + (DWORD)importDesc[count].OriginalFirstThunk); // IAT中的過濾,找到特定的dll與要hook的函數 for ( index = 0; pd_IAT[index] != 0; index++) { // if this is an import by ordinal // the high bit is set if((pd_INTO[index] & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG) { // 得到函數名結構 p_ibn = (PIMAGE_IMPORT_BY_NAME)(pd_INTO[index] + ((DWORD)dosHeader)); // 對比dll名與函數名,找到所需要的 if((_stricmp(dll_name, pc_dlltar) == 0) && (strcmp(p_ibn->Name, pc_fnctar) ==0)) { // Use the trick you already learned to map a different // virtual address to the same physical page so no permission problems // // Map the memory into our domain so we can change the // permissions on the MDL // 改變記憶體屬性,以便修改IAT屬性 // MDL方法修改記憶體屬性,以後的文章中會詳細介紹 p_mdl = MmCreateMdl(NULL, &pd_IAT[index], 4); if(!p_mdl) return STATUS_UNSUCCESSFUL; MmBuildMdlForNonPagedPool(p_mdl); // Change the flags of MDL p_mdl->MdlFlags = p_mdl->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA; MappedImTable = MmMapLocakedPages(p_mdl, KernelMode); // Address of the "new function" // 將“GetProcAddress”指向自己定義的函數 *MappedImTable = d_shareM; // Free MDL MmUnmapLoackedPages(MappedImTable, p_mdl); IoFreeMdl(p_mdl); } } } return STATUS_SUCCESS; } |