[转载]一份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 header
108          //
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          // 根据映像文件大小分配 pool
132          //
133          ImageBase = ExAllocatePoolWithTag(
134                                  NonPagedPool,                           // non-paged
135                                  NtHeader.OptionalHeader.SizeOfImage,    // pool size = SizeOfImage
136                                  ‘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 table
146          //
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 header
154          //
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          // 下面加载映像的每个 section
177          //
178          for (Index = 0; Index < NumberOfSections; Index++)
179          {
180                 //
181                  // 如果 SizeOfRawData = 0 时,跳过 section
182                  //
183                  if (SectionHeader[Index].SizeOfRawData == 0)
184                  {
185                          continue;
186                  }
187
188                 //
189                  // 从文件的 PointerToRawData 偏移量里读取内容到 ImageBase + RAV 地址
190                 // size 为 SizeOfRawData
191                  //
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 = *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 buffer
290
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_TABLE
11  {
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_TABLE
22  {
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 ImportMaintainTable
101          )
102 /*++
103         ProcessImportMaintainTable():
104                 根据导入维护表处理 image 导入符号的重定位
105         input:
106                 ImportMainTainTable - image 导入维护表
107         output:
108                 成功时返回 TURE,否则返回 FALSE
109 --*/
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 table
127                 //      2)import descriptor 的 OrignalFirstThunk 指向 import lookup table
128                 //      3)import descriptor 的 FirstThunk 指向 import address table
129                 //      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 SymbolName
172          )
173 /*++
174         GetAddressOfExportSymbol():
175                 从引用模块里找到符号的导出地址值
176         input:
177                 ModuleBase - 模块的基址
178                 SymbolName - 符号名
179         output:
180                 成功时返回符号地址,否则返回 NULL
181 --*/
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,             // Begin
227                          ExportNamePointerTable + (NumberOfNames - 1),           // End
228                          NumberOfNames,                                          // Count
229                          SymbolName,                                             // SourceString
230                          ModuleBase);                                            // Base of module
231
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 base
253          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 ImageBase
267          )
268 /*++
269         SerachBinaryForSymbol():
270                 在一个区域内使用二分查找匹配的符号名,返回匹配的元素值。
271                 Begin 与 End 指定一个查找区间。
272         input:
273                 Begin - 查找数组的起始点
274                 End - 查找数组的结束点
275                 Count - 元素个数
276                 SourceString - 源串
277         output:
278                 成功时返回匹配元素地址,失败时返回 NULL
279 --*/
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         else
304              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 平台下没有测试。

时间: 2024-10-11 09:39:56

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

【转载】C++ inline 函数

(一)inline函数(摘自C++ Primer的第三版) 在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联. inline int min(int first, int secend) {/****/}; inline 函数对编译器而言必须是可见的,以便它能够在调用点内展开该函数.与非inline函数不同的是,inline函数必须在调用该函数的每个文本文件中定义.当然,对于同一程序的不同文件,如果inline函数出现的话,其定义必须相同.对于由两个文件comput

SQL简体繁体转换函数代码

--生成码表 if exists (select * from dbo.sysobjects where id = object_id(N'[codetable]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [codetable] GOdeclare @j varchar(8000),@f varchar(8000) select @j=' 啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊澳芭捌扒叭吧笆八

jQuery动态添加的元素绑定事件处理函数代码

jQuery动态添加的元素绑定事件处理函数代码 作者: 字体:[增加 减小] 类型:转载 有一段时间没用jquery了,今天又碰到这个问题.当时是知道有livejquery可以解决.但是我并不喜欢为了这个而另外加载一个. 我当时的处理方法是在添加的时候手工绑定事件处理函数.不过新版的jquery已经添加了这个功能.我们已经不需要为此烦恼了. 参考:http://api.jquery.com/live/ 以前我们定义事件,比如为元素定义单击事件是这样写的: 复制代码 代码如下: $('input'

C#实现为类和函数代码自动添加版权注释信息的方法

本文实例讲述了C#实现为类和函数代码自动添加版权注释信息的方法,分享给大家供大家参考之用.具体方法如下: 以web项目为例: 一:给类加注释 1.在visual studio 的安装路径下 如:[盘符]:/Program files/Microsoft Visual Studio 8/Common7/IDE/ItemTemplates/web/cshare/2052/class.zip ,将里面的class.cs改为: /*------------------------------------

php 安全过滤函数代码

php 安全过滤函数代码,防止用户恶意输入内容. //安全过滤输入[jb] function check_str($string, $isurl = false) { $string = preg_replace('/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/','',$string); $string = str_replace(array("\0","%00","\r"),'',$string); empty($i

CMD操作函数代码

一.命名空间 using System.Diagnostics; 二.函数代码 /// <summary> /// CMD运行应用程序 /// </summary> /// <param name="appName">应用程序名</param> /// <param name="arg">参数</param> private void CmdProcess(string appName, str

jQuery的toggleClass()函数代码实例

jQuery的toggleClass()函数代码实例:本章节通过代码实例演示一下toggleClass()函数的用法,此函数可以判断一个元素是否具有指定的样式类,如果有这删除,如果没有则添加,这种功能在一些切换效果中非常有用,代码实例如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" content="

c++截取屏幕图片并保存(函数代码实现)

<strong> //获取桌面窗体的CDC CDC *pdeskdc = GetDesktopWindow()->GetDC(); CRect re; //获取窗体的大小 GetDesktopWindow()->GetClientRect(&re); CBitmap bmp; bmp.CreateCompatibleBitmap(pdeskdc , re.Width() , re.Height()); //创建一个兼容的内存画板 CDC memorydc; memorydc

LoadImage函数问题

loadimage函数加载图片类型 Value Meaning IMAGE_BITMAP Loads a bitmap. IMAGE_CURSOR Loads a cursor. IMAGE_ICON Loads an icon. IMAGE_BITMAP时只能是bmp格式文件,而且一定要是原生的,从其他格式的图片改过来的,LoadImage函数无法获取HANDLE , 且GetLastError会返回0,无法知道真实错误原因 详细分析请参见:http://www.cnblogs.com/hom