diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..106e49c4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,237 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: Left +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: true + AcrossEmptyLines: true + 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 0acbe95e..edf8646f 100644 --- a/.github/workflows/multi-os-build.yml +++ b/.github/workflows/multi-os-build.yml @@ -9,62 +9,68 @@ 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@v4 + - 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@v4 + - name: config + run: >- + cmake + -Bbuild + -DENABLE_TESTS=Off - name: Compile - run: CFLAGS="-I$(brew --prefix openssl@1.1)/include/" LDFLAGS="-L$(brew --prefix openssl@1.1)/lib/" make + run: cmake --build build +# - name: Compile +# run: CFLAGS="-I$(brew --prefix openssl@1.1)/include/" LDFLAGS="-L$(brew --prefix openssl@1.1)/lib/" make 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 + 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@v4 + - 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 +# - name: Compile +# run: make +# - name: Compile Windows-only tools and create a ZIP package +# run: make 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..f114938f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ Debug .cproject .project *.swp +*.plist *~ *.so* *.dylib @@ -15,6 +16,7 @@ Debug *.dSYM *.log .settings +build/** src/ofs2rva src/output src/pedis diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..5970a775 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.20.0) +project(readpe) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(BUILD_DISASSEMBLER "Build with basic disassembler support (deprecated)" OFF) +option(BUILD_LEGACY "Build support for legacy executables by link name" ON) +option(BUILD_STANDALONE "Build separate executables" OFF) +option(BUILD_STATIC "Build static executable" Off) +option(ENABLE_TESTS "Enable testing" On) + +if(BUILD_DISASSEMBLER) + add_subdirectory(lib/libudis86/libudis86) +endif() + +add_subdirectory(lib/libpe) +add_subdirectory(src/plugins) +add_subdirectory(src) + +if(ENABLE_TESTS) + include("${CMAKE_SOURCE_DIR}/cmake/tests.cmake") +endif() + +set( + CPACK_PACKAGE_NAME ${PROJECT_NAME} + CACHE STRING "The resulting package name" +) + + +set(CPACK_STRIP_FILES YES) + + +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_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") + + +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/cmake/TC-mingw64.cmake b/cmake/TC-mingw64.cmake new file mode 100644 index 00000000..59de675d --- /dev/null +++ b/cmake/TC-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/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/tests.cmake b/cmake/tests.cmake new file mode 100644 index 00000000..cf5042d7 --- /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/TC-mingw64.cmake" + INSTALL_COMMAND "" +) + +file(WRITE "${CMAKE_BINARY_DIR}/pev.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/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/include/common.h b/include/common.h index 87b2eb82..491ccb60 100644 --- a/include/common.h +++ b/include/common.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 - common.h - common defitions for the pev toolkit. + common.h - common defitions for the readpe toolkit. - Copyright (C) 2013 - 2020 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,53 +35,66 @@ */ #pragma once +#ifndef READPE_COMMON_H +#define READPE_COMMON_H +#include "config.h" +#include "output.h" +#include "plugins.h" + +#include +#include +#include #include #include -#include #include #include -#include -#include -#include -#include "config.h" -#include "output.h" -#include "plugins.h" +#ifdef __cplusplus +extern "C" { +#endif -#define UNUSED(x) (void)(sizeof((x))) +#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 EXIT_ERROR(msg) \ + { \ + _exit_error_(__FILE__, __LINE__, msg); \ + } -#define MAX_MSG 81 +#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) + +#ifndef VERSION +#define VERSION "1.0" +#endif + +void *malloc_s(size_t size); +void *calloc_s(size_t nmemb, size_t size); + +static inline void readpe_initialize(struct readpe_config *config) +{ + memset(config, 0, sizeof(*config)); + readpe_load_config(config); + plugins_load_all(config); + output_init(); /* Requires plugin for text output. */ +} + +static inline void readpe_finalize(struct readpe_config *config) +{ + output_term(); + plugins_unload_all(); + readpe_cleanup_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" +#endif + +#endif + diff --git a/include/compat/asprintf.h b/include/compat/asprintf.h new file mode 100644 index 00000000..aa9b46ae --- /dev/null +++ b/include/compat/asprintf.h @@ -0,0 +1,52 @@ +/* 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. + */ + +#ifndef ASPRINTF_H_ +#define ASPRINTF_H_ + +#include + +#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) +#ifdef __cplusplus +extern "C" { +#endif + +int vasprintf(char **ptr, const char *format, va_list ap); +int asprintf(char **ptr, const char *format, ...); + +#ifdef __cplusplus +} +#endif +#endif + +#endif + diff --git a/include/config.h b/include/config.h index f12d1a54..c6d01fef 100644 --- a/include/config.h +++ b/include/config.h @@ -4,7 +4,7 @@ config.h - 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 @@ -35,27 +35,37 @@ */ #pragma once +#ifndef READPE_CONFIG_H +#define READPE_CONFIG_H #include -struct _pev_config_t; // Forward declaration. -typedef bool (*pev_config_parse_callback_t)(struct _pev_config_t * const config, const char *name, const char *value); -typedef void (*pev_config_cleanup_callback_t)(void *data); +#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); -typedef struct _pev_config_t { - char *plugins_path; +struct readpe_config { + const char *plugins_path; struct { - pev_config_parse_callback_t parse_callback; - pev_config_cleanup_callback_t cleanup_callback; - void *data; + readpe_config_parse_callback_t parse_callback; + readpe_config_cleanup_callback_t cleanup_callback; + void *data; } user_defined; -} pev_config_t; +}; -const char *pev_plugins_path(void); +const char *readpe_plugins_path(void); -int pev_load_config(pev_config_t * const config); -void pev_cleanup_config(pev_config_t * const config); +int readpe_load_config(struct readpe_config *const config); +void readpe_cleanup_config(struct readpe_config *const config); -#ifdef USE_MY_ASPRINTF -int asprintf( char **, char *, ... ); +#ifdef __cplusplus +} // extern "C" #endif + +#endif + diff --git a/include/modes.h b/include/modes.h new file mode 100644 index 00000000..c99b3287 --- /dev/null +++ b/include/modes.h @@ -0,0 +1,97 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + Copyright (C) 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_MODES_H +#define READPE_MODES_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum MODES { + MODE_BASE = 0, + + MODE_START = 1000, + MODE_HEADERS, + MODE_HEADERS_DOS, + MODE_HEADERS_COFF, + MODE_HEADERS_OPTIONAL, + MODE_DIRECTORIES, + MODE_EXPORTS, + MODE_IMPORTS, + MODE_RESOURCES, // -- peres + // MODE_EXCEPTIONS, + MODE_CERTIFICATES, // not part of image / -- pesec + // MODE_BASE_RELOCATIONS, + // MODE_DEBUG, + // MODE_ARCHITECTURE, + // MODE_GLOBAL_PTR, + // MODE_TLS, + // MODE_LOAD_CONFIGS, + // MODE_BOUND_IMPORT, + // MODE_IAT, + // MODE_DELAY_IMPORT_DESCRIPTOR, + // MODE_CLR_RUNTIME_HEADER, + MODE_SECURITY, // Duplicate of MODE_CERTIFICATES, + MODE_SECTIONS, + MODE_SECTION, + // MODE_LIBRARIES, // -- peldd + + COMMAND_START = 2000, + COMMAND_LIST, + COMMAND_SCAN, // -- pescan + COMMAND_EXTRACT, + COMMAND_HASH, // -- pehash + // COMMAND_HASH_MD5, + // COMMAND_HASH_SHA1, + // COMMAND_HASH_SHA256, + // COMMAND_HASH_SSDEEP, + // COMMAND_HASH_IMPHASH, + COMMAND_STRINGS, // -- pestr + // MODE_STRINGS_ASCII, + // MODE_STRINGS_UNICODE, + + // COMMAND_DISASSAMBLE = 100000, // -- pedis + // COMMAND_PACK, // -- pepack + // COMMAND_ADDRESSING_RELATIVE, // -- ofs2rva + // COMMAND_ADDRESSING_OFFSET, // -- rva2ofs +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + diff --git a/include/output.h b/include/output.h index b762729d..d5028c2e 100644 --- a/include/output.h +++ b/include/output.h @@ -4,7 +4,7 @@ output.h - Symbols and APIs to be used to output data in multiple formats. - Copyright (C) 2012 - 2014 pev authors + 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 @@ -35,78 +35,78 @@ */ #pragma once +#ifndef READPE_OUTPUT_H +#define READPE_OUTPUT_H + +#include +#include #ifdef __cplusplus extern "C" { #endif -#include -#include - typedef int format_id_t; 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 +struct format; // 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 char *const entity_t; +typedef char **const 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_init(void); // IMPORTANT: Requires the text plugin to be loaded. void output_term(void); -const char *output_cmdline(void); -void output_set_cmdline(int argc, char *argv[]); +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); #ifdef __cplusplus -} //extern "C" +} // extern "C" +#endif + #endif + diff --git a/include/output_plugin.h b/include/output_plugin.h index 9dc3de65..49550197 100644 --- a/include/output_plugin.h +++ b/include/output_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. - Copyright (C) 2014 pev authors + 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 @@ -35,9 +35,13 @@ */ #pragma once +#ifndef READPE_OUTPUT_PLUGIN_H +#define READPE_OUTPUT_PLUGIN_H -#include "plugin.h" #include "output.h" +#include "plugin.h" + +#include #ifdef __cplusplus extern "C" { @@ -53,29 +57,46 @@ extern "C" { // 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) +#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 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; - -output_plugin_api_t *output_plugin_api_ptr(void); +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 { + output_plugin_cmdline_fn_t cmdline; + output_plugin_register_format_fn_t register_format; + output_plugin_unregister_format_fn_t unregister_format; + output_plugin_escape_fn_t escape; + output_plugin_escape_ex_fn_t escape_ex; + output_plugin_escape_fn_t escape_quoted; + output_plugin_escape_ex_fn_t escape_ex_quoted; + output_plugin_escape_count_chars_ex_fn_t escape_count_chars_ex; +}; + +struct readpe_output_plugin { + struct readpe_plugin readpe_plugin; + struct format format; +}; + +struct readpe_output_api *readpe_output_api_ptr(void); #ifdef __cplusplus } #endif +#endif // READPE_OUTPUT_PLUGIN_H + diff --git a/include/plugin.h b/include/plugin.h index 74149218..18d87b85 100644 --- a/include/plugin.h +++ b/include/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 plugin.h - Plugin API that every plugin MUST implement. - Copyright (C) 2012 - 2014 pev authors + 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 @@ -35,25 +35,55 @@ */ #pragma once - -#include +#ifndef READPE_PLUGIN_H +#define READPE_PLUGIN_H #ifdef __cplusplus extern "C" { #endif -struct _pev_api_t; +// enum readpe_plugin_type_id { +#define readpe_plugin_type_generic 0 +#define readpe_plugin_type_output 1 +// }; + +struct readpe_api; +struct readpe_output_api; typedef int (*plugin_loaded_fn_t)(void); -typedef int (*plugin_initialize_fn_t)(const struct _pev_api_t *api); +typedef int (*plugin_initialize_fn_t)(const struct readpe_api *api); typedef void (*plugin_shutdown_fn_t)(void); typedef void (*plugin_unloaded_fn_t)(void); -int plugin_loaded(void); -int plugin_initialize(const struct _pev_api_t *api); -void plugin_shutdown(void); -void plugin_unloaded(void); +/* 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 { + struct readpe_output_api *output; +}; + +/* 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 { + int type_id; + plugin_loaded_fn_t loaded; + plugin_initialize_fn_t initialize; + plugin_shutdown_fn_t shutdown; + plugin_unloaded_fn_t unloaded; +}; + +struct readpe_api *readpe_api_ptr(void); #ifdef __cplusplus -} //extern "C" +} // extern "C" #endif +#endif // READPE_PLUGIN_H + diff --git a/include/plugins.h b/include/plugins.h index 30151075..9193afa3 100644 --- a/include/plugins.h +++ b/include/plugins.h @@ -4,7 +4,7 @@ plugins.h - Symbols and definitions for the plugins subsystem. - Copyright (C) 2012 - 2014 pev authors + 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 @@ -35,6 +35,8 @@ */ #pragma once +#ifndef READPE_PLUGINS_H +#define READPE_PLUGINS_H #include "config.h" @@ -42,11 +44,14 @@ extern "C" { #endif -int plugins_load(const char *path); -int plugins_load_all(pev_config_t *config); -int plugins_load_all_from_directory(const char *path); +int plugins_load(const char *path); +int plugins_load_all(struct readpe_config *config); +int plugins_load_all_from_directory(const char *path); void plugins_unload_all(void); #ifdef __cplusplus } #endif + +#endif + diff --git a/include/readpe.h b/include/readpe.h new file mode 100644 index 00000000..4b48ed1c --- /dev/null +++ b/include/readpe.h @@ -0,0 +1,157 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + readpe.h + + Copyright (C) 2023 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 +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + 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 +} hash_alghorithms_e; + +struct readpe_settings { + int mode; + int context; + + char *format; + // bool help; + bool list; + bool verbose; + bool file_version; + + bool res_info; + bool res_named; + bool res_statistics; + bool res_tree; + + bool str_offset; + bool str_section; + unsigned int str_min_length; + + void *cert_out; + void *cert_format; + + char *section_name; + unsigned int section_index; + + bool all; +}; + +// typedef struct { +// const char *certoutform; +// const char *certout; +// } certificate_settings; + +// ------------------------------------------------------------------------- // + +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); +void print_section_by_name(pe_ctx_t *ctx, const char *section_name); +void print_sections(pe_ctx_t *ctx); +void print_sections_list(pe_ctx_t *ctx); + +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_settings *settings); +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); + +// ------------------------------------------------------------------------- // + +typedef struct { + unsigned short strsize; + bool offset; + bool section; +} string_settings; + +void print_strings(pe_ctx_t *ctx); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/pev_api.h b/include/readpe_api.h similarity index 83% rename from include/pev_api.h rename to include/readpe_api.h index 096f362d..785e5f52 100644 --- a/include/pev_api.h +++ b/include/readpe_api.h @@ -1,10 +1,9 @@ -/* vim :set ts=4 sw=4 sts=4 et : */ /* pev - the PE file analyzer toolkit - pev_api.h - Symbols and APIs to be used by all plugins. + readpe_api.h - Symbols and APIs to be used in plugins. - Copyright (C) 2014 pev authors + Copyright (C) 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,21 +34,22 @@ */ #pragma once - +#ifndef READPE_READPE_API_H +#define READPE_READPE_API_H #ifdef __cplusplus extern "C" { #endif -struct _output_plugin_api; // from output_plugin.h +struct readpe_output_api; -typedef struct _pev_api_t { - struct _output_plugin_api *output; -} pev_api_t; +struct readpe_api { + 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 - diff --git a/include/settings.h b/include/settings.h new file mode 100644 index 00000000..ec4dc1d8 --- /dev/null +++ b/include/settings.h @@ -0,0 +1,77 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + Copyright (C) 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_SETTINGS_H +#define READPE_SETTINGS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct readpe_settings { + int mode; + int context; + + char *format; + // bool help; + bool list; + bool verbose; + bool file_version; + + bool res_info; + bool res_statistics; + bool res_tree; + + int str_offset; + int str_section; + int str_min_length; + + void *cert_out; + void *cert_format; + + char *section_name; + unsigned int section_index; + + bool all; +} readpe_settings_t; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + diff --git a/include/stack.h b/include/stack.h index 844fc438..26cb7192 100644 --- a/include/stack.h +++ b/include/stack.h @@ -7,10 +7,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 @@ -20,58 +20,64 @@ 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. */ #pragma once +#ifndef READPE_STACK_H +#define READPE_STACK_H +#include #include +#include +#include +#include -#define STACK_PASTE_2_(_1,_2) _1 ## _2 -#define STACK_PASTE_2(_1,_2) STACK_PASTE_2_(_1, _2) +#define STACK_PASTE_2_(_1, _2) _1##_2 +#define STACK_PASTE_2(_1, _2) STACK_PASTE_2_(_1, _2) -#if !defined(STACK_PREFIX) -# define STACK_PREFIX PEV_ +#if ! defined(STACK_PREFIX) +#define STACK_PREFIX READPE_ #endif -#if !defined(STACK_ELEMENT_TYPE) -# define STACK_ELEMENT_TYPE void * +#if ! defined(STACK_ELEMENT_TYPE) +#define STACK_ELEMENT_TYPE void * #endif -#define STACK_TYPE STACK_PASTE_2(STACK_PREFIX, stack_t) -#define STACK_API(fnname) STACK_PASTE_2(STACK_PREFIX, fnname) +#define STACK_TYPE STACK_PASTE_2(STACK_PREFIX, stack_t) +#define STACK_API(fnname) STACK_PASTE_2(STACK_PREFIX, fnname) // Use these macros! Don't call functions directly. -#define STACK_ALLOC(capacity) STACK_API(stack_alloc)(capacity) -#define STACK_DEALLOC(stack_ptr) STACK_API(stack_dealloc)(stack_ptr) -#define STACK_COUNT(stack_ptr) STACK_API(stack_count)(stack_ptr) -#define STACK_GROW(stack_ptr, capacity) STACK_API(stack_grow)(stack_ptr, capacity) -#define STACK_PUSH(stack_ptr, element) STACK_API(stack_push)(stack_ptr, element) -#define STACK_POP(stack_ptr, element_ptr) STACK_API(stack_pop)(stack_ptr, element_ptr) -#define STACK_PEEK(stack_ptr, element_ptr) STACK_API(stack_peek)(stack_ptr, element_ptr) +#define STACK_ALLOC(capacity) STACK_API(stack_alloc)(capacity) +#define STACK_DEALLOC(stack_ptr) STACK_API(stack_dealloc)(stack_ptr) +#define STACK_COUNT(stack_ptr) STACK_API(stack_count)(stack_ptr) +#define STACK_GROW(stack_ptr, capacity) \ + STACK_API(stack_grow)(stack_ptr, capacity) +#define STACK_PUSH(stack_ptr, element) STACK_API(stack_push)(stack_ptr, element) +#define STACK_POP(stack_ptr, element_ptr) \ + STACK_API(stack_pop)(stack_ptr, element_ptr) +#define STACK_PEEK(stack_ptr, element_ptr) \ + STACK_API(stack_peek)(stack_ptr, element_ptr) typedef struct { - uint16_t capacity; - uint16_t used; + uint16_t capacity; + uint16_t used; STACK_ELEMENT_TYPE *elements; } STACK_TYPE; static STACK_TYPE *STACK_API(stack_alloc)(uint16_t capacity); -static void STACK_API(stack_dealloc)(STACK_TYPE *stack); -static uint16_t STACK_API(stack_count)(STACK_TYPE *stack); -static int STACK_API(stack_grow)(STACK_TYPE *stack, uint16_t capacity); +static void STACK_API(stack_dealloc)(STACK_TYPE *stack); +static uint16_t STACK_API(stack_count)(STACK_TYPE *stack); +static int STACK_API(stack_grow)(STACK_TYPE *stack, uint16_t capacity); static int STACK_API(stack_push)(STACK_TYPE *stack, STACK_ELEMENT_TYPE element); static int STACK_API(stack_pop)(STACK_TYPE *stack, STACK_ELEMENT_TYPE *element); -static int STACK_API(stack_peek)(STACK_TYPE *stack, STACK_ELEMENT_TYPE *element); +static int STACK_API(stack_peek)(STACK_TYPE *stack, + STACK_ELEMENT_TYPE *element); // ---------------------------------------------------------------------------- -#include -#include -#include -#include - -STACK_TYPE * STACK_API(stack_alloc)(uint16_t capacity) { +STACK_TYPE *STACK_API(stack_alloc)(uint16_t capacity) +{ STACK_TYPE *stack = calloc(1, sizeof(STACK_TYPE)); if (stack == NULL) { fprintf(stderr, "stack: failed to allocate\n"); @@ -89,7 +95,8 @@ STACK_TYPE * STACK_API(stack_alloc)(uint16_t capacity) { return stack; } -void STACK_API(stack_dealloc)(STACK_TYPE *stack) { +void STACK_API(stack_dealloc)(STACK_TYPE *stack) +{ assert(stack != NULL); if (stack == NULL) { @@ -97,19 +104,22 @@ void STACK_API(stack_dealloc)(STACK_TYPE *stack) { return; } - if (stack->elements != NULL) + if (stack->elements != NULL) { free(stack->elements); + } - //memset(stack, 0, sizeof(*stack)); + // memset(stack, 0, sizeof(*stack)); free(stack); } -uint16_t STACK_API(stack_count)(STACK_TYPE *stack) { +uint16_t STACK_API(stack_count)(STACK_TYPE *stack) +{ assert(stack != NULL); return stack->used; } -int STACK_API(stack_grow)(STACK_TYPE *stack, uint16_t capacity) { +int STACK_API(stack_grow)(STACK_TYPE *stack, uint16_t capacity) +{ assert(stack != NULL); assert(capacity > stack->capacity); @@ -118,10 +128,10 @@ int STACK_API(stack_grow)(STACK_TYPE *stack, uint16_t capacity) { return -1; } - const size_t element_size = sizeof(STACK_ELEMENT_TYPE); - const size_t new_size = capacity * element_size; + const size_t element_size = sizeof(STACK_ELEMENT_TYPE); + const size_t new_size = capacity * element_size; - STACK_ELEMENT_TYPE *temp = realloc(stack->elements, new_size); + STACK_ELEMENT_TYPE *temp = realloc(stack->elements, new_size); if (temp == NULL) { fprintf(stderr, "stack: failed to allocate requested capacity\n"); return -2; @@ -133,12 +143,14 @@ int STACK_API(stack_grow)(STACK_TYPE *stack, uint16_t capacity) { return 0; } -int STACK_API(stack_push)(STACK_TYPE *stack, STACK_ELEMENT_TYPE element) { +int STACK_API(stack_push)(STACK_TYPE *stack, STACK_ELEMENT_TYPE element) +{ assert(stack != NULL); // Stack is full? if (stack->used >= stack->capacity) { - // TODO(jweyrich): We could call `stack_grow` instead of failing miserably. Make this behavior adjustable? + // TODO(jweyrich): We could call `stack_grow` instead of failing + // miserably. Make this behavior adjustable? fprintf(stderr, "stack: stack is full - failed to push\n"); return -1; } @@ -148,7 +160,8 @@ int STACK_API(stack_push)(STACK_TYPE *stack, STACK_ELEMENT_TYPE element) { return 0; } -int STACK_API(stack_pop)(STACK_TYPE *stack, STACK_ELEMENT_TYPE *element) { +int STACK_API(stack_pop)(STACK_TYPE *stack, STACK_ELEMENT_TYPE *element) +{ assert(stack != NULL); // Stack is empty? @@ -164,7 +177,8 @@ int STACK_API(stack_pop)(STACK_TYPE *stack, STACK_ELEMENT_TYPE *element) { return 0; } -int STACK_API(stack_peek)(STACK_TYPE *stack, STACK_ELEMENT_TYPE *element) { +int STACK_API(stack_peek)(STACK_TYPE *stack, STACK_ELEMENT_TYPE *element) +{ assert(stack != NULL); // Stack is empty? @@ -179,3 +193,6 @@ int STACK_API(stack_peek)(STACK_TYPE *stack, STACK_ELEMENT_TYPE *element) { return 0; } + +#endif + diff --git a/lib/libpe/CMakeLists.txt b/lib/libpe/CMakeLists.txt new file mode 100644 index 00000000..246ffb36 --- /dev/null +++ b/lib/libpe/CMakeLists.txt @@ -0,0 +1,77 @@ +project(libpe LANGUAGES C) + +set(HEADERS + include/libpe/context.h + include/libpe/directories.h + include/libpe/dir_import.h + include/libpe/dir_resources.h + include/libpe/dir_security.h + include/libpe/error.h + include/libpe/exports.h + include/libpe/hashes.h + include/libpe/hdr_dos.h + include/libpe/hdr_coff.h + include/libpe/hdr_optional.h + include/libpe/imports.h + include/libpe/macros.h + include/libpe/ordlookup.h + include/libpe/pe.h + include/libpe/resources.h + include/libpe/sections.h + include/libpe/types_resources.h + include/libpe/utils.h + include/libpe/utlist.h + + libfuzzy/fuzzy.h +) +set(SOURCES + libfuzzy/edit_dist.c + libfuzzy/fuzzy.c + + security.c + error.c + exports.c + hashes.c + imports.c + + misc.c + pe.c + resources.c + utils.c +) + +find_package(OpenSSL REQUIRED) + +if(BUILD_STATIC) + add_library(pe STATIC ${HEADERS} ${SOURCES}) +else() + add_library(pe SHARED ${HEADERS} ${SOURCES}) +endif() + +target_link_libraries(pe PRIVATE OpenSSL::Crypto OpenSSL::SSL ) + +target_include_directories(pe PRIVATE "include" ${OPENSSL_INCLUDE_DIR} ) +if(MSVC) + target_compile_options(pe PRIVATE /W4 /WX) +else() + target_compile_options(pe PRIVATE + -Wall + -Wextra + -Wpedantic + -Wshadow + -Wundef + -Wdouble-promotion + -Wformat=2 + -Wformat-security + -Wconversion + ) +endif() + +install(TARGETS pe + EXPORT peTargets + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include/libpe +) + 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..c81b1d17 100644 --- a/lib/libpe/error.c +++ b/lib/libpe/error.c @@ -21,8 +21,8 @@ #include "libpe/error.h" #include "libpe/macros.h" -#include #include +#include #include // FIX: Since all errors, except the first, @@ -32,103 +32,113 @@ // // 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); +#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..9fe6e68d 100644 --- a/lib/libpe/exports.c +++ b/lib/libpe/exports.c @@ -21,209 +21,236 @@ #include "libpe/exports.h" +#include "libpe/macros.h" #include "libpe/pe.h" #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 = 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); if (ordinal < exp->NumberOfFunctions) { 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 = 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; } -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..9f3b22bb 100644 --- a/lib/libpe/hashes.c +++ b/lib/libpe/hashes.c @@ -21,742 +21,802 @@ #include "libpe/hashes.h" -#include "libpe/error.h" -#include "libpe/pe.h" #include "libfuzzy/fuzzy.h" +#include "libpe/error.h" +#include "libpe/macros.h" #include "libpe/ordlookup.h" +#include "libpe/pe.h" #include "libpe/utlist.h" +#include +#include #include #include -#include -#include +#include #include -#include // 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 = 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; + } 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! #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; +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; - return result; + 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; - } - - 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; - } - - unsigned char md_value[EVP_MAX_MD_SIZE]; - unsigned int md_len; +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, (uint32_t)data_size, output); + return true; + } + + 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; + } + + 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 = 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 = 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 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 = 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..8ddabea2 100644 --- a/lib/libpe/imports.c +++ b/lib/libpe/imports.c @@ -21,487 +21,551 @@ #include "libpe/imports.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 = 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); + 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 index c167ec4c..d8638b68 100644 --- a/lib/libpe/include/libpe/context.h +++ b/lib/libpe/include/libpe/context.h @@ -1,7 +1,7 @@ /* libpe - the PE library - Copyright (C) 2010 - 2023 libpe authors + Copyright (C) 2010 - 2025 libpe authors This file is part of libpe. @@ -22,67 +22,68 @@ #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 "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; + // 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; + // 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; + 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 index eed6e883..bacdbb65 100644 --- a/lib/libpe/include/libpe/dir_import.h +++ b/lib/libpe/include/libpe/dir_import.h @@ -31,56 +31,62 @@ extern "C" { #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; + 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 + 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]; + 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; + 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; + 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) @@ -90,3 +96,4 @@ typedef struct { #endif #endif + diff --git a/lib/libpe/include/libpe/dir_resources.h b/lib/libpe/include/libpe/dir_resources.h index 1163e826..47b5d0ee 100644 --- a/lib/libpe/include/libpe/dir_resources.h +++ b/lib/libpe/include/libpe/dir_resources.h @@ -29,98 +29,100 @@ extern "C" { #endif -#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000 -#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000 +#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 +// 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 + 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; + 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; + 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; + } 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]; + uint16_t Length; + char String[1]; } IMAGE_RESOURCE_DATA_STRING; typedef struct { - uint16_t Length; // Number of Unicode characters - uint16_t String[1]; + 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; + uint32_t OffsetToData; + uint32_t Size; + uint32_t CodePage; + uint32_t Reserved; } IMAGE_RESOURCE_DATA_ENTRY; 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; + 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) @@ -130,3 +132,4 @@ typedef struct { #endif #endif + diff --git a/lib/libpe/include/libpe/dir_security.h b/lib/libpe/include/libpe/dir_security.h index e6560920..33780dfa 100644 --- a/lib/libpe/include/libpe/dir_security.h +++ b/lib/libpe/include/libpe/dir_security.h @@ -30,53 +30,56 @@ extern "C" { #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 +// #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 + // 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 + 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]; + // 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; + uint32_t cbData; + uint8_t *pbData; } CRYPT_DATA_BLOB; #pragma pack(pop) @@ -86,3 +89,4 @@ typedef struct { #endif #endif + diff --git a/lib/libpe/include/libpe/directories.h b/lib/libpe/include/libpe/directories.h index 8346f102..b2242e7a 100644 --- a/lib/libpe/include/libpe/directories.h +++ b/lib/libpe/include/libpe/directories.h @@ -22,10 +22,10 @@ #ifndef LIBPE_DIRECTORIES_H #define LIBPE_DIRECTORIES_H -#include #include "dir_import.h" #include "dir_resources.h" #include "dir_security.h" +#include #ifdef __cplusplus extern "C" { @@ -33,96 +33,115 @@ extern "C" { // 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 + 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; + 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 + 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; + 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 + 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; + 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; + uint32_t VirtualAddress; + uint32_t Size; } IMAGE_DATA_DIRECTORY; #ifdef __cplusplus @@ -130,3 +149,4 @@ typedef struct { #endif #endif + diff --git a/lib/libpe/include/libpe/error.h b/lib/libpe/include/libpe/error.h index 34334934..a3512b73 100644 --- a/lib/libpe/include/libpe/error.h +++ b/lib/libpe/include/libpe/error.h @@ -30,35 +30,35 @@ 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); @@ -69,3 +69,4 @@ void pe_error_print(FILE *stream, pe_err_e error); #endif #endif + diff --git a/lib/libpe/include/libpe/exports.h b/lib/libpe/include/libpe/exports.h index f6617283..78198c26 100644 --- a/lib/libpe/include/libpe/exports.h +++ b/lib/libpe/include/libpe/exports.h @@ -22,26 +22,26 @@ #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 +51,4 @@ void pe_exports_dealloc(pe_exports_t *exports); #endif #endif + diff --git a/lib/libpe/include/libpe/hashes.h b/lib/libpe/include/libpe/hashes.h index 66b22f74..d437e7a2 100644 --- a/lib/libpe/include/libpe/hashes.h +++ b/lib/libpe/include/libpe/hashes.h @@ -22,37 +22,37 @@ #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 +64,4 @@ void pe_hash_dealloc(pe_hash_t *obj); #endif #endif + diff --git a/lib/libpe/include/libpe/hdr_coff.h b/lib/libpe/include/libpe/hdr_coff.h index 31e775be..bbe270d1 100644 --- a/lib/libpe/include/libpe/hdr_coff.h +++ b/lib/libpe/include/libpe/hdr_coff.h @@ -29,138 +29,140 @@ 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 + 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 + // 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 + 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) @@ -170,3 +172,4 @@ typedef struct { #endif #endif + diff --git a/lib/libpe/include/libpe/hdr_dos.h b/lib/libpe/include/libpe/hdr_dos.h index 2344e660..d1a40f38 100644 --- a/lib/libpe/include/libpe/hdr_dos.h +++ b/lib/libpe/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/lib/libpe/include/libpe/hdr_optional.h b/lib/libpe/include/libpe/hdr_optional.h index d8d27da9..6f667ae9 100644 --- a/lib/libpe/include/libpe/hdr_optional.h +++ b/lib/libpe/include/libpe/hdr_optional.h @@ -29,205 +29,215 @@ extern "C" { #endif -// REFERENCE: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx +// 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 + // 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 +// 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 + // 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 +// 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 + // 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+ + 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; + 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 +// 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]; + 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 +// 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]; + 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; + 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) @@ -237,3 +247,4 @@ typedef struct { #endif #endif + diff --git a/lib/libpe/include/libpe/imports.h b/lib/libpe/include/libpe/imports.h index 174871a2..97c53158 100644 --- a/lib/libpe/include/libpe/imports.h +++ b/lib/libpe/include/libpe/imports.h @@ -22,32 +22,32 @@ #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); @@ -73,3 +73,4 @@ void pe_imports_dealloc(pe_imports_t *imports); #endif #endif + diff --git a/lib/libpe/include/libpe/macros.h b/lib/libpe/include/libpe/macros.h index c76fe0b3..9ab469a7 100644 --- a/lib/libpe/include/libpe/macros.h +++ b/lib/libpe/include/libpe/macros.h @@ -26,17 +26,18 @@ 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) +#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/lib/libpe/include/libpe/pe.h b/lib/libpe/include/libpe/pe.h index 7b366a44..070765b8 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 index 42bf01c3..895b86af 100644 --- a/lib/libpe/include/libpe/resources.h +++ b/lib/libpe/include/libpe/resources.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. @@ -22,12 +22,12 @@ #ifndef LIBPE_RESOURCES_H #define LIBPE_RESOURCES_H -#include -#include #include "context.h" -#include "error.h" #include "dir_resources.h" +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -37,42 +37,55 @@ extern "C" { // typedef struct { - char *name; - ResourceType type; - char *extension; - char *dir_name; + 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); +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 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; + 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; + 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); +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); +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); #ifdef __cplusplus @@ -80,3 +93,4 @@ void pe_resources_dealloc(pe_resources_t *obj); #endif #endif + diff --git a/lib/libpe/include/libpe/sections.h b/lib/libpe/include/libpe/sections.h index 9e746b08..20eaba32 100644 --- a/lib/libpe/include/libpe/sections.h +++ b/lib/libpe/include/libpe/sections.h @@ -31,91 +31,167 @@ extern "C" { #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 +// - 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) + 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) + 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)". +// 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 + 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) @@ -125,3 +201,4 @@ typedef struct { #endif #endif + diff --git a/lib/libpe/include/libpe/types_resources.h b/lib/libpe/include/libpe/types_resources.h index 53934c76..7dd06b4a 100644 --- a/lib/libpe/include/libpe/types_resources.h +++ b/lib/libpe/include/libpe/types_resources.h @@ -22,44 +22,51 @@ #ifndef LIBPE_TYPES_RESOURCES_H #define LIBPE_TYPES_RESOURCES_H -#include #include "dir_resources.h" #include "error.h" +#include typedef enum { - LIBPE_RDT_LEVEL1 = 1, - LIBPE_RDT_LEVEL2 = 2, - LIBPE_RDT_LEVEL3 = 3 + 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 + 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. + 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_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/libfuzzy/edit_dist.c b/lib/libpe/libfuzzy/edit_dist.c index 30cc7046..1d507901 100644 --- a/lib/libpe/libfuzzy/edit_dist.c +++ b/lib/libpe/libfuzzy/edit_dist.c @@ -4,22 +4,19 @@ 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 + 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)). @@ -38,68 +35,78 @@ HISTORY #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) +#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 : y; + 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; +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) +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 */ + 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 */ + 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 */ + 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 @@ -108,89 +115,92 @@ edit_distn(const char *from, int from_len, const char *to, int to_len) #define ch 3 #define swap_cost 5 #else - ins = insert_cost; - del = delete_cost; - ch = change_cost; + 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 */ + /* 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); + swap_int(from_len, to_len); + swap_char(from, to); #ifndef TRN_SPEEDUP - swap_int(ins, del); + 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) + /* 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]); + for (col = 0; col < from_len; col++) { + printf(" %c ", from[col]); + } printf("\n "); - for (col = 0; col <= from_len; col++) - printf("%2d ", col * del); + 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. + /* 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. -*/ + 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 @@ -202,18 +212,17 @@ edit_distn(const char *from, int from_len, const char *to, int to_len) #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); + 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]; + if (buffer[index] < low) { + low = buffer[index]; + } #endif - index++; + index++; #ifdef DEBUG_EDITDIST - printf("%2d ", buffer[index - 1]); + printf("%2d ", buffer[index - 1]); #endif } /* for col = 1 */ @@ -222,45 +231,48 @@ edit_distn(const char *from, int from_len, const char *to, int to_len) printf("\n %c %2d ", to[1], 2 * ins); #endif -/* Now handle the rest of the matrix */ + /* 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); + 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]); + printf("%2d ", buffer[index]); #endif #ifdef TRN_SPEEDUP - if (buffer[index] < low || col == 0) - low = buffer[index]; + if (buffer[index] < low || col == 0) { + low = buffer[index]; + } #endif - index = mod(index + 1); - } /* for col = 1 */ + 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"); + 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; + if (low > MIN_DIST) { + break; + } #endif } /* for row = 1 */ row = buffer[mod(index + radix - 1)]; - if (buffer != store) - free((char *) buffer); + if (buffer != store) { + free((char *)buffer); + } return row; } /* edit_distn */ - diff --git a/lib/libpe/libfuzzy/fuzzy.c b/lib/libpe/libfuzzy/fuzzy.c index 122c4622..53cdd9c8 100644 --- a/lib/libpe/libfuzzy/fuzzy.c +++ b/lib/libpe/libfuzzy/fuzzy.c @@ -22,28 +22,28 @@ * http://ssdeep.sf.net/ */ +#include "fuzzy.h" #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) +#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)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX -#define MAX(a,b) ((a)>(b)?(a):(b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #define ROLLING_WINDOW 7 @@ -53,13 +53,14 @@ #define NUM_BLOCKHASHES 31 struct roll_state { - unsigned char window[ROLLING_WINDOW]; - uint32_t h1, h2, h3; - uint32_t n; + 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)); +static void roll_init(/*@out@*/ struct roll_state *self) +{ + memset(self, 0, sizeof(struct roll_state)); } /* @@ -74,31 +75,31 @@ static void roll_init(/*@out@*/ struct roll_state *self) { */ static void roll_hash(struct roll_state *self, unsigned char c) { - self->h2 -= self->h1; - self->h2 += ROLLING_WINDOW * (uint32_t)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->h1 += (uint32_t)c; + self->h1 -= (uint32_t)self->window[self->n % ROLLING_WINDOW]; - self->window[self->n % ROLLING_WINDOW] = c; - self->n++; + 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; + /* 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; + 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; + return (h * HASH_PRIME) ^ c; } /* A blockhash contains a signature state for a specific (implicit) blocksize. @@ -106,150 +107,158 @@ static uint32_t sum_hash(unsigned char c, uint32_t h) * 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 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; +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; + 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; + 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; + 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 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); - } + 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; +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) +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; + 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 @@ -258,157 +267,156 @@ extern const int EOVERFLOW; // We need some extra help on Win32 #ifdef _WIN32 -# define EOVERFLOW 84 -# define ftello ftell -# define fseeko fseek +#define EOVERFLOW 84 +#define ftello ftell +#define fseeko fseek #endif int fuzzy_digest(const struct fuzzy_state *self, - /*@out@*/ char *result, - unsigned int flags) + /*@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; + 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; - 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); + 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; + 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); -} +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) +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; + 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; + 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 @@ -419,30 +427,32 @@ off_t ftello(FILE *); 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; + 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; + 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; } // @@ -455,89 +465,84 @@ int fuzzy_hash_filename(const char *filename, /*@out@*/ char *result) // 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; -} + 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; + char *ret; + size_t i, j, len; - ret = strdup(str); - if (!ret) - return NULL; + ret = strdup(str); + if (!ret) { + return NULL; + } - len = strlen(str); - if (len < 3) - return ret; + 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; - } + 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; - } + // 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); + // 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 = (score * SPAMSUM_LENGTH) / (len1 + 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 + // 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; + // 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; + // 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; + // 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); + // 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; + // 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; } // @@ -611,108 +615,107 @@ static uint32_t score_strings(const char *s1, // 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) - { + 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); - 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; + free(s2); + + return (int)score; } + diff --git a/lib/libpe/libfuzzy/fuzzy.h b/lib/libpe/libfuzzy/fuzzy.h index bd71a0f0..60ce5185 100644 --- a/lib/libpe/libfuzzy/fuzzy.h +++ b/lib/libpe/libfuzzy/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/misc.c b/lib/libpe/misc.c index b854d750..aa70d21e 100644 --- a/lib/libpe/misc.c +++ b/lib/libpe/misc.c @@ -24,259 +24,289 @@ #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? + return !!memmem(ctx->map_addr, (size_t)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; } -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..a697f0ce 100644 --- a/lib/libpe/pe.c +++ b/lib/libpe/pe.c @@ -20,9 +20,14 @@ */ #include "libpe/pe.h" +#include "libpe/macros.h" +#include "libpe/resources.h" +#include #include #include +#include +#include #include #include #include @@ -30,870 +35,961 @@ #include #include #include -#include -#include -#include - -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; -} - -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 ctx->map_size; -} +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, (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; +} + +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, (size_t)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; +} + +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; +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; - - 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; -} - -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; +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; } + +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; } + diff --git a/lib/libpe/resources.c b/lib/libpe/resources.c index b849c476..ac2c0b14 100644 --- a/lib/libpe/resources.c +++ b/lib/libpe/resources.c @@ -1,633 +1,722 @@ /* - libpe - the PE library + libpe - the PE library - Copyright (C) 2010 - 2017 libpe authors + Copyright (C) 2010 - 2025 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/utils.h" #include "libpe/utlist.h" -#include -#include + +#include +#include + #include +#include +#include #include -#include -#include #include // 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 == 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; - - 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_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; + } } -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; - - // 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. - - 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; - - 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; + } + + // 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 *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; } -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; - - 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(); - } - } - - pe_utils_str_widechar2ascii(output, buffer_size, (const char *)data_string_ptr->String, (size_t)data_string_ptr->Length); - - return output; +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 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); + + 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 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 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; } -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(); - } - - 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; +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(); + } + + 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; } -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..ed754ac5 100644 --- a/lib/libpe/utils.c +++ b/lib/libpe/utils.c @@ -22,214 +22,230 @@ #include "libpe/utils.h" #include "libpe/error.h" -#include #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) +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 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; +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()); + // 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; + return pwd == NULL ? NULL : pwd->pw_dir; } + diff --git a/lib/libudis86/libudis86/CMakeLists.txt b/lib/libudis86/libudis86/CMakeLists.txt new file mode 100644 index 00000000..0ffae9db --- /dev/null +++ b/lib/libudis86/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/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/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/premake5.lua b/premake5.lua new file mode 100644 index 00000000..29fd5013 --- /dev/null +++ b/premake5.lua @@ -0,0 +1,119 @@ +workspace "readpe" + configurations { "Static", "Debug", "Release" } + +project "pe" + language "C" + location "lib/libpe" + includedirs { "lib/libpe/include" } + targetdir "build/%{cfg.buildcfg}" + files { "lib/libpe/**.h", "lib/libpe/**.c" } + defines { + "_GNU_SOURCE", + } + links { "crypto" } + + filter { "system:linux" } + links { "m" } + + filter "Static" + kind "StaticLib" + staticruntime "On" + flags { "OmitDefaultLibrary" } + syslibdirs { "/home/gogo/src/git.musl-libc.org/musl/lib" } + links { "c:static", "m:static", "crypto:static" } + + filter "Debug" + kind "StaticLib" + + filter "Release" + kind "SharedLib" + +project "udis86" + kind "StaticLib" + location "lib/libudis86/libudis86" + targetdir "build/%{cfg.buildcfg}" + files { "lib/libudis86/libudis86/*.h", "lib/libudis86/libudis86/*.c" } + defines { "HAVE_STRING_H=1" } + +-- TODO: ZIP Packing +project "readpe" + kind "ConsoleApp" + language "C" + cdialect "gnu11" + location "src" + includedirs { "include", "lib/libpe/include", "lib" } + targetdir "build/%{cfg.buildcfg}" + + files { "src/*.h", "src/*.c", "src/compat/*.c" } + -- removefiles { "src/ofs2rva.c", "src/pedis.c", "src/pehash.c", "src/pepack.c", "src/peres.c", "src/pescan.c", "src/pesec.c", "src/readpe.c", "src/rva2ofs.c" } + + defines { + "_GNU_SOURCE", + "SHAREDIR=\"\"", + "PLUGINSDIR=\"pev/plugins\"" + } + + + warnings "Extra" + + filter { "system:linux" } + links { "m" } + + filter "configurations:Static" + staticruntime "On" + flags { "OmitDefaultLibrary" } + syslibdirs { "/home/gogo/src/git.musl-libc.org/musl/lib" } + links { "pe", "udis86", "c:static", "m:static", "crypto:static", "rt:static", "util:static", "dl:static", "resolv:static", "pthread:static" } + defines { + "CFLAGS=\"-no-pie -static -static-libgcc\"", + "LDFLAGS=\"-Wl,-Bstatic -static-libgcc\"" + } + -- -no-pie -static + + filter "configurations:Debug" + links { "pe", "udis86", "crypto" } + flags { "LinkTimeOptimization" } + defines { "DEBUG" } + enablewarnings { + "pedantic", + "shadow", + "undef", + "double-promotion", + "format=2", + "format-security", + "conversion" + } + symbols "On" + + filter "configurations:Release" + links { "pe", "udis86", "crypto" } + flags { "LinkTimeOptimization" } + defines { "NDEBUG" } + optimize "Full" + +-- Function to create single C file plugin +function create_simple_plugin(name) + project("plugin_" .. name) + kind "SharedLib" + language "C" + location "lib/libpe" + includedirs { "include" } + targetdir "build/%{cfg.buildcfg}/plugins" + files { "src/plugins/" .. name .. ".c" } + + filter "Static" + kind "StaticLib" + + filter "Debug" + kind "SharedLib" + + filter "Release" + kind "SharedLib" +end + +create_simple_plugin "csv" +create_simple_plugin "html" +create_simple_plugin "json" +create_simple_plugin "text" +create_simple_plugin "xml" + 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..e0688fe1 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,140 @@ +project(readpe LANGUAGES C) + +set(HEADERS + ../include/compat/strlcat.h + ../include/compat/sys/queue.h + + ../include/common.h + ../include/config.h + ../include/output.h + ../include/output_plugin.h + ../include/plugin.h + ../include/plugins.h + ../include/readpe.h + ../include/stack.h + + dylib.h +) + +set(SOURCES + compat/strlcat.c + + main.c + + config.c + directories.c + dylib.c + exports.c + hash.c + header.c + imports.c + malloc_s.c + output.c + output_plugin.c + output_text.c + plugin.c + plugins.c + resources.c + section.c + security.c + certificates.c + scan.c +) + +if(BUILD_LEGACY) + if(BUILD_DISASSEMBLER) + list(APPEND SOURCES legacy/pedis.c ) + endif() + + list(APPEND HEADERS + 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(BUILD_STATIC) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + set(BUILD_SHARED_LIBS OFF) + set(CMAKE_EXE_LINKER_FLAGS "-no-pie -static") + + # set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") + # target_compile_options(your_target_name [PUBLIC|PRIVATE] /MT) + # target_link_options(your_target_name [PUBLIC|PRIVATE] /INCREMENTAL:NO /NODEFAULTLIB:MSVCRT) + # set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + # https://stackoverflow.com/questions/24648357/compiling-a-static-executable-with-cmake + # set(CMAKE_MFC_FLAG 1) + +endif() + +if(WIN32) + append( SOURCES + compat/asprintf.c + windows/cpload.c + ) +endif() + +add_compile_definitions( + SHAREDIR="" + PLUGINSDIR="src/plugins" +) + +find_package(OpenSSL) + +add_executable(readpe ${HEADERS} ${SOURCES}) +target_include_directories(readpe PRIVATE + "../include" + "../lib" + "../lib/libpe/include" +) + +target_link_libraries(readpe PRIVATE pe OpenSSL::Crypto m) + + +if(BUILD_LEGACY) + if(BUILD_DISASSEMBLER) + target_link_libraries(readpe PRIVATE udis86) + target_compile_definitions(readpe PRIVATE + READPE_DISASSEMBLER=1 + ) + endif() + + target_compile_definitions(readpe PRIVATE + READPE_LEGACY=1 + ) +endif() + +if(MSVC) + target_compile_options(readpe PRIVATE /W4 /WX) +else() + target_compile_options(readpe PRIVATE + -Wall + -Wextra + -Wpedantic + -Wshadow + -Wundef + -Wdouble-promotion + -Wformat=2 + -Wformat-security + -Wconversion + -ggdb3 + ) +endif() + +install(TARGETS readpe + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include +) + 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..8c92902b --- /dev/null +++ b/src/certificates.c @@ -0,0 +1,447 @@ +/* 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 "common.h" +#include "compat/strlcat.h" +#include "output.h" +#include "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"); + + // 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); + + int issuer_name_len + = X509_NAME_get_text_by_NID(name, NID_commonName, NULL, 0); + if (issuer_name_len > 0) { + output_open_scope("signer", OUTPUT_SCOPE_TYPE_OBJECT); + char issuer_name[issuer_name_len + 1]; + X509_NAME_get_text_by_NID(name, NID_commonName, issuer_name, + issuer_name_len + 1); + 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/compat/asprintf.c b/src/compat/asprintf.c new file mode 100644 index 00000000..3d7010ce --- /dev/null +++ b/src/compat/asprintf.c @@ -0,0 +1,155 @@ +/* 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/asprintf.h" + +#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) + +#include +#include +#include + +#if _MSC_VER < 1800 +#undef va_copy +#define va_copy(dst, src) (dst = src) +#endif + +#ifdef __cplusplus +extern "C" +#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; +} + +#ifdef __cplusplus +extern "C" +#endif + 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 +int asprintf(char **pp, char *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(*pp = p, fmt, args); + + va_end(args_safe); + va_end(args); + + return size; +} +#endif + diff --git a/src/compat/strlcat.c b/src/compat/strlcat.c index 1cdacd39..d7ed7974 100644 --- a/src/compat/strlcat.c +++ b/src/compat/strlcat.c @@ -26,8 +26,7 @@ * 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; const char *s = src; @@ -35,13 +34,15 @@ bsd_strlcat(char *dst, const char *src, size_t 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; + } + 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 +52,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/src/config.c b/src/config.c index 765ef0ab..44d00275 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,48 @@ files in the program, then also delete it here. */ +// #ifdef __STDC_ALLOC_LIB__ +// #define __STDC_WANT_LIB_EXT2__ 1 +// #else +// #define _POSIX_C_SOURCE 200809L +// #endif + #include "config.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__) +#include // FIXME: Why? +#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) \ + || defined(__NetBSD__) || defined(__CYGWIN__) #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 +83,108 @@ 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 (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, ... ) -{ - 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( *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) { +int readpe_load_config(struct readpe_config *const config) +{ 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) ) + 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; + } + } // 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 ) + if (asprintf(&buff, "%s/" DEFAULT_CONFIG_PATH, pe_utils_get_homedir()) + < 0) { return -1; + } 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..833a908b --- /dev/null +++ b/src/directories.c @@ -0,0 +1,153 @@ +/* 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 "common.h" +#include "output.h" +#include "readpe.h" + +#include +#include +#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) +{ + + output_open_scope("Data directories", 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) { + 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 + } else { + output(NULL, pe_directory_name(i)); + } + } + } + + output_close_scope(); // Data directories +} + diff --git a/src/exports.c b/src/exports.c new file mode 100644 index 00000000..6f916067 --- /dev/null +++ b/src/exports.c @@ -0,0 +1,91 @@ +/* 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 "output.h" +#include "readpe.h" + +#include +#include +#include +#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..66cbbee7 --- /dev/null +++ b/src/hash.c @@ -0,0 +1,273 @@ +/* vim :set ts=4 sw=4 sts=4 et : */ +/* + 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 "common.h" +#include "modes.h" +#include "output.h" +#include "readpe.h" + +#include +#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", "ssdeep"}; + 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_settings *settings) +{ + switch (settings->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 (settings->section_name != NULL) { + print_section_hash_by_name(ctx, settings->section_name); + } else if (settings->section_index > 0) { + print_section_hash_by_index(ctx, settings->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..e822d8bf --- /dev/null +++ b/src/header.c @@ -0,0 +1,727 @@ +/* + 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 "common.h" +#include "readpe.h" + +#include +#include +#include +#include +#include + +void print_optional_header(pe_ctx_t *ctx) +{ + IMAGE_OPTIONAL_HEADER *header = pe_optional(ctx); + if (! header) { + LIBPE_WARNING("unable to read Optional (Image) file header"); + } + +#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + typedef struct { + WindowsSubsystem subsystem; + const char *const name; + } WindowsSubsystemName; + static const WindowsSubsystemName subsystemNames[] = { + {IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, + {IMAGE_SUBSYSTEM_NATIVE, "System native" }, + {IMAGE_SUBSYSTEM_WINDOWS_GUI, "Windows GUI" }, + {IMAGE_SUBSYSTEM_WINDOWS_CUI, "Windows CLI" }, + {IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, + {IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, + {IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, + {IMAGE_SUBSYSTEM_POSIX_CUI, "Posix CLI" }, + {IMAGE_SUBSYSTEM_WINDOWS_CE_GUI, "Windows CE GUI" }, + {IMAGE_SUBSYSTEM_EFI_APPLICATION, "EFI application" }, + {IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER, "EFI driver with boot"}, + {IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER, "EFI run-time driver" }, + {IMAGE_SUBSYSTEM_EFI_ROM, "EFI ROM" }, + {IMAGE_SUBSYSTEM_XBOX, "XBOX" }, + {IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION, "Boot application" } + }; + static const size_t max_subsystem = LIBPE_SIZEOF_ARRAY(subsystemNames); +#endif + + if (! header) { + return; + } + + static char s[MAX_MSG]; + + output_open_scope("Optional/Image header", OUTPUT_SCOPE_TYPE_OBJECT); + + switch (header->type) { + case MAGIC_ROM: { + snprintf(s, MAX_MSG, "%#x (%s)", header->_rom->Magic, "ROM"); + output("Magic number", s); + + snprintf(s, MAX_MSG, "%" PRIu8, header->_rom->MajorLinkerVersion); + output("Linker major version", s); + + snprintf(s, MAX_MSG, "%" PRIu8, header->_rom->MinorLinkerVersion); + output("Linker minor version", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfCode); + output("Size of .text section", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfInitializedData); + output("Size of .data section", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfUninitializedData); + output("Size of .bss section", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->AddressOfEntryPoint); + output("Entrypoint", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfCode); + output("Address of .text section", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfData); + output("Address of .data section", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfBss); + output("Address of .bss section", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->GprMask); + output("GprMask", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[0]); + output("CprMask[0]", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[1]); + output("CprMask[1]", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[2]); + output("CprMask[2]", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[3]); + output("CprMask[3]", s); + + snprintf(s, MAX_MSG, "%#x", header->_rom->GpValue); + output("GpValue", s); + break; + } + case MAGIC_PE32_0: + case MAGIC_PE32: { + snprintf(s, MAX_MSG, "%#x (%s)", header->_32->Magic, + header->type == MAGIC_PE32_0 ? "PE32 ZERO" : "PE32"); + output("Magic number", s); + + snprintf(s, MAX_MSG, "%" PRIu8, header->_32->MajorLinkerVersion); + output("Linker major version", s); + + snprintf(s, MAX_MSG, "%" PRIu8, header->_32->MinorLinkerVersion); + output("Linker minor version", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfCode); + output("Size of .text section", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfInitializedData); + output("Size of .data section", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfUninitializedData); + output("Size of .bss section", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->AddressOfEntryPoint); + output("Entrypoint", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->BaseOfCode); + output("Address of .text section", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->BaseOfData); + output("Address of .data section", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->ImageBase); + output("ImageBase", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->SectionAlignment); + output("Alignment of sections", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->FileAlignment); + output("Alignment factor", s); + + snprintf(s, MAX_MSG, "%" PRIu16, + header->_32->MajorOperatingSystemVersion); + output("Major version of required OS", s); + + snprintf(s, MAX_MSG, "%" PRIu16, + header->_32->MinorOperatingSystemVersion); + output("Minor version of required OS", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MajorImageVersion); + output("Major version of image", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MinorImageVersion); + output("Minor version of image", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MajorSubsystemVersion); + output("Major version of subsystem", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MinorSubsystemVersion); + output("Minor version of subsystem", s); + +#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + snprintf(s, MAX_MSG, "Win32 version value: %#x", + header->_32->Win32VersionValue); + output_open_scope(s, OUTPUT_SCOPE_TYPE_OBJECT); + + if (header->_32->Win32VersionValue == 0) { + strcpy(s, "(default)"); + } else { + snprintf(s, MAX_MSG, "%u", header->_32->Win32VersionValue & 0xff); + } + output("Overwrite OS major version", s); + + if (header->_32->Win32VersionValue == 0) { + strcpy(s, "(default)"); + } else { + snprintf(s, MAX_MSG, "%u", + (header->_32->Win32VersionValue >> 8) & 0xff); + } + output("Overwrite OS minor version", s); + + if (header->_32->Win32VersionValue == 0) { + strcpy(s, "(default)"); + } else { + snprintf(s, MAX_MSG, "%u", + (header->_32->Win32VersionValue >> 16) & 0x3fff); + } + output("Overwrite OS build number", s); + + if (header->_32->Win32VersionValue == 0) { + strcpy(s, "(default)"); + } else { + uint8_t platform_id + = (uint8_t) (header->_32->Win32VersionValue >> 30); + static const char *const win32_version_value_platform_id[4] + = {"NT", "CE", "Win32s", "Win9x"}; + snprintf(s, MAX_MSG, "%u (%s)", platform_id, + win32_version_value_platform_id[platform_id]); + } + output("Overwrite OS platform id", s); + + output_close_scope(); +#endif + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfImage); + output("Size of image", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeaders); + output("Size of headers", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->CheckSum); + output("Checksum", s); + + const uint16_t subsystem = header->_32->Subsystem; +#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + const char *subsystem_name = "Unknown"; + for (size_t i = 0; i < max_subsystem; i++) { + if (subsystem == subsystemNames[i].subsystem) { + subsystem_name = subsystemNames[i].name; + } + } +#else + const char *subsystem_name = pe_windows_subsystem_name(subsystem); + if (subsystem_name == NULL) { + subsystem_name = "Unknown"; + } +#endif + snprintf(s, MAX_MSG, "%#x (%s)", subsystem, subsystem_name); + output("Subsystem required", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->DllCharacteristics); + output("DLL characteristics", s); + +#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + output_open_scope("DLL characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); + + for (uint16_t i = 0, flag = 0x0001; i < 16; i++, flag <<= 1) { + if (header->_32->DllCharacteristics & flag) { + const char *characteristic_name + = pe_image_dllcharacteristic_name(flag); + char formatted_characteristic_name[32]; + if (characteristic_name == NULL) { + snprintf(formatted_characteristic_name, + sizeof(formatted_characteristic_name) - 1, + "UNKNOWN[%#x]", flag); + characteristic_name = formatted_characteristic_name; + } + output(NULL, characteristic_name); + } + } + + output_close_scope(); // DLL characteristics names +#endif + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfStackReserve); + output("Size of stack to reserve", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfStackCommit); + output("Size of stack to commit", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeapReserve); + output("Size of heap space to reserve", s); + + snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeapCommit); + output("Size of heap space to commit", s); + +#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + snprintf(s, MAX_MSG, "%#x", header->_32->LoaderFlags); + output("Loader Flags", s); + + output_open_scope("Loader Flags names", OUTPUT_SCOPE_TYPE_ARRAY); + + for (uint32_t i = 0, flag = 0x00000001; i < 32; i++, flag <<= 1) { + if (header->_32->LoaderFlags & flag) { + const char *flag_name = NULL; + char formatted_flag_name[32]; + if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) { + flag_name = pe_dll_image_loader_flags_name(flag); + } + if (flag_name == NULL) { + flag_name = pe_image_loader_flags_name(flag); + } + if (flag_name == NULL) { + snprintf(formatted_flag_name, + sizeof(formatted_flag_name) - 1, "UNKNOWN[%#x]", + flag); + flag_name = formatted_flag_name; + } + output(NULL, flag_name); + } + } + + output_close_scope(); // Loader Flags names +#endif + + break; + } + case MAGIC_PE64: { + snprintf(s, MAX_MSG, "%#x (%s)", header->_64->Magic, "PE32+"); + output("Magic number", s); + + snprintf(s, MAX_MSG, "%" PRIu8, header->_64->MajorLinkerVersion); + output("Linker major version", s); + + snprintf(s, MAX_MSG, "%" PRIu8, header->_64->MinorLinkerVersion); + output("Linker minor version", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfCode); + output("Size of .text section", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfInitializedData); + output("Size of .data section", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfUninitializedData); + output("Size of .bss section", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->AddressOfEntryPoint); + output("Entrypoint", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->BaseOfCode); + output("Address of .text section", s); + + snprintf(s, MAX_MSG, "%#" PRIx64, header->_64->ImageBase); + output("ImageBase", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->SectionAlignment); + output("Alignment of sections", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->FileAlignment); + output("Alignment factor", s); + + snprintf(s, MAX_MSG, "%" PRIu16, + header->_64->MajorOperatingSystemVersion); + output("Major version of required OS", s); + + snprintf(s, MAX_MSG, "%" PRIu16, + header->_64->MinorOperatingSystemVersion); + output("Minor version of required OS", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MajorImageVersion); + output("Major version of image", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MinorImageVersion); + output("Minor version of image", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MajorSubsystemVersion); + output("Major version of subsystem", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MinorSubsystemVersion); + output("Minor version of subsystem", s); + +#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + snprintf(s, MAX_MSG, "Win32 version value: %#x", + header->_64->Win32VersionValue); + output_open_scope(s, OUTPUT_SCOPE_TYPE_OBJECT); + + if (header->_64->Win32VersionValue == 0) { + strcpy(s, "(default)"); + } else { + snprintf(s, MAX_MSG, "%u", header->_64->Win32VersionValue & 0xff); + } + output("Overwrite OS major version", s); + + if (header->_64->Win32VersionValue == 0) { + strcpy(s, "(default)"); + } else { + snprintf(s, MAX_MSG, "%u", + (header->_64->Win32VersionValue >> 8) & 0xff); + } + output("Overwrite OS minor version", s); + + if (header->_64->Win32VersionValue == 0) { + strcpy(s, "(default)"); + } else { + snprintf(s, MAX_MSG, "%u", + (header->_64->Win32VersionValue >> 16) & 0x3fff); + } + output("Overwrite OS build number", s); + + if (header->_64->Win32VersionValue == 0) { + strcpy(s, "(default)"); + } else { + uint8_t platform_id + = (uint8_t) (header->_64->Win32VersionValue >> 30); + static const char *const win32_version_value_platform_id[4] + = {"NT", "CE", "Win32s", "Win9x"}; + snprintf(s, MAX_MSG, "%u (%s)", platform_id, + win32_version_value_platform_id[platform_id]); + } + output("Overwrite OS platform id", s); + + output_close_scope(); +#endif + + snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfImage); + output("Size of image", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfHeaders); + output("Size of headers", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->CheckSum); + output("Checksum", s); + + const uint16_t subsystem = header->_64->Subsystem; +#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + const char *subsystem_name = "Unknown"; + for (size_t i = 0; i < max_subsystem; i++) { + if (subsystem == subsystemNames[i].subsystem) { + subsystem_name = subsystemNames[i].name; + } + } +#else + const char *subsystem_name = pe_windows_subsystem_name(subsystem); + if (subsystem_name == NULL) { + subsystem_name = "Unknown"; + } +#endif + snprintf(s, MAX_MSG, "%#x (%s)", subsystem, subsystem_name); + output("Subsystem required", s); + + snprintf(s, MAX_MSG, "%#x", header->_64->DllCharacteristics); + output("DLL characteristics", s); + +#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + output_open_scope("DLL characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); + + for (uint16_t i = 0, flag = 0x0001; i < 16; i++, flag <<= 1) { + if (header->_64->DllCharacteristics & flag) { + const char *characteristic_name = NULL; + char formatted_characteristic_name[32]; + if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) { + characteristic_name + = pe_dll_image_dllcharacteristic_name(flag); + } + if (characteristic_name == NULL) { + characteristic_name = pe_image_dllcharacteristic_name(flag); + } + if (characteristic_name == NULL) { + snprintf(formatted_characteristic_name, + sizeof(formatted_characteristic_name) - 1, + "UNKNOWN[%#x]", flag); + characteristic_name = formatted_characteristic_name; + } + output(NULL, characteristic_name); + } + } + + output_close_scope(); // DLL characteristics names +#endif + + snprintf(s, MAX_MSG, "%#" PRIx64, header->_64->SizeOfStackReserve); + output("Size of stack to reserve", s); + + snprintf(s, MAX_MSG, "%#" PRIx64, header->_64->SizeOfStackCommit); + output("Size of stack to commit", s); + + snprintf(s, MAX_MSG, "%#" PRIx64, header->_64->SizeOfHeapReserve); + output("Size of heap space to reserve", s); + + snprintf(s, MAX_MSG, "%#" PRIx64, header->_64->SizeOfHeapCommit); + output("Size of heap space to commit", s); + +#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + snprintf(s, MAX_MSG, "%#x", header->_64->LoaderFlags); + output("Loader Flags", s); + + output_open_scope("Loader Flags names", OUTPUT_SCOPE_TYPE_ARRAY); + + for (uint32_t i = 0, flag = 0x00000001; i < 32; i++, flag <<= 1) { + if (header->_64->LoaderFlags & flag) { + const char *flag_name = NULL; + char formatted_flag_name[32]; + if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) { + flag_name = pe_dll_image_loader_flags_name(flag); + } + if (flag_name == NULL) { + flag_name = pe_image_loader_flags_name(flag); + } + if (flag_name == NULL) { + snprintf(formatted_flag_name, + sizeof(formatted_flag_name) - 1, "UNKNOWN[%#x]", + flag); + flag_name = formatted_flag_name; + } + output(NULL, flag_name); + } + } + + output_close_scope(); // Loader Flags names +#endif + + break; + } + } + + output_close_scope(); // Optional/Image heade +} + +void print_coff_header(pe_ctx_t *ctx) +{ + const IMAGE_COFF_HEADER *header = pe_coff(ctx); + if (! header) { + LIBPE_WARNING("unable to read COFF file header"); + } + +#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + typedef struct { + ImageCharacteristics characteristic; + const char *const name; + } ImageCharacteristicsName; + static const ImageCharacteristicsName characteristicsTable[] = { + {IMAGE_FILE_RELOCS_STRIPPED, "base relocations stripped" }, + {IMAGE_FILE_EXECUTABLE_IMAGE, "executable image" }, + {IMAGE_FILE_LINE_NUMS_STRIPPED, "line numbers removed (deprecated)" }, + {IMAGE_FILE_LOCAL_SYMS_STRIPPED, "local symbols removed (deprecated)" }, + {IMAGE_FILE_AGGRESSIVE_WS_TRIM, + "aggressively trim (deprecated for Windows 2000 and later)" }, + {IMAGE_FILE_LARGE_ADDRESS_AWARE, "can handle more than 2 GB addresses" }, + {IMAGE_FILE_16BIT_MACHINE, "" }, + {IMAGE_FILE_BYTES_REVERSED_LO, "little-endian (deprecated)" }, + {IMAGE_FILE_32BIT_MACHINE, "32-bit machine" }, + {IMAGE_FILE_DEBUG_STRIPPED, "debugging information removed" }, + {IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, + "copy to swap if it's on removable media" }, + {IMAGE_FILE_NET_RUN_FROM_SWAP, "copy to swap if it's on network media"}, + {IMAGE_FILE_SYSTEM, "system file" }, + {IMAGE_FILE_DLL, "DLL image" }, + {IMAGE_FILE_UP_SYSTEM_ONLY, "uniprocessor machine" }, + {IMAGE_FILE_BYTES_REVERSED_HI, "big-endian (deprecated)" } + }; + + typedef struct { + MachineType type; + const char *const name; + } MachineTypeName; + static const MachineTypeName machineTypeTable[] = { + {IMAGE_FILE_MACHINE_UNKNOWN, "Any machine type" }, + {IMAGE_FILE_MACHINE_AM33, "Matsushita AM33" }, + {IMAGE_FILE_MACHINE_AMD64, "x86-64 (64-bits)" }, + {IMAGE_FILE_MACHINE_ARM, "ARM little endian" }, + {IMAGE_FILE_MACHINE_ARMV7, "ARMv7 (or higher) Thumb mode only" }, + {IMAGE_FILE_MACHINE_CEE, "clr pure MSIL (object only)" }, + {IMAGE_FILE_MACHINE_EBC, "EFI byte code" }, + {IMAGE_FILE_MACHINE_I386, "Intel 386 and compatible (32-bits)" }, + {IMAGE_FILE_MACHINE_IA64, "Intel Itanium" }, + {IMAGE_FILE_MACHINE_M32R, "Mitsubishi M32R little endian" }, + {IMAGE_FILE_MACHINE_MIPS16, "MIPS16" }, + {IMAGE_FILE_MACHINE_MIPSFPU, "MIPS with FPU" }, + {IMAGE_FILE_MACHINE_MIPSFPU16, "MIPS16 with FPU" }, + {IMAGE_FILE_MACHINE_POWERPC, "Power PC little endian" }, + {IMAGE_FILE_MACHINE_POWERPCFP, "Power PC with floating point support"}, + {IMAGE_FILE_MACHINE_R4000, "MIPS little endian" }, + {IMAGE_FILE_MACHINE_SH3, "Hitachi SH3" }, + {IMAGE_FILE_MACHINE_SH3DSP, "Hitachi SH3 DSP" }, + {IMAGE_FILE_MACHINE_SH4, "Hitachi SH4" }, + {IMAGE_FILE_MACHINE_SH5, "Hitachi SH5" }, + {IMAGE_FILE_MACHINE_THUMB, "ARM or Thumb (\"interworking\")" }, + {IMAGE_FILE_MACHINE_WCEMIPSV2, "MIPS little-endian WCE v2" } + }; + static const size_t max_machine_type = LIBPE_SIZEOF_ARRAY(machineTypeTable); +#endif + + output_open_scope("COFF/File header", OUTPUT_SCOPE_TYPE_OBJECT); + +#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + const char *machine = "Unknown machine type"; + for (size_t i = 0; i < max_machine_type; i++) { + if (header->Machine == machineTypeTable[i].type) { + machine = machineTypeTable[i].name; + } + } +#else + const char *machine = pe_machine_type_name(header->Machine); + if (machine == NULL) { + machine = "Unknown machine type"; + } +#endif + + static char s[MAX_MSG]; + + snprintf(s, MAX_MSG, "%#x %s", header->Machine, machine); + output("Machine", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->NumberOfSections); + output("Number of sections", s); + + char timestr[40] = "invalid"; + const time_t timestamp = header->TimeDateStamp; + struct tm *t = gmtime(×tamp); + if (t) { + strftime(timestr, sizeof(timestr), "%a, %d %b %Y %H:%M:%S UTC", t); + } + snprintf(s, MAX_MSG, "%" PRIu32 " (%s)", header->TimeDateStamp, timestr); + output("Date/time stamp", s); + + snprintf(s, MAX_MSG, "%#x", header->PointerToSymbolTable); + output("Symbol Table offset", s); + + snprintf(s, MAX_MSG, "%" PRIu32, header->NumberOfSymbols); + output("Number of symbols", s); + + snprintf(s, MAX_MSG, "%#x", header->SizeOfOptionalHeader); + output("Size of optional header", s); + + snprintf(s, MAX_MSG, "%#x", header->Characteristics); + output("Characteristics", s); + + output_open_scope("Characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); + + for (uint16_t i = 0, flag = 0x0001; i < 16; i++, flag <<= 1) { + if (header->Characteristics & flag) { +#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + output(NULL, characteristicsTable[i].name); +#else + const char *characteristic_name + = pe_image_characteristic_name(flag); + char formatted_characteristic_name[32]; + if (characteristic_name == NULL) { + snprintf(formatted_characteristic_name, + sizeof(formatted_characteristic_name) - 1, + "UNKNOWN[%#x]", flag); + characteristic_name = formatted_characteristic_name; + } + output(NULL, characteristic_name); +#endif + } + } + + output_close_scope(); // Characteristics names + + output_close_scope(); // COFF/File header +} + +void print_dos_header(pe_ctx_t *ctx) +{ + + IMAGE_DOS_HEADER *header = pe_dos(ctx); + if (! header) { + LIBPE_WARNING("unable to read DOS header"); + } + + char s[MAX_MSG]; + + output_open_scope("DOS Header", OUTPUT_SCOPE_TYPE_OBJECT); + + snprintf(s, MAX_MSG, "%#x (MZ)", header->e_magic); + output("Magic number", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->e_cblp); + output("Bytes in last page", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->e_cp); + output("Pages in file", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->e_crlc); + output("Relocations", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->e_cparhdr); + output("Size of header in paragraphs", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->e_minalloc); + output("Minimum extra paragraphs", s); + + snprintf(s, MAX_MSG, "%" PRIu16, header->e_maxalloc); + output("Maximum extra paragraphs", s); + + snprintf(s, MAX_MSG, "%#x", header->e_ss); + output("Initial (relative) SS value", s); + + snprintf(s, MAX_MSG, "%#x", header->e_sp); + output("Initial SP value", s); + + snprintf(s, MAX_MSG, "%#x", header->e_ip); + output("Initial IP value", s); + + snprintf(s, MAX_MSG, "%#x", header->e_cs); + output("Initial (relative) CS value", s); + + snprintf(s, MAX_MSG, "%#x", header->e_lfarlc); + output("Address of relocation table", s); + + snprintf(s, MAX_MSG, "%#x", header->e_ovno); + output("Overlay number", s); + + snprintf(s, MAX_MSG, "%#x", header->e_oemid); + output("OEM identifier", s); + + snprintf(s, MAX_MSG, "%#x", header->e_oeminfo); + output("OEM information", s); + + snprintf(s, MAX_MSG, "%#x", header->e_lfanew); + output("PE header offset", s); + + output_close_scope(); // DOS Header +} + diff --git a/src/helper.c b/src/helper.c new file mode 100644 index 00000000..ece8fc29 --- /dev/null +++ b/src/helper.c @@ -0,0 +1,58 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + Copyright (C) 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 "common.h" + +#include +#include + +bool str2bool(const char *const str) +{ + if (str == NULL) { + EXIT_ERROR("Internal error (str2bool)"); + } else if (*str == '0') { + return false; + } else if (*str == '1') { + return true; + } else if (strcmp(str, "false") == 0) { + return false; + } else if (strcmp(str, "true") == 0) { + return true; + } else { + EXIT_ERROR("Bad bool variable"); + } + + return false; +} + diff --git a/src/imports.c b/src/imports.c new file mode 100644 index 00000000..7a472a62 --- /dev/null +++ b/src/imports.c @@ -0,0 +1,91 @@ +/* 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 "output.h" +#include "readpe.h" + +#include +#include +#include +#include + +void print_imports(pe_ctx_t *ctx) +{ + output_open_scope("Imported functions", OUTPUT_SCOPE_TYPE_ARRAY); + + const pe_imports_t *imports = pe_imports(ctx); + for (size_t i = 0; i < imports->dll_count; i++) { + const pe_imported_dll_t *dll = &imports->dlls[i]; + output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT); + output("Name", dll->name); + output_open_scope("Functions", OUTPUT_SCOPE_TYPE_ARRAY); + + for (size_t j = 0; j < dll->functions_count; j++) { + const pe_imported_function_t *func = &dll->functions[j]; + output_open_scope("Function", OUTPUT_SCOPE_TYPE_OBJECT); + { + if (func->ordinal) { + char ordinal_str[16]; + snprintf(ordinal_str, sizeof(ordinal_str) - 1, "%" PRIu16, + func->ordinal); + output("Ordinal", ordinal_str); + } else { + char hint_str[16]; + snprintf(hint_str, sizeof(hint_str) - 1, "%" PRIu16, + func->hint); + output("Hint", hint_str); + output("Name", func->name); + } + } + output_close_scope(); // Function + } + + output_close_scope(); // Functions + output_close_scope(); // Library + } + + output_close_scope(); // Imported functions +} + +void print_dependencies(pe_ctx_t *ctx) +{ + output_open_scope("Dependencies", OUTPUT_SCOPE_TYPE_ARRAY); + const pe_imports_t *imports = pe_imports(ctx); + for (size_t i = 0; i < imports->dll_count; i++) { + const pe_imported_dll_t *dll = &imports->dlls[i]; + output(dll->name, NULL); + } + output_close_scope(); +} + diff --git a/src/legacy.h b/src/legacy.h new file mode 100644 index 00000000..81645f2b --- /dev/null +++ b/src/legacy.h @@ -0,0 +1,67 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + Copyright (C) 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_LEGACY_H +#define READPE_LEGACY_H + +#include + +#define TOOLKIT \ + "from readpe " 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." + +#ifdef READPE_DISASSEMBLER +int pedis(int argc, char *argv[]); +#endif + +int pehash(int argc, char *argv[]); +int peldd(int argc, char *argv[]); +int pepack(int argc, char *argv[]); +int peres(int argc, char *argv[]); +int pescan(int argc, char *argv[]); +int pesec(int argc, char *argv[]); +int pestr(int argc, char *argv[]); +int readpe(int argc, char *argv[]); + +int ofs2rva(int argc, char *argv[]); +int rva2ofs(int argc, char *argv[]); + +#endif + diff --git a/src/ofs2rva.c b/src/legacy/ofs2rva.c similarity index 66% rename from src/ofs2rva.c rename to src/legacy/ofs2rva.c index 588a7d5b..0cb23868 100644 --- a/src/ofs2rva.c +++ b/src/legacy/ofs2rva.c @@ -34,60 +34,58 @@ files in the program, then also delete it here. */ -// FIX: Needed if strtoull() is used and to test overflow. -#include +#include "../legacy.h" #include "common.h" +#include + #define PROGRAM "ofs2rva" static void usage(void) { printf("Usage: %s FILE\n" - "Convert raw file offset to RVA\n" - "\nExample: %s 0x1b9b8 calc.exe\n" - "\nOptions:\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM); + "Convert raw file offset to RVA\n" + "\nExample: %s 0x1b9b8 calc.exe\n" + "\nOptions:\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM); } static void parse_options(int argc, char *argv[]) { /* Parameters for getopt_long() function */ - static const char short_options[] = "V"; + static const char short_options[] = "V"; - static const struct option long_options[] = { - { "help", no_argument, NULL, 1 }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } + static const struct option long_options[] = { + {"help", no_argument, NULL, 1 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } }; int c, ind; - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { break; + } - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); } } } -int main(int argc, char *argv[]) +int ofs2rva(int argc, char *argv[]) { - //PEV_INITIALIZE(); - if (argc != 3) { usage(); return EXIT_FAILURE; @@ -105,11 +103,11 @@ int main(int argc, char *argv[]) uint64_t ofs; - // FIX: changed to strtoull(). errno = 0; - ofs = strtoull(argv[1], NULL, 0); - if ( !ofs || errno == ERANGE ) + ofs = strtoull(argv[1], NULL, 0); + if (! ofs || errno == ERANGE) { EXIT_ERROR("invalid offset"); + } err = pe_parse(&ctx); if (err != LIBPE_E_OK) { @@ -117,15 +115,19 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (!pe_is_pe(&ctx)) + if (! pe_is_pe(&ctx)) { EXIT_ERROR("not a valid PE file"); + } - printf("%#"PRIx64"\n", pe_ofs2rva(&ctx, ofs)); + printf("%#" PRIx64 "\n", pe_ofs2rva(&ctx, ofs)); // libera a memoria pe_unload(&ctx); - //PEV_FINALIZE(); - return EXIT_SUCCESS; } + +#ifdef STANDALONE +int main(int argc, char **argv) { return ofs2rva(argc, argv); } +#endif + diff --git a/src/legacy/pedis.c b/src/legacy/pedis.c new file mode 100644 index 00000000..1f004321 --- /dev/null +++ b/src/legacy/pedis.c @@ -0,0 +1,473 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + pev - the PE file analyzer toolkit + + pedis.c - PE disassembler + + 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 "../legacy.h" +#include "common.h" +#include "config.h" +#include "output.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROGRAM "pedis" +#define SPACES 32 // spaces # for text-based output +#define SYN_ATT 1 +#define SYN_INTEL 0 + +typedef struct { + bool all_sections; + char *section; + bool syntax; + uint64_t offset; + uint64_t + nbytes; // limit the number of bytes instructions. 0 means no limit. + uint64_t ninstructions; // limit the number of disassembled instructions. 0 + // means no limit. + bool entrypoint; + bool offset_is_rva; + uint16_t mode; +} options_t; + +static void usage(void) +{ + static char formats[255]; + output_available_formats(formats, sizeof(formats), '|'); + printf( + "Usage: %s OPTIONS FILE\n" + "Disassemble PE sections and functions (by default, until found a RET " + "or LEAVE instruction)\n" + "\nExample: %s -r 0x4c4df putty.exe\n" + "\nOptions:\n" + " --att Set AT&T assembly syntax " + "(default: Intel).\n" + " -e, --entrypoint Disassemble the entire " + "entrypoint function.\n" + " -f, --format <%s> Change output format (default: text).\n" + " -m, --mode <16|32|64> Disassembly mode (default: " + "auto).\n" + " -i Number of instructions to " + "disassemble.\n" + " -n Number of bytes to " + "disassemble\n" + " -o, --offset Disassemble at specified " + "offset, either in decimal or hexadecimal format (prefixed with 0x).\n" + " -r, --rva Disassemble at specified RVA, " + "either in decimal or hexadecimal format (prefixed with 0x).\n" + " -s, --section Disassemble en entire section " + "given.\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM, formats); +} + +static void free_options(options_t *options) +{ + if (options) { + free(options->section); + } + + free(options); +} + +static options_t *parse_options(int argc, char *argv[]) +{ + options_t *options = calloc_s(1, sizeof(options_t)); + + /* Parameters for getopt_long() function */ + static const char short_options[] = "em:i:n:o:r:s:f:V"; + + static const struct option long_options[] = { + {"help", no_argument, NULL, 1 }, + {"att", no_argument, NULL, 2 }, + {"", required_argument, NULL, 'n'}, + {"entrypoint", no_argument, NULL, 'e'}, + {"mode", required_argument, NULL, 'm'}, + {"offset", required_argument, NULL, 'o'}, + {"rva", required_argument, NULL, 'r'}, + {"section", required_argument, NULL, 's'}, + {"format", required_argument, NULL, 'f'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + options->syntax = SYN_INTEL; + + int c, ind; + + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { + break; + } + + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 2: + options->syntax = SYN_ATT; + break; + case 'e': + options->entrypoint = true; + break; + case 'm': + options->mode = (uint16_t) strtol(optarg, NULL, 10); + switch (options->mode) { + default: + EXIT_ERROR("Bad argument for mode."); + case 16: + break; + case 32: + break; + case 64: + break; + } + break; + case 'i': + // FIX: errno is not zeroed automatically if already set. + errno = 0; + options->ninstructions = (uint64_t) strtol(optarg, NULL, 0); + if (errno == ERANGE) { + EXIT_ERROR( + "number of instructions value would underflow or overflow"); + } + break; + case 'n': + // FIX: errno is not zeroed automatically if already set. + errno = 0; + options->nbytes = (uint64_t) strtol(optarg, NULL, 0); + if (errno == ERANGE) { + EXIT_ERROR("number of bytes value would underflow or overflow"); + } + break; + case 'o': + // FIX: errno is not zeroed automatically if already set. + errno = 0; + options->offset = (uint64_t) strtol(optarg, NULL, 0); + if (errno == ERANGE) { + EXIT_ERROR("offset value would underflow or overflow"); + } + options->offset_is_rva = false; + break; + case 'r': + // FIX: errno is not zeroed automatically if already set. + errno = 0; + options->offset = (uint64_t) strtol(optarg, NULL, 0); + if (errno == ERANGE) { + EXIT_ERROR("rva value would underflow or overflow"); + } + options->offset_is_rva = true; + break; + case 's': + options->section = strdup(optarg); + break; + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + case 'f': + if (output_set_format_by_name(optarg) < 0) { + EXIT_ERROR("invalid format option"); + } + break; + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); + } + } + + return options; +} + +static char *insert_spaces(const char *s) +{ + size_t size /*, wsize */; + char *new; + + size = strlen(s); + + if (! size) { + return NULL; + } + + // wsize = size / 2; + size = size + (size / 2); + + // FIXME: Maybe a better approach to the loop below is: + // new = malloc( size + 1 ); + // + // short *p = (short *)str; + // short *q = (short *)new; + // while ( wsize-- ) + // { + // *q++ = *p++; + // *(char *)q++ = ' '; + // } + // *(char *)q = 0; + // return new; + + new = calloc_s(1, size + 1); + + for (unsigned int i = 0, j = 0, pos = 0; i < size; i++) { + if (pos == 2) { + new[i] = ' '; + pos = 0; + } else { + new[i] = s[j++]; + pos++; + } + } + + return new; +} + +static bool is_ret_instruction(unsigned char opcode) +{ + switch (opcode) { + case 0xc9: // leave (this isn't a ret instruction!). + // case 0xc2: // ret imm16 (why not?) + case 0xc3: // ret + case 0xca: // retf imm16 + // case 0xcb: // retf (why not?) + return true; + + default: + return false; + } +} + +static void disassemble_offset(pe_ctx_t *ctx, const options_t *options, + ud_t *ud_obj, uint64_t offset) +{ + if (ctx == NULL || offset == 0) { + return; + } + + uint64_t instr_counter = 0; // counter for disassembled instructions + uint64_t byte_counter = 0; // counter for disassembled bytes + + while (ud_disassemble(ud_obj)) { + char ofs[MAX_MSG], value[MAX_MSG], *bytes; + const uint8_t *opcode = ud_insn_ptr(ud_obj); + + instr_counter++; // increment instruction counter + byte_counter += ud_insn_len(ud_obj); + + if (options->nbytes && byte_counter >= options->nbytes) { + return; + } + + const ud_mnemonic_code_t mnic = ud_insn_mnemonic(ud_obj); + const ud_operand_t *operand = ud_insn_opr(ud_obj, 0); + const ud_type_t op_type = operand != NULL ? operand->type : 0; + + snprintf(ofs, MAX_MSG, "%" PRIx64, + (options->offset_is_rva ? ctx->pe.imagebase : 0) + offset + + ud_insn_off(ud_obj)); + bytes = insert_spaces(ud_insn_hex(ud_obj)); + + if (! bytes) { + return; + } + + // correct near operand addresses for calls and jumps + if (op_type && (op_type != UD_OP_MEM) + && (mnic == UD_Icall || (mnic >= UD_Ijo && mnic <= UD_Ijmp))) { + char *instr_asm = strdup(ud_insn_asm(ud_obj)); + char *instr = strtok(instr_asm, "0x"); + + snprintf(value, MAX_MSG, "%s%*c%s%#" PRIx64, bytes, + SPACES - (int) strlen(bytes), ' ', instr ? instr : "", + ctx->pe.imagebase + offset + ud_insn_off(ud_obj) + + (uint64_t) ud_obj->operand[0].lval.sdword + + ud_insn_len(ud_obj)); + free(instr_asm); + } else { + snprintf(value, MAX_MSG, "%s%*c%s", bytes, + SPACES - (int) strlen(bytes), ' ', ud_insn_asm(ud_obj)); + } + + free(bytes); + output(ofs, value); + + // for sections, we stop at end of section + if (options->section && instr_counter >= options->ninstructions) { + break; + } else if (instr_counter >= options->ninstructions + && options->ninstructions) { + break; + } else if (options->entrypoint) { + // search for LEAVE or RET insrtuctions + for (unsigned int i = 0; i < ud_insn_len(ud_obj); i++) { + if (is_ret_instruction(opcode[i])) { + return; + } + } + } + } +} + +int pedis(int argc, char *argv[]) +{ + struct readpe_config config; + readpe_initialize(&config); + + if (argc < 2) { + usage(); + exit(EXIT_FAILURE); + } + + output_set_cmdline(argc, argv); + + options_t *options = parse_options(argc, argv); // opcoes + + const char *path = argv[argc - 1]; + pe_ctx_t ctx; + + pe_err_e err = pe_load_file(&ctx, path); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (! pe_is_pe(&ctx)) { + EXIT_ERROR("not a valid PE file"); + } + + IMAGE_OPTIONAL_HEADER *optional = pe_optional(&ctx); + if (optional == NULL) { + return EXIT_FAILURE; + } + + uint8_t mode_bits = 0; + switch (optional->type) { + default: + EXIT_ERROR("Unsupported architecture."); + return EXIT_FAILURE; + case MAGIC_PE32: + mode_bits = 32; + break; + case MAGIC_PE64: + mode_bits = 64; + break; + } + + // FIX: Only those are supported here. + IMAGE_COFF_HEADER *coff = pe_coff(&ctx); + switch (coff->Machine) { + case IMAGE_FILE_MACHINE_AMD64: + case IMAGE_FILE_MACHINE_I386: + break; + default: + EXIT_ERROR("Unsupported machine (AMD64 or I386)."); + return EXIT_FAILURE; + } + + ud_t ud_obj; // libudis86 object + ud_init(&ud_obj); + + // set disassembly mode according with PE architecture + ud_set_mode(&ud_obj, options->mode ? (uint8_t) options->mode : mode_bits); + + uint64_t offset = 0; // offset to start disassembly + + if (options->entrypoint) { + offset = pe_rva2ofs(&ctx, ctx.pe.entrypoint); + } else if (options->offset) { + offset = options->offset_is_rva ? pe_rva2ofs(&ctx, options->offset) + : options->offset; + } else if (options->section) { + IMAGE_SECTION_HEADER *section + = pe_section_by_name(&ctx, options->section); + + if (section) { // section found + offset = section->PointerToRawData; + if (! options->ninstructions) { + options->ninstructions = section->SizeOfRawData; + } + } else { + EXIT_ERROR("invalid section name"); + } + } else { + usage(); + return EXIT_FAILURE; + } + + if (! offset) { + fprintf(stderr, "unable to reach file offset (%#" PRIx64 ")\n", offset); + return EXIT_FAILURE; + } + + output_open_document(); + + ud_set_syntax(&ud_obj, options->syntax ? UD_SYN_ATT : UD_SYN_INTEL); + ud_set_input_buffer(&ud_obj, ctx.map_addr, pe_filesize(&ctx)); + // ud_set_input_file(&ud_obj, ctx.stream); + ud_input_skip(&ud_obj, offset); + disassemble_offset(&ctx, options, &ud_obj, offset); + + output_close_document(); + + // libera a memoria + free_options(options); + + // free + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + readpe_finalize(&config); + + return EXIT_SUCCESS; +} + +#ifdef STANDALONE +int main(int argc, char **argv) { return pedis(argc, argv); } +#endif + diff --git a/src/legacy/pehash.c b/src/legacy/pehash.c new file mode 100644 index 00000000..18a2352c --- /dev/null +++ b/src/legacy/pehash.c @@ -0,0 +1,326 @@ +/* vim :set ts=4 sw=4 sts=4 et : */ +/* + readpe - the PE file analyzer toolkit + + pehash.c - calculate hashes of PE pieces + + 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 "../legacy.h" +#include "common.h" +#include "output.h" +#include "readpe.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROGRAM "pehash" + +unsigned pefile_warn = 0; + +typedef struct { + bool all; + bool content; + struct { + bool all; + bool dos; + bool coff; + bool optional; + } headers; + struct { + char *name; + uint16_t index; + } sections; +} options_t; + +static void usage(void) +{ + static char formats[255]; + output_available_formats(formats, sizeof(formats), '|'); + printf("Usage: %s OPTIONS FILE\n" + "Calculate hashes of PE pieces\n" + "\nExample: %s -s '.text' winzip.exe\n" + "\nOptions:\n" + " -f, --format <%s> Change output format (default: text).\n" + " -a, --all Hash file, sections and " + "headers with md5, sha1, sha256, ssdeep and imphash.\n" + " -c, --content Hash only the file content " + "(default).\n" + " -h, --header Hash only the header with " + "the specified name.\n" + " -s, --section Hash only the section with " + "the specified name.\n" + " --section-index Hash only the section at " + "the specified index (1..n).\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM, formats); +} + +static void parse_header_name(options_t *options, const char *l_optarg) +{ + if (strcmp(l_optarg, "dos") == 0) { + options->headers.dos = true; + } else if (strcmp(l_optarg, "coff") == 0) { + options->headers.coff = true; + } else if (strcmp(l_optarg, "optional") == 0) { + options->headers.optional = true; + } else { + EXIT_ERROR("invalid header name option"); + } +} + +static void free_options(options_t *options) +{ + if (options) { + free(options->sections.name); + } + + free(options); +} + +static options_t *parse_options(int argc, char *argv[]) +{ + options_t *options = calloc_s(1, sizeof(options_t)); + + // parameters for getopt_long() function + static const char short_options[] = "f:a:c:h:s:V"; + + static const struct option long_options[] = { + {"help", no_argument, NULL, 1 }, + {"format", required_argument, NULL, 'f'}, + {"all", no_argument, NULL, 'a'}, + {"content", no_argument, NULL, 'c'}, + {"header", required_argument, NULL, 'h'}, + {"section-name", required_argument, NULL, 's'}, + {"section-index", required_argument, NULL, 2 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + // Setting the default option + options->content = true; + + int c, ind; + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { + break; + } + + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'f': + if (output_set_format_by_name(optarg) < 0) { + EXIT_ERROR("invalid format option"); + } + break; + case 'a': + options->all = true; + break; + case 'c': // default + options->all = false; // TODO remover? + options->content = true; + break; + case 's': + options->all = false; + options->headers.all = false; + // TODO: How do we need to handle non-ascii names? + options->sections.name = strdup(optarg); + break; + case 2: + options->all = false; + options->headers.all = false; + options->sections.index = (uint16_t) strtol(optarg, NULL, 10); + if (options->sections.index < 1 + || options->sections.index > MAX_SECTIONS) { + EXIT_ERROR("Bad argument for section-index,"); + } + break; + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + case 'h': + options->all = false; + options->headers.all = false; + parse_header_name(options, optarg); + break; + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); + } + } + + // TODO: Warn about simultaneous usage of -h, -s, and --section-index. + + return options; +} + +int pehash(int argc, char *argv[]) +{ + struct readpe_config config; + readpe_initialize(&config); + + if (argc < 2) { + usage(); + return EXIT_FAILURE; + } + + output_set_cmdline(argc, argv); + + options_t *options = parse_options(argc, argv); + + pe_ctx_t ctx; + + pe_err_e err = pe_load_file(&ctx, argv[argc - 1]); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (! pe_is_pe(&ctx)) { + EXIT_ERROR("not a valid PE file"); + } + + output_open_document(); + + if (options->headers.all || options->headers.dos || options->headers.coff + || options->headers.optional || options->sections.name + || options->sections.index) { + options->all = false; + options->content = false; + } + + if (options->all) { + options->content = true; + options->headers.all = true; + } + + if (options->content) { + print_content_hash(&ctx); + if (! options->all) { // whole file content only + goto BYE; + } + } + + if (options->headers.all) { + options->headers.dos = true; + options->headers.coff = true; + options->headers.optional = true; + } + + if (options->headers.all || options->headers.dos || options->headers.coff + || options->headers.optional) { + output_open_scope("headers", OUTPUT_SCOPE_TYPE_ARRAY); + } + + if (options->headers.all || options->headers.dos) { + output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT); + output("header_name", "IMAGE_DOS_HEADER"); + print_dos_header_hash(&ctx); + output_close_scope(); // header + } + + if (options->headers.all || options->headers.coff) { + output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT); + output("header_name", "IMAGE_COFF_HEADER"); + print_coff_header_hash(&ctx); + output_close_scope(); // header + } + + if (options->headers.all || options->headers.optional) { + output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT); + output("header_name", "IMAGE_OPTIONAL_HEADER"); + print_optional_header_hash(&ctx); + output_close_scope(); // header + } + + if (options->headers.all || options->headers.dos || options->headers.coff + || options->headers.optional) { + output_close_scope(); // headers + } + + if (options->all || options->sections.name || options->sections.index) { + output_open_scope("sections", OUTPUT_SCOPE_TYPE_ARRAY); + } + + if (options->all) { + print_sections_hash(&ctx); + // output_close_scope(); // sections + } else if (options->sections.name != NULL) { + output_open_scope("section", OUTPUT_SCOPE_TYPE_OBJECT); + print_section_hash_by_name(&ctx, options->sections.name); + output_close_scope(); // section + } else if (options->sections.index > 0) { + output_open_scope("section", OUTPUT_SCOPE_TYPE_OBJECT); + print_section_hash_by_index(&ctx, options->sections.index); + output_close_scope(); // section + } + + if (options->all || options->sections.name || options->sections.index) { + output_close_scope(); + } + +BYE: + output_close_document(); + + // free + free_options(options); + + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + readpe_finalize(&config); + return EXIT_SUCCESS; +} + +#ifdef STANDALONE +int main(int argc, char **argv) { return pehash(argc, argv); } +#endif + diff --git a/src/peldd.c b/src/legacy/peldd.c similarity index 61% rename from src/peldd.c rename to src/legacy/peldd.c index b011e017..8e907f6e 100644 --- a/src/peldd.c +++ b/src/legacy/peldd.c @@ -34,9 +34,12 @@ files in the program, then also delete it here. */ +#include "../legacy.h" #include "common.h" -#include #include "output.h" +#include "readpe.h" + +#include #define PROGRAM "peldd" @@ -45,68 +48,57 @@ static void usage(void) static char formats[255]; output_available_formats(formats, sizeof(formats), '|'); printf("Usage: %s FILE\n" - "Display PE library dependencies\n" - "\nExample: %s winzip.exe\n" - "\nOptions:\n" - " -f, --format <%s> Change output format (default: text).\n" - " -V, --version Show version.\n" - " --help Show help.\n", - PROGRAM, PROGRAM, formats); + "Display PE library dependencies\n" + "\nExample: %s winzip.exe\n" + "\nOptions:\n" + " -f, --format <%s> Change output format (default: text).\n" + " -V, --version Show version.\n" + " --help Show help.\n", + PROGRAM, PROGRAM, formats); } static void parse_options(int argc, char *argv[]) { /* Parameters for getopt_long() function */ - static const char short_options[] = "Vf:"; + static const char short_options[] = "Vf:"; - static const struct option long_options[] = { - { "help", no_argument, NULL, 1 }, - { "format", required_argument, NULL, 'f' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } + static const struct option long_options[] = { + {"help", no_argument, NULL, 1 }, + {"format", required_argument, NULL, 'f'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } }; int c, ind; - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { break; - - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - case 'f': - if (output_set_format_by_name(optarg) < 0) - EXIT_ERROR("invalid format option"); - break; - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); } - } -} -static void print_dependencies(pe_ctx_t *ctx) -{ - output_open_scope("Dependencies", OUTPUT_SCOPE_TYPE_ARRAY); - const pe_imports_t *imports = pe_imports(ctx); - for (size_t i=0; i < imports->dll_count; i++) { - const pe_imported_dll_t *dll = &imports->dlls[i]; - output(dll->name, NULL); + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + case 'f': + if (output_set_format_by_name(optarg) < 0) { + EXIT_ERROR("invalid format option"); + } + break; + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); + } } - output_close_scope(); } -int main(int argc, char *argv[]) +int peldd(int argc, char *argv[]) { - pev_config_t config; - PEV_INITIALIZE(&config); + struct readpe_config config; + readpe_initialize(&config); if (argc < 2) { usage(); @@ -119,7 +111,7 @@ int main(int argc, char *argv[]) pe_ctx_t ctx; - pe_err_e err = pe_load_file(&ctx, argv[argc-1]); + pe_err_e err = pe_load_file(&ctx, argv[argc - 1]); if (err != LIBPE_E_OK) { pe_error_print(stderr, err); return EXIT_FAILURE; @@ -131,8 +123,9 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (!pe_is_pe(&ctx)) + if (! pe_is_pe(&ctx)) { EXIT_ERROR("not a valid PE file"); + } output_open_document(); @@ -152,7 +145,12 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - PEV_FINALIZE(&config); + readpe_finalize(&config); return EXIT_SUCCESS; } + +#ifdef STANDALONE +int main(int argc, char **argv) { return peldd(argc, argv); } +#endif + diff --git a/src/pepack.c b/src/legacy/pepack.c similarity index 58% rename from src/pepack.c rename to src/legacy/pepack.c index f2d34908..7bff6a8c 100644 --- a/src/pepack.c +++ b/src/legacy/pepack.c @@ -34,10 +34,12 @@ files in the program, then also delete it here. */ +#include "../legacy.h" #include "common.h" -#include "plugins.h" -#define PROGRAM "pepack" +#include + +#define PROGRAM "pepack" #define MAX_SIG_SIZE 2048 typedef struct { @@ -49,63 +51,65 @@ static void usage(void) static char formats[255]; output_available_formats(formats, sizeof(formats), '|'); printf("Usage: %s FILE\n" - "Search for packers in PE files\n" - "\nExample: %s putty.exe\n" - "\nOptions:\n" - " -d, --database Use database file (default: ./userdb.txt).\n" - " -f, --format <%s> Change output format (default: text).\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM, formats); + "Search for packers in PE files\n" + "\nExample: %s putty.exe\n" + "\nOptions:\n" + " -d, --database Use database file " + "(default: ./userdb.txt).\n" + " -f, --format <%s> Change output format (default: text).\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM, formats); } static void free_options(options_t *options) { - if (options) + if (options) { free(options->dbfile); + } free(options); } static options_t *parse_options(int argc, char *argv[]) { - options_t *options = calloc_s(1, sizeof(options_t)); + options_t *options = calloc_s(1, sizeof(options_t)); /* Parameters for getopt_long() function */ - static const char short_options[] = "d:f:V"; - - static const struct option long_options[] = { - { "database", required_argument, NULL, 'd' }, - { "format", required_argument, NULL, 'f' }, - { "help", no_argument, NULL, 1 }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } + static const char short_options[] = "d:f:V"; + + static const struct option long_options[] = { + {"database", required_argument, NULL, 'd'}, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 1 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } }; int c, ind; - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { break; + } - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'd': - options->dbfile = strdup(optarg); - break; - case 'f': - if (output_set_format_by_name(optarg) < 0) - EXIT_ERROR("invalid format option"); - break; - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'd': + options->dbfile = strdup(optarg); + break; + case 'f': + if (output_set_format_by_name(optarg) < 0) { + EXIT_ERROR("invalid format option"); + } + break; + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); } } @@ -120,26 +124,26 @@ static options_t *parse_options(int argc, char *argv[]) static bool generic_packer(pe_ctx_t *ctx, uint64_t entrypoint) { IMAGE_SECTION_HEADER *section = pe_rva2section(ctx, entrypoint); - if (section == NULL) + if (section == NULL) { return false; + } // we count the flags for the section and if there is more than // 2 it means we don't have the mew_packer - static const SectionCharacteristics invalid_flags[] = { - IMAGE_SCN_MEM_EXECUTE, - IMAGE_SCN_MEM_READ, - IMAGE_SCN_MEM_WRITE - }; + static const SectionCharacteristics invalid_flags[] + = {IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE}; // MEW never leave EP in .text section - if (memcmp(section->Name, ".text", 5) == 0) + if (memcmp(section->Name, ".text", 5) == 0) { return false; + } unsigned short flags_count = 0; - for (size_t i=0; i < LIBPE_SIZEOF_ARRAY(invalid_flags); i++) { - if (section->Characteristics & invalid_flags[i]) + for (size_t i = 0; i < LIBPE_SIZEOF_ARRAY(invalid_flags); i++) { + if (section->Characteristics & (uint32_t) invalid_flags[i]) { flags_count++; + } } return flags_count < 3; @@ -150,9 +154,10 @@ static bool opendb(FILE **fp, const options_t *options) { const char *dbfile = options->dbfile ? options->dbfile : "userdb.txt"; - *fp = fopen(dbfile, "r"); - // FIXME(jweyrich): Granted read permission to the informed dbfile, this will succeed even if it's a directory! - if (!*fp) { + *fp = fopen(dbfile, "r"); + // FIXME(jweyrich): Granted read permission to the informed dbfile, this + // will succeed even if it's a directory! + if (! *fp) { // SHAREDIR is defined via CPPFLAGS in the Makefile *fp = fopen(SHAREDIR "/userdb.txt", "r"); } @@ -167,84 +172,87 @@ static bool match_peid_signature(const unsigned char *data, char *sig) // add null terminator byte_str[2] = '\0'; - while (*sig) - { + while (*sig) { // ignore '=' and blank spaces - if (*sig == '=' || *sig == ' ') - { + if (*sig == '=' || *sig == ' ') { sig++; continue; } // match "??" - if (*sig == '?') - { + if (*sig == '?') { sig += 2; data++; continue; } memcpy(byte_str, sig, 2); - byte = strtoul((char *) byte_str, NULL, 16); + byte = (unsigned char) strtoul((char *) byte_str, NULL, 16); - if (*data++ != byte) + if (*data++ != byte) { return false; + } sig += 2; // next two characters of signature } return true; } -static bool compare_signature(const unsigned char *data, uint64_t ep_offset, FILE *dbfile, char *packer_name, size_t packer_name_len) +static bool compare_signature(const unsigned char *data, uint64_t ep_offset, + FILE *dbfile, char *packer_name, + size_t packer_name_len) { - if (!dbfile || !data) + if (! dbfile || ! data) { return false; + } // FIX: 2 KiB buffer isn't a big deal and this function is single threaded. static char buff[MAX_SIG_SIZE]; - //memset(buff, 0, MAX_SIG_SIZE); - while (fgets(buff, MAX_SIG_SIZE, dbfile)) - { + // memset(buff, 0, MAX_SIG_SIZE); + while (fgets(buff, MAX_SIG_SIZE, dbfile)) { // line length size_t len = strlen(buff); // ignore comments and blank lines - if (*buff == ';' || *buff == '\n' || *buff == '\r') + if (*buff == ';' || *buff == '\n' || *buff == '\r') { continue; + } // remove newline from buffer - if (*(buff+len-1) == '\n') - *(buff+len-1) = '\0'; + if (*(buff + len - 1) == '\n') { + *(buff + len - 1) = '\0'; + } // removing carriage return, if present - if (*(buff+len-2) == '\r') - { - *(buff+len-2) = '\0'; + if (*(buff + len - 2) == '\r') { + *(buff + len - 2) = '\0'; //*(buff+len-1) = '\0'; len--; // update line length } // line have [packer name]? Fill packer_name pointer - if (*buff == '[' && *(buff+len-2) == ']') - { - *(buff+len-2) = '\0'; // remove square brackets - strncpy(packer_name, buff+1, packer_name_len); - packer_name[packer_name_len-1] = '\0'; // Guarantee it's Null-terminated. + if (*buff == '[' && *(buff + len - 2) == ']') { + *(buff + len - 2) = '\0'; // remove square brackets + strncpy(packer_name, buff + 1, packer_name_len); + packer_name[packer_name_len - 1] + = '\0'; // Guarantee it's Null-terminated. } // check if signature match - if (!strncasecmp(buff, "signature", 9)) - if (match_peid_signature(data + ep_offset, buff+9)) + if (! strncasecmp(buff, "signature", 9)) { + if (match_peid_signature(data + ep_offset, buff + 9)) { return true; + } + } } return false; } -int main(int argc, char *argv[]) +int pepack(int argc, char *argv[]) { - pev_config_t config; - PEV_INITIALIZE(&config); + struct readpe_config config; + readpe_initialize(&config); if (argc < 2) { usage(); @@ -253,12 +261,12 @@ int main(int argc, char *argv[]) output_set_cmdline(argc, argv); - options_t *options = parse_options(argc, argv); // opcoes + options_t *options = parse_options(argc, argv); // opcoes - const char *path = argv[argc-1]; - pe_ctx_t ctx; + const char *path = argv[argc - 1]; + pe_ctx_t ctx; - pe_err_e err = pe_load_file(&ctx, path); + pe_err_e err = pe_load_file(&ctx, path); if (err != LIBPE_E_OK) { pe_error_print(stderr, err); return EXIT_FAILURE; @@ -270,31 +278,39 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (!pe_is_pe(&ctx)) + if (! pe_is_pe(&ctx)) { EXIT_ERROR("not a valid PE file"); + } const uint64_t ep_offset = pe_rva2ofs(&ctx, ctx.pe.entrypoint); - if (ep_offset == 0) + if (ep_offset == 0) { EXIT_ERROR("unable to get entrypoint offset"); + } FILE *dbfile; - if (!opendb(&dbfile, options)) - fprintf(stderr, "WARNING: without valid database file, %s will search in generic mode only\n", PROGRAM); + if (! opendb(&dbfile, options)) { + fprintf(stderr, + "WARNING: without valid database file, %s will search in " + "generic mode only\n", + PROGRAM); + } - static char value[MAX_MSG + 1]; + static char value[MAX_MSG + 1]; // TODO(jweyrich): Create a new API to retrieve map_addr. - // TODO(jweyrich): Should we use `LIBPE_PTR_ADD(ctx->map_addr, ep_offset)` instead? + // TODO(jweyrich): Should we use `LIBPE_PTR_ADD(ctx->map_addr, ep_offset)` + // instead? const unsigned char *pe_data = ctx.map_addr; // packer by signature if (compare_signature(pe_data, ep_offset, dbfile, value, MAX_MSG)) ; // generic detection - else if (generic_packer(&ctx, ep_offset)) + else if (generic_packer(&ctx, ep_offset)) { snprintf(value, MAX_MSG, "generic"); - else + } else { snprintf(value, MAX_MSG, "no packer found"); + } output_open_document(); @@ -315,7 +331,12 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - PEV_FINALIZE(&config); + readpe_finalize(&config); return EXIT_SUCCESS; } + +#ifdef STANDALONE +int main(int argc, char **argv) { return pepack(argc, argv); } +#endif + diff --git a/src/legacy/peres.c b/src/legacy/peres.c new file mode 100644 index 00000000..c9bd61ae --- /dev/null +++ b/src/legacy/peres.c @@ -0,0 +1,246 @@ +/* vim :set ts=4 sw=4 sts=4 et : */ +/* + pev - the PE file analyzer toolkit + + peres.c - retrive informations and binary data of resources + + Copyright (C) 2012 - 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. +*/ + +#include "../legacy.h" +#include "common.h" +#include "readpe.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROGRAM "peres" + +typedef struct { + bool all; + bool extract; + bool namedExtract; + bool info; + bool statistics; + bool list; + bool version; + bool help; +} options_t; + +static void usage(void) +{ + static char formats[255]; + output_available_formats(formats, sizeof(formats), '|'); + printf("Usage: %s OPTIONS FILE\n" + "Show information about resource section and extract it\n" + "\nExample: %s -a putty.exe\n" + "\nOptions:\n" + " -a, --all Show all information, " + "statistics and extract resources\n" + " -f, --format <%s> Change output format (default: text)\n" + " -i, --info Show resources information\n" + " -l, --list Show list view\n" + " -s, --statistics Show resources statistics\n" + " -x, --extract Extract resources\n" + " -X, --named-extract Extract resources with path " + "names\n" + " -v, --file-version Show File Version from PE " + "resource directory\n" + " -V, --version Show version and exit\n" + " --help Show this help and exit\n", + PROGRAM, PROGRAM, formats); +} + +static void free_options(options_t *options) +{ + // FIX: Don't need to test for NULL pointer. + // if (options == NULL) + // return; + + free(options); +} + +static options_t *parse_options(int argc, char *argv[]) +{ + options_t *options = calloc_s(1, sizeof *options); + + /* Parameters for getopt_long() function */ + static const char short_options[] = "a:f:ilsxXvV"; + + static const struct option long_options[] = { + {"all", required_argument, NULL, 'a'}, + {"format", required_argument, NULL, 'f'}, + {"info", no_argument, NULL, 'i'}, + {"list", no_argument, NULL, 'l'}, + {"statistics", no_argument, NULL, 's'}, + {"extract", no_argument, NULL, 'x'}, + {"named-extract", no_argument, NULL, 'X'}, + {"file-version", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 1 }, + {NULL, 0, NULL, 0 } + }; + + int c, ind; + + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { + break; + } + + switch (c) { + case 'a': + options->all = true; + break; + case 'f': + if (output_set_format_by_name(optarg) < 0) { + EXIT_ERROR("invalid format option"); + } + break; + case 'i': + options->info = true; + break; + case 'l': + options->list = true; + break; + case 's': + options->statistics = true; + break; + case 'x': + options->extract = true; + break; + case 'X': + options->extract = true; + options->namedExtract = true; + break; + case 'v': + options->version = true; + break; + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); + } + } + + return options; +} + +int peres(int argc, char **argv) +{ + struct readpe_config config; + readpe_initialize(&config); + + if (argc < 3) { + usage(); + exit(EXIT_FAILURE); + } + + output_set_cmdline(argc, argv); + + options_t *options = parse_options(argc, argv); // opcoes + + const char *path = argv[argc - 1]; + pe_ctx_t ctx; + + pe_err_e err = pe_load_file(&ctx, path); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (! pe_is_pe(&ctx)) { + EXIT_ERROR("not a valid PE file"); + } + + output_open_document(); + + if (options->all) { + print_resources(&ctx); + print_resources_stats(&ctx); + print_resources_list(&ctx); + extract_all_resources(&ctx, options->namedExtract); + print_file_version(&ctx); + } else { + if (options->extract) { + extract_all_resources(&ctx, options->namedExtract); + } + if (options->info) { + print_resources(&ctx); + } + if (options->list) { + print_resources_list(&ctx); + } + if (options->statistics) { + print_resources_stats(&ctx); + } + if (options->version) { + print_file_version(&ctx); + } + } + + output_close_document(); + + // libera a memoria + free_options(options); + + // free + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + readpe_finalize(&config); + + return EXIT_SUCCESS; +} + +#ifdef STANDALONE +int main(int argc, char **argv) { return peres(argc, argv); } +#endif + diff --git a/src/legacy/pescan.c b/src/legacy/pescan.c new file mode 100644 index 00000000..ffcc7838 --- /dev/null +++ b/src/legacy/pescan.c @@ -0,0 +1,176 @@ +/* vim :set ts=4 sw=4 sts=4 et : */ +/* + readpe - the PE file analyzer toolkit + + pescan.c - search for suspicious things in PE files. + + 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 "../legacy.h" +#include "common.h" +#include "readpe.h" + +#include +#include +#include + +#define PROGRAM "pescan" + +typedef struct { + bool verbose; +} options_t; + +static void usage(void) +{ + static char formats[255]; + output_available_formats(formats, sizeof(formats), '|'); + printf("Usage: %s OPTIONS FILE\n" + "Search for suspicious things in PE files\n" + "\nExample: %s putty.exe\n" + "\nOptions:\n" + " -f, --format <%s> Change output format (default: text).\n" + " -v, --verbose Show more information about " + "found items.\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM, formats); +} + +static void free_options(options_t *options) +{ + // FIX: Don't need to test for NULL pointer. + // if (options == NULL) + // return; + + free(options); +} + +static options_t *parse_options(int argc, char *argv[]) +{ + options_t *options = calloc_s(1, sizeof(options_t)); + + /* Parameters for getopt_long() function */ + static const char short_options[] = "f:vV"; + + static const struct option long_options[] = { + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 1 }, + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + int c, ind; + + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { + break; + } + + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'f': + if (output_set_format_by_name(optarg) < 0) { + EXIT_ERROR("invalid format option"); + } + break; + case 'v': + options->verbose = true; + break; + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); + } + } + + return options; +} + +int pescan(int argc, char *argv[]) +{ + struct readpe_config config; + readpe_initialize(&config); + + if (argc < 2) { + usage(); + return EXIT_FAILURE; + } + + output_set_cmdline(argc, argv); + + options_t *options = parse_options(argc, argv); // opcoes + + const char *path = argv[argc - 1]; + pe_ctx_t ctx; + + pe_err_e err = pe_load_file(&ctx, path); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (! pe_is_pe(&ctx)) { + EXIT_ERROR("not a valid PE file"); + } + + output_open_document(); + + pe_scan(&ctx, options->verbose); + + output_close_document(); + + // free memory + free_options(options); + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + readpe_finalize(&config); + return EXIT_SUCCESS; +} + +#ifdef STANDALONE +int main(int argc, char **argv) { return pescan(argc, argv); } +#endif + diff --git a/src/legacy/pesec.c b/src/legacy/pesec.c new file mode 100644 index 00000000..9bf9a50f --- /dev/null +++ b/src/legacy/pesec.c @@ -0,0 +1,181 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + pesec.c - Checks for security features in PE files. + + 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 "../legacy.h" +#include "common.h" +#include "readpe.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROGRAM "pesec" + +static struct readpe_settings g_settings; + +static void usage(void) +{ + static char formats[255]; + output_available_formats(formats, sizeof(formats), '|'); + printf("Usage: %s [OPTIONS] FILE\n" + "Check for security features in PE files\n" + "\nExample: %s wordpad.exe\n" + "\nOptions:\n" + " -f, --format <%s> Change output format (default: text)\n" + " -c, --certoutform Specifies the certificate " + "output format (default: text).\n" + " -o, --certout Specifies the output " + "filename to write certificates to (default: stdout).\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM, formats); +} + +static void parse_options(int argc, char *argv[]) +{ + /* Parameters for getopt_long() function */ + static const char short_options[] = "f:c:o:V"; + + static const struct option long_options[] = { + {"format", required_argument, NULL, 'f'}, + {"certoutform", required_argument, NULL, 'c'}, + {"certout", required_argument, NULL, 'o'}, + {"help", no_argument, NULL, 1 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + int c, ind; + + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { + break; + } + + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'f': + if (output_set_format_by_name(optarg) < 0) { + EXIT_ERROR("invalid format option"); + } + break; + case 'v': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + case 'c': + g_settings.cert_format = optarg; + break; + case 'o': + g_settings.cert_out = optarg; + break; + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); + } + } + + return; +} + +int pesec(int argc, char *argv[]) +{ + struct readpe_config config; + readpe_initialize(&config); + + if (argc < 2) { + usage(); + exit(EXIT_FAILURE); + } + + parse_options(argc, argv); + + output_set_cmdline(argc, argv); + + const char *path = argv[argc - 1]; + pe_ctx_t ctx; + + pe_err_e err = pe_load_file(&ctx, path); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (! pe_is_pe(&ctx)) { + EXIT_ERROR("not a valid PE file"); + } + + print_securities(&ctx); + + // certificados + print_certificates_info(&ctx, g_settings.cert_format, g_settings.cert_out, + false); + + output_close_document(); + + // free + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + readpe_finalize(&config); + + return EXIT_SUCCESS; +} + +#ifdef STANDALONE +int main(int argc, char **argv) { return pesec(argc, argv); } +#endif + diff --git a/src/legacy/pestr.c b/src/legacy/pestr.c new file mode 100644 index 00000000..727c4849 --- /dev/null +++ b/src/legacy/pestr.c @@ -0,0 +1,291 @@ +/* vim :set ts=4 sw=4 sts=4 et : */ +/* + pev - the PE file analyzer toolkit + + pestr.c - search for strings in PE files. + + Copyright (C) 2012 - 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. +*/ + +#include "../legacy.h" +#include "common.h" +#include "readpe.h" + +#include +#include +#include +#include +#include + +#define PROGRAM "pestr" +#define BUFSIZE 4 +#define LINE_BUFFER 32768 + +static struct readpe_settings g_settings; + +static void usage(void) +{ + printf( + "Usage: %s OPTIONS FILE\n" + "Search for strings in PE files\n" + "\nExample: %s acrobat.exe\n" + "\nOptions:\n" + " -n, --min-length Set minimum string length " + "(default: 4).\n" + " -o, --offset Show string offset in file.\n" + " -s, --section Show string section, if " + "exists.\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM); +} + +static void parse_options(int argc, char *argv[]) +{ + /* Parameters for getopt_long() function */ + static const char short_options[] = "osn:V"; + + static const struct option long_options[] = { + {"offset", no_argument, NULL, 'o'}, + {"section", no_argument, NULL, 's'}, + {"min-length", required_argument, NULL, 'n'}, + {"help", no_argument, NULL, 1 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + int c, ind; + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { + break; + } + + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'o': + g_settings.str_offset = true; + break; + case 's': + g_settings.str_section = true; + break; + case 'n': { + // FIX: errno isn't automatically zeroed if already set. + errno = 0; + unsigned long value = strtoul(optarg, NULL, 0); + if (value == ULONG_MAX && errno == ERANGE) { + fprintf(stderr, + "The original (nonnegated) value would overflow"); + exit(EXIT_FAILURE); + } + g_settings.str_min_length = (unsigned char) value; + break; + } + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); + } + } + return; +} + +// TODO Move it to libpe +static unsigned char *ofs2section(pe_ctx_t *ctx, uint64_t offset) +{ + IMAGE_SECTION_HEADER **sections = pe_sections(ctx); + + for (uint16_t i = 0; i < ctx->pe.num_sections; i++) { + uint32_t sect_offset = sections[i]->PointerToRawData; + uint32_t sect_size = sections[i]->SizeOfRawData; + + if (offset >= sect_offset && offset <= (sect_offset + sect_size)) { + return (unsigned char *) sections[i]->Name; + } + } + + return NULL; +} + +static void printb(pe_ctx_t *ctx, const uint8_t *bytes, size_t pos, size_t end, + bool is_wide) +{ + if (g_settings.str_offset) { + printf("%#lx\t", (unsigned long) pos); + } + + if (g_settings.str_section) { + char *s = (char *) ofs2section(ctx, pos); + printf("%s\t", s ? s : "[none]"); + } + + // printf("%s\t", is_wide ? "U16LE" : "U8" ); + + if (is_wide) { + for (; pos < end;) { + // Byte swap; Internal PE uses little endian while C uses big endian + wchar_t wc = bytes[pos] | bytes[pos + 1] << 8; + if (wc) { + putwchar(wc); + } + pos += 2; + } + } else { + for (; pos < end; ++pos) { + char c = (char) bytes[pos]; + if (c) { + putchar(c); + } + } + } + + putchar('\n'); +} + +void print_strings(pe_ctx_t *ctx) +{ + const uint64_t pe_size = pe_filesize(ctx); + const uint8_t *pe_raw_data = ctx->map_addr; + + uint16_t chunk; + size_t buff_start = 0; + size_t odd_wbuff_start = 0; + size_t even_wbuff_start = 0; + + for (size_t pe_raw_offset = 0; pe_raw_offset < pe_size; ++pe_raw_offset) { + const uint8_t byte = pe_raw_data[pe_raw_offset]; + + if (pe_raw_offset + 1 < pe_size) { + // Byte swap; Internal PE uses little endian while C uses big endian + chunk = (uint16_t) ((pe_raw_data[pe_raw_offset + 1] << 8) | byte); + } else { + chunk = 0; + } + + if (isprint(byte)) { + if (buff_start == 0) { + buff_start = pe_raw_offset; + } + } else { + if (buff_start != 0) { + if ((pe_raw_offset - buff_start) + >= (g_settings.str_min_length ? g_settings.str_min_length + : 4)) { + printb(ctx, pe_raw_data, buff_start, pe_raw_offset, false); + } + buff_start = 0; + } + } + + if (iswprint(chunk)) { + if (pe_raw_offset & 0x1) { + if (odd_wbuff_start == 0) { + odd_wbuff_start = pe_raw_offset; + } + } else { + if (even_wbuff_start == 0) { + even_wbuff_start = pe_raw_offset; + } + } + } else { + if (pe_raw_offset & 0x1) { + if (odd_wbuff_start != 0) { + if ((pe_raw_offset - odd_wbuff_start) / 2 + >= (g_settings.str_min_length + ? g_settings.str_min_length + : 4)) { + printb(ctx, pe_raw_data, odd_wbuff_start, pe_raw_offset, + true); + } + odd_wbuff_start = 0; + } + } else { + if (even_wbuff_start != 0) { + if ((pe_raw_offset - even_wbuff_start) / 2 + >= (g_settings.str_min_length + ? g_settings.str_min_length + : 4)) { + printb(ctx, pe_raw_data, even_wbuff_start, + pe_raw_offset, true); + } + even_wbuff_start = 0; + } + } + } + } +} + +int pestr(int argc, char *argv[]) +{ + if (argc < 2) { + usage(); + exit(EXIT_FAILURE); + } + + parse_options(argc, argv); // opcoes + + const char *path = argv[argc - 1]; + pe_ctx_t ctx; + + pe_err_e err = pe_load_file(&ctx, path); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (! pe_is_pe(&ctx)) { + EXIT_ERROR("not a valid PE file"); + } + + print_strings(&ctx); + + // free + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +#ifdef STANDALONE +int main(int argc, char **argv) { return pestr(argc, argv); } +#endif + diff --git a/src/legacy/readpe.c b/src/legacy/readpe.c new file mode 100644 index 00000000..226bee1c --- /dev/null +++ b/src/legacy/readpe.c @@ -0,0 +1,284 @@ +/* + readpe - the PE file analyzer toolkit + + src/legacy/readpe.c - collection of pre 1.0 main functions + + Copyright (C) 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 "readpe.h" + +#include "../legacy.h" +#include "common.h" +#include "config.h" + +#include + +#define PROGRAM "readpe" + +typedef struct { + bool all; + bool dos; + bool coff; + bool opt; + bool dirs; + bool imports; + bool exports; + bool all_headers; + bool all_sections; +} options_t; + +static void parse_headers(options_t *options, const char *l_optarg) +{ + if (! strcmp(l_optarg, "dos")) { + options->dos = true; + } else if (! strcmp(l_optarg, "coff")) { + options->coff = true; + } else if (! strcmp(l_optarg, "optional")) { + options->opt = true; + } else { + EXIT_ERROR("invalid header option"); + } +} + +static void free_options(options_t *options) { free(options); } + +static void usage(void) +{ + static char formats[255]; + output_available_formats(formats, sizeof(formats), '|'); + printf("Usage: %s OPTIONS FILE\n" + "Show PE file headers\n" + "\nExample: %s --header optional winzip.exe\n" + "\nOptions:\n" + " -A, --all Full output (default).\n" + " -H, --all-headers Show all PE headers.\n" + " -S, --all-sections Show PE section headers.\n" + " -f, --format <%s> Change output format (default: text).\n" + " -d, --dirs Show data directories.\n" + " -h, --header Show specific header. It " + "can be used multiple times.\n" + " -i, --imports Show imported functions.\n" + " -e, --exports Show exported functions.\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM, formats); +} + +static options_t *parse_options(int argc, char *argv[]) +{ + options_t *options = calloc_s(1, sizeof(options_t)); + + /* Parameters for getopt_long() function */ + static const char short_options[] = "AHSh:dief:V"; + + static const struct option long_options[] = { + {"help", no_argument, NULL, 1 }, + {"all", no_argument, NULL, 'A'}, + {"all-headers", no_argument, NULL, 'H'}, + {"all-sections", no_argument, NULL, 'S'}, + {"header", required_argument, NULL, 'h'}, + {"imports", no_argument, NULL, 'i'}, + {"exports", no_argument, NULL, 'e'}, + {"dirs", no_argument, NULL, 'd'}, + {"format", required_argument, NULL, 'f'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + options->all = true; + + int c, ind; + + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { + break; + } + + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'A': + options->all = true; + break; + case 'H': + options->all = false; + options->all_headers = true; + break; + case 'd': + options->all = false; + options->dirs = true; + break; + case 'S': + options->all = false; + options->all_sections = true; + break; + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + case 'h': + options->all = false; + parse_headers(options, optarg); + break; + case 'i': + options->all = false; + options->imports = true; + break; + case 'e': + options->all = false; + options->exports = true; + break; + case 'f': + if (output_set_format_by_name(optarg) < 0) { + EXIT_ERROR("invalid format option"); + } + break; + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); + } + } + + return options; +} + +int readpe(int argc, char *argv[]) +{ + struct readpe_config config; + readpe_initialize(&config); + + if (argc < 2) { + usage(); + return EXIT_FAILURE; + } + + output_set_cmdline(argc, argv); + + options_t *options = parse_options(argc, argv); // opcoes + + pe_ctx_t ctx; + + pe_err_e err = pe_load_file(&ctx, argv[argc - 1]); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (! pe_is_pe(&ctx)) { + EXIT_ERROR("not a valid PE file"); + } + + output_open_document(); + + // dos header + if (options->dos || options->all_headers || options->all) { + print_dos_header(&ctx); + } + + // coff/file header + if (options->coff || options->all_headers || options->all) { + print_coff_header(&ctx); + } + + // optional header + if (options->opt || options->all_headers || options->all) { + print_optional_header(&ctx); + } + + IMAGE_DATA_DIRECTORY **directories = get_pe_directories(&ctx); + + bool directories_warned = false; + // directories + if (options->dirs || options->all) { + if (directories != NULL) { + print_directories(&ctx); + } else if (pe_is_exec(&ctx) && ! directories_warned) { + LIBPE_WARNING("directories not found"); + directories_warned = true; + } + } + + // imports + if (options->imports || options->all) { + if (directories != NULL) { + print_imports(&ctx); + } else if (pe_is_exec(&ctx) && ! directories_warned) { + LIBPE_WARNING("directories not found"); + directories_warned = true; + } + } + + // exports + if (options->exports || options->all) { + if (directories != NULL) { + print_exports(&ctx); + } else if (pe_is_exec(&ctx) && ! directories_warned) { + LIBPE_WARNING("directories not found"); + directories_warned = true; + } + } + + // sections + if (options->all_sections || options->all) { + if (pe_sections(&ctx) != NULL) { + print_sections(&ctx); + } else { + LIBPE_WARNING("unable to read sections"); + } + } + + output_close_document(); + + // libera a memoria + free_options(options); + + // free + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + readpe_finalize(&config); + + return EXIT_SUCCESS; +} + +#ifdef STANDALONE +int main(int argc, char **argv) { return readpe(argc, argv); } +#endif + diff --git a/src/rva2ofs.c b/src/legacy/rva2ofs.c similarity index 64% rename from src/rva2ofs.c rename to src/legacy/rva2ofs.c index 87de48fd..3647d7db 100644 --- a/src/rva2ofs.c +++ b/src/legacy/rva2ofs.c @@ -34,58 +34,63 @@ files in the program, then also delete it here. */ +#include "../legacy.h" #include "common.h" +#include +#include +#include +#include +#include +#include + #define PROGRAM "rva2ofs" static void usage(void) { printf("Usage: %s FILE\n" - "Convert RVA to raw file offset\n" - "\nExample: %s 0x12db cards.dll\n" - "\nOptions:\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM); + "Convert RVA to raw file offset\n" + "\nExample: %s 0x12db cards.dll\n" + "\nOptions:\n" + " -V, --version Show version.\n" + " --help Show this help.\n", + PROGRAM, PROGRAM); } static void parse_options(int argc, char *argv[]) { /* Parameters for getopt_long() function */ - static const char short_options[] = "V"; + static const char short_options[] = "V"; - static const struct option long_options[] = { - { "help", no_argument, NULL, 1 }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } + static const struct option long_options[] = { + {"help", no_argument, NULL, 1 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } }; int c, ind; - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { + if (c < 0) { break; + } - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); + switch (c) { + case 1: // --help option + usage(); + exit(EXIT_SUCCESS); + case 'V': + printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); + exit(EXIT_FAILURE); } } } -int main(int argc, char *argv[]) +int rva2ofs(int argc, char *argv[]) { - //PEV_INITIALIZE(); - if (argc != 3) { usage(); return EXIT_FAILURE; @@ -101,10 +106,11 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - uint64_t rva = (uint64_t)strtoll(argv[1], NULL, 0); + uint64_t rva = (uint64_t) strtoll(argv[1], NULL, 0); - if (!rva) + if (! rva) { EXIT_ERROR("invalid RVA"); + } err = pe_parse(&ctx); if (err != LIBPE_E_OK) { @@ -112,14 +118,18 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (!pe_is_pe(&ctx)) + if (! pe_is_pe(&ctx)) { EXIT_ERROR("not a valid PE file"); + } - printf("%#"PRIx64"\n", pe_rva2ofs(&ctx, rva)); + printf("%#" PRIx64 "\n", pe_rva2ofs(&ctx, rva)); pe_unload(&ctx); - //PEV_FINALIZE(); - return EXIT_SUCCESS; } + +#ifdef STANDALONE +int main(int argc, char **argv) { return rva2ofs(argc, argv); } +#endif + diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..fce9cfd6 --- /dev/null +++ b/src/main.c @@ -0,0 +1,735 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + main.c - main executable entry + + Copyright (C) 2023 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 "common.h" +#include "config.h" +#include "modes.h" +#include "output.h" +#include "readpe.h" + +#ifdef READPE_LEGACY +#include "legacy.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if __GNUC__ +#define ATTRIBUTE_NORETURN __attribute__((noreturn)) +#else +#define ATTRIBUTE_NORETURN +#endif + +static struct readpe_settings g_settings; +static struct readpe_config g_config; + +// ------------------------------------------------------------------------- // + +struct mode_option { + const char *const name; + const int value; +}; + +static int getopt_mode(int argc, char *const argv[], const char *optstring, + const struct option *longopts, + const struct mode_option *modeopts, int *restrict index) +{ + // static int nextchar; + if (optind >= argc) { + return -1; + } + const char *const arg = argv[optind]; + + if (arg[0] == '-') { + if (arg[1] == '-') { + if (arg[2] == '\0') { + // -- force end scan + return -1; + } + // long option + return getopt_long(argc, argv, optstring, longopts, index); + } else { + // short option + return getopt(argc, argv, optstring); + } + } else { + // mode option + if (modeopts == NULL) { + return -1; + } + + for (int i = 0; (modeopts)[i].name != NULL; ++i) { + struct mode_option opt = (modeopts)[i]; + + if (strcmp(opt.name, arg) == 0) { + optind++; + return opt.value; + } + } + } + + return -1; +} + +// ------------------------------------------------------------------------- // + +// GNU compliant version output +ATTRIBUTE_NORETURN static void version(void) +{ + printf("readpe %s\n" + "Copyright (C) 2023 readpe authors\n" + "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.\n", + VERSION); + exit(EXIT_SUCCESS); +} + +// GNU compliant help output +ATTRIBUTE_NORETURN static void help(void) +{ + const char *helptext + = "Usage: readpe [ | | ] []\n" + "Example: readpe header coff explorer.exe\n" + "\nModes:\n" + " header Output information on PE headers\n" + " Supports submodes:\n" + " dos, coff and optional\n" + " section Output information on PE sections\n" + " Supported submodes are section names\n" + " and section indexes\n" + " directories Output list of available PE directories\n" + " exports Output exported symbols\n" + " imports Output imported libraries and symbols\n" + " resources Output information or extract resources\n" + " certificates Output information or extract certificates\n" + " features Output PE features\n" + " security Output PE security features\n" + "\nCommands:\n" + " hash Output various hashes of file or mode\n" + " Supports header and section\n" + " and their submodes\n" + " scan Search for suspicious things in PE files\n" + "\nArguments:\n" + "-f, --format=FORMAT Set what which output plugin to use\n" + " --file-version Display resource specified file version\n" + "-h, --help Display this help and exit\n" + "-V, --version Display version information and exit\n"; + + printf("%s", helptext); + printf("\nReport bugs to: https://github.com/mentebinaria/readpe/issues\n"); + exit(EXIT_SUCCESS); +} + +// ------------------------------------------------------------------------- // + +static const char scan_shortopts[] = "vf:hV"; +static const struct option scan_longopts[] = { + {"verbose", no_argument, NULL, 'v'}, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char res_shortopts[] = "ltvsf:hV"; +static const struct option res_longopts[] = { + {"list", no_argument, NULL, 'l'}, + {"tree", no_argument, NULL, 't'}, + {"verbose", no_argument, NULL, 'v'}, + {"file-version", no_argument, NULL, 2 }, + {"statistics", no_argument, NULL, 's'}, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char ext_shortopts[] = "nhV"; +static const struct option ext_longopts[] = { + {"named", no_argument, NULL, 'n'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char cert_shortopts[] = "of:hV"; +static const struct option cert_longopts[] = { + {"out", required_argument, NULL, 'o'}, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char section_short[] = "ali:n:f:hV"; +static const struct option section_long[] = { + {"all", no_argument, NULL, 'a'}, + {"list", no_argument, NULL, 'l'}, + {"index", required_argument, NULL, 'i'}, + {"name", required_argument, NULL, 'n'}, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char dir_shortopts[] = "lvf:hV"; +static const struct option dir_longopts[] = { + {"list", no_argument, NULL, 'l'}, + {"verbose", no_argument, NULL, 'v'}, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char default_shortopts[] = "f:hV"; +static const struct option default_longopts[] = { + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char extended_shortopts[] = "alf:hV"; +static const struct option extended_longopts[] = { + {"all", no_argument, NULL, 'a'}, + {"list", no_argument, NULL, 'l'}, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char base_shortopts[] = "f:hV"; +static const struct option base_longopts[] = { + {"file-version", no_argument, NULL, 2 }, + {"get-output-plugins", no_argument, NULL, 3 }, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +// ------------------------------------------------------------------------- // + +static const struct mode_option header_mode[] = { + {"dos", MODE_HEADERS_DOS }, + {"coff", MODE_HEADERS_COFF }, + {"optional", MODE_HEADERS_OPTIONAL}, + {"hash", COMMAND_HASH }, + {NULL, 0 } +}; + +static const struct mode_option hash_mode[] = { + {"hash", COMMAND_HASH}, + {NULL, 0 } +}; + +static const struct mode_option hashstr_mode[] = { + {"hash", COMMAND_HASH }, + {"strings", COMMAND_STRINGS}, + {NULL, 0 } +}; + +static const struct mode_option resource_mode[] = { + {"extract", COMMAND_EXTRACT}, + {NULL, 0 } +}; + +static const struct mode_option base_mode[] = { + {"header", MODE_HEADERS }, + {"section", MODE_SECTIONS }, + {"directories", MODE_DIRECTORIES }, + {"exports", MODE_EXPORTS }, + {"imports", MODE_IMPORTS }, + {"resources", MODE_RESOURCES }, + {"certificates", MODE_CERTIFICATES}, + {"features", MODE_SECURITY }, + {"security", MODE_SECURITY }, + {"strings", COMMAND_STRINGS }, + {"scan", COMMAND_SCAN }, + {"hash", COMMAND_HASH }, + {NULL, 0 } +}; + +// ------------------------------------------------------------------------- // + +#ifdef READPE_LEGACY +static void legacy(int argc, char *argv[]) +{ + const char *bin_name = strrchr(argv[0], '/'); + // If no '/' in caller (called from env) we set it to the original caller + // This obviously does not work for CP/M style pathing + bin_name = bin_name ? bin_name + 1 : argv[0]; + const size_t len = strlen(bin_name); + if (len < 5) { + return; + } + + if (strstr(bin_name, "peldd") == bin_name) { + exit(rva2ofs(argc, argv)); + } else if (strstr(bin_name, "pesec") == bin_name) { + exit(ofs2rva(argc, argv)); + } else if (strstr(bin_name, "pestr") == bin_name) { + exit(ofs2rva(argc, argv)); + } else if (strstr(bin_name, "peres") == bin_name) { + exit(ofs2rva(argc, argv)); + } +#ifdef READPE_DISASSEMBLER + else if (strstr(bin_name, "pedis") == bin_name) { + exit(ofs2rva(argc, argv)); + } +#endif // READPE_DISASSEMBLER + else if (strstr(bin_name, "pescan") == bin_name) { + exit(ofs2rva(argc, argv)); + } else if (strstr(bin_name, "pehash") == bin_name) { + exit(ofs2rva(argc, argv)); + } else if (strstr(bin_name, "pepack") == bin_name) { + exit(rva2ofs(argc, argv)); + } +#if 0 + else if (strstr(bin_name, "ofs2rva") == bin_name) { + exit(ofs2rva(argc, argv)); + } else if (strstr(bin_name, "rva2ofs") == bin_name) { + exit(rva2ofs(argc, argv)); + } +#endif +} +#endif // READPE_LEGACY + +// ------------------------------------------------------------------------- // + +static int handle_file(const char *filename) +{ + pe_ctx_t ctx; + pe_err_e err = pe_load_file(&ctx, filename); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (! pe_is_pe(&ctx)) { + EXIT_ERROR("not a valid PE file"); + } + + output_open_document(); + + switch (g_settings.mode) { + case MODE_BASE: + if (g_settings.file_version) { + print_file_version(&ctx); + break; + } + print_dos_header(&ctx); + print_coff_header(&ctx); + print_optional_header(&ctx); + print_directories(&ctx); + print_imports(&ctx); + print_exports(&ctx); + print_sections(&ctx); + break; + case MODE_HEADERS: + if (g_settings.list) { + // TODO: + printf("dos\ncoff\noptional\n"); + break; + } + print_dos_header(&ctx); + print_coff_header(&ctx); + print_optional_header(&ctx); + break; + case MODE_HEADERS_DOS: + print_dos_header(&ctx); + break; + case MODE_HEADERS_COFF: + print_coff_header(&ctx); + break; + case MODE_HEADERS_OPTIONAL: + print_optional_header(&ctx); + break; + case MODE_DIRECTORIES: + print_directory_list(&ctx, g_settings.verbose); + break; + case MODE_EXPORTS: + print_exports(&ctx); + break; + case MODE_IMPORTS: + if (g_settings.verbose) { + print_imports(&ctx); + break; + } + print_dependencies(&ctx); + break; + + case MODE_RESOURCES: { + bool printed = false; + + if (g_settings.verbose) { + print_resources(&ctx); + printed = true; + } + + if (g_settings.list && ! g_settings.verbose) { + print_resources_list(&ctx); + printed = true; + } + + if (g_settings.res_tree) { + print_resources_tree(&ctx); + printed = true; + } + + if (g_settings.res_statistics) { + print_resources_stats(&ctx); + printed = true; + } + + if (g_settings.file_version) { + print_file_version(&ctx); + printed = true; + } + + // If we haven't printed anything yet + if (! printed) { + print_resources_list(&ctx); + // peres_save_all_resources(&ctx, root_node, + // options->namedExtract); + print_resources_stats(&ctx); + print_file_version(&ctx); + } + break; + } + + // case MODE_EXCEPTIONS: + + case MODE_SECURITY: + print_securities(&ctx); + break; + // fall through + case MODE_CERTIFICATES: + print_certificates(&ctx, g_settings.cert_format, g_settings.cert_out); + break; + + // case MODE_BASE_RELOCATIONS: + // case MODE_DEBUG: + // case MODE_ARCHITECTURE: + // case MODE_GLOBAL_PTR: + // case MODE_TLS: + // case MODE_LOAD_CONFIGS: + // case MODE_BOUND_IMPORT: + // case MODE_IAT: + // case MODE_DELAY_IMPORT_DESCRIPTOR: + // case MODE_CLR_RUNTIME_HEADER: + + case MODE_SECTIONS: + if (g_settings.all) { + print_sections(&ctx); + break; + } + print_sections_list(&ctx); + break; + + case MODE_SECTION: + print_section_by_name(&ctx, g_settings.section_name); + break; + + case COMMAND_STRINGS: + // print_strings(&ctx, &CONFIG.string); + break; + + case COMMAND_HASH: + // TODO + // case COMMAND_HASH_MD5: + // case COMMAND_HASH_SHA1: + // case COMMAND_HASH_SHA256: + // case COMMAND_HASH_SSDEEP: + // case COMMAND_HASH_IMPHASH: + print_hash(&ctx, &g_settings); + break; + + case COMMAND_SCAN: { + pe_scan(&ctx, g_settings.verbose); + break; + } + + case COMMAND_EXTRACT: { + extract_all_resources(&ctx, g_settings.res_named); + break; + } + + default: + printf("Unknown Argument: %d\n", g_settings.mode); + exit(-1); + } + + output_close_document(); + + // free + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +// ------------------------------------------------------------------------- // + +static const char *parse_options(int argc, char *argv[]) +{ + + const char *shortargs = base_shortopts; + const struct option *longargs = base_longopts; + const struct mode_option *modeargs = base_mode; + + int c, file_arg = 0, mode = 0, mode_context = 0, index = 1; + + if (access(argv[1], F_OK) == 0) { + optind++; + file_arg = 1; + } + + while ( + (c = getopt_mode(argc, argv, shortargs, longargs, modeargs, &index))) { + if (c < 0) { + break; + } + + if (c >= MODE_START) { + mode = c; + } + + if (mode < COMMAND_START) { + mode_context = mode; + } + + switch (c) { + // ARGUMENTS + case 2: + g_settings.file_version = true; + modeargs = NULL; + break; + case 'a': + if (mode == MODE_HEADERS || mode == MODE_SECTIONS) { + g_settings.all = true; + modeargs = NULL; + } + break; + case 'V': + version(); + break; + case 'h': + if (mode == MODE_RESOURCES) { + // TODO: print readpe-resources help + printf("readpe-resources --help:\n"); + exit(EXIT_SUCCESS); + } + help(); + break; + case 'f': + if (mode == MODE_CERTIFICATES) { + g_settings.cert_format = optarg; + break; + } + if (output_set_format_by_name(optarg) < 0) { + // static char formats[255]; + // output_available_formats(formats, sizeof(formats), '|'); + // printf("Format: %s\nAvailable: %s\n", optarg, formats); + EXIT_ERROR("invalid format option"); + } + break; + case 'l': + if (mode == MODE_HEADERS || mode == MODE_SECTIONS + || mode == MODE_DIRECTORIES || mode == MODE_RESOURCES + || mode == MODE_IMPORTS) { + g_settings.list = true; + modeargs = NULL; + } + break; + case 'n': + if (mode == COMMAND_EXTRACT && mode_context == MODE_RESOURCES) { + g_settings.res_named = true; + } + break; + case 'p': + /* Note: Declaration after label is an extension*/; + char buffer[255]; + output_available_formats(buffer, sizeof(buffer), '\n'); + printf("%s\n", buffer); + exit(EXIT_SUCCESS); + break; + case 's': + if (mode == MODE_RESOURCES) { + g_settings.res_statistics = true; + modeargs = NULL; + } + break; + case 't': + if (mode == MODE_RESOURCES) { + g_settings.res_tree = true; + modeargs = NULL; + } + break; + case 'v': + if (mode == MODE_DIRECTORIES || mode == MODE_RESOURCES + || mode == COMMAND_SCAN || mode == MODE_IMPORTS) { + g_settings.verbose = true; + modeargs = NULL; + } + break; + + // MODES + case MODE_HEADERS: + shortargs = extended_shortopts; + longargs = extended_longopts; + modeargs = header_mode; + break; + case MODE_HEADERS_DOS: + case MODE_HEADERS_COFF: + case MODE_HEADERS_OPTIONAL: + case MODE_SECTION: + shortargs = default_shortopts; + longargs = default_longopts; + modeargs = hash_mode; + break; + case MODE_SECTIONS: + if ((optind < argc - 1) && (*argv[optind] != '-') + && ! ! strcmp(argv[optind], "hash")) { + mode = MODE_SECTION; + mode_context = MODE_SECTION; + g_settings.section_name = argv[optind]; + ++optind; + } + shortargs = section_short; + longargs = section_long; + modeargs = hashstr_mode; + break; + case MODE_DIRECTORIES: + case MODE_IMPORTS: + shortargs = dir_shortopts; + longargs = dir_longopts; + modeargs = NULL; + break; + case MODE_RESOURCES: + shortargs = res_shortopts; + longargs = res_longopts; + modeargs = resource_mode; + break; + case MODE_CERTIFICATES: + shortargs = cert_shortopts; + longargs = cert_longopts; + modeargs = NULL; + break; + case MODE_SECURITY: + case MODE_EXPORTS: + shortargs = default_shortopts; + longargs = default_longopts; + modeargs = NULL; + break; + + // COMMANDS + case COMMAND_EXTRACT: + shortargs = ext_shortopts; + longargs = ext_longopts; + modeargs = NULL; + break; + case COMMAND_SCAN: + shortargs = scan_shortopts; + longargs = scan_longopts; + modeargs = NULL; + break; + case COMMAND_STRINGS: + case COMMAND_HASH: + shortargs = default_shortopts; + longargs = default_longopts; + modeargs = NULL; + break; + } + } + + if (access(argv[argc - 1], F_OK) == 0) { + file_arg = argc - 1; + } + if (file_arg == 0) { + help(); + } + + g_settings.mode = mode; + g_settings.context = mode_context; + + return argv[file_arg]; +} + +// ------------------------------------------------------------------------- // + +int main(int argc, char *argv[]) +{ +#ifdef READPE_LEGACY + legacy(argc, argv); +#endif // READPE_LEGACY + + // Print help when no arguments are given + if (argc < 2) { + help(); + } + + readpe_initialize(&g_config); + + const char *filename = parse_options(argc, argv); + int return_value = handle_file(filename); + + readpe_finalize(&g_config); + + return return_value; +} + diff --git a/src/malloc_s.c b/src/malloc_s.c index fe566a40..8e4c374e 100644 --- a/src/malloc_s.c +++ b/src/malloc_s.c @@ -2,31 +2,36 @@ #include #include -void *malloc_s(size_t size) { - if (!size) +void *malloc_s(size_t size) +{ + if (! size) { return NULL; + } void *new_mem = malloc(size); - if (!new_mem) { - fprintf(stderr, "fatal: memory exhausted (malloc of %zu bytes)\n", size); + if (! new_mem) { + fprintf(stderr, "fatal: memory exhausted (malloc of %zu bytes)\n", + size); exit(-1); } return new_mem; } -void *calloc_s( size_t nmemb, size_t size ) +void *calloc_s(size_t nmemb, size_t size) { - void *p = NULL; - - if ( size && nmemb ) - if ( ! ( p = calloc( nmemb, size ) ) ) - { - fprintf( stderr, "fatal: unable to calloc (%zu elements of %zu bytes)\n", - nmemb, size ); - exit( EXIT_FAILURE ); + void *p = NULL; + + if (size && nmemb) { + if (! (p = calloc(nmemb, size))) { + fprintf(stderr, + "fatal: unable to calloc (%zu elements of %zu bytes)\n", + nmemb, size); + exit(EXIT_FAILURE); + } } - return p; + return p; } + diff --git a/src/output.c b/src/output.c index aa932c2b..253f2bed 100644 --- a/src/output.c +++ b/src/output.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 output.c - Symbols and APIs to be used to output data in multiple formats. - Copyright (C) 2012 - 2014 pev authors + 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 @@ -35,58 +35,148 @@ */ #include "output.h" -#include "output_plugin.h" -#include "stack.h" + #include "compat/strlcat.h" #include "compat/sys/queue.h" +#include "output_plugin.h" +#include "stack.h" + #include -#include #include +#include +#include // // Global variables // -#define FORMAT_ID_FOR_TEXT 3 +const unsigned int TEXT_SPACES = 32; -static bool g_is_document_open = false; -static const format_t *g_format = NULL; -static STACK_TYPE *g_scope_stack = NULL; -static int g_argc = 0; -static char **g_argv = NULL; -static char *g_cmdline = NULL; +static bool g_is_document_open = false; +static const format_t *g_format = NULL; +static STACK_TYPE *g_scope_stack = NULL; +static int g_argc = 0; +static char **g_argv = NULL; +static char *g_cmdline = NULL; typedef struct _format_entry { const format_t *format; SLIST_ENTRY(_format_entry) entries; } format_entry_t; -static SLIST_HEAD(_format_t_list, _format_entry) g_registered_formats = SLIST_HEAD_INITIALIZER(g_registered_formats); +static SLIST_HEAD(_format_t_list, _format_entry) g_registered_formats + = SLIST_HEAD_INITIALIZER(g_registered_formats); + +// +// Internal text output +// + +static char *escape_text(const struct format *format, const char *str) +{ + return readpe_output_api_ptr()->escape(format, str); +} + +static void to_text_format(const format_t *format, const output_type_e type, + const output_scope_t *scope, const char *key, + const char *value) +{ + static int indent = 0; + + char *const escaped_key = format->escape_fn(format, key); + char *const escaped_value = format->escape_fn(format, value); + + switch (type) { + default: + break; + case OUTPUT_TYPE_SCOPE_OPEN: + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + if (key) { + printf(INDENT(indent++, "%s\n"), escaped_key); + } else { + indent++; + } + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + // putchar('\n'); + if (key) { + printf(INDENT(indent++, "%s\n"), escaped_key); + } else { + indent++; + } + break; + } + break; + case OUTPUT_TYPE_SCOPE_CLOSE: + indent--; + break; + case OUTPUT_TYPE_ATTRIBUTE: { + const size_t key_size = key ? strlen(key) : 0; + if (key && value) { + printf(INDENT(indent, "%s:%*c%s\n"), escaped_key, + (int) (TEXT_SPACES - key_size), ' ', escaped_value); + } else if (key) { + printf(INDENT(indent, "%s\n"), escaped_key); + } else if (value) { + printf(INDENT(indent, "%*c%s\n"), + (int) (TEXT_SPACES - key_size + 1), ' ', escaped_value); + } + break; + } + } + + if (escaped_key != NULL) { + free(escaped_key); + } + if (escaped_value != NULL) { + free(escaped_value); + } +} + +// ---------------------------------------------------------------------------- + +static const struct format g_text_format = {.id = 0, + .name = "text", + .output_fn = &to_text_format, + .escape_fn = &escape_text, + .entities_table = NULL}; + +// ---------------------------------------------------------------------------- // // Definition of internal functions // -static format_entry_t *_lookup_format_entry_by_id(format_id_t id) { +static format_entry_t *_lookup_format_entry_by_id(format_id_t id) +{ format_entry_t *entry; - SLIST_FOREACH(entry, &g_registered_formats, entries) { - if (entry->format->id == id) + SLIST_FOREACH (entry, &g_registered_formats, entries) { + if (entry->format->id == id) { return entry; + } } return NULL; } -static const format_t *_lookup_format_by_id(format_id_t id) { - const format_entry_t *entry = _lookup_format_entry_by_id(id); - if (entry == NULL) - return NULL; - - return entry->format; -} +// FIXME: Unused +// static const format_t *_lookup_format_by_id(format_id_t id) +// { +// const format_entry_t *entry = _lookup_format_entry_by_id(id); +// if (entry == NULL) { +// return NULL; +// } +// +// return entry->format; +// } -static void _unregister_all_formats(void) { - while (!SLIST_EMPTY(&g_registered_formats)) { +static void _unregister_all_formats(void) +{ + while (! SLIST_EMPTY(&g_registered_formats)) { format_entry_t *entry = SLIST_FIRST(&g_registered_formats); SLIST_REMOVE_HEAD(&g_registered_formats, entries); free(entry); @@ -97,10 +187,11 @@ static void _unregister_all_formats(void) { // API // -int output_plugin_register_format(const format_t *format) { +int output_plugin_register_format(const format_t *format) +{ format_entry_t *entry = calloc(1, sizeof *entry); if (entry == NULL) { - //fprintf(stderr, "output: allocation failed for format entry\n"); + // fprintf(stderr, "output: allocation failed for format entry\n"); return -1; } @@ -110,70 +201,75 @@ int output_plugin_register_format(const format_t *format) { return 0; } -void output_plugin_unregister_format(const format_t *format) { +void output_plugin_unregister_format(const format_t *format) +{ format_entry_t *entry = _lookup_format_entry_by_id(format->id); - if (entry == NULL) + if (entry == NULL) { return; + } SLIST_REMOVE(&g_registered_formats, entry, _format_entry, entries); free(entry); } -void output(const char *key, const char *value) { - output_keyval(key, value); -} +void output(const char *key, const char *value) { output_keyval(key, value); } -void output_init(void) { - g_format = _lookup_format_by_id(FORMAT_ID_FOR_TEXT); +void output_init(void) +{ + g_format = &g_text_format; g_scope_stack = STACK_ALLOC(15); - if (g_scope_stack == NULL) + if (g_scope_stack == NULL) { abort(); + } } -void output_term(void) { +void output_term(void) +{ free(g_cmdline); - g_cmdline = NULL; + g_cmdline = NULL; const uint16_t scope_depth = STACK_COUNT(g_scope_stack); if (scope_depth > 0) { - fprintf(stderr, "output: terminating the output while there are open scopes will cause memory leaks"); + fprintf(stderr, "output: terminating the output while there are open " + "scopes will cause memory leaks"); } // TODO(jweyrich): Should we loop to pop + close + output every scope? - if (g_scope_stack != NULL) + if (g_scope_stack != NULL) { STACK_DEALLOC(g_scope_stack); + } _unregister_all_formats(); } -const char *output_cmdline(void) { - return g_cmdline; -} +const char *output_cmdline(void) { return g_cmdline; } -void output_set_cmdline(int argc, char *argv[]) { +void output_set_cmdline(int argc, char *argv[]) +{ g_argc = argc; g_argv = argv; free(g_cmdline); - g_cmdline = pe_utils_str_array_join(g_argv, g_argc, ' '); + g_cmdline = pe_utils_str_array_join(g_argv, (size_t) g_argc, ' '); if (g_cmdline == NULL) { - fprintf(stderr, "output: allocation failed for pe_utils_str_array_join\n"); + fprintf(stderr, + "output: allocation failed for pe_utils_str_array_join\n"); abort(); } - //fprintf(stderr, "DEBUG: cmdline = %s\n", g_cmdline); + // fprintf(stderr, "DEBUG: cmdline = %s\n", g_cmdline); } -const format_t *output_format(void) { - return g_format; -} +const format_t *output_format(void) { return g_format; } -const format_t *output_parse_format(const char *format_name) { +const format_t *output_parse_format(const char *format_name) +{ const format_t *format = NULL; format_entry_t *entry; - SLIST_FOREACH(entry, &g_registered_formats, entries) { - // TODO(jweyrich): Should we use strcasecmp? Conforms to 4.4BSD and POSIX.1-2001, but not to C89 nor C99. + SLIST_FOREACH (entry, &g_registered_formats, entries) { + // TODO(jweyrich): Should we use strcasecmp? Conforms to 4.4BSD and + // POSIX.1-2001, but not to C89 nor C99. if (strcmp(format_name, entry->format->name) == 0) { format = entry->format; break; @@ -183,35 +279,36 @@ const format_t *output_parse_format(const char *format_name) { return format; } -void output_set_format(const format_t *format) { - g_format = format; -} +void output_set_format(const format_t *format) { g_format = format; } -int output_set_format_by_name(const char *format_name) { +int output_set_format_by_name(const char *format_name) +{ const format_t *format = output_parse_format(format_name); - if (format == NULL) + if (format == NULL) { return -1; + } output_set_format(format); return 0; } -size_t output_available_formats(char *buffer, size_t size, char separator) { +size_t output_available_formats(char *buffer, size_t size, char separator) +{ size_t total_available = 0; - size_t consumed = 0; - bool truncated = false; + size_t consumed = 0; + bool truncated = false; // FIXME: Theoretically unecessary, since a NUL char is // appended at the end of the buffer. memset(buffer, 0, size); format_entry_t *entry; - SLIST_FOREACH(entry, &g_registered_formats, entries) { - if (!truncated) { + SLIST_FOREACH (entry, &g_registered_formats, entries) { + if (! truncated) { const char *format_name = entry->format->name; - consumed = bsd_strlcat(buffer, format_name, size); + consumed = bsd_strlcat(buffer, format_name, size); if (consumed > size) { // TODO(jweyrich): Handle truncation. total_available++; @@ -234,36 +331,38 @@ size_t output_available_formats(char *buffer, size_t size, char separator) { return total_available; } -void output_open_document(void) { - output_open_document_with_name(NULL); -} +void output_open_document(void) { output_open_document_with_name(NULL); } -void output_open_document_with_name(const char *document_name) { +void output_open_document_with_name(const char *document_name) +{ assert(g_format != NULL); // Cannot open a new document while there's one already open. - assert(!g_is_document_open); + assert(! g_is_document_open); - const char *key = document_name; + const char *key = document_name; const output_scope_type_e scope_type = OUTPUT_SCOPE_TYPE_DOCUMENT; output_open_scope(key, scope_type); g_is_document_open = true; } -void output_close_document(void) { +void output_close_document(void) +{ assert(g_format != NULL); // Closing a document without first opening it is an error. assert(g_is_document_open); const output_scope_t *scope = NULL; - int ret = STACK_PEEK(g_scope_stack, (void *)&scope); + int ret = STACK_PEEK(g_scope_stack, (void *) &scope); if (ret < 0) { - fprintf(stderr, "output: cannot close a scope that has not been opened.\n"); + fprintf(stderr, + "output: cannot close a scope that has not been opened.\n"); abort(); } if (scope->type != OUTPUT_SCOPE_TYPE_DOCUMENT) { - fprintf(stderr, "output: trying to close a document, but the current scope is of a different type.\n"); + fprintf(stderr, "output: trying to close a document, but the current " + "scope is of a different type.\n"); abort(); } @@ -271,70 +370,90 @@ void output_close_document(void) { g_is_document_open = false; } -void output_open_scope(const char *scope_name, output_scope_type_e scope_type) { +void output_open_scope(const char *scope_name, output_scope_type_e scope_type) +{ assert(g_format != NULL); - const char *key = scope_name; - const char *value = NULL; - const output_type_e type = OUTPUT_TYPE_SCOPE_OPEN; - const uint16_t scope_depth = STACK_COUNT(g_scope_stack); + const char *key = scope_name; + const char *value = NULL; + const output_type_e type = OUTPUT_TYPE_SCOPE_OPEN; + const uint16_t scope_depth = STACK_COUNT(g_scope_stack); - output_scope_t * const scope = malloc(sizeof *scope); - if (scope == NULL) + output_scope_t *const scope = malloc(sizeof *scope); + if (scope == NULL) { + fprintf(stderr, "DEBUG: output_open_scope: vscope_depth=%d ABORT\n", + STACK_COUNT(g_scope_stack)); abort(); // Abort because it failed miserably! + } - scope->name = scope_name == NULL ? NULL : strdup(scope_name); - scope->type = scope_type; + scope->name = NULL; + if (scope_name != NULL) { + const size_t len = strlen(scope_name); + scope->name = malloc(len); + memcpy(scope->name, scope_name, len); + } + scope->type = scope_type; scope->depth = scope_depth + 1; if (scope_depth > 0) { - output_scope_t * parent_scope = NULL; - STACK_PEEK(g_scope_stack, (void *)&parent_scope); + output_scope_t *parent_scope = NULL; + STACK_PEEK(g_scope_stack, (void *) &parent_scope); scope->parent_type = parent_scope->type; } - //fprintf(stderr, "DEBUG: output_open_scope: scope_depth=%d\n", STACK_COUNT(g_scope_stack)); - if (g_format != NULL) + // fprintf(stderr, "DEBUG: output_open_scope: scope_depth=%d\n", + // STACK_COUNT(g_scope_stack)); + if (g_format != NULL) { g_format->output_fn(g_format, type, scope, key, value); + } - int ret = STACK_PUSH(g_scope_stack, (void *)scope); - if (ret < 0) + int ret = STACK_PUSH(g_scope_stack, (void *) scope); + if (ret < 0) { abort(); // Abort because it failed miserably! + } } -void output_close_scope(void) { +void output_close_scope(void) +{ assert(g_format != NULL); output_scope_t *scope = NULL; - int ret = STACK_POP(g_scope_stack, (void *)&scope); + int ret = STACK_POP(g_scope_stack, (void *) &scope); if (ret < 0) { - fprintf(stderr, "output: cannot close a scope that has not been opened.\n"); + fprintf(stderr, + "output: cannot close a scope that has not been opened.\n"); abort(); } - const char *key = NULL; - const char *value = NULL; - const output_type_e type = OUTPUT_TYPE_SCOPE_CLOSE; + const char *key = NULL; + const char *value = NULL; + const output_type_e type = OUTPUT_TYPE_SCOPE_CLOSE; - //fprintf(stderr, "DEBUG: output_close_scope: scope_depth=%d\n", STACK_COUNT(g_scope_stack)); - if (g_format != NULL) + // fprintf(stderr, "DEBUG: output_close_scope: scope_depth=%d\n", + // STACK_COUNT(g_scope_stack)); + if (g_format != NULL) { g_format->output_fn(g_format, type, scope, key, value); + } free(scope->name); free(scope); } -void output_keyval(const char *key, const char *value) { +void output_keyval(const char *key, const char *value) +{ assert(g_format != NULL); - const uint16_t scope_depth = STACK_COUNT(g_scope_stack); - const output_scope_t *scope = NULL; + const uint16_t scope_depth = STACK_COUNT(g_scope_stack); + const output_scope_t *scope = NULL; - if (scope_depth > 0) - STACK_PEEK(g_scope_stack, (void *)&scope); + if (scope_depth > 0) { + STACK_PEEK(g_scope_stack, (void *) &scope); + } const output_type_e type = OUTPUT_TYPE_ATTRIBUTE; - if (g_format != NULL) + if (g_format != NULL) { g_format->output_fn(g_format, type, scope, key, value); + } } + diff --git a/src/output_plugin.c b/src/output_plugin.c index 20656fc7..963137ad 100644 --- a/src/output_plugin.c +++ b/src/output_plugin.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 output_plugin.c - Symbols and APIs to be used by output plugins. - Copyright (C) 2014 pev authors + Copyright (C) 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,43 +35,35 @@ */ #include "output_plugin.h" + +#include #include #include -size_t escape_count_chars_ex(const char *str, size_t len, const entity_table_t entities) { +size_t escape_count_chars_ex(const char *str, size_t len, + const entity_table_t entities) +{ size_t result = 0; for (size_t i = 0; i < len; i++) { - const unsigned char index = (unsigned char)str[i]; - const entity_t entity = entities[index]; + const unsigned char index = (unsigned char) str[i]; + const entity_t entity = entities[index]; result += entity == NULL ? 1 : strlen(entity); } return result; } // Returns a new copy of `str` enclosed with quotes. -static char *strdup_quoted(const char *str) { - if (str == NULL) +static char *strdup_quoted(const char *str) +{ + if (str == NULL) { return NULL; - - // const size_t old_length = strlen(str); - // const size_t new_length = old_length + 2; - // - // char *new_str = malloc(new_length + 1); // Extra byte for NULL terminator - // if (new_str == NULL) - // return NULL; - // - // new_str[0] = '"'; - // new_str[new_length - 1] = '"'; - // new_str[new_length] = '\0'; - // - // memcpy(new_str + 1, str, old_length); - // - // return new_str; + } char *new_str; - if ( asprintf( &new_str, "\"%s\"", str ) < 0 ) - return NULL; + if (asprintf(&new_str, "\"%s\"", str) < 0) { + return NULL; + } return new_str; } @@ -82,32 +74,38 @@ static size_t escape_count_chars(const format_t *format, const char *str, size_t } #endif -char *escape_ex_quoted(const char *str, const entity_table_t entities) { - if (str == NULL) +char *escape_ex_quoted(const char *str, const entity_table_t entities) +{ + if (str == NULL) { return NULL; + } - if (str[0] == '\0') + if (str[0] == '\0') { return strdup("\"\""); + } - if (entities == NULL) + if (entities == NULL) { return strdup_quoted(str); + } const size_t old_length = strlen(str); - const size_t new_length = escape_count_chars_ex(str, old_length, entities) + 2; // Extra bytes for quotes + const size_t new_length = escape_count_chars_ex(str, old_length, entities) + + 2; // Extra bytes for quotes char *new_str = malloc(new_length + 1); // Extra byte for NULL terminator - if (new_str == NULL) + if (new_str == NULL) { abort(); + } - new_str[0] = '"'; + new_str[0] = '"'; new_str[new_length - 1] = '"'; - new_str[new_length] = '\0'; + new_str[new_length] = '\0'; // `consumed` starts in 1 because of the initial quote. - size_t consumed = 1; + size_t consumed = 1; for (size_t i = 0; i < old_length; i++) { - const unsigned char index = (unsigned char)str[i]; - const entity_t entity = entities[index]; + const unsigned char index = (unsigned char) str[i]; + const entity_t entity = entities[index]; if (entity == NULL) { new_str[consumed++] = str[i]; } else { @@ -120,29 +118,34 @@ char *escape_ex_quoted(const char *str, const entity_table_t entities) { return new_str; } -char *escape_ex(const char *str, const entity_table_t entities) { - if (str == NULL) +char *escape_ex(const char *str, const entity_table_t entities) +{ + if (str == NULL) { return NULL; + } - if (str[0] == '\0') + if (str[0] == '\0') { return strdup(""); + } - if (entities == NULL) + if (entities == NULL) { return strdup(str); + } const size_t old_length = strlen(str); const size_t new_length = escape_count_chars_ex(str, old_length, entities); char *new_str = malloc(new_length + 1); // Extra byte for NULL terminator - if (new_str == NULL) + if (new_str == NULL) { abort(); + } new_str[new_length] = '\0'; - size_t consumed = 0; + size_t consumed = 0; for (size_t i = 0; i < old_length; i++) { - const unsigned char index = (unsigned char)str[i]; - const entity_t entity = entities[index]; + const unsigned char index = (unsigned char) str[i]; + const entity_t entity = entities[index]; if (entity == NULL) { new_str[consumed++] = str[i]; } else { @@ -155,28 +158,31 @@ char *escape_ex(const char *str, const entity_table_t entities) { return new_str; } -char *escape(const format_t *format, const char *str) { +char *escape(const format_t *format, const char *str) +{ return escape_ex(str, format->entities_table); } -char *escape_quoted(const format_t *format, const char *str) { +char *escape_quoted(const format_t *format, const char *str) +{ return escape_ex_quoted(str, format->entities_table); } // These 2 are implemented in `output.c`. -extern int output_plugin_register_format(const format_t *format); +extern int output_plugin_register_format(const format_t *format); extern void output_plugin_unregister_format(const format_t *format); -output_plugin_api_t *output_plugin_api_ptr(void) { - static output_plugin_api_t api = { - .output_cmdline = output_cmdline, - .output_plugin_register_format = output_plugin_register_format, - .output_plugin_unregister_format = output_plugin_unregister_format, - .escape_count_chars_ex = escape_count_chars_ex, - .escape_ex = escape_ex, - .escape_ex_quoted = escape_ex_quoted, - .escape = escape, - .escape_quoted = escape_quoted - }; +struct readpe_output_api *readpe_output_api_ptr(void) +{ + static struct readpe_output_api api + = {.cmdline = output_cmdline, + .register_format = output_plugin_register_format, + .unregister_format = output_plugin_unregister_format, + .escape_count_chars_ex = escape_count_chars_ex, + .escape_ex = escape_ex, + .escape_ex_quoted = escape_ex_quoted, + .escape = escape, + .escape_quoted = escape_quoted}; return &api; } + diff --git a/src/output_text.c b/src/output_text.c new file mode 100644 index 00000000..b931cb0e --- /dev/null +++ b/src/output_text.c @@ -0,0 +1,164 @@ +/* + readpe - the PE file analyzer toolkit + + text.c - Principal implementation file for the TEXT output plugin + + Copyright (C) 2012 - 2014 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 "output_plugin.h" +#include "plugin.h" + +#include +#include +#include + +#define FORMAT_ID 3 +#define FORMAT_NAME "text" + +#define PLUGIN_TYPE "output" +#define PLUGIN_NAME FORMAT_NAME + +#define SPACES 32 // spaces # for text-based output + +static const struct readpe_api *g_readpe_api = NULL; + +// ------------------------------------------------------------------------- // + +static char *escape_text(const format_t *format, const char *str) +{ + return g_readpe_api->output->escape(format, str); +} + +static void to_format(const format_t *format, const output_type_e type, + const output_scope_t *scope, const char *key, + const char *value) +{ + static int indent = 0; + + char *const escaped_key = format->escape_fn(format, key); + char *const escaped_value = format->escape_fn(format, value); + + switch (type) { + default: + break; + case OUTPUT_TYPE_SCOPE_OPEN: + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + if (key) { + printf(INDENT(indent++, "%s\n"), escaped_key); + } else { + indent++; + } + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + // putchar('\n'); + if (key) { + printf(INDENT(indent++, "%s\n"), escaped_key); + } else { + indent++; + } + break; + } + break; + case OUTPUT_TYPE_SCOPE_CLOSE: + indent--; + break; + case OUTPUT_TYPE_ATTRIBUTE: { + const size_t key_size = key ? strlen(key) : 0; + if (key && value) { + printf(INDENT(indent, "%s:%*c%s\n"), escaped_key, + (int) (SPACES - key_size), ' ', escaped_value); + } else if (key) { + printf(INDENT(indent, "%s\n"), escaped_key); + } else if (value) { + printf(INDENT(indent, "%*c%s\n"), (int) (SPACES - key_size + 1), + ' ', escaped_value); + } + break; + } + } + + if (escaped_key != NULL) { + free(escaped_key); + } + if (escaped_value != NULL) { + free(escaped_value); + } +} + +// ---------------------------------------------------------------------------- + +static const format_t g_format + = {FORMAT_ID, FORMAT_NAME, &to_format, &escape_text, NULL}; + +// ------------------------------------------------------------------------- // + +static int plugin_loaded(void) +{ + // printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); + return 0; +} + +static void plugin_unloaded(void) +{ + // printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); +} + +static int plugin_initialize(const struct readpe_api *api) +{ + g_readpe_api = api; + int ret = g_readpe_api->output->register_format(&g_format); + if (ret < 0) { + return -1; + } + return 0; +} + +static void plugin_shutdown(void) +{ + g_readpe_api->output->unregister_format(&g_format); +} + +// ------------------------------------------------------------------------- // + +struct readpe_output_plugin readpe_plugin = { + {.type_id = readpe_plugin_type_output, + .loaded = plugin_loaded, + .initialize = plugin_initialize, + .shutdown = plugin_shutdown, + .unloaded = plugin_unloaded}, + g_format +}; + diff --git a/src/pedis.c b/src/pedis.c deleted file mode 100644 index 2f50621d..00000000 --- a/src/pedis.c +++ /dev/null @@ -1,432 +0,0 @@ -/* vim :set ts=4 sw=4 sts=4 et : */ -/* - pev - the PE file analyzer toolkit - - pedis.c - PE disassembler - - Copyright (C) 2012 - 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. -*/ - -#include "common.h" -#include "../lib/libudis86/udis86.h" -#include -#include -#include "plugins.h" - -#define PROGRAM "pedis" - -#define SPACES 32 // spaces # for text-based output - -#define SYN_ATT 1 -#define SYN_INTEL 0 - -typedef struct { - bool all_sections; - char *section; - bool syntax; - uint64_t offset; - uint64_t nbytes; // limit the number of bytes instructions. 0 means no limit. - uint64_t ninstructions; // limit the number of disassembled instructions. 0 means no limit. - bool entrypoint; - bool offset_is_rva; - uint16_t mode; -} options_t; - -static void usage(void) -{ - static char formats[255]; - output_available_formats(formats, sizeof(formats), '|'); - printf("Usage: %s OPTIONS FILE\n" - "Disassemble PE sections and functions (by default, until found a RET or LEAVE instruction)\n" - "\nExample: %s -r 0x4c4df putty.exe\n" - "\nOptions:\n" - " --att Set AT&T assembly syntax (default: Intel).\n" - " -e, --entrypoint Disassemble the entire entrypoint function.\n" - " -f, --format <%s> Change output format (default: text).\n" - " -m, --mode <16|32|64> Disassembly mode (default: auto).\n" - " -i Number of instructions to disassemble.\n" - " -n Number of bytes to disassemble\n" - " -o, --offset Disassemble at specified offset, either in decimal or hexadecimal format (prefixed with 0x).\n" - " -r, --rva Disassemble at specified RVA, either in decimal or hexadecimal format (prefixed with 0x).\n" - " -s, --section Disassemble en entire section given.\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM, formats); -} - -static void free_options(options_t *options) -{ - if (options) - free(options->section); - - free(options); -} - -static options_t *parse_options(int argc, char *argv[]) -{ - options_t *options = calloc_s(1, sizeof(options_t)); - - /* Parameters for getopt_long() function */ - static const char short_options[] = "em:i:n:o:r:s:f:V"; - - static const struct option long_options[] = { - { "help", no_argument, NULL, 1 }, - { "att", no_argument, NULL, 2 }, - { "", required_argument, NULL, 'n' }, - { "entrypoint", no_argument, NULL, 'e' }, - { "mode", required_argument, NULL, 'm' }, - { "offset", required_argument, NULL, 'o' }, - { "rva", required_argument, NULL, 'r' }, - { "section", required_argument, NULL, 's' }, - { "format", required_argument, NULL, 'f' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - options->syntax = SYN_INTEL; - - int c, ind; - - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) - break; - - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 2: - options->syntax = SYN_ATT; - break; - case 'e': - options->entrypoint = true; - break; - case 'm': - options->mode = strtol(optarg, NULL, 10); - switch (options->mode) { - default: EXIT_ERROR("Bad argument for mode."); - case 16: break; - case 32: break; - case 64: break; - } - break; - case 'i': - // FIX: errno is not zeroed automatically if already set. - errno = 0; - options->ninstructions = strtol(optarg, NULL, 0); - if (errno == ERANGE) - EXIT_ERROR("number of instructions value would underflow or overflow"); - break; - case 'n': - // FIX: errno is not zeroed automatically if already set. - errno = 0; - options->nbytes = strtol(optarg, NULL, 0); - if (errno == ERANGE) - EXIT_ERROR("number of bytes value would underflow or overflow"); - break; - case 'o': - // FIX: errno is not zeroed automatically if already set. - errno = 0; - options->offset = strtol(optarg, NULL, 0); - if (errno == ERANGE) - EXIT_ERROR("offset value would underflow or overflow"); - options->offset_is_rva = false; - break; - case 'r': - // FIX: errno is not zeroed automatically if already set. - errno = 0; - options->offset = strtol(optarg, NULL, 0); - if (errno == ERANGE) - EXIT_ERROR("rva value would underflow or overflow"); - options->offset_is_rva = true; - break; - case 's': - options->section = strdup(optarg); - break; - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - case 'f': - if (output_set_format_by_name(optarg) < 0) - EXIT_ERROR("invalid format option"); - break; - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); - } - } - - return options; -} - -static char *insert_spaces(const char *s) -{ - size_t size /*, wsize */; - char *new; - - size = strlen(s); - - if (!size) - return NULL; - - // wsize = size / 2; - size = size + (size/2); - - // FIXME: Maybe a better approach to the loop below is: - // new = malloc( size + 1 ); - // - // short *p = (short *)str; - // short *q = (short *)new; - // while ( wsize-- ) - // { - // *q++ = *p++; - // *(char *)q++ = ' '; - // } - // *(char *)q = 0; - // return new; - - new = calloc_s(1, size+1); - - for (unsigned int i=0, j=0, pos=0; i < size; i++) - { - if (pos==2) - { - new[i] = ' '; - pos=0; - } - else - { - new[i] = s[j++]; - pos++; - } - } - - return new; -} - -static bool is_ret_instruction(unsigned char opcode) -{ - switch (opcode) - { - case 0xc9: // leave (this isn't a ret instruction!). - //case 0xc2: // ret imm16 (why not?) - case 0xc3: // ret - case 0xca: // retf imm16 - //case 0xcb: // retf (why not?) - return true; - - default: - return false; - } -} - -static void disassemble_offset(pe_ctx_t *ctx, const options_t *options, ud_t *ud_obj, uint64_t offset) -{ - if (ctx == NULL || offset == 0) - return; - - uint64_t instr_counter = 0; // counter for disassembled instructions - uint64_t byte_counter = 0; // counter for disassembled bytes - - while (ud_disassemble(ud_obj)) - { - char ofs[MAX_MSG], value[MAX_MSG], *bytes; - const uint8_t *opcode = ud_insn_ptr(ud_obj); - - instr_counter++; // increment instruction counter - byte_counter += ud_insn_len(ud_obj); - - if (options->nbytes && byte_counter >= options->nbytes) - return; - - const ud_mnemonic_code_t mnic = ud_insn_mnemonic(ud_obj); - const ud_operand_t *operand = ud_insn_opr(ud_obj, 0); - const ud_type_t op_type = operand != NULL ? operand->type : 0; - - snprintf(ofs, MAX_MSG, "%"PRIx64, (options->offset_is_rva ? ctx->pe.imagebase : 0) + offset + ud_insn_off(ud_obj)); - bytes = insert_spaces(ud_insn_hex(ud_obj)); - - if (!bytes) - return; - - // correct near operand addresses for calls and jumps - if (op_type && (op_type != UD_OP_MEM) && (mnic == UD_Icall || (mnic >= UD_Ijo && mnic <= UD_Ijmp))) - { - char *instr_asm = strdup(ud_insn_asm(ud_obj)); - char *instr = strtok(instr_asm, "0x"); - - snprintf(value, - MAX_MSG, - "%s%*c%s%#"PRIx64, - bytes, - SPACES - (int) strlen(bytes), - ' ', - instr ? instr : "", - ctx->pe.imagebase + offset + ud_insn_off(ud_obj) + ud_obj->operand[0].lval.sdword + ud_insn_len(ud_obj) - ); - free(instr_asm); - } - else - snprintf(value, MAX_MSG, "%s%*c%s", bytes, SPACES - (int) strlen(bytes), ' ', ud_insn_asm(ud_obj)); - - free(bytes); - output(ofs, value); - - // for sections, we stop at end of section - if (options->section && instr_counter >= options->ninstructions) - break; - else if (instr_counter >= options->ninstructions && options->ninstructions) - break; - else if (options->entrypoint) - { - // search for LEAVE or RET insrtuctions - for (unsigned int i=0; i < ud_insn_len(ud_obj); i++) - if (is_ret_instruction(opcode[i])) - return; - } - } -} - -int main(int argc, char *argv[]) -{ - pev_config_t config; - PEV_INITIALIZE(&config); - - if (argc < 2) { - usage(); - exit(EXIT_FAILURE); - } - - output_set_cmdline(argc, argv); - - options_t *options = parse_options(argc, argv); // opcoes - - const char *path = argv[argc-1]; - pe_ctx_t ctx; - - pe_err_e err = pe_load_file(&ctx, path); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - err = pe_parse(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - if (!pe_is_pe(&ctx)) - EXIT_ERROR("not a valid PE file"); - - IMAGE_OPTIONAL_HEADER *optional = pe_optional(&ctx); - if (optional == NULL) - return EXIT_FAILURE; - - uint8_t mode_bits = 0; - switch (optional->type) { - default: - EXIT_ERROR("Unsupported architecture."); - return EXIT_FAILURE; - case MAGIC_PE32_0: mode_bits = 32; break; - case MAGIC_PE32: mode_bits = 32; break; - case MAGIC_PE64: mode_bits = 64; break; - } - - // FIX: Only those are supported here. - IMAGE_COFF_HEADER *coff = pe_coff(&ctx); - switch ( coff->Machine ) - { - case IMAGE_FILE_MACHINE_AMD64: - case IMAGE_FILE_MACHINE_I386: - break; - default: - EXIT_ERROR("Unsupported machine (AMD64 or I386)." ); - return EXIT_FAILURE; - } - - ud_t ud_obj; // libudis86 object - ud_init(&ud_obj); - - // set disassembly mode according with PE architecture - ud_set_mode(&ud_obj, options->mode ? options->mode : mode_bits); - - uint64_t offset = 0; // offset to start disassembly - - if (options->entrypoint) - offset = pe_rva2ofs(&ctx, ctx.pe.entrypoint); - else if (options->offset) - offset = options->offset_is_rva ? pe_rva2ofs(&ctx, options->offset) : options->offset; - else if (options->section) { - IMAGE_SECTION_HEADER *section = pe_section_by_name(&ctx, options->section); - - if (section) { // section found - offset = section->PointerToRawData; - if (!options->ninstructions) - options->ninstructions = section->SizeOfRawData; - } - else - EXIT_ERROR("invalid section name"); - } else { - usage(); - return EXIT_FAILURE; - } - - if (!offset) { - fprintf(stderr, "unable to reach file offset (%#"PRIx64")\n", offset); - return EXIT_FAILURE; - } - - output_open_document(); - - ud_set_syntax(&ud_obj, options->syntax ? UD_SYN_ATT : UD_SYN_INTEL); - ud_set_input_buffer(&ud_obj, ctx.map_addr, pe_filesize(&ctx)); - //ud_set_input_file(&ud_obj, ctx.stream); - ud_input_skip(&ud_obj, offset); - disassemble_offset(&ctx, options, &ud_obj, offset); - - output_close_document(); - - // libera a memoria - free_options(options); - - // free - err = pe_unload(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - PEV_FINALIZE(&config); - - return EXIT_SUCCESS; -} diff --git a/src/pehash.c b/src/pehash.c deleted file mode 100644 index b6a4e814..00000000 --- a/src/pehash.c +++ /dev/null @@ -1,417 +0,0 @@ -/* vim :set ts=4 sw=4 sts=4 et : */ -/* - pev - the PE file analyzer toolkit - - pehash.c - calculate hashes of PE pieces - - Copyright (C) 2012 - 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. -*/ - -#include "common.h" -#include "plugins.h" - -#define PROGRAM "pehash" - -unsigned pefile_warn = 0; - -typedef struct { - bool all; - bool content; - struct { - bool all; - bool dos; - bool coff; - bool optional; - } headers; - struct { - char *name; - uint16_t index; - } sections; -} options_t; - -static void usage(void) -{ - static char formats[255]; - output_available_formats(formats, sizeof(formats), '|'); - printf("Usage: %s OPTIONS FILE\n" - "Calculate hashes of PE pieces\n" - "\nExample: %s -s '.text' winzip.exe\n" - "\nOptions:\n" - " -f, --format <%s> Change output format (default: text).\n" - " -a, --all Hash file, sections and headers with md5, sha1, sha256, ssdeep and imphash.\n" - " -c, --content Hash only the file content (default).\n" - " -h, --header Hash only the header with the specified name.\n" - " -s, --section Hash only the section with the specified name.\n" - " --section-index Hash only the section at the specified index (1..n).\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM, formats); -} - -static void parse_header_name(options_t *options, const char *optarg) -{ - if (strcmp(optarg, "dos") == 0) - options->headers.dos = true; - else if (strcmp(optarg, "coff") == 0) - options->headers.coff = true; - else if (strcmp(optarg, "optional") == 0) - options->headers.optional = true; - else - EXIT_ERROR("invalid header name option"); -} - -static void free_options(options_t *options) -{ - if (options) - free(options->sections.name); - - free(options); -} - -static options_t *parse_options(int argc, char *argv[]) -{ - options_t *options = calloc_s(1, sizeof(options_t)); - - // parameters for getopt_long() function - static const char short_options[] = "f:a:c:h:s:V"; - - static const struct option long_options[] = { - { "help", no_argument, NULL, 1 }, - { "format", required_argument, NULL, 'f' }, - { "all", no_argument, NULL, 'a' }, - { "content", no_argument, NULL, 'c' }, - { "header", required_argument, NULL, 'h' }, - { "section-name", required_argument, NULL, 's' }, - { "section-index", required_argument, NULL, 2 }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - // Setting the default option - options->content = true; - - int c, ind; - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) - break; - - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'f': - if (output_set_format_by_name(optarg) < 0) - EXIT_ERROR("invalid format option"); - break; - case 'a': - options->all = true; - break; - case 'c': // default - options->all = false; //TODO remover? - options->content = true; - break; - case 's': - options->all = false; - options->headers.all = false; - // TODO: How do we need to handle non-ascii names? - options->sections.name = strdup(optarg); - break; - case 2: - options->all = false; - options->headers.all = false; - options->sections.index = strtol(optarg, NULL, 10); - if (options->sections.index < 1 || options->sections.index > MAX_SECTIONS) { - EXIT_ERROR("Bad argument for section-index,"); - } - break; - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - case 'h': - options->all = false; - options->headers.all = false; - parse_header_name(options, optarg); - break; - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); - } - } - - // TODO: Warn about simultaneous usage of -h, -s, and --section-index. - - return options; -} - -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", "ssdeep" }; - 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); -} - -int main(int argc, char *argv[]) -{ - pev_config_t config; - PEV_INITIALIZE(&config); - - if (argc < 2) { - usage(); - return EXIT_FAILURE; - } - - output_set_cmdline(argc, argv); - - options_t *options = parse_options(argc, argv); - - pe_ctx_t ctx; - - pe_err_e err = pe_load_file(&ctx, argv[argc-1]); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - err = pe_parse(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - if (!pe_is_pe(&ctx)) - EXIT_ERROR("not a valid PE file"); - - const IMAGE_SECTION_HEADER *section_ptr = NULL; - const unsigned char *data = NULL; - uint64_t data_size = 0; - - unsigned c = pe_sections_count(&ctx); - IMAGE_SECTION_HEADER ** const sections = pe_sections(&ctx); - - data = ctx.map_addr; - data_size = pe_filesize(&ctx); - - output_open_document(); - - if (options->headers.all || options->headers.dos || options->headers.coff || options->headers.optional || - options->sections.name || options->sections.index) { - options->all = false; - options->content = false; - } - - if (options->all) { - options->content = true; - options->headers.all = true; - } - - if (options->content) { - 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 - if (!options->all) // whole file content only - goto BYE; - } - - if (options->headers.all) { - options->headers.dos = true; - options->headers.coff = true; - options->headers.optional = true; - } - - if (options->headers.all || options->headers.dos || options->headers.coff || options->headers.optional) - output_open_scope("headers", OUTPUT_SCOPE_TYPE_ARRAY); - - if (options->headers.all || options->headers.dos) { - const IMAGE_DOS_HEADER *dos_hdr = pe_dos(&ctx); - data = (const unsigned char *)dos_hdr; - data_size = sizeof(IMAGE_DOS_HEADER); - - output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT); - output("header_name", "IMAGE_DOS_HEADER"); - print_basic_hash(data, data_size); - output_close_scope(); // header - } - - if (options->headers.all || options->headers.coff) { - const IMAGE_COFF_HEADER *coff_hdr = pe_coff(&ctx); - data = (const unsigned char *)coff_hdr; - data_size = sizeof(IMAGE_COFF_HEADER); - - output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT); - output("header_name", "IMAGE_COFF_HEADER"); - print_basic_hash(data, data_size); - output_close_scope(); // header - } - - if (options->headers.all || options->headers.optional) { - 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_0: - 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; - } - - output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT); - output("header_name", "IMAGE_OPTIONAL_HEADER"); - print_basic_hash(data, data_size); - output_close_scope(); // header - } - - if (options->headers.all || options->headers.dos || options->headers.coff || options->headers.optional) - output_close_scope(); // headers - - if (options->all || options->sections.name || options->sections.index) - output_open_scope("sections", OUTPUT_SCOPE_TYPE_ARRAY); - - if (options->all) { - for (unsigned int i=0; iSizeOfRawData; - 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 - } - } - //output_close_scope(); // sections - } else if (options->sections.name != NULL) { - const IMAGE_SECTION_HEADER *section = pe_section_by_name(&ctx, options->sections.name); - if (section == NULL) { - EXIT_ERROR("The requested section could not be found on this binary"); - } - section_ptr = section; - } else if (options->sections.index > 0) { - const uint16_t num_sections = pe_sections_count(&ctx); - if (num_sections == 0 || options->sections.index > num_sections) { - EXIT_ERROR("The requested section could not be found on this binary"); - } - IMAGE_SECTION_HEADER ** const sections = pe_sections(&ctx); - const IMAGE_SECTION_HEADER *section = sections[options->sections.index - 1]; - section_ptr = section; - } - - 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; - } - } - - if (!options->all && data != NULL) { - output_open_scope("section", OUTPUT_SCOPE_TYPE_OBJECT); - output("section_name", options->sections.name); - print_basic_hash(data, data_size); - output_close_scope(); - } - - if (options->all || options->sections.name || options->sections.index) - output_close_scope(); - -BYE: - output_close_document(); - - // free - free_options(options); - - err = pe_unload(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - PEV_FINALIZE(&config); - return EXIT_SUCCESS; -} diff --git a/src/peres.c b/src/peres.c deleted file mode 100644 index 58bc0c57..00000000 --- a/src/peres.c +++ /dev/null @@ -1,730 +0,0 @@ -/* vim :set ts=4 sw=4 sts=4 et : */ -/* - pev - the PE file analyzer toolkit - - peres.c - retrive informations and binary data of resources - - Copyright (C) 2012 - 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. -*/ - -#include "common.h" -#include -#include -#include -#include -#include -#include - -#define PROGRAM "peres" - -const char *g_resourceDir = "resources"; - -typedef struct { - bool all; - bool extract; - bool namedExtract; - bool info; - bool statistics; - bool list; - bool version; - bool help; -} options_t; - -static void usage(void) -{ - static char formats[255]; - output_available_formats(formats, sizeof(formats), '|'); - printf("Usage: %s OPTIONS FILE\n" - "Show information about resource section and extract it\n" - "\nExample: %s -a putty.exe\n" - "\nOptions:\n" - " -a, --all Show all information, statistics and extract resources\n" - " -f, --format <%s> Change output format (default: text)\n" - " -i, --info Show resources information\n" - " -l, --list Show list view\n" - " -s, --statistics Show resources statistics\n" - " -x, --extract Extract resources\n" - " -X, --named-extract Extract resources with path names\n" - " -v, --file-version Show File Version from PE resource directory\n" - " -V, --version Show version and exit\n" - " --help Show this help and exit\n", - PROGRAM, PROGRAM, formats); -} - -static void free_options(options_t *options) -{ - // FIX: Don't need to test for NULL pointer. - //if (options == NULL) - // return; - - free(options); -} - -static options_t *parse_options(int argc, char *argv[]) -{ - options_t *options = calloc_s(1, sizeof *options); - - /* Parameters for getopt_long() function */ - static const char short_options[] = "a:f:ilsxXvV"; - - static const struct option long_options[] = { - { "all", required_argument, NULL, 'a' }, - { "format", required_argument, NULL, 'f' }, - { "info", no_argument, NULL, 'i' }, - { "list", no_argument, NULL, 'l' }, - { "statistics", no_argument, NULL, 's' }, - { "extract", no_argument, NULL, 'x' }, - { "named-extract", no_argument, NULL, 'X' }, - { "file-version", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 1 }, - { NULL, 0, NULL, 0 } - }; - - int c, ind; - - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) - break; - - switch (c) - { - case 'a': - options->all = true; - break; - case 'f': - if (output_set_format_by_name(optarg) < 0) - EXIT_ERROR("invalid format option"); - break; - case 'i': - options->info = true; - break; - case 'l': - options->list = true; - break; - case 's': - options->statistics = true; - break; - case 'x': - options->extract = true; - break; - case 'X': - options->extract = true; - options->namedExtract = true; - break; - case 'v': - options->version = true; - break; - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); - } - } - - return options; -} - -static void peres_show_node(pe_ctx_t *ctx, const pe_resource_node_t *node) -{ - char value[MAX_MSG]; - - switch (node->type) - { - default: - LIBPE_WARNING("Invalid node type"); - break; - case LIBPE_RDT_RESOURCE_DIRECTORY: - { - const IMAGE_RESOURCE_DIRECTORY * const resourceDirectory = node->raw.resourceDirectory; - - snprintf(value, MAX_MSG, "Resource Directory / %d", node->dirLevel); - output("\nNode Type / Level", value); - - snprintf(value, MAX_MSG, "%d", resourceDirectory->Characteristics); - output("Characteristics", value); - - snprintf(value, MAX_MSG, "%d", resourceDirectory->TimeDateStamp); - output("Timestamp", value); - - snprintf(value, MAX_MSG, "%d", resourceDirectory->MajorVersion); - output("Major Version", value); - - snprintf(value, MAX_MSG, "%d", resourceDirectory->MinorVersion); - output("Minor Version", value); - - snprintf(value, MAX_MSG, "%d", resourceDirectory->NumberOfNamedEntries); - output("Named entries", value); - - snprintf(value, MAX_MSG, "%d", resourceDirectory->NumberOfIdEntries); - output("Id entries", value); - break; - } - case LIBPE_RDT_DIRECTORY_ENTRY: - { - const IMAGE_RESOURCE_DIRECTORY_ENTRY * const directoryEntry = node->raw.directoryEntry; - - snprintf(value, MAX_MSG, "Directory Entry / %d", node->dirLevel); - output("\nNode Type / Level", value); - - snprintf(value, MAX_MSG, "%d", directoryEntry->u0.data.NameOffset); - output("Name offset", value); - - snprintf(value, MAX_MSG, "%d", directoryEntry->u0.data.NameIsString); - output("Name is string", value); - - snprintf(value, MAX_MSG, "%x", directoryEntry->u1.data.OffsetToDirectory); - output("Offset to directory", value); - - snprintf(value, MAX_MSG, "%d", directoryEntry->u1.data.DataIsDirectory); - output("Data is directory", value); - break; - } - case LIBPE_RDT_DATA_STRING: - { - const IMAGE_RESOURCE_DATA_STRING_U * const dataString = node->raw.dataString; - - snprintf(value, MAX_MSG, "Data String / %d", node->dirLevel); - output("\nNode Type / Level", value); - - snprintf(value, MAX_MSG, "%d", dataString->Length); - output("String len", value); - - char ascii_string[MAX_MSG]; - - // FIXME: dataString->Length + 1 is right?! - pe_utils_str_widechar2ascii(ascii_string, sizeof ascii_string, - (const char *)dataString->String, dataString->Length + 1); - - snprintf(value, MAX_MSG, "%s", ascii_string); - output("String", value); - break; - } - case LIBPE_RDT_DATA_ENTRY: - { - const IMAGE_RESOURCE_DATA_ENTRY * const dataEntry = node->raw.dataEntry; - - snprintf(value, MAX_MSG, "Data Entry / %d", node->dirLevel); - output("\nNode Type / Level", value); - - snprintf(value, MAX_MSG, "%x", dataEntry->OffsetToData); - output("OffsetToData", value); - - snprintf(value, MAX_MSG, "%d", dataEntry->Size); - output("Size", value); - - snprintf(value, MAX_MSG, "%d", dataEntry->CodePage); - output("CodePage", value); - - snprintf(value, MAX_MSG, "%d", dataEntry->Reserved); - output("Reserved", value); - break; - } - } -} - -static void peres_show_nodes(pe_ctx_t *ctx, const pe_resource_node_t *node) -{ - if (node == NULL) - return; - - peres_show_node(ctx, node); - - peres_show_nodes(ctx, node->childNode); - peres_show_nodes(ctx, node->nextNode); -} - -static void peres_build_node_filename(pe_ctx_t *ctx, char *output, size_t output_size, const pe_resource_node_t *node) -{ - UNUSED(ctx); - char partial_path[MAX_PATH]; - - for (pe_resource_level_e level = LIBPE_RDT_LEVEL1; level <= node->dirLevel; level++) { - const pe_resource_node_t *dir_entry_node = pe_resource_find_parent_node_by_type_and_level(node, LIBPE_RDT_DIRECTORY_ENTRY, level); - if (dir_entry_node->raw.directoryEntry->u0.data.NameIsString) { - snprintf(partial_path, sizeof(partial_path), "%s ", dir_entry_node->name); - } else { - const pe_resource_entry_info_t *match = pe_resource_entry_info_lookup(dir_entry_node->raw.directoryEntry->u0.data.NameOffset); - if (match != NULL && level == LIBPE_RDT_LEVEL1) { - snprintf(partial_path, sizeof(partial_path), "%s ", match->name); - } else { - snprintf(partial_path, sizeof(partial_path), "%04x ", dir_entry_node->raw.directoryEntry->u0.data.NameOffset); - } - } - - strncat(output, partial_path, output_size - strlen(output) - 1); - } - - size_t length = strlen(output); - output[length - 1] = '\0'; // Remove the last whitespace. -} - -static void peres_show_list_node(pe_ctx_t *ctx, const pe_resource_node_t *node) -{ - if (node->type != LIBPE_RDT_DATA_ENTRY) - return; - - char node_info[MAX_PATH]; - memset(node_info, 0, sizeof(node_info)); - peres_build_node_filename(ctx, node_info, sizeof(node_info), node); - printf("%s (%d bytes)\n", node_info, node->raw.dataEntry->Size); -} - -static void peres_show_list(pe_ctx_t *ctx, const pe_resource_node_t *node) -{ - if (node == NULL) - return; - - peres_show_list_node(ctx, node); - - peres_show_list(ctx, node->childNode); - peres_show_list(ctx, node->nextNode); -} - -#pragma pack(push, 1) -typedef struct { - uint32_t biSize; - int32_t biWidth; - int32_t biHeight; - uint16_t biPlanes; - uint16_t biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - int32_t biXPelsPerMeter; - int32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; -} BITMAPINFOHEADER; -#pragma pack(pop) - -#pragma pack(push, 2) -typedef struct { - uint16_t icReserved; // Always zero - uint16_t icType; // 1 for .ico, 2 for .cur, other values are invalid - uint16_t icImageCount; // number of images in the file -} ICOFILEHEADER; - -typedef struct { - uint8_t biWidth; // Width of image - uint8_t biHeight; // Height of image - uint8_t biClrUsed; // Number of colors used - uint8_t biReserved; // Reserved - union { - uint16_t biPlanes; // ICO - Number of color planes. Should be 0 or 1 - uint16_t biXHotspot; // CUR - Horizontal coord of the hotspot in number of pixels from the left - } u0; - union { - uint16_t biBitCount; // ICO - Number of bits per pixel - uint16_t biYHotspot; // CUR - Vertical coord of the hotspot in number of pixels from the top - } u1; - uint32_t biSizeImage; // Size of image data in bytes - uint32_t biOffBits; // Offset of BMP or PNG data from the beggining of the ICO/CUR file -} ICODIRENTRY; -#pragma pack(pop) - -typedef struct { - bool is_modified; - uint8_t *restore_buffer; - size_t restore_size; -} peres_resource_restore_t; - -static void peres_restore_resource_icon(peres_resource_restore_t *restore, const pe_resource_entry_info_t *entry_info, void *raw_data_ptr, size_t raw_data_size) -{ - if (memcmp(raw_data_ptr, "\x89PNG", 4) == 0) { - // A PNG icon is stored along with its original header, so just return untouched. - return; - } - - const BITMAPINFOHEADER *bitmap = raw_data_ptr; - - // Is it valid? - if (bitmap->biSize != 40) { - LIBPE_WARNING("RT_ICON bitmap is not valid"); - return; - } - - ICOFILEHEADER fileheader = { - .icReserved = 0, - .icType = 1, - .icImageCount = 1, - }; - ICODIRENTRY direntry = { - .biWidth = bitmap->biWidth, - .biHeight = bitmap->biHeight / ((entry_info->type == RT_ICON || entry_info->type == RT_CURSOR || entry_info->type == RT_BITMAP) ? 2 : 1), - .biClrUsed = 0, // What should we put here? - .biReserved = 0, - .u0 = { .biPlanes = 1 }, - .u1 = { .biBitCount = bitmap->biBitCount }, - .biSizeImage = bitmap->biSizeImage, - .biOffBits = sizeof(ICOFILEHEADER) + sizeof(ICODIRENTRY) - }; - - size_t written = 0; - uint8_t *buffer = malloc_s(sizeof(ICOFILEHEADER) + sizeof(ICODIRENTRY) + raw_data_size); - -#define PERES_APPEND(dst, src, size) memcpy(dst + written, src, size); written += size - PERES_APPEND(buffer, &fileheader, sizeof(ICOFILEHEADER)); - PERES_APPEND(buffer, &direntry, sizeof(ICODIRENTRY)); - PERES_APPEND(buffer, raw_data_ptr, raw_data_size); -#undef PERES_APPEND - - restore->is_modified = true; - restore->restore_buffer = buffer; - restore->restore_size = written; -} - -static void peres_restore_resource(peres_resource_restore_t *restore, const pe_resource_entry_info_t *entry_info, void *raw_data_ptr, size_t raw_data_size) -{ - assert(restore != NULL); - assert(raw_data_ptr != NULL); - - // If we don't know this type or the data size is 0, just return with the raw information untouched. - if (entry_info == NULL || raw_data_size == 0) { - goto fallback_untouched; - } - - switch (entry_info->type) { - default: goto fallback_untouched; - case RT_ICON: - peres_restore_resource_icon(restore, entry_info, raw_data_ptr, raw_data_size); - break; - } - - if (restore->is_modified) - return; - -fallback_untouched: - restore->is_modified = false; - restore->restore_buffer = raw_data_ptr; - restore->restore_size = raw_data_size; -} - -static void peres_save_resource(pe_ctx_t *ctx, const pe_resource_node_t *node, bool namedExtract) -{ - UNUSED(ctx); - assert(node != NULL); - assert(node->type == LIBPE_RDT_DATA_ENTRY); - assert(node->dirLevel == LIBPE_RDT_LEVEL3); - - const IMAGE_RESOURCE_DATA_ENTRY *entry = node->raw.dataEntry; - - const uint64_t raw_data_offset = pe_rva2ofs(ctx, entry->OffsetToData); - const size_t raw_data_size = entry->Size; - uint8_t *raw_data_ptr = LIBPE_PTR_ADD(ctx->map_addr, raw_data_offset); - if (!pe_can_read(ctx, raw_data_ptr, raw_data_size)) { - // TODO: Should we report something? - fprintf(stderr, "Attempted to read range [ %p, %p ] which is not within the mapped range [ %p, %lx ]\n", - (void *)raw_data_ptr, LIBPE_PTR_ADD(raw_data_ptr, raw_data_size), - ctx->map_addr, ctx->map_end); - return; - } - - struct stat statDir; - if (stat(g_resourceDir, &statDir) == -1) - mkdir(g_resourceDir, 0700); - - char *dirName; - - const pe_resource_node_t *folder_node = pe_resource_find_parent_node_by_type_and_level(node, LIBPE_RDT_DIRECTORY_ENTRY, LIBPE_RDT_LEVEL1); // dirLevel == 1 is where Resource Types are defined. - const pe_resource_entry_info_t *entry_info = pe_resource_entry_info_lookup(folder_node->raw.directoryEntry->u0.Name); - if (entry_info != NULL) { - if ( asprintf( &dirName, "%s/%s", g_resourceDir, entry_info->dir_name ) < 0 ) - abort(); - //snprintf(dirName, sizeof(dirName), "%s/%s", g_resourceDir, entry_info->dir_name); - } else { - dirName = strdup( g_resourceDir ); - //snprintf(dirName, sizeof(dirName), "%s", g_resourceDir); - } - - if (stat(dirName, &statDir) == -1) - mkdir(dirName, 0700); - - const pe_resource_node_t *name_node = pe_resource_find_parent_node_by_type_and_level(node, LIBPE_RDT_DIRECTORY_ENTRY, LIBPE_RDT_LEVEL2); // dirLevel == 2 - if (name_node == NULL) { - // TODO: Should we report something? - free( dirName ); - fprintf(stderr, "pe_resource_find_parent_node_by_type_and_level returned NULL\n"); - return; - } - //fprintf(stderr, "DEBUG: Name=%d\n", name_node->raw.directoryEntry->u0.Name); - - char *relativeFileName; - - if (namedExtract) { - char fileName[MAX_PATH]; // ok? - - peres_build_node_filename(ctx, fileName, sizeof(fileName), node); - if ( asprintf(&relativeFileName, "%s/%s%s", - dirName, - fileName, - entry_info != NULL ? entry_info->extension : ".bin") < 0 ) - abort(); - } else { - if ( asprintf(&relativeFileName, "%s/" "%" PRIu32 "%s", - dirName, - name_node->raw.directoryEntry->u0.data.NameOffset, - entry_info != NULL ? entry_info->extension : ".bin") < 0 ) - abort(); - } - - free( dirName ); - //printf("DEBUG: raw_data_offset=%#llx, raw_data_size=%ld, relativeFileName=%s\n", raw_data_offset, raw_data_size, relativeFileName); - - peres_resource_restore_t restore = {0}; - peres_restore_resource(&restore, entry_info, raw_data_ptr, raw_data_size); - - FILE *fp = fopen(relativeFileName, "wb+"); - - if (fp == NULL) { - free( relativeFileName ); - // TODO: Should we report something? - return; - } - - fwrite(restore.restore_buffer, restore.restore_size, 1, fp); - - fclose(fp); - - if (restore.is_modified) { - free(restore.restore_buffer); - } - - output("Save On", relativeFileName); - - free( relativeFileName ); -} - -static void peres_save_all_resources(pe_ctx_t *ctx, const pe_resource_node_t *node, bool namedExtract) -{ - if (node == NULL) - return; - - if (node->type == LIBPE_RDT_DATA_ENTRY && node->dirLevel == 3) { - peres_save_resource(ctx, node, namedExtract); - } - - peres_save_all_resources(ctx, node->childNode, namedExtract); - peres_save_all_resources(ctx, node->nextNode, namedExtract); -} - -bool peres_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. - return false; - return node->raw.directoryEntry->u0.data.NameOffset == RT_VERSION; -} - -static void peres_show_version(pe_ctx_t *ctx, const pe_resource_node_t *node) -{ - if (node == NULL) - return; - - pe_resource_node_search_result_t search_result = {0}; - pe_resource_search_nodes(&search_result, node, peres_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); - if (version_node != NULL) { - const uint64_t data_offset = pe_rva2ofs(ctx, version_node->raw.dataEntry->OffsetToData); - const size_t data_size = version_node->raw.dataEntry->Size; - const void *data_ptr = LIBPE_PTR_ADD(ctx->map_addr, 32 + data_offset); // TODO(jweyrich): The literal 32 refers to the size of the - if (!pe_can_read(ctx, data_ptr, data_size)) { - LIBPE_WARNING("Cannot read VS_FIXEDFILEINFO"); - return; - } - - const VS_FIXEDFILEINFO *info_ptr = data_ptr; - - static char value[MAX_MSG]; - - snprintf(value, MAX_MSG, "%u.%u.%u.%u", - (uint32_t)(info_ptr->dwFileVersionMS & 0xffff0000) >> 16, - (uint32_t)info_ptr->dwFileVersionMS & 0x0000ffff, - (uint32_t)(info_ptr->dwFileVersionLS & 0xffff0000) >> 16, - (uint32_t)info_ptr->dwFileVersionLS & 0x0000ffff); - output("File Version", value); - - snprintf(value, MAX_MSG, "%u.%u.%u.%u", - (uint32_t)(info_ptr->dwProductVersionMS & 0xffff0000) >> 16, - (uint32_t)info_ptr->dwProductVersionMS & 0x0000ffff, - (uint32_t)(info_ptr->dwProductVersionLS & 0xffff0000) >> 16, - (uint32_t)info_ptr->dwProductVersionLS & 0x0000ffff); - output("Product Version", value); - } - } - pe_resources_dealloc_node_search_result(&search_result); -} - -typedef struct { - int totalCount; - int totalResourceDirectory; - int totalDirectoryEntry; - int totalDataString; - int totalDataEntry; -} peres_stats_t; - -static void peres_generate_stats(peres_stats_t *stats, const pe_resource_node_t *node) { - if (node == NULL) - return; - - stats->totalCount++; - - switch (node->type) { - case LIBPE_RDT_RESOURCE_DIRECTORY: - stats->totalResourceDirectory++; - break; - case LIBPE_RDT_DIRECTORY_ENTRY: - stats->totalDirectoryEntry++; - break; - case LIBPE_RDT_DATA_STRING: - stats->totalDataString++; - break; - case LIBPE_RDT_DATA_ENTRY: - stats->totalDataEntry++; - break; - } - - if (node->childNode) { - peres_generate_stats(stats, node->childNode); - } - - if (node->nextNode) { - peres_generate_stats(stats, node->nextNode); - } -} - -static void peres_show_stats(const pe_resource_node_t *node) -{ - peres_stats_t stats = {0}; - peres_generate_stats(&stats, node); - - static char value[MAX_MSG]; - - snprintf(value, MAX_MSG, "%d", stats.totalCount); - output("Total Structs", value); - - snprintf(value, MAX_MSG, "%d", stats.totalResourceDirectory); - output("Total Resource Directory", value); - - snprintf(value, MAX_MSG, "%d", stats.totalDirectoryEntry); - output("Total Directory Entry", value); - - snprintf(value, MAX_MSG, "%d", stats.totalDataString); - output("Total Data String", value); - - snprintf(value, MAX_MSG, "%d", stats.totalDataEntry); - output("Total Data Entry", value); -} - -int main(int argc, char **argv) -{ - pev_config_t config; - PEV_INITIALIZE(&config); - - if (argc < 3) { - usage(); - exit(EXIT_FAILURE); - } - - output_set_cmdline(argc, argv); - - options_t *options = parse_options(argc, argv); // opcoes - - const char *path = argv[argc-1]; - pe_ctx_t ctx; - - pe_err_e err = pe_load_file(&ctx, path); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - err = pe_parse(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - if (!pe_is_pe(&ctx)) - EXIT_ERROR("not a valid PE file"); - - output_open_document(); - - pe_resources_t *resources = pe_resources(&ctx); - if (resources == NULL || resources->err != LIBPE_E_OK) { - LIBPE_WARNING("This file has no resources"); - return EXIT_SUCCESS; - } - - pe_resource_node_t *root_node = resources->root_node; - - if (options->all) { - peres_show_nodes(&ctx, root_node); - peres_show_stats(root_node); - peres_show_list(&ctx, root_node); - peres_save_all_resources(&ctx, root_node, options->namedExtract); - peres_show_version(&ctx, root_node); - } else { - if (options->extract) - peres_save_all_resources(&ctx, root_node, options->namedExtract); - if (options->info) - peres_show_nodes(&ctx, root_node); - if (options->list) - peres_show_list(&ctx, root_node); - if (options->statistics) - peres_show_stats(root_node); - if (options->version) - peres_show_version(&ctx, root_node); - } - - output_close_document(); - - // libera a memoria - free_options(options); - - // free - err = pe_unload(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - PEV_FINALIZE(&config); - - return EXIT_SUCCESS; -} diff --git a/src/pescan.c b/src/pescan.c deleted file mode 100644 index deb5ac1c..00000000 --- a/src/pescan.c +++ /dev/null @@ -1,605 +0,0 @@ -/* vim :set ts=4 sw=4 sts=4 et : */ -/* - pev - the PE file analyzer toolkit - - pescan.c - search for suspicious things in PE files. - - 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. -*/ - -#include "common.h" -#include -#include -#include -#include "plugins.h" - -#define PROGRAM "pescan" - -typedef struct { - bool verbose; -} options_t; - -static void usage(void) -{ - static char formats[255]; - output_available_formats(formats, sizeof(formats), '|'); - printf("Usage: %s OPTIONS FILE\n" - "Search for suspicious things in PE files\n" - "\nExample: %s putty.exe\n" - "\nOptions:\n" - " -f, --format <%s> Change output format (default: text).\n" - " -v, --verbose Show more information about found items.\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM, formats); -} - -static void free_options(options_t *options) -{ - // FIX: Don't need to test for NULL pointer. - //if (options == NULL) - // return; - - free(options); -} - -static options_t *parse_options(int argc, char *argv[]) -{ - options_t *options = calloc_s(1, sizeof(options_t)); - - /* Parameters for getopt_long() function */ - static const char short_options[] = "f:vV"; - - static const struct option long_options[] = { - { "format", required_argument, NULL, 'f' }, - { "help", no_argument, NULL, 1 }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - int c, ind; - - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) - break; - - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'f': - if (output_set_format_by_name(optarg) < 0) - EXIT_ERROR("invalid format option"); - break; - case 'v': - options->verbose = true; - break; - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); - } - } - - return options; -} - -// check for abnormal dos stub (common in packed files) -static bool normal_dos_stub(pe_ctx_t *ctx, uint32_t *stub_offset) -{ - static const uint8_t dos_stub[] = - "\x0e" // push cs - "\x1f" // pop ds - "\xba\x0e\x00" // mov dx, 0x0e - "\xb4\x09" // mov ah, 0x09 - "\xcd\x21" // int 0x21 - "\xb8\x01\x4c" // mov ax, 0x4c01 - "\xcd\x21" // int 0x21 - "This program cannot be run in DOS mode.\r\r\n$"; - - const size_t dos_stub_size = sizeof(dos_stub) - 1; // -1 to ignore ending null - - const IMAGE_DOS_HEADER *dos = pe_dos(ctx); - if (dos == NULL) { - LIBPE_WARNING("unable to retrieve PE DOS header"); - return false; - } - - *stub_offset = dos->e_cparhdr << 4; - - // dos stub starts at e_cparhdr shifted by 4 - const char *dos_stub_ptr = LIBPE_PTR_ADD(ctx->map_addr, *stub_offset); - if (!pe_can_read(ctx, dos_stub_ptr, dos_stub_size)) { - LIBPE_WARNING("unable to seek in file"); - return false; - } - - return memcmp(dos_stub, dos_stub_ptr, dos_stub_size) == 0; -} - -static 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; - - if (section->Characteristics & IMAGE_SCN_CNT_CODE) - return NULL; - - return section; -} - -static 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; - - if (directory->Size == 0) - return 0; - - return directory->VirtualAddress; -} - -/* - * -1 - fake tls callbacks detected - * 0 - no tls directory - * >0 - number of callbacks functions found -*/ -static int pe_get_tls_callbacks(pe_ctx_t *ctx, const options_t *options) -{ - 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++) - { - if (tls_addr >= sections[i]->VirtualAddress && - tls_addr < (sections[i]->VirtualAddress + sections[i]->SizeOfRawData)) - { - 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 - - char value[MAX_MSG]; - uint32_t funcaddr = 0; - - 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; - } - - uint32_t funcaddr = *funcaddr_ptr; - if (funcaddr) { - ret = ++j; // function found - - if (options->verbose) { - snprintf(value, MAX_MSG, "%#x", funcaddr); - output("TLS callback function", value); - } - } - } while (funcaddr); - - return ret; - } - } - - return 0; -} - -static bool strisprint(const char *string) -{ - const char *s = string; - - if (strncmp(string, ".tls", 5) == 0) - return false; - - if (*s++ != '.') - return false; - - while (*s) - { - if (!isalpha((int)*s)) - return false; - - s++; - } - return true; -} - -static void stradd(char *dest, const char *src, bool *pad) -{ - if (*pad) - strcat(dest, ", "); - - strcat(dest, src); - *pad = true; -} - -static void print_strange_sections(pe_ctx_t *ctx) -{ - const uint16_t num_sections = pe_sections_count(ctx); - if (num_sections == 0) - return; - - char value[MAX_MSG]; - - if (ctx->pe.num_sections <= 2) - snprintf(value, MAX_MSG, "%d (low)", num_sections); - else if (ctx->pe.num_sections > 8) - snprintf(value, MAX_MSG, "%d (high)", num_sections); - else - snprintf(value, MAX_MSG, "%d", num_sections); - - output("section count", value); - - IMAGE_SECTION_HEADER ** const sections = pe_sections(ctx); - - output_open_scope("sections", OUTPUT_SCOPE_TYPE_ARRAY); - - bool aux = false; - for (uint16_t i=0; i < num_sections; i++, aux=false) - { - output_open_scope("section", OUTPUT_SCOPE_TYPE_OBJECT); - memset(&value, 0, sizeof(value)); - - if (!strisprint((const char *)sections[i]->Name)) - stradd(value, "suspicious name", &aux); - - if (sections[i]->SizeOfRawData == 0) - stradd(value, "zero length", &aux); - else if (sections[i]->SizeOfRawData <= 512) - stradd(value, "small length", &aux); - - // rwx or writable + executable code - if (sections[i]->Characteristics & IMAGE_SCN_MEM_WRITE && - (sections[i]->Characteristics & IMAGE_SCN_CNT_CODE || - sections[i]->Characteristics & IMAGE_SCN_MEM_EXECUTE)) - stradd(value, "self-modifying", &aux); - - if (!aux) - strncpy(value, "normal", 7); - - output((const char *)sections[i]->Name, value); - output_close_scope(); // section - } - output_close_scope(); // sections -} - -static bool normal_imagebase(pe_ctx_t *ctx) -{ - return (ctx->pe.imagebase == 0x100000000 || - ctx->pe.imagebase == 0x1000000 || - ctx->pe.imagebase == 0x400000); -} - -// FIX: Already in libpe! -// new anti-disassembly technique with undocumented Intel FPU instructions -//static bool fpu_trick(pe_ctx_t *ctx) -//{ -// 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; -//} - -static void print_timestamp(const options_t *options, const IMAGE_COFF_HEADER *hdr_coff_ptr) -{ - const time_t now = time(NULL); - static char value[MAX_MSG]; - - if (hdr_coff_ptr->TimeDateStamp == 0) - snprintf(value, MAX_MSG, "zero/invalid"); - else if (hdr_coff_ptr->TimeDateStamp < 946692000) - snprintf(value, MAX_MSG, "too old (pre-2000)"); - else if (hdr_coff_ptr->TimeDateStamp > (uint32_t) now) - snprintf(value, MAX_MSG, "future time"); - else - snprintf(value, MAX_MSG, "normal"); - - if (options->verbose) - { - // FIX: Bigger string because week-day abbreviation is locale dependant. - static char timestr[64]; - strftime(timestr, sizeof timestr, - " - %a, %d %b %Y %H:%M:%S UTC", - gmtime((time_t *) &hdr_coff_ptr->TimeDateStamp)); - - strcat(value, timestr); - } - - output("timestamp", value); -} - -static int8_t 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 -1; - - 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); - - 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 main(int argc, char *argv[]) -{ - pev_config_t config; - PEV_INITIALIZE(&config); - - if (argc < 2) { - usage(); - return EXIT_FAILURE; - } - - output_set_cmdline(argc, argv); - - options_t *options = parse_options(argc, argv); // opcoes - - const char *path = argv[argc-1]; - pe_ctx_t ctx; - - pe_err_e err = pe_load_file(&ctx, path); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - err = pe_parse(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - if (!pe_is_pe(&ctx)) - EXIT_ERROR("not a valid PE file"); - - output_open_document(); - - // File entropy - const double entropy = pe_calculate_entropy_file(&ctx); - - static char value[MAX_MSG]; - - if (entropy < 7.0) - snprintf(value, MAX_MSG, "%f (normal)", entropy); - else - snprintf(value, MAX_MSG, "%f (probably packed)", entropy); - output("file entropy", value); - - if (pe_is_dll(&ctx)) { - uint16_t ret = cpl_analysis(&ctx); - switch (ret) { - case 1: - output("cpl analysis", "malware"); - break; - default: - output("cpl analysis:", "no threat"); - break; - } - } - - output("fpu anti-disassembly", pe_fpu_trick(&ctx) ? "yes" : "no"); - - // imagebase analysis - if (!normal_imagebase(&ctx)) { - if (options->verbose) - snprintf(value, MAX_MSG, "suspicious - %#"PRIx64, ctx.pe.imagebase); - else - snprintf(value, MAX_MSG, "suspicious"); - } else { - if (options->verbose) - snprintf(value, MAX_MSG, "normal - %#"PRIx64, ctx.pe.imagebase); - else - snprintf(value, MAX_MSG, "normal"); - } - output("imagebase", value); - - const IMAGE_OPTIONAL_HEADER *optional = pe_optional(&ctx); - if (optional == NULL) { - LIBPE_WARNING("unable to read optional header"); - } else { - uint32_t ep = optional->_32 ? optional->_32->AddressOfEntryPoint : - optional->_64 ? optional->_64->AddressOfEntryPoint : - optional->_rom ? optional->_rom->AddressOfEntryPoint : - 0; - - // fake ep - if (ep == 0) { - snprintf(value, MAX_MSG, "null"); - } else if (pe_check_fake_entrypoint(&ctx, ep)) { - if (options->verbose) - snprintf(value, MAX_MSG, "fake - va: %#x - raw: %#"PRIx64, ep, pe_rva2ofs(&ctx, ep)); - else - snprintf(value, MAX_MSG, "fake"); - } else { - if (options->verbose) - snprintf(value, MAX_MSG, "normal - va: %#x - raw: %#"PRIx64, ep, pe_rva2ofs(&ctx, ep)); - else - snprintf(value, MAX_MSG, "normal"); - } - - output("entrypoint", value); - } - - // dos stub - uint32_t stub_offset = 0; - if (!normal_dos_stub(&ctx, &stub_offset)) { - if (options->verbose) - snprintf(value, MAX_MSG, "suspicious - raw: %#x", stub_offset); - else - snprintf(value, MAX_MSG, "suspicious"); - } else - snprintf(value, MAX_MSG, "normal"); - - output("DOS stub", value); - - // tls callbacks - int callbacks = pe_get_tls_callbacks(&ctx, options); - - if (callbacks == 0) - snprintf(value, MAX_MSG, "not found"); - else if (callbacks == -1) - snprintf(value, MAX_MSG, "found - no functions"); - else if (callbacks > 0) - snprintf(value, MAX_MSG, "found - %d function(s)", callbacks); - - output("TLS directory", value); - - // invalid timestamp - IMAGE_COFF_HEADER *coff = pe_coff(&ctx); - if (coff == NULL) { - LIBPE_WARNING("unable to read coff header"); - } else { - print_timestamp(options, coff); - } - - // section analysis - print_strange_sections(&ctx); - - output_close_document(); - - // free memory - free_options(options); - err = pe_unload(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - PEV_FINALIZE(&config); - - return EXIT_SUCCESS; -} diff --git a/src/pesec.c b/src/pesec.c deleted file mode 100644 index a31de372..00000000 --- a/src/pesec.c +++ /dev/null @@ -1,528 +0,0 @@ -/* vim :set ts=4 sw=4 sts=4 et : */ -/* - pev - the PE file analyzer toolkit - - pesec.c - Checks for security features in PE files. - - Copyright (C) 2012 - 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. -*/ - -#include "common.h" -#include -#include -#include -#include -#include "compat/strlcat.h" -#include "plugins.h" - -#define PROGRAM "pesec" - -typedef enum { - CERT_FORMAT_TEXT = 1, - CERT_FORMAT_PEM = 2, - CERT_FORMAT_DER = 3 -} cert_format_e; - -typedef struct { - cert_format_e certoutform; - BIO *certout; -} options_t; - -static void usage(void) -{ - static char formats[255]; - output_available_formats(formats, sizeof(formats), '|'); - printf("Usage: %s [OPTIONS] FILE\n" - "Check for security features in PE files\n" - "\nExample: %s wordpad.exe\n" - "\nOptions:\n" - " -f, --format <%s> Change output format (default: text)\n" - " -c, --certoutform Specifies the certificate output format (default: text).\n" - " -o, --certout Specifies the output filename to write certificates to (default: stdout).\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM, formats); -} - -static cert_format_e parse_certoutform(const char *optarg) -{ - cert_format_e result; - - if (strcmp(optarg, "text") == 0) - result = CERT_FORMAT_TEXT; - else if (strcmp(optarg, "pem") == 0) - result = CERT_FORMAT_PEM; - else if (strcmp(optarg, "der") == 0) - result = CERT_FORMAT_DER; - else - EXIT_ERROR("invalid cert_format option"); - - return result; -} - -static BIO *parse_certout(const char *optarg) -{ - BIO *bio = BIO_new(BIO_s_file()); - if (bio == NULL) { - EXIT_ERROR("could not allocate BIO"); - } - - if (strcmp(optarg, "stdout") == 0) { - BIO_set_fp(bio, stdout, BIO_NOCLOSE); - } else if (strcmp(optarg, "stderr") == 0) { - BIO_set_fp(bio, stderr, BIO_NOCLOSE); - } else { - int ret = BIO_write_filename(bio, (char *)optarg); - if (ret == 0) { - BIO_free(bio); - EXIT_ERROR("failed to open file"); - } - } - - return bio; -} - -static void free_options(options_t *options) -{ - if (options && options->certout) - BIO_free(options->certout); - - free(options); -} - -static options_t *parse_options(int argc, char *argv[]) -{ - options_t *options = calloc_s(1, sizeof(options_t)); - - /* Parameters for getopt_long() function */ - static const char short_options[] = "f:c:o:V"; - - static const struct option long_options[] = { - { "format", required_argument, NULL, 'f' }, - { "certoutform", required_argument, NULL, 'c' }, - { "certout", required_argument, NULL, 'o' }, - { "help", no_argument, NULL, 1 }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - int c, ind; - - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) - break; - - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'f': - if (output_set_format_by_name(optarg) < 0) - EXIT_ERROR("invalid format option"); - break; - case 'v': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - case 'c': - options->certoutform = parse_certoutform(optarg); - break; - case 'o': - options->certout = parse_certout(optarg); - break; - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); - } - } - - return options; -} - -/* -find stack cookies, a.k.a canary, buffer security check -option in MVS 2010 -*/ -// FIXME: What about other versions? -static bool stack_cookies(pe_ctx_t *ctx) -{ - static const unsigned char mvs2010[] = { - 0x55, 0x8b, 0xec, 0x83, - 0x33, 0xc5, 0x33, 0xcd, - 0xe8, 0xc3 - }; - - if (ctx == NULL) - return false; - - size_t found = 0; - const uint8_t *file_bytes = LIBPE_PTR_ADD(ctx->map_addr, 0); - const uint64_t filesize = pe_filesize(ctx); - - // FIXME: Is this right?! Seems like partial matches will be - // Accumulated. Example: If all these bytes are found, - // separatelly in the file, this function will return true. - for (uint64_t ofs=0; ofs < filesize; ofs++) { - for (size_t i=0; i < sizeof(mvs2010); i++) { - if (file_bytes[ofs] == mvs2010[i] && found == i) - found++; - } - } - - return found == sizeof(mvs2010); -} - -static void print_certificate(BIO *out, cert_format_e format, X509 *cert) -{ - if (out == NULL) - return; - switch (format) { - default: - case CERT_FORMAT_TEXT: - 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 unsigned int roundBy8( unsigned int n ) -{ - unsigned int t = n & ~7U; - if ( n & 7U ) t += 8; - return t; -} - -static int parse_pkcs7_data(const options_t *options, const CRYPT_DATA_BLOB *blob) -{ - 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, 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: 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; - } - - const int numcerts = certs != NULL ? sk_X509_num(certs) : 0; - for (int i = 0; i < numcerts; i++) { - X509 *cert = sk_X509_value(certs, i); - print_certificate(options->certout, options->certoutform, cert); - // NOTE: Calling X509_free(cert) is unnecessary. - } - - // Print whether certificate signature is valid - if (numcerts > 0) { - 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"); - } - - // Print signers - if (numcerts > 0) { - 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); - - int issuer_name_len = X509_NAME_get_text_by_NID(name, NID_commonName, NULL, 0); - if (issuer_name_len > 0) { - output_open_scope("signer", OUTPUT_SCOPE_TYPE_OBJECT); - char issuer_name[issuer_name_len + 1]; - X509_NAME_get_text_by_NID(name, NID_commonName, issuer_name, issuer_name_len + 1); - output("Issuer", issuer_name); - output_close_scope(); // signer - } - } - output_close_scope(); // signers - } - -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 parse_certificates(const options_t *options, pe_ctx_t *ctx) -{ - 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; - - uint32_t fileOffset = directory->VirtualAddress; // This a file pointer rather than a common RVA. - - // 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); - - fileOffset += roundBy8(cert->dwLength); // Offset to the next certificate. - - 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 = cert->dwLength - offsetof(WIN_CERTIFICATE, bCertificate); - p7data.pbData = cert->bCertificate; - parse_pkcs7_data(options, &p7data); - 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 -} - -int main(int argc, char *argv[]) -{ - pev_config_t config; - PEV_INITIALIZE(&config); - - if (argc < 2) { - usage(); - exit(EXIT_FAILURE); - } - - output_set_cmdline(argc, argv); - - options_t *options = parse_options(argc, argv); // opcoes - - const char *path = argv[argc-1]; - pe_ctx_t ctx; - - pe_err_e err = pe_load_file(&ctx, path); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - err = pe_parse(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - if (!pe_is_pe(&ctx)) - EXIT_ERROR("not a valid PE file"); - - IMAGE_OPTIONAL_HEADER *optional = pe_optional(&ctx); - if (optional == NULL) - return EXIT_FAILURE; - - uint16_t dllchar = 0; - switch (optional->type) { - default: - return EXIT_FAILURE; - case MAGIC_ROM: - break; - case MAGIC_PE32_0: - case MAGIC_PE32: - dllchar = optional->_32->DllCharacteristics; - break; - case MAGIC_PE64: - dllchar = optional->_64->DllCharacteristics; - break; - } - - output_open_document(); - - static char field[MAX_MSG]; - - // aslr - snprintf(field, MAX_MSG, "ASLR"); - output(field, (dllchar & 0x40) ? "yes" : "no"); - - // dep/nx - snprintf(field, MAX_MSG, "DEP/NX"); - output(field, (dllchar & 0x100) ? "yes" : "no"); - - // seh - snprintf(field, MAX_MSG, "SEH"); - output(field, (dllchar & 0x400) ? "no" : "yes"); - - // cfg - snprintf(field, MAX_MSG, "CFG"); - output(field, (dllchar & 0x4000) ? "yes" : "no"); - - // stack cookies - snprintf(field, MAX_MSG, "Stack cookies (EXPERIMENTAL)"); - output(field, stack_cookies(&ctx) ? "yes" : "no"); - - // certificados - parse_certificates(options, &ctx); - - output_close_document(); - - // libera a memoria - free_options(options); - - // free - err = pe_unload(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - PEV_FINALIZE(&config); - - return EXIT_SUCCESS; -} diff --git a/src/pestr.c b/src/pestr.c deleted file mode 100644 index 9cf2db4b..00000000 --- a/src/pestr.c +++ /dev/null @@ -1,278 +0,0 @@ -/* vim :set ts=4 sw=4 sts=4 et : */ -/* - pev - the PE file analyzer toolkit - - pestr.c - search for strings in PE files. - - Copyright (C) 2012 - 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. -*/ - -#include "common.h" -#include -#include -#include -#include - -#define PROGRAM "pestr" -#define BUFSIZE 4 -#define LINE_BUFFER 32768 - -typedef struct { - unsigned short strsize; - bool offset; - bool section; -} options_t; - -static void usage(void) -{ - printf("Usage: %s OPTIONS FILE\n" - "Search for strings in PE files\n" - "\nExample: %s acrobat.exe\n" - "\nOptions:\n" - " -n, --min-length Set minimum string length (default: 4).\n" - " -o, --offset Show string offset in file.\n" - " -s, --section Show string section, if exists.\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM); -} - -static void free_options(options_t *options) -{ - // FIX: Don't need to test for NULL pointer. - //if (options == NULL) - // return; - - free(options); -} - -static options_t *parse_options(int argc, char *argv[]) -{ - options_t *options = calloc_s(1, sizeof(options_t)); - - /* Parameters for getopt_long() function */ - static const char short_options[] = "osn:V"; - - static const struct option long_options[] = { - { "offset", no_argument, NULL, 'o' }, - { "section", no_argument, NULL, 's' }, - { "min-length", required_argument, NULL, 'n' }, - { "help", no_argument, NULL, 1 }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - int c, ind; - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) - break; - - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'o': - options->offset = true; - break; - case 's': - options->section = true; - break; - case 'n': - { - // FIX: errno isn't automatically zeroed if already set. - errno = 0; - unsigned long value = strtoul(optarg, NULL, 0); - if (value == ULONG_MAX && errno == ERANGE) { - fprintf(stderr, "The original (nonnegated) value would overflow"); - exit(EXIT_FAILURE); - } - options->strsize = (unsigned char)value; - break; - } - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); - } - } - return options; -} - -// TODO Move it to libpe -static unsigned char *ofs2section(pe_ctx_t *ctx, uint64_t offset) -{ - IMAGE_SECTION_HEADER **sections = pe_sections(ctx); - - for (uint16_t i=0; i < ctx->pe.num_sections; i++) { - uint32_t sect_offset = sections[i]->PointerToRawData; - uint32_t sect_size = sections[i]->SizeOfRawData; - - if (offset >= sect_offset && offset < (sect_offset + sect_size)) { - return (unsigned char *)sections[i]->Name; - } - } - - return NULL; -} - -static void printb( pe_ctx_t *ctx, - const options_t *options, - const uint8_t *bytes, - size_t pos, - size_t end, - bool is_wide) { - if (options->offset) - printf("%#lx\t", (unsigned long) pos); - - if (options->section) { - char *s = (char *) ofs2section(ctx, pos); - printf("%s\t", s ? s : "[none]"); - } - - // printf("%s\t", is_wide ? "U16LE" : "U8" ); - - if (is_wide) { - for (;pos < end;) { - // Byte swap; Internal PE uses little endian while C uses big endian - wchar_t wc = bytes[pos] | bytes[pos+1]<<8; - if ( wc ) { - putwchar( wc ); - } - pos += 2; - } - } else { - for (;pos < end; ++pos ) { - char c = bytes[pos]; - if ( c ) putchar( c ); - } - } - - putchar('\n'); -} - -int main(int argc, char *argv[]) -{ - if (argc < 2) { - usage(); - exit(EXIT_FAILURE); - } - - options_t *options = parse_options(argc, argv); // opcoes - - const char *path = argv[argc-1]; - pe_ctx_t ctx; - - pe_err_e err = pe_load_file(&ctx, path); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - err = pe_parse(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - if (!pe_is_pe(&ctx)) - EXIT_ERROR("not a valid PE file"); - - const uint64_t pe_size = pe_filesize(&ctx); - const uint8_t *pe_raw_data = ctx.map_addr; - - uint16_t chunk; - size_t buff_start = 0; - size_t odd_wbuff_start = 0; - size_t even_wbuff_start = 0; - - for (size_t pe_raw_offset = 0; pe_raw_offset < pe_size; ++pe_raw_offset) { - const uint8_t byte = pe_raw_data[pe_raw_offset]; - - if (pe_raw_offset+1 < pe_size) - // Byte swap; Internal PE uses little endian while C uses big endian - chunk = byte | pe_raw_data[pe_raw_offset+1]<<8; - else - chunk = 0; - - if (isprint(byte)) { - if ( buff_start == 0 ) { - buff_start = pe_raw_offset; - } - } else { - if ( buff_start != 0 ) { - if ((pe_raw_offset - buff_start) >= (options->strsize ? options->strsize : 4)) - printb(&ctx, options, pe_raw_data, buff_start, pe_raw_offset, false); - buff_start = 0; - } - } - - if (iswprint(chunk)) { - if( pe_raw_offset & 0x1 ) { - if ( odd_wbuff_start == 0 ) { - odd_wbuff_start = pe_raw_offset; - } - } else { - if ( even_wbuff_start == 0 ) { - even_wbuff_start = pe_raw_offset; - } - } - } else { - if( pe_raw_offset & 0x1 ) { - if ( odd_wbuff_start != 0 ) { - if ((pe_raw_offset - odd_wbuff_start)/2 >= (options->strsize ? options->strsize : 4)) - printb(&ctx, options, pe_raw_data, odd_wbuff_start, pe_raw_offset, true); - odd_wbuff_start = 0; - } - } else { - if ( even_wbuff_start != 0 ) { - if ((pe_raw_offset - even_wbuff_start)/2 >= (options->strsize ? options->strsize : 4)) - printb(&ctx, options, pe_raw_data, even_wbuff_start, pe_raw_offset, true); - even_wbuff_start = 0; - } - } - } - } - - // libera a memoria - free_options(options); - - // free - err = pe_unload(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/src/pev.conf b/src/pev.conf deleted file mode 100644 index 6024cc7a..00000000 --- a/src/pev.conf +++ /dev/null @@ -1 +0,0 @@ -plugins_dir=/usr/local/lib/pev/plugins diff --git a/src/pev_api.c b/src/plugin.c similarity index 76% rename from src/pev_api.c rename to src/plugin.c index 8d5031ea..768d8c82 100644 --- a/src/pev_api.c +++ b/src/plugin.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 - pev_api.c - Symbols and APIs to be used by all plugins. + plugin.c - Symbols and APIs to be used by all plugins. - Copyright (C) 2014 pev authors + Copyright (C) 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,21 +34,23 @@ files in the program, then also delete it here. */ -#include "pev_api.h" #include "output_plugin.h" + #include -#include -pev_api_t *pev_api_ptr(void) { +static struct readpe_api g_api; // Garanteed to be zeroed. + +struct readpe_api *readpe_api_ptr(void) +{ static bool initialized = false; - static pev_api_t api; // Garanteed to be zeroed. - if (!initialized) { - initialized = true; - //api = ( pev_api_t ){0}; // FIX: Don't need to zero. - //memset(&api, 0, sizeof(api)); - api.output = output_plugin_api_ptr(); + if (! initialized) { + initialized = true; + // api = ( pev_api_t ){0}; // FIX: Don't need to zero. + // memset(&api, 0, sizeof(api)); + g_api.output = readpe_output_api_ptr(); } - return &api; + return &g_api; } + diff --git a/src/plugins.c b/src/plugins.c index 5dc3cb25..258b9eea 100644 --- a/src/plugins.c +++ b/src/plugins.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 plugins.c - Implementation for the plugins subsystem. - Copyright (C) 2012 - 2014 pev authors + Copyright (C) 2012 - 2014 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,31 +35,33 @@ */ #include "plugins.h" -#include "plugin.h" -#include "dylib.h" -#include "common.h" + #include "compat/sys/queue.h" -#include -#include +#include "config.h" +#include "dylib.h" +#include "plugin.h" + #include #include +#include +#include +#include +#include #include -#include "config.h" -#include "pev_api.h" typedef struct _plugins_entry { - dylib_t library; - plugin_loaded_fn_t plugin_loaded_fn; - plugin_initialize_fn_t plugin_initialize_fn; - plugin_shutdown_fn_t plugin_shutdown_fn; - plugin_unloaded_fn_t plugin_unloaded_fn; + dylib_t library; + struct readpe_plugin *plugin; SLIST_ENTRY(_plugins_entry) entries; } plugins_entry_t; -static SLIST_HEAD(_plugins_t_list, _plugins_entry) g_loaded_plugins = SLIST_HEAD_INITIALIZER(g_loaded_plugins); +static SLIST_HEAD(_plugins_t_list, _plugins_entry) g_loaded_plugins + = SLIST_HEAD_INITIALIZER(g_loaded_plugins); -int plugins_load(const char *path) { +int plugins_load(const char *path) +{ plugins_entry_t *entry = calloc(1, sizeof *entry); + if (entry == NULL) { fprintf(stderr, "plugin: allocation failed for entry\n"); return -1; @@ -67,34 +69,27 @@ int plugins_load(const char *path) { dylib_t *library = &entry->library; - //fprintf(stdout, "plugins: Loading '%s'... ", path); - int ret = dylib_load(library, path); - //fprintf(stdout, "%s.\n", ret < 0 ? "failed" : "ok"); + // fprintf(stdout, "plugins: Loading '%s'... ", path); + int ret = dylib_load(library, path); + // fprintf(stdout, "%s.\n", ret < 0 ? "failed" : "ok"); if (ret < 0) { free(entry); return -2; } - // FIX: Ugly way to do it! - //*(void **)(&entry->plugin_loaded_fn) = dylib_get_symbol(library, "plugin_loaded"); - //*(void **)(&entry->plugin_initialize_fn) = dylib_get_symbol(library, "plugin_initialize"); - //*(void **)(&entry->plugin_shutdown_fn) = dylib_get_symbol(library, "plugin_shutdown"); - //*(void **)(&entry->plugin_unloaded_fn) = dylib_get_symbol(library, "plugin_unloaded"); - entry->plugin_loaded_fn = dylib_get_symbol( library, "plugin_loaded" ); - entry->plugin_initialize_fn = dylib_get_symbol(library, "plugin_initialize"); - entry->plugin_shutdown_fn = dylib_get_symbol(library, "plugin_shutdown"); - entry->plugin_unloaded_fn = dylib_get_symbol(library, "plugin_unloaded"); + entry->plugin = dylib_get_symbol(library, "readpe_plugin"); // Only plugin_initialize_fn and plugin_shutdown_fn are required. - if (entry->plugin_initialize_fn == NULL || entry->plugin_shutdown_fn == NULL) { - fprintf(stderr, "plugins: %s is incompatible with this version.\n", path); + if (entry->plugin->initialize == NULL || entry->plugin->shutdown == NULL) { + fprintf(stderr, "plugins: %s is incompatible with this version.\n", + path); dylib_unload(library); free(entry); return -3; } - if (entry->plugin_loaded_fn != NULL) { - const int loaded = entry->plugin_loaded_fn(); + if (entry->plugin->loaded != NULL) { + const int loaded = entry->plugin->loaded(); if (loaded < 0) { fprintf(stderr, "plugins: plugin didn't load correctly\n"); dylib_unload(library); @@ -103,8 +98,8 @@ int plugins_load(const char *path) { } } - const pev_api_t *pev_api = pev_api_ptr(); - const int initialized = entry->plugin_initialize_fn(pev_api); + const struct readpe_api *readpe_api = readpe_api_ptr(); + const int initialized = entry->plugin->initialize(readpe_api); if (initialized < 0) { fprintf(stderr, "plugins: plugin didn't initialize correctly\n"); dylib_unload(library); @@ -116,13 +111,14 @@ int plugins_load(const char *path) { return 0; } -static void plugin_unload_without_removal(plugins_entry_t *entry) { +static void plugin_unload_without_removal(plugins_entry_t *entry) +{ dylib_t *library = &entry->library; - entry->plugin_shutdown_fn(); + entry->plugin->shutdown(); - if (entry->plugin_unloaded_fn != NULL) { - entry->plugin_unloaded_fn(); + if (entry->plugin->unloaded != NULL) { + entry->plugin->unloaded(); } int ret = dylib_unload(library); @@ -139,27 +135,28 @@ static void plugin_unload(plugins_entry_t *entry) { } #endif -int plugins_load_all_from_directory(const char *path) { +int plugins_load_all_from_directory(const char *path) +{ // FIX: errno isn't automatically zeroed if already set. - errno = 0; + errno = 0; DIR *dir = opendir(path); if (dir == NULL) { - fprintf(stderr, "plugins: could not open directory '%s' -- %s\n", - path, strerror(errno)); + fprintf(stderr, "plugins: could not open directory '%s' -- %s\n", path, + strerror(errno)); return -1; } // FIX: Don't need this. - //long path_max = pathconf(path, _PC_PATH_MAX); - //char *relative_path = malloc(path_max); - //if (relative_path == NULL) { - // fprintf(stderr, "plugins: allocation failed for relative path\n"); - // closedir(dir); - // return -2; + // long path_max = pathconf(path, _PC_PATH_MAX); + // char *relative_path = malloc(path_max); + // if (relative_path == NULL) { + // fprintf(stderr, "plugins: allocation failed for relative path\n"); + // closedir(dir); + // return -2; //} - char *relative_path; + char *relative_path; - size_t load_count = 0; + int load_count = 0; struct dirent *dir_entry; // print all the files and directories within directory @@ -170,47 +167,56 @@ int plugins_load_all_from_directory(const char *path) { while ((dir_entry = readdir(dir)) != NULL) { switch (dir_entry->d_type) { - default: // Unhandled - break; + default: // Unhandled + break; -#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) - case DT_UNKNOWN: +#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__GNU__) \ + || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) + case DT_UNKNOWN: #endif - case DT_REG: // Regular file - { - const char *filename = dir_entry->d_name; - - // TODO(jweyrich): Use macro conditions for each system: .so, .dylib, .dll -#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) - const bool possible_plugin = pe_utils_str_ends_with(filename, ".so") != 0; + case DT_REG: // Regular file + { + const char *filename = dir_entry->d_name; + + // TODO(jweyrich): Use macro conditions for each system: .so, + // .dylib, .dll +#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__GNU__) \ + || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) + const bool possible_plugin + = pe_utils_str_ends_with(filename, ".so") != 0; #elif defined(__APPLE__) - const bool possible_plugin = pe_utils_str_ends_with(filename, ".dylib") != 0; + const bool possible_plugin + = pe_utils_str_ends_with(filename, ".dylib") != 0; #elif defined(__CYGWIN__) - const bool possible_plugin = pe_utils_str_ends_with(filename, ".dll") != 0; + const bool possible_plugin + = pe_utils_str_ends_with(filename, ".dll") != 0; #else #error Not supported #endif - if (!possible_plugin) - break; - - if ( asprintf(&relative_path, "%s/%s", path, filename) < 0 ) - { - fprintf(stderr, "plugins: allocation failed for relative path\n"); - closedir(dir); - return -2; - } - - int ret = plugins_load(relative_path); - free(relative_path); - if (ret < 0) { - closedir(dir); - return ret; - } - load_count++; + if (! possible_plugin) { break; } - case DT_DIR: // Directory - break; + + if (asprintf(&relative_path, "%s/%s", path, filename) < 0) { + fprintf(stderr, + "plugins: allocation failed for relative path\n"); + closedir(dir); + return -2; + } + + int ret = plugins_load(relative_path); + free(relative_path); + if (ret < 0) { + closedir(dir); + return ret; + } + load_count++; + break; + } + case DT_DIR: // Directory + break; } } @@ -219,15 +225,18 @@ int plugins_load_all_from_directory(const char *path) { return load_count; } -int plugins_load_all(pev_config_t *config) { +int plugins_load_all(struct readpe_config *config) +{ return plugins_load_all_from_directory(config->plugins_path); } -void plugins_unload_all(void) { - while (!SLIST_EMPTY(&g_loaded_plugins)) { +void plugins_unload_all(void) +{ + while (! SLIST_EMPTY(&g_loaded_plugins)) { plugins_entry_t *entry = SLIST_FIRST(&g_loaded_plugins); plugin_unload_without_removal(entry); SLIST_REMOVE_HEAD(&g_loaded_plugins, entries); free(entry); } } + diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt new file mode 100644 index 00000000..e2164b30 --- /dev/null +++ b/src/plugins/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(../../include) + +add_library(csv SHARED output/csv.c) +add_library(html SHARED output/html.c) +add_library(json SHARED output/json.c) +add_library(xml SHARED output/xml.c) + +install(TARGETS csv html json xml + LIBRARY DESTINATION share/readpe/plugins + ARCHIVE DESTINATION share/readpe/plugins + RUNTIME DESTINATION bin + INCLUDES DESTINATION include/readpe +) + diff --git a/src/plugins/Makefile b/src/plugins/Makefile deleted file mode 100644 index fe593d42..00000000 --- a/src/plugins/Makefile +++ /dev/null @@ -1,101 +0,0 @@ -####### Compiler options - -override CFLAGS += -O2 -I$(LIBPE) -I"../../include" -W -Wall -Wextra -std=c99 -pedantic -fPIC -override CPPFLAGS += -D_GNU_SOURCE - -PLUGINS = csv html text xml json -VERSION = 1.0 - -plugins_BUILDDIR = ../$(pev_BUILDDIR)/plugins - -csv_srcdir = $(CURDIR) -csv_SRCS = csv.c -csv_OBJS = $(addprefix ${plugins_BUILDDIR}/, $(addsuffix .o, $(basename ${csv_SRCS}))) -csv_LIBNAME = csv_plugin - -html_srcdir = $(CURDIR) -html_SRCS = html.c -html_OBJS = $(addprefix ${plugins_BUILDDIR}/, $(addsuffix .o, $(basename ${html_SRCS}))) -html_LIBNAME = html_plugin - -text_srcdir = $(CURDIR) -text_SRCS = text.c -text_OBJS = $(addprefix ${plugins_BUILDDIR}/, $(addsuffix .o, $(basename ${text_SRCS}))) -text_LIBNAME = text_plugin - -xml_srcdir = $(CURDIR) -xml_SRCS = xml.c -xml_OBJS = $(addprefix ${plugins_BUILDDIR}/, $(addsuffix .o, $(basename ${xml_SRCS}))) -xml_LIBNAME = xml_plugin - -json_srcdir = $(CURDIR) -json_SRCS = json.c -json_OBJS = $(addprefix ${plugins_BUILDDIR}/, $(addsuffix .o, $(basename ${json_SRCS}))) -json_LIBNAME = json_plugin - -####### Build rules - -.PHONY: plugins - -plugins: $(PLUGINS) - -csv: LIBNAME = $(csv_LIBNAME) -csv: $(csv_OBJS) - -html: LIBNAME = $(html_LIBNAME) -html: $(html_OBJS) - -text: LIBNAME = $(text_LIBNAME) -text: $(text_OBJS) - -xml: LIBNAME = $(xml_LIBNAME) -xml: $(xml_OBJS) - -json: LIBNAME = $(json_LIBNAME) -json: $(json_OBJS) - -$(PLUGINS): -ifeq ($(PLATFORM_OS), Linux) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o ${plugins_BUILDDIR}/$(LIBNAME).so $^ -else ifeq ($(PLATFORM_OS), NetBSD) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o ${plugins_BUILDDIR}/$(LIBNAME).so $^ -else ifeq ($(PLATFORM_OS), FreeBSD) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o ${plugins_BUILDDIR}/$(LIBNAME).so $^ -else ifeq ($(PLATFORM_OS), OpenBSD) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o ${plugins_BUILDDIR}/$(LIBNAME).so $^ -else ifeq ($(PLATFORM_OS), GNU) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o ${plugins_BUILDDIR}/$(LIBNAME).so $^ -else ifeq ($(PLATFORM_OS), GNU/kFreeBSD) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o ${plugins_BUILDDIR}/$(LIBNAME).so $^ -else ifeq ($(PLATFORM_OS), Darwin) - $(LINK) -headerpad_max_install_names -bundle \ - -undefined dynamic_lookup -fno-common \ - $(LDFLAGS) -o ${plugins_BUILDDIR}/$(LIBNAME).dylib $^ -else ifeq ($(PLATFORM_OS), CYGWIN) - $(LINK) -shared $(LDFLAGS) -o ${plugins_BUILDDIR}/$(LIBNAME).dll $^ -endif - -$(plugins_BUILDDIR)/%.o: %.c - @$(CHK_DIR_EXISTS) $(dir $@) || $(MKDIR) $(dir $@) - $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $^ - -clean: - $(RM_DIR) ${plugins_BUILDDIR} - -### - -install: installdirs - $(INSTALL_PROGRAM) $(INSTALL_FLAGS) -m 755 $(plugins_BUILDDIR)/$(csv_LIBNAME).* $(DESTDIR)$(pluginsdir) - $(INSTALL_PROGRAM) $(INSTALL_FLAGS) -m 755 $(plugins_BUILDDIR)/$(html_LIBNAME).* $(DESTDIR)$(pluginsdir) - $(INSTALL_PROGRAM) $(INSTALL_FLAGS) -m 755 $(plugins_BUILDDIR)/$(text_LIBNAME).* $(DESTDIR)$(pluginsdir) - $(INSTALL_PROGRAM) $(INSTALL_FLAGS) -m 755 $(plugins_BUILDDIR)/$(xml_LIBNAME).* $(DESTDIR)$(pluginsdir) - $(INSTALL_PROGRAM) $(INSTALL_FLAGS) -m 755 $(plugins_BUILDDIR)/$(json_LIBNAME).* $(DESTDIR)$(pluginsdir) - -install-strip: INSTALL_FLAGS += -s -install-strip: install - -installdirs: - @$(CHK_DIR_EXISTS) $(DESTDIR)$(pluginsdir) || $(MKDIR) $(DESTDIR)$(pluginsdir) - -uninstall: - $(RM_DIR) $(DESTDIR)$(pluginsdir) diff --git a/src/plugins/csv.c b/src/plugins/csv.c deleted file mode 100644 index 07de9ea0..00000000 --- a/src/plugins/csv.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - pev - the PE file analyzer toolkit - - csv.c - Principal implementation file for the CSV output plugin - - Copyright (C) 2012 - 2014 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. -*/ - -#include -#include -#include -#include "pev_api.h" -#include "output_plugin.h" - -const pev_api_t *g_pev_api = NULL; - -// REFERENCE: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references -// CSV entities ',', '"', '\n' -static const entity_t g_entities[255] = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - "\\n", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, "\"\"", NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, -}; - -static char *escape_csv(const format_t *format, const char *str) { - if (str == NULL) - return NULL; - // If `str` contains a line-break, or a double-quote, or a comma, - // escape and enclose the entire `str` with double quotes. - return strpbrk(str, "\n\",") != NULL - ? g_pev_api->output->escape_ex_quoted(str, format->entities_table) - : g_pev_api->output->escape_ex(str, format->entities_table); -} - -// -// The CSV output encloses fields with double quotes if they contain -// any of the following characters: -// -// a) line-break; -// b) double-quote; -// c) comma; -// -// Apart from the enclosing, any double-quote character found is escaped -// to 2 double-quote characters. -// -// KNOWN BUG: -// -// Our CSV output still doesn't follow the following rule: -// > Each record "should" contain the same number of comma-separated -// > fields. -// -// REFERENCE: http://en.wikipedia.org/wiki/Comma-separated_values -// -static void to_format( - const format_t *format, - const output_type_e type, - const output_scope_t *scope, - const char *key, - const char *value) -{ - char * const escaped_key = format->escape_fn(format, key); - char * const escaped_value = format->escape_fn(format, value); - - switch (type) { - default: - break; - case OUTPUT_TYPE_SCOPE_OPEN: - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - case OUTPUT_SCOPE_TYPE_ARRAY: - printf("\n%s\n", escaped_key); - break; - } - break; - case OUTPUT_TYPE_SCOPE_CLOSE: - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - case OUTPUT_SCOPE_TYPE_ARRAY: - printf("\n"); - break; - } - break; - case OUTPUT_TYPE_ATTRIBUTE: - if (key && value) - printf("%s,%s\n", escaped_key, escaped_value); - else if (key) - printf("\n%s\n", escaped_key); - else if (value) - printf(",%s\n", escaped_value); - break; - } - - if (escaped_key != NULL) - free(escaped_key); - if (escaped_value != NULL) - free(escaped_value); -} - -// ---------------------------------------------------------------------------- - -#define FORMAT_ID 1 -#define FORMAT_NAME "csv" - -static const format_t g_format = { - FORMAT_ID, - FORMAT_NAME, - &to_format, - &escape_csv, - (entity_table_t)g_entities -}; - -#define PLUGIN_TYPE "output" -#define PLUGIN_NAME FORMAT_NAME - -int plugin_loaded(void) { - //printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); - return 0; -} - -void plugin_unloaded(void) { - //printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); -} - -int plugin_initialize(const pev_api_t *api) { - g_pev_api = api; - int ret = g_pev_api->output->output_plugin_register_format(&g_format); - if (ret < 0) - return -1; - return 0; -} - -void plugin_shutdown(void) { - g_pev_api->output->output_plugin_unregister_format(&g_format); -} diff --git a/src/plugins/html.c b/src/plugins/html.c deleted file mode 100644 index 956588b5..00000000 --- a/src/plugins/html.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - pev - the PE file analyzer toolkit - - html.c - Principal implementation file for the HTML output plugin - - Copyright (C) 2012 - 2014 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. -*/ - -#include -#include -#include -#include "pev_api.h" -#include "output_plugin.h" - -const pev_api_t *g_pev_api = NULL; - -// REFERENCE: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references -// HTML entities '"', '&', '\'', '<', '>', ... -static const entity_t g_entities[255] = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, """,NULL, NULL, NULL, "&","'", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - "<", NULL, ">", NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, -}; - -static char *escape_html(const format_t *format, const char *str) { - return g_pev_api->output->escape(format, str); -} - -#define TEMPLATE_DOCUMENT_OPEN \ - "\n" \ - "\n" \ - "\n" \ - " \n" \ - " %s\n" \ - "\n" \ - "\n" - -#define TEMPLATE_DOCUMENT_CLOSE \ - "\n" \ - "\n" - -static void to_format( - const format_t *format, - const output_type_e type, - const output_scope_t *scope, - const char *key, - const char *value) -{ - static int indent = 0; - - char * const escaped_key = format->escape_fn(format, key); - char * const escaped_value = format->escape_fn(format, value); - const bool is_within_array = scope->parent_type == OUTPUT_SCOPE_TYPE_ARRAY; - - switch (type) { - default: - break; - case OUTPUT_TYPE_SCOPE_OPEN: - { - // NOTE: HTML doesn't allow `div` inside `ul`. If we're inside a `ul`, it - // means the parent is an array, so we can safely replace `div` by `li`. - const char * wrap_el = is_within_array ? "li" : "div"; - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - printf(TEMPLATE_DOCUMENT_OPEN, g_pev_api->output->output_cmdline()); - indent++; - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - printf(INDENT(indent++, "<%s class=\"object\">\n"), wrap_el); - printf(INDENT(indent, "

%s

\n"), escaped_key); - break; - case OUTPUT_SCOPE_TYPE_ARRAY: - printf(INDENT(indent++, "<%s class=\"array\">\n"), wrap_el); - printf(INDENT(indent, "

%s

\n"), escaped_key); - printf(INDENT(indent++, "
    \n")); - break; - } - break; - } - case OUTPUT_TYPE_SCOPE_CLOSE: - { - if (indent <= 0) { - fprintf(stderr, "html: programming error? indent is <= 0"); - abort(); - } - // NOTE: HTML doesn't allow `div` inside `ul`. If we're inside a `ul`, it - // means the parent is an array, so we can safely replace `div` by `li`. - const char * wrap_el = is_within_array ? "li" : "div"; - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - printf(TEMPLATE_DOCUMENT_CLOSE); - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - printf(INDENT(--indent, "\n"), wrap_el); - break; - case OUTPUT_SCOPE_TYPE_ARRAY: - printf(INDENT(--indent, "
\n")); - printf(INDENT(--indent, "\n"), wrap_el); - break; - } - break; - } - case OUTPUT_TYPE_ATTRIBUTE: - { - const char * wrap_el = scope->type == OUTPUT_SCOPE_TYPE_ARRAY ? "li" : "p"; - if (key && value) { - printf(INDENT(indent, "<%s>%s: %s\n"), wrap_el, escaped_key, escaped_value, wrap_el); - } else if (key) { - putchar('\n'); - printf(INDENT(indent, "<%s>%s\n"), wrap_el, escaped_key, wrap_el); - } else if (value) { - printf(INDENT(indent, "<%s>%s\n"), wrap_el, escaped_value, wrap_el); - } - break; - } - } - - if (escaped_key != NULL) - free(escaped_key); - if (escaped_value != NULL) - free(escaped_value); -} - -// ---------------------------------------------------------------------------- - -#define FORMAT_ID 2 -#define FORMAT_NAME "html" - -static const format_t g_format = { - FORMAT_ID, - FORMAT_NAME, - &to_format, - &escape_html, - (entity_table_t)g_entities -}; - -#define PLUGIN_TYPE "output" -#define PLUGIN_NAME FORMAT_NAME - -int plugin_loaded(void) { - //printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); - return 0; -} - -void plugin_unloaded(void) { - //printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); -} - -int plugin_initialize(const pev_api_t *api) { - g_pev_api = api; - int ret = g_pev_api->output->output_plugin_register_format(&g_format); - if (ret < 0) - return -1; - return 0; -} - -void plugin_shutdown(void) { - g_pev_api->output->output_plugin_unregister_format(&g_format); -} diff --git a/src/plugins/json.c b/src/plugins/json.c deleted file mode 100644 index 1c24000b..00000000 --- a/src/plugins/json.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - pev - the PE file analyzer toolkit - - json.c - Principal implementation file for the JSON output plugin - - Copyright (C) 2012 - 2014 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. -*/ - -#include -#include -#include -#include -#include "pev_api.h" -#include "output_plugin.h" - -const pev_api_t *g_pev_api = NULL; - -// REFERENCE: https://tools.ietf.org/html/rfc7159 -// JSON entities '"', '\', ... -static const entity_t g_entities[255] = { - NULL, "\\u0001","\\u0002","\\u0003","\\u0004","\\u0005","\\u0006","\\u0007","\\b","\\u0009", // 0-9 - "\\n", "\\t", "\\u000c","\\r","\\u000e","\\u000f","\\u0010","\\u0011","\\u0012","\\u0013", // 10-19 - "\\u0014","\\u0015","\\u0016","\\u0017","\\u0018","\\u0019","\\u001a","\\u001b","\\u001c","\\u001d", // 20-29 - "\\u001e","\\u001f",NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL, // 30-39 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 40-49 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 50-59 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 60-69 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 70-79 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 80-89 - NULL, NULL, "\\\\", NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 90-99 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 100-109 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 110-119 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f",NULL, NULL, // 120-129 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 130-139 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 140-149 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 150-159 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 160-169 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 170-179 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 180-189 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 190-199 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 200-209 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 210-219 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 220-229 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 230-239 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 240-249 - NULL, NULL, NULL, NULL, NULL, // 250-254 -}; - -static char *escape_json(const format_t *format, const char *str) { - return g_pev_api->output->escape(format, str); -} - -static void to_format( - const format_t *format, - const output_type_e type, - const output_scope_t *scope, - const char *key, - const char *value) -{ - static int indent = 0; - static int num_attr = 0; - - char * const escaped_key = format->escape_fn(format, key); - char * const escaped_value = format->escape_fn(format, value); - const bool is_within_array = scope->parent_type == OUTPUT_SCOPE_TYPE_ARRAY; - - switch (type) { - default: - break; - case OUTPUT_TYPE_SCOPE_OPEN: - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - printf(INDENT(indent++, "{")); - num_attr = 0; - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - // Already printed an attribute in the same scope? - if (num_attr > 0) - putchar(','); - putchar('\n'); - // NOTE: We don't want duplicate keys inside the array. - if (key && !is_within_array) - printf(INDENT(indent++, "\"%s\": {"), escaped_key); - else - printf(INDENT(indent++, "{")); - num_attr = 0; - break; - case OUTPUT_SCOPE_TYPE_ARRAY: - // Already printed an attribute in the same scope? - if (num_attr > 0) - putchar(','); - putchar('\n'); - // NOTE: We don't want duplicate keys inside the array. - if (key && !is_within_array) - printf(INDENT(indent++, "\"%s\": ["), escaped_key); - else - printf(INDENT(indent++, "[")); - num_attr = 0; - break; - } - break; - case OUTPUT_TYPE_SCOPE_CLOSE: - if (indent <= 0) { - fprintf(stderr, "json: programming error? indent is <= 0"); - abort(); - } - putchar('\n'); - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - printf(INDENT(--indent, "}\n")); - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - printf(INDENT(--indent, "}")); - break; - case OUTPUT_SCOPE_TYPE_ARRAY: - printf(INDENT(--indent, "]")); - break; - } - // Increment the number of attributes because this scope is itself an - // attribute. - num_attr++; - break; - case OUTPUT_TYPE_ATTRIBUTE: - // Already printed an attribute in the same scope? - if (num_attr > 0) - putchar(','); - putchar('\n'); - if (key && value) - printf(INDENT(indent, "\"%s\": \"%s\""), escaped_key, escaped_value); - else if (key) - printf(INDENT(indent, "\"%s\""), escaped_key); - else if (value) - printf(INDENT(indent, "\"%s\""), escaped_value); - num_attr++; - break; - } - - if (escaped_key != NULL) - free(escaped_key); - if (escaped_value != NULL) - free(escaped_value); -} - -// ---------------------------------------------------------------------------- - -#define FORMAT_ID 6 -#define FORMAT_NAME "json" - -static const format_t g_format = { - FORMAT_ID, - FORMAT_NAME, - &to_format, - &escape_json, - (entity_table_t)g_entities -}; - -#define PLUGIN_TYPE "output" -#define PLUGIN_NAME FORMAT_NAME - -int plugin_loaded(void) { - //printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); - return 0; -} - -void plugin_unloaded(void) { - //printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); -} - -int plugin_initialize(const pev_api_t *api) { - g_pev_api = api; - int ret = g_pev_api->output->output_plugin_register_format(&g_format); - if (ret < 0) - return -1; - return 0; -} - -void plugin_shutdown(void) { - g_pev_api->output->output_plugin_unregister_format(&g_format); -} diff --git a/src/plugins/output/csv.c b/src/plugins/output/csv.c new file mode 100644 index 00000000..7b946978 --- /dev/null +++ b/src/plugins/output/csv.c @@ -0,0 +1,215 @@ +/* + readpe - the PE file analyzer toolkit + + csv.c - Principal implementation file for the CSV output plugin + + 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 "output_plugin.h" +#include "plugin.h" + +#include +#include +#include + +#define FORMAT_ID 1 +#define FORMAT_NAME "csv" + +#define PLUGIN_TYPE "output" +#define PLUGIN_NAME FORMAT_NAME + +static const struct readpe_api *g_readpe_api = NULL; + +// ------------------------------------------------------------------------- // + +// REFERENCE: +// http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references +// CSV entities ',', '"', '\n' +static const entity_t g_entities[255] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\n", NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\"\"", NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, +}; + +// ------------------------------------------------------------------------- // + +static char *escape_csv(const struct format *format, const char *str) +{ + if (str == NULL) { + return NULL; + } + // If `str` contains a line-break, or a double-quote, or a comma, + // escape and enclose the entire `str` with double quotes. + return strpbrk(str, "\n\",") != NULL + ? g_readpe_api->output->escape_ex_quoted(str, + format->entities_table) + : g_readpe_api->output->escape_ex(str, format->entities_table); +} + +// +// The CSV output encloses fields with double quotes if they contain +// any of the following characters: +// +// a) line-break; +// b) double-quote; +// c) comma; +// +// Apart from the enclosing, any double-quote character found is escaped +// to 2 double-quote characters. +// +// KNOWN BUG: +// +// Our CSV output still doesn't follow the following rule: +// > Each record "should" contain the same number of comma-separated +// > fields. +// +// REFERENCE: http://en.wikipedia.org/wiki/Comma-separated_values +// +static void to_format(const struct format *format, const output_type_e type, + const output_scope_t *scope, const char *key, + const char *value) +{ + char *const escaped_key = format->escape_fn(format, key); + char *const escaped_value = format->escape_fn(format, value); + + switch (type) { + default: + break; + case OUTPUT_TYPE_SCOPE_OPEN: + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + case OUTPUT_SCOPE_TYPE_ARRAY: + printf("\n%s\n", escaped_key); + break; + } + break; + case OUTPUT_TYPE_SCOPE_CLOSE: + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + case OUTPUT_SCOPE_TYPE_ARRAY: + printf("\n"); + break; + } + break; + case OUTPUT_TYPE_ATTRIBUTE: + if (key && value) { + printf("%s,%s\n", escaped_key, escaped_value); + } else if (key) { + printf("\n%s\n", escaped_key); + } else if (value) { + printf(",%s\n", escaped_value); + } + break; + } + + if (escaped_key != NULL) { + free(escaped_key); + } + if (escaped_value != NULL) { + free(escaped_value); + } +} + +// ---------------------------------------------------------------------------- + +static const struct format g_format + = {.id = FORMAT_ID, + .name = FORMAT_NAME, + .output_fn = &to_format, + .escape_fn = &escape_csv, + .entities_table = (entity_table_t) g_entities}; + +// ------------------------------------------------------------------------- // + +static int plugin_loaded(void) +{ + // printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); + return 0; +} + +static void plugin_unloaded(void) +{ + // printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); +} + +static int plugin_initialize(const struct readpe_api *api) +{ + g_readpe_api = api; + int ret = g_readpe_api->output->register_format(&g_format); + if (ret < 0) { + return -1; + } + return 0; +} + +static void plugin_shutdown(void) +{ + g_readpe_api->output->unregister_format(&g_format); +} + +// ------------------------------------------------------------------------- // + +struct readpe_output_plugin readpe_plugin = { + {.type_id = readpe_plugin_type_output, + .loaded = plugin_loaded, + .initialize = plugin_initialize, + .shutdown = plugin_shutdown, + .unloaded = plugin_unloaded}, + g_format +}; + diff --git a/src/plugins/output/html.c b/src/plugins/output/html.c new file mode 100644 index 00000000..62dae1a6 --- /dev/null +++ b/src/plugins/output/html.c @@ -0,0 +1,244 @@ +/* + readpe - the PE file analyzer toolkit + + html.c - Principal implementation file for the HTML output plugin + + 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 "output_plugin.h" +#include "plugin.h" + +#include +#include +#include + +#define FORMAT_ID 2 +#define FORMAT_NAME "html" + +#define PLUGIN_TYPE "output" +#define PLUGIN_NAME FORMAT_NAME + +static const struct readpe_api *g_readpe_api = NULL; + +// ------------------------------------------------------------------------- // + +// REFERENCE: +// http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references +// HTML entities '"', '&', '\'', '<', '>', ... +static const entity_t g_entities[255] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, """, NULL, NULL, NULL, "&", "'", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "<", NULL, ">", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, +}; + +// ------------------------------------------------------------------------- // + +static char *escape_html(const format_t *format, const char *str) +{ + return g_readpe_api->output->escape(format, str); +} + +#define TEMPLATE_DOCUMENT_OPEN \ + "\n" \ + "\n" \ + "\n" \ + " \n" \ + " %s\n" \ + "\n" \ + "\n" + +#define TEMPLATE_DOCUMENT_CLOSE \ + "\n" \ + "\n" + +static void to_format(const format_t *format, const output_type_e type, + const output_scope_t *scope, const char *key, + const char *value) +{ + static int indent = 0; + + char *const escaped_key = format->escape_fn(format, key); + char *const escaped_value = format->escape_fn(format, value); + const bool is_within_array = scope->parent_type == OUTPUT_SCOPE_TYPE_ARRAY; + + switch (type) { + default: + break; + case OUTPUT_TYPE_SCOPE_OPEN: { + // NOTE: HTML doesn't allow `div` inside `ul`. If we're inside a `ul`, + // it + // means the parent is an array, so we can safely replace `div` by + // `li`. + const char *wrap_el = is_within_array ? "li" : "div"; + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + printf(TEMPLATE_DOCUMENT_OPEN, g_readpe_api->output->cmdline()); + indent++; + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + printf(INDENT(indent++, "<%s class=\"object\">\n"), wrap_el); + printf(INDENT(indent, "

%s

\n"), escaped_key); + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + printf(INDENT(indent++, "<%s class=\"array\">\n"), wrap_el); + printf(INDENT(indent, "

%s

\n"), escaped_key); + printf(INDENT(indent++, "
    \n")); + break; + } + break; + } + case OUTPUT_TYPE_SCOPE_CLOSE: { + if (indent <= 0) { + fprintf(stderr, "html: programming error? indent is <= 0"); + abort(); + } + // NOTE: HTML doesn't allow `div` inside `ul`. If we're inside a `ul`, + // it + // means the parent is an array, so we can safely replace `div` by + // `li`. + const char *wrap_el = is_within_array ? "li" : "div"; + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + printf(TEMPLATE_DOCUMENT_CLOSE); + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + printf(INDENT(--indent, "\n"), wrap_el); + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + printf(INDENT(--indent, "
\n")); + printf(INDENT(--indent, "\n"), wrap_el); + break; + } + break; + } + case OUTPUT_TYPE_ATTRIBUTE: { + const char *wrap_el + = scope->type == OUTPUT_SCOPE_TYPE_ARRAY ? "li" : "p"; + if (key && value) { + printf(INDENT(indent, "<%s>%s: " + "%s\n"), + wrap_el, escaped_key, escaped_value, wrap_el); + } else if (key) { + putchar('\n'); + printf(INDENT(indent, + "<%s>%s\n"), + wrap_el, escaped_key, wrap_el); + } else if (value) { + printf(INDENT(indent, "<%s>%s\n"), + wrap_el, escaped_value, wrap_el); + } + break; + } + } + + if (escaped_key != NULL) { + free(escaped_key); + } + if (escaped_value != NULL) { + free(escaped_value); + } +} + +// ---------------------------------------------------------------------------- + +static const struct format g_format + = {.id = FORMAT_ID, + .name = FORMAT_NAME, + .output_fn = &to_format, + .escape_fn = &escape_html, + .entities_table = (entity_table_t) g_entities}; + +// ------------------------------------------------------------------------- // + +static int plugin_loaded(void) +{ + // printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); + return 0; +} + +static void plugin_unloaded(void) +{ + // printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); +} + +static int plugin_initialize(const struct readpe_api *api) +{ + g_readpe_api = api; + int ret = g_readpe_api->output->register_format(&g_format); + if (ret < 0) { + return -1; + } + return 0; +} + +static void plugin_shutdown(void) +{ + g_readpe_api->output->unregister_format(&g_format); +} + +// ------------------------------------------------------------------------- // + +struct readpe_output_plugin readpe_plugin = { + {.type_id = readpe_plugin_type_output, + .loaded = plugin_loaded, + .initialize = plugin_initialize, + .shutdown = plugin_shutdown, + .unloaded = plugin_unloaded}, + g_format +}; + diff --git a/src/plugins/output/json.c b/src/plugins/output/json.c new file mode 100644 index 00000000..2bcf818b --- /dev/null +++ b/src/plugins/output/json.c @@ -0,0 +1,264 @@ +/* + readpe - the PE file analyzer toolkit + + json.c - Principal implementation file for the JSON output plugin + + 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 "output_plugin.h" +#include "plugin.h" + +#include +#include +#include +#include + +#define FORMAT_ID 6 +#define FORMAT_NAME "json" +#define PLUGIN_TYPE "output" +#define PLUGIN_NAME FORMAT_NAME + +static const struct readpe_api *g_readpe_api = NULL; + +// ------------------------------------------------------------------------- // + +// REFERENCE: https://tools.ietf.org/html/rfc7159 +// JSON entities '"', '\', ... +static const entity_t g_entities[255] = { + NULL, "\\u0001", "\\u0002", "\\u0003", "\\u0004", + "\\u0005", "\\u0006", "\\u0007", "\\b", "\\u0009", // 0-9 + "\\n", "\\t", "\\u000c", "\\r", "\\u000e", + "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", // 10-19 + "\\u0014", "\\u0015", "\\u0016", "\\u0017", "\\u0018", + "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", // 20-29 + "\\u001e", "\\u001f", NULL, NULL, "\\\"", + NULL, NULL, NULL, NULL, NULL, // 30-39 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 40-49 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 50-59 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 60-69 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 70-79 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 80-89 + NULL, NULL, "\\\\", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 90-99 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 100-109 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 110-119 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "\\u007f", NULL, NULL, // 120-129 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 130-139 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 140-149 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 150-159 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 160-169 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 170-179 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 180-189 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 190-199 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 200-209 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 210-219 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 220-229 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 230-239 + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, // 240-249 + NULL, NULL, NULL, NULL, NULL, // 250-254 +}; + +// ------------------------------------------------------------------------- // + +static char *escape_json(const struct format *format, const char *str) +{ + return g_readpe_api->output->escape(format, str); +} + +static void to_format(const struct format *format, const output_type_e type, + const output_scope_t *scope, const char *key, + const char *value) +{ + static int indent = 0; + static int num_attr = 0; + + char *const escaped_key = format->escape_fn(format, key); + char *const escaped_value = format->escape_fn(format, value); + const bool is_within_array = scope->parent_type == OUTPUT_SCOPE_TYPE_ARRAY; + + switch (type) { + case OUTPUT_TYPE_SCOPE_OPEN: + switch (scope->type) { + case OUTPUT_SCOPE_TYPE_DOCUMENT: + printf(INDENT(indent++, "{")); + num_attr = 0; + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + // Already printed an attribute in the same scope? + if (num_attr > 0) { + putchar(','); + } + putchar('\n'); + // NOTE: We don't want duplicate keys inside the array. + if (key && ! is_within_array) { + printf(INDENT(indent++, "\"%s\": {"), escaped_key); + } else { + printf(INDENT(indent++, "{")); + } + num_attr = 0; + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + // Already printed an attribute in the same scope? + if (num_attr > 0) { + putchar(','); + } + putchar('\n'); + // NOTE: We don't want duplicate keys inside the array. + if (key && ! is_within_array) { + printf(INDENT(indent++, "\"%s\": ["), escaped_key); + } else { + printf(INDENT(indent++, "[")); + } + num_attr = 0; + break; + default: + break; + } + break; + case OUTPUT_TYPE_SCOPE_CLOSE: + if (indent <= 0) { + fprintf(stderr, "json: programming error? indent is <= 0"); + abort(); + } + putchar('\n'); + switch (scope->type) { + case OUTPUT_SCOPE_TYPE_DOCUMENT: + printf(INDENT(--indent, "}\n")); + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + printf(INDENT(--indent, "}")); + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + printf(INDENT(--indent, "]")); + break; + default: + break; + } + // Increment the number of attributes because this scope is itself an + // attribute. + num_attr++; + break; + case OUTPUT_TYPE_ATTRIBUTE: + // Already printed an attribute in the same scope? + if (num_attr > 0) { + putchar(','); + } + putchar('\n'); + if (key && value) { + printf(INDENT(indent, "\"%s\": \"%s\""), escaped_key, + escaped_value); + } else if (key) { + printf(INDENT(indent, "\"%s\""), escaped_key); + } else if (value) { + printf(INDENT(indent, "\"%s\""), escaped_value); + } + num_attr++; + break; + default: + break; + } + + if (escaped_key != NULL) { + free(escaped_key); + } + if (escaped_value != NULL) { + free(escaped_value); + } +} + +// ------------------------------------------------------------------------- // + +static const struct format g_format + = {.id = FORMAT_ID, + .name = FORMAT_NAME, + .output_fn = &to_format, + .escape_fn = &escape_json, + .entities_table = (entity_table_t) g_entities}; + +// ------------------------------------------------------------------------- // + +static int plugin_loaded(void) +{ + // printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); + return 0; +} + +static void plugin_unloaded(void) +{ + // printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); +} + +static int plugin_initialize(const struct readpe_api *api) +{ + g_readpe_api = api; + int ret = g_readpe_api->output->register_format(&g_format); + if (ret < 0) { + return -1; + } + return 0; +} + +static void plugin_shutdown(void) +{ + g_readpe_api->output->unregister_format(&g_format); +} + +// ------------------------------------------------------------------------- // + +struct readpe_output_plugin readpe_plugin = { + {.type_id = readpe_plugin_type_output, + .loaded = plugin_loaded, + .initialize = plugin_initialize, + .shutdown = plugin_shutdown, + .unloaded = plugin_unloaded}, + g_format +}; + diff --git a/src/plugins/output/xml.c b/src/plugins/output/xml.c new file mode 100644 index 00000000..2288f3c0 --- /dev/null +++ b/src/plugins/output/xml.c @@ -0,0 +1,222 @@ +/* + readpe - the PE file analyzer toolkit + + xml.c - Principal implementation file for the XML output plugin + + 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 "output_plugin.h" +#include "plugin.h" + +#include +#include + +#define FORMAT_ID 4 +#define FORMAT_NAME "xml" + +#define PLUGIN_TYPE "output" +#define PLUGIN_NAME FORMAT_NAME + +static const struct readpe_api *g_readpe_api = NULL; + +// ------------------------------------------------------------------------- // + +// REFERENCE: +// http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references +// XML entities '"', '&', '\'', '<', '>' +static const entity_t g_entities[255] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, """, NULL, NULL, NULL, "&", "'", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "<", NULL, ">", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, +}; + +// ------------------------------------------------------------------------- // + +static char *escape_xml(const format_t *format, const char *str) +{ + return g_readpe_api->output->escape(format, str); +} + +#define TEMPLATE_DOCUMENT_OPEN \ + "\n\n" + +#define TEMPLATE_DOCUMENT_CLOSE "\n" + +static void to_format(const format_t *format, const output_type_e type, + const output_scope_t *scope, const char *key, + const char *value) +{ + static int indent = 0; + + // FIXME(jweyrich): Somehow output the XML root element. + + char *const escaped_key = format->escape_fn(format, key); + char *const escaped_value = format->escape_fn(format, value); + + // + // Quoting http://www.w3schools.com/xml/xml_elements.asp + // + // XML Naming Rules + // XML elements must follow these naming rules: + // Names can contain letters, numbers, and other characters + // Names cannot start with a number or punctuation character + // Names cannot start with the letters xml (or XML, or Xml, etc) + // Names cannot contain spaces + // + switch (type) { + default: + break; + case OUTPUT_TYPE_SCOPE_OPEN: + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + printf(TEMPLATE_DOCUMENT_OPEN, g_readpe_api->output->cmdline()); + indent++; + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + printf(INDENT(indent++, "\n"), escaped_key); + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + printf(INDENT(indent++, "\n"), escaped_key); + break; + } + break; + case OUTPUT_TYPE_SCOPE_CLOSE: + if (indent <= 0) { + fprintf(stderr, "xml: programming error? indent is <= 0"); + abort(); + } + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + printf(TEMPLATE_DOCUMENT_CLOSE); + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + printf(INDENT(--indent, "\n")); + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + printf(INDENT(--indent, "\n")); + break; + } + break; + case OUTPUT_TYPE_ATTRIBUTE: + if (key && value) { + printf(INDENT(indent, "%s\n"), + escaped_key, escaped_value); + } else if (key) { + printf(INDENT(indent, "\n"), escaped_key); + } else if (value) { + printf(INDENT(indent, "%s\n"), value); + } + break; + } + + if (escaped_key != NULL) { + free(escaped_key); + } + if (escaped_value != NULL) { + free(escaped_value); + } +} + +// ------------------------------------------------------------------------- // + +static const struct format g_format + = {.id = FORMAT_ID, + .name = FORMAT_NAME, + .output_fn = &to_format, + .escape_fn = &escape_xml, + .entities_table = (entity_table_t) g_entities}; + +// ------------------------------------------------------------------------- // + +static int plugin_loaded(void) +{ + // printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); + return 0; +} + +static void plugin_unloaded(void) +{ + // printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); +} + +static int plugin_initialize(const struct readpe_api *api) +{ + g_readpe_api = api; + int ret = g_readpe_api->output->register_format(&g_format); + if (ret < 0) { + return -1; + } + return 0; +} + +static void plugin_shutdown(void) +{ + g_readpe_api->output->unregister_format(&g_format); +} + +// ------------------------------------------------------------------------- // + +struct readpe_output_plugin readpe_plugin = { + {.type_id = readpe_plugin_type_output, + .loaded = plugin_loaded, + .initialize = plugin_initialize, + .shutdown = plugin_shutdown, + .unloaded = plugin_unloaded}, + g_format +}; + diff --git a/src/plugins/text.c b/src/plugins/text.c deleted file mode 100644 index ace85f11..00000000 --- a/src/plugins/text.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - pev - the PE file analyzer toolkit - - text.c - Principal implementation file for the TEXT output plugin - - Copyright (C) 2012 - 2014 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. -*/ - -#include -#include -#include -#include "pev_api.h" -#include "output_plugin.h" - -const pev_api_t *g_pev_api = NULL; - -static char *escape_text(const format_t *format, const char *str) { - return g_pev_api->output->escape(format, str); -} - -#define SPACES 32 // spaces # for text-based output - -static void to_format( - const format_t *format, - const output_type_e type, - const output_scope_t *scope, - const char *key, - const char *value) -{ - static int indent = 0; - - char * const escaped_key = format->escape_fn(format, key); - char * const escaped_value = format->escape_fn(format, value); - - switch (type) { - default: - break; - case OUTPUT_TYPE_SCOPE_OPEN: - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - if (key) { - printf(INDENT(indent++, "%s\n"), escaped_key); - } else { - indent++; - } - break; - case OUTPUT_SCOPE_TYPE_ARRAY: - //putchar('\n'); - if (key) { - printf(INDENT(indent++, "%s\n"), escaped_key); - } else { - indent++; - } - break; - } - break; - case OUTPUT_TYPE_SCOPE_CLOSE: - indent--; - break; - case OUTPUT_TYPE_ATTRIBUTE: - { - const size_t key_size = key ? strlen(key) : 0; - if (key && value) { - printf(INDENT(indent, "%s:%*c%s\n"), escaped_key, (int)(SPACES - key_size), ' ', escaped_value); - } else if (key) { - printf(INDENT(indent, "%s\n"), escaped_key); - } else if (value) { - printf(INDENT(indent, "%*c%s\n"), (int)(SPACES - key_size + 1), ' ', escaped_value); - } - break; - } - } - - if (escaped_key != NULL) - free(escaped_key); - if (escaped_value != NULL) - free(escaped_value); -} - -// ---------------------------------------------------------------------------- - -#define FORMAT_ID 3 -#define FORMAT_NAME "text" - -static const format_t g_format = { - FORMAT_ID, - FORMAT_NAME, - &to_format, - &escape_text, - NULL -}; - -#define PLUGIN_TYPE "output" -#define PLUGIN_NAME FORMAT_NAME - -int plugin_loaded(void) { - //printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); - return 0; -} - -void plugin_unloaded(void) { - //printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); -} - -int plugin_initialize(const pev_api_t *api) { - g_pev_api = api; - int ret = g_pev_api->output->output_plugin_register_format(&g_format); - if (ret < 0) - return -1; - return 0; -} - -void plugin_shutdown(void) { - g_pev_api->output->output_plugin_unregister_format(&g_format); -} diff --git a/src/plugins/xml.c b/src/plugins/xml.c deleted file mode 100644 index da0ad718..00000000 --- a/src/plugins/xml.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - pev - the PE file analyzer toolkit - - xml.c - Principal implementation file for the XML output plugin - - Copyright (C) 2012 - 2014 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. -*/ - -#include -#include -#include "pev_api.h" -#include "output_plugin.h" - -const pev_api_t *g_pev_api = NULL; - -// REFERENCE: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references -// XML entities '"', '&', '\'', '<', '>' -static const entity_t g_entities[255] = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, """,NULL, NULL, NULL, "&","'", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - "<", NULL, ">", NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, -}; - -static char *escape_xml(const format_t *format, const char *str) { - return g_pev_api->output->escape(format, str); -} - -#define TEMPLATE_DOCUMENT_OPEN \ - "\n" - -#define TEMPLATE_DOCUMENT_CLOSE \ - "\n" - -static void to_format( - const format_t *format, - const output_type_e type, - const output_scope_t *scope, - const char *key, - const char *value) -{ - static int indent = 0; - - // FIXME(jweyrich): Somehow output the XML root element. - - char * const escaped_key = format->escape_fn(format, key); - char * const escaped_value = format->escape_fn(format, value); - - // - // Quoting http://www.w3schools.com/xml/xml_elements.asp - // - // XML Naming Rules - // XML elements must follow these naming rules: - // Names can contain letters, numbers, and other characters - // Names cannot start with a number or punctuation character - // Names cannot start with the letters xml (or XML, or Xml, etc) - // Names cannot contain spaces - // - switch (type) { - default: - break; - case OUTPUT_TYPE_SCOPE_OPEN: - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - printf(TEMPLATE_DOCUMENT_OPEN, g_pev_api->output->output_cmdline()); - indent++; - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - printf(INDENT(indent++, "\n"), escaped_key); - break; - case OUTPUT_SCOPE_TYPE_ARRAY: - printf(INDENT(indent++, "\n"), escaped_key); - break; - } - break; - case OUTPUT_TYPE_SCOPE_CLOSE: - if (indent <= 0) { - fprintf(stderr, "xml: programming error? indent is <= 0"); - abort(); - } - switch (scope->type) { - default: - break; - case OUTPUT_SCOPE_TYPE_DOCUMENT: - printf(TEMPLATE_DOCUMENT_CLOSE); - break; - case OUTPUT_SCOPE_TYPE_OBJECT: - printf(INDENT(--indent, "\n")); - break; - case OUTPUT_SCOPE_TYPE_ARRAY: - printf(INDENT(--indent, "\n")); - break; - } - break; - case OUTPUT_TYPE_ATTRIBUTE: - if (key && value) { - printf(INDENT(indent, "%s\n"), escaped_key, escaped_value); - } else if (key) { - printf(INDENT(indent, "\n"), escaped_key); - } else if (value) { - printf(INDENT(indent, "%s\n"), value); - } - break; - } - - if (escaped_key != NULL) - free(escaped_key); - if (escaped_value != NULL) - free(escaped_value); -} - -// ---------------------------------------------------------------------------- - -#define FORMAT_ID 4 -#define FORMAT_NAME "xml" - -static const format_t g_format = { - FORMAT_ID, - FORMAT_NAME, - &to_format, - &escape_xml, - (entity_table_t)g_entities -}; - -#define PLUGIN_TYPE "output" -#define PLUGIN_NAME FORMAT_NAME - -int plugin_loaded(void) { - //printf("Loading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); - return 0; -} - -void plugin_unloaded(void) { - //printf("Unloading %s plugin %s\n", PLUGIN_TYPE, PLUGIN_NAME); -} - -int plugin_initialize(const pev_api_t *api) { - g_pev_api = api; - int ret = g_pev_api->output->output_plugin_register_format(&g_format); - if (ret < 0) - return -1; - return 0; -} - -void plugin_shutdown(void) { - g_pev_api->output->output_plugin_unregister_format(&g_format); -} diff --git a/src/readpe.c b/src/readpe.c deleted file mode 100644 index b35c8fc5..00000000 --- a/src/readpe.c +++ /dev/null @@ -1,1220 +0,0 @@ -/* vim :set ts=4 sw=4 sts=4 et : */ -/* - pev - the PE file analyzer toolkit - - readpe.c - show PE file headers - - 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. -*/ - -#include "common.h" -#include "output.h" -#include -#include - -#define PROGRAM "readpe" - -typedef struct { - bool all; - bool dos; - bool coff; - bool opt; - bool dirs; - bool imports; - bool exports; - bool all_headers; - bool all_sections; -} options_t; - -static void usage(void) -{ - static char formats[255]; - output_available_formats(formats, sizeof(formats), '|'); - printf("Usage: %s OPTIONS FILE\n" - "Show PE file headers\n" - "\nExample: %s --header optional winzip.exe\n" - "\nOptions:\n" - " -A, --all Full output (default).\n" - " -H, --all-headers Show all PE headers.\n" - " -S, --all-sections Show PE section headers.\n" - " -f, --format <%s> Change output format (default: text).\n" - " -d, --dirs Show data directories.\n" - " -h, --header Show specific header. It can be used multiple times.\n" - " -i, --imports Show imported functions.\n" - " -e, --exports Show exported functions.\n" - " -V, --version Show version.\n" - " --help Show this help.\n", - PROGRAM, PROGRAM, formats); -} - -static void parse_headers(options_t *options, const char *optarg) -{ - if (!strcmp(optarg, "dos")) - options->dos = true; - else if (!strcmp(optarg, "coff")) - options->coff = true; - else if (!strcmp(optarg, "optional")) - options->opt = true; - else - EXIT_ERROR("invalid header option"); -} - -static void free_options(options_t *options) -{ - // FIX: Don't need to test for NULL pointer. - //if (options == NULL) - // return; - - free(options); -} - -static options_t *parse_options(int argc, char *argv[]) -{ - options_t *options = calloc_s(1, sizeof(options_t)); - - /* Parameters for getopt_long() function */ - static const char short_options[] = "AHSh:dief:V"; - - static const struct option long_options[] = { - { "help", no_argument, NULL, 1 }, - { "all", no_argument, NULL, 'A' }, - { "all-headers", no_argument, NULL, 'H' }, - { "all-sections", no_argument, NULL, 'S' }, - { "header", required_argument, NULL, 'h' }, - { "imports", no_argument, NULL, 'i' }, - { "exports", no_argument, NULL, 'e' }, - { "dirs", no_argument, NULL, 'd' }, - { "format", required_argument, NULL, 'f' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - options->all = true; - - int c, ind; - - while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) - { - if (c < 0) - break; - - switch (c) - { - case 1: // --help option - usage(); - exit(EXIT_SUCCESS); - case 'A': - options->all = true; - break; - case 'H': - options->all = false; - options->all_headers = true; - break; - case 'd': - options->all = false; - options->dirs = true; - break; - case 'S': - options->all = false; - options->all_sections = true; - break; - case 'V': - printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); - exit(EXIT_SUCCESS); - case 'h': - options->all = false; - parse_headers(options, optarg); - break; - case 'i': - options->all = false; - options->imports = true; - break; - case 'e': - options->all = false; - options->exports = true; - break; - case 'f': - if (output_set_format_by_name(optarg) < 0) - EXIT_ERROR("invalid format option"); - break; - default: - fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); - exit(EXIT_FAILURE); - } - } - - return options; -} - -static void print_sections(pe_ctx_t *ctx) -{ -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - static const char * const flags_name[] = { - "contains executable code", - "contains initialized data", - "contains uninitialized data", - "contains data referenced through the GP", - "contains extended relocations", - "can be discarded as needed", - "cannot be cached", - "is not pageable", - "can be shared in memory", - "is executable", - "is readable", - "is writable" - }; - // valid flags only for executables referenced in pecoffv8 - static const unsigned int valid_flags[] = { - IMAGE_SCN_CNT_CODE, - IMAGE_SCN_CNT_INITIALIZED_DATA, - IMAGE_SCN_CNT_UNINITIALIZED_DATA, - IMAGE_SCN_GPREL, - IMAGE_SCN_LNK_NRELOC_OVFL, - IMAGE_SCN_MEM_DISCARDABLE, - IMAGE_SCN_MEM_NOT_CACHED, - IMAGE_SCN_MEM_NOT_PAGED, - IMAGE_SCN_MEM_SHARED, - IMAGE_SCN_MEM_EXECUTE, - IMAGE_SCN_MEM_READ, - IMAGE_SCN_MEM_WRITE - }; - static const size_t max_flags = LIBPE_SIZEOF_ARRAY(valid_flags); -#endif - - output_open_scope("Sections", OUTPUT_SCOPE_TYPE_ARRAY); - - const uint32_t num_sections = pe_sections_count(ctx); - if (num_sections == 0 || num_sections > MAX_SECTIONS) - return; - - IMAGE_SECTION_HEADER **sections = pe_sections(ctx); - if (sections == NULL) - return; - - static char s[MAX_MSG]; - static char section_name_buffer[SECTION_NAME_SIZE+1]; - - for (uint32_t i=0; i < num_sections; i++) - { - output_open_scope("Section", OUTPUT_SCOPE_TYPE_OBJECT); - - const char *section_name = pe_section_name(ctx, sections[i], section_name_buffer, sizeof(section_name_buffer)); - output("Name", section_name); - - snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)", sections[i]->Misc.VirtualSize, - sections[i]->Misc.VirtualSize); - output("Virtual Size", s); - - snprintf(s, MAX_MSG, "%#x", sections[i]->VirtualAddress); - output("Virtual Address", s); - - snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)", sections[i]->SizeOfRawData, - sections[i]->SizeOfRawData); - output("Size Of Raw Data", s); - - snprintf(s, MAX_MSG, "%#x", sections[i]->PointerToRawData); - output("Pointer To Raw Data", s); - - snprintf(s, MAX_MSG, "%" PRIu16, sections[i]->NumberOfRelocations); - output("Number Of Relocations", s); - - snprintf(s, MAX_MSG, "%#x", sections[i]->Characteristics); - output("Characteristics", s); - - output_open_scope("Characteristic Names", OUTPUT_SCOPE_TYPE_ARRAY); - -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - for (size_t j=0; j < max_flags; j++) { - if (sections[i]->Characteristics & valid_flags[j]) { - snprintf(s, MAX_MSG, "%s", flags_name[j]); - output(NULL, s); - } - } -#else - if (pe_use_rom_section_characteristic(ctx)) { - for (unsigned int flag = 1; flag != 0; flag <<= 1) { - if (sections[i]->Characteristics & flag) { - const char *characteristic_name = pe_rom_section_characteristic_name(flag); - char formatted_characteristic_name[32]; - if (characteristic_name == NULL) { - snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); - characteristic_name = formatted_characteristic_name; - } - output(NULL, characteristic_name); - } - } - } else { - for (unsigned int flag = 1; flag != 0; flag <<= 1) { - if (flag & 0x00F00000) - continue; - if (sections[i]->Characteristics & flag) { - const char *characteristic_name = NULL; - char formatted_characteristic_name[32]; - if (pe_coff(ctx)->Machine == IMAGE_FILE_MACHINE_M68K) - characteristic_name = pe_m68k_section_characteristic_name(flag); - if (characteristic_name == NULL) - characteristic_name = pe_section_characteristic_name(flag); - if (characteristic_name == NULL) { - snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); - characteristic_name = formatted_characteristic_name; - } - output(NULL, characteristic_name); - } - } - if (sections[i]->Characteristics & 0x00F00000) { - unsigned int flag = sections[i]->Characteristics & 0x00F00000; - const char *characteristic_name = pe_section_characteristic_name(flag); - char formatted_characteristic_name[32]; - if (characteristic_name == NULL) { - snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); - characteristic_name = formatted_characteristic_name; - } - output(NULL, characteristic_name); - } - } -#endif - - output_close_scope(); // Characteristic Names - - output_close_scope(); // Section - } - - output_close_scope(); // Sections -} - -static 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); -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - output(directoryEntryNames[i].name, s); -#else - output(pe_directory_name(i), s); -#endif - output_close_scope(); // Directory - } - } - - output_close_scope(); // Data directories -} - -static void print_optional_header(pe_ctx_t *ctx, IMAGE_OPTIONAL_HEADER *header) -{ -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - typedef struct { - WindowsSubsystem subsystem; - const char * const name; - } WindowsSubsystemName; - static const WindowsSubsystemName subsystemNames[] = { - { IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, - { IMAGE_SUBSYSTEM_NATIVE, "System native" }, - { IMAGE_SUBSYSTEM_WINDOWS_GUI, "Windows GUI" }, - { IMAGE_SUBSYSTEM_WINDOWS_CUI, "Windows CLI" }, - { IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, - { IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, - { IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, - { IMAGE_SUBSYSTEM_POSIX_CUI, "Posix CLI" }, - { IMAGE_SUBSYSTEM_WINDOWS_CE_GUI, "Windows CE GUI" }, - { IMAGE_SUBSYSTEM_EFI_APPLICATION, "EFI application" }, - { IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER, "EFI driver with boot" }, - { IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER, "EFI run-time driver" }, - { IMAGE_SUBSYSTEM_EFI_ROM, "EFI ROM" }, - { IMAGE_SUBSYSTEM_XBOX, "XBOX" }, - { IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION, "Boot application" } - }; - static const size_t max_subsystem = LIBPE_SIZEOF_ARRAY(subsystemNames); -#endif - - if (!header) - return; - - static char s[MAX_MSG]; - - output_open_scope("Optional/Image header", OUTPUT_SCOPE_TYPE_OBJECT); - - switch (header->type) - { - case MAGIC_ROM: - { - snprintf(s, MAX_MSG, "%#x (%s)", header->_rom->Magic, "ROM"); - output("Magic number", s); - - snprintf(s, MAX_MSG, "%" PRIu8, header->_rom->MajorLinkerVersion); - output("Linker major version", s); - - snprintf(s, MAX_MSG, "%" PRIu8, header->_rom->MinorLinkerVersion); - output("Linker minor version", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfCode); - output("Size of .text section", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfInitializedData); - output("Size of .data section", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfUninitializedData); - output("Size of .bss section", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->AddressOfEntryPoint); - output("Entrypoint", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfCode); - output("Address of .text section", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfData); - output("Address of .data section", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfBss); - output("Address of .bss section", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->GprMask); - output("GprMask", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[0]); - output("CprMask[0]", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[1]); - output("CprMask[1]", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[2]); - output("CprMask[2]", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[3]); - output("CprMask[3]", s); - - snprintf(s, MAX_MSG, "%#x", header->_rom->GpValue); - output("GpValue", s); - break; - } - case MAGIC_PE32_0: - case MAGIC_PE32: - { - snprintf(s, MAX_MSG, "%#x (%s)", header->_32->Magic, header->type == MAGIC_PE32_0 ? "PE32 ZERO" : "PE32"); - output("Magic number", s); - - snprintf(s, MAX_MSG, "%" PRIu8, header->_32->MajorLinkerVersion); - output("Linker major version", s); - - snprintf(s, MAX_MSG, "%" PRIu8, header->_32->MinorLinkerVersion); - output("Linker minor version", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfCode); - output("Size of .text section", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfInitializedData); - output("Size of .data section", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfUninitializedData); - output("Size of .bss section", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->AddressOfEntryPoint); - output("Entrypoint", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->BaseOfCode); - output("Address of .text section", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->BaseOfData); - output("Address of .data section", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->ImageBase); - output("ImageBase", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->SectionAlignment); - output("Alignment of sections", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->FileAlignment); - output("Alignment factor", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MajorOperatingSystemVersion); - output("Major version of required OS", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MinorOperatingSystemVersion); - output("Minor version of required OS", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MajorImageVersion); - output("Major version of image", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MinorImageVersion); - output("Minor version of image", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MajorSubsystemVersion); - output("Major version of subsystem", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MinorSubsystemVersion); - output("Minor version of subsystem", s); - -#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - snprintf(s, MAX_MSG, "Win32 version value: %#x", header->_32->Win32VersionValue); - output_open_scope(s, OUTPUT_SCOPE_TYPE_OBJECT); - - if (header->_32->Win32VersionValue == 0) - strcpy(s, "(default)"); - else - snprintf(s, MAX_MSG, "%u", header->_32->Win32VersionValue & 0xff); - output("Overwrite OS major version", s); - - if (header->_32->Win32VersionValue == 0) - strcpy(s, "(default)"); - else - snprintf(s, MAX_MSG, "%u", (header->_32->Win32VersionValue >> 8) & 0xff); - output("Overwrite OS minor version", s); - - if (header->_32->Win32VersionValue == 0) - strcpy(s, "(default)"); - else - snprintf(s, MAX_MSG, "%u", (header->_32->Win32VersionValue >> 16) & 0x3fff); - output("Overwrite OS build number", s); - - if (header->_32->Win32VersionValue == 0) - strcpy(s, "(default)"); - else { - uint8_t platform_id = header->_32->Win32VersionValue >> 30; - static const char *const win32_version_value_platform_id[4] = { "NT", "CE", "Win32s", "Win9x" }; - snprintf(s, MAX_MSG, "%u (%s)", platform_id, win32_version_value_platform_id[platform_id]); - } - output("Overwrite OS platform id", s); - - output_close_scope(); -#endif - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfImage); - output("Size of image", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeaders); - output("Size of headers", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->CheckSum); - output("Checksum", s); - - const uint16_t subsystem = header->_32->Subsystem; -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - const char *subsystem_name = "Unknown"; - for (size_t i=0; i < max_subsystem; i++) { - if (subsystem == subsystemNames[i].subsystem) - subsystem_name = subsystemNames[i].name; - } -#else - const char *subsystem_name = pe_windows_subsystem_name(subsystem); - if (subsystem_name == NULL) - subsystem_name = "Unknown"; -#endif - snprintf(s, MAX_MSG, "%#x (%s)", subsystem, subsystem_name); - output("Subsystem required", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->DllCharacteristics); - output("DLL characteristics", s); - -#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - output_open_scope("DLL characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); - - for (uint16_t i=0, flag=0x0001; i < 16; i++, flag <<= 1) { - if (header->_32->DllCharacteristics & flag) { - const char *characteristic_name = NULL; - char formatted_characteristic_name[32]; - if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) - characteristic_name = pe_dll_image_dllcharacteristic_name(flag); - if (characteristic_name == NULL) - characteristic_name = pe_image_dllcharacteristic_name(flag); - if (characteristic_name == NULL) { - snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); - characteristic_name = formatted_characteristic_name; - } - output(NULL, characteristic_name); - } - } - - output_close_scope(); // DLL characteristics names -#endif - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfStackReserve); - output("Size of stack to reserve", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfStackCommit); - output("Size of stack to commit", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeapReserve); - output("Size of heap space to reserve", s); - - snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeapCommit); - output("Size of heap space to commit", s); - -#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - snprintf(s, MAX_MSG, "%#x", header->_32->LoaderFlags); - output("Loader Flags", s); - - output_open_scope("Loader Flags names", OUTPUT_SCOPE_TYPE_ARRAY); - - for (uint32_t i=0, flag=0x00000001; i < 32; i++, flag <<= 1) { - if (header->_32->LoaderFlags & flag) { - const char *flag_name = NULL; - char formatted_flag_name[32]; - if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) - flag_name = pe_dll_image_loader_flags_name(flag); - if (flag_name == NULL) - flag_name = pe_image_loader_flags_name(flag); - if (flag_name == NULL) { - snprintf(formatted_flag_name, sizeof(formatted_flag_name)-1, "UNKNOWN[%#x]", flag); - flag_name = formatted_flag_name; - } - output(NULL, flag_name); - } - } - - output_close_scope(); // Loader Flags names -#endif - - break; - } - case MAGIC_PE64: - { - snprintf(s, MAX_MSG, "%#x (%s)", header->_64->Magic, "PE32+"); - output("Magic number", s); - - snprintf(s, MAX_MSG, "%" PRIu8, header->_64->MajorLinkerVersion); - output("Linker major version", s); - - snprintf(s, MAX_MSG, "%" PRIu8, header->_64->MinorLinkerVersion); - output("Linker minor version", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfCode); - output("Size of .text section", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfInitializedData); - output("Size of .data section", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfUninitializedData); - output("Size of .bss section", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->AddressOfEntryPoint); - output("Entrypoint", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->BaseOfCode); - output("Address of .text section", s); - - snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->ImageBase); - output("ImageBase", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->SectionAlignment); - output("Alignment of sections", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->FileAlignment); - output("Alignment factor", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MajorOperatingSystemVersion); - output("Major version of required OS", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MinorOperatingSystemVersion); - output("Minor version of required OS", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MajorImageVersion); - output("Major version of image", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MinorImageVersion); - output("Minor version of image", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MajorSubsystemVersion); - output("Major version of subsystem", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MinorSubsystemVersion); - output("Minor version of subsystem", s); - -#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - snprintf(s, MAX_MSG, "Win32 version value: %#x", header->_64->Win32VersionValue); - output_open_scope(s, OUTPUT_SCOPE_TYPE_OBJECT); - - if (header->_64->Win32VersionValue == 0) - strcpy(s, "(default)"); - else - snprintf(s, MAX_MSG, "%u", header->_64->Win32VersionValue & 0xff); - output("Overwrite OS major version", s); - - if (header->_64->Win32VersionValue == 0) - strcpy(s, "(default)"); - else - snprintf(s, MAX_MSG, "%u", (header->_64->Win32VersionValue >> 8) & 0xff); - output("Overwrite OS minor version", s); - - if (header->_64->Win32VersionValue == 0) - strcpy(s, "(default)"); - else - snprintf(s, MAX_MSG, "%u", (header->_64->Win32VersionValue >> 16) & 0x3fff); - output("Overwrite OS build number", s); - - if (header->_64->Win32VersionValue == 0) - strcpy(s, "(default)"); - else { - uint8_t platform_id = header->_64->Win32VersionValue >> 30; - static const char *const win32_version_value_platform_id[4] = { "NT", "CE", "Win32s", "Win9x" }; - snprintf(s, MAX_MSG, "%u (%s)", platform_id, win32_version_value_platform_id[platform_id]); - } - output("Overwrite OS platform id", s); - - output_close_scope(); -#endif - - snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfImage); - output("Size of image", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfHeaders); - output("Size of headers", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->CheckSum); - output("Checksum", s); - - const uint16_t subsystem = header->_64->Subsystem; -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - const char *subsystem_name = "Unknown"; - for (size_t i=0; i < max_subsystem; i++) { - if (subsystem == subsystemNames[i].subsystem) - subsystem_name = subsystemNames[i].name; - } -#else - const char *subsystem_name = pe_windows_subsystem_name(subsystem); - if (subsystem_name == NULL) - subsystem_name = "Unknown"; -#endif - snprintf(s, MAX_MSG, "%#x (%s)", subsystem, subsystem_name); - output("Subsystem required", s); - - snprintf(s, MAX_MSG, "%#x", header->_64->DllCharacteristics); - output("DLL characteristics", s); - -#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - output_open_scope("DLL characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); - - for (uint16_t i=0, flag=0x0001; i < 16; i++, flag <<= 1) { - if (header->_64->DllCharacteristics & flag) { - const char *characteristic_name = NULL; - char formatted_characteristic_name[32]; - if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) - characteristic_name = pe_dll_image_dllcharacteristic_name(flag); - if (characteristic_name == NULL) - characteristic_name = pe_image_dllcharacteristic_name(flag); - if (characteristic_name == NULL) { - snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); - characteristic_name = formatted_characteristic_name; - } - output(NULL, characteristic_name); - } - } - - output_close_scope(); // DLL characteristics names -#endif - - snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->SizeOfStackReserve); - output("Size of stack to reserve", s); - - snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->SizeOfStackCommit); - output("Size of stack to commit", s); - - snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->SizeOfHeapReserve); - output("Size of heap space to reserve", s); - - snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->SizeOfHeapCommit); - output("Size of heap space to commit", s); - -#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - snprintf(s, MAX_MSG, "%#x", header->_64->LoaderFlags); - output("Loader Flags", s); - - output_open_scope("Loader Flags names", OUTPUT_SCOPE_TYPE_ARRAY); - - for (uint32_t i=0, flag=0x00000001; i < 32; i++, flag <<= 1) { - if (header->_64->LoaderFlags & flag) { - const char *flag_name = NULL; - char formatted_flag_name[32]; - if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) - flag_name = pe_dll_image_loader_flags_name(flag); - if (flag_name == NULL) - flag_name = pe_image_loader_flags_name(flag); - if (flag_name == NULL) { - snprintf(formatted_flag_name, sizeof(formatted_flag_name)-1, "UNKNOWN[%#x]", flag); - flag_name = formatted_flag_name; - } - output(NULL, flag_name); - } - } - - output_close_scope(); // Loader Flags names -#endif - - break; - } - } - - output_close_scope(); // Optional/Image heade -} - -static void print_coff_header(pe_ctx_t *ctx, IMAGE_COFF_HEADER *header) -{ -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - typedef struct { - ImageCharacteristics characteristic; - const char * const name; - } ImageCharacteristicsName; - static const ImageCharacteristicsName characteristicsTable[] = { - { IMAGE_FILE_RELOCS_STRIPPED, "base relocations stripped" }, - { IMAGE_FILE_EXECUTABLE_IMAGE, "executable image" }, - { IMAGE_FILE_LINE_NUMS_STRIPPED, "line numbers removed (deprecated)" }, - { IMAGE_FILE_LOCAL_SYMS_STRIPPED, "local symbols removed (deprecated)" }, - { IMAGE_FILE_AGGRESSIVE_WS_TRIM, "aggressively trim (deprecated for Windows 2000 and later)" }, - { IMAGE_FILE_LARGE_ADDRESS_AWARE, "can handle more than 2 GB addresses" }, - { IMAGE_FILE_16BIT_MACHINE, "" }, - { IMAGE_FILE_BYTES_REVERSED_LO, "little-endian (deprecated)" }, - { IMAGE_FILE_32BIT_MACHINE, "32-bit machine" }, - { IMAGE_FILE_DEBUG_STRIPPED, "debugging information removed" }, - { IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, "copy to swap if it's on removable media" }, - { IMAGE_FILE_NET_RUN_FROM_SWAP, "copy to swap if it's on network media" }, - { IMAGE_FILE_SYSTEM, "system file" }, - { IMAGE_FILE_DLL, "DLL image" }, - { IMAGE_FILE_UP_SYSTEM_ONLY, "uniprocessor machine" }, - { IMAGE_FILE_BYTES_REVERSED_HI, "big-endian (deprecated)" } - }; - - typedef struct { - MachineType type; - const char * const name; - } MachineTypeName; - static const MachineTypeName machineTypeTable[] = { - { IMAGE_FILE_MACHINE_UNKNOWN, "Any machine type" }, - { IMAGE_FILE_MACHINE_AM33, "Matsushita AM33" }, - { IMAGE_FILE_MACHINE_AMD64, "x86-64 (64-bits)" }, - { IMAGE_FILE_MACHINE_ARM, "ARM little endian" }, - { IMAGE_FILE_MACHINE_ARMV7, "ARMv7 (or higher) Thumb mode only" }, - { IMAGE_FILE_MACHINE_CEE, "clr pure MSIL (object only)" }, - { IMAGE_FILE_MACHINE_EBC, "EFI byte code" }, - { IMAGE_FILE_MACHINE_I386, "Intel 386 and compatible (32-bits)"}, - { IMAGE_FILE_MACHINE_IA64, "Intel Itanium" }, - { IMAGE_FILE_MACHINE_M32R, "Mitsubishi M32R little endian" }, - { IMAGE_FILE_MACHINE_MIPS16, "MIPS16" }, - { IMAGE_FILE_MACHINE_MIPSFPU, "MIPS with FPU" }, - { IMAGE_FILE_MACHINE_MIPSFPU16, "MIPS16 with FPU" }, - { IMAGE_FILE_MACHINE_POWERPC, "Power PC little endian" }, - { IMAGE_FILE_MACHINE_POWERPCFP, "Power PC with floating point support" }, - { IMAGE_FILE_MACHINE_R4000, "MIPS little endian" }, - { IMAGE_FILE_MACHINE_SH3, "Hitachi SH3" }, - { IMAGE_FILE_MACHINE_SH3DSP, "Hitachi SH3 DSP" }, - { IMAGE_FILE_MACHINE_SH4, "Hitachi SH4" }, - { IMAGE_FILE_MACHINE_SH5, "Hitachi SH5" }, - { IMAGE_FILE_MACHINE_THUMB, "ARM or Thumb (\"interworking\")" }, - { IMAGE_FILE_MACHINE_WCEMIPSV2, "MIPS little-endian WCE v2" } - }; - static const size_t max_machine_type = LIBPE_SIZEOF_ARRAY(machineTypeTable); -#endif - - output_open_scope("COFF/File header", OUTPUT_SCOPE_TYPE_OBJECT); - -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - const char *machine = "Unknown machine type"; - for (size_t i=0; i < max_machine_type; i++) { - if (header->Machine == machineTypeTable[i].type) - machine = machineTypeTable[i].name; - } -#else - const char *machine = pe_machine_type_name(header->Machine); - if (machine == NULL) - machine = "Unknown machine type"; -#endif - - static char s[MAX_MSG]; - - snprintf(s, MAX_MSG, "%#x %s", header->Machine, machine); - output("Machine", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->NumberOfSections); - output("Number of sections", s); - - if (pe_is_repro(ctx)) { - snprintf(s, MAX_MSG, "0x%" PRIx32 " (reproducible hash)", header->TimeDateStamp); - } else { - char timestr[40] = "invalid"; - const time_t timestamp = header->TimeDateStamp; - struct tm *t = gmtime(×tamp); - if (t) - strftime(timestr, sizeof(timestr), "%a, %d %b %Y %H:%M:%S UTC", t); - snprintf(s, MAX_MSG, "%" PRIu32 " (%s)", header->TimeDateStamp, timestr); - } - output("Date/time stamp", s); - - snprintf(s, MAX_MSG, "%#x", header->PointerToSymbolTable); - output("Symbol Table offset", s); - - snprintf(s, MAX_MSG, "%" PRIu32, header->NumberOfSymbols); - output("Number of symbols", s); - - snprintf(s, MAX_MSG, "%#x", header->SizeOfOptionalHeader); - output("Size of optional header", s); - - snprintf(s, MAX_MSG, "%#x", header->Characteristics); - output("Characteristics", s); - - output_open_scope("Characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); - - for (uint16_t i=0, flag=0x0001; i < 16; i++, flag <<= 1) { - if (header->Characteristics & flag) { -#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - output(NULL, characteristicsTable[i].name); -#else - const char *characteristic_name = pe_image_characteristic_name(flag); - char formatted_characteristic_name[32]; - if (characteristic_name == NULL) { - snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); - characteristic_name = formatted_characteristic_name; - } - output(NULL, characteristic_name); -#endif - } - } - - output_close_scope(); // Characteristics names - - output_close_scope(); // COFF/File header -} - -static void print_dos_header(IMAGE_DOS_HEADER *header) -{ - char s[MAX_MSG]; - - output_open_scope("DOS Header", OUTPUT_SCOPE_TYPE_OBJECT); - - snprintf(s, MAX_MSG, "%#x (MZ)", header->e_magic); - output("Magic number", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->e_cblp); - output("Bytes in last page", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->e_cp); - output("Pages in file", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->e_crlc); - output("Relocations", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->e_cparhdr); - output("Size of header in paragraphs", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->e_minalloc); - output("Minimum extra paragraphs", s); - - snprintf(s, MAX_MSG, "%" PRIu16, header->e_maxalloc); - output("Maximum extra paragraphs", s); - - snprintf(s, MAX_MSG, "%#x", header->e_ss); - output("Initial (relative) SS value", s); - - snprintf(s, MAX_MSG, "%#x", header->e_sp); - output("Initial SP value", s); - - snprintf(s, MAX_MSG, "%#x", header->e_ip); - output("Initial IP value", s); - - snprintf(s, MAX_MSG, "%#x", header->e_cs); - output("Initial (relative) CS value", s); - - snprintf(s, MAX_MSG, "%#x", header->e_lfarlc); - output("Address of relocation table", s); - - snprintf(s, MAX_MSG, "%#x", header->e_ovno); - output("Overlay number", s); - - snprintf(s, MAX_MSG, "%#x", header->e_oemid); - output("OEM identifier", s); - - snprintf(s, MAX_MSG, "%#x", header->e_oeminfo); - output("OEM information", s); - - snprintf(s, MAX_MSG, "%#x", header->e_lfanew); - output("PE header offset", s); - - output_close_scope(); // DOS Header -} - -static 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->name || exports->functions_count > 0) { - output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT); - output("Name", exports->name); - } - if (exports->functions_count > 0) { - 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 }; - char hint_str[16] = { 0 }; - snprintf(ordinal_str, sizeof(ordinal_str)-1, "%"PRIu32, func->ordinal); - snprintf(address_str, sizeof(address_str)-1, "%#"PRIx32, func->address); - snprintf(hint_str, sizeof(hint_str)-1, "0x%"PRIx32, func->hint); - - 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("Hint", hint_str); - output("Name", full_name); - } else { - output("Ordinal", ordinal_str); - output("Address", address_str); - output("Hint", hint_str); - output("Name", func->name); - } - - output_close_scope(); // Function - } - } - - if (exports->functions_count > 0) { - output_close_scope(); // Functions - } - if (exports->name || exports->functions_count > 0) { - output_close_scope(); // Library - } - - output_close_scope(); // Exported functions -} - -static void print_import_library(const pe_imported_dll_t *dll) -{ - output("Name", dll->name); - output_open_scope("Functions", OUTPUT_SCOPE_TYPE_ARRAY); - - for (size_t j=0; j < dll->functions_count; j++) { - const pe_imported_function_t *func = &dll->functions[j]; - output_open_scope("Function", OUTPUT_SCOPE_TYPE_OBJECT); - { - if (func->ordinal) { - char ordinal_str[16]; - snprintf(ordinal_str, sizeof(ordinal_str)-1, "%"PRIu16, func->ordinal); - output("Ordinal", ordinal_str); - } else { - char hint_str[16]; - snprintf(hint_str, sizeof(hint_str)-1, "0x%"PRIx16, func->hint); - output("Hint", hint_str); - output("Name", func->name); - } - } - output_close_scope(); // Function - } - - output_close_scope(); // Functions -} - -static void print_imports(pe_ctx_t *ctx) -{ - output_open_scope("Imported functions", OUTPUT_SCOPE_TYPE_ARRAY); - - const pe_imports_t *imports = pe_imports(ctx); - for (size_t i=0; i < imports->dll_count; i++) { - const pe_imported_dll_t *dll = &imports->dlls[i]; - output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT); - print_import_library(dll); - output_close_scope(); // Library - } - for (size_t i=0; i < imports->delay_dll_count; i++) { - const pe_imported_dll_t *dll = &imports->delay_dlls[i]; - output_open_scope("Delay Loaded Library", OUTPUT_SCOPE_TYPE_OBJECT); - print_import_library(dll); - output_close_scope(); // Delay Loaded Library - } - - output_close_scope(); // Imported functions -} - -int main(int argc, char *argv[]) -{ - pev_config_t config; - PEV_INITIALIZE(&config); - - if (argc < 2) { - usage(); - return EXIT_FAILURE; - } - - output_set_cmdline(argc, argv); - - options_t *options = parse_options(argc, argv); // opcoes - - pe_ctx_t ctx; - - pe_err_e err = pe_load_file(&ctx, argv[argc-1]); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - err = pe_parse(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - if (!pe_is_pe(&ctx)) - EXIT_ERROR("not a valid PE file"); - - output_open_document(); - - // dos header - if (options->dos || options->all_headers || options->all) { - IMAGE_DOS_HEADER *header_ptr = pe_dos(&ctx); - if (header_ptr) - print_dos_header(header_ptr); - else if (pe_is_exec(&ctx)) { LIBPE_WARNING("unable to read DOS header"); } - } - - // coff/file header - if (options->coff || options->all_headers || options->all) { -#ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 - if (ctx.pe.signature) { - static char s[MAX_MSG]; - output_open_scope("PE header", OUTPUT_SCOPE_TYPE_OBJECT); - snprintf(s, MAX_MSG, "0x%08x (%.4s)", ctx.pe.signature, (const char *)&ctx.pe.signature); - output("Signature", s); - output_close_scope(); - } -#endif - IMAGE_COFF_HEADER *header_ptr = pe_coff(&ctx); - if (header_ptr) - print_coff_header(&ctx, header_ptr); - else { LIBPE_WARNING("unable to read COFF file header"); } - } - - // optional header - if (options->opt || options->all_headers || options->all) { - IMAGE_OPTIONAL_HEADER *header_ptr = pe_optional(&ctx); - if (header_ptr) - print_optional_header(&ctx, header_ptr); - else if (pe_is_exec(&ctx)) { LIBPE_WARNING("unable to read Optional (Image) file header"); } - } - - IMAGE_DATA_DIRECTORY **directories = pe_directories(&ctx); - bool directories_warned = false; - - // directories - if (options->dirs || options->all) { - if (directories != NULL) - print_directories(&ctx); - else if (pe_is_exec(&ctx) && !directories_warned) { - LIBPE_WARNING("directories not found"); - directories_warned = true; - } - } - - // imports - if (options->imports || options->all) { - if (directories != NULL) - print_imports(&ctx); - else if (pe_is_exec(&ctx) && !directories_warned) { - LIBPE_WARNING("directories not found"); - directories_warned = true; - } - } - - // exports - if (options->exports || options->all) { - if (directories != NULL) - print_exports(&ctx); - else if (pe_is_exec(&ctx) && !directories_warned) { - LIBPE_WARNING("directories not found"); - directories_warned = true; - } - } - - // sections - if (options->all_sections || options->all) { - if (pe_sections(&ctx) != NULL) - print_sections(&ctx); - else { LIBPE_WARNING("unable to read sections"); } - } - - output_close_document(); - - // libera a memoria - free_options(options); - - // free - err = pe_unload(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - PEV_FINALIZE(&config); - - return EXIT_SUCCESS; -} diff --git a/src/resources.c b/src/resources.c new file mode 100644 index 00000000..51a73f4d --- /dev/null +++ b/src/resources.c @@ -0,0 +1,712 @@ +/* vim :set ts=4 sw=4 sts=4 et : */ +/* + readpe - the PE file analyzer toolkit + + resources.c - retrive informations and binary data of resources + + 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 "common.h" +#include "libpe/context.h" +#include "output.h" +#include "readpe.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *g_resourceDir = "resources"; + +static const unsigned char *g_tree__ = u8"─"; +static const unsigned char *g_tree_i = u8"│"; +static const unsigned char *g_tree_l = u8"└"; +static const unsigned char *g_tree_t = u8"├"; + +#pragma pack(push, 1) +typedef struct { + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +} BITMAPINFOHEADER; +#pragma pack(pop) + +#pragma pack(push, 2) +typedef struct { + uint16_t icReserved; // Always zero + uint16_t icType; // 1 for .ico, 2 for .cur, other values are invalid + uint16_t icImageCount; // number of images in the file +} ICOFILEHEADER; + +typedef struct { + uint8_t biWidth; // Width of image + uint8_t biHeight; // Height of image + uint8_t biClrUsed; // Number of colors used + uint8_t biReserved; // Reserved + union { + uint16_t biPlanes; // ICO - Number of color planes. Should be 0 or 1 + uint16_t biXHotspot; // CUR - Horizontal coord of the hotspot in number + // of pixels from the left + } u0; + union { + uint16_t biBitCount; // ICO - Number of bits per pixel + uint16_t biYHotspot; // CUR - Vertical coord of the hotspot in number of + // pixels from the top + } u1; + uint32_t biSizeImage; // Size of image data in bytes + uint32_t biOffBits; // Offset of BMP or PNG data from the beggining of the + // ICO/CUR file +} ICODIRENTRY; +#pragma pack(pop) + +struct stored_resource { + bool is_modified; + uint8_t *buffer; + size_t size; +}; + +struct resource_stats { + int totalCount; + int totalResourceDirectory; + int totalDirectoryEntry; + int totalDataString; + int totalDataEntry; +}; + +static void print_resource_node(const pe_resource_node_t *node) +{ + char value[MAX_MSG]; + + switch (node->type) { + default: + LIBPE_WARNING("Invalid node type"); + break; + case LIBPE_RDT_RESOURCE_DIRECTORY: { + const IMAGE_RESOURCE_DIRECTORY *const resourceDirectory + = node->raw.resourceDirectory; + + snprintf(value, MAX_MSG, "Resource Directory / %d", node->dirLevel); + output("\nNode Type / Level", value); + + snprintf(value, MAX_MSG, "%d", resourceDirectory->Characteristics); + output("Characteristics", value); + + snprintf(value, MAX_MSG, "%d", resourceDirectory->TimeDateStamp); + output("Timestamp", value); + + snprintf(value, MAX_MSG, "%d", resourceDirectory->MajorVersion); + output("Major Version", value); + + snprintf(value, MAX_MSG, "%d", resourceDirectory->MinorVersion); + output("Minor Version", value); + + snprintf(value, MAX_MSG, "%d", resourceDirectory->NumberOfNamedEntries); + output("Named entries", value); + + snprintf(value, MAX_MSG, "%d", resourceDirectory->NumberOfIdEntries); + output("Id entries", value); + break; + } + case LIBPE_RDT_DIRECTORY_ENTRY: { + const IMAGE_RESOURCE_DIRECTORY_ENTRY *const directoryEntry + = node->raw.directoryEntry; + + snprintf(value, MAX_MSG, "Directory Entry / %d", node->dirLevel); + output("\nNode Type / Level", value); + + snprintf(value, MAX_MSG, "%d", directoryEntry->u0.data.NameOffset); + output("Name offset", value); + + snprintf(value, MAX_MSG, "%d", directoryEntry->u0.data.NameIsString); + output("Name is string", value); + + snprintf(value, MAX_MSG, "%x", + directoryEntry->u1.data.OffsetToDirectory); + output("Offset to directory", value); + + snprintf(value, MAX_MSG, "%d", directoryEntry->u1.data.DataIsDirectory); + output("Data is directory", value); + break; + } + case LIBPE_RDT_DATA_STRING: { + const IMAGE_RESOURCE_DATA_STRING_U *const dataString + = node->raw.dataString; + + snprintf(value, MAX_MSG, "Data String / %d", node->dirLevel); + output("\nNode Type / Level", value); + + snprintf(value, MAX_MSG, "%d", dataString->Length); + output("String len", value); + + char ascii_string[MAX_MSG]; + + // FIXME: dataString->Length + 1 is right?! + pe_utils_str_widechar2ascii(ascii_string, sizeof ascii_string, + (const char *) dataString->String, + dataString->Length + 1); + + snprintf(value, MAX_MSG, "%s", ascii_string); + output("String", value); + break; + } + case LIBPE_RDT_DATA_ENTRY: { + const IMAGE_RESOURCE_DATA_ENTRY *const dataEntry = node->raw.dataEntry; + + snprintf(value, MAX_MSG, "Data Entry / %d", node->dirLevel); + output("\nNode Type / Level", value); + + snprintf(value, MAX_MSG, "%x", dataEntry->OffsetToData); + output("OffsetToData", value); + + snprintf(value, MAX_MSG, "%d", dataEntry->Size); + output("Size", value); + + snprintf(value, MAX_MSG, "%d", dataEntry->CodePage); + output("CodePage", value); + + snprintf(value, MAX_MSG, "%d", dataEntry->Reserved); + output("Reserved", value); + break; + } + } +} + +static void print_resource_nodes(const pe_resource_node_t *node) +{ + if (node == NULL) { + return; + } + + print_resource_node(node); + + print_resource_nodes(node->childNode); + print_resource_nodes(node->nextNode); +} + +static pe_resource_node_t *get_root_node(pe_ctx_t *ctx) +{ + pe_resources_t *resources = pe_resources(ctx); + if (resources == NULL || resources->err != LIBPE_E_OK) { + LIBPE_WARNING("This file has no resources"); + return NULL; + } + return resources->root_node; +} + +static void build_resource_node_filename(char *output, size_t output_size, + const pe_resource_node_t *node) +{ + char partial_path[MAX_PATH]; + + for (pe_resource_level_e level = LIBPE_RDT_LEVEL1; level <= node->dirLevel; + level++) { + const pe_resource_node_t *dir_entry_node + = pe_resource_find_parent_node_by_type_and_level( + node, LIBPE_RDT_DIRECTORY_ENTRY, level); + if (! dir_entry_node) { + // strncat(output, partial_path, output_size - strlen(output) - 1); + // continue; + return; + } + if (dir_entry_node->raw.directoryEntry->u0.data.NameIsString) { + snprintf(partial_path, sizeof(partial_path), "%s ", + dir_entry_node->name); + } else { + const pe_resource_entry_info_t *match + = pe_resource_entry_info_lookup( + dir_entry_node->raw.directoryEntry->u0.data.NameOffset); + if (match != NULL && level == LIBPE_RDT_LEVEL1) { + snprintf(partial_path, sizeof(partial_path), "%s ", + match->name); + } else { + snprintf( + partial_path, sizeof(partial_path), "%04x ", + dir_entry_node->raw.directoryEntry->u0.data.NameOffset); + } + } + + strncat(output, partial_path, output_size - strlen(output) - 1); + } + + size_t length = strlen(output); + output[length - 1] = '\0'; // Remove the last whitespace. +} + +static void print_resource_node_list(const pe_resource_node_t *node) +{ + if (node->type != LIBPE_RDT_DATA_ENTRY) { + return; + } + + char node_info[MAX_PATH]; + memset(node_info, 0, sizeof(node_info)); + build_resource_node_filename(node_info, sizeof(node_info), node); + printf("%s (%d bytes)\n", node_info, node->raw.dataEntry->Size); +} + +static void print_resource_list(const pe_resource_node_t *node) +{ + if (node == NULL) { + return; + } + + print_resource_node_list(node); + + print_resource_list(node->childNode); + print_resource_list(node->nextNode); +} + +static void print_resource_node_leaf(const pe_resource_node_t *node, + uint8_t depth) +{ + switch (node->type) { + case LIBPE_RDT_DIRECTORY_ENTRY: + printf("D"); + break; + case LIBPE_RDT_DATA_ENTRY: + printf("F"); + break; + default: + return; + } + + printf("%*s+", depth - 1, ""); + + char node_info[MAX_PATH]; + memset(node_info, 0, sizeof(node_info)); + build_resource_node_filename(node_info, sizeof(node_info), node); + printf("%s (%d bytes)\n", node_info, node->raw.dataEntry->Size); +} + +static void print_resource_branch(const pe_resource_node_t *node, uint8_t depth) +{ + if (node == NULL) { + return; + } + + print_resource_node_leaf(node, depth); + + print_resource_branch(node->childNode, depth + 1); + print_resource_branch(node->nextNode, depth); +} + +static void restore_resource_icon(struct stored_resource *resource, + const pe_resource_entry_info_t *entry_info, + void *raw_data_ptr, size_t raw_data_size) +{ + if (memcmp(raw_data_ptr, "\x89PNG", 4) == 0) { + // A PNG icon is stored along with its original header, so just return + // untouched. + return; + } + + const BITMAPINFOHEADER *bitmap = raw_data_ptr; + + // Is it valid? + if (bitmap->biSize != 40) { + LIBPE_WARNING("RT_ICON bitmap is not valid"); + return; + } + + ICOFILEHEADER fileheader = { + .icReserved = 0, + .icType = 1, + .icImageCount = 1, + }; + ICODIRENTRY direntry + = {.biWidth = (unsigned char) bitmap->biWidth, + .biHeight = (unsigned char) (bitmap->biHeight + / ((entry_info->type == RT_ICON + || entry_info->type == RT_CURSOR + || entry_info->type == RT_BITMAP) + ? 2 + : 1)), + .biClrUsed = 0, // What should we put here? + .biReserved = 0, + .u0 = {.biPlanes = 1}, + .u1 = {.biBitCount = bitmap->biBitCount}, + .biSizeImage = bitmap->biSizeImage, + .biOffBits = sizeof(ICOFILEHEADER) + sizeof(ICODIRENTRY)}; + + size_t written = 0; + uint8_t *buffer + = malloc_s(sizeof(ICOFILEHEADER) + sizeof(ICODIRENTRY) + raw_data_size); + +#define PERES_APPEND(dst, src, size) \ + memcpy(dst + written, src, size); \ + written += size + PERES_APPEND(buffer, &fileheader, sizeof(ICOFILEHEADER)); + PERES_APPEND(buffer, &direntry, sizeof(ICODIRENTRY)); + PERES_APPEND(buffer, raw_data_ptr, raw_data_size); +#undef PERES_APPEND + + resource->is_modified = true; + resource->buffer = buffer; + resource->size = written; +} + +static void restore_resource(struct stored_resource *resource, + const pe_resource_entry_info_t *entry_info, + void *raw_data_ptr, size_t raw_data_size) +{ + assert(resource != NULL); + assert(raw_data_ptr != NULL); + + // If we don't know this type or the data size is 0, just return with the + // raw information untouched. + if (entry_info == NULL || raw_data_size == 0) { + goto fallback_untouched; + } + + switch (entry_info->type) { + case RT_ICON: + restore_resource_icon(resource, entry_info, raw_data_ptr, + raw_data_size); + break; + default: + goto fallback_untouched; + } + + if (resource->is_modified) { + return; + } + +fallback_untouched: + resource->is_modified = false; + resource->buffer = raw_data_ptr; + resource->size = raw_data_size; +} + +static void save_resource(pe_ctx_t *ctx, const pe_resource_node_t *node, + bool namedExtract) +{ + assert(node != NULL); + assert(node->type == LIBPE_RDT_DATA_ENTRY); + assert(node->dirLevel == LIBPE_RDT_LEVEL3); + + const IMAGE_RESOURCE_DATA_ENTRY *entry = node->raw.dataEntry; + + const uint64_t raw_data_offset = pe_rva2ofs(ctx, entry->OffsetToData); + const size_t raw_data_size = entry->Size; + uint8_t *raw_data_ptr = LIBPE_PTR_ADD(ctx->map_addr, raw_data_offset); + if (! pe_can_read(ctx, raw_data_ptr, raw_data_size)) { + // TODO: Should we report something? + fprintf(stderr, + "Attempted to read range [ %p, %p ] which is not within the " + "mapped range [ %p, %lx ]\n", + (void *) raw_data_ptr, + LIBPE_PTR_ADD(raw_data_ptr, raw_data_size), ctx->map_addr, + ctx->map_end); + return; + } + + struct stat statDir; + if (stat(g_resourceDir, &statDir) == -1) { + mkdir(g_resourceDir, 0700); + } + + char *dirName; + + const pe_resource_node_t *folder_node + = pe_resource_find_parent_node_by_type_and_level( + node, LIBPE_RDT_DIRECTORY_ENTRY, + LIBPE_RDT_LEVEL1); // dirLevel == 1 is where Resource Types are + // defined. + const pe_resource_entry_info_t *entry_info = pe_resource_entry_info_lookup( + folder_node->raw.directoryEntry->u0.Name); + if (entry_info != NULL) { + if (asprintf(&dirName, "%s/%s", g_resourceDir, entry_info->dir_name) + < 0) { + abort(); + } + // snprintf(dirName, sizeof(dirName), "%s/%s", g_resourceDir, + // entry_info->dir_name); + } else { + const size_t len = strlen(g_resourceDir); + dirName = malloc(len); + memcpy(dirName, g_resourceDir, len); + // snprintf(dirName, sizeof(dirName), "%s", g_resourceDir); + } + + if (stat(dirName, &statDir) == -1) { + mkdir(dirName, 0700); + } + + const pe_resource_node_t *name_node + = pe_resource_find_parent_node_by_type_and_level( + node, LIBPE_RDT_DIRECTORY_ENTRY, LIBPE_RDT_LEVEL2); // dirLevel == 2 + if (name_node == NULL) { + // TODO: Should we report something? + free(dirName); + fprintf( + stderr, + "pe_resource_find_parent_node_by_type_and_level returned NULL\n"); + return; + } + // fprintf(stderr, "DEBUG: Name=%d\n", + // name_node->raw.directoryEntry->u0.Name); + + char *relativeFileName; + + if (namedExtract) { + char fileName[MAX_PATH]; // ok? + + build_resource_node_filename(fileName, sizeof(fileName), node); + if (asprintf(&relativeFileName, "%s/%s%s", dirName, fileName, + entry_info != NULL ? entry_info->extension : ".bin") + < 0) { + abort(); + } + } else { + if (asprintf(&relativeFileName, + "%s/" + "%" PRIu32 "%s", + dirName, name_node->raw.directoryEntry->u0.data.NameOffset, + entry_info != NULL ? entry_info->extension : ".bin") + < 0) { + abort(); + } + } + + free(dirName); + // printf("DEBUG: raw_data_offset=%#llx, raw_data_size=%ld, + // relativeFileName=%s\n", raw_data_offset, raw_data_size, + // relativeFileName); + + struct stored_resource resource = {0}; + restore_resource(&resource, entry_info, raw_data_ptr, raw_data_size); + + FILE *fp = fopen(relativeFileName, "wb+"); + + if (fp == NULL) { + free(relativeFileName); + // TODO: Should we report something? + return; + } + + fwrite(resource.buffer, resource.size, 1, fp); + + fclose(fp); + + if (resource.is_modified) { + free(resource.buffer); + } + + output("Save On", relativeFileName); + + free(relativeFileName); +} + +static void save_all_resources(pe_ctx_t *ctx, const pe_resource_node_t *node, + bool namedExtract) +{ + if (node == NULL) { + return; + } + + if (node->type == LIBPE_RDT_DATA_ENTRY && node->dirLevel == 3) { + save_resource(ctx, node, namedExtract); + } + + save_all_resources(ctx, node->childNode, namedExtract); + save_all_resources(ctx, node->nextNode, namedExtract); +} + +static bool resources_has_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. + return false; + } + return node->raw.directoryEntry->u0.data.NameOffset == RT_VERSION; +} + +static void print_resource_version(pe_ctx_t *ctx, + const pe_resource_node_t *node) +{ + if (node == NULL) { + return; + } + + pe_resource_node_search_result_t search_result = {0}; + pe_resource_search_nodes(&search_result, node, resources_has_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); + if (version_node != NULL) { + const uint64_t data_offset + = pe_rva2ofs(ctx, version_node->raw.dataEntry->OffsetToData); + const size_t data_size = version_node->raw.dataEntry->Size; + const void *data_ptr = LIBPE_PTR_ADD( + ctx->map_addr, + 32 + data_offset); // TODO(jweyrich): The literal 32 refers to + // the size of the + if (! pe_can_read(ctx, data_ptr, data_size)) { + LIBPE_WARNING("Cannot read VS_FIXEDFILEINFO"); + return; + } + + const VS_FIXEDFILEINFO *info_ptr = data_ptr; + + static char value[MAX_MSG]; + + snprintf(value, MAX_MSG, "%u.%u.%u.%u", + (uint32_t) (info_ptr->dwFileVersionMS & 0xffff0000) >> 16, + (uint32_t) info_ptr->dwFileVersionMS & 0x0000ffff, + (uint32_t) (info_ptr->dwFileVersionLS & 0xffff0000) >> 16, + (uint32_t) info_ptr->dwFileVersionLS & 0x0000ffff); + output("File Version", value); + + snprintf( + value, MAX_MSG, "%u.%u.%u.%u", + (uint32_t) (info_ptr->dwProductVersionMS & 0xffff0000) >> 16, + (uint32_t) info_ptr->dwProductVersionMS & 0x0000ffff, + (uint32_t) (info_ptr->dwProductVersionLS & 0xffff0000) >> 16, + (uint32_t) info_ptr->dwProductVersionLS & 0x0000ffff); + output("Product Version", value); + } + } + pe_resources_dealloc_node_search_result(&search_result); +} + +static void generate_resource_stats(struct resource_stats *stats, + const pe_resource_node_t *node) +{ + if (node == NULL) { + return; + } + + stats->totalCount++; + + switch (node->type) { + case LIBPE_RDT_RESOURCE_DIRECTORY: + stats->totalResourceDirectory++; + break; + case LIBPE_RDT_DIRECTORY_ENTRY: + stats->totalDirectoryEntry++; + break; + case LIBPE_RDT_DATA_STRING: + stats->totalDataString++; + break; + case LIBPE_RDT_DATA_ENTRY: + stats->totalDataEntry++; + break; + } + + if (node->childNode) { + generate_resource_stats(stats, node->childNode); + } + + if (node->nextNode) { + generate_resource_stats(stats, node->nextNode); + } +} + +static void print_resource_stats(const pe_resource_node_t *node) +{ + struct resource_stats stats = {0}; + generate_resource_stats(&stats, node); + + static char value[MAX_MSG]; + + snprintf(value, MAX_MSG, "%d", stats.totalCount); + output("Total Structs", value); + + snprintf(value, MAX_MSG, "%d", stats.totalResourceDirectory); + output("Total Resource Directory", value); + + snprintf(value, MAX_MSG, "%d", stats.totalDirectoryEntry); + output("Total Directory Entry", value); + + snprintf(value, MAX_MSG, "%d", stats.totalDataString); + output("Total Data String", value); + + snprintf(value, MAX_MSG, "%d", stats.totalDataEntry); + output("Total Data Entry", value); +} + +void print_resources_list(pe_ctx_t *ctx) +{ + pe_resource_node_t *root_node = get_root_node(ctx); + print_resource_list(root_node); +} + +void print_resources_tree(pe_ctx_t *ctx) +{ + pe_resource_node_t *root_node = get_root_node(ctx); + print_resource_branch(root_node, 0); +} + +void print_resources(pe_ctx_t *ctx) +{ + pe_resource_node_t *root_node = get_root_node(ctx); + print_resource_nodes(root_node); +} + +void print_resources_stats(pe_ctx_t *ctx) +{ + pe_resource_node_t *root_node = get_root_node(ctx); + print_resource_stats(root_node); +} + +void print_file_version(pe_ctx_t *ctx) +{ + pe_resource_node_t *root_node = get_root_node(ctx); + print_resource_version(ctx, root_node); +} + +void extract_all_resources(pe_ctx_t *ctx, bool named) +{ + pe_resource_node_t *root_node = get_root_node(ctx); + save_all_resources(ctx, root_node, named); +} + diff --git a/src/scan.c b/src/scan.c new file mode 100644 index 00000000..e56d88a1 --- /dev/null +++ b/src/scan.c @@ -0,0 +1,527 @@ +/* vim :set ts=4 sw=4 sts=4 et : */ +/* + readpe - the PE file analyzer toolkit + + scan.c - search for suspicious things in PE files. + + 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 "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// check for abnormal dos stub (common in packed files) +static bool normal_dos_stub(pe_ctx_t *ctx, uint32_t *stub_offset) +{ + static const uint8_t dos_stub[] + = "\x0e" // push cs + "\x1f" // pop ds + "\xba\x0e\x00" // mov dx, 0x0e + "\xb4\x09" // mov ah, 0x09 + "\xcd\x21" // int 0x21 + "\xb8\x01\x4c" // mov ax, 0x4c01 + "\xcd\x21" // int 0x21 + "This program cannot be run in DOS mode.\r\r\n$"; + + const size_t dos_stub_size + = sizeof(dos_stub) - 1; // -1 to ignore ending null + + const IMAGE_DOS_HEADER *dos = pe_dos(ctx); + if (dos == NULL) { + LIBPE_WARNING("unable to retrieve PE DOS header"); + return false; + } + + *stub_offset = (uint32_t) dos->e_cparhdr << 4; + + // dos stub starts at e_cparhdr shifted by 4 + const char *dos_stub_ptr = LIBPE_PTR_ADD(ctx->map_addr, *stub_offset); + if (! pe_can_read(ctx, dos_stub_ptr, dos_stub_size)) { + LIBPE_WARNING("unable to seek in file"); + return false; + } + + return memcmp(dos_stub, dos_stub_ptr, dos_stub_size) == 0; +} + +static 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; + } + + if (section->Characteristics & IMAGE_SCN_CNT_CODE) { + return NULL; + } + + return section; +} + +static 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; + } + + if (directory->Size == 0) { + return 0; + } + + return directory->VirtualAddress; +} + +/* + * -1 - fake tls callbacks detected + * 0 - no tls directory + * >0 - number of callbacks functions found + */ +static int pe_get_tls_callbacks(pe_ctx_t *ctx, bool verbose) +{ + 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++) { + if (tls_addr >= sections[i]->VirtualAddress + && tls_addr < (sections[i]->VirtualAddress + + sections[i]->SizeOfRawData)) { + 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 + + char value[MAX_MSG]; + uint32_t funcaddr = 0; + + 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; + } + + if (funcaddr == *funcaddr_ptr) { + LIBPE_WARNING("TLS self reference detected!"); + return 0; + } + + // TODO: No longer shadow global + funcaddr = *funcaddr_ptr; + if (funcaddr) { + ret = ++j; // function found + + if (verbose) { + snprintf(value, MAX_MSG, "%#x", funcaddr); + output("TLS callback function", value); + } + } + } while (funcaddr); + + return ret; + } + } + + return 0; +} + +static bool strisprint(const char *string) +{ + const char *s = string; + + if (strncmp(string, ".tls", 5) == 0) { + return false; + } + + if (*s++ != '.') { + return false; + } + + while (*s) { + if (! isalpha((int) *s)) { + return false; + } + + s++; + } + return true; +} + +static void stradd(char *dest, const char *src, bool *pad) +{ + if (*pad) { + strcat(dest, ", "); + } + + strcat(dest, src); + *pad = true; +} + +static void print_strange_sections(pe_ctx_t *ctx) +{ + const uint16_t num_sections = pe_sections_count(ctx); + if (num_sections == 0) { + return; + } + + char value[MAX_MSG]; + + if (ctx->pe.num_sections <= 2) { + snprintf(value, MAX_MSG, "%d (low)", num_sections); + } else if (ctx->pe.num_sections > 8) { + snprintf(value, MAX_MSG, "%d (high)", num_sections); + } else { + snprintf(value, MAX_MSG, "%d", num_sections); + } + + output("section count", value); + + IMAGE_SECTION_HEADER **const sections = pe_sections(ctx); + + output_open_scope("sections", OUTPUT_SCOPE_TYPE_ARRAY); + + bool aux = false; + for (uint16_t i = 0; i < num_sections; i++, aux = false) { + output_open_scope("section", OUTPUT_SCOPE_TYPE_OBJECT); + memset(&value, 0, sizeof(value)); + + if (! strisprint((const char *) sections[i]->Name)) { + stradd(value, "suspicious name", &aux); + } + + if (sections[i]->SizeOfRawData == 0) { + stradd(value, "zero length", &aux); + } else if (sections[i]->SizeOfRawData <= 512) { + stradd(value, "small length", &aux); + } + + // rwx or writable + executable code + if (sections[i]->Characteristics & (uint32_t) IMAGE_SCN_MEM_WRITE + && (sections[i]->Characteristics & IMAGE_SCN_CNT_CODE + || sections[i]->Characteristics & IMAGE_SCN_MEM_EXECUTE)) { + stradd(value, "self-modifying", &aux); + } + + if (! aux) { + strncpy(value, "normal", 7); + } + + output((const char *) sections[i]->Name, value); + output_close_scope(); // section + } + output_close_scope(); // sections +} + +static bool normal_imagebase(pe_ctx_t *ctx) +{ + return (ctx->pe.imagebase == 0x100000000 || ctx->pe.imagebase == 0x1000000 + || ctx->pe.imagebase == 0x400000); +} + +// FIX: Already in libpe! +// new anti-disassembly technique with undocumented Intel FPU instructions +// static bool fpu_trick(pe_ctx_t *ctx) +//{ +// 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; +//} + +static void print_timestamp(bool verbose, const IMAGE_COFF_HEADER *hdr_coff_ptr) +{ + const time_t now = time(NULL); + static char value[MAX_MSG]; + + if (hdr_coff_ptr->TimeDateStamp == 0) { + snprintf(value, MAX_MSG, "zero/invalid"); + } else if (hdr_coff_ptr->TimeDateStamp < 946692000) { + snprintf(value, MAX_MSG, "too old (pre-2000)"); + } else if (hdr_coff_ptr->TimeDateStamp > (uint32_t) now) { + snprintf(value, MAX_MSG, "future time"); + } else { + snprintf(value, MAX_MSG, "normal"); + } + + if (verbose) { + // FIX: Bigger string because week-day abbreviation is locale dependant. + static char timestr[64]; + strftime(timestr, sizeof timestr, " - %a, %d %b %Y %H:%M:%S UTC", + gmtime((time_t *) &hdr_coff_ptr->TimeDateStamp)); + + strcat(value, timestr); + } + + output("timestamp", value); +} + +static int8_t 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 -1; + } + + 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); + + 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; +} + +void pe_scan(pe_ctx_t *ctx, bool verbose) +{ + // File entropy + const double entropy = pe_calculate_entropy_file(ctx); + + static char value[MAX_MSG]; + + if (entropy < 7.0) { + snprintf(value, MAX_MSG, "%f (normal)", entropy); + } else { + snprintf(value, MAX_MSG, "%f (probably packed)", entropy); + } + output("file entropy", value); + + if (pe_is_dll(ctx)) { + char ret = cpl_analysis(ctx); + switch (ret) { + case 1: + output("cpl analysis", "malware"); + break; + default: + output("cpl analysis:", "no threat"); + break; + } + } + + output("fpu anti-disassembly", pe_fpu_trick(ctx) ? "yes" : "no"); + + // imagebase analysis + if (! normal_imagebase(ctx)) { + if (verbose) { + snprintf(value, MAX_MSG, "suspicious - %#" PRIx64, + ctx->pe.imagebase); + } else { + snprintf(value, MAX_MSG, "suspicious"); + } + } else { + if (verbose) { + snprintf(value, MAX_MSG, "normal - %#" PRIx64, ctx->pe.imagebase); + } else { + snprintf(value, MAX_MSG, "normal"); + } + } + output("imagebase", value); + + const IMAGE_OPTIONAL_HEADER *optional = pe_optional(ctx); + if (optional == NULL) { + LIBPE_WARNING("unable to read optional header"); + } else { + uint32_t ep + = (optional->_32 + ? optional->_32->AddressOfEntryPoint + : (optional->_64 ? optional->_64->AddressOfEntryPoint : 0)); + + // fake ep + if (ep == 0) { + snprintf(value, MAX_MSG, "null"); + } else if (pe_check_fake_entrypoint(ctx, ep)) { + if (verbose) { + snprintf(value, MAX_MSG, "fake - va: %#x - raw: %#" PRIx64, ep, + pe_rva2ofs(ctx, ep)); + } else { + snprintf(value, MAX_MSG, "fake"); + } + } else { + if (verbose) { + snprintf(value, MAX_MSG, "normal - va: %#x - raw: %#" PRIx64, + ep, pe_rva2ofs(ctx, ep)); + } else { + snprintf(value, MAX_MSG, "normal"); + } + } + + output("entrypoint", value); + } + + // dos stub + uint32_t stub_offset = 0; + if (! normal_dos_stub(ctx, &stub_offset)) { + if (verbose) { + snprintf(value, MAX_MSG, "suspicious - raw: %#x", stub_offset); + } else { + snprintf(value, MAX_MSG, "suspicious"); + } + } else { + snprintf(value, MAX_MSG, "normal"); + } + + output("DOS stub", value); + + // tls callbacks + int callbacks = pe_get_tls_callbacks(ctx, verbose); + + if (callbacks == 0) { + snprintf(value, MAX_MSG, "not found"); + } else if (callbacks == -1) { + snprintf(value, MAX_MSG, "found - no functions"); + } else if (callbacks > 0) { + snprintf(value, MAX_MSG, "found - %d function(s)", callbacks); + } + + output("TLS directory", value); + + // invalid timestamp + const IMAGE_COFF_HEADER *coff = pe_coff(ctx); + if (coff == NULL) { + LIBPE_WARNING("unable to read coff header"); + } else { + print_timestamp(verbose, coff); + } + + // section analysis + print_strange_sections(ctx); +} + diff --git a/src/section.c b/src/section.c new file mode 100644 index 00000000..ae4aa7b2 --- /dev/null +++ b/src/section.c @@ -0,0 +1,194 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + 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 "common.h" +#include "output.h" +#include "readpe.h" + +#include +#include + +void print_sections(pe_ctx_t *ctx) +{ + + output_open_scope("Sections", OUTPUT_SCOPE_TYPE_ARRAY); + + const uint32_t num_sections = pe_sections_count(ctx); + if (num_sections == 0 || num_sections > MAX_SECTIONS) { + output_close_scope(); // Sections + return; + } + + IMAGE_SECTION_HEADER **sections = pe_sections(ctx); + if (sections == NULL) { + LIBPE_WARNING("unable to read sections"); + output_close_scope(); // Sections + return; + } + + for (uint32_t i = 0; i < num_sections; i++) { + print_section(ctx, sections[i], NULL); + } + + output_close_scope(); // sections +} + +void print_sections_list(pe_ctx_t *ctx) +{ + output_open_scope("Sections", OUTPUT_SCOPE_TYPE_ARRAY); + + const uint32_t num_sections = pe_sections_count(ctx); + if (num_sections == 0 || num_sections > MAX_SECTIONS) { + output_close_scope(); // Sections + return; + } + + IMAGE_SECTION_HEADER **sections = pe_sections(ctx); + if (sections == NULL) { + LIBPE_WARNING("unable to read sections"); + output_close_scope(); // Sections + return; + } + + static char section_name_buffer[SECTION_NAME_SIZE + 1]; + + for (uint32_t i = 0; i < num_sections; i++) { + const char *section_name = pe_section_name( + ctx, sections[i], section_name_buffer, sizeof(section_name_buffer)); + output(NULL, section_name); + } + + output_close_scope(); // Sections +} + +void print_section(pe_ctx_t *ctx, IMAGE_SECTION_HEADER *section, + const char *section_name) +{ +#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + static const char *const flags_name[] + = {"contains executable code", + "contains initialized data", + "contains uninitialized data", + "contains data referenced through the GP", + "contains extended relocations", + "can be discarded as needed", + "cannot be cached", + "is not pageable", + "can be shared in memory", + "is executable", + "is readable", + "is writable"}; +#endif + // valid flags only for executables referenced in pecoffv8 + static const SectionCharacteristics valid_flags[] + = {IMAGE_SCN_CNT_CODE, + IMAGE_SCN_CNT_INITIALIZED_DATA, + IMAGE_SCN_CNT_UNINITIALIZED_DATA, + IMAGE_SCN_GPREL, + IMAGE_SCN_LNK_NRELOC_OVFL, + IMAGE_SCN_MEM_DISCARDABLE, + IMAGE_SCN_MEM_NOT_CACHED, + IMAGE_SCN_MEM_NOT_PAGED, + IMAGE_SCN_MEM_SHARED, + IMAGE_SCN_MEM_EXECUTE, + IMAGE_SCN_MEM_READ, + IMAGE_SCN_MEM_WRITE}; + + static const size_t max_flags = LIBPE_SIZEOF_ARRAY(valid_flags); + + static char s[MAX_MSG]; + output_open_scope("Section", OUTPUT_SCOPE_TYPE_OBJECT); + + if (section_name == NULL) { + static char section_name_buffer[SECTION_NAME_SIZE + 1]; + section_name = pe_section_name(ctx, section, section_name_buffer, + sizeof(section_name_buffer)); + } + + output("Name", section_name); + + snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)", section->Misc.VirtualSize, + section->Misc.VirtualSize); + output("Virtual Size", s); + + snprintf(s, MAX_MSG, "%#x", section->VirtualAddress); + output("Virtual Address", s); + + snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)", section->SizeOfRawData, + section->SizeOfRawData); + output("Size Of Raw Data", s); + + snprintf(s, MAX_MSG, "%#x", section->PointerToRawData); + output("Pointer To Raw Data", s); + + snprintf(s, MAX_MSG, "%" PRIu16, section->NumberOfRelocations); + output("Number Of Relocations", s); + + snprintf(s, MAX_MSG, "%#x", section->Characteristics); + output("Characteristics", s); + + output_open_scope("Characteristic Names", OUTPUT_SCOPE_TYPE_ARRAY); + + for (size_t j = 0; j < max_flags; j++) { + if (section->Characteristics & (uint32_t) valid_flags[j]) { +#ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 + snprintf(s, MAX_MSG, "%s", flags_name[j]); + output(NULL, s); +#else + const char *characteristic_name + = pe_section_characteristic_name(valid_flags[j]); + char formatted_characteristic_name[32]; + if (characteristic_name == NULL) { + snprintf(formatted_characteristic_name, + sizeof(formatted_characteristic_name) - 1, + "UNKNOWN[%#x]", valid_flags[j]); + characteristic_name = formatted_characteristic_name; + } + output(NULL, characteristic_name); +#endif + } + } + + output_close_scope(); // Characteristic Names + + output_close_scope(); // Section +} + +void print_section_by_name(pe_ctx_t *ctx, const char *section_name) +{ + IMAGE_SECTION_HEADER *section = pe_section_by_name(ctx, section_name); + // IMAGE_SECTION_HEADER **sections = pe_sections(ctx); + print_section(ctx, section, section_name); +} + diff --git a/src/security.c b/src/security.c new file mode 100644 index 00000000..d3af2311 --- /dev/null +++ b/src/security.c @@ -0,0 +1,232 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + security.c - Checks for security features in PE files. + + 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 "common.h" +#include "readpe.h" + +#include +#include + +/* +find stack cookies, a.k.a canary, buffer security check +option in MVS 2010 +*/ +// FIXME: What about other versions? +bool stack_cookies(pe_ctx_t *ctx) +{ + static const unsigned char mvs2010[] + = {0x55, 0x8b, 0xec, 0x83, 0x33, 0xc5, 0x33, 0xcd, 0xe8, 0xc3}; + + if (ctx == NULL) { + return false; + } + + size_t found = 0; + const uint8_t *file_bytes = LIBPE_PTR_ADD(ctx->map_addr, 0); + const uint64_t filesize = pe_filesize(ctx); + + // FIXME: Is this right?! Seems like partial matches will be + // Accumulated. Example: If all these bytes are found, + // separatelly in the file, this function will return true. + for (uint64_t ofs = 0; ofs < filesize; ofs++) { + for (size_t i = 0; i < sizeof(mvs2010); i++) { + if (file_bytes[ofs] == mvs2010[i] && found == i) { + found++; + } + } + } + + return found == sizeof(mvs2010); +} + +void print_securities(pe_ctx_t *ctx) +{ + IMAGE_OPTIONAL_HEADER *optional = pe_optional(ctx); + if (optional == NULL) { + exit(EXIT_FAILURE); // FIXME: exit + } + + uint16_t dllchar = 0; + switch (optional->type) { + default: + exit(EXIT_FAILURE); // FIXME: exit + case MAGIC_PE32: + dllchar = optional->_32->DllCharacteristics; + break; + case MAGIC_PE64: + dllchar = optional->_64->DllCharacteristics; + break; + } + static char field[MAX_MSG]; + // aslr + snprintf(field, MAX_MSG, "ASLR"); + output(field, (dllchar & 0x40) ? "yes" : "no"); + + // dep/nx + snprintf(field, MAX_MSG, "DEP/NX"); + output(field, (dllchar & 0x100) ? "yes" : "no"); + + // seh + snprintf(field, MAX_MSG, "SEH"); + output(field, (dllchar & 0x400) ? "no" : "yes"); + + // cfg + snprintf(field, MAX_MSG, "CFG"); + output(field, (dllchar & 0x4000) ? "yes" : "no"); + + // stack cookies + snprintf(field, MAX_MSG, "Stack cookies (EXPERIMENTAL)"); + output(field, stack_cookies(ctx) ? "yes" : "no"); +} + +// static void parse_certificates(pe_ctx_t *ctx) { +// +// 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; +// +// +// 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 +// // = 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 +// } +// +// } + diff --git a/src/windows/Makefile b/src/windows/Makefile deleted file mode 100644 index 904f514b..00000000 --- a/src/windows/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -CFLAGS = -O2 -W -Wall -Wextra -std=c99 -pedantic -pev_BUILDDIR = ../build/ - -all: - cc $(CFLAGS) -o $(pev_BUILDDIR)/cpload.exe cpload.c -clean: - rm -f cpload.exe diff --git a/t/texe/CMakeLists.txt b/t/texe/CMakeLists.txt new file mode 100644 index 00000000..cb00813f --- /dev/null +++ b/t/texe/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20) +project(texe) + +# Test EXEcutable + +add_executable(t texe.c texe.rc) + diff --git a/t/texe/Makefile b/t/texe/Makefile new file mode 100644 index 00000000..a15c0498 --- /dev/null +++ b/t/texe/Makefile @@ -0,0 +1,14 @@ +# This is probably not standardized but WorksForMe +CC = x86_64-w64-mingw32-gcc +# Very generic test file +CFLAGS += -DNDEBUG -Oz -mtune=generic -march=x86-64 -s -Wl,-s -Wl,--gc-sections + +all: helloworld.exe + +.PHONY: all + +helloworld: helloworld.c + +%.exe: % + mv $@ ../samples/$@ + diff --git a/t/texe/texe.c b/t/texe/texe.c new file mode 100644 index 00000000..308d7b51 --- /dev/null +++ b/t/texe/texe.c @@ -0,0 +1,43 @@ +/* + pev - the PE file analyzer toolkit + + helloworld.c - basic hello world program + + 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. +*/ + +#include +#include + +int main(int argc, char *argv[]) { + printf("Hello World\n"); + return EXIT_SUCCESS; +} + diff --git a/t/texe/texe.rc b/t/texe/texe.rc new file mode 100644 index 00000000..66765fc9 --- /dev/null +++ b/t/texe/texe.rc @@ -0,0 +1,36 @@ +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +FILEFLAGSMASK 0x3fL +#ifdef _DEBUG +FILEFLAGS 0x1L +#else +FILEFLAGS 0x0L +#endif +FILEOS 0x4L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "040904b0" + { + VALUE "Comments", "Executable to throw against test\0" + VALUE "CompanyName", "readpe\0" + VALUE "FileDescription", "base file\0" + VALUE "FileVersion", "1.0.0.0\0" + VALUE "InternalName", "texe\0" + VALUE "LegalCopyright", "GPL-2\0" + VALUE "OriginalFilename", "t.exe\0" + VALUE "ProductName", "TExe\0" + VALUE "ProductVersion", "1.0.0.0\0" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } +} +