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