Home | History | Annotate | Download | only in win
      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.
     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.
     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.
     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 
     72 void PEImage::set_module(HMODULE module) {
     73   module_ = module;
     74 }
     75 
     76 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
     77   return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
     78 }
     79 
     80 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 
     87 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 
     97 WORD PEImage::GetNumSections() const {
     98   return GetNTHeaders()->FileHeader.NumberOfSections;
     99 }
    100 
    101 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
    102   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
    103 
    104   return nt_headers->OptionalHeader.DataDirectory[directory].Size;
    105 }
    106 
    107 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 
    114 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 
    132 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 
    152 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 
    168 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 
    190 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 
    240 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 
    256 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 
    310 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)
    317     return true;
    318 
    319   while (size >= sizeof(IMAGE_BASE_RELOCATION) && base->SizeOfBlock &&
    320          size >= base->SizeOfBlock) {
    321     PWORD reloc = reinterpret_cast<PWORD>(base + 1);
    322     UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
    323         sizeof(WORD);
    324 
    325     for (UINT i = 0; i < num_relocs; i++, reloc++) {
    326       WORD type = *reloc >> 12;
    327       PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
    328 
    329       if (!callback(*this, type, address, cookie))
    330         return false;
    331     }
    332 
    333     size -= base->SizeOfBlock;
    334     base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
    335                reinterpret_cast<char*>(base) + base->SizeOfBlock);
    336   }
    337 
    338   return true;
    339 }
    340 
    341 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
    342                                PVOID cookie) const {
    343   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
    344   PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
    345 
    346   if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
    347     return true;
    348 
    349   for (; import->FirstThunk; import++) {
    350     LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
    351     PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
    352                                        RVAToAddr(import->OriginalFirstThunk));
    353     PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
    354                                 RVAToAddr(import->FirstThunk));
    355 
    356     if (!callback(*this, module_name, name_table, iat, cookie))
    357       return false;
    358   }
    359 
    360   return true;
    361 }
    362 
    363 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
    364                                  LPCSTR module_name,
    365                                  PIMAGE_THUNK_DATA name_table,
    366                                  PIMAGE_THUNK_DATA iat, PVOID cookie) const {
    367   if (NULL == name_table)
    368     return false;
    369 
    370   for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
    371     LPCSTR name = NULL;
    372     WORD ordinal = 0;
    373     WORD hint = 0;
    374 
    375     if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
    376       ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
    377     } else {
    378       PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
    379           RVAToAddr(name_table->u1.ForwarderString));
    380 
    381       hint = import->Hint;
    382       name = reinterpret_cast<LPCSTR>(&import->Name);
    383     }
    384 
    385     if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
    386       return false;
    387   }
    388 
    389   return true;
    390 }
    391 
    392 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
    393   EnumAllImportsStorage temp = { callback, cookie };
    394   return EnumImportChunks(ProcessImportChunk, &temp);
    395 }
    396 
    397 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
    398                                     PVOID cookie) const {
    399   PVOID directory = GetImageDirectoryEntryAddr(
    400                         IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
    401   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
    402   PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
    403 
    404   if (directory == NULL || size == 0)
    405     return true;
    406 
    407   for (; delay_descriptor->rvaHmod; delay_descriptor++) {
    408     PIMAGE_THUNK_DATA name_table;
    409     PIMAGE_THUNK_DATA iat;
    410     PIMAGE_THUNK_DATA bound_iat;    // address of the optional bound IAT
    411     PIMAGE_THUNK_DATA unload_iat;   // address of optional copy of original IAT
    412     LPCSTR module_name;
    413 
    414     // check if VC7-style imports, using RVAs instead of
    415     // VC6-style addresses.
    416     bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
    417 
    418     if (rvas) {
    419       module_name = reinterpret_cast<LPCSTR>(
    420                         RVAToAddr(delay_descriptor->rvaDLLName));
    421       name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
    422                        RVAToAddr(delay_descriptor->rvaINT));
    423       iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
    424                 RVAToAddr(delay_descriptor->rvaIAT));
    425       bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
    426                       RVAToAddr(delay_descriptor->rvaBoundIAT));
    427       unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
    428                        RVAToAddr(delay_descriptor->rvaUnloadIAT));
    429     } else {
    430 #pragma warning(push)
    431 #pragma warning(disable: 4312)
    432       // These casts generate warnings because they are 32 bit specific.
    433       module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
    434       name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
    435                        delay_descriptor->rvaINT);
    436       iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
    437       bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
    438                       delay_descriptor->rvaBoundIAT);
    439       unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
    440                        delay_descriptor->rvaUnloadIAT);
    441 #pragma warning(pop)
    442     }
    443 
    444     if (!callback(*this, delay_descriptor, module_name, name_table, iat,
    445                   bound_iat, unload_iat, cookie))
    446       return false;
    447   }
    448 
    449   return true;
    450 }
    451 
    452 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
    453                                       PImgDelayDescr delay_descriptor,
    454                                       LPCSTR module_name,
    455                                       PIMAGE_THUNK_DATA name_table,
    456                                       PIMAGE_THUNK_DATA iat,
    457                                       PIMAGE_THUNK_DATA bound_iat,
    458                                       PIMAGE_THUNK_DATA unload_iat,
    459                                       PVOID cookie) const {
    460   UNREFERENCED_PARAMETER(bound_iat);
    461   UNREFERENCED_PARAMETER(unload_iat);
    462 
    463   for (; name_table->u1.Ordinal; name_table++, iat++) {
    464     LPCSTR name = NULL;
    465     WORD ordinal = 0;
    466     WORD hint = 0;
    467 
    468     if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
    469       ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
    470     } else {
    471       PIMAGE_IMPORT_BY_NAME import;
    472       bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
    473 
    474       if (rvas) {
    475         import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
    476                      RVAToAddr(name_table->u1.ForwarderString));
    477       } else {
    478 #pragma warning(push)
    479 #pragma warning(disable: 4312)
    480         // This cast generates a warning because it is 32 bit specific.
    481         import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
    482                      name_table->u1.ForwarderString);
    483 #pragma warning(pop)
    484       }
    485 
    486       hint = import->Hint;
    487       name = reinterpret_cast<LPCSTR>(&import->Name);
    488     }
    489 
    490     if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
    491       return false;
    492   }
    493 
    494   return true;
    495 }
    496 
    497 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
    498                                   PVOID cookie) const {
    499   EnumAllImportsStorage temp = { callback, cookie };
    500   return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
    501 }
    502 
    503 bool PEImage::VerifyMagic() const {
    504   PIMAGE_DOS_HEADER dos_header = GetDosHeader();
    505 
    506   if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
    507     return false;
    508 
    509   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
    510 
    511   if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
    512     return false;
    513 
    514   if (nt_headers->FileHeader.SizeOfOptionalHeader !=
    515       sizeof(IMAGE_OPTIONAL_HEADER))
    516     return false;
    517 
    518   if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
    519     return false;
    520 
    521   return true;
    522 }
    523 
    524 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
    525   LPVOID address = RVAToAddr(rva);
    526   return ImageAddrToOnDiskOffset(address, on_disk_offset);
    527 }
    528 
    529 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
    530                                       DWORD *on_disk_offset) const {
    531   if (NULL == address)
    532     return false;
    533 
    534   // Get the section that this address belongs to.
    535   PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
    536   if (NULL == section_header)
    537     return false;
    538 
    539 #pragma warning(push)
    540 #pragma warning(disable: 4311)
    541   // These casts generate warnings because they are 32 bit specific.
    542   // Don't follow the virtual RVAToAddr, use the one on the base.
    543   DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
    544                                     reinterpret_cast<DWORD>(PEImage::RVAToAddr(
    545                                         section_header->VirtualAddress));
    546 #pragma warning(pop)
    547 
    548   *on_disk_offset = section_header->PointerToRawData + offset_within_section;
    549   return true;
    550 }
    551 
    552 PVOID PEImage::RVAToAddr(DWORD rva) const {
    553   if (rva == 0)
    554     return NULL;
    555 
    556   return reinterpret_cast<char*>(module_) + rva;
    557 }
    558 
    559 PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
    560   if (rva == 0)
    561     return NULL;
    562 
    563   PVOID in_memory = PEImage::RVAToAddr(rva);
    564   DWORD disk_offset;
    565 
    566   if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
    567     return NULL;
    568 
    569   return PEImage::RVAToAddr(disk_offset);
    570 }
    571 
    572 }  // namespace win
    573 }  // namespace base
    574