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