首页 > 代码库 > [转载]一份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  }
View Code

 

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;
View Code

 

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  }
View Code


使用 LoadImage() 加载 ntkrnlpa.exe 模块后,可以在 windbg 调试验证结果:

技术分享

上图显示的 ImageBase 值为 0x87176000,这是 ntkrnlpa.exe 加载的基址。我们随便找一个 NT 模块的函数。例如:nt!HalAllocateCommonBuffer 函数,它使用了导入的 HAL 模块 HalAllocateCommonBuffer() 函数。

技术分享

下面观察我们加载的 ntkrnlpa.exe 模块情况如何:它加载的基址在 0x87176000 里。

技术分享

这个反汇编结果显示,函数是一样的。说明我们模块的基址重定位正确!下面我们参观一下导入函数绑定是否正确。图中从 0x871770e8 处读取 HalAllocateCommonBuffer 函数地址。

技术分享

图中说明我们的导入符号绑定是正确的!注意:这个测试在 windows 2003 下进行,没有测试其它平台。WIN 64 平台下没有测试。

[转载]一份LoadImage()函数代码,可以实现基本的重载内核