1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183

base / win / pe_image_reader.h [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_WIN_PE_IMAGE_READER_H_
#define BASE_WIN_PE_IMAGE_READER_H_

#include <windows.h>

#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "base/base_export.h"
#include "base/containers/span.h"
#include "base/memory/raw_span.h"
#include "base/numerics/safe_math.h"

namespace base {
namespace win {

// Parses headers and various data from a PE image. This parser is safe for use
// on untrusted data and works on PE files with different bitness from the
// current process. The PeImageReader is initialized after construction by
// passing the address and size of a PE file that has been read into memory -
// not loaded by the OS as an image. Parsing of a PE file that has been loaded
// as an image can be done with PEImage.
class BASE_EXPORT PeImageReader {
 public:
  enum WordSize {
    WORD_SIZE_32,
    WORD_SIZE_64,
  };

  // A callback invoked by EnumCertificates once for each attribute certificate
  // entry in the image's attribute certificate table. |revision| and
  // |certificate_type| identify the contents of |certificate_data| (which is of
  // |certificate_data_size| bytes). |context| is the value provided by the
  // caller to EnumCertificates(). Implementations must return true to continue
  // the enumeration, or false to abort.
  using EnumCertificatesCallback =
      bool (*)(uint16_t revision,
               uint16_t certificate_type,
               base::span<const uint8_t> certificate_data,
               void* context);

  PeImageReader();

  PeImageReader(const PeImageReader&) = delete;
  PeImageReader& operator=(const PeImageReader&) = delete;

  ~PeImageReader();

  // Returns false if the given data does not appear to be a valid PE image.
  bool Initialize(span<const uint8_t> image_data);

  // Returns the machine word size for the image.
  WordSize GetWordSize();

  const IMAGE_DOS_HEADER* GetDosHeader();
  const IMAGE_FILE_HEADER* GetCoffFileHeader();

  // Returns the optional header data.
  span<const uint8_t> GetOptionalHeaderData();
  size_t GetNumberOfSections();
  const IMAGE_SECTION_HEADER* GetSectionHeaderAt(size_t index);

  // Returns the image's export data (.edata) section, or an empty span if the
  // section is not present.
  span<const uint8_t> GetExportSection();

  size_t GetNumberOfDebugEntries();
  // Returns a pointer to the |index|'th debug directory entry, or nullptr if
  // |index| is out of bounds. |raw_data| is an out-param which will be filled
  // with the corresponding raw data.
  const IMAGE_DEBUG_DIRECTORY* GetDebugEntry(size_t index,
                                             span<const uint8_t>& raw_data);

  // Invokes |callback| once per attribute certificate entry. |context| is a
  // caller-specific value that is passed to |callback|. Returns true if all
  // certificate entries are visited (even if there are no such entries) and
  // |callback| returns true for each. Conversely, returns |false| if |callback|
  // returns false or if the image is malformed in any way.
  bool EnumCertificates(EnumCertificatesCallback callback, void* context);

  // Returns the size of the image file.
  DWORD GetSizeOfImage();

 private:
  // Bits indicating what portions of the image have been validated.
  enum ValidationStages {
    VALID_DOS_HEADER = 1 << 0,
    VALID_PE_SIGNATURE = 1 << 1,
    VALID_COFF_FILE_HEADER = 1 << 2,
    VALID_OPTIONAL_HEADER = 1 << 3,
    VALID_SECTION_HEADERS = 1 << 4,
  };

  // An interface to an image's optional header.
  class OptionalHeader {
   public:
    virtual ~OptionalHeader() = default;

    virtual WordSize GetWordSize() = 0;

    // Returns the offset of the DataDirectory member relative to the start of
    // the optional header.
    virtual size_t GetDataDirectoryOffset() = 0;

    // Returns the number of entries in the data directory.
    virtual DWORD GetDataDirectorySize() = 0;

    // Returns a pointer to the first data directory entry.
    virtual const IMAGE_DATA_DIRECTORY* GetDataDirectoryEntries() = 0;

    // Returns the size of the image file.
    virtual DWORD GetSizeOfImage() = 0;
  };

  template <class OPTIONAL_HEADER_TYPE>
  class OptionalHeaderImpl;

  void Clear();
  bool ValidateDosHeader();
  bool ValidatePeSignature();
  bool ValidateCoffFileHeader();
  bool ValidateOptionalHeader();
  bool ValidateSectionHeaders();

  // Return a pointer to the first byte of the image's optional header.
  const uint8_t* GetOptionalHeaderStart();
  size_t GetOptionalHeaderSize();

  // Returns the desired directory entry, or nullptr if |index| is out of
  // bounds.
  const IMAGE_DATA_DIRECTORY* GetDataDirectoryEntryAt(size_t index);

  // Returns the header for the section that contains the given address, or
  // nullptr if the address is out of bounds or the image does not contain the
  // section.
  const IMAGE_SECTION_HEADER* FindSectionFromRva(uint32_t relative_address);

  // Returns the bytes referenced by the |index|'th data directory entry.
  span<const uint8_t> GetImageData(size_t index);

  // Populates |structure| with a pointer to a desired structure of type T at
  // the given offset if the image is sufficiently large to contain it. Returns
  // false if the structure does not fully fit within the image at the given
  // offset.
  template <typename T>
  bool GetStructureAt(size_t offset, const T** structure) {
    return GetStructureAt(offset, sizeof(**structure), structure);
  }

  // Populates |structure| with a pointer to a desired structure of type T at
  // the given offset if the image is sufficiently large to contain
  // |structure_size| bytes. Returns false if the structure does not fully fit
  // within the image at the given offset.
  template <typename T>
  bool GetStructureAt(size_t offset,
                      size_t structure_size,
                      const T** structure) {
    size_t remaining_bytes = 0;
    if (!CheckSub(image_data_.size(), offset).AssignIfValid(&remaining_bytes)) {
      return false;
    }
    if (structure_size > remaining_bytes) {
      return false;
    }
    *structure = reinterpret_cast<const T*>(image_data_.subspan(offset).data());
    return true;
  }

  raw_span<const uint8_t> image_data_;
  uint32_t validation_state_ = 0;
  std::unique_ptr<OptionalHeader> optional_header_;
};

}  // namespace win
}  // namespace base

#endif  // BASE_WIN_PE_IMAGE_READER_H_