diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..5093d15c
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,237 @@
+---
+Language: Cpp
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignArrayOfStructures: Left
+AlignConsecutiveAssignments:
+ Enabled: true
+ AcrossEmptyLines: false
+ AcrossComments: true
+ AlignCompound: false
+ PadOperators: true
+AlignConsecutiveBitFields:
+ Enabled: true
+ AcrossEmptyLines: true
+ AcrossComments: true
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveDeclarations:
+ Enabled: true
+ AcrossEmptyLines: false
+ AcrossComments: true
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveMacros:
+ Enabled: true
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveShortCaseStatements:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCaseColons: false
+AlignEscapedNewlines: Right
+AlignOperands: Align
+AlignTrailingComments:
+ Kind: Always
+ OverEmptyLines: 0
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortEnumsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: MultiLine
+AttributeMacros:
+ - __capability
+BinPackArguments: true
+BinPackParameters: true
+BitFieldColonSpacing: Both
+BraceWrapping:
+ AfterCaseLabel: true
+ AfterClass: true
+ AfterControlStatement: Always
+ AfterEnum: true
+ AfterExternBlock: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: true
+ BeforeElse: true
+ BeforeLambdaBody: false
+ BeforeWhile: true
+ IndentBraces: true
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakAfterAttributes: Never
+BreakAfterJavaFieldAnnotations: false
+BreakArrays: true
+BreakBeforeBinaryOperators: All
+BreakBeforeConceptDeclarations: Always
+BreakBeforeBraces: Linux
+BreakBeforeInlineASMColon: OnlyMultiline
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeColon
+BreakInheritanceList: BeforeColon
+BreakStringLiterals: true
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - BOOST_FOREACH
+ - CDL_FOREACH
+ - CDL_FOREACH2
+ - DL_FOREACH
+ - DL_FOREACH2
+ - DL_FOREACH_SAFE
+ - DL_FOREACH_SAFE2
+ - LL_FOREACH
+ - LL_FOREACH2
+ - LL_FOREACH_SAFE
+ - LL_FOREACH_SAFE2
+ - Q_FOREACH
+ - SLIST_FOREACH
+IfMacros:
+ - IF_NO_DECLTYPE
+ - KJ_IF_MAYBE
+ - PEV_ABORT_IF
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '.*'
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: '(Test)?$'
+IncludeIsMainSourceRegex: ''
+IndentAccessModifiers: false
+IndentCaseBlocks: false
+IndentCaseLabels: false
+IndentExternBlock: AfterExternBlock
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentRequiresClause: true
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+InsertBraces: true
+InsertNewlineAtEOF: true
+InsertTrailingCommas: None
+IntegerLiteralSeparator:
+ Binary: 0
+ BinaryMinDigits: 0
+ Decimal: 0
+ DecimalMinDigits: 0
+ Hex: 0
+ HexMinDigits: 0
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+KeepEmptyLinesAtEOF: true
+LambdaBodyIndentation: Signature
+LineEnding: DeriveLF
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PackConstructorInitializers: BinPack
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyIndentedWhitespace: 0
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+PPIndentWidth: -1
+QualifierAlignment: Leave
+ReferenceAlignment: Pointer
+ReflowComments: true
+RemoveBracesLLVM: false
+RemoveParentheses: Leave
+RemoveSemicolon: false
+RequiresClausePosition: OwnLine
+RequiresExpressionIndentation: OuterScope
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SortIncludes: CaseSensitive
+SortJavaStaticImport: Before
+SortUsingDeclarations: LexicographicNumeric
+SpaceAfterCStyleCast: true
+SpaceAfterLogicalNot: true
+SpaceAfterTemplateKeyword: false
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeJsonColon: false
+SpaceBeforeParens: ControlStatements
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDefinitionName: true
+ AfterFunctionDeclarationName: true
+ AfterIfMacros: true
+ AfterOverloadedOperator: true
+ AfterRequiresInClause: true
+ AfterRequiresInExpression: true
+ BeforeNonEmptyParentheses: true
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: true
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: Never
+SpacesInContainerLiterals: true
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
+SpacesInParens: Never
+SpacesInParensOptions:
+ InCStyleCasts: false
+ InConditionalStatements: false
+ InEmptyParentheses: false
+ Other: false
+SpacesInSquareBrackets: false
+Standard: Latest
+TabWidth: 4
+UseTab: Never
+VerilogBreakBetweenInstancePorts: true
+WhitespaceSensitiveMacros:
+
diff --git a/.github/workflows/multi-os-build.yml b/.github/workflows/multi-os-build.yml
index b32dfa81..d379790c 100644
--- a/.github/workflows/multi-os-build.yml
+++ b/.github/workflows/multi-os-build.yml
@@ -9,62 +9,74 @@ on:
jobs:
build-linux:
-
runs-on: ubuntu-latest
-
+ strategy:
+ matrix:
+ shared: [true, false]
steps:
-
- name: Install packages
uses: delgurth/get-package@v6
with:
# Space-separated list of packages to install using apt-get. Will only run if on ubuntu.
- apt-get: libssl-dev
-
+ apt-get: libssl-dev cmake binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686
- name: Checkout with submodules
- uses: actions/checkout@v3
- with:
- submodules: 'true'
-
+ uses: actions/checkout@v6
+ - name: config shared=${{ matrix.shared }}
+ run: >-
+ cmake
+ -Bbuild
+ -DBUILD_SHARED_LIBS=${{ matrix.shared }}
- name: Compile
- run: make
-
-
- build-osx:
+ run: cmake --build build
+ - name: test
+ run: ctest --test-dir build -V
+ build-osx:
runs-on: macos-latest
-
steps:
-
- name: Checkout with submodules
- uses: actions/checkout@v3
- with:
- submodules: 'true'
-
+ uses: actions/checkout@v6
+ - name: config
+ run: >-
+ cmake
+ -Bbuild
+ -DENABLE_TESTS=Off
- name: Compile
- run: CFLAGS="-I$(brew --prefix openssl@3)/include/" LDFLAGS="-L$(brew --prefix openssl@3)/lib/" make
+ run: cmake --build build
- build-win64:
-
+ build-win64-cygwin:
runs-on: windows-latest
-
steps:
-
- name: Install Cygwin
# You may pin to the exact commit or the version.
# uses: egor-tensin/setup-cygwin@4f96f9fecb8c952fa32ff791b0a77d93d5191bb4
- uses: egor-tensin/setup-cygwin@v3
+ uses: egor-tensin/setup-cygwin@v4
with:
- platform: x64 # optional, default is x64
install-dir: c:\tools\cygwin # optional, default is C:\tools\cygwin
- packages: gcc-core binutils make zip libssl-devel # optional
-
+ packages: gcc-core gcc-g++ binutils make ninja zip libssl-devel cmake # optional
- name: Checkout with submodules
- uses: actions/checkout@v3
- with:
- submodules: 'true'
-
+ uses: actions/checkout@v6
+ - name: Config
+ run: >-
+ cmake
+ -Bbuild
+ -DENABLE_TESTS=Off
+ -GNinja
- name: Compile
- run: make
-
- - name: Compile Windows-only tools and create a ZIP package
- run: make zip
+ run: cmake --build build
+ - name: Pack
+ run: cd build && cpack -G CygwinBinary
+
+ build-win64-msvc:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v6
+ - uses: ilammy/msvc-dev-cmd@v1
+ - name: Install OpenSSL
+ run: vcpkg install --triplet x64-windows
+ - name: Build with CL.EXE
+ run: |
+ cmake -G "NMake Makefiles" -B build -DENABLE_TESTS=Off -DVCPKG_ROOT="C:/vcpkg"
+ cmake --build build
+ - name: Package
+ run: cd build && cpack -G ZIP
diff --git a/.github/workflows/win-artifact-build.yml b/.github/workflows/win-artifact-build.yml
new file mode 100644
index 00000000..319e97c1
--- /dev/null
+++ b/.github/workflows/win-artifact-build.yml
@@ -0,0 +1,39 @@
+name: win-artifact-build
+
+on:
+ push:
+ branches: [ build ]
+
+jobs:
+ build-win64:
+
+ runs-on: windows-latest
+
+ steps:
+
+ - name: Install Cygwin
+ # You may pin to the exact commit or the version.
+ # uses: egor-tensin/setup-cygwin@4f96f9fecb8c952fa32ff791b0a77d93d5191bb4
+ uses: egor-tensin/setup-cygwin@v3
+ with:
+ platform: x64 # optional, default is x64
+ install-dir: c:\tools\cygwin # optional, default is C:\tools\cygwin
+ packages: gcc-core binutils make zip libssl-devel # optional
+
+ - name: Checkout with submodules
+ uses: actions/checkout@v3
+ with:
+ submodules: 'true'
+
+ - name: Compile
+ run: make
+
+ - name: Compile Windows-only tools and create a ZIP package
+ run: make zip
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: win-build
+ path: ./*.zip
+ retention-days: 1
diff --git a/.gitignore b/.gitignore
index fcd1e7ac..7c5846c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,11 @@
pev.cbp
bin
obj
-pev
-pev.project
Debug
.cproject
.project
*.swp
+*.plist
*~
*.so*
*.dylib
@@ -15,18 +14,7 @@ Debug
*.dSYM
*.log
.settings
-src/ofs2rva
-src/output
-src/pedis
-src/pehash
-src/pepack
-src/pescan
-src/pesec
-src/pestr
-src/readpe
-src/rva2ofs
-src/peres
-src/plugins/build/
+build/**
tests/samples/*
!tests/samples/helloworld.exe
tests/*.exe
@@ -37,3 +25,6 @@ tests/running_report
.DS_Store
._*
.vscode/*
+
+/tools/completion/tmp.readpe.yaml
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..3e74b650
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,149 @@
+cmake_minimum_required(VERSION 3.31.0)
+
+# Disable in-source builds
+set(CMAKE_DISABLE_SOURCE_CHANGES ON)
+set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
+
+project(readpe LANGUAGES C VERSION 1.0.0)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
+
+option(BUILD_DISASSEMBLER "Build with basic disassembler support (deprecated)" OFF)
+option(BUILD_LIBPE_ONLY "Build libpe only" OFF)
+option(BUILD_READPE_ONLY "Build readpe only (requires libpe to be installed)" OFF)
+option(BUILD_READPE_PLUGINS "Build readpe plugins" ON)
+option(ENABLE_TESTS "Enable testing" On)
+
+option(READPE_BUILD_LINK_NAME_SUPPORT "Build support for old executables by link name" ON)
+option(READPE_PACK_HEADERS "Package readpe headers (for plugin development)" OFF)
+option(READPE_PACK_CMAKE_FILES "Package readpe cmake config files (for plugin development)" OFF)
+option(READPE_PACK_COMPLETION "Package bash, fsh, and zsh completion files" ON)
+
+if(WIN32)
+ set(READPE_PACK_COMPLETION OFF)
+endif()
+
+# TODO:
+# option(READPE_STANDALONE "Build separate executables" OFF)
+
+option(LIBPE_SLURP_PE_FILE "Slurp PE file instead of memory mapping it" OFF)
+option(LIBPE_BUILD_STATIC "Link libpe into readpe staticially" Off)
+
+set(DEFAULT_LIBPE_PACK_DEV_FILES ON)
+set(DEFAULT_LIBPE_LINK_SSDEEP OFF)
+if(LIBPE_BUILD_STATIC)
+ # We might as well use GPL code since readpe is GPL anyway
+ set(DEFAULT_LIBPE_LINK_SSDEEP ON)
+ # Don't need dev files if we compile it into readpe
+ set(DEFAULT_LIBPE_PACK_DEV_FILES OFF)
+endif()
+
+option(LIBPE_LINK_SSDEEP "LINK SSDEEP GPL2 code into libpe" ${DEFAULT_LIBPE_LINK_SSDEEP})
+
+option(LIBPE_PACK_HEADERS "Package libpe headers" ${DEFAULT_LIBPE_PACK_DEV_FILES})
+option(LIBPE_PACK_CMAKE_FILES "Package libpe cmake config files" ${DEFAULT_LIBPE_PACK_DEV_FILES})
+option(LIBPE_PACK_PKGCONF_FILES "Package libpe pkgconf files" OFF)
+
+if(BUILD_LIBPE_ONLY OR BUILD_READPE_ONLY)
+ set(ENABLE_TESTS OFF)
+endif()
+
+if(!VCPKG_ROOT)
+ set(VCPKG_ROOT "$ENV{VCPKG_ROOT}")
+endif()
+
+add_subdirectory(extern)
+
+if(MSVC)
+ # vcpkg on Linux has issues hijacking find_package for Windows packages
+ # Maybe I can figure out how this works better at some point
+ # It might come down to running vcpkg through wine also
+ # which probably means I would also run cmake through wine
+ # Anyway this is a workaround for a niche case of a niche case
+ if(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux")
+ include(cmake/toolchains/msvc-wine.cmake)
+ set(OPENSSL_ROOT_DIR ${VCPKG_ROOT}/installed/x64-windows)
+ endif()
+
+ # SLURP_PE_FILE causes the analyzed PE file to be fully read into memory
+ # instead of memory mapping it (which is a POSIX feature).
+ # This is currently a workaround for Windows not supporting mmap
+ set(LIBPE_SLURP_PE_FILE ON)
+
+ # CRT_SECURE_NO_WARNINGS disables all warnings regarding the usage of
+ # functions like sprintf instead of sprintf_s
+ add_definitions(
+ -D_CRT_SECURE_NO_WARNINGS
+ )
+
+ # MSVC debug builds also require urctbased.dll and VCRUNTIME140D.dll
+ # This does not affect RelWithDebInfo hence why I would not package
+ # these as they should be installed through Visual Studio and Windows
+
+ # We are nice to windows users and just let them pack libcrypto
+ install(PROGRAMS
+ "${VCPKG_ROOT}/packages/openssl_x64-windows/bin/libcrypto-3-x64.dll"
+ DESTINATION bin
+ )
+ set(LIBCRYPTO_PACKED TRUE)
+endif()
+
+if(CYGWIN)
+ # TODO: These are probably not needed anymore
+ ## cp $(PEV_DIR)/userdb.txt $(ZIPDIR)
+ ## cp $(PEV_DIR)/windows/run.bat $(ZIPDIR)/
+ install(PROGRAMS
+ /usr/bin/cygwin1.dll
+ /usr/bin/cygcrypto-3.dll
+ /usr/bin/cygz.dll
+ DESTINATION bin
+ )
+endif()
+
+if(BUILD_DISASSEMBLER)
+ add_subdirectory(lib/udis86/libudis86)
+endif()
+
+if(LIBPE_LINK_SSDEEP)
+ add_subdirectory(lib/fuzzy)
+endif()
+
+add_subdirectory(lib/compat)
+
+if(NOT BUILD_READPE_ONLY)
+ add_subdirectory(lib/libpe)
+endif()
+
+if(NOT BUILD_LIBPE_ONLY)
+ add_subdirectory(src)
+
+ if(BUILD_READPE_PLUGINS)
+ add_subdirectory(src/plugins)
+ endif()
+endif()
+
+if(ENABLE_TESTS)
+ include(${CMAKE_SOURCE_DIR}/cmake/tests.cmake)
+endif()
+
+set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
+set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
+set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
+set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
+set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE)
+set(CPACK_RESOURCE_FILE_README ${CMAKE_SOURCE_DIR}/README.md)
+set(CPACK_STRIP_FILES YES)
+
+install(FILES
+ ${CPACK_RESOURCE_FILE_LICENSE}
+ ${CPACK_RESOURCE_FILE_README}
+ TYPE DOC
+)
+
+include(CPack)
+configure_file(${PROJECT_SOURCE_DIR}/cmake/cpackopt.cmake.in
+ ${PROJECT_BINARY_DIR}/cpackopt.cmake
+ @ONLY)
+set(CPACK_PROJECT_CONFIG_FILE ${PROJECT_BINARY_DIR}/cpackopt.cmake)
+
diff --git a/LICENSE.libpe b/LICENSE.libpe
new file mode 100644
index 00000000..02bbb60b
--- /dev/null
+++ b/LICENSE.libpe
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
\ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 3cfc90bb..00000000
--- a/Makefile
+++ /dev/null
@@ -1,27 +0,0 @@
-LIBPE_DIR = lib/libpe
-PEV_DIR = src
-VERSION = 0.85
-ZIPDIR = readpe-$(VERSION)-win
-ZIPFILE = $(ZIPDIR).zip
-
-all:
-%:
- cd $(LIBPE_DIR) && $(MAKE) $@
- cd $(PEV_DIR) && $(MAKE) $@
-
-# Cygwin only
-zip:
- cd $(PEV_DIR)/windows && $(MAKE)
- mkdir -p $(ZIPDIR)/plugins
- cp src/build/plugins/*.dll $(ZIPDIR)/plugins/
- echo -ne "plugins_dir=plugins\r\n" > $(ZIPDIR)/pev.conf
- cp $(PEV_DIR)/userdb.txt $(ZIPDIR)
- cp lib/libpe/libpe.dll $(ZIPDIR)/
- cp /usr/bin/cygwin1.dll $(ZIPDIR)/
- cp /usr/bin/cygcrypto-3.dll $(ZIPDIR)/
- cp /usr/bin/cygz.dll $(ZIPDIR)/
- cp README.md $(ZIPDIR)/
- cp $(PEV_DIR)/build/*.exe $(ZIPDIR)/
- cp $(PEV_DIR)/windows/run.bat $(ZIPDIR)/
- zip -r $(ZIPFILE) $(ZIPDIR)/*
- rm -rf $(ZIPDIR)
diff --git a/README.md b/README.md
index f980112f..716c5287 100644
--- a/README.md
+++ b/README.md
@@ -10,35 +10,37 @@ and analyze PE (Portable Executables) binaries.
## How to build on Linux
cd readpe
- make
+ cmake -B build
+ cmake --build build
-**NOTE**: You may need to install OpenSSL using your package manager. Examples:
+**NOTE**: You may need to install CMake, and OpenSSL using your package manager. Examples:
- apt install libssl-dev
- yum install openssl-devel
+ apt install cmake libssl-dev
+ yum install cmake3 openssl-devel
## How to install on Linux
cd readpe
- sudo make install
+ sudo cmake --install build
echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/libpe.conf
sudo ldconfig
## How to build on macOS
cd readpe
- CFLAGS="-I/usr/local/opt/openssl/include/" LDFLAGS="-L/usr/local/opt/openssl/lib/" make
+ CFLAGS="-I/usr/local/opt/openssl/include/" LDFLAGS="-L/usr/local/opt/openssl/lib/" cmake -B build
+ cmake --build build
-**NOTE**: You may need to install OpenSSL and PCRE via [Homebrew](https://brew.sh):
+**NOTE**: You may need to install CMake, OpenSSL and PCRE via [Homebrew](https://brew.sh):
brew update
- brew install openssl
+ brew install cmake openssl
-## How to build on Windows (via [Cygwin](https://cygwin.com))
+## How to build on Windows using [Cygwin](https://cygwin.com))
cd readpe
- make
- make zip
+ cmake -B build
+ cmake --build build
**NOTE**: The following packages must be installed along with your Cygwin:
@@ -48,9 +50,15 @@ and analyze PE (Portable Executables) binaries.
| Devel | binutils |
| Devel | gcc-core |
| Devel | git |
-| Devel | make |
+| Devel | cmake |
| Net | libssl-devel |
+## How to build on Windows using Visual Studio [EXPERIMENTAL]
+
+Open CMakeLists.txt in Visual Studio.
+
+This is still highly experimental and bug reports are highly appriciated.
+
## FAQ
### Is this pev? / What happened to pev?
@@ -81,7 +89,47 @@ Please refer to ['How to install on Linux'](#how-to-install-on-linux).
## License
This project is licensed under the GNU General Public License version 2
-with the exception of the contents of lib/libpe which are licensed under the
+with the exception of the contents of the lib folder which are licensed under the
GNU Lesser General Public License version 3.
-A copy of these licenses can be found respectively in the project root and lib/libpe.
+A copy of these licenses can be found in the project root directory.
+
+Files included as part of this software from outside sources:
+
+| Files | Project/Author | License |
+| --- | --- | --- |
+| lib/compat/asprintf.c | [Thomas Gamper](https://github.com/eiszapfen2000/asprintf) | [BSD-3-Clause](https://opensource.org/license/bsd-3-clause) |
+| lib/compat/strlcat.c | [OpenBSD Project](https://www.openbsd.org/) | [ISC](https://opensource.org/license/isc) |
+| lib/compat/getopt.c | [GNU C Library](https://sourceware.org/glibc) | [LGPL-2.1+](https://opensource.org/license/lgpl-2-1) |
+| lib/compat/include/getopt.h | [GNU C Library](https://sourceware.org/glibc) | [LGPL-2.1+](https://opensource.org/license/lgpl-2-1) |
+| lib/compat/include/unistd.h | [win32ports/unistd_h](https://github.com/win32ports/unistd_h) | [MIT](https://opensource.org/license/mit) |
+| lib/compat/include/sys/queue.h | [FreeBSD Project](https://www.freebsd.org/) | [BSD-3-Clause](https://opensource.org/license/bsd-3-clause) |
+
+Furthermore src/dylib.c, src/dylib.h, and src/stack.h were originally written under the [MIT License](https://opensource.org/license/mit) by Jardel Weyrich.
+
+### Static linked libraries
+
+#### uthash
+
+Project can be found [here](https://troydhanson.github.io/uthash/)
+Source code can be found [here](https://github.com/troydhanson/uthash)
+Licensed under [BSD-1-Clause](https://opensource.org/license/bsd-1-clause)
+
+#### dirent
+
+Project can be found [here](https://github.com/tronkko/dirent)
+Licensed under [MIT License](https://opensource.org/license/mit)
+
+#### dlfcn-win32
+
+Project can be found [here](https://github.com/dlfcn-win32/dlfcn-win32)
+Licensed under [MIT License](https://opensource.org/license/mit)
+
+### Dynamic linked libraries
+
+#### OpenSSL
+
+Project can be found [here](https://openssl-library.org/)
+Source Code can be found [here](https://github.com/openssl/openssl)
+Licensed under [Apache-2.0](https://opensource.org/license/apache-2.0)
+
diff --git a/build-msvc-wine.sh b/build-msvc-wine.sh
new file mode 100755
index 00000000..c4350433
--- /dev/null
+++ b/build-msvc-wine.sh
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+
+ARCH=${ARCH:-"x64"}
+BIN=${BIN:-"/opt/msvc/bin/$ARCH"}
+
+ARCH=$(. "$BIN/msvcenv.sh" && echo $ARCH)
+mkdir -p msvc-wine
+
+export VCPKG_ROOT=/opt/msvc/VC/vcpkg
+
+cat >msvc-wine/$ARCH-windows.cmake <msvc-wine/$ARCH-windows.cmake </dev/null; then
+ echo "Powershell installed!"
+ export PATH=$BIN:$PATH
+else
+ CMAKE_ARGS+=(
+ -DVCPKG_APPLOCAL_DEPS=OFF
+ )
+fi
+
+case $OSTYPE in
+ darwin*)
+ CMAKE_ARGS+=(
+ # No winbind package available on macOS.
+ # https://github.com/mstorsjo/msvc-wine/issues/6
+ -DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded
+ ) ;;
+esac
+
+# CMAKE_CONFIG=Debug
+CMAKE_CONFIG=RelWithDebInfo
+# CMAKE_CONFIG=Release
+
+cmake -B msvc-wine -DCMAKE_BUILD_TYPE=${CMAKE_CONFIG} "${CMAKE_ARGS[@]}"
+cmake --build msvc-wine --config ${CMAKE_CONFIG} -- -v
+
+cd msvc-wine
+cpack -C ${CMAKE_CONFIG} -G 7Z
+
diff --git a/cmake/cpackopt.cmake.in b/cmake/cpackopt.cmake.in
new file mode 100644
index 00000000..838cd42a
--- /dev/null
+++ b/cmake/cpackopt.cmake.in
@@ -0,0 +1,2 @@
+@_CPACK_OTHER_VARIABLES_@
+
diff --git a/cmake/gcc-static.cmake b/cmake/gcc-static.cmake
new file mode 100644
index 00000000..a7f77b18
--- /dev/null
+++ b/cmake/gcc-static.cmake
@@ -0,0 +1,21 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME linux-musl)
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER gcc)
+set(CMAKE_CXX_COMPILER g++)
+
+# where is the target environment located
+set(CMAKE_FIND_ROOT_PATH
+ /home/gogo/src/git.musl-libc.org/musl/lib
+ /usr
+)
+
+# adjust the default behavior of the FIND_XXX() commands:
+# search programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+
+# search headers and libraries in the target environment
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
diff --git a/cmake/msvc-wine-triplets/x64-windows.cmake b/cmake/msvc-wine-triplets/x64-windows.cmake
new file mode 100644
index 00000000..af5eead7
--- /dev/null
+++ b/cmake/msvc-wine-triplets/x64-windows.cmake
@@ -0,0 +1,11 @@
+# VCPKG
+set(VCPKG_TARGET_ARCHITECTURE x64)
+set(VCPKG_CRT_LINKAGE dynamic)
+set(VCPKG_LIBRARY_LINKAGE dynamic)
+# set(VCPKG_BUILD_TYPE release)
+set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${VCPKG_ROOT_DIR}/scripts/toolchains/windows.cmake")
+
+set(ENV{CC} cl.exe)
+set(ENV{CXX} cl.exe)
+set(ENV{PATH} "/opt/msvc/bin/x64:$ENV{PATH}")
+
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
new file mode 100644
index 00000000..0faf2385
--- /dev/null
+++ b/cmake/tests.cmake
@@ -0,0 +1,113 @@
+enable_testing()
+
+include(ExternalProject)
+ExternalProject_Add(texe
+ SOURCE_DIR ${CMAKE_SOURCE_DIR}/t/texe/
+ CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_SOURCE_DIR}/cmake/toolchains/mingw64.cmake"
+ INSTALL_COMMAND ""
+)
+
+file(WRITE "${CMAKE_BINARY_DIR}/readpe.conf" "plugins_dir=src/plugins")
+
+set(TEXE "texe-prefix/src/texe-build/t.exe")
+set(TRPE "src/readpe")
+
+#################################
+# Black Box Tests #
+#################################
+
+add_test(NAME FormatText COMMAND ${TRPE} ${TEXE})
+add_test(NAME FormatCsv COMMAND ${TRPE} -f csv ${TEXE})
+add_test(NAME FormatHtml COMMAND ${TRPE} -f html ${TEXE})
+add_test(NAME FormatJson COMMAND ${TRPE} -f json ${TEXE})
+add_test(NAME FormatXml COMMAND ${TRPE} -f xml ${TEXE})
+
+# TODO No Tests:
+# readpe resources extract
+# readpe resources extract --name
+# readpe certificates --out
+
+# TODO Not implemented:
+# readpe resources --tree
+# readpe [...] hash
+
+# TODO Quiet fails/Wrong Output:
+# readpe resources --help
+
+add_test(NAME OutputDefault COMMAND ${TRPE} -f json ${TEXE})
+add_test(NAME OutputCertificates COMMAND ${TRPE} -f json certificates ${TEXE})
+add_test(NAME OutputCertificatesPem COMMAND ${TRPE} -f json certificates -f pem ${TEXE})
+add_test(NAME OutputCertificatesText COMMAND ${TRPE} -f json certificates -f text ${TEXE})
+add_test(NAME OutputCertificatesX509 COMMAND ${TRPE} -f json certificates -f x509 ${TEXE})
+add_test(NAME OutputDirectoryListVerbose COMMAND ${TRPE} -f json directory --list --verbose ${TEXE})
+add_test(NAME OutputDirectoryVerbose COMMAND ${TRPE} -f json directory --verbose ${TEXE})
+add_test(NAME OutputExports COMMAND ${TRPE} -f json exports ${TEXE})
+add_test(NAME OutputFeatures COMMAND ${TRPE} -f json features ${TEXE})
+add_test(NAME OutputFileVersion COMMAND ${TRPE} -f json --file-version ${TEXE})
+add_test(NAME OutputHeader COMMAND ${TRPE} -f json header ${TEXE})
+add_test(NAME OutputHeaderAll COMMAND ${TRPE} -f json header --all ${TEXE})
+add_test(NAME OutputHeaderCoff COMMAND ${TRPE} -f json header coff ${TEXE})
+add_test(NAME OutputHeaderDos COMMAND ${TRPE} -f json header dos ${TEXE})
+add_test(NAME OutputHeaderOptional COMMAND ${TRPE} -f json header optional ${TEXE})
+add_test(NAME OutputImports COMMAND ${TRPE} -f json imports ${TEXE})
+add_test(NAME OutputImportsList COMMAND ${TRPE} -f json imports --list ${TEXE})
+add_test(NAME OutputImportsVerbose COMMAND ${TRPE} -f json imports --verbose ${TEXE})
+add_test(NAME OutputResources COMMAND ${TRPE} -f json resources ${TEXE})
+add_test(NAME OutputResourcesFileVersion COMMAND ${TRPE} -f json resources --file-version ${TEXE})
+add_test(NAME OutputResourcesHelp COMMAND ${TRPE} -f json resources --help ${TEXE})
+add_test(NAME OutputResourcesList COMMAND ${TRPE} -f json resources --list ${TEXE})
+add_test(NAME OutputResourcesListVerbose COMMAND ${TRPE} -f json resources --list --verbose ${TEXE})
+add_test(NAME OutputResourcesStatistics COMMAND ${TRPE} -f json resources --statistics ${TEXE})
+add_test(NAME OutputResourcesTree COMMAND ${TRPE} -f json resources --tree ${TEXE})
+add_test(NAME OutputResourcesVerbose COMMAND ${TRPE} -f json resources --verbose ${TEXE})
+add_test(NAME OutputScan COMMAND ${TRPE} -f json scan ${TEXE})
+add_test(NAME OutputScanVerbose COMMAND ${TRPE} -f json scan --verbose ${TEXE})
+add_test(NAME OutputSection COMMAND ${TRPE} -f json section ${TEXE})
+add_test(NAME OutputSectionAll COMMAND ${TRPE} -f json section --all ${TEXE})
+add_test(NAME OutputSecurity COMMAND ${TRPE} -f json security ${TEXE})
+
+set(TestsOutput
+ OutputDefault
+ OutputCertificates
+ OutputCertificatesPem
+ OutputCertificatesText
+ OutputCertificatesX509
+ OutputDirectoryListVerbose
+ OutputDirectoryVerbose
+ OutputExports
+ OutputFeatures
+ OutputFileVersion
+ OutputHeader
+ OutputHeaderAll
+ OutputHeaderCoff
+ OutputHeaderDos
+ OutputHeaderOptional
+ OutputImports
+ OutputImportsList
+ OutputImportsVerbose
+ OutputResources
+ OutputResourcesFileVersion
+ OutputResourcesHelp
+ OutputResourcesList
+ OutputResourcesListVerbose
+ OutputResourcesStatistics
+ OutputResourcesTree
+ OutputResourcesVerbose
+ OutputScan
+ OutputScanVerbose
+ OutputSection
+ OutputSectionAll
+ OutputSecurity
+)
+
+add_test(NAME HashDefault COMMAND ${TRPE} -f json hash ${TEXE})
+add_test(NAME HashHeader COMMAND ${TRPE} -f json header hash ${TEXE})
+add_test(NAME HashHeaderDos COMMAND ${TRPE} -f json header dos hash ${TEXE})
+add_test(NAME HashHeaderCoff COMMAND ${TRPE} -f json header coff hash ${TEXE})
+add_test(NAME HashHeaderOptional COMMAND ${TRPE} -f json header optional hash ${TEXE})
+add_test(NAME HashSection COMMAND ${TRPE} -f json section hash ${TEXE})
+add_test(NAME HashSectionText COMMAND ${TRPE} -f json section .text hash ${TEXE})
+
+set_tests_properties(${TestsOutput}
+ PROPERTIES FAIL_REGULAR_EXPRESSION "unrecognized option")
+
diff --git a/cmake/toolchains/mingw64.cmake b/cmake/toolchains/mingw64.cmake
new file mode 100644
index 00000000..59de675d
--- /dev/null
+++ b/cmake/toolchains/mingw64.cmake
@@ -0,0 +1,33 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+# set(CMAKE_GENERATOR_PLATFORM "x64" CACHE STRING "" FORCE)
+
+# enable_language(C,CXX)
+
+# which compilers to use for C and C++
+find_program(GNUCCi686W86Mingw32 NAMES
+ i686-w64-mingw32-gcc
+ i686-w64-mingw32-gcc-win32
+ i686-w64-mingw32-gcc-posix
+ REQUIRED
+)
+find_program(GNUPlusPlusi686W86Mingw32 NAMES
+ i686-w64-mingw32-g++
+ i686-w64-mingw32-g++-win32
+ i686-w64-mingw32-g++-posix
+ REQUIRED
+)
+set(CMAKE_C_COMPILER ${GNUCCi686W86Mingw32})
+set(CMAKE_CXX_COMPILER ${GNUPlusPlusi686W86Mingw32})
+
+# where is the target environment located
+set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
+
+# adjust the default behavior of the FIND_XXX() commands:
+# search programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+
+# search headers and libraries in the target environment
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
diff --git a/cmake/toolchains/msvc-wine.cmake b/cmake/toolchains/msvc-wine.cmake
new file mode 100644
index 00000000..a4d828f4
--- /dev/null
+++ b/cmake/toolchains/msvc-wine.cmake
@@ -0,0 +1,42 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+
+# Debug versions are only somewhat supported by msvc-wine
+# See https://github.com/mstorsjo/msvc-wine for more info
+# Anyway it's not worth it atm.
+# Just use mingw64 for windows debug builds.
+
+set(MSVC_WINE_ROOT_PATH "/opt/msvc")
+set(VCPKG_ROOT "${MSVC_WINE_ROOT_PATH}/VC/vcpkg")
+
+# enable_language(C,CXX)
+
+# which compilers to use for C and C++
+find_program(MSVC_WINE_CL
+ NAMES
+ cl
+ REQUIRED
+ PATHS
+ "${MSVC_WINE_ROOT_PATH}/bin/x64"
+)
+
+# cl is used for both C and C++
+set(CMAKE_C_COMPILER ${MSVC_WINE_CL})
+set(CMAKE_CXX_COMPILER ${MSVC_WINE_CL})
+set(ENV{CC} ${MSVC_WINE_CL})
+set(ENV{CXX} ecl.exe)
+set(ENV{PATH} "${MSVC_WINE_ROOT_PATH}:${VCPKG_ROOT}/installed/x64-windows/bin:$ENV{PATH}")
+
+# where is the target environment located
+set(CMAKE_FIND_ROOT_PATH "${MSVC_WINE_ROOT_PATH}")
+
+# adjust the default behavior of the FIND_XXX() commands:
+# search programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+
+# search headers and libraries in the target environment
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+# set(VCPKG_BUILD_TYPE release)
+
diff --git a/completion/bash/readpe b/completion/bash/readpe
index d36e4b43..9eb88074 100644
--- a/completion/bash/readpe
+++ b/completion/bash/readpe
@@ -1,5 +1,15 @@
#!/usr/bin/env bash
-complete -F _longopt readpe
+
+function _complete_readpe () {
+ echo $COMP_LINE
+ # local _comp=$COMP_LINE
+ # local _comp=$("${COMP_LINE[@]::${#COMP_LINE[@]}-1} --complete")
+ local _comp=$($COMP_LINE "--complete")
+ # printf "headers directories exports"
+ COMPREPLY=($_comp)
+}
+
+complete -F _complete_readpe readpe
complete -F _longopt pedis
complete -F _longopt pehash
complete -F _longopt peldd
@@ -9,3 +19,4 @@ complete -F _longopt pescan
complete -F _longopt pesec
complete -F _longopt pestr
+
diff --git a/doc/manual/en_us/tools.docbook b/doc/manual/en_us/tools.docbook
index 398637f2..bdd96a15 100644
--- a/doc/manual/en_us/tools.docbook
+++ b/doc/manual/en_us/tools.docbook
@@ -13,18 +13,18 @@ Options:
If -m is present, MESSAGE should be:
- Option Message
- ------------------------------
- init CPL_INIT
- getcount CPL_GETCOUNT
- inquire CPL_INQUIRE
- select CPL_SELECT
- dblclk CPL_DBLCLK
- stop CPL_STOP
- exit CPL_EXIT
- newinquire CPL_NEWINQUIRE
- startwparms CPL_STARTWPARMS
- setup CPL_SETUP
+ Option Message
+ ------------------------------
+ init CPL_INIT
+ getcount CPL_GETCOUNT
+ inquire CPL_INQUIRE
+ select CPL_SELECT
+ dblclk CPL_DBLCLK
+ stop CPL_STOP
+ exit CPL_EXIT
+ newinquire CPL_NEWINQUIRE
+ startwparms CPL_STARTWPARMS
+ setup CPL_SETUP
Otherwise, cpload will send all messages to CPlApplet()
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
new file mode 100644
index 00000000..6c7a271c
--- /dev/null
+++ b/extern/CMakeLists.txt
@@ -0,0 +1,69 @@
+# This file is licensed under the MIT License
+
+include(FetchContent)
+include(CheckIncludeFile)
+
+set(content)
+if(MSVC)
+ # set(dirent_SOURCE_DIR "${CMAKE_SOURCE_DIR}/extern/dirent-1.26")
+ FetchContent_Declare(
+ dirent
+ URL https://github.com/tronkko/dirent/archive/refs/tags/1.26.zip
+ URL_HASH SHA256=6ffcc318f00be192acb611c58aa58ee0cfd96776010680d7f38cf24f3dd8baf9
+ URL_MD5 1ac86947cbba80c15585691dd421e0dc
+ DOWNLOAD_EXTRACT_TIMESTAMP OLD
+ SOURCE_SUBDIR "-"
+ )
+ list(APPEND content dirent)
+
+ # set(dl_SOURCE_DIR "${CMAKE_SOURCE_DIR}/extern/dlfcn-win32-1.4.2")
+ FetchContent_Declare(
+ dl
+ URL https://github.com/dlfcn-win32/dlfcn-win32/archive/refs/tags/v1.4.2.zip
+ URL_HASH SHA256=7c85998ee4296303bde30e8e9c72c28e637b881e2211682915b53455a65a0d07
+ URL_MD5 d0026d224377794ed2a6561500302ccc
+ DOWNLOAD_EXTRACT_TIMESTAMP OLD
+ SOURCE_SUBDIR "-"
+ )
+ list(APPEND content dl)
+endif()
+
+check_include_file(utlist.h utlist_FOUND)
+if(NOT utlist_FOUND)
+ # set(uthash_SOURCE_DIR "${CMAKE_SOURCE_DIR}/extern/uthash-2.3.0")
+ FetchContent_Declare(
+ uthash
+ URL https://github.com/troydhanson/uthash/archive/refs/tags/v2.3.0.zip
+ URL_HASH SHA256=b9a6c503a82a6c6e699e4bdccc2d4f2151cfff81e5e159b50eac89c7d226824d
+ URL_MD5 fb22a40f4ec2181af2c21c2d71a8a30e
+ DOWNLOAD_EXTRACT_TIMESTAMP OLD
+ SOURCE_SUBDIR "-"
+ )
+ list(APPEND content uthash)
+endif()
+
+FetchContent_MakeAvailable(${content})
+
+if(MSVC)
+ add_library(dirent INTERFACE)
+ target_include_directories(dirent INTERFACE ${dirent_SOURCE_DIR}/include)
+ set_target_properties(dirent PROPERTIES
+ INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${dirent_SOURCE_DIR}/include"
+ )
+
+ add_library(dl STATIC
+ ${dl_SOURCE_DIR}/src/dlfcn.c
+ ${dl_SOURCE_DIR}/src/dlfcn.h
+ )
+ target_include_directories(dl INTERFACE ${dl_SOURCE_DIR}/src)
+ set_target_properties(dl PROPERTIES
+ INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${dl_SOURCE_DIR}/src"
+ )
+endif()
+
+add_library(uthash INTERFACE)
+target_include_directories(uthash INTERFACE ${uthash_SOURCE_DIR}/src)
+set_target_properties(uthash PROPERTIES
+ INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${uthash_SOURCE_DIR}/src"
+)
+
diff --git a/include/common.h b/include/common.h
deleted file mode 100644
index 87b2eb82..00000000
--- a/include/common.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/* vim :set ts=4 sw=4 sts=4 et : */
-/*
- pev - the PE file analyzer toolkit
-
- common.h - common defitions for the pev toolkit.
-
- Copyright (C) 2013 - 2020 pev authors
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
- In addition, as a special exception, the copyright holders give
- permission to link the code of portions of this program with the
- OpenSSL library under certain conditions as described in each
- individual source file, and distribute linked combinations
- including the two.
-
- You must obey the GNU General Public License in all respects
- for all of the code used other than OpenSSL. If you modify
- file(s) with this exception, you may extend this exception to your
- version of the file(s), but you are not obligated to do so. If you
- do not wish to do so, delete this exception statement from your
- version. If you delete this exception statement from all source
- files in the program, then also delete it here.
-*/
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include "config.h"
-#include "output.h"
-#include "plugins.h"
-
-#define UNUSED(x) (void)(sizeof((x)))
-
-#define EXIT_ERROR(msg) \
-{ \
- fprintf(stderr, "ERROR: %s [at %s:%d]\n", msg, __FILE__, __LINE__); \
- exit(EXIT_FAILURE); \
-}
-
-#define MAX_MSG 81
-#define MAX_PATH 256
-#define VERSION "0.85"
-#define TOOLKIT "from pev " VERSION " toolkit"
-#define COPY \
-"License GPLv2+: GNU GPL version 2 or later .\n" \
-"This is free software: you are free to change and redistribute it.\n" \
-"There is NO WARRANTY, to the extent permitted by law."
-
-void *malloc_s(size_t size);
-void *calloc_s(size_t nmemb, size_t size);
-
-#define PEV_INITIALIZE(config) \
- do { \
- memset(config, 0, sizeof(*config)); \
- pev_load_config(config); \
- int ret = plugins_load_all(config); \
- if (ret < 0) \
- exit(EXIT_FAILURE); \
- output_init(); /* Requires plugin for text output. */ \
- } while (0)
-
-#define PEV_FINALIZE(config) \
- do { \
- output_term(); \
- plugins_unload_all(); \
- pev_cleanup_config(config); \
- } while (0)
diff --git a/include/compat/strlcat.h b/include/compat/strlcat.h
deleted file mode 100644
index c8a27d82..00000000
--- a/include/compat/strlcat.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* vim :set ts=4 sw=4 sts=4 et : */
-#ifndef STRLCAT_H_INCLUDED
-#define STRLCAT_H_INCLUDED
-
-#include
-
-size_t bsd_strlcat(char *dst, const char *src, size_t siz);
-
-#endif
diff --git a/include/compat/sys/queue.h b/include/compat/sys/queue.h
deleted file mode 100644
index 946c9be7..00000000
--- a/include/compat/sys/queue.h
+++ /dev/null
@@ -1,695 +0,0 @@
-/* vim :set ts=4 sw=4 sts=4 et : */
-/*-
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)queue.h 8.5 (Berkeley) 8/20/94
- * $FreeBSD$
- */
-
-#ifndef _SYS_QUEUE_H_
-#define _SYS_QUEUE_H_
-
-#include
-
-/*
- * This file defines four types of data structures: singly-linked lists,
- * singly-linked tail queues, lists and tail queues.
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction. Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A singly-linked tail queue is headed by a pair of pointers, one to the
- * head of the list and the other to the tail of the list. The elements are
- * singly linked for minimum space and pointer manipulation overhead at the
- * expense of O(n) removal for arbitrary elements. New elements can be added
- * to the list after an existing element, at the head of the list, or at the
- * end of the list. Elements being removed from the head of the tail queue
- * should use the explicit macro for this purpose for optimum efficiency.
- * A singly-linked tail queue may only be traversed in the forward direction.
- * Singly-linked tail queues are ideal for applications with large datasets
- * and few or no removals or for implementing a FIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may be traversed in either direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- *
- *
- * SLIST LIST STAILQ TAILQ
- * _HEAD + + + +
- * _HEAD_INITIALIZER + + + +
- * _ENTRY + + + +
- * _INIT + + + +
- * _EMPTY + + + +
- * _FIRST + + + +
- * _NEXT + + + +
- * _PREV - + - +
- * _LAST - - + +
- * _FOREACH + + + +
- * _FOREACH_FROM + + + +
- * _FOREACH_SAFE + + + +
- * _FOREACH_FROM_SAFE + + + +
- * _FOREACH_REVERSE - - - +
- * _FOREACH_REVERSE_FROM - - - +
- * _FOREACH_REVERSE_SAFE - - - +
- * _FOREACH_REVERSE_FROM_SAFE - - - +
- * _INSERT_HEAD + + + +
- * _INSERT_BEFORE - + - +
- * _INSERT_AFTER + + + +
- * _INSERT_TAIL - - + +
- * _CONCAT - - + +
- * _REMOVE_AFTER + - + -
- * _REMOVE_HEAD + - + -
- * _REMOVE + + + +
- * _SWAP + + + +
- *
- */
-#ifdef QUEUE_MACRO_DEBUG
-/* Store the last 2 places the queue element or head was altered */
-struct qm_trace {
- unsigned long lastline;
- unsigned long prevline;
- const char *lastfile;
- const char *prevfile;
-};
-
-#define TRACEBUF struct qm_trace trace;
-#define TRACEBUF_INITIALIZER { __FILE__, __LINE__, NULL, 0 } ,
-#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
-#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
-
-#define QMD_TRACE_HEAD(head) do { \
- (head)->trace.prevline = (head)->trace.lastline; \
- (head)->trace.prevfile = (head)->trace.lastfile; \
- (head)->trace.lastline = __LINE__; \
- (head)->trace.lastfile = __FILE__; \
-} while (0)
-
-#define QMD_TRACE_ELEM(elem) do { \
- (elem)->trace.prevline = (elem)->trace.lastline; \
- (elem)->trace.prevfile = (elem)->trace.lastfile; \
- (elem)->trace.lastline = __LINE__; \
- (elem)->trace.lastfile = __FILE__; \
-} while (0)
-
-#else
-#define QMD_TRACE_ELEM(elem)
-#define QMD_TRACE_HEAD(head)
-#define QMD_SAVELINK(name, link)
-#define TRACEBUF
-#define TRACEBUF_INITIALIZER
-#define TRASHIT(x)
-#endif /* QUEUE_MACRO_DEBUG */
-
-/*
- * Singly-linked List declarations.
- */
-#define SLIST_HEAD(name, type) \
-struct name { \
- struct type *slh_first; /* first element */ \
-}
-
-#define SLIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define SLIST_ENTRY(type) \
-struct { \
- struct type *sle_next; /* next element */ \
-}
-
-/*
- * Singly-linked List functions.
- */
-#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
-
-#define SLIST_FIRST(head) ((head)->slh_first)
-
-#define SLIST_FOREACH(var, head, field) \
- for ((var) = SLIST_FIRST((head)); \
- (var); \
- (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
- (var); \
- (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = SLIST_FIRST((head)); \
- (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
- (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
- for ((varp) = &SLIST_FIRST((head)); \
- ((var) = *(varp)) != NULL; \
- (varp) = &SLIST_NEXT((var), field))
-
-#define SLIST_INIT(head) do { \
- SLIST_FIRST((head)) = NULL; \
-} while (0)
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
- SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
- SLIST_NEXT((slistelm), field) = (elm); \
-} while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field) do { \
- SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
- SLIST_FIRST((head)) = (elm); \
-} while (0)
-
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-
-#define SLIST_REMOVE(head, elm, type, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
- if (SLIST_FIRST((head)) == (elm)) { \
- SLIST_REMOVE_HEAD((head), field); \
- } \
- else { \
- struct type *curelm = SLIST_FIRST((head)); \
- while (SLIST_NEXT(curelm, field) != (elm)) \
- curelm = SLIST_NEXT(curelm, field); \
- SLIST_REMOVE_AFTER(curelm, field); \
- } \
- TRASHIT(*oldnext); \
-} while (0)
-
-#define SLIST_REMOVE_AFTER(elm, field) do { \
- SLIST_NEXT(elm, field) = \
- SLIST_NEXT(SLIST_NEXT(elm, field), field); \
-} while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) do { \
- SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
-} while (0)
-
-#define SLIST_SWAP(head1, head2, type) do { \
- struct type *swap_first = SLIST_FIRST(head1); \
- SLIST_FIRST(head1) = SLIST_FIRST(head2); \
- SLIST_FIRST(head2) = swap_first; \
-} while (0)
-
-/*
- * Singly-linked Tail queue declarations.
- */
-#define STAILQ_HEAD(name, type) \
-struct name { \
- struct type *stqh_first;/* first element */ \
- struct type **stqh_last;/* addr of last next element */ \
-}
-
-#define STAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).stqh_first }
-
-#define STAILQ_ENTRY(type) \
-struct { \
- struct type *stqe_next; /* next element */ \
-}
-
-/*
- * Singly-linked Tail queue functions.
- */
-#define STAILQ_CONCAT(head1, head2) do { \
- if (!STAILQ_EMPTY((head2))) { \
- *(head1)->stqh_last = (head2)->stqh_first; \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_INIT((head2)); \
- } \
-} while (0)
-
-#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
-
-#define STAILQ_FIRST(head) ((head)->stqh_first)
-
-#define STAILQ_FOREACH(var, head, field) \
- for((var) = STAILQ_FIRST((head)); \
- (var); \
- (var) = STAILQ_NEXT((var), field))
-
-#define STAILQ_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
- (var); \
- (var) = STAILQ_NEXT((var), field))
-
-#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = STAILQ_FIRST((head)); \
- (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
- (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define STAILQ_INIT(head) do { \
- STAILQ_FIRST((head)) = NULL; \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_NEXT((tqelm), field) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_HEAD(head, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_FIRST((head)) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_TAIL(head, elm, field) do { \
- STAILQ_NEXT((elm), field) = NULL; \
- *(head)->stqh_last = (elm); \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_LAST(head, type, field) \
- (STAILQ_EMPTY((head)) ? NULL : \
- __containerof((head)->stqh_last, struct type, field.stqe_next))
-
-#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
-
-#define STAILQ_REMOVE(head, elm, type, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
- if (STAILQ_FIRST((head)) == (elm)) { \
- STAILQ_REMOVE_HEAD((head), field); \
- } \
- else { \
- struct type *curelm = STAILQ_FIRST((head)); \
- while (STAILQ_NEXT(curelm, field) != (elm)) \
- curelm = STAILQ_NEXT(curelm, field); \
- STAILQ_REMOVE_AFTER(head, curelm, field); \
- } \
- TRASHIT(*oldnext); \
-} while (0)
-
-#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
- if ((STAILQ_NEXT(elm, field) = \
- STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_REMOVE_HEAD(head, field) do { \
- if ((STAILQ_FIRST((head)) = \
- STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_SWAP(head1, head2, type) do { \
- struct type *swap_first = STAILQ_FIRST(head1); \
- struct type **swap_last = (head1)->stqh_last; \
- STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_FIRST(head2) = swap_first; \
- (head2)->stqh_last = swap_last; \
- if (STAILQ_EMPTY(head1)) \
- (head1)->stqh_last = &STAILQ_FIRST(head1); \
- if (STAILQ_EMPTY(head2)) \
- (head2)->stqh_last = &STAILQ_FIRST(head2); \
-} while (0)
-
-
-/*
- * List declarations.
- */
-#define LIST_HEAD(name, type) \
-struct name { \
- struct type *lh_first; /* first element */ \
-}
-
-#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define LIST_ENTRY(type) \
-struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
-}
-
-/*
- * List functions.
- */
-
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define QMD_LIST_CHECK_HEAD(head, field) do { \
- if (LIST_FIRST((head)) != NULL && \
- LIST_FIRST((head))->field.le_prev != \
- &LIST_FIRST((head))) \
- panic("Bad list head %p first->prev != head", (head)); \
-} while (0)
-
-#define QMD_LIST_CHECK_NEXT(elm, field) do { \
- if (LIST_NEXT((elm), field) != NULL && \
- LIST_NEXT((elm), field)->field.le_prev != \
- &((elm)->field.le_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
-} while (0)
-
-#define QMD_LIST_CHECK_PREV(elm, field) do { \
- if (*(elm)->field.le_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
-} while (0)
-#else
-#define QMD_LIST_CHECK_HEAD(head, field)
-#define QMD_LIST_CHECK_NEXT(elm, field)
-#define QMD_LIST_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define LIST_EMPTY(head) ((head)->lh_first == NULL)
-
-#define LIST_FIRST(head) ((head)->lh_first)
-
-#define LIST_FOREACH(var, head, field) \
- for ((var) = LIST_FIRST((head)); \
- (var); \
- (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
- (var); \
- (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = LIST_FIRST((head)); \
- (var) && ((tvar) = LIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
- (var) && ((tvar) = LIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define LIST_INIT(head) do { \
- LIST_FIRST((head)) = NULL; \
-} while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) do { \
- QMD_LIST_CHECK_NEXT(listelm, field); \
- if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
- LIST_NEXT((listelm), field)->field.le_prev = \
- &LIST_NEXT((elm), field); \
- LIST_NEXT((listelm), field) = (elm); \
- (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
-} while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
- QMD_LIST_CHECK_PREV(listelm, field); \
- (elm)->field.le_prev = (listelm)->field.le_prev; \
- LIST_NEXT((elm), field) = (listelm); \
- *(listelm)->field.le_prev = (elm); \
- (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
-} while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) do { \
- QMD_LIST_CHECK_HEAD((head), field); \
- if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
- LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
- LIST_FIRST((head)) = (elm); \
- (elm)->field.le_prev = &LIST_FIRST((head)); \
-} while (0)
-
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_PREV(elm, head, type, field) \
- ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
- __containerof((elm)->field.le_prev, struct type, field.le_next))
-
-#define LIST_REMOVE(elm, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.le_next); \
- QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
- QMD_LIST_CHECK_NEXT(elm, field); \
- QMD_LIST_CHECK_PREV(elm, field); \
- if (LIST_NEXT((elm), field) != NULL) \
- LIST_NEXT((elm), field)->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = LIST_NEXT((elm), field); \
- TRASHIT(*oldnext); \
- TRASHIT(*oldprev); \
-} while (0)
-
-#define LIST_SWAP(head1, head2, type, field) do { \
- struct type *swap_tmp = LIST_FIRST((head1)); \
- LIST_FIRST((head1)) = LIST_FIRST((head2)); \
- LIST_FIRST((head2)) = swap_tmp; \
- if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
- swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
- if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
- swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
-} while (0)
-
-/*
- * Tail queue declarations.
- */
-#define TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; /* first element */ \
- struct type **tqh_last; /* addr of last next element */ \
- TRACEBUF \
-}
-
-#define TAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
-
-#define TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; /* next element */ \
- struct type **tqe_prev; /* address of previous next element */ \
- TRACEBUF \
-}
-
-/*
- * Tail queue functions.
- */
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
- if (!TAILQ_EMPTY(head) && \
- TAILQ_FIRST((head))->field.tqe_prev != \
- &TAILQ_FIRST((head))) \
- panic("Bad tailq head %p first->prev != head", (head)); \
-} while (0)
-
-#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
- if (*(head)->tqh_last != NULL) \
- panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
-} while (0)
-
-#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
- if (TAILQ_NEXT((elm), field) != NULL && \
- TAILQ_NEXT((elm), field)->field.tqe_prev != \
- &((elm)->field.tqe_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
-} while (0)
-
-#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
- if (*(elm)->field.tqe_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
-} while (0)
-#else
-#define QMD_TAILQ_CHECK_HEAD(head, field)
-#define QMD_TAILQ_CHECK_TAIL(head, headname)
-#define QMD_TAILQ_CHECK_NEXT(elm, field)
-#define QMD_TAILQ_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define TAILQ_CONCAT(head1, head2, field) do { \
- if (!TAILQ_EMPTY(head2)) { \
- *(head1)->tqh_last = (head2)->tqh_first; \
- (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
- (head1)->tqh_last = (head2)->tqh_last; \
- TAILQ_INIT((head2)); \
- QMD_TRACE_HEAD(head1); \
- QMD_TRACE_HEAD(head2); \
- } \
-} while (0)
-
-#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
-
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-
-#define TAILQ_FOREACH(var, head, field) \
- for ((var) = TAILQ_FIRST((head)); \
- (var); \
- (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
- (var); \
- (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = TAILQ_FIRST((head)); \
- (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
- (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for ((var) = TAILQ_LAST((head), headname); \
- (var); \
- (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
- for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
- (var); \
- (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
- for ((var) = TAILQ_LAST((head), headname); \
- (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
- for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
- (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
- (var) = (tvar))
-
-#define TAILQ_INIT(head) do { \
- TAILQ_FIRST((head)) = NULL; \
- (head)->tqh_last = &TAILQ_FIRST((head)); \
- QMD_TRACE_HEAD(head); \
-} while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- QMD_TAILQ_CHECK_NEXT(listelm, field); \
- if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
- TAILQ_NEXT((elm), field)->field.tqe_prev = \
- &TAILQ_NEXT((elm), field); \
- else { \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_HEAD(head); \
- } \
- TAILQ_NEXT((listelm), field) = (elm); \
- (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
- QMD_TRACE_ELEM(&(elm)->field); \
- QMD_TRACE_ELEM(&listelm->field); \
-} while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
- QMD_TAILQ_CHECK_PREV(listelm, field); \
- (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- TAILQ_NEXT((elm), field) = (listelm); \
- *(listelm)->field.tqe_prev = (elm); \
- (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_ELEM(&(elm)->field); \
- QMD_TRACE_ELEM(&listelm->field); \
-} while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field) do { \
- QMD_TAILQ_CHECK_HEAD(head, field); \
- if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
- TAILQ_FIRST((head))->field.tqe_prev = \
- &TAILQ_NEXT((elm), field); \
- else \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- TAILQ_FIRST((head)) = (elm); \
- (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
- QMD_TRACE_HEAD(head); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) do { \
- QMD_TAILQ_CHECK_TAIL(head, field); \
- TAILQ_NEXT((elm), field) = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_HEAD(head); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-
-#define TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define TAILQ_REMOVE(head, elm, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
- QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
- QMD_TAILQ_CHECK_NEXT(elm, field); \
- QMD_TAILQ_CHECK_PREV(elm, field); \
- if ((TAILQ_NEXT((elm), field)) != NULL) \
- TAILQ_NEXT((elm), field)->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else { \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- QMD_TRACE_HEAD(head); \
- } \
- *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
- TRASHIT(*oldnext); \
- TRASHIT(*oldprev); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_SWAP(head1, head2, type, field) do { \
- struct type *swap_first = (head1)->tqh_first; \
- struct type **swap_last = (head1)->tqh_last; \
- (head1)->tqh_first = (head2)->tqh_first; \
- (head1)->tqh_last = (head2)->tqh_last; \
- (head2)->tqh_first = swap_first; \
- (head2)->tqh_last = swap_last; \
- if ((swap_first = (head1)->tqh_first) != NULL) \
- swap_first->field.tqe_prev = &(head1)->tqh_first; \
- else \
- (head1)->tqh_last = &(head1)->tqh_first; \
- if ((swap_first = (head2)->tqh_first) != NULL) \
- swap_first->field.tqe_prev = &(head2)->tqh_first; \
- else \
- (head2)->tqh_last = &(head2)->tqh_first; \
-} while (0)
-
-#endif /* !_SYS_QUEUE_H_ */
diff --git a/include/libpe/context.h b/include/libpe/context.h
new file mode 100644
index 00000000..aa136dbd
--- /dev/null
+++ b/include/libpe/context.h
@@ -0,0 +1,89 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2025 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_CONTEXT_H
+#define LIBPE_CONTEXT_H
+
+#include "directories.h"
+#include "exports.h"
+#include "hashes.h"
+#include "hdr_coff.h"
+#include "hdr_dos.h"
+#include "hdr_optional.h"
+#include "imports.h"
+#include "sections.h"
+#include "types_resources.h"
+
+#include
+#include
+
+typedef struct {
+ // DOS header
+ IMAGE_DOS_HEADER *dos_hdr;
+ // Signature
+ uint32_t signature;
+ // COFF header
+ IMAGE_COFF_HEADER *coff_hdr;
+ // Optional header
+ void *optional_hdr_ptr;
+ IMAGE_OPTIONAL_HEADER optional_hdr;
+ // Directories
+ uint32_t num_directories;
+ void *directories_ptr;
+ IMAGE_DATA_DIRECTORY **directories; // array up to MAX_DIRECTORIES
+ // Sections
+ uint16_t num_sections;
+ void *sections_ptr;
+ IMAGE_SECTION_HEADER **sections; // array up to MAX_SECTIONS
+ // Symbols
+ uint32_t num_symbols;
+ void *symbols_ptr;
+ // Strings
+ uint32_t strings_size;
+ const char *strings_ptr;
+ uint64_t entrypoint;
+ uint64_t imagebase;
+} pe_file_t;
+
+typedef struct {
+ // Parsed directories
+ pe_imports_t *imports;
+ pe_exports_t *exports;
+ // Hashes
+ pe_hash_headers_t *hash_headers;
+ pe_hash_sections_t *hash_sections;
+ pe_hash_t *hash_file;
+ // Resources
+ pe_resources_t *resources;
+} pe_cached_data_t;
+
+typedef struct pe_ctx {
+ FILE *stream;
+ char *path;
+ void *map_addr;
+ uintmax_t map_size;
+ uintptr_t map_end;
+ pe_file_t pe;
+ pe_cached_data_t cached_data;
+} pe_ctx_t;
+
+#endif
+
diff --git a/include/libpe/dir_import.h b/include/libpe/dir_import.h
new file mode 100644
index 00000000..bacdbb65
--- /dev/null
+++ b/include/libpe/dir_import.h
@@ -0,0 +1,99 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2017 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_DIR_IMPORT_H
+#define LIBPE_DIR_IMPORT_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma pack(push, 1)
+
+typedef struct {
+ union {
+ uint32_t Characteristics; // 0 for terminating null import descriptor
+ uint32_t OriginalFirstThunk; // RVA to original unbound IAT
+ } u1;
+ uint32_t TimeDateStamp;
+ uint32_t ForwarderChain; // -1 if no forwarders
+ uint32_t Name;
+ // RVA to IAT (if bound this IAT has actual addresses)
+ uint32_t FirstThunk;
+} IMAGE_IMPORT_DESCRIPTOR;
+
+typedef struct {
+ union {
+ uint32_t AllAttributes;
+ struct {
+ uint32_t RvaBased : 1; // Delay load version 2
+ uint32_t ReservedAttributes : 31;
+ } u1;
+ } Attributes;
+ uint32_t DllNameRVA; // RVA to the name of the target library
+ // (NULL-terminate ASCII string)
+ uint32_t ModuleHandleRVA; // RVA to the HMODULE caching location (PHMODULE)
+ uint32_t ImportAddressTableRVA; // RVA to the start of the IAT
+ // (PIMAGE_THUNK_DATA)
+ uint32_t ImportNameTableRVA; // RVA to the start of the name table
+ // (PIMAGE_THUNK_DATA::AddressOfData)
+ uint32_t BoundImportAddressTableRVA; // RVA to an optional bound IAT
+ uint32_t UnloadInformationTableRVA; // RVA to an optional unload info table
+ uint32_t
+ TimeDateStamp; // 0 if not bound, Otherwise, date/time of the target DLL
+} IMAGE_DELAYLOAD_DESCRIPTOR;
+
+// import name entry
+typedef struct {
+ uint16_t Hint;
+ uint8_t Name[1];
+} IMAGE_IMPORT_BY_NAME;
+
+typedef struct {
+ union {
+ uint64_t ForwarderString; // RVA to a forwarder string
+ uint64_t Function; // Memory address of the imported function
+ uint64_t Ordinal; // Ordinal value of imported API
+ uint64_t AddressOfData; // RVA to an IMAGE_IMPORT_BY_NAME with the
+ // imported API name
+ } u1;
+} IMAGE_THUNK_DATA64;
+
+typedef struct {
+ union {
+ uint32_t ForwarderString; // RVA to a forwarder string
+ uint32_t Function; // Memory address of the imported function
+ uint32_t Ordinal; // Ordinal value of imported API
+ uint32_t AddressOfData; // RVA to an IMAGE_IMPORT_BY_NAME with the
+ // imported API name
+ } u1;
+} IMAGE_THUNK_DATA32;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/include/libpe/dir_resources.h b/include/libpe/dir_resources.h
new file mode 100644
index 00000000..60c33f76
--- /dev/null
+++ b/include/libpe/dir_resources.h
@@ -0,0 +1,140 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2017 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_DIR_RESOURCES_H
+#define LIBPE_DIR_RESOURCES_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000
+#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000
+
+// REFERENCE:
+// https://docs.microsoft.com/en-us/windows/win32/menurc/resource-types
+typedef enum {
+ RT_CURSOR = 1, // cursor image
+ RT_BITMAP = 2, // bitmap (.bmp)
+ RT_ICON = 3, // icon
+ RT_MENU = 4, // menu
+ RT_DIALOG = 5, // dialog window
+ RT_STRING = 6, // unicode string
+ RT_FONTDIR = 7, // font directory
+ RT_FONT = 8, // font
+ RT_ACCELERATOR = 9, // hot keys
+ RT_RCDATA = 10, // data
+ RT_MESSAGETABLE = 11, // string table
+ RT_GROUP_CURSOR = 12, // cursor group
+ RT_GROUP_ICON = 14, // icon group
+ RT_NAMETABLE = 15, // name table (removed in Windows 3.1)
+ RT_VERSION = 16, // version information
+ RT_DLGINCLUDE
+ = 17, // names of header files for dialogs (*.h) used by compiler
+ RT_PLUGPLAY = 19, // data determined by application
+ RT_VXD = 20, // vxd info
+ RT_ANICURSOR = 21, // animated cursor
+ RT_ANIICON = 22, // animated icon
+ RT_HTML = 23, // html page
+ RT_MANIFEST = 24, // manifest of Windows XP build
+ RT_PSZ = 204, // string resource (used for ttf font file name)
+ RT_DLGINIT = 240, // strings used for initiating some controls in dialogs
+ RT_TOOLBAR = 241 // configuration of toolbars
+} ResourceType;
+
+#pragma pack(push, 1)
+
+typedef struct {
+ uint32_t Characteristics;
+ uint32_t TimeDateStamp;
+ uint16_t MajorVersion;
+ uint16_t MinorVersion;
+ uint16_t NumberOfNamedEntries;
+ uint16_t NumberOfIdEntries;
+} IMAGE_RESOURCE_DIRECTORY;
+
+typedef struct {
+ union {
+ struct {
+ uint32_t NameOffset : 31;
+ uint32_t NameIsString : 1;
+ } data;
+ uint32_t Name;
+ uint16_t Id;
+ } u0;
+ union {
+ uint32_t OffsetToData;
+ struct {
+ uint32_t OffsetToDirectory : 31;
+ uint32_t DataIsDirectory : 1;
+ } data;
+ } u1;
+} IMAGE_RESOURCE_DIRECTORY_ENTRY;
+
+typedef struct {
+ uint16_t Length;
+ char String[1];
+} IMAGE_RESOURCE_DATA_STRING;
+
+typedef struct {
+ uint16_t Length; // Number of Unicode characters
+ uint16_t String[1];
+} IMAGE_RESOURCE_DATA_STRING_U;
+
+typedef struct {
+ uint32_t OffsetToData;
+ uint32_t Size;
+ uint32_t CodePage;
+ uint32_t Reserved;
+} IMAGE_RESOURCE_DATA_ENTRY;
+
+typedef struct {
+ uint16_t wLength;
+ uint16_t wValueLength;
+ uint16_t wType;
+} VS_VERSIONINFO_HEAD;
+
+typedef struct {
+ uint32_t dwSignature;
+ uint32_t dwStrucVersion;
+ uint32_t dwFileVersionMS;
+ uint32_t dwFileVersionLS;
+ uint32_t dwProductVersionMS;
+ uint32_t dwProductVersionLS;
+ uint32_t dwFileFlagsMask;
+ uint32_t dwFileFlags;
+ uint32_t dwFileOS;
+ uint32_t dwFileType;
+ uint32_t dwFileSubtype;
+ uint32_t dwFileDateMS;
+ uint32_t dwFileDateLS;
+} VS_FIXEDFILEINFO;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/include/libpe/dir_security.h b/include/libpe/dir_security.h
new file mode 100644
index 00000000..0ab05fd7
--- /dev/null
+++ b/include/libpe/dir_security.h
@@ -0,0 +1,93 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2017 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#pragma once
+#ifndef LIBPE_DIR_SECURITY_H
+#define LIBPE_DIR_SECURITY_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ANYSIZE_ARRAY 1
+
+// #define WIN_TRUST_MAJOR_REVISION_MASK 0xFFFF0000
+// #define WIN_TRUST_MINOR_REVISION_MASK 0x0000FFFF
+// #define WIN_TRUST_REVISION_1_0 0x00010000
+
+typedef enum {
+ // Version 1, legacy version of the Win_Certificate
+ // structure. It is supported only for purposes of
+ // verifying legacy Authenticode signatures
+ WIN_CERT_REVISION_1_0 = 0x0100,
+ // Version 2 is the current version of the Win_Certificate structure.
+ WIN_CERT_REVISION_2_0 = 0x0200
+} CertRevision;
+
+typedef enum {
+ WIN_CERT_TYPE_X509 = 0x0001, // bCertificate contains an X.509 (Certificate)
+ WIN_CERT_TYPE_PKCS_SIGNED_DATA
+ = 0x0002, // bCertificate contains a PKCS#7 (SignedData structure)
+ WIN_CERT_TYPE_RESERVED_1 = 0x0003, // Reserved
+ WIN_CERT_TYPE_TS_STACK_SIGNED
+ = 0x0004, // Terminal Server Protocol Stack (Certificate signing)
+ WIN_CERT_TYPE_EFI_PKCS115 = 0x0EF0,
+ WIN_CERT_TYPE_EFI_GUID = 0x0EF1
+} CertType;
+
+#pragma pack(push, 4)
+
+// Originally declared in Wintrust.h
+typedef struct {
+ // Specified the size, in bytes, of the WIN_CERTIFICATE structure,
+ // including the data in bCertificate.
+ uint32_t dwLength;
+ // Indicates the revision of the structure.
+ uint16_t wRevision;
+ // Specifies the type of certificate.
+ // This member can be one of the following values:
+ // Value Meaning
+ // ----------------------------------------------------------------------------------------
+ // WIN_CERT_TYPE_X509 The certificate contains an X.509
+ // Certificate. WIN_CERT_TYPE_PKCS_SIGNED_DATA The certificate
+ // contains a PKCS SignedData structure. WIN_CERT_TYPE_RESERVED_1
+ // Reserved.
+ // WIN_CERT_TYPE_TS_STACK_SIGNED
+ uint16_t wCertificateType;
+ // A variable-sized array of bytes that contains the certificate data.
+ uint8_t bCertificate[ANYSIZE_ARRAY];
+} WIN_CERTIFICATE;
+
+typedef struct {
+ uint32_t cbData;
+ uint8_t *pbData;
+} CRYPT_DATA_BLOB;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/include/libpe/directories.h b/include/libpe/directories.h
new file mode 100644
index 00000000..e763cd0b
--- /dev/null
+++ b/include/libpe/directories.h
@@ -0,0 +1,150 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2017 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#pragma once
+#ifndef LIBPE_DIRECTORIES_H
+#define LIBPE_DIRECTORIES_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Directory entries
+typedef enum {
+ IMAGE_DIRECTORY_ENTRY_EXPORT = 0, // Export Table
+ IMAGE_DIRECTORY_ENTRY_IMPORT = 1, // Import Table
+ IMAGE_DIRECTORY_ENTRY_RESOURCE = 2, // Resource Table
+ IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3, // Exception Table
+ IMAGE_DIRECTORY_ENTRY_SECURITY = 4, // Certificate Table
+ IMAGE_DIRECTORY_ENTRY_BASERELOC = 5, // Base Relocation Table
+ IMAGE_DIRECTORY_ENTRY_DEBUG = 6, // Debug
+ // IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7, // (X86 usage)
+ IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7, // Architecture
+ IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8, // Global Ptr
+ IMAGE_DIRECTORY_ENTRY_TLS = 9, // TLS Table
+ IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10, // Load Config Table
+ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11, // Bound Import
+ IMAGE_DIRECTORY_ENTRY_IAT = 12, // IAT
+ IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13, // Delay Import Descriptor
+ IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14, // CLR Runtime Header
+ IMAGE_DIRECTORY_RESERVED = 15 // Reserved, must be zero
+} ImageDirectoryEntry;
+
+typedef struct {
+ uint32_t Characteristics;
+ uint32_t TimeDateStamp;
+ uint16_t MajorVersion;
+ uint16_t MinorVersion;
+ uint32_t Name;
+ uint32_t Base;
+ uint32_t NumberOfFunctions;
+ uint32_t NumberOfNames;
+ uint32_t AddressOfFunctions;
+ uint32_t AddressOfNames;
+ uint32_t AddressOfNameOrdinals;
+} IMAGE_EXPORT_DIRECTORY;
+
+typedef struct {
+ uint32_t StartAddressOfRawData;
+ uint32_t EndAddressOfRawData;
+ uint32_t AddressOfIndex;
+ uint32_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK
+ uint32_t SizeOfZeroFill;
+ uint32_t Characteristics; // reserved for future use
+} IMAGE_TLS_DIRECTORY32;
+
+typedef struct {
+ uint64_t StartAddressOfRawData;
+ uint64_t EndAddressOfRawData;
+ uint64_t AddressOfIndex;
+ uint64_t AddressOfCallBacks;
+ uint32_t SizeOfZeroFill;
+ uint32_t Characteristics;
+} IMAGE_TLS_DIRECTORY64;
+
+typedef enum {
+ IMAGE_DEBUG_TYPE_UNKNOWN = 0, // Unknown value, ignored by all tools
+ IMAGE_DEBUG_TYPE_COFF = 1, // COFF debugging information
+ IMAGE_DEBUG_TYPE_CODEVIEW = 2, // CodeView debugging information or Visual
+ // C++ Program Database debugging information
+ IMAGE_DEBUG_TYPE_FPO = 3, // Frame pointer omission (FPO) information
+ IMAGE_DEBUG_TYPE_MISC
+ = 4, // Location of DBG file with CodeView debugging information
+ IMAGE_DEBUG_TYPE_EXCEPTION
+ = 5, // Exception information, copy of .pdata section
+ IMAGE_DEBUG_TYPE_FIXUP = 6, // Fixup information
+ IMAGE_DEBUG_TYPE_OMAP_TO_SRC
+ = 7, // The mapping from an RVA in image to an RVA in source image
+ IMAGE_DEBUG_TYPE_OMAP_FROM_SRC
+ = 8, // The mapping from an RVA in source image to an RVA in image
+ IMAGE_DEBUG_TYPE_BORLAND = 9, // Borland debugging information
+ IMAGE_DEBUG_TYPE_RESERVED10
+ = 10, // Coldpath / Hotpatch debug information,
+ // https://www.openrce.org/articles/full_view/22
+ IMAGE_DEBUG_TYPE_CLSID = 11,
+ IMAGE_DEBUG_TYPE_VC_FEATURE = 12, // Visual C++ counts / statistics
+ IMAGE_DEBUG_TYPE_POGO = 13, // COFF group information, data for
+ // profile-guided optimization, LINK.EXE /LTCG
+ IMAGE_DEBUG_TYPE_ILTCG = 14, // Incremental link-time code generation,
+ // LINK.EXE /LTCG:INCREMENTAL
+ IMAGE_DEBUG_TYPE_MPX
+ = 15, // Intel Memory Protection Extensions, CL.EXE /d2MPX,
+ // https://devblogs.microsoft.com/cppblog/visual-studio-2015-update-1-new-experimental-feature-mpx/
+ IMAGE_DEBUG_TYPE_REPRO
+ = 16, // PE determinism or reproducibility, LINK.EXE /Brepro
+ IMAGE_DEBUG_TYPE_EMBEDDED_PORTABLE_PDB
+ = 17, // Embedded Portable PDB debugging information,
+ // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
+ IMAGE_DEBUG_TYPE_SPGO = 18, // Sample profile-guided optimization
+ IMAGE_DEBUG_TYPE_PDBCHECKSUM
+ = 19, // PDB Checksum,
+ // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
+ IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS
+ = 20, // Extended DLL characteristics bits
+ IMAGE_DEBUG_TYPE_PERFMAP
+ = 21 // Location of associated Ready To Run PerfMap file,
+ // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
+} ImageDebugType;
+
+typedef struct {
+ uint32_t Characteristics;
+ uint32_t TimeDateStamp;
+ uint16_t MajorVersion;
+ uint16_t MinorVersion;
+ uint32_t Type;
+ uint32_t SizeOfData;
+ uint32_t AddressOfRawData;
+ uint32_t PointerToRawData;
+} IMAGE_DEBUG_DIRECTORY;
+
+typedef struct {
+ uint32_t VirtualAddress;
+ uint32_t Size;
+} IMAGE_DATA_DIRECTORY;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/lib/libpe/include/libpe/error.h b/include/libpe/error.h
similarity index 51%
rename from lib/libpe/include/libpe/error.h
rename to include/libpe/error.h
index 34334934..c80b8fe2 100644
--- a/lib/libpe/include/libpe/error.h
+++ b/include/libpe/error.h
@@ -22,6 +22,8 @@
#ifndef LIBPE_ERROR_H
#define LIBPE_ERROR_H
+#include "macros.h"
+
#include
#ifdef __cplusplus
@@ -30,42 +32,43 @@ extern "C" {
// FIXME: Must be careful with this enum and pe_error_msg() function.
typedef enum {
- LIBPE_E_OK = 0,
- // Declaring negative values this way is EVIL because it
- // BREAKS compatiblity every time we add/remove an error code.
- LIBPE_E_ALLOCATION_FAILURE = -23,
- LIBPE_E_OPEN_FAILED,
- LIBPE_E_FDOPEN_FAILED,
- LIBPE_E_FSTAT_FAILED,
- LIBPE_E_NOT_A_FILE,
- LIBPE_E_NOT_A_PE_FILE,
- LIBPE_E_INVALID_LFANEW,
- LIBPE_E_MISSING_COFF_HEADER,
- LIBPE_E_MISSING_OPTIONAL_HEADER,
- LIBPE_E_INVALID_SIGNATURE,
- LIBPE_E_UNSUPPORTED_IMAGE,
- LIBPE_E_MMAP_FAILED,
- LIBPE_E_MUNMAP_FAILED,
- LIBPE_E_CLOSE_FAILED,
- LIBPE_E_TOO_MANY_DIRECTORIES,
- LIBPE_E_TOO_MANY_SECTIONS,
- LIBPE_E_INVALID_THUNK,
- // Exports
- LIBPE_E_EXPORTS_CANT_READ_RVA,
- LIBPE_E_EXPORTS_CANT_READ_DIR,
- LIBPE_E_EXPORTS_FUNC_NEQ_NAMES,
- // Hashes
- LIBPE_E_HASHING_FAILED,
- // Misc
- LIBPE_E_NO_CALLBACKS_FOUND,
- LIBPE_E_NO_FUNCTIONS_FOUND // this will be -1.
+ LIBPE_E_OK = 0,
+ // Declaring negative values this way is EVIL because it
+ // BREAKS compatiblity every time we add/remove an error code.
+ LIBPE_E_ALLOCATION_FAILURE = -23,
+ LIBPE_E_OPEN_FAILED,
+ LIBPE_E_FDOPEN_FAILED,
+ LIBPE_E_FSTAT_FAILED,
+ LIBPE_E_NOT_A_FILE,
+ LIBPE_E_NOT_A_PE_FILE,
+ LIBPE_E_INVALID_LFANEW,
+ LIBPE_E_MISSING_COFF_HEADER,
+ LIBPE_E_MISSING_OPTIONAL_HEADER,
+ LIBPE_E_INVALID_SIGNATURE,
+ LIBPE_E_UNSUPPORTED_IMAGE,
+ LIBPE_E_MMAP_FAILED,
+ LIBPE_E_MUNMAP_FAILED,
+ LIBPE_E_CLOSE_FAILED,
+ LIBPE_E_TOO_MANY_DIRECTORIES,
+ LIBPE_E_TOO_MANY_SECTIONS,
+ LIBPE_E_INVALID_THUNK,
+ // Exports
+ LIBPE_E_EXPORTS_CANT_READ_RVA,
+ LIBPE_E_EXPORTS_CANT_READ_DIR,
+ LIBPE_E_EXPORTS_FUNC_NEQ_NAMES,
+ // Hashes
+ LIBPE_E_HASHING_FAILED,
+ // Misc
+ LIBPE_E_NO_CALLBACKS_FOUND,
+ LIBPE_E_NO_FUNCTIONS_FOUND // this will be -1.
} pe_err_e;
const char *pe_error_msg(pe_err_e error);
-void pe_error_print(FILE *stream, pe_err_e error);
+PEAPI void pe_error_print(FILE *stream, pe_err_e error);
#ifdef __cplusplus
} // extern "C"
#endif
#endif
+
diff --git a/lib/libpe/include/libpe/exports.h b/include/libpe/exports.h
similarity index 69%
rename from lib/libpe/include/libpe/exports.h
rename to include/libpe/exports.h
index f6617283..cf220aa8 100644
--- a/lib/libpe/include/libpe/exports.h
+++ b/include/libpe/exports.h
@@ -22,26 +22,27 @@
#ifndef LIBPE_EXPORTS_H
#define LIBPE_EXPORTS_H
-#include
#include "error.h"
+#include
+
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
- uint32_t ordinal; // ordinal of the function
- uint32_t hint; // hint (name index) of the function
- char *name; // name of the function
- char *fwd_name; // name of the forwarded function
- uint32_t address; // address of the function
+ uint32_t ordinal; // ordinal of the function
+ uint32_t hint; // hint (name index) of the function
+ char *name; // name of the function
+ char *fwd_name; // name of the forwarded function
+ uint32_t address; // address of the function
} pe_exported_function_t;
typedef struct {
- pe_err_e err;
- char *name; // name of the DLL
- uint32_t functions_count;
- pe_exported_function_t *functions; // array of exported functions
+ pe_err_e err;
+ char *name; // name of the DLL
+ uint32_t functions_count;
+ pe_exported_function_t *functions; // array of exported functions
} pe_exports_t;
void pe_exports_dealloc(pe_exports_t *exports);
@@ -51,3 +52,4 @@ void pe_exports_dealloc(pe_exports_t *exports);
#endif
#endif
+
diff --git a/lib/libpe/include/libpe/hashes.h b/include/libpe/hashes.h
similarity index 79%
rename from lib/libpe/include/libpe/hashes.h
rename to include/libpe/hashes.h
index 66b22f74..b404d55a 100644
--- a/lib/libpe/include/libpe/hashes.h
+++ b/include/libpe/hashes.h
@@ -22,37 +22,38 @@
#ifndef LIBPE_HASHES_H
#define LIBPE_HASHES_H
-#include
#include "error.h"
+#include
+
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
- LIBPE_IMPHASH_FLAVOR_MANDIANT = 1,
- LIBPE_IMPHASH_FLAVOR_PEFILE = 2,
+ LIBPE_IMPHASH_FLAVOR_MANDIANT = 1,
+ LIBPE_IMPHASH_FLAVOR_PEFILE = 2,
} pe_imphash_flavor_e;
typedef struct {
- char *name;
- char *md5;
- char *ssdeep;
- char *sha1;
- char *sha256;
+ char *name;
+ char *md5;
+ char *ssdeep;
+ char *sha1;
+ char *sha256;
} pe_hash_t;
typedef struct {
- pe_err_e err;
- pe_hash_t *dos;
- pe_hash_t *coff;
- pe_hash_t *optional;
+ pe_err_e err;
+ pe_hash_t *dos;
+ pe_hash_t *coff;
+ pe_hash_t *optional;
} pe_hash_headers_t;
typedef struct {
- pe_err_e err;
- uint32_t count;
- pe_hash_t **sections;
+ pe_err_e err;
+ uint32_t count;
+ pe_hash_t **sections;
} pe_hash_sections_t;
void pe_hash_headers_dealloc(pe_hash_headers_t *obj);
@@ -64,3 +65,4 @@ void pe_hash_dealloc(pe_hash_t *obj);
#endif
#endif
+
diff --git a/include/libpe/hdr_coff.h b/include/libpe/hdr_coff.h
new file mode 100644
index 00000000..bbe270d1
--- /dev/null
+++ b/include/libpe/hdr_coff.h
@@ -0,0 +1,175 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2017 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_HDR_COFF_H
+#define LIBPE_HDR_COFF_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
+ IMAGE_FILE_MACHINE_ALPHA_OLD = 0x183,
+ IMAGE_FILE_MACHINE_ALPHA = 0x184,
+ IMAGE_FILE_MACHINE_ALPHA64 = 0x284,
+ IMAGE_FILE_MACHINE_AM33 = 0x1d3,
+ IMAGE_FILE_MACHINE_AMD64 = 0x8664,
+ IMAGE_FILE_MACHINE_ARM = 0x1c0,
+ IMAGE_FILE_MACHINE_ARMV7 = 0x1c4,
+ IMAGE_FILE_MACHINE_ARM64 = 0xaa64,
+ IMAGE_FILE_MACHINE_ARM64EC = 0xa641,
+ IMAGE_FILE_MACHINE_ARM64X = 0xa64e,
+ IMAGE_FILE_MACHINE_CEE = 0xc0ee,
+ IMAGE_FILE_MACHINE_CEF = 0xcef,
+ IMAGE_FILE_MACHINE_CHPE_X86 = 0x3a64,
+ IMAGE_FILE_MACHINE_EBC = 0xebc,
+ IMAGE_FILE_MACHINE_I386 = 0x14c,
+ IMAGE_FILE_MACHINE_I860 = 0x14d,
+ IMAGE_FILE_MACHINE_IA64 = 0x200,
+ IMAGE_FILE_MACHINE_LOONGARCH32 = 0x6232,
+ IMAGE_FILE_MACHINE_LOONGARCH64 = 0x6264,
+ IMAGE_FILE_MACHINE_M32R = 0x9041,
+ IMAGE_FILE_MACHINE_M68K = 0x268,
+ IMAGE_FILE_MACHINE_MIPS16 = 0x266,
+ IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
+ IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
+ IMAGE_FILE_MACHINE_MPPC_601 = 0x601,
+ IMAGE_FILE_MACHINE_OMNI = 0xace1,
+ IMAGE_FILE_MACHINE_PARISC = 0x290,
+ IMAGE_FILE_MACHINE_POWERPC = 0x1f0,
+ IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1,
+ IMAGE_FILE_MACHINE_POWERPCBE = 0x1f2,
+ IMAGE_FILE_MACHINE_R3000 = 0x162,
+ IMAGE_FILE_MACHINE_R3000_BE = 0x160,
+ IMAGE_FILE_MACHINE_R4000 = 0x166,
+ IMAGE_FILE_MACHINE_R10000 = 0x168,
+ IMAGE_FILE_MACHINE_RISCV32 = 0x5032,
+ IMAGE_FILE_MACHINE_RISCV64 = 0x5064,
+ IMAGE_FILE_MACHINE_RISCV128 = 0x5128,
+ IMAGE_FILE_MACHINE_SH3 = 0x1a2,
+ IMAGE_FILE_MACHINE_SH3DSP = 0x1a3,
+ IMAGE_FILE_MACHINE_SH3E = 0x1a4,
+ IMAGE_FILE_MACHINE_SH4 = 0x1a6,
+ IMAGE_FILE_MACHINE_SH5 = 0x1a8,
+ IMAGE_FILE_MACHINE_TRICORE = 0x520,
+ IMAGE_FILE_MACHINE_TAHOE = 0x7cc,
+ IMAGE_FILE_MACHINE_THUMB = 0x1c2,
+ IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
+} MachineType;
+
+typedef enum {
+ // Image only, Windows CE, Windows NT and above. Indicates that the
+ // file does not contain base relocations and must therefore be
+ // loaded at its preferred base address. If the base address is not
+ // available, the loader reports an error. The default behavior of
+ // the linker is to strip base relocations from EXEs.
+ IMAGE_FILE_RELOCS_STRIPPED = 0x0001,
+
+ // Image only. Indicates that the image file is valid and can be run.
+ // If this flag is not set, it indicates a linker error.
+ IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002,
+
+ // COFF line numbers have been removed.
+ // Deprecated and should be zero.
+ IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004,
+
+ // COFF symbol table entries for local symbols have been removed.
+ // Deprecated and should be zero.
+ IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008,
+
+ // Obsolete. Aggressively trim working set.
+ // Deprecated in Windows 2000 and later. Must be zero.
+ IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010,
+
+ // App can handle > 2gb addresses.
+ // Image can be loaded at address above 2GB.
+ IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020,
+
+ // Machine based on 16-bit-word architecture.
+ IMAGE_FILE_16BIT_MACHINE = 0x0040,
+
+ // Bytes of the word are reversed from CPU defaults.
+ // Test either IMAGE_FILE_BYTES_REVERSED_LO or IMAGE_FILE_BYTES_REVERSED_HI,
+ // they are in the same bit position in each short word. Microsoft PE 32-Bit
+ // LINK.EXE Version 1.00 always sets this bit, but no words are reversed.
+ // New LINK.EXE versions never set this bit. Deprecated and should be zero.
+ IMAGE_FILE_BYTES_REVERSED_LO = 0x0080,
+
+ // Machine based on 32-bit-word architecture.
+ IMAGE_FILE_32BIT_MACHINE = 0x0100,
+
+ // Debugging information removed from image file.
+ IMAGE_FILE_DEBUG_STRIPPED = 0x0200,
+
+ // If image is on removable media, fully load it and copy it to the
+ // swap file.
+ IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400,
+
+ // If image is on network media, fully load it and copy it to the
+ // swap file.
+ IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800,
+
+ // The image file is a system kernel-mode file, not a user program.
+ // Images with this flag can't be loaded in user-mode. Images without
+ // this flag can be loaded in both kernel-mode and user-mode.
+ IMAGE_FILE_SYSTEM = 0x1000,
+
+ // The image file is a dynamic-link library (DLL). Such files are
+ // considered executable files for almost all purposes, although
+ // they cannot be directly run.
+ IMAGE_FILE_DLL = 0x2000,
+
+ // File should be run only on a UP (uniprocessor) machine.
+ // When running on multiprocessor machine, process has assigned
+ // one selected core via CPU affinity on which it always run.
+ IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000,
+
+ // Bytes of the word are reversed from CPU defaults.
+ // Test either IMAGE_FILE_BYTES_REVERSED_LO or IMAGE_FILE_BYTES_REVERSED_HI,
+ // they are in the same bit position in each short word. Microsoft PE 32-Bit
+ // LINK.EXE Version 1.00 always sets this bit, but no words are reversed.
+ // New LINK.EXE versions never set this bit. Deprecated and should be zero.
+ IMAGE_FILE_BYTES_REVERSED_HI = 0x8000
+} ImageCharacteristics;
+
+#pragma pack(push, 1)
+
+typedef struct {
+ uint16_t Machine; // MachineType
+ uint16_t NumberOfSections;
+ uint32_t TimeDateStamp;
+ uint32_t PointerToSymbolTable;
+ uint32_t NumberOfSymbols;
+ uint16_t SizeOfOptionalHeader;
+ uint16_t Characteristics; // ImageCharacteristics
+} IMAGE_FILE_HEADER, IMAGE_COFF_HEADER;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/lib/libpe/include/libpe/hdr_dos.h b/include/libpe/hdr_dos.h
similarity index 68%
rename from lib/libpe/include/libpe/hdr_dos.h
rename to include/libpe/hdr_dos.h
index 2344e660..d1a40f38 100644
--- a/lib/libpe/include/libpe/hdr_dos.h
+++ b/include/libpe/hdr_dos.h
@@ -31,25 +31,25 @@ extern "C" {
#pragma pack(push, 1)
typedef struct {
- uint16_t e_magic;
- uint16_t e_cblp;
- uint16_t e_cp;
- uint16_t e_crlc;
- uint16_t e_cparhdr;
- uint16_t e_minalloc;
- uint16_t e_maxalloc;
- uint16_t e_ss;
- uint16_t e_sp;
- uint16_t e_csum;
- uint16_t e_ip;
- uint16_t e_cs;
- uint16_t e_lfarlc;
- uint16_t e_ovno;
- uint16_t e_res[4];
- uint16_t e_oemid;
- uint16_t e_oeminfo;
- uint16_t e_res2[10];
- uint32_t e_lfanew; // sizeof(IMAGE_DOS_HEADER) + size of MS-DOS stub
+ uint16_t e_magic;
+ uint16_t e_cblp;
+ uint16_t e_cp;
+ uint16_t e_crlc;
+ uint16_t e_cparhdr;
+ uint16_t e_minalloc;
+ uint16_t e_maxalloc;
+ uint16_t e_ss;
+ uint16_t e_sp;
+ uint16_t e_csum;
+ uint16_t e_ip;
+ uint16_t e_cs;
+ uint16_t e_lfarlc;
+ uint16_t e_ovno;
+ uint16_t e_res[4];
+ uint16_t e_oemid;
+ uint16_t e_oeminfo;
+ uint16_t e_res2[10];
+ uint32_t e_lfanew; // sizeof(IMAGE_DOS_HEADER) + size of MS-DOS stub
} IMAGE_DOS_HEADER;
#pragma pack(pop)
@@ -59,3 +59,4 @@ typedef struct {
#endif
#endif
+
diff --git a/include/libpe/hdr_optional.h b/include/libpe/hdr_optional.h
new file mode 100644
index 00000000..53a31ab6
--- /dev/null
+++ b/include/libpe/hdr_optional.h
@@ -0,0 +1,250 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2017 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_HDR_OPTIONAL_H
+#define LIBPE_HDR_OPTIONAL_H
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// REFERENCE:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
+typedef enum {
+ // Unknown subsystem
+ IMAGE_SUBSYSTEM_UNKNOWN = 0,
+ // No subsystem required (device drivers and native system processes)
+ IMAGE_SUBSYSTEM_NATIVE = 1,
+ // Windows graphical user interface (GUI) subsystem
+ IMAGE_SUBSYSTEM_WINDOWS_GUI = 2,
+ // Windows character-mode user interface (CUI) subsystem
+ IMAGE_SUBSYSTEM_WINDOWS_CUI = 3,
+ // Old Windows CE subsystem
+ IMAGE_SUBSYSTEM_WINDOWS_OLD_CE_GUI = 4,
+ // OS/2 CUI subsystem
+ IMAGE_SUBSYSTEM_OS2_CUI = 5,
+ // POSIX CUI subsystem
+ IMAGE_SUBSYSTEM_POSIX_CUI = 7,
+ // MMOSA/Native Win32E
+ IMAGE_SUBSYSTEM_MMOSA = 8,
+ // Windows CE system
+ IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9,
+ // Extensible Firmware Interface (EFI) application
+ IMAGE_SUBSYSTEM_EFI_APPLICATION = 10,
+ // EFI driver with boot services
+ IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11,
+ // EFI driver with run-time services
+ IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12,
+ // EFI ROM image
+ IMAGE_SUBSYSTEM_EFI_ROM = 13,
+ // Xbox system
+ IMAGE_SUBSYSTEM_XBOX = 14,
+ // Boot application
+ IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16,
+ // XBOX Code Catalog
+ IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG = 17
+} WindowsSubsystem;
+
+// REFERENCE:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
+typedef enum {
+ // IMAGE_LIBRARY_* defined in PECOFF 4.0
+ // (https://bytepointer.com/resources/pecoff_v4.0.htm) DLL initialization
+ // function called just after process initialization.
+ IMAGE_LIBRARY_PROCESS_INIT = 0x0001,
+ // DLL initialization function called just before process termination.
+ IMAGE_LIBRARY_PROCESS_TERM = 0x0002,
+ // DLL initialization function called just after thread initialization.
+ // This does not apply to the first thread, which is allocated during
+ // process initialization.
+ IMAGE_LIBRARY_THREAD_INIT = 0x0004,
+ // DLL initialization function called just before thread initialization.
+ // This does not apply to the first thread allocated.
+ IMAGE_LIBRARY_THREAD_TERM = 0x0008,
+ // IMAGE_DLLCHARACTERISTICS_RESERVED_10 = 0x0010,
+ // ASLR with 64 bit address space.
+ // Image can be loaded at address above 4GB.
+ IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020,
+ // The DLL can be relocated at load time.
+ IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040,
+ // Code integrity checks are forced.
+ IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080,
+ // The image is compatible with data execution prevention (DEP).
+ // Prevents code execution on the stack, in the PE header and sections
+ // without IMAGE_SCN_MEM_EXECUTE.
+ IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100,
+ // The image is isolation aware, but should not be isolated.
+ // Prevents loading of manifest file (from embedded resource or external
+ // file .manifest) and prevents setting of default activation context.
+ // https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference
+ IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200,
+ // The image does not use structured exception handling (SEH).
+ // No handlers can be called in this image.
+ // Vectored Exception Handler still work.
+ IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400,
+ // Do not bind the image.
+ IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800,
+ // Image is a Wx86 Thunk DLL.
+ // Valid only for non-x86 (risc) DLL files.
+ // Can be generated by undocumented MSVC5+ linker flag /dllchar:x86thunk.
+ IMAGE_DLLCHARACTERISTICS_X86_THUNK = 0x1000,
+ // Image should execute in an AppContainer (Metro Apps in Windows 8).
+ // Valid only for EXE files.
+ IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000,
+ // A WDM driver.
+ IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000,
+ // Image supports Control Flow Guard
+ IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000,
+ // The image is terminal server (Remote Desktop Services) aware.
+ // https://learn.microsoft.com/en-us/windows/win32/termserv/application-compatibility-layer
+ IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
+} ImageDllCharacteristics;
+
+// PECOFF 4.0 (https://bytepointer.com/resources/pecoff_v4.0.htm) and WDK's
+// ntimage.h
+typedef enum {
+ // DLL initialization function: Halt prior to executing first instruction.
+ IMAGE_LOADER_FLAGS_BREAK_ON_LOAD = 0x00000001,
+ // DLL initialization function: Break prior to executing first instruction;
+ // effect is similar to a breakpoint.
+ IMAGE_LOADER_FLAGS_DEBUG_ON_LOAD = 0x00000002,
+ // COM+ image executable; obsolete and automatically set by loader when
+ // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR is present.
+ IMAGE_LOADER_FLAGS_COMPLUS = 0x00000001,
+ // Global subsections apply across TS sessions.
+ IMAGE_LOADER_FLAGS_SYSTEM_GLOBAL = 0x01000000
+} ImageLoaderFlags;
+
+typedef enum {
+ MAGIC_PE32_0 = 0x000,
+ MAGIC_ROM = 0x107,
+ MAGIC_PE32 = 0x10b,
+ MAGIC_PE64 = 0x20b // PE32+
+} opt_type_e;
+
+#pragma pack(push, 1)
+
+typedef struct {
+ uint16_t Magic;
+ uint8_t MajorLinkerVersion;
+ uint8_t MinorLinkerVersion;
+ uint32_t SizeOfCode;
+ uint32_t SizeOfInitializedData;
+ uint32_t SizeOfUninitializedData;
+ uint32_t AddressOfEntryPoint;
+ uint32_t BaseOfCode;
+ uint32_t BaseOfData;
+ uint32_t BaseOfBss;
+ uint32_t GprMask;
+ uint32_t CprMask[4];
+ uint32_t GpValue;
+} IMAGE_ROM_OPTIONAL_HEADER;
+
+// REFERENCE:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
+typedef struct {
+ uint16_t Magic;
+ uint8_t MajorLinkerVersion;
+ uint8_t MinorLinkerVersion;
+ uint32_t SizeOfCode;
+ uint32_t SizeOfInitializedData;
+ uint32_t SizeOfUninitializedData;
+ uint32_t AddressOfEntryPoint;
+ uint32_t BaseOfCode;
+ uint32_t BaseOfData; // only in PE32
+ uint32_t ImageBase;
+ uint32_t SectionAlignment;
+ uint32_t FileAlignment;
+ uint16_t MajorOperatingSystemVersion;
+ uint16_t MinorOperatingSystemVersion;
+ uint16_t MajorImageVersion;
+ uint16_t MinorImageVersion;
+ uint16_t MajorSubsystemVersion;
+ uint16_t MinorSubsystemVersion;
+ uint32_t Win32VersionValue;
+ uint32_t SizeOfImage;
+ uint32_t SizeOfHeaders;
+ uint32_t CheckSum;
+ uint16_t Subsystem; // WindowsSubsystem
+ uint16_t DllCharacteristics;
+ uint32_t SizeOfStackReserve;
+ uint32_t SizeOfStackCommit;
+ uint32_t SizeOfHeapReserve;
+ uint32_t SizeOfHeapCommit;
+ uint32_t LoaderFlags;
+ uint32_t NumberOfRvaAndSizes;
+ // IMAGE_DATA_DIRECTORY DataDirectory[MAX_DIRECTORIES];
+} IMAGE_OPTIONAL_HEADER_32;
+
+// REFERENCE:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
+typedef struct {
+ uint16_t Magic;
+ uint8_t MajorLinkerVersion;
+ uint8_t MinorLinkerVersion;
+ uint32_t SizeOfCode;
+ uint32_t SizeOfInitializedData;
+ uint32_t SizeOfUninitializedData;
+ uint32_t AddressOfEntryPoint;
+ uint32_t BaseOfCode;
+ uint64_t ImageBase;
+ uint32_t SectionAlignment;
+ uint32_t FileAlignment;
+ uint16_t MajorOperatingSystemVersion;
+ uint16_t MinorOperatingSystemVersion;
+ uint16_t MajorImageVersion;
+ uint16_t MinorImageVersion;
+ uint16_t MajorSubsystemVersion;
+ uint16_t MinorSubsystemVersion;
+ uint32_t Win32VersionValue;
+ uint32_t SizeOfImage;
+ uint32_t SizeOfHeaders;
+ uint32_t CheckSum;
+ uint16_t Subsystem; // WindowsSubsystem
+ uint16_t DllCharacteristics;
+ uint64_t SizeOfStackReserve;
+ uint64_t SizeOfStackCommit;
+ uint64_t SizeOfHeapReserve;
+ uint64_t SizeOfHeapCommit;
+ uint32_t LoaderFlags;
+ uint32_t NumberOfRvaAndSizes;
+ // IMAGE_DATA_DIRECTORY DataDirectory[MAX_DIRECTORIES];
+} IMAGE_OPTIONAL_HEADER_64;
+
+typedef struct {
+ uint16_t type; // opt_type_e
+ size_t length;
+ IMAGE_OPTIONAL_HEADER_32 *_32;
+ IMAGE_OPTIONAL_HEADER_64 *_64;
+ IMAGE_ROM_OPTIONAL_HEADER *_rom;
+} IMAGE_OPTIONAL_HEADER;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/lib/libpe/include/libpe/imports.h b/include/libpe/imports.h
similarity index 67%
rename from lib/libpe/include/libpe/imports.h
rename to include/libpe/imports.h
index 174871a2..69bd03f5 100644
--- a/lib/libpe/include/libpe/imports.h
+++ b/include/libpe/imports.h
@@ -22,32 +22,33 @@
#ifndef LIBPE_IMPORTS_H
#define LIBPE_IMPORTS_H
-#include
#include "error.h"
+#include
+
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
- char *name;
+ char *name;
uint16_t hint;
- uint16_t ordinal;
+ uint16_t ordinal;
} pe_imported_function_t;
typedef struct {
- pe_err_e err;
- char *name;
- uint32_t functions_count;
- pe_imported_function_t *functions; // array of imported functions
+ pe_err_e err;
+ char *name;
+ uint32_t functions_count;
+ pe_imported_function_t *functions; // array of imported functions
} pe_imported_dll_t;
typedef struct {
- pe_err_e err;
- uint32_t dll_count;
- pe_imported_dll_t *dlls; // array of DLLs
- uint32_t delay_dll_count;
- pe_imported_dll_t *delay_dlls;
+ pe_err_e err;
+ uint32_t dll_count;
+ pe_imported_dll_t *dlls; // array of DLLs
+ uint32_t delay_dll_count;
+ pe_imported_dll_t *delay_dlls;
} pe_imports_t;
void pe_imports_dealloc(pe_imports_t *imports);
@@ -58,14 +59,14 @@ void pe_imports_dealloc(pe_imports_t *imports);
* functions[i] has functions corresponding to names[i]
*
* "Imports": [
- * {
- * "DllName": "SHELL32.dll",
- * "Functions": [
- * "ShellExecuteA",
- * "FindExecutableA"
- * ]
- * }
- * ]
+ * {
+ * "DllName": "SHELL32.dll",
+ * "Functions": [
+ * "ShellExecuteA",
+ * "FindExecutableA"
+ * ]
+ * }
+ * ]
*/
#ifdef __cplusplus
@@ -73,3 +74,4 @@ void pe_imports_dealloc(pe_imports_t *imports);
#endif
#endif
+
diff --git a/lib/libpe/include/libpe/macros.h b/include/libpe/macros.h
similarity index 62%
rename from lib/libpe/include/libpe/macros.h
rename to include/libpe/macros.h
index c76fe0b3..dd8daaad 100644
--- a/lib/libpe/include/libpe/macros.h
+++ b/include/libpe/macros.h
@@ -26,17 +26,24 @@
extern "C" {
#endif
-#define LIBPE_PTR_ADD(p, o) ((void *)((char *)(p) + (o)))
-#define LIBPE_SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0]))
-#define LIBPE_SIZEOF_MEMBER(type, member) sizeof(((type *)0)->member)
+#ifdef _WIN32
+#define PEAPI __declspec(dllexport)
+#else
+#define PEAPI
+#endif
+
+#define LIBPE_PTR_ADD(p, o) ((void *) ((char *) (p) + (o)))
+#define LIBPE_SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0]))
+#define LIBPE_SIZEOF_MEMBER(type, member) sizeof(((type *) 0)->member)
-#define LIBPE_WARNING(msg) \
-{ \
- fprintf(stderr, "WARNING: %s [at %s:%d]\n", msg, __FILE__, __LINE__); \
-}
+#define LIBPE_WARNING(msg) \
+ { \
+ fprintf(stderr, "WARNING: %s [at %s:%d]\n", msg, __FILE__, __LINE__); \
+ }
#ifdef __cplusplus
} // extern "C"
#endif
#endif
+
diff --git a/include/libpe/ordlookup.h b/include/libpe/ordlookup.h
new file mode 100644
index 00000000..1c050bc6
--- /dev/null
+++ b/include/libpe/ordlookup.h
@@ -0,0 +1,567 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2017 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+// This lookup database is necessary to match Ero Carrera's pefile imphash
+// implementation used by VT and other programs
+// https://github.com/erocarrera/pefile/tree/master/ordlookup
+
+#pragma once
+#ifndef LIBPE_ORDLOOKUP_H
+#define LIBPE_ORDLOOKUP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ int number;
+ char *fname;
+} ord_t;
+
+ord_t oleaut32_arr[] = {
+ {2, "SysAllocString"},
+ {3, "SysReAllocString"},
+ {4, "SysAllocStringLen"},
+ {5, "SysReAllocStringLen"},
+ {6, "SysFreeString"},
+ {7, "SysStringLen"},
+ {8, "VariantInit"},
+ {9, "VariantClear"},
+ {10, "VariantCopy"},
+ {11, "VariantCopyInd"},
+ {12, "VariantChangeType"},
+ {13, "VariantTimeToDosDateTime"},
+ {14, "DosDateTimeToVariantTime"},
+ {15, "SafeArrayCreate"},
+ {16, "SafeArrayDestroy"},
+ {17, "SafeArrayGetDim"},
+ {18, "SafeArrayGetElemsize"},
+ {19, "SafeArrayGetUBound"},
+ {20, "SafeArrayGetLBound"},
+ {21, "SafeArrayLock"},
+ {22, "SafeArrayUnlock"},
+ {23, "SafeArrayAccessData"},
+ {24, "SafeArrayUnaccessData"},
+ {25, "SafeArrayGetElement"},
+ {26, "SafeArrayPutElement"},
+ {27, "SafeArrayCopy"},
+ {28, "DispGetParam"},
+ {29, "DispGetIDsOfNames"},
+ {30, "DispInvoke"},
+ {31, "CreateDispTypeInfo"},
+ {32, "CreateStdDispatch"},
+ {33, "RegisterActiveObject"},
+ {34, "RevokeActiveObject"},
+ {35, "GetActiveObject"},
+ {36, "SafeArrayAllocDescriptor"},
+ {37, "SafeArrayAllocData"},
+ {38, "SafeArrayDestroyDescriptor"},
+ {39, "SafeArrayDestroyData"},
+ {40, "SafeArrayRedim"},
+ {41, "SafeArrayAllocDescriptorEx"},
+ {42, "SafeArrayCreateEx"},
+ {43, "SafeArrayCreateVectorEx"},
+ {44, "SafeArraySetRecordInfo"},
+ {45, "SafeArrayGetRecordInfo"},
+ {46, "VarParseNumFromStr"},
+ {47, "VarNumFromParseNum"},
+ {48, "VarI2FromUI1"},
+ {49, "VarI2FromI4"},
+ {50, "VarI2FromR4"},
+ {51, "VarI2FromR8"},
+ {52, "VarI2FromCy"},
+ {53, "VarI2FromDate"},
+ {54, "VarI2FromStr"},
+ {55, "VarI2FromDisp"},
+ {56, "VarI2FromBool"},
+ {57, "SafeArraySetIID"},
+ {58, "VarI4FromUI1"},
+ {59, "VarI4FromI2"},
+ {60, "VarI4FromR4"},
+ {61, "VarI4FromR8"},
+ {62, "VarI4FromCy"},
+ {63, "VarI4FromDate"},
+ {64, "VarI4FromStr"},
+ {65, "VarI4FromDisp"},
+ {66, "VarI4FromBool"},
+ {67, "SafeArrayGetIID"},
+ {68, "VarR4FromUI1"},
+ {69, "VarR4FromI2"},
+ {70, "VarR4FromI4"},
+ {71, "VarR4FromR8"},
+ {72, "VarR4FromCy"},
+ {73, "VarR4FromDate"},
+ {74, "VarR4FromStr"},
+ {75, "VarR4FromDisp"},
+ {76, "VarR4FromBool"},
+ {77, "SafeArrayGetVartype"},
+ {78, "VarR8FromUI1"},
+ {79, "VarR8FromI2"},
+ {80, "VarR8FromI4"},
+ {81, "VarR8FromR4"},
+ {82, "VarR8FromCy"},
+ {83, "VarR8FromDate"},
+ {84, "VarR8FromStr"},
+ {85, "VarR8FromDisp"},
+ {86, "VarR8FromBool"},
+ {87, "VarFormat"},
+ {88, "VarDateFromUI1"},
+ {89, "VarDateFromI2"},
+ {90, "VarDateFromI4"},
+ {91, "VarDateFromR4"},
+ {92, "VarDateFromR8"},
+ {93, "VarDateFromCy"},
+ {94, "VarDateFromStr"},
+ {95, "VarDateFromDisp"},
+ {96, "VarDateFromBool"},
+ {97, "VarFormatDateTime"},
+ {98, "VarCyFromUI1"},
+ {99, "VarCyFromI2"},
+ {100, "VarCyFromI4"},
+ {101, "VarCyFromR4"},
+ {102, "VarCyFromR8"},
+ {103, "VarCyFromDate"},
+ {104, "VarCyFromStr"},
+ {105, "VarCyFromDisp"},
+ {106, "VarCyFromBool"},
+ {107, "VarFormatNumber"},
+ {108, "VarBstrFromUI1"},
+ {109, "VarBstrFromI2"},
+ {110, "VarBstrFromI4"},
+ {111, "VarBstrFromR4"},
+ {112, "VarBstrFromR8"},
+ {113, "VarBstrFromCy"},
+ {114, "VarBstrFromDate"},
+ {115, "VarBstrFromDisp"},
+ {116, "VarBstrFromBool"},
+ {117, "VarFormatPercent"},
+ {118, "VarBoolFromUI1"},
+ {119, "VarBoolFromI2"},
+ {120, "VarBoolFromI4"},
+ {121, "VarBoolFromR4"},
+ {122, "VarBoolFromR8"},
+ {123, "VarBoolFromDate"},
+ {124, "VarBoolFromCy"},
+ {125, "VarBoolFromStr"},
+ {126, "VarBoolFromDisp"},
+ {127, "VarFormatCurrency"},
+ {128, "VarWeekdayName"},
+ {129, "VarMonthName"},
+ {130, "VarUI1FromI2"},
+ {131, "VarUI1FromI4"},
+ {132, "VarUI1FromR4"},
+ {133, "VarUI1FromR8"},
+ {134, "VarUI1FromCy"},
+ {135, "VarUI1FromDate"},
+ {136, "VarUI1FromStr"},
+ {137, "VarUI1FromDisp"},
+ {138, "VarUI1FromBool"},
+ {139, "VarFormatFromTokens"},
+ {140, "VarTokenizeFormatString"},
+ {141, "VarAdd"},
+ {142, "VarAnd"},
+ {143, "VarDiv"},
+ {144, "DllCanUnloadNow"},
+ {145, "DllGetClassObject"},
+ {146, "DispCallFunc"},
+ {147, "VariantChangeTypeEx"},
+ {148, "SafeArrayPtrOfIndex"},
+ {149, "SysStringByteLen"},
+ {150, "SysAllocStringByteLen"},
+ {151, "DllRegisterServer"},
+ {152, "VarEqv"},
+ {153, "VarIdiv"},
+ {154, "VarImp"},
+ {155, "VarMod"},
+ {156, "VarMul"},
+ {157, "VarOr"},
+ {158, "VarPow"},
+ {159, "VarSub"},
+ {160, "CreateTypeLib"},
+ {161, "LoadTypeLib"},
+ {162, "LoadRegTypeLib"},
+ {163, "RegisterTypeLib"},
+ {164, "QueryPathOfRegTypeLib"},
+ {165, "LHashValOfNameSys"},
+ {166, "LHashValOfNameSysA"},
+ {167, "VarXor"},
+ {168, "VarAbs"},
+ {169, "VarFix"},
+ {170, "OaBuildVersion"},
+ {171, "ClearCustData"},
+ {172, "VarInt"},
+ {173, "VarNeg"},
+ {174, "VarNot"},
+ {175, "VarRound"},
+ {176, "VarCmp"},
+ {177, "VarDecAdd"},
+ {178, "VarDecDiv"},
+ {179, "VarDecMul"},
+ {180, "CreateTypeLib2"},
+ {181, "VarDecSub"},
+ {182, "VarDecAbs"},
+ {183, "LoadTypeLibEx"},
+ {184, "SystemTimeToVariantTime"},
+ {185, "VariantTimeToSystemTime"},
+ {186, "UnRegisterTypeLib"},
+ {187, "VarDecFix"},
+ {188, "VarDecInt"},
+ {189, "VarDecNeg"},
+ {190, "VarDecFromUI1"},
+ {191, "VarDecFromI2"},
+ {192, "VarDecFromI4"},
+ {193, "VarDecFromR4"},
+ {194, "VarDecFromR8"},
+ {195, "VarDecFromDate"},
+ {196, "VarDecFromCy"},
+ {197, "VarDecFromStr"},
+ {198, "VarDecFromDisp"},
+ {199, "VarDecFromBool"},
+ {200, "GetErrorInfo"},
+ {201, "SetErrorInfo"},
+ {202, "CreateErrorInfo"},
+ {203, "VarDecRound"},
+ {204, "VarDecCmp"},
+ {205, "VarI2FromI1"},
+ {206, "VarI2FromUI2"},
+ {207, "VarI2FromUI4"},
+ {208, "VarI2FromDec"},
+ {209, "VarI4FromI1"},
+ {210, "VarI4FromUI2"},
+ {211, "VarI4FromUI4"},
+ {212, "VarI4FromDec"},
+ {213, "VarR4FromI1"},
+ {214, "VarR4FromUI2"},
+ {215, "VarR4FromUI4"},
+ {216, "VarR4FromDec"},
+ {217, "VarR8FromI1"},
+ {218, "VarR8FromUI2"},
+ {219, "VarR8FromUI4"},
+ {220, "VarR8FromDec"},
+ {221, "VarDateFromI1"},
+ {222, "VarDateFromUI2"},
+ {223, "VarDateFromUI4"},
+ {224, "VarDateFromDec"},
+ {225, "VarCyFromI1"},
+ {226, "VarCyFromUI2"},
+ {227, "VarCyFromUI4"},
+ {228, "VarCyFromDec"},
+ {229, "VarBstrFromI1"},
+ {230, "VarBstrFromUI2"},
+ {231, "VarBstrFromUI4"},
+ {232, "VarBstrFromDec"},
+ {233, "VarBoolFromI1"},
+ {234, "VarBoolFromUI2"},
+ {235, "VarBoolFromUI4"},
+ {236, "VarBoolFromDec"},
+ {237, "VarUI1FromI1"},
+ {238, "VarUI1FromUI2"},
+ {239, "VarUI1FromUI4"},
+ {240, "VarUI1FromDec"},
+ {241, "VarDecFromI1"},
+ {242, "VarDecFromUI2"},
+ {243, "VarDecFromUI4"},
+ {244, "VarI1FromUI1"},
+ {245, "VarI1FromI2"},
+ {246, "VarI1FromI4"},
+ {247, "VarI1FromR4"},
+ {248, "VarI1FromR8"},
+ {249, "VarI1FromDate"},
+ {250, "VarI1FromCy"},
+ {251, "VarI1FromStr"},
+ {252, "VarI1FromDisp"},
+ {253, "VarI1FromBool"},
+ {254, "VarI1FromUI2"},
+ {255, "VarI1FromUI4"},
+ {256, "VarI1FromDec"},
+ {257, "VarUI2FromUI1"},
+ {258, "VarUI2FromI2"},
+ {259, "VarUI2FromI4"},
+ {260, "VarUI2FromR4"},
+ {261, "VarUI2FromR8"},
+ {262, "VarUI2FromDate"},
+ {263, "VarUI2FromCy"},
+ {264, "VarUI2FromStr"},
+ {265, "VarUI2FromDisp"},
+ {266, "VarUI2FromBool"},
+ {267, "VarUI2FromI1"},
+ {268, "VarUI2FromUI4"},
+ {269, "VarUI2FromDec"},
+ {270, "VarUI4FromUI1"},
+ {271, "VarUI4FromI2"},
+ {272, "VarUI4FromI4"},
+ {273, "VarUI4FromR4"},
+ {274, "VarUI4FromR8"},
+ {275, "VarUI4FromDate"},
+ {276, "VarUI4FromCy"},
+ {277, "VarUI4FromStr"},
+ {278, "VarUI4FromDisp"},
+ {279, "VarUI4FromBool"},
+ {280, "VarUI4FromI1"},
+ {281, "VarUI4FromUI2"},
+ {282, "VarUI4FromDec"},
+ {283, "BSTR_UserSize"},
+ {284, "BSTR_UserMarshal"},
+ {285, "BSTR_UserUnmarshal"},
+ {286, "BSTR_UserFree"},
+ {287, "VARIANT_UserSize"},
+ {288, "VARIANT_UserMarshal"},
+ {289, "VARIANT_UserUnmarshal"},
+ {290, "VARIANT_UserFree"},
+ {291, "LPSAFEARRAY_UserSize"},
+ {292, "LPSAFEARRAY_UserMarshal"},
+ {293, "LPSAFEARRAY_UserUnmarshal"},
+ {294, "LPSAFEARRAY_UserFree"},
+ {295, "LPSAFEARRAY_Size"},
+ {296, "LPSAFEARRAY_Marshal"},
+ {297, "LPSAFEARRAY_Unmarshal"},
+ {298, "VarDecCmpR8"},
+ {299, "VarCyAdd"},
+ {300, "DllUnregisterServer"},
+ {301, "OACreateTypeLib2"},
+ {303, "VarCyMul"},
+ {304, "VarCyMulI4"},
+ {305, "VarCySub"},
+ {306, "VarCyAbs"},
+ {307, "VarCyFix"},
+ {308, "VarCyInt"},
+ {309, "VarCyNeg"},
+ {310, "VarCyRound"},
+ {311, "VarCyCmp"},
+ {312, "VarCyCmpR8"},
+ {313, "VarBstrCat"},
+ {314, "VarBstrCmp"},
+ {315, "VarR8Pow"},
+ {316, "VarR4CmpR8"},
+ {317, "VarR8Round"},
+ {318, "VarCat"},
+ {319, "VarDateFromUdateEx"},
+ {322, "GetRecordInfoFromGuids"},
+ {323, "GetRecordInfoFromTypeInfo"},
+ {325, "SetVarConversionLocaleSetting"},
+ {326, "GetVarConversionLocaleSetting"},
+ {327, "SetOaNoCache"},
+ {329, "VarCyMulI8"},
+ {330, "VarDateFromUdate"},
+ {331, "VarUdateFromDate"},
+ {332, "GetAltMonthNames"},
+ {333, "VarI8FromUI1"},
+ {334, "VarI8FromI2"},
+ {335, "VarI8FromR4"},
+ {336, "VarI8FromR8"},
+ {337, "VarI8FromCy"},
+ {338, "VarI8FromDate"},
+ {339, "VarI8FromStr"},
+ {340, "VarI8FromDisp"},
+ {341, "VarI8FromBool"},
+ {342, "VarI8FromI1"},
+ {343, "VarI8FromUI2"},
+ {344, "VarI8FromUI4"},
+ {345, "VarI8FromDec"},
+ {346, "VarI2FromI8"},
+ {347, "VarI2FromUI8"},
+ {348, "VarI4FromI8"},
+ {349, "VarI4FromUI8"},
+ {360, "VarR4FromI8"},
+ {361, "VarR4FromUI8"},
+ {362, "VarR8FromI8"},
+ {363, "VarR8FromUI8"},
+ {364, "VarDateFromI8"},
+ {365, "VarDateFromUI8"},
+ {366, "VarCyFromI8"},
+ {367, "VarCyFromUI8"},
+ {368, "VarBstrFromI8"},
+ {369, "VarBstrFromUI8"},
+ {370, "VarBoolFromI8"},
+ {371, "VarBoolFromUI8"},
+ {372, "VarUI1FromI8"},
+ {373, "VarUI1FromUI8"},
+ {374, "VarDecFromI8"},
+ {375, "VarDecFromUI8"},
+ {376, "VarI1FromI8"},
+ {377, "VarI1FromUI8"},
+ {378, "VarUI2FromI8"},
+ {379, "VarUI2FromUI8"},
+ {401, "OleLoadPictureEx"},
+ {402, "OleLoadPictureFileEx"},
+ {411, "SafeArrayCreateVector"},
+ {412, "SafeArrayCopyData"},
+ {413, "VectorFromBstr"},
+ {414, "BstrFromVector"},
+ {415, "OleIconToCursor"},
+ {416, "OleCreatePropertyFrameIndirect"},
+ {417, "OleCreatePropertyFrame"},
+ {418, "OleLoadPicture"},
+ {419, "OleCreatePictureIndirect"},
+ {420, "OleCreateFontIndirect"},
+ {421, "OleTranslateColor"},
+ {422, "OleLoadPictureFile"},
+ {423, "OleSavePictureFile"},
+ {424, "OleLoadPicturePath"},
+ {425, "VarUI4FromI8"},
+ {426, "VarUI4FromUI8"},
+ {427, "VarI8FromUI8"},
+ {428, "VarUI8FromI8"},
+ {429, "VarUI8FromUI1"},
+ {430, "VarUI8FromI2"},
+ {431, "VarUI8FromR4"},
+ {432, "VarUI8FromR8"},
+ {433, "VarUI8FromCy"},
+ {434, "VarUI8FromDate"},
+ {435, "VarUI8FromStr"},
+ {436, "VarUI8FromDisp"},
+ {437, "VarUI8FromBool"},
+ {438, "VarUI8FromI1"},
+ {439, "VarUI8FromUI2"},
+ {440, "VarUI8FromUI4"},
+ {441, "VarUI8FromDec"},
+ {442, "RegisterTypeLibForUser"},
+ {443, "UnRegisterTypeLibForUser"},
+ {0}
+};
+
+ord_t ws2_32_arr[] = {
+ {1, "accept"},
+ {2, "bind"},
+ {3, "closesocket"},
+ {4, "connect"},
+ {5, "getpeername"},
+ {6, "getsockname"},
+ {7, "getsockopt"},
+ {8, "htonl"},
+ {9, "htons"},
+ {10, "ioctlsocket"},
+ {11, "inet_addr"},
+ {12, "inet_ntoa"},
+ {13, "listen"},
+ {14, "ntohl"},
+ {15, "ntohs"},
+ {16, "recv"},
+ {17, "recvfrom"},
+ {18, "select"},
+ {19, "send"},
+ {20, "sendto"},
+ {21, "setsockopt"},
+ {22, "shutdown"},
+ {23, "socket"},
+ {24, "GetAddrInfoW"},
+ {25, "GetNameInfoW"},
+ {26, "WSApSetPostRoutine"},
+ {27, "FreeAddrInfoW"},
+ {28, "WPUCompleteOverlappedRequest"},
+ {29, "WSAAccept"},
+ {30, "WSAAddressToStringA"},
+ {31, "WSAAddressToStringW"},
+ {32, "WSACloseEvent"},
+ {33, "WSAConnect"},
+ {34, "WSACreateEvent"},
+ {35, "WSADuplicateSocketA"},
+ {36, "WSADuplicateSocketW"},
+ {37, "WSAEnumNameSpaceProvidersA"},
+ {38, "WSAEnumNameSpaceProvidersW"},
+ {39, "WSAEnumNetworkEvents"},
+ {40, "WSAEnumProtocolsA"},
+ {41, "WSAEnumProtocolsW"},
+ {42, "WSAEventSelect"},
+ {43, "WSAGetOverlappedResult"},
+ {44, "WSAGetQOSByName"},
+ {45, "WSAGetServiceClassInfoA"},
+ {46, "WSAGetServiceClassInfoW"},
+ {47, "WSAGetServiceClassNameByClassIdA"},
+ {48, "WSAGetServiceClassNameByClassIdW"},
+ {49, "WSAHtonl"},
+ {50, "WSAHtons"},
+ {51, "gethostbyaddr"},
+ {52, "gethostbyname"},
+ {53, "getprotobyname"},
+ {54, "getprotobynumber"},
+ {55, "getservbyname"},
+ {56, "getservbyport"},
+ {57, "gethostname"},
+ {58, "WSAInstallServiceClassA"},
+ {59, "WSAInstallServiceClassW"},
+ {60, "WSAIoctl"},
+ {61, "WSAJoinLeaf"},
+ {62, "WSALookupServiceBeginA"},
+ {63, "WSALookupServiceBeginW"},
+ {64, "WSALookupServiceEnd"},
+ {65, "WSALookupServiceNextA"},
+ {66, "WSALookupServiceNextW"},
+ {67, "WSANSPIoctl"},
+ {68, "WSANtohl"},
+ {69, "WSANtohs"},
+ {70, "WSAProviderConfigChange"},
+ {71, "WSARecv"},
+ {72, "WSARecvDisconnect"},
+ {73, "WSARecvFrom"},
+ {74, "WSARemoveServiceClass"},
+ {75, "WSAResetEvent"},
+ {76, "WSASend"},
+ {77, "WSASendDisconnect"},
+ {78, "WSASendTo"},
+ {79, "WSASetEvent"},
+ {80, "WSASetServiceA"},
+ {81, "WSASetServiceW"},
+ {82, "WSASocketA"},
+ {83, "WSASocketW"},
+ {84, "WSAStringToAddressA"},
+ {85, "WSAStringToAddressW"},
+ {86, "WSAWaitForMultipleEvents"},
+ {87, "WSCDeinstallProvider"},
+ {88, "WSCEnableNSProvider"},
+ {89, "WSCEnumProtocols"},
+ {90, "WSCGetProviderPath"},
+ {91, "WSCInstallNameSpace"},
+ {92, "WSCInstallProvider"},
+ {93, "WSCUnInstallNameSpace"},
+ {94, "WSCUpdateProvider"},
+ {95, "WSCWriteNameSpaceOrder"},
+ {96, "WSCWriteProviderOrder"},
+ {97, "freeaddrinfo"},
+ {98, "getaddrinfo"},
+ {99, "getnameinfo"},
+ {101, "WSAAsyncSelect"},
+ {102, "WSAAsyncGetHostByAddr"},
+ {103, "WSAAsyncGetHostByName"},
+ {104, "WSAAsyncGetProtoByNumber"},
+ {105, "WSAAsyncGetProtoByName"},
+ {106, "WSAAsyncGetServByPort"},
+ {107, "WSAAsyncGetServByName"},
+ {108, "WSACancelAsyncRequest"},
+ {109, "WSASetBlockingHook"},
+ {110, "WSAUnhookBlockingHook"},
+ {111, "WSAGetLastError"},
+ {112, "WSASetLastError"},
+ {113, "WSACancelBlockingCall"},
+ {114, "WSAIsBlocking"},
+ {115, "WSAStartup"},
+ {116, "WSACleanup"},
+ {151, "__WSAFDIsSet"},
+ {500, "WEP"},
+ {0}
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/include/libpe/pe.h b/include/libpe/pe.h
new file mode 100644
index 00000000..392df484
--- /dev/null
+++ b/include/libpe/pe.h
@@ -0,0 +1,166 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2025 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_PE_H
+#define LIBPE_PE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "context.h"
+#include "dir_security.h"
+#include "macros.h"
+// #include "dir_security.h"
+// #include "directories.h"
+// #include "error.h"
+// #include "exports.h"
+// #include "hashes.h"
+// #include "hdr_coff.h"
+// #include "hdr_dos.h"
+// #include "hdr_optional.h"
+// #include "imports.h"
+// #include "sections.h"
+
+#include
+#include
+
+static const char __MAGIC_MZ[2] = {'M', 'Z'};
+static const uint16_t MAGIC_MZ = ('Z' << 8) + 'M';
+
+// #define MAGIC_MZ 0x5a4d // Belongs to the DOS header
+#define MAX_DIRECTORIES 16
+#define MAX_SECTIONS 96
+
+// TODO(jweyrich): Does the PE spec define a length limit for
+// function names and import/export library names?
+#define MAX_DLL_NAME 256
+#define MAX_FUNCTION_NAME 512
+
+static const uint32_t IMAGE_ORDINAL_FLAG32 = 0x80000000;
+static const uint64_t IMAGE_ORDINAL_FLAG64 = 0x8000000000000000;
+#define IMAGE_ORDINAL_MASK(ctx) \
+ ((ctx->pe.optional_hdr.type == MAGIC_PE32) ? IMAGE_ORDINAL_FLAG32 \
+ : IMAGE_ORDINAL_FLAG64)
+
+#define SIGNATURE_PE 0x00004550 // PE\0\0 in little-endian
+#define SIGNATURE_PL \
+ 0x00004C50 // PL\0\0 in little-endian, used by Phar Lap TNT DOS extender
+#define SIGNATURE_PX \
+ 0x00005850 // PX\0\0 in little-endian, used by HX DOS extender
+
+typedef enum {
+ LIBPE_OPT_NOCLOSE_FD = (1 << 0), // Keeps `stream` open for further usage.
+ LIBPE_OPT_OPEN_RW = (1 << 1) // Open file for read and writing
+} pe_option_e;
+
+typedef uint16_t pe_options_e; // bitmasked pe_option_e values
+
+// General functions
+PEAPI bool pe_can_read(const pe_ctx_t *ctx, const void *ptr, size_t size);
+PEAPI pe_err_e pe_load_file(pe_ctx_t *ctx, const char *path);
+pe_err_e pe_load_file_ext(pe_ctx_t *ctx, const char *path,
+ pe_options_e options);
+PEAPI pe_err_e pe_unload(pe_ctx_t *ctx);
+PEAPI pe_err_e pe_parse(pe_ctx_t *ctx);
+bool pe_is_loaded(const pe_ctx_t *ctx);
+PEAPI bool pe_is_pe(const pe_ctx_t *ctx);
+PEAPI bool pe_is_exec(const pe_ctx_t *ctx);
+bool pe_is_obj(const pe_ctx_t *ctx);
+bool pe_is_rom(const pe_ctx_t *ctx);
+PEAPI bool pe_is_dll(const pe_ctx_t *ctx);
+PEAPI uint64_t pe_filesize(const pe_ctx_t *ctx);
+PEAPI IMAGE_SECTION_HEADER *pe_rva2section(pe_ctx_t *ctx, uint64_t rva);
+PEAPI uint64_t pe_rva2ofs(const pe_ctx_t *ctx, uint64_t rva);
+PEAPI uint64_t pe_ofs2rva(const pe_ctx_t *ctx, uint64_t ofs);
+
+// Header functions
+PEAPI IMAGE_DOS_HEADER *pe_dos(pe_ctx_t *ctx);
+PEAPI IMAGE_COFF_HEADER *pe_coff(pe_ctx_t *ctx);
+PEAPI IMAGE_OPTIONAL_HEADER *pe_optional(pe_ctx_t *ctx);
+PEAPI uint32_t pe_directories_count(const pe_ctx_t *ctx);
+PEAPI IMAGE_DATA_DIRECTORY **pe_directories(pe_ctx_t *ctx);
+PEAPI IMAGE_DATA_DIRECTORY *pe_directory_by_entry(pe_ctx_t *ctx,
+ ImageDirectoryEntry entry);
+PEAPI uint16_t pe_sections_count(const pe_ctx_t *ctx);
+PEAPI IMAGE_SECTION_HEADER **pe_sections(pe_ctx_t *ctx);
+PEAPI IMAGE_SECTION_HEADER *pe_section_by_name(pe_ctx_t *ctx,
+ const char *section_name);
+PEAPI const char *pe_section_name(const pe_ctx_t *ctx,
+ const IMAGE_SECTION_HEADER *section_hdr,
+ char *out_name, size_t out_name_size);
+
+PEAPI const char *pe_machine_type_name(MachineType type);
+PEAPI const char *
+pe_image_characteristic_name(ImageCharacteristics characteristic);
+PEAPI const char *
+pe_image_dllcharacteristic_name(ImageDllCharacteristics characteristic);
+PEAPI const char *
+pe_dll_image_dllcharacteristic_name(ImageDllCharacteristics characteristic);
+PEAPI const char *pe_image_loader_flags_name(ImageLoaderFlags flags);
+PEAPI const char *pe_dll_image_loader_flags_name(ImageLoaderFlags flags);
+PEAPI const char *pe_windows_subsystem_name(WindowsSubsystem subsystem);
+PEAPI const char *pe_directory_name(ImageDirectoryEntry entry);
+PEAPI const char *
+pe_section_characteristic_name(SectionCharacteristics characteristic);
+const char *
+pe_m68k_section_characteristic_name(SectionCharacteristics characteristic);
+const char *
+pe_rom_section_characteristic_name(ROMSectionCharacteristics characteristic);
+bool pe_use_rom_section_characteristic(pe_ctx_t *ctx);
+bool pe_is_repro(pe_ctx_t *ctx);
+
+// Hash functions
+PEAPI size_t pe_hash_recommended_size(void);
+PEAPI bool pe_hash_raw_data(char *output, size_t output_size,
+ const char *alg_name, const unsigned char *data,
+ size_t data_size);
+pe_hash_headers_t *pe_get_headers_hashes(pe_ctx_t *ctx);
+pe_hash_sections_t *pe_get_sections_hash(pe_ctx_t *ctx);
+pe_hash_t *pe_get_file_hash(pe_ctx_t *ctx);
+PEAPI char *pe_imphash(pe_ctx_t *ctx, pe_imphash_flavor_e flavor);
+
+// Imports functions
+PEAPI pe_imports_t *pe_imports(pe_ctx_t *ctx);
+
+// Exports functions
+PEAPI pe_exports_t *pe_exports(pe_ctx_t *ctx);
+
+// Certificate functtions
+uint32_t pe_certificate_count(pe_ctx_t *ctx);
+PEAPI uint32_t pe_certificates(pe_ctx_t *ctx, WIN_CERTIFICATE ***certs);
+
+// Resources functions
+PEAPI pe_resources_t *pe_resources(pe_ctx_t *ctx);
+
+// Misc functions
+PEAPI double pe_calculate_entropy_file(pe_ctx_t *ctx);
+PEAPI bool pe_fpu_trick(pe_ctx_t *ctx);
+int pe_get_cpl_analysis(pe_ctx_t *ctx);
+int pe_has_fake_entrypoint(pe_ctx_t *ctx);
+int pe_get_tls_callback(pe_ctx_t *ctx);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/include/libpe/resources.h b/include/libpe/resources.h
new file mode 100644
index 00000000..bdc636a3
--- /dev/null
+++ b/include/libpe/resources.h
@@ -0,0 +1,101 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2025 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_RESOURCES_H
+#define LIBPE_RESOURCES_H
+
+#include "context.h"
+#include "macros.h"
+#include "types_resources.h"
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Type Lookup for IMAGE_RESOURCE_DATA_ENTRY
+//
+
+typedef struct {
+ char *name;
+ ResourceType type;
+ char *extension;
+ char *dir_name;
+} pe_resource_entry_info_t;
+
+PEAPI const pe_resource_entry_info_t *
+pe_resource_entry_info_lookup(uint32_t name_offset);
+
+//
+// Search nodes
+//
+
+typedef bool (*pe_resource_node_predicate_fn)(const pe_resource_node_t *node);
+
+typedef struct pe_resource_node_search_result_item {
+ const pe_resource_node_t *node;
+ struct pe_resource_node_search_result_item *next;
+} pe_resource_node_search_result_item_t;
+
+typedef struct {
+ size_t count;
+ pe_resource_node_search_result_item_t *items;
+} pe_resource_node_search_result_t;
+
+PEAPI void pe_resource_search_nodes(pe_resource_node_search_result_t *result,
+ const pe_resource_node_t *node,
+ pe_resource_node_predicate_fn predicate);
+PEAPI void pe_resources_dealloc_node_search_result(
+ pe_resource_node_search_result_t *result);
+
+//
+// Main
+//
+
+pe_resource_node_t *pe_resource_root_node(const pe_resource_node_t *node);
+pe_resource_node_t *
+pe_resource_last_child_node(const pe_resource_node_t *parent_node);
+PEAPI pe_resource_node_t *
+pe_resource_find_node_by_type_and_level(const pe_resource_node_t *node,
+ pe_resource_node_type_e type,
+ uint32_t dirLevel);
+PEAPI pe_resource_node_t *
+pe_resource_find_parent_node_by_type_and_level(const pe_resource_node_t *node,
+ pe_resource_node_type_e type,
+ uint32_t dirLevel);
+char *
+pe_resource_parse_string_u(pe_ctx_t *ctx, char *output, size_t output_size,
+ const IMAGE_RESOURCE_DATA_STRING_U *data_string_ptr);
+void pe_resources_dealloc(pe_resources_t *obj);
+PEAPI const VS_FIXEDFILEINFO *
+pe_resource_get_fixedfileinfo(const pe_ctx_t *ctx,
+ const pe_resource_node_t *node, void **child_out);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/include/libpe/sections.h b/include/libpe/sections.h
new file mode 100644
index 00000000..afc9785e
--- /dev/null
+++ b/include/libpe/sections.h
@@ -0,0 +1,212 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2017 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_SECTIONS_H
+#define LIBPE_SECTIONS_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SECTION_NAME_SIZE 8
+
+// These informations were filled from various sources:
+// - various versions of SDK files ntimage.h, winnt.h and coff.doc, pecoff*.doc
+// documents
+// - behavior of MSVC40 CL.EXE (I386 and M68K versions), LINK.EXE and
+// DUMPBIN.EXE
+typedef enum {
+ IMAGE_SCN_SCALE_INDEX
+ = 0x00000001, // Address of tls index is scaled (multiplied by 4). This
+ // is valid only for .tls section and only on MIPS.
+ IMAGE_SCN_TYPE_NO_LOAD = 0x00000002, // Reserved.
+ IMAGE_SCN_TYPE_GROUPED
+ = 0x00000004, // Used for 16-bit offset code. Linker combines sections
+ // with the same name (they may have different flags) into
+ // one output section with max 64 kB size. All offsets
+ // inside the section are signed 16-bit from the middle of
+ // the section. This is valid only for object files. (Not
+ // supported by LINK.EXE)
+ IMAGE_SCN_TYPE_NO_PAD = 0x00000008, // Same as IMAGE_SCN_ALIGN_1BYTES. This
+ // is valid only for object files.
+ IMAGE_SCN_TYPE_COPY = 0x00000010, // Reserved.
+ IMAGE_SCN_CNT_CODE = 0x00000020, // The section contains executable code.
+ IMAGE_SCN_CNT_INITIALIZED_DATA
+ = 0x00000040, // The section contains initialized data.
+ IMAGE_SCN_CNT_UNINITIALIZED_DATA
+ = 0x00000080, // The section contains uninitialized data.
+ IMAGE_SCN_LNK_OTHER
+ = 0x00000100, // The section contains other than info, code or data.
+ // This is valid only for object files.
+ IMAGE_SCN_LNK_INFO
+ = 0x00000200, // The section contains comments or other information.
+ // This is valid only for object files.
+ IMAGE_SCN_LNK_OVERLAY
+ = 0x00000400, // The section contains an overlay (Reserved).
+ IMAGE_SCN_LNK_REMOVE
+ = 0x00000800, // The section will not become part of the image. This is
+ // valid only for object files.
+ IMAGE_SCN_LNK_COMDAT = 0x00001000, // The section contains COMDAT data. This
+ // is valid only for object files.
+ // RESERVED = 0x00002000, // Reserved.
+ IMAGE_SCN_MEM_PROTECTED
+ = 0x00004000, // The section is memory protected. This is valid only for
+ // M68K (Mac OS memory management).
+ IMAGE_SCN_NO_DEFER_SPEC_EXC
+ = 0x00004000, // Reset speculative exceptions handling bits in the TLB
+ // entries for this section. This is not valid for M68K.
+ IMAGE_SCN_MEM_FARDATA
+ = 0x00008000, // The section contains FAR_EXTERNAL relocations. This is
+ // valid only for M68K (Mac OS memory management).
+ IMAGE_SCN_GPREL
+ = 0x00008000, // The section contains data referenced through the global
+ // pointer. This is not valid for M68K.
+ IMAGE_SCN_MEM_SYSHEAP
+ = 0x00010000, // The section uses System heap. This is valid only for
+ // M68K (Mac OS memory management).
+ IMAGE_SCN_MEM_PURGEABLE
+ = 0x00020000, // The section can be released from RAM. This is valid
+ // only for M68K (Mac OS memory management).
+ IMAGE_SCN_MEM_16BIT
+ = 0x00020000, // The section contains 16-bit code. This is valid only
+ // for non-M68K architectures where it makes sense (I386,
+ // THUMB, MIPS16, MIPSFPU16, ...).
+ IMAGE_SCN_MEM_LOCKED
+ = 0x00040000, // The section is locked/resident and prevented from being
+ // moved in RAM. This is valid only for M68K (Mac OS
+ // memory management) and I386 object files (e.g. for
+ // building Linear Executables).
+ IMAGE_SCN_MEM_PRELOAD
+ = 0x00080000, // The section is preloaded to RAM. This is valid only for
+ // M68K (Mac OS memory management) and I386 object files
+ // (e.g. for building Linear Executables).
+ IMAGE_SCN_ALIGN_1BYTES = 0x00100000, // Align data on a 1-byte boundary.
+ // This is valid only for object files.
+ IMAGE_SCN_ALIGN_2BYTES = 0x00200000, // Align data on a 2-byte boundary.
+ // This is valid only for object files.
+ IMAGE_SCN_ALIGN_4BYTES = 0x00300000, // Align data on a 4-byte boundary.
+ // This is valid only for object files.
+ IMAGE_SCN_ALIGN_8BYTES = 0x00400000, // Align data on a 8-byte boundary.
+ // This is valid only for object files.
+ IMAGE_SCN_ALIGN_16BYTES
+ = 0x00500000, // Align data on a 16-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_32BYTES
+ = 0x00600000, // Align data on a 32-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_64BYTES
+ = 0x00700000, // Align data on a 64-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_128BYTES
+ = 0x00800000, // Align data on a 128-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_256BYTES
+ = 0x00900000, // Align data on a 256-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_512BYTES
+ = 0x00A00000, // Align data on a 512-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_1024BYTES
+ = 0x00B00000, // Align data on a 1024-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_2048BYTES
+ = 0x00C00000, // Align data on a 2048-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_4096BYTES
+ = 0x00D00000, // Align data on a 4096-byte boundary. This is valid only
+ // for object files.
+ IMAGE_SCN_ALIGN_8192BYTES
+ = 0x00E00000, // Align data on a 8192-byte boundary. This is valid only
+ // for object files.
+ // RESERVED = 0x00F00000, //
+ // Reserved.
+ IMAGE_SCN_LNK_NRELOC_OVFL
+ = 0x01000000, // The section contains extended relocations. This is
+ // valid only for object files.
+ IMAGE_SCN_MEM_DISCARDABLE
+ = 0x02000000, // The section can be discarded as needed.
+ IMAGE_SCN_MEM_NOT_CACHED = 0x04000000, // The section cannot be cached.
+ IMAGE_SCN_MEM_NOT_PAGED = 0x08000000, // The section cannot be paged.
+ IMAGE_SCN_MEM_SHARED
+ = 0x10000000, // The section is shareable. When used with a DLL, the
+ // data in this section will be shared among all processes
+ // using the DLL.
+ IMAGE_SCN_MEM_EXECUTE = 0x20000000, // The section is executable.
+ IMAGE_SCN_MEM_READ = 0x40000000, // The section is readable.
+#ifdef _MSC_VER
+ IMAGE_SCN_MEM_WRITE = 0x80000000U // The section is writeable. (0x80000000U)
+#else
+ IMAGE_SCN_MEM_WRITE = -2147483648 // The section is writeable. (0x80000000U)
+#endif
+} SectionCharacteristics;
+
+// Used only when IMAGE_ROM_OPTIONAL_HEADER is present
+typedef enum {
+ STYP_DUMMY = 0x00000001, // Dummy
+ STYP_TEXT = 0x00000020, // Text
+ STYP_DATA = 0x00000040, // Data
+ STYP_SBSS = 0x00000080, // GP Uninit Data
+ STYP_RDATA = 0x00000100, // Readonly Data
+ STYP_SDATA = 0x00000200, // GP Init Data
+ STYP_BSS = 0x00000400, // Uninit Data
+ STYP_UCODE = 0x00000800, // UCode
+ STYP_LIT8 = 0x08000000, // Literal 8
+ STYP_LIT4 = 0x10000000, // Literal 4
+ S_NRELOC_OVFL = 0x20000000, // Non-Relocatable overlay
+ STYP_LIB = 0x40000000, // Library
+#ifdef _MSC_VER
+ STYP_INIT = 0x80000000U // Init Code (0x80000000U)
+#else
+ STYP_INIT = -2147483648 // Init Code (0x80000000U)
+#endif
+} ROMSectionCharacteristics;
+
+#pragma pack(push, 1)
+
+// Quoting pecoff_v8.docx: "Entries in the section table are numbered starting
+// from one (1)".
+typedef struct {
+ uint8_t Name[SECTION_NAME_SIZE]; // TODO: Should we use char instead?
+ union {
+ uint32_t PhysicalAddress; // same value as next field
+ uint32_t VirtualSize;
+ } Misc;
+ uint32_t VirtualAddress;
+ uint32_t SizeOfRawData;
+ uint32_t PointerToRawData;
+ uint32_t PointerToRelocations; // always zero in executables
+ uint32_t PointerToLinenumbers; // deprecated
+ uint16_t NumberOfRelocations;
+ uint16_t NumberOfLinenumbers; // deprecated
+ uint32_t
+ Characteristics; // SectionCharacteristics or ROMSectionCharacteristics
+} IMAGE_SECTION_HEADER;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/include/libpe/types_resources.h b/include/libpe/types_resources.h
new file mode 100644
index 00000000..73ddd6f3
--- /dev/null
+++ b/include/libpe/types_resources.h
@@ -0,0 +1,73 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2023 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#ifndef LIBPE_TYPES_RESOURCES_H
+#define LIBPE_TYPES_RESOURCES_H
+
+#include "dir_resources.h"
+#include "error.h"
+
+#include
+
+typedef enum {
+ LIBPE_RDT_LEVEL1 = 1,
+ LIBPE_RDT_LEVEL2 = 2,
+ LIBPE_RDT_LEVEL3 = 3
+} pe_resource_level_e;
+
+typedef enum {
+ LIBPE_RDT_RESOURCE_DIRECTORY = 1,
+ LIBPE_RDT_DIRECTORY_ENTRY = 2,
+ LIBPE_RDT_DATA_STRING = 3,
+ LIBPE_RDT_DATA_ENTRY = 4
+} pe_resource_node_type_e;
+
+typedef struct pe_resource_node {
+ uint16_t depth;
+ uint32_t dirLevel; // pe_resouces_level_e
+ pe_resource_node_type_e type;
+ char *name;
+ union {
+ void *raw_ptr; // We are allowed to rely on type-punning in C99, but not
+ // in C++.
+ IMAGE_RESOURCE_DIRECTORY
+ *resourceDirectory; // type == LIBPE_RDT_RESOURCE_DIRECTORY
+ IMAGE_RESOURCE_DIRECTORY_ENTRY
+ *directoryEntry; // type == LIBPE_RDT_DIRECTORY_ENTRY
+ IMAGE_RESOURCE_DATA_STRING_U
+ *dataString; // type == LIBPE_RDT_DATA_STRING
+ IMAGE_RESOURCE_DATA_ENTRY *dataEntry; // type == LIBPE_RDT_DATA_ENTRY
+ } raw;
+ struct pe_resource_node *parentNode; // Points to the parent node, if any.
+ struct pe_resource_node *childNode; // Points to the 1st child node, if any.
+ struct pe_resource_node
+ *nextNode; // Points to the next sibling node, if any.
+} pe_resource_node_t;
+
+typedef struct {
+ pe_err_e err;
+ void *resource_base_ptr; // A pointer to the beggining of the
+ // `IMAGE_RESOURCE_DIRECTORY`.
+ pe_resource_node_t *root_node;
+} pe_resources_t;
+
+#endif
+
diff --git a/lib/libpe/include/libpe/utils.h b/include/libpe/utils.h
similarity index 59%
rename from lib/libpe/include/libpe/utils.h
rename to include/libpe/utils.h
index 9b6a55c9..c1f21426 100644
--- a/lib/libpe/include/libpe/utils.h
+++ b/include/libpe/utils.h
@@ -22,6 +22,8 @@
#ifndef LIBPE_UTILS_H
#define LIBPE_UTILS_H
+#include "macros.h"
+
#include
#include
@@ -30,34 +32,40 @@ extern "C" {
#endif
#ifndef pe_utils_min
-// IMPORTANT: Do not pass expressions as arguments because this macro evaluates each argument more than once!
-# define pe_utils_min(a, b) (((a) < (b)) ? (a) : (b))
+// IMPORTANT: Do not pass expressions as arguments because this macro evaluates
+// each argument more than once!
+#define pe_utils_min(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef pe_utils_max
-// IMPORTANT: Do not pass expressions as arguments because this macro evaluates each argument more than once!
-# define pe_utils_max(a, b) (((a) > (b)) ? (a) : (b))
+// IMPORTANT: Do not pass expressions as arguments because this macro evaluates
+// each argument more than once!
+#define pe_utils_max(a, b) (((a) > (b)) ? (a) : (b))
#endif
-bool pe_utils_str_ends_with(const char* text, const char* pattern);
-char *pe_utils_str_inplace_ltrim(char *str);
-char *pe_utils_str_inplace_rtrim(char *str);
-char *pe_utils_str_inplace_trim(char *str);
-char *pe_utils_str_array_join(char *strings[], size_t count, char delimiter);
-void pe_utils_str_widechar2ascii(char *output, size_t output_size, const char *widechar, size_t widechar_count);
+PEAPI bool pe_utils_str_ends_with(const char *text, const char *pattern);
+char *pe_utils_str_inplace_ltrim(char *str);
+char *pe_utils_str_inplace_rtrim(char *str);
+PEAPI char *pe_utils_str_inplace_trim(char *str);
+PEAPI char *pe_utils_str_array_join(char *strings[], size_t count,
+ char delimiter);
+PEAPI void pe_utils_str_widechar2ascii(char *output, size_t output_size,
+ const char *widechar,
+ size_t widechar_count);
// FIX: Don't need this.
#if 0
int pe_utils_round_up(int num_to_round, int multiple);
#endif
-int pe_utils_is_file_readable(const char *path);
+PEAPI int pe_utils_is_file_readable(const char *path);
// IMPORTANT: This is not thread-safe - not reentrant.
-const char *pe_utils_get_homedir(void);
+PEAPI const char *pe_utils_get_homedir(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif
+
diff --git a/include/pev_api.h b/include/readpe/api.h
similarity index 72%
rename from include/pev_api.h
rename to include/readpe/api.h
index 096f362d..f027102b 100644
--- a/include/pev_api.h
+++ b/include/readpe/api.h
@@ -1,10 +1,10 @@
/* vim :set ts=4 sw=4 sts=4 et : */
/*
- pev - the PE file analyzer toolkit
+ readpe - the PE file analyzer toolkit
- pev_api.h - Symbols and APIs to be used by all plugins.
+ api.h - Readpe API that plugins can use to access internal functions.
- Copyright (C) 2014 pev authors
+ Copyright (C) 2012 - 2026 readpe authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,21 +35,28 @@
*/
#pragma once
+#ifndef READPE_API_H
+#define READPE_API_H
#ifdef __cplusplus
extern "C" {
#endif
-struct _output_plugin_api; // from output_plugin.h
+struct readpe_api;
+struct readpe_output_api;
-typedef struct _pev_api_t {
- struct _output_plugin_api *output;
-} pev_api_t;
+/* This is the api that is provided by readpe and thus contains symbols
+ * and functions that plugins can call from the main executable.
+ */
+struct readpe_api {
+ const unsigned int version;
+ const struct readpe_output_api *output;
+};
-pev_api_t *pev_api_ptr(void);
+struct readpe_api *readpe_api_ptr(void);
#ifdef __cplusplus
-} //extern "C"
+} // extern "C"
#endif
-
+#endif // READPE_API_H
diff --git a/include/readpe/config.h b/include/readpe/config.h
new file mode 100644
index 00000000..09a264c1
--- /dev/null
+++ b/include/readpe/config.h
@@ -0,0 +1,140 @@
+/* vim :set ts=4 sw=4 sts=4 et : */
+/*
+ readpe - the PE file analyzer toolkit
+
+ config.h
+
+ Copyright (C) 2013 - 2026 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#pragma once
+#ifndef READPE_CONFIG_H
+#define READPE_CONFIG_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct readpe_config; // Forward declaration.
+
+typedef bool (*readpe_config_parse_callback_t)(
+ struct readpe_config *const config, const char *name, const char *value);
+typedef void (*readpe_config_cleanup_callback_t)(void *data);
+
+struct readpe_config_certificates {
+ void *output_path;
+ void *format;
+};
+
+struct readpe_config_resource {
+ bool info;
+ bool names;
+ bool statistics;
+ bool tree;
+};
+
+struct readpe_config_section {
+ char *name;
+ unsigned int index;
+};
+
+struct readpe_config_string {
+ int min_length;
+ int offset;
+ int section;
+};
+
+struct readpe_config {
+ const char *plugins_path;
+ char *format;
+
+ bool all;
+ bool file_version;
+ bool list;
+ bool verbose;
+
+ int mode;
+ int context;
+
+ struct readpe_config_certificates certificates;
+ struct readpe_config_resource resource;
+ struct readpe_config_section section;
+ struct readpe_config_string string;
+
+ struct {
+ readpe_config_parse_callback_t parse_callback;
+ readpe_config_cleanup_callback_t cleanup_callback;
+ void *data;
+ } user_defined;
+ // TODO: Add functionality
+ // Plugins should be able to register a config struct
+ void *plugins;
+};
+
+const char *readpe_plugins_path(void);
+
+int readpe_load_config(struct readpe_config *const config);
+void readpe_cleanup_config(struct readpe_config *const config);
+
+// Plugins should use these so changes to the structs don't lead to page errors
+char *readpe_get_plugins_path(void);
+char *readpe_get_format(void);
+bool readpe_get_all(void);
+bool readpe_get_file_version(void);
+bool readpe_get_list(void);
+bool readpe_get_verbose(void);
+int readpe_get_mode(void);
+int readpe_get_context(void);
+
+void *readpe_get_certificates_output_path(void);
+void *readpe_get_certificates_format(void);
+
+bool readpe_get_resource_info_enabled(void);
+bool readpe_get_resource_names_enabled(void);
+bool readpe_get_resource_statistics_enabled(void);
+bool readpe_get_resource_tree_enabled(void);
+
+char *readpe_get_section_name(void);
+unsigned int readpe_get_section_index(void);
+
+int readpe_get_string_min_length(void);
+int readpe_get_string_offset(void);
+int readpe_get_string_section(void);
+
+void readpe_set_all(bool all);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/include/plugin.h b/include/readpe/helper.h
similarity index 59%
rename from include/plugin.h
rename to include/readpe/helper.h
index 74149218..7aedf82a 100644
--- a/include/plugin.h
+++ b/include/readpe/helper.h
@@ -1,10 +1,10 @@
-/* vim :set ts=4 sw=4 sts=4 et : */
+/* vim: set ts=4 sw=4 noet: */
/*
pev - the PE file analyzer toolkit
- plugin.h - Plugin API that every plugin MUST implement.
+ common.h - common defitions for the readpe toolkit.
- Copyright (C) 2012 - 2014 pev authors
+ Copyright (C) 2013 - 2025 readpe authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,25 +35,49 @@
*/
#pragma once
+#ifndef READPE_COMMON_H
+#define READPE_COMMON_H
+#include
#include
+#include
+#include
#ifdef __cplusplus
extern "C" {
#endif
-struct _pev_api_t;
+#define UNUSED(x) (void) (sizeof((x)))
-typedef int (*plugin_loaded_fn_t)(void);
-typedef int (*plugin_initialize_fn_t)(const struct _pev_api_t *api);
-typedef void (*plugin_shutdown_fn_t)(void);
-typedef void (*plugin_unloaded_fn_t)(void);
+#define EXIT_ERROR(msg) \
+ { \
+ _exit_error_(__FILE__, __LINE__, msg); \
+ }
-int plugin_loaded(void);
-int plugin_initialize(const struct _pev_api_t *api);
-void plugin_shutdown(void);
-void plugin_unloaded(void);
+#define MAX_MSG 81
+#define MAX_PATH 256
+
+#ifndef VERSION
+#define VERSION "1.0"
+#endif
+
+struct readpe_config;
+
+void *malloc_s(size_t size);
+void *calloc_s(size_t nmemb, size_t size);
+bool str2bool(const char *const str);
+void readpe_initialize(struct readpe_config *config);
+void readpe_finalize(struct readpe_config *config);
+
+static inline void _exit_error_(const char *file, int line, const char *message)
+{
+ fprintf(stderr, "Error: %s [at %s:%d]\n", message, file, line);
+ exit(EXIT_FAILURE);
+}
#ifdef __cplusplus
-} //extern "C"
+} // extern "C"
+#endif
+
#endif
+
diff --git a/include/output.h b/include/readpe/output.h
similarity index 53%
rename from include/output.h
rename to include/readpe/output.h
index b762729d..7e551630 100644
--- a/include/output.h
+++ b/include/readpe/output.h
@@ -1,10 +1,10 @@
/* vim :set ts=4 sw=4 sts=4 et : */
/*
- pev - the PE file analyzer toolkit
+ readpe - the PE file analyzer toolkit
output.h - Symbols and APIs to be used to output data in multiple formats.
- Copyright (C) 2012 - 2014 pev authors
+ Copyright (C) 2012 - 2026 readpe authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,78 +35,82 @@
*/
#pragma once
+#ifndef READPE_OUTPUT_H
+#define READPE_OUTPUT_H
+
+#include
+#include
#ifdef __cplusplus
extern "C" {
#endif
-#include
-#include
-
typedef int format_id_t;
+// Forward declaration
+struct format;
+struct readpe_config;
+
typedef enum {
- OUTPUT_TYPE_SCOPE_UNKNOWN = 0,
- OUTPUT_TYPE_SCOPE_OPEN = 1,
- OUTPUT_TYPE_SCOPE_CLOSE = 2,
- OUTPUT_TYPE_ATTRIBUTE = 3
+ OUTPUT_TYPE_SCOPE_UNKNOWN = 0,
+ OUTPUT_TYPE_SCOPE_OPEN = 1,
+ OUTPUT_TYPE_SCOPE_CLOSE = 2,
+ OUTPUT_TYPE_ATTRIBUTE = 3
} output_type_e;
typedef enum {
- OUTPUT_SCOPE_TYPE_UNKNOWN = 0,
- OUTPUT_SCOPE_TYPE_DOCUMENT = 1,
- OUTPUT_SCOPE_TYPE_OBJECT = 2,
- OUTPUT_SCOPE_TYPE_ARRAY = 3
+ OUTPUT_SCOPE_TYPE_UNKNOWN = 0,
+ OUTPUT_SCOPE_TYPE_DOCUMENT = 1,
+ OUTPUT_SCOPE_TYPE_OBJECT = 2,
+ OUTPUT_SCOPE_TYPE_ARRAY = 3
} output_scope_type_e;
typedef struct {
- char *name;
+ char *name;
output_scope_type_e type;
- uint16_t depth;
+ uint16_t depth;
output_scope_type_e parent_type;
} output_scope_t;
-struct _format_t; // Forward declaration
-
-typedef void (*output_fn)(
- const struct _format_t *format,
- const output_type_e type,
- const output_scope_t *scope,
- const char *key,
- const char *value);
+typedef void (*output_fn)(const struct format *format, const output_type_e type,
+ const output_scope_t *scope, const char *key,
+ const char *value);
-typedef char * (*escape_fn)(
- const struct _format_t *format,
- const char *str);
+typedef char *(*escape_fn)(const struct format *format, const char *str);
-typedef char * const entity_t;
-typedef char ** const entity_table_t;
+typedef const char *entity_t;
+typedef const entity_t *entity_table_t;
-typedef struct _format_t {
- const format_id_t id;
- const char *name;
- const output_fn output_fn;
- const escape_fn escape_fn;
+typedef struct format {
+ const format_id_t id;
+ const char *name;
+ const output_fn output_fn;
+ const escape_fn escape_fn;
const entity_table_t entities_table;
} format_t;
-void output_init(void); // IMPORTANT: Requires the text plugin to be already loaded.
-void output_term(void);
-const char *output_cmdline(void);
-void output_set_cmdline(int argc, char *argv[]);
+void output_init(struct readpe_config *config);
+void output_term(void);
+const char *output_cmdline(void);
+void output_set_cmdline(int argc, char *argv[]);
const format_t *output_format(void);
const format_t *output_parse_format(const char *format_name);
-void output_set_format(const format_t *format);
-int output_set_format_by_name(const char *format_name);
+void output_set_format(const format_t *format);
+int output_set_format_by_name(const char *format_name);
size_t output_available_formats(char *buffer, size_t size, char separator);
-void output_open_document(void);
-void output_open_document_with_name(const char *document_name);
-void output_close_document(void);
-void output_open_scope(const char *scope_name, output_scope_type_e type);
-void output_close_scope(void);
-void output(const char *key, const char *value);
-void output_keyval(const char *key, const char *value);
+void output_open_document(void);
+void output_open_document_with_name(const char *document_name);
+void output_close_document(void);
+void output_open_scope(const char *scope_name, output_scope_type_e type);
+void output_close_scope(void);
+void output(const char *key, const char *value);
+void output_keyval(const char *key, const char *value);
+
+struct readpe_output_plugin *get_default_output_plugin(void);
#ifdef __cplusplus
-} //extern "C"
+} // extern "C"
#endif
+
+#endif
+
diff --git a/include/output_plugin.h b/include/readpe/plugin.h
similarity index 52%
rename from include/output_plugin.h
rename to include/readpe/plugin.h
index 9dc3de65..bfc00292 100644
--- a/include/output_plugin.h
+++ b/include/readpe/plugin.h
@@ -1,10 +1,10 @@
/* vim :set ts=4 sw=4 sts=4 et : */
/*
- pev - the PE file analyzer toolkit
+ readpe - the PE file analyzer toolkit
- output_plugin.h - Symbols and APIs to be used by output plugins.
+ plugin.h - Plugin API that every plugin must implement.
- Copyright (C) 2014 pev authors
+ Copyright (C) 2012 - 2026 readpe authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,47 +35,51 @@
*/
#pragma once
-
-#include "plugin.h"
-#include "output.h"
+#ifndef READPE_PLUGIN_H
+#define READPE_PLUGIN_H
#ifdef __cplusplus
extern "C" {
#endif
-//
-// Type definitions
-//
-
-// All definitions are in output.h
-
-//
-// Indentation macros
-//
-
-#define INDENT_TAB_SIZE 4
-#define INDENT_COLUMNS_(level) (int)((int)(level) * (int)INDENT_TAB_SIZE)
-#define INDENT_FORMAT_ "%*s"
-#define INDENT_ARGS_(level) INDENT_COLUMNS_(level), ""
-#define INDENT(level, format) INDENT_FORMAT_ format, INDENT_ARGS_(level)
+// enum readpe_plugin_type_id {
+#define readpe_plugin_type_generic 0
+#define readpe_plugin_type_output 1
+// };
-//
-// Public API specific for output plugins.
-//
-
-typedef struct _output_plugin_api {
- const char * (* output_cmdline)(void);
- int (* output_plugin_register_format)(const format_t *format);
- void (* output_plugin_unregister_format)(const format_t *format);
- size_t (* escape_count_chars_ex)(const char *str, size_t len, const entity_table_t entities);
- char * (* escape_ex)(const char *str, const entity_table_t entities);
- char * (* escape_ex_quoted)(const char *str, const entity_table_t entities);
- char * (* escape)(const format_t *format, const char *str);
- char * (* escape_quoted)(const format_t *format, const char *str);
-} output_plugin_api_t;
+#ifdef _WIN32
+#define READPE_API __declspec(dllexport)
+#else
+#define READPE_API
+#endif
-output_plugin_api_t *output_plugin_api_ptr(void);
+struct readpe_api;
+
+typedef int (*plugin_loaded_fn)(void);
+typedef int (*plugin_initialize_fn)(const struct readpe_api *api);
+typedef void (*plugin_shutdown_fn)(void);
+typedef void (*plugin_unloaded_fn)(void);
+
+/* Every plugin shared object should export a structure like this.
+ * The C standard does not allow for dynamic loading of functions
+ * Hence why dylib_get_symbol does not return a void(*)(void) but a void*!
+ *
+ * Every plugin type shall include this struct as its first element.
+ * This allows that the memory address pointing to the struct
+ * is also pointing to the plugin type enum.
+ * This can then be used to cast up a generic plugin back to
+ * it's specific type if need should arise.
+ */
+struct readpe_plugin {
+ const int type_id;
+ const plugin_loaded_fn loaded;
+ const plugin_initialize_fn initialize;
+ const plugin_shutdown_fn shutdown;
+ const plugin_unloaded_fn unloaded;
+};
#ifdef __cplusplus
-}
+} // extern "C"
#endif
+#endif // READPE_PLUGIN_H
+
diff --git a/include/readpe/plugin/output.h b/include/readpe/plugin/output.h
new file mode 100644
index 00000000..0add7490
--- /dev/null
+++ b/include/readpe/plugin/output.h
@@ -0,0 +1,102 @@
+/* vim :set ts=4 sw=4 sts=4 et : */
+/*
+ readpe - the PE file analyzer toolkit
+
+ output_plugin.h - Symbols and APIs to be used by output plugins.
+
+ Copyright (C) 2014 - 2025 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#pragma once
+#ifndef READPE_OUTPUT_PLUGIN_H
+#define READPE_OUTPUT_PLUGIN_H
+
+#include "../output.h"
+#include "../plugin.h"
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Type definitions
+//
+
+// All definitions are in output.h
+
+//
+// Indentation macros
+//
+
+#define INDENT_TAB_SIZE 4
+#define INDENT_COLUMNS_(level) (int) ((int) (level) * (int) INDENT_TAB_SIZE)
+#define INDENT_FORMAT_ "%*s"
+#define INDENT_ARGS_(level) INDENT_COLUMNS_(level), ""
+#define INDENT(level, format) INDENT_FORMAT_ format, INDENT_ARGS_(level)
+
+//
+// Public API specific for output plugins.
+//
+
+typedef const char *(*output_plugin_cmdline_fn_t)(void);
+typedef int (*output_plugin_register_format_fn_t)(const format_t *format);
+typedef void (*output_plugin_unregister_format_fn_t)(const format_t *format);
+typedef size_t (*output_plugin_escape_count_chars_ex_fn_t)(
+ const char *str, size_t len, const entity_table_t entities);
+typedef char *(*output_plugin_escape_fn_t)(const format_t *format,
+ const char *str);
+typedef char *(*output_plugin_escape_ex_fn_t)(const char *str,
+ const entity_table_t entities);
+
+struct readpe_output_api {
+ const output_plugin_cmdline_fn_t cmdline;
+ const output_plugin_register_format_fn_t register_format;
+ const output_plugin_unregister_format_fn_t unregister_format;
+ const output_plugin_escape_fn_t escape;
+ const output_plugin_escape_ex_fn_t escape_ex;
+ const output_plugin_escape_fn_t escape_quoted;
+ const output_plugin_escape_ex_fn_t escape_ex_quoted;
+ const output_plugin_escape_count_chars_ex_fn_t escape_count_chars_ex;
+};
+
+struct readpe_output_plugin {
+ const struct readpe_plugin readpe_plugin;
+ const struct format *format;
+};
+
+struct readpe_output_api *readpe_output_api_ptr(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // READPE_OUTPUT_PLUGIN_H
+
diff --git a/include/readpe/readpe.h b/include/readpe/readpe.h
new file mode 100644
index 00000000..85e6cf69
--- /dev/null
+++ b/include/readpe/readpe.h
@@ -0,0 +1,116 @@
+/* vim: set ts=4 sw=4 noet: */
+/*
+ readpe - the PE file analyzer toolkit
+
+ readpe.h
+
+ Copyright (C) 2023 - 2026 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#pragma once
+#ifndef READPE_READPE_H
+#define READPE_READPE_H
+
+#include "libpe/context.h"
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct readpe_config;
+
+typedef enum CERT_FORMAT {
+ CERT_FORMAT_X509 = 1,
+ CERT_FORMAT_PEM = 2,
+ CERT_FORMAT_DER = 3
+} cert_format_e;
+
+typedef enum HASH_ALGORITHMS {
+ HASH_MD5,
+ HASH_SHA1,
+ HASH_SHA256,
+ HASH_SSDEEP,
+ HASH_IMPHASH,
+ HASH_ALL = 999
+} hash_alghorithms_e;
+
+// ------------------------------------------------------------------------- //
+
+void print_dos_header(pe_ctx_t *ctx);
+void print_coff_header(pe_ctx_t *ctx);
+void print_optional_header(pe_ctx_t *ctx);
+
+void print_section(pe_ctx_t *ctx, IMAGE_SECTION_HEADER *section,
+ const char *section_name, struct readpe_config *config);
+void print_section_by_name(pe_ctx_t *ctx, const char *section_name,
+ struct readpe_config *config);
+void print_sections(pe_ctx_t *ctx, struct readpe_config *config);
+void print_sections_list(pe_ctx_t *ctx, struct readpe_config *config);
+
+IMAGE_DATA_DIRECTORY **get_pe_directories(pe_ctx_t *ctx);
+void print_directories(pe_ctx_t *ctx);
+void print_directory_list(pe_ctx_t *ctx, bool verbose);
+void print_imports(pe_ctx_t *ctx);
+void print_exports(pe_ctx_t *ctx);
+void print_dependencies(pe_ctx_t *ctx);
+
+void print_resources(pe_ctx_t *ctx);
+void print_resources_list(pe_ctx_t *ctx);
+void print_resources_tree(pe_ctx_t *ctx);
+void print_resources_stats(pe_ctx_t *ctx);
+void print_file_version(pe_ctx_t *ctx);
+void extract_all_resources(pe_ctx_t *ctx, bool named);
+
+void print_hash(pe_ctx_t *ctx, const struct readpe_config *config);
+void print_content_hash(pe_ctx_t *ctx);
+void print_dos_header_hash(pe_ctx_t *ctx);
+void print_coff_header_hash(pe_ctx_t *ctx);
+void print_optional_header_hash(pe_ctx_t *ctx);
+void print_sections_hash(pe_ctx_t *ctx);
+void print_section_hash_by_name(pe_ctx_t *ctx, char *name);
+void print_section_hash_by_index(pe_ctx_t *ctx, unsigned int index);
+
+void pe_scan(pe_ctx_t *ctx, bool verbose);
+bool stack_cookies(pe_ctx_t *ctx);
+void print_securities(pe_ctx_t *ctx);
+void print_certificates(pe_ctx_t *ctx, const char *format, const char *out);
+void print_certificates_info(pe_ctx_t *ctx, const char *format, const char *out,
+ bool verbose);
+
+// ------------------------------------------------------------------------- //
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/include/readpe/settings.h b/include/readpe/settings.h
new file mode 100644
index 00000000..044551f9
--- /dev/null
+++ b/include/readpe/settings.h
@@ -0,0 +1,122 @@
+/* vim: set ts=4 sw=4 noet: */
+/*
+ readpe - the PE file analyzer toolkit
+
+ Copyright (C) 2025 - 2026 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#pragma once
+#ifndef READPE_SETTINGS_H
+#define READPE_SETTINGS_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct readpe_settings_certificates {
+ void *output_path;
+ void *format;
+};
+
+struct readpe_settings_resource {
+ bool info;
+ bool names;
+ bool statistics;
+ bool tree;
+};
+
+struct readpe_settings_section {
+ char *name;
+ unsigned int index;
+};
+
+struct readpe_settings_string {
+ int min_length;
+ int offset;
+ int section;
+};
+
+struct readpe_settings {
+ char *plugins_path;
+ char *format;
+
+ bool all;
+ bool file_version;
+ bool list;
+ bool verbose;
+
+ int mode;
+ int context;
+
+ struct readpe_settings_certificates *certificates;
+ struct readpe_settings_resource *resource;
+ struct readpe_settings_section *section;
+ struct readpe_settings_string *string;
+
+ // TODO: Add functionality
+ // Plugins should be able to register a settings struct
+ void *plugins[];
+};
+
+// Plugins should use these so changes to the structs don't lead to page errors
+char *readpe_get_plugins_path(void);
+char *readpe_get_format(void);
+bool readpe_get_all(void);
+bool readpe_get_file_version(void);
+bool readpe_get_list(void);
+bool readpe_get_verbose(void);
+int readpe_get_mode(void);
+int readpe_get_context(void);
+
+void *readpe_get_certificates_output_path(void);
+void *readpe_get_certificates_format(void);
+
+bool readpe_get_resource_info_enabled(void);
+bool readpe_get_resource_names_enabled(void);
+bool readpe_get_resource_statistics_enabled(void);
+bool readpe_get_resource_tree_enabled(void);
+
+char *readpe_get_section_name(void);
+unsigned int readpe_get_section_index(void);
+
+int readpe_get_string_min_length(void);
+int readpe_get_string_offset(void);
+int readpe_get_string_section(void);
+
+void readpe_set_all(bool all);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/lib/libudis86/udis86.h b/include/udis86.h
similarity index 100%
rename from lib/libudis86/udis86.h
rename to include/udis86.h
diff --git a/lib/compat/CMakeLists.txt b/lib/compat/CMakeLists.txt
new file mode 100644
index 00000000..558f1daa
--- /dev/null
+++ b/lib/compat/CMakeLists.txt
@@ -0,0 +1,49 @@
+
+set(HEADERS
+ compat.h
+)
+
+set(SOURCES
+ strlcat.c
+ asprintf.c
+)
+
+if(MSVC)
+ list(APPEND HEADERS
+ include/getopt.h
+ include/sys/queue.h
+ include/unistd.h
+ )
+ list(APPEND SOURCES
+ getline.c
+ getopt.c
+ strndup.c
+ )
+endif()
+
+add_library(compat OBJECT ${HEADERS} ${SOURCES})
+
+set(COMPAT_INCLUDE_DIRS
+ "${CMAKE_SOURCE_DIR}/include"
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+)
+
+if(MSVC)
+ target_link_libraries(compat PUBLIC dl)
+ list(APPEND COMPAT_INCLUDE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}/include"
+ )
+
+endif()
+
+target_include_directories(compat PRIVATE
+ "${COMPAT_INCLUDE_DIRS}"
+)
+set_target_properties(compat PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${COMPAT_INCLUDE_DIRS}"
+)
+
+add_library(readpe::compat ALIAS compat)
+
+# TODO: Option to install compatibility headers
+
diff --git a/lib/compat/asprintf.c b/lib/compat/asprintf.c
new file mode 100644
index 00000000..1ab65b7b
--- /dev/null
+++ b/lib/compat/asprintf.c
@@ -0,0 +1,161 @@
+/* BSD 3-Clause License
+ *
+ * Copyright (c) 2018, Thomas Gamper
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * asprintf
+ *
+ * An implementation of
+ * [vasprintf](http://man7.org/linux/man-pages/man3/asprintf.3.html) and
+ * [asprintf](http://man7.org/linux/man-pages/man3/asprintf.3.html) for the
+ * Microsoft Windows platform. This implementation takes advantage of the
+ * security enhancements provided by the [Microsoft CRT]
+ * (https://docs.microsoft.com/de-de/cpp/c-runtime-library/security-features-in-the-crt).
+ * Works with all Visual C++ versions starting from Visual C++ 2008.
+ */
+
+#include "compat.h"
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(WIN32) || defined(l_WIN32) || defined(WIN64) || defined(_WIN64)
+
+#include
+
+#if _MSC_VER < 1800
+#undef va_copy
+#define va_copy(dst, src) (dst = src)
+#endif
+
+int vasprintf(char **strp, const char *fmt, va_list ap)
+{
+ va_list ap_copy;
+ int formattedLength, actualLength;
+ size_t requiredSize;
+
+ // be paranoid
+ *strp = NULL;
+
+ // copy va_list, as it is used twice
+ va_copy(ap_copy, ap);
+
+ // compute length of formatted string, without NULL terminator
+ formattedLength = _vscprintf(fmt, ap_copy);
+ va_end(ap_copy);
+
+ // bail out on error
+ if (formattedLength < 0) {
+ return -1;
+ }
+
+ // allocate buffer, with NULL terminator
+ requiredSize = ((size_t) formattedLength) + 1;
+ *strp = (char *) malloc(requiredSize);
+
+ // bail out on failed memory allocation
+ if (*strp == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ // write formatted string to buffer, use security hardened _s function
+ actualLength = vsnprintf_s(*strp, requiredSize, requiredSize - 1, fmt, ap);
+
+ // again, be paranoid
+ if (actualLength != formattedLength) {
+ free(*strp);
+ *strp = NULL;
+ errno = EOTHER;
+ return -1;
+ }
+
+ return formattedLength;
+}
+
+int asprintf(char **strp, const char *fmt, ...)
+{
+ int result;
+
+ va_list ap;
+ va_start(ap, fmt);
+ result = vasprintf(strp, fmt, ap);
+ va_end(ap);
+
+ return result;
+}
+
+#endif
+
+#ifdef USE_MY_ASPRINTF
+
+#include
+
+int asprintf(char **restrict strp, const char *restrict fmt, ...)
+{
+ char *p;
+ int size;
+ va_list args, args_safe;
+
+ va_start(args, fmt);
+ va_copy(args_safe, args);
+
+ // Just get the string size.
+ if ((size = vsnprintf(NULL, 0, fmt, args_safe)) < 0) {
+ va_end(args_safe);
+ va_end(args);
+ return -1;
+ }
+
+ if (! (p = malloc(size + 1))) {
+ va_end(args_safe);
+ va_end(args);
+ return -1;
+ }
+
+ vsprintf(*strp = p, fmt, args);
+
+ va_end(args_safe);
+ va_end(args);
+
+ return size;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/lib/compat/compat.h b/lib/compat/compat.h
new file mode 100644
index 00000000..c1bb4709
--- /dev/null
+++ b/lib/compat/compat.h
@@ -0,0 +1,130 @@
+/*
+ compat.h - Compatability header for libpe/readpe
+
+ Copyright (C) 2026 readpe authors
+
+ This file is part of readpe.
+
+ readpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ readpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+/*
+ * Note to naming of POSIX functions:
+ * MSVC has deprecated versions of POSIX functions
+ * Hence most functions have an added readpe_ prefix
+ */
+
+#pragma once
+#ifndef READPE_COMPAT_H
+#define READPE_COMPAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include
+
+#if defined(__unix__) || defined(__unix) \
+ || (defined(__APPLE__) && defined(__MACH__))
+#include
+#endif
+
+// ----------- //
+// -- POSIX -- //
+// ----------- //
+#if defined(_POSIX_VERSION)
+
+#include
+#include
+
+#define readpe_access access
+#define readpe_getline getline
+#define readpe_mkdir mkdir
+#define readpe_strdup strdup
+#define readpe_strncasecmp strncasecmp
+#define readpe_strndup strndup
+// -- POSIX END --
+
+// --------------- //
+// -- MSVC/UCRT -- //
+// --------------- //
+#elif defined(_MSC_VER)
+
+#include
+#include
+#include
+
+typedef unsigned int __mode_t;
+typedef __mode_t mode_t;
+typedef int64_t ssize_t;
+
+#define F_OK 0
+#define readpe_access _access
+#define readpe_mkdir(p, x) _mkdir(p)
+#define readpe_strncasecmp _strnicmp
+
+#if (__STDC_VERSION__ >= 202311L)
+#define readpe_strdup strdup
+#define readpe_strndup strndup
+#else
+#define readpe_strdup _strdup
+char *readpe_strndup(const char *src, size_t size);
+#endif
+
+ssize_t readpe_getline(char **restrict lineptr, size_t *restrict n,
+ FILE *restrict stream);
+// -- MSVC/UCRT END --
+
+// ---------------- //
+// -- Standard C -- //
+// ---------------- //
+#else
+
+#include
+
+ssize_t readpe_getline(char **restrict lineptr, size_t *restrict n,
+ FILE *restrict stream);
+
+#if (__STDC_VERSION__ >= 202311L)
+#define readpe_strdup strdup
+#define readpe_strndup strndup
+#else
+char *readpe_strndup(const char *src, size_t size);
+#endif
+
+/* TODO:
+ * readpe_strdup Requires C23 Standard
+ * readpe_access Doable by trying to open the file
+ * readpe_strncasecmp Doable but unicode makes this harder
+ * readpe_mkdir Impossible; Ironically easy with C++
+ */
+
+// As long as there are open TODOs we can't build this project on standard C
+#error "Your compiler or operating system is currently not supported"
+// -- Standard C END --
+#endif
+
+int asprintf(char **restrict strp, const char *restrict fmt, ...);
+int vasprintf(char **restrict strp, const char *restrict fmt, va_list ap);
+size_t bsd_strlcat(char *dst, const char *src, size_t siz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/lib/compat/getline.c b/lib/compat/getline.c
new file mode 100644
index 00000000..d0955f9e
--- /dev/null
+++ b/lib/compat/getline.c
@@ -0,0 +1,88 @@
+/*
+ getline.c - Standard C implimentation of POSIX getline function
+
+ Copyright (C) 2026 readpe authors
+
+ This file is part of readpe.
+
+ readpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ readpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#include "compat.h"
+
+#include
+
+// #include
+
+// #define ENOMEM 12
+// #define EINVAL 22
+
+#define LINELEN 80
+#define LINEMAX 0x7FFFFFFF
+
+ssize_t readpe_getline(char **restrict lineptr, size_t *restrict n,
+ FILE *restrict stream)
+{
+
+ if (lineptr == NULL || n == NULL) {
+ // errno = EINVAL;
+ return -1;
+ }
+
+ if (stream == NULL) {
+ // errno = EINVAL;
+ return -1;
+ }
+
+ if (*lineptr == NULL) {
+ *lineptr = malloc(LINELEN);
+ if (*lineptr == NULL) {
+ // errno = ENOMEM;
+ return -1;
+ }
+ *n = LINELEN;
+ }
+
+ size_t i;
+
+ for (i = 0; i < LINEMAX; ++i) {
+ int c = fgetc(stream);
+ if (c == EOF) {
+ return -1;
+ }
+
+ if (i > *n) {
+ char *tempptr = realloc(*lineptr, *n + LINELEN);
+ if (tempptr == NULL) {
+ // errno = ENOMEM;
+ return 1;
+ }
+
+ *lineptr = tempptr;
+ *n += LINELEN;
+ }
+
+ (*lineptr)[i] = (char) c;
+ if (c == '\n') {
+ ++i;
+ break;
+ }
+ }
+
+ return (ssize_t) i;
+}
+
+#undef LINELEN
+#undef LINEMAX
+
diff --git a/lib/compat/getopt.c b/lib/compat/getopt.c
new file mode 100644
index 00000000..948c72f5
--- /dev/null
+++ b/lib/compat/getopt.c
@@ -0,0 +1,1274 @@
+/* Getopt for GNU.
+NOTE: getopt is now part of the C library, so if you don't know what
+"Keep this file name-space clean" means, talk to drepper@gnu.org
+before changing it!
+Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001
+Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with the GNU C Library; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in .
+Ditto for AIX 3.2 and . */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+reject `defined (const)'. */
+# ifndef const
+# define const
+# endif
+#endif
+
+#include
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+actually compiling the library itself. This code is part of the GNU C
+Library, but also included in many other GNU distributions. Compiling
+and linking in this code is a waste when using the GNU C library
+(especially if it is a shared library). Rather than having every GNU
+program understand `configure --with-gnu-libc' and omit the object files,
+it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+# define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+contain conflicting prototypes for getopt. */
+# include
+# include
+#endif /* GNU C library. */
+
+#ifdef VMS
+# include
+# if HAVE_STRING_H - 0
+# include
+# endif
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages. */
+# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
+# include
+# ifndef _
+# define _(msgid) gettext (msgid)
+# endif
+# else
+# define _(msgid) (msgid)
+# endif
+# if defined _LIBC && defined USE_IN_LIBIO
+# include
+# endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+but it behaves differently for the user, since it allows the user
+to intersperse the options with the other arguments.
+
+As `getopt' works, it permutes the elements of ARGV so that,
+when it is done, all the options precede everything else. Thus
+all application programs are extended to handle flexible argument order.
+
+Setting the environment variable POSIXLY_CORRECT disables permutation.
+Then the behavior is completely standard.
+
+GNU application programs can use a third alternative mode in which
+they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+When `getopt' finds an option that takes an argument,
+the argument value is returned here.
+Also, when `ordering' is RETURN_IN_ORDER,
+each non-option ARGV-element is returned here. */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+This is used for communication to and from the caller
+and for communication between successive calls to `getopt'.
+
+On entry to `getopt', zero means this is the first call; initialize.
+
+When `getopt' returns -1, this is the index of the first of the
+non-option elements that the caller should itself scan.
+
+Otherwise, `optind' communicates from one call to the next
+how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+causes problems with re-calling getopt as programs generally don't
+know that. */
+
+int __getopt_initialized;
+
+/* The next char to be scanned in the option-element
+in which the last option character we returned was found.
+This allows us to pick up the scan where we left off.
+
+If this is zero, or a null string, it means resume the scan
+by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+This must be initialized on some systems to avoid linking in the
+system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+If the caller did not specify anything,
+the default is REQUIRE_ORDER if the environment variable
+POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+REQUIRE_ORDER means don't recognize them as options;
+stop option processing when the first non-option is seen.
+This is what Unix does.
+This mode of operation is selected by either setting the environment
+variable POSIXLY_CORRECT, or using `+' as the first character
+of the list of option characters.
+
+PERMUTE is the default. We permute the contents of ARGV as we scan,
+so that eventually all the non-options are at the end. This allows options
+to be given in any order, even with programs that were not written to
+expect this.
+
+RETURN_IN_ORDER is an option available to programs that were written
+to expect options and other ARGV-elements in any order and that care about
+the ordering of the two. We describe each non-option ARGV-element
+as if it were the argument of an option with character code 1.
+Using `-' as the first character of the list of option characters
+selects this mode of operation.
+
+The special argument `--' forces an end of option-scanning regardless
+of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+`--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+because there are many ways it can cause trouble.
+On some systems, it contains special magic macros that don't work
+in GCC. */
+# include
+# define my_index strchr
+#else
+
+#define HAVE_STRING_H 1
+# if HAVE_STRING_H
+# include
+# else
+# include
+# endif
+
+/* Avoid depending on library functions or files
+whose names are inconsistent. */
+
+#ifndef getenv
+extern char *getenv();
+#endif
+
+static char *
+my_index(str, chr)
+const char *str;
+int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *)str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+That was relevant to code that was here before. */
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
+/* gcc with -traditional declares the built-in strlen to return int,
+and has done so at least since version 2.4.5. -- rms. */
+extern int strlen(const char *);
+# endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+been skipped. `first_nonopt' is the index in ARGV of the first of them;
+`last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Stored original parameters.
+XXX This is no good solution. We should rather copy the args so
+that we can compare them later. But we must not use malloc(3). */
+extern int __libc_argc;
+extern char **__libc_argv;
+
+/* Bash 2.0 gives us an environment variable containing flags
+indicating ARGV elements that should not be considered arguments. */
+
+# ifdef USE_NONOPTION_FLAGS
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+# endif
+
+# ifdef USE_NONOPTION_FLAGS
+# define SWAP_FLAGS(ch1, ch2) \
+if (nonoption_flags_len > 0) \
+{ \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+}
+# else
+# define SWAP_FLAGS(ch1, ch2)
+# endif
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+One subsequence is elements [first_nonopt,last_nonopt)
+which contains all the non-options that have been skipped so far.
+The other is elements [last_nonopt,optind), which contains all
+the options processed since those non-options were skipped.
+
+`first_nonopt' and `last_nonopt' are relocated so that they describe
+the new indices of the non-options in ARGV after they are moved. */
+
+#if defined __STDC__ && __STDC__
+static void exchange(char **);
+#endif
+
+static void
+exchange(argv)
+char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ /* First make sure the handling of the `__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = malloc(top + 1);
+ if (new_str == NULL)
+ nonoption_flags_len = nonoption_flags_max_len = 0;
+ else
+ {
+ memset(__mempcpy(new_str, __getopt_nonoption_flags,
+ nonoption_flags_max_len),
+ '\0', top + 1 - nonoption_flags_max_len);
+ nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS(bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS(bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+#if defined __STDC__ && __STDC__
+static const char *_getopt_initialize(int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize(argc, argv, optstring)
+int argc;
+char *const *argv;
+const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ if (posixly_correct == NULL
+ && argc == __libc_argc && argv == __libc_argv)
+ {
+ if (nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = nonoption_flags_max_len = strlen(orig_str);
+ if (nonoption_flags_max_len < argc)
+ nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *)malloc(nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ nonoption_flags_max_len = -1;
+ else
+ memset(__mempcpy(__getopt_nonoption_flags, orig_str, len),
+ '\0', nonoption_flags_max_len - len);
+ }
+ }
+ nonoption_flags_len = nonoption_flags_max_len;
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+given in OPTSTRING.
+
+If an element of ARGV starts with '-', and is not exactly "-" or "--",
+then it is an option element. The characters of this element
+(aside from the initial '-') are option characters. If `getopt'
+is called repeatedly, it returns successively each of the option characters
+from each of the option elements.
+
+If `getopt' finds another option character, it returns that character,
+updating `optind' and `nextchar' so that the next call to `getopt' can
+resume the scan with the following option character or ARGV-element.
+
+If there are no more option characters, `getopt' returns -1.
+Then `optind' is the index in ARGV of the first ARGV-element
+that is not an option. (The ARGV-elements have been permuted
+so that those that are not options now come last.)
+
+OPTSTRING is a string containing the legitimate option characters.
+If an option character is seen that is not listed in OPTSTRING,
+return '?' after printing an error message. If you set `opterr' to
+zero, the error message is suppressed but we still return '?'.
+
+If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+so the following text in the same ARGV-element, or the text of the following
+ARGV-element, is returned in `optarg'. Two colons mean an option that
+wants an optional arg; if there is text in the current ARGV-element,
+it is returned in `optarg', otherwise `optarg' is set to zero.
+
+If OPTSTRING starts with `-' or `+', it requests different methods of
+handling the non-option ARGV-elements.
+See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+Long-named options begin with `--' instead of `-'.
+Their names may be abbreviated as long as the abbreviation is unique
+or is an exact match for some defined option. If they have an
+argument, it follows the option name in the same ARGV-element, separated
+from the option name by a `=', or else the in next ARGV-element.
+When `getopt' finds a long-named option, it returns 0 if that option's
+`flag' field is nonzero, the value of the option's `val' field
+if the `flag' field is zero.
+
+The elements of ARGV aren't really const, because we permute them.
+But we pretend they're const in the prototype to be compatible
+with other systems.
+
+LONGOPTS is a vector of `struct option' terminated by an
+element containing a name which is zero.
+
+LONGIND returns the index in LONGOPT of the long-named option found.
+It is only valid when a long-named option has been found by the most
+recent call.
+
+If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+long-named options. */
+
+int
+_getopt_internal(argc, argv, optstring, longopts, longind, long_only)
+int argc;
+char *const *argv;
+const char *optstring;
+const struct option *longopts;
+int *longind;
+int long_only;
+{
+ int print_errors = opterr;
+ if (optstring[0] == ':')
+ print_errors = 0;
+
+ if (argc < 1)
+ return -1;
+
+ optarg = NULL;
+
+ if (optind == 0 || !__getopt_initialized)
+ {
+ if (optind == 0)
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize(argc, argv, optstring);
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange((char **)argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp(argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange((char **)argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp(p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int)(nameend - nextchar)
+ == (unsigned int)strlen(p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only
+ || pfound->has_arg != p->has_arg
+ || pfound->flag != p->flag
+ || pfound->val != p->val)
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ __asprintf(&buf, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#else
+ fprintf(stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+#endif
+ }
+ nextchar += strlen(nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+#endif
+
+ if (argv[optind - 1][1] == '-')
+ {
+ /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ __asprintf(&buf, _("\
+ %s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#else
+ fprintf(stderr, _("\
+ %s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#endif
+ }
+ else
+ {
+ /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ __asprintf(&buf, _("\
+ %s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0],
+ pfound->name);
+#else
+ fprintf(stderr, _("\
+ %s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#endif
+ }
+
+ nextchar += strlen(nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ __asprintf(&buf,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#else
+ fprintf(stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+#endif
+ }
+ nextchar += strlen(nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen(nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index(optstring, *nextchar) == NULL)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+#endif
+
+ if (argv[optind][1] == '-')
+ {
+ /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ __asprintf(&buf, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+#else
+ fprintf(stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+#endif
+ }
+ else
+ {
+ /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ __asprintf(&buf, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+#else
+ fprintf(stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#endif
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index(optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+#endif
+
+ if (posixly_correct)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ __asprintf(&buf, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+#else
+ fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c);
+#endif
+ }
+ else
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ __asprintf(&buf, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+#else
+ fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ __asprintf(&buf, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#else
+ fprintf(stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp(p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int)(nameend - nextchar) == strlen(p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ __asprintf(&buf, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#else
+ fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+#endif
+ }
+ nextchar += strlen(nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ __asprintf(&buf, _("\
+ %s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#else
+ fprintf(stderr, _("\
+ %s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#endif
+ }
+
+ nextchar += strlen(nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ __asprintf(&buf, _("\
+ %s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#else
+ fprintf(stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+#endif
+ }
+ nextchar += strlen(nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen(nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ __asprintf(&buf,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+
+ if (_IO_fwide(stderr, 0) > 0)
+ __fwprintf(stderr, L"%s", buf);
+ else
+ fputs(buf, stderr);
+
+ free(buf);
+#else
+ fprintf(stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt(argc, argv, optstring)
+int argc;
+char *const *argv;
+const char *optstring;
+{
+ return _getopt_internal(argc, argv, optstring,
+ (const struct option *) 0,
+ (int *)0,
+ 0);
+}
+
+
+
+
+int
+getopt_long(int argc, char *const *argv, const char *options,
+const struct option *long_options, int *opt_index)
+{
+ return _getopt_internal(argc, argv, options, long_options, opt_index, 0, 0);
+}
+
+int
+getopt_long_only(int argc, char *const *argv, const char *options,
+const struct option *long_options, int *opt_index)
+{
+ return _getopt_internal(argc, argv, options, long_options, opt_index, 1, 0);
+}
+
+
+
+
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+the above definition of `getopt'. */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt(argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf("option %c\n", c);
+ break;
+
+ case 'a':
+ printf("option a\n");
+ break;
+
+ case 'b':
+ printf("option b\n");
+ break;
+
+ case 'c':
+ printf("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf("%s ", argv[optind++]);
+ printf("\n");
+ }
+
+ exit(0);
+}
+
+#endif /* TEST */
diff --git a/lib/compat/include/getopt.h b/lib/compat/include/getopt.h
new file mode 100644
index 00000000..bdb94fba
--- /dev/null
+++ b/lib/compat/include/getopt.h
@@ -0,0 +1,192 @@
+/* Declarations for getopt.
+ Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2009,2010
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _GETOPT_H
+
+#ifndef __need_getopt
+#define _GETOPT_H 1
+#endif
+
+/* If __GNU_LIBRARY__ is not already defined, either we are being used
+ standalone, or this is the first header included in the source file.
+ If we are being used with glibc, we need to include , but
+ that does not exist if we are standalone. So: if __GNU_LIBRARY__ is
+ not defined, include , which will pull in for us
+ if it's from glibc. (Why ctype.h? It's guaranteed to exist and it
+ doesn't flood the namespace with stuff the way some other headers do.) */
+#if ! defined __GNU_LIBRARY__
+#include
+#endif
+
+#ifndef __THROW
+#ifndef __GNUC_PREREQ
+#define __GNUC_PREREQ(maj, min) (0)
+#endif
+#if defined __cplusplus && __GNUC_PREREQ(2, 8)
+#define __THROW throw()
+#else
+#define __THROW
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+#ifndef __need_getopt
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option {
+ const char *name;
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+#endif /* need getopt */
+
+/* Get definitions and prototypes for functions to process the
+ arguments in ARGV (ARGC of them, minus the program name) for
+ options given in OPTS.
+
+ Return the option character from OPTS just read. Return -1 when
+ there are no more options. For unrecognized options, or options
+ missing arguments, `optopt' is set to the option letter, and '?' is
+ returned.
+
+ The OPTS string is a list of characters which are recognized option
+ letters, optionally followed by colons, specifying that that letter
+ takes an argument, to be placed in `optarg'.
+
+ If a letter in OPTS is followed by two colons, its argument is
+ optional. This behavior is specific to the GNU `getopt'.
+
+ The argument `--' causes premature termination of argument
+ scanning, explicitly telling `getopt' that there are no more
+ options.
+
+ If OPTS begins with `--', then non-option arguments are treated as
+ arguments to the option '\0'. This behavior is specific to the GNU
+ `getopt'. */
+
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt(int ___argc, char *const *___argv,
+ const char *__shortopts) __THROW;
+
+#if defined __need_getopt && defined __USE_POSIX2 \
+ && ! defined __USE_POSIX_IMPLICITLY && ! defined __USE_GNU
+/* The GNU getopt has more functionality than the standard version. The
+ additional functionality can be disable at runtime. This redirection
+ helps to also do this at runtime. */
+#ifdef __REDIRECT
+extern int __REDIRECT_NTH(getopt,
+ (int ___argc, char *const *___argv,
+ const char *__shortopts),
+ __posix_getopt);
+#else
+extern int __posix_getopt(int ___argc, char *const *___argv,
+ const char *__shortopts) __THROW;
+#define getopt __posix_getopt
+#endif
+#endif
+#else /* not __GNU_LIBRARY__ */
+extern int getopt();
+#endif /* __GNU_LIBRARY__ */
+
+#ifndef __need_getopt
+extern int getopt_long(int ___argc, char *const *___argv,
+ const char *__shortopts, const struct option *__longopts,
+ int *__longind) __THROW;
+extern int getopt_long_only(int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts,
+ int *__longind) __THROW;
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Make sure we later can get all the definitions and declarations. */
+#undef __need_getopt
+
+#endif /* getopt.h */
+
diff --git a/lib/compat/include/sys/queue.h b/lib/compat/include/sys/queue.h
new file mode 100644
index 00000000..e8b955e5
--- /dev/null
+++ b/lib/compat/include/sys/queue.h
@@ -0,0 +1,706 @@
+/* vim :set ts=4 sw=4 sts=4 et : */
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+// #include
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may be traversed in either direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - + - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_FROM + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_FROM_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_FROM - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _FOREACH_REVERSE_FROM_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_AFTER + - + -
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ * _SWAP + + + +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+ unsigned long lastline;
+ unsigned long prevline;
+ const char *lastfile;
+ const char *prevfile;
+};
+
+#define TRACEBUF struct qm_trace trace;
+#define TRACEBUF_INITIALIZER {__FILE__, __LINE__, NULL, 0},
+#define TRASHIT(x) \
+ do { \
+ (x) = (void *) -1; \
+ } while (0)
+#define QMD_SAVELINK(name, link) void **name = (void *) &(link)
+
+#define QMD_TRACE_HEAD(head) \
+ do { \
+ (head)->trace.prevline = (head)->trace.lastline; \
+ (head)->trace.prevfile = (head)->trace.lastfile; \
+ (head)->trace.lastline = __LINE__; \
+ (head)->trace.lastfile = __FILE__; \
+ } while (0)
+
+#define QMD_TRACE_ELEM(elem) \
+ do { \
+ (elem)->trace.prevline = (elem)->trace.lastline; \
+ (elem)->trace.prevfile = (elem)->trace.lastfile; \
+ (elem)->trace.lastline = __LINE__; \
+ (elem)->trace.lastfile = __FILE__; \
+ } while (0)
+
+#else
+#define QMD_TRACE_ELEM(elem)
+#define QMD_TRACE_HEAD(head)
+#define QMD_SAVELINK(name, link)
+#define TRACEBUF
+#define TRACEBUF_INITIALIZER
+#define TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+ struct name { \
+ struct type *slh_first; /* first element */ \
+ }
+
+#define SLIST_HEAD_INITIALIZER(head) {NULL}
+
+#define SLIST_ENTRY(type) \
+ struct { \
+ struct type *sle_next; /* next element */ \
+ }
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); (var); (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); (var) = (tvar))
+
+#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); (var) = (tvar))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); ((var) = *(varp)) != NULL; \
+ (varp) = &SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) \
+ do { \
+ SLIST_FIRST((head)) = NULL; \
+ } while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) \
+ do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+ } while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) \
+ do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+ } while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) \
+ do { \
+ QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_REMOVE_AFTER(curelm, field); \
+ } \
+ TRASHIT(*oldnext); \
+ } while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) \
+ do { \
+ SLIST_NEXT(elm, field) = SLIST_NEXT(SLIST_NEXT(elm, field), field); \
+ } while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) \
+ do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+ } while (0)
+
+#define SLIST_SWAP(head1, head2, type) \
+ do { \
+ struct type *swap_first = SLIST_FIRST(head1); \
+ SLIST_FIRST(head1) = SLIST_FIRST(head2); \
+ SLIST_FIRST(head2) = swap_first; \
+ } while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+ struct name { \
+ struct type *stqh_first; /* first element */ \
+ struct type **stqh_last; /* addr of last next element */ \
+ }
+
+#define STAILQ_HEAD_INITIALIZER(head) {NULL, &(head).stqh_first}
+
+#define STAILQ_ENTRY(type) \
+ struct { \
+ struct type *stqe_next; /* next element */ \
+ }
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_CONCAT(head1, head2) \
+ do { \
+ if (! STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+ } while (0)
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = STAILQ_FIRST((head)); (var); (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); (var) = (tvar))
+
+#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); (var) = (tvar))
+
+#define STAILQ_INIT(head) \
+ do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+ } while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) \
+ do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+ } while (0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+ } while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) \
+ ? NULL \
+ : __containerof((head)->stqh_last, struct type, field.stqe_next))
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) \
+ do { \
+ QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ STAILQ_REMOVE_AFTER(head, curelm, field); \
+ } \
+ TRASHIT(*oldnext); \
+ } while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) \
+ do { \
+ if ((STAILQ_NEXT(elm, field) \
+ = STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) \
+ == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) \
+ do { \
+ if ((STAILQ_FIRST((head)) = STAILQ_NEXT(STAILQ_FIRST((head)), field)) \
+ == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+ } while (0)
+
+#define STAILQ_SWAP(head1, head2, type) \
+ do { \
+ struct type *swap_first = STAILQ_FIRST(head1); \
+ struct type **swap_last = (head1)->stqh_last; \
+ STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_FIRST(head2) = swap_first; \
+ (head2)->stqh_last = swap_last; \
+ if (STAILQ_EMPTY(head1)) \
+ (head1)->stqh_last = &STAILQ_FIRST(head1); \
+ if (STAILQ_EMPTY(head2)) \
+ (head2)->stqh_last = &STAILQ_FIRST(head2); \
+ } while (0)
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type) \
+ struct name { \
+ struct type *lh_first; /* first element */ \
+ }
+
+#define LIST_HEAD_INITIALIZER(head) {NULL}
+
+#define LIST_ENTRY(type) \
+ struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+ }
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_LIST_CHECK_HEAD(head, field) \
+ do { \
+ if (LIST_FIRST((head)) != NULL \
+ && LIST_FIRST((head))->field.le_prev != &LIST_FIRST((head))) \
+ panic("Bad list head %p first->prev != head", (head)); \
+ } while (0)
+
+#define QMD_LIST_CHECK_NEXT(elm, field) \
+ do { \
+ if (LIST_NEXT((elm), field) != NULL \
+ && LIST_NEXT((elm), field)->field.le_prev \
+ != &((elm)->field.le_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+ } while (0)
+
+#define QMD_LIST_CHECK_PREV(elm, field) \
+ do { \
+ if (*(elm)->field.le_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+ } while (0)
+#else
+#define QMD_LIST_CHECK_HEAD(head, field)
+#define QMD_LIST_CHECK_NEXT(elm, field)
+#define QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); (var); (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); (var) = (tvar))
+
+#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); (var) = (tvar))
+
+#define LIST_INIT(head) \
+ do { \
+ LIST_FIRST((head)) = NULL; \
+ } while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) \
+ do { \
+ QMD_LIST_CHECK_NEXT(listelm, field); \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \
+ LIST_NEXT((listelm), field)->field.le_prev \
+ = &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+ } while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) \
+ do { \
+ QMD_LIST_CHECK_PREV(listelm, field); \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+ } while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) \
+ do { \
+ QMD_LIST_CHECK_HEAD((head), field); \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+ } while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_PREV(elm, head, type, field) \
+ ((elm)->field.le_prev == &LIST_FIRST((head)) \
+ ? NULL \
+ : __containerof((elm)->field.le_prev, struct type, field.le_next))
+
+#define LIST_REMOVE(elm, field) \
+ do { \
+ QMD_SAVELINK(oldnext, (elm)->field.le_next); \
+ QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
+ QMD_LIST_CHECK_NEXT(elm, field); \
+ QMD_LIST_CHECK_PREV(elm, field); \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+ TRASHIT(*oldnext); \
+ TRASHIT(*oldprev); \
+ } while (0)
+
+#define LIST_SWAP(head1, head2, type, field) \
+ do { \
+ struct type *swap_tmp = LIST_FIRST((head1)); \
+ LIST_FIRST((head1)) = LIST_FIRST((head2)); \
+ LIST_FIRST((head2)) = swap_tmp; \
+ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
+ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
+ } while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+ struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+ }
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ {NULL, &(head).tqh_first, TRACEBUF_INITIALIZER}
+
+#define TAILQ_ENTRY(type) \
+ struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+ }
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_TAILQ_CHECK_HEAD(head, field) \
+ do { \
+ if (! TAILQ_EMPTY(head) \
+ && TAILQ_FIRST((head))->field.tqe_prev != &TAILQ_FIRST((head))) \
+ panic("Bad tailq head %p first->prev != head", (head)); \
+ } while (0)
+
+#define QMD_TAILQ_CHECK_TAIL(head, field) \
+ do { \
+ if (*(head)->tqh_last != NULL) \
+ panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
+ } while (0)
+
+#define QMD_TAILQ_CHECK_NEXT(elm, field) \
+ do { \
+ if (TAILQ_NEXT((elm), field) != NULL \
+ && TAILQ_NEXT((elm), field)->field.tqe_prev \
+ != &((elm)->field.tqe_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+ } while (0)
+
+#define QMD_TAILQ_CHECK_PREV(elm, field) \
+ do { \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+ } while (0)
+#else
+#define QMD_TAILQ_CHECK_HEAD(head, field)
+#define QMD_TAILQ_CHECK_TAIL(head, headname)
+#define QMD_TAILQ_CHECK_NEXT(elm, field)
+#define QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define TAILQ_CONCAT(head1, head2, field) \
+ do { \
+ if (! TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ QMD_TRACE_HEAD(head1); \
+ QMD_TRACE_HEAD(head2); \
+ } \
+ } while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); (var); (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); (var) = (tvar))
+
+#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INIT(head) \
+ do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ } while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
+ do { \
+ QMD_TAILQ_CHECK_NEXT(listelm, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev \
+ = &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+ } while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) \
+ do { \
+ QMD_TAILQ_CHECK_PREV(listelm, field); \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+ } while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ QMD_TAILQ_CHECK_HEAD(head, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ } while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ QMD_TAILQ_CHECK_TAIL(head, field); \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ } while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *) ((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *) ((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) \
+ do { \
+ QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
+ QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
+ QMD_TAILQ_CHECK_NEXT(elm, field); \
+ QMD_TAILQ_CHECK_PREV(elm, field); \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ QMD_TRACE_HEAD(head); \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ TRASHIT(*oldnext); \
+ TRASHIT(*oldprev); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ } while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) \
+ do { \
+ struct type *swap_first = (head1)->tqh_first; \
+ struct type **swap_last = (head1)->tqh_last; \
+ (head1)->tqh_first = (head2)->tqh_first; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ (head2)->tqh_first = swap_first; \
+ (head2)->tqh_last = swap_last; \
+ if ((swap_first = (head1)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head1)->tqh_first; \
+ else \
+ (head1)->tqh_last = &(head1)->tqh_first; \
+ if ((swap_first = (head2)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head2)->tqh_first; \
+ else \
+ (head2)->tqh_last = &(head2)->tqh_first; \
+ } while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
+
diff --git a/lib/compat/include/unistd.h b/lib/compat/include/unistd.h
new file mode 100644
index 00000000..8c6fde0e
--- /dev/null
+++ b/lib/compat/include/unistd.h
@@ -0,0 +1,314 @@
+/*
+MIT License
+Copyright (c) 2019 win32ports
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#pragma once
+
+#ifndef __UNISTD_H_17CD2BD1_839A_4E25_97C7_DE9544B8B59C__
+#define __UNISTD_H_17CD2BD1_839A_4E25_97C7_DE9544B8B59C__
+
+#ifndef _WIN32
+
+#pragma message("this unistd.h implementation is for Windows only!")
+
+#else /* _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef _INC_IO
+#include /* _access() */
+#endif /* _INC_IO */
+
+#ifndef _INC_DIRECT
+#include /* _chdir() */
+#endif /* _INC_DIRECT */
+
+#ifndef _INC_PROCESS
+#include /* _execl() */
+#endif /* _INC_PROCESS */
+
+#include /* */
+
+#ifndef access
+#define access _access
+#endif /* access */
+
+#ifndef R_OK
+#define R_OK 04
+#endif /* R_OK */
+
+#ifndef W_OK
+#define W_OK 02
+#endif /* W_OK */
+
+#ifndef X_OK
+#define X_OK R_OK
+#endif /* X_OK */
+
+#ifndef F_OK
+#define F_OK 00
+#endif /* F_OK */
+
+#ifndef chdir
+#define chdir _chdir
+#endif /* chdir */
+
+#ifndef close
+#define close _close
+#endif /* close */
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif /* STDIN_FILENO */
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif /* STDOUT_FILENO */
+
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif /* STDERR_FILENO */
+
+#ifndef dup
+#define dup _dup
+#endif /* dup */
+
+#ifndef dup2
+#define dup2 _dup2
+#endif /* dup2 */
+
+#ifndef execl
+#define execl _execl
+#endif /* execl */
+
+#ifndef execle
+#define execle _execle
+#endif /* execle */
+
+#ifndef execlp
+#define execlp _execlp
+#endif /* execlp */
+
+#ifndef execp
+#define execp _execp
+#endif /* execp */
+
+#ifndef execpe
+#define execpe _execpe
+#endif /* execpe */
+
+#ifndef execpp
+#define execpp _execpp
+#endif /* execpp */
+
+#ifndef rmdir
+#define rmdir _rmdir
+#endif /* rmdir */
+
+#ifndef unlink
+#define unlink _unlink
+#endif /* unlink */
+
+/* permission bits below must be defined in sys/stat.h, but MSVC lacks them */
+
+#ifndef S_IRWXU
+#define S_IRWXU 0700
+#endif /* S_IRWXU */
+
+#ifndef S_IRUSR
+#define S_IRUSR 0400
+#endif /* S_IRUSR */
+
+#ifndef S_IWUSR
+#define S_IWUSR 0200
+#endif /* S_IWUSR */
+
+#ifndef S_IXUSR
+#define S_IXUSR 0100
+#endif /* S_IXUSR */
+
+#ifndef S_IRWXG
+#define S_IRWXG 070
+#endif /* S_IRWXG */
+
+#ifndef S_IRGRP
+#define S_IRGRP 040
+#endif /* S_IRGRP */
+
+#ifndef S_IWGRP
+#define S_IWGRP 020
+#endif /* S_IWGRP */
+
+#ifndef S_IXGRP
+#define S_IXGRP 010
+#endif /* S_IXGRP */
+
+#ifndef S_IRWXO
+#define S_IRWXO 07
+#endif /* S_IRWXO */
+
+#ifndef S_IROTH
+#define S_IROTH 04
+#endif /* S_IROTH */
+
+#ifndef S_IWOTH
+#define S_IWOTH 02
+#endif /* S_IWOTH */
+
+#ifndef S_IXOTH
+#define S_IXOTH 01
+#endif /* S_IXOTH */
+
+#ifndef S_ISUID
+#define S_ISUID 04000
+#endif /* S_ISUID */
+
+#ifndef S_ISGID
+#define S_ISGID 02000
+#endif /* S_ISGID */
+
+#ifndef S_ISVTX
+#define S_ISVTX 01000
+#endif /* S_ISVTX */
+
+#ifndef S_IRWXUGO
+#define S_IRWXUGO 0777
+#endif /* S_IRWXUGO */
+
+#ifndef S_IALLUGO
+#define S_IALLUGO 0777
+#endif /* S_IALLUGO */
+
+#ifndef S_IRUGO
+#define S_IRUGO 0444
+#endif /* S_IRUGO */
+
+#ifndef S_IWUGO
+#define S_IWUGO 0222
+#endif /* S_IWUGO */
+
+#ifndef S_IXUGO
+#define S_IXUGO 0111
+#endif /* S_IXUGO */
+
+#ifndef _S_IFMT
+#define _S_IFMT 0xF000
+#endif /* _S_IFMT */
+
+#ifndef _S_IFIFO
+#define _S_IFIFO 0x1000
+#endif /* _S_IFIFO */
+
+#ifndef _S_IFCHR
+#define _S_IFCHR 0x2000
+#endif /* _S_IFCHR */
+
+#ifndef _S_IFDIR
+#define _S_IFDIR 0x4000
+#endif /* _S_IFDIR */
+
+#ifndef _S_IFBLK
+#define _S_IFBLK 0x6000
+#endif /* _S_IFBLK */
+
+#ifndef _S_IFREG
+#define _S_IFREG 0x8000
+#endif /* _S_IFREG */
+
+#ifndef _S_IFLNK
+#define _S_IFLNK 0xA000
+#endif /* _S_IFLNK */
+
+#ifndef _S_IFSOCK
+#define _S_IFSOCK 0xC000
+#endif /* _S_IFSOCK */
+
+#ifndef S_IFMT
+#define S_IFMT _S_IFMT
+#endif /* S_IFMT */
+
+#ifndef S_IFIFO
+#define S_IFIFO _S_IFIFO
+#endif /* S_IFIFO */
+
+#ifndef S_IFCHR
+#define S_IFCHR _S_IFCHR
+#endif /* S_IFCHR */
+
+#ifndef S_IFDIR
+#define S_IFDIR _S_IFDIR
+#endif /* S_IFDIR */
+
+#ifndef S_IFBLK
+#define S_IFBLK _S_IFBLK
+#endif /* S_IFBLK */
+
+#ifndef S_IFREG
+#define S_IFREG _S_IFREG
+#endif /* S_IFREG */
+
+#ifndef S_IFLNK
+#define S_IFLNK _S_IFLNK
+#endif /* S_IFLNK */
+
+#ifndef S_IFSOCK
+#define S_IFSOCK _S_IFSOCK
+#endif /* S_IFSOCK */
+
+#ifndef S_ISTYPE
+#define S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask))
+#endif /* S_ISTYPE */
+
+#ifndef S_ISFIFO
+#define S_ISFIFO(mode) S_ISTYPE(mode, S_IFIFO)
+#endif /* S_ISFIFO */
+
+#ifndef S_ISCHR
+#define S_ISCHR(mode) S_ISTYPE(mode, S_IFCHR)
+#endif /* S_ISCHR */
+
+#ifndef S_ISDIR
+#define S_ISDIR(mode) S_ISTYPE(mode, S_IFDIR)
+#endif /* S_ISDIR */
+
+#ifndef S_ISBLK
+#define S_ISBLK(mode) S_ISTYPE(mode, S_IFBLK)
+#endif /* S_ISBLK */
+
+#ifndef S_ISREG
+#define S_ISREG(mode) S_ISTYPE(mode, S_IFREG)
+#endif /* S_ISREG */
+
+#ifndef S_ISLNK
+#define S_ISLNK(mode) S_ISTYPE(mode, S_IFLNK)
+#endif /* S_ISLNK */
+
+#ifndef S_ISSOCK
+#define S_ISSOCK(mode) S_ISTYPE(mode, S_IFSOCK)
+#endif /* S_ISSOCK */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _WIN32 */
+
+#endif /* __UNISTD_H_17CD2BD1_839A_4E25_97C7_DE9544B8B59C__ */
diff --git a/src/compat/strlcat.c b/lib/compat/strlcat.c
similarity index 79%
rename from src/compat/strlcat.c
rename to lib/compat/strlcat.c
index 1cdacd39..11942074 100644
--- a/src/compat/strlcat.c
+++ b/lib/compat/strlcat.c
@@ -16,7 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "compat/strlcat.h"
+#include "compat.h"
+
#include
/*
@@ -26,22 +27,23 @@
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
-size_t
-bsd_strlcat(char *dst, const char *src, size_t siz)
+size_t bsd_strlcat(char *dst, const char *src, size_t siz)
{
- char *d = dst;
+ char *d = dst;
const char *s = src;
- size_t n = siz;
- size_t dlen;
+ size_t n = siz;
+ size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
- while (n-- != 0 && *d != '\0')
+ while (n-- != 0 && *d != '\0') {
d++;
- dlen = d - dst;
- n = siz - dlen;
+ }
+ dlen = (size_t) (d - dst);
+ n = siz - dlen;
- if (n == 0)
- return(dlen + strlen(s));
+ if (n == 0) {
+ return (dlen + strlen(s));
+ }
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
@@ -51,5 +53,6 @@ bsd_strlcat(char *dst, const char *src, size_t siz)
}
*d = '\0';
- return(dlen + (s - src)); /* count does not include NUL */
+ return (dlen + (size_t) (s - src)); /* count does not include NUL */
}
+
diff --git a/lib/compat/strndup.c b/lib/compat/strndup.c
new file mode 100644
index 00000000..acbf15a6
--- /dev/null
+++ b/lib/compat/strndup.c
@@ -0,0 +1,45 @@
+/*
+ strndup.c - Standard C implimentation of POSIX strndup function
+
+ Copyright (C) 2026 readpe authors
+
+ This file is part of readpe.
+
+ readpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ readpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#include "compat.h"
+
+#include
+#include
+#include
+
+char *readpe_strndup(const char *src, size_t size)
+{
+ if (size == 0) {
+ return NULL;
+ }
+
+ char *str = calloc(size + 1, sizeof(char));
+
+ if (! str) {
+ return NULL;
+ }
+
+ strncpy(str, src, size);
+ str[size] = '\0';
+
+ return str;
+}
+
diff --git a/lib/fuzzy/CMakeLists.txt b/lib/fuzzy/CMakeLists.txt
new file mode 100644
index 00000000..b3967a2a
--- /dev/null
+++ b/lib/fuzzy/CMakeLists.txt
@@ -0,0 +1,13 @@
+project(libfuzzy)
+
+set(HEADERS
+ fuzzy.h
+)
+
+set(SOURCES
+ edit_dist.c
+ fuzzy.c
+)
+
+add_library(fuzzy STATIC ${HEADERS} ${SOURCES})
+
diff --git a/lib/fuzzy/edit_dist.c b/lib/fuzzy/edit_dist.c
new file mode 100644
index 00000000..1d507901
--- /dev/null
+++ b/lib/fuzzy/edit_dist.c
@@ -0,0 +1,278 @@
+/*
+ This edit distance code is taken from trn3.6. A few minor
+ modifications have been made by Andrew Tridgell
+ for use in spamsum.
+*/
+
+/***************************************************************************/
+
+/* The authors make no claims as to the fitness or correctness of this software
+ * for any use whatsoever, and it is provided as is. Any use of this software
+ * is at the user's own risk.
+ */
+
+#include
+
+/* edit_dist -- returns the minimum edit distance between two strings
+
+ Program by: Mark Maimone CMU Computer Science 13 Nov 89
+ Last Modified: 28 Jan 90
+
+ If the input strings have length n and m, the algorithm runs in time
+ O(nm) and space O(min(m,n)).
+
+HISTORY
+ 13 Nov 89 (mwm) Created edit_dist() and set_costs().
+
+ 28 Jan 90 (mwm) Added view_costs(). Should verify that THRESHOLD
+ computations will work even when THRESHOLD is not a multiple of
+ sizeof(int).
+
+ 17 May 93 (mwm) Improved performance when used with trn's newsgroup
+ processing; assume all costs are 1, and you can terminate when a
+ threshold is exceeded.
+*/
+
+#define MIN_DIST 100
+
+#define TRN_SPEEDUP /* Use a less-general version of the \
+ routine, one that's better for trn. \
+ All change costs are 1, and it's okay \
+ to terminate if the edit distance is \
+ known to exceed MIN_DIST */
+
+#define THRESHOLD \
+ 4000 /* worry about allocating more memory only \
+when this # of bytes is exceeded */
+#define STRLENTHRESHOLD ((int)((THRESHOLD / sizeof(int) - 3) / 2))
+
+#define SAFE_ASSIGN(x, y) (((x) != NULL) ? (*(x) = (y)) : (y))
+
+#define swap_int(x, y) \
+ do { \
+ int _iswap = (x); \
+ (x) = (y); \
+ (y) = _iswap; \
+ } while (0)
+#define swap_char(x, y) \
+ do { \
+ const char *_cswap = (x); \
+ (x) = (y); \
+ (y) = _cswap; \
+ } while (0)
+
+static inline int min3(int x, int y, int z)
+{
+ return x < y ? (x < z ? x : z) : (z < y) ? z : y;
+}
+static inline int min2(int x, int y) { return x < y ? x : y; }
+
+static int insert_cost = 1;
+static int delete_cost = 1;
+#ifndef TRN_SPEEDUP
+static int change_cost = 1;
+static int swap_cost = 1;
+#endif
+
+/* edit_distn -- returns the edit distance between two strings, or -1 on
+ failure */
+
+int edit_distn(const char *from, int from_len, const char *to, int to_len)
+{
+#ifndef TRN_SPEEDUP
+ register int ins, del, ch; /* local copies of edit costs */
+#endif
+ register int row, col, index; /* dynamic programming counters */
+ register int radix; /* radix for modular indexing */
+#ifdef TRN_SPEEDUP
+ register int low;
+#endif
+ int *buffer; /* pointer to storage for one row
+ of the d.p. array */
+ int store[THRESHOLD / sizeof(int)];
+ /* a small amount of static
+ storage, to be used when the
+ input strings are small enough */
+
+ /* Handle trivial cases when one string is empty */
+
+ if (from == NULL || !from_len) {
+ if (to == NULL || !to_len) {
+ return 0;
+ } else {
+ return to_len * insert_cost;
+ }
+ } else if (to == NULL || !to_len) {
+ return from_len * delete_cost;
+ }
+
+ /* Initialize registers */
+
+ radix = 2 * from_len + 3;
+#ifdef TRN_SPEEDUP
+#define ins 1
+#define del 1
+#define ch 3
+#define swap_cost 5
+#else
+ ins = insert_cost;
+ del = delete_cost;
+ ch = change_cost;
+#endif
+
+ /* Make from short enough to fit in the static storage, if it's at all
+ possible */
+
+ if (from_len > to_len && from_len > STRLENTHRESHOLD) {
+ swap_int(from_len, to_len);
+ swap_char(from, to);
+#ifndef TRN_SPEEDUP
+ swap_int(ins, del);
+#endif
+ } /* if from_len > to_len */
+
+ /* Allocate the array storage (from the heap if necessary) */
+
+ if (from_len <= STRLENTHRESHOLD) {
+ buffer = store;
+ } else {
+ buffer = (int *)malloc((unsigned int)radix * sizeof(int));
+ }
+
+ /* Here's where the fun begins. We will find the minimum edit distance
+ using dynamic programming. We only need to store two rows of the matrix
+ at a time, since we always progress down the matrix. For example,
+ given the strings "one" and "two", and insert, delete and change costs
+ equal to 1:
+
+ _ o n e
+ _ 0 1 2 3
+ t 1 1 2 3
+ w 2 2 2 3
+ o 3 2 3 3
+
+ The dynamic programming recursion is defined as follows:
+
+ ar(x,0) := x * insert_cost
+ ar(0,y) := y * delete_cost
+ ar(x,y) := min(a(x - 1, y - 1) + (from[x] == to[y] ? 0 : change),
+ a(x - 1, y) + insert_cost,
+ a(x, y - 1) + delete_cost,
+ a(x - 2, y - 2) + (from[x] == to[y-1] &&
+ from[x-1] == to[y] ? swap_cost :
+ infinity))
+
+ Since this only looks at most two rows and three columns back, we need
+ only store the values for the two preceeding rows. In this
+ implementation, we do not explicitly store the zero column, so only 2 *
+ from_len + 2 words are needed. However, in the implementation of the
+ swap_cost check, the current matrix value is used as a buffer; we
+ can't overwrite the earlier value until the swap_cost check has
+ been performed. So we use 2 * from_len + 3 elements in the buffer.
+ */
+
+#define ar(x, y, index) \
+ (((x) == 0) ? (y) * del : (((y) == 0) ? (x) * ins : buffer[mod(index)]))
+#define NW(x, y) ar(x, y, index + from_len + 2)
+#define N(x, y) ar(x, y, index + from_len + 3)
+#define W(x, y) ar(x, y, index + radix - 1)
+#define NNWW(x, y) ar(x, y, index + 1)
+#define mod(x) ((x) % radix)
+
+ index = 0;
+
+#ifdef DEBUG_EDITDIST
+ printf(" ");
+ for (col = 0; col < from_len; col++) {
+ printf(" %c ", from[col]);
+ }
+ printf("\n ");
+
+ for (col = 0; col <= from_len; col++) {
+ printf("%2d ", col * del);
+ }
+#endif
+
+ /* Row 0 is handled implicitly; its value at a given column is col*del.
+ The loop below computes the values for Row 1. At this point we know the
+ strings are nonempty. We also don't need to consider swap costs in row
+ 1.
+
+ COMMENT: the indicies row and col below point into the STRING, so
+ the corresponding MATRIX indicies are row+1 and col+1.
+ */
+
+ buffer[index++] = min2(ins + del, (from[0] == to[0] ? 0 : ch));
+#ifdef TRN_SPEEDUP
+ low = buffer[mod(index + radix - 1)];
+#endif
+
+#ifdef DEBUG_EDITDIST
+ printf("\n %c %2d %2d ", to[0], ins, buffer[index - 1]);
+#endif
+
+ for (col = 1; col < from_len; col++) {
+ buffer[index] = min3(col * del + ((from[col] == to[0]) ? 0 : ch),
+ (col + 1) * del + ins, buffer[index - 1] + del);
+#ifdef TRN_SPEEDUP
+ if (buffer[index] < low) {
+ low = buffer[index];
+ }
+#endif
+ index++;
+
+#ifdef DEBUG_EDITDIST
+ printf("%2d ", buffer[index - 1]);
+#endif
+
+ } /* for col = 1 */
+
+#ifdef DEBUG_EDITDIST
+ printf("\n %c %2d ", to[1], 2 * ins);
+#endif
+
+ /* Now handle the rest of the matrix */
+
+ for (row = 1; row < to_len; row++) {
+ for (col = 0; col < from_len; col++) {
+ buffer[index]
+ = min3(NW(row, col) + ((from[col] == to[row]) ? 0 : ch),
+ N(row, col + 1) + ins, W(row + 1, col) + del);
+ if (from[col] == to[row - 1] && col > 0
+ && from[col - 1] == to[row]) {
+ buffer[index]
+ = min2(buffer[index], NNWW(row - 1, col - 1) + swap_cost);
+ }
+
+#ifdef DEBUG_EDITDIST
+ printf("%2d ", buffer[index]);
+#endif
+#ifdef TRN_SPEEDUP
+ if (buffer[index] < low || col == 0) {
+ low = buffer[index];
+ }
+#endif
+
+ index = mod(index + 1);
+ } /* for col = 1 */
+#ifdef DEBUG_EDITDIST
+ if (row < to_len - 1) {
+ printf("\n %c %2d ", to[row + 1], (row + 2) * ins);
+ } else {
+ printf("\n");
+ }
+#endif
+#ifdef TRN_SPEEDUP
+ if (low > MIN_DIST) {
+ break;
+ }
+#endif
+ } /* for row = 1 */
+
+ row = buffer[mod(index + radix - 1)];
+ if (buffer != store) {
+ free((char *)buffer);
+ }
+ return row;
+} /* edit_distn */
+
diff --git a/lib/fuzzy/fuzzy.c b/lib/fuzzy/fuzzy.c
new file mode 100644
index 00000000..68f6f65f
--- /dev/null
+++ b/lib/fuzzy/fuzzy.c
@@ -0,0 +1,736 @@
+/* ssdeep
+ * Copyright (C) 2002 Andrew Tridgell
+ * Copyright (C) 2006 ManTech International Corporation
+ * Copyright (C) 2013 Helmut Grohne
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Earlier versions of this code were named fuzzy.c and can be found at:
+ * http://www.samba.org/ftp/unpacked/junkcode/spamsum/
+ * http://ssdeep.sf.net/
+ */
+
+#include "fuzzy.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef _MSC_VER
+#define ftello ftell
+#define fseeko fseek
+typedef long off_t;
+
+#else
+#include
+
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define likely(x) __builtin_expect(! ! (x), 1)
+#define unlikely(x) __builtin_expect(! ! (x), 0)
+#else
+#define likely(x) x
+#define unlikely(x) x
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define ROLLING_WINDOW 7
+#define MIN_BLOCKSIZE 3
+#define HASH_PRIME 0x01000193
+#define HASH_INIT 0x28021967
+#define NUM_BLOCKHASHES 31
+
+struct roll_state {
+ unsigned char window[ROLLING_WINDOW];
+ uint32_t h1, h2, h3;
+ uint32_t n;
+};
+
+static void roll_init(/*@out@*/ struct roll_state *self)
+{
+ memset(self, 0, sizeof(struct roll_state));
+}
+
+/*
+ * a rolling hash, based on the Adler checksum. By using a rolling hash
+ * we can perform auto resynchronisation after inserts/deletes
+
+ * internally, h1 is the sum of the bytes in the window and h2
+ * is the sum of the bytes times the index
+
+ * h3 is a shift/xor based rolling hash, and is mostly needed to ensure that
+ * we can cope with large blocksize values
+ */
+static void roll_hash(struct roll_state *self, unsigned char c)
+{
+ self->h2 -= self->h1;
+ self->h2 += ROLLING_WINDOW * (uint32_t) c;
+
+ self->h1 += (uint32_t) c;
+ self->h1 -= (uint32_t) self->window[self->n % ROLLING_WINDOW];
+
+ self->window[self->n % ROLLING_WINDOW] = c;
+ self->n++;
+
+ /* The original spamsum AND'ed this value with 0xFFFFFFFF which
+ * in theory should have no effect. This AND has been removed
+ * for performance (jk) */
+ self->h3 <<= 5;
+ self->h3 ^= c;
+}
+
+static uint32_t roll_sum(const struct roll_state *self)
+{
+ return self->h1 + self->h2 + self->h3;
+}
+
+/* A simple non-rolling hash, based on the FNV hash. */
+static uint32_t sum_hash(unsigned char c, uint32_t h)
+{
+ return (h * HASH_PRIME) ^ c;
+}
+
+/* A blockhash contains a signature state for a specific (implicit) blocksize.
+ * The blocksize is given by SSDEEP_BS(index). The h and halfh members are the
+ * FNV hashes, where halfh stops to be reset after digest is SPAMSUM_LENGTH/2
+ * long. The halfh hash is needed be able to truncate digest for the second
+ * output hash to stay compatible with ssdeep output. */
+struct blockhash_context {
+ uint32_t h, halfh;
+ char digest[SPAMSUM_LENGTH];
+ unsigned int dlen;
+};
+
+struct fuzzy_state {
+ unsigned int bhstart, bhend;
+ struct blockhash_context bh[NUM_BLOCKHASHES];
+ size_t total_size;
+ struct roll_state roll;
+};
+
+#define SSDEEP_BS(index) (((uint32_t) MIN_BLOCKSIZE) << (index))
+
+/*@only@*/ /*@null@*/ struct fuzzy_state *fuzzy_new(void)
+{
+ struct fuzzy_state *self;
+ if (NULL == (self = malloc(sizeof(struct fuzzy_state)))) {
+ /* malloc sets ENOMEM */
+ return NULL;
+ }
+ self->bhstart = 0;
+ self->bhend = 1;
+ self->bh[0].h = HASH_INIT;
+ self->bh[0].halfh = HASH_INIT;
+ self->bh[0].dlen = 0;
+ self->total_size = 0;
+ roll_init(&self->roll);
+ return self;
+}
+
+static void fuzzy_try_fork_blockhash(struct fuzzy_state *self)
+{
+ struct blockhash_context *obh, *nbh;
+ if (self->bhend >= NUM_BLOCKHASHES) {
+ return;
+ }
+ assert(self->bhend > 0);
+ obh = self->bh + (self->bhend - 1);
+ nbh = obh + 1;
+ nbh->h = obh->h;
+ nbh->halfh = obh->halfh;
+ nbh->dlen = 0;
+ ++self->bhend;
+}
+
+static void fuzzy_try_reduce_blockhash(struct fuzzy_state *self)
+{
+ assert(self->bhstart < self->bhend);
+ if (self->bhend - self->bhstart < 2) {
+ /* Need at least two working hashes. */
+ return;
+ }
+ if ((size_t) SSDEEP_BS(self->bhstart) * SPAMSUM_LENGTH
+ >= self->total_size) {
+ /* Initial blocksize estimate would select this or a smaller
+ * blocksize. */
+ return;
+ }
+ if (self->bh[self->bhstart + 1].dlen < SPAMSUM_LENGTH / 2) {
+ /* Estimate adjustment would select this blocksize. */
+ return;
+ }
+ /* At this point we are clearly no longer interested in the
+ * start_blocksize. Get rid of it. */
+ ++self->bhstart;
+}
+
+static const char *b64
+ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void fuzzy_engine_step(struct fuzzy_state *self, unsigned char c)
+{
+ size_t h;
+ unsigned int i;
+ /* At each character we update the rolling hash and the normal hashes.
+ * When the rolling hash hits a reset value then we emit a normal hash
+ * as a element of the signature and reset the normal hash. */
+ roll_hash(&self->roll, c);
+ h = roll_sum(&self->roll);
+
+ for (i = self->bhstart; i < self->bhend; ++i) {
+ self->bh[i].h = sum_hash(c, self->bh[i].h);
+ self->bh[i].halfh = sum_hash(c, self->bh[i].halfh);
+ }
+
+ for (i = self->bhstart; i < self->bhend; ++i) {
+ /* With growing blocksize almost no runs fail the next test. */
+ if (likely(h % SSDEEP_BS(i) != SSDEEP_BS(i) - 1)) {
+ /* Once this condition is false for one bs, it is
+ * automatically false for all further bs. I.e. if
+ * h === -1 (mod 2*bs) then h === -1 (mod bs). */
+ break;
+ }
+ /* We have hit a reset point. We now emit hashes which are
+ * based on all characters in the piece of the message between
+ * the last reset point and this one */
+ if (unlikely(0 == self->bh[i].dlen)) {
+ /* Can only happen 30 times. */
+ /* First step for this blocksize. Clone next. */
+ fuzzy_try_fork_blockhash(self);
+ }
+ if (self->bh[i].dlen < SPAMSUM_LENGTH - 1) {
+ /* We can have a problem with the tail overflowing. The
+ * easiest way to cope with this is to only reset the
+ * normal hash if we have room for more characters in
+ * our signature. This has the effect of combining the
+ * last few pieces of the message into a single piece
+ * */
+ self->bh[i].digest[self->bh[i].dlen++] = b64[self->bh[i].h % 64];
+ self->bh[i].h = HASH_INIT;
+ if (self->bh[i].dlen < SPAMSUM_LENGTH / 2) {
+ self->bh[i].halfh = HASH_INIT;
+ }
+ } else {
+ fuzzy_try_reduce_blockhash(self);
+ }
+ }
+}
+
+int fuzzy_update(struct fuzzy_state *self, const unsigned char *buffer,
+ size_t buffer_size)
+{
+ self->total_size += buffer_size;
+ for (; buffer_size > 0; ++buffer, --buffer_size) {
+ fuzzy_engine_step(self, *buffer);
+ }
+ return 0;
+}
+
+static int memcpy_eliminate_sequences(char *dst, const char *src, int n)
+{
+ const char *srcend = src + n;
+ assert(n >= 0);
+ if (src < srcend) {
+ *dst++ = *src++;
+ }
+ if (src < srcend) {
+ *dst++ = *src++;
+ }
+ if (src < srcend) {
+ *dst++ = *src++;
+ }
+ while (src < srcend) {
+ if (*src == dst[-1] && *src == dst[-2] && *src == dst[-3]) {
+ ++src;
+ --n;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ return n;
+}
+
+// We need some extra help on Win32
+#if defined(_WIN32)
+#ifndef EOVERFLOW
+#define EOVERFLOW 84
+#endif
+#define ftello ftell
+#define fseeko fseek
+#else
+#ifdef S_SPLINT_S
+extern const int EOVERFLOW;
+#endif
+
+#endif
+
+int fuzzy_digest(const struct fuzzy_state *self,
+ /*@out@*/ char *result, unsigned int flags)
+{
+ unsigned int bi = self->bhstart;
+ uint32_t h = roll_sum(&self->roll);
+ int i, remain = FUZZY_MAX_RESULT - 1; /* Exclude terminating '\0'. */
+ /* Verify that our elimination was not overeager. */
+ assert(bi == 0
+ || (size_t) SSDEEP_BS(bi) / 2 * SPAMSUM_LENGTH < self->total_size);
+
+ /* Initial blocksize guess. */
+ while ((size_t) SSDEEP_BS(bi) * SPAMSUM_LENGTH < self->total_size) {
+ ++bi;
+ if (bi >= NUM_BLOCKHASHES) {
+ /* The input exceeds data types. */
+ errno = EOVERFLOW;
+ return -1;
+ }
+ }
+ /* Adapt blocksize guess to actual digest length. */
+ while (bi >= self->bhend) {
+ --bi;
+ }
+ while (bi > self->bhstart && self->bh[bi].dlen < SPAMSUM_LENGTH / 2) {
+ --bi;
+ }
+ assert(! (bi > 0 && self->bh[bi].dlen < SPAMSUM_LENGTH / 2));
+
+ i = snprintf(result, (size_t) remain, "%u:", SSDEEP_BS(bi));
+ if (i <= 0) {
+ /* Maybe snprintf has set errno here? */
+ return -1;
+ }
+ assert(i < remain);
+ remain -= i;
+ result += i;
+ i = (int) self->bh[bi].dlen;
+ assert(i <= remain);
+ if ((flags & FUZZY_FLAG_ELIMSEQ) != 0) {
+ i = memcpy_eliminate_sequences(result, self->bh[bi].digest, i);
+ } else {
+ memcpy(result, self->bh[bi].digest, (size_t) i);
+ }
+ result += i;
+ remain -= i;
+ if (h != 0) {
+ assert(remain > 0);
+ *result = b64[self->bh[bi].h % 64];
+ if ((flags & FUZZY_FLAG_ELIMSEQ) == 0 || i < 3 || *result != result[-1]
+ || *result != result[-2] || *result != result[-3]) {
+ ++result;
+ --remain;
+ }
+ }
+ assert(remain > 0);
+ *result++ = ':';
+ --remain;
+ if (bi < self->bhend - 1) {
+ ++bi;
+ i = (int) self->bh[bi].dlen;
+ if ((flags & FUZZY_FLAG_NOTRUNC) == 0 && i > SPAMSUM_LENGTH / 2 - 1) {
+ i = SPAMSUM_LENGTH / 2 - 1;
+ }
+ assert(i <= remain);
+ if ((flags & FUZZY_FLAG_ELIMSEQ) != 0) {
+ i = memcpy_eliminate_sequences(result, self->bh[bi].digest, i);
+ } else {
+ memcpy(result, self->bh[bi].digest, (size_t) i);
+ }
+ result += i;
+ remain -= i;
+ if (h != 0) {
+ assert(remain > 0);
+ h = (flags & FUZZY_FLAG_NOTRUNC) != 0 ? self->bh[bi].h
+ : self->bh[bi].halfh;
+ *result = b64[h % 64];
+ if ((flags & FUZZY_FLAG_ELIMSEQ) == 0 || i < 3
+ || *result != result[-1] || *result != result[-2]
+ || *result != result[-3]) {
+ ++result;
+ --remain;
+ }
+ }
+ } else if (h != 0) {
+ assert(self->bh[bi].dlen == 0);
+ assert(remain > 0);
+ *result++ = b64[self->bh[bi].h % 64];
+ /* No need to bother with FUZZY_FLAG_ELIMSEQ, because this
+ * digest has length 1. */
+ --remain;
+ }
+ *result = '\0';
+ return 0;
+}
+
+void fuzzy_free(/*@only@*/ struct fuzzy_state *self) { free(self); }
+
+int fuzzy_hash_buf(const unsigned char *buf, uint32_t buf_len,
+ /*@out@*/ char *result)
+{
+ struct fuzzy_state *ctx;
+ int ret = -1;
+ if (NULL == (ctx = fuzzy_new())) {
+ return -1;
+ }
+ if (fuzzy_update(ctx, buf, buf_len) < 0) {
+ goto out;
+ }
+ if (fuzzy_digest(ctx, result, 0) < 0) {
+ goto out;
+ }
+ ret = 0;
+out:
+ fuzzy_free(ctx);
+ return ret;
+}
+
+int fuzzy_hash_stream(FILE *handle, /*@out@*/ char *result)
+{
+ struct fuzzy_state *ctx;
+ unsigned char buffer[4096];
+ size_t n;
+ int ret = -1;
+ if (NULL == (ctx = fuzzy_new())) {
+ return -1;
+ }
+ for (;;) {
+ n = fread(buffer, 1, 4096, handle);
+ if (0 == n) {
+ break;
+ }
+ if (fuzzy_update(ctx, buffer, n) < 0) {
+ goto out;
+ }
+ }
+ if (ferror(handle) != 0) {
+ goto out;
+ }
+ if (fuzzy_digest(ctx, result, 0) < 0) {
+ goto out;
+ }
+ ret = 0;
+out:
+ fuzzy_free(ctx);
+ return ret;
+}
+
+#ifdef S_SPLINT_S
+typedef size_t off_t;
+int fseeko(FILE *, off_t, int);
+off_t ftello(FILE *);
+#endif
+
+int fuzzy_hash_file(FILE *handle, /*@out@*/ char *result)
+{
+ off_t fpos;
+ int status;
+ fpos = ftello(handle);
+ if (fseek(handle, 0, SEEK_SET) < 0) {
+ return -1;
+ }
+ status = fuzzy_hash_stream(handle, result);
+ if (status == 0) {
+ if (fseeko(handle, fpos, SEEK_SET) < 0) {
+ return -1;
+ }
+ }
+ return status;
+}
+
+int fuzzy_hash_filename(const char *filename, /*@out@*/ char *result)
+{
+ int status;
+ FILE *handle = fopen(filename, "rb");
+ if (NULL == handle) {
+ return -1;
+ }
+ status = fuzzy_hash_stream(handle, result);
+ /* We cannot do anything about an fclose failure. */
+ (void) fclose(handle);
+ return status;
+}
+
+//
+// We only accept a match if we have at least one common substring in
+// the signature of length ROLLING_WINDOW. This dramatically drops the
+// false positive rate for low score thresholds while having
+// negligable affect on the rate of spam detection.
+//
+// return 1 if the two strings do have a common substring, 0 otherwise
+//
+static int has_common_substring(const char *s1, const char *s2)
+{
+ int i, j;
+ int num_hashes;
+ uint32_t hashes[SPAMSUM_LENGTH];
+
+ // there are many possible algorithms for common substring
+ // detection. In this case I am re-using the rolling hash code
+ // to act as a filter for possible substring matches
+
+ memset(hashes, 0, sizeof(hashes));
+
+ // first compute the windowed rolling hash at each offset in
+ // the first string
+ struct roll_state state;
+ roll_init(&state);
+
+ for (i = 0; s1[i]; i++) {
+ roll_hash(&state, (unsigned char) s1[i]);
+ hashes[i] = roll_sum(&state);
+ }
+ num_hashes = i;
+
+ roll_init(&state);
+
+ // now for each offset in the second string compute the
+ // rolling hash and compare it to all of the rolling hashes
+ // for the first string. If one matches then we have a
+ // candidate substring match. We then confirm that match with
+ // a direct string comparison */
+ for (i = 0; s2[i]; i++) {
+ roll_hash(&state, (unsigned char) s2[i]);
+ uint32_t h = roll_sum(&state);
+ if (i < ROLLING_WINDOW - 1) {
+ continue;
+ }
+ for (j = ROLLING_WINDOW - 1; j < num_hashes; j++) {
+ if (hashes[j] != 0 && hashes[j] == h) {
+ // we have a potential match - confirm it
+ if (strlen(s2 + i - (ROLLING_WINDOW - 1)) >= ROLLING_WINDOW
+ && strncmp(s2 + i - (ROLLING_WINDOW - 1),
+ s1 + j - (ROLLING_WINDOW - 1), ROLLING_WINDOW)
+ == 0) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+// eliminate sequences of longer than 3 identical characters. These
+// sequences contain very little information so they tend to just bias
+// the result unfairly
+static char *eliminate_sequences(const char *str)
+{
+ char *ret;
+ size_t i, j, len;
+
+ ret = strdup(str);
+ if (! ret) {
+ return NULL;
+ }
+
+ len = strlen(str);
+ if (len < 3) {
+ return ret;
+ }
+
+ for (i = j = 3; i < len; i++) {
+ if (str[i] != str[i - 1] || str[i] != str[i - 2]
+ || str[i] != str[i - 3]) {
+ ret[j++] = str[i];
+ }
+ }
+
+ ret[j] = 0;
+
+ return ret;
+}
+
+int edit_distn(const char *from, int from_len, const char *to, int to_len);
+//
+// this is the low level string scoring algorithm. It takes two strings
+// and scores them on a scale of 0-100 where 0 is a terrible match and
+// 100 is a great match. The block_size is used to cope with very small
+// messages.
+//
+static uint32_t score_strings(const char *s1, const char *s2,
+ unsigned int block_size)
+{
+ uint32_t score;
+ size_t len1, len2;
+
+ len1 = strlen(s1);
+ len2 = strlen(s2);
+
+ if (len1 > SPAMSUM_LENGTH || len2 > SPAMSUM_LENGTH) {
+ // not a real spamsum signature?
+ return 0;
+ }
+
+ // the two strings must have a common substring of length
+ // ROLLING_WINDOW to be candidates
+ if (has_common_substring(s1, s2) == 0) {
+ return 0;
+ }
+
+ // compute the edit distance between the two strings. The edit distance
+ // gives us a pretty good idea of how closely related the two strings are
+ score = (uint32_t) edit_distn(s1, (int) len1, s2, (int) len2);
+
+ // scale the edit distance by the lengths of the two
+ // strings. This changes the score to be a measure of the
+ // proportion of the message that has changed rather than an
+ // absolute quantity. It also copes with the variability of
+ // the string lengths.
+ score = (uint32_t) ((score * SPAMSUM_LENGTH) / (len1 + len2));
+
+ // at this stage the score occurs roughly on a 0-64 scale,
+ // with 0 being a good match and 64 being a complete
+ // mismatch
+
+ // rescale to a 0-100 scale (friendlier to humans)
+ score = (100 * score) / 64;
+
+ // it is possible to get a score above 100 here, but it is a
+ // really terrible match
+ if (score >= 100) {
+ return 0;
+ }
+
+ // now re-scale on a 0-100 scale with 0 being a poor match and
+ // 100 being a excellent match.
+ score = 100 - score;
+
+ // printf ("len1: %"PRIu32" len2: %"PRIu32"\n", len1, len2);
+
+ // when the blocksize is small we don't want to exaggerate the match size
+ if (score > block_size / MIN_BLOCKSIZE * MIN(len1, len2)) {
+ score = (uint32_t) (block_size / MIN_BLOCKSIZE * MIN(len1, len2));
+ }
+ return score;
+}
+
+//
+// Given two spamsum strings return a value indicating the degree
+// to which they match.
+//
+int fuzzy_compare(const char *str1, const char *str2)
+{
+ unsigned int block_size1, block_size2;
+ uint32_t score = 0;
+ char *s1, *s2;
+ char *s1_1, *s1_2, *s1_3;
+ char *s2_1, *s2_2, *s2_3;
+
+ if (NULL == str1 || NULL == str2) {
+ return -1;
+ }
+
+ // each spamsum is prefixed by its block size
+ if (sscanf(str1, "%u:", &block_size1) != 1
+ || sscanf(str2, "%u:", &block_size2) != 1) {
+ return -1;
+ }
+
+ // if the blocksizes don't match then we are comparing
+ // apples to oranges. This isn't an 'error' per se. We could
+ // have two valid signatures, but they can't be compared.
+ if (block_size1 != block_size2 && block_size1 != block_size2 * 2
+ && block_size2 != block_size1 * 2) {
+ return 0;
+ }
+
+ // move past the prefix
+ str1 = strchr(str1, ':');
+ str2 = strchr(str2, ':');
+
+ if (! str1 || ! str2) {
+ // badly formed ...
+ return -1;
+ }
+
+ // there is very little information content is sequences of
+ // the same character like 'LLLLL'. Eliminate any sequences
+ // longer than 3. This is especially important when combined
+ // with the has_common_substring() test below.
+ // NOTE: This function duplciates str1 and str2
+ s1 = eliminate_sequences(str1 + 1);
+ if (! s1) {
+ return 0;
+ }
+ s2 = eliminate_sequences(str2 + 1);
+ if (! s2) {
+ free(s1);
+ return 0;
+ }
+
+ // now break them into the two pieces
+ s1_1 = s1;
+ s2_1 = s2;
+
+ s1_2 = strchr(s1, ':');
+ s2_2 = strchr(s2, ':');
+
+ if (! s1_2 || ! s2_2) {
+ // a signature is malformed - it doesn't have 2 parts
+ free(s1);
+ free(s2);
+ return -1;
+ }
+
+ // Chop the first substring. We terminate the first substring
+ // and then advance the pointer to the start of the second substring.
+ *s1_2 = 0;
+ s1_2++;
+ *s2_2 = 0;
+ s2_2++;
+
+ // Chop the second string at the comma--just before the filename.
+ // If the strings don't have a comma (i.e. don't have a filename)
+ // that's ok. It's not an error. This function can be called on
+ // signatures which don't have filenames attached.
+ // We also don't have to advance past the comma however. We don't care
+ // about the filename
+ s1_3 = strchr(s1_2, ',');
+ s2_3 = strchr(s2_2, ',');
+ if (s1_3 != NULL) {
+ *s1_3 = 0;
+ }
+ if (s2_3 != NULL) {
+ *s2_3 = 0;
+ }
+
+ // each signature has a string for two block sizes. We now
+ // choose how to combine the two block sizes. We checked above
+ // that they have at least one block size in common
+ if (block_size1 == block_size2) {
+ uint32_t score1, score2;
+ score1 = score_strings(s1_1, s2_1, block_size1);
+ score2 = score_strings(s1_2, s2_2, block_size1 * 2);
+ score = MAX(score1, score2);
+ } else if (block_size1 == block_size2 * 2) {
+ score = score_strings(s1_1, s2_2, block_size1);
+ } else {
+ score = score_strings(s1_2, s2_1, block_size2);
+ }
+
+ free(s1);
+ free(s2);
+
+ return (int) score;
+}
+
diff --git a/lib/libpe/libfuzzy/fuzzy.h b/lib/fuzzy/fuzzy.h
similarity index 90%
rename from lib/libpe/libfuzzy/fuzzy.h
rename to lib/fuzzy/fuzzy.h
index bd71a0f0..60ce5185 100644
--- a/lib/libpe/libfuzzy/fuzzy.h
+++ b/lib/fuzzy/fuzzy.h
@@ -52,7 +52,6 @@
/// @link fuzzy_compare() compute the
/// similarity between any two fuzzy signatures @endlink.
-
#include
#include
@@ -94,9 +93,8 @@ extern /*@only@*/ /*@null@*/ struct fuzzy_state *fuzzy_new(void);
* @param buffer_size The length of the given buffer
* @return zero on success, non-zero on error
*/
-extern int fuzzy_update(struct fuzzy_state *state,
- const unsigned char *buffer,
- size_t buffer_size);
+extern int fuzzy_update(struct fuzzy_state *state, const unsigned char *buffer,
+ size_t buffer_size);
/**
* @brief Obtain the fuzzy hash from the state.
@@ -109,9 +107,8 @@ extern int fuzzy_update(struct fuzzy_state *state,
* represented by a zero.
* @return zero on success, non-zero on error
*/
-extern int fuzzy_digest(const struct fuzzy_state *state,
- /*@out@*/ char *result,
- unsigned int flags);
+extern int fuzzy_digest(const struct fuzzy_state *state,
+ /*@out@*/ char *result, unsigned int flags);
/**
* @brief Dispose a fuzzy state.
@@ -130,9 +127,8 @@ extern void fuzzy_free(/*@only@*/ struct fuzzy_state *state);
* must be allocated to hold at least FUZZY_MAX_RESULT bytes.
* @return Returns zero on success, non-zero on error.
*/
-extern int fuzzy_hash_buf(const unsigned char *buf,
- uint32_t buf_len,
- /*@out@*/ char *result);
+extern int fuzzy_hash_buf(const unsigned char *buf, uint32_t buf_len,
+ /*@out@*/ char *result);
/**
* @brief Compute the fuzzy hash of a file using an open handle
@@ -148,7 +144,7 @@ extern int fuzzy_hash_buf(const unsigned char *buf,
* variable must be allocated to hold at least FUZZY_MAX_RESULT bytes.
* @return Returns zero on success, non-zero on error
*/
-extern int fuzzy_hash_file(FILE *handle, /*@out@*/ char *result);
+extern int fuzzy_hash_file(FILE *handle, /*@out@*/ char *result);
/**
* @brief Compute the fuzzy hash of a stream using an open handle
@@ -164,7 +160,7 @@ extern int fuzzy_hash_file(FILE *handle, /*@out@*/ char *result);
* variable must be allocated to hold at least FUZZY_MAX_RESULT bytes.
* @return Returns zero on success, non-zero on error
*/
-extern int fuzzy_hash_stream(FILE *handle, /*@out@*/ char *result);
+extern int fuzzy_hash_stream(FILE *handle, /*@out@*/ char *result);
/**
* @brief Compute the fuzzy hash of a file
@@ -178,7 +174,7 @@ extern int fuzzy_hash_stream(FILE *handle, /*@out@*/ char *result);
* variable must be allocated to hold at least FUZZY_MAX_RESULT bytes.
* @return Returns zero on success, non-zero on error.
*/
-extern int fuzzy_hash_filename(const char *filename, /*@out@*/ char * result);
+extern int fuzzy_hash_filename(const char *filename, /*@out@*/ char *result);
/// Computes the match score between two fuzzy hash signatures.
/// @return Returns a value from zero to 100 indicating the
@@ -186,7 +182,7 @@ extern int fuzzy_hash_filename(const char *filename, /*@out@*/ char * result);
/// two signatures. A match score of zero indicates the sigantures
/// did not match. When an error occurs, such as if one of the
/// inputs is NULL, returns -1.
-extern int fuzzy_compare(const char *sig1, const char *sig2);
+extern int fuzzy_compare(const char *sig1, const char *sig2);
/** Length of an individual fuzzy hash signature component. */
#define SPAMSUM_LENGTH 64
@@ -200,3 +196,4 @@ extern int fuzzy_compare(const char *sig1, const char *sig2);
#endif
#endif
+
diff --git a/lib/libpe/CMakeLists.txt b/lib/libpe/CMakeLists.txt
new file mode 100644
index 00000000..2c7f55ab
--- /dev/null
+++ b/lib/libpe/CMakeLists.txt
@@ -0,0 +1,163 @@
+project(libpe LANGUAGES C VERSION ${readpe_VERSION})
+
+include(CMakePackageConfigHelpers)
+include(GNUInstallDirs)
+
+set(HEADERS
+ ${CMAKE_SOURCE_DIR}/include/libpe/context.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/directories.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/dir_import.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/dir_resources.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/dir_security.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/error.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/exports.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/hashes.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/hdr_dos.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/hdr_coff.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/hdr_optional.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/imports.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/macros.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/ordlookup.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/pe.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/resources.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/sections.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/types_resources.h
+ ${CMAKE_SOURCE_DIR}/include/libpe/utils.h
+)
+
+set(SOURCES
+ error.c
+ exports.c
+ hashes.c
+ imports.c
+ misc.c
+ pe.c
+ resources.c
+ security.c
+ utils.c
+)
+
+set(LIBPE_LINKING SHARED)
+if(LIBPE_BUILD_STATIC)
+ set(LIBPE_LINKING STATIC)
+endif()
+
+find_package(OpenSSL REQUIRED COMPONENTS Crypto)
+
+add_library(pe ${LIBPE_LINKING} ${HEADERS} ${SOURCES})
+add_library(readpe::libpe ALIAS pe)
+
+target_link_libraries(pe PRIVATE
+ OpenSSL::Crypto
+ readpe::compat
+ uthash
+)
+
+target_include_directories(pe PRIVATE
+ "${CMAKE_SOURCE_DIR}/include"
+)
+
+target_include_directories(pe
+ PUBLIC
+ $
+ $
+)
+
+# Warnings for better code
+if(MSVC)
+ target_compile_options(pe PRIVATE
+ /W4
+ )
+elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
+ target_link_libraries(pe PRIVATE m)
+ target_compile_options(pe PRIVATE
+ -Wall
+ -Wextra
+ -Wpedantic
+ -pedantic
+ -Wshadow
+ -Wundef
+ -Wdouble-promotion
+ -Wformat=2
+ -Wformat-security
+ -Wconversion
+ )
+endif()
+
+# Make sure to call it libpe.dll under windows
+if(WIN32)
+ set_target_properties(pe PROPERTIES PREFIX "lib")
+endif()
+
+# Slurp file instead of memory mapping
+if(LIBPE_SLURP_PE_FILE)
+ target_compile_definitions(pe PRIVATE
+ -D_SLURP_PE_FILE
+ )
+endif()
+
+# Link in libfuzzy (GPL)
+if(LIBPE_LINK_SSDEEP)
+ target_link_libraries(pe PRIVATE fuzzy)
+ target_compile_definitions(pe PRIVATE
+ -DLIBPE_LINK_SSDEEP=1
+ )
+endif()
+
+# Install libpe
+install(TARGETS pe
+ EXPORT libpe-targets
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+# Install libpe headers
+if(LIBPE_PACK_HEADERS)
+ install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/libpe
+ DESTINATION include/
+ FILES_MATCHING
+ PATTERN "*.h"
+ )
+endif()
+
+# Install cmake files for linking against libpe (find_package)
+if(LIBPE_PACK_CMAKE_FILES)
+ export(EXPORT libpe-targets
+ FILE "${CMAKE_CURRENT_BINARY_DIR}/libpe-targets.cmake"
+ NAMESPACE readpe::
+ )
+
+ configure_package_config_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/libpe-config.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/libpe-config.cmake
+ INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
+ PATH_VARS
+ CMAKE_INSTALL_INCLUDEDIR
+ NO_CHECK_REQUIRED_COMPONENTS_MACRO
+ )
+
+ install(EXPORT libpe-targets
+ FILE libpe-targets.cmake
+ NAMESPACE readpe::
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libpe
+ )
+
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libpe-config.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libpe
+ )
+endif()
+
+# Install pkg-config files as alternative of linking against libpe w\o cmake
+if(LIBPE_PACK_PKGCONF_FILES)
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/libpe.pc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/libpe.pc
+ @ONLY
+ )
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libpe.pc
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
+ )
+endif()
+
diff --git a/lib/libpe/Makefile b/lib/libpe/Makefile
deleted file mode 100644
index ad829475..00000000
--- a/lib/libpe/Makefile
+++ /dev/null
@@ -1,169 +0,0 @@
-####### Platform specifics
-
-# cut is necessary for Cygwin
-PLATFORM_OS := $(shell uname | cut -d_ -f1)
-
-####### Makefile Conventions - Directory variables
-
-srcdir = .
-prefix = /usr/local
-exec_prefix = $(prefix)
-sysconfdir = $(prefix)/etc
-includedir = $(prefix)/include
-datarootdir = $(prefix)/share
-localstatedir = $(prefix)/var
-bindir = $(exec_prefix)/bin
-libdir = $(exec_prefix)/lib
-libexecdir = $(exec_prefix)/libexec
-sbindir = $(exec_prefix)/sbin
-datadir = $(datarootdir)
-docdir = $(datarootdir)/doc/pev
-infodir = $(datarootdir)/info
-localedir = $(datarootdir)/locale
-
-mandir = $(datarootdir)/man
-manext = .1
-man1dir = $(mandir)/man1
-man1ext = .1
-
-####### Makefile Conventions - Utilities
-
-CC ?= gcc
-LINK = $(CC)
-CHK_DIR_EXISTS = test -d
-CHK_FILE_EXISTS = test -f
-INSTALL = install
-INSTALL_DATA = ${INSTALL} -m 644
-INSTALL_PROGRAM = $(INSTALL)
-SYMLINK = ln -sf
-MKDIR = mkdir -p
-RM = rm -f
-RM_DIR = rm -rf
-
-ifeq ($(PLATFORM_OS), Darwin)
- STRIP = strip -x
-else
- STRIP = strip --strip-unneeded
-endif
-
-####### Compiler options
-
-override CFLAGS += \
- -O2 -ffast-math \
- -I"./include" \
- -fPIC \
- -W -Wall -Wextra -pedantic -std=c99 -c
-
-#override LDFLAGS += -lssl -lcrypto
-LIBS = -lssl -lcrypto -lm
-
-# --- FIX: -fPIC is necessary to ALL shared objects! Changed above.
-#ifneq ($(PLATFORM_OS), CYGWIN)
-# override CFLAGS += -fPIC
-#endif
-
-VERSION = 0.85
-LIBNAME = libpe
-
-SRC_DIRS = $(srcdir) $(srcdir)/libfuzzy
-
-libpe_BUILDDIR = $(CURDIR)/build
-libpe_SRCS_FILTER = $(sort $(wildcard ${dir}/*.c))
-libpe_SRCS = $(foreach dir, ${SRC_DIRS}, ${libpe_SRCS_FILTER})
-libpe_OBJS = $(addprefix ${libpe_BUILDDIR}/, $(addsuffix .o, $(basename ${libpe_SRCS})))
-
-####### Build rules
-
-.PHONY : libpe install strip-binaries install-strip uninstall clean
-
-all: libpe
-
-# FIX: WARNING.. ld expects -l option at the END of the command line or after the object files.
-# From gcc's documentation:
-#
-# It makes a difference where in the command you write this option; the linker searches and processes
-# libraries and object files in the order they are specified. Thus, ‘foo.o -lz bar.o’ searches library
-# ‘z’ after file foo.o but before bar.o. If bar.o refers to functions in ‘z’, those functions
-# may not be loaded.
-#
-
-libpe: CPPFLAGS += -D_GNU_SOURCE
-ifeq ($(PLATFORM_OS), CYGWIN)
-libpe: CPPFLAGS += -D_XOPEN_SOURCE=600
-endif
-libpe: $(libpe_OBJS)
-ifeq ($(PLATFORM_OS), Linux)
- $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS)
-else ifeq ($(PLATFORM_OS), NetBSD)
- $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS)
-else ifeq ($(PLATFORM_OS), FreeBSD)
- $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS)
-else ifeq ($(PLATFORM_OS), OpenBSD)
- $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS)
-else ifeq ($(PLATFORM_OS), GNU)
- $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS)
-else ifeq ($(PLATFORM_OS), GNU/kFreeBSD)
- $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS)
-else ifeq ($(PLATFORM_OS), Darwin)
- $(LINK) -headerpad_max_install_names -dynamiclib \
- -flat_namespace -install_name $(libdir)/$(LIBNAME).$(VERSION).dylib \
- -current_version $(VERSION) -compatibility_version $(VERSION) \
- $(LDFLAGS) -o $(LIBNAME).dylib $^ $(LIBS)
-else ifeq ($(PLATFORM_OS), CYGWIN)
- $(LINK) -shared -o $(LIBNAME).dll $^ $(LDFLAGS) $(LIBS)
-endif
-
-$(libpe_BUILDDIR)/%.o: %.c
- @$(CHK_DIR_EXISTS) $(dir $@) || $(MKDIR) $(dir $@)
- $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
-
-install: installdirs
-ifeq ($(PLATFORM_OS), Linux)
- $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION)
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1
-else ifeq ($(PLATFORM_OS), NetBSD)
- $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION)
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1
-else ifeq ($(PLATFORM_OS), FreeBSD)
- $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION)
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1
-else ifeq ($(PLATFORM_OS), OpenBSD)
- $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION)
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1
-else ifeq ($(PLATFORM_OS), GNU)
- $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION)
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1
-else ifeq ($(PLATFORM_OS), GNU/kFreeBSD)
- $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION)
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1
-else ifeq ($(PLATFORM_OS), Darwin)
- $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).dylib $(DESTDIR)$(libdir)/$(LIBNAME).$(VERSION).dylib
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).$(VERSION).dylib $(LIBNAME).dylib
- cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).$(VERSION).dylib $(LIBNAME).1.dylib
-else ifeq ($(PLATFORM_OS), CYGWIN)
- # TODO
-endif
-
-installdirs:
- @$(CHK_DIR_EXISTS) $(DESTDIR) || $(MKDIR) $(DESTDIR)
- @$(CHK_DIR_EXISTS) $(DESTDIR)$(libdir) || $(MKDIR) $(DESTDIR)$(libdir)
-
-install-strip: INSTALL_FLAGS += -s
-install-strip: install
-
-uninstall:
- $(RM) $(DESTDIR)$(libdir)/$(LIBNAME).so* \
- $(DESTDIR)$(libdir)/$(LIBNAME)*.dylib
-
-clean:
- $(RM_DIR) $(libpe_BUILDDIR)
- $(RM) $(LIBNAME)*.o \
- $(LIBNAME)*.so \
- $(LIBNAME)*.dylib \
- $(LIBNAME)*.dll
diff --git a/lib/libpe/error.c b/lib/libpe/error.c
index 3caf1bb4..8fe8c2b1 100644
--- a/lib/libpe/error.c
+++ b/lib/libpe/error.c
@@ -20,9 +20,11 @@
*/
#include "libpe/error.h"
+
#include "libpe/macros.h"
-#include
+
#include
+#include
#include
// FIX: Since all errors, except the first,
@@ -32,103 +34,116 @@
//
// If you change pe_err_e in libpe/error.h, this
// this must be changed too.
-const char *pe_error_msg(pe_err_e error) {
- static const char * const errors[] = {
- // This is 0
- "no error", // LIBPE_E_OK,
-
- // FIX: Strings in reverse...
- // ALL those errors are 'negative' (as in libpe/error.h).
-
- // Misc
- "no functions found", //LIBPE_E_NO_FUNCIONS_FOUND
- "no callbacks found", //LIBPE_E_NO_CALLBACKS_FOUND
-
- // Hashes Errors
- "error calculating hash", // LIBPE_E_HASHING_FAILED
-
- // Exports errors
- "number of functions not equal to number of names", //LIBPE_E_EXPORTS_FUNC_NEQ_NAMES
- "cannot read exports directory", // LIBPE_E_EXPORTS_CANT_READ_DIR
- "cannot read relative virtual address", //LIBPE_E_EXPORTS_CANT_READ_RVA
-
- "type punning failed", // LIBPE_E_TYPE_PUNNING_FAILED
- "too many sections", // LIBPE_E_TOO_MANY_SECTIONS,
- "too many directories", // LIBPE_E_TOO_MANY_DIRECTORIES,
- "close() failed", // LIBPE_E_CLOSE_FAILED,
- "munmap() failed", // LIBPE_E_MUNMAP_FAILED,
- "mmap() failed", // LIBPE_E_MMAP_FAILED,
- "unsupported image format", // LIBPE_E_UNSUPPORTED_IMAGE,
- "invalid signature", // LIBPE_E_INVALID_SIGNATURE,
- "missing OPTIONAL header", // LIBPE_E_MISSING_OPTIONAL_HEADER,
- "missing COFF header", // LIBPE_E_MISSING_COFF_HEADER,
- "invalid e_lfanew", // LIBPE_E_INVALID_LFANEW,
- "not a PE file", // LIBPE_E_NOT_A_PE_FILE,
- "not a regular file", // LIBPE_E_NOT_A_FILE,
- "fstat() failed", // LIBPE_E_FSTAT_FAILED,
- "fdopen() failed", // LIBPE_E_FDOPEN_FAILED,
- "open() failed", // LIBPE_E_OPEN_FAILED,
- "allocation failure" // LIBPE_E_ALLOCATION_FAILURE,
- };
-
- // FIX: Convoluted way to use negative errors! The code below is easier and faster.
-// static const size_t index_max = LIBPE_SIZEOF_ARRAY(errors);
-// size_t index = index_max + error;
-// return (index < index_max)
-// ? errors[index]
-// : (index == index_max)
-// ? errors[0] // LIBPE_E_OK
-// : "invalid error code";
-
- unsigned int index = abs(error);
- if ( index >= LIBPE_SIZEOF_ARRAY(errors) )
- return "invalid error code";
- return errors[index];
+const char *pe_error_msg(pe_err_e error)
+{
+ static const char *const errors[] = {
+ // This is 0
+ "no error", // LIBPE_E_OK,
+
+ // FIX: Strings in reverse...
+ // ALL those errors are 'negative' (as in libpe/error.h).
+
+ // Misc
+ "no functions found", // LIBPE_E_NO_FUNCIONS_FOUND
+ "no callbacks found", // LIBPE_E_NO_CALLBACKS_FOUND
+
+ // Hashes Errors
+ "error calculating hash", // LIBPE_E_HASHING_FAILED
+
+ // Exports errors
+ "number of functions not equal to number of names", // LIBPE_E_EXPORTS_FUNC_NEQ_NAMES
+ "cannot read exports directory", // LIBPE_E_EXPORTS_CANT_READ_DIR
+ "cannot read relative virtual address", // LIBPE_E_EXPORTS_CANT_READ_RVA
+
+ "type punning failed", // LIBPE_E_TYPE_PUNNING_FAILED
+ "too many sections", // LIBPE_E_TOO_MANY_SECTIONS,
+ "too many directories", // LIBPE_E_TOO_MANY_DIRECTORIES,
+ "close() failed", // LIBPE_E_CLOSE_FAILED,
+ "munmap() failed", // LIBPE_E_MUNMAP_FAILED,
+ "mmap() failed", // LIBPE_E_MMAP_FAILED,
+ "unsupported image format", // LIBPE_E_UNSUPPORTED_IMAGE,
+ "invalid signature", // LIBPE_E_INVALID_SIGNATURE,
+ "missing OPTIONAL header", // LIBPE_E_MISSING_OPTIONAL_HEADER,
+ "missing COFF header", // LIBPE_E_MISSING_COFF_HEADER,
+ "invalid e_lfanew", // LIBPE_E_INVALID_LFANEW,
+ "not a PE file", // LIBPE_E_NOT_A_PE_FILE,
+ "not a regular file", // LIBPE_E_NOT_A_FILE,
+ "fstat() failed", // LIBPE_E_FSTAT_FAILED,
+ "fdopen() failed", // LIBPE_E_FDOPEN_FAILED,
+ "open() failed", // LIBPE_E_OPEN_FAILED,
+ "allocation failure" // LIBPE_E_ALLOCATION_FAILURE,
+ };
+
+ // FIX: Convoluted way to use negative errors! The code below is easier and
+ // faster.
+ // static const size_t index_max = LIBPE_SIZEOF_ARRAY(errors);
+ // size_t index = index_max + error;
+ // return (index < index_max)
+ // ? errors[index]
+ // : (index == index_max)
+ // ? errors[0] // LIBPE_E_OK
+ // : "invalid error code";
+
+ unsigned int index = (unsigned int) abs(error);
+ if (index >= LIBPE_SIZEOF_ARRAY(errors)) {
+ return "invalid error code";
+ }
+ return errors[index];
}
-void pe_error_print(FILE *stream, pe_err_e error) {
- if (errno == 0) {
- fprintf(stream, "ERROR [%d]: %s\n", error, pe_error_msg(error));
- } else {
- char errmsg[255];
-
- /*
- * Quotes from https://linux.die.net/man/3/strerror_r
- *
- * The strerror_r() function is similar to strerror(), but is thread safe. This function
- * is available in two versions: an XSI-compliant version specified in POSIX.1-2001
- * (available since glibc 2.3.4, but not POSIX-compliant until glibc 2.13), and a
- * GNU-specific version (available since glibc 2.0). The XSI-compliant version is provided
- * with the feature test macros settings shown in the SYNOPSIS; otherwise the GNU-specific
- * version is provided. If no feature test macros are explicitly defined, then (since
- * glibc 2.4) _POSIX_SOURCE is defined by default with the value 200112L, so that the
- * XSI-compliant version of strerror_r() is provided by default.
- *
- * The XSI-compliant strerror_r() is preferred for portable applications. It returns the
- * error string in the user-supplied buffer buf of length buflen.
- *
- * The GNU-specific strerror_r() returns a pointer to a string containing the error
- * message. This may be either a pointer to a string that the function stores in buf, or
- * a pointer to some (immutable) static string (in which case buf is unused). If the
- * function stores a string in buf, then at most buflen bytes are stored (the string may
- * be truncated if buflen is too small and errnum is unknown). The string always includes
- * a terminating null byte.
- */
-
- // Since we define _GNU_SOURCE in our Makefile, strerror_r should be GNU-compliant.
- // However, looks like if you're on macOS, strerror_r is XSI-compliant.
+void pe_error_print(FILE *stream, pe_err_e error)
+{
+ if (errno == 0) {
+ fprintf(stream, "ERROR [%d]: %s\n", error, pe_error_msg(error));
+ } else {
+ char errmsg[255];
+
+ /*
+ * Quotes from https://linux.die.net/man/3/strerror_r
+ *
+ * The strerror_r() function is similar to strerror(), but is thread
+ * safe. This function is available in two versions: an XSI-compliant
+ * version specified in POSIX.1-2001 (available since glibc 2.3.4, but
+ * not POSIX-compliant until glibc 2.13), and a GNU-specific version
+ * (available since glibc 2.0). The XSI-compliant version is provided
+ * with the feature test macros settings shown in the SYNOPSIS;
+ * otherwise the GNU-specific version is provided. If no feature test
+ * macros are explicitly defined, then (since glibc 2.4) _POSIX_SOURCE
+ * is defined by default with the value 200112L, so that the
+ * XSI-compliant version of strerror_r() is provided by default.
+ *
+ * The XSI-compliant strerror_r() is preferred for portable
+ * applications. It returns the error string in the user-supplied buffer
+ * buf of length buflen.
+ *
+ * The GNU-specific strerror_r() returns a pointer to a string
+ * containing the error message. This may be either a pointer to a
+ * string that the function stores in buf, or a pointer to some
+ * (immutable) static string (in which case buf is unused). If the
+ * function stores a string in buf, then at most buflen bytes are stored
+ * (the string may be truncated if buflen is too small and errnum is
+ * unknown). The string always includes a terminating null byte.
+ */
+
+ // Since we define _GNU_SOURCE in our Makefile, strerror_r should be
+ // GNU-compliant. However, looks like if you're on macOS, strerror_r is
+ // XSI-compliant.
#if defined(__DARWIN_C_LEVEL) // XSI-compliant
- /* int ret = */ strerror_r(errno, errmsg, sizeof errmsg);
- const char *errmsg_ptr = errmsg;
+ /* int ret = */ strerror_r(errno, errmsg, sizeof errmsg);
+ const char *errmsg_ptr = errmsg;
#elif defined(_GNU_SOURCE) // GNU-specific
- const char *errmsg_ptr = strerror_r(errno, errmsg, sizeof errmsg);
-#else // Fallback to XSI-compliant
- /* int ret = */ strerror_r(errno, errmsg, sizeof errmsg);
- const char *errmsg_ptr = errmsg;
+ const char *errmsg_ptr = strerror_r(errno, errmsg, sizeof errmsg);
+#elif defined(_MSC_VER) // C11
+ /* int ret = */ strerror_s(errmsg, sizeof(errmsg), errno);
+ const char *errmsg_ptr = errmsg;
+#else // Fallback to XSI-compliant
+ /* int ret = */ strerror_r(errno, errmsg, sizeof errmsg);
+ const char *errmsg_ptr = errmsg;
#endif
- fprintf(stream, "ERROR [%d]: %s (%s)\n", error, pe_error_msg(error),
- errmsg_ptr);
- }
+ fprintf(stream, "ERROR [%d]: %s (%s)\n", error, pe_error_msg(error),
+ errmsg_ptr);
+ }
}
+
diff --git a/lib/libpe/exports.c b/lib/libpe/exports.c
index 1de2dd0b..42acffda 100644
--- a/lib/libpe/exports.c
+++ b/lib/libpe/exports.c
@@ -21,209 +21,243 @@
#include "libpe/exports.h"
+#include "compat.h"
+#include "libpe/macros.h"
#include "libpe/pe.h"
+
+#include
#include
#include
-pe_exports_t *pe_exports(pe_ctx_t *ctx) {
- if (ctx->cached_data.exports != NULL)
- return ctx->cached_data.exports;
-
- pe_exports_t *exports = ctx->cached_data.exports = calloc(1, sizeof(pe_exports_t));
- if (exports == NULL) {
- // TODO(jweyrich): Should we report an error? If yes, we need a redesign.
- return NULL;
- }
-
- exports->err = LIBPE_E_OK;
-
- const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_EXPORT);
- if (dir == NULL) {
- return exports;
- }
-
- const uint64_t va = dir->VirtualAddress;
- if (va == 0) {
- // NOTE: This file has no exported symbols.
- return exports;
- }
-
- uint64_t ofs;
-
- ofs = pe_rva2ofs(ctx, va);
- const IMAGE_EXPORT_DIRECTORY *exp = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, exp, sizeof(IMAGE_EXPORT_DIRECTORY))) {
- exports->err = LIBPE_E_EXPORTS_CANT_READ_DIR;
- return exports;
- }
-
- ofs = pe_rva2ofs(ctx, exp->Name);
- const char *name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, name_ptr, 1)) {
- exports->err = LIBPE_E_EXPORTS_CANT_READ_RVA;
- return exports;
- }
-
- exports->name = strdup(name_ptr);
-
- const uint32_t ordinal_base = exp->Base;
-
- ofs = pe_rva2ofs(ctx, exp->AddressOfNames);
- const uint32_t *rva_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, rva_ptr, sizeof(uint32_t))) {
- exports->err = LIBPE_E_EXPORTS_CANT_READ_RVA;
- return exports;
- }
-
- // If `NumberOfNames == 0` then all functions are exported by ordinal.
- // Otherwise `NumberOfNames` should be equal to `NumberOfFunctions`
- // if (exp->NumberOfNames != 0 && exp->NumberOfNames != exp->NumberOfFunctions) {
- // exports->err = LIBPE_E_EXPORTS_FUNC_NEQ_NAMES;
- // return exports;
- // }
-
- //
- // The format of IMAGE_EXPORT_DIRECTORY can be seen in http://i.msdn.microsoft.com/dynimg/IC60608.gif
- //
-
- // We want to use `NumberOfFunctions` for looping as it's the total number of functions/symbols
- // exported by the module. On the other hand, `NumberOfNames` is the number of
- // functions/symbols exported by name only.
-
- exports->functions_count = exp->NumberOfFunctions;
- exports->functions = calloc(exp->NumberOfFunctions, sizeof(pe_exported_function_t));
- if (exports->functions == NULL) {
- exports->err = LIBPE_E_ALLOCATION_FAILURE;
- return exports;
- }
-
- const uint64_t offset_to_AddressOfFunctions = pe_rva2ofs(ctx, exp->AddressOfFunctions);
- const uint64_t offset_to_AddressOfNames = pe_rva2ofs(ctx, exp->AddressOfNames);
- const uint64_t offset_to_AddressOfNameOrdinals = pe_rva2ofs(ctx, exp->AddressOfNameOrdinals);
-
- uint64_t offsets_to_Names[exp->NumberOfFunctions];
- memset(offsets_to_Names, 0, sizeof(offsets_to_Names)); // This is needed for VLAs.
-
- uint32_t hint_name_indexes[exp->NumberOfFunctions];
- memset(hint_name_indexes, 0, sizeof(hint_name_indexes));
-
- //
- // Names
- //
-
- // Names table is indexed by hint (name index) number
- for (uint32_t i=0; i < exp->NumberOfNames; i++) {
- uint64_t entry_ordinal_list_ptr = offset_to_AddressOfNameOrdinals + sizeof(uint16_t) * i;
- uint16_t *entry_ordinal_list = LIBPE_PTR_ADD(ctx->map_addr, entry_ordinal_list_ptr);
-
- if (!pe_can_read(ctx, entry_ordinal_list, sizeof(uint16_t))) {
- // TODO: Should we report something?
- break;
- }
-
- // In this NameOrdinals table is stored unbiased ordinal number
- const uint16_t ordinal = *entry_ordinal_list;
-
- uint64_t entry_name_list_ptr = offset_to_AddressOfNames + sizeof(uint32_t) * i;
- uint32_t *entry_name_list = LIBPE_PTR_ADD(ctx->map_addr, entry_name_list_ptr);
-
- if (!pe_can_read(ctx, entry_name_list, sizeof(uint32_t))) {
- // TODO: Should we report something?
- break;
- }
-
- const uint32_t entry_name_rva = *entry_name_list;
- const uint64_t entry_name_ofs = pe_rva2ofs(ctx, entry_name_rva);
+pe_exports_t *pe_exports(pe_ctx_t *ctx)
+{
+ if (ctx->cached_data.exports != NULL) {
+ return ctx->cached_data.exports;
+ }
+
+ pe_exports_t *exports = ctx->cached_data.exports
+ = calloc(1, sizeof(pe_exports_t));
+ if (exports == NULL) {
+ // TODO(jweyrich): Should we report an error? If yes, we need a
+ // redesign.
+ return NULL;
+ }
+
+ exports->err = LIBPE_E_OK;
+
+ const IMAGE_DATA_DIRECTORY *dir
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_EXPORT);
+ if (dir == NULL) {
+ return exports;
+ }
+
+ const uint64_t va = dir->VirtualAddress;
+ if (va == 0) {
+ // NOTE: This file has no exported symbols.
+ return exports;
+ }
+
+ uint64_t ofs;
+
+ ofs = pe_rva2ofs(ctx, va);
+ const IMAGE_EXPORT_DIRECTORY *exp = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, exp, sizeof(IMAGE_EXPORT_DIRECTORY))) {
+ exports->err = LIBPE_E_EXPORTS_CANT_READ_DIR;
+ return exports;
+ }
+
+ ofs = pe_rva2ofs(ctx, exp->Name);
+ const char *name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, name_ptr, 1)) {
+ exports->err = LIBPE_E_EXPORTS_CANT_READ_RVA;
+ return exports;
+ }
+
+ exports->name = readpe_strdup(name_ptr);
+
+ const uint32_t ordinal_base = exp->Base;
+
+ ofs = pe_rva2ofs(ctx, exp->AddressOfNames);
+ const uint32_t *rva_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, rva_ptr, sizeof(uint32_t))) {
+ exports->err = LIBPE_E_EXPORTS_CANT_READ_RVA;
+ return exports;
+ }
+
+ // If `NumberOfNames == 0` then all functions are exported by ordinal.
+ // Otherwise `NumberOfNames` should be equal to `NumberOfFunctions`
+ // if (exp->NumberOfNames != 0 && exp->NumberOfNames !=
+ // exp->NumberOfFunctions) { exports->err = LIBPE_E_EXPORTS_FUNC_NEQ_NAMES;
+ // return exports;
+ // }
+
+ //
+ // The format of IMAGE_EXPORT_DIRECTORY can be seen in
+ // http://i.msdn.microsoft.com/dynimg/IC60608.gif
+ //
+
+ // We want to use `NumberOfFunctions` for looping as it's the total number
+ // of functions/symbols exported by the module. On the other hand,
+ // `NumberOfNames` is the number of functions/symbols exported by name only.
+
+ exports->functions_count = exp->NumberOfFunctions;
+ exports->functions
+ = calloc(exp->NumberOfFunctions, sizeof(pe_exported_function_t));
+ if (exports->functions == NULL) {
+ exports->err = LIBPE_E_ALLOCATION_FAILURE;
+ return exports;
+ }
+
+ const uint64_t offset_to_AddressOfFunctions
+ = pe_rva2ofs(ctx, exp->AddressOfFunctions);
+ const uint64_t offset_to_AddressOfNames
+ = pe_rva2ofs(ctx, exp->AddressOfNames);
+ const uint64_t offset_to_AddressOfNameOrdinals
+ = pe_rva2ofs(ctx, exp->AddressOfNameOrdinals);
+
+ uint64_t *offsets_to_Names
+ = calloc(exp->NumberOfFunctions, sizeof(uint64_t));
+ // uint64_t offsets_to_Names[exp->NumberOfFunctions];
+ // memset(offsets_to_Names, 0,
+ // sizeof(offsets_to_Names)); // This is needed for VLAs.
+
+ uint32_t *hint_name_indexes
+ = calloc(exp->NumberOfFunctions, sizeof(uint32_t));
+ // uint32_t hint_name_indexes[exp->NumberOfFunctions];
+ // memset(hint_name_indexes, 0, sizeof(hint_name_indexes));
+
+ //
+ // Names
+ //
+
+ // Names table is indexed by hint (name index) number
+ for (uint32_t i = 0; i < exp->NumberOfNames; i++) {
+ uint64_t entry_ordinal_list_ptr
+ = offset_to_AddressOfNameOrdinals + sizeof(uint16_t) * i;
+ uint16_t *entry_ordinal_list
+ = LIBPE_PTR_ADD(ctx->map_addr, entry_ordinal_list_ptr);
+
+ if (! pe_can_read(ctx, entry_ordinal_list, sizeof(uint16_t))) {
+ // TODO: Should we report something?
+ break;
+ }
+
+ // In this NameOrdinals table is stored unbiased ordinal number
+ const uint16_t ordinal = *entry_ordinal_list;
+
+ uint64_t entry_name_list_ptr
+ = offset_to_AddressOfNames + sizeof(uint32_t) * i;
+ uint32_t *entry_name_list
+ = LIBPE_PTR_ADD(ctx->map_addr, entry_name_list_ptr);
+
+ if (! pe_can_read(ctx, entry_name_list, sizeof(uint32_t))) {
+ // TODO: Should we report something?
+ break;
+ }
+
+ const uint32_t entry_name_rva = *entry_name_list;
+ const uint64_t entry_name_ofs = pe_rva2ofs(ctx, entry_name_rva);
if (ordinal < exp->NumberOfFunctions) {
- offsets_to_Names[ordinal] = entry_name_ofs;
+ offsets_to_Names[ordinal] = entry_name_ofs;
hint_name_indexes[ordinal] = i;
}
- }
-
- //
- // Functions
- //
-
- // Functions table is indexed by unbiased ordinal number
- for (uint32_t i=0; i < exp->NumberOfFunctions; i++) {
- uint64_t entry_va_list_ptr = offset_to_AddressOfFunctions + sizeof(uint32_t) * i;
- uint32_t *entry_va_list = LIBPE_PTR_ADD(ctx->map_addr, entry_va_list_ptr);
-
- if (!pe_can_read(ctx, entry_va_list, sizeof(uint32_t))) {
- break;
- }
-
- // Add `Base` to the element of `AddressOfNameOrdinals` array to get the correct ordinal..
- //const uint16_t entry_ordinal = exp->Base + *entry_ordinal_list;
- const uint32_t entry_va = *entry_va_list;
- const uint64_t entry_name_ofs = offsets_to_Names[i];
-
- // FIX: Don't need to zero all elements!
- // FIXME: 300 bytes is enough or too much?
- char fname[300];
- fname[0] = 0;
-
- if (entry_name_ofs != 0) {
- const char *entry_name = LIBPE_PTR_ADD(ctx->map_addr, entry_name_ofs);
-
- // Validate whether it's ok to access at least 1 byte after entry_name.
- // It might be '\0', for example.
- if (!pe_can_read(ctx, entry_name, 1)) {
- break;
- }
-
- //printf("ord=%d, va=%x, name=%s\n", entry_ordinal, entry_va, entry_name);
-
- const size_t fname_size = sizeof(fname);
- strncpy(fname, entry_name, fname_size-1);
- // Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
- fname[fname_size - 1] = '\0';
- }
-
- exports->functions[i].ordinal = ordinal_base + i;
- exports->functions[i].hint = hint_name_indexes[i];
- exports->functions[i].address = entry_va;
-
- exports->functions[i].name = strdup(fname);
- if (exports->functions[i].name == NULL) {
- exports->err = LIBPE_E_ALLOCATION_FAILURE;
- return exports;
- }
-
- // Check whether the exported function is forwarded.
- // It's forwarded if its RVA is inside the exports section.
- if (entry_va >= va && entry_va <= va + dir->Size) {
- // When a symbol is forwarded, its RVA points to a string containing
- // the name of the DLL and symbol to which it is forwarded.
- const uint64_t fw_entry_name_ofs = pe_rva2ofs(ctx, entry_va);
- const char *fw_entry_name = LIBPE_PTR_ADD(ctx->map_addr, fw_entry_name_ofs);
-
- // Validate whether it's ok to access at least 1 byte after fw_entry_name.
- // It might be '\0', for example.
- if (!pe_can_read(ctx, fw_entry_name, 1)) {
- break;
- }
-
- exports->functions[i].fwd_name = strdup(fw_entry_name);
- if (exports->functions[i].fwd_name == NULL) {
- exports->err = LIBPE_E_ALLOCATION_FAILURE;
- return exports;
- }
- }
- }
-
- return exports;
+ }
+
+ //
+ // Functions
+ //
+
+ // Functions table is indexed by unbiased ordinal number
+ for (uint32_t i = 0; i < exp->NumberOfFunctions; i++) {
+ uint64_t entry_va_list_ptr
+ = offset_to_AddressOfFunctions + sizeof(uint32_t) * i;
+ uint32_t *entry_va_list
+ = LIBPE_PTR_ADD(ctx->map_addr, entry_va_list_ptr);
+
+ if (! pe_can_read(ctx, entry_va_list, sizeof(uint32_t))) {
+ break;
+ }
+
+ // Add `Base` to the element of `AddressOfNameOrdinals` array to get the
+ // correct ordinal..
+ // const uint16_t entry_ordinal = exp->Base + *entry_ordinal_list;
+ const uint32_t entry_va = *entry_va_list;
+ const uint64_t entry_name_ofs = offsets_to_Names[i];
+
+ // FIX: Don't need to zero all elements!
+ // FIXME: 300 bytes is enough or too much?
+ char fname[300];
+ fname[0] = 0;
+
+ if (entry_name_ofs != 0) {
+ const char *entry_name
+ = LIBPE_PTR_ADD(ctx->map_addr, entry_name_ofs);
+
+ // Validate whether it's ok to access at least 1 byte after
+ // entry_name. It might be '\0', for example.
+ if (! pe_can_read(ctx, entry_name, 1)) {
+ break;
+ }
+
+ // printf("ord=%d, va=%x, name=%s\n", entry_ordinal, entry_va,
+ // entry_name);
+
+ const size_t fname_size = sizeof(fname);
+ strncpy(fname, entry_name, fname_size - 1);
+ // Because `strncpy` does not guarantee to NUL terminate the string
+ // itself, this must be done explicitly.
+ fname[fname_size - 1] = '\0';
+ }
+
+ exports->functions[i].ordinal = ordinal_base + i;
+ exports->functions[i].hint = hint_name_indexes[i];
+ exports->functions[i].address = entry_va;
+
+ exports->functions[i].name = readpe_strdup(fname);
+ if (exports->functions[i].name == NULL) {
+ exports->err = LIBPE_E_ALLOCATION_FAILURE;
+ return exports;
+ }
+
+ // Check whether the exported function is forwarded.
+ // It's forwarded if its RVA is inside the exports section.
+ if (entry_va >= va && entry_va <= va + dir->Size) {
+ // When a symbol is forwarded, its RVA points to a string containing
+ // the name of the DLL and symbol to which it is forwarded.
+ const uint64_t fw_entry_name_ofs = pe_rva2ofs(ctx, entry_va);
+ const char *fw_entry_name
+ = LIBPE_PTR_ADD(ctx->map_addr, fw_entry_name_ofs);
+
+ // Validate whether it's ok to access at least 1 byte after
+ // fw_entry_name. It might be '\0', for example.
+ if (! pe_can_read(ctx, fw_entry_name, 1)) {
+ break;
+ }
+
+ exports->functions[i].fwd_name = readpe_strdup(fw_entry_name);
+ if (exports->functions[i].fwd_name == NULL) {
+ exports->err = LIBPE_E_ALLOCATION_FAILURE;
+ return exports;
+ }
+ }
+ }
+
+ return exports;
}
-void pe_exports_dealloc(pe_exports_t *obj) {
- if (obj == NULL)
- return;
+void pe_exports_dealloc(pe_exports_t *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
- for (uint32_t i=0; i < obj->functions_count; i++)
- free(obj->functions[i].name);
+ for (uint32_t i = 0; i < obj->functions_count; i++) {
+ free(obj->functions[i].name);
+ }
- free(obj->functions);
- free(obj->name);
- free(obj);
+ free(obj->functions);
+ free(obj->name);
+ free(obj);
}
+
diff --git a/lib/libpe/hashes.c b/lib/libpe/hashes.c
index 23cce4c4..97fed74b 100644
--- a/lib/libpe/hashes.c
+++ b/lib/libpe/hashes.c
@@ -21,742 +21,836 @@
#include "libpe/hashes.h"
+#include "compat.h"
+#include "libpe/dir_import.h"
#include "libpe/error.h"
-#include "libpe/pe.h"
-#include "libfuzzy/fuzzy.h"
+#include "libpe/macros.h"
#include "libpe/ordlookup.h"
-#include "libpe/utlist.h"
+#include "libpe/pe.h"
+#include
+#include
+#include
#include
#include
-#include
-#include
+#include
#include
-#include
+#include
+
+#ifdef LIBPE_LINK_SSDEEP
+#include "../fuzzy/fuzzy.h"
+#endif
// add utility
-#define PEV_ABORT_IF(cond) \
- do { (cond) ? abort() : (void)0; } while (0)
+#define PEV_ABORT_IF(cond) \
+ do { \
+ (cond) ? abort() : (void) 0; \
+ } while (0)
/* By liw. */
-static char *last_strstr(char *haystack, const char *needle) {
- if (needle == NULL || *needle == '\0')
- return haystack;
-
- char *result = NULL;
- for (;;) {
- char *p = strstr(haystack, needle);
- if (p == NULL)
- break;
- result = p;
- haystack = p + 1;
- }
-
- return result;
+static char *last_strstr(char *haystack, const char *needle)
+{
+ if (needle == NULL || *needle == '\0') {
+ return haystack;
+ }
+
+ char *result = NULL;
+ for (;;) {
+ char *p = strstr(haystack, needle);
+ if (p == NULL) {
+ break;
+ }
+ result = p;
+ haystack = p + 1;
+ }
+
+ return result;
}
-static pe_err_e get_hashes(pe_hash_t *output, const char *name, const unsigned char *data, size_t data_size) {
- pe_err_e ret = LIBPE_E_OK;
-
- const size_t hash_maxsize = pe_hash_recommended_size();
- char *hash_value = calloc(1, hash_maxsize);
- if (hash_value == NULL) {
- ret = LIBPE_E_ALLOCATION_FAILURE;
- goto error;
- }
-
- output->name = strdup(name);
- if (output->name == NULL) {
- ret = LIBPE_E_ALLOCATION_FAILURE;
- goto error;
- }
-
- bool hash_ok;
-
- hash_ok = pe_hash_raw_data(hash_value, hash_maxsize, "md5", data, data_size);
- if (!hash_ok) {
- ret = LIBPE_E_HASHING_FAILED;
- goto error;
- }
- output->md5 = strdup(hash_value);
- if (output->md5 == NULL) {
- ret = LIBPE_E_ALLOCATION_FAILURE;
- goto error;
- }
-
- hash_ok = pe_hash_raw_data(hash_value, hash_maxsize, "sha1", data, data_size);
- if (!hash_ok) {
- ret = LIBPE_E_HASHING_FAILED;
- goto error;
- }
- output->sha1 = strdup(hash_value);
- if (output->sha1 == NULL) {
- ret = LIBPE_E_ALLOCATION_FAILURE;
- goto error;
- }
-
- hash_ok = pe_hash_raw_data(hash_value, hash_maxsize, "sha256", data, data_size);
- if (!hash_ok) {
- ret = LIBPE_E_HASHING_FAILED;
- goto error;
- }
- output->sha256 = strdup(hash_value);
- if (output->sha256 == NULL) {
- ret = LIBPE_E_ALLOCATION_FAILURE;
- goto error;
- }
-
- hash_ok = pe_hash_raw_data(hash_value, hash_maxsize, "ssdeep", data, data_size);
- if (!hash_ok) {
- ret = LIBPE_E_HASHING_FAILED;
- goto error;
- }
- output->ssdeep = strdup(hash_value);
- if (output->ssdeep == NULL) {
- ret = LIBPE_E_ALLOCATION_FAILURE;
- goto error;
- }
+static pe_err_e get_hashes(pe_hash_t *output, const char *name,
+ const unsigned char *data, size_t data_size)
+{
+ pe_err_e ret = LIBPE_E_OK;
+
+ const size_t hash_maxsize = pe_hash_recommended_size();
+ char *hash_value = calloc(1, hash_maxsize);
+ if (hash_value == NULL) {
+ ret = LIBPE_E_ALLOCATION_FAILURE;
+ goto error;
+ }
+
+ output->name = readpe_strdup(name);
+ if (output->name == NULL) {
+ ret = LIBPE_E_ALLOCATION_FAILURE;
+ goto error;
+ }
+
+ bool hash_ok;
+
+ hash_ok
+ = pe_hash_raw_data(hash_value, hash_maxsize, "md5", data, data_size);
+ if (! hash_ok) {
+ ret = LIBPE_E_HASHING_FAILED;
+ goto error;
+ }
+ output->md5 = readpe_strdup(hash_value);
+ if (output->md5 == NULL) {
+ ret = LIBPE_E_ALLOCATION_FAILURE;
+ goto error;
+ }
+
+ hash_ok
+ = pe_hash_raw_data(hash_value, hash_maxsize, "sha1", data, data_size);
+ if (! hash_ok) {
+ ret = LIBPE_E_HASHING_FAILED;
+ goto error;
+ }
+ output->sha1 = readpe_strdup(hash_value);
+ if (output->sha1 == NULL) {
+ ret = LIBPE_E_ALLOCATION_FAILURE;
+ goto error;
+ }
+
+ hash_ok
+ = pe_hash_raw_data(hash_value, hash_maxsize, "sha256", data, data_size);
+ if (! hash_ok) {
+ ret = LIBPE_E_HASHING_FAILED;
+ goto error;
+ }
+ output->sha256 = readpe_strdup(hash_value);
+ if (output->sha256 == NULL) {
+ ret = LIBPE_E_ALLOCATION_FAILURE;
+ goto error;
+ }
+
+ hash_ok
+ = pe_hash_raw_data(hash_value, hash_maxsize, "ssdeep", data, data_size);
+ if (! hash_ok) {
+ ret = LIBPE_E_HASHING_FAILED;
+ goto error;
+ }
+ output->ssdeep = readpe_strdup(hash_value);
+ if (output->ssdeep == NULL) {
+ ret = LIBPE_E_ALLOCATION_FAILURE;
+ goto error;
+ }
error:
- free(hash_value);
- return ret;
+ free(hash_value);
+ return ret;
}
-static pe_err_e get_headers_dos_hash(pe_ctx_t *ctx, pe_hash_t *output) {
- const IMAGE_DOS_HEADER *sample = pe_dos(ctx);
- const unsigned char *data = (const unsigned char *)sample;
- const uint64_t data_size = sizeof(IMAGE_DOS_HEADER);
- return get_hashes(output, "IMAGE_DOS_HEADER", data, data_size);
+static pe_err_e get_headers_dos_hash(pe_ctx_t *ctx, pe_hash_t *output)
+{
+ const IMAGE_DOS_HEADER *sample = pe_dos(ctx);
+ const unsigned char *data = (const unsigned char *) sample;
+ const uint64_t data_size = sizeof(IMAGE_DOS_HEADER);
+ return get_hashes(output, "IMAGE_DOS_HEADER", data, data_size);
}
-static pe_err_e get_headers_coff_hash(pe_ctx_t *ctx, pe_hash_t *output) {
- const IMAGE_COFF_HEADER *sample = pe_coff(ctx);
- const unsigned char *data = (const unsigned char *)sample;
- const uint64_t data_size = sizeof(IMAGE_COFF_HEADER);
- return get_hashes(output, "IMAGE_COFF_HEADER", data, data_size);
+static pe_err_e get_headers_coff_hash(pe_ctx_t *ctx, pe_hash_t *output)
+{
+ const IMAGE_COFF_HEADER *sample = pe_coff(ctx);
+ const unsigned char *data = (const unsigned char *) sample;
+ const uint64_t data_size = sizeof(IMAGE_COFF_HEADER);
+ return get_hashes(output, "IMAGE_COFF_HEADER", data, data_size);
}
-static pe_err_e get_headers_optional_hash(pe_ctx_t *ctx, pe_hash_t *output) {
- const IMAGE_OPTIONAL_HEADER *sample = pe_optional(ctx);
-
- switch (sample->type) {
- case MAGIC_ROM:
- {
- const unsigned char *data = (const unsigned char *)sample->_rom;
- const uint64_t data_size = sizeof(IMAGE_ROM_OPTIONAL_HEADER);
- return get_hashes(output, "IMAGE_ROM_OPTIONAL_HEADER", data, data_size);
- }
- case MAGIC_PE32_0:
- case MAGIC_PE32:
- {
- const unsigned char *data = (const unsigned char *)sample->_32;
- const uint64_t data_size = sizeof(IMAGE_OPTIONAL_HEADER_32);
- return get_hashes(output, "IMAGE_OPTIONAL_HEADER_32", data, data_size);
- }
- case MAGIC_PE64:
- {
- const unsigned char *data = (const unsigned char *)sample->_64;
- const uint64_t data_size = sizeof(IMAGE_OPTIONAL_HEADER_64);
- return get_hashes(output, "IMAGE_OPTIONAL_HEADER_64", data, data_size);
- }
- default:
- return LIBPE_E_UNSUPPORTED_IMAGE;
- }
+static pe_err_e get_headers_optional_hash(pe_ctx_t *ctx, pe_hash_t *output)
+{
+ const IMAGE_OPTIONAL_HEADER *sample = pe_optional(ctx);
+
+ switch (sample->type) {
+ case MAGIC_ROM: {
+ const unsigned char *data = (const unsigned char *) sample->_rom;
+ const uint64_t data_size = sizeof(IMAGE_ROM_OPTIONAL_HEADER);
+ return get_hashes(output, "IMAGE_ROM_OPTIONAL_HEADER", data, data_size);
+ }
+ case MAGIC_PE32_0:
+ case MAGIC_PE32: {
+ const unsigned char *data = (const unsigned char *) sample->_32;
+ const uint64_t data_size = sizeof(IMAGE_OPTIONAL_HEADER_32);
+ return get_hashes(output, "IMAGE_OPTIONAL_HEADER_32", data, data_size);
+ }
+ case MAGIC_PE64: {
+ const unsigned char *data = (const unsigned char *) sample->_64;
+ const uint64_t data_size = sizeof(IMAGE_OPTIONAL_HEADER_64);
+ return get_hashes(output, "IMAGE_OPTIONAL_HEADER_64", data, data_size);
+ }
+ default:
+ return LIBPE_E_UNSUPPORTED_IMAGE;
+ }
}
// FIX: Don't need to allocate space for these constants!
+// As of SHA512
+// #define EVP_MAX_MD_SIZE 64
+// 64 * 2 + 1 = 129
#define G_OPENSSL_HASH_MAXSIZE (EVP_MAX_MD_SIZE * 2 + 1)
-#define G_SSDEEP_HASH_MAXSIZE (FUZZY_MAX_RESULT)
-size_t pe_hash_recommended_size(void) {
- // Since standard C lacks max(), we do it manually.
- const size_t result = G_OPENSSL_HASH_MAXSIZE > G_SSDEEP_HASH_MAXSIZE
- ? G_OPENSSL_HASH_MAXSIZE
- : G_SSDEEP_HASH_MAXSIZE;
+#ifdef LIBPE_LINK_SSDEEP
+// #define SPAMSUM_LENGTH 64
+// #define FUZZY_MAX_RESULT (2 * SPAMSUM_LENGTH + 20)
+// 2 * 64 + 20 = 148
+#define G_SSDEEP_HASH_MAXSIZE (FUZZY_MAX_RESULT)
+#endif
- return result;
+size_t pe_hash_recommended_size(void)
+{
+#ifdef LIBPE_LINK_SSDEEP
+ // Since standard C lacks max(), we do it manually.
+ const size_t result = G_OPENSSL_HASH_MAXSIZE > G_SSDEEP_HASH_MAXSIZE
+ ? G_OPENSSL_HASH_MAXSIZE
+ : G_SSDEEP_HASH_MAXSIZE;
+#else
+ const size_t result = G_OPENSSL_HASH_MAXSIZE;
+#endif
+ return result;
}
// add function to tranforms set of bytes in hex equivalente into output string
-static void to_hex_str(const uint8_t* input, char* output, size_t n)
+static void to_hex_str(const uint8_t *input, char *output, size_t n)
{
- for (const uint8_t* input_ptr = input; n; --n, ++input_ptr)
- {
- unsigned b = (*input_ptr);
- *output++ = "0123456789abcdef"[b >> 4];
- *output++ = "0123456789abcdef"[b & 0xf];
- }
- *output = '\0';
+ for (const uint8_t *input_ptr = input; n; --n, ++input_ptr) {
+ unsigned b = (*input_ptr);
+ *output++ = "0123456789abcdef"[b >> 4];
+ *output++ = "0123456789abcdef"[b & 0xf];
+ }
+ *output = '\0';
}
-bool pe_hash_raw_data(char *output, size_t output_size, const char *alg_name, const unsigned char *data, size_t data_size) {
- if (strcmp("ssdeep", alg_name) == 0) {
- if (output_size < G_SSDEEP_HASH_MAXSIZE) {
- // Not enough space.
- return false;
- }
-
- fuzzy_hash_buf(data, data_size, output);
- return true;
- }
+bool pe_hash_raw_data(char *output, size_t output_size, const char *alg_name,
+ const unsigned char *data, size_t data_size)
+{
+#ifdef LIBPE_LINK_SSDEEP
+ if (strcmp("ssdeep", alg_name) == 0) {
+ if (output_size < G_SSDEEP_HASH_MAXSIZE) {
+ // Not enough space.
+ return false;
+ }
+
+ fuzzy_hash_buf(data, (uint32_t) data_size, output);
+ return true;
+ }
+#endif
- if (output_size < G_OPENSSL_HASH_MAXSIZE) {
- // Not enough space.
- return false;
- }
+ if (output_size < G_OPENSSL_HASH_MAXSIZE) {
+ // Not enough space.
+ return false;
+ }
- const EVP_MD *md = EVP_get_digestbyname(alg_name);
- if (md == NULL) {
- // Unsupported hash algorithm.
- return false;
- }
+ const EVP_MD *md = EVP_get_digestbyname(alg_name);
+ if (md == NULL) {
+ // Unsupported hash algorithm.
+ return false;
+ }
- unsigned char md_value[EVP_MAX_MD_SIZE];
- unsigned int md_len;
+ unsigned char md_value[EVP_MAX_MD_SIZE];
+ unsigned int md_len;
// See https://wiki.openssl.org/index.php/1.1_API_Changes
#if OPENSSL_VERSION_NUMBER < 0x10100000L
- EVP_MD_CTX md_ctx_auto;
- EVP_MD_CTX *md_ctx = &md_ctx_auto;
+ EVP_MD_CTX md_ctx_auto;
+ EVP_MD_CTX *md_ctx = &md_ctx_auto;
#else
- EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
+ EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
#endif
- // FIXME: Handle errors - Check return values.
- EVP_MD_CTX_init(md_ctx);
- EVP_DigestInit_ex(md_ctx, md, NULL);
- EVP_DigestUpdate(md_ctx, data, data_size);
- EVP_DigestFinal_ex(md_ctx, md_value, &md_len);
+ // FIXME: Handle errors - Check return values.
+ EVP_MD_CTX_init(md_ctx);
+ EVP_DigestInit_ex(md_ctx, md, NULL);
+ EVP_DigestUpdate(md_ctx, data, data_size);
+ EVP_DigestFinal_ex(md_ctx, md_value, &md_len);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
- EVP_MD_CTX_cleanup(md_ctx);
+ EVP_MD_CTX_cleanup(md_ctx);
#else
- EVP_MD_CTX_free(md_ctx);
+ EVP_MD_CTX_free(md_ctx);
#endif
- // FIX: Better than going through all the input calculating the byte2hex of each byte.
- to_hex_str(md_value, output, md_len);
- return true;
+ // FIX: Better than going through all the input calculating the byte2hex of
+ // each byte.
+ to_hex_str(md_value, output, md_len);
+ return true;
}
-pe_hash_headers_t *pe_get_headers_hashes(pe_ctx_t *ctx) {
- if (ctx->cached_data.hash_headers != NULL)
- return ctx->cached_data.hash_headers;
-
- pe_hash_headers_t *result = ctx->cached_data.hash_headers = calloc(1, sizeof(pe_hash_headers_t));
- if (result == NULL) {
- // TODO(jweyrich): Should we report an error? If yes, we need a redesign.
- return NULL;
- }
-
- result->err = LIBPE_E_OK;
-
- pe_err_e status = LIBPE_E_OK;
-
- result->dos = malloc(sizeof(pe_hash_t));
- if (result->dos == NULL) {
- result->err = LIBPE_E_ALLOCATION_FAILURE;
- goto error;
- }
- status = get_headers_dos_hash(ctx, result->dos);
- if (status != LIBPE_E_OK) {
- result->err = status;
- goto error;
- }
-
- result->optional = malloc(sizeof(pe_hash_t));
- if (result->optional == NULL) {
- result->err = LIBPE_E_ALLOCATION_FAILURE;
- goto error;
- }
- status = get_headers_optional_hash(ctx, result->optional);
- if (status != LIBPE_E_OK) {
- result->err = status;
- goto error;
- }
-
- result->coff = malloc(sizeof(pe_hash_t));
- if (result->coff == NULL) {
- status = LIBPE_E_ALLOCATION_FAILURE;
- result->err = status;
- goto error;
- }
- status = get_headers_coff_hash(ctx, result->coff);
- if (status != LIBPE_E_OK) {
- result->err = status;
- goto error;
- }
+pe_hash_headers_t *pe_get_headers_hashes(pe_ctx_t *ctx)
+{
+ if (ctx->cached_data.hash_headers != NULL) {
+ return ctx->cached_data.hash_headers;
+ }
+
+ pe_hash_headers_t *result = ctx->cached_data.hash_headers
+ = calloc(1, sizeof(pe_hash_headers_t));
+ if (result == NULL) {
+ // TODO(jweyrich): Should we report an error? If yes, we need a
+ // redesign.
+ return NULL;
+ }
+
+ result->err = LIBPE_E_OK;
+
+ pe_err_e status = LIBPE_E_OK;
+
+ result->dos = malloc(sizeof(pe_hash_t));
+ if (result->dos == NULL) {
+ result->err = LIBPE_E_ALLOCATION_FAILURE;
+ goto error;
+ }
+ status = get_headers_dos_hash(ctx, result->dos);
+ if (status != LIBPE_E_OK) {
+ result->err = status;
+ goto error;
+ }
+
+ result->optional = malloc(sizeof(pe_hash_t));
+ if (result->optional == NULL) {
+ result->err = LIBPE_E_ALLOCATION_FAILURE;
+ goto error;
+ }
+ status = get_headers_optional_hash(ctx, result->optional);
+ if (status != LIBPE_E_OK) {
+ result->err = status;
+ goto error;
+ }
+
+ result->coff = malloc(sizeof(pe_hash_t));
+ if (result->coff == NULL) {
+ status = LIBPE_E_ALLOCATION_FAILURE;
+ result->err = status;
+ goto error;
+ }
+ status = get_headers_coff_hash(ctx, result->coff);
+ if (status != LIBPE_E_OK) {
+ result->err = status;
+ goto error;
+ }
error:
- return result;
+ return result;
}
-pe_hash_sections_t *pe_get_sections_hash(pe_ctx_t *ctx) {
- if (ctx->cached_data.hash_sections != NULL)
- return ctx->cached_data.hash_sections;
-
- pe_hash_sections_t *result = ctx->cached_data.hash_sections = calloc(1, sizeof(pe_hash_sections_t));
- if (result == NULL) {
- // TODO(jweyrich): Should we report an error? If yes, we need a redesign.
- return NULL;
- }
-
- result->err = LIBPE_E_OK;
-
- const size_t num_sections = pe_sections_count(ctx);
-
- // Allocate an array of pointers once so we can store each pe_hash_t pointer in the
- // respective result->sections[i].
- result->sections = calloc(num_sections, sizeof(pe_hash_t *));
- if (result->sections == NULL) {
- result->err = LIBPE_E_ALLOCATION_FAILURE;
- return result;
- }
-
- IMAGE_SECTION_HEADER ** const sections = pe_sections(ctx);
-
- for (size_t i=0; i < num_sections; i++) {
- uint64_t data_size = sections[i]->SizeOfRawData;
- const unsigned char *data = LIBPE_PTR_ADD(ctx->map_addr, sections[i]->PointerToRawData);
-
- if (!pe_can_read(ctx, data, data_size)) {
- //fprintf(stderr, "%s\n", "unable to read sections data");
- continue;
- }
-
- if (data_size) {
- char *name = (char *)sections[i]->Name;
-
- pe_hash_t *section_hash = calloc(1, sizeof(pe_hash_t));
- if (section_hash == NULL) {
- result->err = LIBPE_E_ALLOCATION_FAILURE;
- break;
- }
-
- pe_err_e status = get_hashes(section_hash, name, data, data_size);
- if (status != LIBPE_E_OK) {
- // TODO: Should we skip this section and continue the loop?
- result->err = status;
- free(section_hash);
- break;
- }
-
- result->sections[result->count] = section_hash;
- result->count++;
- }
- }
-
- return result;
+pe_hash_sections_t *pe_get_sections_hash(pe_ctx_t *ctx)
+{
+ if (ctx->cached_data.hash_sections != NULL) {
+ return ctx->cached_data.hash_sections;
+ }
+
+ pe_hash_sections_t *result = ctx->cached_data.hash_sections
+ = calloc(1, sizeof(pe_hash_sections_t));
+ if (result == NULL) {
+ // TODO(jweyrich): Should we report an error?
+ // If yes, we need a redesign.
+ return NULL;
+ }
+
+ result->err = LIBPE_E_OK;
+
+ const size_t num_sections = pe_sections_count(ctx);
+
+ // Allocate an array of pointers once so we can store each pe_hash_t pointer
+ // in the respective result->sections[i].
+ result->sections = calloc(num_sections, sizeof(pe_hash_t *));
+ if (result->sections == NULL) {
+ result->err = LIBPE_E_ALLOCATION_FAILURE;
+ return result;
+ }
+
+ IMAGE_SECTION_HEADER **const sections = pe_sections(ctx);
+
+ for (size_t i = 0; i < num_sections; i++) {
+ uint64_t data_size = sections[i]->SizeOfRawData;
+ const unsigned char *data
+ = LIBPE_PTR_ADD(ctx->map_addr, sections[i]->PointerToRawData);
+
+ if (! pe_can_read(ctx, data, data_size)) {
+ // fprintf(stderr, "%s\n", "unable to read sections data");
+ continue;
+ }
+
+ if (data_size) {
+ char *name = (char *) sections[i]->Name;
+
+ pe_hash_t *section_hash = calloc(1, sizeof(pe_hash_t));
+ if (section_hash == NULL) {
+ result->err = LIBPE_E_ALLOCATION_FAILURE;
+ break;
+ }
+
+ pe_err_e status = get_hashes(section_hash, name, data, data_size);
+ if (status != LIBPE_E_OK) {
+ // TODO: Should we skip this section and continue the loop?
+ result->err = status;
+ free(section_hash);
+ break;
+ }
+
+ result->sections[result->count] = section_hash;
+ result->count++;
+ }
+ }
+
+ return result;
}
-pe_hash_t *pe_get_file_hash(pe_ctx_t *ctx) {
- if (ctx->cached_data.hash_file != NULL)
- return ctx->cached_data.hash_file;
-
- pe_hash_t *hash = ctx->cached_data.hash_file = calloc(1, sizeof(pe_hash_t));
- if (hash == NULL) {
- // TODO(jweyrich): Should we report an error? If yes, we need a redesign.
- return NULL;
- }
-
- const uint64_t data_size = pe_filesize(ctx);
- pe_err_e status = get_hashes(hash, "PEfile hash", ctx->map_addr, data_size);
- if (status != LIBPE_E_OK)
- abort();
- return hash;
+pe_hash_t *pe_get_file_hash(pe_ctx_t *ctx)
+{
+ if (ctx->cached_data.hash_file != NULL) {
+ return ctx->cached_data.hash_file;
+ }
+
+ pe_hash_t *hash = ctx->cached_data.hash_file = calloc(1, sizeof(pe_hash_t));
+ if (hash == NULL) {
+ // TODO(jweyrich): Should we report an error? If yes, we need a
+ // redesign.
+ return NULL;
+ }
+
+ const uint64_t data_size = pe_filesize(ctx);
+ pe_err_e status = get_hashes(hash, "PEfile hash", ctx->map_addr, data_size);
+ if (status != LIBPE_E_OK) {
+ abort();
+ }
+ return hash;
}
typedef struct element {
- char *dll_name;
- char *function_name;
- //struct element *prev; // needed for a doubly-linked list only
- struct element *next; // needed for singly- or doubly-linked lists
+ char *dll_name;
+ char *function_name;
+ // struct element *prev; // needed for a doubly-linked list only
+ struct element *next; // needed for singly- or doubly-linked lists
} element_t;
// strlwr - string lowercase
-static void pe_transform_to_lowercase_str(char* str)
+static void pe_transform_to_lowercase_str(char *str)
{
- if (str == NULL)
- // TODO: Should we warn here?
- return;
-
- for (char* p = str; *p; ++p)
- *p = tolower((unsigned char)*p);
+ if (str == NULL) {
+ // TODO: Should we warn here?
+ return;
+ }
+
+ for (char *p = str; *p; ++p) {
+ *p = (char) tolower((unsigned char) *p);
+ }
}
-static void pe_get_all_ord_lkp_func_name_with_hint(element_t* elem_ptr, ord_t* ord_ptr, int hint)
+static void pe_get_all_ord_lkp_func_name_with_hint(element_t *elem_ptr,
+ ord_t *ord_ptr, int hint)
{
- for (ord_t* p = ord_ptr; p->number; ++p)
- {
- if (hint == p->number)
- {
- errno = 0;
- elem_ptr->function_name = strdup(p->fname);
- PEV_ABORT_IF(!elem_ptr->function_name || errno == ENOMEM);
- break;
- }
- }
+ for (ord_t *p = ord_ptr; p->number; ++p) {
+ if (hint == p->number) {
+ errno = 0;
+ elem_ptr->function_name = readpe_strdup(p->fname);
+ PEV_ABORT_IF (! elem_ptr->function_name || errno == ENOMEM)
+ ;
+ break;
+ }
+ }
}
-static void imphash_load_imported_functions(pe_ctx_t *ctx, uint64_t offset, char *dll_name, element_t **head, pe_imphash_flavor_e flavor) {
- if (dll_name == NULL || dll_name[0] == '\0')
- return;
-
- uint64_t ofs = offset;
-
- char* hint_str = NULL;
- char* fname = NULL;
-
- bool is_ordinal = false;
- int errcode = 0; // for asprintf return code
-
- while (1) {
- switch (ctx->pe.optional_hdr.type) {
- case MAGIC_PE32_0:
- case MAGIC_PE32:
- {
- const IMAGE_THUNK_DATA32 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA32))) {
- // TODO: Should we report something?
- return;
- }
-
- // Type punning
- const uint32_t thunk_type = *(uint32_t *)thunk;
- if (thunk_type == 0)
- return;
-
- is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
-
- if (is_ordinal) {
- errcode = asprintf(&hint_str, "%"PRIu32,
- thunk->u1.Ordinal & ~((uint32_t)IMAGE_ORDINAL_MASK(ctx)));
-
- // FIX-ME: devemos abortar a execucao?
- PEV_ABORT_IF(errcode == -1);
-
- } else {
- const uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
- const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
- if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
- // TODO: Should we report something?
- return;
- }
-
- errcode = asprintf(&hint_str, "%"PRIu16, imp_name->Hint);
- PEV_ABORT_IF(errcode == -1);
-
- errno = 0;
-
- // if the character '\0' comes before MAX_FUNCTION_NAME - 1
- // we duplicate the string and put it in fname
- // if you can't find '\ 0' copy up to the maximum
- // MAX_FUNCTION_NAME - 1 characters
- fname = strndup((char*)imp_name->Name, MAX_FUNCTION_NAME - 1);
- PEV_ABORT_IF(!fname || errno == ENOMEM);
- }
-
- ofs += sizeof(IMAGE_THUNK_DATA32);
- break;
- }
- case MAGIC_PE64:
- {
- const IMAGE_THUNK_DATA64 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA64))) {
- // TODO: Should we report something?
- return;
- }
-
- // Type punning
- const uint64_t thunk_type = *(uint64_t *)thunk;
- if (thunk_type == 0)
- return;
-
- is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
-
- if (is_ordinal) {
- errcode = asprintf(&hint_str, "%"PRIu64,
- (uint64_t)(thunk->u1.Ordinal & ~(IMAGE_ORDINAL_MASK(ctx))));
-
- PEV_ABORT_IF(errcode == -1);
-
- } else {
- uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
- const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
- if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
- // TODO: Should we report something?
- return;
- }
-
- errcode = asprintf(&hint_str, "%"PRIu16, imp_name->Hint);
- PEV_ABORT_IF(errcode == -1);
-
-
- errno = 0;
- fname = strndup((char*)imp_name->Name, MAX_FUNCTION_NAME - 1);
- PEV_ABORT_IF(!fname || errno == ENOMEM);
- }
- ofs += sizeof(IMAGE_THUNK_DATA64);
- break;
- }
- default:
- return;
- }
-
- // Beginning of imphash logic - that's the weirdest thing I've even seen...
- pe_transform_to_lowercase_str(dll_name);
- char *aux = NULL;
-
- //TODO use a reverse search function instead
-
- switch (flavor) {
- default: abort();
- case LIBPE_IMPHASH_FLAVOR_MANDIANT:
- {
- aux = last_strstr(dll_name, ".");
- break;
- }
- case LIBPE_IMPHASH_FLAVOR_PEFILE:
- {
- aux = last_strstr(dll_name, ".dll");
- if (aux)
- *aux = '\0';
-
- aux = last_strstr(dll_name, ".ocx");
- if (aux)
- *aux = '\0';
-
- aux = last_strstr(dll_name, ".sys");
- if (aux)
- *aux = '\0';
- break;
- }
- }
-
- if (aux)
- *aux = '\0';
-
- pe_transform_to_lowercase_str(fname);
-
- element_t *el = calloc(1, sizeof(element_t));
- if (el == NULL) {
- // TODO: Handle allocation failure.
- abort();
- }
-
- errno = 0;
- el->dll_name = strdup(dll_name);
-
- // add verification of allocation error
- PEV_ABORT_IF(!el->dll_name || errno == ENOMEM);
-
- switch (flavor) {
- default: abort();
- case LIBPE_IMPHASH_FLAVOR_MANDIANT:
- {
- el->function_name = is_ordinal ? hint_str : fname;
- break;
- }
- case LIBPE_IMPHASH_FLAVOR_PEFILE:
- {
- errno = 0;
-
- char* rest = NULL;
- int hint = (int) strtol(hint_str, &rest, 10);
-
- // should we treat the error or abort?
- PEV_ABORT_IF(hint_str == rest || errno == ERANGE);
-
- if (strncmp(dll_name, "oleaut32", 8) == 0 && is_ordinal) {
- pe_get_all_ord_lkp_func_name_with_hint(el, oleaut32_arr, hint);
- } else if (strncmp(dll_name, "ws2_32", 6) == 0 && is_ordinal) {
- pe_get_all_ord_lkp_func_name_with_hint(el, ws2_32_arr, hint);
- }
- else
- {
- if (is_ordinal) {
- char* ord_str = NULL;
-
- errcode = asprintf(&ord_str, "ord%s", hint_str);
- PEV_ABORT_IF(errcode == -1);
-
- el->function_name = ord_str;
- } else {
- el->function_name = fname;
- }
- }
-
- break;
- }
- }
-
- pe_transform_to_lowercase_str(el->function_name);
- LL_APPEND(*head, el);
- }
+static void imphash_load_imported_functions(pe_ctx_t *ctx, uint64_t offset,
+ char *dll_name, element_t **head,
+ pe_imphash_flavor_e flavor)
+{
+ if (dll_name == NULL || dll_name[0] == '\0') {
+ return;
+ }
+
+ uint64_t ofs = offset;
+
+ char *hint_str = NULL;
+ char *fname = NULL;
+
+ bool is_ordinal = false;
+ int errcode = 0; // for asprintf return code
+
+ while (1) {
+ switch (ctx->pe.optional_hdr.type) {
+ case MAGIC_PE32_0:
+ case MAGIC_PE32: {
+ const IMAGE_THUNK_DATA32 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA32))) {
+ // TODO: Should we report something?
+ return;
+ }
+
+ // Type punning
+ const uint32_t thunk_type = *(uint32_t *) thunk;
+ if (thunk_type == 0) {
+ return;
+ }
+
+ is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
+
+ if (is_ordinal) {
+ errcode = asprintf(&hint_str, "%" PRIu32,
+ thunk->u1.Ordinal
+ & ~((uint32_t) IMAGE_ORDINAL_MASK(ctx)));
+
+ // FIX-ME: devemos abortar a execucao?
+ PEV_ABORT_IF (errcode == -1)
+ ;
+
+ } else {
+ const uint64_t imp_ofs
+ = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
+ const IMAGE_IMPORT_BY_NAME *imp_name
+ = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
+ if (! pe_can_read(ctx, imp_name,
+ sizeof(IMAGE_IMPORT_BY_NAME))) {
+ // TODO: Should we report something?
+ return;
+ }
+
+ errcode = asprintf(&hint_str, "%" PRIu16, imp_name->Hint);
+ PEV_ABORT_IF (errcode == -1)
+ ;
+
+ errno = 0;
+
+ // if the character '\0' comes before MAX_FUNCTION_NAME - 1
+ // we duplicate the string and put it in fname
+ // if you can't find '\ 0' copy up to the maximum
+ // MAX_FUNCTION_NAME - 1 characters
+ fname = readpe_strndup((char *) imp_name->Name,
+ MAX_FUNCTION_NAME - 1);
+ PEV_ABORT_IF (! fname || errno == ENOMEM)
+ ;
+ }
+
+ ofs += sizeof(IMAGE_THUNK_DATA32);
+ break;
+ }
+ case MAGIC_PE64: {
+ const IMAGE_THUNK_DATA64 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA64))) {
+ // TODO: Should we report something?
+ return;
+ }
+
+ // Type punning
+ const uint64_t thunk_type = *(uint64_t *) thunk;
+ if (thunk_type == 0) {
+ return;
+ }
+
+ is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
+
+ if (is_ordinal) {
+ errcode = asprintf(&hint_str, "%" PRIu64,
+ (uint64_t) (thunk->u1.Ordinal
+ & ~(IMAGE_ORDINAL_MASK(ctx))));
+
+ PEV_ABORT_IF (errcode == -1)
+ ;
+
+ } else {
+ uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
+ const IMAGE_IMPORT_BY_NAME *imp_name
+ = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
+ if (! pe_can_read(ctx, imp_name,
+ sizeof(IMAGE_IMPORT_BY_NAME))) {
+ // TODO: Should we report something?
+ return;
+ }
+
+ errcode = asprintf(&hint_str, "%" PRIu16, imp_name->Hint);
+ PEV_ABORT_IF (errcode == -1)
+ ;
+
+ errno = 0;
+ fname = readpe_strndup((char *) imp_name->Name,
+ MAX_FUNCTION_NAME - 1);
+ PEV_ABORT_IF (! fname || errno == ENOMEM)
+ ;
+ }
+ ofs += sizeof(IMAGE_THUNK_DATA64);
+ break;
+ }
+ default:
+ return;
+ }
+
+ // Beginning of imphash logic - that's the weirdest thing I've even
+ // seen...
+ pe_transform_to_lowercase_str(dll_name);
+ char *aux = NULL;
+
+ // TODO use a reverse search function instead
+
+ switch (flavor) {
+ default:
+ abort();
+ case LIBPE_IMPHASH_FLAVOR_MANDIANT: {
+ aux = last_strstr(dll_name, ".");
+ break;
+ }
+ case LIBPE_IMPHASH_FLAVOR_PEFILE: {
+ aux = last_strstr(dll_name, ".dll");
+ if (aux) {
+ *aux = '\0';
+ }
+
+ aux = last_strstr(dll_name, ".ocx");
+ if (aux) {
+ *aux = '\0';
+ }
+
+ aux = last_strstr(dll_name, ".sys");
+ if (aux) {
+ *aux = '\0';
+ }
+ break;
+ }
+ }
+
+ if (aux) {
+ *aux = '\0';
+ }
+
+ pe_transform_to_lowercase_str(fname);
+
+ element_t *el = calloc(1, sizeof(element_t));
+ if (el == NULL) {
+ // TODO: Handle allocation failure.
+ abort();
+ }
+
+ errno = 0;
+ el->dll_name = readpe_strdup(dll_name);
+
+ // add verification of allocation error
+ PEV_ABORT_IF (! el->dll_name || errno == ENOMEM)
+ ;
+
+ switch (flavor) {
+ default:
+ abort();
+ case LIBPE_IMPHASH_FLAVOR_MANDIANT: {
+ el->function_name = is_ordinal ? hint_str : fname;
+ break;
+ }
+ case LIBPE_IMPHASH_FLAVOR_PEFILE: {
+ errno = 0;
+
+ char *rest = NULL;
+ int hint = (int) strtol(hint_str, &rest, 10);
+
+ // should we treat the error or abort?
+ PEV_ABORT_IF (hint_str == rest || errno == ERANGE)
+ ;
+
+ if (strncmp(dll_name, "oleaut32", 8) == 0 && is_ordinal) {
+ pe_get_all_ord_lkp_func_name_with_hint(el, oleaut32_arr, hint);
+ } else if (strncmp(dll_name, "ws2_32", 6) == 0 && is_ordinal) {
+ pe_get_all_ord_lkp_func_name_with_hint(el, ws2_32_arr, hint);
+ } else {
+ if (is_ordinal) {
+ char *ord_str = NULL;
+
+ errcode = asprintf(&ord_str, "ord%s", hint_str);
+ PEV_ABORT_IF (errcode == -1)
+ ;
+
+ el->function_name = ord_str;
+ } else {
+ el->function_name = fname;
+ }
+ }
+
+ break;
+ }
+ }
+
+ pe_transform_to_lowercase_str(el->function_name);
+ LL_APPEND(*head, el);
+ }
}
-static void freeNodes(element_t *currentNode) {
- if (currentNode == NULL)
- return;
-
- while(currentNode != NULL) {
- element_t *temp = currentNode;
- currentNode = currentNode->next;
- free(temp->function_name);
- free(temp->dll_name);
- free(temp);
- }
+static void freeNodes(element_t *currentNode)
+{
+ if (currentNode == NULL) {
+ return;
+ }
+
+ while (currentNode != NULL) {
+ element_t *temp = currentNode;
+ currentNode = currentNode->next;
+ free(temp->function_name);
+ free(temp->dll_name);
+ free(temp);
+ }
}
-char *pe_imphash(pe_ctx_t *ctx, pe_imphash_flavor_e flavor) {
- const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
- if (dir == NULL)
- return NULL;
-
- const uint64_t va = dir->VirtualAddress;
- if (va == 0) {
- //fprintf(stderr, "import directory not found\n");
- return NULL;
- }
-
- uint64_t ofs = pe_rva2ofs(ctx, va);
-
- element_t *elt, *tmp, *head = NULL;
- int count = 0;
-
- while (1) {
- IMAGE_IMPORT_DESCRIPTOR *id = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, id, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
- // TODO: Should we report something?
- return NULL;
- }
-
- if (!id->u1.OriginalFirstThunk && !id->FirstThunk)
- break;
-
- ofs += sizeof(IMAGE_IMPORT_DESCRIPTOR);
- const uint64_t aux = ofs; // Store current ofs
-
- ofs = pe_rva2ofs(ctx, id->Name);
- if (ofs == 0 || ofs > (uint64_t) ctx->map_size)
- return NULL;
-
- const char *dll_name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, dll_name_ptr, 1)) {
- // TODO: Should we report something?
- break;
- }
-
- char* dll_name = NULL;
- errno = 0;
-
- // if the character '\0' comes before MAX_DLL_NAME - 1
- // we duplicate the string and put it in fname
- // if you can't find '\ 0' copy up to the maximum
- // MAX_DLL_NAME - 1 characters
- dll_name = strndup(dll_name_ptr, MAX_DLL_NAME - 1);
- PEV_ABORT_IF(!dll_name || errno == ENOMEM);
-
- ofs = pe_rva2ofs(ctx, id->u1.OriginalFirstThunk ? id->u1.OriginalFirstThunk : id->FirstThunk);
- if (ofs == 0) {
- free(dll_name);
- break;
- }
-
- imphash_load_imported_functions(ctx, ofs, dll_name, &head, flavor);
-
- // release dll_name from memory
- free(dll_name);
-
- // Restore previous ofs
- ofs = aux;
- }
-
- LL_COUNT(head, elt, count);
-
- // Allocate enough memory to store N times "dll_name.func_name,", plus 1 byte for the NUL terminator.
- const size_t imphash_string_size = count * (MAX_DLL_NAME + MAX_FUNCTION_NAME + 2) + 1;
- char *imphash_string = calloc(1, imphash_string_size);
-
- if (imphash_string == NULL) {
- // TODO: Handle allocation failure.
- abort();
- }
-
- LL_FOREACH_SAFE(head, elt, tmp) {
- sprintf(imphash_string + strlen(imphash_string), "%s.%s,", elt->dll_name, elt->function_name);
- LL_DELETE(head, elt);
- }
-
- assert(!elt);
- freeNodes(head);
-
- size_t imphash_string_len = strlen(imphash_string);
- if (imphash_string_len == 0) {
- free(imphash_string);
- //ret = LIBPE_E_ALLOCATION_FAILURE;
- return NULL;
- }
-
- // Remove the last comma sign and decrement the string length by 1.
- imphash_string[imphash_string_len-1] = '\0';
- imphash_string_len--;
-
- const unsigned char *data = (const unsigned char *)imphash_string;
- const size_t data_size = imphash_string_len;
-
- const size_t hash_maxsize = pe_hash_recommended_size();
- char *hash_value = calloc(1, hash_maxsize);
- if (hash_value == NULL) {
- free(imphash_string);
- //ret = LIBPE_E_ALLOCATION_FAILURE;
- return NULL;
- }
-
- const bool hash_ok = pe_hash_raw_data(hash_value, hash_maxsize, "md5", data, data_size);
-
- free(imphash_string);
-
- //printf("### DEBUG imphash_string [%zu] = %s\n", imphash_string_len, imphash_string);
-
- if (!hash_ok) {
- free(hash_value);
- return NULL;
- }
-
- return hash_value;
+char *pe_imphash(pe_ctx_t *ctx, pe_imphash_flavor_e flavor)
+{
+ const IMAGE_DATA_DIRECTORY *dir
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
+ if (dir == NULL) {
+ return NULL;
+ }
+
+ const uint64_t va = dir->VirtualAddress;
+ if (va == 0) {
+ // fprintf(stderr, "import directory not found\n");
+ return NULL;
+ }
+
+ uint64_t ofs = pe_rva2ofs(ctx, va);
+
+ element_t *elt, *tmp, *head = NULL;
+ int count = 0;
+
+ while (1) {
+ IMAGE_IMPORT_DESCRIPTOR *id = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, id, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
+ // TODO: Should we report something?
+ return NULL;
+ }
+
+ if (! id->u1.OriginalFirstThunk && ! id->FirstThunk) {
+ break;
+ }
+
+ ofs += sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ const uint64_t aux = ofs; // Store current ofs
+
+ ofs = pe_rva2ofs(ctx, id->Name);
+ if (ofs == 0 || ofs > (uint64_t) ctx->map_size) {
+ return NULL;
+ }
+
+ const char *dll_name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, dll_name_ptr, 1)) {
+ // TODO: Should we report something?
+ break;
+ }
+
+ char *dll_name = NULL;
+ errno = 0;
+
+ // if the character '\0' comes before MAX_DLL_NAME - 1
+ // we duplicate the string and put it in fname
+ // if you can't find '\ 0' copy up to the maximum
+ // MAX_DLL_NAME - 1 characters
+ dll_name = readpe_strndup(dll_name_ptr, MAX_DLL_NAME - 1);
+ PEV_ABORT_IF (! dll_name || errno == ENOMEM)
+ ;
+
+ ofs = pe_rva2ofs(ctx, id->u1.OriginalFirstThunk
+ ? id->u1.OriginalFirstThunk
+ : id->FirstThunk);
+ if (ofs == 0) {
+ free(dll_name);
+ break;
+ }
+
+ imphash_load_imported_functions(ctx, ofs, dll_name, &head, flavor);
+
+ // release dll_name from memory
+ free(dll_name);
+
+ // Restore previous ofs
+ ofs = aux;
+ }
+
+ LL_COUNT(head, elt, count);
+
+ // Allocate enough memory to store N times "dll_name.func_name,", plus 1
+ // byte for the NUL terminator.
+ const size_t imphash_string_size
+ = (size_t) count * (MAX_DLL_NAME + MAX_FUNCTION_NAME + 2) + 1;
+ char *imphash_string = calloc(1, imphash_string_size);
+
+ if (imphash_string == NULL) {
+ // TODO: Handle allocation failure.
+ abort();
+ }
+
+ LL_FOREACH_SAFE (head, elt, tmp) {
+ sprintf(imphash_string + strlen(imphash_string), "%s.%s,",
+ elt->dll_name, elt->function_name);
+ LL_DELETE(head, elt);
+ }
+
+ assert(! elt);
+ freeNodes(head);
+
+ size_t imphash_string_len = strlen(imphash_string);
+ if (imphash_string_len == 0) {
+ free(imphash_string);
+ // ret = LIBPE_E_ALLOCATION_FAILURE;
+ return NULL;
+ }
+
+ // Remove the last comma sign and decrement the string length by 1.
+ imphash_string[imphash_string_len - 1] = '\0';
+ imphash_string_len--;
+
+ const unsigned char *data = (const unsigned char *) imphash_string;
+ const size_t data_size = imphash_string_len;
+
+ const size_t hash_maxsize = pe_hash_recommended_size();
+ char *hash_value = calloc(1, hash_maxsize);
+ if (hash_value == NULL) {
+ free(imphash_string);
+ // ret = LIBPE_E_ALLOCATION_FAILURE;
+ return NULL;
+ }
+
+ const bool hash_ok
+ = pe_hash_raw_data(hash_value, hash_maxsize, "md5", data, data_size);
+
+ free(imphash_string);
+
+ // printf("### DEBUG imphash_string [%zu] = %s\n", imphash_string_len,
+ // imphash_string);
+
+ if (! hash_ok) {
+ free(hash_value);
+ return NULL;
+ }
+
+ return hash_value;
}
-void pe_hash_headers_dealloc(pe_hash_headers_t *obj) {
- if (obj == NULL)
- return;
-
- pe_hash_dealloc(obj->dos);
- pe_hash_dealloc(obj->coff);
- pe_hash_dealloc(obj->optional);
- free(obj);
+void pe_hash_headers_dealloc(pe_hash_headers_t *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
+
+ pe_hash_dealloc(obj->dos);
+ pe_hash_dealloc(obj->coff);
+ pe_hash_dealloc(obj->optional);
+ free(obj);
}
-void pe_hash_sections_dealloc(pe_hash_sections_t *obj) {
- if (obj == NULL)
- return;
+void pe_hash_sections_dealloc(pe_hash_sections_t *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
- for (uint32_t i=0; i < obj->count; i++) {
- pe_hash_dealloc(obj->sections[i]);
- }
+ for (uint32_t i = 0; i < obj->count; i++) {
+ pe_hash_dealloc(obj->sections[i]);
+ }
- free(obj->sections);
- free(obj);
+ free(obj->sections);
+ free(obj);
}
-void pe_hash_dealloc(pe_hash_t *obj) {
- if (obj == NULL)
- return;
-
- free(obj->name);
- free(obj->md5);
- free(obj->sha1);
- free(obj->sha256);
- free(obj->ssdeep);
- free(obj);
+void pe_hash_dealloc(pe_hash_t *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
+
+ free(obj->name);
+ free(obj->md5);
+ free(obj->sha1);
+ free(obj->sha256);
+ free(obj->ssdeep);
+ free(obj);
}
+
diff --git a/lib/libpe/imports.c b/lib/libpe/imports.c
index f79ed642..5211825a 100644
--- a/lib/libpe/imports.c
+++ b/lib/libpe/imports.c
@@ -21,487 +21,553 @@
#include "libpe/imports.h"
+#include "compat.h"
+#include "libpe/dir_import.h"
+#include "libpe/macros.h"
#include "libpe/pe.h"
+
+#include
+#include
#include
#include
#include
-#include
-#include
-
-static uint32_t get_dll_count(pe_ctx_t *ctx) {
- uint32_t count = 0;
-
- const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
- if (dir == NULL)
- return count;
-
- const uint64_t va = dir->VirtualAddress;
- if (va == 0) {
- // TODO: report error?
- return count;
- }
-
- uint64_t ofs = pe_rva2ofs(ctx, va);
-
- while (1) {
- IMAGE_IMPORT_DESCRIPTOR *id = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, id, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
- // TODO: Should we report something?
- return count;
- }
-
- if (!id->u1.OriginalFirstThunk && !id->FirstThunk)
- break;
-
- ofs += sizeof(IMAGE_IMPORT_DESCRIPTOR);
-
- const uint64_t aux = ofs; // Store current ofs
- ofs = pe_rva2ofs(ctx, id->Name);
- if (ofs == 0)
- break;
- ofs = pe_rva2ofs(ctx, id->u1.OriginalFirstThunk
- ? id->u1.OriginalFirstThunk
- : id->FirstThunk);
- if (ofs == 0)
- break;
-
- count++;
- ofs = aux; // Restore previous ofs
- }
-
- return count;
+static uint32_t get_dll_count(pe_ctx_t *ctx)
+{
+ uint32_t count = 0;
+
+ const IMAGE_DATA_DIRECTORY *dir
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
+ if (dir == NULL) {
+ return count;
+ }
+
+ const uint64_t va = dir->VirtualAddress;
+ if (va == 0) {
+ // TODO: report error?
+ return count;
+ }
+
+ uint64_t ofs = pe_rva2ofs(ctx, va);
+
+ while (1) {
+ IMAGE_IMPORT_DESCRIPTOR *id = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, id, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
+ // TODO: Should we report something?
+ return count;
+ }
+
+ if (! id->u1.OriginalFirstThunk && ! id->FirstThunk) {
+ break;
+ }
+
+ ofs += sizeof(IMAGE_IMPORT_DESCRIPTOR);
+
+ const uint64_t aux = ofs; // Store current ofs
+ ofs = pe_rva2ofs(ctx, id->Name);
+ if (ofs == 0) {
+ break;
+ }
+
+ ofs = pe_rva2ofs(ctx, id->u1.OriginalFirstThunk
+ ? id->u1.OriginalFirstThunk
+ : id->FirstThunk);
+ if (ofs == 0) {
+ break;
+ }
+
+ count++;
+ ofs = aux; // Restore previous ofs
+ }
+
+ return count;
}
-static uint32_t get_delay_dll_count(pe_ctx_t *ctx) {
- uint32_t count = 0;
-
- const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
- if (dir == NULL)
- return count;
-
- const uint64_t va = dir->VirtualAddress;
- if (va == 0) {
- // TODO: report error?
- return count;
- }
-
- uint64_t ofs = pe_rva2ofs(ctx, va);
-
- while (1) {
- IMAGE_DELAYLOAD_DESCRIPTOR *dd = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, dd, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) {
- // TODO: Should we report something?
- return count;
- }
-
- if (!dd->ImportNameTableRVA)
- break;
-
- ofs += sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
-
- const uint64_t aux = ofs; // Store current ofs
- ofs = pe_rva2ofs(ctx, dd->DllNameRVA);
- if (ofs == 0)
- break;
-
- ofs = pe_rva2ofs(ctx, dd->ImportNameTableRVA);
- if (ofs == 0)
- break;
-
- count++;
- ofs = aux; // Restore previous ofs
- }
-
- return count;
+static uint32_t get_delay_dll_count(pe_ctx_t *ctx)
+{
+ uint32_t count = 0;
+
+ const IMAGE_DATA_DIRECTORY *dir
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+ if (dir == NULL) {
+ return count;
+ }
+
+ const uint64_t va = dir->VirtualAddress;
+ if (va == 0) {
+ // TODO: report error?
+ return count;
+ }
+
+ uint64_t ofs = pe_rva2ofs(ctx, va);
+
+ while (1) {
+ IMAGE_DELAYLOAD_DESCRIPTOR *dd = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, dd, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) {
+ // TODO: Should we report something?
+ return count;
+ }
+
+ if (! dd->ImportNameTableRVA) {
+ break;
+ }
+
+ ofs += sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
+
+ const uint64_t aux = ofs; // Store current ofs
+ ofs = pe_rva2ofs(ctx, dd->DllNameRVA);
+ if (ofs == 0) {
+ break;
+ }
+
+ ofs = pe_rva2ofs(ctx, dd->ImportNameTableRVA);
+ if (ofs == 0) {
+ break;
+ }
+
+ count++;
+ ofs = aux; // Restore previous ofs
+ }
+
+ return count;
}
-static uint32_t get_functions_count(pe_ctx_t *ctx, uint64_t offset, bool rva_based) {
- uint64_t ofs = offset - (rva_based ? 0 : ctx->pe.imagebase);
- uint32_t count = 0;
-
- while (1) {
- switch (ctx->pe.optional_hdr.type) {
- case MAGIC_PE32_0:
- case MAGIC_PE32:
- {
- const IMAGE_THUNK_DATA32 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA32)))
- return count;
-
- // Type punning
- const uint32_t thunk_type = *(uint32_t *)thunk;
- if (thunk_type == 0)
- return count;
-
- bool is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
-
- if (!is_ordinal) {
- const uint32_t rva = thunk->u1.AddressOfData - (rva_based ? 0 : ctx->pe.imagebase);
- const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
- const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
- if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME)))
- return count;
- }
-
- ofs += sizeof(IMAGE_THUNK_DATA32);
- break;
- }
- case MAGIC_PE64:
- {
- const IMAGE_THUNK_DATA64 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA64)))
- return count;
-
- const uint64_t thunk_type = *(uint64_t *)thunk;
- if (thunk_type == 0)
- return count;
-
- bool is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
-
- if (!is_ordinal) {
- uint64_t rva = thunk->u1.AddressOfData - (rva_based ? 0 : ctx->pe.imagebase);
- uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
- const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
- if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME)))
- return count;
- }
-
- ofs += sizeof(IMAGE_THUNK_DATA64);
- break;
- }
- }
-
- count++;
- }
-
- return count;
+static uint32_t get_functions_count(pe_ctx_t *ctx, uint64_t offset,
+ bool rva_based)
+{
+ uint64_t ofs = offset - (rva_based ? 0 : ctx->pe.imagebase);
+ uint32_t count = 0;
+
+ while (1) {
+ switch (ctx->pe.optional_hdr.type) {
+ case MAGIC_PE32_0:
+ case MAGIC_PE32: {
+ const IMAGE_THUNK_DATA32 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA32))) {
+ return count;
+ }
+
+ // Type punning
+ const uint32_t thunk_type = *(uint32_t *) thunk;
+ if (thunk_type == 0) {
+ return count;
+ }
+
+ bool is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
+
+ if (! is_ordinal) {
+ const uint32_t rva
+ = thunk->u1.AddressOfData
+ - (rva_based ? 0 : (uint32_t) ctx->pe.imagebase);
+ const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
+ const IMAGE_IMPORT_BY_NAME *imp_name
+ = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
+ if (! pe_can_read(ctx, imp_name,
+ sizeof(IMAGE_IMPORT_BY_NAME))) {
+ return count;
+ }
+ }
+
+ ofs += sizeof(IMAGE_THUNK_DATA32);
+ break;
+ }
+ case MAGIC_PE64: {
+ const IMAGE_THUNK_DATA64 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA64))) {
+ return count;
+ }
+
+ const uint64_t thunk_type = *(uint64_t *) thunk;
+ if (thunk_type == 0) {
+ return count;
+ }
+
+ bool is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
+
+ if (! is_ordinal) {
+ uint64_t rva = thunk->u1.AddressOfData
+ - (rva_based ? 0 : ctx->pe.imagebase);
+ uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
+ const IMAGE_IMPORT_BY_NAME *imp_name
+ = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
+ if (! pe_can_read(ctx, imp_name,
+ sizeof(IMAGE_IMPORT_BY_NAME))) {
+ return count;
+ }
+ }
+
+ ofs += sizeof(IMAGE_THUNK_DATA64);
+ break;
+ }
+ }
+
+ count++;
+ }
+
+ return count;
}
-static pe_err_e parse_imported_functions(pe_ctx_t *ctx, pe_imported_dll_t *imported_dll, uint64_t offset, bool rva_based) {
- imported_dll->err = LIBPE_E_OK;
- imported_dll->functions_count = get_functions_count(ctx, offset, rva_based);
-
- imported_dll->functions = calloc(imported_dll->functions_count, sizeof(pe_imported_function_t));
- if (imported_dll->functions == NULL) {
- imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
- return imported_dll->err;
- }
-
- // FIX: Unecessary to fill the buffer with zeroes.
- char fname[MAX_FUNCTION_NAME];
- const size_t size_fname = sizeof(fname);
-
- bool is_ordinal = false;
- uint16_t ordinal = 0;
- uint16_t hint = 0;
- uint64_t ofs = offset - (rva_based ? 0 : ctx->pe.imagebase);
-
- for (uint32_t i=0; i < imported_dll->functions_count; i++) {
- switch (ctx->pe.optional_hdr.type) {
- case MAGIC_PE32_0:
- case MAGIC_PE32:
- {
- const IMAGE_THUNK_DATA32 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA32))) {
- imported_dll->err = LIBPE_E_INVALID_THUNK;
- return imported_dll->err;
- }
-
- // Type punning
- const uint32_t thunk_type = *(uint32_t *)thunk;
- if (thunk_type == 0) {
- imported_dll->err = LIBPE_E_INVALID_THUNK;
- return imported_dll->err;
- }
-
- // If the MSB of the member is 1, the function is exported by ordinal.
- is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
-
- if (is_ordinal) {
- hint = 0;
- ordinal = (thunk->u1.Ordinal & ~(IMAGE_ORDINAL_MASK(ctx))) & 0xffff;
- } else {
- const uint32_t rva = thunk->u1.AddressOfData - (rva_based ? 0 : ctx->pe.imagebase);
- const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
- const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
- if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
- imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
- return imported_dll->err;
- }
-
- hint = imp_name->Hint;
- ordinal = 0;
-
- strncpy(fname, (char *)imp_name->Name, size_fname-1);
- // Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
- fname[size_fname - 1] = '\0';
- }
-
- ofs += sizeof(IMAGE_THUNK_DATA32);
- break;
- }
- case MAGIC_PE64:
- {
- const IMAGE_THUNK_DATA64 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA64))) {
- imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
- return imported_dll->err; // DO something so that API notifies of the error
- }
-
- // Type punning
- const uint64_t thunk_type = *(uint64_t *)thunk;
- if (thunk_type == 0) {
- imported_dll->err = LIBPE_E_INVALID_THUNK;
- return imported_dll->err;
- }
-
- // If the MSB of the member is 1, the function is exported by ordinal.
- is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
-
- if (is_ordinal) {
- hint = 0; // No hint
- ordinal = (thunk->u1.Ordinal & ~(IMAGE_ORDINAL_MASK(ctx))) & 0xffff;
- } else {
- const uint64_t rva = thunk->u1.AddressOfData - (rva_based ? 0 : ctx->pe.imagebase);
- const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
- const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
- if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
- imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
- return imported_dll->err;
- }
-
- hint = imp_name->Hint;
- ordinal = 0; // No ordinal
-
- strncpy(fname, (char *)imp_name->Name, size_fname-1);
- // Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
- fname[size_fname - 1] = '\0';
- }
-
- ofs += sizeof(IMAGE_THUNK_DATA64);
- break;
- }
- }
-
- imported_dll->functions[i].hint = hint;
- imported_dll->functions[i].ordinal = ordinal;
-
- if (!is_ordinal) {
- imported_dll->functions[i].name = strdup(fname);
- if (imported_dll->functions[i].name == NULL) {
- imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
- return imported_dll->err;
- }
- }
- }
-
- return LIBPE_E_OK;
+static pe_err_e parse_imported_functions(pe_ctx_t *ctx,
+ pe_imported_dll_t *imported_dll,
+ uint64_t offset, bool rva_based)
+{
+ imported_dll->err = LIBPE_E_OK;
+ imported_dll->functions_count = get_functions_count(ctx, offset, rva_based);
+
+ imported_dll->functions
+ = calloc(imported_dll->functions_count, sizeof(pe_imported_function_t));
+ if (imported_dll->functions == NULL) {
+ imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imported_dll->err;
+ }
+
+ // FIX: Unecessary to fill the buffer with zeroes.
+ char fname[MAX_FUNCTION_NAME];
+ const size_t size_fname = sizeof(fname);
+
+ bool is_ordinal = false;
+ uint16_t ordinal = 0;
+ uint16_t hint = 0;
+ uint64_t ofs = offset - (rva_based ? 0 : ctx->pe.imagebase);
+
+ for (uint32_t i = 0; i < imported_dll->functions_count; i++) {
+ switch (ctx->pe.optional_hdr.type) {
+ case MAGIC_PE32_0:
+ case MAGIC_PE32: {
+ const IMAGE_THUNK_DATA32 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA32))) {
+ imported_dll->err = LIBPE_E_INVALID_THUNK;
+ return imported_dll->err;
+ }
+
+ // Type punning
+ const uint32_t thunk_type = *(uint32_t *) thunk;
+ if (thunk_type == 0) {
+ imported_dll->err = LIBPE_E_INVALID_THUNK;
+ return imported_dll->err;
+ }
+
+ // If the MSB of the member is 1, the function is exported by
+ // ordinal.
+ is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
+
+ if (is_ordinal) {
+ hint = 0;
+ ordinal
+ = (thunk->u1.Ordinal & ~(IMAGE_ORDINAL_MASK(ctx))) & 0xffff;
+ } else {
+ const uint32_t rva
+ = thunk->u1.AddressOfData
+ - (rva_based ? 0 : (uint32_t) ctx->pe.imagebase);
+ const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
+ const IMAGE_IMPORT_BY_NAME *imp_name
+ = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
+ if (! pe_can_read(ctx, imp_name,
+ sizeof(IMAGE_IMPORT_BY_NAME))) {
+ imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imported_dll->err;
+ }
+
+ hint = imp_name->Hint;
+ ordinal = 0;
+
+ strncpy(fname, (char *) imp_name->Name, size_fname - 1);
+ // Because `strncpy` does not guarantee to NUL terminate the
+ // string itself, this must be done explicitly.
+ fname[size_fname - 1] = '\0';
+ }
+
+ ofs += sizeof(IMAGE_THUNK_DATA32);
+ break;
+ }
+ case MAGIC_PE64: {
+ const IMAGE_THUNK_DATA64 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA64))) {
+ imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imported_dll
+ ->err; // DO something so that API notifies of the error
+ }
+
+ // Type punning
+ const uint64_t thunk_type = *(uint64_t *) thunk;
+ if (thunk_type == 0) {
+ imported_dll->err = LIBPE_E_INVALID_THUNK;
+ return imported_dll->err;
+ }
+
+ // If the MSB of the member is 1, the function is exported by
+ // ordinal.
+ is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
+
+ if (is_ordinal) {
+ hint = 0; // No hint
+ ordinal
+ = (thunk->u1.Ordinal & ~(IMAGE_ORDINAL_MASK(ctx))) & 0xffff;
+ } else {
+ const uint64_t rva = thunk->u1.AddressOfData
+ - (rva_based ? 0 : ctx->pe.imagebase);
+ const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
+ const IMAGE_IMPORT_BY_NAME *imp_name
+ = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
+ if (! pe_can_read(ctx, imp_name,
+ sizeof(IMAGE_IMPORT_BY_NAME))) {
+ imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imported_dll->err;
+ }
+
+ hint = imp_name->Hint;
+ ordinal = 0; // No ordinal
+
+ strncpy(fname, (char *) imp_name->Name, size_fname - 1);
+ // Because `strncpy` does not guarantee to NUL terminate the
+ // string itself, this must be done explicitly.
+ fname[size_fname - 1] = '\0';
+ }
+
+ ofs += sizeof(IMAGE_THUNK_DATA64);
+ break;
+ }
+ }
+
+ imported_dll->functions[i].hint = hint;
+ imported_dll->functions[i].ordinal = ordinal;
+
+ if (! is_ordinal) {
+ imported_dll->functions[i].name = readpe_strdup(fname);
+ if (imported_dll->functions[i].name == NULL) {
+ imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imported_dll->err;
+ }
+ }
+ }
+
+ return LIBPE_E_OK;
}
-pe_imports_t *pe_imports(pe_ctx_t *ctx) {
- if (ctx->cached_data.imports != NULL)
- return ctx->cached_data.imports;
-
- pe_imports_t *imports = ctx->cached_data.imports = calloc(1, sizeof(pe_imports_t));
- if (imports == NULL) {
- // TODO(jweyrich): Should we report an error? If yes, we need a redesign.
- return NULL;
- }
-
- imports->err = LIBPE_E_OK;
-
- imports->dll_count = get_dll_count(ctx);
- imports->delay_dll_count = get_delay_dll_count(ctx);
- if (imports->dll_count == 0 && imports->delay_dll_count == 0)
- return imports;
-
- // Allocate array to store DLLs
- if (imports->dll_count != 0) {
- imports->dlls = calloc(imports->dll_count, sizeof(pe_imported_dll_t));
- if (imports->dlls == NULL) {
- imports->err = LIBPE_E_ALLOCATION_FAILURE;
- return imports;
- }
- }
-
- // Allocate array to store delay loaded DLLs
- if (imports->delay_dll_count != 0) {
- imports->delay_dlls = calloc(imports->delay_dll_count, sizeof(pe_imported_dll_t));
- if (imports->delay_dlls == NULL) {
- imports->err = LIBPE_E_ALLOCATION_FAILURE;
- return imports;
- }
- }
-
- uint64_t ofs = 0;
-
- if (imports->dll_count != 0) {
- const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
- if (dir == NULL) {
- return imports;
- }
-
- const uint64_t va = dir->VirtualAddress;
- if (va == 0) {
- // TODO: report error?
- return imports;
- }
-
- ofs = pe_rva2ofs(ctx, va);
- }
-
- for (uint32_t i=0; i < imports->dll_count; i++) {
- IMAGE_IMPORT_DESCRIPTOR *id = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, id, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
- break;
- }
-
- if (!id->u1.OriginalFirstThunk && !id->FirstThunk)
- break;
-
- ofs += sizeof(IMAGE_IMPORT_DESCRIPTOR);
- const uint64_t aux = ofs; // Store current ofs
-
- ofs = pe_rva2ofs(ctx, id->Name);
- if (ofs == 0)
- break;
-
- const char *dll_name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, dll_name_ptr, 1)) {
- // TODO: Should we report something?
- break;
- }
-
- pe_imported_dll_t * const dll = &imports->dlls[i];
-
- // Allocate string to store DLL name
- const size_t dll_name_size = MAX_DLL_NAME;
- dll->name = calloc(1, dll_name_size);
- if (dll->name == NULL) {
- imports->err = LIBPE_E_ALLOCATION_FAILURE;
- return imports;
- }
-
- // Validate whether it's ok to access at least 1 byte after dll_name_ptr.
- // It might be '\0', for example.
- strncpy(dll->name, dll_name_ptr, dll_name_size-1);
- // Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
- dll->name[dll_name_size - 1] = '\0';
-
- ofs = pe_rva2ofs(ctx, id->u1.OriginalFirstThunk
- ? id->u1.OriginalFirstThunk
- : id->FirstThunk);
- if (ofs == 0) {
- break;
- }
-
- // IMAGE_IMPORT_DESCRIPTOR is always RVA based.
- pe_err_e parse_err = parse_imported_functions(ctx, dll, ofs, true);
- if (parse_err != LIBPE_E_OK) {
- imports->err = parse_err;
- return imports;
- }
-
- ofs = aux; // Restore previous ofs
- }
-
- if (imports->delay_dll_count != 0) {
- const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
- if (dir == NULL) {
- return imports;
- }
-
- const uint64_t va = dir->VirtualAddress;
- if (va == 0) {
- // TODO: report error?
- return imports;
- }
-
- ofs = pe_rva2ofs(ctx, va);
- }
-
- for (uint32_t i=0; i < imports->delay_dll_count; i++) {
- IMAGE_DELAYLOAD_DESCRIPTOR *dd = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, dd, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) {
- break;
- }
-
- if (!dd->ImportNameTableRVA)
- break;
-
- ofs += sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
- const uint64_t aux = ofs; // Store current ofs
-
- ofs = pe_rva2ofs(ctx, dd->DllNameRVA - (dd->Attributes.u1.RvaBased ? 0 : ctx->pe.imagebase));
- if (ofs == 0)
- break;
-
- const char *dll_name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, dll_name_ptr, 1)) {
- // TODO: Should we report something?
- break;
- }
-
- pe_imported_dll_t * const dll = &imports->delay_dlls[i];
-
- // Allocate string to store DLL name
- const size_t dll_name_size = MAX_DLL_NAME;
- dll->name = calloc(1, dll_name_size);
- if (dll->name == NULL) {
- imports->err = LIBPE_E_ALLOCATION_FAILURE;
- return imports;
- }
-
- // Validate whether it's ok to access at least 1 byte after dll_name_ptr.
- // It might be '\0', for example.
- strncpy(dll->name, dll_name_ptr, dll_name_size-1);
- // Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
- dll->name[dll_name_size - 1] = '\0';
-
- ofs = pe_rva2ofs(ctx, dd->ImportNameTableRVA);
- if (ofs == 0) {
- break;
- }
-
- // IMAGE_DELAYLOAD_DESCRIPTOR v1 is absolute address based and v2 is RVA based.
- // LINK.EXE from Visual C++ 6.0 generates IMAGE_DELAYLOAD_DESCRIPTOR v1.
- // LINK.EXE from Visual C++ 7.0/2002 and new generates IMAGE_DELAYLOAD_DESCRIPTOR v2.
- pe_err_e parse_err = parse_imported_functions(ctx, dll, ofs, dd->Attributes.u1.RvaBased);
- if (parse_err != LIBPE_E_OK) {
- imports->err = parse_err;
- return imports;
- }
-
- ofs = aux; // Restore previous ofs
- }
-
- return imports;
+pe_imports_t *pe_imports(pe_ctx_t *ctx)
+{
+ if (ctx->cached_data.imports != NULL) {
+ return ctx->cached_data.imports;
+ }
+
+ pe_imports_t *imports = ctx->cached_data.imports
+ = calloc(1, sizeof(pe_imports_t));
+ if (imports == NULL) {
+ // TODO(jweyrich): Should we report an error? If yes, we need a
+ // redesign.
+ return NULL;
+ }
+
+ imports->err = LIBPE_E_OK;
+
+ imports->dll_count = get_dll_count(ctx);
+ imports->delay_dll_count = get_delay_dll_count(ctx);
+ if (imports->dll_count == 0 && imports->delay_dll_count == 0) {
+ return imports;
+ }
+
+ // Allocate array to store DLLs
+ if (imports->dll_count != 0) {
+ imports->dlls = calloc(imports->dll_count, sizeof(pe_imported_dll_t));
+ if (imports->dlls == NULL) {
+ imports->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imports;
+ }
+ }
+
+ // Allocate array to store delay loaded DLLs
+ if (imports->delay_dll_count != 0) {
+ imports->delay_dlls
+ = calloc(imports->delay_dll_count, sizeof(pe_imported_dll_t));
+ if (imports->delay_dlls == NULL) {
+ imports->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imports;
+ }
+ }
+
+ uint64_t ofs = 0;
+
+ if (imports->dll_count != 0) {
+ const IMAGE_DATA_DIRECTORY *dir
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
+ if (dir == NULL) {
+ return imports;
+ }
+
+ const uint64_t va = dir->VirtualAddress;
+ if (va == 0) {
+ // TODO: report error?
+ return imports;
+ }
+
+ ofs = pe_rva2ofs(ctx, va);
+ }
+
+ for (uint32_t i = 0; i < imports->dll_count; i++) {
+ IMAGE_IMPORT_DESCRIPTOR *id = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, id, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
+ break;
+ }
+
+ if (! id->u1.OriginalFirstThunk && ! id->FirstThunk) {
+ break;
+ }
+
+ ofs += sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ const uint64_t aux = ofs; // Store current ofs
+
+ ofs = pe_rva2ofs(ctx, id->Name);
+ if (ofs == 0) {
+ break;
+ }
+
+ const char *dll_name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, dll_name_ptr, 1)) {
+ // TODO: Should we report something?
+ break;
+ }
+
+ pe_imported_dll_t *const dll = &imports->dlls[i];
+
+ // Allocate string to store DLL name
+ const size_t dll_name_size = MAX_DLL_NAME;
+ dll->name = calloc(1, dll_name_size);
+ if (dll->name == NULL) {
+ imports->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imports;
+ }
+
+ // Validate whether it's ok to access at least 1 byte after
+ // dll_name_ptr. It might be '\0', for example.
+ strncpy(dll->name, dll_name_ptr, dll_name_size - 1);
+ // Because `strncpy` does not guarantee to NUL terminate the string
+ // itself, this must be done explicitly.
+ dll->name[dll_name_size - 1] = '\0';
+
+ ofs = pe_rva2ofs(ctx, id->u1.OriginalFirstThunk
+ ? id->u1.OriginalFirstThunk
+ : id->FirstThunk);
+ if (ofs == 0) {
+ break;
+ }
+
+ // IMAGE_IMPORT_DESCRIPTOR is always RVA based.
+ pe_err_e parse_err = parse_imported_functions(ctx, dll, ofs, true);
+ if (parse_err != LIBPE_E_OK) {
+ imports->err = parse_err;
+ return imports;
+ }
+
+ ofs = aux; // Restore previous ofs
+ }
+
+ if (imports->delay_dll_count != 0) {
+ const IMAGE_DATA_DIRECTORY *dir
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+ if (dir == NULL) {
+ return imports;
+ }
+
+ const uint64_t va = dir->VirtualAddress;
+ if (va == 0) {
+ // TODO: report error?
+ return imports;
+ }
+
+ ofs = pe_rva2ofs(ctx, va);
+ }
+
+ for (uint32_t i = 0; i < imports->delay_dll_count; i++) {
+ IMAGE_DELAYLOAD_DESCRIPTOR *dd = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, dd, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) {
+ break;
+ }
+
+ if (! dd->ImportNameTableRVA) {
+ break;
+ }
+
+ ofs += sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
+ const uint64_t aux = ofs; // Store current ofs
+
+ ofs = pe_rva2ofs(
+ ctx, dd->DllNameRVA
+ - (dd->Attributes.u1.RvaBased ? 0 : ctx->pe.imagebase));
+ if (ofs == 0) {
+ break;
+ }
+
+ const char *dll_name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, dll_name_ptr, 1)) {
+ // TODO: Should we report something?
+ break;
+ }
+
+ pe_imported_dll_t *const dll = &imports->delay_dlls[i];
+
+ // Allocate string to store DLL name
+ const size_t dll_name_size = MAX_DLL_NAME;
+ dll->name = calloc(1, dll_name_size);
+ if (dll->name == NULL) {
+ imports->err = LIBPE_E_ALLOCATION_FAILURE;
+ return imports;
+ }
+
+ // Validate whether it's ok to access at least 1 byte after
+ // dll_name_ptr. It might be '\0', for example.
+ strncpy(dll->name, dll_name_ptr, dll_name_size - 1);
+ // Because `strncpy` does not guarantee to NUL terminate the string
+ // itself, this must be done explicitly.
+ dll->name[dll_name_size - 1] = '\0';
+
+ ofs = pe_rva2ofs(ctx, dd->ImportNameTableRVA);
+ if (ofs == 0) {
+ break;
+ }
+
+ // IMAGE_DELAYLOAD_DESCRIPTOR v1 is absolute address based and v2 is RVA
+ // based. LINK.EXE from Visual C++ 6.0 generates
+ // IMAGE_DELAYLOAD_DESCRIPTOR v1. LINK.EXE from Visual C++ 7.0/2002 and
+ // new generates IMAGE_DELAYLOAD_DESCRIPTOR v2.
+ pe_err_e parse_err = parse_imported_functions(
+ ctx, dll, ofs, (dd->Attributes.u1.RvaBased != 0));
+ if (parse_err != LIBPE_E_OK) {
+ imports->err = parse_err;
+ return imports;
+ }
+
+ ofs = aux; // Restore previous ofs
+ }
+
+ return imports;
}
-void pe_imports_dealloc(pe_imports_t *obj) {
- if (obj == NULL)
- return;
-
- for (uint32_t i=0; i < obj->dll_count; i++) {
- const pe_imported_dll_t *dll = &obj->dlls[i];
- for (uint32_t j=0; j < dll->functions_count; j++) {
- const pe_imported_function_t *function = &dll->functions[j];
- free(function->name);
- }
- free(dll->name);
- free(dll->functions);
- }
- for (uint32_t i=0; i < obj->delay_dll_count; i++) {
- const pe_imported_dll_t *dll = &obj->delay_dlls[i];
- for (uint32_t j=0; j < dll->functions_count; j++) {
- const pe_imported_function_t *function = &dll->functions[j];
- free(function->name);
- }
- free(dll->name);
- free(dll->functions);
- }
- free(obj->dlls);
- free(obj->delay_dlls);
- free(obj);
+void pe_imports_dealloc(pe_imports_t *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < obj->dll_count; i++) {
+ const pe_imported_dll_t *dll = &obj->dlls[i];
+ for (uint32_t j = 0; j < dll->functions_count; j++) {
+ const pe_imported_function_t *function = &dll->functions[j];
+ free(function->name);
+ }
+ free(dll->name);
+ free(dll->functions);
+ }
+ for (uint32_t i = 0; i < obj->delay_dll_count; i++) {
+ const pe_imported_dll_t *dll = &obj->delay_dlls[i];
+ for (uint32_t j = 0; j < dll->functions_count; j++) {
+ const pe_imported_function_t *function = &dll->functions[j];
+ free(function->name);
+ }
+ free(dll->name);
+ free(dll->functions);
+ }
+ free(obj->dlls);
+ free(obj->delay_dlls);
+ free(obj);
}
+
diff --git a/lib/libpe/include/libpe/context.h b/lib/libpe/include/libpe/context.h
deleted file mode 100644
index c167ec4c..00000000
--- a/lib/libpe/include/libpe/context.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2023 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_CONTEXT_H
-#define LIBPE_CONTEXT_H
-
-#include
-#include
-
-#include "hdr_dos.h"
-#include "hdr_coff.h"
-#include "hdr_optional.h"
-#include "directories.h"
-#include "sections.h"
-#include "imports.h"
-#include "exports.h"
-#include "hashes.h"
-#include "types_resources.h"
-
-typedef struct {
- // DOS header
- IMAGE_DOS_HEADER *dos_hdr;
- // Signature
- uint32_t signature;
- // COFF header
- IMAGE_COFF_HEADER *coff_hdr;
- // Optional header
- void *optional_hdr_ptr;
- IMAGE_OPTIONAL_HEADER optional_hdr;
- // Directories
- uint32_t num_directories;
- void *directories_ptr;
- IMAGE_DATA_DIRECTORY **directories; // array up to MAX_DIRECTORIES
- // Sections
- uint16_t num_sections;
- void *sections_ptr;
- IMAGE_SECTION_HEADER **sections; // array up to MAX_SECTIONS
- // Symbols
- uint32_t num_symbols;
- void *symbols_ptr;
- // Strings
- uint32_t strings_size;
- const char *strings_ptr;
- uint64_t entrypoint;
- uint64_t imagebase;
-} pe_file_t;
-
-typedef struct {
- // Parsed directories
- pe_imports_t *imports;
- pe_exports_t *exports;
- // Hashes
- pe_hash_headers_t *hash_headers;
- pe_hash_sections_t *hash_sections;
- pe_hash_t *hash_file;
- // Resources
- pe_resources_t *resources;
-} pe_cached_data_t;
-
-typedef struct pe_ctx {
- FILE *stream;
- char *path;
- void *map_addr;
- off_t map_size;
- uintptr_t map_end;
- pe_file_t pe;
- pe_cached_data_t cached_data;
-} pe_ctx_t;
-
-#endif
diff --git a/lib/libpe/include/libpe/dir_import.h b/lib/libpe/include/libpe/dir_import.h
deleted file mode 100644
index eed6e883..00000000
--- a/lib/libpe/include/libpe/dir_import.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_DIR_IMPORT_H
-#define LIBPE_DIR_IMPORT_H
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#pragma pack(push, 1)
-
-typedef struct {
- union {
- uint32_t Characteristics; // 0 for terminating null import descriptor
- uint32_t OriginalFirstThunk; // RVA to original unbound IAT
- } u1;
- uint32_t TimeDateStamp;
- uint32_t ForwarderChain; // -1 if no forwarders
- uint32_t Name;
- // RVA to IAT (if bound this IAT has actual addresses)
- uint32_t FirstThunk;
-} IMAGE_IMPORT_DESCRIPTOR;
-
-typedef struct {
- union {
- uint32_t AllAttributes;
- struct {
- uint32_t RvaBased : 1; // Delay load version 2
- uint32_t ReservedAttributes : 31;
- } u1;
- } Attributes;
- uint32_t DllNameRVA; // RVA to the name of the target library (NULL-terminate ASCII string)
- uint32_t ModuleHandleRVA; // RVA to the HMODULE caching location (PHMODULE)
- uint32_t ImportAddressTableRVA; // RVA to the start of the IAT (PIMAGE_THUNK_DATA)
- uint32_t ImportNameTableRVA; // RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData)
- uint32_t BoundImportAddressTableRVA; // RVA to an optional bound IAT
- uint32_t UnloadInformationTableRVA; // RVA to an optional unload info table
- uint32_t TimeDateStamp; // 0 if not bound, Otherwise, date/time of the target DLL
-} IMAGE_DELAYLOAD_DESCRIPTOR;
-
-// import name entry
-typedef struct {
- uint16_t Hint;
- uint8_t Name[1];
-} IMAGE_IMPORT_BY_NAME;
-
-typedef struct {
- union {
- uint64_t ForwarderString; // RVA to a forwarder string
- uint64_t Function; // Memory address of the imported function
- uint64_t Ordinal; // Ordinal value of imported API
- uint64_t AddressOfData; // RVA to an IMAGE_IMPORT_BY_NAME with the imported API name
- } u1;
-} IMAGE_THUNK_DATA64;
-
-typedef struct {
- union {
- uint32_t ForwarderString; // RVA to a forwarder string
- uint32_t Function; // Memory address of the imported function
- uint32_t Ordinal; // Ordinal value of imported API
- uint32_t AddressOfData; // RVA to an IMAGE_IMPORT_BY_NAME with the imported API name
- } u1;
-} IMAGE_THUNK_DATA32;
-
-#pragma pack(pop)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif
diff --git a/lib/libpe/include/libpe/dir_resources.h b/lib/libpe/include/libpe/dir_resources.h
deleted file mode 100644
index ff8ff92e..00000000
--- a/lib/libpe/include/libpe/dir_resources.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_DIR_RESOURCES_H
-#define LIBPE_DIR_RESOURCES_H
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000
-#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000
-
-// REFERENCE: https://docs.microsoft.com/en-us/windows/win32/menurc/resource-types
-typedef enum {
- RT_CURSOR = 1, // cursor image
- RT_BITMAP = 2, // bitmap (.bmp)
- RT_ICON = 3, // icon
- RT_MENU = 4, // menu
- RT_DIALOG = 5, // dialog window
- RT_STRING = 6, // unicode string
- RT_FONTDIR = 7, // font directory
- RT_FONT = 8, // font
- RT_ACCELERATOR = 9, // hot keys
- RT_RCDATA = 10, // data
- RT_MESSAGETABLE = 11, // string table
- RT_GROUP_CURSOR = 12, // cursor group
- RT_GROUP_ICON = 14, // icon group
- RT_NAMETABLE = 15, // name table (removed in Windows 3.1)
- RT_VERSION = 16, // version information
- RT_DLGINCLUDE = 17, // names of header files for dialogs (*.h) used by compiler
- RT_PLUGPLAY = 19, // data determined by application
- RT_VXD = 20, // vxd info
- RT_ANICURSOR = 21, // animated cursor
- RT_ANIICON = 22, // animated icon
- RT_HTML = 23, // html page
- RT_MANIFEST = 24, // manifest of Windows XP build
- RT_PSZ = 204, // string resource (used for ttf font file name)
- RT_DLGINIT = 240, // strings used for initiating some controls in dialogs
- RT_TOOLBAR = 241 // configuration of toolbars
-} ResourceType;
-
-#pragma pack(push, 1)
-
-typedef struct {
- uint32_t Characteristics;
- uint32_t TimeDateStamp;
- uint16_t MajorVersion;
- uint16_t MinorVersion;
- uint16_t NumberOfNamedEntries;
- uint16_t NumberOfIdEntries;
-} IMAGE_RESOURCE_DIRECTORY;
-
-typedef struct {
- union {
- struct {
- uint32_t NameOffset:31;
- uint32_t NameIsString:1;
- } data;
- uint32_t Name;
- uint16_t Id;
- } u0;
- union {
- uint32_t OffsetToData;
- struct {
- uint32_t OffsetToDirectory:31;
- uint32_t DataIsDirectory:1;
- } data;
- } u1;
-} IMAGE_RESOURCE_DIRECTORY_ENTRY;
-
-typedef struct {
- uint16_t Length;
- char String[1];
-} IMAGE_RESOURCE_DATA_STRING;
-
-typedef struct {
- uint16_t Length; // Number of Unicode characters
- uint16_t String[1];
-} IMAGE_RESOURCE_DATA_STRING_U;
-
-typedef struct {
- uint32_t OffsetToData;
- uint32_t Size;
- uint32_t CodePage;
- uint32_t Reserved;
-} IMAGE_RESOURCE_DATA_ENTRY;
-
-typedef struct {
- uint16_t wLength;
- uint16_t wValueLength;
- uint16_t wType;
-} VS_VERSIONINFO_HEAD;
-
-typedef struct {
- uint32_t dwSignature;
- uint32_t dwStrucVersion;
- uint32_t dwFileVersionMS;
- uint32_t dwFileVersionLS;
- uint32_t dwProductVersionMS;
- uint32_t dwProductVersionLS;
- uint32_t dwFileFlagsMask;
- uint32_t dwFileFlags;
- uint32_t dwFileOS;
- uint32_t dwFileType;
- uint32_t dwFileSubtype;
- uint32_t dwFileDateMS;
- uint32_t dwFileDateLS;
-} VS_FIXEDFILEINFO;
-
-#pragma pack(pop)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif
diff --git a/lib/libpe/include/libpe/dir_security.h b/lib/libpe/include/libpe/dir_security.h
deleted file mode 100644
index e6560920..00000000
--- a/lib/libpe/include/libpe/dir_security.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_DIR_SECURITY_H
-#define LIBPE_DIR_SECURITY_H
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define ANYSIZE_ARRAY 1
-
-// #define WIN_TRUST_MAJOR_REVISION_MASK 0xFFFF0000
-// #define WIN_TRUST_MINOR_REVISION_MASK 0x0000FFFF
-// #define WIN_TRUST_REVISION_1_0 0x00010000
-
-typedef enum {
- // Version 1, legacy version of the Win_Certificate
- // structure. It is supported only for purposes of
- // verifying legacy Authenticode signatures
- WIN_CERT_REVISION_1_0 = 0x0100,
- // Version 2 is the current version of the Win_Certificate structure.
- WIN_CERT_REVISION_2_0 = 0x0200
-} CertRevision;
-
-typedef enum {
- WIN_CERT_TYPE_X509 = 0x0001, // bCertificate contains an X.509 (Certificate)
- WIN_CERT_TYPE_PKCS_SIGNED_DATA = 0x0002, // bCertificate contains a PKCS#7 (SignedData structure)
- WIN_CERT_TYPE_RESERVED_1 = 0x0003, // Reserved
- WIN_CERT_TYPE_TS_STACK_SIGNED = 0x0004, // Terminal Server Protocol Stack (Certificate signing)
- WIN_CERT_TYPE_EFI_PKCS115 = 0x0EF0,
- WIN_CERT_TYPE_EFI_GUID = 0x0EF1
-} CertType;
-
-#pragma pack(push, 4)
-
-// Originally declared in Wintrust.h
-typedef struct {
- // Specified the size, in bytes, of the WIN_CERTIFICATE structure,
- // including the data in bCertificate.
- uint32_t dwLength;
- // Indicates the revision of the structure.
- uint16_t wRevision;
- // Specifies the type of certificate.
- // This member can be one of the following values:
- // Value Meaning
- // ----------------------------------------------------------------------------------------
- // WIN_CERT_TYPE_X509 The certificate contains an X.509 Certificate.
- // WIN_CERT_TYPE_PKCS_SIGNED_DATA The certificate contains a PKCS SignedData structure.
- // WIN_CERT_TYPE_RESERVED_1 Reserved.
- // WIN_CERT_TYPE_TS_STACK_SIGNED
- uint16_t wCertificateType;
- // A variable-sized array of bytes that contains the certificate data.
- uint8_t bCertificate[ANYSIZE_ARRAY];
-} WIN_CERTIFICATE;
-
-typedef struct {
- uint32_t cbData;
- uint8_t *pbData;
-} CRYPT_DATA_BLOB;
-
-#pragma pack(pop)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif
diff --git a/lib/libpe/include/libpe/directories.h b/lib/libpe/include/libpe/directories.h
deleted file mode 100644
index 8346f102..00000000
--- a/lib/libpe/include/libpe/directories.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_DIRECTORIES_H
-#define LIBPE_DIRECTORIES_H
-
-#include
-#include "dir_import.h"
-#include "dir_resources.h"
-#include "dir_security.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Directory entries
-typedef enum {
- IMAGE_DIRECTORY_ENTRY_EXPORT = 0, // Export Table
- IMAGE_DIRECTORY_ENTRY_IMPORT = 1, // Import Table
- IMAGE_DIRECTORY_ENTRY_RESOURCE = 2, // Resource Table
- IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3, // Exception Table
- IMAGE_DIRECTORY_ENTRY_SECURITY = 4, // Certificate Table
- IMAGE_DIRECTORY_ENTRY_BASERELOC = 5, // Base Relocation Table
- IMAGE_DIRECTORY_ENTRY_DEBUG = 6, // Debug
- //IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7, // (X86 usage)
- IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7, // Architecture
- IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8, // Global Ptr
- IMAGE_DIRECTORY_ENTRY_TLS = 9, // TLS Table
- IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10, // Load Config Table
- IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11, // Bound Import
- IMAGE_DIRECTORY_ENTRY_IAT = 12, // IAT
- IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13, // Delay Import Descriptor
- IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14, // CLR Runtime Header
- IMAGE_DIRECTORY_RESERVED = 15 // Reserved, must be zero
-} ImageDirectoryEntry;
-
-typedef struct {
- uint32_t Characteristics;
- uint32_t TimeDateStamp;
- uint16_t MajorVersion;
- uint16_t MinorVersion;
- uint32_t Name;
- uint32_t Base;
- uint32_t NumberOfFunctions;
- uint32_t NumberOfNames;
- uint32_t AddressOfFunctions;
- uint32_t AddressOfNames;
- uint32_t AddressOfNameOrdinals;
-} IMAGE_EXPORT_DIRECTORY;
-
-typedef struct {
- uint32_t StartAddressOfRawData;
- uint32_t EndAddressOfRawData;
- uint32_t AddressOfIndex;
- uint32_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK
- uint32_t SizeOfZeroFill;
- uint32_t Characteristics; // reserved for future use
-} IMAGE_TLS_DIRECTORY32;
-
-typedef struct {
- uint64_t StartAddressOfRawData;
- uint64_t EndAddressOfRawData;
- uint64_t AddressOfIndex;
- uint64_t AddressOfCallBacks;
- uint32_t SizeOfZeroFill;
- uint32_t Characteristics;
-} IMAGE_TLS_DIRECTORY64;
-
-typedef enum {
- IMAGE_DEBUG_TYPE_UNKNOWN = 0, // Unknown value, ignored by all tools
- IMAGE_DEBUG_TYPE_COFF = 1, // COFF debugging information
- IMAGE_DEBUG_TYPE_CODEVIEW = 2, // CodeView debugging information or Visual C++ Program Database debugging information
- IMAGE_DEBUG_TYPE_FPO = 3, // Frame pointer omission (FPO) information
- IMAGE_DEBUG_TYPE_MISC = 4, // Location of DBG file with CodeView debugging information
- IMAGE_DEBUG_TYPE_EXCEPTION = 5, // Exception information, copy of .pdata section
- IMAGE_DEBUG_TYPE_FIXUP = 6, // Fixup information
- IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7, // The mapping from an RVA in image to an RVA in source image
- IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8, // The mapping from an RVA in source image to an RVA in image
- IMAGE_DEBUG_TYPE_BORLAND = 9, // Borland debugging information
- IMAGE_DEBUG_TYPE_RESERVED10 = 10, // Coldpath / Hotpatch debug information, https://www.openrce.org/articles/full_view/22
- IMAGE_DEBUG_TYPE_CLSID = 11,
- IMAGE_DEBUG_TYPE_VC_FEATURE = 12, // Visual C++ counts / statistics
- IMAGE_DEBUG_TYPE_POGO = 13, // COFF group information, data for profile-guided optimization, LINK.EXE /LTCG
- IMAGE_DEBUG_TYPE_ILTCG = 14, // Incremental link-time code generation, LINK.EXE /LTCG:INCREMENTAL
- IMAGE_DEBUG_TYPE_MPX = 15, // Intel Memory Protection Extensions, CL.EXE /d2MPX, https://devblogs.microsoft.com/cppblog/visual-studio-2015-update-1-new-experimental-feature-mpx/
- IMAGE_DEBUG_TYPE_REPRO = 16, // PE determinism or reproducibility, LINK.EXE /Brepro
- IMAGE_DEBUG_TYPE_EMBEDDED_PORTABLE_PDB = 17, // Embedded Portable PDB debugging information, https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
- IMAGE_DEBUG_TYPE_SPGO = 18, // Sample profile-guided optimization
- IMAGE_DEBUG_TYPE_PDBCHECKSUM = 19, // PDB Checksum, https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
- IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20, // Extended DLL characteristics bits
- IMAGE_DEBUG_TYPE_PERFMAP = 21 // Location of associated Ready To Run PerfMap file, https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
-} ImageDebugType;
-
-typedef struct {
- uint32_t Characteristics;
- uint32_t TimeDateStamp;
- uint16_t MajorVersion;
- uint16_t MinorVersion;
- uint32_t Type;
- uint32_t SizeOfData;
- uint32_t AddressOfRawData;
- uint32_t PointerToRawData;
-} IMAGE_DEBUG_DIRECTORY;
-
-typedef struct {
- uint32_t VirtualAddress;
- uint32_t Size;
-} IMAGE_DATA_DIRECTORY;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif
diff --git a/lib/libpe/include/libpe/hdr_coff.h b/lib/libpe/include/libpe/hdr_coff.h
deleted file mode 100644
index 31e775be..00000000
--- a/lib/libpe/include/libpe/hdr_coff.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_HDR_COFF_H
-#define LIBPE_HDR_COFF_H
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
- IMAGE_FILE_MACHINE_ALPHA_OLD = 0x183,
- IMAGE_FILE_MACHINE_ALPHA = 0x184,
- IMAGE_FILE_MACHINE_ALPHA64 = 0x284,
- IMAGE_FILE_MACHINE_AM33 = 0x1d3,
- IMAGE_FILE_MACHINE_AMD64 = 0x8664,
- IMAGE_FILE_MACHINE_ARM = 0x1c0,
- IMAGE_FILE_MACHINE_ARMV7 = 0x1c4,
- IMAGE_FILE_MACHINE_ARM64 = 0xaa64,
- IMAGE_FILE_MACHINE_ARM64EC = 0xa641,
- IMAGE_FILE_MACHINE_ARM64X = 0xa64e,
- IMAGE_FILE_MACHINE_CEE = 0xc0ee,
- IMAGE_FILE_MACHINE_CEF = 0xcef,
- IMAGE_FILE_MACHINE_CHPE_X86 = 0x3a64,
- IMAGE_FILE_MACHINE_EBC = 0xebc,
- IMAGE_FILE_MACHINE_I386 = 0x14c,
- IMAGE_FILE_MACHINE_I860 = 0x14d,
- IMAGE_FILE_MACHINE_IA64 = 0x200,
- IMAGE_FILE_MACHINE_LOONGARCH32 = 0x6232,
- IMAGE_FILE_MACHINE_LOONGARCH64 = 0x6264,
- IMAGE_FILE_MACHINE_M32R = 0x9041,
- IMAGE_FILE_MACHINE_M68K = 0x268,
- IMAGE_FILE_MACHINE_MIPS16 = 0x266,
- IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
- IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
- IMAGE_FILE_MACHINE_MPPC_601 = 0x601,
- IMAGE_FILE_MACHINE_OMNI = 0xace1,
- IMAGE_FILE_MACHINE_PARISC = 0x290,
- IMAGE_FILE_MACHINE_POWERPC = 0x1f0,
- IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1,
- IMAGE_FILE_MACHINE_POWERPCBE = 0x1f2,
- IMAGE_FILE_MACHINE_R3000 = 0x162,
- IMAGE_FILE_MACHINE_R3000_BE = 0x160,
- IMAGE_FILE_MACHINE_R4000 = 0x166,
- IMAGE_FILE_MACHINE_R10000 = 0x168,
- IMAGE_FILE_MACHINE_RISCV32 = 0x5032,
- IMAGE_FILE_MACHINE_RISCV64 = 0x5064,
- IMAGE_FILE_MACHINE_RISCV128 = 0x5128,
- IMAGE_FILE_MACHINE_SH3 = 0x1a2,
- IMAGE_FILE_MACHINE_SH3DSP = 0x1a3,
- IMAGE_FILE_MACHINE_SH3E = 0x1a4,
- IMAGE_FILE_MACHINE_SH4 = 0x1a6,
- IMAGE_FILE_MACHINE_SH5 = 0x1a8,
- IMAGE_FILE_MACHINE_TRICORE = 0x520,
- IMAGE_FILE_MACHINE_TAHOE = 0x7cc,
- IMAGE_FILE_MACHINE_THUMB = 0x1c2,
- IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
-} MachineType;
-
-typedef enum {
- // Image only, Windows CE, Windows NT and above. Indicates that the
- // file does not contain base relocations and must therefore be
- // loaded at its preferred base address. If the base address is not
- // available, the loader reports an error. The default behavior of
- // the linker is to strip base relocations from EXEs.
- IMAGE_FILE_RELOCS_STRIPPED = 0x0001,
-
- // Image only. Indicates that the image file is valid and can be run.
- // If this flag is not set, it indicates a linker error.
- IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002,
-
- // COFF line numbers have been removed.
- // Deprecated and should be zero.
- IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004,
-
- // COFF symbol table entries for local symbols have been removed.
- // Deprecated and should be zero.
- IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008,
-
- // Obsolete. Aggressively trim working set.
- // Deprecated in Windows 2000 and later. Must be zero.
- IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010,
-
- // App can handle > 2gb addresses.
- // Image can be loaded at address above 2GB.
- IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020,
-
- // Machine based on 16-bit-word architecture.
- IMAGE_FILE_16BIT_MACHINE = 0x0040,
-
- // Bytes of the word are reversed from CPU defaults.
- // Test either IMAGE_FILE_BYTES_REVERSED_LO or IMAGE_FILE_BYTES_REVERSED_HI, they are in the same bit position in each short word.
- // Microsoft PE 32-Bit LINK.EXE Version 1.00 always sets this bit, but no words are reversed. New LINK.EXE versions never set this bit.
- // Deprecated and should be zero.
- IMAGE_FILE_BYTES_REVERSED_LO = 0x0080,
-
- // Machine based on 32-bit-word architecture.
- IMAGE_FILE_32BIT_MACHINE = 0x0100,
-
- // Debugging information removed from image file.
- IMAGE_FILE_DEBUG_STRIPPED = 0x0200,
-
- // If image is on removable media, fully load it and copy it to the
- // swap file.
- IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400,
-
- // If image is on network media, fully load it and copy it to the
- // swap file.
- IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800,
-
- // The image file is a system kernel-mode file, not a user program.
- // Images with this flag can't be loaded in user-mode. Images without
- // this flag can be loaded in both kernel-mode and user-mode.
- IMAGE_FILE_SYSTEM = 0x1000,
-
- // The image file is a dynamic-link library (DLL). Such files are
- // considered executable files for almost all purposes, although
- // they cannot be directly run.
- IMAGE_FILE_DLL = 0x2000,
-
- // File should be run only on a UP (uniprocessor) machine.
- // When running on multiprocessor machine, process has assigned
- // one selected core via CPU affinity on which it always run.
- IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000,
-
- // Bytes of the word are reversed from CPU defaults.
- // Test either IMAGE_FILE_BYTES_REVERSED_LO or IMAGE_FILE_BYTES_REVERSED_HI, they are in the same bit position in each short word.
- // Microsoft PE 32-Bit LINK.EXE Version 1.00 always sets this bit, but no words are reversed. New LINK.EXE versions never set this bit.
- // Deprecated and should be zero.
- IMAGE_FILE_BYTES_REVERSED_HI = 0x8000
-} ImageCharacteristics;
-
-#pragma pack(push, 1)
-
-typedef struct {
- uint16_t Machine; // MachineType
- uint16_t NumberOfSections;
- uint32_t TimeDateStamp;
- uint32_t PointerToSymbolTable;
- uint32_t NumberOfSymbols;
- uint16_t SizeOfOptionalHeader;
- uint16_t Characteristics; // ImageCharacteristics
-} IMAGE_FILE_HEADER, IMAGE_COFF_HEADER;
-
-#pragma pack(pop)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif
diff --git a/lib/libpe/include/libpe/hdr_optional.h b/lib/libpe/include/libpe/hdr_optional.h
deleted file mode 100644
index d8d27da9..00000000
--- a/lib/libpe/include/libpe/hdr_optional.h
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_HDR_OPTIONAL_H
-#define LIBPE_HDR_OPTIONAL_H
-
-#include
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// REFERENCE: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
-typedef enum {
- // Unknown subsystem
- IMAGE_SUBSYSTEM_UNKNOWN = 0,
- // No subsystem required (device drivers and native system processes)
- IMAGE_SUBSYSTEM_NATIVE = 1,
- // Windows graphical user interface (GUI) subsystem
- IMAGE_SUBSYSTEM_WINDOWS_GUI = 2,
- // Windows character-mode user interface (CUI) subsystem
- IMAGE_SUBSYSTEM_WINDOWS_CUI = 3,
- // Old Windows CE subsystem
- IMAGE_SUBSYSTEM_WINDOWS_OLD_CE_GUI = 4,
- // OS/2 CUI subsystem
- IMAGE_SUBSYSTEM_OS2_CUI = 5,
- // POSIX CUI subsystem
- IMAGE_SUBSYSTEM_POSIX_CUI = 7,
- // MMOSA/Native Win32E
- IMAGE_SUBSYSTEM_MMOSA = 8,
- // Windows CE system
- IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9,
- // Extensible Firmware Interface (EFI) application
- IMAGE_SUBSYSTEM_EFI_APPLICATION = 10,
- // EFI driver with boot services
- IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11,
- // EFI driver with run-time services
- IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12,
- // EFI ROM image
- IMAGE_SUBSYSTEM_EFI_ROM = 13,
- // Xbox system
- IMAGE_SUBSYSTEM_XBOX = 14,
- // Boot application
- IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16,
- // XBOX Code Catalog
- IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG = 17
-} WindowsSubsystem;
-
-// REFERENCE: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
-typedef enum {
- // IMAGE_LIBRARY_* defined in PECOFF 4.0 (https://bytepointer.com/resources/pecoff_v4.0.htm)
- // DLL initialization function called just after process initialization.
- IMAGE_LIBRARY_PROCESS_INIT = 0x0001,
- // DLL initialization function called just before process termination.
- IMAGE_LIBRARY_PROCESS_TERM = 0x0002,
- // DLL initialization function called just after thread initialization.
- // This does not apply to the first thread, which is allocated during process initialization.
- IMAGE_LIBRARY_THREAD_INIT = 0x0004,
- // DLL initialization function called just before thread initialization.
- // This does not apply to the first thread allocated.
- IMAGE_LIBRARY_THREAD_TERM = 0x0008,
- // IMAGE_DLLCHARACTERISTICS_RESERVED_10 = 0x0010,
- // ASLR with 64 bit address space.
- // Image can be loaded at address above 4GB.
- IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020,
- // The DLL can be relocated at load time.
- IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040,
- // Code integrity checks are forced.
- IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080,
- // The image is compatible with data execution prevention (DEP).
- // Prevents code execution on the stack, in the PE header and sections without IMAGE_SCN_MEM_EXECUTE.
- IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100,
- // The image is isolation aware, but should not be isolated.
- // Prevents loading of manifest file (from embedded resource or external file .manifest)
- // and prevents setting of default activation context.
- // https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference
- IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200,
- // The image does not use structured exception handling (SEH).
- // No handlers can be called in this image.
- // Vectored Exception Handler still work.
- IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400,
- // Do not bind the image.
- IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800,
- // Image is a Wx86 Thunk DLL.
- // Valid only for non-x86 (risc) DLL files.
- // Can be generated by undocumented MSVC5+ linker flag /dllchar:x86thunk.
- IMAGE_DLLCHARACTERISTICS_X86_THUNK = 0x1000,
- // Image should execute in an AppContainer (Metro Apps in Windows 8).
- // Valid only for EXE files.
- IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000,
- // A WDM driver.
- IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000,
- // Image supports Control Flow Guard
- IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000,
- // The image is terminal server (Remote Desktop Services) aware.
- // https://learn.microsoft.com/en-us/windows/win32/termserv/application-compatibility-layer
- IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
-} ImageDllCharacteristics;
-
-// PECOFF 4.0 (https://bytepointer.com/resources/pecoff_v4.0.htm) and WDK's ntimage.h
-typedef enum {
- // DLL initialization function: Halt prior to executing first instruction.
- IMAGE_LOADER_FLAGS_BREAK_ON_LOAD = 0x00000001,
- // DLL initialization function: Break prior to executing first instruction; effect is similar to a breakpoint.
- IMAGE_LOADER_FLAGS_DEBUG_ON_LOAD = 0x00000002,
- // COM+ image executable; obsolete and automatically set by loader when IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR is present.
- IMAGE_LOADER_FLAGS_COMPLUS = 0x00000001,
- // Global subsections apply across TS sessions.
- IMAGE_LOADER_FLAGS_SYSTEM_GLOBAL = 0x01000000
-} ImageLoaderFlags;
-
-typedef enum {
- MAGIC_PE32_0 = 0x000,
- MAGIC_ROM = 0x107,
- MAGIC_PE32 = 0x10b,
- MAGIC_PE64 = 0x20b // PE32+
-} opt_type_e;
-
-#pragma pack(push, 1)
-
-typedef struct {
- uint16_t Magic;
- uint8_t MajorLinkerVersion;
- uint8_t MinorLinkerVersion;
- uint32_t SizeOfCode;
- uint32_t SizeOfInitializedData;
- uint32_t SizeOfUninitializedData;
- uint32_t AddressOfEntryPoint;
- uint32_t BaseOfCode;
- uint32_t BaseOfData;
- uint32_t BaseOfBss;
- uint32_t GprMask;
- uint32_t CprMask[4];
- uint32_t GpValue;
-} IMAGE_ROM_OPTIONAL_HEADER;
-
-// REFERENCE: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
-typedef struct {
- uint16_t Magic;
- uint8_t MajorLinkerVersion;
- uint8_t MinorLinkerVersion;
- uint32_t SizeOfCode;
- uint32_t SizeOfInitializedData;
- uint32_t SizeOfUninitializedData;
- uint32_t AddressOfEntryPoint;
- uint32_t BaseOfCode;
- uint32_t BaseOfData; // only in PE32
- uint32_t ImageBase;
- uint32_t SectionAlignment;
- uint32_t FileAlignment;
- uint16_t MajorOperatingSystemVersion;
- uint16_t MinorOperatingSystemVersion;
- uint16_t MajorImageVersion;
- uint16_t MinorImageVersion;
- uint16_t MajorSubsystemVersion;
- uint16_t MinorSubsystemVersion;
- uint32_t Win32VersionValue;
- uint32_t SizeOfImage;
- uint32_t SizeOfHeaders;
- uint32_t CheckSum;
- uint16_t Subsystem; // WindowsSubsystem
- uint16_t DllCharacteristics;
- uint32_t SizeOfStackReserve;
- uint32_t SizeOfStackCommit;
- uint32_t SizeOfHeapReserve;
- uint32_t SizeOfHeapCommit;
- uint32_t LoaderFlags;
- uint32_t NumberOfRvaAndSizes;
- // IMAGE_DATA_DIRECTORY DataDirectory[MAX_DIRECTORIES];
-} IMAGE_OPTIONAL_HEADER_32;
-
-// REFERENCE: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
-typedef struct {
- uint16_t Magic;
- uint8_t MajorLinkerVersion;
- uint8_t MinorLinkerVersion;
- uint32_t SizeOfCode;
- uint32_t SizeOfInitializedData;
- uint32_t SizeOfUninitializedData;
- uint32_t AddressOfEntryPoint;
- uint32_t BaseOfCode;
- uint64_t ImageBase;
- uint32_t SectionAlignment;
- uint32_t FileAlignment;
- uint16_t MajorOperatingSystemVersion;
- uint16_t MinorOperatingSystemVersion;
- uint16_t MajorImageVersion;
- uint16_t MinorImageVersion;
- uint16_t MajorSubsystemVersion;
- uint16_t MinorSubsystemVersion;
- uint32_t Win32VersionValue;
- uint32_t SizeOfImage;
- uint32_t SizeOfHeaders;
- uint32_t CheckSum;
- uint16_t Subsystem; // WindowsSubsystem
- uint16_t DllCharacteristics;
- uint64_t SizeOfStackReserve;
- uint64_t SizeOfStackCommit;
- uint64_t SizeOfHeapReserve;
- uint64_t SizeOfHeapCommit;
- uint32_t LoaderFlags;
- uint32_t NumberOfRvaAndSizes;
- // IMAGE_DATA_DIRECTORY DataDirectory[MAX_DIRECTORIES];
-} IMAGE_OPTIONAL_HEADER_64;
-
-typedef struct {
- uint16_t type; // opt_type_e
- size_t length;
- IMAGE_OPTIONAL_HEADER_32 *_32;
- IMAGE_OPTIONAL_HEADER_64 *_64;
- IMAGE_ROM_OPTIONAL_HEADER *_rom;
-} IMAGE_OPTIONAL_HEADER;
-
-#pragma pack(pop)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif
diff --git a/lib/libpe/include/libpe/ordlookup.h b/lib/libpe/include/libpe/ordlookup.h
deleted file mode 100644
index c0dc8974..00000000
--- a/lib/libpe/include/libpe/ordlookup.h
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-// This lookup database is necessary to match Ero Carrera's pefile imphash implementation used by VT and other programs
-// https://github.com/erocarrera/pefile/tree/master/ordlookup
-
-#pragma once
-
-typedef struct {
- int number;
- char *fname;
-} ord_t;
-
-ord_t oleaut32_arr[] = {
- { 2, "SysAllocString" },
- { 3, "SysReAllocString" },
- { 4, "SysAllocStringLen" },
- { 5, "SysReAllocStringLen" },
- { 6, "SysFreeString" },
- { 7, "SysStringLen" },
- { 8, "VariantInit" },
- { 9, "VariantClear" },
- { 10, "VariantCopy" },
- { 11, "VariantCopyInd" },
- { 12, "VariantChangeType" },
- { 13, "VariantTimeToDosDateTime" },
- { 14, "DosDateTimeToVariantTime" },
- { 15, "SafeArrayCreate" },
- { 16, "SafeArrayDestroy" },
- { 17, "SafeArrayGetDim" },
- { 18, "SafeArrayGetElemsize" },
- { 19, "SafeArrayGetUBound" },
- { 20, "SafeArrayGetLBound" },
- { 21, "SafeArrayLock" },
- { 22, "SafeArrayUnlock" },
- { 23, "SafeArrayAccessData" },
- { 24, "SafeArrayUnaccessData" },
- { 25, "SafeArrayGetElement" },
- { 26, "SafeArrayPutElement" },
- { 27, "SafeArrayCopy" },
- { 28, "DispGetParam" },
- { 29, "DispGetIDsOfNames" },
- { 30, "DispInvoke" },
- { 31, "CreateDispTypeInfo" },
- { 32, "CreateStdDispatch" },
- { 33, "RegisterActiveObject" },
- { 34, "RevokeActiveObject" },
- { 35, "GetActiveObject" },
- { 36, "SafeArrayAllocDescriptor" },
- { 37, "SafeArrayAllocData" },
- { 38, "SafeArrayDestroyDescriptor" },
- { 39, "SafeArrayDestroyData" },
- { 40, "SafeArrayRedim" },
- { 41, "SafeArrayAllocDescriptorEx" },
- { 42, "SafeArrayCreateEx" },
- { 43, "SafeArrayCreateVectorEx" },
- { 44, "SafeArraySetRecordInfo" },
- { 45, "SafeArrayGetRecordInfo" },
- { 46, "VarParseNumFromStr" },
- { 47, "VarNumFromParseNum" },
- { 48, "VarI2FromUI1" },
- { 49, "VarI2FromI4" },
- { 50, "VarI2FromR4" },
- { 51, "VarI2FromR8" },
- { 52, "VarI2FromCy" },
- { 53, "VarI2FromDate" },
- { 54, "VarI2FromStr" },
- { 55, "VarI2FromDisp" },
- { 56, "VarI2FromBool" },
- { 57, "SafeArraySetIID" },
- { 58, "VarI4FromUI1" },
- { 59, "VarI4FromI2" },
- { 60, "VarI4FromR4" },
- { 61, "VarI4FromR8" },
- { 62, "VarI4FromCy" },
- { 63, "VarI4FromDate" },
- { 64, "VarI4FromStr" },
- { 65, "VarI4FromDisp" },
- { 66, "VarI4FromBool" },
- { 67, "SafeArrayGetIID" },
- { 68, "VarR4FromUI1" },
- { 69, "VarR4FromI2" },
- { 70, "VarR4FromI4" },
- { 71, "VarR4FromR8" },
- { 72, "VarR4FromCy" },
- { 73, "VarR4FromDate" },
- { 74, "VarR4FromStr" },
- { 75, "VarR4FromDisp" },
- { 76, "VarR4FromBool" },
- { 77, "SafeArrayGetVartype" },
- { 78, "VarR8FromUI1" },
- { 79, "VarR8FromI2" },
- { 80, "VarR8FromI4" },
- { 81, "VarR8FromR4" },
- { 82, "VarR8FromCy" },
- { 83, "VarR8FromDate" },
- { 84, "VarR8FromStr" },
- { 85, "VarR8FromDisp" },
- { 86, "VarR8FromBool" },
- { 87, "VarFormat" },
- { 88, "VarDateFromUI1" },
- { 89, "VarDateFromI2" },
- { 90, "VarDateFromI4" },
- { 91, "VarDateFromR4" },
- { 92, "VarDateFromR8" },
- { 93, "VarDateFromCy" },
- { 94, "VarDateFromStr" },
- { 95, "VarDateFromDisp" },
- { 96, "VarDateFromBool" },
- { 97, "VarFormatDateTime" },
- { 98, "VarCyFromUI1" },
- { 99, "VarCyFromI2" },
- { 100, "VarCyFromI4" },
- { 101, "VarCyFromR4" },
- { 102, "VarCyFromR8" },
- { 103, "VarCyFromDate" },
- { 104, "VarCyFromStr" },
- { 105, "VarCyFromDisp" },
- { 106, "VarCyFromBool" },
- { 107, "VarFormatNumber" },
- { 108, "VarBstrFromUI1" },
- { 109, "VarBstrFromI2" },
- { 110, "VarBstrFromI4" },
- { 111, "VarBstrFromR4" },
- { 112, "VarBstrFromR8" },
- { 113, "VarBstrFromCy" },
- { 114, "VarBstrFromDate" },
- { 115, "VarBstrFromDisp" },
- { 116, "VarBstrFromBool" },
- { 117, "VarFormatPercent" },
- { 118, "VarBoolFromUI1" },
- { 119, "VarBoolFromI2" },
- { 120, "VarBoolFromI4" },
- { 121, "VarBoolFromR4" },
- { 122, "VarBoolFromR8" },
- { 123, "VarBoolFromDate" },
- { 124, "VarBoolFromCy" },
- { 125, "VarBoolFromStr" },
- { 126, "VarBoolFromDisp" },
- { 127, "VarFormatCurrency" },
- { 128, "VarWeekdayName" },
- { 129, "VarMonthName" },
- { 130, "VarUI1FromI2" },
- { 131, "VarUI1FromI4" },
- { 132, "VarUI1FromR4" },
- { 133, "VarUI1FromR8" },
- { 134, "VarUI1FromCy" },
- { 135, "VarUI1FromDate" },
- { 136, "VarUI1FromStr" },
- { 137, "VarUI1FromDisp" },
- { 138, "VarUI1FromBool" },
- { 139, "VarFormatFromTokens" },
- { 140, "VarTokenizeFormatString" },
- { 141, "VarAdd" },
- { 142, "VarAnd" },
- { 143, "VarDiv" },
- { 144, "DllCanUnloadNow" },
- { 145, "DllGetClassObject" },
- { 146, "DispCallFunc" },
- { 147, "VariantChangeTypeEx" },
- { 148, "SafeArrayPtrOfIndex" },
- { 149, "SysStringByteLen" },
- { 150, "SysAllocStringByteLen" },
- { 151, "DllRegisterServer" },
- { 152, "VarEqv" },
- { 153, "VarIdiv" },
- { 154, "VarImp" },
- { 155, "VarMod" },
- { 156, "VarMul" },
- { 157, "VarOr" },
- { 158, "VarPow" },
- { 159, "VarSub" },
- { 160, "CreateTypeLib" },
- { 161, "LoadTypeLib" },
- { 162, "LoadRegTypeLib" },
- { 163, "RegisterTypeLib" },
- { 164, "QueryPathOfRegTypeLib" },
- { 165, "LHashValOfNameSys" },
- { 166, "LHashValOfNameSysA" },
- { 167, "VarXor" },
- { 168, "VarAbs" },
- { 169, "VarFix" },
- { 170, "OaBuildVersion" },
- { 171, "ClearCustData" },
- { 172, "VarInt" },
- { 173, "VarNeg" },
- { 174, "VarNot" },
- { 175, "VarRound" },
- { 176, "VarCmp" },
- { 177, "VarDecAdd" },
- { 178, "VarDecDiv" },
- { 179, "VarDecMul" },
- { 180, "CreateTypeLib2" },
- { 181, "VarDecSub" },
- { 182, "VarDecAbs" },
- { 183, "LoadTypeLibEx" },
- { 184, "SystemTimeToVariantTime" },
- { 185, "VariantTimeToSystemTime" },
- { 186, "UnRegisterTypeLib" },
- { 187, "VarDecFix" },
- { 188, "VarDecInt" },
- { 189, "VarDecNeg" },
- { 190, "VarDecFromUI1" },
- { 191, "VarDecFromI2" },
- { 192, "VarDecFromI4" },
- { 193, "VarDecFromR4" },
- { 194, "VarDecFromR8" },
- { 195, "VarDecFromDate" },
- { 196, "VarDecFromCy" },
- { 197, "VarDecFromStr" },
- { 198, "VarDecFromDisp" },
- { 199, "VarDecFromBool" },
- { 200, "GetErrorInfo" },
- { 201, "SetErrorInfo" },
- { 202, "CreateErrorInfo" },
- { 203, "VarDecRound" },
- { 204, "VarDecCmp" },
- { 205, "VarI2FromI1" },
- { 206, "VarI2FromUI2" },
- { 207, "VarI2FromUI4" },
- { 208, "VarI2FromDec" },
- { 209, "VarI4FromI1" },
- { 210, "VarI4FromUI2" },
- { 211, "VarI4FromUI4" },
- { 212, "VarI4FromDec" },
- { 213, "VarR4FromI1" },
- { 214, "VarR4FromUI2" },
- { 215, "VarR4FromUI4" },
- { 216, "VarR4FromDec" },
- { 217, "VarR8FromI1" },
- { 218, "VarR8FromUI2" },
- { 219, "VarR8FromUI4" },
- { 220, "VarR8FromDec" },
- { 221, "VarDateFromI1" },
- { 222, "VarDateFromUI2" },
- { 223, "VarDateFromUI4" },
- { 224, "VarDateFromDec" },
- { 225, "VarCyFromI1" },
- { 226, "VarCyFromUI2" },
- { 227, "VarCyFromUI4" },
- { 228, "VarCyFromDec" },
- { 229, "VarBstrFromI1" },
- { 230, "VarBstrFromUI2" },
- { 231, "VarBstrFromUI4" },
- { 232, "VarBstrFromDec" },
- { 233, "VarBoolFromI1" },
- { 234, "VarBoolFromUI2" },
- { 235, "VarBoolFromUI4" },
- { 236, "VarBoolFromDec" },
- { 237, "VarUI1FromI1" },
- { 238, "VarUI1FromUI2" },
- { 239, "VarUI1FromUI4" },
- { 240, "VarUI1FromDec" },
- { 241, "VarDecFromI1" },
- { 242, "VarDecFromUI2" },
- { 243, "VarDecFromUI4" },
- { 244, "VarI1FromUI1" },
- { 245, "VarI1FromI2" },
- { 246, "VarI1FromI4" },
- { 247, "VarI1FromR4" },
- { 248, "VarI1FromR8" },
- { 249, "VarI1FromDate" },
- { 250, "VarI1FromCy" },
- { 251, "VarI1FromStr" },
- { 252, "VarI1FromDisp" },
- { 253, "VarI1FromBool" },
- { 254, "VarI1FromUI2" },
- { 255, "VarI1FromUI4" },
- { 256, "VarI1FromDec" },
- { 257, "VarUI2FromUI1" },
- { 258, "VarUI2FromI2" },
- { 259, "VarUI2FromI4" },
- { 260, "VarUI2FromR4" },
- { 261, "VarUI2FromR8" },
- { 262, "VarUI2FromDate" },
- { 263, "VarUI2FromCy" },
- { 264, "VarUI2FromStr" },
- { 265, "VarUI2FromDisp" },
- { 266, "VarUI2FromBool" },
- { 267, "VarUI2FromI1" },
- { 268, "VarUI2FromUI4" },
- { 269, "VarUI2FromDec" },
- { 270, "VarUI4FromUI1" },
- { 271, "VarUI4FromI2" },
- { 272, "VarUI4FromI4" },
- { 273, "VarUI4FromR4" },
- { 274, "VarUI4FromR8" },
- { 275, "VarUI4FromDate" },
- { 276, "VarUI4FromCy" },
- { 277, "VarUI4FromStr" },
- { 278, "VarUI4FromDisp" },
- { 279, "VarUI4FromBool" },
- { 280, "VarUI4FromI1" },
- { 281, "VarUI4FromUI2" },
- { 282, "VarUI4FromDec" },
- { 283, "BSTR_UserSize" },
- { 284, "BSTR_UserMarshal" },
- { 285, "BSTR_UserUnmarshal" },
- { 286, "BSTR_UserFree" },
- { 287, "VARIANT_UserSize" },
- { 288, "VARIANT_UserMarshal" },
- { 289, "VARIANT_UserUnmarshal" },
- { 290, "VARIANT_UserFree" },
- { 291, "LPSAFEARRAY_UserSize" },
- { 292, "LPSAFEARRAY_UserMarshal" },
- { 293, "LPSAFEARRAY_UserUnmarshal" },
- { 294, "LPSAFEARRAY_UserFree" },
- { 295, "LPSAFEARRAY_Size" },
- { 296, "LPSAFEARRAY_Marshal" },
- { 297, "LPSAFEARRAY_Unmarshal" },
- { 298, "VarDecCmpR8" },
- { 299, "VarCyAdd" },
- { 300, "DllUnregisterServer" },
- { 301, "OACreateTypeLib2" },
- { 303, "VarCyMul" },
- { 304, "VarCyMulI4" },
- { 305, "VarCySub" },
- { 306, "VarCyAbs" },
- { 307, "VarCyFix" },
- { 308, "VarCyInt" },
- { 309, "VarCyNeg" },
- { 310, "VarCyRound" },
- { 311, "VarCyCmp" },
- { 312, "VarCyCmpR8" },
- { 313, "VarBstrCat" },
- { 314, "VarBstrCmp" },
- { 315, "VarR8Pow" },
- { 316, "VarR4CmpR8" },
- { 317, "VarR8Round" },
- { 318, "VarCat" },
- { 319, "VarDateFromUdateEx" },
- { 322, "GetRecordInfoFromGuids" },
- { 323, "GetRecordInfoFromTypeInfo" },
- { 325, "SetVarConversionLocaleSetting" },
- { 326, "GetVarConversionLocaleSetting" },
- { 327, "SetOaNoCache" },
- { 329, "VarCyMulI8" },
- { 330, "VarDateFromUdate" },
- { 331, "VarUdateFromDate" },
- { 332, "GetAltMonthNames" },
- { 333, "VarI8FromUI1" },
- { 334, "VarI8FromI2" },
- { 335, "VarI8FromR4" },
- { 336, "VarI8FromR8" },
- { 337, "VarI8FromCy" },
- { 338, "VarI8FromDate" },
- { 339, "VarI8FromStr" },
- { 340, "VarI8FromDisp" },
- { 341, "VarI8FromBool" },
- { 342, "VarI8FromI1" },
- { 343, "VarI8FromUI2" },
- { 344, "VarI8FromUI4" },
- { 345, "VarI8FromDec" },
- { 346, "VarI2FromI8" },
- { 347, "VarI2FromUI8" },
- { 348, "VarI4FromI8" },
- { 349, "VarI4FromUI8" },
- { 360, "VarR4FromI8" },
- { 361, "VarR4FromUI8" },
- { 362, "VarR8FromI8" },
- { 363, "VarR8FromUI8" },
- { 364, "VarDateFromI8" },
- { 365, "VarDateFromUI8" },
- { 366, "VarCyFromI8" },
- { 367, "VarCyFromUI8" },
- { 368, "VarBstrFromI8" },
- { 369, "VarBstrFromUI8" },
- { 370, "VarBoolFromI8" },
- { 371, "VarBoolFromUI8" },
- { 372, "VarUI1FromI8" },
- { 373, "VarUI1FromUI8" },
- { 374, "VarDecFromI8" },
- { 375, "VarDecFromUI8" },
- { 376, "VarI1FromI8" },
- { 377, "VarI1FromUI8" },
- { 378, "VarUI2FromI8" },
- { 379, "VarUI2FromUI8" },
- { 401, "OleLoadPictureEx" },
- { 402, "OleLoadPictureFileEx" },
- { 411, "SafeArrayCreateVector" },
- { 412, "SafeArrayCopyData" },
- { 413, "VectorFromBstr" },
- { 414, "BstrFromVector" },
- { 415, "OleIconToCursor" },
- { 416, "OleCreatePropertyFrameIndirect" },
- { 417, "OleCreatePropertyFrame" },
- { 418, "OleLoadPicture" },
- { 419, "OleCreatePictureIndirect" },
- { 420, "OleCreateFontIndirect" },
- { 421, "OleTranslateColor" },
- { 422, "OleLoadPictureFile" },
- { 423, "OleSavePictureFile" },
- { 424, "OleLoadPicturePath" },
- { 425, "VarUI4FromI8" },
- { 426, "VarUI4FromUI8" },
- { 427, "VarI8FromUI8" },
- { 428, "VarUI8FromI8" },
- { 429, "VarUI8FromUI1" },
- { 430, "VarUI8FromI2" },
- { 431, "VarUI8FromR4" },
- { 432, "VarUI8FromR8" },
- { 433, "VarUI8FromCy" },
- { 434, "VarUI8FromDate" },
- { 435, "VarUI8FromStr" },
- { 436, "VarUI8FromDisp" },
- { 437, "VarUI8FromBool" },
- { 438, "VarUI8FromI1" },
- { 439, "VarUI8FromUI2" },
- { 440, "VarUI8FromUI4" },
- { 441, "VarUI8FromDec" },
- { 442, "RegisterTypeLibForUser" },
- { 443, "UnRegisterTypeLibForUser" },
- { 0 }
-};
-
-ord_t ws2_32_arr[] = {
- { 1, "accept" },
- { 2, "bind" },
- { 3, "closesocket" },
- { 4, "connect" },
- { 5, "getpeername" },
- { 6, "getsockname" },
- { 7, "getsockopt" },
- { 8, "htonl" },
- { 9, "htons" },
- { 10, "ioctlsocket" },
- { 11, "inet_addr" },
- { 12, "inet_ntoa" },
- { 13, "listen" },
- { 14, "ntohl" },
- { 15, "ntohs" },
- { 16, "recv" },
- { 17, "recvfrom" },
- { 18, "select" },
- { 19, "send" },
- { 20, "sendto" },
- { 21, "setsockopt" },
- { 22, "shutdown" },
- { 23, "socket" },
- { 24, "GetAddrInfoW" },
- { 25, "GetNameInfoW" },
- { 26, "WSApSetPostRoutine" },
- { 27, "FreeAddrInfoW" },
- { 28, "WPUCompleteOverlappedRequest" },
- { 29, "WSAAccept" },
- { 30, "WSAAddressToStringA" },
- { 31, "WSAAddressToStringW" },
- { 32, "WSACloseEvent" },
- { 33, "WSAConnect" },
- { 34, "WSACreateEvent" },
- { 35, "WSADuplicateSocketA" },
- { 36, "WSADuplicateSocketW" },
- { 37, "WSAEnumNameSpaceProvidersA" },
- { 38, "WSAEnumNameSpaceProvidersW" },
- { 39, "WSAEnumNetworkEvents" },
- { 40, "WSAEnumProtocolsA" },
- { 41, "WSAEnumProtocolsW" },
- { 42, "WSAEventSelect" },
- { 43, "WSAGetOverlappedResult" },
- { 44, "WSAGetQOSByName" },
- { 45, "WSAGetServiceClassInfoA" },
- { 46, "WSAGetServiceClassInfoW" },
- { 47, "WSAGetServiceClassNameByClassIdA" },
- { 48, "WSAGetServiceClassNameByClassIdW" },
- { 49, "WSAHtonl" },
- { 50, "WSAHtons" },
- { 51, "gethostbyaddr" },
- { 52, "gethostbyname" },
- { 53, "getprotobyname" },
- { 54, "getprotobynumber" },
- { 55, "getservbyname" },
- { 56, "getservbyport" },
- { 57, "gethostname" },
- { 58, "WSAInstallServiceClassA" },
- { 59, "WSAInstallServiceClassW" },
- { 60, "WSAIoctl" },
- { 61, "WSAJoinLeaf" },
- { 62, "WSALookupServiceBeginA" },
- { 63, "WSALookupServiceBeginW" },
- { 64, "WSALookupServiceEnd" },
- { 65, "WSALookupServiceNextA" },
- { 66, "WSALookupServiceNextW" },
- { 67, "WSANSPIoctl" },
- { 68, "WSANtohl" },
- { 69, "WSANtohs" },
- { 70, "WSAProviderConfigChange" },
- { 71, "WSARecv" },
- { 72, "WSARecvDisconnect" },
- { 73, "WSARecvFrom" },
- { 74, "WSARemoveServiceClass" },
- { 75, "WSAResetEvent" },
- { 76, "WSASend" },
- { 77, "WSASendDisconnect" },
- { 78, "WSASendTo" },
- { 79, "WSASetEvent" },
- { 80, "WSASetServiceA" },
- { 81, "WSASetServiceW" },
- { 82, "WSASocketA" },
- { 83, "WSASocketW" },
- { 84, "WSAStringToAddressA" },
- { 85, "WSAStringToAddressW" },
- { 86, "WSAWaitForMultipleEvents" },
- { 87, "WSCDeinstallProvider" },
- { 88, "WSCEnableNSProvider" },
- { 89, "WSCEnumProtocols" },
- { 90, "WSCGetProviderPath" },
- { 91, "WSCInstallNameSpace" },
- { 92, "WSCInstallProvider" },
- { 93, "WSCUnInstallNameSpace" },
- { 94, "WSCUpdateProvider" },
- { 95, "WSCWriteNameSpaceOrder" },
- { 96, "WSCWriteProviderOrder" },
- { 97, "freeaddrinfo" },
- { 98, "getaddrinfo" },
- { 99, "getnameinfo" },
- { 101, "WSAAsyncSelect" },
- { 102, "WSAAsyncGetHostByAddr" },
- { 103, "WSAAsyncGetHostByName" },
- { 104, "WSAAsyncGetProtoByNumber" },
- { 105, "WSAAsyncGetProtoByName" },
- { 106, "WSAAsyncGetServByPort" },
- { 107, "WSAAsyncGetServByName" },
- { 108, "WSACancelAsyncRequest" },
- { 109, "WSASetBlockingHook" },
- { 110, "WSAUnhookBlockingHook" },
- { 111, "WSAGetLastError" },
- { 112, "WSASetLastError" },
- { 113, "WSACancelBlockingCall" },
- { 114, "WSAIsBlocking" },
- { 115, "WSAStartup" },
- { 116, "WSACleanup" },
- { 151, "__WSAFDIsSet" },
- { 500, "WEP" },
- { 0 }
-};
diff --git a/lib/libpe/include/libpe/pe.h b/lib/libpe/include/libpe/pe.h
index ed0447ce..611f64c4 100644
--- a/lib/libpe/include/libpe/pe.h
+++ b/lib/libpe/include/libpe/pe.h
@@ -1,7 +1,7 @@
/*
libpe - the PE library
- Copyright (C) 2010 - 2017 libpe authors
+ Copyright (C) 2010 - 2025 libpe authors
This file is part of libpe.
@@ -26,27 +26,30 @@
extern "C" {
#endif
-#include "macros.h"
-#include
-#include
-#include
-#include
-#include
-
#include "context.h"
+#include "dir_security.h"
+#include "directories.h"
#include "error.h"
-#include "hdr_dos.h"
+#include "exports.h"
+#include "hashes.h"
#include "hdr_coff.h"
+#include "hdr_dos.h"
#include "hdr_optional.h"
-#include "directories.h"
-#include "sections.h"
-#include "hashes.h"
#include "imports.h"
-#include "exports.h"
-#include "resources.h"
-#include "utils.h"
+#include "sections.h"
-#define MAGIC_MZ 0x5a4d // Belongs to the DOS header
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+static const char __MAGIC_MZ[2] = {'M', 'Z'};
+static const uint16_t MAGIC_MZ = ('Z' << 8) + 'M';
+
+// #define MAGIC_MZ 0x5a4d // Belongs to the DOS header
#define MAX_DIRECTORIES 16
#define MAX_SECTIONS 96
@@ -66,8 +69,8 @@ static const uint64_t IMAGE_ORDINAL_FLAG64 = 0x8000000000000000;
#define SIGNATURE_PX 0x00005850 // PX\0\0 in little-endian, used by HX DOS extender
typedef enum {
- LIBPE_OPT_NOCLOSE_FD = (1 << 0), // Keeps `stream` open for further usage.
- LIBPE_OPT_OPEN_RW = (1 << 1) // Open file for read and writing
+ LIBPE_OPT_NOCLOSE_FD = (1 << 0), // Keeps `stream` open for further usage.
+ LIBPE_OPT_OPEN_RW = (1 << 1) // Open file for read and writing
} pe_option_e;
typedef uint16_t pe_options_e; // bitmasked pe_option_e values
@@ -75,7 +78,8 @@ typedef uint16_t pe_options_e; // bitmasked pe_option_e values
// General functions
bool pe_can_read(const pe_ctx_t *ctx, const void *ptr, size_t size);
pe_err_e pe_load_file(pe_ctx_t *ctx, const char *path);
-pe_err_e pe_load_file_ext(pe_ctx_t *ctx, const char *path, pe_options_e options);
+pe_err_e pe_load_file_ext(pe_ctx_t *ctx, const char *path,
+ pe_options_e options);
pe_err_e pe_unload(pe_ctx_t *ctx);
pe_err_e pe_parse(pe_ctx_t *ctx);
bool pe_is_loaded(const pe_ctx_t *ctx);
@@ -95,11 +99,15 @@ IMAGE_COFF_HEADER *pe_coff(pe_ctx_t *ctx);
IMAGE_OPTIONAL_HEADER *pe_optional(pe_ctx_t *ctx);
uint32_t pe_directories_count(const pe_ctx_t *ctx);
IMAGE_DATA_DIRECTORY **pe_directories(pe_ctx_t *ctx);
-IMAGE_DATA_DIRECTORY *pe_directory_by_entry(pe_ctx_t *ctx, ImageDirectoryEntry entry);
+IMAGE_DATA_DIRECTORY *pe_directory_by_entry(pe_ctx_t *ctx,
+ ImageDirectoryEntry entry);
uint16_t pe_sections_count(const pe_ctx_t *ctx);
IMAGE_SECTION_HEADER **pe_sections(pe_ctx_t *ctx);
-IMAGE_SECTION_HEADER *pe_section_by_name(pe_ctx_t *ctx, const char *section_name);
-const char *pe_section_name(const pe_ctx_t *ctx, const IMAGE_SECTION_HEADER *section_hdr, char *out_name, size_t out_name_size);
+IMAGE_SECTION_HEADER *pe_section_by_name(pe_ctx_t *ctx,
+ const char *section_name);
+const char *pe_section_name(const pe_ctx_t *ctx,
+ const IMAGE_SECTION_HEADER *section_hdr,
+ char *out_name, size_t out_name_size);
const char *pe_machine_type_name(MachineType type);
const char *pe_image_characteristic_name(ImageCharacteristics characteristic);
@@ -117,7 +125,8 @@ bool pe_is_repro(pe_ctx_t *ctx);
// Hash functions
size_t pe_hash_recommended_size(void);
-bool pe_hash_raw_data(char *output, size_t output_size, const char *alg_name, const unsigned char *data, size_t data_size);
+bool pe_hash_raw_data(char *output, size_t output_size, const char *alg_name,
+ const unsigned char *data, size_t data_size);
pe_hash_headers_t *pe_get_headers_hashes(pe_ctx_t *ctx);
pe_hash_sections_t *pe_get_sections_hash(pe_ctx_t *ctx);
pe_hash_t *pe_get_file_hash(pe_ctx_t *ctx);
@@ -129,6 +138,10 @@ pe_imports_t *pe_imports(pe_ctx_t *ctx);
// Exports functions
pe_exports_t *pe_exports(pe_ctx_t *ctx);
+// Certificate functtions
+uint32_t pe_certificate_count(pe_ctx_t *ctx);
+uint32_t pe_certificates(pe_ctx_t *ctx, WIN_CERTIFICATE ***certs);
+
// Resources functions
pe_resources_t *pe_resources(pe_ctx_t *ctx);
@@ -144,3 +157,4 @@ int pe_get_tls_callback(pe_ctx_t *ctx);
#endif
#endif
+
diff --git a/lib/libpe/include/libpe/resources.h b/lib/libpe/include/libpe/resources.h
deleted file mode 100644
index f51b74ba..00000000
--- a/lib/libpe/include/libpe/resources.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_RESOURCES_H
-#define LIBPE_RESOURCES_H
-
-#include
-#include
-#include "context.h"
-#include "error.h"
-#include "dir_resources.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// Type Lookup for IMAGE_RESOURCE_DATA_ENTRY
-//
-
-typedef struct {
- char *name;
- ResourceType type;
- char *extension;
- char *dir_name;
-} pe_resource_entry_info_t;
-
-const pe_resource_entry_info_t *pe_resource_entry_info_lookup(uint32_t name_offset);
-
-//
-// Search nodes
-//
-
-typedef bool (* pe_resource_node_predicate_fn)(const pe_resource_node_t *node);
-
-typedef struct pe_resource_node_search_result_item {
- const pe_resource_node_t *node;
- struct pe_resource_node_search_result_item *next;
-} pe_resource_node_search_result_item_t;
-
-typedef struct {
- size_t count;
- pe_resource_node_search_result_item_t *items;
-} pe_resource_node_search_result_t;
-
-void pe_resource_search_nodes(pe_resource_node_search_result_t *result, const pe_resource_node_t *node, pe_resource_node_predicate_fn predicate);
-void pe_resources_dealloc_node_search_result(pe_resource_node_search_result_t *result);
-
-//
-// Main
-//
-
-pe_resource_node_t *pe_resource_root_node(const pe_resource_node_t *node);
-pe_resource_node_t *pe_resource_last_child_node(const pe_resource_node_t *parent_node);
-pe_resource_node_t *pe_resource_find_node_by_type_and_level(const pe_resource_node_t *node, pe_resource_node_type_e type, uint32_t dirLevel);
-pe_resource_node_t *pe_resource_find_parent_node_by_type_and_level(const pe_resource_node_t *node, pe_resource_node_type_e type, uint32_t dirLevel);
-char *pe_resource_parse_string_u(pe_ctx_t *ctx, char *output, size_t output_size, const IMAGE_RESOURCE_DATA_STRING_U *data_string_ptr);
-void pe_resources_dealloc(pe_resources_t *obj);
-const VS_FIXEDFILEINFO *pe_resource_get_fixedfileinfo(const pe_ctx_t *ctx, const pe_resource_node_t *node, void **child_out);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif
diff --git a/lib/libpe/include/libpe/sections.h b/lib/libpe/include/libpe/sections.h
deleted file mode 100644
index 9e746b08..00000000
--- a/lib/libpe/include/libpe/sections.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2017 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_SECTIONS_H
-#define LIBPE_SECTIONS_H
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define SECTION_NAME_SIZE 8
-
-// These informations were filled from various sources:
-// - various versions of SDK files ntimage.h, winnt.h and coff.doc, pecoff*.doc documents
-// - behavior of MSVC40 CL.EXE (I386 and M68K versions), LINK.EXE and DUMPBIN.EXE
-typedef enum {
- IMAGE_SCN_SCALE_INDEX = 0x00000001, // Address of tls index is scaled (multiplied by 4). This is valid only for .tls section and only on MIPS.
- IMAGE_SCN_TYPE_NO_LOAD = 0x00000002, // Reserved.
- IMAGE_SCN_TYPE_GROUPED = 0x00000004, // Used for 16-bit offset code. Linker combines sections with the same name (they may have different flags) into one output section with max 64 kB size. All offsets inside the section are signed 16-bit from the middle of the section. This is valid only for object files. (Not supported by LINK.EXE)
- IMAGE_SCN_TYPE_NO_PAD = 0x00000008, // Same as IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files.
- IMAGE_SCN_TYPE_COPY = 0x00000010, // Reserved.
- IMAGE_SCN_CNT_CODE = 0x00000020, // The section contains executable code.
- IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040, // The section contains initialized data.
- IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080, // The section contains uninitialized data.
- IMAGE_SCN_LNK_OTHER = 0x00000100, // The section contains other than info, code or data. This is valid only for object files.
- IMAGE_SCN_LNK_INFO = 0x00000200, // The section contains comments or other information. This is valid only for object files.
- IMAGE_SCN_LNK_OVERLAY = 0x00000400, // The section contains an overlay (Reserved).
- IMAGE_SCN_LNK_REMOVE = 0x00000800, // The section will not become part of the image. This is valid only for object files.
- IMAGE_SCN_LNK_COMDAT = 0x00001000, // The section contains COMDAT data. This is valid only for object files.
-// RESERVED = 0x00002000, // Reserved.
- IMAGE_SCN_MEM_PROTECTED = 0x00004000, // The section is memory protected. This is valid only for M68K (Mac OS memory management).
- IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000, // Reset speculative exceptions handling bits in the TLB entries for this section. This is not valid for M68K.
- IMAGE_SCN_MEM_FARDATA = 0x00008000, // The section contains FAR_EXTERNAL relocations. This is valid only for M68K (Mac OS memory management).
- IMAGE_SCN_GPREL = 0x00008000, // The section contains data referenced through the global pointer. This is not valid for M68K.
- IMAGE_SCN_MEM_SYSHEAP = 0x00010000, // The section uses System heap. This is valid only for M68K (Mac OS memory management).
- IMAGE_SCN_MEM_PURGEABLE = 0x00020000, // The section can be released from RAM. This is valid only for M68K (Mac OS memory management).
- IMAGE_SCN_MEM_16BIT = 0x00020000, // The section contains 16-bit code. This is valid only for non-M68K architectures where it makes sense (I386, THUMB, MIPS16, MIPSFPU16, ...).
- IMAGE_SCN_MEM_LOCKED = 0x00040000, // The section is locked/resident and prevented from being moved in RAM. This is valid only for M68K (Mac OS memory management) and I386 object files (e.g. for building Linear Executables).
- IMAGE_SCN_MEM_PRELOAD = 0x00080000, // The section is preloaded to RAM. This is valid only for M68K (Mac OS memory management) and I386 object files (e.g. for building Linear Executables).
- IMAGE_SCN_ALIGN_1BYTES = 0x00100000, // Align data on a 1-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_2BYTES = 0x00200000, // Align data on a 2-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_4BYTES = 0x00300000, // Align data on a 4-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_8BYTES = 0x00400000, // Align data on a 8-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_16BYTES = 0x00500000, // Align data on a 16-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_32BYTES = 0x00600000, // Align data on a 32-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_64BYTES = 0x00700000, // Align data on a 64-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_128BYTES = 0x00800000, // Align data on a 128-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_256BYTES = 0x00900000, // Align data on a 256-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_512BYTES = 0x00A00000, // Align data on a 512-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000, // Align data on a 1024-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000, // Align data on a 2048-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000, // Align data on a 4096-byte boundary. This is valid only for object files.
- IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000, // Align data on a 8192-byte boundary. This is valid only for object files.
-// RESERVED = 0x00F00000, // Reserved.
- IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000, // The section contains extended relocations. This is valid only for object files.
- IMAGE_SCN_MEM_DISCARDABLE = 0x02000000, // The section can be discarded as needed.
- IMAGE_SCN_MEM_NOT_CACHED = 0x04000000, // The section cannot be cached.
- IMAGE_SCN_MEM_NOT_PAGED = 0x08000000, // The section cannot be paged.
- IMAGE_SCN_MEM_SHARED = 0x10000000, // The section is shareable. When used with a DLL, the data in this section will be shared among all processes using the DLL.
- IMAGE_SCN_MEM_EXECUTE = 0x20000000, // The section is executable.
- IMAGE_SCN_MEM_READ = 0x40000000, // The section is readable.
- IMAGE_SCN_MEM_WRITE = -2147483648 // The section is writeable. (0x80000000U)
-} SectionCharacteristics;
-
-// Used only when IMAGE_ROM_OPTIONAL_HEADER is present
-typedef enum {
- STYP_DUMMY = 0x00000001, // Dummy
- STYP_TEXT = 0x00000020, // Text
- STYP_DATA = 0x00000040, // Data
- STYP_SBSS = 0x00000080, // GP Uninit Data
- STYP_RDATA = 0x00000100, // Readonly Data
- STYP_SDATA = 0x00000200, // GP Init Data
- STYP_BSS = 0x00000400, // Uninit Data
- STYP_UCODE = 0x00000800, // UCode
- STYP_LIT8 = 0x08000000, // Literal 8
- STYP_LIT4 = 0x10000000, // Literal 4
- S_NRELOC_OVFL = 0x20000000, // Non-Relocatable overlay
- STYP_LIB = 0x40000000, // Library
- STYP_INIT = -2147483648 // Init Code (0x80000000U)
-} ROMSectionCharacteristics;
-
-#pragma pack(push, 1)
-
-// Quoting pecoff_v8.docx: "Entries in the section table are numbered starting from one (1)".
-typedef struct {
- uint8_t Name[SECTION_NAME_SIZE]; // TODO: Should we use char instead?
- union {
- uint32_t PhysicalAddress; // same value as next field
- uint32_t VirtualSize;
- } Misc;
- uint32_t VirtualAddress;
- uint32_t SizeOfRawData;
- uint32_t PointerToRawData;
- uint32_t PointerToRelocations; // always zero in executables
- uint32_t PointerToLinenumbers; // deprecated
- uint16_t NumberOfRelocations;
- uint16_t NumberOfLinenumbers; // deprecated
- uint32_t Characteristics; // SectionCharacteristics or ROMSectionCharacteristics
-} IMAGE_SECTION_HEADER;
-
-#pragma pack(pop)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif
diff --git a/lib/libpe/include/libpe/types_resources.h b/lib/libpe/include/libpe/types_resources.h
deleted file mode 100644
index 53934c76..00000000
--- a/lib/libpe/include/libpe/types_resources.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- libpe - the PE library
-
- Copyright (C) 2010 - 2023 libpe authors
-
- This file is part of libpe.
-
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
-*/
-
-#ifndef LIBPE_TYPES_RESOURCES_H
-#define LIBPE_TYPES_RESOURCES_H
-
-#include
-#include "dir_resources.h"
-#include "error.h"
-
-typedef enum {
- LIBPE_RDT_LEVEL1 = 1,
- LIBPE_RDT_LEVEL2 = 2,
- LIBPE_RDT_LEVEL3 = 3
-} pe_resource_level_e;
-
-typedef enum {
- LIBPE_RDT_RESOURCE_DIRECTORY = 1,
- LIBPE_RDT_DIRECTORY_ENTRY = 2,
- LIBPE_RDT_DATA_STRING = 3,
- LIBPE_RDT_DATA_ENTRY = 4
-} pe_resource_node_type_e;
-
-typedef struct pe_resource_node {
- uint16_t depth;
- uint32_t dirLevel; // pe_resouces_level_e
- pe_resource_node_type_e type;
- char *name;
- union {
- void *raw_ptr; // We are allowed to rely on type-punning in C99, but not in C++.
- IMAGE_RESOURCE_DIRECTORY *resourceDirectory; // type == LIBPE_RDT_RESOURCE_DIRECTORY
- IMAGE_RESOURCE_DIRECTORY_ENTRY *directoryEntry; // type == LIBPE_RDT_DIRECTORY_ENTRY
- IMAGE_RESOURCE_DATA_STRING_U *dataString; // type == LIBPE_RDT_DATA_STRING
- IMAGE_RESOURCE_DATA_ENTRY *dataEntry; // type == LIBPE_RDT_DATA_ENTRY
- } raw;
- struct pe_resource_node *parentNode; // Points to the parent node, if any.
- struct pe_resource_node *childNode; // Points to the 1st child node, if any.
- struct pe_resource_node *nextNode; // Points to the next sibling node, if any.
-} pe_resource_node_t;
-
-typedef struct {
- pe_err_e err;
- void *resource_base_ptr; // A pointer to the beggining of the `IMAGE_RESOURCE_DIRECTORY`.
- pe_resource_node_t *root_node;
-} pe_resources_t;
-
-#endif
diff --git a/lib/libpe/include/libpe/utlist.h b/lib/libpe/include/libpe/utlist.h
deleted file mode 100644
index 9b5534ff..00000000
--- a/lib/libpe/include/libpe/utlist.h
+++ /dev/null
@@ -1,895 +0,0 @@
-/*
-Copyright (c) 2007-2016, Troy D. Hanson http://troydhanson.github.com/uthash/
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#ifndef UTLIST_H
-#define UTLIST_H
-
-#define UTLIST_VERSION 2.0.1
-
-#include
-
-/*
- * This file contains macros to manipulate singly and doubly-linked lists.
- *
- * 1. LL_ macros: singly-linked lists.
- * 2. DL_ macros: doubly-linked lists.
- * 3. CDL_ macros: circular doubly-linked lists.
- *
- * To use singly-linked lists, your structure must have a "next" pointer.
- * To use doubly-linked lists, your structure must "prev" and "next" pointers.
- * Either way, the pointer to the head of the list must be initialized to NULL.
- *
- * ----------------.EXAMPLE -------------------------
- * struct item {
- * int id;
- * struct item *prev, *next;
- * }
- *
- * struct item *list = NULL:
- *
- * int main() {
- * struct item *item;
- * ... allocate and populate item ...
- * DL_APPEND(list, item);
- * }
- * --------------------------------------------------
- *
- * For doubly-linked lists, the append and delete macros are O(1)
- * For singly-linked lists, append and delete are O(n) but prepend is O(1)
- * The sort macro is O(n log(n)) for all types of single/double/circular lists.
- */
-
-/* These macros use decltype or the earlier __typeof GNU extension.
- As decltype is only available in newer compilers (VS2010 or gcc 4.3+
- when compiling c++ code), this code uses whatever method is needed
- or, for VS2008 where neither is available, uses casting workarounds. */
-#ifdef _MSC_VER /* MS compiler */
-#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
-#define LDECLTYPE(x) decltype(x)
-#else /* VS2008 or older (or VS2010 in C mode) */
-#define NO_DECLTYPE
-#endif
-#elif defined(__ICCARM__)
-#define NO_DECLTYPE
-#else /* GNU, Sun and other compilers */
-#define LDECLTYPE(x) __typeof(x)
-#endif
-
-/* for VS2008 we use some workarounds to get around the lack of decltype,
- * namely, we always reassign our tmp variable to the list head if we need
- * to dereference its prev/next pointers, and save/restore the real head.*/
-#ifdef NO_DECLTYPE
-#define IF_NO_DECLTYPE(x) x
-#define LDECLTYPE(x) char*
-#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
-#define _NEXT(elt,list,next) ((char*)((list)->next))
-#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
-/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */
-#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
-#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
-#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
-#else
-#define IF_NO_DECLTYPE(x)
-#define _SV(elt,list)
-#define _NEXT(elt,list,next) ((elt)->next)
-#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to)
-/* #define _PREV(elt,list,prev) ((elt)->prev) */
-#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)
-#define _RS(list)
-#define _CASTASGN(a,b) (a)=(b)
-#endif
-
-/******************************************************************************
- * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort *
- * Unwieldy variable names used here to avoid shadowing passed-in variables. *
- *****************************************************************************/
-#define LL_SORT(list, cmp) \
- LL_SORT2(list, cmp, next)
-
-#define LL_SORT2(list, cmp, next) \
-do { \
- LDECLTYPE(list) _ls_p; \
- LDECLTYPE(list) _ls_q; \
- LDECLTYPE(list) _ls_e; \
- LDECLTYPE(list) _ls_tail; \
- IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \
- int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
- if (list) { \
- _ls_insize = 1; \
- _ls_looping = 1; \
- while (_ls_looping) { \
- _CASTASGN(_ls_p,list); \
- (list) = NULL; \
- _ls_tail = NULL; \
- _ls_nmerges = 0; \
- while (_ls_p) { \
- _ls_nmerges++; \
- _ls_q = _ls_p; \
- _ls_psize = 0; \
- for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
- _ls_psize++; \
- _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
- if (!_ls_q) break; \
- } \
- _ls_qsize = _ls_insize; \
- while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
- if (_ls_psize == 0) { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
- } else if (_ls_qsize == 0 || !_ls_q) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
- } else if (cmp(_ls_p,_ls_q) <= 0) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
- } else { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
- } \
- if (_ls_tail) { \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
- } else { \
- _CASTASGN(list,_ls_e); \
- } \
- _ls_tail = _ls_e; \
- } \
- _ls_p = _ls_q; \
- } \
- if (_ls_tail) { \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
- } \
- if (_ls_nmerges <= 1) { \
- _ls_looping=0; \
- } \
- _ls_insize *= 2; \
- } \
- } \
-} while (0)
-
-
-#define DL_SORT(list, cmp) \
- DL_SORT2(list, cmp, prev, next)
-
-#define DL_SORT2(list, cmp, prev, next) \
-do { \
- LDECLTYPE(list) _ls_p; \
- LDECLTYPE(list) _ls_q; \
- LDECLTYPE(list) _ls_e; \
- LDECLTYPE(list) _ls_tail; \
- IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \
- int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
- if (list) { \
- _ls_insize = 1; \
- _ls_looping = 1; \
- while (_ls_looping) { \
- _CASTASGN(_ls_p,list); \
- (list) = NULL; \
- _ls_tail = NULL; \
- _ls_nmerges = 0; \
- while (_ls_p) { \
- _ls_nmerges++; \
- _ls_q = _ls_p; \
- _ls_psize = 0; \
- for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
- _ls_psize++; \
- _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
- if (!_ls_q) break; \
- } \
- _ls_qsize = _ls_insize; \
- while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \
- if (_ls_psize == 0) { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
- } else if ((_ls_qsize == 0) || (!_ls_q)) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
- } else if (cmp(_ls_p,_ls_q) <= 0) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
- } else { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
- } \
- if (_ls_tail) { \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
- } else { \
- _CASTASGN(list,_ls_e); \
- } \
- _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
- _ls_tail = _ls_e; \
- } \
- _ls_p = _ls_q; \
- } \
- _CASTASGN((list)->prev, _ls_tail); \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
- if (_ls_nmerges <= 1) { \
- _ls_looping=0; \
- } \
- _ls_insize *= 2; \
- } \
- } \
-} while (0)
-
-#define CDL_SORT(list, cmp) \
- CDL_SORT2(list, cmp, prev, next)
-
-#define CDL_SORT2(list, cmp, prev, next) \
-do { \
- LDECLTYPE(list) _ls_p; \
- LDECLTYPE(list) _ls_q; \
- LDECLTYPE(list) _ls_e; \
- LDECLTYPE(list) _ls_tail; \
- LDECLTYPE(list) _ls_oldhead; \
- LDECLTYPE(list) _tmp; \
- int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
- if (list) { \
- _ls_insize = 1; \
- _ls_looping = 1; \
- while (_ls_looping) { \
- _CASTASGN(_ls_p,list); \
- _CASTASGN(_ls_oldhead,list); \
- (list) = NULL; \
- _ls_tail = NULL; \
- _ls_nmerges = 0; \
- while (_ls_p) { \
- _ls_nmerges++; \
- _ls_q = _ls_p; \
- _ls_psize = 0; \
- for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
- _ls_psize++; \
- _SV(_ls_q,list); \
- if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \
- _ls_q = NULL; \
- } else { \
- _ls_q = _NEXT(_ls_q,list,next); \
- } \
- _RS(list); \
- if (!_ls_q) break; \
- } \
- _ls_qsize = _ls_insize; \
- while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
- if (_ls_psize == 0) { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
- if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
- } else if (_ls_qsize == 0 || !_ls_q) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
- if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
- } else if (cmp(_ls_p,_ls_q) <= 0) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
- if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
- } else { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
- if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
- } \
- if (_ls_tail) { \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
- } else { \
- _CASTASGN(list,_ls_e); \
- } \
- _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
- _ls_tail = _ls_e; \
- } \
- _ls_p = _ls_q; \
- } \
- _CASTASGN((list)->prev,_ls_tail); \
- _CASTASGN(_tmp,list); \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \
- if (_ls_nmerges <= 1) { \
- _ls_looping=0; \
- } \
- _ls_insize *= 2; \
- } \
- } \
-} while (0)
-
-/******************************************************************************
- * singly linked list macros (non-circular) *
- *****************************************************************************/
-#define LL_PREPEND(head,add) \
- LL_PREPEND2(head,add,next)
-
-#define LL_PREPEND2(head,add,next) \
-do { \
- (add)->next = (head); \
- (head) = (add); \
-} while (0)
-
-#define LL_CONCAT(head1,head2) \
- LL_CONCAT2(head1,head2,next)
-
-#define LL_CONCAT2(head1,head2,next) \
-do { \
- LDECLTYPE(head1) _tmp; \
- if (head1) { \
- _tmp = (head1); \
- while (_tmp->next) { _tmp = _tmp->next; } \
- _tmp->next=(head2); \
- } else { \
- (head1)=(head2); \
- } \
-} while (0)
-
-#define LL_APPEND(head,add) \
- LL_APPEND2(head,add,next)
-
-#define LL_APPEND2(head,add,next) \
-do { \
- LDECLTYPE(head) _tmp; \
- (add)->next=NULL; \
- if (head) { \
- _tmp = (head); \
- while (_tmp->next) { _tmp = _tmp->next; } \
- _tmp->next=(add); \
- } else { \
- (head)=(add); \
- } \
-} while (0)
-
-#define LL_DELETE(head,del) \
- LL_DELETE2(head,del,next)
-
-#define LL_DELETE2(head,del,next) \
-do { \
- LDECLTYPE(head) _tmp; \
- if ((head) == (del)) { \
- (head)=(head)->next; \
- } else { \
- _tmp = (head); \
- while (_tmp->next && (_tmp->next != (del))) { \
- _tmp = _tmp->next; \
- } \
- if (_tmp->next) { \
- _tmp->next = (del)->next; \
- } \
- } \
-} while (0)
-
-#define LL_COUNT(head,el,counter) \
- LL_COUNT2(head,el,counter,next) \
-
-#define LL_COUNT2(head,el,counter,next) \
-do { \
- (counter) = 0; \
- LL_FOREACH2(head,el,next) { ++(counter); } \
-} while (0)
-
-#define LL_FOREACH(head,el) \
- LL_FOREACH2(head,el,next)
-
-#define LL_FOREACH2(head,el,next) \
- for ((el) = (head); el; (el) = (el)->next)
-
-#define LL_FOREACH_SAFE(head,el,tmp) \
- LL_FOREACH_SAFE2(head,el,tmp,next)
-
-#define LL_FOREACH_SAFE2(head,el,tmp,next) \
- for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp))
-
-#define LL_SEARCH_SCALAR(head,out,field,val) \
- LL_SEARCH_SCALAR2(head,out,field,val,next)
-
-#define LL_SEARCH_SCALAR2(head,out,field,val,next) \
-do { \
- LL_FOREACH2(head,out,next) { \
- if ((out)->field == (val)) break; \
- } \
-} while (0)
-
-#define LL_SEARCH(head,out,elt,cmp) \
- LL_SEARCH2(head,out,elt,cmp,next)
-
-#define LL_SEARCH2(head,out,elt,cmp,next) \
-do { \
- LL_FOREACH2(head,out,next) { \
- if ((cmp(out,elt))==0) break; \
- } \
-} while (0)
-
-#define LL_REPLACE_ELEM2(head, el, add, next) \
-do { \
- LDECLTYPE(head) _tmp; \
- assert((head) != NULL); \
- assert((el) != NULL); \
- assert((add) != NULL); \
- (add)->next = (el)->next; \
- if ((head) == (el)) { \
- (head) = (add); \
- } else { \
- _tmp = (head); \
- while (_tmp->next && (_tmp->next != (el))) { \
- _tmp = _tmp->next; \
- } \
- if (_tmp->next) { \
- _tmp->next = (add); \
- } \
- } \
-} while (0)
-
-#define LL_REPLACE_ELEM(head, el, add) \
- LL_REPLACE_ELEM2(head, el, add, next)
-
-#define LL_PREPEND_ELEM2(head, el, add, next) \
-do { \
- if (el) { \
- LDECLTYPE(head) _tmp; \
- assert((head) != NULL); \
- assert((add) != NULL); \
- (add)->next = (el); \
- if ((head) == (el)) { \
- (head) = (add); \
- } else { \
- _tmp = (head); \
- while (_tmp->next && (_tmp->next != (el))) { \
- _tmp = _tmp->next; \
- } \
- if (_tmp->next) { \
- _tmp->next = (add); \
- } \
- } \
- } else { \
- LL_APPEND2(head, add, next); \
- } \
-} while (0) \
-
-#define LL_PREPEND_ELEM(head, el, add) \
- LL_PREPEND_ELEM2(head, el, add, next)
-
-#define LL_APPEND_ELEM2(head, el, add, next) \
-do { \
- if (el) { \
- assert((head) != NULL); \
- assert((add) != NULL); \
- (add)->next = (el)->next; \
- (el)->next = (add); \
- } else { \
- LL_PREPEND2(head, add, next); \
- } \
-} while (0) \
-
-#define LL_APPEND_ELEM(head, el, add) \
- LL_APPEND_ELEM2(head, el, add, next)
-
-#ifdef NO_DECLTYPE
-/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */
-
-#undef LL_CONCAT2
-#define LL_CONCAT2(head1,head2,next) \
-do { \
- char *_tmp; \
- if (head1) { \
- _tmp = (char*)(head1); \
- while ((head1)->next) { (head1) = (head1)->next; } \
- (head1)->next = (head2); \
- _RS(head1); \
- } else { \
- (head1)=(head2); \
- } \
-} while (0)
-
-#undef LL_APPEND2
-#define LL_APPEND2(head,add,next) \
-do { \
- if (head) { \
- (add)->next = head; /* use add->next as a temp variable */ \
- while ((add)->next->next) { (add)->next = (add)->next->next; } \
- (add)->next->next=(add); \
- } else { \
- (head)=(add); \
- } \
- (add)->next=NULL; \
-} while (0)
-
-#undef LL_DELETE2
-#define LL_DELETE2(head,del,next) \
-do { \
- if ((head) == (del)) { \
- (head)=(head)->next; \
- } else { \
- char *_tmp = (char*)(head); \
- while ((head)->next && ((head)->next != (del))) { \
- (head) = (head)->next; \
- } \
- if ((head)->next) { \
- (head)->next = ((del)->next); \
- } \
- _RS(head); \
- } \
-} while (0)
-
-#undef LL_REPLACE_ELEM2
-#define LL_REPLACE_ELEM2(head, el, add, next) \
-do { \
- assert((head) != NULL); \
- assert((el) != NULL); \
- assert((add) != NULL); \
- if ((head) == (el)) { \
- (head) = (add); \
- } else { \
- (add)->next = head; \
- while ((add)->next->next && ((add)->next->next != (el))) { \
- (add)->next = (add)->next->next; \
- } \
- if ((add)->next->next) { \
- (add)->next->next = (add); \
- } \
- } \
- (add)->next = (el)->next; \
-} while (0)
-
-#undef LL_PREPEND_ELEM2
-#define LL_PREPEND_ELEM2(head, el, add, next) \
-do { \
- if (el) { \
- assert((head) != NULL); \
- assert((add) != NULL); \
- if ((head) == (el)) { \
- (head) = (add); \
- } else { \
- (add)->next = (head); \
- while ((add)->next->next && ((add)->next->next != (el))) { \
- (add)->next = (add)->next->next; \
- } \
- if ((add)->next->next) { \
- (add)->next->next = (add); \
- } \
- } \
- (add)->next = (el); \
- } else { \
- LL_APPEND2(head, add, next); \
- } \
-} while (0) \
-
-#endif /* NO_DECLTYPE */
-
-/******************************************************************************
- * doubly linked list macros (non-circular) *
- *****************************************************************************/
-#define DL_PREPEND(head,add) \
- DL_PREPEND2(head,add,prev,next)
-
-#define DL_PREPEND2(head,add,prev,next) \
-do { \
- (add)->next = (head); \
- if (head) { \
- (add)->prev = (head)->prev; \
- (head)->prev = (add); \
- } else { \
- (add)->prev = (add); \
- } \
- (head) = (add); \
-} while (0)
-
-#define DL_APPEND(head,add) \
- DL_APPEND2(head,add,prev,next)
-
-#define DL_APPEND2(head,add,prev,next) \
-do { \
- if (head) { \
- (add)->prev = (head)->prev; \
- (head)->prev->next = (add); \
- (head)->prev = (add); \
- (add)->next = NULL; \
- } else { \
- (head)=(add); \
- (head)->prev = (head); \
- (head)->next = NULL; \
- } \
-} while (0)
-
-#define DL_CONCAT(head1,head2) \
- DL_CONCAT2(head1,head2,prev,next)
-
-#define DL_CONCAT2(head1,head2,prev,next) \
-do { \
- LDECLTYPE(head1) _tmp; \
- if (head2) { \
- if (head1) { \
- _CASTASGN(_tmp, (head2)->prev); \
- (head2)->prev = (head1)->prev; \
- (head1)->prev->next = (head2); \
- _CASTASGN((head1)->prev, _tmp); \
- } else { \
- (head1)=(head2); \
- } \
- } \
-} while (0)
-
-#define DL_DELETE(head,del) \
- DL_DELETE2(head,del,prev,next)
-
-#define DL_DELETE2(head,del,prev,next) \
-do { \
- assert((del)->prev != NULL); \
- if ((del)->prev == (del)) { \
- (head)=NULL; \
- } else if ((del)==(head)) { \
- (del)->next->prev = (del)->prev; \
- (head) = (del)->next; \
- } else { \
- (del)->prev->next = (del)->next; \
- if ((del)->next) { \
- (del)->next->prev = (del)->prev; \
- } else { \
- (head)->prev = (del)->prev; \
- } \
- } \
-} while (0)
-
-#define DL_COUNT(head,el,counter) \
- DL_COUNT2(head,el,counter,next) \
-
-#define DL_COUNT2(head,el,counter,next) \
-do { \
- (counter) = 0; \
- DL_FOREACH2(head,el,next) { ++(counter); } \
-} while (0)
-
-#define DL_FOREACH(head,el) \
- DL_FOREACH2(head,el,next)
-
-#define DL_FOREACH2(head,el,next) \
- for ((el) = (head); el; (el) = (el)->next)
-
-/* this version is safe for deleting the elements during iteration */
-#define DL_FOREACH_SAFE(head,el,tmp) \
- DL_FOREACH_SAFE2(head,el,tmp,next)
-
-#define DL_FOREACH_SAFE2(head,el,tmp,next) \
- for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp))
-
-/* these are identical to their singly-linked list counterparts */
-#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
-#define DL_SEARCH LL_SEARCH
-#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2
-#define DL_SEARCH2 LL_SEARCH2
-
-#define DL_REPLACE_ELEM2(head, el, add, prev, next) \
-do { \
- assert((head) != NULL); \
- assert((el) != NULL); \
- assert((add) != NULL); \
- if ((head) == (el)) { \
- (head) = (add); \
- (add)->next = (el)->next; \
- if ((el)->next == NULL) { \
- (add)->prev = (add); \
- } else { \
- (add)->prev = (el)->prev; \
- (add)->next->prev = (add); \
- } \
- } else { \
- (add)->next = (el)->next; \
- (add)->prev = (el)->prev; \
- (add)->prev->next = (add); \
- if ((el)->next == NULL) { \
- (head)->prev = (add); \
- } else { \
- (add)->next->prev = (add); \
- } \
- } \
-} while (0)
-
-#define DL_REPLACE_ELEM(head, el, add) \
- DL_REPLACE_ELEM2(head, el, add, prev, next)
-
-#define DL_PREPEND_ELEM2(head, el, add, prev, next) \
-do { \
- if (el) { \
- assert((head) != NULL); \
- assert((add) != NULL); \
- (add)->next = (el); \
- (add)->prev = (el)->prev; \
- (el)->prev = (add); \
- if ((head) == (el)) { \
- (head) = (add); \
- } else { \
- (add)->prev->next = (add); \
- } \
- } else { \
- DL_APPEND2(head, add, prev, next); \
- } \
-} while (0) \
-
-#define DL_PREPEND_ELEM(head, el, add) \
- DL_PREPEND_ELEM2(head, el, add, prev, next)
-
-#define DL_APPEND_ELEM2(head, el, add, prev, next) \
-do { \
- if (el) { \
- assert((head) != NULL); \
- assert((add) != NULL); \
- (add)->next = (el)->next; \
- (add)->prev = (el); \
- (el)->next = (add); \
- if ((add)->next) { \
- (add)->next->prev = (add); \
- } else { \
- (head)->prev = (add); \
- } \
- } else { \
- DL_PREPEND2(head, add, prev, next); \
- } \
-} while (0) \
-
-#define DL_APPEND_ELEM(head, el, add) \
- DL_APPEND_ELEM2(head, el, add, prev, next)
-
-/******************************************************************************
- * circular doubly linked list macros *
- *****************************************************************************/
-#define CDL_APPEND(head,add) \
- CDL_APPEND2(head,add,prev,next)
-
-#define CDL_APPEND2(head,add,prev,next) \
-do { \
- if (head) { \
- (add)->prev = (head)->prev; \
- (add)->next = (head); \
- (head)->prev = (add); \
- (add)->prev->next = (add); \
- } else { \
- (add)->prev = (add); \
- (add)->next = (add); \
- (head) = (add); \
- } \
-} while (0)
-
-#define CDL_PREPEND(head,add) \
- CDL_PREPEND2(head,add,prev,next)
-
-#define CDL_PREPEND2(head,add,prev,next) \
-do { \
- if (head) { \
- (add)->prev = (head)->prev; \
- (add)->next = (head); \
- (head)->prev = (add); \
- (add)->prev->next = (add); \
- } else { \
- (add)->prev = (add); \
- (add)->next = (add); \
- } \
- (head) = (add); \
-} while (0)
-
-#define CDL_DELETE(head,del) \
- CDL_DELETE2(head,del,prev,next)
-
-#define CDL_DELETE2(head,del,prev,next) \
-do { \
- if (((head)==(del)) && ((head)->next == (head))) { \
- (head) = NULL; \
- } else { \
- (del)->next->prev = (del)->prev; \
- (del)->prev->next = (del)->next; \
- if ((del) == (head)) (head)=(del)->next; \
- } \
-} while (0)
-
-#define CDL_COUNT(head,el,counter) \
- CDL_COUNT2(head,el,counter,next) \
-
-#define CDL_COUNT2(head, el, counter,next) \
-do { \
- (counter) = 0; \
- CDL_FOREACH2(head,el,next) { ++(counter); } \
-} while (0)
-
-#define CDL_FOREACH(head,el) \
- CDL_FOREACH2(head,el,next)
-
-#define CDL_FOREACH2(head,el,next) \
- for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next))
-
-#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \
- CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)
-
-#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \
- for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \
- (el) && ((tmp2) = (el)->next, 1); \
- (el) = ((el) == (tmp1) ? NULL : (tmp2)))
-
-#define CDL_SEARCH_SCALAR(head,out,field,val) \
- CDL_SEARCH_SCALAR2(head,out,field,val,next)
-
-#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \
-do { \
- CDL_FOREACH2(head,out,next) { \
- if ((out)->field == (val)) break; \
- } \
-} while (0)
-
-#define CDL_SEARCH(head,out,elt,cmp) \
- CDL_SEARCH2(head,out,elt,cmp,next)
-
-#define CDL_SEARCH2(head,out,elt,cmp,next) \
-do { \
- CDL_FOREACH2(head,out,next) { \
- if ((cmp(out,elt))==0) break; \
- } \
-} while (0)
-
-#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \
-do { \
- assert((head) != NULL); \
- assert((el) != NULL); \
- assert((add) != NULL); \
- if ((el)->next == (el)) { \
- (add)->next = (add); \
- (add)->prev = (add); \
- (head) = (add); \
- } else { \
- (add)->next = (el)->next; \
- (add)->prev = (el)->prev; \
- (add)->next->prev = (add); \
- (add)->prev->next = (add); \
- if ((head) == (el)) { \
- (head) = (add); \
- } \
- } \
-} while (0)
-
-#define CDL_REPLACE_ELEM(head, el, add) \
- CDL_REPLACE_ELEM2(head, el, add, prev, next)
-
-#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \
-do { \
- if (el) { \
- assert((head) != NULL); \
- assert((add) != NULL); \
- (add)->next = (el); \
- (add)->prev = (el)->prev; \
- (el)->prev = (add); \
- (add)->prev->next = (add); \
- if ((head) == (el)) { \
- (head) = (add); \
- } \
- } else { \
- CDL_APPEND2(head, add, prev, next); \
- } \
-} while (0)
-
-#define CDL_PREPEND_ELEM(head, el, add) \
- CDL_PREPEND_ELEM2(head, el, add, prev, next)
-
-#define CDL_APPEND_ELEM2(head, el, add, prev, next) \
-do { \
- if (el) { \
- assert((head) != NULL); \
- assert((add) != NULL); \
- (add)->next = (el)->next; \
- (add)->prev = (el); \
- (el)->next = (add); \
- (add)->next->prev = (add); \
- } else { \
- CDL_PREPEND2(head, add, prev, next); \
- } \
-} while (0)
-
-#define CDL_APPEND_ELEM(head, el, add) \
- CDL_APPEND_ELEM2(head, el, add, prev, next)
-
-#endif /* UTLIST_H */
diff --git a/lib/libpe/libfuzzy/edit_dist.c b/lib/libpe/libfuzzy/edit_dist.c
deleted file mode 100644
index 30cc7046..00000000
--- a/lib/libpe/libfuzzy/edit_dist.c
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- This edit distance code is taken from trn3.6. A few minor
- modifications have been made by Andrew Tridgell
- for use in spamsum.
-*/
-
-
-/***************************************************************************/
-
-
-/* The authors make no claims as to the fitness or correctness of this software
- * for any use whatsoever, and it is provided as is. Any use of this software
- * is at the user's own risk.
- */
-
-#include
-#include
-
-/* edit_dist -- returns the minimum edit distance between two strings
-
- Program by: Mark Maimone CMU Computer Science 13 Nov 89
- Last Modified: 28 Jan 90
-
- If the input strings have length n and m, the algorithm runs in time
- O(nm) and space O(min(m,n)).
-
-HISTORY
- 13 Nov 89 (mwm) Created edit_dist() and set_costs().
-
- 28 Jan 90 (mwm) Added view_costs(). Should verify that THRESHOLD
- computations will work even when THRESHOLD is not a multiple of
- sizeof(int).
-
- 17 May 93 (mwm) Improved performance when used with trn's newsgroup
- processing; assume all costs are 1, and you can terminate when a
- threshold is exceeded.
-*/
-
-#define MIN_DIST 100
-
-#define TRN_SPEEDUP /* Use a less-general version of the
- routine, one that's better for trn.
- All change costs are 1, and it's okay
- to terminate if the edit distance is
- known to exceed MIN_DIST */
-
-#define THRESHOLD 4000 /* worry about allocating more memory only
- when this # of bytes is exceeded */
-#define STRLENTHRESHOLD ((int) ((THRESHOLD / sizeof (int) - 3) / 2))
-
-#define SAFE_ASSIGN(x,y) (((x) != NULL) ? (*(x) = (y)) : (y))
-
-#define swap_int(x,y) do { int _iswap = (x); (x) = (y); (y) = _iswap; } while (0)
-#define swap_char(x,y) do { const char *_cswap = (x); (x) = (y); (y) = _cswap; } while (0)
-
-static inline int min3(int x, int y, int z) {
- return x < y ? (x < z ? x : z) : (z < y) ? z : y;
-}
-static inline int min2(int x, int y)
-{
- return x < y ? x : y;
-}
-
-static int insert_cost = 1;
-static int delete_cost = 1;
-#ifndef TRN_SPEEDUP
-static int change_cost = 1;
-static int swap_cost = 1;
-#endif
-
-/* edit_distn -- returns the edit distance between two strings, or -1 on
- failure */
-
-int
-edit_distn(const char *from, int from_len, const char *to, int to_len)
-{
-#ifndef TRN_SPEEDUP
- register int ins, del, ch; /* local copies of edit costs */
-#endif
- register int row, col, index; /* dynamic programming counters */
- register int radix; /* radix for modular indexing */
-#ifdef TRN_SPEEDUP
- register int low;
-#endif
- int *buffer; /* pointer to storage for one row
- of the d.p. array */
- int store[THRESHOLD / sizeof (int)];
- /* a small amount of static
- storage, to be used when the
- input strings are small enough */
-
-/* Handle trivial cases when one string is empty */
-
- if (from == NULL || !from_len)
- if (to == NULL || !to_len)
- return 0;
- else
- return to_len * insert_cost;
- else if (to == NULL || !to_len)
- return from_len * delete_cost;
-
-/* Initialize registers */
-
- radix = 2 * from_len + 3;
-#ifdef TRN_SPEEDUP
-#define ins 1
-#define del 1
-#define ch 3
-#define swap_cost 5
-#else
- ins = insert_cost;
- del = delete_cost;
- ch = change_cost;
-#endif
-
-/* Make from short enough to fit in the static storage, if it's at all
- possible */
-
- if (from_len > to_len && from_len > STRLENTHRESHOLD) {
- swap_int(from_len, to_len);
- swap_char(from, to);
-#ifndef TRN_SPEEDUP
- swap_int(ins, del);
-#endif
- } /* if from_len > to_len */
-
-/* Allocate the array storage (from the heap if necessary) */
-
- if (from_len <= STRLENTHRESHOLD)
- buffer = store;
- else
- buffer = (int *) malloc(radix * sizeof (int));
-
-/* Here's where the fun begins. We will find the minimum edit distance
- using dynamic programming. We only need to store two rows of the matrix
- at a time, since we always progress down the matrix. For example,
- given the strings "one" and "two", and insert, delete and change costs
- equal to 1:
-
- _ o n e
- _ 0 1 2 3
- t 1 1 2 3
- w 2 2 2 3
- o 3 2 3 3
-
- The dynamic programming recursion is defined as follows:
-
- ar(x,0) := x * insert_cost
- ar(0,y) := y * delete_cost
- ar(x,y) := min(a(x - 1, y - 1) + (from[x] == to[y] ? 0 : change),
- a(x - 1, y) + insert_cost,
- a(x, y - 1) + delete_cost,
- a(x - 2, y - 2) + (from[x] == to[y-1] &&
- from[x-1] == to[y] ? swap_cost :
- infinity))
-
- Since this only looks at most two rows and three columns back, we need
- only store the values for the two preceeding rows. In this
- implementation, we do not explicitly store the zero column, so only 2 *
- from_len + 2 words are needed. However, in the implementation of the
- swap_cost check, the current matrix value is used as a buffer; we
- can't overwrite the earlier value until the swap_cost check has
- been performed. So we use 2 * from_len + 3 elements in the buffer.
-*/
-
-#define ar(x,y,index) (((x) == 0) ? (y) * del : (((y) == 0) ? (x) * ins : \
- buffer[mod(index)]))
-#define NW(x,y) ar(x, y, index + from_len + 2)
-#define N(x,y) ar(x, y, index + from_len + 3)
-#define W(x,y) ar(x, y, index + radix - 1)
-#define NNWW(x,y) ar(x, y, index + 1)
-#define mod(x) ((x) % radix)
-
- index = 0;
-
-#ifdef DEBUG_EDITDIST
- printf(" ");
- for (col = 0; col < from_len; col++)
- printf(" %c ", from[col]);
- printf("\n ");
-
- for (col = 0; col <= from_len; col++)
- printf("%2d ", col * del);
-#endif
-
-/* Row 0 is handled implicitly; its value at a given column is col*del.
- The loop below computes the values for Row 1. At this point we know the
- strings are nonempty. We also don't need to consider swap costs in row
- 1.
-
- COMMENT: the indicies row and col below point into the STRING, so
- the corresponding MATRIX indicies are row+1 and col+1.
-*/
-
- buffer[index++] = min2(ins + del, (from[0] == to[0] ? 0 : ch));
-#ifdef TRN_SPEEDUP
- low = buffer[mod(index + radix - 1)];
-#endif
-
-#ifdef DEBUG_EDITDIST
- printf("\n %c %2d %2d ", to[0], ins, buffer[index - 1]);
-#endif
-
- for (col = 1; col < from_len; col++) {
- buffer[index] = min3(
- col * del + ((from[col] == to[0]) ? 0 : ch),
- (col + 1) * del + ins,
- buffer[index - 1] + del);
-#ifdef TRN_SPEEDUP
- if (buffer[index] < low)
- low = buffer[index];
-#endif
- index++;
-
-#ifdef DEBUG_EDITDIST
- printf("%2d ", buffer[index - 1]);
-#endif
-
- } /* for col = 1 */
-
-#ifdef DEBUG_EDITDIST
- printf("\n %c %2d ", to[1], 2 * ins);
-#endif
-
-/* Now handle the rest of the matrix */
-
- for (row = 1; row < to_len; row++) {
- for (col = 0; col < from_len; col++) {
- buffer[index] = min3(
- NW(row, col) + ((from[col] == to[row]) ? 0 : ch),
- N(row, col + 1) + ins,
- W(row + 1, col) + del);
- if (from[col] == to[row - 1] && col > 0 &&
- from[col - 1] == to[row])
- buffer[index] = min2(buffer[index],
- NNWW(row - 1, col - 1) + swap_cost);
-
-#ifdef DEBUG_EDITDIST
- printf("%2d ", buffer[index]);
-#endif
-#ifdef TRN_SPEEDUP
- if (buffer[index] < low || col == 0)
- low = buffer[index];
-#endif
-
- index = mod(index + 1);
- } /* for col = 1 */
-#ifdef DEBUG_EDITDIST
- if (row < to_len - 1)
- printf("\n %c %2d ", to[row+1], (row + 2) * ins);
- else
- printf("\n");
-#endif
-#ifdef TRN_SPEEDUP
- if (low > MIN_DIST)
- break;
-#endif
- } /* for row = 1 */
-
- row = buffer[mod(index + radix - 1)];
- if (buffer != store)
- free((char *) buffer);
- return row;
-} /* edit_distn */
-
-
diff --git a/lib/libpe/libfuzzy/fuzzy.c b/lib/libpe/libfuzzy/fuzzy.c
deleted file mode 100644
index 122c4622..00000000
--- a/lib/libpe/libfuzzy/fuzzy.c
+++ /dev/null
@@ -1,718 +0,0 @@
-/* ssdeep
- * Copyright (C) 2002 Andrew Tridgell
- * Copyright (C) 2006 ManTech International Corporation
- * Copyright (C) 2013 Helmut Grohne
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Earlier versions of this code were named fuzzy.c and can be found at:
- * http://www.samba.org/ftp/unpacked/junkcode/spamsum/
- * http://ssdeep.sf.net/
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include "fuzzy.h"
-
-#if defined(__GNUC__) && __GNUC__ >= 3
-#define likely(x) __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
-#else
-#define likely(x) x
-#define unlikely(x) x
-#endif
-
-#ifndef MIN
-#define MIN(a,b) ((a)<(b)?(a):(b))
-#endif
-
-#ifndef MAX
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#endif
-
-#define ROLLING_WINDOW 7
-#define MIN_BLOCKSIZE 3
-#define HASH_PRIME 0x01000193
-#define HASH_INIT 0x28021967
-#define NUM_BLOCKHASHES 31
-
-struct roll_state {
- unsigned char window[ROLLING_WINDOW];
- uint32_t h1, h2, h3;
- uint32_t n;
-};
-
-static void roll_init(/*@out@*/ struct roll_state *self) {
- memset(self, 0, sizeof(struct roll_state));
-}
-
-/*
- * a rolling hash, based on the Adler checksum. By using a rolling hash
- * we can perform auto resynchronisation after inserts/deletes
-
- * internally, h1 is the sum of the bytes in the window and h2
- * is the sum of the bytes times the index
-
- * h3 is a shift/xor based rolling hash, and is mostly needed to ensure that
- * we can cope with large blocksize values
- */
-static void roll_hash(struct roll_state *self, unsigned char c)
-{
- self->h2 -= self->h1;
- self->h2 += ROLLING_WINDOW * (uint32_t)c;
-
- self->h1 += (uint32_t)c;
- self->h1 -= (uint32_t)self->window[self->n % ROLLING_WINDOW];
-
- self->window[self->n % ROLLING_WINDOW] = c;
- self->n++;
-
- /* The original spamsum AND'ed this value with 0xFFFFFFFF which
- * in theory should have no effect. This AND has been removed
- * for performance (jk) */
- self->h3 <<= 5;
- self->h3 ^= c;
-}
-
-static uint32_t roll_sum(const struct roll_state *self)
-{
- return self->h1 + self->h2 + self->h3;
-}
-
-/* A simple non-rolling hash, based on the FNV hash. */
-static uint32_t sum_hash(unsigned char c, uint32_t h)
-{
- return (h * HASH_PRIME) ^ c;
-}
-
-/* A blockhash contains a signature state for a specific (implicit) blocksize.
- * The blocksize is given by SSDEEP_BS(index). The h and halfh members are the
- * FNV hashes, where halfh stops to be reset after digest is SPAMSUM_LENGTH/2
- * long. The halfh hash is needed be able to truncate digest for the second
- * output hash to stay compatible with ssdeep output. */
-struct blockhash_context
-{
- uint32_t h, halfh;
- char digest[SPAMSUM_LENGTH];
- unsigned int dlen;
-};
-
-struct fuzzy_state
-{
- unsigned int bhstart, bhend;
- struct blockhash_context bh[NUM_BLOCKHASHES];
- size_t total_size;
- struct roll_state roll;
-};
-
-#define SSDEEP_BS(index) (((uint32_t)MIN_BLOCKSIZE) << (index))
-
-/*@only@*/ /*@null@*/ struct fuzzy_state *fuzzy_new(void)
-{
- struct fuzzy_state *self;
- if(NULL == (self = malloc(sizeof(struct fuzzy_state))))
- /* malloc sets ENOMEM */
- return NULL;
- self->bhstart = 0;
- self->bhend = 1;
- self->bh[0].h = HASH_INIT;
- self->bh[0].halfh = HASH_INIT;
- self->bh[0].dlen = 0;
- self->total_size = 0;
- roll_init(&self->roll);
- return self;
-}
-
-static void fuzzy_try_fork_blockhash(struct fuzzy_state *self)
-{
- struct blockhash_context *obh, *nbh;
- if (self->bhend >= NUM_BLOCKHASHES)
- return;
- assert(self->bhend > 0);
- obh = self->bh + (self->bhend - 1);
- nbh = obh + 1;
- nbh->h = obh->h;
- nbh->halfh = obh->halfh;
- nbh->dlen = 0;
- ++self->bhend;
-}
-
-static void fuzzy_try_reduce_blockhash(struct fuzzy_state *self)
-{
- assert(self->bhstart < self->bhend);
- if (self->bhend - self->bhstart < 2)
- /* Need at least two working hashes. */
- return;
- if ((size_t)SSDEEP_BS(self->bhstart) * SPAMSUM_LENGTH >=
- self->total_size)
- /* Initial blocksize estimate would select this or a smaller
- * blocksize. */
- return;
- if (self->bh[self->bhstart + 1].dlen < SPAMSUM_LENGTH / 2)
- /* Estimate adjustment would select this blocksize. */
- return;
- /* At this point we are clearly no longer interested in the
- * start_blocksize. Get rid of it. */
- ++self->bhstart;
-}
-
-static const char *b64 =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static void fuzzy_engine_step(struct fuzzy_state *self, unsigned char c)
-{
- size_t h;
- unsigned int i;
- /* At each character we update the rolling hash and the normal hashes.
- * When the rolling hash hits a reset value then we emit a normal hash
- * as a element of the signature and reset the normal hash. */
- roll_hash(&self->roll, c);
- h = roll_sum(&self->roll);
-
- for (i = self->bhstart; i < self->bhend; ++i)
- {
- self->bh[i].h = sum_hash(c, self->bh[i].h);
- self->bh[i].halfh = sum_hash(c, self->bh[i].halfh);
- }
-
- for (i = self->bhstart; i < self->bhend; ++i)
- {
- /* With growing blocksize almost no runs fail the next test. */
- if (likely(h % SSDEEP_BS(i) != SSDEEP_BS(i) - 1))
- /* Once this condition is false for one bs, it is
- * automatically false for all further bs. I.e. if
- * h === -1 (mod 2*bs) then h === -1 (mod bs). */
- break;
- /* We have hit a reset point. We now emit hashes which are
- * based on all characters in the piece of the message between
- * the last reset point and this one */
- if (unlikely(0 == self->bh[i].dlen)) {
- /* Can only happen 30 times. */
- /* First step for this blocksize. Clone next. */
- fuzzy_try_fork_blockhash(self);
- }
- if (self->bh[i].dlen < SPAMSUM_LENGTH - 1) {
- /* We can have a problem with the tail overflowing. The
- * easiest way to cope with this is to only reset the
- * normal hash if we have room for more characters in
- * our signature. This has the effect of combining the
- * last few pieces of the message into a single piece
- * */
- self->bh[i].digest[self->bh[i].dlen++] =
- b64[self->bh[i].h % 64];
- self->bh[i].h = HASH_INIT;
- if (self->bh[i].dlen < SPAMSUM_LENGTH / 2)
- self->bh[i].halfh = HASH_INIT;
- } else
- fuzzy_try_reduce_blockhash(self);
- }
-}
-
-int fuzzy_update(struct fuzzy_state *self,
- const unsigned char *buffer,
- size_t buffer_size) {
- self->total_size += buffer_size;
- for ( ;buffer_size > 0; ++buffer, --buffer_size)
- fuzzy_engine_step(self, *buffer);
- return 0;
-}
-
-static int memcpy_eliminate_sequences(char *dst,
- const char *src,
- int n)
-{
- const char *srcend = src + n;
- assert(n >= 0);
- if (src < srcend) *dst++ = *src++;
- if (src < srcend) *dst++ = *src++;
- if (src < srcend) *dst++ = *src++;
- while (src < srcend)
- if (*src == dst[-1] && *src == dst[-2] && *src == dst[-3])
- {
- ++src;
- --n;
- } else
- *dst++ = *src++;
- return n;
-}
-
-#ifdef S_SPLINT_S
-extern const int EOVERFLOW;
-#endif
-
-// We need some extra help on Win32
-#ifdef _WIN32
-# define EOVERFLOW 84
-# define ftello ftell
-# define fseeko fseek
-#endif
-
-int fuzzy_digest(const struct fuzzy_state *self,
- /*@out@*/ char *result,
- unsigned int flags)
-{
- unsigned int bi = self->bhstart;
- uint32_t h = roll_sum(&self->roll);
- int i, remain = FUZZY_MAX_RESULT - 1; /* Exclude terminating '\0'. */
- /* Verify that our elimination was not overeager. */
- assert(bi == 0 || (size_t)SSDEEP_BS(bi) / 2 * SPAMSUM_LENGTH <
- self->total_size);
-
- /* Initial blocksize guess. */
- while ((size_t)SSDEEP_BS(bi) * SPAMSUM_LENGTH < self->total_size) {
- ++bi;
- if (bi >= NUM_BLOCKHASHES) {
- /* The input exceeds data types. */
- errno = EOVERFLOW;
- return -1;
- }
- }
- /* Adapt blocksize guess to actual digest length. */
- while (bi >= self->bhend)
- --bi;
- while (bi > self->bhstart && self->bh[bi].dlen < SPAMSUM_LENGTH / 2)
- --bi;
- assert (!(bi > 0 && self->bh[bi].dlen < SPAMSUM_LENGTH / 2));
-
- i = snprintf(result, (size_t)remain, "%u:", SSDEEP_BS(bi));
- if (i <= 0)
- /* Maybe snprintf has set errno here? */
- return -1;
- assert(i < remain);
- remain -= i;
- result += i;
- i = (int)self->bh[bi].dlen;
- assert(i <= remain);
- if ((flags & FUZZY_FLAG_ELIMSEQ) != 0)
- i = memcpy_eliminate_sequences(result, self->bh[bi].digest, i);
- else
- memcpy(result, self->bh[bi].digest, (size_t)i);
- result += i;
- remain -= i;
- if (h != 0)
- {
- assert(remain > 0);
- *result = b64[self->bh[bi].h % 64];
- if((flags & FUZZY_FLAG_ELIMSEQ) == 0 || i < 3 ||
- *result != result[-1] ||
- *result != result[-2] ||
- *result != result[-3]) {
- ++result;
- --remain;
- }
- }
- assert(remain > 0);
- *result++ = ':';
- --remain;
- if (bi < self->bhend - 1)
- {
- ++bi;
- i = (int)self->bh[bi].dlen;
- if ((flags & FUZZY_FLAG_NOTRUNC) == 0 &&
- i > SPAMSUM_LENGTH / 2 - 1)
- i = SPAMSUM_LENGTH / 2 - 1;
- assert(i <= remain);
- if ((flags & FUZZY_FLAG_ELIMSEQ) != 0)
- i = memcpy_eliminate_sequences(result,
- self->bh[bi].digest, i);
- else
- memcpy(result, self->bh[bi].digest, (size_t)i);
- result += i;
- remain -= i;
- if (h != 0) {
- assert(remain > 0);
- h = (flags & FUZZY_FLAG_NOTRUNC) != 0 ? self->bh[bi].h :
- self->bh[bi].halfh;
- *result = b64[h % 64];
- if ((flags & FUZZY_FLAG_ELIMSEQ) == 0 || i < 3 ||
- *result != result[-1] ||
- *result != result[-2] ||
- *result != result[-3])
- {
- ++result;
- --remain;
- }
- }
- } else if (h != 0)
- {
- assert(self->bh[bi].dlen == 0);
- assert(remain > 0);
- *result++ = b64[self->bh[bi].h % 64];
- /* No need to bother with FUZZY_FLAG_ELIMSEQ, because this
- * digest has length 1. */
- --remain;
- }
- *result = '\0';
- return 0;
-}
-
-void fuzzy_free(/*@only@*/ struct fuzzy_state *self)
-{
- free(self);
-}
-
-int fuzzy_hash_buf(const unsigned char *buf,
- uint32_t buf_len,
- /*@out@*/ char *result)
-{
- struct fuzzy_state *ctx;
- int ret = -1;
- if (NULL == (ctx = fuzzy_new()))
- return -1;
- if (fuzzy_update(ctx, buf, buf_len) < 0)
- goto out;
- if (fuzzy_digest(ctx, result, 0) < 0)
- goto out;
- ret = 0;
- out:
- fuzzy_free(ctx);
- return ret;
-}
-
-int fuzzy_hash_stream(FILE *handle, /*@out@*/ char *result)
-{
- struct fuzzy_state *ctx;
- unsigned char buffer[4096];
- size_t n;
- int ret = -1;
- if (NULL == (ctx = fuzzy_new()))
- return -1;
- for(;;)
- {
- n = fread(buffer, 1, 4096, handle);
- if (0 == n)
- break;
- if (fuzzy_update(ctx, buffer, n) < 0)
- goto out;
- }
- if (ferror(handle) != 0)
- goto out;
- if (fuzzy_digest(ctx, result, 0) < 0)
- goto out;
- ret = 0;
- out:
- fuzzy_free(ctx);
- return ret;
-}
-
-#ifdef S_SPLINT_S
-typedef size_t off_t;
-int fseeko(FILE *, off_t, int);
-off_t ftello(FILE *);
-#endif
-
-int fuzzy_hash_file(FILE *handle, /*@out@*/ char *result)
-{
- off_t fpos;
- int status;
- fpos = ftello(handle);
- if (fseek(handle, 0, SEEK_SET) < 0)
- return -1;
- status = fuzzy_hash_stream(handle, result);
- if (status == 0)
- {
- if (fseeko(handle, fpos, SEEK_SET) < 0)
- return -1;
- }
- return status;
-}
-
-int fuzzy_hash_filename(const char *filename, /*@out@*/ char *result)
-{
- int status;
- FILE *handle = fopen(filename, "rb");
- if (NULL == handle)
- return -1;
- status = fuzzy_hash_stream(handle, result);
- /* We cannot do anything about an fclose failure. */
- (void)fclose(handle);
- return status;
-}
-
-//
-// We only accept a match if we have at least one common substring in
-// the signature of length ROLLING_WINDOW. This dramatically drops the
-// false positive rate for low score thresholds while having
-// negligable affect on the rate of spam detection.
-//
-// return 1 if the two strings do have a common substring, 0 otherwise
-//
-static int has_common_substring(const char *s1, const char *s2)
-{
- int i, j;
- int num_hashes;
- uint32_t hashes[SPAMSUM_LENGTH];
-
- // there are many possible algorithms for common substring
- // detection. In this case I am re-using the rolling hash code
- // to act as a filter for possible substring matches
-
- memset(hashes, 0, sizeof(hashes));
-
- // first compute the windowed rolling hash at each offset in
- // the first string
- struct roll_state state;
- roll_init (&state);
-
- for (i=0;s1[i];i++)
- {
- roll_hash(&state, (unsigned char)s1[i]);
- hashes[i] = roll_sum(&state);
- }
- num_hashes = i;
-
- roll_init(&state);
-
- // now for each offset in the second string compute the
- // rolling hash and compare it to all of the rolling hashes
- // for the first string. If one matches then we have a
- // candidate substring match. We then confirm that match with
- // a direct string comparison */
- for (i=0;s2[i];i++)
- {
- roll_hash(&state, (unsigned char)s2[i]);
- uint32_t h = roll_sum(&state);
- if (i < ROLLING_WINDOW-1) continue;
- for (j=ROLLING_WINDOW-1;j= ROLLING_WINDOW &&
- strncmp(s2+i-(ROLLING_WINDOW-1),
- s1+j-(ROLLING_WINDOW-1),
- ROLLING_WINDOW) == 0)
- {
- return 1;
- }
- }
- }
- }
-
- return 0;
-}
-
-
-// eliminate sequences of longer than 3 identical characters. These
-// sequences contain very little information so they tend to just bias
-// the result unfairly
-static char *eliminate_sequences(const char *str)
-{
- char *ret;
- size_t i, j, len;
-
- ret = strdup(str);
- if (!ret)
- return NULL;
-
- len = strlen(str);
- if (len < 3)
- return ret;
-
- for (i=j=3 ; i SPAMSUM_LENGTH || len2 > SPAMSUM_LENGTH) {
- // not a real spamsum signature?
- return 0;
- }
-
- // the two strings must have a common substring of length
- // ROLLING_WINDOW to be candidates
- if (has_common_substring(s1, s2) == 0) {
- return 0;
- }
-
- // compute the edit distance between the two strings. The edit distance gives
- // us a pretty good idea of how closely related the two strings are
- score = edit_distn(s1, len1, s2, len2);
-
- // scale the edit distance by the lengths of the two
- // strings. This changes the score to be a measure of the
- // proportion of the message that has changed rather than an
- // absolute quantity. It also copes with the variability of
- // the string lengths.
- score = (score * SPAMSUM_LENGTH) / (len1 + len2);
-
- // at this stage the score occurs roughly on a 0-64 scale,
- // with 0 being a good match and 64 being a complete
- // mismatch
-
- // rescale to a 0-100 scale (friendlier to humans)
- score = (100 * score) / 64;
-
- // it is possible to get a score above 100 here, but it is a
- // really terrible match
- if (score >= 100)
- return 0;
-
- // now re-scale on a 0-100 scale with 0 being a poor match and
- // 100 being a excellent match.
- score = 100 - score;
-
- // printf ("len1: %"PRIu32" len2: %"PRIu32"\n", len1, len2);
-
- // when the blocksize is small we don't want to exaggerate the match size
- if (score > block_size/MIN_BLOCKSIZE * MIN(len1, len2))
- {
- score = block_size/MIN_BLOCKSIZE * MIN(len1, len2);
- }
- return score;
-}
-
-//
-// Given two spamsum strings return a value indicating the degree
-// to which they match.
-//
-int fuzzy_compare(const char *str1, const char *str2)
-{
- unsigned int block_size1, block_size2;
- uint32_t score = 0;
- char *s1, *s2;
- char *s1_1, *s1_2, *s1_3;
- char *s2_1, *s2_2, *s2_3;
-
- if (NULL == str1 || NULL == str2)
- return -1;
-
- // each spamsum is prefixed by its block size
- if (sscanf(str1, "%u:", &block_size1) != 1 ||
- sscanf(str2, "%u:", &block_size2) != 1) {
- return -1;
- }
-
- // if the blocksizes don't match then we are comparing
- // apples to oranges. This isn't an 'error' per se. We could
- // have two valid signatures, but they can't be compared.
- if (block_size1 != block_size2 &&
- block_size1 != block_size2*2 &&
- block_size2 != block_size1*2) {
- return 0;
- }
-
- // move past the prefix
- str1 = strchr(str1, ':');
- str2 = strchr(str2, ':');
-
- if (!str1 || !str2) {
- // badly formed ...
- return -1;
- }
-
- // there is very little information content is sequences of
- // the same character like 'LLLLL'. Eliminate any sequences
- // longer than 3. This is especially important when combined
- // with the has_common_substring() test below.
- // NOTE: This function duplciates str1 and str2
- s1 = eliminate_sequences(str1+1);
- if (!s1)
- return 0;
- s2 = eliminate_sequences(str2+1);
- if (!s2)
- {
- free(s1);
- return 0;
- }
-
- // now break them into the two pieces
- s1_1 = s1;
- s2_1 = s2;
-
- s1_2 = strchr(s1, ':');
- s2_2 = strchr(s2, ':');
-
- if (!s1_2 || !s2_2) {
- // a signature is malformed - it doesn't have 2 parts
- free(s1); free(s2);
- return -1;
- }
-
- // Chop the first substring. We terminate the first substring
- // and then advance the pointer to the start of the second substring.
- *s1_2 = 0;
- s1_2++;
- *s2_2 = 0;
- s2_2++;
-
- // Chop the second string at the comma--just before the filename.
- // If the strings don't have a comma (i.e. don't have a filename)
- // that's ok. It's not an error. This function can be called on
- // signatures which don't have filenames attached.
- // We also don't have to advance past the comma however. We don't care
- // about the filename
- s1_3 = strchr(s1_2, ',');
- s2_3 = strchr(s2_2, ',');
- if (s1_3 != NULL)
- *s1_3 = 0;
- if (s2_3 != NULL)
- *s2_3 = 0;
-
- // each signature has a string for two block sizes. We now
- // choose how to combine the two block sizes. We checked above
- // that they have at least one block size in common
- if (block_size1 == block_size2)
- {
- uint32_t score1, score2;
- score1 = score_strings(s1_1, s2_1, block_size1);
- score2 = score_strings(s1_2, s2_2, block_size1*2);
- score = MAX(score1, score2);
- }
- else if (block_size1 == block_size2*2)
- {
- score = score_strings(s1_1, s2_2, block_size1);
- }
- else
- {
- score = score_strings(s1_2, s2_1, block_size2);
- }
-
- free(s1);
- free(s2);
-
- return (int)score;
-}
diff --git a/lib/libpe/libpe-config.cmake.in b/lib/libpe/libpe-config.cmake.in
new file mode 100644
index 00000000..79e1c93b
--- /dev/null
+++ b/lib/libpe/libpe-config.cmake.in
@@ -0,0 +1,15 @@
+@PACKAGE_INIT@
+
+include(CMakeFindDependencyMacro)
+
+find_dependency(OpenSSL REQUIRED COMPONENTS Crypto)
+
+set_and_check(pe_INCLUDEDIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+
+include(${CMAKE_CURRENT_LIST_DIR}/libpe-targets.cmake)
+
+add_library(readpe::libpe ALIAS readpe::pe)
+
+set(pe_LIBRARIES readpe::libpe)
+set(pe_INCLUDE_DIRS ${pe_INCLUDEDIR})
+
diff --git a/lib/libpe/libpe.pc.in b/lib/libpe/libpe.pc.in
new file mode 100644
index 00000000..afe63a58
--- /dev/null
+++ b/lib/libpe/libpe.pc.in
@@ -0,0 +1,9 @@
+libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@
+includedir=@CMAKE_INSTALL_PREFIX@/include/libpe
+
+Name: libpe
+Description: Library to read PE file information
+Version: @PROJECT_VERSION@
+Requires:
+Libs: -L${libdir} -lpe
+Cflags: -I${includedir}
diff --git a/lib/libpe/misc.c b/lib/libpe/misc.c
index b854d750..c53c28c5 100644
--- a/lib/libpe/misc.c
+++ b/lib/libpe/misc.c
@@ -24,259 +24,295 @@
#define _GNU_SOURCE
#endif
+#include "libpe/macros.h"
#include "libpe/pe.h"
-#include
-#include
+
#include
+#include
+#include
#include
-static double calculate_entropy(const unsigned int counted_bytes[256], const size_t total_length) {
- double entropy = 0.;
+static double calculate_entropy(const unsigned int counted_bytes[256],
+ const size_t total_length)
+{
+ double entropy = 0.;
- for (size_t i = 0; i < 256; i++) {
- double temp = (double)counted_bytes[i] / total_length;
- if (temp > 0.)
- entropy += temp * fabs( log2(temp) );
- }
+ for (size_t i = 0; i < 256; i++) {
+ double temp = (double) counted_bytes[i] / (double) total_length;
+ if (temp > 0.) {
+ entropy += temp * fabs(log2(temp));
+ }
+ }
- return entropy;
+ return entropy;
}
-double pe_calculate_entropy_file(pe_ctx_t *ctx) {
- unsigned int counted_bytes[256] = { 0 };
+double pe_calculate_entropy_file(pe_ctx_t *ctx)
+{
+ unsigned int counted_bytes[256] = {0};
- const uint8_t *file_bytes = LIBPE_PTR_ADD(ctx->map_addr, 0);
- const uint64_t filesize = pe_filesize(ctx);
- for (uint64_t ofs=0; ofs < filesize; ofs++) {
- const uint8_t byte = file_bytes[ofs];
- counted_bytes[byte]++;
- }
+ const uint8_t *file_bytes = LIBPE_PTR_ADD(ctx->map_addr, 0);
+ const uint64_t filesize = pe_filesize(ctx);
+ for (uint64_t ofs = 0; ofs < filesize; ofs++) {
+ const uint8_t byte = file_bytes[ofs];
+ counted_bytes[byte]++;
+ }
- return calculate_entropy(counted_bytes, (size_t)filesize);
+ return calculate_entropy(counted_bytes, (size_t) filesize);
}
-bool pe_fpu_trick(pe_ctx_t *ctx) {
- // NOTE: What 0xdf has to do with fpu?
- return !! memmem( ctx->map_addr, ctx->map_size, "\xdf\xdf\xdf\xdf", 4 );
-
-// const char *opcode_ptr = ctx->map_addr;
-//
-// for (uint32_t i=0, times=0; i < ctx->map_size; i++) {
-// if (*opcode_ptr++ == '\xdf') {
-// if (++times == 4)
-// return true;
-// } else {
-// times = 0;
-// }
-// }
-//
-// return false;
+bool pe_fpu_trick(pe_ctx_t *ctx)
+{
+ // TODO: What 0xdf has to do with fpu?
+#ifndef _MSC_VER
+ return ! ! memmem(ctx->map_addr, (size_t) ctx->map_size, "\xdf\xdf\xdf\xdf",
+ 4);
+#else
+ const char *opcode_ptr = ctx->map_addr;
+
+ for (size_t i = 0, times = 0; i < ctx->map_size; i++) {
+ if (*opcode_ptr++ == '\xdf') {
+ if (++times == 4) {
+ return true;
+ }
+ } else {
+ times = 0;
+ }
+ }
+
+ return false;
+#endif
}
-int cpl_analysis(pe_ctx_t *ctx) {
- const IMAGE_COFF_HEADER *hdr_coff_ptr = pe_coff(ctx);
- const IMAGE_DOS_HEADER *hdr_dos_ptr = pe_dos(ctx);
-
- if (hdr_coff_ptr == NULL || hdr_dos_ptr == NULL)
- return 0;
-
- static const uint16_t characteristics1 =
- ( IMAGE_FILE_EXECUTABLE_IMAGE
- | IMAGE_FILE_LINE_NUMS_STRIPPED
- | IMAGE_FILE_LOCAL_SYMS_STRIPPED
- | IMAGE_FILE_BYTES_REVERSED_LO
- | IMAGE_FILE_32BIT_MACHINE
- | IMAGE_FILE_DLL
- | IMAGE_FILE_BYTES_REVERSED_HI);
- static const uint16_t characteristics2 =
- ( IMAGE_FILE_EXECUTABLE_IMAGE
- | IMAGE_FILE_LINE_NUMS_STRIPPED
- | IMAGE_FILE_LOCAL_SYMS_STRIPPED
- | IMAGE_FILE_BYTES_REVERSED_LO
- | IMAGE_FILE_32BIT_MACHINE
- | IMAGE_FILE_DEBUG_STRIPPED
- | IMAGE_FILE_DLL
- | IMAGE_FILE_BYTES_REVERSED_HI);
- static const uint16_t characteristics3 =
- ( IMAGE_FILE_EXECUTABLE_IMAGE
- | IMAGE_FILE_LINE_NUMS_STRIPPED
- | IMAGE_FILE_32BIT_MACHINE
- | IMAGE_FILE_DEBUG_STRIPPED
- | IMAGE_FILE_DLL);
-
- // FIXME: Which timestamps are those?
- // UNIX timestams:
- // 708992537 = 19/jun/1992 @ 19:22:17
- // 1354555867 = 3/dez/2012 @ 15:31:07
- //
- // Findings:
- // * 708992537 is the timestamp from an old delphi compiler bug
- // * 1354555867 was probably just the current time
- if ((hdr_coff_ptr->TimeDateStamp == 708992537 ||
- hdr_coff_ptr->TimeDateStamp > 1354555867)
- && (hdr_coff_ptr->Characteristics == characteristics1 || // equals 0xa18e
- hdr_coff_ptr->Characteristics == characteristics2 || // equals 0xa38e
- hdr_coff_ptr->Characteristics == characteristics3) // equals 0x2306
- && hdr_dos_ptr->e_sp == 0xb8 // ???
- )
- return 1;
-
- return 0;
+int cpl_analysis(pe_ctx_t *ctx)
+{
+ const IMAGE_COFF_HEADER *hdr_coff_ptr = pe_coff(ctx);
+ const IMAGE_DOS_HEADER *hdr_dos_ptr = pe_dos(ctx);
+
+ if (hdr_coff_ptr == NULL || hdr_dos_ptr == NULL) {
+ return 0;
+ }
+
+ static const uint16_t characteristics1
+ = (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LINE_NUMS_STRIPPED
+ | IMAGE_FILE_LOCAL_SYMS_STRIPPED | IMAGE_FILE_BYTES_REVERSED_LO
+ | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_DLL
+ | IMAGE_FILE_BYTES_REVERSED_HI);
+ static const uint16_t characteristics2
+ = (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LINE_NUMS_STRIPPED
+ | IMAGE_FILE_LOCAL_SYMS_STRIPPED | IMAGE_FILE_BYTES_REVERSED_LO
+ | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_DEBUG_STRIPPED
+ | IMAGE_FILE_DLL | IMAGE_FILE_BYTES_REVERSED_HI);
+ static const uint16_t characteristics3
+ = (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LINE_NUMS_STRIPPED
+ | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_DEBUG_STRIPPED
+ | IMAGE_FILE_DLL);
+
+ // FIXME: Which timestamps are those?
+ // UNIX timestams:
+ // 708992537 = 19/jun/1992 @ 19:22:17
+ // 1354555867 = 3/dez/2012 @ 15:31:07
+ //
+ // Findings:
+ // * 708992537 is the timestamp from an old delphi compiler bug
+ // * 1354555867 was probably just the current time
+ if ((hdr_coff_ptr->TimeDateStamp == 708992537
+ || hdr_coff_ptr->TimeDateStamp > 1354555867)
+ && (hdr_coff_ptr->Characteristics == characteristics1
+ || // equals 0xa18e
+ hdr_coff_ptr->Characteristics == characteristics2
+ || // equals 0xa38e
+ hdr_coff_ptr->Characteristics == characteristics3) // equals 0x2306
+ && hdr_dos_ptr->e_sp == 0xb8 // ???
+ ) {
+ return 1;
+ }
+
+ return 0;
}
-int pe_get_cpl_analysis(pe_ctx_t *ctx) {
- return pe_is_dll(ctx) ? cpl_analysis(ctx) : -1;
+int pe_get_cpl_analysis(pe_ctx_t *ctx)
+{
+ return pe_is_dll(ctx) ? cpl_analysis(ctx) : -1;
}
-const IMAGE_SECTION_HEADER *pe_check_fake_entrypoint(pe_ctx_t *ctx, uint32_t ep) {
- const uint16_t num_sections = pe_sections_count(ctx);
- if (num_sections == 0)
- return NULL;
+const IMAGE_SECTION_HEADER *pe_check_fake_entrypoint(pe_ctx_t *ctx, uint32_t ep)
+{
+ const uint16_t num_sections = pe_sections_count(ctx);
+ if (num_sections == 0) {
+ return NULL;
+ }
- const IMAGE_SECTION_HEADER *section = pe_rva2section(ctx, ep);
- if (section == NULL)
- return NULL;
+ const IMAGE_SECTION_HEADER *section = pe_rva2section(ctx, ep);
+ if (section == NULL) {
+ return NULL;
+ }
- if (section->Characteristics & IMAGE_SCN_CNT_CODE)
- return NULL;
+ if (section->Characteristics & IMAGE_SCN_CNT_CODE) {
+ return NULL;
+ }
- return section;
+ return section;
}
-int pe_has_fake_entrypoint(pe_ctx_t *ctx) {
- const IMAGE_OPTIONAL_HEADER *optional = pe_optional(ctx);
- if (optional == NULL)
- return -1; // Unable to read optional header.
-
- const uint32_t ep =
- optional->_32 ? optional->_32->AddressOfEntryPoint :
- optional->_64 ? optional->_64->AddressOfEntryPoint :
- optional->_rom ? optional->_rom->AddressOfEntryPoint :
- 0;
-
- int value;
-
- if (ep == 0) {
- value = -2; // null
- } else if (pe_check_fake_entrypoint(ctx, ep)) {
- value = 1; // fake
- } else {
- value = 0; // normal
- }
-
- return value;
+int pe_has_fake_entrypoint(pe_ctx_t *ctx)
+{
+ const IMAGE_OPTIONAL_HEADER *optional = pe_optional(ctx);
+ if (optional == NULL) {
+ return -1; // Unable to read optional header.
+ }
+
+ const uint32_t ep = optional->_32 ? optional->_32->AddressOfEntryPoint
+ : optional->_64 ? optional->_64->AddressOfEntryPoint
+ : optional->_rom ? optional->_rom->AddressOfEntryPoint
+ : 0;
+
+ int value;
+
+ if (ep == 0) {
+ value = -2; // null
+ } else if (pe_check_fake_entrypoint(ctx, ep)) {
+ value = 1; // fake
+ } else {
+ value = 0; // normal
+ }
+
+ return value;
}
-uint32_t pe_get_tls_directory(pe_ctx_t *ctx) {
- if (ctx->pe.num_directories == 0 || ctx->pe.num_directories > MAX_DIRECTORIES)
- return 0;
+uint32_t pe_get_tls_directory(pe_ctx_t *ctx)
+{
+ if (ctx->pe.num_directories == 0
+ || ctx->pe.num_directories > MAX_DIRECTORIES) {
+ return 0;
+ }
- const IMAGE_DATA_DIRECTORY *directory = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_TLS);
- if (directory == NULL)
- return 0;
+ const IMAGE_DATA_DIRECTORY *directory
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_TLS);
+ if (directory == NULL) {
+ return 0;
+ }
- if (directory->Size == 0)
- return 0;
+ if (directory->Size == 0) {
+ return 0;
+ }
- return directory->VirtualAddress;
+ return directory->VirtualAddress;
}
-static int count_tls_callbacks(pe_ctx_t *ctx) {
- int ret = 0;
-
- const IMAGE_OPTIONAL_HEADER *optional_hdr = pe_optional(ctx);
- if (optional_hdr == NULL)
- return 0;
-
- IMAGE_SECTION_HEADER ** const sections = pe_sections(ctx);
- if (sections == NULL)
- return 0;
-
- const uint64_t tls_addr = pe_get_tls_directory(ctx);
- if (tls_addr == 0)
- return 0;
-
- const uint16_t num_sections = pe_sections_count(ctx);
-
- uint64_t ofs = 0;
-
- // search for tls in all sections
- for (uint16_t i=0, j=0; i < num_sections; i++) {
- const bool can_process = tls_addr >= sections[i]->VirtualAddress
- && tls_addr < (sections[i]->VirtualAddress + sections[i]->SizeOfRawData);
-
- if (!can_process)
- continue;
-
- ofs = tls_addr - sections[i]->VirtualAddress + sections[i]->PointerToRawData;
-
- switch (optional_hdr->type) {
- default:
- return 0;
- case MAGIC_PE32_0:
- case MAGIC_PE32:
- {
- const IMAGE_TLS_DIRECTORY32 *tls_dir = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, tls_dir, sizeof(IMAGE_TLS_DIRECTORY32))) {
- // TODO: Should we report something?
- return 0;
- }
-
- if (!(tls_dir->AddressOfCallBacks & optional_hdr->_32->ImageBase))
- break;
-
- ofs = pe_rva2ofs(ctx, tls_dir->AddressOfCallBacks - optional_hdr->_32->ImageBase);
- break;
- }
- case MAGIC_PE64:
- {
- const IMAGE_TLS_DIRECTORY64 *tls_dir = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, tls_dir, sizeof(IMAGE_TLS_DIRECTORY64))) {
- // TODO: Should we report something?
- return 0;
- }
-
- if (!(tls_dir->AddressOfCallBacks & optional_hdr->_64->ImageBase))
- break;
-
- ofs = pe_rva2ofs(ctx, tls_dir->AddressOfCallBacks - optional_hdr->_64->ImageBase);
- break;
- }
- }
-
- ret = -1; // tls directory and section exists
-
- uint32_t funcaddr = 0;
-
- // FIXME: Why this loop if 'funcaddr' isn't updated?
- do {
- const uint32_t *funcaddr_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- if (!pe_can_read(ctx, funcaddr_ptr, sizeof(*funcaddr_ptr))) {
- // TODO: Should we report something?
- return 0;
- }
-
- // FIXME: This funcaddr is declared in block scope!
- uint32_t funcaddr = *funcaddr_ptr;
- if (funcaddr) {
- ret = ++j; // function found
- }
- } while (funcaddr);
- }
-
- return ret;
+static int count_tls_callbacks(pe_ctx_t *ctx)
+{
+ int ret = 0;
+
+ const IMAGE_OPTIONAL_HEADER *optional_hdr = pe_optional(ctx);
+ if (optional_hdr == NULL) {
+ return 0;
+ }
+
+ IMAGE_SECTION_HEADER **const sections = pe_sections(ctx);
+ if (sections == NULL) {
+ return 0;
+ }
+
+ const uint64_t tls_addr = pe_get_tls_directory(ctx);
+ if (tls_addr == 0) {
+ return 0;
+ }
+
+ const uint16_t num_sections = pe_sections_count(ctx);
+
+ uint64_t ofs = 0;
+
+ // search for tls in all sections
+ for (uint16_t i = 0, j = 0; i < num_sections; i++) {
+ const bool can_process = tls_addr >= sections[i]->VirtualAddress
+ && tls_addr < (sections[i]->VirtualAddress
+ + sections[i]->SizeOfRawData);
+
+ if (! can_process) {
+ continue;
+ }
+
+ ofs = tls_addr - sections[i]->VirtualAddress
+ + sections[i]->PointerToRawData;
+
+ switch (optional_hdr->type) {
+ default:
+ return 0;
+ case MAGIC_PE32_0:
+ case MAGIC_PE32: {
+ const IMAGE_TLS_DIRECTORY32 *tls_dir
+ = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, tls_dir, sizeof(IMAGE_TLS_DIRECTORY32))) {
+ // TODO: Should we report something?
+ return 0;
+ }
+
+ if (! (tls_dir->AddressOfCallBacks
+ & optional_hdr->_32->ImageBase)) {
+ break;
+ }
+
+ ofs = pe_rva2ofs(ctx, tls_dir->AddressOfCallBacks
+ - optional_hdr->_32->ImageBase);
+ break;
+ }
+ case MAGIC_PE64: {
+ const IMAGE_TLS_DIRECTORY64 *tls_dir
+ = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, tls_dir, sizeof(IMAGE_TLS_DIRECTORY64))) {
+ // TODO: Should we report something?
+ return 0;
+ }
+
+ if (! (tls_dir->AddressOfCallBacks
+ & optional_hdr->_64->ImageBase)) {
+ break;
+ }
+
+ ofs = pe_rva2ofs(ctx, tls_dir->AddressOfCallBacks
+ - optional_hdr->_64->ImageBase);
+ break;
+ }
+ }
+
+ ret = -1; // tls directory and section exists
+
+ uint32_t funcaddr = 0;
+
+ // FIXME: Why this loop if 'funcaddr' isn't updated?
+ do {
+ const uint32_t *funcaddr_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ if (! pe_can_read(ctx, funcaddr_ptr, sizeof(*funcaddr_ptr))) {
+ // TODO: Should we report something?
+ return 0;
+ }
+
+ // FIXME: This funcaddr is declared in block scope!
+ // TODO: Removed block scoped - crashes?
+ funcaddr = *funcaddr_ptr;
+ if (funcaddr) {
+ ret = ++j; // function found
+ }
+ } while (funcaddr);
+ }
+
+ return ret;
}
-int pe_get_tls_callback(pe_ctx_t *ctx) {
- const int callbacks = count_tls_callbacks(ctx);
- int ret = 0;
+int pe_get_tls_callback(pe_ctx_t *ctx)
+{
+ const int callbacks = count_tls_callbacks(ctx);
+ int ret = 0;
- if (callbacks == 0)
- ret = LIBPE_E_NO_CALLBACKS_FOUND; // not found
- else if (callbacks == -1) // FIXME: Is this correct?
- ret = LIBPE_E_NO_FUNCTIONS_FOUND; // found no functions
- else if (callbacks > 0)
- ret = callbacks;
+ if (callbacks == 0) {
+ ret = LIBPE_E_NO_CALLBACKS_FOUND; // not found
+ } else if (callbacks == -1) { // FIXME: Is this correct?
+ ret = LIBPE_E_NO_FUNCTIONS_FOUND; // found no functions
+ } else if (callbacks > 0) {
+ ret = callbacks;
+ }
- return ret;
+ return ret;
}
+
diff --git a/lib/libpe/pe.c b/lib/libpe/pe.c
index 172e95cc..a1396a03 100644
--- a/lib/libpe/pe.c
+++ b/lib/libpe/pe.c
@@ -1,7 +1,7 @@
/*
libpe - the PE library
- Copyright (C) 2010 - 2017 libpe authors
+ Copyright (C) 2010 - 2026 libpe authors
This file is part of libpe.
@@ -21,879 +21,1052 @@
#include "libpe/pe.h"
+#include "compat.h"
+#include "libpe/macros.h"
+#include "libpe/resources.h"
+#include "libpe/sections.h"
+
+#include
#include
#include
+#include
+#include
#include
#include
#include
#include
#include
+#include
+
+#ifndef _SLURP_PE_FILE
#include
#include
-#include
-#include
-#include
+#endif
+
+bool pe_can_read(const pe_ctx_t *ctx, const void *ptr, size_t size)
+{
+ const uintptr_t start = (uintptr_t) ptr;
+ const uintptr_t end = start + size;
+ return start >= (uintptr_t) ctx->map_addr
+ && end <= (uintptr_t) ctx->map_end;
+}
-bool pe_can_read(const pe_ctx_t *ctx, const void *ptr, size_t size) {
- const uintptr_t start = (uintptr_t)ptr;
- const uintptr_t end = start + size;
- return start >= (uintptr_t)ctx->map_addr && end <= (uintptr_t)ctx->map_end;
-}
-
-pe_err_e pe_load_file(pe_ctx_t *ctx, const char *path) {
- return pe_load_file_ext(ctx, path, 0);
-}
-
-pe_err_e pe_load_file_ext(pe_ctx_t *ctx, const char *path, pe_options_e options) {
- // Cleanup the whole struct.
- memset(ctx, 0, sizeof(pe_ctx_t));
-
- ctx->path = strdup(path);
- if (ctx->path == NULL) {
- //perror("strdup");
- return LIBPE_E_ALLOCATION_FAILURE;
- }
-
- // Open the file.
- int oflag = options & LIBPE_OPT_OPEN_RW ? O_RDWR : O_RDONLY;
- const int fd = open(ctx->path, oflag);
- if (fd == -1) {
- //perror("open");
- return LIBPE_E_OPEN_FAILED;
- }
-
- int ret = 0;
-
- // Stat the fd to retrieve the file informations.
- // If file is a symlink, fstat will stat the pointed file, not the link.
- struct stat stat;
- ret = fstat(fd, &stat);
- if (ret == -1) {
- close(fd);
- //perror("fstat");
- return LIBPE_E_FSTAT_FAILED;
- }
-
- // Check if we're dealing with a regular file.
- if (!S_ISREG(stat.st_mode)) {
- close(fd);
- //fprintf(stderr, "%s is not a file\n", ctx->path);
- return LIBPE_E_NOT_A_FILE;
- }
-
- // Grab the file size.
- ctx->map_size = stat.st_size;
-
- // Create the virtual memory mapping.
- int mprot = options & LIBPE_OPT_OPEN_RW ? PROT_READ|PROT_WRITE /* Pages may be written */ : PROT_READ;
- // MAP_SHARED makes updates to the mapping visible to other processes that map this file.
- // The file may not actually be updated until msync(2) or munmap() is called.
- int mflags = options & LIBPE_OPT_OPEN_RW ? MAP_SHARED : MAP_PRIVATE;
- ctx->map_addr = mmap(NULL, ctx->map_size, mprot, mflags, fd, 0);
- if (ctx->map_addr == MAP_FAILED) {
- close(fd);
- //perror("mmap");
- return LIBPE_E_MMAP_FAILED;
- }
-
- ctx->map_end = (uintptr_t)LIBPE_PTR_ADD(ctx->map_addr, ctx->map_size);
-
- if (options & LIBPE_OPT_NOCLOSE_FD) {
- // The file descriptor is not dup'ed, and will be closed when the stream created by fdopen() is closed.
- FILE *fp = fdopen(fd, options & LIBPE_OPT_OPEN_RW ? "r+b" : "rb"); // NOTE: 'b' is ignored on all POSIX conforming systems.
- if (fp == NULL) {
- //perror("fdopen");
- return LIBPE_E_FDOPEN_FAILED;
- }
- ctx->stream = fp;
- } else {
- // We can now close the fd.
- ret = close(fd);
- if (ret == -1) {
- //perror("close");
- return LIBPE_E_CLOSE_FAILED;
- }
- }
-
- // Give advice about how we'll use our memory mapping.
- ret = madvise(ctx->map_addr, ctx->map_size, MADV_SEQUENTIAL);
- if (ret < 0) {
- //perror("madvise");
- // NOTE: This is a recoverable error. Do not abort.
- }
-
- OpenSSL_add_all_digests();
-
- return LIBPE_E_OK;
-}
-
-static void cleanup_cached_data(pe_ctx_t *ctx) {
- pe_imports_dealloc(ctx->cached_data.imports);
- pe_exports_dealloc(ctx->cached_data.exports);
- pe_hash_headers_dealloc(ctx->cached_data.hash_headers);
- pe_hash_sections_dealloc(ctx->cached_data.hash_sections);
- pe_hash_dealloc(ctx->cached_data.hash_file);
- pe_resources_dealloc(ctx->cached_data.resources);
- memset(&ctx->cached_data, 0, sizeof(pe_cached_data_t));
-}
-
-pe_err_e pe_unload(pe_ctx_t *ctx) {
- if (ctx->stream != NULL) {
- fclose(ctx->stream);
- }
-
- free(ctx->path);
-
- // Dealloc internal pointers.
- free(ctx->pe.directories);
- free(ctx->pe.sections);
-
- cleanup_cached_data(ctx);
-
- // Dealloc the virtual mapping.
- if (ctx->map_addr != NULL) {
- int ret = munmap(ctx->map_addr, ctx->map_size);
- if (ret != 0) {
- //perror("munmap");
- return LIBPE_E_MUNMAP_FAILED;
- }
- }
-
- CRYPTO_cleanup_all_ex_data();
- EVP_cleanup(); // Clean OpenSSL_add_all_digests.
-
- // Cleanup the whole struct.
- memset(ctx, 0, sizeof(pe_ctx_t));
-
- return LIBPE_E_OK;
-}
-
-pe_err_e pe_parse(pe_ctx_t *ctx) {
- ctx->pe.dos_hdr = ctx->map_addr;
- if (ctx->pe.dos_hdr->e_magic == MAGIC_MZ) {
- const uint32_t *signature_ptr = LIBPE_PTR_ADD(ctx->pe.dos_hdr,
- ctx->pe.dos_hdr->e_lfanew);
- if (!pe_can_read(ctx, signature_ptr, LIBPE_SIZEOF_MEMBER(pe_file_t, signature)))
- return LIBPE_E_INVALID_LFANEW;
-
- // NT signature (PE\0\0)
- ctx->pe.signature = *signature_ptr;
-
- switch (ctx->pe.signature) {
- default:
- //fprintf(stderr, "Invalid signature: %x\n", ctx->pe.signature);
- return LIBPE_E_INVALID_SIGNATURE;
- case SIGNATURE_PE:
- case SIGNATURE_PL:
- case SIGNATURE_PX:
- break;
- }
-
- ctx->pe.coff_hdr = LIBPE_PTR_ADD(signature_ptr,
- LIBPE_SIZEOF_MEMBER(pe_file_t, signature));
-
- } else if (pe_machine_type_name(ctx->pe.dos_hdr->e_magic) != NULL) {
- ctx->pe.coff_hdr = (void *)ctx->pe.dos_hdr;
- ctx->pe.dos_hdr = NULL;
- } else {
- return LIBPE_E_NOT_A_PE_FILE;
- }
-
- if (!pe_can_read(ctx, ctx->pe.coff_hdr, sizeof(IMAGE_COFF_HEADER)))
- return LIBPE_E_MISSING_COFF_HEADER;
-
- ctx->pe.num_sections = ctx->pe.coff_hdr->NumberOfSections;
-
- if (ctx->pe.coff_hdr->SizeOfOptionalHeader > 0) {
-
- // Optional header points right after the COFF header.
- ctx->pe.optional_hdr_ptr = LIBPE_PTR_ADD(ctx->pe.coff_hdr,
- sizeof(IMAGE_COFF_HEADER));
-
- // Figure out whether it's a PE32 or PE32+.
- uint16_t *opt_type_ptr = ctx->pe.optional_hdr_ptr;
- if (!pe_can_read(ctx, opt_type_ptr,
- LIBPE_SIZEOF_MEMBER(IMAGE_OPTIONAL_HEADER, type)))
- return LIBPE_E_MISSING_OPTIONAL_HEADER;
-
- ctx->pe.optional_hdr.type = *opt_type_ptr;
-
- switch (ctx->pe.optional_hdr.type) {
- default:
- return LIBPE_E_UNSUPPORTED_IMAGE;
- case MAGIC_ROM:
- if (ctx->pe.coff_hdr->SizeOfOptionalHeader !=
- sizeof(IMAGE_ROM_OPTIONAL_HEADER))
- return LIBPE_E_UNSUPPORTED_IMAGE;
- if (!pe_can_read(ctx, ctx->pe.optional_hdr_ptr,
- sizeof(IMAGE_ROM_OPTIONAL_HEADER)))
- return LIBPE_E_MISSING_OPTIONAL_HEADER;
- ctx->pe.optional_hdr._rom = ctx->pe.optional_hdr_ptr;
- ctx->pe.optional_hdr.length = sizeof(IMAGE_ROM_OPTIONAL_HEADER);
- ctx->pe.entrypoint = ctx->pe.optional_hdr._rom->AddressOfEntryPoint;
- break;
- case MAGIC_PE32_0:
- case MAGIC_PE32:
- if (ctx->pe.coff_hdr->SizeOfOptionalHeader <
- sizeof(IMAGE_OPTIONAL_HEADER_32))
- return LIBPE_E_UNSUPPORTED_IMAGE;
- if (!pe_can_read(ctx, ctx->pe.optional_hdr_ptr,
- sizeof(IMAGE_OPTIONAL_HEADER_32)))
- return LIBPE_E_MISSING_OPTIONAL_HEADER;
- ctx->pe.optional_hdr._32 = ctx->pe.optional_hdr_ptr;
- ctx->pe.optional_hdr.length = sizeof(IMAGE_OPTIONAL_HEADER_32);
- ctx->pe.num_directories =
- ctx->pe.optional_hdr._32->NumberOfRvaAndSizes;
- ctx->pe.entrypoint = ctx->pe.optional_hdr._32->AddressOfEntryPoint;
- ctx->pe.imagebase = ctx->pe.optional_hdr._32->ImageBase;
- break;
- case MAGIC_PE64:
- if (ctx->pe.coff_hdr->SizeOfOptionalHeader <
- sizeof(IMAGE_OPTIONAL_HEADER_64))
- return LIBPE_E_UNSUPPORTED_IMAGE;
- if (!pe_can_read(ctx, ctx->pe.optional_hdr_ptr,
- sizeof(IMAGE_OPTIONAL_HEADER_64)))
- return LIBPE_E_MISSING_OPTIONAL_HEADER;
- ctx->pe.optional_hdr._64 = ctx->pe.optional_hdr_ptr;
- ctx->pe.optional_hdr.length = sizeof(IMAGE_OPTIONAL_HEADER_64);
- ctx->pe.num_directories =
- ctx->pe.optional_hdr._64->NumberOfRvaAndSizes;
- ctx->pe.entrypoint = ctx->pe.optional_hdr._64->AddressOfEntryPoint;
- ctx->pe.imagebase = ctx->pe.optional_hdr._64->ImageBase;
- break;
- }
-
- }
-
- if (ctx->pe.num_directories > MAX_DIRECTORIES) {
- //fprintf(stderr, "Too many directories (%u)\n", ctx->pe.num_directories);
- return LIBPE_E_TOO_MANY_DIRECTORIES;
- }
-
- if (ctx->pe.num_sections > MAX_SECTIONS) {
- //fprintf(stderr, "Too many sections (%u)\n", ctx->pe.num_sections);
- return LIBPE_E_TOO_MANY_SECTIONS;
- }
-
- if (ctx->pe.optional_hdr_ptr)
- ctx->pe.directories_ptr = LIBPE_PTR_ADD(ctx->pe.optional_hdr_ptr,
- ctx->pe.optional_hdr.length);
-
- uint32_t sections_offset = sizeof(IMAGE_FILE_HEADER)
- + (uint32_t)ctx->pe.coff_hdr->SizeOfOptionalHeader;
- ctx->pe.sections_ptr = LIBPE_PTR_ADD(ctx->pe.coff_hdr, sections_offset);
-
- if (ctx->pe.num_directories > 0) {
- ctx->pe.directories = malloc(ctx->pe.num_directories
- * sizeof(IMAGE_DATA_DIRECTORY *));
- if (ctx->pe.directories == NULL)
- return LIBPE_E_ALLOCATION_FAILURE;
- for (uint32_t i = 0; i < ctx->pe.num_directories; i++) {
- ctx->pe.directories[i] = LIBPE_PTR_ADD(ctx->pe.directories_ptr,
- i * sizeof(IMAGE_DATA_DIRECTORY));
- }
- } else {
- ctx->pe.directories_ptr = NULL;
- }
-
- if (ctx->pe.num_sections > 0) {
- ctx->pe.sections = malloc(ctx->pe.num_sections
- * sizeof(IMAGE_SECTION_HEADER *));
- if (ctx->pe.sections == NULL)
- return LIBPE_E_ALLOCATION_FAILURE;
- for (uint32_t i = 0; i < ctx->pe.num_sections; i++) {
- ctx->pe.sections[i] = LIBPE_PTR_ADD(ctx->pe.sections_ptr,
- i * sizeof(IMAGE_SECTION_HEADER));
- }
- } else {
- ctx->pe.sections_ptr = NULL;
- }
-
- if (ctx->pe.coff_hdr->PointerToSymbolTable != 0) {
- uint32_t symbols_offset = ctx->pe.coff_hdr->PointerToSymbolTable;
- if (symbols_offset < ctx->map_size) {
- ctx->pe.symbols_ptr = LIBPE_PTR_ADD(ctx->map_addr, symbols_offset);
- ctx->pe.num_symbols = ctx->pe.coff_hdr->NumberOfSymbols;
- if (symbols_offset + ctx->pe.num_symbols * 18 < ctx->map_size) {
- ctx->pe.strings_ptr = LIBPE_PTR_ADD(ctx->pe.symbols_ptr, ctx->pe.num_symbols * 18);
- ctx->pe.strings_size = *(uint32_t *)ctx->pe.strings_ptr;
- if (ctx->pe.strings_size < 4 ||
- symbols_offset + ctx->pe.num_symbols * 18 + ctx->pe.strings_size > ctx->map_size) {
- ctx->pe.strings_ptr = NULL;
- ctx->pe.strings_size = 0;
- }
- }
- if (ctx->pe.num_symbols == 0)
- ctx->pe.symbols_ptr = NULL;
- }
- }
-
- return LIBPE_E_OK;
-}
-
-bool pe_is_loaded(const pe_ctx_t *ctx) {
- return ctx->map_addr != NULL && ctx->map_size > 0;
-}
-
-bool pe_is_pe(const pe_ctx_t *ctx) {
- return pe_is_exec(ctx) || pe_is_obj(ctx) || pe_is_rom(ctx);
-}
-
-bool pe_is_exec(const pe_ctx_t *ctx) {
- // Check MZ header
- if (ctx->pe.dos_hdr == NULL || ctx->pe.dos_hdr->e_magic != MAGIC_MZ)
- return false;
-
- // Check PE signature
- if (ctx->pe.signature != SIGNATURE_PE && ctx->pe.signature != SIGNATURE_PL && ctx->pe.signature != SIGNATURE_PX)
- return false;
-
- return true;
-}
-
-bool pe_is_obj(const pe_ctx_t *ctx) {
- // Object file does not have neither MZ header nor PE\0\0 signature nor optional header
- if (ctx->pe.dos_hdr != NULL || ctx->pe.signature != 0 || ctx->pe.optional_hdr_ptr != NULL)
- return false;
-
- return true;
-}
-
-bool pe_is_rom(const pe_ctx_t *ctx) {
- // ROM file does not have neither MZ header nor PE\0\0 signature
- if (ctx->pe.dos_hdr != NULL || ctx->pe.signature != 0)
- return false;
-
- // ROM file has either MAGIC_ROM optional header (R3000, R4000, R10000, ALPHA) or MAGIC_PE32 optional header (I386, MPPC_601, POWERPC, ...)
- if (ctx->pe.optional_hdr_ptr == NULL || (ctx->pe.optional_hdr.type != MAGIC_ROM && ctx->pe.optional_hdr.type != MAGIC_PE32))
- return false;
-
- return true;
+pe_err_e pe_load_file(pe_ctx_t *ctx, const char *path)
+{
+ return pe_load_file_ext(ctx, path, 0);
}
-
-bool pe_is_dll(const pe_ctx_t *ctx) {
- if (!pe_is_exec(ctx))
- return false;
- if (ctx->pe.coff_hdr == NULL)
- return false;
- return ctx->pe.coff_hdr->Characteristics & IMAGE_FILE_DLL ? true : false;
+
+// TODO: Reevaluate the differences between these implementations
+// Load entire executable into memory instead of memory mapping
+#ifdef _SLURP_PE_FILE
+pe_err_e pe_load_file_ext(pe_ctx_t *ctx, const char *path, pe_options_e options)
+{
+ // Cleanup the whole struct.
+ memset(ctx, 0, sizeof(pe_ctx_t));
+
+ size_t length = strlen(path);
+ ctx->path = malloc(length);
+ strncpy(ctx->path, path, length);
+
+ if (ctx->path == NULL) {
+ return LIBPE_E_ALLOCATION_FAILURE;
+ }
+
+ // Open the file
+ FILE *fp = fopen(ctx->path, options & LIBPE_OPT_OPEN_RW ? "r+b" : "rb");
+ if (fp == NULL) {
+ return LIBPE_E_OPEN_FAILED;
+ }
+
+ // Grab the file size.
+ fseek(fp, 0L, SEEK_END);
+ long fsize = ftell(fp);
+ if (fsize == -1) {
+ fclose(fp);
+ return LIBPE_E_FSTAT_FAILED;
+ }
+ ctx->map_size = fsize;
+ ctx->map_addr = malloc(fsize);
+
+ if (ctx->map_addr == NULL) {
+ // perror("mmap");
+ fclose(fp);
+ return LIBPE_E_MMAP_FAILED;
+ }
+
+ ctx->map_end = (uintptr_t) LIBPE_PTR_ADD(ctx->map_addr, ctx->map_size);
+ fseek(fp, 0L, SEEK_SET);
+ size_t readsize = fread(ctx->map_addr, sizeof(char), ctx->map_size, fp);
+
+ if (readsize == 0) {
+ fclose(fp);
+ return LIBPE_E_MMAP_FAILED;
+ }
+
+ if (options & LIBPE_OPT_NOCLOSE_FD) {
+ ctx->stream = fp;
+ } else {
+ fclose(fp);
+ }
+
+ OpenSSL_add_all_digests();
+
+ return LIBPE_E_OK;
}
-uint64_t pe_filesize(const pe_ctx_t *ctx) {
- return ctx->map_size;
+#else
+pe_err_e pe_load_file_ext(pe_ctx_t *ctx, const char *path, pe_options_e options)
+{
+ // Cleanup the whole struct.
+ memset(ctx, 0, sizeof(pe_ctx_t));
+
+ ctx->path = readpe_strdup(path);
+ if (ctx->path == NULL) {
+ // perror("strdup");
+ return LIBPE_E_ALLOCATION_FAILURE;
+ }
+
+ // Open the file.
+ int oflag = options & LIBPE_OPT_OPEN_RW ? O_RDWR : O_RDONLY;
+ const int fd = open(ctx->path, oflag);
+ if (fd == -1) {
+ // perror("open");
+ return LIBPE_E_OPEN_FAILED;
+ }
+
+ int ret = 0;
+
+ // Stat the fd to retrieve the file informations.
+ // If file is a symlink, fstat will stat the pointed file, not the link.
+ struct stat stat;
+ ret = fstat(fd, &stat);
+ if (ret == -1) {
+ close(fd);
+ // perror("fstat");
+ return LIBPE_E_FSTAT_FAILED;
+ }
+
+ // Check if we're dealing with a regular file.
+ if (! S_ISREG(stat.st_mode)) {
+ close(fd);
+ // fprintf(stderr, "%s is not a file\n", ctx->path);
+ return LIBPE_E_NOT_A_FILE;
+ }
+
+ // Grab the file size.
+ ctx->map_size = (uintmax_t) stat.st_size;
+
+ // Create the virtual memory mapping.
+ int mprot = options & LIBPE_OPT_OPEN_RW
+ ? PROT_READ | PROT_WRITE /* Pages may be written */
+ : PROT_READ;
+ // MAP_SHARED makes updates to the mapping visible to other processes that
+ // map this file. The file may not actually be updated until msync(2) or
+ // munmap() is called.
+ int mflags = options & LIBPE_OPT_OPEN_RW ? MAP_SHARED : MAP_PRIVATE;
+ ctx->map_addr = mmap(NULL, (size_t) ctx->map_size, mprot, mflags, fd, 0);
+ if (ctx->map_addr == MAP_FAILED) {
+ close(fd);
+ // perror("mmap");
+ return LIBPE_E_MMAP_FAILED;
+ }
+
+ ctx->map_end = (uintptr_t) LIBPE_PTR_ADD(ctx->map_addr, ctx->map_size);
+
+ if (options & LIBPE_OPT_NOCLOSE_FD) {
+ // The file descriptor is not dup'ed, and will be closed when the stream
+ // created by fdopen() is closed.
+ FILE *fp = fdopen(fd, options & LIBPE_OPT_OPEN_RW
+ ? "r+b"
+ : "rb"); // NOTE: 'b' is ignored on all POSIX
+ // conforming systems.
+ if (fp == NULL) {
+ // perror("fdopen");
+ return LIBPE_E_FDOPEN_FAILED;
+ }
+ ctx->stream = fp;
+ } else {
+ // We can now close the fd.
+ ret = close(fd);
+ if (ret == -1) {
+ // perror("close");
+ return LIBPE_E_CLOSE_FAILED;
+ }
+ }
+
+ // Give advice about how we'll use our memory mapping.
+ ret = madvise(ctx->map_addr, (size_t) ctx->map_size, MADV_SEQUENTIAL);
+ if (ret < 0) {
+ // perror("madvise");
+ // NOTE: This is a recoverable error. Do not abort.
+ }
+
+ OpenSSL_add_all_digests();
+
+ return LIBPE_E_OK;
+}
+#endif
+
+static void cleanup_cached_data(pe_ctx_t *ctx)
+{
+ pe_imports_dealloc(ctx->cached_data.imports);
+ pe_exports_dealloc(ctx->cached_data.exports);
+ pe_hash_headers_dealloc(ctx->cached_data.hash_headers);
+ pe_hash_sections_dealloc(ctx->cached_data.hash_sections);
+ pe_hash_dealloc(ctx->cached_data.hash_file);
+ pe_resources_dealloc(ctx->cached_data.resources);
+ memset(&ctx->cached_data, 0, sizeof(pe_cached_data_t));
}
-// return the section of given rva
-IMAGE_SECTION_HEADER *pe_rva2section(pe_ctx_t *ctx, uint64_t rva) {
- if (rva == 0 || ctx->pe.sections == NULL)
- return NULL;
+pe_err_e pe_unload(pe_ctx_t *ctx)
+{
+ if (ctx->stream != NULL) {
+ fclose(ctx->stream);
+ }
+
+ free(ctx->path);
+
+ // Dealloc internal pointers.
+ free(ctx->pe.directories);
+ free(ctx->pe.sections);
+
+ cleanup_cached_data(ctx);
+
+ // Dealloc the virtual mapping.
+ if (ctx->map_addr != NULL) {
+#ifdef _MSC_VER
+ free(ctx->map_addr);
+#else
+ int ret = munmap(ctx->map_addr, (size_t) ctx->map_size);
+ if (ret != 0) {
+ // perror("munmap");
+ return LIBPE_E_MUNMAP_FAILED;
+ }
+#endif
+ }
- for (uint32_t i=0; i < ctx->pe.num_sections; i++) {
- const uint64_t start = ctx->pe.sections[i]->VirtualAddress;
- const uint64_t end = ctx->pe.sections[i]->VirtualAddress + ctx->pe.sections[i]->Misc.VirtualSize;
- if (rva >= start && rva <= end)
- return ctx->pe.sections[i];
- }
- return NULL;
+ CRYPTO_cleanup_all_ex_data();
+ EVP_cleanup(); // Clean OpenSSL_add_all_digests.
+
+ // Cleanup the whole struct.
+ memset(ctx, 0, sizeof(pe_ctx_t));
+
+ return LIBPE_E_OK;
+}
+
+pe_err_e pe_parse(pe_ctx_t *ctx)
+{
+ ctx->pe.dos_hdr = ctx->map_addr;
+ if (ctx->pe.dos_hdr->e_magic == MAGIC_MZ) {
+ const uint32_t *signature_ptr
+ = LIBPE_PTR_ADD(ctx->pe.dos_hdr, ctx->pe.dos_hdr->e_lfanew);
+ if (! pe_can_read(ctx, signature_ptr,
+ LIBPE_SIZEOF_MEMBER(pe_file_t, signature))) {
+ return LIBPE_E_INVALID_LFANEW;
+ }
+
+ // NT signature (PE\0\0)
+ ctx->pe.signature = *signature_ptr;
+
+ switch (ctx->pe.signature) {
+ default:
+ // fprintf(stderr, "Invalid signature: %x\n", ctx->pe.signature);
+ return LIBPE_E_INVALID_SIGNATURE;
+ case SIGNATURE_PE:
+ case SIGNATURE_PL:
+ case SIGNATURE_PX:
+ break;
+ }
+
+ ctx->pe.coff_hdr = LIBPE_PTR_ADD(
+ signature_ptr, LIBPE_SIZEOF_MEMBER(pe_file_t, signature));
+
+ } else if (pe_machine_type_name(ctx->pe.dos_hdr->e_magic) != NULL) {
+ ctx->pe.coff_hdr = (void *) ctx->pe.dos_hdr;
+ ctx->pe.dos_hdr = NULL;
+ } else {
+ return LIBPE_E_NOT_A_PE_FILE;
+ }
+
+ if (! pe_can_read(ctx, ctx->pe.coff_hdr, sizeof(IMAGE_COFF_HEADER))) {
+ return LIBPE_E_MISSING_COFF_HEADER;
+ }
+
+ ctx->pe.num_sections = ctx->pe.coff_hdr->NumberOfSections;
+
+ if (ctx->pe.coff_hdr->SizeOfOptionalHeader > 0) {
+
+ // Optional header points right after the COFF header.
+ ctx->pe.optional_hdr_ptr
+ = LIBPE_PTR_ADD(ctx->pe.coff_hdr, sizeof(IMAGE_COFF_HEADER));
+
+ // Figure out whether it's a PE32 or PE32+.
+ uint16_t *opt_type_ptr = ctx->pe.optional_hdr_ptr;
+ if (! pe_can_read(ctx, opt_type_ptr,
+ LIBPE_SIZEOF_MEMBER(IMAGE_OPTIONAL_HEADER, type))) {
+ return LIBPE_E_MISSING_OPTIONAL_HEADER;
+ }
+
+ ctx->pe.optional_hdr.type = *opt_type_ptr;
+
+ switch (ctx->pe.optional_hdr.type) {
+ default:
+ return LIBPE_E_UNSUPPORTED_IMAGE;
+ case MAGIC_ROM:
+ if (ctx->pe.coff_hdr->SizeOfOptionalHeader
+ != sizeof(IMAGE_ROM_OPTIONAL_HEADER)) {
+ return LIBPE_E_UNSUPPORTED_IMAGE;
+ }
+ if (! pe_can_read(ctx, ctx->pe.optional_hdr_ptr,
+ sizeof(IMAGE_ROM_OPTIONAL_HEADER))) {
+ return LIBPE_E_MISSING_OPTIONAL_HEADER;
+ }
+ ctx->pe.optional_hdr._rom = ctx->pe.optional_hdr_ptr;
+ ctx->pe.optional_hdr.length = sizeof(IMAGE_ROM_OPTIONAL_HEADER);
+ ctx->pe.entrypoint = ctx->pe.optional_hdr._rom->AddressOfEntryPoint;
+ break;
+ case MAGIC_PE32_0:
+ case MAGIC_PE32:
+ if (ctx->pe.coff_hdr->SizeOfOptionalHeader
+ < sizeof(IMAGE_OPTIONAL_HEADER_32)) {
+ return LIBPE_E_UNSUPPORTED_IMAGE;
+ }
+ if (! pe_can_read(ctx, ctx->pe.optional_hdr_ptr,
+ sizeof(IMAGE_OPTIONAL_HEADER_32))) {
+ return LIBPE_E_MISSING_OPTIONAL_HEADER;
+ }
+ ctx->pe.optional_hdr._32 = ctx->pe.optional_hdr_ptr;
+ ctx->pe.optional_hdr.length = sizeof(IMAGE_OPTIONAL_HEADER_32);
+ ctx->pe.num_directories
+ = ctx->pe.optional_hdr._32->NumberOfRvaAndSizes;
+ ctx->pe.entrypoint = ctx->pe.optional_hdr._32->AddressOfEntryPoint;
+ ctx->pe.imagebase = ctx->pe.optional_hdr._32->ImageBase;
+ break;
+ case MAGIC_PE64:
+ if (ctx->pe.coff_hdr->SizeOfOptionalHeader
+ < sizeof(IMAGE_OPTIONAL_HEADER_64)) {
+ return LIBPE_E_UNSUPPORTED_IMAGE;
+ }
+ if (! pe_can_read(ctx, ctx->pe.optional_hdr_ptr,
+ sizeof(IMAGE_OPTIONAL_HEADER_64))) {
+ return LIBPE_E_MISSING_OPTIONAL_HEADER;
+ }
+ ctx->pe.optional_hdr._64 = ctx->pe.optional_hdr_ptr;
+ ctx->pe.optional_hdr.length = sizeof(IMAGE_OPTIONAL_HEADER_64);
+ ctx->pe.num_directories
+ = ctx->pe.optional_hdr._64->NumberOfRvaAndSizes;
+ ctx->pe.entrypoint = ctx->pe.optional_hdr._64->AddressOfEntryPoint;
+ ctx->pe.imagebase = ctx->pe.optional_hdr._64->ImageBase;
+ break;
+ }
+ }
+
+ if (ctx->pe.num_directories > MAX_DIRECTORIES) {
+ // fprintf(stderr, "Too many directories (%u)\n",
+ // ctx->pe.num_directories);
+ return LIBPE_E_TOO_MANY_DIRECTORIES;
+ }
+
+ if (ctx->pe.num_sections > MAX_SECTIONS) {
+ // fprintf(stderr, "Too many sections (%u)\n", ctx->pe.num_sections);
+ return LIBPE_E_TOO_MANY_SECTIONS;
+ }
+
+ if (ctx->pe.optional_hdr_ptr) {
+ ctx->pe.directories_ptr = LIBPE_PTR_ADD(ctx->pe.optional_hdr_ptr,
+ ctx->pe.optional_hdr.length);
+ }
+
+ uint32_t sections_offset
+ = sizeof(IMAGE_FILE_HEADER)
+ + (uint32_t) ctx->pe.coff_hdr->SizeOfOptionalHeader;
+ ctx->pe.sections_ptr = LIBPE_PTR_ADD(ctx->pe.coff_hdr, sections_offset);
+
+ if (ctx->pe.num_directories > 0) {
+ ctx->pe.directories
+ = malloc(ctx->pe.num_directories * sizeof(IMAGE_DATA_DIRECTORY *));
+ if (ctx->pe.directories == NULL) {
+ return LIBPE_E_ALLOCATION_FAILURE;
+ }
+ for (uint32_t i = 0; i < ctx->pe.num_directories; i++) {
+ ctx->pe.directories[i] = LIBPE_PTR_ADD(
+ ctx->pe.directories_ptr, i * sizeof(IMAGE_DATA_DIRECTORY));
+ }
+ } else {
+ ctx->pe.directories_ptr = NULL;
+ }
+
+ if (ctx->pe.num_sections > 0) {
+ ctx->pe.sections
+ = malloc(ctx->pe.num_sections * sizeof(IMAGE_SECTION_HEADER *));
+ if (ctx->pe.sections == NULL) {
+ return LIBPE_E_ALLOCATION_FAILURE;
+ }
+ for (uint32_t i = 0; i < ctx->pe.num_sections; i++) {
+ ctx->pe.sections[i] = LIBPE_PTR_ADD(
+ ctx->pe.sections_ptr, i * sizeof(IMAGE_SECTION_HEADER));
+ }
+ } else {
+ ctx->pe.sections_ptr = NULL;
+ }
+
+ if (ctx->pe.coff_hdr->PointerToSymbolTable != 0) {
+ uint32_t symbols_offset = ctx->pe.coff_hdr->PointerToSymbolTable;
+ if (symbols_offset < ctx->map_size) {
+ ctx->pe.symbols_ptr = LIBPE_PTR_ADD(ctx->map_addr, symbols_offset);
+ ctx->pe.num_symbols = ctx->pe.coff_hdr->NumberOfSymbols;
+ if (symbols_offset + ctx->pe.num_symbols * 18 < ctx->map_size) {
+ ctx->pe.strings_ptr = LIBPE_PTR_ADD(ctx->pe.symbols_ptr,
+ ctx->pe.num_symbols * 18);
+ ctx->pe.strings_size = *(uint32_t *) ctx->pe.strings_ptr;
+ if (ctx->pe.strings_size < 4
+ || symbols_offset + ctx->pe.num_symbols * 18
+ + ctx->pe.strings_size
+ > ctx->map_size) {
+ ctx->pe.strings_ptr = NULL;
+ ctx->pe.strings_size = 0;
+ }
+ }
+ if (ctx->pe.num_symbols == 0) {
+ ctx->pe.symbols_ptr = NULL;
+ }
+ }
+ }
+
+ return LIBPE_E_OK;
+}
+
+bool pe_is_loaded(const pe_ctx_t *ctx)
+{
+ return ctx->map_addr != NULL && ctx->map_size > 0;
+}
+
+bool pe_is_pe(const pe_ctx_t *ctx)
+{
+ return pe_is_exec(ctx) || pe_is_obj(ctx) || pe_is_rom(ctx);
+}
+
+bool pe_is_exec(const pe_ctx_t *ctx)
+{
+ // Check MZ header
+ if (ctx->pe.dos_hdr == NULL || ctx->pe.dos_hdr->e_magic != MAGIC_MZ) {
+ return false;
+ }
+
+ // Check PE signature
+ if (ctx->pe.signature != SIGNATURE_PE && ctx->pe.signature != SIGNATURE_PL
+ && ctx->pe.signature != SIGNATURE_PX) {
+ return false;
+ }
+
+ return true;
+}
+
+bool pe_is_obj(const pe_ctx_t *ctx)
+{
+ // Object file does not have neither MZ header nor PE\0\0 signature nor
+ // optional header
+ if (ctx->pe.dos_hdr != NULL || ctx->pe.signature != 0
+ || ctx->pe.optional_hdr_ptr != NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+bool pe_is_rom(const pe_ctx_t *ctx)
+{
+ // ROM file does not have neither MZ header nor PE\0\0 signature
+ if (ctx->pe.dos_hdr != NULL || ctx->pe.signature != 0) {
+ return false;
+ }
+
+ // ROM file has either MAGIC_ROM optional header (R3000, R4000, R10000,
+ // ALPHA) or MAGIC_PE32 optional header (I386, MPPC_601, POWERPC, ...)
+ if (ctx->pe.optional_hdr_ptr == NULL
+ || (ctx->pe.optional_hdr.type != MAGIC_ROM
+ && ctx->pe.optional_hdr.type != MAGIC_PE32)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool pe_is_dll(const pe_ctx_t *ctx)
+{
+ if (! pe_is_exec(ctx)) {
+ return false;
+ }
+ if (ctx->pe.coff_hdr == NULL) {
+ return false;
+ }
+ return ctx->pe.coff_hdr->Characteristics & IMAGE_FILE_DLL ? true : false;
+}
+
+uint64_t pe_filesize(const pe_ctx_t *ctx) { return (uint64_t) ctx->map_size; }
+
+// return the section of given rva
+IMAGE_SECTION_HEADER *pe_rva2section(pe_ctx_t *ctx, uint64_t rva)
+{
+ if (rva == 0 || ctx->pe.sections == NULL) {
+ return NULL;
+ }
+
+ for (uint32_t i = 0; i < ctx->pe.num_sections; i++) {
+ const uint64_t start = ctx->pe.sections[i]->VirtualAddress;
+ const uint64_t end = ctx->pe.sections[i]->VirtualAddress
+ + ctx->pe.sections[i]->Misc.VirtualSize;
+ if (rva >= start && rva <= end) {
+ return ctx->pe.sections[i];
+ }
+ }
+ return NULL;
}
// Converts a RVA (Relative Virtual Address) to a raw file offset
-uint64_t pe_rva2ofs(const pe_ctx_t *ctx, uint64_t rva) {
- if (rva == 0)
- return 0;
-
- if (ctx->pe.sections == NULL)
- return rva;
-
- // Find out which section the given RVA belongs
- for (uint32_t i=0; i < ctx->pe.num_sections; i++) {
- if (ctx->pe.sections[i] == NULL)
- return 0;
-
- // Use SizeOfRawData if VirtualSize == 0
- size_t section_size = ctx->pe.sections[i]->Misc.VirtualSize;
- if (section_size == 0)
- section_size = ctx->pe.sections[i]->SizeOfRawData;
-
- if (ctx->pe.sections[i]->VirtualAddress <= rva) {
- if ((ctx->pe.sections[i]->VirtualAddress + section_size) > rva) {
- rva -= ctx->pe.sections[i]->VirtualAddress;
- rva += ctx->pe.sections[i]->PointerToRawData;
- return rva;
- }
- }
- }
-
- // Handle PE with a single section
- if (ctx->pe.num_sections == 1) {
- rva -= ctx->pe.sections[0]->VirtualAddress;
- rva += ctx->pe.sections[0]->PointerToRawData;
- return rva;
- }
-
- return rva; // PE with no sections, return RVA
+uint64_t pe_rva2ofs(const pe_ctx_t *ctx, uint64_t rva)
+{
+ if (rva == 0) {
+ return 0;
+ }
+
+ if (ctx->pe.sections == NULL) {
+ return rva;
+ }
+
+ // Find out which section the given RVA belongs
+ for (uint32_t i = 0; i < ctx->pe.num_sections; i++) {
+ if (ctx->pe.sections[i] == NULL) {
+ return 0;
+ }
+
+ // Use SizeOfRawData if VirtualSize == 0
+ size_t section_size = ctx->pe.sections[i]->Misc.VirtualSize;
+ if (section_size == 0) {
+ section_size = ctx->pe.sections[i]->SizeOfRawData;
+ }
+
+ if (ctx->pe.sections[i]->VirtualAddress <= rva) {
+ if ((ctx->pe.sections[i]->VirtualAddress + section_size) > rva) {
+ rva -= ctx->pe.sections[i]->VirtualAddress;
+ rva += ctx->pe.sections[i]->PointerToRawData;
+ return rva;
+ }
+ }
+ }
+
+ // Handle PE with a single section
+ if (ctx->pe.num_sections == 1) {
+ rva -= ctx->pe.sections[0]->VirtualAddress;
+ rva += ctx->pe.sections[0]->PointerToRawData;
+ return rva;
+ }
+
+ return rva; // PE with no sections, return RVA
}
// Returns the RVA for a given offset
-uint64_t pe_ofs2rva(const pe_ctx_t *ctx, uint64_t ofs) {
- if (ofs == 0 || ctx->pe.sections == NULL)
- return 0;
+uint64_t pe_ofs2rva(const pe_ctx_t *ctx, uint64_t ofs)
+{
+ if (ofs == 0 || ctx->pe.sections == NULL) {
+ return 0;
+ }
+
+ for (uint32_t i = 0; i < ctx->pe.num_sections; i++) {
+ if (ctx->pe.sections[i] == NULL) {
+ return 0;
+ }
+
+ if (ctx->pe.sections[i]->PointerToRawData <= ofs) {
+ if ((ctx->pe.sections[i]->PointerToRawData
+ + ctx->pe.sections[i]->SizeOfRawData)
+ > ofs) {
+ ofs -= ctx->pe.sections[i]->PointerToRawData;
+ ofs += ctx->pe.sections[i]->VirtualAddress;
+ return ofs;
+ }
+ }
+ }
+ return 0;
+}
+
+IMAGE_DOS_HEADER *pe_dos(pe_ctx_t *ctx) { return ctx->pe.dos_hdr; }
- for (uint32_t i=0; i < ctx->pe.num_sections; i++) {
- if (ctx->pe.sections[i] == NULL)
- return 0;
+IMAGE_COFF_HEADER *pe_coff(pe_ctx_t *ctx) { return ctx->pe.coff_hdr; }
- if (ctx->pe.sections[i]->PointerToRawData <= ofs) {
- if ((ctx->pe.sections[i]->PointerToRawData +
- ctx->pe.sections[i]->SizeOfRawData) > ofs) {
- ofs -= ctx->pe.sections[i]->PointerToRawData;
- ofs += ctx->pe.sections[i]->VirtualAddress;
- return ofs;
- }
- }
- }
- return 0;
-}
-
-IMAGE_DOS_HEADER *pe_dos(pe_ctx_t *ctx) {
- return ctx->pe.dos_hdr;
-}
-
-IMAGE_COFF_HEADER *pe_coff(pe_ctx_t *ctx) {
- return ctx->pe.coff_hdr;
-}
-
-IMAGE_OPTIONAL_HEADER *pe_optional(pe_ctx_t *ctx) {
- if (ctx->pe.optional_hdr_ptr == NULL)
- return NULL;
- return &ctx->pe.optional_hdr;
-}
-
-uint32_t pe_directories_count(const pe_ctx_t *ctx) {
- return ctx->pe.num_directories;
-}
-
-IMAGE_DATA_DIRECTORY **pe_directories(pe_ctx_t *ctx) {
- return ctx->pe.directories;
-}
-
-IMAGE_DATA_DIRECTORY *pe_directory_by_entry(pe_ctx_t *ctx, ImageDirectoryEntry entry) {
- if (ctx->pe.directories == NULL || entry > ctx->pe.num_directories - 1)
- return NULL;
-
- return ctx->pe.directories[entry];
-}
-
-uint16_t pe_sections_count(const pe_ctx_t *ctx) {
- return ctx->pe.num_sections;
-}
-
-IMAGE_SECTION_HEADER **pe_sections(pe_ctx_t *ctx) {
- return ctx->pe.sections;
-}
-
-IMAGE_SECTION_HEADER *pe_section_by_name(pe_ctx_t *ctx, const char *name) {
- if (ctx->pe.sections == NULL || name == NULL)
- return NULL;
-
- for (uint32_t i=0; i < ctx->pe.num_sections; i++) {
- if (strncmp((const char *)ctx->pe.sections[i]->Name, name, SECTION_NAME_SIZE) == 0)
- return ctx->pe.sections[i];
- }
- return NULL;
-}
-
-const char *pe_section_name(const pe_ctx_t *ctx, const IMAGE_SECTION_HEADER *section_hdr, char *out_name, size_t out_name_size) {
- assert(ctx != NULL);
- assert(out_name_size >= SECTION_NAME_SIZE+1);
- strncpy(out_name, (const char *)section_hdr->Name, SECTION_NAME_SIZE);
- out_name[SECTION_NAME_SIZE] = '\0';
- if (out_name[0] == '/' && out_name[1] >= '0' && out_name[1] <= '9' && ctx->pe.strings_ptr) {
- char *endptr = NULL;
- long int offset = -1;
- errno = 0;
- offset = strtol(out_name+1, &endptr, 10);
- if (errno == 0 && *endptr == 0 && offset >= 0 && offset < ctx->pe.strings_size) {
- return ctx->pe.strings_ptr + offset;
- }
- }
- return out_name;
-}
-
-#define LIBPE_ENTRY(v) { v, # v }
-
-const char *pe_machine_type_name(MachineType type) {
- typedef struct {
- MachineType type;
- const char * const name;
- } MachineEntry;
-
- static const MachineEntry names[] = {
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_UNKNOWN),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_ALPHA_OLD),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_ALPHA),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_ALPHA64),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_AM33),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_AMD64),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARM),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARMV7),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARM64),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARM64EC),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARM64X),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_CEE),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_CEF),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_CHPE_X86),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_EBC),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_I386),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_I860),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_IA64),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_LOONGARCH32),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_LOONGARCH64),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_M32R),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_M68K),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_MIPS16),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_MIPSFPU),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_MIPSFPU16),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_MPPC_601),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_OMNI),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_PARISC),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_POWERPC),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_POWERPCFP),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_POWERPCBE),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_R3000),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_R3000_BE),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_R4000),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_R10000),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_RISCV32),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_RISCV64),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_RISCV128),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH3),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH3DSP),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH3E),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH4),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH5),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_TRICORE),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_TAHOE),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_THUMB),
- LIBPE_ENTRY(IMAGE_FILE_MACHINE_WCEMIPSV2)
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (type == names[i].type)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_image_characteristic_name(ImageCharacteristics characteristic) {
- typedef struct {
- ImageCharacteristics characteristic;
- const char * const name;
- } ImageCharacteristicsName;
-
- static const ImageCharacteristicsName names[] = {
- LIBPE_ENTRY(IMAGE_FILE_RELOCS_STRIPPED),
- LIBPE_ENTRY(IMAGE_FILE_EXECUTABLE_IMAGE),
- LIBPE_ENTRY(IMAGE_FILE_LINE_NUMS_STRIPPED),
- LIBPE_ENTRY(IMAGE_FILE_LOCAL_SYMS_STRIPPED),
- LIBPE_ENTRY(IMAGE_FILE_AGGRESSIVE_WS_TRIM),
- LIBPE_ENTRY(IMAGE_FILE_LARGE_ADDRESS_AWARE),
- LIBPE_ENTRY(IMAGE_FILE_16BIT_MACHINE),
- LIBPE_ENTRY(IMAGE_FILE_BYTES_REVERSED_LO),
- LIBPE_ENTRY(IMAGE_FILE_32BIT_MACHINE),
- LIBPE_ENTRY(IMAGE_FILE_DEBUG_STRIPPED),
- LIBPE_ENTRY(IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP),
- LIBPE_ENTRY(IMAGE_FILE_NET_RUN_FROM_SWAP),
- LIBPE_ENTRY(IMAGE_FILE_SYSTEM),
- LIBPE_ENTRY(IMAGE_FILE_DLL),
- LIBPE_ENTRY(IMAGE_FILE_UP_SYSTEM_ONLY),
- LIBPE_ENTRY(IMAGE_FILE_BYTES_REVERSED_HI)
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (characteristic == names[i].characteristic)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_image_dllcharacteristic_name(ImageDllCharacteristics characteristic) {
- typedef struct {
- ImageDllCharacteristics characteristic;
- const char * const name;
- } ImageDllCharacteristicsName;
-
- static const ImageDllCharacteristicsName names[] = {
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_NX_COMPAT),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_NO_ISOLATION),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_NO_SEH),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_NO_BIND),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_APPCONTAINER),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_WDM_DRIVER),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_GUARD_CF),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE)
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (characteristic == names[i].characteristic)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_dll_image_dllcharacteristic_name(ImageDllCharacteristics characteristic) {
- typedef struct {
- ImageDllCharacteristics characteristic;
- const char * const name;
- } ImageDllCharacteristicsName;
-
- static const ImageDllCharacteristicsName names[] = {
- LIBPE_ENTRY(IMAGE_LIBRARY_PROCESS_INIT),
- LIBPE_ENTRY(IMAGE_LIBRARY_PROCESS_TERM),
- LIBPE_ENTRY(IMAGE_LIBRARY_THREAD_INIT),
- LIBPE_ENTRY(IMAGE_LIBRARY_THREAD_TERM),
- LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_X86_THUNK),
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (characteristic == names[i].characteristic)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_image_loader_flags_name(ImageLoaderFlags flags) {
- typedef struct {
- ImageLoaderFlags flags;
- const char * const name;
- } ImageLoaderFlagsName;
-
- static const ImageLoaderFlagsName names[] = {
- LIBPE_ENTRY(IMAGE_LOADER_FLAGS_COMPLUS),
- LIBPE_ENTRY(IMAGE_LOADER_FLAGS_SYSTEM_GLOBAL),
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (flags == names[i].flags)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_dll_image_loader_flags_name(ImageLoaderFlags flags) {
- typedef struct {
- ImageLoaderFlags flags;
- const char * const name;
- } ImageLoaderFlagsName;
-
- static const ImageLoaderFlagsName names[] = {
- LIBPE_ENTRY(IMAGE_LOADER_FLAGS_BREAK_ON_LOAD),
- LIBPE_ENTRY(IMAGE_LOADER_FLAGS_DEBUG_ON_LOAD),
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (flags == names[i].flags)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_windows_subsystem_name(WindowsSubsystem subsystem) {
- typedef struct {
- WindowsSubsystem subsystem;
- const char * const name;
- } WindowsSubsystemName;
-
- static const WindowsSubsystemName names[] = {
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_UNKNOWN),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_NATIVE),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_GUI),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_CUI),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_OLD_CE_GUI),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_OS2_CUI),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_POSIX_CUI),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_MMOSA),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_CE_GUI),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_EFI_APPLICATION),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_EFI_ROM),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_XBOX),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION),
- LIBPE_ENTRY(IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG)
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (subsystem == names[i].subsystem)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_directory_name(ImageDirectoryEntry entry) {
- typedef struct {
- ImageDirectoryEntry entry;
- const char * const name;
- } ImageDirectoryEntryName;
-
- static const ImageDirectoryEntryName names[] = {
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_EXPORT),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_IMPORT),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_RESOURCE),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_EXCEPTION),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_SECURITY),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_BASERELOC),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_DEBUG),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_ARCHITECTURE),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_GLOBALPTR),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_TLS),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_IAT),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT),
- LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR),
- LIBPE_ENTRY(IMAGE_DIRECTORY_RESERVED)
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (entry == names[i].entry)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_section_characteristic_name(SectionCharacteristics characteristic) {
- typedef struct {
- SectionCharacteristics characteristic;
- const char * const name;
- } SectionCharacteristicsName;
-
- static const SectionCharacteristicsName names[] = {
- LIBPE_ENTRY(IMAGE_SCN_SCALE_INDEX),
- LIBPE_ENTRY(IMAGE_SCN_TYPE_NO_LOAD),
- LIBPE_ENTRY(IMAGE_SCN_TYPE_GROUPED),
- LIBPE_ENTRY(IMAGE_SCN_TYPE_NO_PAD),
- LIBPE_ENTRY(IMAGE_SCN_TYPE_COPY),
- LIBPE_ENTRY(IMAGE_SCN_CNT_CODE),
- LIBPE_ENTRY(IMAGE_SCN_CNT_INITIALIZED_DATA),
- LIBPE_ENTRY(IMAGE_SCN_CNT_UNINITIALIZED_DATA),
- LIBPE_ENTRY(IMAGE_SCN_LNK_OTHER),
- LIBPE_ENTRY(IMAGE_SCN_LNK_INFO),
- LIBPE_ENTRY(IMAGE_SCN_LNK_OVERLAY),
- LIBPE_ENTRY(IMAGE_SCN_LNK_REMOVE),
- LIBPE_ENTRY(IMAGE_SCN_LNK_COMDAT),
- LIBPE_ENTRY(IMAGE_SCN_NO_DEFER_SPEC_EXC),
- LIBPE_ENTRY(IMAGE_SCN_GPREL),
- LIBPE_ENTRY(IMAGE_SCN_MEM_16BIT),
- LIBPE_ENTRY(IMAGE_SCN_MEM_LOCKED),
- LIBPE_ENTRY(IMAGE_SCN_MEM_PRELOAD),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_1BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_2BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_4BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_8BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_16BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_32BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_64BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_128BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_256BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_512BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_1024BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_2048BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_4096BYTES),
- LIBPE_ENTRY(IMAGE_SCN_ALIGN_8192BYTES),
- LIBPE_ENTRY(IMAGE_SCN_LNK_NRELOC_OVFL),
- LIBPE_ENTRY(IMAGE_SCN_MEM_DISCARDABLE),
- LIBPE_ENTRY(IMAGE_SCN_MEM_NOT_CACHED),
- LIBPE_ENTRY(IMAGE_SCN_MEM_NOT_PAGED),
- LIBPE_ENTRY(IMAGE_SCN_MEM_SHARED),
- LIBPE_ENTRY(IMAGE_SCN_MEM_EXECUTE),
- LIBPE_ENTRY(IMAGE_SCN_MEM_READ),
- LIBPE_ENTRY(IMAGE_SCN_MEM_WRITE)
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (characteristic == names[i].characteristic)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_m68k_section_characteristic_name(SectionCharacteristics characteristic) {
- typedef struct {
- SectionCharacteristics characteristic;
- const char * const name;
- } SectionCharacteristicsName;
-
- static const SectionCharacteristicsName names[] = {
- LIBPE_ENTRY(IMAGE_SCN_MEM_PROTECTED),
- LIBPE_ENTRY(IMAGE_SCN_MEM_FARDATA),
- LIBPE_ENTRY(IMAGE_SCN_MEM_SYSHEAP),
- LIBPE_ENTRY(IMAGE_SCN_MEM_PURGEABLE),
- LIBPE_ENTRY(IMAGE_SCN_MEM_LOCKED),
- LIBPE_ENTRY(IMAGE_SCN_MEM_PRELOAD),
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (characteristic == names[i].characteristic)
- return names[i].name;
- }
- return NULL;
-}
-
-const char *pe_rom_section_characteristic_name(ROMSectionCharacteristics characteristic) {
- typedef struct {
- ROMSectionCharacteristics characteristic;
- const char * const name;
- } ROMSectionCharacteristicsName;
-
- static const ROMSectionCharacteristicsName names[] = {
- LIBPE_ENTRY(STYP_DUMMY),
- LIBPE_ENTRY(STYP_TEXT),
- LIBPE_ENTRY(STYP_DATA),
- LIBPE_ENTRY(STYP_SBSS),
- LIBPE_ENTRY(STYP_RDATA),
- LIBPE_ENTRY(STYP_SDATA),
- LIBPE_ENTRY(STYP_BSS),
- LIBPE_ENTRY(STYP_UCODE),
- LIBPE_ENTRY(STYP_LIT8),
- LIBPE_ENTRY(STYP_LIT4),
- LIBPE_ENTRY(S_NRELOC_OVFL),
- LIBPE_ENTRY(STYP_LIB),
- LIBPE_ENTRY(STYP_INIT),
- };
-
- for (unsigned int i=0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
- if (characteristic == names[i].characteristic)
- return names[i].name;
- }
- return NULL;
-}
-
-bool pe_use_rom_section_characteristic(pe_ctx_t *ctx) {
- return ctx->pe.optional_hdr_ptr != NULL && ctx->pe.optional_hdr.type == MAGIC_ROM;
-}
-
-bool pe_is_repro(pe_ctx_t *ctx) {
- const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_DEBUG);
- if (dir == NULL)
- return false;
-
- uint64_t va = dir->VirtualAddress;
- if (va == 0)
- return false;
-
- uint64_t ofs = pe_rva2ofs(ctx, va);
- const IMAGE_DEBUG_DIRECTORY *debugs = LIBPE_PTR_ADD(ctx->map_addr, ofs);
- uint32_t count = dir->Size / sizeof(debugs[0]);
- for (uint32_t i = 0; i < count; i++) {
- if (!pe_can_read(ctx, &debugs[i], sizeof(debugs[i])))
- return false;
- if (debugs[i].Type == IMAGE_DEBUG_TYPE_REPRO)
- return true;
- }
-
- return false;
+IMAGE_OPTIONAL_HEADER *pe_optional(pe_ctx_t *ctx)
+{
+ if (ctx->pe.optional_hdr_ptr == NULL) {
+ return NULL;
+ }
+ return &ctx->pe.optional_hdr;
}
+
+uint32_t pe_directories_count(const pe_ctx_t *ctx)
+{
+ return ctx->pe.num_directories;
+}
+
+IMAGE_DATA_DIRECTORY **pe_directories(pe_ctx_t *ctx)
+{
+ return ctx->pe.directories;
+}
+
+IMAGE_DATA_DIRECTORY *pe_directory_by_entry(pe_ctx_t *ctx,
+ ImageDirectoryEntry entry)
+{
+ if (ctx->pe.directories == NULL
+ || entry > (ImageDirectoryEntry) (ctx->pe.num_directories - 1)) {
+ return NULL;
+ }
+
+ return ctx->pe.directories[entry];
+}
+
+uint16_t pe_sections_count(const pe_ctx_t *ctx) { return ctx->pe.num_sections; }
+
+IMAGE_SECTION_HEADER **pe_sections(pe_ctx_t *ctx) { return ctx->pe.sections; }
+
+IMAGE_SECTION_HEADER *pe_section_by_name(pe_ctx_t *ctx, const char *name)
+{
+ if (ctx->pe.sections == NULL || name == NULL) {
+ return NULL;
+ }
+
+ for (uint32_t i = 0; i < ctx->pe.num_sections; i++) {
+ if (strncmp((const char *) ctx->pe.sections[i]->Name, name,
+ SECTION_NAME_SIZE)
+ == 0) {
+ return ctx->pe.sections[i];
+ }
+ }
+ return NULL;
+}
+
+const char *pe_section_name(const pe_ctx_t *ctx,
+ const IMAGE_SECTION_HEADER *section_hdr,
+ char *out_name, size_t out_name_size)
+{
+ assert(ctx != NULL);
+ // assert(out_name_size >= SECTION_NAME_SIZE + 1);
+ if (out_name_size < SECTION_NAME_SIZE + 1) {
+ return NULL;
+ }
+
+ strncpy(out_name, (const char *) section_hdr->Name, SECTION_NAME_SIZE);
+ out_name[SECTION_NAME_SIZE] = '\0';
+ if (out_name[0] == '/' && out_name[1] >= '0' && out_name[1] <= '9'
+ && ctx->pe.strings_ptr) {
+ char *endptr = NULL;
+ long int offset = -1;
+ errno = 0;
+ offset = strtol(out_name + 1, &endptr, 10);
+ if (errno == 0 && *endptr == 0 && offset >= 0
+ && offset < (long) ctx->pe.strings_size) {
+ return ctx->pe.strings_ptr + offset;
+ }
+ }
+ return out_name;
+}
+
+#define LIBPE_ENTRY(v) {v, #v}
+
+const char *pe_machine_type_name(MachineType type)
+{
+ typedef struct {
+ MachineType type;
+ const char *const name;
+ } MachineEntry;
+
+ static const MachineEntry names[]
+ = {LIBPE_ENTRY(IMAGE_FILE_MACHINE_UNKNOWN),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_ALPHA_OLD),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_ALPHA),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_ALPHA64),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_AM33),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_AMD64),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARM),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARMV7),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARM64),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARM64EC),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_ARM64X),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_CEE),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_CEF),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_CHPE_X86),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_EBC),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_I386),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_I860),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_IA64),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_LOONGARCH32),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_LOONGARCH64),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_M32R),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_M68K),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_MIPS16),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_MIPSFPU),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_MIPSFPU16),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_MPPC_601),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_OMNI),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_PARISC),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_POWERPC),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_POWERPCFP),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_POWERPCBE),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_R3000),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_R3000_BE),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_R4000),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_R10000),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_RISCV32),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_RISCV64),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_RISCV128),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH3),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH3DSP),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH3E),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH4),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_SH5),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_TRICORE),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_TAHOE),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_THUMB),
+ LIBPE_ENTRY(IMAGE_FILE_MACHINE_WCEMIPSV2)};
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (type == names[i].type) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *pe_image_characteristic_name(ImageCharacteristics characteristic)
+{
+ typedef struct {
+ ImageCharacteristics characteristic;
+ const char *const name;
+ } ImageCharacteristicsName;
+
+ static const ImageCharacteristicsName names[]
+ = {LIBPE_ENTRY(IMAGE_FILE_RELOCS_STRIPPED),
+ LIBPE_ENTRY(IMAGE_FILE_EXECUTABLE_IMAGE),
+ LIBPE_ENTRY(IMAGE_FILE_LINE_NUMS_STRIPPED),
+ LIBPE_ENTRY(IMAGE_FILE_LOCAL_SYMS_STRIPPED),
+ LIBPE_ENTRY(IMAGE_FILE_AGGRESSIVE_WS_TRIM),
+ LIBPE_ENTRY(IMAGE_FILE_LARGE_ADDRESS_AWARE),
+ LIBPE_ENTRY(IMAGE_FILE_16BIT_MACHINE),
+ LIBPE_ENTRY(IMAGE_FILE_BYTES_REVERSED_LO),
+ LIBPE_ENTRY(IMAGE_FILE_32BIT_MACHINE),
+ LIBPE_ENTRY(IMAGE_FILE_DEBUG_STRIPPED),
+ LIBPE_ENTRY(IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP),
+ LIBPE_ENTRY(IMAGE_FILE_NET_RUN_FROM_SWAP),
+ LIBPE_ENTRY(IMAGE_FILE_SYSTEM),
+ LIBPE_ENTRY(IMAGE_FILE_DLL),
+ LIBPE_ENTRY(IMAGE_FILE_UP_SYSTEM_ONLY),
+ LIBPE_ENTRY(IMAGE_FILE_BYTES_REVERSED_HI)};
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (characteristic == names[i].characteristic) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *
+pe_image_dllcharacteristic_name(ImageDllCharacteristics characteristic)
+{
+ typedef struct {
+ ImageDllCharacteristics characteristic;
+ const char *const name;
+ } ImageDllCharacteristicsName;
+
+ static const ImageDllCharacteristicsName names[]
+ = {LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_NX_COMPAT),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_NO_ISOLATION),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_NO_SEH),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_NO_BIND),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_APPCONTAINER),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_WDM_DRIVER),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_GUARD_CF),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE)};
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (characteristic == names[i].characteristic) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *
+pe_dll_image_dllcharacteristic_name(ImageDllCharacteristics characteristic)
+{
+ typedef struct {
+ ImageDllCharacteristics characteristic;
+ const char *const name;
+ } ImageDllCharacteristicsName;
+
+ static const ImageDllCharacteristicsName names[] = {
+ LIBPE_ENTRY(IMAGE_LIBRARY_PROCESS_INIT),
+ LIBPE_ENTRY(IMAGE_LIBRARY_PROCESS_TERM),
+ LIBPE_ENTRY(IMAGE_LIBRARY_THREAD_INIT),
+ LIBPE_ENTRY(IMAGE_LIBRARY_THREAD_TERM),
+ LIBPE_ENTRY(IMAGE_DLLCHARACTERISTICS_X86_THUNK),
+ };
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (characteristic == names[i].characteristic) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *pe_image_loader_flags_name(ImageLoaderFlags flags)
+{
+ typedef struct {
+ ImageLoaderFlags flags;
+ const char *const name;
+ } ImageLoaderFlagsName;
+
+ static const ImageLoaderFlagsName names[] = {
+ LIBPE_ENTRY(IMAGE_LOADER_FLAGS_COMPLUS),
+ LIBPE_ENTRY(IMAGE_LOADER_FLAGS_SYSTEM_GLOBAL),
+ };
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (flags == names[i].flags) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *pe_dll_image_loader_flags_name(ImageLoaderFlags flags)
+{
+ typedef struct {
+ ImageLoaderFlags flags;
+ const char *const name;
+ } ImageLoaderFlagsName;
+
+ static const ImageLoaderFlagsName names[] = {
+ LIBPE_ENTRY(IMAGE_LOADER_FLAGS_BREAK_ON_LOAD),
+ LIBPE_ENTRY(IMAGE_LOADER_FLAGS_DEBUG_ON_LOAD),
+ };
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (flags == names[i].flags) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *pe_windows_subsystem_name(WindowsSubsystem subsystem)
+{
+ typedef struct {
+ WindowsSubsystem subsystem;
+ const char *const name;
+ } WindowsSubsystemName;
+
+ static const WindowsSubsystemName names[]
+ = {LIBPE_ENTRY(IMAGE_SUBSYSTEM_UNKNOWN),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_NATIVE),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_GUI),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_CUI),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_OLD_CE_GUI),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_OS2_CUI),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_POSIX_CUI),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_MMOSA),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_CE_GUI),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_EFI_APPLICATION),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_EFI_ROM),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_XBOX),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION),
+ LIBPE_ENTRY(IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG)};
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (subsystem == names[i].subsystem) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *pe_directory_name(ImageDirectoryEntry entry)
+{
+ typedef struct {
+ ImageDirectoryEntry entry;
+ const char *const name;
+ } ImageDirectoryEntryName;
+
+ static const ImageDirectoryEntryName names[]
+ = {LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_EXPORT),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_IMPORT),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_RESOURCE),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_EXCEPTION),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_SECURITY),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_BASERELOC),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_DEBUG),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_ARCHITECTURE),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_GLOBALPTR),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_TLS),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_IAT),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR),
+ LIBPE_ENTRY(IMAGE_DIRECTORY_RESERVED)};
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (entry == names[i].entry) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *
+pe_section_characteristic_name(SectionCharacteristics characteristic)
+{
+ typedef struct {
+ SectionCharacteristics characteristic;
+ const char *const name;
+ } SectionCharacteristicsName;
+
+ static const SectionCharacteristicsName names[]
+ = {LIBPE_ENTRY(IMAGE_SCN_SCALE_INDEX),
+ LIBPE_ENTRY(IMAGE_SCN_TYPE_NO_LOAD),
+ LIBPE_ENTRY(IMAGE_SCN_TYPE_GROUPED),
+ LIBPE_ENTRY(IMAGE_SCN_TYPE_NO_PAD),
+ LIBPE_ENTRY(IMAGE_SCN_TYPE_COPY),
+ LIBPE_ENTRY(IMAGE_SCN_CNT_CODE),
+ LIBPE_ENTRY(IMAGE_SCN_CNT_INITIALIZED_DATA),
+ LIBPE_ENTRY(IMAGE_SCN_CNT_UNINITIALIZED_DATA),
+ LIBPE_ENTRY(IMAGE_SCN_LNK_OTHER),
+ LIBPE_ENTRY(IMAGE_SCN_LNK_INFO),
+ LIBPE_ENTRY(IMAGE_SCN_LNK_OVERLAY),
+ LIBPE_ENTRY(IMAGE_SCN_LNK_REMOVE),
+ LIBPE_ENTRY(IMAGE_SCN_LNK_COMDAT),
+ LIBPE_ENTRY(IMAGE_SCN_NO_DEFER_SPEC_EXC),
+ LIBPE_ENTRY(IMAGE_SCN_GPREL),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_16BIT),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_LOCKED),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_PRELOAD),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_1BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_2BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_4BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_8BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_16BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_32BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_64BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_128BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_256BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_512BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_1024BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_2048BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_4096BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_ALIGN_8192BYTES),
+ LIBPE_ENTRY(IMAGE_SCN_LNK_NRELOC_OVFL),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_DISCARDABLE),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_NOT_CACHED),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_NOT_PAGED),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_SHARED),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_EXECUTE),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_READ),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_WRITE)};
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (characteristic == names[i].characteristic) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *
+pe_m68k_section_characteristic_name(SectionCharacteristics characteristic)
+{
+ typedef struct {
+ SectionCharacteristics characteristic;
+ const char *const name;
+ } SectionCharacteristicsName;
+
+ static const SectionCharacteristicsName names[] = {
+ LIBPE_ENTRY(IMAGE_SCN_MEM_PROTECTED),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_FARDATA),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_SYSHEAP),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_PURGEABLE),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_LOCKED),
+ LIBPE_ENTRY(IMAGE_SCN_MEM_PRELOAD),
+ };
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (characteristic == names[i].characteristic) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+const char *
+pe_rom_section_characteristic_name(ROMSectionCharacteristics characteristic)
+{
+ typedef struct {
+ ROMSectionCharacteristics characteristic;
+ const char *const name;
+ } ROMSectionCharacteristicsName;
+
+ static const ROMSectionCharacteristicsName names[] = {
+ LIBPE_ENTRY(STYP_DUMMY), LIBPE_ENTRY(STYP_TEXT),
+ LIBPE_ENTRY(STYP_DATA), LIBPE_ENTRY(STYP_SBSS),
+ LIBPE_ENTRY(STYP_RDATA), LIBPE_ENTRY(STYP_SDATA),
+ LIBPE_ENTRY(STYP_BSS), LIBPE_ENTRY(STYP_UCODE),
+ LIBPE_ENTRY(STYP_LIT8), LIBPE_ENTRY(STYP_LIT4),
+ LIBPE_ENTRY(S_NRELOC_OVFL), LIBPE_ENTRY(STYP_LIB),
+ LIBPE_ENTRY(STYP_INIT),
+ };
+
+ for (unsigned int i = 0; i < LIBPE_SIZEOF_ARRAY(names); i++) {
+ if (characteristic == names[i].characteristic) {
+ return names[i].name;
+ }
+ }
+ return NULL;
+}
+
+bool pe_use_rom_section_characteristic(pe_ctx_t *ctx)
+{
+ return ctx->pe.optional_hdr_ptr != NULL
+ && ctx->pe.optional_hdr.type == MAGIC_ROM;
+}
+
+bool pe_is_repro(pe_ctx_t *ctx)
+{
+ const IMAGE_DATA_DIRECTORY *dir
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_DEBUG);
+ if (dir == NULL) {
+ return false;
+ }
+
+ uint64_t va = dir->VirtualAddress;
+ if (va == 0) {
+ return false;
+ }
+
+ uint64_t ofs = pe_rva2ofs(ctx, va);
+ const IMAGE_DEBUG_DIRECTORY *debugs = LIBPE_PTR_ADD(ctx->map_addr, ofs);
+ uint32_t count = dir->Size / sizeof(debugs[0]);
+ for (uint32_t i = 0; i < count; i++) {
+ if (! pe_can_read(ctx, &debugs[i], sizeof(debugs[i]))) {
+ return false;
+ }
+ if (debugs[i].Type == IMAGE_DEBUG_TYPE_REPRO) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
diff --git a/lib/libpe/resources.c b/lib/libpe/resources.c
index 4a2d7f97..8a8e414f 100644
--- a/lib/libpe/resources.c
+++ b/lib/libpe/resources.c
@@ -1,670 +1,770 @@
/*
- libpe - the PE library
+ libpe - the PE library
- Copyright (C) 2010 - 2017 libpe authors
+ Copyright (C) 2010 - 2026 libpe authors
- This file is part of libpe.
+ This file is part of libpe.
- libpe is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- libpe is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with libpe. If not, see .
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
*/
#include "libpe/resources.h"
+
#include "libpe/dir_resources.h"
+#include "libpe/macros.h"
#include "libpe/pe.h"
-#include "libpe/utlist.h"
#include "libpe/types_resources.h"
-#include
-#include
+#include "libpe/utils.h"
+
#include
+#include
+#include
#include
-#include
#include
+#include
#include
+#include
-// REFERENCE: https://msdn.microsoft.com/en-us/library/ms648009(v=vs.85).aspx
+// REFERENCE:
+// https://msdn.microsoft.com/en-us/library/ms648009(v=vs.85).aspx
static const pe_resource_entry_info_t g_resource_dataentry_info_table[] = {
- { "???_0", 0, ".0", "_0" },
- { "RT_CURSOR", RT_CURSOR, ".cur", "cursors" },
- { "RT_BITMAP", RT_BITMAP, ".bmp", "bitmaps" },
- { "RT_ICON", RT_ICON, ".ico", "icons" },
- { "RT_MENU", RT_MENU, ".rc", "menus" },
- { "RT_DIALOG", RT_DIALOG, ".dlg", "dialogs" },
- { "RT_STRING", RT_STRING, ".rc", "strings" },
- { "RT_FONTDIR", RT_FONTDIR, ".fnt", "fontdirs" },
- { "RT_FONT", RT_FONT, ".fnt", "fonts" },
- { "RT_ACCELERATOR", RT_ACCELERATOR, ".rc", "accelerators" },
- { "RT_RCDATA", RT_RCDATA, ".rc", "rcdatas" },
- { "RT_MESSAGETABLE", RT_MESSAGETABLE, ".mc", "messagetables" },
- { "RT_GROUP_CURSOR", RT_GROUP_CURSOR, ".cur", "groupcursors" },
- { "???_13", 13, ".13", "_13" },
- { "RT_GROUP_ICON", RT_GROUP_ICON, ".ico", "groupicons" },
- { "RT_NAMETABLE", RT_NAMETABLE, ".rc", "nametable" },
- { "RT_VERSION", RT_VERSION, ".rc", "versions" },
- { "RT_DLGINCLUDE", RT_DLGINCLUDE, ".rc", "dlgincludes" },
- { "???_18", 18, ".18", "_18" },
- { "RT_PLUGPLAY", RT_PLUGPLAY, ".rc", "plugplays" },
- { "RT_VXD", RT_VXD, ".rc", "vxds" },
- { "RT_ANICURSOR", RT_ANICURSOR, ".rc", "anicursors" },
- { "RT_ANIICON", RT_ANIICON, ".rc", "aniicons" },
- { "RT_HTML", RT_HTML, ".html", "htmls" },
- { "RT_MANIFEST", RT_MANIFEST, ".xml", "manifests" },
- { "RT_PSZ", RT_PSZ, ".rc", "psz" },
- { "RT_DLGINIT", RT_DLGINIT, ".rc", "dlginits" },
- { "RT_TOOLBAR", RT_TOOLBAR, ".rc", "toolbars" },
- { NULL }
+ {"???_0", 0, ".0", "_0" },
+ {"RT_CURSOR", RT_CURSOR, ".cur", "cursors" },
+ {"RT_BITMAP", RT_BITMAP, ".bmp", "bitmaps" },
+ {"RT_ICON", RT_ICON, ".ico", "icons" },
+ {"RT_MENU", RT_MENU, ".rc", "menus" },
+ {"RT_DIALOG", RT_DIALOG, ".dlg", "dialogs" },
+ {"RT_STRING", RT_STRING, ".rc", "strings" },
+ {"RT_FONTDIR", RT_FONTDIR, ".fnt", "fontdirs" },
+ {"RT_FONT", RT_FONT, ".fnt", "fonts" },
+ {"RT_ACCELERATOR", RT_ACCELERATOR, ".rc", "accelerators" },
+ {"RT_RCDATA", RT_RCDATA, ".rc", "rcdatas" },
+ {"RT_MESSAGETABLE", RT_MESSAGETABLE, ".mc", "messagetables"},
+ {"RT_GROUP_CURSOR", RT_GROUP_CURSOR, ".cur", "groupcursors" },
+ {"???_13", 13, ".13", "_13" },
+ {"RT_GROUP_ICON", RT_GROUP_ICON, ".ico", "groupicons" },
+ {"RT_NAMETABLE", RT_NAMETABLE, ".rc", "nametable" },
+ {"RT_VERSION", RT_VERSION, ".rc", "versions" },
+ {"RT_DLGINCLUDE", RT_DLGINCLUDE, ".rc", "dlgincludes" },
+ {"???_18", 18, ".18", "_18" },
+ {"RT_PLUGPLAY", RT_PLUGPLAY, ".rc", "plugplays" },
+ {"RT_VXD", RT_VXD, ".rc", "vxds" },
+ {"RT_ANICURSOR", RT_ANICURSOR, ".rc", "anicursors" },
+ {"RT_ANIICON", RT_ANIICON, ".rc", "aniicons" },
+ {"RT_HTML", RT_HTML, ".html", "htmls" },
+ {"RT_MANIFEST", RT_MANIFEST, ".xml", "manifests" },
+ {"RT_PSZ", RT_PSZ, ".rc", "psz" },
+ {"RT_DLGINIT", RT_DLGINIT, ".rc", "dlginits" },
+ {"RT_TOOLBAR", RT_TOOLBAR, ".rc", "toolbars" },
+ {NULL, 0, NULL, NULL }
};
-const pe_resource_entry_info_t *pe_resource_entry_info_lookup(uint32_t name_offset) {
- const pe_resource_entry_info_t *p;
+const pe_resource_entry_info_t *
+pe_resource_entry_info_lookup(uint32_t name_offset)
+{
+ const pe_resource_entry_info_t *p;
- p = g_resource_dataentry_info_table;
- while ( p->name )
- {
- if ( p->type == name_offset )
- return p;
- p++;
- }
+ p = g_resource_dataentry_info_table;
+ while (p->name) {
+ if (p->type == (ResourceType) name_offset) {
+ return p;
+ }
+ p++;
+ }
- return NULL;
+ return NULL;
}
-void pe_resources_dealloc_node_search_result(pe_resource_node_search_result_t *result) {
- if (result == NULL)
- return;
+void pe_resources_dealloc_node_search_result(
+ pe_resource_node_search_result_t *result)
+{
+ if (result == NULL) {
+ return;
+ }
- pe_resource_node_search_result_item_t *item = result->items;
- while (item != NULL) {
- pe_resource_node_search_result_item_t *next = item->next;
- free(item);
- item = next;
- }
+ pe_resource_node_search_result_item_t *item = result->items;
+ while (item != NULL) {
+ pe_resource_node_search_result_item_t *next = item->next;
+ free(item);
+ item = next;
+ }
}
-void pe_resource_search_nodes(pe_resource_node_search_result_t *result, const pe_resource_node_t *node, pe_resource_node_predicate_fn predicate) {
- assert(result != NULL);
-
- if (node == NULL)
- return;
-
- if (predicate(node)) {
- // Found the matching node. Return it.
- pe_resource_node_search_result_item_t *item = calloc(1, sizeof(*item));
- if (item == NULL) {
- // TODO: Handle allocation failure.
- abort();
- }
- item->node = node;
- LL_APPEND(result->items, item);
- result->count++;
- // IMPORTANT: We do NOT return early because we want all matching nodes.
- }
-
- // Traverse the tree to find the matching node.
- pe_resource_search_nodes(result, node->childNode, predicate);
- pe_resource_search_nodes(result, node->nextNode, predicate);
+void pe_resource_search_nodes(pe_resource_node_search_result_t *result,
+ const pe_resource_node_t *node,
+ pe_resource_node_predicate_fn predicate)
+{
+ assert(result != NULL);
+
+ if (node == NULL) {
+ return;
+ }
+
+ if (predicate(node)) {
+ // Found the matching node. Return it.
+ pe_resource_node_search_result_item_t *item = calloc(1, sizeof(*item));
+ if (item == NULL) {
+ // TODO: Handle allocation failure.
+ abort();
+ }
+ item->node = node;
+ LL_APPEND(result->items, item);
+ result->count++;
+ // IMPORTANT: We do NOT return early because we want all matching nodes.
+ }
+
+ // Traverse the tree to find the matching node.
+ pe_resource_search_nodes(result, node->childNode, predicate);
+ pe_resource_search_nodes(result, node->nextNode, predicate);
}
-pe_resource_node_t *pe_resource_root_node(const pe_resource_node_t *node) {
- if (node == NULL)
- return NULL;
-
- // Traverse the linked-list to find the root parent node.
- pe_resource_node_t *parent = node->parentNode;
- while (parent != NULL) {
- if (parent->parentNode == NULL) {
- // Found the root parent node. Return it.
- return parent;
- }
- // Move to the next parent node.
- parent = parent->parentNode;
- }
-
- return (pe_resource_node_t *)node; // Return the node itself if it has no parent.
+pe_resource_node_t *pe_resource_root_node(const pe_resource_node_t *node)
+{
+ if (node == NULL) {
+ return NULL;
+ }
+
+ // Traverse the linked-list to find the root parent node.
+ pe_resource_node_t *parent = node->parentNode;
+ while (parent != NULL) {
+ if (parent->parentNode == NULL) {
+ // Found the root parent node. Return it.
+ return parent;
+ }
+ // Move to the next parent node.
+ parent = parent->parentNode;
+ }
+
+ // Return the node itself if it has no parent.
+ return (pe_resource_node_t *) node;
}
-pe_resource_node_t *pe_resource_last_child_node(const pe_resource_node_t *parent_node) {
- if (parent_node == NULL)
- return NULL;
-
- // Traverse the linked-list to find the last child node.
- pe_resource_node_t *child = parent_node->childNode;
- while (child != NULL) {
- if (child->nextNode == NULL) {
- // Found the last child node. Return it.
- return child;
- }
- // Move to the next node.
- child = child->nextNode;
- }
-
- return NULL;
+pe_resource_node_t *
+pe_resource_last_child_node(const pe_resource_node_t *parent_node)
+{
+ if (parent_node == NULL) {
+ return NULL;
+ }
+
+ // Traverse the linked-list to find the last child node.
+ pe_resource_node_t *child = parent_node->childNode;
+ while (child != NULL) {
+ if (child->nextNode == NULL) {
+ // Found the last child node. Return it.
+ return child;
+ }
+ // Move to the next node.
+ child = child->nextNode;
+ }
+
+ return NULL;
}
-pe_resource_node_t *pe_resource_find_node_by_type_and_level(const pe_resource_node_t *node, pe_resource_node_type_e type, uint32_t dirLevel) {
- if (node == NULL)
- return NULL;
+pe_resource_node_t *
+pe_resource_find_node_by_type_and_level(const pe_resource_node_t *node,
+ pe_resource_node_type_e type,
+ uint32_t dirLevel)
+{
+ if (node == NULL) {
+ return NULL;
+ }
- // Found the matching node. Return it.
- if (node->type == type && node->dirLevel == dirLevel) {
- return (pe_resource_node_t *)node;
- }
+ // Found the matching node. Return it.
+ if (node->type == type && node->dirLevel == dirLevel) {
+ return (pe_resource_node_t *) node;
+ }
- // Traverse the tree to find the matching node.
+ // Traverse the tree to find the matching node.
- const pe_resource_node_t *child = pe_resource_find_node_by_type_and_level(node->childNode, type, dirLevel);
- // Found the matching node. Return it.
- if (child != NULL)
- return (pe_resource_node_t *)child;
+ const pe_resource_node_t *child = pe_resource_find_node_by_type_and_level(
+ node->childNode, type, dirLevel);
+ // Found the matching node. Return it.
+ if (child != NULL) {
+ return (pe_resource_node_t *) child;
+ }
- const pe_resource_node_t *sibling = pe_resource_find_node_by_type_and_level(node->nextNode, type, dirLevel);
- // Found the matching node. Return it.
- if (sibling != NULL)
- return (pe_resource_node_t *)sibling;
+ const pe_resource_node_t *sibling = pe_resource_find_node_by_type_and_level(
+ node->nextNode, type, dirLevel);
+ // Found the matching node. Return it.
+ if (sibling != NULL) {
+ return (pe_resource_node_t *) sibling;
+ }
- return NULL;
+ return NULL;
}
-pe_resource_node_t *pe_resource_find_parent_node_by_type_and_level(const pe_resource_node_t *node, pe_resource_node_type_e type, uint32_t dirLevel) {
- if (node == NULL)
- return NULL;
-
- // Traverse the linked-list to find the matching parent node.
- pe_resource_node_t *parent = node->parentNode;
- while (parent != NULL) {
- if (parent->type == type && parent->dirLevel == dirLevel) {
- // Found the matching parent node. Return it.
- return parent;
- }
- // Move to the next parent node.
- parent = parent->parentNode;
- }
-
- return NULL;
+pe_resource_node_t *
+pe_resource_find_parent_node_by_type_and_level(const pe_resource_node_t *node,
+ pe_resource_node_type_e type,
+ uint32_t dirLevel)
+{
+ if (node == NULL) {
+ return NULL;
+ }
+
+ // Traverse the linked-list to find the matching parent node.
+ pe_resource_node_t *parent = node->parentNode;
+ while (parent != NULL) {
+ if (parent->type == type && parent->dirLevel == dirLevel) {
+ // Found the matching parent node. Return it.
+ return parent;
+ }
+ // Move to the next parent node.
+ parent = parent->parentNode;
+ }
+
+ return NULL;
}
-char *pe_resource_parse_string_u(pe_ctx_t *ctx, char *output, size_t output_size, const IMAGE_RESOURCE_DATA_STRING_U *data_string_ptr) {
- if (data_string_ptr == NULL)
- return NULL;
+char *
+pe_resource_parse_string_u(pe_ctx_t *ctx, char *output, size_t output_size,
+ const IMAGE_RESOURCE_DATA_STRING_U *data_string_ptr)
+{
+ if (data_string_ptr == NULL) {
+ return NULL;
+ }
- if (!pe_can_read(ctx, data_string_ptr->String, data_string_ptr->Length)) {
- LIBPE_WARNING("Cannot read string from IMAGE_RESOURCE_DATA_STRING_U");
- return NULL;
- }
+ if (! pe_can_read(ctx, data_string_ptr->String, data_string_ptr->Length)) {
+ LIBPE_WARNING("Cannot read string from IMAGE_RESOURCE_DATA_STRING_U");
+ return NULL;
+ }
- // If the caller provided a NULL pointer, we do the allocation and return it.
- const size_t buffer_size = output_size == 0 ? (size_t)data_string_ptr->Length + 1 : output_size;
- if (output == NULL) {
- output = malloc(buffer_size);
- if (output == NULL) {
- // TODO: Handle allocation failure.
- abort();
- }
- }
+ // If the caller provided a NULL pointer, we do the allocation and return
+ // it.
+ const size_t buffer_size
+ = output_size == 0 ? (size_t) data_string_ptr->Length + 1 : output_size;
+ if (output == NULL) {
+ output = malloc(buffer_size);
+ if (output == NULL) {
+ // TODO: Handle allocation failure.
+ abort();
+ }
+ }
- pe_utils_str_widechar2ascii(output, buffer_size, (const char *)data_string_ptr->String, (size_t)data_string_ptr->Length);
+ pe_utils_str_widechar2ascii(output, buffer_size,
+ (const char *) data_string_ptr->String,
+ (size_t) data_string_ptr->Length);
- return output;
+ return output;
}
// FIX: Retired code just to avoid unecessary warnings.
#if 0
static char *pe_resource_name_from_id(pe_ctx_t *ctx, char *out_name, size_t out_name_size, uint32_t id) {
- const bool is_string = id & IMAGE_RESOURCE_NAME_IS_STRING; // entry->u0.data.NameIsString
-
- // If it's a regular ID, simply use it.
- if (!is_string) {
- if (out_name == NULL) {
- const size_t estimated_size = 8 + 1; // 8 == strlen("FFFFFFFF"), +1 for the `\0`.
- out_name = malloc(estimated_size);
- if (out_name == NULL) {
- // TODO: Handle allocation failure.
- abort();
- }
- }
-
- snprintf(out_name, out_name_size, "%X", id);
- return out_name;
- }
-
- id &= ~(uint32_t)IMAGE_RESOURCE_NAME_IS_STRING; // Ignore the highest bit.
- const IMAGE_RESOURCE_DATA_STRING_U *data_string_u = LIBPE_PTR_ADD(ctx->cached_data.resources->resource_base_ptr, id);
- if (!pe_can_read(ctx, data_string_u, sizeof(IMAGE_RESOURCE_DATA_STRING_U))) {
- LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DATA_STRING_U");
- return false;
- }
-
- out_name = pe_resource_parse_string_u(ctx, out_name, out_name_size, data_string_u);
- return out_name;
+ const bool is_string = id & IMAGE_RESOURCE_NAME_IS_STRING; // entry->u0.data.NameIsString
+
+ // If it's a regular ID, simply use it.
+ if (!is_string) {
+ if (out_name == NULL) {
+ const size_t estimated_size = 8 + 1; // 8 == strlen("FFFFFFFF"), +1 for the `\0`.
+ out_name = malloc(estimated_size);
+ if (out_name == NULL) {
+ // TODO: Handle allocation failure.
+ abort();
+ }
+ }
+
+ snprintf(out_name, out_name_size, "%X", id);
+ return out_name;
+ }
+
+ id &= ~(uint32_t)IMAGE_RESOURCE_NAME_IS_STRING; // Ignore the highest bit.
+ const IMAGE_RESOURCE_DATA_STRING_U *data_string_u = LIBPE_PTR_ADD(ctx->cached_data.resources->resource_base_ptr, id);
+ if (!pe_can_read(ctx, data_string_u, sizeof(IMAGE_RESOURCE_DATA_STRING_U))) {
+ LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DATA_STRING_U");
+ return false;
+ }
+
+ out_name = pe_resource_parse_string_u(ctx, out_name, out_name_size, data_string_u);
+ return out_name;
}
static char *pe_resource_name_from_type(char *out_name, size_t out_name_size, uint32_t type) {
- const pe_resource_entry_info_t *match = pe_resource_entry_info_lookup(type);
-
- if (out_name == NULL) {
- const size_t estimated_size = (match != NULL ? strlen(match->name) : 8) + 1; // 8 == strlen("FFFFFFFF"), +1 for the `\0`.
- out_name = malloc(estimated_size);
- if (out_name == NULL) {
- // TODO: Handle allocation failure.
- abort();
- }
- }
-
- if (match != NULL) {
- strncpy(out_name, match->name, out_name_size);
- out_name[out_name_size - 1] = '\0';
- } else {
- snprintf(out_name, out_name_size, "%" PRIX32, type);
- }
-
- return out_name;
+ const pe_resource_entry_info_t *match = pe_resource_entry_info_lookup(type);
+
+ if (out_name == NULL) {
+ const size_t estimated_size = (match != NULL ? strlen(match->name) : 8) + 1; // 8 == strlen("FFFFFFFF"), +1 for the `\0`.
+ out_name = malloc(estimated_size);
+ if (out_name == NULL) {
+ // TODO: Handle allocation failure.
+ abort();
+ }
+ }
+
+ if (match != NULL) {
+ strncpy(out_name, match->name, out_name_size);
+ out_name[out_name_size - 1] = '\0';
+ } else {
+ snprintf(out_name, out_name_size, "%" PRIX32, type);
+ }
+
+ return out_name;
}
static void pe_resource_debug_node(pe_ctx_t *ctx, const pe_resource_node_t *node) {
- if (node == NULL)
- return;
-
- switch (node->type) {
- default:
- LIBPE_WARNING("Invalid node type");
- break;
- case LIBPE_RDT_RESOURCE_DIRECTORY:
- {
- char resource_name[256];
- const size_t resource_name_size = sizeof(resource_name);
+ if (node == NULL)
+ return;
+
+ switch (node->type) {
+ default:
+ LIBPE_WARNING("Invalid node type");
+ break;
+ case LIBPE_RDT_RESOURCE_DIRECTORY:
+ {
+ char resource_name[256];
+ const size_t resource_name_size = sizeof(resource_name);
resource_name[0] = '\0';
- if (node->dirLevel == LIBPE_RDT_LEVEL1) { // dirLevel == 1 is where Resource Types are defined.
- if (node->parentNode != NULL && node->parentNode->type == LIBPE_RDT_DIRECTORY_ENTRY) {
- IMAGE_RESOURCE_DIRECTORY_ENTRY *dir_entry = node->parentNode->raw.directoryEntry;
- if (dir_entry->u0.data.NameIsString) {
- pe_resource_name_from_id(ctx, resource_name, resource_name_size, dir_entry->u0.Name);
- } else {
- pe_resource_name_from_type(resource_name, resource_name_size, dir_entry->u0.Name);
- }
- }
- } else {
- if (node->parentNode != NULL && node->parentNode->type == LIBPE_RDT_DIRECTORY_ENTRY) {
- IMAGE_RESOURCE_DIRECTORY_ENTRY *dir_entry = node->parentNode->raw.directoryEntry;
- pe_resource_name_from_id(ctx, resource_name, resource_name_size, dir_entry->u0.Name);
- } else {
- resource_name[0] = '0';
- resource_name[1] = '\0';
- }
- }
-
- const IMAGE_RESOURCE_DIRECTORY * const dir = node->raw.resourceDirectory;
-
- // Indentation.
- for (size_t i=0; i < node->depth; i++)
- printf(" ");
- printf("LIBPE_RDT_RESOURCE_DIRECTORY [dirLevel=%d]: ", node->dirLevel);
-
- printf("ResDir (%s) Entries:%02u[%02X] (Named:%02u[%02X], ID:%02u[%02X]) TimeDate:%08u[%08X]",
- resource_name,
- dir->NumberOfIdEntries + dir->NumberOfNamedEntries,
- dir->NumberOfIdEntries + dir->NumberOfNamedEntries,
- dir->NumberOfNamedEntries,
- dir->NumberOfNamedEntries,
- dir->NumberOfIdEntries,
- dir->NumberOfIdEntries,
- dir->TimeDateStamp,
- dir->TimeDateStamp
- );
-
- if (dir->MajorVersion || dir->MinorVersion)
- printf(" Vers:%u.%02u", dir->MajorVersion, dir->MinorVersion);
-
- if (dir->Characteristics)
- printf(" Char:%08u[%08X]", dir->Characteristics, dir->Characteristics);
-
- printf("\n");
- break;
- }
- case LIBPE_RDT_DIRECTORY_ENTRY:
- {
- // Indentation.
- for (size_t i=0; i < node->depth; i++)
- printf(" ");
- printf("LIBPE_RDT_DIRECTORY_ENTRY [dirLevel=%d]: ", node->dirLevel);
-
- const IMAGE_RESOURCE_DIRECTORY_ENTRY * const entry = node->raw.directoryEntry;
-
- if (entry->u0.data.NameIsString) { // entry->u0.Name & IMAGE_RESOURCE_NAME_IS_STRING
- char res_name[256];
- pe_resource_name_from_id(ctx, res_name, sizeof(res_name), entry->u0.Name);
- printf("Name: %s DataEntryOffs: %08u[%08X]\n",
- res_name, entry->u1.OffsetToData, entry->u1.OffsetToData);
- } else {
- printf("ID: %08u[%08X] DataEntryOffs: %08u[%08X]\n",
- entry->u0.Name, entry->u0.Name, entry->u1.OffsetToData, entry->u1.OffsetToData);
- }
- break;
- }
- case LIBPE_RDT_DATA_STRING:
- {
- const IMAGE_RESOURCE_DATA_STRING_U * const dataString = node->raw.dataString;
-
- char ascii_string[256];
- pe_resource_parse_string_u(ctx, ascii_string, sizeof(ascii_string), dataString);
-
- // Indentation.
- for (size_t i=0; i < node->depth; i++)
- printf(" ");
- printf("LIBPE_RDT_DATA_STRING [dirLevel=%d]: ", node->dirLevel);
-
- printf("String: %s Length: %02hu\n", ascii_string, dataString->Length);
- break;
- }
- case LIBPE_RDT_DATA_ENTRY:
- {
- const IMAGE_RESOURCE_DATA_ENTRY * const data_entry = node->raw.dataEntry;
-
- // Indentation.
- for (size_t i=0; i < node->depth; i++)
- printf(" ");
- printf("LIBPE_RDT_DATA_ENTRY [dirLevel=%d]: ", node->dirLevel);
-
- printf("DataRVA: %05u[%05X] DataSize: %05u[%05X] CodePage: %u[%X]\n",
- data_entry->OffsetToData,
- data_entry->OffsetToData,
- data_entry->Size,
- data_entry->Size,
- data_entry->CodePage,
- data_entry->CodePage);
- break;
- }
- }
+ if (node->dirLevel == LIBPE_RDT_LEVEL1) { // dirLevel == 1 is where Resource Types are defined.
+ if (node->parentNode != NULL && node->parentNode->type == LIBPE_RDT_DIRECTORY_ENTRY) {
+ IMAGE_RESOURCE_DIRECTORY_ENTRY *dir_entry = node->parentNode->raw.directoryEntry;
+ if (dir_entry->u0.data.NameIsString) {
+ pe_resource_name_from_id(ctx, resource_name, resource_name_size, dir_entry->u0.Name);
+ } else {
+ pe_resource_name_from_type(resource_name, resource_name_size, dir_entry->u0.Name);
+ }
+ }
+ } else {
+ if (node->parentNode != NULL && node->parentNode->type == LIBPE_RDT_DIRECTORY_ENTRY) {
+ IMAGE_RESOURCE_DIRECTORY_ENTRY *dir_entry = node->parentNode->raw.directoryEntry;
+ pe_resource_name_from_id(ctx, resource_name, resource_name_size, dir_entry->u0.Name);
+ } else {
+ resource_name[0] = '0';
+ resource_name[1] = '\0';
+ }
+ }
+
+ const IMAGE_RESOURCE_DIRECTORY * const dir = node->raw.resourceDirectory;
+
+ // Indentation.
+ for (size_t i=0; i < node->depth; i++)
+ printf(" ");
+ printf("LIBPE_RDT_RESOURCE_DIRECTORY [dirLevel=%d]: ", node->dirLevel);
+
+ printf("ResDir (%s) Entries:%02u[%02X] (Named:%02u[%02X], ID:%02u[%02X]) TimeDate:%08u[%08X]",
+ resource_name,
+ dir->NumberOfIdEntries + dir->NumberOfNamedEntries,
+ dir->NumberOfIdEntries + dir->NumberOfNamedEntries,
+ dir->NumberOfNamedEntries,
+ dir->NumberOfNamedEntries,
+ dir->NumberOfIdEntries,
+ dir->NumberOfIdEntries,
+ dir->TimeDateStamp,
+ dir->TimeDateStamp
+ );
+
+ if (dir->MajorVersion || dir->MinorVersion)
+ printf(" Vers:%u.%02u", dir->MajorVersion, dir->MinorVersion);
+
+ if (dir->Characteristics)
+ printf(" Char:%08u[%08X]", dir->Characteristics, dir->Characteristics);
+
+ printf("\n");
+ break;
+ }
+ case LIBPE_RDT_DIRECTORY_ENTRY:
+ {
+ // Indentation.
+ for (size_t i=0; i < node->depth; i++)
+ printf(" ");
+ printf("LIBPE_RDT_DIRECTORY_ENTRY [dirLevel=%d]: ", node->dirLevel);
+
+ const IMAGE_RESOURCE_DIRECTORY_ENTRY * const entry = node->raw.directoryEntry;
+
+ if (entry->u0.data.NameIsString) { // entry->u0.Name & IMAGE_RESOURCE_NAME_IS_STRING
+ char res_name[256];
+ pe_resource_name_from_id(ctx, res_name, sizeof(res_name), entry->u0.Name);
+ printf("Name: %s DataEntryOffs: %08u[%08X]\n",
+ res_name, entry->u1.OffsetToData, entry->u1.OffsetToData);
+ } else {
+ printf("ID: %08u[%08X] DataEntryOffs: %08u[%08X]\n",
+ entry->u0.Name, entry->u0.Name, entry->u1.OffsetToData, entry->u1.OffsetToData);
+ }
+ break;
+ }
+ case LIBPE_RDT_DATA_STRING:
+ {
+ const IMAGE_RESOURCE_DATA_STRING_U * const dataString = node->raw.dataString;
+
+ char ascii_string[256];
+ pe_resource_parse_string_u(ctx, ascii_string, sizeof(ascii_string), dataString);
+
+ // Indentation.
+ for (size_t i=0; i < node->depth; i++)
+ printf(" ");
+ printf("LIBPE_RDT_DATA_STRING [dirLevel=%d]: ", node->dirLevel);
+
+ printf("String: %s Length: %02hu\n", ascii_string, dataString->Length);
+ break;
+ }
+ case LIBPE_RDT_DATA_ENTRY:
+ {
+ const IMAGE_RESOURCE_DATA_ENTRY * const data_entry = node->raw.dataEntry;
+
+ // Indentation.
+ for (size_t i=0; i < node->depth; i++)
+ printf(" ");
+ printf("LIBPE_RDT_DATA_ENTRY [dirLevel=%d]: ", node->dirLevel);
+
+ printf("DataRVA: %05u[%05X] DataSize: %05u[%05X] CodePage: %u[%X]\n",
+ data_entry->OffsetToData,
+ data_entry->OffsetToData,
+ data_entry->Size,
+ data_entry->Size,
+ data_entry->CodePage,
+ data_entry->CodePage);
+ break;
+ }
+ }
}
static void pe_resource_debug_nodes(pe_ctx_t *ctx, const pe_resource_node_t *node) {
- if (node == NULL)
- return;
+ if (node == NULL)
+ return;
- pe_resource_debug_node(ctx, node);
+ pe_resource_debug_node(ctx, node);
- pe_resource_debug_nodes(ctx, node->childNode);
- pe_resource_debug_nodes(ctx, node->nextNode);
+ pe_resource_debug_nodes(ctx, node->childNode);
+ pe_resource_debug_nodes(ctx, node->nextNode);
}
#endif
-static pe_resource_node_t *pe_resource_create_node(uint8_t depth, pe_resource_node_type_e type, void *raw_ptr, pe_resource_node_t *parent_node) {
- pe_resource_node_t *node = calloc(1, sizeof(pe_resource_node_t));
- if (node == NULL) {
- // TODO: Handle allocation failure.
- abort();
- }
- node->depth = depth;
- node->type = type;
-
- // Determine directory level.
- if (parent_node != NULL) {
- // node->dirLevel = parent_node->type == LIBPE_RDT_RESOURCE_DIRECTORY && node->type == LIBPE_RDT_DIRECTORY_ENTRY
- node->dirLevel = parent_node->type == LIBPE_RDT_RESOURCE_DIRECTORY
- ? parent_node->dirLevel + 1
- : parent_node->dirLevel;
- } else {
- node->dirLevel = 0; // Only the root directory has dirLevel == 0.
- }
-
- // Establish relationships. Makes the node more human!
- if (parent_node != NULL) {
- node->parentNode = parent_node;
-
- if (parent_node->childNode == NULL) {
- // This is the 1st child node of parent_node.
- parent_node->childNode = node;
- } else {
- // This is NOT the 1st child node of parent_node, so we need to append it to the end of the linked-list.
- pe_resource_node_t *last_child_node = pe_resource_last_child_node(parent_node);
- if (last_child_node != NULL) {
- // Found the last child node. Append our new node.
- last_child_node->nextNode = node;
- }
- }
- }
-
- node->raw.raw_ptr = raw_ptr;
-
- switch (type) {
- default:
- LIBPE_WARNING("Invalid node type");
- break;
- case LIBPE_RDT_RESOURCE_DIRECTORY:
- node->raw.resourceDirectory = raw_ptr;
- break;
- case LIBPE_RDT_DIRECTORY_ENTRY:
- node->raw.directoryEntry = raw_ptr;
- break;
- case LIBPE_RDT_DATA_STRING:
- node->raw.dataString = raw_ptr;
- break;
- case LIBPE_RDT_DATA_ENTRY:
- node->raw.dataEntry = raw_ptr;
- break;
- }
-
- return node;
+static pe_resource_node_t *
+pe_resource_create_node(uint16_t depth, pe_resource_node_type_e type,
+ void *raw_ptr, pe_resource_node_t *parent_node)
+{
+ pe_resource_node_t *node = calloc(1, sizeof(pe_resource_node_t));
+ if (node == NULL) {
+ // TODO: Handle allocation failure.
+ abort();
+ }
+ node->depth = depth;
+ node->type = type;
+
+ // Determine directory level.
+ if (parent_node != NULL) {
+ // node->dirLevel = parent_node->type == LIBPE_RDT_RESOURCE_DIRECTORY &&
+ // node->type == LIBPE_RDT_DIRECTORY_ENTRY
+ node->dirLevel = parent_node->type == LIBPE_RDT_RESOURCE_DIRECTORY
+ ? parent_node->dirLevel + 1
+ : parent_node->dirLevel;
+ } else {
+ node->dirLevel = 0; // Only the root directory has dirLevel == 0.
+ }
+
+ // Establish relationships. Makes the node more human!
+ if (parent_node != NULL) {
+ node->parentNode = parent_node;
+
+ if (parent_node->childNode == NULL) {
+ // This is the 1st child node of parent_node.
+ parent_node->childNode = node;
+ } else {
+ // This is NOT the 1st child node of parent_node, so we need to
+ // append it to the end of the linked-list.
+ pe_resource_node_t *last_child_node
+ = pe_resource_last_child_node(parent_node);
+ if (last_child_node != NULL) {
+ // Found the last child node. Append our new node.
+ last_child_node->nextNode = node;
+ }
+ }
+ }
+
+ node->raw.raw_ptr = raw_ptr;
+
+ switch (type) {
+ default:
+ LIBPE_WARNING("Invalid node type");
+ break;
+ case LIBPE_RDT_RESOURCE_DIRECTORY:
+ node->raw.resourceDirectory = raw_ptr;
+ break;
+ case LIBPE_RDT_DIRECTORY_ENTRY:
+ node->raw.directoryEntry = raw_ptr;
+ break;
+ case LIBPE_RDT_DATA_STRING:
+ node->raw.dataString = raw_ptr;
+ break;
+ case LIBPE_RDT_DATA_ENTRY:
+ node->raw.dataEntry = raw_ptr;
+ break;
+ }
+
+ return node;
}
-static void pe_resource_free_nodes(pe_resource_node_t *node) {
- if (node == NULL)
- return;
+static void pe_resource_free_nodes(pe_resource_node_t *node)
+{
+ if (node == NULL) {
+ return;
+ }
- pe_resource_free_nodes(node->childNode);
- pe_resource_free_nodes(node->nextNode);
+ pe_resource_free_nodes(node->childNode);
+ pe_resource_free_nodes(node->nextNode);
- free(node->name);
- free(node);
+ free(node->name);
+ free(node);
}
-static bool pe_resource_parse_nodes(pe_ctx_t *ctx, pe_resource_node_t *node) {
- switch (node->type) {
- default:
- LIBPE_WARNING("Invalid node type");
- return false;
- case LIBPE_RDT_RESOURCE_DIRECTORY:
- {
- const IMAGE_RESOURCE_DIRECTORY * const resdir_ptr = node->raw.resourceDirectory;
- IMAGE_RESOURCE_DIRECTORY_ENTRY *first_entry_ptr = LIBPE_PTR_ADD(resdir_ptr, sizeof(IMAGE_RESOURCE_DIRECTORY));
- const size_t total_entries = resdir_ptr->NumberOfIdEntries + resdir_ptr->NumberOfNamedEntries;
-
- for (size_t i = 0; i < total_entries; i++) {
- IMAGE_RESOURCE_DIRECTORY_ENTRY *entry = &first_entry_ptr[i];
- if (!pe_can_read(ctx, entry, sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY))) {
- LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DIRECTORY_ENTRY");
- break;
- }
-
- pe_resource_node_t *new_node = pe_resource_create_node(node->depth + 1, LIBPE_RDT_DIRECTORY_ENTRY, entry, node);
- pe_resource_parse_nodes(ctx, new_node);
- }
- break;
- }
- case LIBPE_RDT_DIRECTORY_ENTRY:
- {
- const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry_ptr = node->raw.directoryEntry;
-
- //fprintf(stderr, "DEBUG: id=%#x, dataOffset=%#x\n", entry_ptr->u0.Id, entry_ptr->u1.OffsetToData);
-
- pe_resource_node_t *new_node = NULL;
-
- // This resource has a name?
- if (entry_ptr->u0.data.NameIsString) { // entry->u0.Name & IMAGE_RESOURCE_NAME_IS_STRING
- IMAGE_RESOURCE_DATA_STRING_U *data_string_ptr = LIBPE_PTR_ADD(ctx->cached_data.resources->resource_base_ptr, entry_ptr->u0.data.NameOffset);
- if (!pe_can_read(ctx, data_string_ptr, sizeof(IMAGE_RESOURCE_DATA_STRING_U))) {
- LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DATA_STRING_U");
- return NULL;
- }
-
- node->name = pe_resource_parse_string_u(ctx, NULL, 0, data_string_ptr);
-
- new_node = pe_resource_create_node(node->depth + 1, LIBPE_RDT_DATA_STRING, data_string_ptr, node);
- pe_resource_parse_nodes(ctx, new_node);
- }
-
- // Is it a directory?
- if (entry_ptr->u1.data.DataIsDirectory) { // entry->u1.OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY
- IMAGE_RESOURCE_DIRECTORY *child_resdir_ptr = LIBPE_PTR_ADD(ctx->cached_data.resources->resource_base_ptr, entry_ptr->u1.data.OffsetToDirectory);
- if (!pe_can_read(ctx, child_resdir_ptr, sizeof(IMAGE_RESOURCE_DIRECTORY))) {
- LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DIRECTORY");
- break;
- }
- new_node = pe_resource_create_node(node->depth + 1, LIBPE_RDT_RESOURCE_DIRECTORY, child_resdir_ptr, node);
- } else { // Not a directory
- IMAGE_RESOURCE_DATA_ENTRY *data_entry_ptr = LIBPE_PTR_ADD(ctx->cached_data.resources->resource_base_ptr, entry_ptr->u1.data.OffsetToDirectory);
- if (!pe_can_read(ctx, data_entry_ptr, sizeof(IMAGE_RESOURCE_DATA_ENTRY))) {
- LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DATA_ENTRY");
- break;
- }
- new_node = pe_resource_create_node(node->depth + 1, LIBPE_RDT_DATA_ENTRY, data_entry_ptr, node);
- }
-
- pe_resource_parse_nodes(ctx, new_node);
-
- break;
- }
- case LIBPE_RDT_DATA_STRING:
- {
- const IMAGE_RESOURCE_DATA_STRING_U *data_string_ptr = node->raw.dataString;
- if (!pe_can_read(ctx, data_string_ptr, sizeof(IMAGE_RESOURCE_DATA_STRING_U))) {
- LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DATA_STRING_U");
- break;
- }
-
- // TODO(jweyrich): We should store the result in the node to be useful,
- // but we still don't store specific data in the node, except for its name.
- char *buffer = pe_resource_parse_string_u(ctx, NULL, 0, data_string_ptr);
- fprintf(stderr, "DEBUG: Length=%hu, String=%s\n", data_string_ptr->Length, buffer);
- free(buffer);
- break;
- }
- case LIBPE_RDT_DATA_ENTRY:
- {
- // const IMAGE_RESOURCE_DATA_ENTRY *data_entry_ptr = node->raw.dataEntry;
-
- // fprintf(stderr, "DEBUG: CodePage=%u, OffsetToData=%u[%#x], Reserved=%u[%#x], Size=%u[%#x]\n",
- // data_entry_ptr->CodePage,
- // data_entry_ptr->OffsetToData,
- // data_entry_ptr->OffsetToData,
- // data_entry_ptr->Reserved,
- // data_entry_ptr->Reserved,
- // data_entry_ptr->Size,
- // data_entry_ptr->Size);
-
- ////////////////////////////////////////////////////////////////////////////////////
- // TODO(jweyrich): To be written.
- ////////////////////////////////////////////////////////////////////////////////////
- break;
- }
- }
-
- return true;
+static bool pe_resource_parse_nodes(pe_ctx_t *ctx, pe_resource_node_t *node)
+{
+ switch (node->type) {
+ default:
+ LIBPE_WARNING("Invalid node type");
+ return false;
+ case LIBPE_RDT_RESOURCE_DIRECTORY: {
+ const IMAGE_RESOURCE_DIRECTORY *const resdir_ptr
+ = node->raw.resourceDirectory;
+ IMAGE_RESOURCE_DIRECTORY_ENTRY *first_entry_ptr
+ = LIBPE_PTR_ADD(resdir_ptr, sizeof(IMAGE_RESOURCE_DIRECTORY));
+ const size_t total_entries
+ = resdir_ptr->NumberOfIdEntries + resdir_ptr->NumberOfNamedEntries;
+
+ for (size_t i = 0; i < total_entries; i++) {
+ IMAGE_RESOURCE_DIRECTORY_ENTRY *entry = &first_entry_ptr[i];
+ if (! pe_can_read(ctx, entry,
+ sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY))) {
+ LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DIRECTORY_ENTRY");
+ break;
+ }
+
+ pe_resource_node_t *new_node = pe_resource_create_node(
+ node->depth + 1, LIBPE_RDT_DIRECTORY_ENTRY, entry, node);
+ pe_resource_parse_nodes(ctx, new_node);
+ }
+ break;
+ }
+ case LIBPE_RDT_DIRECTORY_ENTRY: {
+ const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry_ptr
+ = node->raw.directoryEntry;
+
+ // fprintf(stderr, "DEBUG: id=%#x, dataOffset=%#x\n", entry_ptr->u0.Id,
+ // entry_ptr->u1.OffsetToData);
+
+ pe_resource_node_t *new_node = NULL;
+
+ // This resource has a name?
+ if (entry_ptr->u0.data.NameIsString) { // entry->u0.Name &
+ // IMAGE_RESOURCE_NAME_IS_STRING
+ IMAGE_RESOURCE_DATA_STRING_U *data_string_ptr
+ = LIBPE_PTR_ADD(ctx->cached_data.resources->resource_base_ptr,
+ entry_ptr->u0.data.NameOffset);
+ if (! pe_can_read(ctx, data_string_ptr,
+ sizeof(IMAGE_RESOURCE_DATA_STRING_U))) {
+ LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DATA_STRING_U");
+ return false;
+ }
+
+ node->name
+ = pe_resource_parse_string_u(ctx, NULL, 0, data_string_ptr);
+
+ new_node = pe_resource_create_node(
+ node->depth + 1, LIBPE_RDT_DATA_STRING, data_string_ptr, node);
+ pe_resource_parse_nodes(ctx, new_node);
+ }
+
+ // Is it a directory?
+ if (entry_ptr->u1.data.DataIsDirectory) {
+ // entry->u1.OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY
+ IMAGE_RESOURCE_DIRECTORY *child_resdir_ptr
+ = LIBPE_PTR_ADD(ctx->cached_data.resources->resource_base_ptr,
+ entry_ptr->u1.data.OffsetToDirectory);
+ if (! pe_can_read(ctx, child_resdir_ptr,
+ sizeof(IMAGE_RESOURCE_DIRECTORY))) {
+ LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DIRECTORY");
+ break;
+ }
+ new_node = pe_resource_create_node(node->depth + 1,
+ LIBPE_RDT_RESOURCE_DIRECTORY,
+ child_resdir_ptr, node);
+ } else { // Not a directory
+ IMAGE_RESOURCE_DATA_ENTRY *data_entry_ptr
+ = LIBPE_PTR_ADD(ctx->cached_data.resources->resource_base_ptr,
+ entry_ptr->u1.data.OffsetToDirectory);
+ if (! pe_can_read(ctx, data_entry_ptr,
+ sizeof(IMAGE_RESOURCE_DATA_ENTRY))) {
+ LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DATA_ENTRY");
+ break;
+ }
+ new_node = pe_resource_create_node(
+ node->depth + 1, LIBPE_RDT_DATA_ENTRY, data_entry_ptr, node);
+ }
+
+ pe_resource_parse_nodes(ctx, new_node);
+
+ break;
+ }
+ case LIBPE_RDT_DATA_STRING: {
+ const IMAGE_RESOURCE_DATA_STRING_U *data_string_ptr
+ = node->raw.dataString;
+ if (! pe_can_read(ctx, data_string_ptr,
+ sizeof(IMAGE_RESOURCE_DATA_STRING_U))) {
+ LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DATA_STRING_U");
+ break;
+ }
+
+ // TODO(jweyrich): We should store the result in the node to be useful,
+ // but we still don't store specific data in the node, except for its
+ // name.
+ char *buffer
+ = pe_resource_parse_string_u(ctx, NULL, 0, data_string_ptr);
+ fprintf(stderr, "DEBUG: Length=%hu, String=%s\n",
+ data_string_ptr->Length, buffer);
+ free(buffer);
+ break;
+ }
+ case LIBPE_RDT_DATA_ENTRY: {
+ // const IMAGE_RESOURCE_DATA_ENTRY *data_entry_ptr =
+ // node->raw.dataEntry;
+
+ // fprintf(stderr, "DEBUG: CodePage=%u, OffsetToData=%u[%#x],
+ // Reserved=%u[%#x], Size=%u[%#x]\n", data_entry_ptr->CodePage,
+ // data_entry_ptr->OffsetToData,
+ // data_entry_ptr->OffsetToData,
+ // data_entry_ptr->Reserved,
+ // data_entry_ptr->Reserved,
+ // data_entry_ptr->Size,
+ // data_entry_ptr->Size);
+
+ ////////////////////////////////////////////////////////////////////////////////////
+ // TODO(jweyrich): To be written.
+ ////////////////////////////////////////////////////////////////////////////////////
+ break;
+ }
+ }
+
+ return true;
}
-static pe_resource_node_t *pe_resource_parse(pe_ctx_t *ctx, void *resource_base_ptr) {
- pe_resource_node_t *root_node = pe_resource_create_node(0, LIBPE_RDT_RESOURCE_DIRECTORY, resource_base_ptr, NULL);
- pe_resource_parse_nodes(ctx, root_node);
- //pe_resource_debug_nodes(ctx, root_node);
- return root_node;
+static pe_resource_node_t *pe_resource_parse(pe_ctx_t *ctx,
+ void *resource_base_ptr)
+{
+ pe_resource_node_t *root_node = pe_resource_create_node(
+ 0, LIBPE_RDT_RESOURCE_DIRECTORY, resource_base_ptr, NULL);
+ pe_resource_parse_nodes(ctx, root_node);
+ // pe_resource_debug_nodes(ctx, root_node);
+ return root_node;
}
-static void *pe_resource_base_ptr(pe_ctx_t *ctx) {
- const IMAGE_DATA_DIRECTORY * const directory = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_RESOURCE);
- if (directory == NULL) {
- LIBPE_WARNING("Resource directory does not exist")
- return NULL;
- }
- if (directory->VirtualAddress == 0) {
- LIBPE_WARNING("Resource directory VA is zero")
- return NULL;
- }
- if (directory->Size == 0) {
- // Windows does not seem to care about the size
- // so we just continue with a warning
- LIBPE_WARNING("Resource directory size is 0")
- }
-
- const uintptr_t offset = pe_rva2ofs(ctx, directory->VirtualAddress);
- void *ptr = LIBPE_PTR_ADD(ctx->map_addr, offset);
- if (!pe_can_read(ctx, ptr, sizeof(IMAGE_RESOURCE_DIRECTORY))) {
- LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DIRECTORY");
- return NULL;
- }
-
- return ptr;
+static void *pe_resource_base_ptr(pe_ctx_t *ctx)
+{
+ const IMAGE_DATA_DIRECTORY *const directory
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_RESOURCE);
+ if (directory == NULL) {
+ LIBPE_WARNING("Resource directory does not exist");
+ return NULL;
+ }
+ if (directory->VirtualAddress == 0) {
+ LIBPE_WARNING("Resource directory VA is zero");
+ return NULL;
+ }
+ if (directory->Size == 0) {
+ // Windows does not seem to care about the size
+ // so we just continue with a warning
+ LIBPE_WARNING("Resource directory size is 0");
+ }
+
+ const uintptr_t offset = pe_rva2ofs(ctx, directory->VirtualAddress);
+ void *ptr = LIBPE_PTR_ADD(ctx->map_addr, offset);
+ if (! pe_can_read(ctx, ptr, sizeof(IMAGE_RESOURCE_DIRECTORY))) {
+ LIBPE_WARNING("Cannot read IMAGE_RESOURCE_DIRECTORY");
+ return NULL;
+ }
+
+ return ptr;
}
-bool pe_resource_contains_version_node(const pe_resource_node_t *node) {
- if (node->type != LIBPE_RDT_DIRECTORY_ENTRY)
+bool pe_resource_contains_version_node(const pe_resource_node_t *node)
+{
+ if (node->type != LIBPE_RDT_DIRECTORY_ENTRY) {
return false;
- if (node->dirLevel != LIBPE_RDT_LEVEL1) // dirLevel == 1 belongs to the resource type directory.
+ }
+ if (node->dirLevel != LIBPE_RDT_LEVEL1) { // dirLevel == 1 belongs to the
+ // resource type directory.
return false;
+ }
return node->raw.directoryEntry->u0.data.NameOffset == RT_VERSION;
}
-const VS_FIXEDFILEINFO *pe_resource_get_fixedfileinfo(const pe_ctx_t *ctx, const pe_resource_node_t *node, void **child_out)
+const VS_FIXEDFILEINFO *
+pe_resource_get_fixedfileinfo(const pe_ctx_t *ctx,
+ const pe_resource_node_t *node, void **child_out)
{
- if (node == NULL)
+ if (node == NULL) {
return NULL;
+ }
VS_FIXEDFILEINFO *info_ptr = NULL;
pe_resource_node_search_result_t search_result = {0};
- pe_resource_search_nodes(&search_result, node, pe_resource_contains_version_node);
+ pe_resource_search_nodes(&search_result, node,
+ pe_resource_contains_version_node);
pe_resource_node_search_result_item_t *result_item = {0};
- LL_FOREACH(search_result.items, result_item) {
- const pe_resource_node_t *version_node = pe_resource_find_node_by_type_and_level(result_item->node, LIBPE_RDT_DATA_ENTRY, LIBPE_RDT_LEVEL3);
+ LL_FOREACH (search_result.items, result_item) {
+ const pe_resource_node_t *version_node
+ = pe_resource_find_node_by_type_and_level(
+ result_item->node, LIBPE_RDT_DATA_ENTRY, LIBPE_RDT_LEVEL3);
if (version_node != NULL) {
- const uint64_t data_offset = pe_rva2ofs(ctx, version_node->raw.dataEntry->OffsetToData);
+ const uint64_t data_offset
+ = pe_rva2ofs(ctx, version_node->raw.dataEntry->OffsetToData);
const size_t data_size = version_node->raw.dataEntry->Size;
- const VS_VERSIONINFO_HEAD *data_ptr = (VS_VERSIONINFO_HEAD *) LIBPE_PTR_ADD(ctx->map_addr, data_offset);
- if (!pe_can_read(ctx, data_ptr, data_size)) {
+ const VS_VERSIONINFO_HEAD *data_ptr
+ = (VS_VERSIONINFO_HEAD *) LIBPE_PTR_ADD(ctx->map_addr,
+ data_offset);
+ if (! pe_can_read(ctx, data_ptr, data_size)) {
LIBPE_WARNING("Cannot read VS_FIXEDFILEINFO");
break;
}
- const uint16_t *szkey = LIBPE_PTR_ADD(data_ptr, sizeof(VS_VERSIONINFO_HEAD));
+ const uint16_t *szkey
+ = LIBPE_PTR_ADD(data_ptr, sizeof(VS_VERSIONINFO_HEAD));
// char key[256];
// mbstate_t state = {0};
int key_size = 0;
// We could test this against the string "VS_VERSIONINFO"
- for(;key_size < 256;++key_size) {
+ for (; key_size < 256; ++key_size) {
// c16rtomb(&key[key_size], szkey[key_size], &state);
// We are looking for the end of the string
// Sadly there is no portable function to help us with
// 16 bit charatcers
- if( szkey[key_size] == 0 ) break;
+ if (szkey[key_size] == 0) {
+ break;
+ }
}
// printf("\n%s\n", key);
- int info_offset = (key_size*sizeof(uint16_t)) + sizeof(VS_VERSIONINFO_HEAD);
+ int info_offset = (key_size * (int) sizeof(uint16_t))
+ + (int) sizeof(VS_VERSIONINFO_HEAD);
// Align to next 32bit/4byte
- info_offset += 4-(info_offset%4);
+ info_offset += 4 - (info_offset % 4);
- info_ptr = (VS_FIXEDFILEINFO *) LIBPE_PTR_ADD(data_ptr, info_offset);
+ info_ptr
+ = (VS_FIXEDFILEINFO *) LIBPE_PTR_ADD(data_ptr, info_offset);
- if( info_ptr->dwSignature != 0xFEEF04BD ) {
+ if (info_ptr->dwSignature != 0xFEEF04BD) {
LIBPE_WARNING("Wrong signature for VS_FIXEDFILEINFO");
info_ptr = NULL;
}
- if(child_out != NULL) {
- *child_out = LIBPE_PTR_ADD(data_ptr, info_offset + data_ptr->wValueLength);
+ if (child_out != NULL) {
+ *child_out = LIBPE_PTR_ADD(
+ data_ptr, info_offset + data_ptr->wValueLength);
}
break;
@@ -675,29 +775,36 @@ const VS_FIXEDFILEINFO *pe_resource_get_fixedfileinfo(const pe_ctx_t *ctx, const
return info_ptr;
}
-pe_resources_t *pe_resources(pe_ctx_t *ctx) {
- if (ctx->cached_data.resources != NULL)
- return ctx->cached_data.resources;
+pe_resources_t *pe_resources(pe_ctx_t *ctx)
+{
+ if (ctx->cached_data.resources != NULL) {
+ return ctx->cached_data.resources;
+ }
- pe_resources_t *res_ptr = calloc(1, sizeof(pe_resources_t));
- if (res_ptr == NULL) {
- // TODO: Handle allocation failure.
- abort();
- }
+ pe_resources_t *res_ptr = calloc(1, sizeof(pe_resources_t));
+ if (res_ptr == NULL) {
+ // TODO: Handle allocation failure.
+ abort();
+ }
- ctx->cached_data.resources = res_ptr;
- ctx->cached_data.resources->err = LIBPE_E_OK;
- ctx->cached_data.resources->resource_base_ptr = pe_resource_base_ptr(ctx); // Various parts of the parsing rely on `resource_base_ptr`.
- if (ctx->cached_data.resources->resource_base_ptr != NULL) {
- ctx->cached_data.resources->root_node = pe_resource_parse(ctx, ctx->cached_data.resources->resource_base_ptr);
- }
+ ctx->cached_data.resources = res_ptr;
+ ctx->cached_data.resources->err = LIBPE_E_OK;
+ ctx->cached_data.resources->resource_base_ptr = pe_resource_base_ptr(
+ ctx); // Various parts of the parsing rely on `resource_base_ptr`.
+ if (ctx->cached_data.resources->resource_base_ptr != NULL) {
+ ctx->cached_data.resources->root_node = pe_resource_parse(
+ ctx, ctx->cached_data.resources->resource_base_ptr);
+ }
- return ctx->cached_data.resources;
+ return ctx->cached_data.resources;
}
-void pe_resources_dealloc(pe_resources_t *obj) {
- if (obj == NULL)
- return;
- pe_resource_free_nodes(obj->root_node);
- free(obj);
+void pe_resources_dealloc(pe_resources_t *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
+ pe_resource_free_nodes(obj->root_node);
+ free(obj);
}
+
diff --git a/lib/libpe/security.c b/lib/libpe/security.c
new file mode 100644
index 00000000..a69758b7
--- /dev/null
+++ b/lib/libpe/security.c
@@ -0,0 +1,126 @@
+/*
+ libpe - the PE library
+
+ Copyright (C) 2010 - 2025 libpe authors
+
+ This file is part of libpe.
+
+ libpe is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libpe is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libpe. If not, see .
+*/
+
+#include "libpe/dir_security.h"
+
+#include "libpe/pe.h"
+#include "libpe/macros.h"
+
+#include
+#include
+
+static inline uint32_t roundBy8(uint32_t x) { return (x + 7) & 0xfffffff8; }
+
+unsigned int pe_certificate_count(pe_ctx_t *ctx)
+{
+ const IMAGE_DATA_DIRECTORY *const directory
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_SECURITY);
+ if (directory == NULL) {
+ return 0;
+ }
+
+ if (directory->VirtualAddress == 0 || directory->Size == 0) {
+ return 0;
+ }
+
+ unsigned int certs = 0;
+ // This a file pointer rather than a common RVA.
+ uint32_t fileOffset = directory->VirtualAddress;
+
+ while (fileOffset - directory->VirtualAddress < directory->Size) {
+ // Read the size of this WIN_CERTIFICATE
+ uint32_t *dwLength_ptr = LIBPE_PTR_ADD(ctx->map_addr, fileOffset);
+ if (!pe_can_read(ctx, dwLength_ptr, sizeof(uint32_t))) {
+ // TODO: Should we report something?
+ return 0;
+ }
+ ++certs;
+ // Type punning
+ uint32_t dwLength = *(uint32_t *)dwLength_ptr;
+ fileOffset += roundBy8(dwLength);
+
+ if (fileOffset - directory->VirtualAddress > directory->Size) {
+ LIBPE_WARNING("either the attribute certificate table or the Size "
+ "field is corrupted");
+ break;
+ }
+ }
+
+ return certs;
+}
+
+uint32_t pe_certificates(pe_ctx_t *ctx, WIN_CERTIFICATE ***out)
+{
+ uint32_t count = pe_certificate_count(ctx);
+
+ if (count == 0) {
+ return count;
+ }
+
+ WIN_CERTIFICATE **certs;
+ certs = malloc(count * sizeof(WIN_CERTIFICATE *));
+
+ const IMAGE_DATA_DIRECTORY *const directory
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_SECURITY);
+ if (directory == NULL) {
+ free(certs);
+ return 0;
+ }
+
+ if (directory->VirtualAddress == 0 || directory->Size == 0) {
+ free(certs);
+ return 0;
+ }
+
+ // This is a file pointer rather than a common RVA.
+ uint32_t fileOffset = directory->VirtualAddress;
+
+ // Doing a second round trip to not having to allocate memory in a loop
+ for (uint32_t i = 0; i < count; ++i) {
+ // Read the size of this WIN_CERTIFICATE
+ uint32_t *dwLength_ptr = LIBPE_PTR_ADD(ctx->map_addr, fileOffset);
+ if (!pe_can_read(ctx, dwLength_ptr, sizeof(uint32_t))) {
+ // No warning as we already warned in pe_certificate_count
+ free(certs);
+ return 0;
+ }
+ // Type punning
+ uint32_t dwLength = *(uint32_t *)dwLength_ptr;
+
+ certs[i] = LIBPE_PTR_ADD(ctx->map_addr, fileOffset);
+ if (!pe_can_read(ctx, certs[i], dwLength)) {
+ free(certs);
+ return 0;
+ }
+
+ // Offset to the next certificate.
+ fileOffset += roundBy8(certs[i]->dwLength);
+
+ if (fileOffset - directory->VirtualAddress > directory->Size) {
+ // No warning as we already warned in pe_certificate_count
+ break;
+ }
+ }
+
+ *out = certs;
+ return count;
+}
+
diff --git a/lib/libpe/utils.c b/lib/libpe/utils.c
index 5ac12550..7fdbfda5 100644
--- a/lib/libpe/utils.c
+++ b/lib/libpe/utils.c
@@ -21,215 +21,243 @@
*/
#include "libpe/utils.h"
+
+#include "compat.h"
#include "libpe/error.h"
-#include
+
#include
+#include
+#include
+#include
#include
#include
#include
-#include
#include
-#include
#include
-#include
-bool pe_utils_str_ends_with(const char* text, const char* pattern)
+#ifndef _MSC_VER
+#include
+#endif
+
+bool pe_utils_str_ends_with(const char *text, const char *pattern)
{
- if (!text || !pattern)
- return false;
+ if (! text || ! pattern) {
+ return false;
+ }
- const size_t n = strspn(pattern, text);
- if (*(pattern + n) != '\0')
- return false;
+ const size_t n = strspn(pattern, text);
+ if (*(pattern + n) != '\0') {
+ return false;
+ }
- return !memcmp(text + strlen(text) - n, pattern, n);
+ return ! memcmp(text + strlen(text) - n, pattern, n);
}
-char *pe_utils_str_inplace_ltrim(char *str) {
- return str + strspn( str, " \f\n\r\t\v" );
+char *pe_utils_str_inplace_ltrim(char *str)
+{
+ return str + strspn(str, " \f\n\r\t\v");
}
-char *pe_utils_str_inplace_rtrim(char *str) {
- const size_t length = strlen(str);
- char *ptr = str + length - 1;
+char *pe_utils_str_inplace_rtrim(char *str)
+{
+ const size_t length = strlen(str);
+ char *ptr = str + length - 1;
- // If str points to a empty string, ptr will point
- // to a place before str...
- while (ptr > str && isspace(*ptr))
- ptr--;
+ // If str points to a empty string, ptr will point
+ // to a place before str...
+ while (ptr > str && isspace(*ptr)) {
+ ptr--;
+ }
- // Move back to space.
- // Replace it with '\0'.
- *++ptr = 0;
+ // Move back to space.
+ // Replace it with '\0'.
+ *++ptr = 0;
- return str;
+ return str;
}
-char *pe_utils_str_inplace_trim(char *str) {
- char *ptr;
+char *pe_utils_str_inplace_trim(char *str)
+{
+ char *ptr;
- ptr = pe_utils_str_inplace_ltrim( str );
- return pe_utils_str_inplace_rtrim( ptr );
+ ptr = pe_utils_str_inplace_ltrim(str);
+ return pe_utils_str_inplace_rtrim(ptr);
}
-char *pe_utils_str_array_join(char *strings[], size_t count, char delimiter) {
- size_t i;
-
- if (strings == NULL || strings[0] == NULL)
- return strdup("");
-
- // Count how much memory the resulting string is going to need,
- // considering delimiters for each string. The last delimiter will
- // be a NUL terminator;
- size_t result_length = 0;
- for (i = 0; i < count; i++) {
- result_length += strlen(strings[i]) + 1;
- }
-
- // Allocate the resulting string.
- char *result = malloc(result_length);
- if (result == NULL)
- return NULL; // Return NULL because it failed miserably!
-
- // FIX: Instead of copying char by char, uses sprintf/strcpy to do it.
- char *p;
-
- p = result;
- for ( i = 0; i < count - 1; i++ )
- {
- int size;
-
- size = sprintf( p, "%s%c", strings[i], delimiter );
- p += size;
- }
- strcpy( p, strings[i] );
-
-//
-// // Null terminate it.
-// result[--result_length] = '\0';
-//
-// // Join all strings.
-// char **current_string = strings;
-// char *current_char = current_string[0];
-// for (size_t i = 0; i < result_length; i++) {
-// if (*current_char != '\0') {
-// result[i] = *current_char++;
-// } else {
-// // Reached the end of a string. Add a delimiter and move to the next one.
-// result[i] = delimiter;
-// current_string++;
-// current_char = current_string[0];
-// }
-// }
-
- return result;
+char *pe_utils_str_array_join(char *strings[], size_t count, char delimiter)
+{
+ size_t i;
+
+ if (strings == NULL || strings[0] == NULL) {
+ return readpe_strdup("");
+ }
+
+ // Count how much memory the resulting string is going to need,
+ // considering delimiters for each string. The last delimiter will
+ // be a NUL terminator;
+ size_t result_length = 0;
+ for (i = 0; i < count; i++) {
+ result_length += strlen(strings[i]) + 1;
+ }
+
+ // Allocate the resulting string.
+ char *result = malloc(result_length);
+ if (result == NULL) {
+ return NULL; // Return NULL because it failed miserably!
+ }
+
+ // FIX: Instead of copying char by char, uses sprintf/strcpy to do it.
+ char *p;
+
+ p = result;
+ for (i = 0; i < count - 1; i++) {
+ int size;
+
+ size = sprintf(p, "%s%c", strings[i], delimiter);
+ p += size;
+ }
+ strcpy(p, strings[i]);
+
+ //
+ // // Null terminate it.
+ // result[--result_length] = '\0';
+ //
+ // // Join all strings.
+ // char **current_string = strings;
+ // char *current_char = current_string[0];
+ // for (size_t i = 0; i < result_length; i++) {
+ // if (*current_char != '\0') {
+ // result[i] = *current_char++;
+ // } else {
+ // // Reached the end of a string. Add a delimiter and move to
+ // the
+ // next one. result[i] = delimiter; current_string++;
+ // current_char = current_string[0];
+ // }
+ // }
+
+ return result;
}
-static char windows1252_char( uint16_t chr )
+static char windows1252_char(uint16_t chr)
{
- // windows-1252 Unicode codepoints from 0x80 to 0x9f.
- // These 32 unicode codepoints was taken from Wikipedia:
- // https://en.wikipedia.org/wiki/Windows-1252
- static const uint16_t w1252chrs[] = {
- 0x20ac,
- 0, // invalid
- 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030,
- 0x0160, 0x2039, 0x0152,
- 0, // invalid
- 0x017d,
- 0, // invalid
- 0, // invalid
- 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc,
- 0x2122, 0x0161, 0x203a, 0x0153,
- 0, // invalid
- 0x017e, 0x0178
- };
-
- // Return any char in range of ASCII or ISO-8859-1.
- // FIXME: 0xa0 is a 'non breaking space'. It could be converted to ' ',
- // but I didn't. Feel free to do it if you need.
- if ( chr <= 0x7f || ( chr >= 0xa0 && chr <= 0xff ) )
- // if ( chr == 0xa0 ) return ' '; else
- return chr;
-
- // Return any char inside WINDOWS-1252 codepage range of 0x80 to 0x9f.
- for ( unsigned int i = 0; i < sizeof w1252chrs / sizeof w1252chrs[0]; i++ )
- if ( chr == w1252chrs[i] )
- return 0x80 + i;
-
- // Any other char returns 0 (to ignore).
+ // windows-1252 Unicode codepoints from 0x80 to 0x9f.
+ // These 32 unicode codepoints was taken from Wikipedia:
+ // https://en.wikipedia.org/wiki/Windows-1252
+ static const uint16_t w1252chrs[]
+ = {0x20ac,
+ 0, // invalid
+ 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152,
+ 0, // invalid
+ 0x017d,
+ 0, // invalid
+ 0, // invalid
+ 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013,
+ 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153,
+ 0, // invalid
+ 0x017e, 0x0178};
+
+ // Return any char in range of ASCII or ISO-8859-1.
+ // FIXME: 0xa0 is a 'non breaking space'. It could be converted to ' ',
+ // but I didn't. Feel free to do it if you need.
+ if (chr <= 0x7f || (chr >= 0xa0 && chr <= 0xff)) {
+ // if ( chr == 0xa0 ) return ' '; else
+ return (char) chr;
+ }
+
+ // Return any char inside WINDOWS-1252 codepage range of 0x80 to 0x9f.
+ for (unsigned int i = 0; i < sizeof w1252chrs / sizeof w1252chrs[0]; i++) {
+ if (chr == w1252chrs[i]) {
+ return (char) (0x80 + i);
+ }
+ }
+
+ // Any other char returns 0 (to ignore).
return 0;
}
-void pe_utils_str_widechar2ascii(char *output, size_t output_size, const char *widechar, size_t widechar_count) {
- // FIX: Quick & dirty UFT16 to WINDOWS-1252 conversion
- size_t length = pe_utils_min(output_size - 1, widechar_count);
- uint16_t *p = (uint16_t *)widechar;
- while (length--) {
- char c = windows1252_char( *p );
+void pe_utils_str_widechar2ascii(char *output, size_t output_size,
+ const char *widechar, size_t widechar_count)
+{
+ // FIX: Quick & dirty UFT16 to WINDOWS-1252 conversion
+ size_t length = pe_utils_min(output_size - 1, widechar_count);
+ uint16_t *p = (uint16_t *) widechar;
+ while (length--) {
+ char c = windows1252_char(*p);
- // ignores "invalid" char.
- if ( c )
- *output++ = c;
+ // ignores "invalid" char.
+ if (c) {
+ *output++ = c;
+ }
- p++;
- }
+ p++;
+ }
- *output = '\0';
+ *output = '\0';
}
// FIX: Don't need this here. Only used in pesec.c!
#if 0
int pe_utils_round_up(int num_to_round, int multiple) {
- if (multiple == 0)
- return 0;
+ if (multiple == 0)
+ return 0;
- return (num_to_round + multiple - 1) / multiple * multiple;
+ return (num_to_round + multiple - 1) / multiple * multiple;
}
#endif
// FIXME: Don't need to open the file!
// FIXME: I believe I saw the same routine inside another function in pe.c.
-int pe_utils_is_file_readable(const char *path) {
- // Open the file.
- const int fd = open(path, O_RDWR);
- if (fd == -1) {
- //perror("open");
- return LIBPE_E_OPEN_FAILED;
- }
-
- // Stat the fd to retrieve the file informations.
- // If file is a symlink, fstat will stat the pointed file, not the link.
- struct stat stat;
- int ret = fstat(fd, &stat);
- if (ret == -1) {
- close(fd);
- //perror("fstat");
- return LIBPE_E_FSTAT_FAILED;
- }
-
- // Check if we're dealing with a regular file.
- if (!S_ISREG(stat.st_mode)) {
- close(fd);
- //fprintf(stderr, "%s is not a file\n", path);
- return LIBPE_E_NOT_A_FILE;
- }
-
- close(fd);
-
- return LIBPE_E_OK;
+int pe_utils_is_file_readable(const char *path)
+{
+ // Open the file.
+ const int fd = open(path, O_RDWR);
+ if (fd == -1) {
+ // perror("open");
+ return LIBPE_E_OPEN_FAILED;
+ }
+
+ // Stat the fd to retrieve the file informations.
+ // If file is a symlink, fstat will stat the pointed file, not the link.
+ struct stat stat;
+ int ret = fstat(fd, &stat);
+ if (ret == -1) {
+ close(fd);
+ // perror("fstat");
+ return LIBPE_E_FSTAT_FAILED;
+ }
+
+ // Check if we're dealing with a regular file.
+ if (! S_ISREG(stat.st_mode)) {
+ close(fd);
+ // fprintf(stderr, "%s is not a file\n", path);
+ return LIBPE_E_NOT_A_FILE;
+ }
+
+ close(fd);
+
+ return LIBPE_E_OK;
}
// IMPORTANT: This is not thread-safe - not reentrant.
-const char *pe_utils_get_homedir(void) {
- const char *homedir = getenv("HOME");
- if (homedir != NULL)
- return homedir;
-
- // FIXME: Instead of using getpwuid() we could use
- // getpwuid_r() to make this function 'thread-safe'.
- errno = 0;
- struct passwd *pwd = getpwuid(getuid());
-
- return pwd == NULL ? NULL : pwd->pw_dir;
+const char *pe_utils_get_homedir(void)
+{
+#ifndef _MSC_VER
+ const char *homedir = getenv("HOME");
+ if (homedir != NULL) {
+ return homedir;
+ }
+
+ // FIXME: Instead of using getpwuid() we could use
+ // getpwuid_r() to make this function 'thread-safe'.
+ errno = 0;
+ struct passwd *pwd = getpwuid(getuid());
+
+ return pwd == NULL ? NULL : pwd->pw_dir;
+#else
+ // TODO
+ return NULL;
+#endif
}
+
diff --git a/lib/libpe/version.h.in b/lib/libpe/version.h.in
new file mode 100644
index 00000000..860645cf
--- /dev/null
+++ b/lib/libpe/version.h.in
@@ -0,0 +1,9 @@
+
+#pragma once
+#ifndef LIBPE_VERSION_H
+#define LIBPE_VERSION_H
+
+const char *LIBPE_VERSION = "@libpe_VERSION@";
+
+#endif
+
diff --git a/lib/libudis86/libudis86/Makefile.am b/lib/libudis86/libudis86/Makefile.am
deleted file mode 100644
index f822f5c3..00000000
--- a/lib/libudis86/libudis86/Makefile.am
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# -- udis86/libudis86
-#
-
-PYTHON = @PYTHON@
-OPTABLE = @top_srcdir@/docs/x86/optable.xml
-
-MAINTAINERCLEANFILES = Makefile.in
-
-lib_LTLIBRARIES = libudis86.la
-
-libudis86_la_SOURCES = \
- itab.c \
- decode.c \
- syn.c \
- syn-intel.c \
- syn-att.c \
- udis86.c \
- udint.h \
- syn.h \
- decode.h
-
-include_ladir = ${includedir}/libudis86
-include_la_HEADERS = \
- types.h \
- extern.h \
- itab.h
-
-
-BUILT_SOURCES = \
- itab.c \
- itab.h
-
-#
-# DLLs may not contain undefined symbol references.
-# We have the linker check this explicitly.
-#
-if TARGET_WINDOWS
-libudis86_la_LDFLAGS = -no-undefined -version-info 0:0:0
-endif
-
-itab.c itab.h: $(OPTABLE) \
- $(top_srcdir)/scripts/ud_itab.py \
- $(top_srcdir)/scripts/ud_opcode.py \
- $(top_srcdir)/scripts/ud_optable.py
- $(PYTHON) $(top_srcdir)/scripts/ud_itab.py $(OPTABLE) $(srcdir)
-
-
-clean-local:
- rm -rf $(BUILT_SOURCES)
-
-maintainer-clean-local:
diff --git a/lib/libudis86/libudis86/Makefile.in b/lib/libudis86/libudis86/Makefile.in
deleted file mode 100644
index 1a54c00d..00000000
--- a/lib/libudis86/libudis86/Makefile.in
+++ /dev/null
@@ -1,692 +0,0 @@
-# Makefile.in generated by automake 1.13.1 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994-2012 Free Software Foundation, Inc.
-
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-@SET_MAKE@
-
-#
-# -- udis86/libudis86
-#
-
-
-VPATH = @srcdir@
-am__make_dryrun = \
- { \
- am__dry=no; \
- case $$MAKEFLAGS in \
- *\\[\ \ ]*) \
- echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
- | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
- *) \
- for am__flg in $$MAKEFLAGS; do \
- case $$am__flg in \
- *=*|--*) ;; \
- *n*) am__dry=yes; break;; \
- esac; \
- done;; \
- esac; \
- test $$am__dry = yes; \
- }
-pkgdatadir = $(datadir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = @build@
-host_triplet = @host@
-subdir = libudis86
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
- $(top_srcdir)/build/depcomp $(include_la_HEADERS)
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/build/m4/libtool.m4 \
- $(top_srcdir)/build/m4/ltoptions.m4 \
- $(top_srcdir)/build/m4/ltsugar.m4 \
- $(top_srcdir)/build/m4/ltversion.m4 \
- $(top_srcdir)/build/m4/lt~obsolete.m4 \
- $(top_srcdir)/m4/ax_compare_version.m4 \
- $(top_srcdir)/m4/ax_prog_sphinx_version.m4 \
- $(top_srcdir)/m4/ax_prog_yasm_version.m4 \
- $(top_srcdir)/m4/ax_with_prog.m4 \
- $(top_srcdir)/m4/ax_with_python.m4 $(top_srcdir)/configure.ac
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
- $(ACLOCAL_M4)
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/config.h
-CONFIG_CLEAN_FILES =
-CONFIG_CLEAN_VPATH_FILES =
-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
-am__vpath_adj = case $$p in \
- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
- *) f=$$p;; \
- esac;
-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
-am__install_max = 40
-am__nobase_strip_setup = \
- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
-am__nobase_strip = \
- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
-am__nobase_list = $(am__nobase_strip_setup); \
- for p in $$list; do echo "$$p $$p"; done | \
- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
- if (++n[$$2] == $(am__install_max)) \
- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
- END { for (dir in files) print dir, files[dir] }'
-am__base_list = \
- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
-am__uninstall_files_from_dir = { \
- test -z "$$files" \
- || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
- || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
- $(am__cd) "$$dir" && rm -f $$files; }; \
- }
-am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(include_ladir)"
-LTLIBRARIES = $(lib_LTLIBRARIES)
-libudis86_la_LIBADD =
-am_libudis86_la_OBJECTS = itab.lo decode.lo syn.lo syn-intel.lo \
- syn-att.lo udis86.lo
-libudis86_la_OBJECTS = $(am_libudis86_la_OBJECTS)
-AM_V_lt = $(am__v_lt_@AM_V@)
-am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
-am__v_lt_0 = --silent
-am__v_lt_1 =
-libudis86_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(libudis86_la_LDFLAGS) $(LDFLAGS) -o $@
-AM_V_P = $(am__v_P_@AM_V@)
-am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
-am__v_P_0 = false
-am__v_P_1 = :
-AM_V_GEN = $(am__v_GEN_@AM_V@)
-am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
-am__v_GEN_0 = @echo " GEN " $@;
-am__v_GEN_1 =
-AM_V_at = $(am__v_at_@AM_V@)
-am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
-am__v_at_0 = @
-am__v_at_1 =
-DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
-depcomp = $(SHELL) $(top_srcdir)/build/depcomp
-am__depfiles_maybe = depfiles
-am__mv = mv -f
-COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
- $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
- $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
- $(AM_CFLAGS) $(CFLAGS)
-AM_V_CC = $(am__v_CC_@AM_V@)
-am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
-am__v_CC_0 = @echo " CC " $@;
-am__v_CC_1 =
-CCLD = $(CC)
-LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(AM_LDFLAGS) $(LDFLAGS) -o $@
-AM_V_CCLD = $(am__v_CCLD_@AM_V@)
-am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
-am__v_CCLD_0 = @echo " CCLD " $@;
-am__v_CCLD_1 =
-SOURCES = $(libudis86_la_SOURCES)
-DIST_SOURCES = $(libudis86_la_SOURCES)
-am__can_run_installinfo = \
- case $$AM_UPDATE_INFO_DIR in \
- n|no|NO) false;; \
- *) (install-info --version) >/dev/null 2>&1;; \
- esac
-HEADERS = $(include_la_HEADERS)
-am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
-# Read a list of newline-separated strings from the standard input,
-# and print each of them once, without duplicates. Input order is
-# *not* preserved.
-am__uniquify_input = $(AWK) '\
- BEGIN { nonempty = 0; } \
- { items[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in items) print i; }; } \
-'
-# Make sure the list of sources is unique. This is necessary because,
-# e.g., the same source file might be shared among _SOURCES variables
-# for different programs/libraries.
-am__define_uniq_tagged_files = \
- list='$(am__tagged_files)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-ACLOCAL = @ACLOCAL@
-ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
-AMTAR = @AMTAR@
-AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
-AR = @AR@
-AS = @AS@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-CC = @CC@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CPP = @CPP@
-CPPFLAGS = @CPPFLAGS@
-CYGPATH_W = @CYGPATH_W@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-DLLTOOL = @DLLTOOL@
-DSYMUTIL = @DSYMUTIL@
-DUMPBIN = @DUMPBIN@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-EXEEXT = @EXEEXT@
-FGREP = @FGREP@
-GREP = @GREP@
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-LD = @LD@
-LDFLAGS = @LDFLAGS@
-LIBOBJS = @LIBOBJS@
-LIBS = @LIBS@
-LIBTOOL = @LIBTOOL@
-LIPO = @LIPO@
-LN_S = @LN_S@
-LTLIBOBJS = @LTLIBOBJS@
-MAKEINFO = @MAKEINFO@
-MANIFEST_TOOL = @MANIFEST_TOOL@
-MKDIR_P = @MKDIR_P@
-NM = @NM@
-NMEDIT = @NMEDIT@
-OBJDUMP = @OBJDUMP@
-OBJEXT = @OBJEXT@
-OTOOL = @OTOOL@
-OTOOL64 = @OTOOL64@
-PACKAGE = @PACKAGE@
-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
-PACKAGE_NAME = @PACKAGE_NAME@
-PACKAGE_STRING = @PACKAGE_STRING@
-PACKAGE_TARNAME = @PACKAGE_TARNAME@
-PACKAGE_URL = @PACKAGE_URL@
-PACKAGE_VERSION = @PACKAGE_VERSION@
-PATH_SEPARATOR = @PATH_SEPARATOR@
-PYTHON = @PYTHON@
-RANLIB = @RANLIB@
-SED = @SED@
-SET_MAKE = @SET_MAKE@
-SHELL = @SHELL@
-SPHINX_BUILD = @SPHINX_BUILD@
-SPHINX_VERSION = @SPHINX_VERSION@
-STRIP = @STRIP@
-VERSION = @VERSION@
-YASM = @YASM@
-YASM_VERSION = @YASM_VERSION@
-abs_builddir = @abs_builddir@
-abs_srcdir = @abs_srcdir@
-abs_top_builddir = @abs_top_builddir@
-abs_top_srcdir = @abs_top_srcdir@
-ac_ct_AR = @ac_ct_AR@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
-am__include = @am__include@
-am__leading_dot = @am__leading_dot@
-am__quote = @am__quote@
-am__tar = @am__tar@
-am__untar = @am__untar@
-bindir = @bindir@
-build = @build@
-build_alias = @build_alias@
-build_cpu = @build_cpu@
-build_os = @build_os@
-build_vendor = @build_vendor@
-builddir = @builddir@
-datadir = @datadir@
-datarootdir = @datarootdir@
-docdir = @docdir@
-dvidir = @dvidir@
-exec_prefix = @exec_prefix@
-host = @host@
-host_alias = @host_alias@
-host_cpu = @host_cpu@
-host_os = @host_os@
-host_vendor = @host_vendor@
-htmldir = @htmldir@
-includedir = @includedir@
-infodir = @infodir@
-install_sh = @install_sh@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localedir = @localedir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-oldincludedir = @oldincludedir@
-pdfdir = @pdfdir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-srcdir = @srcdir@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-top_build_prefix = @top_build_prefix@
-top_builddir = @top_builddir@
-top_srcdir = @top_srcdir@
-OPTABLE = @top_srcdir@/docs/x86/optable.xml
-MAINTAINERCLEANFILES = Makefile.in
-lib_LTLIBRARIES = libudis86.la
-libudis86_la_SOURCES = \
- itab.c \
- decode.c \
- syn.c \
- syn-intel.c \
- syn-att.c \
- udis86.c \
- udint.h \
- syn.h \
- decode.h
-
-include_ladir = ${includedir}/libudis86
-include_la_HEADERS = \
- types.h \
- extern.h \
- itab.h
-
-BUILT_SOURCES = \
- itab.c \
- itab.h
-
-
-#
-# DLLs may not contain undefined symbol references.
-# We have the linker check this explicitly.
-#
-@TARGET_WINDOWS_TRUE@libudis86_la_LDFLAGS = -no-undefined -version-info 0:0:0
-all: $(BUILT_SOURCES)
- $(MAKE) $(AM_MAKEFLAGS) all-am
-
-.SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
-$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
- @for dep in $?; do \
- case '$(am__configure_deps)' in \
- *$$dep*) \
- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
- && { if test -f $@; then exit 0; else break; fi; }; \
- exit 1;; \
- esac; \
- done; \
- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign libudis86/Makefile'; \
- $(am__cd) $(top_srcdir) && \
- $(AUTOMAKE) --foreign libudis86/Makefile
-.PRECIOUS: Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
- @case '$?' in \
- *config.status*) \
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
- *) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
- esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-
-$(top_srcdir)/configure: $(am__configure_deps)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4): $(am__aclocal_m4_deps)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(am__aclocal_m4_deps):
-
-install-libLTLIBRARIES: $(lib_LTLIBRARIES)
- @$(NORMAL_INSTALL)
- @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
- list2=; for p in $$list; do \
- if test -f $$p; then \
- list2="$$list2 $$p"; \
- else :; fi; \
- done; \
- test -z "$$list2" || { \
- echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
- $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
- echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
- $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
- }
-
-uninstall-libLTLIBRARIES:
- @$(NORMAL_UNINSTALL)
- @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
- for p in $$list; do \
- $(am__strip_dir) \
- echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
- $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
- done
-
-clean-libLTLIBRARIES:
- -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; \
- locs=`for p in $$list; do echo $$p; done | \
- sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
- sort -u`; \
- test -z "$$locs" || { \
- echo rm -f $${locs}; \
- rm -f $${locs}; \
- }
-libudis86.la: $(libudis86_la_OBJECTS) $(libudis86_la_DEPENDENCIES) $(EXTRA_libudis86_la_DEPENDENCIES)
- $(AM_V_CCLD)$(libudis86_la_LINK) -rpath $(libdir) $(libudis86_la_OBJECTS) $(libudis86_la_LIBADD) $(LIBS)
-
-mostlyclean-compile:
- -rm -f *.$(OBJEXT)
-
-distclean-compile:
- -rm -f *.tab.c
-
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decode.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/itab.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syn-att.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syn-intel.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syn.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udis86.Plo@am__quote@
-
-.c.o:
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
-
-.c.obj:
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
-
-.c.lo:
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
-
-mostlyclean-libtool:
- -rm -f *.lo
-
-clean-libtool:
- -rm -rf .libs _libs
-install-include_laHEADERS: $(include_la_HEADERS)
- @$(NORMAL_INSTALL)
- @list='$(include_la_HEADERS)'; test -n "$(include_ladir)" || list=; \
- if test -n "$$list"; then \
- echo " $(MKDIR_P) '$(DESTDIR)$(include_ladir)'"; \
- $(MKDIR_P) "$(DESTDIR)$(include_ladir)" || exit 1; \
- fi; \
- for p in $$list; do \
- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
- echo "$$d$$p"; \
- done | $(am__base_list) | \
- while read files; do \
- echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(include_ladir)'"; \
- $(INSTALL_HEADER) $$files "$(DESTDIR)$(include_ladir)" || exit $$?; \
- done
-
-uninstall-include_laHEADERS:
- @$(NORMAL_UNINSTALL)
- @list='$(include_la_HEADERS)'; test -n "$(include_ladir)" || list=; \
- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- dir='$(DESTDIR)$(include_ladir)'; $(am__uninstall_files_from_dir)
-
-ID: $(am__tagged_files)
- $(am__define_uniq_tagged_files); mkid -fID $$unique
-tags: tags-am
-TAGS: tags
-
-tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
- set x; \
- here=`pwd`; \
- $(am__define_uniq_tagged_files); \
- shift; \
- if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
- test -n "$$unique" || unique=$$empty_fix; \
- if test $$# -gt 0; then \
- $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
- "$$@" $$unique; \
- else \
- $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
- $$unique; \
- fi; \
- fi
-ctags: ctags-am
-
-CTAGS: ctags
-ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
- $(am__define_uniq_tagged_files); \
- test -z "$(CTAGS_ARGS)$$unique" \
- || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
- $$unique
-
-GTAGS:
- here=`$(am__cd) $(top_builddir) && pwd` \
- && $(am__cd) $(top_srcdir) \
- && gtags -i $(GTAGS_ARGS) "$$here"
-cscopelist: cscopelist-am
-
-cscopelist-am: $(am__tagged_files)
- list='$(am__tagged_files)'; \
- case "$(srcdir)" in \
- [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
- *) sdir=$(subdir)/$(srcdir) ;; \
- esac; \
- for i in $$list; do \
- if test -f "$$i"; then \
- echo "$(subdir)/$$i"; \
- else \
- echo "$$sdir/$$i"; \
- fi; \
- done >> $(top_builddir)/cscope.files
-
-distclean-tags:
- -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
-distdir: $(DISTFILES)
- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
- list='$(DISTFILES)'; \
- dist_files=`for file in $$list; do echo $$file; done | \
- sed -e "s|^$$srcdirstrip/||;t" \
- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
- case $$dist_files in \
- */*) $(MKDIR_P) `echo "$$dist_files" | \
- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
- sort -u` ;; \
- esac; \
- for file in $$dist_files; do \
- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
- if test -d $$d/$$file; then \
- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
- if test -d "$(distdir)/$$file"; then \
- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
- fi; \
- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
- fi; \
- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
- else \
- test -f "$(distdir)/$$file" \
- || cp -p $$d/$$file "$(distdir)/$$file" \
- || exit 1; \
- fi; \
- done
-check-am: all-am
-check: $(BUILT_SOURCES)
- $(MAKE) $(AM_MAKEFLAGS) check-am
-all-am: Makefile $(LTLIBRARIES) $(HEADERS)
-installdirs:
- for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(include_ladir)"; do \
- test -z "$$dir" || $(MKDIR_P) "$$dir"; \
- done
-install: $(BUILT_SOURCES)
- $(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
-
-install-am: all-am
- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-am
-install-strip:
- if test -z '$(STRIP)'; then \
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- install; \
- else \
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
- fi
-mostlyclean-generic:
-
-clean-generic:
-
-distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-
-maintainer-clean-generic:
- @echo "This command is intended for maintainers to use"
- @echo "it deletes files that may require special tools to rebuild."
- -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
- -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-clean: clean-am
-
-clean-am: clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
- mostlyclean-am
-
-distclean: distclean-am
- -rm -rf ./$(DEPDIR)
- -rm -f Makefile
-distclean-am: clean-am distclean-compile distclean-generic \
- distclean-tags
-
-dvi: dvi-am
-
-dvi-am:
-
-html: html-am
-
-html-am:
-
-info: info-am
-
-info-am:
-
-install-data-am: install-include_laHEADERS
-
-install-dvi: install-dvi-am
-
-install-dvi-am:
-
-install-exec-am: install-libLTLIBRARIES
-
-install-html: install-html-am
-
-install-html-am:
-
-install-info: install-info-am
-
-install-info-am:
-
-install-man:
-
-install-pdf: install-pdf-am
-
-install-pdf-am:
-
-install-ps: install-ps-am
-
-install-ps-am:
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
- -rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic \
- maintainer-clean-local
-
-mostlyclean: mostlyclean-am
-
-mostlyclean-am: mostlyclean-compile mostlyclean-generic \
- mostlyclean-libtool
-
-pdf: pdf-am
-
-pdf-am:
-
-ps: ps-am
-
-ps-am:
-
-uninstall-am: uninstall-include_laHEADERS uninstall-libLTLIBRARIES
-
-.MAKE: all check install install-am install-strip
-
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libLTLIBRARIES clean-libtool clean-local cscopelist-am \
- ctags ctags-am distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags distdir dvi dvi-am html \
- html-am info info-am install install-am install-data \
- install-data-am install-dvi install-dvi-am install-exec \
- install-exec-am install-html install-html-am \
- install-include_laHEADERS install-info install-info-am \
- install-libLTLIBRARIES install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
- maintainer-clean-generic maintainer-clean-local mostlyclean \
- mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
- pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
- uninstall-include_laHEADERS uninstall-libLTLIBRARIES
-
-
-itab.c itab.h: $(OPTABLE) \
- $(top_srcdir)/scripts/ud_itab.py \
- $(top_srcdir)/scripts/ud_opcode.py \
- $(top_srcdir)/scripts/ud_optable.py
- $(PYTHON) $(top_srcdir)/scripts/ud_itab.py $(OPTABLE) $(srcdir)
-
-clean-local:
- rm -rf $(BUILT_SOURCES)
-
-maintainer-clean-local:
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
diff --git a/lib/udis86/libudis86/CMakeLists.txt b/lib/udis86/libudis86/CMakeLists.txt
new file mode 100644
index 00000000..8853c274
--- /dev/null
+++ b/lib/udis86/libudis86/CMakeLists.txt
@@ -0,0 +1,26 @@
+project(libudis86)
+
+set(HEADERS
+ decode.h
+ extern.h
+ itab.h
+ syn.h
+ types.h
+ udint.h
+)
+
+set(SOURCES
+ decode.c
+ itab.c
+ syn-att.c
+ syn-intel.c
+ syn.c
+ udis86.c
+)
+
+add_compile_definitions(
+ HAVE_STRING_H=1
+)
+
+add_library(udis86 STATIC ${HEADERS} ${SOURCES})
+
diff --git a/lib/libudis86/libudis86/decode.c b/lib/udis86/libudis86/decode.c
similarity index 100%
rename from lib/libudis86/libudis86/decode.c
rename to lib/udis86/libudis86/decode.c
diff --git a/lib/libudis86/libudis86/decode.h b/lib/udis86/libudis86/decode.h
similarity index 100%
rename from lib/libudis86/libudis86/decode.h
rename to lib/udis86/libudis86/decode.h
diff --git a/lib/libudis86/libudis86/extern.h b/lib/udis86/libudis86/extern.h
similarity index 100%
rename from lib/libudis86/libudis86/extern.h
rename to lib/udis86/libudis86/extern.h
diff --git a/lib/libudis86/libudis86/itab.c b/lib/udis86/libudis86/itab.c
similarity index 100%
rename from lib/libudis86/libudis86/itab.c
rename to lib/udis86/libudis86/itab.c
diff --git a/lib/libudis86/libudis86/itab.h b/lib/udis86/libudis86/itab.h
similarity index 100%
rename from lib/libudis86/libudis86/itab.h
rename to lib/udis86/libudis86/itab.h
diff --git a/lib/libudis86/libudis86/syn-att.c b/lib/udis86/libudis86/syn-att.c
similarity index 100%
rename from lib/libudis86/libudis86/syn-att.c
rename to lib/udis86/libudis86/syn-att.c
diff --git a/lib/libudis86/libudis86/syn-intel.c b/lib/udis86/libudis86/syn-intel.c
similarity index 100%
rename from lib/libudis86/libudis86/syn-intel.c
rename to lib/udis86/libudis86/syn-intel.c
diff --git a/lib/libudis86/libudis86/syn.c b/lib/udis86/libudis86/syn.c
similarity index 100%
rename from lib/libudis86/libudis86/syn.c
rename to lib/udis86/libudis86/syn.c
diff --git a/lib/libudis86/libudis86/syn.h b/lib/udis86/libudis86/syn.h
similarity index 100%
rename from lib/libudis86/libudis86/syn.h
rename to lib/udis86/libudis86/syn.h
diff --git a/lib/libudis86/libudis86/types.h b/lib/udis86/libudis86/types.h
similarity index 100%
rename from lib/libudis86/libudis86/types.h
rename to lib/udis86/libudis86/types.h
diff --git a/lib/libudis86/libudis86/udint.h b/lib/udis86/libudis86/udint.h
similarity index 100%
rename from lib/libudis86/libudis86/udint.h
rename to lib/udis86/libudis86/udint.h
diff --git a/lib/libudis86/libudis86/udis86.c b/lib/udis86/libudis86/udis86.c
similarity index 100%
rename from lib/libudis86/libudis86/udis86.c
rename to lib/udis86/libudis86/udis86.c
diff --git a/pev.conf b/pev.conf
deleted file mode 100644
index 892be030..00000000
--- a/pev.conf
+++ /dev/null
@@ -1 +0,0 @@
-plugins_dir=src/build/plugins
diff --git a/readpe.conf b/readpe.conf
new file mode 100644
index 00000000..76660f71
--- /dev/null
+++ b/readpe.conf
@@ -0,0 +1 @@
+plugins_dir=src/plugins
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..c4ce26bb
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,200 @@
+project(readpe LANGUAGES C VERSION ${readpe_VERSION})
+
+include(CMakePackageConfigHelpers)
+include(GNUInstallDirs)
+
+set(HEADERS
+ ${CMAKE_SOURCE_DIR}/include/readpe/config.h
+ ${CMAKE_SOURCE_DIR}/include/readpe/output.h
+ ${CMAKE_SOURCE_DIR}/include/readpe/readpe.h
+ ${CMAKE_SOURCE_DIR}/include/readpe/plugin.h
+ ${CMAKE_SOURCE_DIR}/include/readpe/helper.h
+
+ ${CMAKE_SOURCE_DIR}/include/readpe/plugin/output.h
+)
+
+set(SOURCES
+ main.c
+
+ certificates.c
+ config.c
+ directories.c
+ dylib.c
+ exports.c
+ hash.c
+ header.c
+ helper.c
+ imports.c
+ malloc_s.c
+ output.c
+ output_plugin.c
+ output_text.c
+ plugin.c
+ plugins.c
+ resources.c
+ scan.c
+ section.c
+ security.c
+
+ plugins.h
+ stack.h
+ dylib.h
+)
+
+if(READPE_BUILD_LINK_NAME_SUPPORT)
+ if(BUILD_DISASSEMBLER)
+ list(APPEND SOURCES legacy/pedis.c )
+ endif()
+
+ list(APPEND HEADERS
+ legacy/legacy.h
+ )
+ list(APPEND SOURCES
+ legacy/pehash.c
+ legacy/peldd.c
+ legacy/pepack.c
+ legacy/peres.c
+ legacy/pescan.c
+ legacy/pesec.c
+ legacy/pestr.c
+ legacy/readpe.c
+ legacy/ofs2rva.c
+ legacy/rva2ofs.c
+ )
+endif()
+
+if(WIN32)
+ # TODO
+ # list(APPEND SOURCES windows/cpload.c)
+endif()
+
+if(BUILD_READPE_ONLY)
+ find_package(libpe REQUIRED)
+endif()
+find_package(OpenSSL REQUIRED COMPONENTS Crypto)
+
+configure_file(version.h.in version.h @ONLY)
+
+add_executable(readpe ${HEADERS} ${SOURCES})
+target_link_libraries(readpe PRIVATE
+ OpenSSL::Crypto
+ readpe::compat
+ readpe::libpe
+ uthash
+)
+
+target_include_directories(readpe PRIVATE
+ "${CMAKE_SOURCE_DIR}/include"
+ "${CMAKE_SOURCE_DIR}/include/readpe"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+target_include_directories(readpe PUBLIC
+ $
+ $
+)
+
+target_compile_definitions(readpe PRIVATE
+ HAS_CMAKE_VERSION=1
+ # -DVER_COMPANYNAME_STR=""
+ VER_FILEVERSION_STR="${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0"
+
+ SHAREDIR=""
+ PLUGINSDIR="plugins"
+)
+
+# Warnings for better code
+if(MSVC)
+ target_link_libraries(readpe PRIVATE dirent dl)
+ target_compile_options(readpe PRIVATE /W4 /WX)
+elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
+ target_link_libraries(readpe PRIVATE m)
+ target_compile_options(readpe PRIVATE
+ -Wall
+ -Wextra
+ -Wpedantic
+ -pedantic
+ -Wshadow
+ -Wundef
+ -Wdouble-promotion
+ -Wformat=2
+ -Wformat-security
+ -Wconversion
+ -ggdb3
+ )
+endif()
+
+if(LIBPE_LINK_SSDEEP)
+ target_compile_definitions(pe PRIVATE
+ -DLIBPE_LINK_SSDEEP=1
+ )
+endif()
+
+if(READPE_BUILD_LINK_NAME_SUPPORT)
+ # TODO: Currently not in readpe
+ if(BUILD_DISASSEMBLER)
+ target_link_libraries(readpe PRIVATE udis86)
+ target_compile_definitions(readpe PRIVATE
+ READPE_DISASSEMBLER=1
+ )
+ endif()
+
+ target_compile_definitions(readpe PRIVATE
+ READPE_LINK_NAMES=1
+ )
+endif()
+
+# Install readpe
+install(TARGETS readpe
+ EXPORT readpe-targets
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+if(READPE_PACK_COMPLETION)
+ install(FILES
+ ${CMAKE_SOURCE_DIR}/tools/completion/readpe.bash
+ ${CMAKE_SOURCE_DIR}/tools/completion/readpe.fsh
+ ${CMAKE_SOURCE_DIR}/tools/completion/readpe.zsh
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/readpe
+ )
+endif()
+
+# Install readpe headers for plugin development
+if(READPE_PACK_HEADERS)
+ install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/readpe
+ DESTINATION include/
+ FILES_MATCHING
+ PATTERN "*.h"
+ )
+endif()
+
+# Install readpe cmake files for plugin development
+if(READPE_PACK_CMAKE_FILES)
+ export(EXPORT readpe-targets
+ FILE "${CMAKE_CURRENT_BINARY_DIR}/readpe-targets.cmake"
+ NAMESPACE readpe::
+ )
+
+ configure_package_config_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/readpe-config.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/readpe-config.cmake
+ INSTALL_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
+ PATH_VARS
+ CMAKE_INSTALL_INCLUDEDIR
+ NO_CHECK_REQUIRED_COMPONENTS_MACRO
+ )
+
+ install(EXPORT readpe-targets
+ FILE readpe-targets.cmake
+ NAMESPACE readpe::
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/readpe
+ )
+
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/readpe-config.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/readpe
+ )
+endif()
+
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644
index adc3bf87..00000000
--- a/src/Makefile
+++ /dev/null
@@ -1,180 +0,0 @@
-####### Platform specifics
-
-# cut is necessary for Cygwin
-export PLATFORM_OS := $(shell uname | cut -d_ -f1)
-
-####### Makefile Conventions - Directory variables
-
-srcdir = .
-prefix = /usr/local
-
-exec_prefix = $(prefix)
-sysconfdir = $(prefix)/etc
-includedir = $(prefix)/include
-datarootdir = $(prefix)/share
-localstatedir = $(prefix)/var
-
-bindir = $(exec_prefix)/bin
-libdir = $(exec_prefix)/lib
-libexecdir = $(exec_prefix)/libexec
-sbindir = $(exec_prefix)/sbin
-
-datadir = $(datarootdir)
-docdir = $(datarootdir)/doc/pev
-infodir = $(datarootdir)/info
-localedir = $(datarootdir)/locale
-
-mandir = $(datarootdir)/man
-manext = .1
-man1dir = $(mandir)/man1
-man1ext = .1
-
-export pluginsdir = $(libdir)/pev/plugins
-
-####### Makefile Conventions - Utilities
-
-export CC ?= gcc
-export LINK = $(CC)
-export CHK_DIR_EXISTS = test -d
-export CHK_FILE_EXISTS = test -f
-export INSTALL = install
-export INSTALL_DATA = ${INSTALL} -m 644
-export INSTALL_PROGRAM = ${INSTALL}
-export SYMLINK = ln -sf
-export MKDIR = mkdir -p
-export RM = rm -f
-export RM_DIR = rm -rf
-ifeq ($(PLATFORM_OS), Darwin)
- export STRIP = strip -x
-else
- export STRIP = strip --strip-unneeded
-endif
-
-####### Compiler options
-
-override LDFLAGS += -L$(LIBPE) -lpe -lcrypto -lssl -ldl -lm
-override CFLAGS += -O2 -ffast-math -I$(LIBPE)/include -I"../include" -W -Wall -Wextra -Wno-implicit-fallthrough -std=c99 -pedantic
-
-# To compile for production define the symbol NDEBUG before invoking this makefile.
-override CPPFLAGS += \
- -D_GNU_SOURCE \
- -DSHAREDIR="\"$(SHAREDIR)"\" \
- -DPLUGINSDIR="\"$(pluginsdir)"\"
-
-ifeq ($(PLATFORM_OS), Darwin)
- # We disable warnings for deprecated declarations since Apple deprecated OpenSSL in Mac OS X 10.7
- override CFLAGS += -Wno-deprecated-declarations
-endif
-
-ifeq ($(PLATFORM_OS), CYGWIN)
- override CPPFLAGS += -D_XOPEN_SOURCE=600
-endif
-
-SRC_DIRS = $(srcdir) $(srcdir)/compat
-
-PROGS = readpe rva2ofs ofs2rva pehash pesec pescan pepack pestr pedis peres peldd
-PLUGINS_DIR = $(srcdir)/plugins
-SHAREDIR = $(datadir)/pev
-export LIBPE = $(realpath $(srcdir)/../lib/libpe)
-LIBUDIS86 = $(srcdir)/../lib/libudis86
-MANDIR = $(srcdir)/../doc/manpages
-
-export pev_BUILDDIR = ./build
-pev_SRCS_FILTER = $(sort $(wildcard ${dir}/*.c))
-pev_SRCS = $(foreach dir, ${SRC_DIRS}, ${pev_SRCS_FILTER})
-pev_OBJS = $(addprefix ${pev_BUILDDIR}/, $(addsuffix .o, $(basename ${pev_SRCS})))
-
-pev_COMMON_DEPS = \
- $(pev_BUILDDIR)/compat/strlcat.o \
- $(pev_BUILDDIR)/config.o \
- $(pev_BUILDDIR)/dylib.o \
- $(pev_BUILDDIR)/malloc_s.o \
- $(pev_BUILDDIR)/plugins.o \
- $(pev_BUILDDIR)/output_plugin.o \
- $(pev_BUILDDIR)/output.o \
- $(pev_BUILDDIR)/pev_api.o
-
-####### Build rules
-
-.PHONY: plugins install installdirs uninstall clean
-
-all: $(PROGS) plugins
-
-plugins:
- cd $(PLUGINS_DIR) && $(MAKE) $@
-
-ofs2rva: $(pev_BUILDDIR)/ofs2rva.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS)
-
-pedis: CPPFLAGS += -DHAVE_STRING_H
-pedis: CFLAGS += -I$(LIBUDIS86)
-pedis: $(pev_BUILDDIR)/pedis.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) $(sort $(LIBUDIS86)/libudis86/*.c)
-
-pehash: $(pev_BUILDDIR)/pehash.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-pepack: $(pev_BUILDDIR)/pepack.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-peres: $(pev_BUILDDIR)/peres.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-pescan: LDFLAGS += -lm
-pescan: $(pev_BUILDDIR)/pescan.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-pesec: LDFLAGS += -lcrypto
-pesec: $(pev_BUILDDIR)/pesec.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-pestr: $(pev_BUILDDIR)/pestr.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-readpe: $(pev_BUILDDIR)/readpe.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-rva2ofs: $(pev_BUILDDIR)/rva2ofs.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-peldd: $(pev_BUILDDIR)/peldd.o $(pev_OBJS)
- $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
-
-# Generic rule matching sources
-
-$(pev_BUILDDIR)/%.o: %.c
- @$(CHK_DIR_EXISTS) $(dir $@) || $(MKDIR) $(dir $@)
- $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) $(INCPATH)
-
-install: installdirs
- for prog in $(PROGS); do \
- $(INSTALL_PROGRAM) $(INSTALL_FLAGS) $(pev_BUILDDIR)/$$prog $(DESTDIR)$(bindir); \
- $(CHK_FILE_EXISTS) $(MANDIR)/$$prog$(man1ext) && \
- gzip -c -9 $(MANDIR)/$$prog$(man1ext) > $(DESTDIR)$(man1dir)/$$prog$(man1ext).gz || \
- echo -n; \
- done
-
- $(INSTALL_DATA) $(srcdir)/userdb.txt $(DESTDIR)$(SHAREDIR)
- cd $(PLUGINS_DIR) && $(MAKE) $@
-
-install-strip: INSTALL_FLAGS += -s
-install-strip: install
-
-installdirs:
- @$(CHK_DIR_EXISTS) $(DESTDIR) || $(MKDIR) $(DESTDIR)
- @$(CHK_DIR_EXISTS) $(DESTDIR)$(bindir) || $(MKDIR) $(DESTDIR)$(bindir)
- @$(CHK_DIR_EXISTS) $(DESTDIR)$(man1dir) || $(MKDIR) $(DESTDIR)$(man1dir)
- @$(CHK_DIR_EXISTS) $(DESTDIR)$(SHAREDIR) || $(MKDIR) $(DESTDIR)$(SHAREDIR)
-
-uninstall:
- for prog in $(PROGS); do \
- $(RM) $(DESTDIR)$(bindir)/$$prog; \
- $(RM) $(DESTDIR)$(man1dir)/$$prog$(man1ext).gz; \
- done
- $(RM_DIR) $(DESTDIR)$(SHAREDIR)
- cd $(PLUGINS_DIR) && $(MAKE) $@
-
-clean:
- $(RM_DIR) $(pev_BUILDDIR)
- $(RM) $(PROGS)
- cd $(PLUGINS_DIR) && $(MAKE) $@
diff --git a/src/certificates.c b/src/certificates.c
new file mode 100644
index 00000000..24545ad6
--- /dev/null
+++ b/src/certificates.c
@@ -0,0 +1,449 @@
+/* vim: set ts=4 sw=4 noet: */
+/*
+ readpe - the PE file analyzer toolkit
+
+ Copyright (C) 2012 - 2025 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#include "compat.h"
+#include "libpe/macros.h"
+#include "libpe/pe.h"
+#include "readpe/helper.h"
+#include "readpe/output.h"
+#include "readpe/readpe.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+static inline uint32_t roundBy8(uint32_t x) { return (x + 7) & 0xfffffff8; }
+// static unsigned int roundBy8(unsigned int n)
+// {
+// unsigned int t = n & ~7U;
+// if (n & 7U) {
+// t += 8;
+// }
+// return t;
+// }
+
+static cert_format_e parse_certoutform(const char *l_optarg)
+{
+ cert_format_e result = CERT_FORMAT_X509;
+
+ if (strcmp(l_optarg, "text") == 0) {
+ result = CERT_FORMAT_X509;
+ } else if (strcmp(l_optarg, "x509") == 0) {
+ result = CERT_FORMAT_X509;
+ } else if (strcmp(l_optarg, "pem") == 0) {
+ result = CERT_FORMAT_PEM;
+ } else if (strcmp(l_optarg, "der") == 0) {
+ result = CERT_FORMAT_DER;
+ } else {
+ EXIT_ERROR("invalid cert_format option");
+ }
+
+ return result;
+}
+
+static BIO *parse_certout(const char *l_optarg)
+{
+ BIO *bio = BIO_new(BIO_s_file());
+ if (bio == NULL) {
+ EXIT_ERROR("could not allocate BIO");
+ }
+
+ if (strcmp(l_optarg, "stdout") == 0) {
+ BIO_set_fp(bio, stdout, BIO_NOCLOSE);
+ } else if (strcmp(l_optarg, "stderr") == 0) {
+ BIO_set_fp(bio, stderr, BIO_NOCLOSE);
+ } else {
+ int ret = BIO_write_filename(bio, (char *) l_optarg);
+ if (ret == 0) {
+ BIO_free(bio);
+ EXIT_ERROR("failed to open file");
+ }
+ }
+
+ return bio;
+}
+
+static void print_certificate(BIO *out, cert_format_e format, X509 *cert)
+{
+ if (out == NULL) {
+ return;
+ }
+ switch (format) {
+ default:
+ case CERT_FORMAT_X509:
+ X509_print(out, cert);
+ break;
+ case CERT_FORMAT_PEM:
+ PEM_write_bio_X509(out, cert);
+ break;
+ case CERT_FORMAT_DER:
+ LIBPE_WARNING("DER format is not yet supported for output");
+ break;
+ }
+}
+
+static int parse_pkcs7_data(STACK_OF(X509) * *certs,
+ const CRYPT_DATA_BLOB *blob, PKCS7 **p7)
+{
+ int result = 0;
+ const cert_format_e input_fmt = CERT_FORMAT_DER;
+ // PKCS7 *p7 = NULL; /* Need to be initialized! */
+ BIO *in;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ CRYPTO_malloc_init();
+#endif
+ ERR_load_crypto_strings();
+ OpenSSL_add_all_algorithms();
+
+ in = BIO_new_mem_buf(blob->pbData, (int) blob->cbData);
+ if (in == NULL) {
+ result = -2;
+ goto error;
+ }
+
+ // FIXME: input_fmt never changed!
+ switch (input_fmt) {
+ default:
+ LIBPE_WARNING("unhandled input format for certificate");
+ break;
+ case CERT_FORMAT_DER:
+ *p7 = d2i_PKCS7_bio(in, NULL);
+ break;
+ case CERT_FORMAT_PEM:
+ *p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL);
+ break;
+ }
+
+ if (p7 == NULL) {
+ ERR_print_errors_fp(stderr);
+ result = -3;
+ goto error;
+ }
+
+ // STACK_OF(X509) *certs = NULL;
+
+ int type = OBJ_obj2nid((*p7)->type);
+ switch (type) {
+ default:
+ LIBPE_WARNING("unhandled certificate type");
+ break;
+ case NID_pkcs7_signed: // PKCS7_type_is_signed(p7)
+ *certs = (*p7)->d.sign->cert;
+ break;
+ case NID_pkcs7_signedAndEnveloped: // PKCS7_type_is_signedAndEnveloped(p7)
+ *certs = (*p7)->d.signed_and_enveloped->cert;
+ break;
+ }
+
+error:
+ if (p7 != NULL) {
+ // PKCS7_free(p7);
+ }
+ if (in != NULL) {
+ BIO_free(in);
+ }
+
+ // Deallocate everything from OpenSSL_add_all_algorithms
+ EVP_cleanup();
+ // Deallocate everything from ERR_load_crypto_strings
+ ERR_free_strings();
+
+ return result;
+}
+
+static void print_pkcs7_data(const CRYPT_DATA_BLOB *blob, cert_format_e format,
+ BIO *out, bool verbose)
+{
+ PKCS7 *p7 = NULL; /* Need to be initialized! */
+ STACK_OF(X509) *certs = NULL;
+ int res = parse_pkcs7_data(&certs, blob, &p7);
+
+ if (res < 0) {
+ if (p7 != NULL) {
+ PKCS7_free(p7);
+ }
+ // explode
+ return;
+ }
+
+ const int numcerts = certs != NULL ? sk_X509_num(certs) : 0;
+ if (verbose) {
+ for (int i = 0; i < numcerts; i++) {
+ X509 *cert = sk_X509_value(certs, i);
+ print_certificate(out, format, cert);
+ // NOTE: Calling X509_free(cert) is unnecessary.
+ }
+ }
+
+ if (numcerts > 0) {
+ // Print whether certificate signature is valid
+ X509 *subject = sk_X509_value(certs, 0);
+ X509 *issuer = sk_X509_value(certs, numcerts - 1);
+ EVP_PKEY *issuer_pubkey = X509_get_pubkey(issuer);
+ int valid_sig = X509_verify(subject, issuer_pubkey);
+ EVP_PKEY_free(issuer_pubkey);
+ output("Signature", valid_sig == 1 ? "valid" : "invalid");
+
+ char issuer_name[65];
+
+ // Print signers
+ output_open_scope("signers", OUTPUT_SCOPE_TYPE_ARRAY);
+ for (int i = 0; i < numcerts; i++) {
+ X509 *cert = sk_X509_value(certs, i);
+ X509_NAME *name = X509_get_subject_name(cert);
+
+ memset(&issuer_name, 0, 65);
+ int issuer_name_len = X509_NAME_get_text_by_NID(
+ name, NID_commonName, issuer_name, 64);
+ if (issuer_name_len > 0) {
+ output_open_scope("signer", OUTPUT_SCOPE_TYPE_OBJECT);
+ output("Issuer", issuer_name);
+ output_close_scope(); // signer
+ }
+ }
+ output_close_scope(); // signers
+ }
+
+ if (p7 != NULL) {
+ PKCS7_free(p7);
+ }
+}
+
+void print_certificates(pe_ctx_t *ctx, const char *format, const char *out)
+{
+ cert_format_e out_format
+ = format ? parse_certoutform(format) : CERT_FORMAT_X509;
+ BIO *out_file = parse_certout(out ? out : "stdout");
+
+ WIN_CERTIFICATE **certs = NULL;
+ uint32_t cert_count = pe_certificates(ctx, &certs);
+
+ for (uint32_t i = 0; i < cert_count; ++i) {
+ WIN_CERTIFICATE *cert = certs[i];
+
+ switch (cert->wRevision) {
+ default:
+ LIBPE_WARNING("unknown wRevision");
+ break;
+ case WIN_CERT_REVISION_1_0:
+ LIBPE_WARNING("WIN_CERT_REVISION_1_0 is not supported");
+ break;
+ case WIN_CERT_REVISION_2_0:
+ break;
+ }
+
+ switch (cert->wCertificateType) {
+ default:
+ LIBPE_WARNING("unknown wCertificateType");
+ break;
+ case WIN_CERT_TYPE_X509:
+ LIBPE_WARNING("WIN_CERT_TYPE_X509 is not supported");
+ break;
+ case WIN_CERT_TYPE_PKCS_SIGNED_DATA: {
+ CRYPT_DATA_BLOB p7data;
+ p7data.cbData
+ = (uint32_t) (cert->dwLength
+ - offsetof(WIN_CERTIFICATE, bCertificate));
+ p7data.pbData = cert->bCertificate;
+ STACK_OF(X509) * x509certs;
+ PKCS7 *p7 = NULL; /* Need to be initialized! */
+ int res = parse_pkcs7_data(&x509certs, &p7data, &p7);
+ // print_certificate(out_file, out_format, x509cert);
+
+ if (res < 0) {
+ return;
+ }
+
+ const int numcerts = certs != NULL ? sk_X509_num(x509certs) : 0;
+ for (int j = 0; j < numcerts; ++j) {
+ X509 *x509cert = sk_X509_value(x509certs, j);
+ print_certificate(out_file, out_format, x509cert);
+ // NOTE: Calling X509_free(cert) is unnecessary.
+ }
+ if (p7 != NULL) {
+ PKCS7_free(p7);
+ }
+ break;
+ }
+ case WIN_CERT_TYPE_TS_STACK_SIGNED:
+ LIBPE_WARNING("WIN_CERT_TYPE_TS_STACK_SIGNED is not supported");
+ break;
+ case WIN_CERT_TYPE_EFI_PKCS115:
+ LIBPE_WARNING("WIN_CERT_TYPE_EFI_PKCS115 is not supported");
+ break;
+ case WIN_CERT_TYPE_EFI_GUID:
+ LIBPE_WARNING("WIN_CERT_TYPE_EFI_GUID is not supported");
+ break;
+ }
+ }
+
+ if (certs != NULL) {
+ free(certs);
+ }
+ free(out_file);
+
+ // STACK_OF(X509) *certs = NULL;
+
+ // int res = parse_pkcs7_data(certs, blob);
+}
+
+void print_certificates_info(pe_ctx_t *ctx, const char *format, const char *out,
+ bool verbose)
+{
+ cert_format_e _format
+ = format ? parse_certoutform(format) : CERT_FORMAT_X509;
+ BIO *_out = parse_certout(out ? out : "stdout");
+
+ const IMAGE_DATA_DIRECTORY *const directory
+ = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_SECURITY);
+ if (directory == NULL) {
+ return;
+ }
+
+ if (directory->VirtualAddress == 0 || directory->Size == 0) {
+ return;
+ }
+
+ // This a file pointer rather than a common RVA.
+ uint32_t fileOffset = directory->VirtualAddress;
+
+ // TODO(jweyrich): We should count how many certificates the file has, and
+ // based on this decide whether to proceed and open the certificates scope.
+ output_open_scope("certificates", OUTPUT_SCOPE_TYPE_ARRAY);
+ while (fileOffset - directory->VirtualAddress < directory->Size) {
+ // Read the size of this WIN_CERTIFICATE
+ uint32_t *dwLength_ptr = LIBPE_PTR_ADD(ctx->map_addr, fileOffset);
+ if (! pe_can_read(ctx, dwLength_ptr, sizeof(uint32_t))) {
+ output_close_scope(); // certificates
+ // TODO: Should we report something?
+ return;
+ }
+ // Type punning
+ uint32_t dwLength = *(uint32_t *) dwLength_ptr;
+
+ WIN_CERTIFICATE *cert = LIBPE_PTR_ADD(ctx->map_addr, fileOffset);
+ if (! pe_can_read(ctx, cert, dwLength)) {
+ output_close_scope(); // certificates
+ // TODO: Should we report something?
+ return;
+ }
+
+ output_open_scope("certificate", OUTPUT_SCOPE_TYPE_OBJECT);
+
+ static char value[MAX_MSG];
+
+ snprintf(value, MAX_MSG, "%u bytes", cert->dwLength);
+ output("Length", value);
+
+ snprintf(value, MAX_MSG, "0x%x (%s)", cert->wRevision,
+ cert->wRevision == WIN_CERT_REVISION_1_0 ? "1"
+ : cert->wRevision == WIN_CERT_REVISION_2_0 ? "2"
+ : "unknown");
+ output("Revision", value);
+
+ snprintf(value, MAX_MSG, "0x%x", cert->wCertificateType);
+ switch (cert->wCertificateType) {
+ default:
+ bsd_strlcat(value, " (UNKNOWN)", MAX_MSG);
+ break;
+ case WIN_CERT_TYPE_X509:
+ bsd_strlcat(value, " (X509)", MAX_MSG);
+ break;
+ case WIN_CERT_TYPE_PKCS_SIGNED_DATA:
+ bsd_strlcat(value, " (PKCS_SIGNED_DATA)", MAX_MSG);
+ break;
+ case WIN_CERT_TYPE_TS_STACK_SIGNED:
+ bsd_strlcat(value, " (TS_STACK_SIGNED)", MAX_MSG);
+ break;
+ }
+ output("Type", value);
+
+ // Offset to the next certificate.
+ fileOffset += roundBy8(cert->dwLength);
+
+ if (fileOffset - directory->VirtualAddress > directory->Size) {
+ LIBPE_WARNING("either the attribute certificate table or the Size "
+ "field is corrupted");
+ output_close_scope(); // certificate
+ break; // Exit the while-loop.
+ }
+
+ switch (cert->wRevision) {
+ default:
+ LIBPE_WARNING("unknown wRevision");
+ break;
+ case WIN_CERT_REVISION_1_0:
+ LIBPE_WARNING("WIN_CERT_REVISION_1_0 is not supported");
+ break;
+ case WIN_CERT_REVISION_2_0:
+ break;
+ }
+
+ switch (cert->wCertificateType) {
+ default:
+ LIBPE_WARNING("unknown wCertificateType");
+ break;
+ case WIN_CERT_TYPE_X509:
+ LIBPE_WARNING("WIN_CERT_TYPE_X509 is not supported");
+ break;
+ case WIN_CERT_TYPE_PKCS_SIGNED_DATA: {
+ CRYPT_DATA_BLOB p7data;
+ p7data.cbData
+ = (uint32_t) (cert->dwLength
+ - offsetof(WIN_CERTIFICATE, bCertificate));
+ p7data.pbData = cert->bCertificate;
+ print_pkcs7_data(&p7data, _format, _out, verbose);
+ break;
+ }
+ case WIN_CERT_TYPE_TS_STACK_SIGNED:
+ LIBPE_WARNING("WIN_CERT_TYPE_TS_STACK_SIGNED is not supported");
+ break;
+ case WIN_CERT_TYPE_EFI_PKCS115:
+ LIBPE_WARNING("WIN_CERT_TYPE_EFI_PKCS115 is not supported");
+ break;
+ case WIN_CERT_TYPE_EFI_GUID:
+ LIBPE_WARNING("WIN_CERT_TYPE_EFI_GUID is not supported");
+ break;
+ }
+ output_close_scope(); // certificate
+ }
+ output_close_scope(); // certificates
+
+ free(_out);
+}
+
diff --git a/src/config.c b/src/config.c
index 765ef0ab..65b7abb5 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,10 +1,10 @@
/* vim :set ts=4 sw=4 sts=4 et : */
/*
- pev - the PE file analyzer toolkit
+ readpe - the PE file analyzer toolkit
config.c
- Copyright (C) 2013 - 2014 pev authors
+ Copyright (C) 2013 - 2025 readpe authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -34,34 +34,43 @@
files in the program, then also delete it here.
*/
-#include "config.h"
-#include
+#include "readpe/config.h"
+
+#include "compat.h"
+
+#include
#include
+#include
+#include
+#include
+#include
#include
#include
-#include
-#if defined(__linux__)
-#include // FIXME: Why?
-#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__CYGWIN__)
+
+#ifndef _MSC_VER
#include
#endif
-#define DEFAULT_CONFIG_FILENAME "pev.conf"
+#define DEFAULT_CONFIG_FILENAME "readpe.conf"
#if defined(__CYGWIN__) // Set current directory as default
-#define DEFAULT_CONFIG_PATH DEFAULT_CONFIG_FILENAME
+#define DEFAULT_CONFIG_PATH DEFAULT_CONFIG_FILENAME
#define DEFAULT_PLUGINS_PATH "plugins"
#else
-#define DEFAULT_CONFIG_PATH ".config/pev/" DEFAULT_CONFIG_FILENAME
+#define DEFAULT_CONFIG_PATH ".config/readpe/" DEFAULT_CONFIG_FILENAME
// PLUGINSDIR is defined via CPPFLAGS in the Makefile
#define DEFAULT_PLUGINS_PATH PLUGINSDIR
#endif
-static bool _load_config_cb(pev_config_t * const config, const char *name, const char *value) {
- //fprintf(stderr, "DEBUG: %s=%s\n", name, value);
+static bool _load_config_cb(struct readpe_config *const config,
+ const char *name, const char *value)
+{
+ // fprintf(stderr, "DEBUG: %s=%s\n", name, value);
- if (!strcmp("plugins_dir", name)) {
- config->plugins_path = strdup(value);
+ if (! strcmp("plugins_dir", name)) {
+ size_t len = strlen(value);
+ config->plugins_path = malloc(len);
+ memcpy((char *) config->plugins_path, value, len);
return true;
}
@@ -69,122 +78,130 @@ static bool _load_config_cb(pev_config_t * const config, const char *name, const
}
// FIX: Now the lines of config can have any size!
-static int _load_config_and_parse(pev_config_t * const config, const char *path, pev_config_parse_callback_t pev_cb) {
+static int _load_config_and_parse(struct readpe_config *const config,
+ const char *path,
+ readpe_config_parse_callback_t readpe_cb)
+{
FILE *fp = fopen(path, "r");
- if (fp == NULL)
+ if (fp == NULL) {
return 0;
+ }
- char *p, *line = NULL;
+ char *p, *line = NULL;
size_t size = 0;
- while ( getline( &line, &size, fp ) != -1 )
- {
+ while (readpe_getline(&line, &size, fp) != -1) {
// remove newline
- if ((p = strrchr( line, '\n')) != NULL) *p = '\0';
+ if ((p = strrchr(line, '\n')) != NULL) {
+ *p = '\0';
+ }
p = pe_utils_str_inplace_trim(line);
// if not a comment line...
- if (*p != '#')
- {
- char *param = strtok(p, "=");
- char *value = strtok(NULL, "=");
+ if (*p != '#') {
+ char *param = strtok(p, "=");
+ char *value = strtok(NULL, "=");
const char *trimmed_param = pe_utils_str_inplace_trim(param);
const char *trimmed_value = pe_utils_str_inplace_trim(value);
- //fprintf(stderr, "DEBUG: '%s'='%s'\n", trimmed_param, trimmed_value);
- const bool processed = pev_cb(config, trimmed_param, trimmed_value);
+ // fprintf(stderr, "DEBUG: '%s'='%s'\n", trimmed_param,
+ // trimmed_value);
+ const bool processed
+ = readpe_cb(config, trimmed_param, trimmed_value);
- if (!processed && config->user_defined.parse_callback != NULL)
- config->user_defined.parse_callback(config->user_defined.data, trimmed_param, trimmed_value);
+ if (! processed && config->user_defined.parse_callback != NULL) {
+ config->user_defined.parse_callback(
+ config->user_defined.data, trimmed_param, trimmed_value);
+ }
}
- free( line );
+ free(line);
line = NULL;
size = 0;
}
- free( line );
+ free(line);
fclose(fp);
return 1;
}
-#ifdef USE_MY_ASPRINTF
-int asprintf( char **pp, char *fmt, ... )
+// FIX: To avoid using fixed size PATH names we can use asprintf().
+int readpe_load_config(struct readpe_config *const config)
{
- char *p;
- int size;
- va_list args, args_safe;
-
- va_start( args, fmt );
- va_copy( args_safe, args );
-
- // Just get the string size.
- if ( ( size = vsnprintf( NULL, 0, fmt, args_safe ) ) < 0 )
- {
- va_end( args_safe );
- va_end( args );
- return -1;
+ char *buff;
+
+ int ret = pe_utils_is_file_readable(DEFAULT_CONFIG_FILENAME);
+ if (ret == LIBPE_E_OK) {
+ if (! _load_config_and_parse(config, DEFAULT_CONFIG_FILENAME,
+ _load_config_cb)) {
+ return -1;
+ }
}
- if ( ! ( p = malloc( size + 1 ) ) )
- {
- va_end( args_safe );
- va_end( args );
+#if defined(_MSC_VER)
+ if (asprintf(&buff, "%s/readpe/" DEFAULT_CONFIG_FILENAME, getenv("APPDATA"))
+ < 0) {
return -1;
}
- vsprintf( *pp = p, fmt, args );
-
- va_end( args_safe );
- va_end( args );
-
- return size;
-}
-#endif
-
-// FIX: To avoid using fixed size PATH names we can use asprintf().
-int pev_load_config(pev_config_t * const config) {
- char *buff;
+#elif defined(__linux__) && ! defined(__CYGWIN__)
+ char *xdg = getenv("XDG_CONFIG_HOME");
+ if (xdg) {
+ if (asprintf(&buff, "%s/readpe/" DEFAULT_CONFIG_FILENAME, xdg) < 0) {
+ return -1;
+ }
- int ret = pe_utils_is_file_readable(DEFAULT_CONFIG_FILENAME);
- if (ret == LIBPE_E_OK)
- if ( ! _load_config_and_parse(config, DEFAULT_CONFIG_FILENAME, _load_config_cb) )
+ } else {
+ if (asprintf(&buff, "%s/.config/readpe/" DEFAULT_CONFIG_FILENAME,
+ pe_utils_get_homedir())
+ < 0) {
return -1;
+ }
+ }
- // OBS: If asprintf isn't available to your system, use the definition above
- // using -DUSE_MY_ASPRINTF at compile time.
- if ( asprintf(&buff, "%s/" DEFAULT_CONFIG_PATH, pe_utils_get_homedir()) < 0 )
+#else
+ if (asprintf(&buff, "%s/" DEFAULT_CONFIG_FILENAME, pe_utils_get_homedir())
+ < 0) {
return -1;
+ }
+
+#endif
ret = pe_utils_is_file_readable(buff);
- if (ret == LIBPE_E_OK)
- if ( ! _load_config_and_parse(config, buff, _load_config_cb) )
- {
- free( buff );
+ if (ret == LIBPE_E_OK) {
+ if (! _load_config_and_parse(config, buff, _load_config_cb)) {
+ free(buff);
return -1;
}
+ }
- free( buff );
+ free(buff);
//
// Default values
//
- if (config->plugins_path == NULL)
- config->plugins_path = strdup(DEFAULT_PLUGINS_PATH);
+ if (config->plugins_path == NULL) {
+ size_t len = strlen(DEFAULT_PLUGINS_PATH);
+ config->plugins_path = malloc(len);
+ memcpy((char *) config->plugins_path, DEFAULT_PLUGINS_PATH, len);
+ }
return 0;
}
-void pev_cleanup_config(pev_config_t * const config) {
- if (config == NULL)
+void readpe_cleanup_config(struct readpe_config *const config)
+{
+ if (config == NULL) {
return;
+ }
- if ( config->user_defined.cleanup_callback &&
- config->user_defined.data )
+ if (config->user_defined.cleanup_callback && config->user_defined.data) {
config->user_defined.cleanup_callback(config->user_defined.data);
+ }
- free(config->plugins_path);
+ free((void *) config->plugins_path);
config->plugins_path = NULL;
}
+
diff --git a/src/directories.c b/src/directories.c
new file mode 100644
index 00000000..2d57bca6
--- /dev/null
+++ b/src/directories.c
@@ -0,0 +1,156 @@
+/* vim :set ts=4 sw=4 sts=4 et : */
+/*
+ readpe - the PE file analyzer toolkit
+
+ Copyright (C) 2013 - 2025 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#include "libpe/macros.h"
+#include "libpe/pe.h"
+#include "readpe/helper.h"
+#include "readpe/output.h"
+#include "readpe/readpe.h"
+
+#include
+#include
+
+IMAGE_DATA_DIRECTORY **get_pe_directories(pe_ctx_t *ctx)
+{
+ IMAGE_DATA_DIRECTORY **directories = pe_directories(ctx);
+ if (directories == NULL) {
+ LIBPE_WARNING("directories not found");
+ }
+
+ return directories;
+}
+
+void print_directories(pe_ctx_t *ctx)
+{
+#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06
+ typedef struct {
+ ImageDirectoryEntry entry;
+ const char *const name;
+ } ImageDirectoryEntryName;
+ static const ImageDirectoryEntryName directoryEntryNames[] = {
+ {IMAGE_DIRECTORY_ENTRY_EXPORT, "Export Table"}, // "Export directory",
+ {IMAGE_DIRECTORY_ENTRY_IMPORT, "Import Table"}, // "Import directory",
+ {IMAGE_DIRECTORY_ENTRY_RESOURCE,
+ "Resource Table" }, // "Resource directory",
+ {IMAGE_DIRECTORY_ENTRY_EXCEPTION,
+ "Exception Table" }, // "Exception directory",
+ {IMAGE_DIRECTORY_ENTRY_SECURITY,
+ "Certificate Table" }, // "Security directory",
+ {IMAGE_DIRECTORY_ENTRY_BASERELOC,
+ "Base Relocation Table" }, // "Base relocation table",
+ {IMAGE_DIRECTORY_ENTRY_DEBUG, "Debug" }, // "Debug directory",
+ {IMAGE_DIRECTORY_ENTRY_ARCHITECTURE,
+ "Architecture" }, // "Architecture-specific data",
+ {IMAGE_DIRECTORY_ENTRY_GLOBALPTR, "Global Ptr" }, // "Global pointer",
+ {IMAGE_DIRECTORY_ENTRY_TLS,
+ "Thread Local Storage (TLS)" }, // "Thread local storage (TLS)
+ // directory",
+ {IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
+ "Load Config Table" }, // "Load configuration directory",
+ {IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
+ "Bound Import" }, // "Bound import directory",
+ {IMAGE_DIRECTORY_ENTRY_IAT,
+ "Import Address Table (IAT)" }, // "Import address table (IAT)",
+ {IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT,
+ "Delay Import Descriptor" }, // "Delay import table",
+ {IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
+ "CLR Runtime Header" }, // "COM descriptor table"
+ {IMAGE_DIRECTORY_RESERVED, "" } // "Reserved"
+ };
+ // static const size_t max_directory_entry = LIBPE_SIZEOF_ARRAY(names);
+#endif
+ output_open_scope("Data directories", OUTPUT_SCOPE_TYPE_ARRAY);
+
+ const uint32_t num_directories = pe_directories_count(ctx);
+ if (num_directories == 0 || num_directories > MAX_DIRECTORIES) {
+ return;
+ }
+
+ IMAGE_DATA_DIRECTORY **directories = pe_directories(ctx);
+ if (directories == NULL) {
+ return;
+ }
+
+ static char s[MAX_MSG];
+
+ for (uint32_t i = 0; i < num_directories; i++) {
+ if (directories[i]->Size) {
+ // output_open_scope("Directory", OUTPUT_SCOPE_TYPE_OBJECT);
+ snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)",
+ directories[i]->VirtualAddress, directories[i]->Size);
+ output(pe_directory_name(i), s);
+ // output_close_scope(); // Directory
+ }
+ }
+
+ output_close_scope(); // Data directories
+}
+
+void print_directory_list(pe_ctx_t *ctx, bool verbose)
+{
+
+ if (verbose) {
+ output_open_scope("Data directories", OUTPUT_SCOPE_TYPE_OBJECT);
+ } else {
+ output_open_scope(NULL, OUTPUT_SCOPE_TYPE_ARRAY);
+ }
+
+ // output_open_scope(NULL, OUTPUT_SCOPE_TYPE_ARRAY);
+ const uint32_t num_directories = pe_directories_count(ctx);
+ if (num_directories == 0 || num_directories > MAX_DIRECTORIES) {
+ return;
+ }
+
+ IMAGE_DATA_DIRECTORY **directories = pe_directories(ctx);
+ if (directories == NULL) {
+ return;
+ }
+
+ static char s[MAX_MSG];
+
+ for (uint32_t i = 0; i < num_directories; i++) {
+ if (directories[i]->Size) {
+ if (verbose) {
+ snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)",
+ directories[i]->VirtualAddress, directories[i]->Size);
+ output(pe_directory_name(i), s);
+ } else {
+ output(NULL, pe_directory_name(i));
+ }
+ }
+ }
+
+ output_close_scope(); // Data directories
+}
+
diff --git a/src/dylib.c b/src/dylib.c
index 42ddf802..ee9e703f 100644
--- a/src/dylib.c
+++ b/src/dylib.c
@@ -5,10 +5,10 @@
Copyright (c) 2013, Jardel Weyrich
Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
@@ -18,9 +18,9 @@
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
In addition, as a special exception, the copyright holders give
permission to link the code of portions of this program with the
@@ -38,41 +38,49 @@
*/
#include "dylib.h"
-#include "common.h"
+
+#include "compat.h"
+
#include
#include
-#include
+#include
// FIX: Don't need to export.
-//inline const char *dylib_error(dylib_t *lib) {
+// inline const char *dylib_error(dylib_t *lib) {
// UNUSED(lib);
// return dlerror();
//}
#define dylib_error(...) dlerror()
-int dylib_load(dylib_t *lib, const char *path) {
+int dylib_load(dylib_t *lib, const char *path)
+{
if (lib->handle) {
- fprintf(stderr, "Can't load library because it's already loaded: %s\n", lib->path);
+ fprintf(stderr, "Can't load library because it's already loaded: %s\n",
+ lib->path);
return -1;
}
lib->handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
- if ( ! lib->handle ) {
- fprintf(stderr, "Failed to load library %s: %s\n", path, dylib_error(lib));
+ if (! lib->handle) {
+ fprintf(stderr, "Failed to load library %s: %s\n", path,
+ dylib_error(lib));
return -1;
}
- lib->path = strdup(path);
+ lib->path = readpe_strdup(path);
return 0;
}
-int dylib_unload(dylib_t *lib) {
+int dylib_unload(dylib_t *lib)
+{
int ret;
- if ( !lib->handle ) {
- fprintf(stderr, "Can't unload library '%s' because it's not loaded\n", lib->path);
+ if (! lib->handle) {
+ fprintf(stderr, "Can't unload library '%s' because it's not loaded\n",
+ lib->path);
return -1;
}
ret = dlclose(lib->handle);
if (ret != 0) {
- fprintf(stderr, "Failed to unload library %s: %s\n", lib->path, dylib_error(lib));
+ fprintf(stderr, "Failed to unload library %s: %s\n", lib->path,
+ dylib_error(lib));
return -1;
}
lib->handle = NULL;
@@ -81,15 +89,19 @@ int dylib_unload(dylib_t *lib) {
return 0;
}
-void *dylib_get_symbol(dylib_t *lib, const char *symbol) {
+void *dylib_get_symbol(dylib_t *lib, const char *symbol)
+{
void *addr = dlsym(lib->handle, symbol);
if (addr == NULL) {
- fprintf(stderr, "Symbol '%s' not found in '%s': %s\n", symbol, lib->path, dylib_error(lib));
+ fprintf(stderr, "Symbol '%s' not found in '%s': %s\n", symbol,
+ lib->path, dylib_error(lib));
}
return addr;
}
-int dylib_has_symbol(dylib_t *lib, const char *symbol) {
+int dylib_has_symbol(dylib_t *lib, const char *symbol)
+{
void *addr = dlsym(lib->handle, symbol);
return addr != NULL;
}
+
diff --git a/src/exports.c b/src/exports.c
new file mode 100644
index 00000000..b3503d37
--- /dev/null
+++ b/src/exports.c
@@ -0,0 +1,90 @@
+/* vim :set ts=4 sw=4 sts=4 et : */
+/*
+ readpe - the PE file analyzer toolkit
+
+ Copyright (C) 2013 - 2025 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#include "libpe/context.h"
+#include "libpe/pe.h"
+#include "readpe/output.h"
+#include "readpe/readpe.h"
+
+#include
+
+void print_exports(pe_ctx_t *ctx)
+{
+ output_open_scope("Exported functions", OUTPUT_SCOPE_TYPE_ARRAY);
+
+ const pe_exports_t *exports = pe_exports(ctx);
+
+ if (exports->functions_count > 0) {
+ output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT);
+ output("Name", exports->name);
+ output_open_scope("Functions", OUTPUT_SCOPE_TYPE_ARRAY);
+ }
+
+ for (size_t i = 0; i < exports->functions_count; i++) {
+ const pe_exported_function_t *func = &exports->functions[i];
+ if (func->address != 0) {
+ output_open_scope("Function", OUTPUT_SCOPE_TYPE_OBJECT);
+
+ char ordinal_str[32] = {0};
+ char address_str[16] = {0};
+ snprintf(ordinal_str, sizeof(ordinal_str) - 1, "%" PRIu32,
+ func->ordinal);
+ snprintf(address_str, sizeof(address_str) - 1, "%#" PRIx32,
+ func->address);
+
+ if (func->fwd_name != NULL) {
+ char full_name[300 * 2 + 4];
+ snprintf(full_name, sizeof(full_name) - 1, "%s -> %s",
+ func->name, func->fwd_name);
+ output("Ordinal", ordinal_str);
+ output("Address", address_str);
+ output("Name", full_name);
+ } else {
+ output("Ordinal", ordinal_str);
+ output("Address", address_str);
+ output("Name", func->name);
+ }
+
+ output_close_scope(); // Function
+ }
+ }
+
+ if (exports->functions_count > 0) {
+ output_close_scope(); // Functions
+ output_close_scope(); // Library
+ }
+
+ output_close_scope(); // Exported functions
+}
+
diff --git a/src/hash.c b/src/hash.c
new file mode 100644
index 00000000..556be6d7
--- /dev/null
+++ b/src/hash.c
@@ -0,0 +1,280 @@
+/* vim :set ts=4 sw=4 sts=4 et : */
+/*
+ readpe - the PE file analyzer toolkit
+
+ Copyright (C) 2012 - 2026 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#include "libpe/macros.h"
+#include "libpe/pe.h"
+#include "modes.h"
+#include "readpe/config.h"
+#include "readpe/helper.h"
+#include "readpe/output.h"
+#include "readpe/readpe.h"
+
+#include
+#include
+
+static void print_basic_hash(const unsigned char *data, size_t data_size)
+{
+ if (! data || ! data_size) {
+ return;
+ }
+
+ const char *basic_hashes[] = {"md5", "sha1", "sha256"
+#ifdef LIBPE_LINK_SSDEEP
+ ,
+ "ssdeep"
+#endif
+ };
+ const size_t hash_value_size = pe_hash_recommended_size();
+ char *hash_value = malloc_s(hash_value_size);
+
+ for (size_t i = 0; i < sizeof(basic_hashes) / sizeof(char *); i++) {
+ memset(hash_value, 0, hash_value_size);
+ pe_hash_raw_data(hash_value, hash_value_size, basic_hashes[i], data,
+ data_size);
+ output(basic_hashes[i], hash_value);
+ }
+
+ free(hash_value);
+}
+
+void print_content_hash(pe_ctx_t *ctx)
+{
+ const unsigned char *data = ctx->map_addr;
+ uint64_t data_size = pe_filesize(ctx);
+
+ output_open_scope("file", OUTPUT_SCOPE_TYPE_OBJECT);
+ output("filepath", ctx->path);
+ print_basic_hash(data, data_size);
+
+ char *imphash = NULL;
+
+ // imphash = pe_imphash(&ctx, LIBPE_IMPHASH_FLAVOR_MANDIANT);
+ // output("imphash (Mandiant)", imphash);
+ // free(imphash);
+
+ imphash = pe_imphash(ctx, LIBPE_IMPHASH_FLAVOR_PEFILE);
+
+ if (imphash) {
+ output("imphash", imphash);
+ free(imphash);
+ }
+
+ output_close_scope(); // file
+}
+
+void print_dos_header_hash(pe_ctx_t *ctx)
+{
+ const IMAGE_DOS_HEADER *dos_hdr = pe_dos(ctx);
+ const unsigned char *data = (const unsigned char *) dos_hdr;
+ uint64_t data_size = sizeof(IMAGE_DOS_HEADER);
+ print_basic_hash(data, data_size);
+}
+
+void print_coff_header_hash(pe_ctx_t *ctx)
+{
+ const IMAGE_COFF_HEADER *coff_hdr = pe_coff(ctx);
+ const unsigned char *data = (const unsigned char *) coff_hdr;
+ uint64_t data_size = sizeof(IMAGE_COFF_HEADER);
+ print_basic_hash(data, data_size);
+}
+
+void print_optional_header_hash(pe_ctx_t *ctx)
+{
+ const unsigned char *data = NULL;
+ uint64_t data_size = 0;
+
+ const IMAGE_OPTIONAL_HEADER *opt_hdr = pe_optional(ctx);
+ switch (opt_hdr->type) {
+ case MAGIC_ROM:
+ if (! pe_can_read(ctx, opt_hdr->_rom,
+ sizeof(IMAGE_ROM_OPTIONAL_HEADER))) {
+ // TODO: Should we report something?
+ break;
+ }
+ data = (const unsigned char *) opt_hdr->_rom;
+ data_size = sizeof(IMAGE_ROM_OPTIONAL_HEADER);
+ break;
+ case MAGIC_PE32:
+ if (! pe_can_read(ctx, opt_hdr->_32,
+ sizeof(IMAGE_OPTIONAL_HEADER_32))) {
+ // TODO: Should we report something?
+ break;
+ }
+ data = (const unsigned char *) opt_hdr->_32;
+ data_size = sizeof(IMAGE_OPTIONAL_HEADER_32);
+ break;
+ case MAGIC_PE64:
+ if (! pe_can_read(ctx, opt_hdr->_64,
+ sizeof(IMAGE_OPTIONAL_HEADER_64))) {
+ // TODO: Should we report something?
+ break;
+ }
+ data = (const unsigned char *) opt_hdr->_64;
+ data_size = sizeof(IMAGE_OPTIONAL_HEADER_64);
+ break;
+ }
+
+ print_basic_hash(data, data_size);
+}
+
+void print_sections_hash(pe_ctx_t *ctx)
+{
+ const unsigned char *data = NULL;
+ uint64_t data_size = 0;
+ unsigned c = pe_sections_count(ctx);
+ IMAGE_SECTION_HEADER **const sections = pe_sections(ctx);
+
+ for (unsigned int i = 0; i < c; i++) {
+ data_size = sections[i]->SizeOfRawData;
+ data = LIBPE_PTR_ADD(ctx->map_addr, sections[i]->PointerToRawData);
+
+ if (! pe_can_read(ctx, data, data_size)) {
+ LIBPE_WARNING("Unable to read section data");
+ } else {
+ output_open_scope("section", OUTPUT_SCOPE_TYPE_OBJECT);
+ output("section_name", (char *) sections[i]->Name);
+ if (data_size) {
+ print_basic_hash(data, data_size);
+ }
+ output_close_scope(); // section
+ }
+ }
+}
+
+static void print_section_hash(pe_ctx_t *ctx,
+ const IMAGE_SECTION_HEADER *section_ptr)
+{
+ const unsigned char *data = NULL;
+ uint64_t data_size = 0;
+
+ if (section_ptr != NULL) {
+ if (section_ptr->SizeOfRawData > 0) {
+ const uint8_t *section_data_ptr
+ = LIBPE_PTR_ADD(ctx->map_addr, section_ptr->PointerToRawData);
+ // fprintf(stderr, "map_addr = %p\n", ctx.map_addr);
+ // fprintf(stderr, "section_data_ptr = %p\n", section_data_ptr);
+ // fprintf(stderr, "SizeOfRawData = %u\n",
+ // section_ptr->SizeOfRawData);
+ if (! pe_can_read(ctx, section_data_ptr,
+ section_ptr->SizeOfRawData)) {
+ EXIT_ERROR("The requested section has an invalid size");
+ }
+ data = (const unsigned char *) section_data_ptr;
+ data_size = section_ptr->SizeOfRawData;
+ } else {
+ data = (const unsigned char *) "";
+ data_size = 0;
+ }
+ }
+
+ char name[9] = {0};
+ strncpy(name, (char *) section_ptr->Name, 8);
+ name[8] = 0;
+
+ if (data != NULL) {
+ output("section_name", (char *) name);
+ print_basic_hash(data, data_size);
+ }
+}
+
+void print_section_hash_by_index(pe_ctx_t *ctx, unsigned int index)
+{
+
+ IMAGE_SECTION_HEADER **const sections = pe_sections(ctx);
+ const uint16_t num_sections = pe_sections_count(ctx);
+ if (num_sections == 0 || index > num_sections) {
+ EXIT_ERROR("The requested section could not be found on this binary");
+ }
+ const IMAGE_SECTION_HEADER *section = sections[index - 1];
+ print_section_hash(ctx, section);
+}
+
+void print_section_hash_by_name(pe_ctx_t *ctx, char *name)
+{
+ const IMAGE_SECTION_HEADER *section = pe_section_by_name(ctx, name);
+ if (section == NULL) {
+ EXIT_ERROR("The requested section could not be found on this binary");
+ }
+ print_section_hash(ctx, section);
+}
+
+void print_hash(pe_ctx_t *ctx, const struct readpe_config *config)
+{
+ switch (config->context) {
+ case MODE_HEADERS:
+ output_open_scope("headers", OUTPUT_SCOPE_TYPE_ARRAY);
+
+ output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT);
+ output("header_name", "IMAGE_DOS_HEADER");
+ print_dos_header_hash(ctx);
+ output_close_scope(); // header
+
+ output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT);
+ output("header_name", "IMAGE_COFF_HEADER");
+ print_coff_header_hash(ctx);
+ output_close_scope(); // header
+
+ output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT);
+ output("header_name", "IMAGE_OPTIONAL_HEADER");
+ print_optional_header_hash(ctx);
+ output_close_scope(); // header
+ output_close_scope(); // headers
+ break;
+ case MODE_HEADERS_DOS:
+ print_dos_header_hash(ctx);
+ break;
+ case MODE_HEADERS_COFF:
+ print_coff_header_hash(ctx);
+ break;
+ case MODE_HEADERS_OPTIONAL:
+ print_optional_header_hash(ctx);
+ break;
+ case MODE_SECTIONS:
+ output_open_scope("sections", OUTPUT_SCOPE_TYPE_ARRAY);
+ print_sections_hash(ctx);
+ output_close_scope(); // sections
+ break;
+ case MODE_SECTION:
+ if (config->section.name != NULL) {
+ print_section_hash_by_name(ctx, config->section.name);
+ } else if (config->section.index > 0) {
+ print_section_hash_by_index(ctx, config->section.index);
+ }
+ break;
+ default:
+ print_content_hash(ctx);
+ break;
+ }
+}
+
diff --git a/src/header.c b/src/header.c
new file mode 100644
index 00000000..e3e8248d
--- /dev/null
+++ b/src/header.c
@@ -0,0 +1,729 @@
+/*
+ readpe - the PE file analyzer toolkit
+
+ Copyright (C) 2013 - 2025 readpe authors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#include "libpe/context.h"
+#include "libpe/hdr_optional.h"
+#include "libpe/macros.h"
+#include "libpe/pe.h"
+#include "readpe/helper.h"
+#include "readpe/output.h"
+#include "readpe/readpe.h"
+
+#include
+#include