首页 > 代码库 > [转载]一份LoadImage()函数代码,可以实现基本的重载内核
[转载]一份LoadImage()函数代码,可以实现基本的重载内核
这里实现一个 LoadImage() 函数,用来加载目标 image 文件。如下面的用法:
1 ... ... 2 3 4 RtlInitUnicodeString(&ImageFile, L"\\??\\C:\\windows\\system32\\ntkrnlpa.exe"); // 目标文件为 ntkrnlpa.exe 5 LoadImage(&ImageFile, &ImageBase); // 加载目标 image, ImageBase 返回 image 基址 6 7 8 ... ... 9 10 11 ExFreePoolWithTag(ImageBase, ‘fubi‘); // 最后释放 Image 占用内存
LoadImage() 内部实现了基址重定位以及导入符号绑定操作。
1 BOOLEAN LoadImage( 2 IN PUNICODE_STRING ImageFile, 3 OUT PVOID *OutImageBase 4 ) 5 /*++ 6 LoadImage(): 7 加载目标映像文件到目标 buffer 中。 8 input: 9 ImageFile - 提供目标映像文件名。 10 ImageBase - 提供输出的目标 buffer,这个 buffer 必须能装下映像文件。 11 函数内部分配 buffer,并返回 buffer 给 caller。当失败时返回 NULL。 12 ImageBase 使用 ExAllocatePoolWithTag() 分配,caller 需要使用 13 ExFreePoolWithTag() 来释放,Tag = ‘fubi‘。 14 output: 15 TRUE - 成功, 否则返回 FALSE 指示失败,OutImageBase 返回 NULL。 16 --*/ 17 { 18 NTSTATUS Status; 19 OBJECT_ATTRIBUTES ObjAttributes; 20 HANDLE FileHandle; 21 IO_STATUS_BLOCK IoStatusBlock; 22 LARGE_INTEGER Offset; 23 24 IMAGE_DOS_HEADER DosHeader; // DOS header 25 IMAGE_NT_HEADERS NtHeader; // NT header 26 PIMAGE_SECTION_HEADER SectionHeader; // section header 27 PIMAGE_BASE_RELOCATION BaseRelocation; // base relocation 28 PIMAGE_BASE_RELOCATION BaseRelocationTop; // base relocation 区域顶部 29 PBASE_RELOCATION_ENTRY BaseRelocEntry; // base relocation entry 30 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor; // image import descriptor 区域 31 PIMPORT_LOOKUP_TABLE_ENTRY LookupTableEntry; // 符号查找表项 32 33 IMAGE_IMPORT_MAINTAIN_TABLE ImportMaintainTable = { 0, NULL, { 0 } }; 34 35 36 ULONG BaseRelocationSize; // 重定位区域 size 37 PULONG_PTR RelocationAddress; // 需要重定位的地址 38 ULONG_PTR RelocationValue; // 需要重定位的值 39 PULONG_PTR BoundSymbolAddress; // 需要绑定符号的地址 40 PCHAR Symbol; // 通用符号名地址 41 42 PVOID OriginalImageBase = NULL; 43 PVOID ImageBase = NULL; 44 PVOID ImageSection = NULL; 45 ULONG NumberOfSections; 46 ULONG SectionOffset; 47 ULONG Index = 0; 48 BOOLEAN Result = TRUE; 49 50 *OutImageBase = NULL; // 初始设置 OutImageBase 51 52 InitializeObjectAttributes( 53 &ObjAttributes, 54 ImageFile, 55 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 56 NULL, 57 NULL); 58 59 // 60 // 打开映像文件 61 // 62 Status = ZwCreateFile( 63 &FileHandle, 64 FILE_ALL_ACCESS, 65 &ObjAttributes, 66 &IoStatusBlock, 67 NULL, 68 FILE_ATTRIBUTE_NORMAL, 69 FILE_SHARE_READ, 70 FILE_OPEN, 71 FILE_NON_DIRECTORY_FILE, 72 NULL, 73 0); 74 75 if (NT_SUCCESS(Status) == FALSE) 76 { 77 KdPrint(("failure to open image file")); 78 return FALSE; 79 } 80 81 // 82 // 读取文件内容: 83 // 1. DOS header 84 // 2. NT header 85 // 3. Section header 86 // 87 Offset.QuadPart = 0; 88 Status = ZwReadFile( 89 FileHandle, 90 NULL, 91 NULL, 92 NULL, 93 &IoStatusBlock, 94 &DosHeader, // DOS 头部 95 sizeof(IMAGE_DOS_HEADER), 96 &Offset, 97 NULL); 98 99 if (NT_SUCCESS(Status) == FALSE)100 {101 KdPrint(("failure to read image file"));102 ZwClose(FileHandle);103 return FALSE;104 }105 106 //107 // 读取 NT header108 //109 Offset.QuadPart = DosHeader.e_lfanew;110 Status = ZwReadFile(111 FileHandle,112 NULL,113 NULL,114 NULL,115 &IoStatusBlock,116 &NtHeader, // NT 头部117 sizeof(IMAGE_NT_HEADERS),118 &Offset,119 NULL); 120 121 if (NT_SUCCESS(Status) == FALSE)122 {123 KdPrint(("failure to read image file"));124 ZwClose(FileHandle);125 return FALSE;126 }127 128 129 130 //131 // 根据映像文件大小分配 pool132 //133 ImageBase = ExAllocatePoolWithTag(134 NonPagedPool, // non-paged135 NtHeader.OptionalHeader.SizeOfImage, // pool size = SizeOfImage136 ‘fubi‘); // ‘ibuf‘137 if (ImageBase == NULL)138 {139 KdPrint(("failure to allocate image pool"));140 ZwClose(FileHandle);141 return FALSE;142 }143 144 //145 // 定位 section table146 //147 NumberOfSections = NtHeader.FileHeader.NumberOfSections;148 SectionOffset = DosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS);149 SectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)ImageBase + SectionOffset);150 151 152 //153 // 读取 section header154 //155 Offset.QuadPart = SectionOffset;156 Status = ZwReadFile(157 FileHandle,158 NULL,159 NULL,160 NULL,161 &IoStatusBlock,162 SectionHeader,163 NumberOfSections * sizeof(IMAGE_SECTION_HEADER),164 &Offset,165 NULL); 166 167 if (NT_SUCCESS(Status) == FALSE)168 {169 KdPrint(("failure to read image file"));170 ZwClose(FileHandle);171 return FALSE;172 }173 174 175 //176 // 下面加载映像的每个 section177 //178 for (Index = 0; Index < NumberOfSections; Index++)179 {180 //181 // 如果 SizeOfRawData = http://www.mamicode.com/0 时,跳过 section>182 //183 if (SectionHeader[Index].SizeOfRawData =http://www.mamicode.com/= 0)184 {185 continue;186 }187 188 //189 // 从文件的 PointerToRawData 偏移量里读取内容到 ImageBase + RAV 地址190 // size 为 SizeOfRawData191 //192 Offset.QuadPart = SectionHeader[Index].PointerToRawData;193 ImageSection = (PVOID)((ULONG_PTR)ImageBase + SectionHeader[Index].VirtualAddress);194 Status = ZwReadFile(195 FileHandle,196 NULL,197 NULL,198 NULL,199 &IoStatusBlock,200 ImageSection,201 SectionHeader[Index].SizeOfRawData,202 &Offset,203 NULL); 204 205 if (NT_SUCCESS(Status) == FALSE)206 {207 KdPrint(("failure to read image file"));208 ZwClose(FileHandle);209 return FALSE;210 }211 }212 213 214 //215 // 复制 DOS header 和 NT header 到 Image 区域216 //217 RtlCopyMemory(ImageBase, &DosHeader, sizeof(DosHeader));218 RtlCopyMemory((PVOID)((ULONG_PTR)ImageBase + (ULONG_PTR)DosHeader.e_lfanew), &NtHeader, sizeof(NtHeader));219 220 221 //222 // 下面进行 base 重定位223 //224 225 //226 // 找到 IMAGE_BASE_RELOCATION 表区域227 //228 BaseRelocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase + 229 (ULONG_PTR)NtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); 230 BaseRelocationSize = NtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;231 BaseRelocationTop = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseRelocation + BaseRelocationSize - 1);232 OriginalImageBase = NtHeader.OptionalHeader.ImageBase; // 映像文件的原始 ImageBase 值233 234 while (BaseRelocation < BaseRelocationTop)235 {236 //237 // 对每一个 IMAGE_BASE_RELOCATION 表区域内的 entry 进行调整238 // 239 for (BaseRelocEntry = (PBASE_RELOCATION_ENTRY)((ULONG_PTR)BaseRelocation + 8);240 (ULONG_PTR)BaseRelocEntry < ((ULONG_PTR)BaseRelocation + BaseRelocation->SizeOfBlock);241 BaseRelocEntry++)242 { 243 switch (BaseRelocEntry->Type)244 {245 case IMAGE_REL_BASED_HIGHLOW:246 //247 // 找到需要进行重定位的地址248 //249 RelocationAddress = (PULONG_PTR)((ULONG_PTR)ImageBase 250 + BaseRelocation->VirtualAddress 251 + (ULONG_PTR)BaseRelocEntry->Offset);252 //253 // 读取原值进行调整254 //255 RelocationValue = http://www.mamicode.com/*RelocationAddress;256 *RelocationAddress = RelocationValue - (ULONG_PTR)OriginalImageBase + (ULONG_PTR)ImageBase;257 break;258 case IMAGE_REL_BASED_DIR64:259 break;260 }261 }262 263 //264 // 指向下一个 IMAGE_BASE_RELOCATION 表区域265 //266 BaseRelocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseRelocation + BaseRelocation->SizeOfBlock);267 }268 269 //270 // 下面进行导入符号的绑定271 //272 273 ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)ImageBase +274 (ULONG_PTR)NtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);275 276 //277 // 得到 image 的所有导入模块及基址278 //279 Result = InitImportMaintainTable(&ImportMaintainTable, ImageBase, ImportDescriptor);280 281 //282 // 进行导入符号绑定处理283 //284 if (Result == TRUE)285 Result = ProcessImportMaintainTable(&ImportMaintainTable);286 287 288 ZwClose(FileHandle); 289 *OutImageBase = ImageBase; // 返回 image buffer290 291 return TRUE;292 }
InitImportMaintainTable() 函数用来生成初始化的 IMAGE_IMPORT_MAINTAIN_TABLE 结构,它的定义如下:
1 // 2 // 预定义 image 导入模块的最大数量 3 // 4 #define IMPORT_MODULE_MAX_NUMBER 100 5 6 7 // 8 // image 所引用的模块表 9 //10 typedef struct _IMAGE_REFERENCE_MODULE_TABLE11 {12 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor; // 导入模块信息13 PVOID ModuleBase; // 模块基址14 } IMAGE_REFERENCE_MODULE_TABLE, *PIMAGE_REFERENCE_MODULE_TABLE;15 16 17 18 //19 // image 导入维护表20 //21 typedef struct _IMAGE_IMPORT_MAINTAIN_TABLE22 {23 ULONG NumberOfReferenceModules; // 引用模块的数量24 PVOID ImageBase; // image 模块的基址25 26 //27 // 预定义导入 IMPORT_MODULE_MAX_NUMBER 模块28 //29 IMAGE_REFERENCE_MODULE_TABLE Entry[IMPORT_MODULE_MAX_NUMBER];30 31 } IMAGE_IMPORT_MAINTAIN_TABLE, *PIMAGE_IMPORT_MAINTAIN_TABLE;
IMAGE_IMPORT_MAINTAIN_TABLE 结构用来处理 image 导入符号的绑定,这项工作由 ProcessImportMaintainTable() 函数负责。ProcessImportMaintainTable() 调用 GetAddressOfExportSymbol() 函数得到符号在引用模块的导出地址值。
下面是 InitImportMaintainTable(),ProcessImportMaintainTable(),GetAddressOfExportSymbol() 以及 SerachBinaryForSymbol() 函数的实现:
1 BOOLEAN InitImportMaintainTable( 2 INOUT PIMAGE_IMPORT_MAINTAIN_TABLE ImportMaintainTable, 3 IN PVOID ImageBase, 4 IN PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor 5 ) 6 /*++ 7 InitImportMaintainTable(): 8 根据提供的 image 导入描述符表初始化 ImportMaintainTable。 9 input: 10 ImportMaintainTable - image 导入维护表。 11 ImageBase - image 基址 12 ImportDescriptor - image 的导入描述符表。 13 output: 14 成功返回 TRUE,否则返回 FALSE。 15 --*/ 16 { 17 PIMAGE_REFERENCE_MODULE_TABLE ReferenceModuleTable = ImportMaintainTable->Entry; 18 ULONG NumberOfModules = 0; 19 NTSTATUS Status; 20 ULONG RequiredLength; 21 PLIST_ENTRY Next; 22 PRTL_PROCESS_MODULE_INFORMATION ModuleInfo; 23 PKLDR_DATA_TABLE_ENTRY LdrDataTableEntry; 24 ANSI_STRING AnsiString; 25 26 PCHAR ModuleName; 27 ULONG Index; 28 ULONG Result = 0; 29 30 31 while(!IS_NULL_IMAGE_IMPORT_DESCRIPTOR(*ImportDescriptor)) 32 { 33 // 34 // 查找对应的导入文件模块 35 // 36 ReferenceModuleTable->ImportDescriptor = ImportDescriptor; 37 ImportDescriptor++; 38 ReferenceModuleTable++; 39 NumberOfModules++; 40 } 41 42 ImportMaintainTable->NumberOfReferenceModules = NumberOfModules; // image 引用的模块数 43 ImportMaintainTable->ImageBase = ImageBase; // image 基址 44 45 46 // 47 // 遍历 PsLoadedModuleList 列表,从已加载的模块里找到 image 所引用模块的基址 48 // 注意: 49 // 这里保留未实现未加载的模块! 50 // 51 for (Next = PsLoadedModuleListPointer->Flink; 52 Next != PsLoadedModuleListPointer; Next = Next->Flink) 53 { 54 LdrDataTableEntry = CONTAINING_RECORD(Next, 55 KLDR_DATA_TABLE_ENTRY, 56 InLoadOrderLinks 57 ); 58 // 59 // 匹配 image 导入模块名字 60 // 61 for (Index = 0; Index < ImportMaintainTable->NumberOfReferenceModules; Index++) 62 { 63 // 64 // 检查模块名,相等则找到引用模块的 Base 值 65 // 66 ModuleName = (PCHAR)((ULONG_PTR)ImageBase + 67 ImportMaintainTable->Entry[Index].ImportDescriptor->Name); 68 69 // 70 // 说明: 71 // 1)加载模块链中的模块名字是 UNICODE 字符串,而 image 导入模块名字是 ANSI 字符串。 72 // 2)需要在 UNICODE 串与 ANSI 串之间进行对比。模块名字是忽略大小写的,因此,使用 TRUE 参数。 73 // 74 if (CompareAnsiCharToUnicodeChar( 75 LdrDataTableEntry->BaseDllName.Buffer, // 需要对比的模块名 76 ModuleName, // image 所引用的模块名 77 TRUE) == 0) 78 { 79 ImportMaintainTable->Entry[Index].ModuleBase = LdrDataTableEntry->DllBase; 80 Result++; 81 } 82 } 83 } 84 85 if (Result == ImportMaintainTable->NumberOfReferenceModules) 86 return TRUE; 87 88 return FALSE; 89 } 90 91 92 93 94 95 96 97 98 99 BOOLEAN ProcessImportMaintainTable(100 IN PIMAGE_IMPORT_MAINTAIN_TABLE ImportMaintainTable101 )102 /*++103 ProcessImportMaintainTable():104 根据导入维护表处理 image 导入符号的重定位105 input:106 ImportMainTainTable - image 导入维护表107 output:108 成功时返回 TURE,否则返回 FALSE109 --*/110 {111 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;112 ULONG NumberOfReferenceModules = ImportMaintainTable->NumberOfReferenceModules;113 PVOID ImageBase = ImportMaintainTable->ImageBase;114 ULONG ModuleIndex, Index;115 PCHAR SymbolName; 116 PVOID SymbolAddress; 117 118 PIMPORT_LOOKUP_TABLE_ENTRY ImportLookupTable;119 PIMPORT_ADDRESS_TABLE_ENTRY ImportAddressTable;120 PIMAGE_IMPORT_BY_NAME HitNameTable;121 122 for (ModuleIndex = 0; ModuleIndex < NumberOfReferenceModules; ModuleIndex++)123 {124 //125 // 说明:126 // 1)从 import descriptor 里得到 import lookup table 与 import address table127 // 2)import descriptor 的 OrignalFirstThunk 指向 import lookup table128 // 3)import descriptor 的 FirstThunk 指向 import address table129 // 4)用 import lookup table entry 指向的 IMPORT_BY_NAME table entry 在引用模块里查找符号地址。130 //131 ImportDescriptor = ImportMaintainTable->Entry[ModuleIndex].ImportDescriptor;132 ImportLookupTable = (PIMPORT_LOOKUP_TABLE_ENTRY)133 ((ULONG_PTR)ImageBase + ImportDescriptor->OriginalFirstThunk);134 ImportAddressTable = (PIMPORT_ADDRESS_TABLE_ENTRY)135 ((ULONG_PTR)ImageBase + ImportDescriptor->FirstThunk);136 137 for (Index = 0; ImportLookupTable[Index].NameTable != 0; Index++)138 {139 HitNameTable = (PIMAGE_IMPORT_BY_NAME)140 ((ULONG_PTR)ImageBase + ImportLookupTable[Index].NameTable);141 SymbolName = (PCHAR)HitNameTable->Name;142 143 //144 // 在引用的模块里查找符号的地址,SymbolAddress 返回地址值145 // SymbolAddress = NULL 时,表明查找失败。146 //147 SymbolAddress = GetAddressOfExportSymbol(148 ImportMaintainTable->Entry[ModuleIndex].ModuleBase,149 SymbolName);150 151 if (SymbolAddress == NULL)152 {153 return FALSE;154 }155 156 //157 // 将符号的真实地址绑定到 import address table entry 里。158 //159 ImportAddressTable[Index].AddressOfSymbol = (ULONG_PTR)SymbolAddress;160 }161 }162 163 return TRUE;164 }165 166 167 168 169 PVOID GetAddressOfExportSymbol(170 IN PVOID ModuleBase,171 IN PCHAR SymbolName172 )173 /*++174 GetAddressOfExportSymbol():175 从引用模块里找到符号的导出地址值176 input:177 ModuleBase - 模块的基址178 SymbolName - 符号名179 output:180 成功时返回符号地址,否则返回 NULL181 --*/182 {183 PIMAGE_NT_HEADERS NtHeader; // 模块 NT 头184 PIMAGE_EXPORT_DIRECTORY ExportDirectory; // 导出目录表 185 186 PULONG ExportAddressTable; // 导出地址表187 PULONG ExportNamePointerTable; // 导出名字指针表188 PUSHORT ExportNameOrdinalTable; // 导出名字序数表189 ULONG OrdinalBase; // 导出序数基值190 PCHAR ExportName; // 导出的符号名字191 192 ULONG NumberOfFunctions; // 导出地址表 entries 数量193 ULONG NumberOfNames; // 导出名字指针表 entries 数量194 195 196 PVOID SymbolAddress = NULL;197 PULONG NamePointer;198 LONG Index;199 200 NtHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)ModuleBase + 201 (ULONG_PTR)((PIMAGE_DOS_HEADER)ModuleBase)->e_lfanew); 202 ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)ModuleBase +203 (ULONG_PTR)(NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress));204 205 //206 // 获取导出表格207 //208 ExportAddressTable = (PULONG)((ULONG_PTR)ModuleBase +209 (ULONG_PTR)(ExportDirectory->AddressOfFunctions));210 ExportNamePointerTable = (PULONG)((ULONG_PTR)ModuleBase +211 (ULONG_PTR)(ExportDirectory->AddressOfNames));212 ExportNameOrdinalTable = (PUSHORT)((ULONG_PTR)ModuleBase +213 (ULONG_PTR)(ExportDirectory->AddressOfNameOrdinals)); 214 215 216 NumberOfFunctions = ExportDirectory->NumberOfFunctions;217 NumberOfNames = ExportDirectory->NumberOfNames;218 OrdinalBase = ExportDirectory->Base;219 220 //221 // 查找符号算法:222 // 1) 使用二分查找法,在 name pointer table 里找到匹配的符号名,得到一个 name index 值。223 // 2)将 name index 作为 name ordinal table 的 index 值,获得一个 oridnal 值。224 // 3)将 ordinal - OrdinalBase 作为 index 值,在 export address table 里找到最终的符号地址。225 //226 NamePointer = SerachBinaryForSymbol(ExportNamePointerTable, // Begin227 ExportNamePointerTable + (NumberOfNames - 1), // End228 NumberOfNames, // Count229 SymbolName, // SourceString230 ModuleBase); // Base of module231 232 if (NamePointer == NULL)233 return NULL;234 235 //236 // 说明:237 // 1) NamePointer 返回 Begin 到 End 间匹配元素的地址值,NamePointe - Begin 得到 index 值。238 // 2)用这个 index 值在 name ordinal table 里找到对应的 ordinal 值。239 // 3)用这个 ordinal 值在 export address table 里找到符号的真实地址。240 //241 // 注意:242 // 在 microsoft PE 文件档里,明确指出从 name pointer table 里得到的 index 值需要减去 Ordinal Base 值,243 // 但是经过测试,实际上并不需要减去 ordinal base 值。也就是:244 // Index = (ULONG)ExportNameOrdinalTable[Index] - OrdinalBase; // 这个是错误245 // 而是:Index = (ULONG)ExportNameOrdinalTable[Index]; // 不能减 ordinal base,否则计算错误。246 //247 248 Index = NamePointer - ExportNamePointerTable;249 //250 //Index = (ULONG)ExportNameOrdinalTable[Index] - OrdinalBase; // 减 ordinal base 值计算有误。251 //252 Index = (ULONG)ExportNameOrdinalTable[Index]; // 实际中不需要减 ordinal base253 SymbolAddress = (PVOID)((ULONG_PTR)ModuleBase + ExportAddressTable[Index]);254 255 return SymbolAddress;256 }257 258 259 260 261 PULONG SerachBinaryForSymbol(262 IN PULONG Begin,263 IN PULONG End,264 IN ULONG Count,265 IN PCHAR SourceString,266 IN PVOID ImageBase267 )268 /*++269 SerachBinaryForSymbol():270 在一个区域内使用二分查找匹配的符号名,返回匹配的元素值。271 Begin 与 End 指定一个查找区间。272 input:273 Begin - 查找数组的起始点274 End - 查找数组的结束点275 Count - 元素个数276 SourceString - 源串277 output:278 成功时返回匹配元素地址,失败时返回 NULL279 --*/280 {281 PCHAR DestString;282 ULONG Index = 0;283 LONG Result;284 285 if (Begin > End)286 return NULL;287 288 Index = Count / 2;289 DestString = (PCHAR)((ULONG_PTR)ImageBase + (ULONG_PTR)Begin[Index]);290 291 //292 // 说明:293 // 1)导入符号(源串)与导出符号(目标串)是 ANSI char 字符串。因此,需要进行 ANSI char 字符串对比。294 // 2)需要区分字符的大小写。因此,使用 FALSE 参数。295 //296 Result = CompareAnsiChar(DestString, SourceString, FALSE);297 298 if (Result == 0)299 return (Begin + Index); // 如果匹配,则返回元素地址值。300 301 if (Result < 0)302 Begin = Begin + (Index + 1); // 源串大于目标串,则在区间上半部分继续查找303 else304 End = Begin + (Index - 1); // 源串小于目标串,由在区间下半部分继续查找305 306 Count = Count - Index - 1;307 308 return SerachBinaryForSymbol(Begin, End, Count, SourceString, ImageBase);309 }
使用 LoadImage() 加载 ntkrnlpa.exe 模块后,可以在 windbg 调试验证结果:
上图显示的 ImageBase 值为 0x87176000,这是 ntkrnlpa.exe 加载的基址。我们随便找一个 NT 模块的函数。例如:nt!HalAllocateCommonBuffer 函数,它使用了导入的 HAL 模块 HalAllocateCommonBuffer() 函数。
下面观察我们加载的 ntkrnlpa.exe 模块情况如何:它加载的基址在 0x87176000 里。
这个反汇编结果显示,函数是一样的。说明我们模块的基址重定位正确!下面我们参观一下导入函数绑定是否正确。图中从 0x871770e8 处读取 HalAllocateCommonBuffer 函数地址。
图中说明我们的导入符号绑定是正确的!注意:这个测试在 windows 2003 下进行,没有测试其它平台。WIN 64 平台下没有测试。
[转载]一份LoadImage()函数代码,可以实现基本的重载内核