读书笔记_windows下的混合钩子(HOOK)_part 2
1. 分析PE文件
下面接着看如果分析PE文件,从而找到其要导入的DLL。
首先看PE文件的导入数据.idata段。.idata段是导入数据,包括导入库和导入地址名称表。在WINNT.H中所定义的数据目录为:
// 目录入口
// 导出目录
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
// 导入目录
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
// 资源目录
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
// 异常目录
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
// 安全目录
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
// 重定位基本表
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
// 调试目录
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
// 描述字串
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
// 机器值(MIPS GP)
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
// TLS目录
#define IMAGE_DIRECTORY_ENTRY_TLS 9
// 载入配置目录
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
在分析PE时,首先要得到导入段(即DataDirectory的IMAGE_DIRECTROY_ENTRY_IMPORT)的RVA。将RVA与模块在内存中的起始地址相加(基地址),就会得到虚拟地址,这个地址也就是指向IMAGE_IMPORT_DESCRIPTOR的指针。
关于IMAGE_IMPORT_DESCRIPTOR的描述如下:
IMAGE_IMPORT_DESCRIPTOR是在导入段中(Imports Section)中,DataDirectory 入口的导入点都是指向IMAGE_IMPORT_DESCRIPTOR结构的,具体的结构如下表所示:
Size | Member | Description |
DWORD | OriginalFirstThunk | This field is badly named. It contains the RVA of the Import Name Table (INT). This is an array of IMAGE_THUNK_DATA structures. This field is set to 0 to indicate the end of the array of IMAGE_IMPORT_DESCRIPTORs. |
DWORD | TimeDateStamp | This is 0 if this executable is not bound against the imported DLL. When binding in the old style (see the section on Binding), this field contains the time/date stamp (number of seconds since 1/1/1970 GMT) when the binding occurred. When binding in the new style, this field is set to -1. |
DWORD | ForwarderChain | This is the Index of the first forwarded API. Set to -1 if no forwarders. Only used for old-style binding, which could not handle forwarded APIs efficiently. |
DWORD | Name | The RVA of the ASCII string with the name of the imported DLL. |
DWORD | FirstThunk | Contains the RVA of the Import Address Table (IAT). This is array of IMAGE_THUNK_DATA structures. |
每个导入执行块都有一个IMAGE_IMPORT_DESCRIPTOR结构。
通过得到第一个IMAGE_IMPORT_DESCRIPTOR结构后,由于所有的DLL都具有相应的IMAGE_IMPORT_DESCRIPTOR结构,当发现Characteristics域为0的结构时,就已经达到该模块所导入的最后一个DLL。
每个IMAGE_IMPORT_DESCRIPTOR结构中包含指向两个独立数组的指针。其中一个是该模块从给定的DLL中导入的所有函数的地址数据指针,通过IMAGE_IMPORT_DESCRIPTOR的FirstThunk成员到达该地址表,IMAGE_IMPORT_DESCRIPTOR中的OriginalFirstThunk用于寻找指向IMAGE_IMPORT_BY_NAME结构的指针数组,这些结构包含了被导入函数的名称,除非函数是根据序号导入的。
2. HookImportsOfImage函数
来看HookImportsOfImage函数,它的功能是扫描所有模块,确定它们是否从Kernel32.dll导入了GetProcAddress函数,如果发现了这个IAT,可以首先修改IAT的内存保护机制,改变权限后就使用钩子重写IAT中的地址。来具体看HookImportsOfImage函数,
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; dosHeader = (PIMAGE_DOS_HEADER) image_addr;
pNTHeader = MakePtr ( PIMAGE_NT_HEADERS, dosHeader, dosHeader->e_lfanew );
// First, verify that the e_lfanew field gave us a reasonable // pointer, then verify the PE signature. if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE) return STATUS_INVALID_IMAGE_FORMAT; importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if ( !importsStartRVA ) return STATUS_INVALID_IMAGE_FORMAT;
importDesc = ( PIMAGE_IMPORT_DESCRIPTOR ) (importsStartRVA + (DWORD)dosHeader);
for(count = 0; importDesc[count].Characteristics != 0; count++) { dll_name = (char*)(importDesc[count].Name + (DWORD)dosHeader); pd_IAT = (PDWORD)(((DWORD)dosHeader) + (DWORD)importDesc[count].FirstThunk); pd_INTO = (PDWORD)(((DWORD)dosHeader) + (DWORD)importDesc[count].OriginalFirstThunk); for ( index = 0; pd_IAT[index] != 0; index++) { // if this is an import by ordinal // the high bit is set if((pd_INT[index] & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG) { p_ibn = (PIMAGE_IMPORT_BY_NAME)(pd_INTO[index] + ((DWORD)dosHeader)); 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 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" *MappedImTable = d_shareM; // Free MDL MmUnmapLoackedPages(MappedImTable, p_mdl); IoFreeMdl(p_mdl);
} } } return STATUS_SUCCESS;
} |
HookImportsOfImage是一个回调函数,每当将一个映像(进程,设备驱动程序,DLL等)加载到内存中是,都会调用它。代码已经搜索了每个映像,检查它是否导入了钩子的目标函数,如果找到该目标函数,则替换它在IAT中的地址。