• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 Renato Tegon Forti, Antony Polukhin.
2 // Copyright 2015-2020 Antony Polukhin.
3 //
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE_1_0.txt
6 // or copy at http://www.boost.org/LICENSE_1_0.txt)
7 
8 #ifndef BOOST_DLL_DETAIL_WINDOWS_PE_INFO_HPP
9 #define BOOST_DLL_DETAIL_WINDOWS_PE_INFO_HPP
10 
11 #include <boost/dll/config.hpp>
12 
13 #ifdef BOOST_HAS_PRAGMA_ONCE
14 # pragma once
15 #endif
16 
17 #include <cstring>
18 #include <fstream>
19 #include <string> // for std::getline
20 
21 #include <boost/assert.hpp>
22 #include <boost/cstdint.hpp>
23 
24 namespace boost { namespace dll { namespace detail {
25 
26 // reference:
27 // http://www.joachim-bauch.de/tutorials/loading-a-dll-from-memory/
28 // http://msdn.microsoft.com/en-us/magazine/ms809762.aspx
29 // http://msdn.microsoft.com/en-us/magazine/cc301808.aspx
30 //
31 
32 // Basic Windows typedefs. We can not use <boost/winapi/basic_types.hpp> header
33 // because that header must be included only on Windows platform
34 typedef unsigned char BYTE_;
35 typedef unsigned short WORD_;
36 typedef boost::uint32_t DWORD_;
37 typedef boost::int32_t LONG_;
38 typedef boost::uint32_t ULONG_;
39 typedef boost::int64_t LONGLONG_;
40 typedef boost::uint64_t ULONGLONG_;
41 
42 struct IMAGE_DOS_HEADER_ { // 32/64 independent header
43     boost::dll::detail::WORD_   e_magic;        // Magic number
44     boost::dll::detail::WORD_   e_cblp;         // Bytes on last page of file
45     boost::dll::detail::WORD_   e_cp;           // Pages in file
46     boost::dll::detail::WORD_   e_crlc;         // Relocations
47     boost::dll::detail::WORD_   e_cparhdr;      // Size of header in paragraphs
48     boost::dll::detail::WORD_   e_minalloc;     // Minimum extra paragraphs needed
49     boost::dll::detail::WORD_   e_maxalloc;     // Maximum extra paragraphs needed
50     boost::dll::detail::WORD_   e_ss;           // Initial (relative) SS value
51     boost::dll::detail::WORD_   e_sp;           // Initial SP value
52     boost::dll::detail::WORD_   e_csum;         // Checksum
53     boost::dll::detail::WORD_   e_ip;           // Initial IP value
54     boost::dll::detail::WORD_   e_cs;           // Initial (relative) CS value
55     boost::dll::detail::WORD_   e_lfarlc;       // File address of relocation table
56     boost::dll::detail::WORD_   e_ovno;         // Overlay number
57     boost::dll::detail::WORD_   e_res[4];       // Reserved words
58     boost::dll::detail::WORD_   e_oemid;        // OEM identifier (for e_oeminfo)
59     boost::dll::detail::WORD_   e_oeminfo;      // OEM information; e_oemid specific
60     boost::dll::detail::WORD_   e_res2[10];     // Reserved words
61     boost::dll::detail::LONG_   e_lfanew;       // File address of new exe header
62 };
63 
64 struct IMAGE_FILE_HEADER_ { // 32/64 independent header
65     boost::dll::detail::WORD_   Machine;
66     boost::dll::detail::WORD_   NumberOfSections;
67     boost::dll::detail::DWORD_  TimeDateStamp;
68     boost::dll::detail::DWORD_  PointerToSymbolTable;
69     boost::dll::detail::DWORD_  NumberOfSymbols;
70     boost::dll::detail::WORD_   SizeOfOptionalHeader;
71     boost::dll::detail::WORD_   Characteristics;
72 };
73 
74 struct IMAGE_DATA_DIRECTORY_ { // 32/64 independent header
75     boost::dll::detail::DWORD_  VirtualAddress;
76     boost::dll::detail::DWORD_  Size;
77 };
78 
79 struct IMAGE_EXPORT_DIRECTORY_ { // 32/64 independent header
80     boost::dll::detail::DWORD_  Characteristics;
81     boost::dll::detail::DWORD_  TimeDateStamp;
82     boost::dll::detail::WORD_   MajorVersion;
83     boost::dll::detail::WORD_   MinorVersion;
84     boost::dll::detail::DWORD_  Name;
85     boost::dll::detail::DWORD_  Base;
86     boost::dll::detail::DWORD_  NumberOfFunctions;
87     boost::dll::detail::DWORD_  NumberOfNames;
88     boost::dll::detail::DWORD_  AddressOfFunctions;
89     boost::dll::detail::DWORD_  AddressOfNames;
90     boost::dll::detail::DWORD_  AddressOfNameOrdinals;
91 };
92 
93 struct IMAGE_SECTION_HEADER_ { // 32/64 independent header
94     static const std::size_t    IMAGE_SIZEOF_SHORT_NAME_ = 8;
95 
96     boost::dll::detail::BYTE_   Name[IMAGE_SIZEOF_SHORT_NAME_];
97     union {
98         boost::dll::detail::DWORD_   PhysicalAddress;
99         boost::dll::detail::DWORD_   VirtualSize;
100     } Misc;
101     boost::dll::detail::DWORD_  VirtualAddress;
102     boost::dll::detail::DWORD_  SizeOfRawData;
103     boost::dll::detail::DWORD_  PointerToRawData;
104     boost::dll::detail::DWORD_  PointerToRelocations;
105     boost::dll::detail::DWORD_  PointerToLinenumbers;
106     boost::dll::detail::WORD_   NumberOfRelocations;
107     boost::dll::detail::WORD_   NumberOfLinenumbers;
108     boost::dll::detail::DWORD_  Characteristics;
109 };
110 
111 
112 template <class AddressOffsetT>
113 struct IMAGE_OPTIONAL_HEADER_template {
114     static const std::size_t IMAGE_NUMBEROF_DIRECTORY_ENTRIES_ = 16;
115 
116     boost::dll::detail::WORD_   Magic;
117     boost::dll::detail::BYTE_   MajorLinkerVersion;
118     boost::dll::detail::BYTE_   MinorLinkerVersion;
119     boost::dll::detail::DWORD_  SizeOfCode;
120     boost::dll::detail::DWORD_  SizeOfInitializedData;
121     boost::dll::detail::DWORD_  SizeOfUninitializedData;
122     boost::dll::detail::DWORD_  AddressOfEntryPoint;
123     union {
124         boost::dll::detail::DWORD_   BaseOfCode;
125         unsigned char padding_[sizeof(AddressOffsetT) == 8 ? 4 : 8]; // in x64 version BaseOfData does not exist
126     } BaseOfCode_and_BaseOfData;
127 
128     AddressOffsetT              ImageBase;
129     boost::dll::detail::DWORD_  SectionAlignment;
130     boost::dll::detail::DWORD_  FileAlignment;
131     boost::dll::detail::WORD_   MajorOperatingSystemVersion;
132     boost::dll::detail::WORD_   MinorOperatingSystemVersion;
133     boost::dll::detail::WORD_   MajorImageVersion;
134     boost::dll::detail::WORD_   MinorImageVersion;
135     boost::dll::detail::WORD_   MajorSubsystemVersion;
136     boost::dll::detail::WORD_   MinorSubsystemVersion;
137     boost::dll::detail::DWORD_  Win32VersionValue;
138     boost::dll::detail::DWORD_  SizeOfImage;
139     boost::dll::detail::DWORD_  SizeOfHeaders;
140     boost::dll::detail::DWORD_  CheckSum;
141     boost::dll::detail::WORD_   Subsystem;
142     boost::dll::detail::WORD_   DllCharacteristics;
143     AddressOffsetT              SizeOfStackReserve;
144     AddressOffsetT              SizeOfStackCommit;
145     AddressOffsetT              SizeOfHeapReserve;
146     AddressOffsetT              SizeOfHeapCommit;
147     boost::dll::detail::DWORD_  LoaderFlags;
148     boost::dll::detail::DWORD_  NumberOfRvaAndSizes;
149     IMAGE_DATA_DIRECTORY_       DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES_];
150 };
151 
152 typedef IMAGE_OPTIONAL_HEADER_template<boost::dll::detail::DWORD_>      IMAGE_OPTIONAL_HEADER32_;
153 typedef IMAGE_OPTIONAL_HEADER_template<boost::dll::detail::ULONGLONG_>  IMAGE_OPTIONAL_HEADER64_;
154 
155 template <class AddressOffsetT>
156 struct IMAGE_NT_HEADERS_template {
157     boost::dll::detail::DWORD_                      Signature;
158     IMAGE_FILE_HEADER_                              FileHeader;
159     IMAGE_OPTIONAL_HEADER_template<AddressOffsetT>  OptionalHeader;
160 };
161 
162 typedef IMAGE_NT_HEADERS_template<boost::dll::detail::DWORD_>      IMAGE_NT_HEADERS32_;
163 typedef IMAGE_NT_HEADERS_template<boost::dll::detail::ULONGLONG_>  IMAGE_NT_HEADERS64_;
164 
165 
166 template <class AddressOffsetT>
167 class pe_info {
168     typedef IMAGE_NT_HEADERS_template<AddressOffsetT>   header_t;
169     typedef IMAGE_EXPORT_DIRECTORY_                     exports_t;
170     typedef IMAGE_SECTION_HEADER_                       section_t;
171     typedef IMAGE_DOS_HEADER_                           dos_t;
172 
173     template <class T>
read_raw(std::ifstream & fs,T & value,std::size_t size=sizeof (T))174     static void read_raw(std::ifstream& fs, T& value, std::size_t size = sizeof(T)) {
175         fs.read(reinterpret_cast<char*>(&value), size);
176     }
177 
178 public:
parsing_supported(std::ifstream & fs)179     static bool parsing_supported(std::ifstream& fs) {
180         dos_t dos;
181         fs.seekg(0);
182         fs.read(reinterpret_cast<char*>(&dos), sizeof(dos));
183 
184         // 'MZ' and 'ZM' according to Wikipedia
185         if (dos.e_magic != 0x4D5A && dos.e_magic != 0x5A4D) {
186             return false;
187         }
188 
189         header_t h;
190         fs.seekg(dos.e_lfanew);
191         fs.read(reinterpret_cast<char*>(&h), sizeof(h));
192 
193         return h.Signature == 0x00004550 // 'PE00'
194                 && h.OptionalHeader.Magic == (sizeof(boost::uint32_t) == sizeof(AddressOffsetT) ? 0x10B : 0x20B);
195     }
196 
197 private:
header(std::ifstream & fs)198     static header_t header(std::ifstream& fs) {
199         header_t h;
200 
201         dos_t dos;
202         fs.seekg(0);
203         read_raw(fs, dos);
204 
205         fs.seekg(dos.e_lfanew);
206         read_raw(fs, h);
207 
208         return h;
209     }
210 
exports(std::ifstream & fs,const header_t & h)211     static exports_t exports(std::ifstream& fs, const header_t& h) {
212         static const unsigned int IMAGE_DIRECTORY_ENTRY_EXPORT_ = 0;
213         const std::size_t exp_virtual_address = h.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT_].VirtualAddress;
214         exports_t exports;
215 
216         if (exp_virtual_address == 0) {
217             // The virtual address can be 0 in case there are no exported symbols
218             std::memset(&exports, 0, sizeof(exports));
219             return exports;
220         }
221 
222         const std::size_t real_offset = get_file_offset(fs, exp_virtual_address, h);
223         BOOST_ASSERT(real_offset);
224 
225         fs.seekg(real_offset);
226         read_raw(fs, exports);
227 
228         return exports;
229     }
230 
get_file_offset(std::ifstream & fs,std::size_t virtual_address,const header_t & h)231     static std::size_t get_file_offset(std::ifstream& fs, std::size_t virtual_address, const header_t& h) {
232         BOOST_ASSERT(virtual_address);
233 
234         section_t image_section_header;
235 
236         {   // fs.seekg to the beginning on section headers
237             dos_t dos;
238             fs.seekg(0);
239             read_raw(fs, dos);
240             fs.seekg(dos.e_lfanew + sizeof(header_t));
241         }
242 
243         for (std::size_t i = 0;i < h.FileHeader.NumberOfSections;++i) {
244             read_raw(fs, image_section_header);
245             if (virtual_address >= image_section_header.VirtualAddress
246                 && virtual_address < image_section_header.VirtualAddress + image_section_header.SizeOfRawData)
247             {
248                 return image_section_header.PointerToRawData + virtual_address - image_section_header.VirtualAddress;
249             }
250         }
251 
252         return 0;
253     }
254 
255 public:
sections(std::ifstream & fs)256     static std::vector<std::string> sections(std::ifstream& fs) {
257         std::vector<std::string> ret;
258 
259         const header_t h = header(fs);
260         ret.reserve(h.FileHeader.NumberOfSections);
261 
262         // get names, e.g: .text .rdata .data .rsrc .reloc
263         section_t image_section_header;
264         char name_helper[section_t::IMAGE_SIZEOF_SHORT_NAME_ + 1];
265         std::memset(name_helper, 0, sizeof(name_helper));
266         for (std::size_t i = 0;i < h.FileHeader.NumberOfSections;++i) {
267             // There is no terminating null character if the string is exactly eight characters long
268             read_raw(fs, image_section_header);
269             std::memcpy(name_helper, image_section_header.Name, section_t::IMAGE_SIZEOF_SHORT_NAME_);
270 
271             if (name_helper[0] != '/') {
272                 ret.push_back(name_helper);
273             } else {
274                 // For longer names, image_section_header.Name contains a slash (/) followed by ASCII representation of a decimal number.
275                 // this number is an offset into the string table.
276                 // TODO: fixme
277                 ret.push_back(name_helper);
278             }
279         }
280 
281         return ret;
282     }
283 
symbols(std::ifstream & fs)284     static std::vector<std::string> symbols(std::ifstream& fs) {
285         std::vector<std::string> ret;
286 
287         const header_t h = header(fs);
288         const exports_t exprt = exports(fs, h);
289         const std::size_t exported_symbols = exprt.NumberOfNames;
290 
291         if (exported_symbols == 0) {
292             return ret;
293         }
294 
295         const std::size_t fixed_names_addr = get_file_offset(fs, exprt.AddressOfNames, h);
296 
297         ret.reserve(exported_symbols);
298         boost::dll::detail::DWORD_ name_offset;
299         std::string symbol_name;
300         for (std::size_t i = 0;i < exported_symbols;++i) {
301             fs.seekg(fixed_names_addr + i * sizeof(name_offset));
302             read_raw(fs, name_offset);
303             fs.seekg(get_file_offset(fs, name_offset, h));
304             std::getline(fs, symbol_name, '\0');
305             ret.push_back(symbol_name);
306         }
307 
308         return ret;
309     }
310 
symbols(std::ifstream & fs,const char * section_name)311     static std::vector<std::string> symbols(std::ifstream& fs, const char* section_name) {
312         std::vector<std::string> ret;
313 
314         const header_t h = header(fs);
315 
316         std::size_t section_begin_addr = 0;
317         std::size_t section_end_addr = 0;
318 
319         {   // getting address range for the section
320             section_t image_section_header;
321             char name_helper[section_t::IMAGE_SIZEOF_SHORT_NAME_ + 1];
322             std::memset(name_helper, 0, sizeof(name_helper));
323             for (std::size_t i = 0;i < h.FileHeader.NumberOfSections;++i) {
324                 // There is no terminating null character if the string is exactly eight characters long
325                 read_raw(fs, image_section_header);
326                 std::memcpy(name_helper, image_section_header.Name, section_t::IMAGE_SIZEOF_SHORT_NAME_);
327                 if (!std::strcmp(section_name, name_helper)) {
328                     section_begin_addr = image_section_header.PointerToRawData;
329                     section_end_addr = section_begin_addr + image_section_header.SizeOfRawData;
330                 }
331             }
332 
333             // returning empty result if section was not found
334             if(section_begin_addr == 0 || section_end_addr == 0)
335                 return ret;
336         }
337 
338         const exports_t exprt = exports(fs, h);
339         const std::size_t exported_symbols = exprt.NumberOfFunctions;
340         const std::size_t fixed_names_addr = get_file_offset(fs, exprt.AddressOfNames, h);
341         const std::size_t fixed_ordinals_addr = get_file_offset(fs, exprt.AddressOfNameOrdinals, h);
342         const std::size_t fixed_functions_addr = get_file_offset(fs, exprt.AddressOfFunctions, h);
343 
344         ret.reserve(exported_symbols);
345         boost::dll::detail::DWORD_ ptr;
346         boost::dll::detail::WORD_ ordinal;
347         std::string symbol_name;
348         for (std::size_t i = 0;i < exported_symbols;++i) {
349             // getting ordinal
350             fs.seekg(fixed_ordinals_addr + i * sizeof(ordinal));
351             read_raw(fs, ordinal);
352 
353             // getting function addr
354             fs.seekg(fixed_functions_addr + ordinal * sizeof(ptr));
355             read_raw(fs, ptr);
356             ptr = static_cast<boost::dll::detail::DWORD_>( get_file_offset(fs, ptr, h) );
357 
358             if (ptr >= section_end_addr || ptr < section_begin_addr) {
359                 continue;
360             }
361 
362             fs.seekg(fixed_names_addr + i * sizeof(ptr));
363             read_raw(fs, ptr);
364             fs.seekg(get_file_offset(fs, ptr, h));
365             std::getline(fs, symbol_name, '\0');
366             ret.push_back(symbol_name);
367         }
368 
369         return ret;
370     }
371 
372     // a test method to get dependents modules,
373     // who my plugin imports (1st level only)
374     /*
375     e.g. for myself I get:
376       KERNEL32.dll
377       MSVCP110D.dll
378       boost_system-vc-mt-gd-1_56.dll
379       MSVCR110D.dll
380     */
381     /*
382     static std::vector<std::string> depend_of(boost::dll::fs::error_code &ec) BOOST_NOEXCEPT {
383         std::vector<std::string> ret;
384 
385         IMAGE_DOS_HEADER* image_dos_header = (IMAGE_DOS_HEADER*)native();
386         if(!image_dos_header) {
387             // ERROR_BAD_EXE_FORMAT
388             ec = boost::dll::fs::make_error_code(
389                  boost::dll::fs::errc::executable_format_error
390                  );
391 
392             return ret;
393         }
394 
395         IMAGE_OPTIONAL_HEADER* image_optional_header = (IMAGE_OPTIONAL_HEADER*)((boost::dll::detail::BYTE_*)native() + image_dos_header->e_lfanew + 24);
396         if(!image_optional_header) {
397             // ERROR_BAD_EXE_FORMAT
398             ec = boost::dll::fs::make_error_code(
399                  boost::dll::fs::errc::executable_format_error
400                  );
401 
402             return ret;
403         }
404 
405         IMAGE_IMPORT_DESCRIPTOR* image_import_descriptor =  (IMAGE_IMPORT_DESCRIPTOR*)((boost::dll::detail::BYTE_*)native() + image_optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
406         if(!image_import_descriptor) {
407             // ERROR_BAD_EXE_FORMAT
408             ec = boost::dll::fs::make_error_code(
409                  boost::dll::fs::errc::executable_format_error
410                  );
411 
412             return ret;
413         }
414 
415         while(image_import_descriptor->FirstThunk) {
416            std::string module_name = reinterpret_cast<char*>((boost::dll::detail::BYTE_*)native() + image_import_descriptor->Name);
417 
418            if(module_name.size()) {
419               ret.push_back(module_name);
420            }
421 
422            image_import_descriptor++;
423         }
424 
425         return ret;
426     }
427 */
428 };
429 
430 typedef pe_info<boost::dll::detail::DWORD_>      pe_info32;
431 typedef pe_info<boost::dll::detail::ULONGLONG_>  pe_info64;
432 
433 }}} // namespace boost::dll::detail
434 
435 #endif // BOOST_DLL_DETAIL_WINDOWS_PE_INFO_HPP
436