• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // This file implements PEImage, a generic class to manipulate PE files.
6 // This file was adapted from GreenBorder's Code.
7 
8 #include "base/pe_image.h"
9 
10 #ifdef _WIN64
11 #error This code is not tested on x64. Please make sure all the base unit tests\
12  pass before doing any real work. The current unit tests don't test the\
13  differences between 32- and 64-bits implementations. Bugs may slip through.\
14  You need to improve the coverage before continuing.
15 #endif
16 
17 // Structure to perform imports enumerations.
18 struct EnumAllImportsStorage {
19   PEImage::EnumImportsFunction callback;
20   PVOID cookie;
21 };
22 
23 // Callback used to enumerate imports. See EnumImportChunksFunction.
ProcessImportChunk(const PEImage & image,LPCSTR module,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie)24 bool ProcessImportChunk(const PEImage &image, LPCSTR module,
25                         PIMAGE_THUNK_DATA name_table,
26                         PIMAGE_THUNK_DATA iat, PVOID cookie) {
27   EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
28                                        cookie);
29 
30   return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
31                                   storage.cookie);
32 }
33 
34 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
35 bool ProcessDelayImportChunk(const PEImage &image,
36                              PImgDelayDescr delay_descriptor,
37                              LPCSTR module, PIMAGE_THUNK_DATA name_table,
38                              PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
39                              PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
40   EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
41                                        cookie);
42 
43   return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
44                                        module, name_table, iat, bound_iat,
45                                        unload_iat, storage.cookie);
46 }
47 
48 void PEImage::set_module(HMODULE module) {
49   module_ = module;
50 }
51 
52 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
53   return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
54 }
55 
56 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
57   PIMAGE_DOS_HEADER dos_header = GetDosHeader();
58 
59   return reinterpret_cast<PIMAGE_NT_HEADERS>(
60       reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
61 }
62 
63 PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
64   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
65   PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
66 
67   if (section < nt_headers->FileHeader.NumberOfSections)
68     return first_section + section;
69   else
70     return NULL;
71 }
72 
73 WORD PEImage::GetNumSections() const {
74   return GetNTHeaders()->FileHeader.NumberOfSections;
75 }
76 
77 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
78   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
79 
80   return nt_headers->OptionalHeader.DataDirectory[directory].Size;
81 }
82 
83 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
84   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
85 
86   return RVAToAddr(
87       nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
88 }
89 
90 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
91   PBYTE target = reinterpret_cast<PBYTE>(address);
92   PIMAGE_SECTION_HEADER section;
93 
94   for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
95     // Don't use the virtual RVAToAddr.
96     PBYTE start = reinterpret_cast<PBYTE>(
97                       PEImage::RVAToAddr(section->VirtualAddress));
98 
99     DWORD size = section->Misc.VirtualSize;
100 
101     if ((start <= target) && (start + size > target))
102       return section;
103   }
104 
105   return NULL;
106 }
107 
GetImageSectionHeaderByName(LPCSTR section_name) const108 PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
109     LPCSTR section_name) const {
110   if (NULL == section_name)
111     return NULL;
112 
113   PIMAGE_SECTION_HEADER ret = NULL;
114   int num_sections = GetNumSections();
115 
116   for (int i = 0; i < num_sections; i++) {
117     PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
118     if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
119                        sizeof(section->Name))) {
120       ret = section;
121       break;
122     }
123   }
124 
125   return ret;
126 }
127 
GetExportEntry(LPCSTR name) const128 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
129   PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
130 
131   if (NULL == exports)
132     return NULL;
133 
134   WORD ordinal = 0;
135   if (!GetProcOrdinal(name, &ordinal))
136     return NULL;
137 
138   PDWORD functions = reinterpret_cast<PDWORD>(
139                          RVAToAddr(exports->AddressOfFunctions));
140 
141   return functions + ordinal - exports->Base;
142 }
143 
GetProcAddress(LPCSTR function_name) const144 FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
145   PDWORD export_entry = GetExportEntry(function_name);
146   if (NULL == export_entry)
147     return NULL;
148 
149   PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
150 
151   PBYTE exports = reinterpret_cast<PBYTE>(
152       GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
153   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
154 
155   // Check for forwarded exports as a special case.
156   if (exports <= function && exports + size > function)
157 #pragma warning(push)
158 #pragma warning(disable: 4312)
159     // This cast generates a warning because it is 32 bit specific.
160     return reinterpret_cast<FARPROC>(0xFFFFFFFF);
161 #pragma warning(pop)
162 
163   return reinterpret_cast<FARPROC>(function);
164 }
165 
GetProcOrdinal(LPCSTR function_name,WORD * ordinal) const166 bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
167   if (NULL == ordinal)
168     return false;
169 
170   PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
171 
172   if (NULL == exports)
173     return false;
174 
175   if (IsOrdinal(function_name)) {
176     *ordinal = ToOrdinal(function_name);
177   } else {
178     PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
179     PDWORD lower = names;
180     PDWORD upper = names + exports->NumberOfNames;
181     int cmp = -1;
182 
183     // Binary Search for the name.
184     while (lower != upper) {
185       PDWORD middle = lower + (upper - lower) / 2;
186       LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
187 
188       cmp = strcmp(function_name, name);
189 
190       if (cmp == 0) {
191         lower = middle;
192         break;
193       }
194 
195       if (cmp > 0)
196         lower = middle + 1;
197       else
198         upper = middle;
199     }
200 
201     if (cmp != 0)
202       return false;
203 
204 
205     PWORD ordinals = reinterpret_cast<PWORD>(
206                          RVAToAddr(exports->AddressOfNameOrdinals));
207 
208     *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
209   }
210 
211   return true;
212 }
213 
EnumSections(EnumSectionsFunction callback,PVOID cookie) const214 bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
215   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
216   UINT num_sections = nt_headers->FileHeader.NumberOfSections;
217   PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
218 
219   for (UINT i = 0; i < num_sections; i++, section++) {
220     PVOID section_start = RVAToAddr(section->VirtualAddress);
221     DWORD size = section->Misc.VirtualSize;
222 
223     if (!callback(*this, section, section_start, size, cookie))
224       return false;
225   }
226 
227   return true;
228 }
229 
EnumExports(EnumExportsFunction callback,PVOID cookie) const230 bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
231   PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
232   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
233 
234   // Check if there are any exports at all.
235   if (NULL == directory || 0 == size)
236     return true;
237 
238   PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
239                                         directory);
240   UINT ordinal_base = exports->Base;
241   UINT num_funcs = exports->NumberOfFunctions;
242   UINT num_names = exports->NumberOfNames;
243   PDWORD functions  = reinterpret_cast<PDWORD>(RVAToAddr(
244                           exports->AddressOfFunctions));
245   PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
246   PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
247                        exports->AddressOfNameOrdinals));
248 
249   for (UINT count = 0; count < num_funcs; count++) {
250     PVOID func = RVAToAddr(functions[count]);
251     if (NULL == func)
252       continue;
253 
254     // Check for a name.
255     LPCSTR name = NULL;
256     UINT hint;
257     for (hint = 0; hint < num_names; hint++) {
258       if (ordinals[hint] == count) {
259         name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
260         break;
261       }
262     }
263 
264     if (name == NULL)
265       hint = 0;
266 
267     // Check for forwarded exports.
268     LPCSTR forward = NULL;
269     if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
270         reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
271             size) {
272       forward = reinterpret_cast<LPCSTR>(func);
273       func = 0;
274     }
275 
276     if (!callback(*this, ordinal_base + count, hint, name, func, forward,
277                   cookie))
278       return false;
279   }
280 
281   return true;
282 }
283 
EnumRelocs(EnumRelocsFunction callback,PVOID cookie) const284 bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
285   PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
286   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
287   PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
288       directory);
289 
290   if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
291     return true;
292 
293   while (base->SizeOfBlock) {
294     PWORD reloc = reinterpret_cast<PWORD>(base + 1);
295     UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
296         sizeof(WORD);
297 
298     for (UINT i = 0; i < num_relocs; i++, reloc++) {
299       WORD type = *reloc >> 12;
300       PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
301 
302       if (!callback(*this, type, address, cookie))
303         return false;
304     }
305 
306     base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
307                reinterpret_cast<char*>(base) + base->SizeOfBlock);
308   }
309 
310   return true;
311 }
312 
EnumImportChunks(EnumImportChunksFunction callback,PVOID cookie) const313 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
314                                PVOID cookie) const {
315   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
316   PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
317 
318   if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
319     return true;
320 
321   for (; import->FirstThunk; import++) {
322     LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
323     PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
324                                        RVAToAddr(import->OriginalFirstThunk));
325     PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
326                                 RVAToAddr(import->FirstThunk));
327 
328     if (!callback(*this, module_name, name_table, iat, cookie))
329       return false;
330   }
331 
332   return true;
333 }
334 
EnumOneImportChunk(EnumImportsFunction callback,LPCSTR module_name,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie) const335 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
336                                  LPCSTR module_name,
337                                  PIMAGE_THUNK_DATA name_table,
338                                  PIMAGE_THUNK_DATA iat, PVOID cookie) const {
339   if (NULL == name_table)
340     return false;
341 
342   for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
343     LPCSTR name = NULL;
344     WORD ordinal = 0;
345     WORD hint = 0;
346 
347     if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
348       ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
349     } else {
350       PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
351           RVAToAddr(name_table->u1.ForwarderString));
352 
353       hint = import->Hint;
354       name = reinterpret_cast<LPCSTR>(&import->Name);
355     }
356 
357     if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
358       return false;
359   }
360 
361   return true;
362 }
363 
EnumAllImports(EnumImportsFunction callback,PVOID cookie) const364 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
365   EnumAllImportsStorage temp = { callback, cookie };
366   return EnumImportChunks(ProcessImportChunk, &temp);
367 }
368 
EnumDelayImportChunks(EnumDelayImportChunksFunction callback,PVOID cookie) const369 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
370                                     PVOID cookie) const {
371   PVOID directory = GetImageDirectoryEntryAddr(
372                         IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
373   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
374   PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
375 
376   if (directory == NULL || size == 0)
377     return true;
378 
379   for (; delay_descriptor->rvaHmod; delay_descriptor++) {
380     PIMAGE_THUNK_DATA name_table;
381     PIMAGE_THUNK_DATA iat;
382     PIMAGE_THUNK_DATA bound_iat;    // address of the optional bound IAT
383     PIMAGE_THUNK_DATA unload_iat;   // address of optional copy of original IAT
384     LPCSTR module_name;
385 
386     // check if VC7-style imports, using RVAs instead of
387     // VC6-style addresses.
388     bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
389 
390     if (rvas) {
391       module_name = reinterpret_cast<LPCSTR>(
392                         RVAToAddr(delay_descriptor->rvaDLLName));
393       name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
394                        RVAToAddr(delay_descriptor->rvaINT));
395       iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
396                 RVAToAddr(delay_descriptor->rvaIAT));
397       bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
398                       RVAToAddr(delay_descriptor->rvaBoundIAT));
399       unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
400                        RVAToAddr(delay_descriptor->rvaUnloadIAT));
401     } else {
402 #pragma warning(push)
403 #pragma warning(disable: 4312)
404       // These casts generate warnings because they are 32 bit specific.
405       module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
406       name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
407                        delay_descriptor->rvaINT);
408       iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
409       bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
410                       delay_descriptor->rvaBoundIAT);
411       unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
412                        delay_descriptor->rvaUnloadIAT);
413 #pragma warning(pop)
414     }
415 
416     if (!callback(*this, delay_descriptor, module_name, name_table, iat,
417                   bound_iat, unload_iat, cookie))
418       return false;
419   }
420 
421   return true;
422 }
423 
EnumOneDelayImportChunk(EnumImportsFunction callback,PImgDelayDescr delay_descriptor,LPCSTR module_name,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PIMAGE_THUNK_DATA bound_iat,PIMAGE_THUNK_DATA unload_iat,PVOID cookie) const424 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
425                                       PImgDelayDescr delay_descriptor,
426                                       LPCSTR module_name,
427                                       PIMAGE_THUNK_DATA name_table,
428                                       PIMAGE_THUNK_DATA iat,
429                                       PIMAGE_THUNK_DATA bound_iat,
430                                       PIMAGE_THUNK_DATA unload_iat,
431                                       PVOID cookie) const {
432   UNREFERENCED_PARAMETER(bound_iat);
433   UNREFERENCED_PARAMETER(unload_iat);
434 
435   for (; name_table->u1.Ordinal; name_table++, iat++) {
436     LPCSTR name = NULL;
437     WORD ordinal = 0;
438     WORD hint = 0;
439 
440     if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
441       ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
442     } else {
443       PIMAGE_IMPORT_BY_NAME import;
444       bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
445 
446       if (rvas) {
447         import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
448                      RVAToAddr(name_table->u1.ForwarderString));
449       } else {
450 #pragma warning(push)
451 #pragma warning(disable: 4312)
452         // This cast generates a warning because it is 32 bit specific.
453         import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
454                      name_table->u1.ForwarderString);
455 #pragma warning(pop)
456       }
457 
458       hint = import->Hint;
459       name = reinterpret_cast<LPCSTR>(&import->Name);
460     }
461 
462     if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
463       return false;
464   }
465 
466   return true;
467 }
468 
EnumAllDelayImports(EnumImportsFunction callback,PVOID cookie) const469 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
470                                   PVOID cookie) const {
471   EnumAllImportsStorage temp = { callback, cookie };
472   return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
473 }
474 
VerifyMagic() const475 bool PEImage::VerifyMagic() const {
476   PIMAGE_DOS_HEADER dos_header = GetDosHeader();
477 
478   if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
479     return false;
480 
481   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
482 
483   if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
484     return false;
485 
486   if (nt_headers->FileHeader.SizeOfOptionalHeader !=
487       sizeof(IMAGE_OPTIONAL_HEADER))
488     return false;
489 
490   if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
491     return false;
492 
493   return true;
494 }
495 
ImageRVAToOnDiskOffset(DWORD rva,DWORD * on_disk_offset) const496 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
497   LPVOID address = RVAToAddr(rva);
498   return ImageAddrToOnDiskOffset(address, on_disk_offset);
499 }
500 
ImageAddrToOnDiskOffset(LPVOID address,DWORD * on_disk_offset) const501 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
502                                       DWORD *on_disk_offset) const {
503   if (NULL == address)
504     return false;
505 
506   // Get the section that this address belongs to.
507   PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
508   if (NULL == section_header)
509     return false;
510 
511 #pragma warning(push)
512 #pragma warning(disable: 4311)
513   // These casts generate warnings because they are 32 bit specific.
514   // Don't follow the virtual RVAToAddr, use the one on the base.
515   DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
516                                     reinterpret_cast<DWORD>(PEImage::RVAToAddr(
517                                         section_header->VirtualAddress));
518 #pragma warning(pop)
519 
520   *on_disk_offset = section_header->PointerToRawData + offset_within_section;
521   return true;
522 }
523 
RVAToAddr(DWORD rva) const524 PVOID PEImage::RVAToAddr(DWORD rva) const {
525   if (rva == 0)
526     return NULL;
527 
528   return reinterpret_cast<char*>(module_) + rva;
529 }
530 
RVAToAddr(DWORD rva) const531 PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
532   if (rva == 0)
533     return NULL;
534 
535   PVOID in_memory = PEImage::RVAToAddr(rva);
536   DWORD dummy;
537 
538   if (!ImageAddrToOnDiskOffset(in_memory, &dummy))
539     return NULL;
540 
541   return in_memory;
542 }
543