• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_WIN_PE_IMAGE_READER_H_
6 #define BASE_WIN_PE_IMAGE_READER_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <windows.h>
11 
12 #include <memory>
13 
14 #include "base/base_export.h"
15 
16 namespace base {
17 namespace win {
18 
19 // Parses headers and various data from a PE image. This parser is safe for use
20 // on untrusted data and works on PE files with different bitness from the
21 // current process. The PeImageReader is initialized after construction by
22 // passing the address and size of a PE file that has been read into memory -
23 // not loaded by the OS as an image. Parsing of a PE file that has been loaded
24 // as an image can be done with PEImage.
25 class BASE_EXPORT PeImageReader {
26  public:
27   enum WordSize {
28     WORD_SIZE_32,
29     WORD_SIZE_64,
30   };
31 
32   // A callback invoked by EnumCertificates once for each attribute certificate
33   // entry in the image's attribute certificate table. |revision| and
34   // |certificate_type| identify the contents of |certificate_data| (which is of
35   // |certificate_data_size| bytes). |context| is the value provided by the
36   // caller to EnumCertificates(). Implementations must return true to continue
37   // the enumeration, or false to abort.
38   using EnumCertificatesCallback = bool (*)(uint16_t revision,
39                                             uint16_t certificate_type,
40                                             const uint8_t* certificate_data,
41                                             size_t certificate_data_size,
42                                             void* context);
43 
44   PeImageReader();
45 
46   PeImageReader(const PeImageReader&) = delete;
47   PeImageReader& operator=(const PeImageReader&) = delete;
48 
49   ~PeImageReader();
50 
51   // Returns false if the given data does not appear to be a valid PE image.
52   bool Initialize(const uint8_t* image_data, size_t image_size);
53 
54   // Returns the machine word size for the image.
55   WordSize GetWordSize();
56 
57   const IMAGE_DOS_HEADER* GetDosHeader();
58   const IMAGE_FILE_HEADER* GetCoffFileHeader();
59 
60   // Returns a pointer to the optional header and its size.
61   const uint8_t* GetOptionalHeaderData(size_t* optional_data_size);
62   size_t GetNumberOfSections();
63   const IMAGE_SECTION_HEADER* GetSectionHeaderAt(size_t index);
64 
65   // Returns a pointer to the image's export data (.edata) section and its size,
66   // or nullptr if the section is not present.
67   const uint8_t* GetExportSection(size_t* section_size);
68 
69   size_t GetNumberOfDebugEntries();
70   const IMAGE_DEBUG_DIRECTORY* GetDebugEntry(size_t index,
71                                              const uint8_t** raw_data,
72                                              size_t* raw_data_size);
73 
74   // Invokes |callback| once per attribute certificate entry. |context| is a
75   // caller-specific value that is passed to |callback|. Returns true if all
76   // certificate entries are visited (even if there are no such entries) and
77   // |callback| returns true for each. Conversely, returns |false| if |callback|
78   // returns false or if the image is malformed in any way.
79   bool EnumCertificates(EnumCertificatesCallback callback, void* context);
80 
81   // Returns the size of the image file.
82   DWORD GetSizeOfImage();
83 
84  private:
85   // Bits indicating what portions of the image have been validated.
86   enum ValidationStages {
87     VALID_DOS_HEADER = 1 << 0,
88     VALID_PE_SIGNATURE = 1 << 1,
89     VALID_COFF_FILE_HEADER = 1 << 2,
90     VALID_OPTIONAL_HEADER = 1 << 3,
91     VALID_SECTION_HEADERS = 1 << 4,
92   };
93 
94   // An interface to an image's optional header.
95   class OptionalHeader {
96    public:
97     virtual ~OptionalHeader() = default;
98 
99     virtual WordSize GetWordSize() = 0;
100 
101     // Returns the offset of the DataDirectory member relative to the start of
102     // the optional header.
103     virtual size_t GetDataDirectoryOffset() = 0;
104 
105     // Returns the number of entries in the data directory.
106     virtual DWORD GetDataDirectorySize() = 0;
107 
108     // Returns a pointer to the first data directory entry.
109     virtual const IMAGE_DATA_DIRECTORY* GetDataDirectoryEntries() = 0;
110 
111     // Returns the size of the image file.
112     virtual DWORD GetSizeOfImage() = 0;
113   };
114 
115   template <class OPTIONAL_HEADER_TYPE>
116   class OptionalHeaderImpl;
117 
118   void Clear();
119   bool ValidateDosHeader();
120   bool ValidatePeSignature();
121   bool ValidateCoffFileHeader();
122   bool ValidateOptionalHeader();
123   bool ValidateSectionHeaders();
124 
125   // Return a pointer to the first byte of the image's optional header.
126   const uint8_t* GetOptionalHeaderStart();
127   size_t GetOptionalHeaderSize();
128 
129   // Returns the desired directory entry, or nullptr if |index| is out of
130   // bounds.
131   const IMAGE_DATA_DIRECTORY* GetDataDirectoryEntryAt(size_t index);
132 
133   // Returns the header for the section that contains the given address, or
134   // nullptr if the address is out of bounds or the image does not contain the
135   // section.
136   const IMAGE_SECTION_HEADER* FindSectionFromRva(uint32_t relative_address);
137 
138   // Returns a pointer to the |data_length| bytes referenced by the |index|'th
139   // data directory entry.
140   const uint8_t* GetImageData(size_t index, size_t* data_length);
141 
142   // Populates |structure| with a pointer to a desired structure of type T at
143   // the given offset if the image is sufficiently large to contain it. Returns
144   // false if the structure does not fully fit within the image at the given
145   // offset.
146   template <typename T>
GetStructureAt(size_t offset,const T ** structure)147   bool GetStructureAt(size_t offset, const T** structure) {
148     return GetStructureAt(offset, sizeof(**structure), structure);
149   }
150 
151   // Populates |structure| with a pointer to a desired structure of type T at
152   // the given offset if the image is sufficiently large to contain
153   // |structure_size| bytes. Returns false if the structure does not fully fit
154   // within the image at the given offset.
155   template <typename T>
GetStructureAt(size_t offset,size_t structure_size,const T ** structure)156   bool GetStructureAt(size_t offset,
157                       size_t structure_size,
158                       const T** structure) {
159     if (offset > image_size_)
160       return false;
161     if (structure_size > image_size_ - offset)
162       return false;
163     *structure = reinterpret_cast<const T*>(image_data_ + offset);
164     return true;
165   }
166 
167   const uint8_t* image_data_ = nullptr;
168   size_t image_size_ = 0;
169   uint32_t validation_state_ = 0;
170   std::unique_ptr<OptionalHeader> optional_header_;
171 };
172 
173 }  // namespace win
174 }  // namespace base
175 
176 #endif  // BASE_WIN_PE_IMAGE_READER_H_
177