From 2c775e5d6a62f18b954121517fd74c99af6cefa2 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 21 Oct 2019 09:00:54 -0400 Subject: [PATCH 01/10] Directory entry retrieval (#90) * pe-parser-library: Directory entry extraction Also runs clang-format on all library files. * dump-pe: Refactor, clang-format * pe-parser-library: Use enum for directory kinds * travis: Refactor * travis: Fixup stages * travis: Fix matrix * examples, pe-parser-library, pepy: clang-format * travis: Use minimal for lint * travis: Use find * clang-format: Remove old option * travis: More experimentation * travis: Move addons * travis: Remove coverity * travis: Hackery * travis: Move addons up * .travis: clang-format-8 * examples: clang-format * travis: Fix homebrew * CONTRIBUTING: Add contrib guidelines * travis: Build python ext, reenable coverity Remove old build files. * travis: Re-add coverity secret * travis: Build with coverity in a separate dir --- .clang-format | 1 - .travis.yml | 78 ++++-- CONTRIBUTING.md | 26 ++ dump-pe/main.cpp | 239 +++++++++--------- dump-prog/meson.build | 7 - examples/peaddrconv/main.cpp | 5 +- meson.build | 8 - parser-library/meson.build | 11 - .../include/parser-library/nt-headers.h | 131 ++++++++-- .../include/parser-library/parse.h | 9 +- .../include/parser-library/to_string.h | 2 +- pe-parser-library/src/parse.cpp | 141 ++++++++--- pe-parser-library/src/unicode_codecvt.cpp | 15 +- pe-parser-library/src/unicode_icu.cpp | 3 +- python/pepy.cpp | 11 +- travis.sh | 210 --------------- 16 files changed, 441 insertions(+), 456 deletions(-) create mode 100644 CONTRIBUTING.md delete mode 100644 dump-prog/meson.build delete mode 100644 meson.build delete mode 100644 parser-library/meson.build delete mode 100755 travis.sh diff --git a/.clang-format b/.clang-format index 02e245f..207911d 100644 --- a/.clang-format +++ b/.clang-format @@ -2,7 +2,6 @@ AlignEscapedNewlinesLeft: true AllowShortFunctionsOnASingleLine: false BinPackArguments: false BinPackParameters: false -BreakBeforeBraces: Attach ColumnLimit: 80 IndentCaseLabels: true IndentWidth: 2 diff --git a/.travis.yml b/.travis.yml index ff11b28..a9230be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,32 +1,56 @@ -language: cpp +__build_stage_script: &__build_stage_script + stage: build + language: cpp + script: + - mkdir build && cd build + - cmake .. + - make + - cd ../python + - python2 setup.py build + - python3 setup.py build + addons: + apt: + packages: + - cmake + - python2.7 + - python-dev + - build-essential + - realpath + - libicu-dev + homebrew: + packages: + - cmake + coverity_scan: + project: + name: "trailofbits/pe-parse" + description: "Principled, lightweight C/C++ PE parser" + notification_email: dan@trailofbits.com + build_command_prepend: mkdir cov_build && cd cov_build && cmake .. + build_command: make + branch_pattern: master -os: -- linux -- osx - -compiler: -- clang -- gcc - -before_install: -- ./travis.sh "$TRAVIS_OS_NAME" initialize -script: -- ./travis.sh "$TRAVIS_OS_NAME" build - -matrix: - exclude: - - compiler: gcc - os: osx +jobs: + include: + - stage: lint + language: minimal + dist: bionic + addons: + apt: + packages: + - clang-format-8 + script: + - find . \( -name '*.h' \) -or \( -name '*.cpp' \) | xargs clang-format -i -style=file + - git diff --exit-code + - <<: *__build_stage_script + os: linux + compiler: clang + - <<: *__build_stage_script + os: linux + compiler: gcc + - <<: *__build_stage_script + os: osx + compiler: clang env: global: - secure: "O+BGqz4ugoVIJbQTh0dJjKRrsSVzkCYSe0WpRzEWK3l8Mw7hqX300g81TxRwTzN2zfUsROMzaeGaXWfGzYakgW59K1WIioaczxtv2MzzUQTbqzJPa+qQoP9bk/b2wJ5jcOL965/rudRju4UiIwuIgzDAMN3nAfIEJgV/2zANLIg=" - -addons: - coverity_scan: - project: - name: "trailofbits/pe-parse" - description: "Principled, lightweight C/C++ PE parser" - notification_email: dan@trailofbits.com - build_command: "./travis.sh linux build" - branch_pattern: master diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c08017d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,26 @@ +Contributing to pe-parse +======================== + +Hello, and welcome to the contributing guidelines for pe-parse! + +For general building instructions, see the [README](README.md). + +For licensing information, see the [LICENSE](LICENSE.txt) file. pe-parse includes a CLA; you will be +automatically prompted to sign it during your first PR. + +## General contribution guidelines + +* Your changes should be valid C++11 +* Your changes should work across all major compiler vendors (GCC, Clang, MSVC) and all +major operating systems (Linux, macOS, Windows) +* Your changes should be auto-formatted with `clang-format -style=file` +* Your changes should not introduce *mandatory* third-party dependencies + +## Adding features + +Feature additions to either the parsing library or `dump-pe` are welcome! + +Check out the following issue labels for some contribution ideas: + +* [Enhancements](https://github.com/trailofbits/pe-parse/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) +* [Hacktoberfest](https://github.com/trailofbits/pe-parse/issues?q=is%3Aissue+is%3Aopen+label%3Ahacktoberfest) diff --git a/dump-pe/main.cpp b/dump-pe/main.cpp index e1c8c3e..53d252f 100644 --- a/dump-pe/main.cpp +++ b/dump-pe/main.cpp @@ -22,10 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include -#include -#include #include +#include +#include +#include #include @@ -232,8 +232,8 @@ int printRich(void *N, rich_entry r) { std::cout << std::setw(10) << "ProdId:" << std::setw(7) << r.ProductId; std::cout << std::setw(10) << "Build:" << std::setw(7) << r.BuildNumber; - std::cout << std::setw(10) << "Name:" - << std::setw(20) << GetRichProductName(r.ProductId, r.BuildNumber); + std::cout << std::setw(10) << "Name:" << std::setw(20) + << GetRichProductName(r.ProductId, r.BuildNumber); std::cout << std::setw(10) << "Count:" << std::setw(7) << r.Count << "\n"; return 0; } @@ -275,18 +275,17 @@ int printSecs(void *N, if (data) std::cout << "Sec Size: " << std::dec << data->bufLen << "\n"; else - std::cout << "Sec Size: 0" << "\n"; + std::cout << "Sec Size: 0" + << "\n"; return 0; } -#define DUMP_FIELD(x) \ - std::cout << "" #x << ": 0x"; \ - std::cout << std::hex << static_cast(p->peHeader.nt.x) \ - << "\n"; -#define DUMP_DEC_FIELD(x) \ - std::cout << "" #x << ": "; \ - std::cout << std::dec << static_cast(p->peHeader.nt.x) \ - << "\n"; +#define DUMP_FIELD(x) \ + std::cout << "" #x << ": 0x"; \ + std::cout << std::hex << static_cast(p->peHeader.nt.x) << "\n"; +#define DUMP_DEC_FIELD(x) \ + std::cout << "" #x << ": "; \ + std::cout << std::dec << static_cast(p->peHeader.nt.x) << "\n"; int main(int argc, char *argv[]) { if (argc != 2 || (argc == 2 && std::strcmp(argv[1], "--help") == 0)) { @@ -297,119 +296,127 @@ int main(int argc, char *argv[]) { } parsed_pe *p = ParsePEFromFile(argv[1]); + if (p == nullptr) { + std::cout << "Error: " << GetPEErr() << " (" << GetPEErrString() << ")" + << "\n"; + std::cout << "Location: " << GetPEErrLoc() << "\n"; + return 1; + } - if (p != NULL) { - // Print Rich header info - if(p->peHeader.rich.isPresent) { - std::cout << "Rich header: present\n"; - IterRich(p, printRich, NULL); - } else { - std::cout << "Rich header: not present\n"; - } - // print out some things - DUMP_FIELD(Signature); - DUMP_FIELD(FileHeader.Machine); - DUMP_FIELD(FileHeader.NumberOfSections); - DUMP_DEC_FIELD(FileHeader.TimeDateStamp); - DUMP_FIELD(FileHeader.PointerToSymbolTable); - DUMP_DEC_FIELD(FileHeader.NumberOfSymbols); - DUMP_FIELD(FileHeader.SizeOfOptionalHeader); - DUMP_FIELD(FileHeader.Characteristics); - if (p->peHeader.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) { - DUMP_FIELD(OptionalHeader.Magic); - DUMP_DEC_FIELD(OptionalHeader.MajorLinkerVersion); - DUMP_DEC_FIELD(OptionalHeader.MinorLinkerVersion); - DUMP_FIELD(OptionalHeader.SizeOfCode); - DUMP_FIELD(OptionalHeader.SizeOfInitializedData); - DUMP_FIELD(OptionalHeader.SizeOfUninitializedData); - DUMP_FIELD(OptionalHeader.AddressOfEntryPoint); - DUMP_FIELD(OptionalHeader.BaseOfCode); - DUMP_FIELD(OptionalHeader.BaseOfData); - DUMP_FIELD(OptionalHeader.ImageBase); - DUMP_FIELD(OptionalHeader.SectionAlignment); - DUMP_FIELD(OptionalHeader.FileAlignment); - DUMP_DEC_FIELD(OptionalHeader.MajorOperatingSystemVersion); - DUMP_DEC_FIELD(OptionalHeader.MinorOperatingSystemVersion); - DUMP_DEC_FIELD(OptionalHeader.Win32VersionValue); - DUMP_FIELD(OptionalHeader.SizeOfImage); - DUMP_FIELD(OptionalHeader.SizeOfHeaders); - DUMP_FIELD(OptionalHeader.CheckSum); - DUMP_FIELD(OptionalHeader.Subsystem); - DUMP_FIELD(OptionalHeader.DllCharacteristics); - DUMP_FIELD(OptionalHeader.SizeOfStackReserve); - DUMP_FIELD(OptionalHeader.SizeOfStackCommit); - DUMP_FIELD(OptionalHeader.SizeOfHeapReserve); - DUMP_FIELD(OptionalHeader.SizeOfHeapCommit); - DUMP_FIELD(OptionalHeader.LoaderFlags); - DUMP_DEC_FIELD(OptionalHeader.NumberOfRvaAndSizes); - } else { - DUMP_FIELD(OptionalHeader64.Magic); - DUMP_DEC_FIELD(OptionalHeader64.MajorLinkerVersion); - DUMP_DEC_FIELD(OptionalHeader64.MinorLinkerVersion); - DUMP_FIELD(OptionalHeader64.SizeOfCode); - DUMP_FIELD(OptionalHeader64.SizeOfInitializedData); - DUMP_FIELD(OptionalHeader64.SizeOfUninitializedData); - DUMP_FIELD(OptionalHeader64.AddressOfEntryPoint); - DUMP_FIELD(OptionalHeader64.BaseOfCode); - DUMP_FIELD(OptionalHeader64.ImageBase); - DUMP_FIELD(OptionalHeader64.SectionAlignment); - DUMP_FIELD(OptionalHeader64.FileAlignment); - DUMP_DEC_FIELD(OptionalHeader64.MajorOperatingSystemVersion); - DUMP_DEC_FIELD(OptionalHeader64.MinorOperatingSystemVersion); - DUMP_DEC_FIELD(OptionalHeader64.Win32VersionValue); - DUMP_FIELD(OptionalHeader64.SizeOfImage); - DUMP_FIELD(OptionalHeader64.SizeOfHeaders); - DUMP_FIELD(OptionalHeader64.CheckSum); - DUMP_FIELD(OptionalHeader64.Subsystem); - DUMP_FIELD(OptionalHeader64.DllCharacteristics); - DUMP_FIELD(OptionalHeader64.SizeOfStackReserve); - DUMP_FIELD(OptionalHeader64.SizeOfStackCommit); - DUMP_FIELD(OptionalHeader64.SizeOfHeapReserve); - DUMP_FIELD(OptionalHeader64.SizeOfHeapCommit); - DUMP_FIELD(OptionalHeader64.LoaderFlags); - DUMP_DEC_FIELD(OptionalHeader64.NumberOfRvaAndSizes); - } + // Print Rich header info + if (p->peHeader.rich.isPresent) { + std::cout << "Rich header: present\n"; + IterRich(p, printRich, NULL); + } else { + std::cout << "Rich header: not present\n"; + } + // print out some things + DUMP_FIELD(Signature); + DUMP_FIELD(FileHeader.Machine); + DUMP_FIELD(FileHeader.NumberOfSections); + DUMP_DEC_FIELD(FileHeader.TimeDateStamp); + DUMP_FIELD(FileHeader.PointerToSymbolTable); + DUMP_DEC_FIELD(FileHeader.NumberOfSymbols); + DUMP_FIELD(FileHeader.SizeOfOptionalHeader); + DUMP_FIELD(FileHeader.Characteristics); + if (p->peHeader.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) { + DUMP_FIELD(OptionalHeader.Magic); + DUMP_DEC_FIELD(OptionalHeader.MajorLinkerVersion); + DUMP_DEC_FIELD(OptionalHeader.MinorLinkerVersion); + DUMP_FIELD(OptionalHeader.SizeOfCode); + DUMP_FIELD(OptionalHeader.SizeOfInitializedData); + DUMP_FIELD(OptionalHeader.SizeOfUninitializedData); + DUMP_FIELD(OptionalHeader.AddressOfEntryPoint); + DUMP_FIELD(OptionalHeader.BaseOfCode); + DUMP_FIELD(OptionalHeader.BaseOfData); + DUMP_FIELD(OptionalHeader.ImageBase); + DUMP_FIELD(OptionalHeader.SectionAlignment); + DUMP_FIELD(OptionalHeader.FileAlignment); + DUMP_DEC_FIELD(OptionalHeader.MajorOperatingSystemVersion); + DUMP_DEC_FIELD(OptionalHeader.MinorOperatingSystemVersion); + DUMP_DEC_FIELD(OptionalHeader.Win32VersionValue); + DUMP_FIELD(OptionalHeader.SizeOfImage); + DUMP_FIELD(OptionalHeader.SizeOfHeaders); + DUMP_FIELD(OptionalHeader.CheckSum); + DUMP_FIELD(OptionalHeader.Subsystem); + DUMP_FIELD(OptionalHeader.DllCharacteristics); + DUMP_FIELD(OptionalHeader.SizeOfStackReserve); + DUMP_FIELD(OptionalHeader.SizeOfStackCommit); + DUMP_FIELD(OptionalHeader.SizeOfHeapReserve); + DUMP_FIELD(OptionalHeader.SizeOfHeapCommit); + DUMP_FIELD(OptionalHeader.LoaderFlags); + DUMP_DEC_FIELD(OptionalHeader.NumberOfRvaAndSizes); + } else { + DUMP_FIELD(OptionalHeader64.Magic); + DUMP_DEC_FIELD(OptionalHeader64.MajorLinkerVersion); + DUMP_DEC_FIELD(OptionalHeader64.MinorLinkerVersion); + DUMP_FIELD(OptionalHeader64.SizeOfCode); + DUMP_FIELD(OptionalHeader64.SizeOfInitializedData); + DUMP_FIELD(OptionalHeader64.SizeOfUninitializedData); + DUMP_FIELD(OptionalHeader64.AddressOfEntryPoint); + DUMP_FIELD(OptionalHeader64.BaseOfCode); + DUMP_FIELD(OptionalHeader64.ImageBase); + DUMP_FIELD(OptionalHeader64.SectionAlignment); + DUMP_FIELD(OptionalHeader64.FileAlignment); + DUMP_DEC_FIELD(OptionalHeader64.MajorOperatingSystemVersion); + DUMP_DEC_FIELD(OptionalHeader64.MinorOperatingSystemVersion); + DUMP_DEC_FIELD(OptionalHeader64.Win32VersionValue); + DUMP_FIELD(OptionalHeader64.SizeOfImage); + DUMP_FIELD(OptionalHeader64.SizeOfHeaders); + DUMP_FIELD(OptionalHeader64.CheckSum); + DUMP_FIELD(OptionalHeader64.Subsystem); + DUMP_FIELD(OptionalHeader64.DllCharacteristics); + DUMP_FIELD(OptionalHeader64.SizeOfStackReserve); + DUMP_FIELD(OptionalHeader64.SizeOfStackCommit); + DUMP_FIELD(OptionalHeader64.SizeOfHeapReserve); + DUMP_FIELD(OptionalHeader64.SizeOfHeapCommit); + DUMP_FIELD(OptionalHeader64.LoaderFlags); + DUMP_DEC_FIELD(OptionalHeader64.NumberOfRvaAndSizes); + } #undef DUMP_FIELD #undef DUMP_DEC_FIELD - std::cout << "Imports: " << "\n"; - IterImpVAString(p, printImports, NULL); - std::cout << "Relocations: " << "\n"; - IterRelocs(p, printRelocs, NULL); - std::cout << "Symbols (symbol table): " << "\n"; - IterSymbols(p, printSymbols, NULL); - std::cout << "Sections: " << "\n"; - IterSec(p, printSecs, NULL); - std::cout << "Exports: " << "\n"; - IterExpVA(p, printExps, NULL); + std::cout << "Imports: " + << "\n"; + IterImpVAString(p, printImports, NULL); + std::cout << "Relocations: " + << "\n"; + IterRelocs(p, printRelocs, NULL); + std::cout << "Symbols (symbol table): " + << "\n"; + IterSymbols(p, printSymbols, NULL); + std::cout << "Sections: " + << "\n"; + IterSec(p, printSecs, NULL); + std::cout << "Exports: " + << "\n"; + IterExpVA(p, printExps, NULL); - // read the first 8 bytes from the entry point and print them - VA entryPoint; - if (GetEntryPoint(p, entryPoint)) { - std::cout << "First 8 bytes from entry point (0x"; + // read the first 8 bytes from the entry point and print them + VA entryPoint; + if (GetEntryPoint(p, entryPoint)) { + std::cout << "First 8 bytes from entry point (0x"; - std::cout << std::hex << entryPoint << "):" << "\n"; - for (std::size_t i = 0; i < 8; i++) { - std::uint8_t b; - if (!ReadByteAtVA(p, i + entryPoint, b)) { - std::cout << " ERR"; - } else { - std::cout << " 0x" << std::hex << static_cast(b); - } + std::cout << std::hex << entryPoint << "):" + << "\n"; + for (std::size_t i = 0; i < 8; i++) { + std::uint8_t b; + if (!ReadByteAtVA(p, i + entryPoint, b)) { + std::cout << " ERR"; + } else { + std::cout << " 0x" << std::hex << static_cast(b); } - - std::cout << "\n"; } - std::cout << "Resources: " << "\n"; - IterRsrc(p, printRsrc, NULL); - DestructParsedPE(p); - } else { - std::cout << "Error: " << GetPEErr() << " (" << GetPEErrString() << ")" - << "\n"; - std::cout << "Location: " << GetPEErrLoc() << "\n"; + std::cout << "\n"; } + std::cout << "Resources: " + << "\n"; + IterRsrc(p, printRsrc, NULL); + + DestructParsedPE(p); + return 0; } diff --git a/dump-prog/meson.build b/dump-prog/meson.build deleted file mode 100644 index 2625ea7..0000000 --- a/dump-prog/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -dump_prog = executable( - 'dump-prog', - 'dump.cpp', - include_directories : [ incdirs ], - install : true, - install_dir : join_paths(get_option('datadir'), 'pe-parse/examples'), - link_with : pe_parser_library) diff --git a/examples/peaddrconv/main.cpp b/examples/peaddrconv/main.cpp index f671110..3bb0741 100644 --- a/examples/peaddrconv/main.cpp +++ b/examples/peaddrconv/main.cpp @@ -1,7 +1,7 @@ +#include #include #include #include -#include #include #include @@ -262,7 +262,8 @@ int main(int argc, char *argv[]) { char *last_parsed_char = nullptr; errno = 0; - std::uint64_t address = std::strtoull(address_as_string, &last_parsed_char, 16); + std::uint64_t address = + std::strtoull(address_as_string, &last_parsed_char, 16); if (address == 0U && *last_parsed_char != 0) { std::cout << "Invalid address specified\n"; return 1; diff --git a/meson.build b/meson.build deleted file mode 100644 index 3a5f4a1..0000000 --- a/meson.build +++ /dev/null @@ -1,8 +0,0 @@ -project('pe-parse', - 'cpp', - default_options : [ 'cpp_std=c++11' ], - license : [ 'MIT' ], - ) - -subdir('parser-library') -subdir('dump-prog') diff --git a/parser-library/meson.build b/parser-library/meson.build deleted file mode 100644 index 451bc9b..0000000 --- a/parser-library/meson.build +++ /dev/null @@ -1,11 +0,0 @@ -incdirs = include_directories('.') -parser_source = [ - 'buffer.cpp', - 'parse.cpp' - ] - -pe_parser_library = library('pe-parser-library', - sources : parser_source, - install : true - ) -install_headers('parse.h', subdir : 'pe-parse') diff --git a/pe-parser-library/include/parser-library/nt-headers.h b/pe-parser-library/include/parser-library/nt-headers.h index 2c714ab..c64bfda 100644 --- a/pe-parser-library/include/parser-library/nt-headers.h +++ b/pe-parser-library/include/parser-library/nt-headers.h @@ -47,21 +47,6 @@ constexpr std::uint16_t NT_OPTIONAL_32_MAGIC = 0x10B; constexpr std::uint16_t NT_OPTIONAL_64_MAGIC = 0x20B; constexpr std::uint16_t NT_SHORT_NAME_LEN = 8; constexpr std::uint16_t SYMTAB_RECORD_LEN = 18; -constexpr std::uint16_t DIR_EXPORT = 0; -constexpr std::uint16_t DIR_IMPORT = 1; -constexpr std::uint16_t DIR_RESOURCE = 2; -constexpr std::uint16_t DIR_EXCEPTION = 3; -constexpr std::uint16_t DIR_SECURITY = 4; -constexpr std::uint16_t DIR_BASERELOC = 5; -constexpr std::uint16_t DIR_DEBUG = 6; -constexpr std::uint16_t DIR_ARCHITECTURE = 7; -constexpr std::uint16_t DIR_GLOBALPTR = 8; -constexpr std::uint16_t DIR_TLS = 9; -constexpr std::uint16_t DIR_LOAD_CONFIG = 10; -constexpr std::uint16_t DIR_BOUND_IMPORT = 11; -constexpr std::uint16_t DIR_IAT = 12; -constexpr std::uint16_t DIR_DELAY_IMPORT = 13; -constexpr std::uint16_t DIR_COM_DESCRIPTOR = 14; // Machine Types constexpr std::uint16_t IMAGE_FILE_MACHINE_UNKNOWN = 0x0; @@ -263,6 +248,25 @@ struct data_directory { std::uint32_t Size; }; +enum data_directory_kind { + DIR_EXPORT = 0, + DIR_IMPORT = 1, + DIR_RESOURCE = 2, + DIR_EXCEPTION = 3, + DIR_SECURITY = 4, + DIR_BASERELOC = 5, + DIR_DEBUG = 6, + DIR_ARCHITECTURE = 7, + DIR_GLOBALPTR = 8, + DIR_TLS = 9, + DIR_LOAD_CONFIG = 10, + DIR_BOUND_IMPORT = 11, + DIR_IAT = 12, + DIR_DELAY_IMPORT = 13, + DIR_COM_DESCRIPTOR = 14, + DIR_RESERVED = 15, +}; + struct optional_header_32 { std::uint16_t Magic; std::uint8_t MajorLinkerVersion; @@ -451,4 +455,101 @@ struct reloc_block { std::uint32_t PageRVA; std::uint32_t BlockSize; }; + +struct image_load_config_code_integrity { + std::uint16_t Flags; + std::uint16_t Catalog; + std::uint32_t CatalogOffset; + std::uint32_t Reserved; +}; + +struct image_load_config_32 { + std::uint32_t Size; + std::uint32_t TimeDateStamp; + std::uint16_t MajorVersion; + std::uint16_t MinorVersion; + std::uint32_t GlobalFlagsClear; + std::uint32_t GlobalFlagsSet; + std::uint32_t CriticalSectionDefaultTimeout; + std::uint32_t DeCommitFreeBlockThreshold; + std::uint32_t DeCommitTotalFreeThreshold; + std::uint32_t LockPrefixTable; + std::uint32_t MaximumAllocationSize; + std::uint32_t VirtualMemoryThreshold; + std::uint32_t ProcessHeapFlags; + std::uint32_t ProcessAffinityMask; + std::uint16_t CSDVersion; + std::uint16_t DependentLoadFlags; + std::uint32_t EditList; + std::uint32_t SecurityCookie; + std::uint32_t SEHandlerTable; + std::uint32_t SEHandlerCount; + std::uint32_t GuardCFCheckFunctionPointer; + std::uint32_t GuardCFDispatchFunctionPointer; + std::uint32_t GuardCFFunctionTable; + std::uint32_t GuardCFFunctionCount; + std::uint32_t GuardFlags; + image_load_config_code_integrity CodeIntegrity; + std::uint32_t GuardAddressTakenIatEntryTable; + std::uint32_t GuardAddressTakenIatEntryCount; + std::uint32_t GuardLongJumpTargetTable; + std::uint32_t GuardLongJumpTargetCount; + std::uint32_t DynamicValueRelocTable; + std::uint32_t CHPEMetadataPointer; + std::uint32_t GuardRFFailureRoutine; + std::uint32_t GuardRFFailureRoutineFunctionPointer; + std::uint32_t DynamicValueRelocTableOffset; + std::uint16_t DynamicValueRelocTableSection; + std::uint16_t Reserved2; + std::uint32_t GuardRFVerifyStackPointerFunctionPointer; + std::uint32_t HotPatchTableOffset; + std::uint32_t Reserved3; + std::uint32_t EnclaveConfigurationPointer; + std::uint32_t VolatileMetadataPointer; +}; + +struct image_load_config_64 { + std::uint32_t Size; + std::uint32_t TimeDateStamp; + std::uint16_t MajorVersion; + std::uint16_t MinorVersion; + std::uint32_t GlobalFlagsClear; + std::uint32_t GlobalFlagsSet; + std::uint32_t CriticalSectionDefaultTimeout; + std::uint64_t DeCommitFreeBlockThreshold; + std::uint64_t DeCommitTotalFreeThreshold; + std::uint64_t LockPrefixTable; + std::uint64_t MaximumAllocationSize; + std::uint64_t VirtualMemoryThreshold; + std::uint64_t ProcessAffinityMask; + std::uint32_t ProcessHeapFlags; + std::uint16_t CSDVersion; + std::uint16_t DependentLoadFlags; + std::uint64_t EditList; + std::uint64_t SecurityCookie; + std::uint64_t SEHandlerTable; + std::uint64_t SEHandlerCount; + std::uint64_t GuardCFCheckFunctionPointer; + std::uint64_t GuardCFDispatchFunctionPointer; + std::uint64_t GuardCFFunctionTable; + std::uint64_t GuardCFFunctionCount; + std::uint32_t GuardFlags; + image_load_config_code_integrity CodeIntegrity; + std::uint64_t GuardAddressTakenIatEntryTable; + std::uint64_t GuardAddressTakenIatEntryCount; + std::uint64_t GuardLongJumpTargetTable; + std::uint64_t GuardLongJumpTargetCount; + std::uint64_t DynamicValueRelocTable; + std::uint64_t CHPEMetadataPointer; + std::uint64_t GuardRFFailureRoutine; + std::uint64_t GuardRFFailureRoutineFunctionPointer; + std::uint32_t DynamicValueRelocTableOffset; + std::uint16_t DynamicValueRelocTableSection; + std::uint16_t Reserved2; + std::uint64_t GuardRFVerifyStackPointerFunctionPointer; + std::uint32_t HotPatchTableOffset; + std::uint32_t Reserved3; + std::uint64_t EnclaveConfigurationPointer; + std::uint64_t VolatileMetadataPointer; +}; } // namespace peparse diff --git a/pe-parser-library/include/parser-library/parse.h b/pe-parser-library/include/parser-library/parse.h index 55f45b8..3e4281b 100644 --- a/pe-parser-library/include/parser-library/parse.h +++ b/pe-parser-library/include/parser-library/parse.h @@ -32,7 +32,7 @@ THE SOFTWARE. #include "to_string.h" #ifdef _MSC_VER -#define __typeof__(x) std::remove_reference < decltype(x) > ::type +#define __typeof__(x) std::remove_reference < decltype(x)> ::type #endif #define PE_ERR(x) \ @@ -137,6 +137,7 @@ enum pe_err { PEERR_MAGIC = 9, PEERR_BUFFER = 10, PEERR_ADDRESS = 11, + PEERR_SIZE = 12, }; bool readByte(bounded_buffer *b, std::uint32_t offset, std::uint8_t &out); @@ -167,7 +168,8 @@ typedef struct _parsed_pe { // Resolve a Rich header product id / build number pair to a known // product name typedef std::pair ProductKey; -const std::string& GetRichProductName(std::uint16_t prodId, std::uint16_t buildNum); +const std::string &GetRichProductName(std::uint16_t prodId, + std::uint16_t buildNum); // get parser error status as integer std::uint32_t GetPEErr(); @@ -230,4 +232,7 @@ const char *GetMachineAsString(parsed_pe *pe); // get subsystem as human readable string const char *GetSubsystemAsString(parsed_pe *pe); + +// get a table or string by its data directory entry +const void *GetDataDirectoryEntry(parsed_pe *pe, data_directory_kind dirnum); } // namespace peparse diff --git a/pe-parser-library/include/parser-library/to_string.h b/pe-parser-library/include/parser-library/to_string.h index a47caff..a5fc1d2 100644 --- a/pe-parser-library/include/parser-library/to_string.h +++ b/pe-parser-library/include/parser-library/to_string.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #ifdef USE_ICU4C #include diff --git a/pe-parser-library/src/parse.cpp b/pe-parser-library/src/parse.cpp index 125de84..901c6e5 100644 --- a/pe-parser-library/src/parse.cpp +++ b/pe-parser-library/src/parse.cpp @@ -23,10 +23,11 @@ THE SOFTWARE. */ #include +#include #include #include -#include #include +#include #include #include @@ -125,14 +126,14 @@ struct parsed_pe_internal { // The mapping of Rich header product id / build number pairs // to strings static const std::map ProductMap = { - {std::make_pair(1, 0), "Imported Functions"} -}; + {std::make_pair(1, 0), "Imported Functions"}}; static const std::string kUnknownProduct = ""; // Resolve a Rich header product id / build number pair to a known // product name -const std::string& GetRichProductName(std::uint16_t prodId, std::uint16_t buildNum) { +const std::string &GetRichProductName(std::uint16_t prodId, + std::uint16_t buildNum) { auto it = ProductMap.find(std::make_pair(prodId, buildNum)); if (it != ProductMap.end()) { return it->second; @@ -144,18 +145,21 @@ const std::string& GetRichProductName(std::uint16_t prodId, std::uint16_t buildN std::uint32_t err = 0; std::string err_loc; -static const char *pe_err_str[] = {"None", - "Out of memory", - "Invalid header", - "Invalid section", - "Invalid resource", - "Unable to get section for VA", - "Unable to read data", - "Unable to open", - "Unable to stat", - "Bad magic", - "Invalid buffer", - "Invalid address",}; +static const char *pe_err_str[] = { + "None", + "Out of memory", + "Invalid header", + "Invalid section", + "Invalid resource", + "Unable to get section for VA", + "Unable to read data", + "Unable to open", + "Unable to stat", + "Bad magic", + "Invalid buffer", + "Invalid address", + "Invalid size", +}; std::uint32_t GetPEErr() { return err; @@ -230,8 +234,9 @@ const char *GetSymbolTableStorageClassName(std::uint8_t id) { } } -static bool -readCString(const bounded_buffer &buffer, std::uint32_t off, std::string &result) { +static bool readCString(const bounded_buffer &buffer, + std::uint32_t off, + std::string &result) { if (off < buffer.bufLen) { std::uint8_t *p = buffer.buf; std::uint32_t n = buffer.bufLen; @@ -282,7 +287,9 @@ void IterRsrc(parsed_pe *pe, iterRsrc cb, void *cbd) { return; } -bool parse_resource_id(bounded_buffer *data, std::uint32_t id, std::string &result) { +bool parse_resource_id(bounded_buffer *data, + std::uint32_t id, + std::string &result) { std::uint16_t len; if (!readWord(data, id, len)) { return false; @@ -388,9 +395,10 @@ bool parse_resource_table(bounded_buffer *sectionData, } } } else { - /* .rsrc can accomodate up to 2**31 levels, but Windows only uses 3 by convention. - * As such, any depth above 3 indicates potentially unchecked recusion. - * See: https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-rsrc-section + /* .rsrc can accomodate up to 2**31 levels, but Windows only uses 3 by + * convention. As such, any depth above 3 indicates potentially unchecked + * recusion. See: + * https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-rsrc-section */ PE_ERR(PEERR_RESC); @@ -575,7 +583,8 @@ bool getSections(bounded_buffer *b, // now we have the section header information, so fill in a section // object appropriately section thisSec; - for (std::uint32_t charIndex = 0; charIndex < NT_SHORT_NAME_LEN; charIndex++) { + for (std::uint32_t charIndex = 0; charIndex < NT_SHORT_NAME_LEN; + charIndex++) { std::uint8_t c = curSec.Name[charIndex]; if (c == 0) { break; @@ -825,7 +834,9 @@ bool readNtHeader(bounded_buffer *b, nt_header_32 &header) { return true; } -bool readRichHeader(bounded_buffer *rich_buf, std::uint32_t key, rich_header &rich_hdr) { +bool readRichHeader(bounded_buffer *rich_buf, + std::uint32_t key, + rich_header &rich_hdr) { if (rich_buf == nullptr) { return false; } @@ -837,8 +848,8 @@ bool readRichHeader(bounded_buffer *rich_buf, std::uint32_t key, rich_header &ri // The first decrypted DWORD value of the rich header // at offset 0 should be 0x536e6144 aka the "DanS" signature if (!readDword(rich_buf, 0, encrypted_dword)) { - PE_ERR(PEERR_READ); - return false; + PE_ERR(PEERR_READ); + return false; } decrypted_dword = encrypted_dword ^ key; @@ -859,7 +870,7 @@ bool readRichHeader(bounded_buffer *rich_buf, std::uint32_t key, rich_header &ri // a DWORD is 4 bytes. Loop is incrementing 8 bytes, however // we are reading two DWORDS at a time, which is the size // of one rich header entry. - for (std::uint32_t i = 16; i < rich_buf->bufLen-8; i += 8) { + for (std::uint32_t i = 16; i < rich_buf->bufLen - 8; i += 8) { rich_entry entry; // Read first DWORD of entry and decrypt it if (!readDword(rich_buf, i, encrypted_dword)) { @@ -873,7 +884,7 @@ bool readRichHeader(bounded_buffer *rich_buf, std::uint32_t key, rich_header &ri entry.BuildNumber = (decrypted_dword & 0xFFFF); // The second DWORD represents the use count - if (!readDword(rich_buf, i+4, encrypted_dword)) { + if (!readDword(rich_buf, i + 4, encrypted_dword)) { PE_ERR(PEERR_READ); return false; } @@ -883,13 +894,12 @@ bool readRichHeader(bounded_buffer *rich_buf, std::uint32_t key, rich_header &ri // Preserve the individual entry rich_hdr.Entries.push_back(entry); - } // Preserve the end signature aka "Rich" magic - if (!readDword(rich_buf, rich_buf->bufLen-4, rich_hdr.EndSignature)) { - PE_ERR(PEERR_READ); - return false; + if (!readDword(rich_buf, rich_buf->bufLen - 4, rich_hdr.EndSignature)) { + PE_ERR(PEERR_READ); + return false; }; if (rich_hdr.EndSignature != RICH_MAGIC_END) { PE_ERR(PEERR_MAGIC); @@ -897,7 +907,7 @@ bool readRichHeader(bounded_buffer *rich_buf, std::uint32_t key, rich_header &ri } // Preserve the decryption key - rich_hdr.DecryptionKey = key; + rich_hdr.DecryptionKey = key; return true; } @@ -960,7 +970,8 @@ bool getHeader(bounded_buffer *file, pe_header &p, bounded_buffer *&rem) { } // Split the Rich header out into its own buffer - bounded_buffer *richBuf = splitBuffer(file, 0x80, rich_end_signature_offset + 4); + bounded_buffer *richBuf = + splitBuffer(file, 0x80, rich_end_signature_offset + 4); if (richBuf == nullptr) { return false; } @@ -992,12 +1003,12 @@ bool getHeader(bounded_buffer *file, pe_header &p, bounded_buffer *&rem) { std::uint32_t rem_size; if (p.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) { // signature + file_header + optional_header_32 - rem_size = - sizeof(std::uint32_t) + sizeof(file_header) + sizeof(optional_header_32); + rem_size = sizeof(std::uint32_t) + sizeof(file_header) + + sizeof(optional_header_32); } else if (p.nt.OptionalMagic == NT_OPTIONAL_64_MAGIC) { // signature + file_header + optional_header_64 - rem_size = - sizeof(std::uint32_t) + sizeof(file_header) + sizeof(optional_header_64); + rem_size = sizeof(std::uint32_t) + sizeof(file_header) + + sizeof(optional_header_64); } else { PE_ERR(PEERR_MAGIC); deleteBuffer(ntBuf); @@ -1529,8 +1540,8 @@ bool getImports(parsed_pe *p) { ent.moduleName = modName; p->internal->imports.push_back(ent); } else { - std::string symName = - "ORDINAL_" + modName + "_" + to_string(oval, std::dec); + std::string symName = "ORDINAL_" + modName + "_" + + to_string(oval, std::dec); importent ent; @@ -1579,7 +1590,8 @@ bool getSymbolTable(parsed_pe *p) { std::uint32_t offset = p->peHeader.nt.FileHeader.PointerToSymbolTable; - for (std::uint32_t i = 0; i < p->peHeader.nt.FileHeader.NumberOfSymbols; i++) { + for (std::uint32_t i = 0; i < p->peHeader.nt.FileHeader.NumberOfSymbols; + i++) { symbol sym; // Read name @@ -1607,7 +1619,8 @@ bool getSymbolTable(parsed_pe *p) { strOffset += sizeof(std::uint8_t); } } else { - for (std::uint8_t n = 0; n < NT_SHORT_NAME_LEN && sym.name.shortName[n] != 0; + for (std::uint8_t n = 0; + n < NT_SHORT_NAME_LEN && sym.name.shortName[n] != 0; n++) { sym.strName.push_back(static_cast(sym.name.shortName[n])); } @@ -2192,4 +2205,48 @@ const char *GetSubsystemAsString(parsed_pe *pe) { } } +const void *GetDataDirectoryEntry(parsed_pe *pe, data_directory_kind dirnum) { + if (pe == nullptr) { + PE_ERR(PEERR_NONE); + return nullptr; + } + + data_directory dir; + VA addr; + if (pe->peHeader.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) { + dir = pe->peHeader.nt.OptionalHeader.DataDirectory[dirnum]; + addr = dir.VirtualAddress + pe->peHeader.nt.OptionalHeader.ImageBase; + } else if (pe->peHeader.nt.OptionalMagic == NT_OPTIONAL_64_MAGIC) { + dir = pe->peHeader.nt.OptionalHeader64.DataDirectory[dirnum]; + addr = dir.VirtualAddress + pe->peHeader.nt.OptionalHeader64.ImageBase; + } else { + PE_ERR(PEERR_MAGIC); + return nullptr; + } + + if (dir.Size <= 0) { + PE_ERR(PEERR_SIZE); + return nullptr; + } + + section sec; + if (!getSecForVA(pe->internal->secs, addr, sec)) { + PE_ERR(PEERR_SECTVA); + return nullptr; + } + + auto off = static_cast(addr - sec.sectionBase); + if (off + dir.Size >= sec.sectionData->bufLen) { + PE_ERR(PEERR_SIZE); + return nullptr; + } + + std::cerr << "off: 0x" << std::hex << off << std::endl; + + std::vector rawEntry(sec.sectionData->buf + off, + sec.sectionData->buf + off + dir.Size); + + return rawEntry.data(); +} + } // namespace peparse diff --git a/pe-parser-library/src/unicode_codecvt.cpp b/pe-parser-library/src/unicode_codecvt.cpp index c76c3c7..f297408 100644 --- a/pe-parser-library/src/unicode_codecvt.cpp +++ b/pe-parser-library/src/unicode_codecvt.cpp @@ -22,17 +22,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include -#include #include +#include +#include namespace peparse { -// See https://stackoverflow.com/questions/38688417/utf-conversion-functions-in-c11 -std::string from_utf16(const UCharString &u) -{ +// See +// https://stackoverflow.com/questions/38688417/utf-conversion-functions-in-c11 +std::string from_utf16(const UCharString &u) { #if defined(_MSC_VER) - // std::wstring_convert, char16_t>convert; // Doesn't compile with Visual Studio. - // See https://stackoverflow.com/questions/32055357/visual-studio-c-2015-stdcodecvt-with-char16-t-or-char32-t + // std::wstring_convert, char16_t>convert; + // // Doesn't compile with Visual Studio. See + // https://stackoverflow.com/questions/32055357/visual-studio-c-2015-stdcodecvt-with-char16-t-or-char32-t std::wstring_convert, std::int16_t> convert; auto p = reinterpret_cast(u.data()); return convert.to_bytes(p, p + u.size()); diff --git a/pe-parser-library/src/unicode_icu.cpp b/pe-parser-library/src/unicode_icu.cpp index 365cb1f..ef1bd9e 100644 --- a/pe-parser-library/src/unicode_icu.cpp +++ b/pe-parser-library/src/unicode_icu.cpp @@ -26,8 +26,7 @@ THE SOFTWARE. #include namespace peparse { -std::string from_utf16(const UCharString &u) -{ +std::string from_utf16(const UCharString &u) { icu::UnicodeString utf16_string = icu::UnicodeString(u.data(), u.length()); std::string result; utf16_string.toUTF8String(result); diff --git a/python/pepy.cpp b/python/pepy.cpp index 64b71e8..d9396e9 100644 --- a/python/pepy.cpp +++ b/python/pepy.cpp @@ -704,7 +704,8 @@ static PyObject *pepy_parsed_get_entry_point(PyObject *self, PyObject *args) { return ret; } -static PyObject *pepy_parsed_get_machine_as_str(PyObject *self, PyObject *args) { +static PyObject *pepy_parsed_get_machine_as_str(PyObject *self, + PyObject *args) { PyObject *ret; const char *str; @@ -721,7 +722,8 @@ static PyObject *pepy_parsed_get_machine_as_str(PyObject *self, PyObject *args) return ret; } -static PyObject *pepy_parsed_get_subsystem_as_str(PyObject *self, PyObject *args) { +static PyObject *pepy_parsed_get_subsystem_as_str(PyObject *self, + PyObject *args) { PyObject *ret; const char *str; @@ -1079,9 +1081,8 @@ static PyObject *pepy_parsed_get_relocations(PyObject *self, PyObject *args) { #define PEPY_PARSED_GET(ATTR, VAL) \ static PyObject *pepy_parsed_get_##ATTR(PyObject *self, void *closure) { \ - PyObject *ret = \ - PyLong_FromUnsignedLongLong( \ - ((pepy_parsed *) self)->pe->peHeader.nt.VAL); \ + PyObject *ret = PyLong_FromUnsignedLongLong( \ + ((pepy_parsed *) self)->pe->peHeader.nt.VAL); \ if (!ret) \ PyErr_SetString(PyExc_AttributeError, "Error getting attribute."); \ return ret; \ diff --git a/travis.sh b/travis.sh deleted file mode 100755 index 6cd3e68..0000000 --- a/travis.sh +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env bash - -main() { - if [ $# -ne 2 ] ; then - printf "Usage:\n\ttravis.sh \n" - return 1 - fi - - local platform_name="$1" - local operation_type="$2" - - if [[ "${platform_name}" != "osx" && "${platform_name}" != "linux" ]] ; then - printf "Invalid platform: ${platform_name}\n" - return 1 - fi - - if [[ "${operation_type}" == "initialize" ]] ; then - "${platform_name}_initialize" - return $? - - elif [[ "$operation_type" == "build" ]] ; then - "${platform_name}_build" - return $? - - else - printf "Invalid operation\n" - return 1 - fi -} - -get_processor_count() { - which nproc > /dev/null - if [ $? -eq 0 ] ; then - nproc - return 0 - fi - - which sysctl > /dev/null - if [ $? -eq 0 ] ; then - sysctl -n hw.ncpu - return 0 - fi - - return 1 -} - -linux_initialize() { - printf "Initializing platform: linux\n" - local log_file=`mktemp` - - printf " > Updating the package database..\n" - sudo apt-get -qq update > "${log_file}" 2>&1 - if [ $? -ne 0 ] ; then - printf " x The package database could not be updated\n\n\n" - cat "${log_file}" - return 1 - fi - - printf " > Installing the required packages...\n" - sudo apt-get install -qqy cmake python2.7 python-dev build-essential realpath libicu-dev > "${log_file}" 2>&1 - if [ $? -ne 0 ] ; then - printf " x Could not install the required dependencies\n\n\n" - cat "${log_file}" - return 1 - fi - - printf " > The system has been successfully initialized\n" - return 0 -} - -osx_initialize() { - printf "Initializing platform: macOS\n" - local log_file=`mktemp` - - printf " > Updating the package database..\n" - brew update > "${log_file}" 2>&1 - if [ $? -ne 0 ] ; then - printf " x The package database could not be updated\n\n\n" - cat "${log_file}" - return 1 - fi - - printf " > Installing CMake...\n" - brew install cmake > "${log_file}" 2>&1 - if [ $? -ne 0 ] ; then - printf " x Failed to install CMake\n\n\n" - cat "${log_file}" - fi - - printf " > The system has been successfully initialized\n" - return 0 -} - -common_build() { - printf "Gathering system information...\n" - - which cmake > /dev/null - printf " > CMake version: " - if [ $? -eq 0 ] ; then - cmake --version | head -n 1 - else - printf "not found\n" - fi - - which gcc > /dev/null - printf " > GCC version: " - if [ $? -eq 0 ] ; then - gcc --version | head -n 1 - else - printf "not found\n" - fi - - which clang > /dev/null - printf " > Clang version: " - if [ $? -eq 0 ] ; then - clang --version | head -n 1 - else - printf "not found\n" - fi - - printf "\n" - - printf "Library\n" - if [ ! -d "build" ] ; then - printf " > Creating the build directory...\n" - mkdir "build" - if [ $? -ne 0 ] ; then - printf " x Failed to create the build directory\n" - return 1 - fi - fi - - local log_file=`mktemp` - local processor_count=`get_processor_count` - - printf " > Configuring...\n" - ( cd "build" && cmake .. ) > "$log_file" 2>&1 - if [ $? -ne 0 ] ; then - printf " x Configure failed; CMake returned an error.\n\n\n" - cat "$log_file" - return 1 - fi - - printf " > Building...\n" - ( cd "build" && make -j "${processor_count}" ) > "$log_file" 2>&1 - if [ $? -ne 0 ] ; then - printf " x The build has failed.\n\n\n" - cat "$log_file" - return 1 - fi - - printf " > Installing...\n" - sudo touch /usr/lib/test_file > /dev/null 2>&1 - if [ $? -ne 0 ] ; then - printf " x Access denied to /usr/lib; the 'install' step will be skipped\n" - - else - ( cd "build" && sudo make install ) > "$log_file" 2>&1 - if [ $? -ne 0 ] ; then - printf " x Failed to install the library.\n\n\n" - cat "$log_file" - return 1 - fi - fi - - printf "\n" - - printf "pepy\n" - - printf " > Building...\n" - ( cd python && python2 ./setup.py build ) > "$log_file" 2>&1 - if [ $? -ne 0 ] ; then - printf " x Build failed.\n\n\n" - cat "$log_file" - return 1 - fi - - return 0 -} - -linux_build() { - printf "Building platform: linux\n" - - source /etc/*-release - printf "Distribution: ${DISTRIB_DESCRIPTION}\n\n" - - common_build - if [ $? -ne 0 ] ; then - return 1 - fi - - return 0 -} - -osx_build() { - printf "Building platform: macOS\n\n" - - printf "macOS version: " - sw_vers -productVersion - - common_build - if [ $? -ne 0 ] ; then - return 1 - fi - - return 0 -} - -main $@ -exit $? From b3ab75577d9228797a649adf338bb61bc4f6915f Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 21 Oct 2019 12:18:18 -0400 Subject: [PATCH 02/10] parse: Remove debug print --- pe-parser-library/src/parse.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/pe-parser-library/src/parse.cpp b/pe-parser-library/src/parse.cpp index 901c6e5..acc9028 100644 --- a/pe-parser-library/src/parse.cpp +++ b/pe-parser-library/src/parse.cpp @@ -2241,8 +2241,6 @@ const void *GetDataDirectoryEntry(parsed_pe *pe, data_directory_kind dirnum) { return nullptr; } - std::cerr << "off: 0x" << std::hex << off << std::endl; - std::vector rawEntry(sec.sectionData->buf + off, sec.sectionData->buf + off + dir.Size); From 40987e1f7e936efe0aaeae0818c09384d4ac0675 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 21 Oct 2019 19:51:41 -0400 Subject: [PATCH 03/10] pe-parser-library: Fix GetDataDirectoryEntry (#95) * pe-parser-library: Fix GetDataDirectoryEntry Avoids UB. * pe-parser-library: clang-format * pe-parser-library: Use ref --- .../include/parser-library/parse.h | 4 +++- pe-parser-library/src/parse.cpp | 22 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/pe-parser-library/include/parser-library/parse.h b/pe-parser-library/include/parser-library/parse.h index 3e4281b..70e23ab 100644 --- a/pe-parser-library/include/parser-library/parse.h +++ b/pe-parser-library/include/parser-library/parse.h @@ -234,5 +234,7 @@ const char *GetMachineAsString(parsed_pe *pe); const char *GetSubsystemAsString(parsed_pe *pe); // get a table or string by its data directory entry -const void *GetDataDirectoryEntry(parsed_pe *pe, data_directory_kind dirnum); +bool GetDataDirectoryEntry(parsed_pe *pe, + data_directory_kind dirnum, + std::vector &raw_entry); } // namespace peparse diff --git a/pe-parser-library/src/parse.cpp b/pe-parser-library/src/parse.cpp index acc9028..e7e6982 100644 --- a/pe-parser-library/src/parse.cpp +++ b/pe-parser-library/src/parse.cpp @@ -2205,10 +2205,14 @@ const char *GetSubsystemAsString(parsed_pe *pe) { } } -const void *GetDataDirectoryEntry(parsed_pe *pe, data_directory_kind dirnum) { +bool GetDataDirectoryEntry(parsed_pe *pe, + data_directory_kind dirnum, + std::vector &raw_entry) { + raw_entry.clear(); + if (pe == nullptr) { PE_ERR(PEERR_NONE); - return nullptr; + return false; } data_directory dir; @@ -2221,30 +2225,30 @@ const void *GetDataDirectoryEntry(parsed_pe *pe, data_directory_kind dirnum) { addr = dir.VirtualAddress + pe->peHeader.nt.OptionalHeader64.ImageBase; } else { PE_ERR(PEERR_MAGIC); - return nullptr; + return false; } if (dir.Size <= 0) { PE_ERR(PEERR_SIZE); - return nullptr; + return false; } section sec; if (!getSecForVA(pe->internal->secs, addr, sec)) { PE_ERR(PEERR_SECTVA); - return nullptr; + return false; } auto off = static_cast(addr - sec.sectionBase); if (off + dir.Size >= sec.sectionData->bufLen) { PE_ERR(PEERR_SIZE); - return nullptr; + return false; } - std::vector rawEntry(sec.sectionData->buf + off, - sec.sectionData->buf + off + dir.Size); + raw_entry.assign(sec.sectionData->buf + off, + sec.sectionData->buf + off + dir.Size); - return rawEntry.data(); + return true; } } // namespace peparse From 67a09499bf37728a3fb49622a5aefe4dfd9bfcf0 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Thu, 24 Oct 2019 01:15:56 -0400 Subject: [PATCH 04/10] parse: Fix unsafe cast (#96) * parse: Fix unsafe cast * parse: Fix uninitialized warning This is a false positive from cl.exe's /W4: We only use this variable in a branch where it ends up initialized. --- pe-parser-library/src/parse.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pe-parser-library/src/parse.cpp b/pe-parser-library/src/parse.cpp index e7e6982..340c9be 100644 --- a/pe-parser-library/src/parse.cpp +++ b/pe-parser-library/src/parse.cpp @@ -126,7 +126,9 @@ struct parsed_pe_internal { // The mapping of Rich header product id / build number pairs // to strings static const std::map ProductMap = { - {std::make_pair(1, 0), "Imported Functions"}}; + {std::make_pair(static_cast(1), + static_cast(0)), + "Imported Functions"}}; static const std::string kUnknownProduct = ""; @@ -939,7 +941,7 @@ bool getHeader(bounded_buffer *file, pe_header &p, bounded_buffer *&rem) { // read rich header std::uint32_t dword; - std::uint32_t rich_end_signature_offset; + std::uint32_t rich_end_signature_offset = 0; std::uint32_t xor_key; bool found_rich = false; From b4e269592164f06becd0949bf3bda204c6b42f2b Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Thu, 24 Oct 2019 01:40:21 -0400 Subject: [PATCH 05/10] travis: Add Windows build (#97) --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index a9230be..ee2db06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,13 @@ jobs: - <<: *__build_stage_script os: osx compiler: clang + - stage: build + os: windows + language: cpp + script: + - mkdir build && cd build + - cmake -G "Visual Studio 15 2017 Win64" .. + - cmake --build . env: global: From fe719c9b87f1532a6518a23fed9be2e7093d90c1 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Sun, 27 Oct 2019 21:36:27 -0400 Subject: [PATCH 06/10] pe-parser-library, dump-pe: Windows.h accommodations (#99) Makes it easier to use pe-parse in a translation unit where Windows.h is also included. --- dump-pe/main.cpp | 16 +++++++-------- .../include/parser-library/nt-headers.h | 20 ++++++++++--------- .../include/parser-library/parse.h | 2 ++ 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/dump-pe/main.cpp b/dump-pe/main.cpp index 53d252f..b2e589a 100644 --- a/dump-pe/main.cpp +++ b/dump-pe/main.cpp @@ -64,28 +64,28 @@ int printRelocs(void *N, VA relocAddr, reloc_type type) { std::cout << "TYPE: "; switch (type) { - case ABSOLUTE: + case RELOC_ABSOLUTE: std::cout << "ABSOLUTE"; break; - case HIGH: + case RELOC_HIGH: std::cout << "HIGH"; break; - case LOW: + case RELOC_LOW: std::cout << "LOW"; break; - case HIGHLOW: + case RELOC_HIGHLOW: std::cout << "HIGHLOW"; break; - case HIGHADJ: + case RELOC_HIGHADJ: std::cout << "HIGHADJ"; break; - case MIPS_JMPADDR: + case RELOC_MIPS_JMPADDR: std::cout << "MIPS_JMPADDR"; break; - case MIPS_JMPADDR16: + case RELOC_MIPS_JMPADDR16: std::cout << "MIPS_JMPADD16"; break; - case DIR64: + case RELOC_DIR64: std::cout << "DIR64"; break; default: diff --git a/pe-parser-library/include/parser-library/nt-headers.h b/pe-parser-library/include/parser-library/nt-headers.h index c64bfda..583a7be 100644 --- a/pe-parser-library/include/parser-library/nt-headers.h +++ b/pe-parser-library/include/parser-library/nt-headers.h @@ -48,6 +48,7 @@ constexpr std::uint16_t NT_OPTIONAL_64_MAGIC = 0x20B; constexpr std::uint16_t NT_SHORT_NAME_LEN = 8; constexpr std::uint16_t SYMTAB_RECORD_LEN = 18; +#ifndef _PEPARSE_WINDOWS_CONFLICTS // Machine Types constexpr std::uint16_t IMAGE_FILE_MACHINE_UNKNOWN = 0x0; constexpr std::uint16_t IMAGE_FILE_MACHINE_ALPHA = 0x1d3; // Alpha_AXP @@ -209,6 +210,7 @@ constexpr std::uint8_t IMAGE_SYM_CLASS_FILE = 103; constexpr std::uint8_t IMAGE_SYM_CLASS_SECTION = 104; constexpr std::uint8_t IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105; constexpr std::uint8_t IMAGE_SYM_CLASS_CLR_TOKEN = 107; +#endif // clang-format on struct dos_header { @@ -440,15 +442,15 @@ struct export_dir_table { }; enum reloc_type { - ABSOLUTE = 0, - HIGH = 1, - LOW = 2, - HIGHLOW = 3, - HIGHADJ = 4, - MIPS_JMPADDR = 5, - MIPS_JMPADDR16 = 9, - IA64_IMM64 = 9, - DIR64 = 10 + RELOC_ABSOLUTE = 0, + RELOC_HIGH = 1, + RELOC_LOW = 2, + RELOC_HIGHLOW = 3, + RELOC_HIGHADJ = 4, + RELOC_MIPS_JMPADDR = 5, + RELOC_MIPS_JMPADDR16 = 9, + RELOC_IA64_IMM64 = 9, + RELOC_DIR64 = 10 }; struct reloc_block { diff --git a/pe-parser-library/include/parser-library/parse.h b/pe-parser-library/include/parser-library/parse.h index 70e23ab..ed86c0e 100644 --- a/pe-parser-library/include/parser-library/parse.h +++ b/pe-parser-library/include/parser-library/parse.h @@ -99,6 +99,7 @@ struct resource { bounded_buffer *buf; }; +#ifndef _PEPARSE_WINDOWS_CONFLICTS // http://msdn.microsoft.com/en-us/library/ms648009(v=vs.85).aspx enum resource_type { RT_CURSOR = 1, @@ -123,6 +124,7 @@ enum resource_type { RT_HTML = 23, RT_MANIFEST = 24 }; +#endif enum pe_err { PEERR_NONE = 0, From ea3970c9a084da49fc4211e2d17413aee7d01725 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sun, 10 Nov 2019 01:19:34 +0100 Subject: [PATCH 07/10] Fix PEPARSE_INCLUDE_DIR for cross-compiling (#101) --- pe-parser-library/cmake/peparse-config.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pe-parser-library/cmake/peparse-config.cmake b/pe-parser-library/cmake/peparse-config.cmake index 940ecb7..38a2280 100644 --- a/pe-parser-library/cmake/peparse-config.cmake +++ b/pe-parser-library/cmake/peparse-config.cmake @@ -1,4 +1,8 @@ -find_path(PEPARSE_INCLUDE_DIR $) +if(CMAKE_CROSSCOMPILING) + find_path(PEPARSE_INCLUDE_DIR "parser-library/parse.h") +else() + find_path(PEPARSE_INCLUDE_DIR $) +endif() find_library(PEPARSE_LIBRARIES NAMES "libpe-parser-library" "pe-parser-library") include(FindPackageHandleStandardArgs) From bd68ba418f2a9099dfc28fa1ea2e7b3f74e0917b Mon Sep 17 00:00:00 2001 From: Stefan Siegfried <8906941+stefansiegfried@users.noreply.github.com> Date: Sun, 17 Nov 2019 01:45:55 +0900 Subject: [PATCH 08/10] Populate Rich header version strings (#94) --- dump-pe/main.cpp | 261 +++++----- .../include/parser-library/nt-headers.h | 2 + .../include/parser-library/parse.h | 5 +- pe-parser-library/src/parse.cpp | 464 +++++++++++++++++- 4 files changed, 592 insertions(+), 140 deletions(-) diff --git a/dump-pe/main.cpp b/dump-pe/main.cpp index b2e589a..357809d 100644 --- a/dump-pe/main.cpp +++ b/dump-pe/main.cpp @@ -229,11 +229,12 @@ int printSymbols(void *N, int printRich(void *N, rich_entry r) { static_cast(N); - + std::cout << std::dec; std::cout << std::setw(10) << "ProdId:" << std::setw(7) << r.ProductId; std::cout << std::setw(10) << "Build:" << std::setw(7) << r.BuildNumber; - std::cout << std::setw(10) << "Name:" << std::setw(20) - << GetRichProductName(r.ProductId, r.BuildNumber); + std::cout << std::setw(10) << "Name:" << std::setw(40) + << GetRichProductName(r.BuildNumber) << " " + << GetRichObjectType(r.ProductId); std::cout << std::setw(10) << "Count:" << std::setw(7) << r.Count << "\n"; return 0; } @@ -282,10 +283,13 @@ int printSecs(void *N, #define DUMP_FIELD(x) \ std::cout << "" #x << ": 0x"; \ - std::cout << std::hex << static_cast(p->peHeader.nt.x) << "\n"; + std::cout << std::hex << static_cast(p->peHeader.x) << "\n"; #define DUMP_DEC_FIELD(x) \ std::cout << "" #x << ": "; \ - std::cout << std::dec << static_cast(p->peHeader.nt.x) << "\n"; + std::cout << std::dec << static_cast(p->peHeader.x) << "\n"; +#define DUMP_BOOL_FIELD(x) \ + std::cout << "" #x << ": "; \ + std::cout << std::boolalpha << static_cast(p->peHeader.x) << "\n"; int main(int argc, char *argv[]) { if (argc != 2 || (argc == 2 && std::strcmp(argv[1], "--help") == 0)) { @@ -303,120 +307,153 @@ int main(int argc, char *argv[]) { return 1; } - // Print Rich header info - if (p->peHeader.rich.isPresent) { - std::cout << "Rich header: present\n"; - IterRich(p, printRich, NULL); - } else { - std::cout << "Rich header: not present\n"; - } - // print out some things - DUMP_FIELD(Signature); - DUMP_FIELD(FileHeader.Machine); - DUMP_FIELD(FileHeader.NumberOfSections); - DUMP_DEC_FIELD(FileHeader.TimeDateStamp); - DUMP_FIELD(FileHeader.PointerToSymbolTable); - DUMP_DEC_FIELD(FileHeader.NumberOfSymbols); - DUMP_FIELD(FileHeader.SizeOfOptionalHeader); - DUMP_FIELD(FileHeader.Characteristics); - if (p->peHeader.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) { - DUMP_FIELD(OptionalHeader.Magic); - DUMP_DEC_FIELD(OptionalHeader.MajorLinkerVersion); - DUMP_DEC_FIELD(OptionalHeader.MinorLinkerVersion); - DUMP_FIELD(OptionalHeader.SizeOfCode); - DUMP_FIELD(OptionalHeader.SizeOfInitializedData); - DUMP_FIELD(OptionalHeader.SizeOfUninitializedData); - DUMP_FIELD(OptionalHeader.AddressOfEntryPoint); - DUMP_FIELD(OptionalHeader.BaseOfCode); - DUMP_FIELD(OptionalHeader.BaseOfData); - DUMP_FIELD(OptionalHeader.ImageBase); - DUMP_FIELD(OptionalHeader.SectionAlignment); - DUMP_FIELD(OptionalHeader.FileAlignment); - DUMP_DEC_FIELD(OptionalHeader.MajorOperatingSystemVersion); - DUMP_DEC_FIELD(OptionalHeader.MinorOperatingSystemVersion); - DUMP_DEC_FIELD(OptionalHeader.Win32VersionValue); - DUMP_FIELD(OptionalHeader.SizeOfImage); - DUMP_FIELD(OptionalHeader.SizeOfHeaders); - DUMP_FIELD(OptionalHeader.CheckSum); - DUMP_FIELD(OptionalHeader.Subsystem); - DUMP_FIELD(OptionalHeader.DllCharacteristics); - DUMP_FIELD(OptionalHeader.SizeOfStackReserve); - DUMP_FIELD(OptionalHeader.SizeOfStackCommit); - DUMP_FIELD(OptionalHeader.SizeOfHeapReserve); - DUMP_FIELD(OptionalHeader.SizeOfHeapCommit); - DUMP_FIELD(OptionalHeader.LoaderFlags); - DUMP_DEC_FIELD(OptionalHeader.NumberOfRvaAndSizes); - } else { - DUMP_FIELD(OptionalHeader64.Magic); - DUMP_DEC_FIELD(OptionalHeader64.MajorLinkerVersion); - DUMP_DEC_FIELD(OptionalHeader64.MinorLinkerVersion); - DUMP_FIELD(OptionalHeader64.SizeOfCode); - DUMP_FIELD(OptionalHeader64.SizeOfInitializedData); - DUMP_FIELD(OptionalHeader64.SizeOfUninitializedData); - DUMP_FIELD(OptionalHeader64.AddressOfEntryPoint); - DUMP_FIELD(OptionalHeader64.BaseOfCode); - DUMP_FIELD(OptionalHeader64.ImageBase); - DUMP_FIELD(OptionalHeader64.SectionAlignment); - DUMP_FIELD(OptionalHeader64.FileAlignment); - DUMP_DEC_FIELD(OptionalHeader64.MajorOperatingSystemVersion); - DUMP_DEC_FIELD(OptionalHeader64.MinorOperatingSystemVersion); - DUMP_DEC_FIELD(OptionalHeader64.Win32VersionValue); - DUMP_FIELD(OptionalHeader64.SizeOfImage); - DUMP_FIELD(OptionalHeader64.SizeOfHeaders); - DUMP_FIELD(OptionalHeader64.CheckSum); - DUMP_FIELD(OptionalHeader64.Subsystem); - DUMP_FIELD(OptionalHeader64.DllCharacteristics); - DUMP_FIELD(OptionalHeader64.SizeOfStackReserve); - DUMP_FIELD(OptionalHeader64.SizeOfStackCommit); - DUMP_FIELD(OptionalHeader64.SizeOfHeapReserve); - DUMP_FIELD(OptionalHeader64.SizeOfHeapCommit); - DUMP_FIELD(OptionalHeader64.LoaderFlags); - DUMP_DEC_FIELD(OptionalHeader64.NumberOfRvaAndSizes); - } + if (p != NULL) { + // Print DOS header + DUMP_FIELD(dos.e_magic); + DUMP_FIELD(dos.e_cp); + DUMP_FIELD(dos.e_crlc); + DUMP_FIELD(dos.e_cparhdr); + DUMP_FIELD(dos.e_minalloc); + DUMP_FIELD(dos.e_maxalloc); + DUMP_FIELD(dos.e_ss); + DUMP_FIELD(dos.e_sp); + DUMP_FIELD(dos.e_csum); + DUMP_FIELD(dos.e_ip); + DUMP_FIELD(dos.e_cs); + DUMP_FIELD(dos.e_lfarlc); + DUMP_FIELD(dos.e_ovno); + DUMP_FIELD(dos.e_res[0]); + DUMP_FIELD(dos.e_res[1]); + DUMP_FIELD(dos.e_res[2]); + DUMP_FIELD(dos.e_res[3]); + DUMP_FIELD(dos.e_oemid); + DUMP_FIELD(dos.e_oeminfo); + DUMP_FIELD(dos.e_res2[0]); + DUMP_FIELD(dos.e_res2[1]); + DUMP_FIELD(dos.e_res2[2]); + DUMP_FIELD(dos.e_res2[3]); + DUMP_FIELD(dos.e_res2[4]); + DUMP_FIELD(dos.e_res2[5]); + DUMP_FIELD(dos.e_res2[6]); + DUMP_FIELD(dos.e_res2[7]); + DUMP_FIELD(dos.e_res2[8]); + DUMP_FIELD(dos.e_res2[9]); + DUMP_FIELD(dos.e_lfanew); + // Print Rich header info + DUMP_BOOL_FIELD(rich.isPresent); + if (p->peHeader.rich.isPresent) { + DUMP_FIELD(rich.DecryptionKey); + DUMP_FIELD(rich.Checksum); + DUMP_BOOL_FIELD(rich.isValid); + IterRich(p, printRich, NULL); + } + // print out some things + DUMP_FIELD(nt.Signature); + DUMP_FIELD(nt.FileHeader.Machine); + DUMP_FIELD(nt.FileHeader.NumberOfSections); + DUMP_DEC_FIELD(nt.FileHeader.TimeDateStamp); + DUMP_FIELD(nt.FileHeader.PointerToSymbolTable); + DUMP_DEC_FIELD(nt.FileHeader.NumberOfSymbols); + DUMP_FIELD(nt.FileHeader.SizeOfOptionalHeader); + DUMP_FIELD(nt.FileHeader.Characteristics); + if (p->peHeader.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) { + DUMP_FIELD(nt.OptionalHeader.Magic); + DUMP_DEC_FIELD(nt.OptionalHeader.MajorLinkerVersion); + DUMP_DEC_FIELD(nt.OptionalHeader.MinorLinkerVersion); + DUMP_FIELD(nt.OptionalHeader.SizeOfCode); + DUMP_FIELD(nt.OptionalHeader.SizeOfInitializedData); + DUMP_FIELD(nt.OptionalHeader.SizeOfUninitializedData); + DUMP_FIELD(nt.OptionalHeader.AddressOfEntryPoint); + DUMP_FIELD(nt.OptionalHeader.BaseOfCode); + DUMP_FIELD(nt.OptionalHeader.BaseOfData); + DUMP_FIELD(nt.OptionalHeader.ImageBase); + DUMP_FIELD(nt.OptionalHeader.SectionAlignment); + DUMP_FIELD(nt.OptionalHeader.FileAlignment); + DUMP_DEC_FIELD(nt.OptionalHeader.MajorOperatingSystemVersion); + DUMP_DEC_FIELD(nt.OptionalHeader.MinorOperatingSystemVersion); + DUMP_DEC_FIELD(nt.OptionalHeader.Win32VersionValue); + DUMP_FIELD(nt.OptionalHeader.SizeOfImage); + DUMP_FIELD(nt.OptionalHeader.SizeOfHeaders); + DUMP_FIELD(nt.OptionalHeader.CheckSum); + DUMP_FIELD(nt.OptionalHeader.Subsystem); + DUMP_FIELD(nt.OptionalHeader.DllCharacteristics); + DUMP_FIELD(nt.OptionalHeader.SizeOfStackReserve); + DUMP_FIELD(nt.OptionalHeader.SizeOfStackCommit); + DUMP_FIELD(nt.OptionalHeader.SizeOfHeapReserve); + DUMP_FIELD(nt.OptionalHeader.SizeOfHeapCommit); + DUMP_FIELD(nt.OptionalHeader.LoaderFlags); + DUMP_DEC_FIELD(nt.OptionalHeader.NumberOfRvaAndSizes); + } else { + DUMP_FIELD(nt.OptionalHeader64.Magic); + DUMP_DEC_FIELD(nt.OptionalHeader64.MajorLinkerVersion); + DUMP_DEC_FIELD(nt.OptionalHeader64.MinorLinkerVersion); + DUMP_FIELD(nt.OptionalHeader64.SizeOfCode); + DUMP_FIELD(nt.OptionalHeader64.SizeOfInitializedData); + DUMP_FIELD(nt.OptionalHeader64.SizeOfUninitializedData); + DUMP_FIELD(nt.OptionalHeader64.AddressOfEntryPoint); + DUMP_FIELD(nt.OptionalHeader64.BaseOfCode); + DUMP_FIELD(nt.OptionalHeader64.ImageBase); + DUMP_FIELD(nt.OptionalHeader64.SectionAlignment); + DUMP_FIELD(nt.OptionalHeader64.FileAlignment); + DUMP_DEC_FIELD(nt.OptionalHeader64.MajorOperatingSystemVersion); + DUMP_DEC_FIELD(nt.OptionalHeader64.MinorOperatingSystemVersion); + DUMP_DEC_FIELD(nt.OptionalHeader64.Win32VersionValue); + DUMP_FIELD(nt.OptionalHeader64.SizeOfImage); + DUMP_FIELD(nt.OptionalHeader64.SizeOfHeaders); + DUMP_FIELD(nt.OptionalHeader64.CheckSum); + DUMP_FIELD(nt.OptionalHeader64.Subsystem); + DUMP_FIELD(nt.OptionalHeader64.DllCharacteristics); + DUMP_FIELD(nt.OptionalHeader64.SizeOfStackReserve); + DUMP_FIELD(nt.OptionalHeader64.SizeOfStackCommit); + DUMP_FIELD(nt.OptionalHeader64.SizeOfHeapReserve); + DUMP_FIELD(nt.OptionalHeader64.SizeOfHeapCommit); + DUMP_FIELD(nt.OptionalHeader64.LoaderFlags); + DUMP_DEC_FIELD(nt.OptionalHeader64.NumberOfRvaAndSizes); + } #undef DUMP_FIELD #undef DUMP_DEC_FIELD - std::cout << "Imports: " - << "\n"; - IterImpVAString(p, printImports, NULL); - std::cout << "Relocations: " - << "\n"; - IterRelocs(p, printRelocs, NULL); - std::cout << "Symbols (symbol table): " - << "\n"; - IterSymbols(p, printSymbols, NULL); - std::cout << "Sections: " - << "\n"; - IterSec(p, printSecs, NULL); - std::cout << "Exports: " - << "\n"; - IterExpVA(p, printExps, NULL); - - // read the first 8 bytes from the entry point and print them - VA entryPoint; - if (GetEntryPoint(p, entryPoint)) { - std::cout << "First 8 bytes from entry point (0x"; - - std::cout << std::hex << entryPoint << "):" + std::cout << "Imports: " << "\n"; - for (std::size_t i = 0; i < 8; i++) { - std::uint8_t b; - if (!ReadByteAtVA(p, i + entryPoint, b)) { - std::cout << " ERR"; - } else { - std::cout << " 0x" << std::hex << static_cast(b); + IterImpVAString(p, printImports, NULL); + std::cout << "Relocations: " + << "\n"; + IterRelocs(p, printRelocs, NULL); + std::cout << "Symbols (symbol table): " + << "\n"; + IterSymbols(p, printSymbols, NULL); + std::cout << "Sections: " + << "\n"; + IterSec(p, printSecs, NULL); + std::cout << "Exports: " + << "\n"; + IterExpVA(p, printExps, NULL); + + // read the first 8 bytes from the entry point and print them + VA entryPoint; + if (GetEntryPoint(p, entryPoint)) { + std::cout << "First 8 bytes from entry point (0x"; + std::cout << std::hex << entryPoint << "):" + << "\n"; + for (std::size_t i = 0; i < 8; i++) { + std::uint8_t b; + if (!ReadByteAtVA(p, i + entryPoint, b)) { + std::cout << " ERR"; + } else { + std::cout << " 0x" << std::hex << static_cast(b); + } } + + std::cout << "\n"; } - std::cout << "\n"; + std::cout << "Resources: " + << "\n"; + IterRsrc(p, printRsrc, NULL); + + DestructParsedPE(p); + + return 0; } - - std::cout << "Resources: " - << "\n"; - IterRsrc(p, printRsrc, NULL); - - DestructParsedPE(p); - - return 0; } diff --git a/pe-parser-library/include/parser-library/nt-headers.h b/pe-parser-library/include/parser-library/nt-headers.h index 583a7be..0056211 100644 --- a/pe-parser-library/include/parser-library/nt-headers.h +++ b/pe-parser-library/include/parser-library/nt-headers.h @@ -359,7 +359,9 @@ struct rich_header { std::vector Entries; std::uint32_t EndSignature; std::uint32_t DecryptionKey; + std::uint32_t Checksum; bool isPresent; + bool isValid; }; /* diff --git a/pe-parser-library/include/parser-library/parse.h b/pe-parser-library/include/parser-library/parse.h index ed86c0e..0a20d85 100644 --- a/pe-parser-library/include/parser-library/parse.h +++ b/pe-parser-library/include/parser-library/parse.h @@ -157,6 +157,7 @@ uint64_t bufLen(bounded_buffer *b); struct parsed_pe_internal; typedef struct _pe_header { + dos_header dos; rich_header rich; nt_header_32 nt; } pe_header; @@ -170,8 +171,8 @@ typedef struct _parsed_pe { // Resolve a Rich header product id / build number pair to a known // product name typedef std::pair ProductKey; -const std::string &GetRichProductName(std::uint16_t prodId, - std::uint16_t buildNum); +const std::string &GetRichObjectType(std::uint16_t prodId); +const std::string &GetRichProductName(std::uint16_t buildNum); // get parser error status as integer std::uint32_t GetPEErr(); diff --git a/pe-parser-library/src/parse.cpp b/pe-parser-library/src/parse.cpp index 340c9be..106e8f0 100644 --- a/pe-parser-library/src/parse.cpp +++ b/pe-parser-library/src/parse.cpp @@ -123,20 +123,347 @@ struct parsed_pe_internal { std::vector symbols; }; -// The mapping of Rich header product id / build number pairs -// to strings -static const std::map ProductMap = { - {std::make_pair(static_cast(1), - static_cast(0)), - "Imported Functions"}}; +// String representation of Rich header object types +static const std::string kProdId_C = "[ C ]"; +static const std::string kProdId_CPP = "[C++]"; +static const std::string kProdId_RES = "[RES]"; +static const std::string kProdId_IMP = "[IMP]"; +static const std::string kProdId_EXP = "[EXP]"; +static const std::string kProdId_ASM = "[ASM]"; +static const std::string kProdId_LNK = "[LNK]"; +static const std::string kProdId_UNK = "[ ? ]"; + +// Mapping of Rich header Product ID to object type string +// Source: https://github.com/dishather/richprint/blob/master/comp_id.txt +static const std::map ProductIdMap = { + {std::make_pair(static_cast(0x0000), kProdId_UNK)}, + {std::make_pair(static_cast(0x0002), kProdId_IMP)}, + {std::make_pair(static_cast(0x0004), kProdId_LNK)}, + {std::make_pair(static_cast(0x0006), kProdId_RES)}, + {std::make_pair(static_cast(0x000A), kProdId_C)}, + {std::make_pair(static_cast(0x000B), kProdId_CPP)}, + {std::make_pair(static_cast(0x000F), kProdId_ASM)}, + {std::make_pair(static_cast(0x0015), kProdId_C)}, + {std::make_pair(static_cast(0x0016), kProdId_CPP)}, + {std::make_pair(static_cast(0x0019), kProdId_IMP)}, + {std::make_pair(static_cast(0x001C), kProdId_C)}, + {std::make_pair(static_cast(0x001D), kProdId_CPP)}, + {std::make_pair(static_cast(0x003D), kProdId_LNK)}, + {std::make_pair(static_cast(0x003F), kProdId_EXP)}, + {std::make_pair(static_cast(0x0040), kProdId_ASM)}, + {std::make_pair(static_cast(0x0045), kProdId_RES)}, + {std::make_pair(static_cast(0x005A), kProdId_LNK)}, + {std::make_pair(static_cast(0x005C), kProdId_EXP)}, + {std::make_pair(static_cast(0x005D), kProdId_IMP)}, + {std::make_pair(static_cast(0x005E), kProdId_RES)}, + {std::make_pair(static_cast(0x005F), kProdId_C)}, + {std::make_pair(static_cast(0x0060), kProdId_CPP)}, + {std::make_pair(static_cast(0x006D), kProdId_C)}, + {std::make_pair(static_cast(0x006E), kProdId_CPP)}, + {std::make_pair(static_cast(0x0078), kProdId_LNK)}, + {std::make_pair(static_cast(0x007A), kProdId_EXP)}, + {std::make_pair(static_cast(0x007B), kProdId_IMP)}, + {std::make_pair(static_cast(0x007C), kProdId_RES)}, + {std::make_pair(static_cast(0x007D), kProdId_ASM)}, + {std::make_pair(static_cast(0x0083), kProdId_C)}, + {std::make_pair(static_cast(0x0084), kProdId_CPP)}, + {std::make_pair(static_cast(0x0091), kProdId_LNK)}, + {std::make_pair(static_cast(0x0092), kProdId_EXP)}, + {std::make_pair(static_cast(0x0093), kProdId_IMP)}, + {std::make_pair(static_cast(0x0094), kProdId_RES)}, + {std::make_pair(static_cast(0x0095), kProdId_ASM)}, + {std::make_pair(static_cast(0x009A), kProdId_RES)}, + {std::make_pair(static_cast(0x009B), kProdId_EXP)}, + {std::make_pair(static_cast(0x009C), kProdId_IMP)}, + {std::make_pair(static_cast(0x009D), kProdId_LNK)}, + {std::make_pair(static_cast(0x009E), kProdId_ASM)}, + {std::make_pair(static_cast(0x00AA), kProdId_C)}, + {std::make_pair(static_cast(0x00AB), kProdId_CPP)}, + {std::make_pair(static_cast(0x00C9), kProdId_RES)}, + {std::make_pair(static_cast(0x00CA), kProdId_EXP)}, + {std::make_pair(static_cast(0x00CB), kProdId_IMP)}, + {std::make_pair(static_cast(0x00CC), kProdId_LNK)}, + {std::make_pair(static_cast(0x00CD), kProdId_ASM)}, + {std::make_pair(static_cast(0x00CE), kProdId_C)}, + {std::make_pair(static_cast(0x00CF), kProdId_CPP)}, + {std::make_pair(static_cast(0x00DB), kProdId_RES)}, + {std::make_pair(static_cast(0x00DC), kProdId_EXP)}, + {std::make_pair(static_cast(0x00DD), kProdId_IMP)}, + {std::make_pair(static_cast(0x00DE), kProdId_LNK)}, + {std::make_pair(static_cast(0x00DF), kProdId_ASM)}, + {std::make_pair(static_cast(0x00E0), kProdId_C)}, + {std::make_pair(static_cast(0x00E1), kProdId_CPP)}, + {std::make_pair(static_cast(0x00FF), kProdId_RES)}, + {std::make_pair(static_cast(0x0100), kProdId_EXP)}, + {std::make_pair(static_cast(0x0101), kProdId_IMP)}, + {std::make_pair(static_cast(0x0102), kProdId_LNK)}, + {std::make_pair(static_cast(0x0103), kProdId_ASM)}, + {std::make_pair(static_cast(0x0104), kProdId_C)}, + {std::make_pair(static_cast(0x0105), kProdId_CPP)}}; + +// Mapping of Rich header build number to version strings +static const std::map ProductMap = { + // Source: https://github.com/dishather/richprint/blob/master/comp_id.txt + {std::make_pair(static_cast(0x0000), "Imported Functions")}, + {std::make_pair(static_cast(0x0684), + "VS97 v5.0 SP3 cvtres 5.00.1668")}, + {std::make_pair(static_cast(0x06B8), + "VS98 v6.0 cvtres build 1720")}, + {std::make_pair(static_cast(0x06C8), + "VS98 v6.0 SP6 cvtres build 1736")}, + {std::make_pair(static_cast(0x1C87), + "VS97 v5.0 SP3 link 5.10.7303")}, + {std::make_pair(static_cast(0x5E92), + "VS2015 v14.0 UPD3 build 24210")}, + {std::make_pair(static_cast(0x5E95), + "VS2015 UPD3 build 24213")}, + + // http://bytepointer.com/articles/the_microsoft_rich_header.htm + {std::make_pair(static_cast(0x0BEC), + "VS2003 v7.1 Free Toolkit .NET build 3052")}, + {std::make_pair(static_cast(0x0C05), + "VS2003 v7.1 .NET build 3077")}, + {std::make_pair(static_cast(0x0FC3), + "VS2003 v7.1 | Windows Server 2003 SP1 DDK build 4035")}, + {std::make_pair(static_cast(0x1C83), "MASM 6.13.7299")}, + {std::make_pair(static_cast(0x178E), + "VS2003 v7.1 SP1 .NET build 6030")}, + {std::make_pair(static_cast(0x1FE8), + "VS98 v6.0 RTM/SP1/SP2 build 8168")}, + {std::make_pair(static_cast(0x1FE9), + "VB 6.0/SP1/SP2 build 8169")}, + {std::make_pair(static_cast(0x20FC), "MASM 6.14.8444")}, + {std::make_pair(static_cast(0x20FF), + "VC++ 6.0 SP3 build 8447")}, + {std::make_pair(static_cast(0x212F), + "VB 6.0 SP3 build 8495")}, + {std::make_pair(static_cast(0x225F), + "VS 6.0 SP4 build 8799")}, + {std::make_pair(static_cast(0x2263), "MASM 6.15.8803")}, + {std::make_pair(static_cast(0x22AD), + "VB 6.0 SP4 build 8877")}, + {std::make_pair(static_cast(0x2304), + "VB 6.0 SP5 build 8964")}, + {std::make_pair(static_cast(0x2306), + "VS 6.0 SP5 build 8966")}, + // {std::make_pair(static_cast(0x2346), "MASM 6.15.9030 + // (VS.NET 7.0 BETA 1)")}, + {std::make_pair(static_cast(0x2346), + "VS 7.0 2000 Beta 1 build 9030")}, + {std::make_pair(static_cast(0x2354), + "VS 6.0 SP5 Processor Pack build 9044")}, + {std::make_pair(static_cast(0x2426), + "VS2001 v7.0 Beta 2 build 9254")}, + {std::make_pair(static_cast(0x24FA), + "VS2002 v7.0 .NET build 9466")}, + {std::make_pair(static_cast(0x2636), + "VB 6.0 SP6 / VC++ build 9782")}, + {std::make_pair(static_cast(0x26E3), + "VS2002 v7.0 SP1 build 9955")}, + {std::make_pair(static_cast(0x520D), + "VS2013 v12.[0,1] build 21005")}, + {std::make_pair(static_cast(0x521E), + "VS2008 v9.0 build 21022")}, + {std::make_pair(static_cast(0x56C7), + "VS2015 v14.0 build 22215")}, + {std::make_pair(static_cast(0x59F2), + "VS2015 v14.0 build 23026")}, + {std::make_pair(static_cast(0x5BD2), + "VS2015 v14.0 UPD1 build 23506")}, + {std::make_pair(static_cast(0x5D10), + "VS2015 v14.0 UPD2 build 23824")}, + {std::make_pair(static_cast(0x5E97), + "VS2015 v14.0 UPD3.1 build 24215")}, + {std::make_pair(static_cast(0x7725), + "VS2013 v12.0 UPD2 build 30501")}, + {std::make_pair(static_cast(0x766F), + "VS2010 v10.0 build 30319")}, + {std::make_pair(static_cast(0x7809), + "VS2008 v9.0 SP1 build 30729")}, + {std::make_pair(static_cast(0x797D), + "VS2013 v12.0 UPD4 build 31101")}, + {std::make_pair(static_cast(0x9D1B), + "VS2010 v10.0 SP1 build 40219")}, + {std::make_pair(static_cast(0x9EB5), + "VS2013 v12.0 UPD5 build 40629")}, + {std::make_pair(static_cast(0xC497), + "VS2005 v8.0 (Beta) build 50327")}, + {std::make_pair(static_cast(0xC627), + "VS2005 v8.0 | VS2012 v11.0 build 50727")}, + {std::make_pair(static_cast(0xC751), + "VS2012 v11.0 Nov CTP build 51025")}, + {std::make_pair(static_cast(0xC7A2), + "VS2012 v11.0 UPD1 build 51106")}, + {std::make_pair(static_cast(0xEB9B), + "VS2012 v11.0 UPD2 build 60315")}, + {std::make_pair(static_cast(0xECC2), + "VS2012 v11.0 UPD3 build 60610")}, + {std::make_pair(static_cast(0xEE66), + "VS2012 v11.0 UPD4 build 61030")}, + {std::make_pair(static_cast(0x5E9A), + "VS2015 v14.0 build 24218")}, + {std::make_pair(static_cast(0x61BB), + "VS2017 v14.1 build 25019")}, + + // https://dev.to/yumetodo/list-of-mscver-and-mscfullver-8nd + {std::make_pair(static_cast(0x2264), + "VS 6 [SP5,SP6] build 8804")}, + {std::make_pair(static_cast(0x23D8), "Windows XP SP1 DDK")}, + {std::make_pair(static_cast(0x0883), + "Windows Server 2003 DDK")}, + {std::make_pair(static_cast(0x08F4), + "VS2003 v7.1 .NET Beta build 2292")}, + {std::make_pair(static_cast(0x9D76), + "Windows Server 2003 SP1 DDK (for AMD64)")}, + {std::make_pair(static_cast(0x9E9F), + "VS2005 v8.0 Beta 1 build 40607")}, + {std::make_pair(static_cast(0xC427), + "VS2005 v8.0 Beta 2 build 50215")}, + {std::make_pair(static_cast(0xC490), + "VS2005 v8.0 build 50320")}, + {std::make_pair(static_cast(0x50E2), + "VS2008 v9.0 Beta 2 build 20706")}, + {std::make_pair(static_cast(0x501A), + "VS2010 v10.0 Beta 1 build 20506")}, + {std::make_pair(static_cast(0x520B), + "VS2010 v10.0 Beta 2 build 21003")}, + {std::make_pair(static_cast(0x5089), + "VS2013 v12.0 Preview build 20617")}, + {std::make_pair(static_cast(0x515B), + "VS2013 v12.0 RC build 20827")}, + {std::make_pair(static_cast(0x527A), + "VS2013 v12.0 Nov CTP build 21114")}, + {std::make_pair(static_cast(0x63A3), + "VS2017 v15.3.3 build 25507")}, + {std::make_pair(static_cast(0x63C6), + "VS2017 v15.4.4 build 25542")}, + {std::make_pair(static_cast(0x63CB), + "VS2017 v15.4.5 build 25547")}, + {std::make_pair(static_cast(0x7674), + "VS2013 v12.0 UPD2 RC build 30324")}, + + // https://walbourn.github.io/visual-studio-2015-update-2/ + {std::make_pair(static_cast(0x5D6E), + "VS2015 v14.0 UPD2 build 23918")}, + + // https://walbourn.github.io/visual-studio-2017/ + {std::make_pair(static_cast(0x61B9), + "VS2017 v15.[0,1] build 25017")}, + {std::make_pair(static_cast(0x63A2), + "VS2017 v15.2 build 25019")}, + + // https://walbourn.github.io/vs-2017-15-5-update/ + {std::make_pair(static_cast(0x64E6), + "VS2017 v15 build 25830")}, + {std::make_pair(static_cast(0x64E7), + "VS2017 v15.5.2 build 25831")}, + {std::make_pair(static_cast(0x64EA), + "VS2017 v15.5.[3,4] build 25834")}, + {std::make_pair(static_cast(0x64EB), + "VS2017 v15.5.[5,6,7] build 25835")}, + + // https://walbourn.github.io/vs-2017-15-6-update/ + {std::make_pair(static_cast(0x6610), + "VS2017 v15.6.[0,1,2] build 26128")}, + {std::make_pair(static_cast(0x6611), + "VS2017 v15.6.[3,4] build 26129")}, + {std::make_pair(static_cast(0x6613), + "VS2017 v15.6.6 build 26131")}, + {std::make_pair(static_cast(0x6614), + "VS2017 v15.6.7 build 26132")}, + + // https://devblogs.microsoft.com/visualstudio/visual-studio-2017-update/ + {std::make_pair(static_cast(0x6723), + "VS2017 v15.1 build 26403")}, + + // https://walbourn.github.io/vs-2017-15-7-update/ + {std::make_pair(static_cast(0x673C), + "VS2017 v15.7.[0,1] build 26428")}, + {std::make_pair(static_cast(0x673D), + "VS2017 v15.7.2 build 26429")}, + {std::make_pair(static_cast(0x673E), + "VS2017 v15.7.3 build 26430")}, + {std::make_pair(static_cast(0x673F), + "VS2017 v15.7.4 build 26431")}, + {std::make_pair(static_cast(0x6741), + "VS2017 v15.7.5 build 26433")}, + + // https://walbourn.github.io/visual-studio-2019/ + {std::make_pair(static_cast(0x6B74), + "VS2019 v16.0.0 build 27508")}, + + // https://walbourn.github.io/vs-2017-15-8-update/ + {std::make_pair(static_cast(0x6866), + "VS2017 v15.8.0 build 26726")}, + {std::make_pair(static_cast(0x6869), + "VS2017 v15.8.4 build 26729")}, + {std::make_pair(static_cast(0x686A), + "VS2017 v15.8.9 build 26730")}, + {std::make_pair(static_cast(0x686C), + "VS2017 v15.8.5 build 26732")}, + + // https://walbourn.github.io/vs-2017-15-9-update/ + {std::make_pair(static_cast(0x698F), + "VS2017 v15.9.[0,1] build 27023")}, + {std::make_pair(static_cast(0x6990), + "VS2017 v15.9.2 build 27024")}, + {std::make_pair(static_cast(0x6991), + "VS2017 v15.9.4 build 27025")}, + {std::make_pair(static_cast(0x6992), + "VS2017 v15.9.5 build 27026")}, + {std::make_pair(static_cast(0x6993), + "VS2017 v15.9.7 build 27027")}, + {std::make_pair(static_cast(0x6996), + "VS2017 v15.9.11 build 27030")}, + {std::make_pair(static_cast(0x6997), + "VS2017 v15.9.12 build 27031")}, + {std::make_pair(static_cast(0x6998), + "VS2017 v15.9.14 build 27032")}, + {std::make_pair(static_cast(0x699A), + "VS2017 v15.9.16 build 27034")}, + + // https://walbourn.github.io/visual-studio-2019/ + {std::make_pair(static_cast(0x6B74), + "VS2019 v16.0.0 RTM build 27508")}, + + // https://walbourn.github.io/vs-2019-update-1/ + {std::make_pair(static_cast(0x6C36), + "VS2019 v16.1.2 UPD1 build 27702")}, + + // https://walbourn.github.io/vs-2019-update-2/ + {std::make_pair(static_cast(0x6D01), + "VS2019 v16.2.3 UPD2 build 27905")}, + + // https://walbourn.github.io/vs-2019-update-3/ + {std::make_pair(static_cast(0x6DC9), + "VS2019 v16.3.2 UPD3 build 28105")}, + + // https://walbourn.github.io/visual-studio-2013-update-3/ + {std::make_pair(static_cast(0x7803), + "VS2013 v12.0 UPD3 build 30723")}, + + // experimentation + {std::make_pair(static_cast(0x685B), + "VS2017 v15.8.? build 26715")}, +}; static const std::string kUnknownProduct = ""; -// Resolve a Rich header product id / build number pair to a known -// product name -const std::string &GetRichProductName(std::uint16_t prodId, - std::uint16_t buildNum) { - auto it = ProductMap.find(std::make_pair(prodId, buildNum)); +// Returns a stringified Rich header object type given a product id +const std::string &GetRichObjectType(std::uint16_t prodId) { + + auto it = ProductIdMap.find(prodId); + if (it != ProductIdMap.end()) { + return it->second; + } else { + return kProdId_UNK; + } +} + +// Returns a stringified Rich header product name given a build number +const std::string &GetRichProductName(std::uint16_t buildNum) { + + auto it = ProductMap.find(buildNum); if (it != ProductMap.end()) { return it->second; } else { @@ -836,6 +1163,41 @@ bool readNtHeader(bounded_buffer *b, nt_header_32 &header) { return true; } +// zero extends its first argument to 32 bits and then performs a rotate left +// operation equal to the second arguments value of the first argument’s bits +static inline std::uint32_t rol(std::uint32_t val, std::uint32_t num) { + return ((val << num) & 0xffffffff) | (val >> (32 - num)); +} + +std::uint32_t calculateRichChecksum(const bounded_buffer *b, pe_header &p) { + + // First, calculate the sum of the DOS header bytes each rotated left the + // number of times their position relative to the start of the DOS header e.g. + // second byte is rotated left 2x using rol operation + std::uint32_t checksum = 0; + + for (uint8_t i = 0; i < RICH_OFFSET; i++) { + + // skip over dos e_lfanew field at offset 0x3C + if (i >= 0x3C && i <= 0x3F) { + continue; + } + checksum += rol(b->buf[i], i); + } + + // Next, take summation of each Rich header entry by combining its ProductId + // and BuildNumber into a single 32 bit number and rotating by its count. + for (rich_entry entry : p.rich.Entries) { + std::uint32_t num = + static_cast((entry.ProductId << 16) | entry.BuildNumber); + checksum += rol(num, entry.Count & 0x1F); + } + + checksum += RICH_OFFSET; + + return checksum; +} + bool readRichHeader(bounded_buffer *rich_buf, std::uint32_t key, rich_header &rich_hdr) { @@ -914,30 +1276,62 @@ bool readRichHeader(bounded_buffer *rich_buf, return true; } +bool readDosHeader(bounded_buffer *file, dos_header &dos_hdr) { + if (file == nullptr) { + return false; + } + + READ_WORD(file, 0, dos_hdr, e_magic); + READ_WORD(file, 0, dos_hdr, e_cblp); + READ_WORD(file, 0, dos_hdr, e_cp); + READ_WORD(file, 0, dos_hdr, e_crlc); + READ_WORD(file, 0, dos_hdr, e_cparhdr); + READ_WORD(file, 0, dos_hdr, e_minalloc); + READ_WORD(file, 0, dos_hdr, e_maxalloc); + READ_WORD(file, 0, dos_hdr, e_ss); + READ_WORD(file, 0, dos_hdr, e_sp); + READ_WORD(file, 0, dos_hdr, e_csum); + READ_WORD(file, 0, dos_hdr, e_ip); + READ_WORD(file, 0, dos_hdr, e_cs); + READ_WORD(file, 0, dos_hdr, e_lfarlc); + READ_WORD(file, 0, dos_hdr, e_ovno); + READ_WORD(file, 0, dos_hdr, e_res[0]); + READ_WORD(file, 0, dos_hdr, e_res[1]); + READ_WORD(file, 0, dos_hdr, e_res[2]); + READ_WORD(file, 0, dos_hdr, e_res[3]); + READ_WORD(file, 0, dos_hdr, e_oemid); + READ_WORD(file, 0, dos_hdr, e_oeminfo); + READ_WORD(file, 0, dos_hdr, e_res2[0]); + READ_WORD(file, 0, dos_hdr, e_res2[1]); + READ_WORD(file, 0, dos_hdr, e_res2[2]); + READ_WORD(file, 0, dos_hdr, e_res2[3]); + READ_WORD(file, 0, dos_hdr, e_res2[4]); + READ_WORD(file, 0, dos_hdr, e_res2[5]); + READ_WORD(file, 0, dos_hdr, e_res2[6]); + READ_WORD(file, 0, dos_hdr, e_res2[7]); + READ_WORD(file, 0, dos_hdr, e_res2[8]); + READ_WORD(file, 0, dos_hdr, e_res2[9]); + READ_DWORD(file, 0, dos_hdr, e_lfanew); + + return true; +} + bool getHeader(bounded_buffer *file, pe_header &p, bounded_buffer *&rem) { if (file == nullptr) { return false; } - // start by reading MZ - std::uint16_t tmp = 0; - std::uint32_t curOffset = 0; - if (!readWord(file, curOffset, tmp)) { - PE_ERR(PEERR_READ); - return false; - } - if (tmp != MZ_MAGIC) { + // read the DOS header + readDosHeader(file, p.dos); + + if (p.dos.e_magic != MZ_MAGIC) { PE_ERR(PEERR_MAGIC); return false; } - // read the offset to the NT headers - std::uint32_t offset; - if (!readDword(file, _offset(dos_header, e_lfanew), offset)) { - PE_ERR(PEERR_READ); - return false; - } - curOffset += offset; + // get the offset to the NT headers + std::uint32_t offset = p.dos.e_lfanew; + std::uint32_t curOffset = offset; // read rich header std::uint32_t dword; @@ -983,6 +1377,24 @@ bool getHeader(bounded_buffer *file, pe_header &p, bounded_buffer *&rem) { deleteBuffer(richBuf); } + // Split the DOS header into a separate buffer which + // starts at offset 0 and has length 0x80 + bounded_buffer *dosBuf = splitBuffer(file, 0, RICH_OFFSET); + if (dosBuf == nullptr) { + return false; + } + // Calculate checksum + p.rich.Checksum = calculateRichChecksum(dosBuf, p); + if (p.rich.Checksum == p.rich.DecryptionKey) { + p.rich.isValid = true; + } else { + p.rich.isValid = false; + } + if (dosBuf != nullptr) { + deleteBuffer(dosBuf); + } + + // Rich header not present } else { p.rich.isPresent = false; } From 2acb1fc9750c0f5f6676f7b94f409e457b96c3f8 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Fri, 6 Dec 2019 13:36:08 -0500 Subject: [PATCH 09/10] Dockerfile: Dockerize pe-parse (#102) Adds a default entrypoint for the dump-pe as well. --- Dockerfile | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3db0ff5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM alpine:latest +ARG BUILD_TYPE=Release + +LABEL name "pe-parse" +LABEL src "https://github.com/trailofbits/pe-parse" +LABEL creator "Trail of Bits" +LABEL dockerfile_maintenance "William Woodruff " +LABEL desc "Principled, lightweight C/C++ PE parser" + +RUN apk add --no-cache cmake icu-dev build-base + +COPY . /app/pe-parse +WORKDIR /app/pe-parse +RUN mkdir build && \ + cd build && \ + cmake -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" .. && \ + cmake --build . --config "${BUILD_TYPE}" && \ + cmake --build . --config "${BUILD_TYPE}" --target install + +ENTRYPOINT [ "/usr/bin/dump-pe" ] +CMD ["--help"] From 5a21e222923be573de41917195f39e22875be56f Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 23 Dec 2019 09:28:47 -0600 Subject: [PATCH 10/10] Github actions (#103) --- .github/workflows/ci.yml | 49 ++++++++++++++++++++++++++++++++++++++++ README.md | 6 ++--- 2 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0fe7154 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + schedule: + # run CI every day even if no PRs/merges occur + - cron: '0 12 * * *' + +jobs: + test: + strategy: + matrix: + platform: ["ubuntu-latest", "macos-latest"] + compiler: + - { CC: "clang", CXX: "clang++" } + - { CC: "gcc", CXX: "g++" } + exclude: + - platform: macos-latest + compiler: { CC: "gcc", CXX: "g++" } + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - name: Build C + env: + CC: ${{ matrix.compiler.CC }} + CXX: ${{ matrix.compiler.CXX }} + run: | + mkdir build + cd build + cmake .. + make + - name: Build Python + run: | + cd python + python2 setup.py build + python3 setup.py build + test-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Build C + run: | + mkdir build + cd build + cmake -G "Visual Studio 16 2019" -A x64 .. + cmake --build . diff --git a/README.md b/README.md index 2682a3f..b943386 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ pe-parse ========================================= -[![Build Status](https://travis-ci.org/trailofbits/pe-parse.svg?branch=master)](https://travis-ci.org/trailofbits/pe-parse) +[![Build Status](https://img.shields.io/github/workflow/status/trailofbits/pe-parse/CI/master)](https://github.com/trailofbits/pe-parse/actions?query=workflow%3ACI) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3671/badge.svg)](https://scan.coverity.com/projects/3671) -pe-parse is a principled, lightweight parser for windows portable executable files. It was created to assist in compiled program analysis, potentially of programs of unknown origins. This means that it should be resistant to malformed or maliciously crafted PE files, and it should support questions that analysis software would ask of an executable program container. For example, listing relocations, describing imports and exports, and supporting byte reads from virtual addresses as well as file offsets. +pe-parse is a principled, lightweight parser for windows portable executable files. It was created to assist in compiled program analysis, potentially of programs of unknown origins. This means that it should be resistant to malformed or maliciously crafted PE files, and it should support questions that analysis software would ask of an executable program container. For example, listing relocations, describing imports and exports, and supporting byte reads from virtual addresses as well as file offsets. pe-parse supports these use cases via a minimal API that provides methods for * Opening and closing a PE file @@ -16,7 +16,7 @@ pe-parse supports these use cases via a minimal API that provides methods for * Reading bytes from specified virtual addresses * Retrieving the program entry point -The interface is defined in `parser-library/parse.h`. The program in `dump-prog/dump.cpp` is an example of using the parser-library API to dump information about a PE file. +The interface is defined in `parser-library/parse.h`. The program in `dump-prog/dump.cpp` is an example of using the parser-library API to dump information about a PE file. Internally, the parser-library uses a bounded buffer abstraction to access information stored in the PE file. This should help in constructing a sane parser that allows for detection of the use of bogus values in the PE that would result in out of bounds accesses of the input buffer. Once data is read from the file it is sanitized and placed in C++ STL containers of internal types.