security/sandbox/chromium/base/win/pe_image.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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.
     5 // This file implements PEImage, a generic class to manipulate PE files.
     6 // This file was adapted from GreenBorder's Code.
     8 #include "base/win/pe_image.h"
    10 namespace base {
    11 namespace win {
    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
    22 // Structure to perform imports enumerations.
    23 struct EnumAllImportsStorage {
    24   PEImage::EnumImportsFunction callback;
    25   PVOID cookie;
    26 };
    28 namespace {
    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     }
    41     return (*reinterpret_cast<const unsigned char*>(s1) -
    42             *reinterpret_cast<const unsigned char*>(s2));
    43   }
    45 }  // namespace
    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);
    54   return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
    55                                   storage.cookie);
    56 }
    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);
    67   return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
    68                                        module, name_table, iat, bound_iat,
    69                                        unload_iat, storage.cookie);
    70 }
    72 void PEImage::set_module(HMODULE module) {
    73   module_ = module;
    74 }
    76 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
    77   return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
    78 }
    80 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
    81   PIMAGE_DOS_HEADER dos_header = GetDosHeader();
    83   return reinterpret_cast<PIMAGE_NT_HEADERS>(
    84       reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
    85 }
    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);
    91   if (section < nt_headers->FileHeader.NumberOfSections)
    92     return first_section + section;
    93   else
    94     return NULL;
    95 }
    97 WORD PEImage::GetNumSections() const {
    98   return GetNTHeaders()->FileHeader.NumberOfSections;
    99 }
   101 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
   102   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
   104   return nt_headers->OptionalHeader.DataDirectory[directory].Size;
   105 }
   107 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
   108   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
   110   return RVAToAddr(
   111       nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
   112 }
   114 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
   115   PBYTE target = reinterpret_cast<PBYTE>(address);
   116   PIMAGE_SECTION_HEADER section;
   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));
   123     DWORD size = section->Misc.VirtualSize;
   125     if ((start <= target) && (start + size > target))
   126       return section;
   127   }
   129   return NULL;
   130 }
   132 PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
   133     LPCSTR section_name) const {
   134   if (NULL == section_name)
   135     return NULL;
   137   PIMAGE_SECTION_HEADER ret = NULL;
   138   int num_sections = GetNumSections();
   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   }
   149   return ret;
   150 }
   152 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
   153   PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
   155   if (NULL == exports)
   156     return NULL;
   158   WORD ordinal = 0;
   159   if (!GetProcOrdinal(name, &ordinal))
   160     return NULL;
   162   PDWORD functions = reinterpret_cast<PDWORD>(
   163                          RVAToAddr(exports->AddressOfFunctions));
   165   return functions + ordinal - exports->Base;
   166 }
   168 FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
   169   PDWORD export_entry = GetExportEntry(function_name);
   170   if (NULL == export_entry)
   171     return NULL;
   173   PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
   175   PBYTE exports = reinterpret_cast<PBYTE>(
   176       GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
   177   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
   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)
   187   return reinterpret_cast<FARPROC>(function);
   188 }
   190 bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
   191   if (NULL == ordinal)
   192     return false;
   194   PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
   196   if (NULL == exports)
   197     return false;
   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;
   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));
   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);
   216       if (cmp == 0) {
   217         lower = middle;
   218         break;
   219       }
   221       if (cmp > 0)
   222         lower = middle + 1;
   223       else
   224         upper = middle;
   225     }
   227     if (cmp != 0)
   228       return false;
   231     PWORD ordinals = reinterpret_cast<PWORD>(
   232                          RVAToAddr(exports->AddressOfNameOrdinals));
   234     *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
   235   }
   237   return true;
   238 }
   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);
   245   for (UINT i = 0; i < num_sections; i++, section++) {
   246     PVOID section_start = RVAToAddr(section->VirtualAddress);
   247     DWORD size = section->Misc.VirtualSize;
   249     if (!callback(*this, section, section_start, size, cookie))
   250       return false;
   251   }
   253   return true;
   254 }
   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);
   260   // Check if there are any exports at all.
   261   if (NULL == directory || 0 == size)
   262     return true;
   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));
   275   for (UINT count = 0; count < num_funcs; count++) {
   276     PVOID func = RVAToAddr(functions[count]);
   277     if (NULL == func)
   278       continue;
   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     }
   290     if (name == NULL)
   291       hint = 0;
   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     }
   302     if (!callback(*this, ordinal_base + count, hint, name, func, forward,
   303                   cookie))
   304       return false;
   305   }
   307   return true;
   308 }
   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);
   316   if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
   317     return true;
   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);
   324     for (UINT i = 0; i < num_relocs; i++, reloc++) {
   325       WORD type = *reloc >> 12;
   326       PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
   328       if (!callback(*this, type, address, cookie))
   329         return false;
   330     }
   332     base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
   333                reinterpret_cast<char*>(base) + base->SizeOfBlock);
   334   }
   336   return true;
   337 }
   339 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
   340                                PVOID cookie) const {
   341   DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
   342   PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
   344   if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
   345     return true;
   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));
   354     if (!callback(*this, module_name, name_table, iat, cookie))
   355       return false;
   356   }
   358   return true;
   359 }
   361 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;
   368   for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
   369     LPCSTR name = NULL;
   370     WORD ordinal = 0;
   371     WORD hint = 0;
   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));
   379       hint = import->Hint;
   380       name = reinterpret_cast<LPCSTR>(&import->Name);
   381     }
   383     if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
   384       return false;
   385   }
   387   return true;
   388 }
   390 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
   391   EnumAllImportsStorage temp = { callback, cookie };
   392   return EnumImportChunks(ProcessImportChunk, &temp);
   393 }
   395 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);
   402   if (directory == NULL || size == 0)
   403     return true;
   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;
   412     // check if VC7-style imports, using RVAs instead of
   413     // VC6-style addresses.
   414     bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
   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     }
   442     if (!callback(*this, delay_descriptor, module_name, name_table, iat,
   443                   bound_iat, unload_iat, cookie))
   444       return false;
   445   }
   447   return true;
   448 }
   450 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);
   461   for (; name_table->u1.Ordinal; name_table++, iat++) {
   462     LPCSTR name = NULL;
   463     WORD ordinal = 0;
   464     WORD hint = 0;
   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;
   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       }
   484       hint = import->Hint;
   485       name = reinterpret_cast<LPCSTR>(&import->Name);
   486     }
   488     if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
   489       return false;
   490   }
   492   return true;
   493 }
   495 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
   496                                   PVOID cookie) const {
   497   EnumAllImportsStorage temp = { callback, cookie };
   498   return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
   499 }
   501 bool PEImage::VerifyMagic() const {
   502   PIMAGE_DOS_HEADER dos_header = GetDosHeader();
   504   if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
   505     return false;
   507   PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
   509   if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
   510     return false;
   512   if (nt_headers->FileHeader.SizeOfOptionalHeader !=
   513       sizeof(IMAGE_OPTIONAL_HEADER))
   514     return false;
   516   if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
   517     return false;
   519   return true;
   520 }
   522 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
   523   LPVOID address = RVAToAddr(rva);
   524   return ImageAddrToOnDiskOffset(address, on_disk_offset);
   525 }
   527 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
   528                                       DWORD *on_disk_offset) const {
   529   if (NULL == address)
   530     return false;
   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;
   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)
   546   *on_disk_offset = section_header->PointerToRawData + offset_within_section;
   547   return true;
   548 }
   550 PVOID PEImage::RVAToAddr(DWORD rva) const {
   551   if (rva == 0)
   552     return NULL;
   554   return reinterpret_cast<char*>(module_) + rva;
   555 }
   557 PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
   558   if (rva == 0)
   559     return NULL;
   561   PVOID in_memory = PEImage::RVAToAddr(rva);
   562   DWORD disk_offset;
   564   if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
   565     return NULL;
   567   return PEImage::RVAToAddr(disk_offset);
   568 }
   570 }  // namespace win
   571 }  // namespace base

mercurial