From 1dc2c5356608b885ba8449c91b85121d23f87b2e Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 17 Mar 2020 13:38:56 -0400 Subject: [PATCH] Release 1.0 prep work (#113) Co-authored-by: Eric Kilmer --- .github/workflows/ci.yml | 74 ++++++- .gitignore | 7 +- LICENSE.txt => LICENSE | 0 MANIFEST.in | 3 + README.md | 85 +++++--- VERSION | 1 + pe-parser-library/CMakeLists.txt | 6 + pepy/README.md | 204 ++++++++++++++++++ {python => pepy}/pepy.cpp | 6 +- python/README.md | 217 -------------------- python/test.py | 99 --------- python/setup.py => setup.py | 38 ++-- test/assets/example.exe | Bin 0 -> 119296 bytes python/test_python3.py => test/test_pepy.py | 0 util/release | 43 ++++ 15 files changed, 411 insertions(+), 372 deletions(-) rename LICENSE.txt => LICENSE (100%) create mode 100644 MANIFEST.in create mode 100644 VERSION create mode 100644 pepy/README.md rename {python => pepy}/pepy.cpp (99%) delete mode 100644 python/README.md delete mode 100755 python/test.py rename python/setup.py => setup.py (70%) create mode 100644 test/assets/example.exe rename python/test_python3.py => test/test_pepy.py (100%) create mode 100755 util/release diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0fe7154..6ec3da6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,10 +10,11 @@ on: - cron: '0 12 * * *' jobs: - test: + pe-parse: strategy: matrix: platform: ["ubuntu-latest", "macos-latest"] + build-type: ["Debug", "Release"] compiler: - { CC: "clang", CXX: "clang++" } - { CC: "gcc", CXX: "g++" } @@ -23,27 +24,80 @@ jobs: runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v2 - - name: Build C + - name: build env: CC: ${{ matrix.compiler.CC }} CXX: ${{ matrix.compiler.CXX }} run: | mkdir build cd build - cmake .. - make - - name: Build Python + cmake -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} .. + cmake --build . + - name: test + run: | + ./build/dump-pe/dump-pe ./test/assets/example.exe + + pepy: + strategy: + matrix: + platform: ["ubuntu-latest", "macos-latest"] + python: + - "3.6" + - "3.7" + - "3.8" + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: build run: | - cd python - python2 setup.py build python3 setup.py build - test-windows: + - name: sdist and install + run: | + python3 setup.py sdist + python3 -m pip install --user dist/*.tar.gz + - name: test + run: | + python3 test/test_pepy.py test/assets/example.exe + + pe-parse-windows: + strategy: + matrix: + build-type: ["Debug", "Release"] runs-on: windows-latest steps: - uses: actions/checkout@v2 - - name: Build C + - name: build run: | mkdir build cd build cmake -G "Visual Studio 16 2019" -A x64 .. - cmake --build . + cmake --build . --config ${{ matrix.build-type }} + - name: test + run: | + .\build\dump-pe\${{ matrix.build-type }}\dump-pe.exe .\test\assets\example.exe + + pepy-windows: + strategy: + matrix: + python: + - "3.6" + - "3.7" + - "3.8" + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: build + run: | + python setup.py build + - name: install + run: | + python -m pip install --user . + - name: test + run: | + python test/test_pepy.py test/assets/example.exe diff --git a/.gitignore b/.gitignore index 806b84b..8236e7d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ Makefile cmake_install.cmake dump-prog/dump-prog *.swp -python/build +build/ .idea cmake-build-debug cmake-build-release @@ -11,4 +11,7 @@ CMakeSettings.json .vs .vscode examples_build - +.DS_Store +dist/ +MANIFEST +*.egg-info/ diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..b430c9d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include VERSION +include pepy/README.md +include pe-parser-library/include/parser-library/*.h diff --git a/README.md b/README.md index e0eb136..390b7c1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ 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) -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 @@ -15,20 +20,34 @@ 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`. -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. +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. + +pe-parse includes Python bindings via `pepy`, which can be installed via `pip`: + +```bash +$ pip3 install pepy +``` + +More information about `pepy` can be found in its [README](./pepy/README.md). + +## Dependencies -Dependencies -======== ### CMake * Debian/Ubuntu: `sudo apt-get install cmake` * RedHat/Fedora: `sudo yum install cmake` * OSX: `brew install cmake` * Windows: Download the installer from the [CMake page](https://cmake.org/download/) -Building -======== +## Building + ### Generic instructions ``` git clone https://github.com/trailofbits/pe-parse.git @@ -38,37 +57,48 @@ mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release .. -cmake --build . --config Release +cmake --build . # optional -cmake --build . --config Release --target install +cmake --build . --target install ``` -PE files that have a Resource section with strings for the Type are encoded in UTF-16, but that `std::string` expects UTF-8. Some cross-platform solution -is desired. You can let cmake choose one it finds in your build environment or you can choose one from the following options yourself and specify it with -the `-DUNICODE_LIBRARY` argument when generating the project files with cmake: -* `icu` (preferred) - "[ICU](http://site.icu-project.org/) is a mature, widely used set of C/C++ and Java libraries providing Unicode and Globalization support for software applications" -* `codecvt` - A C++ library header file ([now deprecated](http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0618r0.html)) supported by some C++ runtimes +PE files that have a Resource section with strings for the Type are encoded in UTF-16, but that +`std::string` expects UTF-8. Some cross-platform solution is desired. + +You can let `cmake` choose one it finds in your build environment or you can choose one from the +following options yourself and specify it with the `-DUNICODE_LIBRARY` argument when generating the +project files with `cmake`: + +* `icu` (preferred) - "[ICU](http://site.icu-project.org/) is a mature, widely used set of C/C++ +and Java libraries providing Unicode and Globalization support for software applications" +* `codecvt` - A C++ library header file +([now deprecated](http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0618r0.html)) supported +by some C++ runtimes ### Notes about Windows -If you are building on Windows with Visual Studio, the generator option can be used to select the compiler version and the output architecture: +If you are building on Windows with Visual Studio, the generator option can be used to select the +compiler version and the output architecture: ``` # Compile 64-bit binaries with Visual Studio 2017 -cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_BUILD_TYPE=Release .. +cmake -G "Visual Studio 15 2017 Win64" .. # Compile 32-bit binaries with Visual Studio 2017 -cmake -G "Visual Studio 15 2017" -DCMAKE_BUILD_TYPE=Release .. +cmake -G "Visual Studio 15 2017" .. ``` -Visual Studio 2015 or higher is required to use codecvt, but you also have the option of using [ICU](http://site.icu-project.org/). The easiest way to -get started with ICU in Windows is with [vcpkg](https://vcpkg.readthedocs.io/): `vcpkg install icu`. Then add the -`-DCMAKE_TOOLCHAIN_FILE=C:\src\vcpkg\scripts\buildsystems\vcpkg.cmake` argument when generating the project files with cmake to add the appropriate -library and include directories to the project. +Visual Studio 2015 or higher is required to use codecvt, but you also have the option of using +[ICU](http://site.icu-project.org/). The easiest way to get started with ICU in Windows is with +[vcpkg](https://vcpkg.readthedocs.io/): `vcpkg install icu`. + +Then, add the `-DCMAKE_TOOLCHAIN_FILE=C:\src\vcpkg\scripts\buildsystems\vcpkg.cmake` argument when +generating the project files with cmake to add the appropriate library and include directories to +the project. + +## Using the library -Using the library -======= Once the library is installed, linking to it is easy! Add the following lines in your CMake project: ``` @@ -80,6 +110,7 @@ target_include_directories(your_target_name PRIVATE ${PEPARSE_INCLUDE_DIRS}) You can see a full example in the examples/peaddrconv folder. -Authors -======= -pe-parse was designed and implemented by Andrew Ruef (andrew@trailofbits.com), with significant contributions from [Wesley Shields](https://github.com/wxsBSD). +## Authors + +pe-parse was designed and implemented by Andrew Ruef (andrew@trailofbits.com), with significant +contributions from [Wesley Shields](https://github.com/wxsBSD). diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..fedbd0b --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0-rc.2 diff --git a/pe-parser-library/CMakeLists.txt b/pe-parser-library/CMakeLists.txt index 50129d2..cde9917 100644 --- a/pe-parser-library/CMakeLists.txt +++ b/pe-parser-library/CMakeLists.txt @@ -1,6 +1,12 @@ cmake_minimum_required(VERSION 3.7) project(pe-parser-library) +message(STATUS "VERSION file: ${CMAKE_SOURCE_DIR}/VERSION") + +file(READ "${CMAKE_SOURCE_DIR}/VERSION" PEPARSE_VERSION) +string(STRIP "${PEPARSE_VERSION}" PEPARSE_VERSION) +add_compile_definitions(PEPARSE_VERSION="${PEPARSE_VERSION}") + set(UNICODE_LIBRARY "any" CACHE STRING "Select a unicode library") set_property(CACHE UNICODE_LIBRARY PROPERTY STRINGS "any" "icu" "codecvt") diff --git a/pepy/README.md b/pepy/README.md new file mode 100644 index 0000000..2213119 --- /dev/null +++ b/pepy/README.md @@ -0,0 +1,204 @@ +pepy +==== +pepy (pronounced p-pie) is a python binding to the pe-parse parser. + +pepy supports Python versions 3.6 and above. + +The easiest way to use pepy is to install it via pip: + +```bash +$ pip3 install pepy +``` + +## Building + +If you can build pe-parse and have a working python environment (headers and +libraries) you can build pepy. + +1. Build pepy: + * `python3 setup.py build` +2. Install pepy: + * `python3 setup.py install` + +**Building on Windows:** Python 3.x is typically installed as _python.exe_, +**NOT** _python3.exe_. + +## Using + +### Parsed object + +There are a number of objects involved in pepy. The main one is the **parsed** +object. This object is returned by the *parse* method. + +```python +import pepy +p = pepy.parse("/path/to/exe") +``` + +The **parsed** object has a number of methods: + +* `get_entry_point`: Return the entry point address +* `get_machine_as_str`: Return the machine as a human readable string +* `get_subsystem_as_str`: Return the subsystem as a human readable string +* `get_bytes`: Return the first N bytes at a given address +* `get_sections`: Return a list of section objects +* `get_imports`: Return a list of import objects +* `get_exports`: Return a list of export objects +* `get_relocations`: Return a list of relocation objects +* `get_resources`: Return a list of resource objects + +The **parsed** object has a number of attributes: + +* `signature` +* `machine` +* `numberofsections` +* `timedatestamp` +* `numberofsymbols` +* `characteristics` +* `magic` +* `majorlinkerver` +* `minorlinkerver` +* `codesize` +* `initdatasize` +* `uninitdatasize` +* `entrypointaddr` +* `baseofcode` +* `baseofdata` +* `imagebase` +* `sectionalignement` +* `filealignment` +* `majorosver` +* `minorosver` +* `win32ver` +* `imagesize` +* `headersize` +* `checksum` +* `subsystem` +* `dllcharacteristics` +* `stackreservesize` +* `stackcommitsize` +* `heapreservesize` +* `heapcommitsize` +* `loaderflags` +* `rvasandsize` + +Example: + +```python +import time +import pepy + +p = pepy.parse("/path/to/exe") +print("Timedatestamp: %s" % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(p.timedatestamp))) +ep = p.get_entry_point() +print("Entry point: 0x%x" % ep) +``` + +The `get_sections`, `get_imports`, `get_exports`, `get_relocations` and +`get_resources` methods each return a list of objects. The type of object +depends upon the method called. `get_sections` returns a list of `section` +objects, `get_imports` returns a list of `import` objects, etc. + +### Section Object + +The `section` object has the following attributes: + +* `base` +* `length` +* `virtaddr` +* `virtsize` +* `numrelocs` +* `numlinenums` +* `characteristics` +* `data` + +### Import Object + +The `import` object has the following attributes: + +* `sym` +* `name` +* `addr` + +### Export Object + +The `export` object has the following attributes: + +* `mod` +* `func` +* `addr` + +### Relocation Object + +The `relocation` object has the following attributes: + +* `type` +* `addr` + +### Resource Object + +The `resource` object has the following attributes: + +* `type_str` +* `name_str` +* `lang_str` +* `type` +* `name` +* `lang` +* `codepage` +* `RVA` +* `size` +* `data` + +The `resource` object has the following methods: + +* `type_as_str` + +Resources are stored in a directory structure. The first three levels of the +are called `type`, `name` and `lang`. Each of these levels can have +either a pre-defined value or a custom string. The pre-defined values are +stored in the `type`, `name` and `lang` attributes. If a custom string is +found it will be stored in the `type_str`, `name_str` and `lang_str` +attributes. The `type_as_str` method can be used to convert a pre-defined +type value to a string representation. + +The following code shows how to iterate through resources: + +```python +import pepy + +from hashlib import md5 +import sys + +p = pepy.parse(sys.argv[1]) +resources = p.get_resources() +print("Resources: (%i)" % len(resources)) +for resource in resources: + print("[+] MD5: (%i) %s" % (len(resource.data), md5(resource.data).hexdigest())) + if resource.type_str: + print("\tType string: %s" % resource.type_str) + else: + print("\tType: %s (%s)" % (hex(resource.type), resource.type_as_str())) + if resource.name_str: + print("\tName string: %s" % resource.name_str) + else: + print("\tName: %s" % hex(resource.name)) + if resource.lang_str: + print("\tLang string: %s" % resource.lang_str) + else: + print("\tLang: %s" % hex(resource.lang)) + print("\tCodepage: %s" % hex(resource.codepage)) + print("\tRVA: %s" % hex(resource.RVA)) + print("\tSize: %s" % hex(resource.size)) +``` + +Note that some binaries (particularly packed) may have corrupt resource entries. +In these cases you may find that `len(resource.data)` is 0 but `resource.size` is +greater than 0. The `size` attribute is the size of the data as declared by the +resource data entry. + +## Authors + +pe-parse was designed and implemented by Andrew Ruef (andrew@trailofbits.com). + +pepy was written by Wesley Shields (wxs@atarininja.org). diff --git a/python/pepy.cpp b/pepy/pepy.cpp similarity index 99% rename from python/pepy.cpp rename to pepy/pepy.cpp index d9396e9..5903ffc 100644 --- a/python/pepy.cpp +++ b/pepy/pepy.cpp @@ -31,7 +31,9 @@ using namespace peparse; -#define PEPY_VERSION "0.3" +/* NOTE(ww): These don't necessarily have to be the same, but currently are. + */ +#define PEPY_VERSION PEPARSE_VERSION /* * Add some definition for compatibility between python2 and python3 @@ -1384,6 +1386,8 @@ static PyObject *pepi_module_init(void) { PyModule_AddObject(m, "pepy_resource", (PyObject *) &pepy_resource_type); PyModule_AddStringMacro(m, PEPY_VERSION); + PyModule_AddStringMacro(m, PEPARSE_VERSION); + PyModule_AddStringConstant(m, "__version__", PEPY_VERSION); PyModule_AddIntMacro(m, MZ_MAGIC); PyModule_AddIntMacro(m, NT_MAGIC); diff --git a/python/README.md b/python/README.md deleted file mode 100644 index 2be2c02..0000000 --- a/python/README.md +++ /dev/null @@ -1,217 +0,0 @@ -pepy -==== -pepy (pronounced p-pie) is a python binding to the pe-parse parser. - -Building -======== -If you can build pe-parse and have a working python environment (headers and -libraries) you can build pepy. - -Python 2.7 ----------- -1. Build pepy: - * python setup.py build -2. Install pepy: - * python setup.py install - -**Building on Windows:** If you get a build error of 'Unable to find -vcvarsall.bat', you must set the `VS90COMNTOOLS` environment variable prior -to the appropriate path as per -[this SO article](http://stackoverflow.com/questions/2817869/error-unable-to-find-vcvarsall-bat): -> While running setup.py for package installations, Python 2.7 searches for an -> installed Visual Studio 2008. You can trick Python to use a newer Visual -> Studio by setting the correct path in VS90COMNTOOLS environment variable -> before calling setup.py. -> -> Execute the following command based on the version of Visual Studio installed: -> * Visual Studio 2010 (VS10): `SET VS90COMNTOOLS=%VS100COMNTOOLS%` -> * Visual Studio 2012 (VS11): `SET VS90COMNTOOLS=%VS110COMNTOOLS%` -> * Visual Studio 2013 (VS12): `SET VS90COMNTOOLS=%VS120COMNTOOLS%` -> * Visual Studio 2015/2017 (VS14): `SET VS90COMNTOOLS=%VS140COMNTOOLS%` - -Python 3.x ----------- -1. Build pepy: - * python3 setup.py build -2. Install pepy: - * python3 setup.py install - -**Building on Windows:** Python 3.x is typically installed as _python.exe_ -**NOT** _python3.exe_. - -Using -===== -Parsed object -------------- -There are a number of objects involved in pepy. The main one is the **parsed** -object. This object is returned by the *parse* method. - -``` -import pepy -p = pepy.parse("/path/to/exe") -``` - -The **parsed** object has a number of methods: - -* get_entry_point: Return the entry point address -* get_machine_as_str: Return the machine as a human readable string -* get_subsystem_as_str: Return the subsystem as a human readable string -* get_bytes: Return the first N bytes at a given address -* get_sections: Return a list of section objects -* get_imports: Return a list of import objects -* get_exports: Return a list of export objects -* get_relocations: Return a list of relocation objects -* get_resources: Return a list of resource objects - -The **parsed** object has a number of attributes: - -* signature -* machine -* numberofsections -* timedatestamp -* numberofsymbols -* characteristics -* magic -* majorlinkerver -* minorlinkerver -* codesize -* initdatasize -* uninitdatasize -* entrypointaddr -* baseofcode -* baseofdata -* imagebase -* sectionalignement -* filealignment -* majorosver -* minorosver -* win32ver -* imagesize -* headersize -* checksum -* subsystem -* dllcharacteristics -* stackreservesize -* stackcommitsize -* heapreservesize -* heapcommitsize -* loaderflags -* rvasandsize - -Example: -``` -import time -import pepy - -p = pepy.parse("/path/to/exe") -print "Timedatestamp: %s" % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(p.timedatestamp)) -ep = p.get_entry_point() -print "Entry point: 0x%x" % ep -``` - -The *get_sections*, *get_imports*, *get_exports*, *get_relocations* and -*get_resources* methods each return a list of objects. The type of object -depends upon the method called. *get_sections* returns a list of **section** -objects, *get_imports* returns a list of **import** objects, etc. - -Section Object --------------- -The **section** object has the following attributes: - -* base -* length -* virtaddr -* virtsize -* numrelocs -* numlinenums -* characteristics -* data - -Import Object -------------- -The **import** object has the following attributes: - -* sym -* name -* addr - -Export Object -------------- -The **export** object has the following attributes: - -* mod -* func -* addr - -Relocation Object ------------------ -The **relocation** object has the following attributes: - -* type -* addr - -Resource Object ---------------- -The **resource** object has the following attributes: - -* type_str -* name_str -* lang_str -* type -* name -* lang -* codepage -* RVA -* size -* data - -The **resource** object has the following methods: - -* type_as_str - -Resources are stored in a directory structure. The first three levels of the -are called **type**, **name** and **lang**. Each of these levels can have -either a pre-defined value or a custom string. The pre-defined values are -stored in the *type*, *name* and *lang* attributes. If a custom string is -found it will be stored in the *type_str*, *name_str* and *lang_str* -attributes. The *type_as_str* method can be used to convert a pre-defined -type value to a string representation. - -The following code shows how to iterate through resources: - -``` -import pepy - -from hashlib import md5 - -p = pepy.parse(sys.argv[1]) -resources = p.get_resources() -print "Resources: (%i)" % len(resources) -for resource in resources: - print "[+] MD5: (%i) %s" % (len(resource.data), md5(resource.data).hexdigest()) - if resource.type_str: - print "\tType string: %s" % resource.type_str - else: - print "\tType: %s (%s)" % (hex(resource.type), resource.type_as_str()) - if resource.name_str: - print "\tName string: %s" % resource.name_str - else: - print "\tName: %s" % hex(resource.name) - if resource.lang_str: - print "\tLang string: %s" % resource.lang_str - else: - print "\tLang: %s" % hex(resource.lang) - print "\tCodepage: %s" % hex(resource.codepage) - print "\tRVA: %s" % hex(resource.RVA) - print "\tSize: %s" % hex(resource.size) -``` - -Note that some binaries (particularly packed) may have corrupt resource entries. -In these cases you may find that len(resource.data) is 0 but resource.size is -greater than 0. The *size* attribute is the size of the data as declared by the -resource data entry. - -Authors -======= -pe-parse was designed and implemented by Andrew Ruef (andrew@trailofbits.com) -pepy was written by Wesley Shields (wxs@atarininja.org) diff --git a/python/test.py b/python/test.py deleted file mode 100755 index a6f3db5..0000000 --- a/python/test.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python - -import sys -import time -import pepy -import binascii - -from hashlib import md5 - -try: - p = pepy.parse(sys.argv[1]) -except pepy.error as e: - print e - sys.exit(1) - -print "Magic: %s" % hex(p.magic) -print "Signature: %s" % hex(p.signature) -print "Machine: %s (%s)" % (hex(p.machine), p.get_machine_as_str()) -print "Number of sections: %s" % p.numberofsections -print "Number of symbols: %s" % p.numberofsymbols -print "Characteristics: %s" % hex(p.characteristics) -print "Timedatestamp: %s" % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(p.timedatestamp)) -print "Major linker version: %s" % hex(p.majorlinkerver) -print "Minor linker version: %s" % hex(p.minorlinkerver) -print "Size of code: %s" % hex(p.codesize) -print "Size of initialized data: %s" % hex(p.initdatasize) -print "Size of uninitialized data: %s" % hex(p.uninitdatasize) -print "Address of entry point: %s" % hex(p.entrypointaddr) -print "Base address of code: %s" % hex(p.baseofcode) -try: - print "Base address of data: %s" % hex(p.baseofdata) -except: - # Not available on PE32+, ignore it. - pass -print "Image base address: %s" % hex(p.imagebase) -print "Section alignment: %s" % hex(p.sectionalignement) -print "File alignment: %s" % hex(p.filealignment) -print "Major OS version: %s" % hex(p.majorosver) -print "Minor OS version: %s" % hex(p.minorosver) -print "Win32 version: %s" % hex(p.win32ver) -print "Size of image: %s" % hex(p.imagesize) -print "Size of headers: %s" % hex(p.headersize) -print "Checksum: %s" % hex(p.checksum) -print "Subsystem: %s (%s)" % (hex(p.subsystem), p.get_subsystem_as_str()) -print "DLL characteristics: %s" % hex(p.dllcharacteristics) -print "Size of stack reserve: %s" % hex(p.stackreservesize) -print "Size of stack commit: %s" % hex(p.stackcommitsize) -print "Size of heap reserve: %s" % hex(p.heapreservesize) -print "Size of heap commit: %s" % hex(p.heapcommitsize) -print "Loader flags: %s" % hex(p.loaderflags) -print "Number of RVA and sizes: %s" % hex(p.rvasandsize) -ep = p.get_entry_point() -byts = p.get_bytes(ep, 8) -print "Bytes at %s: %s" % (hex(ep), ' '.join(['0x' + binascii.hexlify(b) for b in str(byts)])) -sections = p.get_sections() -print "Sections: (%i)" % len(sections) -for sect in sections: - print "[+] %s" % sect.name - print "\tBase: %s" % hex(sect.base) - print "\tLength: %s" % sect.length - print "\tVirtual address: %s" % hex(sect.virtaddr) - print "\tVirtual size: %i" % sect.virtsize - print "\tNumber of Relocations: %i" % sect.numrelocs - print "\tNumber of Line Numbers: %i" % sect.numlinenums - print "\tCharacteristics: %s" % hex(sect.characteristics) - if sect.length: - print "\tFirst 10 bytes: 0x%s" % binascii.hexlify(sect.data[:10]) - print "\tMD5: %s" % md5(sect.data).hexdigest() -imports = p.get_imports() -print "Imports: (%i)" % len(imports) -for imp in imports: - print "[+] Symbol: %s (%s %s)" % (imp.sym, imp.name, hex(imp.addr)) -exports = p.get_exports() -print "Exports: (%i)" % len(exports) -for exp in exports: - print "[+] Module: %s (%s %s)" % (exp.mod, exp.func, hex(exp.addr)) -relocations = p.get_relocations() -print "Relocations: (%i)" % len(relocations) -for reloc in relocations: - print "[+] Type: %s (%s)" % (reloc.type, hex(reloc.addr)) -resources = p.get_resources() -print "Resources: (%i)" % len(resources) -for resource in resources: - print "[+] MD5: (%i) %s" % (len(resource.data), md5(resource.data).hexdigest()) - if resource.type_str: - print "\tType string: %s" % resource.type_str - else: - print "\tType: %s (%s)" % (hex(resource.type), resource.type_as_str()) - if resource.name_str: - print "\tName string: %s" % resource.name_str - else: - print "\tName: %s" % hex(resource.name) - if resource.lang_str: - print "\tLang string: %s" % resource.lang_str - else: - print "\tLang: %s" % hex(resource.lang) - print "\tCodepage: %s" % hex(resource.codepage) - print "\tRVA: %s" % hex(resource.RVA) - print "\tSize: %s" % hex(resource.size) diff --git a/python/setup.py b/setup.py similarity index 70% rename from python/setup.py rename to setup.py index 274c39b..9beb85d 100644 --- a/python/setup.py +++ b/setup.py @@ -23,42 +23,47 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. -from distutils.core import setup, Extension +from setuptools import setup, Extension import os import sys import platform -here = os.path.abspath(os.path.dirname(__file__)) +here = os.path.dirname(__file__) +pepy = os.path.join(here, "pepy") + +with open(os.path.join(pepy, "README.md")) as f: + README = f.read() + +with open(os.path.join(here, "VERSION")) as f: + VERSION = f.read().strip() SOURCE_FILES = [ - os.path.join(here, "pepy.cpp"), - os.path.abspath(os.path.join(here, "..", "pe-parser-library", "src", "parse.cpp")), - os.path.abspath(os.path.join(here, "..", "pe-parser-library", "src", "buffer.cpp")), - os.path.abspath( - os.path.join(here, "..", "pe-parser-library", "src", "unicode_codecvt.cpp") - ), + os.path.join(pepy, "pepy.cpp"), + os.path.join(here, "pe-parser-library", "src", "parse.cpp"), + os.path.join(here, "pe-parser-library", "src", "buffer.cpp"), + os.path.join(here, "pe-parser-library", "src", "unicode_codecvt.cpp"), ] if platform.system() == "Windows": INCLUDE_DIRS = [ os.path.abspath(os.path.join(os.path.dirname(sys.executable), "include")), - os.path.abspath(os.path.join(here, "..", "pe-parser-library", "include")), + os.path.join(here, "pe-parser-library", "include"), "C:\\usr\\include", ] LIBRARY_DIRS = [ os.path.abspath(os.path.join(os.path.dirname(sys.executable), "libs")), "C:\\usr\\lib", ] - COMPILE_ARGS = ["/EHsc"] + COMPILE_ARGS = ["/EHsc", f'/D"PEPARSE_VERSION=\\"{VERSION}\\""'] else: INCLUDE_DIRS = [ "/usr/local/include", "/opt/local/include", "/usr/include", - os.path.abspath(os.path.join(here, "..", "pe-parser-library", "include")), + os.path.join(here, "pe-parser-library", "include"), ] LIBRARY_DIRS = ["/usr/lib", "/usr/local/lib"] - COMPILE_ARGS = ["-std=c++11", "-g", "-O0"] # Debug only + COMPILE_ARGS = ["-std=c++11", f'-DPEPARSE_VERSION="{VERSION}"'] extension_mod = Extension( "pepy", @@ -69,14 +74,15 @@ extension_mod = Extension( library_dirs=LIBRARY_DIRS, ) - setup( name="pepy", - version="0.1", - description="python bindings for pe-parse", + python_requires=">= 3.6", + version=VERSION, + description="Python bindings for pe-parse", + long_description=README, + long_description_content_type="text/markdown", author="Wesley Shields", author_email="wxs@atarininja.org", license="BSD", - long_description="Python bindings for pe-parse", ext_modules=[extension_mod], ) diff --git a/test/assets/example.exe b/test/assets/example.exe new file mode 100644 index 0000000000000000000000000000000000000000..cbbda0cc32cdfbc7da2443028e5a8e6af0d162e9 GIT binary patch literal 119296 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

dgqv#N?`^1*K`fM|p$FaPoH&CTEv1ubWklOs8 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 I{xAan0Q27QI{*Lx literal 0 HcmV?d00001 diff --git a/python/test_python3.py b/test/test_pepy.py similarity index 100% rename from python/test_python3.py rename to test/test_pepy.py diff --git a/util/release b/util/release new file mode 100755 index 0000000..c99bc33 --- /dev/null +++ b/util/release @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# release: perform the chore work required for a pe-parse/pepy release + +set -eo pipefail + +function installed { + cmd=$(command -v "${1}") + + [[ -n "${cmd}" ]] && [[ -f "${cmd}" ]] + return ${?} +} + +function die { + >&2 echo "Barf: ${*}" + exit 1 +} + +# Fail early if we don't have the expected tools. +for tool in git python3 twine; do + installed "${tool}" || die "Missing dependency: ${tool}" +done + +# Fail early if `git status` reports any untracked changes. +[[ -n $(git status -s) ]] && die "Untracked changes in repo" + +# Next, check the VERSION in version and make sure it doesn't already have a git tag. +[[ -f ./VERSION ]] || die "Missing VERSION file; wrong directory?" +version=$(<./VERSION) +[[ -n $(git tag -l "${version}") ]] && die "git tag for ${version} already exists!" + +# Next, craft a tag for the current HEAD. Push both the current commit and the tag. +git tag "${version}" +git push +git push origin "${version}" + +# Finally, build pepy and publish it to PyPI. +# Nuke the dist/ directory before, to avoid old sdists. +rm -rf dist/ +python3 setup.py sdist +twine upload dist/* + +echo OK