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
This commit is contained in:
William Woodruff 2019-10-21 09:00:54 -04:00 committed by GitHub
parent 1544c61c38
commit 2c775e5d6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 441 additions and 456 deletions

View File

@ -2,7 +2,6 @@ AlignEscapedNewlinesLeft: true
AllowShortFunctionsOnASingleLine: false AllowShortFunctionsOnASingleLine: false
BinPackArguments: false BinPackArguments: false
BinPackParameters: false BinPackParameters: false
BreakBeforeBraces: Attach
ColumnLimit: 80 ColumnLimit: 80
IndentCaseLabels: true IndentCaseLabels: true
IndentWidth: 2 IndentWidth: 2

View File

@ -1,32 +1,56 @@
language: cpp __build_stage_script: &__build_stage_script
stage: build
os: language: cpp
- linux script:
- osx - mkdir build && cd build
- cmake ..
compiler: - make
- clang - cd ../python
- gcc - python2 setup.py build
- python3 setup.py build
before_install: addons:
- ./travis.sh "$TRAVIS_OS_NAME" initialize apt:
script: packages:
- ./travis.sh "$TRAVIS_OS_NAME" build - cmake
- python2.7
matrix: - python-dev
exclude: - build-essential
- compiler: gcc - realpath
os: osx - libicu-dev
homebrew:
env: packages:
global: - cmake
- secure: "O+BGqz4ugoVIJbQTh0dJjKRrsSVzkCYSe0WpRzEWK3l8Mw7hqX300g81TxRwTzN2zfUsROMzaeGaXWfGzYakgW59K1WIioaczxtv2MzzUQTbqzJPa+qQoP9bk/b2wJ5jcOL965/rudRju4UiIwuIgzDAMN3nAfIEJgV/2zANLIg="
addons:
coverity_scan: coverity_scan:
project: project:
name: "trailofbits/pe-parse" name: "trailofbits/pe-parse"
description: "Principled, lightweight C/C++ PE parser" description: "Principled, lightweight C/C++ PE parser"
notification_email: dan@trailofbits.com notification_email: dan@trailofbits.com
build_command: "./travis.sh linux build" build_command_prepend: mkdir cov_build && cd cov_build && cmake ..
build_command: make
branch_pattern: master branch_pattern: master
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="

26
CONTRIBUTING.md Normal file
View File

@ -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)

View File

@ -22,10 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstring> #include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <parser-library/parse.h> #include <parser-library/parse.h>
@ -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) << "ProdId:" << std::setw(7) << r.ProductId;
std::cout << std::setw(10) << "Build:" << std::setw(7) << r.BuildNumber; std::cout << std::setw(10) << "Build:" << std::setw(7) << r.BuildNumber;
std::cout << std::setw(10) << "Name:" std::cout << std::setw(10) << "Name:" << std::setw(20)
<< std::setw(20) << GetRichProductName(r.ProductId, r.BuildNumber); << GetRichProductName(r.ProductId, r.BuildNumber);
std::cout << std::setw(10) << "Count:" << std::setw(7) << r.Count << "\n"; std::cout << std::setw(10) << "Count:" << std::setw(7) << r.Count << "\n";
return 0; return 0;
} }
@ -275,18 +275,17 @@ int printSecs(void *N,
if (data) if (data)
std::cout << "Sec Size: " << std::dec << data->bufLen << "\n"; std::cout << "Sec Size: " << std::dec << data->bufLen << "\n";
else else
std::cout << "Sec Size: 0" << "\n"; std::cout << "Sec Size: 0"
<< "\n";
return 0; return 0;
} }
#define DUMP_FIELD(x) \ #define DUMP_FIELD(x) \
std::cout << "" #x << ": 0x"; \ std::cout << "" #x << ": 0x"; \
std::cout << std::hex << static_cast<std::uint64_t>(p->peHeader.nt.x) \ std::cout << std::hex << static_cast<std::uint64_t>(p->peHeader.nt.x) << "\n";
<< "\n";
#define DUMP_DEC_FIELD(x) \ #define DUMP_DEC_FIELD(x) \
std::cout << "" #x << ": "; \ std::cout << "" #x << ": "; \
std::cout << std::dec << static_cast<std::uint64_t>(p->peHeader.nt.x) \ std::cout << std::dec << static_cast<std::uint64_t>(p->peHeader.nt.x) << "\n";
<< "\n";
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
if (argc != 2 || (argc == 2 && std::strcmp(argv[1], "--help") == 0)) { if (argc != 2 || (argc == 2 && std::strcmp(argv[1], "--help") == 0)) {
@ -297,10 +296,15 @@ int main(int argc, char *argv[]) {
} }
parsed_pe *p = ParsePEFromFile(argv[1]); 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 // Print Rich header info
if(p->peHeader.rich.isPresent) { if (p->peHeader.rich.isPresent) {
std::cout << "Rich header: present\n"; std::cout << "Rich header: present\n";
IterRich(p, printRich, NULL); IterRich(p, printRich, NULL);
} else { } else {
@ -373,15 +377,20 @@ int main(int argc, char *argv[]) {
#undef DUMP_FIELD #undef DUMP_FIELD
#undef DUMP_DEC_FIELD #undef DUMP_DEC_FIELD
std::cout << "Imports: " << "\n"; std::cout << "Imports: "
<< "\n";
IterImpVAString(p, printImports, NULL); IterImpVAString(p, printImports, NULL);
std::cout << "Relocations: " << "\n"; std::cout << "Relocations: "
<< "\n";
IterRelocs(p, printRelocs, NULL); IterRelocs(p, printRelocs, NULL);
std::cout << "Symbols (symbol table): " << "\n"; std::cout << "Symbols (symbol table): "
<< "\n";
IterSymbols(p, printSymbols, NULL); IterSymbols(p, printSymbols, NULL);
std::cout << "Sections: " << "\n"; std::cout << "Sections: "
<< "\n";
IterSec(p, printSecs, NULL); IterSec(p, printSecs, NULL);
std::cout << "Exports: " << "\n"; std::cout << "Exports: "
<< "\n";
IterExpVA(p, printExps, NULL); IterExpVA(p, printExps, NULL);
// read the first 8 bytes from the entry point and print them // read the first 8 bytes from the entry point and print them
@ -389,7 +398,8 @@ int main(int argc, char *argv[]) {
if (GetEntryPoint(p, entryPoint)) { if (GetEntryPoint(p, entryPoint)) {
std::cout << "First 8 bytes from entry point (0x"; std::cout << "First 8 bytes from entry point (0x";
std::cout << std::hex << entryPoint << "):" << "\n"; std::cout << std::hex << entryPoint << "):"
<< "\n";
for (std::size_t i = 0; i < 8; i++) { for (std::size_t i = 0; i < 8; i++) {
std::uint8_t b; std::uint8_t b;
if (!ReadByteAtVA(p, i + entryPoint, b)) { if (!ReadByteAtVA(p, i + entryPoint, b)) {
@ -402,14 +412,11 @@ int main(int argc, char *argv[]) {
std::cout << "\n"; std::cout << "\n";
} }
std::cout << "Resources: " << "\n"; std::cout << "Resources: "
IterRsrc(p, printRsrc, NULL);
DestructParsedPE(p);
} else {
std::cout << "Error: " << GetPEErr() << " (" << GetPEErrString() << ")"
<< "\n"; << "\n";
std::cout << "Location: " << GetPEErrLoc() << "\n"; IterRsrc(p, printRsrc, NULL);
}
DestructParsedPE(p);
return 0; return 0;
} }

View File

@ -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)

View File

@ -1,7 +1,7 @@
#include <algorithm>
#include <iostream> #include <iostream>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <algorithm>
#include <climits> #include <climits>
#include <cstring> #include <cstring>
@ -262,7 +262,8 @@ int main(int argc, char *argv[]) {
char *last_parsed_char = nullptr; char *last_parsed_char = nullptr;
errno = 0; 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) { if (address == 0U && *last_parsed_char != 0) {
std::cout << "Invalid address specified\n"; std::cout << "Invalid address specified\n";
return 1; return 1;

View File

@ -1,8 +0,0 @@
project('pe-parse',
'cpp',
default_options : [ 'cpp_std=c++11' ],
license : [ 'MIT' ],
)
subdir('parser-library')
subdir('dump-prog')

View File

@ -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')

View File

@ -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_OPTIONAL_64_MAGIC = 0x20B;
constexpr std::uint16_t NT_SHORT_NAME_LEN = 8; constexpr std::uint16_t NT_SHORT_NAME_LEN = 8;
constexpr std::uint16_t SYMTAB_RECORD_LEN = 18; 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 // Machine Types
constexpr std::uint16_t IMAGE_FILE_MACHINE_UNKNOWN = 0x0; constexpr std::uint16_t IMAGE_FILE_MACHINE_UNKNOWN = 0x0;
@ -263,6 +248,25 @@ struct data_directory {
std::uint32_t Size; 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 { struct optional_header_32 {
std::uint16_t Magic; std::uint16_t Magic;
std::uint8_t MajorLinkerVersion; std::uint8_t MajorLinkerVersion;
@ -451,4 +455,101 @@ struct reloc_block {
std::uint32_t PageRVA; std::uint32_t PageRVA;
std::uint32_t BlockSize; 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 } // namespace peparse

View File

@ -32,7 +32,7 @@ THE SOFTWARE.
#include "to_string.h" #include "to_string.h"
#ifdef _MSC_VER #ifdef _MSC_VER
#define __typeof__(x) std::remove_reference < decltype(x) > ::type #define __typeof__(x) std::remove_reference < decltype(x)> ::type
#endif #endif
#define PE_ERR(x) \ #define PE_ERR(x) \
@ -137,6 +137,7 @@ enum pe_err {
PEERR_MAGIC = 9, PEERR_MAGIC = 9,
PEERR_BUFFER = 10, PEERR_BUFFER = 10,
PEERR_ADDRESS = 11, PEERR_ADDRESS = 11,
PEERR_SIZE = 12,
}; };
bool readByte(bounded_buffer *b, std::uint32_t offset, std::uint8_t &out); 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 // Resolve a Rich header product id / build number pair to a known
// product name // product name
typedef std::pair<std::uint16_t, std::uint16_t> ProductKey; typedef std::pair<std::uint16_t, std::uint16_t> 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 // get parser error status as integer
std::uint32_t GetPEErr(); std::uint32_t GetPEErr();
@ -230,4 +232,7 @@ const char *GetMachineAsString(parsed_pe *pe);
// get subsystem as human readable string // get subsystem as human readable string
const char *GetSubsystemAsString(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);
} // namespace peparse } // namespace peparse

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <string>
#include <sstream> #include <sstream>
#include <string>
#ifdef USE_ICU4C #ifdef USE_ICU4C
#include <unicode/unistr.h> #include <unicode/unistr.h>

View File

@ -23,10 +23,11 @@ THE SOFTWARE.
*/ */
#include <algorithm> #include <algorithm>
#include <array>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <vector>
#include <stdexcept> #include <stdexcept>
#include <vector>
#include <parser-library/nt-headers.h> #include <parser-library/nt-headers.h>
#include <parser-library/parse.h> #include <parser-library/parse.h>
@ -125,14 +126,14 @@ struct parsed_pe_internal {
// The mapping of Rich header product id / build number pairs // The mapping of Rich header product id / build number pairs
// to strings // to strings
static const std::map<ProductKey, const std::string> ProductMap = { static const std::map<ProductKey, const std::string> ProductMap = {
{std::make_pair(1, 0), "Imported Functions"} {std::make_pair(1, 0), "Imported Functions"}};
};
static const std::string kUnknownProduct = "<unknown>"; static const std::string kUnknownProduct = "<unknown>";
// Resolve a Rich header product id / build number pair to a known // Resolve a Rich header product id / build number pair to a known
// product name // 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)); auto it = ProductMap.find(std::make_pair(prodId, buildNum));
if (it != ProductMap.end()) { if (it != ProductMap.end()) {
return it->second; return it->second;
@ -144,7 +145,8 @@ const std::string& GetRichProductName(std::uint16_t prodId, std::uint16_t buildN
std::uint32_t err = 0; std::uint32_t err = 0;
std::string err_loc; std::string err_loc;
static const char *pe_err_str[] = {"None", static const char *pe_err_str[] = {
"None",
"Out of memory", "Out of memory",
"Invalid header", "Invalid header",
"Invalid section", "Invalid section",
@ -155,7 +157,9 @@ static const char *pe_err_str[] = {"None",
"Unable to stat", "Unable to stat",
"Bad magic", "Bad magic",
"Invalid buffer", "Invalid buffer",
"Invalid address",}; "Invalid address",
"Invalid size",
};
std::uint32_t GetPEErr() { std::uint32_t GetPEErr() {
return err; return err;
@ -230,8 +234,9 @@ const char *GetSymbolTableStorageClassName(std::uint8_t id) {
} }
} }
static bool static bool readCString(const bounded_buffer &buffer,
readCString(const bounded_buffer &buffer, std::uint32_t off, std::string &result) { std::uint32_t off,
std::string &result) {
if (off < buffer.bufLen) { if (off < buffer.bufLen) {
std::uint8_t *p = buffer.buf; std::uint8_t *p = buffer.buf;
std::uint32_t n = buffer.bufLen; std::uint32_t n = buffer.bufLen;
@ -282,7 +287,9 @@ void IterRsrc(parsed_pe *pe, iterRsrc cb, void *cbd) {
return; 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; std::uint16_t len;
if (!readWord(data, id, len)) { if (!readWord(data, id, len)) {
return false; return false;
@ -388,9 +395,10 @@ bool parse_resource_table(bounded_buffer *sectionData,
} }
} }
} else { } else {
/* .rsrc can accomodate up to 2**31 levels, but Windows only uses 3 by convention. /* .rsrc can accomodate up to 2**31 levels, but Windows only uses 3 by
* As such, any depth above 3 indicates potentially unchecked recusion. * convention. As such, any depth above 3 indicates potentially unchecked
* See: https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-rsrc-section * recusion. See:
* https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-rsrc-section
*/ */
PE_ERR(PEERR_RESC); 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 // now we have the section header information, so fill in a section
// object appropriately // object appropriately
section thisSec; 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]; std::uint8_t c = curSec.Name[charIndex];
if (c == 0) { if (c == 0) {
break; break;
@ -825,7 +834,9 @@ bool readNtHeader(bounded_buffer *b, nt_header_32 &header) {
return true; 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) { if (rich_buf == nullptr) {
return false; return false;
} }
@ -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 // a DWORD is 4 bytes. Loop is incrementing 8 bytes, however
// we are reading two DWORDS at a time, which is the size // we are reading two DWORDS at a time, which is the size
// of one rich header entry. // 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; rich_entry entry;
// Read first DWORD of entry and decrypt it // Read first DWORD of entry and decrypt it
if (!readDword(rich_buf, i, encrypted_dword)) { 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); entry.BuildNumber = (decrypted_dword & 0xFFFF);
// The second DWORD represents the use count // 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); PE_ERR(PEERR_READ);
return false; return false;
} }
@ -883,11 +894,10 @@ bool readRichHeader(bounded_buffer *rich_buf, std::uint32_t key, rich_header &ri
// Preserve the individual entry // Preserve the individual entry
rich_hdr.Entries.push_back(entry); rich_hdr.Entries.push_back(entry);
} }
// Preserve the end signature aka "Rich" magic // Preserve the end signature aka "Rich" magic
if (!readDword(rich_buf, rich_buf->bufLen-4, rich_hdr.EndSignature)) { if (!readDword(rich_buf, rich_buf->bufLen - 4, rich_hdr.EndSignature)) {
PE_ERR(PEERR_READ); PE_ERR(PEERR_READ);
return false; return false;
}; };
@ -960,7 +970,8 @@ bool getHeader(bounded_buffer *file, pe_header &p, bounded_buffer *&rem) {
} }
// Split the Rich header out into its own buffer // 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) { if (richBuf == nullptr) {
return false; return false;
} }
@ -992,12 +1003,12 @@ bool getHeader(bounded_buffer *file, pe_header &p, bounded_buffer *&rem) {
std::uint32_t rem_size; std::uint32_t rem_size;
if (p.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) { if (p.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) {
// signature + file_header + optional_header_32 // signature + file_header + optional_header_32
rem_size = rem_size = sizeof(std::uint32_t) + sizeof(file_header) +
sizeof(std::uint32_t) + sizeof(file_header) + sizeof(optional_header_32); sizeof(optional_header_32);
} else if (p.nt.OptionalMagic == NT_OPTIONAL_64_MAGIC) { } else if (p.nt.OptionalMagic == NT_OPTIONAL_64_MAGIC) {
// signature + file_header + optional_header_64 // signature + file_header + optional_header_64
rem_size = rem_size = sizeof(std::uint32_t) + sizeof(file_header) +
sizeof(std::uint32_t) + sizeof(file_header) + sizeof(optional_header_64); sizeof(optional_header_64);
} else { } else {
PE_ERR(PEERR_MAGIC); PE_ERR(PEERR_MAGIC);
deleteBuffer(ntBuf); deleteBuffer(ntBuf);
@ -1529,8 +1540,8 @@ bool getImports(parsed_pe *p) {
ent.moduleName = modName; ent.moduleName = modName;
p->internal->imports.push_back(ent); p->internal->imports.push_back(ent);
} else { } else {
std::string symName = std::string symName = "ORDINAL_" + modName + "_" +
"ORDINAL_" + modName + "_" + to_string<std::uint32_t>(oval, std::dec); to_string<std::uint32_t>(oval, std::dec);
importent ent; importent ent;
@ -1579,7 +1590,8 @@ bool getSymbolTable(parsed_pe *p) {
std::uint32_t offset = p->peHeader.nt.FileHeader.PointerToSymbolTable; 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; symbol sym;
// Read name // Read name
@ -1607,7 +1619,8 @@ bool getSymbolTable(parsed_pe *p) {
strOffset += sizeof(std::uint8_t); strOffset += sizeof(std::uint8_t);
} }
} else { } 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++) { n++) {
sym.strName.push_back(static_cast<char>(sym.name.shortName[n])); sym.strName.push_back(static_cast<char>(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<std::uint32_t>(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<uint8_t> rawEntry(sec.sectionData->buf + off,
sec.sectionData->buf + off + dir.Size);
return rawEntry.data();
}
} // namespace peparse } // namespace peparse

View File

@ -22,17 +22,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
#include <parser-library/to_string.h>
#include <locale>
#include <codecvt> #include <codecvt>
#include <locale>
#include <parser-library/to_string.h>
namespace peparse { namespace peparse {
// See https://stackoverflow.com/questions/38688417/utf-conversion-functions-in-c11 // See
std::string from_utf16(const UCharString &u) // https://stackoverflow.com/questions/38688417/utf-conversion-functions-in-c11
{ std::string from_utf16(const UCharString &u) {
#if defined(_MSC_VER) #if defined(_MSC_VER)
// std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>convert; // Doesn't compile with Visual Studio. // std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>convert;
// See https://stackoverflow.com/questions/32055357/visual-studio-c-2015-stdcodecvt-with-char16-t-or-char32-t // // 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::codecvt_utf8<std::int16_t>, std::int16_t> convert; std::wstring_convert<std::codecvt_utf8<std::int16_t>, std::int16_t> convert;
auto p = reinterpret_cast<const std::int16_t *>(u.data()); auto p = reinterpret_cast<const std::int16_t *>(u.data());
return convert.to_bytes(p, p + u.size()); return convert.to_bytes(p, p + u.size());

View File

@ -26,8 +26,7 @@ THE SOFTWARE.
#include <unicode/unistr.h> #include <unicode/unistr.h>
namespace peparse { 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()); icu::UnicodeString utf16_string = icu::UnicodeString(u.data(), u.length());
std::string result; std::string result;
utf16_string.toUTF8String(result); utf16_string.toUTF8String(result);

View File

@ -704,7 +704,8 @@ static PyObject *pepy_parsed_get_entry_point(PyObject *self, PyObject *args) {
return ret; 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; PyObject *ret;
const char *str; const char *str;
@ -721,7 +722,8 @@ static PyObject *pepy_parsed_get_machine_as_str(PyObject *self, PyObject *args)
return ret; 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; PyObject *ret;
const char *str; const char *str;
@ -1079,8 +1081,7 @@ static PyObject *pepy_parsed_get_relocations(PyObject *self, PyObject *args) {
#define PEPY_PARSED_GET(ATTR, VAL) \ #define PEPY_PARSED_GET(ATTR, VAL) \
static PyObject *pepy_parsed_get_##ATTR(PyObject *self, void *closure) { \ static PyObject *pepy_parsed_get_##ATTR(PyObject *self, void *closure) { \
PyObject *ret = \ PyObject *ret = PyLong_FromUnsignedLongLong( \
PyLong_FromUnsignedLongLong( \
((pepy_parsed *) self)->pe->peHeader.nt.VAL); \ ((pepy_parsed *) self)->pe->peHeader.nt.VAL); \
if (!ret) \ if (!ret) \
PyErr_SetString(PyExc_AttributeError, "Error getting attribute."); \ PyErr_SetString(PyExc_AttributeError, "Error getting attribute."); \

210
travis.sh
View File

@ -1,210 +0,0 @@
#!/usr/bin/env bash
main() {
if [ $# -ne 2 ] ; then
printf "Usage:\n\ttravis.sh <linux|osx> <initialize|build>\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 $?