1 // Copyright (c) 2010 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/win/pe_image.h"
9 
10 #include <stddef.h>
11 #include <set>
12 #include <string>
13 
14 #include "base/no_destructor.h"
15 #include "base/win/current_module.h"
16 
17 namespace base {
18 namespace win {
19 
20 // Structure to perform imports enumerations.
21 struct EnumAllImportsStorage {
22   PEImage::EnumImportsFunction callback;
23   PVOID cookie;
24 };
25 
26 namespace {
27 
28 // PdbInfo Signature
29 const DWORD kPdbInfoSignature = 'SDSR';
30 
31 // Compare two strings byte by byte on an unsigned basis.
32 //   if s1 == s2, return 0
33 //   if s1 < s2, return negative
34 //   if s1 > s2, return positive
35 // Exception if inputs are invalid.
StrCmpByByte(LPCSTR s1,LPCSTR s2)36 int StrCmpByByte(LPCSTR s1, LPCSTR s2) {
37   while (*s1 != '\0' && *s1 == *s2) {
38     ++s1;
39     ++s2;
40   }
41 
42   return (*reinterpret_cast<const unsigned char*>(s1) -
43           *reinterpret_cast<const unsigned char*>(s2));
44 }
45 
46 struct PdbInfo {
47   DWORD Signature;
48   GUID Guid;
49   DWORD Age;
50   char PdbFileName[1];
51 };
52 
53 #define LDR_IS_DATAFILE(handle) (((ULONG_PTR)(handle)) & (ULONG_PTR)1)
54 #define LDR_IS_IMAGEMAPPING(handle) (((ULONG_PTR)(handle)) & (ULONG_PTR)2)
55 #define LDR_IS_RESOURCE(handle) \
56   (LDR_IS_IMAGEMAPPING(handle) || LDR_IS_DATAFILE(handle))
57 
58 }  // namespace
59 
60 // Callback used to enumerate imports. See EnumImportChunksFunction.
ProcessImportChunk(const PEImage & image,LPCSTR module,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie)61 bool ProcessImportChunk(const PEImage& image,
62                         LPCSTR module,
63                         PIMAGE_THUNK_DATA name_table,
64                         PIMAGE_THUNK_DATA iat,
65                         PVOID cookie) {
66   EnumAllImportsStorage& storage =
67       *reinterpret_cast<EnumAllImportsStorage*>(cookie);
68 
69   return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
70                                   storage.cookie);
71 }
72 
73 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
ProcessDelayImportChunk(const PEImage & image,PImgDelayDescr delay_descriptor,LPCSTR module,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie)74 bool ProcessDelayImportChunk(const PEImage& image,
75                              PImgDelayDescr delay_descriptor,
76                              LPCSTR module,
77                              PIMAGE_THUNK_DATA name_table,
78                              PIMAGE_THUNK_DATA iat,
79                              PVOID cookie) {
80   EnumAllImportsStorage& storage =
81       *reinterpret_cast<EnumAllImportsStorage*>(cookie);
82 
83   return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
84                                        module, name_table, iat, storage.cookie);
85 }
86 
set_module(HMODULE module)87 void PEImage::set_module(HMODULE module) {
88   module_ = module;
89 }
90 
GetDosHeader() const91 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
92   return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
93 }
94 
GetNTHeaders() const95 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
96   PIMAGE_DOS_HEADER dos_header = GetDosHeader();
97 
98   return reinterpret_cast<PIMAGE_NT_HEADERS>(
99       reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
100 }
101 
GetSectionHeader(UINT section) const102 PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
103   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
104   PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
105 
106   if (section < nt_headers->FileHeader.NumberOfSections)
107     return first_section + section;
108   else
109     return nullptr;
110 }
111 
GetNumSections() const112 WORD PEImage::GetNumSections() const {
113   return GetNTHeaders()->FileHeader.NumberOfSections;
114 }
115 
GetImageDirectoryEntrySize(UINT directory) const116 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
117   const IMAGE_DATA_DIRECTORY* const entry = GetDataDirectory(directory);
118   return entry ? entry->Size : 0;
119 }
120 
GetImageDirectoryEntryAddr(UINT directory) const121 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
122   const IMAGE_DATA_DIRECTORY* const entry = GetDataDirectory(directory);
123   return entry ? RVAToAddr(entry->VirtualAddress) : nullptr;
124 }
125 
GetImageSectionFromAddr(PVOID address) const126 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
127   PBYTE target = reinterpret_cast<PBYTE>(address);
128   PIMAGE_SECTION_HEADER section;
129 
130   for (UINT i = 0; nullptr != (section = GetSectionHeader(i)); i++) {
131     // Don't use the virtual RVAToAddr.
132     PBYTE start =
133         reinterpret_cast<PBYTE>(PEImage::RVAToAddr(section->VirtualAddress));
134 
135     DWORD size = section->Misc.VirtualSize;
136 
137     if ((start <= target) && (start + size > target))
138       return section;
139   }
140 
141   return nullptr;
142 }
143 
GetImageSectionHeaderByName(LPCSTR section_name) const144 PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
145     LPCSTR section_name) const {
146   if (nullptr == section_name)
147     return nullptr;
148 
149   PIMAGE_SECTION_HEADER ret = nullptr;
150   int num_sections = GetNumSections();
151 
152   for (int i = 0; i < num_sections; i++) {
153     PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
154     if (_strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
155                   sizeof(section->Name)) == 0) {
156       ret = section;
157       break;
158     }
159   }
160 
161   return ret;
162 }
163 
GetDebugId(LPGUID guid,LPDWORD age,LPCSTR * pdb_filename,size_t * pdb_filename_length) const164 bool PEImage::GetDebugId(LPGUID guid,
165                          LPDWORD age,
166                          LPCSTR* pdb_filename,
167                          size_t* pdb_filename_length) const {
168   DWORD debug_directory_size =
169       GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG);
170   PIMAGE_DEBUG_DIRECTORY debug_directory =
171       reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(
172           GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG));
173   if (!debug_directory)
174     return false;
175 
176   size_t directory_count = debug_directory_size / sizeof(IMAGE_DEBUG_DIRECTORY);
177   for (size_t index = 0; index < directory_count; ++index) {
178     const IMAGE_DEBUG_DIRECTORY& entry = debug_directory[index];
179     if (entry.Type != IMAGE_DEBUG_TYPE_CODEVIEW)
180       continue;  // Unsupported debugging info format.
181     if (entry.SizeOfData < sizeof(PdbInfo))
182       continue;  // The data is too small to hold PDB info.
183     const PdbInfo* pdb_info =
184         reinterpret_cast<const PdbInfo*>(RVAToAddr(entry.AddressOfRawData));
185     if (!pdb_info)
186       continue;  // The data is not present in a mapped section.
187     if (pdb_info->Signature != kPdbInfoSignature)
188       continue;  // Unsupported PdbInfo signature
189 
190     if (guid)
191       *guid = pdb_info->Guid;
192     if (age)
193       *age = pdb_info->Age;
194     if (pdb_filename) {
195       const size_t length_max =
196           entry.SizeOfData - offsetof(PdbInfo, PdbFileName);
197       const char* eos = pdb_info->PdbFileName;
198       for (const char* const end = pdb_info->PdbFileName + length_max;
199            eos < end && *eos; ++eos)
200         ;
201       *pdb_filename_length = eos - pdb_info->PdbFileName;
202       *pdb_filename = pdb_info->PdbFileName;
203     }
204     return true;
205   }
206   return false;
207 }
208 
GetExportEntry(LPCSTR name) const209 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
210   PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
211 
212   if (nullptr == exports)
213     return nullptr;
214 
215   WORD ordinal = 0;
216   if (!GetProcOrdinal(name, &ordinal))
217     return nullptr;
218 
219   PDWORD functions =
220       reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfFunctions));
221 
222   return functions + ordinal - exports->Base;
223 }
224 
GetProcAddress(LPCSTR function_name) const225 FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
226   PDWORD export_entry = GetExportEntry(function_name);
227   if (nullptr == export_entry)
228     return nullptr;
229 
230   PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
231 
232   PBYTE exports = reinterpret_cast<PBYTE>(
233       GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
234   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
235   if (!exports || !size)
236     return nullptr;
237 
238   // Check for forwarded exports as a special case.
239   if (exports <= function && exports + size > function)
240     return reinterpret_cast<FARPROC>(-1);
241 
242   return reinterpret_cast<FARPROC>(function);
243 }
244 
GetProcOrdinal(LPCSTR function_name,WORD * ordinal) const245 bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD* ordinal) const {
246   if (nullptr == ordinal)
247     return false;
248 
249   PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
250 
251   if (nullptr == exports)
252     return false;
253 
254   if (IsOrdinal(function_name)) {
255     *ordinal = ToOrdinal(function_name);
256   } else {
257     PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
258     PDWORD lower = names;
259     PDWORD upper = names + exports->NumberOfNames;
260     int cmp = -1;
261 
262     // Binary Search for the name.
263     while (lower != upper) {
264       PDWORD middle = lower + (upper - lower) / 2;
265       LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
266 
267       // This may be called by sandbox before MSVCRT dll loads, so can't use
268       // CRT function here.
269       cmp = StrCmpByByte(function_name, name);
270 
271       if (cmp == 0) {
272         lower = middle;
273         break;
274       }
275 
276       if (cmp > 0)
277         lower = middle + 1;
278       else
279         upper = middle;
280     }
281 
282     if (cmp != 0)
283       return false;
284 
285     PWORD ordinals =
286         reinterpret_cast<PWORD>(RVAToAddr(exports->AddressOfNameOrdinals));
287 
288     *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
289   }
290 
291   return true;
292 }
293 
EnumSections(EnumSectionsFunction callback,PVOID cookie) const294 bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
295   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
296   UINT num_sections = nt_headers->FileHeader.NumberOfSections;
297   PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
298 
299   for (UINT i = 0; i < num_sections; i++, section++) {
300     PVOID section_start = RVAToAddr(section->VirtualAddress);
301     DWORD size = section->Misc.VirtualSize;
302 
303     if (!callback(*this, section, section_start, size, cookie))
304       return false;
305   }
306 
307   return true;
308 }
309 
EnumExports(EnumExportsFunction callback,PVOID cookie) const310 bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
311   PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
312   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
313 
314   // Check if there are any exports at all.
315   if (!directory || !size)
316     return true;
317 
318   PIMAGE_EXPORT_DIRECTORY exports =
319       reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(directory);
320   UINT ordinal_base = exports->Base;
321   UINT num_funcs = exports->NumberOfFunctions;
322   UINT num_names = exports->NumberOfNames;
323   PDWORD functions =
324       reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfFunctions));
325   PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
326   PWORD ordinals =
327       reinterpret_cast<PWORD>(RVAToAddr(exports->AddressOfNameOrdinals));
328 
329   for (UINT count = 0; count < num_funcs; count++) {
330     PVOID func = RVAToAddr(functions[count]);
331     if (nullptr == func)
332       continue;
333 
334     // Check for a name.
335     LPCSTR name = nullptr;
336     UINT hint;
337     for (hint = 0; hint < num_names; hint++) {
338       if (ordinals[hint] == count) {
339         name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
340         break;
341       }
342     }
343 
344     if (name == nullptr)
345       hint = 0;
346 
347     // Check for forwarded exports.
348     LPCSTR forward = nullptr;
349     if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
350         reinterpret_cast<char*>(func) <=
351             reinterpret_cast<char*>(directory) + size) {
352       forward = reinterpret_cast<LPCSTR>(func);
353       func = nullptr;
354     }
355 
356     if (!callback(*this, ordinal_base + count, hint, name, func, forward,
357                   cookie))
358       return false;
359   }
360 
361   return true;
362 }
363 
EnumRelocs(EnumRelocsFunction callback,PVOID cookie) const364 bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
365   PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
366   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
367 
368   if (!directory || !size)
369     return true;
370 
371   PIMAGE_BASE_RELOCATION base =
372       reinterpret_cast<PIMAGE_BASE_RELOCATION>(directory);
373   while (size >= sizeof(IMAGE_BASE_RELOCATION) && base->SizeOfBlock &&
374          size >= base->SizeOfBlock) {
375     PWORD reloc = reinterpret_cast<PWORD>(base + 1);
376     UINT num_relocs =
377         (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
378 
379     for (UINT i = 0; i < num_relocs; i++, reloc++) {
380       WORD type = *reloc >> 12;
381       PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
382 
383       if (!callback(*this, type, address, cookie))
384         return false;
385     }
386 
387     size -= base->SizeOfBlock;
388     base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
389         reinterpret_cast<char*>(base) + base->SizeOfBlock);
390   }
391 
392   return true;
393 }
394 
EnumImportChunks(EnumImportChunksFunction callback,PVOID cookie,LPCSTR target_module_name) const395 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
396                                PVOID cookie,
397                                LPCSTR target_module_name) const {
398   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
399   PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
400 
401   if (import == nullptr || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
402     return true;
403 
404   for (; import->FirstThunk; import++) {
405     LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
406     PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
407         RVAToAddr(import->OriginalFirstThunk));
408     PIMAGE_THUNK_DATA iat =
409         reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToAddr(import->FirstThunk));
410 
411     if (target_module_name == nullptr ||
412         (lstrcmpiA(module_name, target_module_name) == 0)) {
413       if (!callback(*this, module_name, name_table, iat, cookie))
414         return false;
415     }
416   }
417 
418   return true;
419 }
420 
EnumOneImportChunk(EnumImportsFunction callback,LPCSTR module_name,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie) const421 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
422                                  LPCSTR module_name,
423                                  PIMAGE_THUNK_DATA name_table,
424                                  PIMAGE_THUNK_DATA iat,
425                                  PVOID cookie) const {
426   if (nullptr == name_table)
427     return false;
428 
429   for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
430     LPCSTR name = nullptr;
431     WORD ordinal = 0;
432     WORD hint = 0;
433 
434     if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
435       ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
436     } else {
437       PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
438           RVAToAddr(name_table->u1.ForwarderString));
439 
440       hint = import->Hint;
441       name = reinterpret_cast<LPCSTR>(&import->Name);
442     }
443 
444     if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
445       return false;
446   }
447 
448   return true;
449 }
450 
EnumAllImports(EnumImportsFunction callback,PVOID cookie,LPCSTR target_module_name) const451 bool PEImage::EnumAllImports(EnumImportsFunction callback,
452                              PVOID cookie,
453                              LPCSTR target_module_name) const {
454   EnumAllImportsStorage temp = {callback, cookie};
455   return EnumImportChunks(ProcessImportChunk, &temp, target_module_name);
456 }
457 
EnumDelayImportChunks(EnumDelayImportChunksFunction callback,PVOID cookie,LPCSTR target_module_name) const458 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
459                                     PVOID cookie,
460                                     LPCSTR target_module_name) const {
461   PVOID directory =
462       GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
463   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
464 
465   if (!directory || !size)
466     return true;
467 
468   PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
469   for (; delay_descriptor->rvaHmod; delay_descriptor++) {
470     PIMAGE_THUNK_DATA name_table;
471     PIMAGE_THUNK_DATA iat;
472     LPCSTR module_name;
473 
474     // check if VC7-style imports, using RVAs instead of
475     // VC6-style addresses.
476     bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
477 
478     if (rvas) {
479       module_name =
480           reinterpret_cast<LPCSTR>(RVAToAddr(delay_descriptor->rvaDLLName));
481       name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
482           RVAToAddr(delay_descriptor->rvaINT));
483       iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
484           RVAToAddr(delay_descriptor->rvaIAT));
485     } else {
486       // Values in IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT are 32-bit, even on 64-bit
487       // platforms. See section 4.8 of PECOFF image spec rev 8.3.
488       module_name = reinterpret_cast<LPCSTR>(
489           static_cast<uintptr_t>(delay_descriptor->rvaDLLName));
490       name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
491           static_cast<uintptr_t>(delay_descriptor->rvaINT));
492       iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
493           static_cast<uintptr_t>(delay_descriptor->rvaIAT));
494     }
495 
496     if (target_module_name == nullptr ||
497         (lstrcmpiA(module_name, target_module_name) == 0)) {
498       if (target_module_name) {
499         // Ensure all imports are properly loaded for the target module so that
500         // the callback is operating on a fully-realized set of imports.
501         // This call only loads the imports for the module where this code is
502         // executing, so it is only helpful or meaningful to do this if the
503         // current module is the module whose IAT we are enumerating.
504         // Use the module_name as retrieved from the IAT because this method
505         // is case sensitive.
506         if (module_ == CURRENT_MODULE() && !LDR_IS_RESOURCE(module_)) {
507           static base::NoDestructor<std::set<std::string>> loaded_dlls;
508           // pair.second is true if this is a new element
509           if (loaded_dlls.get()->emplace(module_name).second)
510             ::__HrLoadAllImportsForDll(module_name);
511         }
512       }
513 
514       if (!callback(*this, delay_descriptor, module_name, name_table, iat,
515                     cookie))
516         return false;
517     }
518   }
519 
520   return true;
521 }
522 
EnumOneDelayImportChunk(EnumImportsFunction callback,PImgDelayDescr delay_descriptor,LPCSTR module_name,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie) const523 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
524                                       PImgDelayDescr delay_descriptor,
525                                       LPCSTR module_name,
526                                       PIMAGE_THUNK_DATA name_table,
527                                       PIMAGE_THUNK_DATA iat,
528                                       PVOID cookie) const {
529   for (; name_table->u1.Ordinal; name_table++, iat++) {
530     LPCSTR name = nullptr;
531     WORD ordinal = 0;
532     WORD hint = 0;
533 
534     if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
535       ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
536     } else {
537       PIMAGE_IMPORT_BY_NAME import;
538       bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
539 
540       if (rvas) {
541         import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
542             RVAToAddr(name_table->u1.ForwarderString));
543       } else {
544         import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
545             name_table->u1.ForwarderString);
546       }
547 
548       hint = import->Hint;
549       name = reinterpret_cast<LPCSTR>(&import->Name);
550     }
551 
552     if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
553       return false;
554   }
555 
556   return true;
557 }
558 
EnumAllDelayImports(EnumImportsFunction callback,PVOID cookie,LPCSTR target_module_name) const559 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
560                                   PVOID cookie,
561                                   LPCSTR target_module_name) const {
562   EnumAllImportsStorage temp = {callback, cookie};
563   return EnumDelayImportChunks(ProcessDelayImportChunk, &temp,
564                                target_module_name);
565 }
566 
VerifyMagic() const567 bool PEImage::VerifyMagic() const {
568   PIMAGE_DOS_HEADER dos_header = GetDosHeader();
569 
570   if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
571     return false;
572 
573   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
574 
575   if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
576     return false;
577 
578   if (nt_headers->FileHeader.SizeOfOptionalHeader !=
579       sizeof(IMAGE_OPTIONAL_HEADER))
580     return false;
581 
582   if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
583     return false;
584 
585   return true;
586 }
587 
ImageRVAToOnDiskOffset(uintptr_t rva,DWORD * on_disk_offset) const588 bool PEImage::ImageRVAToOnDiskOffset(uintptr_t rva,
589                                      DWORD* on_disk_offset) const {
590   LPVOID address = RVAToAddr(rva);
591   return ImageAddrToOnDiskOffset(address, on_disk_offset);
592 }
593 
ImageAddrToOnDiskOffset(LPVOID address,DWORD * on_disk_offset) const594 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
595                                       DWORD* on_disk_offset) const {
596   if (nullptr == address)
597     return false;
598 
599   // Get the section that this address belongs to.
600   PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
601   if (nullptr == section_header)
602     return false;
603 
604   // Don't follow the virtual RVAToAddr, use the one on the base.
605   DWORD offset_within_section =
606       static_cast<DWORD>(reinterpret_cast<uintptr_t>(address)) -
607       static_cast<DWORD>(reinterpret_cast<uintptr_t>(
608           PEImage::RVAToAddr(section_header->VirtualAddress)));
609 
610   *on_disk_offset = section_header->PointerToRawData + offset_within_section;
611   return true;
612 }
613 
RVAToAddr(uintptr_t rva) const614 PVOID PEImage::RVAToAddr(uintptr_t rva) const {
615   if (rva == 0)
616     return nullptr;
617 
618   return reinterpret_cast<char*>(module_) + rva;
619 }
620 
GetDataDirectory(UINT directory) const621 const IMAGE_DATA_DIRECTORY* PEImage::GetDataDirectory(UINT directory) const {
622   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
623 
624   // Does the image report that it includes this directory entry?
625   if (directory >= nt_headers->OptionalHeader.NumberOfRvaAndSizes)
626     return nullptr;
627 
628   // Is there space for this directory entry in the optional header?
629   if (nt_headers->FileHeader.SizeOfOptionalHeader <
630       (offsetof(IMAGE_OPTIONAL_HEADER, DataDirectory) +
631        (directory + 1) * sizeof(IMAGE_DATA_DIRECTORY))) {
632     return nullptr;
633   }
634 
635   return &nt_headers->OptionalHeader.DataDirectory[directory];
636 }
637 
RVAToAddr(uintptr_t rva) const638 PVOID PEImageAsData::RVAToAddr(uintptr_t rva) const {
639   if (rva == 0)
640     return nullptr;
641 
642   PVOID in_memory = PEImage::RVAToAddr(rva);
643   DWORD disk_offset;
644 
645   if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
646     return nullptr;
647 
648   return PEImage::RVAToAddr(disk_offset);
649 }
650 
651 }  // namespace win
652 }  // namespace base
653