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