• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2010 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 // This file was adapted from GreenBorder's Code.
6 // To understand what this class is about (for other than well known functions
7 // as GetProcAddress), a good starting point is "An In-Depth Look into the
8 // Win32 Portable Executable File Format" by Matt Pietrek:
9 // http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx
10 
11 #ifndef BASE_WIN_PE_IMAGE_H_
12 #define BASE_WIN_PE_IMAGE_H_
13 
14 #include <windows.h>
15 
16 #include <stdint.h>
17 
18 #include <DelayIMP.h>
19 
20 namespace base {
21 namespace win {
22 
23 // This class is a wrapper for the Portable Executable File Format (PE).
24 // Its main purpose is to provide an easy way to work with imports and exports
25 // from a file, mapped in memory as image. A PEImage object is constructed from
26 // a loaded PE file by passing the HMODULE to the constructor. Loading a PE file
27 // as an image will execute code and should only be done with trusted images.
28 // Parsing of untrusted PE files is better done with PeImageReader.
29 // PEImage can only parse PE files that match the bitness of the process.
30 // See also PEImageAsData.
31 class PEImage {
32  public:
33   // Callback to enumerate sections.
34   // cookie is the value passed to the enumerate method.
35   // Returns true to continue the enumeration.
36   using EnumSectionsFunction =
37       bool (*)(const PEImage&, PIMAGE_SECTION_HEADER, PVOID, DWORD, PVOID);
38 
39   // Callback to enumerate exports.
40   // function is the actual address of the symbol. If forward is not null, it
41   // contains the dll and symbol to forward this export to. cookie is the value
42   // passed to the enumerate method.
43   // Returns true to continue the enumeration.
44   using EnumExportsFunction =
45       bool (*)(const PEImage&, DWORD, DWORD, LPCSTR, PVOID, LPCSTR, PVOID);
46 
47   // Callback to enumerate import blocks.
48   // name_table and iat point to the imports name table and address table for
49   // this block. cookie is the value passed to the enumerate method.
50   // Returns true to continue the enumeration.
51   using EnumImportChunksFunction = bool (*)(const PEImage&,
52                                             LPCSTR,
53                                             PIMAGE_THUNK_DATA,
54                                             PIMAGE_THUNK_DATA,
55                                             PVOID);
56 
57   // Callback to enumerate imports.
58   // module is the dll that exports this symbol. cookie is the value passed to
59   // the enumerate method.
60   // Returns true to continue the enumeration.
61   using EnumImportsFunction = bool (*)(const PEImage&,
62                                        LPCSTR,
63                                        DWORD,
64                                        LPCSTR,
65                                        DWORD,
66                                        PIMAGE_THUNK_DATA,
67                                        PVOID);
68 
69   // Callback to enumerate delayed import blocks.
70   // module is the dll that exports this block of symbols. cookie is the value
71   // passed to the enumerate method.
72   // Returns true to continue the enumeration.
73   using EnumDelayImportChunksFunction = bool (*)(const PEImage&,
74                                                  PImgDelayDescr,
75                                                  LPCSTR,
76                                                  PIMAGE_THUNK_DATA,
77                                                  PIMAGE_THUNK_DATA,
78                                                  PVOID);
79 
80   // Callback to enumerate relocations.
81   // cookie is the value passed to the enumerate method.
82   // Returns true to continue the enumeration.
83   using EnumRelocsFunction = bool (*)(const PEImage&, WORD, PVOID, PVOID);
84 
PEImage(HMODULE module)85   explicit PEImage(HMODULE module) : module_(module) {}
PEImage(const void * module)86   explicit PEImage(const void* module) {
87     module_ = reinterpret_cast<HMODULE>(const_cast<void*>(module));
88   }
89 
90   virtual ~PEImage() = default;
91 
92   // Gets the HMODULE for this object.
93   HMODULE module() const;
94 
95   // Sets this object's HMODULE.
96   void set_module(HMODULE module);
97 
98   // Checks if this symbol is actually an ordinal.
99   static bool IsOrdinal(LPCSTR name);
100 
101   // Converts a named symbol to the corresponding ordinal.
102   static WORD ToOrdinal(LPCSTR name);
103 
104   // Returns the DOS_HEADER for this PE.
105   PIMAGE_DOS_HEADER GetDosHeader() const;
106 
107   // Returns the NT_HEADER for this PE.
108   PIMAGE_NT_HEADERS GetNTHeaders() const;
109 
110   // Returns number of sections of this PE.
111   WORD GetNumSections() const;
112 
113   // Returns the header for a given section.
114   // returns NULL if there is no such section.
115   PIMAGE_SECTION_HEADER GetSectionHeader(WORD section) const;
116 
117   // Returns the size of a given directory entry or 0 if |directory| is out of
118   // bounds.
119   DWORD GetImageDirectoryEntrySize(UINT directory) const;
120 
121   // Returns the address of a given directory entry or NULL if |directory| is
122   // out of bounds.
123   PVOID GetImageDirectoryEntryAddr(UINT directory) const;
124 
125   // Returns the section header for a given address.
126   // Use: s = image.GetImageSectionFromAddr(a);
127   // Post: 's' is the section header of the section that contains 'a'
128   //       or NULL if there is no such section.
129   PIMAGE_SECTION_HEADER GetImageSectionFromAddr(PVOID address) const;
130 
131   // Returns the section header for a given section.
132   PIMAGE_SECTION_HEADER GetImageSectionHeaderByName(LPCSTR section_name) const;
133 
134   // Returns the first block of imports.
135   PIMAGE_IMPORT_DESCRIPTOR GetFirstImportChunk() const;
136 
137   // Returns the exports directory.
138   PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const;
139 
140   // Retrieves the contents of the image's CodeView debug entry, returning true
141   // if such an entry is found and is within a section mapped into the current
142   // process's memory. |guid|, |age|, and |pdb_filename| are each optional and
143   // may be NULL. |pdb_filename_length| is mandatory if |pdb_filename| is not
144   // NULL, as the latter is populated with a direct reference to a string in the
145   // image that is is not guaranteed to be terminated (note: informal
146   // documentation indicates that it should be terminated, but the data is
147   // untrusted). Furthermore, owing to its nature of being a string in the
148   // image, it is only valid while the image is mapped into the process, and the
149   // caller is not responsible for freeing it. |pdb_filename_length| is
150   // populated with the string length of |pdb_filename| (not including a
151   // terminator) and must be used rather than relying on |pdb_filename| being
152   // properly terminated.
153   bool GetDebugId(LPGUID guid,
154                   LPDWORD age,
155                   LPCSTR* pdb_filename,
156                   size_t* pdb_filename_length) const;
157 
158   // Returns a given export entry.
159   // Use: e = image.GetExportEntry(f);
160   // Pre: 'f' is either a zero terminated string or ordinal
161   // Post: 'e' is a pointer to the export directory entry
162   //       that contains 'f's export RVA, or NULL if 'f'
163   //       is not exported from this image
164   PDWORD GetExportEntry(LPCSTR name) const;
165 
166   // Returns the address for a given exported symbol.
167   // Use: p = image.GetProcAddress(f);
168   // Pre: 'f' is either a zero terminated string or ordinal.
169   // Post: if 'f' is a non-forwarded export from image, 'p' is
170   //       the exported function. If 'f' is a forwarded export
171   //       then p is the special value -1. In this case
172   //       RVAToAddr(*GetExportEntry) can be used to resolve
173   //       the string that describes the forward.
174   FARPROC GetProcAddress(LPCSTR function_name) const;
175 
176   // Retrieves the ordinal for a given exported symbol.
177   // Returns true if the symbol was found.
178   bool GetProcOrdinal(LPCSTR function_name, WORD* ordinal) const;
179 
180   // Enumerates PE sections.
181   // cookie is a generic cookie to pass to the callback.
182   // Returns true on success.
183   bool EnumSections(EnumSectionsFunction callback, PVOID cookie) const;
184 
185   // Enumerates PE exports.
186   // cookie is a generic cookie to pass to the callback.
187   // Returns true on success.
188   bool EnumExports(EnumExportsFunction callback, PVOID cookie) const;
189 
190   // Enumerates PE imports.
191   // cookie is a generic cookie to pass to the callback.
192   // Returns true on success.
193   // Use |target_module_name| to ensure the callback is only invoked for the
194   // specified module.
195   bool EnumAllImports(EnumImportsFunction callback,
196                       PVOID cookie,
197                       LPCSTR target_module_name) const;
198 
199   // Enumerates PE import blocks.
200   // cookie is a generic cookie to pass to the callback.
201   // Returns true on success.
202   // Use |target_module_name| to ensure the callback is only invoked for the
203   // specified module.
204   bool EnumImportChunks(EnumImportChunksFunction callback,
205                         PVOID cookie,
206                         LPCSTR target_module_name) const;
207 
208   // Enumerates the imports from a single PE import block.
209   // cookie is a generic cookie to pass to the callback.
210   // Returns true on success.
211   bool EnumOneImportChunk(EnumImportsFunction callback,
212                           LPCSTR module_name,
213                           PIMAGE_THUNK_DATA name_table,
214                           PIMAGE_THUNK_DATA iat,
215                           PVOID cookie) const;
216 
217   // Enumerates PE delay imports.
218   // cookie is a generic cookie to pass to the callback.
219   // Returns true on success.
220   // Use |target_module_name| to ensure the callback is only invoked for the
221   // specified module. If this parameter is non-null then all delayloaded
222   // imports are resolved when the target module is found.
223   bool EnumAllDelayImports(EnumImportsFunction callback,
224                            PVOID cookie,
225                            LPCSTR target_module_name) const;
226 
227   // Enumerates PE delay import blocks.
228   // cookie is a generic cookie to pass to the callback.
229   // Returns true on success.
230   // Use |target_module_name| to ensure the callback is only invoked for the
231   // specified module. If this parameter is non-null then all delayloaded
232   // imports are resolved when the target module is found.
233   bool EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
234                              PVOID cookie,
235                              LPCSTR target_module_name) const;
236 
237   // Enumerates imports from a single PE delay import block.
238   // cookie is a generic cookie to pass to the callback.
239   // Returns true on success.
240   bool EnumOneDelayImportChunk(EnumImportsFunction callback,
241                                PImgDelayDescr delay_descriptor,
242                                LPCSTR module_name,
243                                PIMAGE_THUNK_DATA name_table,
244                                PIMAGE_THUNK_DATA iat,
245                                PVOID cookie) const;
246 
247   // Enumerates PE relocation entries.
248   // cookie is a generic cookie to pass to the callback.
249   // Returns true on success.
250   bool EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const;
251 
252   // Verifies the magic values on the PE file.
253   // Returns true if all values are correct.
254   bool VerifyMagic() const;
255 
256   // Converts an rva value to the appropriate address.
257   virtual PVOID RVAToAddr(uintptr_t rva) const;
258 
259   // Converts an rva value to an offset on disk.
260   // Returns true on success.
261   bool ImageRVAToOnDiskOffset(uintptr_t rva, DWORD* on_disk_offset) const;
262 
263   // Converts an address to an offset on disk.
264   // Returns true on success.
265   bool ImageAddrToOnDiskOffset(LPVOID address, DWORD* on_disk_offset) const;
266 
267  private:
268   // Returns a pointer to a data directory, or NULL if |directory| is out of
269   // range.
270   const IMAGE_DATA_DIRECTORY* GetDataDirectory(UINT directory) const;
271 
272   HMODULE module_;
273 };
274 
275 // This class is an extension to the PEImage class that allows working with PE
276 // files mapped as data instead of as image file.
277 class PEImageAsData : public PEImage {
278  public:
PEImageAsData(HMODULE hModule)279   explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {}
280 
281   PVOID RVAToAddr(uintptr_t rva) const override;
282 };
283 
IsOrdinal(LPCSTR name)284 inline bool PEImage::IsOrdinal(LPCSTR name) {
285   return reinterpret_cast<uintptr_t>(name) <= 0xFFFF;
286 }
287 
ToOrdinal(LPCSTR name)288 inline WORD PEImage::ToOrdinal(LPCSTR name) {
289   return static_cast<WORD>(reinterpret_cast<intptr_t>(name));
290 }
291 
module()292 inline HMODULE PEImage::module() const {
293   return module_;
294 }
295 
GetFirstImportChunk()296 inline PIMAGE_IMPORT_DESCRIPTOR PEImage::GetFirstImportChunk() const {
297   return reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
298       GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_IMPORT));
299 }
300 
GetExportDirectory()301 inline PIMAGE_EXPORT_DIRECTORY PEImage::GetExportDirectory() const {
302   return reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
303       GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
304 }
305 
306 }  // namespace win
307 }  // namespace base
308 
309 #endif  // BASE_WIN_PE_IMAGE_H_
310