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