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