└── pepp ├── ExportDirectory.cpp ├── ExportDirectory.hpp ├── FileHeader.hpp ├── Image.cpp ├── Image.hpp ├── ImportDirectory.cpp ├── ImportDirectory.hpp ├── OptionalHeader.cpp ├── OptionalHeader.hpp ├── PEHeader.cpp ├── PEHeader.hpp ├── PELibrary.hpp ├── PEUtil.cpp ├── PEUtil.hpp ├── RelocationDirectory.cpp ├── RelocationDirectory.hpp ├── SectionHeader.cpp ├── SectionHeader.hpp └── misc ├── Address.hpp ├── ByteVector.hpp ├── Concept.hpp ├── File.cpp ├── File.hpp └── NonCopyable.hpp /pepp/ExportDirectory.cpp: -------------------------------------------------------------------------------- 1 | #include "PELibrary.hpp" 2 | 3 | using namespace pepp; 4 | 5 | template class ExportDirectory<32>; 6 | template class ExportDirectory<64>; 7 | 8 | template 9 | ExportData_t ExportDirectory::getExport(std::uint32_t idx, bool demangle /*= true*/) const 10 | { 11 | if (!isPresent()) 12 | return {}; 13 | 14 | if (idx < getNumberOfNames()) { 15 | std::uint8_t* base{}; 16 | std::uint32_t funcAddresses{}; 17 | std::uint32_t funcNames{}; 18 | std::uint32_t funcOrdinals{}; 19 | std::uint32_t funcNamesOffset{}; 20 | mem::ByteVector const* buffer{}; 21 | 22 | funcOrdinals = m_image->getPEHdr().rvaToOffset(getAddressOfNameOrdinals()); 23 | uint16_t rlIdx = m_image->buffer().deref(funcOrdinals + (idx * sizeof uint16_t)); 24 | 25 | funcAddresses = m_image->getPEHdr().rvaToOffset(getAddressOfFunctions() + sizeof(std::uint32_t) * rlIdx); 26 | funcNames = m_image->getPEHdr().rvaToOffset(getAddressOfNames() + sizeof(std::uint32_t) * idx); 27 | funcNamesOffset = m_image->getPEHdr().rvaToOffset(m_image->buffer().deref(funcNames)); 28 | 29 | 30 | if (funcAddresses && funcNames && funcOrdinals) 31 | { 32 | return 33 | { 34 | demangle ? DemangleName(m_image->buffer().as(funcNamesOffset)) : m_image->buffer().as(funcNamesOffset), 35 | m_image->buffer().deref(funcAddresses), 36 | m_base->Base + idx, 37 | rlIdx 38 | }; 39 | } 40 | } 41 | 42 | return {}; 43 | } 44 | 45 | template 46 | ExportData_t pepp::ExportDirectory::getExport(std::string_view name, bool demangle) const 47 | { 48 | for (int i = 0; i < getNumberOfNames(); i++) 49 | { 50 | ExportData_t data = getExport(i, demangle); 51 | if (data.name == name) 52 | return data; 53 | } 54 | 55 | return {}; 56 | } 57 | 58 | template 59 | void ExportDirectory::traverseExports(const std::function& cb_func, bool demangle) 60 | { 61 | for (int i = 0; i < getNumberOfNames(); i++) 62 | { 63 | ExportData_t data = getExport(i, demangle); 64 | if (data.rva != 0) 65 | cb_func(&data); 66 | } 67 | } 68 | 69 | template 70 | bool ExportDirectory::isPresent() const noexcept 71 | { 72 | return m_image->getPEHdr().getOptionalHdr().getDataDir(DIRECTORY_ENTRY_EXPORT).Size > 0; 73 | } 74 | 75 | template 76 | void ExportDirectory::add(std::string_view name, std::uint32_t rva) 77 | { 78 | // TODO 79 | } 80 | -------------------------------------------------------------------------------- /pepp/ExportDirectory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pepp 6 | { 7 | struct ExportData_t 8 | { 9 | std::string name{}; 10 | std::uint32_t rva = 0; 11 | std::uint32_t base_ordinal = 0xffffffff; 12 | std::uint32_t name_ordinal = 0xffffffff; 13 | }; 14 | 15 | template 16 | class ExportDirectory : public pepp::msc::NonCopyable 17 | { 18 | friend class Image<32>; 19 | friend class Image<64>; 20 | 21 | Image* m_image; 22 | detail::Image_t<>::ExportDirectory_t *m_base; 23 | public: 24 | ExportData_t getExport(std::uint32_t idx, bool demangle = true) const; 25 | ExportData_t getExport(std::string_view name, bool demangle = true) const; 26 | void add(std::string_view name, std::uint32_t rva); 27 | void traverseExports(const std::function& cb_func, bool demangle = true); 28 | bool isPresent() const noexcept; 29 | 30 | void setNumberOfFunctions(std::uint32_t num) { 31 | m_base->NumberOfFunctions = num; 32 | } 33 | 34 | std::uint32_t getNumberOfFunctions() const { 35 | return m_base->NumberOfFunctions; 36 | } 37 | 38 | void setNumberOfNames(std::uint32_t num) { 39 | m_base->NumberOfNames = num; 40 | } 41 | 42 | std::uint32_t getNumberOfNames() const { 43 | return m_base->NumberOfNames; 44 | } 45 | 46 | void setCharacteristics(std::uint32_t chrs) { 47 | m_base->Characteristics = chrs; 48 | } 49 | 50 | std::uint32_t getCharacteristics() const { 51 | return m_base->Characteristics; 52 | } 53 | 54 | void setTimeDateStamp(std::uint32_t TimeDateStamp) { 55 | m_base->TimeDateStamp = TimeDateStamp; 56 | } 57 | 58 | std::uint32_t getTimeDateStamp() const { 59 | return m_base->TimeDateStamp; 60 | } 61 | 62 | void setAddressOfFunctions(std::uint32_t AddressOfFunctions) { 63 | m_base->AddressOfFunctions = AddressOfFunctions; 64 | } 65 | 66 | std::uint32_t getAddressOfFunctions() const { 67 | return m_base->AddressOfFunctions; 68 | } 69 | 70 | void setAddressOfNames(std::uint32_t AddressOfNames) { 71 | m_base->AddressOfNames = AddressOfNames; 72 | } 73 | 74 | std::uint32_t getAddressOfNames() const { 75 | return m_base->AddressOfNames; 76 | } 77 | 78 | void setAddressOfNameOrdinals(std::uint32_t AddressOfNamesOrdinals) { 79 | m_base->AddressOfNameOrdinals = AddressOfNamesOrdinals; 80 | } 81 | 82 | std::uint32_t getAddressOfNameOrdinals() const { 83 | return m_base->AddressOfNameOrdinals; 84 | } 85 | 86 | 87 | constexpr std::size_t size() const { 88 | return sizeof(decltype(*m_base)); 89 | } 90 | 91 | private: 92 | //! Setup the directory 93 | void _setup(Image* image) { 94 | m_image = image; 95 | m_base = reinterpret_cast( 96 | &image->base()[image->getPEHdr().rvaToOffset( 97 | image->getPEHdr().getOptionalHdr().getDataDir(DIRECTORY_ENTRY_EXPORT).VirtualAddress)]); 98 | } 99 | }; 100 | } -------------------------------------------------------------------------------- /pepp/FileHeader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pepp 4 | { 5 | enum class PEMachine 6 | { 7 | MACHINE_I386 = 0x14c, 8 | MACHINE_IA64 = 0x200, 9 | MACHINE_AMD64 = 0x8664 10 | }; 11 | 12 | class FileHeader : pepp::msc::NonCopyable 13 | { 14 | friend class PEHeader<32>; 15 | friend class PEHeader<64>; 16 | 17 | IMAGE_FILE_HEADER* m_base; 18 | public: 19 | FileHeader() 20 | { 21 | } 22 | 23 | void setMachine(PEMachine machine) { 24 | m_base->Machine = static_cast(machine); 25 | } 26 | 27 | PEMachine getMachine() const { 28 | return static_cast(m_base->Machine); 29 | } 30 | 31 | void setNumberOfSections(std::uint16_t numSections) { 32 | m_base->NumberOfSections = numSections; 33 | } 34 | 35 | std::uint16_t getNumberOfSections() const { 36 | return m_base->NumberOfSections; 37 | } 38 | 39 | void setTimeDateStamp(std::uint32_t dwTimeDateStamp) { 40 | m_base->TimeDateStamp = dwTimeDateStamp; 41 | } 42 | 43 | std::uint32_t getTimeDateStamp() const { 44 | return m_base->TimeDateStamp; 45 | } 46 | 47 | void setPointerToSymbolTable(std::uint32_t dwPointerToSymbolTable) { 48 | m_base->PointerToSymbolTable = dwPointerToSymbolTable; 49 | } 50 | 51 | std::uint32_t setPointerToSymbolTable() const { 52 | return m_base->PointerToSymbolTable; 53 | } 54 | 55 | void setNumberOfSymbols(std::uint32_t numSymbols) { 56 | m_base->NumberOfSymbols = numSymbols; 57 | } 58 | 59 | std::uint32_t getNumberOfSymbols() const { 60 | return m_base->NumberOfSymbols; 61 | } 62 | 63 | void setSizeOfOptionalHeader(std::uint16_t size) { 64 | m_base->SizeOfOptionalHeader = size; 65 | } 66 | 67 | std::uint16_t getSizeOfOptionalHeader() const { 68 | return m_base->SizeOfOptionalHeader; 69 | } 70 | 71 | void setCharacteristics(std::uint16_t chars) { 72 | m_base->Characteristics = chars; 73 | } 74 | 75 | std::uint16_t getCharacteristics() const { 76 | return m_base->Characteristics; 77 | } 78 | 79 | IMAGE_FILE_HEADER* native() const { 80 | return m_base; 81 | } 82 | private: 83 | template 84 | void _setup(Image* image) { 85 | m_base = &image->getPEHdr().native()->FileHeader; 86 | } 87 | }; 88 | } -------------------------------------------------------------------------------- /pepp/Image.cpp: -------------------------------------------------------------------------------- 1 | #include "PELibrary.hpp" 2 | #include "PEUtil.hpp" 3 | #include 4 | 5 | using namespace pepp; 6 | 7 | // Explicit templates. 8 | template class Image<32>; 9 | template class Image<64>; 10 | 11 | template 12 | Image::Image() 13 | : m_isParsed(false) 14 | { 15 | } 16 | 17 | template 18 | Image::Image(const Image& rhs) 19 | : m_fileName(rhs.m_fileName) 20 | //, m_imageBuffer(std::move(rhs.m_imageBuffer)) -- bad 21 | , m_imageBuffer(rhs.m_imageBuffer) 22 | , m_isParsed(false) 23 | { 24 | // Ensure that the file was read. 25 | assert(m_imageBuffer.size() > 0); 26 | 27 | // Validate there is a valid MZ signature. 28 | _validate(); 29 | } 30 | 31 | template 32 | constexpr PEMachine Image::getMachine() const 33 | { 34 | if constexpr (bitsize == 32) 35 | return PEMachine::MACHINE_I386; 36 | 37 | return PEMachine::MACHINE_AMD64; 38 | } 39 | 40 | 41 | template 42 | Image::Image(std::string_view filepath) 43 | : m_fileName(filepath) 44 | , m_isParsed(false) 45 | { 46 | io::File file(m_fileName, io::kFileInput | io::kFileBinary); 47 | 48 | std::vector data{ file.Read() }; 49 | 50 | m_imageBuffer.resize(data.size()); 51 | m_imageBuffer.copy_data(0, data.data(), data.size()); 52 | 53 | // Ensure that the file was read. 54 | assert(m_imageBuffer.size() > 0); 55 | 56 | // Validate there is a valid MZ signature. 57 | _validate(); 58 | } 59 | 60 | template 61 | Image::Image(const void* data, std::size_t size) 62 | : m_isParsed(false) 63 | { 64 | m_imageBuffer.resize(size); 65 | std::memcpy(&m_imageBuffer[0], data, size); 66 | 67 | // Validate there is a valid MZ signature. 68 | _validate(); 69 | } 70 | 71 | template 72 | void pepp::Image::setFromMemory(const void* data, std::size_t size) 73 | { 74 | m_imageBuffer.resize(size); 75 | std::memcpy(&m_imageBuffer[0], data, size); 76 | 77 | // Validate there is a valid MZ signature. 78 | _validate(); 79 | } 80 | 81 | template 82 | bool Image::setFromMappedMemory(void* data, std::size_t size) noexcept 83 | { 84 | m_MZHeader = reinterpret_cast::MZHeader_t*>(data); 85 | 86 | // Valid MZ tag? 87 | assert(magic() == IMAGE_DOS_SIGNATURE); 88 | 89 | // Setup the PE header data. 90 | m_PEHeader._setup(this); 91 | 92 | assert(m_PEHeader.isTaggedPE()); 93 | 94 | m_imageBuffer.resize(size); 95 | std::memcpy(&m_imageBuffer[0], data, m_imageBuffer.size()); 96 | 97 | // Okay just _validate now. 98 | _validate(); 99 | 100 | // It's runtime, so map it. 101 | setAsMapped(); 102 | _validate(); 103 | 104 | 105 | return true; 106 | } 107 | 108 | template 109 | bool pepp::Image::setFromFilePath(std::string_view file_path) 110 | { 111 | m_fileName = file_path; 112 | 113 | io::File file(m_fileName, io::kFileInput | io::kFileBinary); 114 | 115 | if (!file.Exists()) 116 | return false; 117 | 118 | std::vector data{ file.Read() }; 119 | 120 | m_imageBuffer.resize(data.size()); 121 | m_imageBuffer.copy_data(0, data.data(), data.size()); 122 | 123 | // Ensure that the file was read. 124 | assert(m_imageBuffer.size() > 0); 125 | 126 | // Validate there is a valid MZ signature. 127 | _validate(); 128 | 129 | return wasParsed(); 130 | } 131 | 132 | template 133 | bool Image::hasDataDirectory(PEDirectoryEntry entry) 134 | { 135 | return getPEHdr().getOptionalHdr().getDataDir(entry).Size > 0; 136 | } 137 | 138 | template 139 | void Image::writeToFile(std::string_view filepath) 140 | { 141 | io::File file(filepath, io::kFileOutput | io::kFileBinary); 142 | file.Write(m_imageBuffer); 143 | } 144 | 145 | template 146 | void Image::_validate() 147 | { 148 | m_MZHeader = reinterpret_cast::MZHeader_t*>(base()); 149 | 150 | // Valid MZ tag? 151 | if (magic() != IMAGE_DOS_SIGNATURE) 152 | return; 153 | 154 | // Setup the PE header data. 155 | m_PEHeader._setup(this); 156 | 157 | if (!m_PEHeader.isTaggedPE()) 158 | return; 159 | 160 | // Setup sections 161 | m_rawSectionHeaders = (m_PEHeader.m_PEHdr ? reinterpret_cast(IMAGE_FIRST_SECTION(m_PEHeader.m_PEHdr)) : nullptr); 162 | 163 | if (m_rawSectionHeaders == nullptr) 164 | return; 165 | 166 | // Ensure the Image class was constructed with the correct bitsize. 167 | if constexpr (bitsize == 32) 168 | { 169 | if (m_PEHeader.getOptionalHdr().getMagic() != PEMagic::HDR_32) 170 | return; 171 | } 172 | else if constexpr (bitsize == 64) 173 | { 174 | if (m_PEHeader.getOptionalHdr().getMagic() != PEMagic::HDR_64) 175 | return; 176 | } 177 | 178 | // Setup export directory 179 | m_exportDirectory._setup(this); 180 | 181 | // Setup import directory 182 | m_importDirectory._setup(this); 183 | 184 | // Setup reloc directory 185 | m_relocDirectory._setup(this); 186 | 187 | // We hit the end, so everything should be properly parsed. 188 | m_isParsed = true; 189 | } 190 | 191 | template 192 | bool Image::appendExport(std::string_view exportName, std::uint32_t rva) 193 | { 194 | getExportDir().add(exportName, rva); 195 | return false; 196 | } 197 | 198 | template 199 | void pepp::Image::scrambleVaData(uint32_t va, uint32_t size) 200 | { 201 | //if (va > GetPEHeader().GetOptionalHeader().GetSizeOfImage()) 202 | // return; 203 | 204 | uint32_t offset = getPEHdr().rvaToOffset(va); 205 | 206 | for (uint32_t i = 0; i < size; ++i) 207 | { 208 | if ((offset + i) > buffer().size()) 209 | break; 210 | 211 | buffer().deref(offset + i) = rand() % 0xff; 212 | } 213 | } 214 | 215 | template 216 | bool pepp::Image::isDll() const 217 | { 218 | return this->getPEHdr().getFileHdr().getCharacteristics() & IMAGE_FILE_DLL; 219 | } 220 | 221 | template 222 | bool pepp::Image::isSystemFile() const 223 | { 224 | return this->getPEHdr().getFileHdr().getCharacteristics() & IMAGE_FILE_SYSTEM; 225 | } 226 | 227 | template 228 | bool pepp::Image::isDllOrSystemFile() const 229 | { 230 | return isDll() || isSystemFile(); 231 | } 232 | 233 | template 234 | void pepp::Image::relocateImage(uintptr_t imageBase) 235 | { 236 | uintptr_t delta = (imageBase - getImageBase()); 237 | 238 | m_relocDirectory.forEachEntry( 239 | [&](BlockEntry& entry) 240 | { 241 | uint32_t offset = getPEHdr().rvaToOffset(entry.getRva()); 242 | 243 | switch (entry.getType()) 244 | { 245 | case RelocationType::REL_BASED_ABSOLUTE: 246 | break; 247 | case RelocationType::REL_BASED_DIR64: 248 | if constexpr (bitsize == 32) 249 | DebugBreak(); 250 | buffer().deref(offset) += delta; 251 | break; 252 | case RelocationType::REL_BASED_HIGHLOW: 253 | buffer().deref(offset) += (uint32_t)delta; 254 | break; 255 | case RelocationType::REL_BASED_HIGH: 256 | buffer().deref(offset) += HIWORD(delta); 257 | break; 258 | case RelocationType::REL_BASED_LOW: 259 | buffer().deref(offset) += LOWORD(delta); 260 | break; 261 | default: 262 | DebugBreak(); 263 | } 264 | } 265 | ); 266 | } 267 | 268 | template 269 | bool Image::extendSection(std::string_view sectionName, std::uint32_t delta) 270 | { 271 | std::uint32_t fileAlignment = getPEHdr().getOptionalHdr().getFileAlignment(); 272 | std::uint32_t sectAlignment = getPEHdr().getOptionalHdr().getSectionAlignment(); 273 | if (fileAlignment == 0 || sectAlignment == 0 || delta == 0) 274 | return false; 275 | 276 | SectionHeader& header = getSectionHdr(sectionName); 277 | 278 | if (header.getName() != ".dummy") 279 | { 280 | std::unique_ptr zero_buf(new uint8_t[delta]{}); 281 | 282 | uint32_t ptr = header.getPtrToRawData() + header.getSizeOfRawData(); 283 | 284 | header.setSizeOfRawData(align(header.getSizeOfRawData() + delta, fileAlignment)); 285 | header.setVirtualSize(header.getVirtualSize() + delta); 286 | 287 | for (int i = 0; i < MAX_DIRECTORY_COUNT; i++) 288 | { 289 | auto& dir = getPEHdr().getOptionalHdr().getDataDir(i); 290 | 291 | if (dir.VirtualAddress == header.getVirtualAddress()) 292 | { 293 | dir.Size += delta; 294 | break; 295 | } 296 | } 297 | 298 | // Update image size 299 | getPEHdr().getOptionalHdr().setSizeOfImage(align(getPEHdr().getOptionalHdr().getSizeOfImage() + delta, sectAlignment)); 300 | 301 | // Fill in data 302 | //buffer().insert_data(header.getPtrToRawData() + header.getSizeOfRawData(), zero_buf.get(), delta); 303 | //buffer().insert_data(header.getPtrToRawData() + header.getSizeOfRawData() - delta, zero_buf.get(), delta); 304 | buffer().insert(buffer().begin() + ptr, align(delta, fileAlignment), 0); 305 | //buffer().resize(align(buffer().size() + delta, fileAlignment)); 306 | 307 | // Re-validate the image/headers. 308 | _validate(); 309 | 310 | return true; 311 | } 312 | 313 | return false; 314 | } 315 | 316 | template 317 | std::uint32_t Image::findPadding(SectionHeader* s, std::uint8_t v, std::size_t n, std::uint32_t alignment) 318 | { 319 | bool bTraverseUp = s == nullptr; 320 | std::uint32_t startOffset{}; 321 | 322 | n = align(n, alignment); 323 | 324 | if (s == nullptr) 325 | s = &m_rawSectionHeaders[getNumberOfSections() - 1]; 326 | 327 | startOffset = s->getPtrToRawData(); 328 | 329 | std::vector::iterator it = buffer().end(); 330 | 331 | // Start from bottom to top, or vice versa? 332 | if (bTraverseUp) 333 | { 334 | std::vector tmpData(n, v); 335 | 336 | for (std::uint32_t i = startOffset + s->getSizeOfRawData(); i > n; i = align(i - n, alignment)) 337 | { 338 | if (memcmp(&buffer()[i - n], tmpData.data(), tmpData.size()) == 0) 339 | { 340 | it = buffer().begin() + (i - n); 341 | break; 342 | } 343 | } 344 | } 345 | else 346 | { 347 | std::vector tmpData(n, v); 348 | 349 | for (std::uint32_t i = startOffset; i < startOffset + (buffer().size() - startOffset); i = align(i + n, alignment)) 350 | { 351 | if (memcmp(&buffer()[i], tmpData.data(), tmpData.size()) == 0) 352 | { 353 | it = buffer().begin() + (i); 354 | break; 355 | } 356 | } 357 | } 358 | 359 | 360 | if (it == buffer().end()) 361 | return -1; 362 | 363 | return (std::uint32_t)std::distance(buffer().begin(), it); 364 | } 365 | 366 | template 367 | std::uint32_t Image::findZeroPadding(SectionHeader* s, std::size_t n, std::uint32_t alignment) 368 | { 369 | return findPadding(s, 0x0, n, alignment); 370 | } 371 | 372 | template 373 | std::vector Image::findBinarySequence(SectionHeader* s, std::string_view binary_seq) const 374 | { 375 | constexpr auto ascii_to_byte = [](const char ch) [[msvc::forceinline]] { 376 | if (ch >= '0' && ch <= '9') 377 | return std::uint8_t(ch - '0'); 378 | if (ch >= 'A' && ch <= 'F') 379 | return std::uint8_t(ch - 'A' + '\n'); 380 | return std::uint8_t(ch - 'a' + '\n'); 381 | }; 382 | 383 | std::vector offsets{}; 384 | 385 | if (s == nullptr) 386 | s = &m_rawSectionHeaders[getNumberOfSections() - 1]; 387 | 388 | std::uint32_t start_offset = s->getPtrToRawData(); 389 | std::uint32_t result = 0; 390 | std::uint32_t match_count = 0; 391 | 392 | for (std::uint32_t i = start_offset; i <= start_offset + s->getSizeOfRawData(); ++i) 393 | { 394 | for (int c = 0; c < binary_seq.size();) 395 | { 396 | if (binary_seq[c] == ' ') 397 | { 398 | ++c; 399 | continue; 400 | } 401 | 402 | if (binary_seq[c] == '?') 403 | { 404 | ++c; 405 | ++match_count; 406 | continue; 407 | } 408 | 409 | if (buffer()[i + match_count++] != ((ascii_to_byte(binary_seq[c]) << 4) | ascii_to_byte(binary_seq[c + 1]))) 410 | { 411 | result = 0; 412 | break; 413 | } 414 | 415 | result = i; 416 | c += 2; 417 | } 418 | 419 | if (result) 420 | { 421 | offsets.emplace_back(i); 422 | i += match_count - 1; 423 | } 424 | 425 | 426 | match_count = 0; 427 | result = 0; 428 | } 429 | 430 | return offsets; 431 | } 432 | 433 | template 434 | std::vector> Image::findBinarySequences(SectionHeader* s, std::initializer_list> binary_seq) const 435 | { 436 | constexpr auto ascii_to_byte = [](const char ch) [[msvc::forceinline]] { 437 | if (ch >= '0' && ch <= '9') 438 | return std::uint8_t(ch - '0'); 439 | if (ch >= 'A' && ch <= 'F') 440 | return std::uint8_t(ch - 'A' + '\n'); 441 | return std::uint8_t(ch - 'a' + '\n'); 442 | }; 443 | 444 | std::vector> offsets{}; 445 | 446 | if (s == nullptr) 447 | s = &m_rawSectionHeaders[getNumberOfSections() - 1]; 448 | 449 | std::uint32_t start_offset = s->getPtrToRawData(); 450 | std::pair result{}; 451 | std::uint32_t match_count = 0; 452 | 453 | for (std::uint32_t i = start_offset; i <= start_offset + s->getSizeOfRawData(); ++i) 454 | { 455 | for (auto const& seq : binary_seq) 456 | { 457 | for (int c = 0; c < seq.second.size();) 458 | { 459 | if (seq.second[c] == ' ') 460 | { 461 | ++c; 462 | continue; 463 | } 464 | 465 | if (seq.second[c] == '?') 466 | { 467 | ++c; 468 | ++match_count; 469 | continue; 470 | } 471 | 472 | std::uint8_t _byte = ((ascii_to_byte(seq.second[c]) << 4) | ascii_to_byte(seq.second[c + 1])); 473 | 474 | if (buffer()[i + match_count++] != _byte) 475 | { 476 | result = { 0,0 }; 477 | break; 478 | } 479 | 480 | result = {seq.first, i}; 481 | c += 2; 482 | } 483 | 484 | if (result.second) 485 | { 486 | offsets.emplace_back(std::move(result)); 487 | break; 488 | } 489 | 490 | match_count = 0; 491 | result = { 0, 0 }; 492 | } 493 | 494 | i += std::max(match_count - 1, 0); 495 | match_count = 0; 496 | } 497 | 498 | return offsets; 499 | } 500 | 501 | template 502 | bool Image::appendSection(std::string_view section_name, std::uint32_t size, std::uint32_t chrs, SectionHeader* out) 503 | { 504 | std::uint32_t fileAlignment = getPEHdr().getOptionalHdr().getFileAlignment(); 505 | std::uint32_t sectAlignment = getPEHdr().getOptionalHdr().getSectionAlignment(); 506 | if (fileAlignment == 0 || sectAlignment == 0) 507 | return false; 508 | 509 | std::uint32_t alignedFileSize = align(size, fileAlignment); 510 | std::uint32_t alignedVirtSize = size; 511 | std::uint32_t oldFileSize = getPEHdr().getOptionalHdr().getSizeOfImage(); 512 | size_t oldSize = buffer().size(); 513 | 514 | // Build a section (these should be the only necessary values to fill) 515 | SectionHeader sec; 516 | memset(&sec, 0, sizeof(sec)); 517 | 518 | sec.setName(section_name); 519 | sec.setSizeOfRawData(alignedFileSize); 520 | sec.setVirtualSize(alignedVirtSize); 521 | sec.setCharacteristics(chrs); 522 | sec.setVirtualAddress(getPEHdr().getNextSectionRva()); 523 | sec.setPointerToRawData(getPEHdr().getNextSectionOffset()); 524 | 525 | // Update image size 526 | getPEHdr().getOptionalHdr().setSizeOfImage(align4kb(getPEHdr().getOptionalHdr().getSizeOfImage() + size)); 527 | 528 | // Update PE header info. 529 | uint32_t numSections = getNumberOfSections(); 530 | getPEHdr().getFileHdr().setNumberOfSections(numSections + 1); 531 | getPEHdr().getOptionalHdr().setSizeOfCode(getPEHdr().getOptionalHdr().getSizeOfCode() + alignedVirtSize); 532 | getPEHdr().getOptionalHdr().setSizeOfHeaders(getPEHdr().getOptionalHdr().getSizeOfHeaders() + sizeof(sec)); 533 | 534 | // Add it in the raw section header 535 | SectionHeader& lastHdr = getSectionHdr(numSections); 536 | memcpy(&lastHdr, &sec, sizeof(sec)); 537 | 538 | if (out) 539 | memcpy(out, &m_rawSectionHeaders[numSections], sizeof(SectionHeader)); 540 | 541 | // buffer().resize(buffer().size() + alignedFileSize); 542 | buffer().insert(buffer().begin() + sec.getPtrToRawData(), alignedFileSize, 0); 543 | 544 | // Re-validate the image/headers. 545 | _validate(); 546 | 547 | return true; 548 | } 549 | 550 | 551 | template 552 | void pepp::Image::setAsMapped() noexcept 553 | { 554 | for (std::uint16_t i = 0; i < getNumberOfSections(); ++i) 555 | { 556 | SectionHeader& sec = getSectionHdr(i); 557 | 558 | sec.setPointerToRawData(sec.getVirtualAddress()); 559 | sec.setSizeOfRawData(sec.getVirtualSize()); 560 | } 561 | 562 | m_isMemMapped = true; 563 | } 564 | 565 | template 566 | void pepp::Image::mapToBuffer(pepp::Address<> basePtr, const std::vector& ignore) 567 | { 568 | for (int i = 0; i < getNumberOfSections(); ++i) 569 | { 570 | SectionHeader& sec = getSectionHdr(i); 571 | 572 | bool bSkip = false; 573 | 574 | if (!ignore.empty()) 575 | { 576 | for (auto& item : ignore) 577 | { 578 | if (item == sec.getName()) 579 | { 580 | bSkip = true; 581 | break; 582 | } 583 | } 584 | } 585 | 586 | if (bSkip) 587 | continue; 588 | 589 | memcpy((basePtr.ptr() + sec.getVirtualAddress()), &base()[sec.getPtrToRawData()], sec.getSizeOfRawData()); 590 | } 591 | } -------------------------------------------------------------------------------- /pepp/Image.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pepp 4 | { 5 | template 6 | class PEHeader; 7 | class SectionHeader; 8 | template 9 | class ExportDirectory; 10 | template 11 | class ImportDirectory; 12 | template 13 | class RelocationDirectory; 14 | enum SectionCharacteristics; 15 | enum PEDirectoryEntry; 16 | enum class PEMachine; 17 | 18 | namespace detail 19 | { 20 | template 21 | struct Image_t 22 | { 23 | using MZHeader_t = IMAGE_DOS_HEADER; 24 | using ImportDescriptor_t = IMAGE_IMPORT_DESCRIPTOR; 25 | using BoundImportDescriptor_t = IMAGE_BOUND_IMPORT_DESCRIPTOR; 26 | using ResourceDirectory_t = IMAGE_RESOURCE_DIRECTORY; 27 | using ResourceDirectoryEntry_t = IMAGE_RESOURCE_DIRECTORY_ENTRY; 28 | using SectionHeader_t = IMAGE_SECTION_HEADER; 29 | using FileHeader_t = IMAGE_FILE_HEADER; 30 | using DataDirectory_t = IMAGE_DATA_DIRECTORY; 31 | using ExportDirectory_t = IMAGE_EXPORT_DIRECTORY; 32 | using RelocationBase_t = IMAGE_BASE_RELOCATION; 33 | using ImportAddressTable_t = std::uint32_t; 34 | }; 35 | 36 | template<> struct Image_t<64> 37 | { 38 | using Header_t = IMAGE_NT_HEADERS64; 39 | using TLSDirectory_t = IMAGE_TLS_DIRECTORY64; 40 | using ThunkData_t = IMAGE_THUNK_DATA64; 41 | using Address_t = std::uint64_t; 42 | using OptionalHeader_t = IMAGE_OPTIONAL_HEADER64; 43 | }; 44 | 45 | template<> struct Image_t<32> 46 | { 47 | using Header_t = IMAGE_NT_HEADERS32; 48 | using TLSDirectory_t = IMAGE_TLS_DIRECTORY32; 49 | using ThunkData_t = IMAGE_THUNK_DATA32; 50 | using Address_t = std::uint32_t; 51 | using OptionalHeader_t = IMAGE_OPTIONAL_HEADER32; 52 | }; 53 | } 54 | 55 | /// 56 | // - class Image 57 | // - Used for runtime or static analysis/manipulating of PE files. 58 | /// 59 | template 60 | class Image : pepp::msc::NonCopyable 61 | { 62 | using CPEHeader = const PEHeader; 63 | using CExportDirectory = const ExportDirectory; 64 | using CImportDirectory = const ImportDirectory; 65 | 66 | public: 67 | 68 | // - Publicize the detail::Image_t used by this image. 69 | using ImageData_t = detail::Image_t; 70 | 71 | friend class PEHeader; 72 | 73 | static_assert(bitsize == 32 || bitsize == 64, "Invalid bitsize fed into PE::Image"); 74 | private: 75 | detail::Image_t<>::MZHeader_t* m_MZHeader; 76 | std::string m_fileName{}; 77 | mem::ByteVector m_imageBuffer{}; 78 | PEHeader m_PEHeader; 79 | // - Sections 80 | SectionHeader* m_rawSectionHeaders; 81 | // - Exports 82 | ExportDirectory m_exportDirectory; 83 | // - Imports 84 | ImportDirectory m_importDirectory; 85 | // - Relocations 86 | RelocationDirectory m_relocDirectory; 87 | // - Is image mapped? Rva2Offset becomes obsolete 88 | bool m_isMemMapped = false; 89 | // - Is image successfully parsed? 90 | bool m_isParsed; 91 | public: 92 | 93 | // - Default ctor. 94 | Image(); 95 | 96 | // - Used to construct a `class Image` via a existing file 97 | Image(std::string_view filepath); 98 | 99 | // - Used to construct a `class Image` via a memory buffer 100 | Image(const void* data, std::size_t size); 101 | 102 | // - Used to construct via another `class Image` 103 | Image(const Image& image); 104 | 105 | // - Initialization routines 106 | void setFromMemory(const void* data, std::size_t size); 107 | bool setFromMappedMemory(void* data, std::size_t size) noexcept; 108 | bool setFromFilePath(std::string_view file_path); 109 | 110 | // - Get the start pointer of the buffer. 111 | std::uint8_t* base() { 112 | return m_imageBuffer.data(); 113 | } 114 | 115 | mem::ByteVector& buffer() { 116 | return m_imageBuffer; 117 | } 118 | 119 | const mem::ByteVector& buffer() const { 120 | return m_imageBuffer; 121 | } 122 | 123 | // - Magic number in the DOS header. 124 | std::uint16_t magic() const { 125 | return m_MZHeader->e_magic; 126 | } 127 | 128 | // - Get the Image Base 129 | detail::Image_t::Address_t getImageBase() const noexcept { 130 | return m_PEHeader.getOptionalHdr().getImageBase(); 131 | } 132 | 133 | // - PEHeader wrapper 134 | class PEHeader& getPEHdr() { 135 | return m_PEHeader; 136 | } 137 | 138 | class ExportDirectory& getExportDir() { 139 | return m_exportDirectory; 140 | } 141 | 142 | class ImportDirectory& getImportDir() { 143 | return m_importDirectory; 144 | } 145 | 146 | class RelocationDirectory& getRelocDir() { 147 | return m_relocDirectory; 148 | } 149 | 150 | const PEHeader& getPEHdr() const { 151 | return m_PEHeader; 152 | } 153 | 154 | const class ExportDirectory& getExportDir() const { 155 | return m_exportDirectory; 156 | } 157 | 158 | const class ImportDirectory& getImportDir() const { 159 | return m_importDirectory; 160 | } 161 | 162 | const class RelocationDirectory& getRelocDir() const { 163 | return m_relocDirectory; 164 | } 165 | 166 | // - Native pointer 167 | detail::Image_t<>::MZHeader_t* native() { 168 | return m_MZHeader; 169 | } 170 | 171 | // - Assign all sections to the appropriate VA 172 | void setAsMapped() noexcept; 173 | 174 | void mapToBuffer(pepp::Address<> base, const std::vector& ignore = {}); 175 | 176 | // - Get PEMachine 177 | constexpr PEMachine getMachine() const; 178 | 179 | // - Is X64 180 | static constexpr unsigned int getBitSize() { return bitsize; } 181 | 182 | // - Add a new section to the image 183 | bool appendSection(std::string_view sectionName, std::uint32_t size, std::uint32_t chars, SectionHeader* out = nullptr); 184 | 185 | // - Extend an existing section (will break things depending on the section) 186 | bool extendSection(std::string_view sectionName, std::uint32_t delta); 187 | 188 | // - Append a new export 189 | bool appendExport(std::string_view exportName, std::uint32_t rva); 190 | 191 | void scrambleVaData(uint32_t va, uint32_t size); 192 | 193 | bool isDll() const; 194 | bool isSystemFile() const; 195 | bool isDllOrSystemFile() const; 196 | 197 | // - 198 | void relocateImage(uintptr_t imageBase); 199 | 200 | // - Find offset padding of value v with count n, starting at specified header or bottom of image if none specified 201 | std::uint32_t findPadding(SectionHeader* s, std::uint8_t v, std::size_t n, std::uint32_t alignment = 0); 202 | 203 | // - Find offset zero padding up to N bytes, starting at specified header or bottom of image if none specified 204 | std::uint32_t findZeroPadding(SectionHeader* s, std::size_t n, std::uint32_t alignment = 0); 205 | 206 | // - Find (wildcard acceptable) binary sequence 207 | std::vector findBinarySequence(SectionHeader* s, std::string_view binary_seq) const; 208 | std::vector> findBinarySequences(SectionHeader* s, std::initializer_list> binary_seq) const; 209 | 210 | // - Check if a data directory is "present" 211 | // - - Necessary before actually using the directory 212 | // - (e.g not all images will have a valid IMAGE_EXPORT_DIRECTORY) 213 | bool hasDataDirectory(PEDirectoryEntry entry); 214 | 215 | // - Write out to file 216 | void writeToFile(std::string_view filepath); 217 | 218 | // - Wrappers 219 | SectionHeader& getSectionHdr(std::uint16_t dwIndex) { 220 | return m_PEHeader.getSectionHeader(dwIndex); 221 | } 222 | SectionHeader& getSectionHdr(std::string_view name) { 223 | return m_PEHeader.getSectionHeader(name); 224 | } 225 | SectionHeader& getSectionHdrFromVa(std::uint32_t va) { 226 | return m_PEHeader.getSectionHeaderFromVa(va); 227 | } 228 | SectionHeader& getSectionHdrFromOffset(std::uint32_t offset) { 229 | return m_PEHeader.getSectionHeaderFromOffset(offset); 230 | } 231 | std::uint16_t getNumberOfSections() const { 232 | return m_PEHeader.getFileHdr().getNumberOfSections(); 233 | } 234 | 235 | constexpr auto getWordSize() const { 236 | return bitsize == 64 ? sizeof(std::uint64_t) : sizeof(std::uint32_t); 237 | } 238 | 239 | bool wasParsed() const { 240 | return m_isParsed; 241 | } 242 | 243 | private: 244 | // - Setup internal objects/pointers and validate they are proper. 245 | void _validate(); 246 | }; 247 | 248 | using Image64 = Image<64>; 249 | using Image86 = Image<32>; 250 | 251 | 252 | 253 | } -------------------------------------------------------------------------------- /pepp/ImportDirectory.cpp: -------------------------------------------------------------------------------- 1 | #include "PELibrary.hpp" 2 | 3 | using namespace pepp; 4 | 5 | 6 | // Explicit templates. 7 | template class ImportDirectory<32>; 8 | template class ImportDirectory<64>; 9 | 10 | template 11 | bool ImportDirectory::importsModule(std::string_view module, std::uint32_t* name_rva) const 12 | { 13 | auto descriptor = m_base; 14 | mem::ByteVector const* buffer = &m_image->buffer(); 15 | 16 | while (descriptor->FirstThunk != 0) { 17 | std::uint32_t offset = m_image->getPEHdr().rvaToOffset(descriptor->Name); 18 | 19 | std::string_view modname = buffer->as(offset); 20 | 21 | if (_stricmp(modname.data(), module.data()) == 0) 22 | { 23 | if (name_rva) 24 | *name_rva = descriptor->Name; 25 | 26 | return true; 27 | } 28 | 29 | descriptor++; 30 | } 31 | 32 | if (name_rva) 33 | *name_rva = 0; 34 | 35 | return false; 36 | } 37 | 38 | template 39 | bool ImportDirectory::hasModuleImport(std::string_view module, std::string_view import, std::uint32_t* rva) const 40 | { 41 | auto descriptor = m_base; 42 | mem::ByteVector const* buffer = &m_image->buffer(); 43 | 44 | while (descriptor->Characteristics != 0) { 45 | std::uint32_t offset = m_image->getPEHdr().rvaToOffset(descriptor->Name); 46 | 47 | if (_stricmp(buffer->as(offset), module.data()) == 0) 48 | { 49 | std::int32_t index = 0; 50 | typename detail::Image_t::ThunkData_t* firstThunk = 51 | buffer->as(m_image->getPEHdr().rvaToOffset(descriptor->OriginalFirstThunk)); 52 | 53 | while (firstThunk->u1.AddressOfData) 54 | { 55 | // 56 | // TODO: Ordinals not handled here. 57 | if (isImportOrdinal(firstThunk->u1.Ordinal)) 58 | { 59 | index++; 60 | firstThunk++; 61 | continue; 62 | } 63 | 64 | IMAGE_IMPORT_BY_NAME* _imp = 65 | buffer->as(m_image->getPEHdr().rvaToOffset(firstThunk->u1.AddressOfData)); 66 | 67 | if (import == _imp->Name) 68 | { 69 | if (rva) 70 | *rva = descriptor->FirstThunk + (index * m_image->getWordSize()); 71 | 72 | return true; 73 | } 74 | 75 | index++; 76 | firstThunk++; 77 | } 78 | } 79 | 80 | descriptor++; 81 | } 82 | 83 | if (rva) 84 | *rva = 0; 85 | 86 | return false; 87 | } 88 | 89 | template 90 | void ImportDirectory::addModuleImport(std::string_view module, std::string_view import, std::uint32_t* rva) 91 | { 92 | // TODO: Clean this up and optimize some things. 93 | 94 | auto descriptor = m_base; 95 | mem::ByteVector* buffer = &m_image->buffer(); 96 | 97 | std::unique_ptr descriptors; 98 | std::uint32_t vsize = 0, rawsize = 0; 99 | 100 | vsize = m_image->getPEHdr() 101 | .getOptionalHdr() 102 | .getDataDir(DIRECTORY_ENTRY_IMPORT).Size; 103 | 104 | 105 | descriptors.reset(new uint8_t[vsize]); 106 | memset(descriptors.get(), 0, vsize); 107 | 108 | SectionHeader newSec; 109 | 110 | // 111 | // Add in all the descriptors, so we can relocate them. 112 | while (descriptor->Characteristics != 0) 113 | { 114 | std::memcpy(&descriptors.get()[rawsize], descriptor, sizeof(*descriptor)); 115 | rawsize += sizeof detail::Image_t<>::ImportDescriptor_t; 116 | 117 | std::memset(descriptor, 0x0, sizeof(*descriptor)); 118 | 119 | descriptor++; 120 | } 121 | 122 | // 123 | // For the null term. 124 | rawsize += sizeof detail::Image_t<>::ImportDescriptor_t; 125 | 126 | // 127 | // Create a new section for the descriptors 128 | if (newSec = m_image->getSectionHdr(".pepp"); newSec.getName() == ".dummy") 129 | { 130 | // 131 | // We split a new section into two portions 132 | // The first part contains IAT addresses, or IMAGE_IMPORT_BY_NAME structs. 133 | // The second part contains import descriptors 134 | // NOTE: The section size may need to be modified depending on how many imports need to be added 135 | // This is using quite a large section due to a IAT rebuilding tool I created previously. 136 | m_image->appendSection( 137 | ".pepp", 138 | 20 * PAGE_SIZE, 139 | SCN_MEM_READ | 140 | SCN_MEM_WRITE | 141 | SCN_CNT_INITIALIZED_DATA | 142 | SCN_MEM_EXECUTE, &newSec); 143 | 144 | memset(buffer->as(newSec.getPtrToRawData()), 0xcc, newSec.getSizeOfRawData()); 145 | 146 | newSec.setPtrToRelocations(0); 147 | newSec.setPtrToLinenumbers(0); 148 | newSec.setNumberOfRelocations(0); 149 | newSec.setNumberOfLinenumbers(0); 150 | 151 | // Ghetto, needed for now. 152 | memcpy(&m_image->getSectionHdr(".pepp"), &newSec, sizeof newSec); 153 | 154 | // 155 | // Set the new base. 156 | m_base = reinterpret_cast( 157 | &m_image->base()[m_image->getPEHdr().rvaToOffset( 158 | newSec.getVirtualAddress() + (10*PAGE_SIZE))]); 159 | } 160 | 161 | // 162 | // Fill in the original descriptors 163 | std::memcpy(&buffer->at(newSec.getPtrToRawData() + (10*PAGE_SIZE)), descriptors.get(), vsize); 164 | 165 | // 166 | // Set the new directory 167 | m_image->getPEHdr() 168 | .getOptionalHdr() 169 | .getDataDir(DIRECTORY_ENTRY_IMPORT).VirtualAddress 170 | = newSec.getVirtualAddress() + (10*PAGE_SIZE); 171 | m_image->getPEHdr() 172 | .getOptionalHdr() 173 | .getDataDir(DIRECTORY_ENTRY_IMPORT).Size 174 | = vsize + sizeof detail::Image_t<>::ImportDescriptor_t; 175 | 176 | std::uint32_t descriptor_offset = newSec.getPtrToRawData() + (10*PAGE_SIZE) + vsize - sizeof(*descriptor); 177 | descriptor = (decltype(descriptor)) & ((*buffer)[descriptor_offset]); 178 | 179 | // 180 | // Fill in default values, we don't use these 181 | descriptor->ForwarderChain = 0; 182 | descriptor->TimeDateStamp = 0; 183 | 184 | // 185 | // 1) Check if requested module already exists as string, and use that RVA 186 | std::uint32_t name_rva = 0; 187 | std::uint32_t tmp_offset = 0; 188 | std::uint32_t iat_rva = 0; 189 | std::uint32_t tmp_rva = 0; 190 | std::uint32_t oft_offset = 0; 191 | std::uint32_t oft_rva = 0; 192 | 193 | if (!importsModule(module, &name_rva)) 194 | { 195 | // 2) If 1 isn't possible, add a section or extend the data section (hard) 196 | // and add in the module name manually 197 | // - set descriptor->Name to that rva 198 | tmp_offset = m_image->findPadding(&newSec, 0xcc, module.size() + 1); 199 | name_rva = m_image->getPEHdr().offsetToRva(tmp_offset); 200 | 201 | std::memcpy(buffer->as(tmp_offset), module.data(), module.size()); 202 | buffer->as(tmp_offset)[module.size()] = 0; 203 | } 204 | 205 | descriptor->Name = name_rva; 206 | 207 | using ImageThunkData_t = typename detail::Image_t::ThunkData_t; 208 | 209 | ImageThunkData_t thunks[2]; 210 | 211 | // 3) Add in FirstThunk 212 | tmp_offset = m_image->findPadding(&newSec, 0xcc, sizeof(thunks), m_image->getWordSize()); 213 | 214 | iat_rva = m_image->getPEHdr().offsetToRva(tmp_offset); 215 | 216 | // 217 | // Fill in values so that it doesn't get taken up next time this function is called 218 | // Also, these need to be zero. 219 | memset(buffer->as(tmp_offset), 0x00, sizeof(thunks)); 220 | 221 | ImageThunkData_t* firstThunk = m_image->buffer().as(tmp_offset); 222 | firstThunk->u1.AddressOfData = iat_rva; 223 | 224 | descriptor->FirstThunk = iat_rva; 225 | if (rva) 226 | *rva = iat_rva; 227 | 228 | // 4) Add in OriginalFirstThunk 229 | tmp_offset = m_image->findPadding(&newSec, 0xcc, sizeof(thunks), m_image->getWordSize()); 230 | 231 | tmp_rva = m_image->getPEHdr().offsetToRva(tmp_offset); 232 | 233 | // 234 | // Fill in values so that it doesn't get taken up next time this function is called 235 | // Also, these need to be zero. 236 | memset(buffer->as(tmp_offset), 0x00, sizeof(thunks)); 237 | 238 | oft_offset = m_image->findPadding(&newSec, 0xcc, sizeof(std::uint16_t) + import.size() + 1, m_image->getWordSize()); 239 | oft_rva = m_image->getPEHdr().offsetToRva(oft_offset); 240 | // 241 | // Copy in name to the oft rva 242 | IMAGE_IMPORT_BY_NAME* imp = buffer->as(oft_offset); 243 | imp->Hint = 0x0000; 244 | 245 | memcpy(&imp->Name[0], import.data(), import.size()); 246 | imp->Name[import.size()] = 0; 247 | 248 | ImageThunkData_t* ogFirstThunk = m_image->buffer().as(tmp_offset); 249 | ogFirstThunk->u1.AddressOfData = oft_rva; 250 | (ogFirstThunk + 1)->u1.AddressOfData = 0; 251 | 252 | descriptor->OriginalFirstThunk = tmp_rva; 253 | 254 | // 255 | // Finally null terminate 256 | memset((descriptor + 1), 0, sizeof(decltype(*descriptor))); 257 | } 258 | 259 | template 260 | void ImportDirectory::addModuleImports(std::string_view module, std::initializer_list imports, std::uint32_t* rva) 261 | { 262 | // TODO: Clean this up and optimize some things. 263 | 264 | auto descriptor = m_base; 265 | mem::ByteVector* buffer = &m_image->buffer(); 266 | 267 | std::unique_ptr descriptors; 268 | std::uint32_t vsize = 0, rawsize = 0; 269 | 270 | vsize = m_image->getPEHdr() 271 | .getOptionalHdr() 272 | .getDataDir(DIRECTORY_ENTRY_IMPORT).Size; 273 | 274 | 275 | descriptors.reset(new uint8_t[vsize]); 276 | memset(descriptors.get(), 0, vsize); 277 | 278 | SectionHeader newSec; 279 | 280 | // 281 | // Add in all the descriptors, so we can relocate them. 282 | while (descriptor->Characteristics != 0) 283 | { 284 | std::memcpy(&descriptors.get()[rawsize], descriptor, sizeof(*descriptor)); 285 | rawsize += sizeof detail::Image_t<>::ImportDescriptor_t; 286 | 287 | std::memset(descriptor, 0x0, sizeof(*descriptor)); 288 | 289 | descriptor++; 290 | } 291 | 292 | // 293 | // For the null term. 294 | rawsize += sizeof detail::Image_t<>::ImportDescriptor_t; 295 | 296 | // 297 | // Create a new section for the descriptors 298 | if (newSec = m_image->getSectionHdr(".pepp"); newSec.getName() == ".dummy") 299 | { 300 | // 301 | // We split a new section into two portions 302 | // The first part contains IAT addresses, or IMAGE_IMPORT_BY_NAME structs. 303 | // The second part contains import descriptors 304 | m_image->appendSection( 305 | ".pepp", 306 | 2 * PAGE_SIZE, 307 | SCN_MEM_READ | 308 | SCN_MEM_WRITE | 309 | SCN_CNT_INITIALIZED_DATA | 310 | SCN_MEM_EXECUTE, &newSec); 311 | 312 | memset(buffer->as(newSec.getPtrToRawData()), 0xcc, newSec.getSizeOfRawData()); 313 | 314 | newSec.setPtrToRelocations(0); 315 | newSec.setPtrToLinenumbers(0); 316 | newSec.setNumberOfRelocations(0); 317 | newSec.setNumberOfLinenumbers(0); 318 | 319 | // Ghetto, needed for now. 320 | memcpy(&m_image->getSectionHdr(".pepp"), &newSec, sizeof newSec); 321 | 322 | // 323 | // Set the new base. 324 | m_base = reinterpret_cast( 325 | &m_image->base()[m_image->getPEHdr().rvaToOffset( 326 | newSec.getVirtualAddress() + PAGE_SIZE)]); 327 | } 328 | 329 | // 330 | // Fill in the original descriptors 331 | std::memcpy(&buffer->at(newSec.getPtrToRawData() + PAGE_SIZE), descriptors.get(), vsize); 332 | 333 | // 334 | // Set the new directory 335 | m_image->getPEHdr() 336 | .getOptionalHdr() 337 | .getDataDir(DIRECTORY_ENTRY_IMPORT).VirtualAddress 338 | = newSec.getVirtualAddress() + PAGE_SIZE; 339 | m_image->getPEHdr() 340 | .getOptionalHdr() 341 | .getDataDir(DIRECTORY_ENTRY_IMPORT).Size 342 | = vsize + sizeof detail::Image_t<>::ImportDescriptor_t; 343 | 344 | std::uint32_t descriptor_offset = newSec.getPtrToRawData() + PAGE_SIZE + vsize - sizeof(*descriptor); 345 | descriptor = (decltype(descriptor)) & ((*buffer)[descriptor_offset]); 346 | 347 | // 348 | // Fill in default values, we don't use these 349 | descriptor->ForwarderChain = 0; 350 | descriptor->TimeDateStamp = 0; 351 | 352 | // 353 | // 1) Check if requested module already exists as string, and use that RVA 354 | std::uint32_t name_rva = 0; 355 | std::uint32_t tmp_offset = 0; 356 | std::uint32_t iat_rva = 0; 357 | std::uint32_t tmp_rva = 0; 358 | std::uint32_t oft_offset = 0; 359 | std::uint32_t oft_rva = 0; 360 | 361 | if (!importsModule(module, &name_rva)) 362 | { 363 | // 2) If 1 isn't possible, add a section or extend the data section (hard) 364 | // and add in the module name manually 365 | // - set descriptor->Name to that rva 366 | tmp_offset = m_image->findPadding(&newSec, 0xcc, module.size() + 1); 367 | name_rva = m_image->getPEHdr().offsetToRva(tmp_offset); 368 | 369 | std::memcpy(buffer->as(tmp_offset), module.data(), module.size()); 370 | buffer->as(tmp_offset)[module.size()] = 0; 371 | } 372 | 373 | descriptor->Name = name_rva; 374 | 375 | using ImageThunkData_t = typename detail::Image_t::ThunkData_t; 376 | 377 | 378 | std::size_t thunksize = (imports.size() + 1) * sizeof(ImageThunkData_t); 379 | 380 | 381 | // 3) Add in FirstThunk 382 | tmp_offset = m_image->findPadding(&newSec, 0xcc, thunksize, m_image->getWordSize()); 383 | iat_rva = m_image->getPEHdr().offsetToRva(tmp_offset); 384 | 385 | // 386 | // Fill in values so that it doesn't get taken up next time this function is called 387 | // Also, these need to be zero. 388 | memset(buffer->as(tmp_offset), 0x00, thunksize); 389 | 390 | ImageThunkData_t* firstThunk = m_image->buffer().as(tmp_offset); 391 | firstThunk->u1.AddressOfData = iat_rva; 392 | 393 | descriptor->FirstThunk = iat_rva; 394 | 395 | 396 | 397 | // 4) Add in OriginalFirstThunk 398 | tmp_offset = m_image->findPadding(&newSec, 0xcc, thunksize, m_image->getWordSize()); 399 | tmp_rva = m_image->getPEHdr().offsetToRva(tmp_offset); 400 | 401 | // 402 | // Fill in values so that it doesn't get taken up next time this function is called 403 | // Also, these need to be zero. 404 | memset(buffer->as(tmp_offset), 0x00, thunksize); 405 | 406 | 407 | ImageThunkData_t* ogFirstThunk = m_image->buffer().as(tmp_offset); 408 | 409 | int i = 0; 410 | for (auto it = imports.begin(); it != imports.end(); it++) 411 | { 412 | oft_offset = m_image->findPadding(&newSec, 0xcc, sizeof(std::uint16_t) + it->size() + 1, m_image->getWordSize()); 413 | oft_rva = m_image->getPEHdr().offsetToRva(oft_offset); 414 | // 415 | // Copy in name to the oft rva 416 | IMAGE_IMPORT_BY_NAME* imp = buffer->as(oft_offset); 417 | imp->Hint = 0x0000; 418 | 419 | memcpy(&imp->Name[0], it->data(), it->size()); 420 | imp->Name[it->size()] = '\0'; 421 | 422 | if (rva) 423 | rva[i] = iat_rva + (m_image->getWordSize() * i++); 424 | 425 | ogFirstThunk->u1.AddressOfData = oft_rva; 426 | ogFirstThunk++; 427 | } 428 | 429 | 430 | ogFirstThunk->u1.AddressOfData = 0; 431 | descriptor->OriginalFirstThunk = tmp_rva; 432 | 433 | // 434 | // Finally null terminate 435 | memset((descriptor + 1), 0, sizeof(decltype(*descriptor))); 436 | } 437 | 438 | template 439 | void ImportDirectory::traverseImports(const std::function& cb_func) 440 | { 441 | auto descriptor = m_base; 442 | mem::ByteVector const* buffer = &m_image->buffer(); 443 | 444 | while (descriptor->Characteristics != 0) { 445 | std::uint32_t offset = m_image->getPEHdr().rvaToOffset(descriptor->Name); 446 | const char* module = buffer->as(offset); 447 | std::int32_t index = 0; 448 | typename detail::Image_t::ThunkData_t* firstThunk = 449 | buffer->as(m_image->getPEHdr().rvaToOffset(descriptor->OriginalFirstThunk)); 450 | 451 | ModuleImportData_t data{}; 452 | data.module_name_rva = descriptor->Name; 453 | data.module_name = module; 454 | data.import_rva = -1; 455 | 456 | while (firstThunk->u1.AddressOfData) 457 | { 458 | IMAGE_IMPORT_BY_NAME* _imp = 459 | buffer->as(m_image->getPEHdr().rvaToOffset(firstThunk->u1.AddressOfData)); 460 | 461 | if (isImportOrdinal(firstThunk->u1.Ordinal)) 462 | { 463 | data.ordinal = true; 464 | data.import_variant = (std::uint64_t)firstThunk->u1.Ordinal; 465 | data.import_name_rva = 0; 466 | } 467 | else 468 | { 469 | data.import_variant = static_cast(_imp->Name); 470 | data.import_name_rva = firstThunk->u1.AddressOfData + sizeof(std::uint16_t); 471 | } 472 | 473 | data.import_rva = descriptor->FirstThunk + (index * m_image->getWordSize()); 474 | 475 | // 476 | // Call the callback 477 | cb_func(&data); 478 | 479 | index++; 480 | firstThunk++; 481 | } 482 | 483 | descriptor++; 484 | } 485 | } 486 | 487 | template 488 | void ImportDirectory::getIATOffsets(std::uint32_t& begin, std::uint32_t& end) noexcept 489 | { 490 | // 491 | // Null out. 492 | begin = end = 0; 493 | 494 | IMAGE_DATA_DIRECTORY const& iat = m_image->getPEHdr().getOptionalHdr().getDataDir(IMAGE_DIRECTORY_ENTRY_IAT); 495 | if (iat.Size == 0) 496 | return; 497 | 498 | 499 | begin = m_image->getPEHdr().rvaToOffset(iat.VirtualAddress); 500 | end = begin + iat.Size; 501 | } 502 | 503 | template 504 | void pepp::ImportDirectory::getIATRvas(std::uint32_t& begin, std::uint32_t& end) noexcept 505 | { 506 | // 507 | // Null out. 508 | begin = end = 0; 509 | 510 | IMAGE_DATA_DIRECTORY const& iat = m_image->getPEHdr().getOptionalHdr().getDataDir(IMAGE_DIRECTORY_ENTRY_IAT); 511 | if (iat.Size == 0) 512 | return; 513 | 514 | 515 | begin = iat.VirtualAddress; 516 | end = begin + iat.Size; 517 | } 518 | -------------------------------------------------------------------------------- /pepp/ImportDirectory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace pepp 8 | { 9 | struct ModuleImportData_t 10 | { 11 | std::uint32_t module_name_rva; 12 | std::string module_name; 13 | std::uint32_t import_name_rva; 14 | std::variant import_variant; 15 | std::uint32_t import_rva; 16 | bool ordinal; 17 | }; 18 | 19 | static constexpr auto IMPORT_ORDINAL_FLAG_32 = IMAGE_ORDINAL_FLAG32; 20 | static constexpr auto IMPORT_ORDINAL_FLAG_64 = IMAGE_ORDINAL_FLAG64; 21 | 22 | template 23 | class ImportDirectory : pepp::msc::NonCopyable 24 | { 25 | friend class Image<32>; 26 | friend class Image<64>; 27 | 28 | Image* m_image; 29 | detail::Image_t<>::ImportDescriptor_t* m_base; 30 | detail::Image_t<>::ImportAddressTable_t m_iat_base; 31 | public: 32 | ImportDirectory() = default; 33 | 34 | bool importsModule(std::string_view module, std::uint32_t* name_rva = nullptr) const; 35 | bool hasModuleImport(std::string_view module, std::string_view import, std::uint32_t* rva = nullptr) const; 36 | void addModuleImport(std::string_view module, std::string_view import, std::uint32_t* rva = nullptr); 37 | void addModuleImports(std::string_view module, std::initializer_list imports, std::uint32_t* rva = nullptr); 38 | void traverseImports(const std::function& cb_func); 39 | 40 | void setCharacteristics(std::uint32_t chrs) { 41 | m_base->Characteristics = chrs; 42 | } 43 | 44 | std::uint32_t getCharacteristics() const { 45 | return m_base->Characteristics; 46 | } 47 | 48 | void setTimeDateStamp(std::uint32_t TimeDateStamp) { 49 | m_base->TimeDateStamp = TimeDateStamp; 50 | } 51 | 52 | std::uint32_t getTimeDateStamp() const { 53 | return m_base->TimeDateStamp; 54 | } 55 | 56 | //! Util 57 | template 58 | bool isImportOrdinal(T ord) const requires pepp::msc::MemoryAddress { 59 | if constexpr (bitsize == 64) 60 | return (ord & IMPORT_ORDINAL_FLAG_64) != 0; 61 | return (ord & IMPORT_ORDINAL_FLAG_32) != 0; 62 | } 63 | 64 | void getIATOffsets(std::uint32_t& begin, std::uint32_t& end) noexcept; 65 | void getIATRvas(std::uint32_t& begin, std::uint32_t& end) noexcept; 66 | 67 | private: 68 | //! Setup the directory 69 | void _setup(Image* image) { 70 | m_image = image; 71 | m_base = reinterpret_cast( 72 | &image->base()[image->getPEHdr().rvaToOffset( 73 | image->getPEHdr().getOptionalHdr().getDataDir(DIRECTORY_ENTRY_IMPORT).VirtualAddress)]); 74 | m_iat_base = reinterpret_cast( 75 | &image->base()[image->getPEHdr().rvaToOffset( 76 | image->getPEHdr().getOptionalHdr().getDataDir(DIRECTORY_ENTRY_IAT).VirtualAddress)]); 77 | } 78 | }; 79 | } -------------------------------------------------------------------------------- /pepp/OptionalHeader.cpp: -------------------------------------------------------------------------------- 1 | #include "PELibrary.hpp" 2 | 3 | using namespace pepp; 4 | 5 | // Explicit templates. 6 | template class OptionalHeader<32>; 7 | template class OptionalHeader<64>; 8 | 9 | template 10 | inline OptionalHeader::OptionalHeader() 11 | { 12 | } 13 | 14 | template 15 | void OptionalHeader::setMagic(PEMagic magic) 16 | { 17 | m_base->Magic = static_cast(magic); 18 | } 19 | 20 | template 21 | PEMagic OptionalHeader::getMagic() const 22 | { 23 | return static_cast(m_base->Magic); 24 | } 25 | 26 | 27 | template 28 | void OptionalHeader::setImageBase(detail::Image_t::Address_t address) 29 | { 30 | m_base->ImageBase = address; 31 | } 32 | 33 | template 34 | detail::Image_t::Address_t OptionalHeader::getImageBase() const 35 | { 36 | return m_base->ImageBase; 37 | } 38 | 39 | template 40 | void OptionalHeader::setSizeOfImage(std::uint32_t size) 41 | { 42 | m_base->SizeOfImage = size; 43 | } 44 | 45 | template 46 | std::uint32_t OptionalHeader::getSizeOfImage() const 47 | { 48 | return m_base->SizeOfImage; 49 | } 50 | 51 | template 52 | void OptionalHeader::setSizeOfCode(std::uint32_t dwSize) 53 | { 54 | m_base->SizeOfCode = dwSize; 55 | } 56 | 57 | template 58 | std::uint32_t OptionalHeader::getSizeOfCode() const 59 | { 60 | return m_base->SizeOfCode; 61 | } 62 | 63 | template 64 | void OptionalHeader::setSizeOfInitializedData(std::uint32_t dwSize) 65 | { 66 | m_base->SizeOfInitializedData = dwSize; 67 | } 68 | 69 | template 70 | std::uint32_t OptionalHeader::getSizeOfInitializedData() const 71 | { 72 | return m_base->SizeOfInitializedData; 73 | } 74 | 75 | template 76 | void pepp::OptionalHeader::setSizeOfHeaders(std::uint32_t dwSize) 77 | { 78 | m_base->SizeOfHeaders = dwSize; 79 | } 80 | 81 | template 82 | std::uint32_t pepp::OptionalHeader::getSizeOfHeaders() const 83 | { 84 | return m_base->SizeOfHeaders; 85 | } 86 | 87 | template 88 | void OptionalHeader::setSizeOfUninitializedData(std::uint32_t dwSize) 89 | { 90 | m_base->SizeOfUninitializedData = dwSize; 91 | } 92 | 93 | template 94 | std::uint32_t OptionalHeader::getSizeOfUninitializedData() const 95 | { 96 | return m_base->SizeOfUninitializedData; 97 | } 98 | 99 | template 100 | void OptionalHeader::setBaseOfCode(std::uint32_t dwBase) 101 | { 102 | m_base->BaseOfCode = dwBase; 103 | } 104 | 105 | template 106 | std::uint32_t OptionalHeader::getBaseOfCode() const 107 | { 108 | return m_base->BaseOfCode; 109 | } 110 | 111 | template 112 | void OptionalHeader::setAddressOfEntryPoint(std::uint32_t dwBase) 113 | { 114 | m_base->AddressOfEntryPoint = dwBase; 115 | } 116 | 117 | template 118 | std::uint32_t OptionalHeader::getAddressOfEntryPoint() const 119 | { 120 | return m_base->AddressOfEntryPoint; 121 | } 122 | 123 | template 124 | std::uint32_t OptionalHeader::getFileAlignment() const 125 | { 126 | return m_base->FileAlignment; 127 | } 128 | 129 | template 130 | std::uint32_t OptionalHeader::getSectionAlignment() const 131 | { 132 | return m_base->SectionAlignment; 133 | } 134 | 135 | template 136 | bool OptionalHeader::hasRelocations() const 137 | { 138 | return m_base->DataDirectory[DIRECTORY_ENTRY_BASERELOC].Size > 0; 139 | } 140 | -------------------------------------------------------------------------------- /pepp/OptionalHeader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pepp 4 | { 5 | enum PEDirectoryEntry 6 | { 7 | DIRECTORY_ENTRY_EXPORT = 0, // Export Directory 8 | DIRECTORY_ENTRY_IMPORT = 1, // Import Directory 9 | DIRECTORY_ENTRY_RESOURCE = 2, // Resource Directory 10 | DIRECTORY_ENTRY_EXCEPTION = 3, // Exception Directory 11 | DIRECTORY_ENTRY_SECURITY = 4, // Security Directory 12 | DIRECTORY_ENTRY_BASERELOC = 5, // Base Relocation Table 13 | DIRECTORY_ENTRY_DEBUG = 6, // Debug Directory 14 | DIRECTORY_ENTRY_ARCHITECTURE = 7, // Architecture Specific Data 15 | DIRECTORY_ENTRY_GLOBALPTR = 8, // RVA of GP 16 | DIRECTORY_ENTRY_TLS = 9, // TLS Directory 17 | DIRECTORY_ENTRY_LOAD_CONFIG = 10, // Load Configuration Directory 18 | DIRECTORY_ENTRY_BOUND_IMPORT = 11, // Bound Import Directory in headers 19 | DIRECTORY_ENTRY_IAT = 12, // Import Address Table 20 | DIRECTORY_ENTRY_DELAY_IMPORT = 13, // Delay Load Import Descriptors 21 | DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor 22 | }; 23 | 24 | enum class PEMagic 25 | { 26 | HDR_32 = 0x10b, 27 | HDR_64 = 0x20b, 28 | HDR_ROM = 0x107 29 | }; 30 | 31 | template 32 | class OptionalHeader : pepp::msc::NonCopyable 33 | { 34 | friend class PEHeader; 35 | friend class Image; 36 | 37 | using ImageData_t = detail::Image_t; 38 | 39 | Image* m_Image; 40 | ImageData_t::OptionalHeader_t* m_base{ nullptr }; 41 | public: 42 | OptionalHeader(); 43 | 44 | 45 | //! Getter/setter for OptionalHeader.Magic 46 | void setMagic(PEMagic magic); 47 | PEMagic getMagic() const; 48 | 49 | //! Getter/setter for OptionalHeader.ImageBase 50 | void setImageBase(detail::Image_t::Address_t address); 51 | detail::Image_t::Address_t getImageBase() const; 52 | 53 | //! Getter/setter for OptionalHeader.SizeOfImage 54 | void setSizeOfImage(std::uint32_t size); 55 | std::uint32_t getSizeOfImage() const; 56 | 57 | //! Getter/setter for OptionalHeader.SizeOfCode 58 | void setSizeOfCode(std::uint32_t dwSize); 59 | std::uint32_t getSizeOfCode() const; 60 | 61 | //! Getter/setter for OptionalHeader.SizeOfInitializedData 62 | void setSizeOfInitializedData(std::uint32_t dwSize); 63 | std::uint32_t getSizeOfInitializedData() const; 64 | 65 | //! Getter/setter for OptionalHeader.SizeOfInitializedData 66 | void setSizeOfHeaders(std::uint32_t dwSize); 67 | std::uint32_t getSizeOfHeaders() const; 68 | 69 | //! Getter/setter for OptionalHeader.SizeOfUninitializedData 70 | void setSizeOfUninitializedData(std::uint32_t dwSize); 71 | std::uint32_t getSizeOfUninitializedData() const; 72 | 73 | //! Getter/setter for OptionalHeader.BaseOfCode 74 | void setBaseOfCode(std::uint32_t dwBase); 75 | std::uint32_t getBaseOfCode() const; 76 | 77 | //! Getter/setter for OptionalHeader.AddressOfEntryPoint 78 | void setAddressOfEntryPoint(std::uint32_t dwBase); 79 | std::uint32_t getAddressOfEntryPoint() const; 80 | 81 | //! Getter for OptionalHeader.FileAlignment 82 | std::uint32_t getFileAlignment() const; 83 | 84 | //! Getter for OptionalHeader.SectionAlignment 85 | std::uint32_t getSectionAlignment() const; 86 | 87 | //! Get data directory 88 | detail::Image_t<>::DataDirectory_t& getDataDir(int idx) const { 89 | return m_base->DataDirectory[idx]; 90 | } 91 | 92 | //! Calculate the number of directories present (not NumberOfRvaAndSizes) 93 | std::uint32_t getDirectoryCount() const { 94 | std::uint32_t count{ 0ul }; 95 | for (int i = 0; i < MAX_DIRECTORY_COUNT; i++) 96 | { 97 | if (getDataDir(i).Size > 0) { 98 | ++count; 99 | } 100 | } 101 | return count; 102 | } 103 | 104 | std::uint8_t* base() const { 105 | return (std::uint8_t*)m_base; 106 | } 107 | 108 | //! Check if image has relocations 109 | bool hasRelocations() const; 110 | 111 | private: 112 | void _setup(Image* image) { 113 | m_Image = image; 114 | m_base = &image->getPEHdr().native()->OptionalHeader; 115 | } 116 | }; 117 | } -------------------------------------------------------------------------------- /pepp/PEHeader.cpp: -------------------------------------------------------------------------------- 1 | #include "PELibrary.hpp" 2 | 3 | using namespace pepp; 4 | 5 | // Explicit templates. 6 | template class PEHeader<32>; 7 | template class PEHeader<64>; 8 | 9 | template 10 | inline PEHeader::PEHeader() 11 | : m_image(nullptr) 12 | { 13 | } 14 | 15 | 16 | template 17 | std::uint32_t PEHeader::calcSizeOfImage() 18 | { 19 | std::uint32_t dwLowestRva{ 0 }; 20 | std::uint32_t dwHighestRva{ 0 }; 21 | 22 | for (std::uint16_t n = 0; n < getFileHdr().getNumberOfSections(); n++) { 23 | // 24 | // Skip sections with bad Misc.VirtualSize 25 | if (m_image->m_rawSectionHeaders[n].getVirtualSize() == 0) 26 | continue; 27 | // 28 | // Fill in high/low rvas if possible. 29 | if (m_image->m_rawSectionHeaders[n].getVirtualAddress() < dwLowestRva) 30 | dwLowestRva = 31 | m_image->m_rawSectionHeaders[n].getVirtualAddress(); 32 | if (m_image->m_rawSectionHeaders[n].getVirtualAddress() > dwHighestRva) 33 | dwHighestRva = 34 | m_image->m_rawSectionHeaders[n].getVirtualAddress() + m_image->m_rawSectionHeaders[n].getVirtualSize(); 35 | } 36 | 37 | return (dwHighestRva - dwLowestRva); 38 | } 39 | 40 | template 41 | std::uint32_t PEHeader::getStartOfCode() 42 | { 43 | return m_OptionalHeader.getBaseOfCode(); 44 | } 45 | 46 | template 47 | std::uint32_t PEHeader::getNextSectionOffset() 48 | { 49 | std::uint16_t nlastSecIdx = getFileHdr().getNumberOfSections() - 1; 50 | SectionHeader const& sec = getSectionHeader(nlastSecIdx); 51 | std::uint32_t uNextOffset = sec.getPtrToRawData() + sec.getSizeOfRawData(); 52 | 53 | /* 54 | * FileAlignment 55 | * The alignment of the raw data of sections in the image file, in bytes. 56 | */ 57 | return align(uNextOffset, getOptionalHdr().getFileAlignment()); 58 | } 59 | 60 | template 61 | std::uint32_t PEHeader::getNextSectionRva() 62 | { 63 | std::uint16_t nlastSecIdx = getFileHdr().getNumberOfSections() - 1; 64 | SectionHeader const& sec = getSectionHeader(nlastSecIdx); 65 | std::uint32_t uNextRva = sec.getVirtualAddress() + sec.getVirtualSize(); 66 | 67 | /* 68 | * SectionAlignment 69 | * The alignment of sections loaded in memory, in bytes. 70 | */ 71 | return align(uNextRva, getOptionalHdr().getSectionAlignment()); 72 | } 73 | -------------------------------------------------------------------------------- /pepp/PEHeader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pepp 4 | { 5 | constexpr static int MAX_DIRECTORY_COUNT = 16; 6 | 7 | template 8 | class Image; 9 | template 10 | class OptionalHeader; 11 | 12 | class SectionHeader; 13 | class FileHeader; 14 | 15 | template 16 | class PEHeader : pepp::msc::NonCopyable 17 | { 18 | friend class Image; 19 | 20 | using ImageData_t = detail::Image_t; 21 | 22 | Image* m_image; 23 | ImageData_t::Header_t* m_PEHdr = nullptr; 24 | FileHeader m_FileHeader; 25 | OptionalHeader m_OptionalHeader; 26 | private: 27 | //! Private constructor, this should never be established outside of `class Image` 28 | PEHeader(); 29 | public: 30 | 31 | class FileHeader& getFileHdr() { 32 | return m_FileHeader; 33 | } 34 | 35 | const class FileHeader& getFileHdr() const { 36 | return m_FileHeader; 37 | } 38 | 39 | class OptionalHeader& getOptionalHdr() { 40 | return m_OptionalHeader; 41 | } 42 | 43 | const class OptionalHeader& getOptionalHdr() const { 44 | return m_OptionalHeader; 45 | } 46 | 47 | SectionHeader& getSectionHeader(std::uint16_t dwIndex) { 48 | static SectionHeader dummy{}; 49 | 50 | if (dwIndex < m_image->getNumberOfSections()) 51 | return m_image->m_rawSectionHeaders[dwIndex]; 52 | 53 | return dummy; 54 | } 55 | 56 | SectionHeader& getSectionHeader(std::string_view name) { 57 | static SectionHeader dummy{}; 58 | 59 | for (std::uint16_t n = 0; n < m_image->getNumberOfSections(); n++) 60 | { 61 | if (m_image->m_rawSectionHeaders[n].getName().compare(name) == 0) { 62 | return m_image->m_rawSectionHeaders[n]; 63 | } 64 | } 65 | 66 | return dummy; 67 | } 68 | 69 | SectionHeader& getSectionHeaderFromVa(std::uint32_t va) { 70 | static SectionHeader dummy{}; 71 | 72 | for (std::uint16_t n = 0; n < m_image->getNumberOfSections(); n++) 73 | { 74 | if (m_image->m_rawSectionHeaders[n].hasVirtualAddress(va)) { 75 | return m_image->m_rawSectionHeaders[n]; 76 | } 77 | } 78 | 79 | return dummy; 80 | } 81 | 82 | SectionHeader& getSectionHeaderFromOffset(std::uint32_t offset) { 83 | static SectionHeader dummy{}; 84 | 85 | for (std::uint16_t n = 0; n < m_image->getNumberOfSections(); n++) 86 | { 87 | if (m_image->m_rawSectionHeaders[n].hasOffset(offset)) { 88 | return m_image->m_rawSectionHeaders[n]; 89 | } 90 | } 91 | 92 | return dummy; 93 | } 94 | 95 | 96 | 97 | //! Calculate the number of directories present (not NumberOfRvaAndSizes) 98 | std::uint32_t getDirectoryCount() const { 99 | return getOptionalHdr().getDirectoryCount(); 100 | } 101 | 102 | //! Convert a relative virtual address to a file offset 103 | std::uint32_t rvaToOffset(std::uint32_t rva) { 104 | SectionHeader const& sec { getSectionHeaderFromVa(rva) }; 105 | // 106 | // Did we get one? 107 | if (sec.getName() != ".dummy") { 108 | return sec.getPtrToRawData() + rva - sec.getVirtualAddress(); 109 | } 110 | 111 | return 0ul; 112 | } 113 | 114 | //! Convert a file offset back to a relative virtual address 115 | std::uint32_t offsetToRva(std::uint32_t offset) { 116 | SectionHeader const& sec{ getSectionHeaderFromOffset(offset) }; 117 | // 118 | // Did we get one? 119 | if (sec.getName() != ".dummy") { 120 | return (sec.getVirtualAddress() + offset) - sec.getPtrToRawData(); 121 | } 122 | 123 | return 0ul; 124 | } 125 | 126 | //! Convert a rel. virtual address to a virtual address 127 | detail::Image_t::Address_t rvaToVa(std::uint32_t rva) const { 128 | return m_OptionalHeader.getImageBase() + rva; 129 | } 130 | 131 | //! Used to check if the NT tag is present. 132 | bool isTaggedPE() const { 133 | return m_PEHdr->Signature == IMAGE_NT_SIGNATURE; 134 | } 135 | 136 | std::uint8_t* base() const { 137 | return (std::uint8_t*)m_PEHdr; 138 | } 139 | 140 | constexpr std::size_t size() const { 141 | return sizeof(decltype(*m_PEHdr)); 142 | } 143 | 144 | //! Return native pointer 145 | detail::Image_t::Header_t* native() { 146 | return m_PEHdr; 147 | } 148 | 149 | //! Manually calculate the size of the image 150 | std::uint32_t calcSizeOfImage(); 151 | 152 | //! Manually calculate the start of the code section 153 | std::uint32_t getStartOfCode(); 154 | 155 | //! Calculate next section offset 156 | std::uint32_t getNextSectionOffset(); 157 | 158 | //! Calculate next section rva 159 | std::uint32_t getNextSectionRva(); 160 | private: 161 | //! Setup the header 162 | void _setup(Image* image) { 163 | m_image = image; 164 | m_PEHdr = reinterpret_cast(m_image->base() + m_image->m_MZHeader->e_lfanew); 165 | m_FileHeader._setup(image); 166 | m_OptionalHeader._setup(image); 167 | } 168 | }; 169 | } -------------------------------------------------------------------------------- /pepp/PELibrary.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "misc/File.hpp" 11 | #include "misc/NonCopyable.hpp" 12 | #include "misc/ByteVector.hpp" 13 | #include "misc/Concept.hpp" 14 | #include "misc/Address.hpp" 15 | 16 | #include "Image.hpp" 17 | #include "PEHeader.hpp" 18 | #include "SectionHeader.hpp" 19 | #include "FileHeader.hpp" 20 | #include "OptionalHeader.hpp" 21 | #include "ExportDirectory.hpp" 22 | #include "ImportDirectory.hpp" 23 | #include "RelocationDirectory.hpp" 24 | #include "PEUtil.hpp" 25 | 26 | -------------------------------------------------------------------------------- /pepp/PEUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "PELibrary.hpp" 2 | 3 | #include 4 | #pragma comment(lib, "dbghelp.lib") 5 | 6 | using namespace pepp; 7 | 8 | std::string pepp::DemangleName(std::string_view mangled_name) 9 | { 10 | // 11 | // TODO: Don't rely on DbgHelp?? 12 | char undecorated_name[1024]; 13 | UnDecorateSymbolName( 14 | mangled_name.data(), 15 | undecorated_name, 16 | sizeof undecorated_name, 17 | UNDNAME_32_BIT_DECODE | UNDNAME_NAME_ONLY); 18 | 19 | return undecorated_name; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /pepp/PEUtil.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | namespace pepp 5 | { 6 | //! Windows 32/64bit declare the page size as 4kb (4096) 7 | static constexpr std::uint32_t PAGE_SIZE = 0x1000; 8 | 9 | //! Align a value 10 | template 11 | constexpr __forceinline auto align(V_t value, std::uint32_t alignment) requires std::unsigned_integral 12 | { 13 | if (alignment == 0) 14 | return value; 15 | return (value + (alignment - 1)) & ~(alignment - 1); 16 | //return (value + alignment - 1) / alignment * alignment; 17 | } 18 | 19 | //! Make a value 4kb aligned (for section purposes) 20 | template 21 | constexpr __forceinline auto align4kb(V_t v) requires std::unsigned_integral 22 | { 23 | return align(v, PAGE_SIZE); 24 | } 25 | 26 | //! Demangle a mangled name (MS supplied) 27 | std::string DemangleName(std::string_view mangled_name); 28 | } -------------------------------------------------------------------------------- /pepp/RelocationDirectory.cpp: -------------------------------------------------------------------------------- 1 | #include "PELibrary.hpp" 2 | 3 | using namespace pepp; 4 | 5 | // Explicit templates. 6 | template class RelocationDirectory<32>; 7 | template class RelocationDirectory<64>; 8 | 9 | template 10 | int RelocationDirectory::getNumBlocks() const 11 | { 12 | auto base = m_base; 13 | int count = 0; 14 | 15 | while (base->VirtualAddress) 16 | { 17 | count++; 18 | base = decltype(base)((char*)base + base->SizeOfBlock); 19 | } 20 | 21 | return count; 22 | } 23 | 24 | template 25 | int RelocationDirectory::getNumEntries(detail::Image_t<>::RelocationBase_t* reloc) const 26 | { 27 | // MSDN: The Block Size field is then followed by any number of Type or Offset field entries. 28 | // Each entry is a WORD (2 bytes) 29 | return (reloc->SizeOfBlock - sizeof(decltype(*reloc))) / sizeof(std::uint16_t); 30 | } 31 | 32 | template 33 | std::uint32_t RelocationDirectory::getRemainingFreeBytes() const 34 | { 35 | auto base = m_base; 36 | std::uint32_t count = 0; 37 | 38 | while (base->VirtualAddress) 39 | { 40 | count += base->SizeOfBlock; 41 | base = decltype(base)((char*)base + base->SizeOfBlock); 42 | } 43 | 44 | return std::max(m_section->getVirtualSize() - count, 0); 45 | } 46 | 47 | template 48 | bool pepp::RelocationDirectory::changeRelocationType(std::uint32_t rva, RelocationType type) 49 | { 50 | auto base = m_base; 51 | std::vector entries; 52 | 53 | while (base->VirtualAddress) 54 | { 55 | int numEntries = getNumEntries(base); 56 | std::uint16_t* entry = (std::uint16_t*)(base + 1); 57 | 58 | for (int i = 0; i != numEntries; i++, entry++) 59 | { 60 | BlockEntry block(base->VirtualAddress, *entry); 61 | if (block.getRva() == rva) 62 | { 63 | *entry = craftRelocationBlockEntry(type, block.getOffset()); 64 | return true; 65 | } 66 | } 67 | 68 | base = decltype(base)((char*)base + base->SizeOfBlock); 69 | } 70 | 71 | return false; 72 | } 73 | 74 | template 75 | std::vector RelocationDirectory::getBlockEntries(int blockIdx) 76 | { 77 | auto base = m_base; 78 | int count = 0; 79 | std::vector entries; 80 | 81 | while (base->VirtualAddress) 82 | { 83 | if (count == blockIdx) 84 | { 85 | int numEntries = getNumEntries(base); 86 | std::uint16_t* entry = (std::uint16_t*)(base + 1); 87 | 88 | for (int i = 0; i != numEntries; i++, entry++) 89 | { 90 | entries.emplace_back(base->VirtualAddress, *entry); 91 | } 92 | } 93 | 94 | base = decltype(base)((char*)base + base->SizeOfBlock); 95 | count++; 96 | } 97 | 98 | return entries; 99 | } 100 | 101 | template 102 | BlockStream RelocationDirectory::createBlock(std::uint32_t rva, std::uint32_t num_entries) 103 | { 104 | std::uint32_t size = sizeof(detail::Image_t<>::RelocationBase_t) + (num_entries * sizeof(std::uint16_t)); 105 | std::uint32_t rsize = m_section->getVirtualSize(); 106 | 107 | //std::uint32_t remainingBytesLeft = getRemainingFreeBytes(); 108 | 109 | // Extend .reloc section if needed 110 | //if (remainingBytesLeft < size) 111 | //{ 112 | // if (!m_image->extendSection(m_section->getName(), size - remainingBytesLeft)) 113 | // return BlockStream(); 114 | //} 115 | 116 | auto base = m_base; 117 | 118 | // Traverse to last block 119 | while (base->VirtualAddress) 120 | { 121 | if (base->VirtualAddress == rva) 122 | { 123 | return BlockStream(base); 124 | } 125 | 126 | base = decltype(base)((char*)base + base->SizeOfBlock); 127 | } 128 | 129 | std::uint16_t* entryPtr = (std::uint16_t*)(base + 1); 130 | 131 | for (int i = 0; i <= num_entries; ++i) 132 | entryPtr[i] = 0x0; 133 | 134 | //printf("block va: 0x%x\n", base->VirtualAddress); 135 | //printf("block sz: 0x%x\n", base->SizeOfBlock); 136 | 137 | //printf("new block va: 0x%x\n", rva); 138 | //printf("new block sz: 0x%x\n", size); 139 | 140 | // Set the new block's descriptor 141 | base->VirtualAddress = rva; 142 | base->SizeOfBlock = size; 143 | 144 | return BlockStream(base); 145 | } 146 | 147 | template 148 | BlockStream pepp::RelocationDirectory::getBlockStream(std::uint32_t rva) 149 | { 150 | auto base = m_base; 151 | 152 | // Traverse to last block 153 | while (base->VirtualAddress) 154 | { 155 | if (base->VirtualAddress == rva) 156 | return BlockStream(base); 157 | 158 | base = decltype(base)((char*)base + base->SizeOfBlock); 159 | } 160 | 161 | return BlockStream(); 162 | } 163 | 164 | template 165 | void pepp::RelocationDirectory::extend(std::uint32_t num_entries) 166 | { 167 | std::uint32_t size = sizeof(detail::Image_t<>::RelocationBase_t) + num_entries * sizeof(std::uint16_t); 168 | std::uint32_t remaining_bytes = getRemainingFreeBytes(); 169 | 170 | if (remaining_bytes < size) 171 | { 172 | m_image->extendSection(m_section->getName(), size); 173 | //__debugbreak(); 174 | } 175 | } 176 | 177 | template 178 | void pepp::RelocationDirectory::forEachEntry(std::function Callback) 179 | { 180 | auto base = m_base; 181 | std::vector entries; 182 | 183 | while (base->VirtualAddress) 184 | { 185 | int numEntries = getNumEntries(base); 186 | std::uint16_t* entry = (std::uint16_t*)(base + 1); 187 | 188 | for (int i = 0; i != numEntries; i++, entry++) 189 | { 190 | BlockEntry block(base->VirtualAddress, *entry); 191 | Callback(block); 192 | } 193 | 194 | base = decltype(base)((char*)base + base->SizeOfBlock); 195 | } 196 | } 197 | 198 | template 199 | bool pepp::RelocationDirectory::isRelocationPresent(std::uint32_t rva) const 200 | { 201 | auto base = m_base; 202 | std::vector entries; 203 | 204 | while (base->VirtualAddress) 205 | { 206 | int numEntries = getNumEntries(base); 207 | std::uint16_t* entry = (std::uint16_t*)(base + 1); 208 | 209 | for (int i = 0; i != numEntries; i++, entry++) 210 | { 211 | BlockEntry block(base->VirtualAddress, *entry); 212 | if (block.getRva() == rva) 213 | return true; 214 | } 215 | 216 | base = decltype(base)((char*)base + base->SizeOfBlock); 217 | } 218 | 219 | return false; 220 | } 221 | 222 | template 223 | std::uint32_t pepp::RelocationDirectory::getTotalBlockSize() 224 | { 225 | auto base = m_base; 226 | std::uint32_t count = 0; 227 | 228 | // Traverse to last block 229 | while (base->SizeOfBlock) 230 | { 231 | if (base->SizeOfBlock >= 0x1000) 232 | break; 233 | 234 | count += base->SizeOfBlock; 235 | base = decltype(base)((char*)base + base->SizeOfBlock); 236 | } 237 | 238 | return count; 239 | } 240 | 241 | template 242 | void pepp::RelocationDirectory::increaseBlockSize(std::uint32_t rva, std::uint32_t num_entries) 243 | { 244 | auto base = m_base; 245 | 246 | while (base->VirtualAddress) 247 | { 248 | if (base->VirtualAddress == rva) 249 | { 250 | base->SizeOfBlock += (num_entries * sizeof(uint16_t)); 251 | base->SizeOfBlock = (base->SizeOfBlock + 0x3) & ~0x3; 252 | break; 253 | } 254 | 255 | base = decltype(base)((char*)base + base->SizeOfBlock); 256 | } 257 | } 258 | 259 | template 260 | void pepp::RelocationDirectory::adjustBlockToFit(uint32_t delta) 261 | { 262 | auto base = m_base; 263 | std::uint32_t count = 0; 264 | 265 | // Traverse to last block 266 | while (true) 267 | { 268 | auto next = decltype(base)((char*)base + base->SizeOfBlock); 269 | if (next->SizeOfBlock == 0 || next->SizeOfBlock >= 0x1000) 270 | break; 271 | 272 | base = next; 273 | } 274 | 275 | base->SizeOfBlock += delta; 276 | } 277 | -------------------------------------------------------------------------------- /pepp/RelocationDirectory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pepp 4 | { 5 | /* 6 | * Relocations: research32.blogspot.com/2015/01/base-relocation-table.html 7 | * Format looks like 8 | * 00 10 00 00 | RVA of Block 9 | * 28 01 00 00 | Size of Block 10 | * ?? ?? ?? ?? ..... | Entries in block 11 | * (entry count = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOC)) / sizeof(WORD)) 12 | */ 13 | enum RelocationType : std::int8_t 14 | { 15 | REL_BASED_ABSOLUTE = 0, 16 | REL_BASED_HIGH = 1, 17 | REL_BASED_LOW = 2, 18 | REL_BASED_HIGHLOW = 3, 19 | REL_BASED_HIGHADJ = 4, 20 | REL_BASED_MACHINE_SPECIFIC_5 = 5, 21 | REL_BASED_RESERVED = 6, 22 | REL_BASED_MACHINE_SPECIFIC_7 = 7, 23 | REL_BASED_MACHINE_SPECIFIC_8 = 8, 24 | REL_BASED_MACHINE_SPECIFIC_9 = 9, 25 | REL_BASED_DIR64 = 10 26 | }; 27 | 28 | constexpr std::uint16_t craftRelocationBlockEntry(RelocationType type, std::uint16_t offset) noexcept { 29 | return offset | (type << 12); 30 | } 31 | 32 | class BlockEntry 33 | { 34 | std::uint32_t m_va; 35 | std::uint16_t m_entry; 36 | public: 37 | BlockEntry(std::uint32_t va, std::uint16_t entry) 38 | : m_va(va) 39 | , m_entry(entry) 40 | { 41 | } 42 | 43 | RelocationType getType() const 44 | { 45 | return static_cast(m_entry >> 12); 46 | } 47 | 48 | std::uint32_t getOffset() const 49 | { 50 | // Single out the last 12 bits of the entry 51 | return static_cast(m_entry & ((1 << 12) - 1)); 52 | } 53 | 54 | std::uint32_t getRva() const 55 | { 56 | return m_va + getOffset(); 57 | } 58 | 59 | constexpr operator std::uint16_t() const 60 | { 61 | return m_entry; 62 | } 63 | }; 64 | 65 | class BlockStream 66 | { 67 | std::uint16_t* m_base; 68 | std::uint32_t m_idx; 69 | detail::Image_t<>::RelocationBase_t* m_reloc; 70 | public: 71 | BlockStream() 72 | : m_base(nullptr) 73 | , m_idx(0) 74 | , m_reloc(nullptr) 75 | { 76 | } 77 | 78 | BlockStream(detail::Image_t<>::RelocationBase_t* reloc) 79 | : m_base((std::uint16_t*)(reloc + 1)) 80 | , m_idx(0) 81 | , m_reloc(reloc) 82 | { 83 | while (m_base[m_idx]) 84 | { 85 | ++m_idx; 86 | } 87 | } 88 | 89 | void append(RelocationType type, std::uint16_t offset) 90 | { 91 | if (m_base == nullptr) 92 | return; 93 | 94 | if (m_idx * sizeof(uint16_t) >= (m_reloc->SizeOfBlock - sizeof(*m_reloc))) 95 | { 96 | __debugbreak(); 97 | } 98 | 99 | m_base[m_idx++] = craftRelocationBlockEntry(type, offset); 100 | } 101 | 102 | std::uint32_t index() const 103 | { 104 | return m_idx; 105 | } 106 | 107 | bool valid() const 108 | { 109 | return m_base != nullptr; 110 | } 111 | }; 112 | 113 | template 114 | class RelocationDirectory : pepp::msc::NonCopyable 115 | { 116 | friend class Image<32>; 117 | friend class Image<64>; 118 | 119 | using PatchType_t = typename detail::Image_t::Address_t; 120 | 121 | Image* m_image; 122 | detail::Image_t<>::RelocationBase_t* m_base; 123 | SectionHeader* m_section; 124 | public: 125 | 126 | int getNumBlocks() const; 127 | int getNumEntries(detail::Image_t<>::RelocationBase_t* reloc) const; 128 | std::uint32_t getRemainingFreeBytes() const; 129 | bool changeRelocationType(std::uint32_t rva, RelocationType type); 130 | std::vector getBlockEntries(int blockIdx); 131 | BlockStream createBlock(std::uint32_t rva, std::uint32_t num_entries); 132 | BlockStream getBlockStream(std::uint32_t rva); 133 | void extend(std::uint32_t num_entries); 134 | void forEachEntry(std::function Callback); 135 | bool isRelocationPresent(std::uint32_t rva) const; 136 | std::uint32_t getTotalBlockSize(); 137 | void increaseBlockSize(std::uint32_t rva, std::uint32_t num_entries); 138 | void adjustBlockToFit(uint32_t delta); 139 | detail::Image_t<>::RelocationBase_t* getBase() { return m_base; } 140 | 141 | bool isPresent() const { 142 | return m_image->getPEHdr().getOptionalHdr().getDataDir(DIRECTORY_ENTRY_BASERELOC).Size > 0; 143 | } 144 | private: 145 | //! Setup the directory 146 | void _setup(Image* image) { 147 | m_image = image; 148 | m_base = reinterpret_cast( 149 | &image->base()[image->getPEHdr().rvaToOffset( 150 | image->getPEHdr().getOptionalHdr().getDataDir(DIRECTORY_ENTRY_BASERELOC).VirtualAddress)]); 151 | m_section = 152 | &image->getSectionHdrFromVa(image->getPEHdr().getOptionalHdr().getDataDir(DIRECTORY_ENTRY_BASERELOC).VirtualAddress); 153 | } 154 | }; 155 | } -------------------------------------------------------------------------------- /pepp/SectionHeader.cpp: -------------------------------------------------------------------------------- 1 | #include "PELibrary.hpp" 2 | 3 | using namespace pepp; 4 | 5 | void SectionHeader::setName(std::string_view name) 6 | { 7 | std:memcpy(m_base.Name, name.data(), name.size()); 8 | m_base.Name[name.size()] = '\0'; 9 | } 10 | 11 | std::string SectionHeader::getName() const 12 | { 13 | char szData[9]; 14 | std::memcpy(szData, m_base.Name, sizeof m_base.Name); 15 | szData[8] = '\0'; 16 | return szData; 17 | } 18 | 19 | std::uint32_t SectionHeader::getFileAddress() const 20 | { 21 | return m_base.Misc.PhysicalAddress; 22 | } 23 | 24 | void SectionHeader::setFileAddress(std::uint32_t fileAddress) 25 | { 26 | m_base.Misc.PhysicalAddress = fileAddress; 27 | } 28 | 29 | std::uint32_t SectionHeader::getVirtualSize() const 30 | { 31 | return m_base.Misc.VirtualSize; 32 | } 33 | 34 | void SectionHeader::setVirtualSize(std::uint32_t virtualSize) 35 | { 36 | m_base.Misc.VirtualSize = virtualSize; 37 | } 38 | 39 | std::uint32_t SectionHeader::getVirtualAddress() const 40 | { 41 | return m_base.VirtualAddress; 42 | } 43 | 44 | void SectionHeader::setVirtualAddress(std::uint32_t va) 45 | { 46 | m_base.VirtualAddress = va; 47 | } 48 | 49 | std::uint32_t SectionHeader::getSizeOfRawData() const 50 | { 51 | return m_base.SizeOfRawData; 52 | } 53 | 54 | void SectionHeader::setSizeOfRawData(std::uint32_t sz) 55 | { 56 | m_base.SizeOfRawData = sz; 57 | } 58 | 59 | std::uint32_t SectionHeader::getPtrToRawData() const 60 | { 61 | return m_base.PointerToRawData; 62 | } 63 | 64 | void SectionHeader::setPointerToRawData(std::uint32_t ptr) 65 | { 66 | m_base.PointerToRawData = ptr; 67 | } 68 | 69 | std::uint32_t SectionHeader::getPtrToRelocations() const 70 | { 71 | return m_base.PointerToRelocations; 72 | } 73 | 74 | void SectionHeader::setPtrToRelocations(std::uint32_t ptr) 75 | { 76 | m_base.PointerToRelocations = ptr; 77 | } 78 | 79 | std::uint32_t SectionHeader::getPtrToLinenumbers() const 80 | { 81 | return m_base.PointerToLinenumbers; 82 | } 83 | 84 | void SectionHeader::setPtrToLinenumbers(std::uint32_t ptr) 85 | { 86 | m_base.PointerToLinenumbers = ptr; 87 | } 88 | 89 | std::uint16_t SectionHeader::getNumberOfRelocations() const 90 | { 91 | return m_base.NumberOfRelocations; 92 | } 93 | 94 | void SectionHeader::setNumberOfRelocations(std::uint16_t num) 95 | { 96 | m_base.NumberOfRelocations = num; 97 | } 98 | 99 | std::uint16_t SectionHeader::getNumberOfLinenumbers() const 100 | { 101 | return m_base.NumberOfLinenumbers; 102 | } 103 | 104 | void SectionHeader::setNumberOfLinenumbers(std::uint16_t num) 105 | { 106 | m_base.NumberOfLinenumbers = num; 107 | } 108 | 109 | std::uint32_t SectionHeader::getCharacteristics() const 110 | { 111 | return m_base.Characteristics; 112 | } 113 | 114 | void SectionHeader::setCharacteristics(std::uint32_t chars) 115 | { 116 | m_base.Characteristics = chars; 117 | } 118 | -------------------------------------------------------------------------------- /pepp/SectionHeader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pepp 4 | { 5 | template 6 | class Image; 7 | 8 | enum SectionCharacteristics : std::uint32_t 9 | { 10 | SCN_TYPE_NO_PAD = 0x00000008, // Reserved. 11 | SCN_CNT_CODE = 0x00000020, // Section contains code. 12 | SCN_CNT_INITIALIZED_DATA = 0x00000040, // Section contains initialized data. 13 | SCN_CNT_UNINITIALIZED_DATA = 0x00000080, // Section contains uninitialized data. 14 | SCN_LNK_OTHER = 0x00000100, // Reserved. 15 | SCN_LNK_INFO = 0x00000200, // Section contains comments or some other type of information. 16 | SCN_LNK_REMOVE = 0x00000800, // Section contents will not become part of image. 17 | SCN_LNK_COMDAT = 0x00001000, // Section contents comdat. 18 | SCN_NO_DEFER_SPEC_EXC = 0x00004000, // Reset speculative exceptions handling bits in the TLB entries for this section. 19 | SCN_GPREL = 0x00008000, // Section content can be accessed relative to GP 20 | SCN_MEM_FARDATA = 0x00008000, 21 | SCN_MEM_PURGEABLE = 0x00020000, 22 | SCN_MEM_16BIT = 0x00020000, 23 | SCN_MEM_LOCKED = 0x00040000, 24 | SCN_MEM_PRELOAD = 0x00080000, 25 | SCN_ALIGN_1BYTES = 0x00100000, // 26 | SCN_ALIGN_2BYTES = 0x00200000, // 27 | SCN_ALIGN_4BYTES = 0x00300000, // 28 | SCN_ALIGN_8BYTES = 0x00400000, // 29 | SCN_ALIGN_16BYTES = 0x00500000, // Default alignment if no others are specified. 30 | SCN_ALIGN_32BYTES = 0x00600000, // 31 | SCN_ALIGN_64BYTES = 0x00700000, // 32 | SCN_ALIGN_128BYTES = 0x00800000, // 33 | SCN_ALIGN_256BYTES = 0x00900000, // 34 | SCN_ALIGN_512BYTES = 0x00A00000, // 35 | SCN_ALIGN_1024BYTES = 0x00B00000, // 36 | SCN_ALIGN_2048BYTES = 0x00C00000, // 37 | SCN_ALIGN_4096BYTES = 0x00D00000, // 38 | SCN_ALIGN_8192BYTES = 0x00E00000, // 39 | SCN_ALIGN_MASK = 0x00F00000, 40 | SCN_LNK_NRELOC_OVFL = 0x01000000, // Section contains extended relocations. 41 | SCN_MEM_DISCARDABLE = 0x02000000, // Section can be discarded. 42 | SCN_MEM_NOT_CACHED = 0x04000000, // Section is not cachable. 43 | SCN_MEM_NOT_PAGED = 0x08000000, // Section is not pageable. 44 | SCN_MEM_SHARED = 0x10000000, // Section is shareable. 45 | SCN_MEM_EXECUTE = 0x20000000, // Section is executable. 46 | SCN_MEM_READ = 0x40000000, // Section is readable. 47 | SCN_MEM_WRITE = 0x80000000 // Section is writeable. 48 | }; 49 | 50 | class SectionHeader 51 | { 52 | friend class Image<32>; 53 | friend class Image<64>; 54 | 55 | detail::Image_t<>::SectionHeader_t m_base; 56 | public: 57 | SectionHeader() { 58 | memcpy(m_base.Name, ".dummy", sizeof(".dummy")); 59 | } 60 | 61 | //! Getter/setter for SectionHeader.Name 62 | void setName(std::string_view name); 63 | std::string getName() const; 64 | 65 | //! Getter/setters for SectionHeader.Misc 66 | std::uint32_t getFileAddress() const; 67 | void setFileAddress(std::uint32_t fileAddress); 68 | 69 | std::uint32_t getVirtualSize() const; 70 | void setVirtualSize(std::uint32_t virtualSize); 71 | 72 | //! Getter/setter for VirtualAddress 73 | std::uint32_t getVirtualAddress() const; 74 | void setVirtualAddress(std::uint32_t va); 75 | 76 | //! Getter/setter for SizeOfRawData 77 | std::uint32_t getSizeOfRawData() const; 78 | void setSizeOfRawData(std::uint32_t sz); 79 | 80 | //! Getter/setter for SizeOfRawData 81 | std::uint32_t getPtrToRawData() const; 82 | void setPointerToRawData(std::uint32_t ptr); 83 | 84 | //! Getter/setter for PointerToRelocations 85 | std::uint32_t getPtrToRelocations() const; 86 | void setPtrToRelocations(std::uint32_t ptr); 87 | 88 | //! Getter/setter for PointerToLinenumbers 89 | std::uint32_t getPtrToLinenumbers() const; 90 | void setPtrToLinenumbers(std::uint32_t ptr); 91 | 92 | //! Getter/setter for NumberOfRelocations 93 | std::uint16_t getNumberOfRelocations() const; 94 | void setNumberOfRelocations(std::uint16_t num); 95 | 96 | //! Getter/setter for 97 | std::uint16_t getNumberOfLinenumbers() const; 98 | void setNumberOfLinenumbers(std::uint16_t num); 99 | 100 | //! Getter/setter for 101 | std::uint32_t getCharacteristics() const; 102 | void setCharacteristics(std::uint32_t chars); 103 | 104 | 105 | // Section utility functions 106 | void addCharacteristic(std::uint32_t dwChar) { 107 | m_base.Characteristics |= dwChar; 108 | } 109 | void stripCharacteristic(std::uint32_t dwChar) { 110 | m_base.Characteristics &= ~dwChar; 111 | } 112 | bool isReadable() const { 113 | return getCharacteristics() & SCN_MEM_READ; 114 | } 115 | bool isWriteable() const { 116 | return getCharacteristics() & SCN_MEM_WRITE; 117 | } 118 | bool isExecutable() const { 119 | return getCharacteristics() & SCN_MEM_EXECUTE; 120 | } 121 | bool hasVirtualAddress(std::uint32_t va) const { 122 | return va >= m_base.VirtualAddress && va < m_base.VirtualAddress + m_base.Misc.VirtualSize; 123 | } 124 | bool hasOffset(std::uint32_t offset) const { 125 | return offset >= m_base.PointerToRawData && offset < m_base.PointerToRawData + m_base.SizeOfRawData; 126 | } 127 | }; 128 | 129 | static_assert(sizeof(SectionHeader) == sizeof(detail::Image_t<>::SectionHeader_t), "Invalid size of SectionHeader"); 130 | } -------------------------------------------------------------------------------- /pepp/misc/Address.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include "Concept.hpp" 5 | 6 | namespace pepp { 7 | template 8 | class Address 9 | { 10 | value_t m_address; 11 | public: 12 | template 13 | constexpr Address(T value = 0) noexcept requires pepp::msc::MemoryAddress 14 | : m_address((value_t)(value)) 15 | { 16 | } 17 | 18 | constexpr Address(const Address& rhs) = default; 19 | ~Address() = default; 20 | 21 | Address& operator=(const Address& rhs) noexcept 22 | { 23 | m_address = rhs.m_address; 24 | return *this; 25 | } 26 | 27 | constexpr explicit operator std::uintptr_t() const noexcept 28 | { 29 | return m_address; 30 | } 31 | 32 | constexpr value_t uintptr() const noexcept 33 | { 34 | return m_address; 35 | } 36 | 37 | constexpr size_t size() const noexcept 38 | { 39 | return sizeof value_t; 40 | } 41 | 42 | template 43 | C* ptr() noexcept requires pepp::msc::MemoryAddress 44 | { 45 | return reinterpret_cast(m_address); 46 | } 47 | 48 | template 49 | C* ptr() const noexcept requires pepp::msc::MemoryAddress 50 | { 51 | return reinterpret_cast(m_address); 52 | } 53 | 54 | template 55 | C as() noexcept 56 | { 57 | return (C)m_address; 58 | } 59 | 60 | template 61 | C as() const noexcept 62 | { 63 | return (C)m_address; 64 | } 65 | 66 | template 67 | C& deref() noexcept 68 | { 69 | return *reinterpret_cast(m_address); 70 | } 71 | 72 | template 73 | C& deref(size_t idx) noexcept 74 | { 75 | return *reinterpret_cast(m_address + idx); 76 | } 77 | 78 | constexpr bool operator==(const Address& rhs) const noexcept 79 | { 80 | return m_address == rhs.m_address; 81 | } 82 | 83 | constexpr bool operator!=(const Address& rhs) const noexcept 84 | { 85 | return m_address != rhs.m_address; 86 | } 87 | 88 | constexpr bool operator>=(const Address& rhs) const noexcept 89 | { 90 | return m_address >= rhs.m_address; 91 | } 92 | 93 | constexpr bool operator<=(const Address& rhs) const noexcept 94 | { 95 | return m_address <= rhs.m_address; 96 | } 97 | 98 | constexpr bool operator>(const Address& rhs) const noexcept 99 | { 100 | return m_address > rhs.m_address; 101 | } 102 | 103 | constexpr bool operator<(const Address& rhs) const noexcept 104 | { 105 | return m_address < rhs.m_address; 106 | } 107 | 108 | constexpr Address operator+(const Address& rhs) const noexcept 109 | { 110 | return m_address + rhs.m_address; 111 | } 112 | 113 | constexpr Address operator-(const Address& rhs) const noexcept 114 | { 115 | return m_address - rhs.m_address; 116 | } 117 | 118 | constexpr Address operator*(const Address& rhs) const noexcept 119 | { 120 | return m_address * rhs.m_address; 121 | } 122 | 123 | constexpr Address operator/(const Address& rhs) const noexcept 124 | { 125 | return m_address / rhs.m_address; 126 | } 127 | 128 | constexpr Address& operator+=(const Address& rhs) noexcept 129 | { 130 | m_address += rhs.m_address; 131 | return *this; 132 | } 133 | 134 | constexpr Address& operator-=(const Address& rhs) noexcept 135 | { 136 | m_address -= rhs.m_address; 137 | return *this; 138 | } 139 | 140 | constexpr Address& operator*=(const Address& rhs) noexcept 141 | { 142 | m_address *= rhs.m_address; 143 | return *this; 144 | } 145 | 146 | constexpr Address operator>>(const Address& rhs) const noexcept 147 | { 148 | return m_address >> rhs.m_address; 149 | } 150 | 151 | constexpr Address operator<<(const Address& rhs) const noexcept 152 | { 153 | return m_address << rhs.m_address; 154 | } 155 | 156 | constexpr Address operator^(const Address& rhs) const noexcept 157 | { 158 | return m_address ^ rhs.m_address; 159 | } 160 | 161 | constexpr Address operator&(const Address& rhs) const noexcept 162 | { 163 | return m_address & rhs.m_address; 164 | } 165 | 166 | constexpr Address operator|(const Address& rhs) const noexcept 167 | { 168 | return m_address | rhs.m_address; 169 | } 170 | 171 | /* 172 | /! 173 | */ 174 | 175 | constexpr Address& operator>>=(const Address& rhs) noexcept 176 | { 177 | m_address >>= rhs.m_address; 178 | return *this; 179 | } 180 | 181 | constexpr Address& operator<<=(const Address& rhs) noexcept 182 | { 183 | m_address <<= rhs.m_address; 184 | return *this; 185 | } 186 | 187 | constexpr Address& operator^=(const Address& rhs) noexcept 188 | { 189 | m_address ^= rhs.m_address; 190 | return *this; 191 | } 192 | 193 | constexpr Address& operator&=(const Address& rhs) noexcept 194 | { 195 | m_address &= rhs.m_address; 196 | return *this; 197 | } 198 | 199 | constexpr Address& operator|=(const Address& rhs) noexcept 200 | { 201 | m_address |= rhs.m_address; 202 | return *this; 203 | } 204 | }; 205 | } -------------------------------------------------------------------------------- /pepp/misc/ByteVector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pepp::mem { 6 | class ByteVector : public std::vector 7 | { 8 | public: 9 | // 10 | //! Used for pushing a single byte. 11 | //! Example: push(0x55) 12 | // 13 | constexpr ByteVector& push_byte(std::uint8_t byte) { 14 | push_back(byte); 15 | return *this; 16 | } 17 | 18 | // 19 | //! Used for pushing raw data 20 | //! Example: push_raw(data, size) 21 | // 22 | template 23 | constexpr ByteVector& push_raw(const T* data, std::size_t rsize) { 24 | resize(size() + rsize); 25 | memcpy(&(*this)[size() - rsize], data, rsize); 26 | return *this; 27 | } 28 | 29 | // 30 | //! Used for pushing a container. 31 | //! Example: push_container(array/vector/etc) 32 | // 33 | template 34 | constexpr ByteVector& push_container(const T& container) { 35 | for (auto c : container) 36 | push_back(static_cast(c)); 37 | return *this; 38 | } 39 | 40 | // 41 | //! Used for pushing a raw array. 42 | //! Example: push_array({0x55, 0x8b, 0xec}) 43 | // 44 | template 45 | constexpr ByteVector& push_array(const std::initializer_list& list) requires std::convertible_to { 46 | for (auto c : list) 47 | push_back(static_cast(c)); 48 | return *this; 49 | } 50 | 51 | // 52 | //! Used for pushing a iterator range. 53 | //! Example: push_range(c.begin(), c.end()) 54 | // 55 | template 56 | constexpr ByteVector& push_range(iterator_t first, iterator_t last) { 57 | while (first != last) { 58 | push_back(static_cast(*first)); 59 | first++; 60 | } 61 | return *this; 62 | } 63 | 64 | // 65 | //! Used for pushing arguments. 66 | //! Example: push_args(0x55, 0x8b, 0xec) 67 | // 68 | constexpr ByteVector& push_args(std::convertible_to auto... args) { 69 | for (auto arg : { args... }) 70 | push_back(static_cast(arg)); 71 | return *this; 72 | } 73 | 74 | // 75 | //! Push a double dword 76 | //! Example: push_dword(0xdeadbeef) .. 77 | // 78 | constexpr ByteVector& push_dword(std::uint32_t dword) { 79 | push_back(static_cast(dword & 0xFF)); 80 | push_back(static_cast((dword >> 8) & 0xFF)); 81 | push_back(static_cast((dword >> 16) & 0xFF)); 82 | push_back(static_cast((dword >> 24) & 0xFF)); 83 | return *this; 84 | } 85 | 86 | // 87 | //! Push a quad word 88 | //! Example: push_qword(0x1122334411223344) .. 89 | // 90 | constexpr ByteVector& push_qword(std::uint64_t qword) { 91 | push_back(static_cast(qword & 0xFF)); 92 | push_back(static_cast((qword >> 8) & 0xFF)); 93 | push_back(static_cast((qword >> 16) & 0xFF)); 94 | push_back(static_cast((qword >> 24) & 0xFF)); 95 | push_back(static_cast((qword >> 32) & 0xFF)); 96 | push_back(static_cast((qword >> 40) & 0xFF)); 97 | push_back(static_cast((qword >> 48) & 0xFF)); 98 | push_back(static_cast((qword >> 56) & 0xFF)); 99 | return *this; 100 | } 101 | 102 | // 103 | //! Copy in data at specified index 104 | //! Example: copy_data(0, data, size) 105 | // 106 | template 107 | ByteVector& copy_data(std::size_t idx, const T* data, std::size_t rsize) { 108 | if (size() > idx) { 109 | std::memcpy(&(*this)[idx], data, rsize); 110 | } 111 | return *this; 112 | } 113 | 114 | // 115 | //! Add/insert in data at specified index 116 | //! Example: insert_data(0, data, size) 117 | // 118 | template 119 | ByteVector& insert_data(std::size_t idx, const T* data, std::size_t rsize) { 120 | if (size() <= idx) 121 | resize(idx); 122 | insert(begin() + idx, rsize, 0x0); 123 | memcpy(&(*this)[idx], data, rsize); 124 | return *this; 125 | } 126 | 127 | // 128 | //! Interpret data as T 129 | //! Example: as(0x0/) 130 | // 131 | template 132 | T as(std::size_t idx = 0x0) const { 133 | return (T)(&at(idx)); 134 | } 135 | template 136 | T as(std::size_t idx = 0x0) { 137 | return (T)(&at(idx)); 138 | } 139 | 140 | // 141 | //! Dereference bytes as T 142 | //! Example: deref(0x0/) 143 | // 144 | template 145 | T deref(std::size_t idx = 0x0) const { 146 | return *(T*)(&at(idx)); 147 | } 148 | template 149 | T& deref(std::size_t idx = 0x0) { 150 | return *(T*)(&at(idx)); 151 | } 152 | }; 153 | } -------------------------------------------------------------------------------- /pepp/misc/Concept.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pepp::msc 6 | { 7 | template 8 | concept Arithmetic = std::is_arithmetic_v; 9 | template 10 | concept MemoryAddress = std::is_integral_v || std::is_pointer_v; 11 | } -------------------------------------------------------------------------------- /pepp/misc/File.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "File.hpp" 7 | 8 | namespace pepp::io { 9 | 10 | File::File(std::string_view filename, int flags) 11 | : m_filename(filename) 12 | , m_flags(flags) 13 | { 14 | } 15 | 16 | File::File(File&& other) 17 | : m_filename(std::move(other.m_filename)) 18 | , m_flags(other.m_flags) 19 | { 20 | } 21 | 22 | void File::Write(std::string_view text) 23 | { 24 | m_out_file.open(m_filename, m_flags & ~kFileInput); 25 | if (m_out_file.is_open()) { 26 | m_out_file << text; 27 | m_out_file.close(); 28 | } 29 | } 30 | 31 | void File::Write(const std::vector& data) 32 | { 33 | m_out_file.open(m_filename, m_flags & ~kFileInput); 34 | if (m_out_file.is_open()) { 35 | m_out_file.write((const char*)data.data(), data.size()); 36 | m_out_file.close(); 37 | } 38 | } 39 | 40 | void File::Write(void* data, size_t size) 41 | { 42 | m_out_file.open(m_filename, m_flags & ~kFileInput); 43 | if (m_out_file.is_open()) 44 | { 45 | m_out_file.write((const char*)data, size); 46 | m_out_file.close(); 47 | } 48 | } 49 | 50 | bool File::Exists() 51 | { 52 | return std::filesystem::exists(m_filename); 53 | } 54 | 55 | std::vector File::Read() 56 | { 57 | std::vector file_buffer; 58 | 59 | m_in_file.open(m_filename, m_flags & ~kFileOutput); 60 | 61 | if (m_in_file.is_open()) { 62 | file_buffer = std::vector(std::istreambuf_iterator(m_in_file), {}); 63 | m_in_file.close(); 64 | } 65 | 66 | return file_buffer; 67 | } 68 | 69 | std::uintmax_t File::GetSize() 70 | { 71 | return std::filesystem::file_size(m_filename); 72 | } 73 | 74 | File& io::File::operator=(File&& rhs) 75 | { 76 | if (this == &rhs) 77 | return *this; 78 | 79 | std::swap(m_filename, rhs.m_filename); 80 | std::swap(m_flags, rhs.m_flags); 81 | return *this; 82 | } 83 | 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /pepp/misc/File.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pepp::io 6 | { 7 | enum FileFlags { 8 | kFileInput = 1, 9 | kFileOutput = 2, 10 | kFileApp = 8, 11 | kFileTrunc = 16, 12 | kFileBinary = 32 13 | }; 14 | 15 | class File { 16 | public: 17 | File() = default; 18 | File(const File& other) = default; 19 | 20 | File(std::string_view filename, int flags); 21 | File(File&& other); 22 | 23 | void Write(std::string_view text); 24 | void Write(const std::vector& data); 25 | void Write(void* data, size_t size); 26 | bool Exists(); 27 | std::vector Read(); 28 | std::uintmax_t GetSize(); 29 | 30 | File& operator=(File&& rhs); 31 | 32 | private: 33 | std::string m_filename; 34 | int m_flags; 35 | std::ofstream m_out_file; 36 | std::ifstream m_in_file; 37 | }; 38 | } -------------------------------------------------------------------------------- /pepp/misc/NonCopyable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pepp::msc 4 | { 5 | class NonCopyable { 6 | public: 7 | NonCopyable() = default; 8 | NonCopyable(const NonCopyable&) = delete; 9 | NonCopyable& operator=(const NonCopyable&) = delete; 10 | }; 11 | } --------------------------------------------------------------------------------