├── .appveyor.yml ├── .github └── workflows │ └── main.yml ├── .gitignore ├── CMakeLists.txt ├── Doxyfile ├── LICENSE ├── README.md ├── libpeconv ├── CMakeLists.txt ├── include │ ├── peconv.h │ └── peconv │ │ ├── buffer_util.h │ │ ├── caves.h │ │ ├── delayed_imports_loader.h │ │ ├── exceptions_parser.h │ │ ├── exported_func.h │ │ ├── exports_lookup.h │ │ ├── exports_mapper.h │ │ ├── file_util.h │ │ ├── find_base.h │ │ ├── fix_imports.h │ │ ├── function_resolver.h │ │ ├── hooks.h │ │ ├── imports_loader.h │ │ ├── imports_uneraser.h │ │ ├── load_config_defs.h │ │ ├── load_config_util.h │ │ ├── pe_dumper.h │ │ ├── pe_hdrs_helper.h │ │ ├── pe_loader.h │ │ ├── pe_mode_detector.h │ │ ├── pe_raw_to_virtual.h │ │ ├── pe_virtual_to_raw.h │ │ ├── peb_lookup.h │ │ ├── relocate.h │ │ ├── remote_pe_reader.h │ │ ├── resource_parser.h │ │ ├── resource_util.h │ │ ├── tls_parser.h │ │ ├── unicode.h │ │ └── util.h └── src │ ├── buffer_util.cpp │ ├── caves.cpp │ ├── delayed_imports_loader.cpp │ ├── exceptions_parser.cpp │ ├── exported_func.cpp │ ├── exports_lookup.cpp │ ├── exports_mapper.cpp │ ├── file_util.cpp │ ├── find_base.cpp │ ├── fix_dot_net_ep.cpp │ ├── fix_dot_net_ep.h │ ├── fix_imports.cpp │ ├── function_resolver.cpp │ ├── hooks.cpp │ ├── imports_loader.cpp │ ├── imports_uneraser.cpp │ ├── load_config_util.cpp │ ├── ntddk.h │ ├── pe_dumper.cpp │ ├── pe_hdrs_helper.cpp │ ├── pe_loader.cpp │ ├── pe_mode_detector.cpp │ ├── pe_raw_to_virtual.cpp │ ├── pe_virtual_to_raw.cpp │ ├── peb_lookup.cpp │ ├── relocate.cpp │ ├── remote_pe_reader.cpp │ ├── resource_parser.cpp │ ├── resource_util.cpp │ ├── tls_parser.cpp │ └── util.cpp ├── pe_unmapper └── README.md ├── run_pe ├── CMakeLists.txt ├── README.md ├── main.cpp ├── patch_ntdll.cpp ├── patch_ntdll.h ├── run_pe.cpp └── run_pe.h └── tests ├── CMakeLists.txt ├── greek_to_me.bin ├── main.cpp ├── resource.h ├── resource.rc ├── shellc32.h ├── shellc64.h ├── shellcodes.h ├── test_case1 ├── CMakeLists.txt └── main.cpp ├── test_case2 ├── README.md └── payload.dll ├── test_case3 ├── CMakeLists.txt ├── bin │ ├── test_case3_32.exe │ └── test_case3_64.exe ├── checksum.cpp ├── checksum.h └── main.cpp ├── test_case4 ├── CMakeLists.txt └── main.cpp ├── test_case5 ├── CMakeLists.txt ├── main.cpp └── test_case5_dll │ ├── CMakeLists.txt │ ├── include │ └── api.h │ ├── main.cpp │ └── main.def ├── test_case6 ├── CMakeLists.txt ├── callback.h ├── main.cpp ├── main.h ├── sockets.cpp └── sockets.h ├── test_case7 ├── CMakeLists.txt └── main.cpp ├── test_crackme_f4_3.cpp ├── test_crackme_f4_3.h ├── test_crackme_f4_6.cpp ├── test_crackme_f4_6.h ├── test_delayed_imps.cpp ├── test_delayed_imps.h ├── test_exceptions.cpp ├── test_exceptions.h ├── test_fix_dotnet.cpp ├── test_fix_dotnet.h ├── test_format_detect.cpp ├── test_format_detect.h ├── test_found_base.cpp ├── test_found_base.h ├── test_hooking_imps.cpp ├── test_hooking_imps.h ├── test_hooking_local.cpp ├── test_hooking_local.h ├── test_imp_list.cpp ├── test_imp_list.h ├── test_imports_mix.cpp ├── test_imports_mix.h ├── test_load_ntdll.cpp ├── test_load_ntdll.h ├── test_loading.cpp ├── test_loading.h ├── test_loading_imps.cpp ├── test_loading_imps.h ├── test_peb_lookup.cpp ├── test_peb_lookup.h ├── test_replacing_func.cpp ├── test_replacing_func.h ├── test_tls_callbacks.cpp └── test_tls_callbacks.h /.appveyor.yml: -------------------------------------------------------------------------------- 1 | shallow_clone: true 2 | 3 | os: 4 | - Visual Studio 2015 5 | - Visual Studio 2019 6 | 7 | platform: 8 | - x64 9 | 10 | branches: 11 | only: 12 | - master 13 | 14 | install: 15 | - set PATH=C:\Program Files\CMake\bin;%PATH% 16 | 17 | build: 18 | verbosity: detailed 19 | 20 | configuration: 21 | - Release 22 | - Debug 23 | 24 | environment: 25 | artifactName: $(APPVEYOR_PROJECT_NAME)-$(APPVEYOR_REPO_COMMIT)-$(CONFIGURATION) 26 | matrix: 27 | - env_arch: "x64" 28 | - env_arch: "x86" 29 | 30 | before_build: 31 | - mkdir build 32 | - cd build 33 | - if [%env_arch%]==[x64] ( 34 | cmake .. -A x64 ) 35 | - if [%env_arch%]==[x86] ( 36 | cmake .. -A Win32 ) 37 | - cmake -DCMAKE_INSTALL_PREFIX:PATH=%APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT% .. 38 | 39 | build_script: 40 | - cmake --build . --config %CONFIGURATION% --target install 41 | 42 | test_script: 43 | - cp "..\tests\test_case3\bin\test_case3_32.exe" %APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT%/ 44 | - cp "..\tests\test_case3\bin\test_case3_64.exe" %APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT%/ 45 | - if [%env_arch%]==[x64] ( 46 | cp "..\tests\test_case2\payload.dll" %APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT%/ ) 47 | - ctest -V 48 | 49 | after_test: 50 | - mkdir %artifactName% 51 | - cp -r %APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT%/* %artifactName% 52 | 53 | artifacts: 54 | - path: build\%artifactName% 55 | 56 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Doxygen Action 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 24 | - uses: actions/checkout@v2 25 | 26 | - name: Doxygen Action 27 | uses: mattnotmitt/doxygen-action@v1.1.0 28 | with: 29 | # Path to Doxyfile 30 | doxyfile-path: "./Doxyfile" # default is ./Doxyfile 31 | # Working directory 32 | working-directory: "." # default is . 33 | 34 | - name: Deploy 35 | uses: peaceiris/actions-gh-pages@v3 36 | with: 37 | github_token: ${{ secrets.GITHUB_TOKEN }} 38 | publish_dir: ./docs/html 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs 2 | *.bak 3 | .vs 4 | out 5 | *.aps 6 | /CMakeSettings.json -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | 3 | project ( peconv ) 4 | option(PECONV_BUILD_TESTING "enable testing for peconv" ON) 5 | option(PECONV_LIB_INSTALL "Enable install" ON) 6 | option(PECONV_UNICODE "Enable Unicode" OFF) 7 | 8 | # modules: 9 | set ( M_PECONV_LIB "libpeconv" ) 10 | set ( M_PECONV_TEST "tests" ) 11 | set ( M_PE_UNMAPPER "pe_unmapper" ) 12 | set ( M_RUN_PE "run_pe" ) 13 | 14 | # modules paths: 15 | set ( PECONV_DIR "${CMAKE_SOURCE_DIR}/${M_PECONV_LIB}" CACHE PATH "PEConvLib main path" ) 16 | set ( UNMAPPER_DIR "${CMAKE_SOURCE_DIR}/${M_PE_UNMAPPER}" CACHE PATH "PEUnmapper main path" ) 17 | set ( RUNPE_DIR "${CMAKE_SOURCE_DIR}/${M_RUN_PE}" CACHE PATH "RunPE main path" ) 18 | 19 | # enable unicode support 20 | if(PECONV_UNICODE) 21 | add_definitions (-DUNICODE -D_UNICODE) 22 | endif() 23 | 24 | # Add sub-directories 25 | # 26 | # libs 27 | add_subdirectory ( libpeconv ) 28 | set ( PECONV_LIB $ CACHE PATH "PEConvLib library path" ) 29 | 30 | #demos: 31 | 32 | add_subdirectory ( run_pe ) 33 | add_dependencies ( run_pe libpeconv ) 34 | 35 | # Setup testing 36 | if(PECONV_BUILD_TESTING) 37 | enable_testing() 38 | 39 | # executables 40 | add_subdirectory ( ${M_PECONV_TEST} ) 41 | add_dependencies ( ${M_PECONV_TEST} libpeconv ) 42 | endif() 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017-2022, hasherezade 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libPeConv 2 | [![Build status](https://ci.appveyor.com/api/projects/status/pqo6ob148pf5b352?svg=true)](https://ci.appveyor.com/project/hasherezade/libpeconv) 3 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/55911b033cf145d38d6e38a0c005b686)](https://app.codacy.com/gh/hasherezade/libpeconv/dashboard?branch=master) 4 | [![Commit activity](https://img.shields.io/github/commit-activity/m/hasherezade/libpeconv)](https://github.com/hasherezade/libpeconv/commits) 5 | [![Last Commit](https://img.shields.io/github/last-commit/hasherezade/libpeconv/master)](https://github.com/hasherezade/libpeconv/commits) 6 | 7 | [![License](https://img.shields.io/badge/License-BSD%202--Clause-blue.svg)](https://opensource.org/licenses/BSD-2-Clause) 8 | [![Platform Badge](https://img.shields.io/badge/Windows-0078D6?logo=windows)](https://github.com/hasherezade/libpeconv) 9 | 10 | *A library to load and manipulate PE files.* 11 | 12 | ### Objectives 13 | 14 | The goal of libPEConv was to create a "swiss army knife" for custom loading of PE files. It gathers various helper functions that you can quickly integrate in your own loader. For example: remapping sections, applying relocations, loading imports, parsing resources. 15 | 16 | Not only it allows for loading PE files, but also for customizing of some steps, i.e. IAT hooking (by providing custom IAT resolvers), and functions redirection. Yet, it is NOT focused on inline hooking and should not be confused with libraries such as MS Detours or MinHook. 17 | 18 | LibPeConv can be used for creating PE binders, as it allows to load a PE directly from the resource, and integrate it as if it was a local code. 19 | 20 | As well it can help you in dumping PEs from the memory, and rebuilding their IATs. 21 | 22 | WARNING: applications that use [MUI are not supported](https://github.com/hasherezade/libpeconv/issues/44). 23 | 24 | ### Basic example 25 | 26 | *The simplest usecase*: use libPeConv to manually load and run an EXE of you choice. 27 | 28 | ```C 29 | #include 30 | #include 31 | 32 | #include // include libPeConv header 33 | 34 | int main(int argc, char *argv[]) 35 | { 36 | if (argc < 2) { 37 | std::cout << "Args: " << std::endl; 38 | return 0; 39 | } 40 | LPCSTR pe_path = argv[1]; 41 | 42 | // manually load the PE file using libPeConv: 43 | size_t v_size = 0; 44 | #ifdef LOAD_FROM_PATH 45 | //if the PE is dropped on the disk, you can load it from the file: 46 | BYTE* my_pe = peconv::load_pe_executable(pe_path, v_size); 47 | #else 48 | size_t bufsize = 0; 49 | BYTE *buffer = peconv::load_file(pe_path, bufsize); 50 | 51 | // if the file is NOT dropped on the disk, you can load it directly from a memory buffer: 52 | BYTE* my_pe = peconv::load_pe_executable(buffer, bufsize, v_size); 53 | #endif 54 | if (!my_pe) { 55 | return -1; 56 | } 57 | 58 | // if the loaded PE needs to access resources, you may need to connect it to the PEB: 59 | peconv::set_main_module_in_peb((HMODULE)my_pe); 60 | 61 | // load delayed imports (if present): 62 | const ULONGLONG load_base = (ULONGLONG)my_pe; 63 | peconv::load_delayed_imports(my_pe, load_base); 64 | 65 | // if needed, you can run TLS callbacks before the Entry Point: 66 | peconv::run_tls_callbacks(my_pe, v_size); 67 | 68 | //calculate the Entry Point of the manually loaded module 69 | DWORD ep_rva = peconv::get_entry_point_rva(my_pe); 70 | if (!ep_rva) { 71 | return -2; 72 | } 73 | ULONG_PTR ep_va = ep_rva + (ULONG_PTR) my_pe; 74 | //assuming that the payload is an EXE file (not DLL) this will be the simplest prototype of the main: 75 | int (*new_main)() = (int(*)())ep_va; 76 | 77 | //call the Entry Point of the manually loaded PE: 78 | return new_main(); 79 | } 80 | 81 | ``` 82 | *See also: https://github.com/hasherezade/libpeconv_tpl/blob/master/project_template/main.cpp* 83 | 84 | ### Read more 85 | + [Wiki](https://github.com/hasherezade/libpeconv/wiki) 86 | + [Docs](https://hasherezade.github.io/libpeconv/) 87 | + [Examples](https://github.com/hasherezade/libpeconv/tree/master/tests) 88 | + [Tutorials](https://hshrzd.wordpress.com/tag/libpeconv/) 89 | + [Project template](https://github.com/hasherezade/libpeconv_project_template) 90 | -------------------------------------------------------------------------------- /libpeconv/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.12 ) 2 | project ( libpeconv ) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | include_directories ( 7 | include 8 | ) 9 | 10 | set (srcs 11 | src/pe_hdrs_helper.cpp 12 | src/pe_mode_detector.cpp 13 | src/pe_raw_to_virtual.cpp 14 | src/pe_virtual_to_raw.cpp 15 | src/relocate.cpp 16 | src/buffer_util.cpp 17 | src/remote_pe_reader.cpp 18 | src/imports_loader.cpp 19 | src/delayed_imports_loader.cpp 20 | src/fix_imports.cpp 21 | src/pe_loader.cpp 22 | src/pe_dumper.cpp 23 | src/exports_lookup.cpp 24 | src/function_resolver.cpp 25 | src/hooks.cpp 26 | src/exported_func.cpp 27 | src/exports_mapper.cpp 28 | src/resource_parser.cpp 29 | src/file_util.cpp 30 | src/resource_util.cpp 31 | src/imports_uneraser.cpp 32 | src/load_config_util.cpp 33 | src/caves.cpp 34 | src/util.cpp 35 | src/fix_dot_net_ep.cpp 36 | src/find_base.cpp 37 | src/peb_lookup.cpp 38 | src/tls_parser.cpp 39 | src/exceptions_parser.cpp 40 | ) 41 | 42 | set (hdrs 43 | include/peconv.h 44 | include/peconv/pe_hdrs_helper.h 45 | include/peconv/pe_mode_detector.h 46 | include/peconv/pe_raw_to_virtual.h 47 | include/peconv/pe_virtual_to_raw.h 48 | include/peconv/relocate.h 49 | include/peconv/util.h 50 | include/peconv/buffer_util.h 51 | include/peconv/remote_pe_reader.h 52 | include/peconv/imports_loader.h 53 | include/peconv/delayed_imports_loader.h 54 | include/peconv/fix_imports.h 55 | include/peconv/pe_loader.h 56 | include/peconv/pe_dumper.h 57 | include/peconv/exports_lookup.h 58 | include/peconv/function_resolver.h 59 | include/peconv/hooks.h 60 | include/peconv/exported_func.h 61 | include/peconv/exports_mapper.h 62 | include/peconv/resource_parser.h 63 | include/peconv/file_util.h 64 | include/peconv/resource_util.h 65 | include/peconv/imports_uneraser.h 66 | include/peconv/load_config_util.h 67 | include/peconv/load_config_defs.h 68 | include/peconv/caves.h 69 | include/peconv/find_base.h 70 | include/peconv/peb_lookup.h 71 | include/peconv/tls_parser.h 72 | include/peconv/exceptions_parser.h 73 | include/peconv/unicode.h 74 | src/fix_dot_net_ep.h #not in API 75 | ) 76 | 77 | add_library ( ${PROJECT_NAME} STATIC ${hdrs} ${srcs} ) 78 | 79 | if(PECONV_LIB_INSTALL) 80 | include(GNUInstallDirs) 81 | 82 | install(TARGETS ${PROJECT_NAME} 83 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 84 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 85 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 86 | 87 | install(DIRECTORY "include/" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 88 | endif() 89 | -------------------------------------------------------------------------------- /libpeconv/include/peconv.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Master include file, including everything else. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "peconv/buffer_util.h" 9 | #include "peconv/util.h" 10 | #include "peconv/pe_hdrs_helper.h" 11 | #include "peconv/pe_mode_detector.h" 12 | #include "peconv/pe_raw_to_virtual.h" 13 | #include "peconv/pe_virtual_to_raw.h" 14 | #include "peconv/relocate.h" 15 | #include "peconv/remote_pe_reader.h" 16 | #include "peconv/imports_loader.h" 17 | #include "peconv/pe_loader.h" 18 | #include "peconv/pe_dumper.h" 19 | #include "peconv/exports_lookup.h" 20 | #include "peconv/function_resolver.h" 21 | #include "peconv/hooks.h" 22 | #include "peconv/exports_mapper.h" 23 | #include "peconv/caves.h" 24 | #include "peconv/fix_imports.h" 25 | #include "peconv/delayed_imports_loader.h" 26 | #include "peconv/resource_parser.h" 27 | #include "peconv/load_config_util.h" 28 | #include "peconv/peb_lookup.h" 29 | #include "peconv/find_base.h" 30 | #include "peconv/tls_parser.h" 31 | #include "peconv/exceptions_parser.h" 32 | #include "peconv/unicode.h" 33 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/buffer_util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Definitions of the used buffer types. Functions for their allocation and deallocation. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #define MAX_DWORD 0xffffffff 11 | #define MAX_WORD 0xffff 12 | #define MASK_TO_DWORD(val) ((val < MAX_DWORD) ? (val & MAX_DWORD) : MAX_DWORD) 13 | #define MASK_TO_WORD(val) ((val < MAX_WORD) ? (val & MAX_WORD) : MAX_WORD) 14 | 15 | namespace peconv { 16 | 17 | /** 18 | Validates pointers, checks if the particular field is inside the given buffer. Sizes must be given in bytes. 19 | \param buffer_bgn : the start address of the buffer 20 | \param buffer_size : the size of the buffer 21 | \param field_bgn : the start address of the field 22 | \param field_size : the size of the field 23 | \return true if the field (defined by its start address: field_bgn, and size: field_size) is contained within the given buffer 24 | (defined by its start address: buffer_bgn, and size: buffer_size). 25 | false otherwise 26 | */ 27 | bool validate_ptr( 28 | IN const void* buffer_bgn, 29 | IN size_t buffer_size, 30 | IN const void* field_bgn, 31 | IN size_t field_size 32 | ); 33 | 34 | //----------------------------------------------------------------------------------- 35 | // 36 | // supported buffers: 37 | // 38 | /** 39 | A buffer allocated on the heap of a process, not aligned to the beginning of a memory page. 40 | */ 41 | typedef PBYTE UNALIGNED_BUF; 42 | 43 | /** 44 | A buffer allocated in a virtual space of a process, aligned to the beginning of a memory page. 45 | */ 46 | typedef PBYTE ALIGNED_BUF; 47 | 48 | // 49 | // alloc/free unaligned buffers: 50 | // 51 | /** 52 | Allocates a buffer on the heap. Can be used in the cases when the buffer does not have to start at the beginning of a page. 53 | */ 54 | UNALIGNED_BUF alloc_unaligned(size_t buf_size); 55 | 56 | // 57 | /** 58 | Frees buffer allocated by alloc_unaligned. 59 | */ 60 | void free_unaligned(UNALIGNED_BUF section_buffer); 61 | 62 | // 63 | // alloc/free aligned buffers: 64 | // 65 | 66 | /** 67 | Allocates a buffer of a virtual memory (using VirtualAlloc). Can be used in the cases when the buffer have to be aligned to the beginning of a page. 68 | */ 69 | ALIGNED_BUF alloc_aligned(size_t buffer_size, DWORD protect, void* desired_base=nullptr); 70 | 71 | /** 72 | Frees buffer allocated by alloc_aligned. 73 | */ 74 | bool free_aligned(ALIGNED_BUF buffer, size_t buffer_size=0); 75 | 76 | //PE buffers (wrappers) 77 | 78 | /** 79 | Allocates an aligned buffer for a PE file. 80 | */ 81 | ALIGNED_BUF alloc_pe_buffer(size_t buffer_size, DWORD protect, void* desired_base=nullptr); 82 | 83 | /** 84 | Free the memory allocated with alloc_pe_buffer. 85 | */ 86 | bool free_pe_buffer(ALIGNED_BUF buffer, size_t buffer_size=0); 87 | 88 | }; //namespace peconv 89 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/caves.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions related to finding caves in the loaded PE file. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace peconv { 11 | 12 | /** 13 | Finds cave at the end of the image (extend last section's raw size without extending the full image size) 14 | */ 15 | PBYTE find_ending_cave(BYTE* module_ptr, size_t module_size, const DWORD cave_size, const DWORD cave_charact=IMAGE_SCN_MEM_READ); 16 | 17 | /** 18 | Finds cave in the difference between the original raw size, and the raw size rounded to the aligmnent 19 | */ 20 | PBYTE find_alignment_cave(BYTE* modulePtr, size_t moduleSize, const DWORD cave_size, const DWORD req_charact = IMAGE_SCN_MEM_READ); 21 | 22 | /** 23 | Finds cave at the end of the section, that comes from a NULL padding or INT3 padding 24 | */ 25 | PBYTE find_padding_cave(BYTE* modulePtr, size_t moduleSize, const size_t minimal_size, const DWORD req_charact = IMAGE_SCN_MEM_READ); 26 | 27 | };//namespace peconv 28 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/delayed_imports_loader.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Parsing and filling the Delayload Import Table. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include "pe_hdrs_helper.h" 11 | #include "function_resolver.h" 12 | 13 | #if (defined(_WIN32_WINNT) && _WIN32_WINNT > 0x0601) || __MINGW32__ //Windows SDK version 6.1 (Windows 7) 14 | #define DELAYLOAD_IMPORTS_DEFINED 15 | #endif 16 | 17 | #ifndef DELAYLOAD_IMPORTS_DEFINED 18 | #include "pshpack4.h" 19 | 20 | typedef struct _IMAGE_DELAYLOAD_DESCRIPTOR { 21 | union { 22 | DWORD AllAttributes; 23 | struct { 24 | DWORD RvaBased : 1; // Delay load version 2 25 | DWORD ReservedAttributes : 31; 26 | } DUMMYSTRUCTNAME; 27 | } Attributes; 28 | 29 | DWORD DllNameRVA; // RVA to the name of the target library (NULL-terminate ASCII string) 30 | DWORD ModuleHandleRVA; // RVA to the HMODULE caching location (PHMODULE) 31 | DWORD ImportAddressTableRVA; // RVA to the start of the IAT (PIMAGE_THUNK_DATA) 32 | DWORD ImportNameTableRVA; // RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData) 33 | DWORD BoundImportAddressTableRVA; // RVA to an optional bound IAT 34 | DWORD UnloadInformationTableRVA; // RVA to an optional unload info table 35 | DWORD TimeDateStamp; // 0 if not bound, 36 | // Otherwise, date/time of the target DLL 37 | 38 | } IMAGE_DELAYLOAD_DESCRIPTOR, *PIMAGE_DELAYLOAD_DESCRIPTOR; 39 | 40 | typedef const IMAGE_DELAYLOAD_DESCRIPTOR *PCIMAGE_DELAYLOAD_DESCRIPTOR; 41 | 42 | #include "poppack.h" 43 | #endif 44 | 45 | namespace peconv { 46 | 47 | /** 48 | Get the Delayload Imports directory. Returns the pointer to the first descriptor. The size of the directory is passed via variable dir_size. 49 | */ 50 | IMAGE_DELAYLOAD_DESCRIPTOR* get_delayed_imps(IN const BYTE* modulePtr, IN const size_t moduleSize, OUT size_t &dir_size); 51 | 52 | /** 53 | Fill the Delayload Imports in the given module. 54 | \param modulePtr : the pointer to the module where the imports needs to be filled. 55 | \param moduleBase : the base to which the module was relocated, it may (or not) be the same as modulePtr 56 | \param func_resolver : the resolver that will be used for loading the imports 57 | \return : true if resolving all succeeded, false otherwise 58 | */ 59 | bool load_delayed_imports(BYTE* modulePtr, const ULONGLONG moduleBase, t_function_resolver* func_resolver = nullptr); 60 | 61 | }; // namespace peconv 62 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/exceptions_parser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions related to Exceptions Table 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "peconv/buffer_util.h" 9 | 10 | namespace peconv { 11 | 12 | /** 13 | Allows to activate the Exception table from the manually loaded module. 14 | For 32-bits the loaded image should enable /SAFESEH linker option, 15 | otherwise the exception handler cannot pass the RtlIsValidHandler() check 16 | when an exception occurs 17 | */ 18 | bool setup_exceptions(IN BYTE* modulePtr, IN size_t moduleSize); 19 | 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/exported_func.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief A definition of ExportedFunc class - used for storing the details of the exported function. Helper functions related to the export parsing. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace peconv { 14 | 15 | /** 16 | Check if the pointer redirects to a forwarder - if so, return the length, otherwise return 0. 17 | */ 18 | size_t forwarder_name_len(BYTE* fPtr); 19 | 20 | /** 21 | get the DLL name without the extension 22 | */ 23 | std::string get_dll_shortname(const std::string& str); 24 | 25 | /** 26 | Get the function name from the string in a format: DLL_name.function_name 27 | */ 28 | std::string get_func_name(const std::string& str); 29 | 30 | /** 31 | Convert ordinal value to the ordinal string (in a format #[ordinal]) 32 | */ 33 | std::string ordinal_to_string(DWORD func_ordinal); 34 | 35 | /** 36 | Check if the given string is in a format typical for storing ordinals (#[ordinal]) 37 | */ 38 | bool is_ordinal_string(const std::string& str); 39 | 40 | /** 41 | Get the ordinal value from the ordinal string (in a format #[ordinal]) 42 | */ 43 | DWORD ordinal_string_to_val(const std::string& str); 44 | 45 | /** 46 | Convert the function in a format: DLL_name.function_name into a normalized form (DLL name in lowercase). 47 | */ 48 | std::string format_dll_func(const std::string& str); 49 | 50 | /** 51 | A class storing the information about the exported function. 52 | */ 53 | class ExportedFunc 54 | { 55 | public: 56 | /** 57 | Converts the name to the normalized format. 58 | */ 59 | static std::string formatName(std::string name); 60 | 61 | //! Compares functions' names. If function is defined by an ordinal, compares ordinals. Does not include the DLL name in the comparison. 62 | static bool isTheSameFuncName(const peconv::ExportedFunc& func1, const peconv::ExportedFunc& func2); 63 | 64 | //! Compares functions' DLL names. 65 | static bool isTheSameDllName(const peconv::ExportedFunc& func1, const peconv::ExportedFunc& func2); 66 | 67 | //! Compares functions' names. If function is defined by an ordinal, compares ordinals. Includes the DLL name in the comparison. 68 | static bool isTheSameFunc(const peconv::ExportedFunc& func1, const peconv::ExportedFunc& func2); 69 | 70 | std::string libName; 71 | std::string funcName; 72 | DWORD funcOrdinal; 73 | bool isByOrdinal; 74 | 75 | //default constructor: 76 | ExportedFunc() : funcOrdinal(0), isByOrdinal(false) {} 77 | 78 | ExportedFunc(const ExportedFunc& other); 79 | ExportedFunc(std::string libName, std::string funcName, DWORD funcOrdinal); 80 | ExportedFunc(std::string libName, DWORD funcOrdinal); 81 | ExportedFunc(const std::string &forwarderName); 82 | 83 | /** 84 | Compare two functions with each other. 85 | Gives the priority to the named functions: if one of the compared functions is unnamed, the named one is treated as smaller. 86 | If both functions are unnamed, the function with the smaller ordinal is treated as smaller. 87 | Otherwise, the function with the shorter name is treated as smaller. 88 | */ 89 | bool operator < (const ExportedFunc& other) const 90 | { 91 | //if only one function is named, give the preference to the named one: 92 | const size_t thisNameLen = this->funcName.length(); 93 | const size_t otherNameLen = other.funcName.length(); 94 | if (thisNameLen == 0 && otherNameLen > 0) { 95 | return false; 96 | } 97 | if (thisNameLen > 0 && otherNameLen == 0) { 98 | return true; 99 | } 100 | //select by shorter lib name: 101 | int cmp = libName.compare(other.libName); 102 | if (cmp != 0) { 103 | return cmp < 0; 104 | } 105 | if (thisNameLen == 0 || otherNameLen == 0) { 106 | return this->funcOrdinal < other.funcOrdinal; 107 | } 108 | if (thisNameLen != otherNameLen) { 109 | return thisNameLen < otherNameLen; 110 | } 111 | cmp = funcName.compare(other.funcName); 112 | return cmp < 0; 113 | } 114 | 115 | /** 116 | Gets a string representation of the variable. Full info about the function: library, name, ordinal. 117 | */ 118 | std::string toString() const; 119 | 120 | /** 121 | Gets a string representation of the variable. Short info about the function: only function name or ordinal (if the name is missing). 122 | */ 123 | std::string nameToString() const; 124 | 125 | bool isValid() const 126 | { 127 | return (funcName != "" || funcOrdinal != -1); 128 | } 129 | }; 130 | 131 | }; //namespace peconv 132 | 133 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/exports_lookup.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Searching specific functions in PE's Exports Table. 4 | */ 5 | 6 | #pragma once 7 | #include 8 | 9 | #include "pe_hdrs_helper.h" 10 | #include "function_resolver.h" 11 | #include "exports_mapper.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace peconv { 18 | 19 | /** 20 | Gets the function address by the name. Uses Export Table lookup. 21 | WARNING: doesn't work for the forwarded functions. 22 | */ 23 | FARPROC get_exported_func(PVOID modulePtr, LPCSTR wanted_name); 24 | 25 | /** 26 | Gets list of all the functions from a given module that are exported by names. 27 | */ 28 | size_t get_exported_names(PVOID modulePtr, std::vector &names_list); 29 | 30 | /** 31 | Function resolver using Export Table lookup. 32 | */ 33 | class export_based_resolver : default_func_resolver { 34 | public: 35 | /** 36 | Get the address (VA) of the function with the given name, from the given DLL. 37 | Uses Export Table lookup as a primary method of finding the import. On failure it falls back to the default Functions Resolver. 38 | \param func_name : the name of the function 39 | \param lib_name : the name of the DLL 40 | \return Virtual Address of the exported function 41 | */ 42 | virtual FARPROC resolve_func(LPCSTR lib_name, LPCSTR func_name); 43 | }; 44 | 45 | /** 46 | Read the DLL name from the Export Table. 47 | */ 48 | LPSTR read_dll_name(HMODULE modulePtr); 49 | 50 | }; //namespace peconv 51 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/file_util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions related to operations on files. Wrappers for read/write. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | #include "buffer_util.h" 12 | 13 | namespace peconv { 14 | 15 | /** 16 | Maps a file with the given path and copies its raw content into the output buffer. 17 | If read_size is not zero, it reads maximum read_size of bytes. If read_size is zero, it reads the full file. 18 | The actual read size is returned back in read_size. 19 | Automatically allocates a buffer of the required size. 20 | */ 21 | peconv::UNALIGNED_BUF load_file(IN LPCTSTR filename, OUT size_t &r_size); 22 | 23 | /** 24 | Reads a raw content of the file with the given path. 25 | If read_size is not zero, it reads maximum read_size of bytes. If read_size is zero, it reads the full file. 26 | The actual read size is returned back in read_size. 27 | Automatically allocates a buffer of the required size. 28 | */ 29 | peconv::UNALIGNED_BUF read_from_file(IN LPCTSTR path, IN OUT size_t &read_size); 30 | 31 | /** 32 | Writes a buffer of bytes into a file of given path. 33 | \param path : the path to the output file 34 | \param dump_data : the buffer to be dumped 35 | \param dump_size : the size of data to be dumped (in bytes) 36 | \return true if succeeded, false if failed 37 | */ 38 | bool dump_to_file(IN LPCTSTR path, IN PBYTE dump_data, IN size_t dump_size); 39 | 40 | /** 41 | Free the buffer allocated by load_file/read_from_file 42 | */ 43 | void free_file(IN peconv::UNALIGNED_BUF buffer); 44 | 45 | /** 46 | Get the file name from the given path. 47 | */ 48 | std::string get_file_name(IN const std::string full_path); 49 | 50 | /** 51 | Get the directory name from the given path. It assumes that a directory name always ends with a separator ("/" or "\") 52 | */ 53 | std::string get_directory_name(IN const std::string full_path); 54 | 55 | /** 56 | Find a position of possible file extension. If not found, gives string length. 57 | */ 58 | size_t find_extension_pos(IN const std::string str); 59 | 60 | }; //namespace peconv 61 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/find_base.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions related to finding a base to which the module was relocated. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace peconv { 11 | 12 | /** 13 | Try to find a base to which the PE file was relocated, basing on the filled relocations. 14 | WARNING: the found base is an estimate, and sometimes may not be fully accurate. 15 | \param module_ptr : the module which's base is being searched 16 | \param module_size : the size of the given module 17 | \return the base to which the module was relocated 18 | */ 19 | ULONGLONG find_base_candidate(IN BYTE *module_ptr, IN size_t module_size); 20 | }; 21 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/fix_imports.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions and classes responsible for fixing Import Table. A definition of ImportedDllCoverage class. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "pe_hdrs_helper.h" 18 | #include "exports_lookup.h" 19 | #include "exports_mapper.h" 20 | 21 | #define MIN_DLL_LEN 5 22 | 23 | namespace peconv { 24 | 25 | /** 26 | a helper class that allows to store information about functions that could not be covered by the given mapping 27 | */ 28 | class ImpsNotCovered 29 | { 30 | public: 31 | ImpsNotCovered() {} 32 | ~ImpsNotCovered() {} 33 | 34 | /* 35 | Number of stored records 36 | */ 37 | size_t count() { return thunkToAddr.size(); } 38 | 39 | void insert(DWORD thunkRVA, ULONGLONG searchedAddr); 40 | 41 | std::map thunkToAddr; //addresses of not recovered functions with their thunks (call_via) 42 | }; 43 | 44 | /** 45 | fix imports in the given module, using the given map of all available exports 46 | */ 47 | bool fix_imports(IN OUT PVOID modulePtr, IN size_t moduleSize, IN const peconv::ExportsMapper& exportsMap, OUT OPTIONAL peconv::ImpsNotCovered* notCovered); 48 | 49 | /** 50 | a helper class that allows to find out where the functions are imported from 51 | */ 52 | class ImportedDllCoverage 53 | { 54 | public: 55 | /** 56 | A constructor of an object of ImportedDllCoverage class. 57 | \param _addresses : the list of filled imports (VAs): the addresses to be covered 58 | \param _exportsMap : the map of the exports of all the loaded DLLs (the space in which we will be searching) 59 | */ 60 | ImportedDllCoverage(std::set& _addresses, const peconv::ExportsMapper& _exportsMap) 61 | : addresses(_addresses), exportsMap(_exportsMap) 62 | { 63 | } 64 | 65 | /** 66 | Checks if all the addresses can be covered by one DLL. If yes, this dll will be saved into: dllName. 67 | \return true if the covering DLL for the addresses was found. false otherwise. 68 | */ 69 | bool findCoveringDll(); 70 | 71 | /** 72 | Maps the addresses from the set to functions from the given DLL. 73 | Results are saved into: addrToFunc. 74 | Addresses that could not be covered by the given DLL are saved into notFound. 75 | Before each execution, the content of involved variables is erased. 76 | \param _mappedDllName : the name of the DLL that we will be used to mapping. This DLL is saved into mappedDllName. 77 | \return a number of covered functions 78 | */ 79 | size_t mapAddressesToFunctions(const std::string &_mappedDllName); 80 | 81 | /** 82 | Check if the functions mapping is complete. 83 | \return the status: true if all the addresses are mapped to specific exports, false if not 84 | */ 85 | bool isMappingComplete() { return (addresses.size() == addrToFunc.size()) ? true : false; } 86 | 87 | /** 88 | A mapping associating each of the covered function addresses with the set of exports (from mapped DLL) that cover this address 89 | */ 90 | std::map> addrToFunc; 91 | 92 | /** 93 | Addresses of the functions not found in the mapped DLL 94 | */ 95 | std::set notFound; 96 | 97 | /** 98 | Name of the covering DLL 99 | */ 100 | std::string dllName; 101 | 102 | protected: 103 | /** 104 | A name of the DLL that was used for mapping. In a typical scenario it will be the same as covering DLL, but may be set different. 105 | */ 106 | std::string mappedDllName; 107 | 108 | /** 109 | A supplied set of the addresses of imported functions. 110 | Those addressed will be covered (associated with the corresponding exports from available DLLs, defined by exportsMap). 111 | */ 112 | std::set &addresses; 113 | 114 | /** 115 | A supplied exportsMap. Only used as a lookup, no changes applied. 116 | */ 117 | const peconv::ExportsMapper& exportsMap; 118 | }; 119 | } 120 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/function_resolver.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Definitions of basic Imports Resolver classes. They can be used for filling imports when the PE is loaded. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace peconv { 11 | /** 12 | A base class for functions resolver. 13 | */ 14 | class t_function_resolver { 15 | public: 16 | /** 17 | Get the address (VA) of the function with the given name, from the given DLL. 18 | \param func_name : the name of the function 19 | \param lib_name : the name of the DLL 20 | \return Virtual Address of the exported function 21 | */ 22 | virtual FARPROC resolve_func(LPCSTR lib_name, LPCSTR func_name) = 0; 23 | }; 24 | 25 | /** 26 | A default functions resolver, using LoadLibraryA and GetProcAddress. 27 | */ 28 | class default_func_resolver : t_function_resolver { 29 | public: 30 | /** 31 | Get the address (VA) of the function with the given name, from the given DLL, using LoadLibraryA and GetProcAddress. 32 | \param func_name : the name of the function 33 | \param lib_name : the name of the DLL 34 | \return Virtual Address of the exported function 35 | */ 36 | virtual FARPROC resolve_func(LPCSTR lib_name, LPCSTR func_name); 37 | }; 38 | 39 | }; //namespace peconv 40 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/hooks.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions related to hooking the loaded PE. Reditecting/replacing a functions with another. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "function_resolver.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include "peconv/buffer_util.h" 15 | 16 | namespace peconv { 17 | 18 | /** 19 | A buffer storing a binary patch, that can be applied on a module. Used as a restorable backup in case of function patching. 20 | */ 21 | class PatchBackup { 22 | public: 23 | /** 24 | Creates an empty backup. 25 | */ 26 | PatchBackup() 27 | : buffer(nullptr), bufferSize(0), sourcePtr(nullptr) 28 | { 29 | } 30 | 31 | ~PatchBackup() { 32 | deleteBackup(); 33 | } 34 | 35 | /** 36 | Destroys the backup and resets internal fields. 37 | */ 38 | void deleteBackup() 39 | { 40 | if (buffer) { 41 | delete[] buffer; 42 | bufferSize = 0; 43 | sourcePtr = nullptr; 44 | } 45 | } 46 | 47 | /** 48 | Reads bytes from the binary to the backup. The source buffer must be within the current process. 49 | */ 50 | bool makeBackup(BYTE *patch_ptr, size_t patch_size); 51 | 52 | /** 53 | Applies the backup back to the pointer from which it was read. 54 | */ 55 | bool applyBackup(); 56 | 57 | /** 58 | Checks if the buffer was filled. 59 | */ 60 | bool isBackup() 61 | { 62 | return buffer != nullptr; 63 | } 64 | 65 | protected: 66 | BYTE *buffer; 67 | size_t bufferSize; 68 | 69 | BYTE *sourcePtr; 70 | }; 71 | 72 | 73 | /** 74 | A functions resolver that can be used for hooking IAT. Allows for defining functions that are supposed to be replaced. 75 | */ 76 | class hooking_func_resolver : peconv::default_func_resolver { 77 | public: 78 | /** 79 | Define a function that will be replaced. 80 | \param name : a name of the function that will be replaced 81 | \param function : an address of the replacement function 82 | */ 83 | void add_hook(const std::string &name, FARPROC function) 84 | { 85 | hooks_map[name] = function; 86 | } 87 | 88 | /** 89 | Define a DLL that will be replaced. 90 | \param dll_name : a name of the DLL to be replaced 91 | \param new_dll : a name of the new DLL that will be loaded instead 92 | */ 93 | void replace_dll(std::string dll_name, const std::string &new_dll) 94 | { 95 | dll_replacements_map[dll_name] = new_dll; 96 | } 97 | 98 | /** 99 | Get the address (VA) of the function with the given name, from the given DLL. If the function was hooked, it retrieves the address of the replacement function instead. 100 | \param func_name : the name of the function 101 | \param lib_name : the name of the DLL 102 | \return Virtual Address of the exported function, or the address of the replacement function. 103 | */ 104 | virtual FARPROC resolve_func(LPCSTR lib_name, LPCSTR func_name); 105 | 106 | private: 107 | std::map hooks_map; 108 | std::map dll_replacements_map; 109 | }; 110 | 111 | /** 112 | Installs inline hook at the given ptr. Returns the number of bytes overwriten. 113 | 64 bit version. 114 | \param ptr : pointer to the function to be replaced 115 | \param new_offset : VA of the new function 116 | \param backup : (optional) backup that can be used to reverse the changes 117 | \return size of the applied patch 118 | */ 119 | size_t redirect_to_local64(void *ptr, ULONGLONG new_offset, PatchBackup* backup = nullptr); 120 | 121 | /** 122 | Installs inline hook at the given ptr. Returns the number of bytes overwriten. 123 | 32 bit version. 124 | \param ptr : pointer to the function to be replaced 125 | \param new_offset : VA of the new function 126 | \param backup : (optional) backup that can be used to reverse the changes 127 | \return size of the applied patch 128 | */ 129 | size_t redirect_to_local32(void *ptr, DWORD new_offset, PatchBackup* backup = nullptr); 130 | 131 | /** 132 | Installs inline hook at the given ptr. Returns the number of bytes overwriten. 133 | Uses bitness of the current applications for the bitness of the intalled hook. 134 | \param ptr : pointer to the function to be replaced 135 | \param new_function_ptr : pointer to the new function 136 | \param backup : (optional) backup that can be used to reverse the changes 137 | \return size of the applied patch 138 | */ 139 | size_t redirect_to_local(void *ptr, void* new_function_ptr, PatchBackup* backup = nullptr); 140 | 141 | /** 142 | Replaces a target address of JMP [DWORD] or CALL [DWORD] 143 | */ 144 | bool replace_target(BYTE *ptr, ULONGLONG dest_addr); 145 | 146 | };//namespace peconv 147 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/imports_loader.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Parsing and filling the Import Table. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | #include "pe_hdrs_helper.h" 12 | #include "function_resolver.h" 13 | #include "exports_mapper.h" 14 | 15 | namespace peconv { 16 | 17 | /** 18 | A class defining a callback that will be executed when the next imported function was found 19 | */ 20 | class ImportThunksCallback 21 | { 22 | public: 23 | ImportThunksCallback(BYTE* _modulePtr, size_t _moduleSize) 24 | : modulePtr(_modulePtr), moduleSize(_moduleSize) 25 | { 26 | this->is64b = is64bit((BYTE*)modulePtr); 27 | } 28 | 29 | /** 30 | A callback that will be executed by process_import_table when the next imported function was found 31 | \param libName : the pointer to the DLL name 32 | \param origFirstThunkPtr : the pointer to the Original First Thunk 33 | \param firstThunkPtr : the pointer to the First Thunk 34 | \return : true if processing succeeded, false otherwise 35 | */ 36 | virtual bool processThunks(LPSTR libName, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr) = 0; 37 | 38 | protected: 39 | BYTE* modulePtr; 40 | size_t moduleSize; 41 | bool is64b; 42 | }; 43 | 44 | 45 | struct ImportsCollection 46 | { 47 | public: 48 | ImportsCollection() {}; 49 | ~ImportsCollection() 50 | { 51 | std::map::iterator itr; 52 | for (itr = thunkToFunc.begin(); itr != thunkToFunc.end(); ++itr) { 53 | peconv::ExportedFunc* exp = itr->second; 54 | if (!exp) continue; 55 | delete exp; 56 | } 57 | thunkToFunc.clear(); 58 | } 59 | 60 | size_t size() 61 | { 62 | return thunkToFunc.size(); 63 | } 64 | 65 | std::map thunkToFunc; 66 | }; 67 | 68 | /** 69 | Process the given PE's import table and execute the callback each time when the new imported function was found 70 | \param modulePtr : a pointer to the loded PE (in virtual format) 71 | \param moduleSize : a size of the supplied PE 72 | \param callback : a callback that will be executed to process each imported function 73 | \return : true if processing succeeded, false otherwise 74 | */ 75 | bool process_import_table(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ImportThunksCallback *callback); 76 | 77 | /** 78 | Fills imports of the given PE with the help of the defined functions resolver. 79 | \param modulePtr : a pointer to the loded PE (in virtual format) 80 | \param func_resolver : a resolver that will be used to fill the thunk of the import 81 | \return : true if loading all functions succeeded, false otherwise 82 | */ 83 | bool load_imports(BYTE* modulePtr, t_function_resolver* func_resolver=nullptr); 84 | 85 | /** 86 | Checks if the given PE has a valid import table. 87 | */ 88 | bool has_valid_import_table(const PBYTE modulePtr, size_t moduleSize); 89 | 90 | /** 91 | Checks if the given lib_name is a valid DLL name. 92 | A valid name must contain printable characters. Empty name is also acceptable (may have been erased). 93 | */ 94 | bool is_valid_import_name(const PBYTE modulePtr, const size_t moduleSize, LPSTR lib_name); 95 | 96 | /** 97 | * Collects all the Import Thunks RVAs (via which Imports are called) 98 | */ 99 | bool collect_thunks(IN BYTE* modulePtr, IN SIZE_T moduleSize, OUT std::set& thunk_rvas); 100 | 101 | bool collect_imports(IN BYTE* modulePtr, IN SIZE_T moduleSize, OUT ImportsCollection &collection); 102 | 103 | }; // namespace peconv 104 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/imports_uneraser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief A definition of ImportsUneraser class - for recovery of a partialy erased Import Table. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include "fix_imports.h" 17 | #include "caves.h" 18 | 19 | namespace peconv { 20 | 21 | /** 22 | A class responsible for recovering the partially erased Import Table from the PE. 23 | */ 24 | class ImportsUneraser 25 | { 26 | public: 27 | ImportsUneraser(PVOID _modulePtr, size_t _moduleSize) 28 | : modulePtr((PBYTE)_modulePtr), moduleSize(_moduleSize) 29 | { 30 | is64 = peconv::is64bit((BYTE*)modulePtr); 31 | } 32 | 33 | /** 34 | Fill the imported functions' names in the given Import Descriptor, using the given coverage. 35 | Collect addressees of functions that couldn't be filled with the given mapping. 36 | \param lib_desc : the IMAGE_IMPORT_DESCRIPTOR where the functions' names should be set 37 | \param dllCoverage : a mapping associating addresses with the corresponding exports from available DLLs 38 | \param not_covered : a set of addresses that could not be found in the supplied mapping 39 | \return true if succeeded 40 | */ 41 | bool uneraseDllImports(IN OUT IMAGE_IMPORT_DESCRIPTOR* lib_desc, IN ImportedDllCoverage &dllCoverage, OUT OPTIONAL ImpsNotCovered* not_covered); 42 | 43 | /** 44 | Recover the imported DLL name in the given Import Descriptor, filling it with the given dll_name. 45 | */ 46 | bool uneraseDllName(IMAGE_IMPORT_DESCRIPTOR* lib_desc, const std::string &dll_name); 47 | 48 | protected: 49 | /** 50 | Copy the given DLL name into the given IMAGE_IMPORT_DESCRIPTOR. Validates the data correctness before writing. 51 | \param lib_desc : the IMAGE_IMPORT_DESCRIPTOR where the DLL name should be set 52 | \param dll_name : the DLL name that needs to be written into the lib_desc 53 | \return true if succeeded 54 | */ 55 | bool writeFoundDllName(IMAGE_IMPORT_DESCRIPTOR* lib_desc, const std::string &dll_name); 56 | 57 | /** 58 | Fill the names of imported functions with names of the prepared mapping. 59 | Collect addressees of functions that couldn't be filled with the given mapping. 60 | \param lib_desc : the IMAGE_IMPORT_DESCRIPTOR where the functions' names should be set 61 | \param ordinal_flag : the flag that is used to recognize import by ordinal (32 or 64 bit) 62 | \param addr_to_func : a mapping assigning functions' addresses to their definitions (names etc.) 63 | \param not_covered : a set of addresses that could not be found in the supplied mapping 64 | \return true if succeeded 65 | */ 66 | template 67 | bool fillImportNames(IN OUT IMAGE_IMPORT_DESCRIPTOR* lib_desc, 68 | IN const FIELD_T ordinal_flag, 69 | IN std::map> &addr_to_func, 70 | OUT OPTIONAL ImpsNotCovered* not_covered 71 | ); 72 | 73 | template 74 | bool findNameInBinaryAndFill(IMAGE_IMPORT_DESCRIPTOR* lib_desc, 75 | LPVOID call_via_ptr, 76 | LPVOID thunk_ptr, 77 | const FIELD_T ordinal_flag, 78 | std::map> &addr_to_func 79 | ); 80 | 81 | /** 82 | Fill the function data into the given IMAGE_THUNK_DATA. 83 | \param desc : the poiner to IMAGE_THUNK_DATA that will be filled 84 | \param ordinal_flag : an ordinal flag: 32 or 64 bit 85 | \param foundFunc : the ExportedFunc that will be used for filling the desc 86 | */ 87 | template 88 | bool writeFoundFunction(IMAGE_THUNK_DATA_T* desc, const FIELD_T ordinal_flag, const ExportedFunc &foundFunc); 89 | 90 | PBYTE modulePtr; 91 | size_t moduleSize; 92 | bool is64; 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/load_config_util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Fetching Load Config Directory and recognizing its version. 4 | */ 5 | 6 | #pragma once 7 | #include 8 | 9 | #include "buffer_util.h" 10 | #include "load_config_defs.h" 11 | 12 | namespace peconv { 13 | 14 | /** 15 | A version of Load Config Directory. 16 | */ 17 | typedef enum { 18 | LOAD_CONFIG_NONE = 0, /**< Load Config Directory not found */ 19 | LOAD_CONFIG_W7_VER = 7, /**< Load Config Directory in the Windows 7 version */ 20 | LOAD_CONFIG_W8_VER = 8, /**< Load Config Directory in the Windows 8 version */ 21 | LOAD_CONFIG_W10_VER = 10, /**< Load Config Directory in the Windows 10 version */ 22 | LOAD_CONFIG_UNK_VER = -1 /**< Load Config Directory in an unknown version */ 23 | } t_load_config_ver; 24 | 25 | /** 26 | Get a pointer to the Load Config Directory within the given PE. 27 | \param buffer : a buffer containing the PE file in a Virtual format 28 | \param buf_size : size of the buffer 29 | \return a pointer to the Load Config Directory, NULL if the given PE does not have this directory 30 | */ 31 | BYTE* get_load_config_ptr(BYTE* buffer, size_t buf_size); 32 | 33 | /** 34 | Detect which version of Load Config Directory was used in the given PE. 35 | \param buffer : a buffer containing the PE file in a Virtual format 36 | \param buf_size : size of the buffer 37 | \param ld_config_ptr : pointer to the Load Config Directory within the given PE 38 | \return detected version of Load Config Directory 39 | */ 40 | t_load_config_ver get_load_config_version(BYTE* buffer, size_t buf_size, BYTE* ld_config_ptr); 41 | 42 | }; // namespace peconv 43 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/pe_dumper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Dumping PE from the memory buffer into a file. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "exports_mapper.h" 10 | 11 | namespace peconv { 12 | 13 | /** 14 | A mode in which the PE fille be dumped. 15 | */ 16 | typedef enum { 17 | PE_DUMP_AUTO = 0, /**< autodetect which dump mode is the most suitable for the given input */ 18 | PE_DUMP_VIRTUAL,/**< dump as it is in the memory (virtual) */ 19 | PE_DUMP_UNMAP, /**< convert to the raw format: using raw sections' headers */ 20 | PE_DUMP_REALIGN, /**< convert to the raw format: by realigning raw sections' headers to be the same as virtual (useful if the PE was unpacked in memory) */ 21 | PE_DUMP_MODES_COUNT /**< total number of the dump modes */ 22 | } t_pe_dump_mode; 23 | 24 | /** 25 | Detect dump mode that is the most suitable for the given input. 26 | \param buffer : the buffer containing the PE to be dumped. 27 | \param buffer_size : the size of the given buffer 28 | */ 29 | t_pe_dump_mode detect_dump_mode(IN const BYTE* buffer, IN size_t buffer_size); 30 | 31 | /** 32 | Dumps PE from the fiven buffer into a file. It expects the module base and size to be given. 33 | \param outputFilePath : name of the file where the dump should be saved 34 | \param buffer : the buffer containing the PE to be dumped. WARNING: the buffer may be preprocessed before dumping. 35 | \param buffer_size : the size of the given buffer 36 | \param module_base : the base to which the PE buffer was relocated 37 | \param dump_mode : specifies in which format the PE should be dumped. If the mode was set to PE_DUMP_AUTO, it autodetects mode and returns the detected one. 38 | \param exportsMap : optional. If exportsMap is supplied, it will try to recover destroyed import table of the PE, basing on the supplied map of exported functions. 39 | */ 40 | bool dump_pe( 41 | IN LPCTSTR outputFilePath, 42 | IN OUT BYTE* buffer, 43 | IN size_t buffer_size, 44 | IN const ULONGLONG module_base, 45 | IN OUT t_pe_dump_mode &dump_mode, 46 | IN OPTIONAL const peconv::ExportsMapper* exportsMap = nullptr 47 | ); 48 | 49 | };// namespace peconv 50 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/pe_loader.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Loading PE from a file with the help of the custom loader. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "pe_raw_to_virtual.h" 9 | #include "function_resolver.h" 10 | 11 | namespace peconv { 12 | /** 13 | Reads PE from the given buffer into memory and maps it into virtual format. 14 | (Automatic raw to virtual conversion). 15 | If the executable flag is true, the PE file is loaded into executable memory. 16 | If the relocate flag is true, applies relocations. Does not load imports. 17 | Automatically allocates buffer of the needed size (the size is returned in outputSize). The buffer can be freed by the function free_pe_buffer. 18 | */ 19 | BYTE* load_pe_module(BYTE* payload_raw, size_t r_size, OUT size_t &v_size, bool executable, bool relocate, ULONG_PTR desired_base = 0); 20 | 21 | /** 22 | Reads PE from the given file into memory and maps it into vitual format. 23 | (Automatic raw to virtual conversion). 24 | If the executable flag is true, the PE file is loaded into executable memory. 25 | If the relocate flag is true, applies relocations. Does not load imports. 26 | Automatically allocates buffer of the needed size (the size is returned in outputSize). The buffer can be freed by the function free_pe_buffer. 27 | */ 28 | BYTE* load_pe_module(LPCTSTR filename, OUT size_t &v_size, bool executable, bool relocate, ULONG_PTR desired_base = 0); 29 | 30 | /** 31 | Loads full PE from the raw buffer in a way in which it can be directly executed: remaps to virual format, applies relocations, loads imports. 32 | Allows for supplying custom function resolver. 33 | */ 34 | BYTE* load_pe_executable(BYTE* payload_raw, size_t r_size, OUT size_t &v_size, t_function_resolver* import_resolver = nullptr, ULONG_PTR desired_base = 0); 35 | 36 | /** 37 | Loads full PE from file in a way in which it can be directly executed: remaps to virtual format, applies relocations, loads imports. 38 | Allows for supplying custom function resolver. 39 | */ 40 | BYTE* load_pe_executable(LPCTSTR filename, OUT size_t &v_size, t_function_resolver* import_resolver = nullptr); 41 | 42 | };// namespace peconv 43 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/pe_mode_detector.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Detecting in which mode is the PE in the supplied buffer (i.e. raw, virtual). Analyzes PE features typical for particular modes. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include "pe_hdrs_helper.h" 11 | 12 | namespace peconv { 13 | 14 | /** 15 | check if the PE in the memory is in raw format 16 | */ 17 | bool is_pe_raw( 18 | IN const BYTE* pe_buffer, 19 | IN size_t pe_size 20 | ); 21 | 22 | /** 23 | check if Virtual section addresses are identical to Raw addresses (i.e. if the PE was realigned) 24 | */ 25 | bool is_pe_raw_eq_virtual( 26 | IN const BYTE* pe_buffer, 27 | IN size_t pe_size 28 | ); 29 | 30 | /** 31 | checks if the PE has sections that were unpacked/expanded in the memory 32 | */ 33 | bool is_pe_expanded( 34 | IN const BYTE* pe_buffer, 35 | IN size_t pe_size 36 | ); 37 | 38 | /** 39 | checks if the given section was unpacked in the memory 40 | */ 41 | bool is_section_expanded(IN const BYTE* pe_buffer, 42 | IN size_t pe_size, 43 | IN const PIMAGE_SECTION_HEADER sec 44 | ); 45 | 46 | };// namespace peconv 47 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/pe_raw_to_virtual.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Converting PE from raw to virtual format. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | #include "buffer_util.h" 12 | 13 | namespace peconv { 14 | 15 | /** 16 | Converts a raw PE supplied in a buffer to a virtual format. 17 | If the executable flag is true (default), the PE file is loaded into executable memory. 18 | Does not apply relocations. Does not load imports. 19 | Automatically allocates buffer of the needed size (the size is returned in outputSize). The buffer can be freed by the function free_pe_module. 20 | If the desired_base is defined (0 by default), it enforces allocation at the particular base. 21 | */ 22 | BYTE* pe_raw_to_virtual( 23 | IN const BYTE* rawPeBuffer, 24 | IN size_t rawPeSize, 25 | OUT size_t &outputSize, 26 | IN OPTIONAL bool executable = true, 27 | IN OPTIONAL ULONG_PTR desired_base = 0 28 | ); 29 | 30 | }; // namespace peconv 31 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/pe_virtual_to_raw.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Converting PE from virtual to raw format. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include "buffer_util.h" 11 | 12 | namespace peconv { 13 | 14 | /** 15 | Maps virtual image of PE to into raw. Automaticaly applies relocations. 16 | Automatically allocates buffer of the needed size (the size is returned in outputSize). 17 | \param payload : the PE in the Virtual format that needs to be converted into the Raw format 18 | \param in_size : size of the input buffer (the PE in the Virtual format) 19 | \param loadBase : the base to which the given PE was relocated 20 | \param outputSize : the size of the output buffer (the PE in the Raw format) 21 | \param rebuffer : if set (default), the input buffer is rebuffered and the original buffer is not modified. 22 | \return a buffer of the outputSize, containing the Raw PE. The buffer can be freed by the function free_pe_module. 23 | */ 24 | BYTE* pe_virtual_to_raw( 25 | IN BYTE* payload, 26 | IN size_t in_size, 27 | IN ULONGLONG loadBase, 28 | OUT size_t &outputSize, 29 | IN OPTIONAL bool rebuffer=true 30 | ); 31 | 32 | /* 33 | Modifies raw alignment of the PE to be the same as virtual alignment. 34 | \param payload : the PE in the Virtual format that needs to be realigned 35 | \param in_size : size of the input buffer 36 | \param loadBase : the base to which the given PE was relocated 37 | \param outputSize : the size of the output buffer (the PE in the Raw format) 38 | \return a buffer of the outputSize, containing the realigned PE. The buffer can be freed by the function free_pe_module. 39 | */ 40 | BYTE* pe_realign_raw_to_virtual( 41 | IN const BYTE* payload, 42 | IN size_t in_size, 43 | IN ULONGLONG loadBase, 44 | OUT size_t &outputSize 45 | ); 46 | 47 | };//namespace peconv 48 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/peb_lookup.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions for retrieving process information from PEB. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace peconv { 11 | 12 | /** 13 | Gets handle to the given module via PEB. A low-level equivalent of `GetModuleHandleW`. 14 | \param module_name : (optional) the name of the DLL loaded within the current process. If not set, the main module of the current process is used. 15 | \return the handle of the DLL with given name, or, if the name was not given, the handle of the main module of the current process. 16 | */ 17 | HMODULE get_module_via_peb(IN OPTIONAL LPCWSTR module_name = nullptr); 18 | 19 | 20 | /** 21 | Gets size of the given module via PEB. 22 | \param hModule : (optional) the base of the module which's size we want to retrieve. If not set, the main module of the current process is used. 23 | \return the size of the given module. 24 | */ 25 | size_t get_module_size_via_peb(IN OPTIONAL HMODULE hModule = nullptr); 26 | 27 | /** 28 | Sets the given module as the main module in the current PEB. 29 | \param hModule : the module to be connected to the current PEB. 30 | \return true if succeeded, false if failed 31 | */ 32 | bool set_main_module_in_peb(HMODULE hModule); 33 | 34 | /** 35 | Gets the main module from the current PEB. 36 | \return the main module connected to the current PEB. 37 | */ 38 | HMODULE get_main_module_via_peb(); 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/relocate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Operating on PE file's relocations table. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace peconv { 11 | 12 | typedef struct _BASE_RELOCATION_ENTRY { 13 | WORD Offset : 12; 14 | WORD Type : 4; 15 | } BASE_RELOCATION_ENTRY; 16 | 17 | class RelocBlockCallback 18 | { 19 | public: 20 | RelocBlockCallback(bool _is64bit) 21 | : is64bit(_is64bit) 22 | { 23 | } 24 | 25 | virtual bool processRelocField(ULONG_PTR relocField) = 0; 26 | 27 | protected: 28 | bool is64bit; 29 | }; 30 | 31 | // Processs the relocation table and make your own callback on each relocation field 32 | bool process_relocation_table(IN PVOID modulePtr, IN SIZE_T moduleSize, IN RelocBlockCallback *callback); 33 | 34 | /** 35 | Applies relocations on the PE in virtual format. Relocates it from the old base given to the new base given. 36 | If 0 was supplied as the old base, it assumes that the old base is the ImageBase given in the header. 37 | \param modulePtr : a buffer containing the PE to be relocated 38 | \param moduleSize : the size of the given PE buffer 39 | \param newBase : a base to which the PE should be relocated 40 | \param oldBase : a base to which the PE is currently relocated (if not set, the imageBase from the header will be used) 41 | */ 42 | bool relocate_module(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ULONGLONG newBase, IN ULONGLONG oldBase = 0); 43 | 44 | /** 45 | Checks if the given PE has a valid relocations table. 46 | \param modulePtr : a buffer containing the PE to be checked 47 | \param moduleSize : the size of the given PE buffer 48 | */ 49 | bool has_valid_relocation_table(IN const PBYTE modulePtr, IN const size_t moduleSize); 50 | 51 | };//namespace peconv 52 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/resource_parser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Parsing PE's resource directory. 4 | */ 5 | 6 | #pragma once 7 | #include 8 | 9 | namespace peconv { 10 | /** 11 | A callback that will be executed by the function parse_resources when the Resource Entry was found. 12 | */ 13 | typedef bool(*t_on_res_entry_found) ( 14 | BYTE* modulePtr, 15 | IMAGE_RESOURCE_DIRECTORY_ENTRY *root_dir, 16 | IMAGE_RESOURCE_DATA_ENTRY *curr_entry 17 | ); 18 | 19 | /** 20 | A function walking through the Resource Tree of the given PE. On each Resource Entry found, the callback is executed. 21 | \param modulePtr : pointer to the buffer with the PE in a Virtual format 22 | \param on_entry : a callback function executed on each Resource Entry 23 | */ 24 | bool parse_resources(BYTE* modulePtr, t_on_res_entry_found on_entry); 25 | }; 26 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/resource_util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions related to manual retrieving of PE resources. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "buffer_util.h" 10 | 11 | namespace peconv { 12 | 13 | const LPSTR RT_RCDATA_A = MAKEINTRESOURCEA(10); 14 | 15 | /** 16 | Maps a resource with the given id + type and copies its raw content into the output buffer. 17 | If out_size is not zero, it reads maximum out_size of bytes. If out_size is zero, it reads the full resource. 18 | The actual read size is returned back in out_size. 19 | Automatically allocates a buffer of the required size. 20 | If hInstance is NULL, it search the resource in the current module. Otherwise, it search in the given module. 21 | */ 22 | peconv::ALIGNED_BUF load_resource_data(OUT size_t &out_size, const int res_id, const LPSTR res_type = RT_RCDATA_A, HMODULE hInstance = nullptr); 23 | 24 | /** 25 | Free the buffer with PE Resources, mapped by the function load_resource_data. 26 | */ 27 | void free_resource_data(peconv::ALIGNED_BUF buffer); 28 | 29 | /** 30 | a helper function to get the module handle of the current DLL 31 | */ 32 | HMODULE get_current_module_handle(); 33 | 34 | }; //namespace peconv 35 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/tls_parser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Functions related to TLS Callbacks 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace peconv { 12 | 13 | /** 14 | A helper function, normalizing virtual addresses. It automatically detects if the given virtual address is VA or RVA, and converts it into RVA 15 | \param imgBase : the base address to which the module was relocated 16 | \param imgSize : the size of the image 17 | \param virtualAddr : the virtual address (RVA or VA) that we want to convert (within the module described by imgBase and imgSize) 18 | \param outRVA : the output of the conversion (RVA) 19 | \return true if the conversion was successful, false otherwise 20 | */ 21 | bool virtual_addr_to_rva(IN const ULONGLONG imgBase, IN const DWORD imgSize, IN ULONGLONG virtualAddr, OUT DWORD &outRVA); 22 | 23 | /** 24 | A function listing RVAs of all TLS callbacks that are present in the given module. 25 | \param modulePtr : pointer to the buffer with the PE in a Virtual format, relocated to the load base 26 | \param moduleSize : size of the given module (if 0 given, the imageSize from the PE headers will be used) 27 | \param tls_callbacks : a vector of TLS callbacks addresses (as given in the TLS table) 28 | \return number of TLS callbacks added to the list 29 | */ 30 | size_t list_tls_callbacks(IN PVOID modulePtr, IN size_t moduleSize, OUT std::vector &tls_callbacks); 31 | 32 | /** 33 | A function running all the TLS callbacks that are present in the given module, one by one. 34 | \param modulePtr : pointer to the buffer with the PE in a Virtual format, relocated to the load base 35 | \param moduleSize : size of the given module (if 0 given, the imageSize from the PE headers will be used) 36 | \param dwReason : a parameter (dwReason) that will be passed to the callback function 37 | \return number of TLS callbacks executed 38 | */ 39 | size_t run_tls_callbacks(IN PVOID modulePtr, IN size_t moduleSize=0, IN DWORD dwReason = DLL_PROCESS_ATTACH); 40 | 41 | }; //namespace peconv 42 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/unicode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef UNICODE 4 | #define tcout wcout 5 | #define tcerr wcerr 6 | #define tstring wstring 7 | #else 8 | #define tcout cout 9 | #define tcerr cerr 10 | #define tstring string 11 | #endif // UNICODE 12 | -------------------------------------------------------------------------------- /libpeconv/include/peconv/util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Miscellaneous utility functions. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "file_util.h" 9 | #include "resource_util.h" 10 | 11 | #ifdef _MSC_VER 12 | #define PECONV_FORCEINLINE __forceinline 13 | #define PECONV_TRY_EXCEPT_BLOCK_START __try { 14 | #define PECONV_TRY_EXCEPT_BLOCK_END __except (EXCEPTION_EXECUTE_HANDLER) { 15 | #else 16 | #define PECONV_FORCEINLINE __attribute__((always_inline)) inline 17 | #define PECONV_TRY_EXCEPT_BLOCK_START try { 18 | #define PECONV_TRY_EXCEPT_BLOCK_END catch (...) { 19 | #endif 20 | 21 | 22 | namespace peconv { 23 | /** 24 | Checks if the given buffer is fully filled with the specified character. 25 | \param cave_ptr : pointer to the buffer to be checked 26 | \param cave_size : size of the buffer to be checked 27 | \param padding_char : the required character 28 | */ 29 | bool is_padding(const BYTE* cave_ptr, size_t cave_size, const BYTE padding_char); 30 | 31 | /** 32 | Wrapper for GetProcessId - for a backward compatibility with old versions of Windows 33 | */ 34 | DWORD get_process_id(HANDLE hProcess); 35 | 36 | /** 37 | Verifies if the calling process has a defined access to the specified continuous range of memory, defined by areaStart and areaSize. 38 | If the area includes pages that are not commited, or pages with access rights PAGE_GUARD | PAGE_NOACCESS, it is treated as inaccessible. 39 | \param areaStart : A pointer to the first byte of the memory block 40 | \param areaSize : The size of the memory block, in bytes. If this parameter is zero, the return value is false. 41 | \param accessRights : The access rights to be checked 42 | */ 43 | bool is_mem_accessible(LPCVOID areaStart, SIZE_T areaSize, DWORD accessRights); 44 | 45 | /** 46 | Verifies that the calling process has read access to the specified range of memory. 47 | \param areaStart : A pointer to the first byte of the memory block 48 | \param areaSize : The size of the memory block, in bytes. If this parameter is zero, the return value is true (bad pointer). 49 | */ 50 | bool is_bad_read_ptr(LPCVOID areaStart, SIZE_T areaSize); 51 | }; -------------------------------------------------------------------------------- /libpeconv/src/buffer_util.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/buffer_util.h" 2 | 3 | #include 4 | 5 | // 6 | // validate pointer: 7 | // 8 | 9 | bool peconv::validate_ptr(IN const void* buffer_bgn, IN size_t buffer_size, IN const void* field_bgn, IN size_t field_size) 10 | { 11 | if (buffer_bgn == nullptr || field_bgn == nullptr) { 12 | return false; 13 | } 14 | BYTE* _start = (BYTE*)buffer_bgn; 15 | BYTE* _field_start = (BYTE*)field_bgn; 16 | if (_field_start < _start) { 17 | return false; 18 | } 19 | size_t start_delta = (ULONG_PTR)_field_start - (ULONG_PTR)_start; 20 | size_t area_size = start_delta + field_size; 21 | if (area_size > buffer_size) { 22 | return false; 23 | } 24 | if (area_size < field_size || area_size < start_delta) { 25 | #ifdef _DEBUG 26 | std::cout << "Integer Overflow, limit exceeded! start_delta: " << start_delta << " field_size: " << field_size << " area_size: " << area_size << "\n"; 27 | #endif 28 | return false; 29 | } 30 | return true; 31 | } 32 | 33 | //----------------------------------------------------------------------------------- 34 | // 35 | // alloc/free unaligned buffers: 36 | // 37 | 38 | //allocates a buffer that does not have to start from the beginning of the section 39 | peconv::UNALIGNED_BUF peconv::alloc_unaligned(size_t buf_size) 40 | { 41 | if (!buf_size) return NULL; 42 | 43 | UNALIGNED_BUF buf = (UNALIGNED_BUF) calloc(buf_size, sizeof(BYTE)); 44 | return buf; 45 | } 46 | 47 | void peconv::free_unaligned(peconv::UNALIGNED_BUF section_buffer) 48 | { 49 | free(section_buffer); 50 | } 51 | 52 | // 53 | // alloc/free aligned buffers: 54 | // 55 | 56 | peconv::ALIGNED_BUF peconv::alloc_aligned(size_t buffer_size, DWORD protect, void* desired_base) 57 | { 58 | if (!buffer_size) return NULL; 59 | 60 | ALIGNED_BUF buf = (ALIGNED_BUF) VirtualAlloc(desired_base, buffer_size, MEM_COMMIT | MEM_RESERVE, protect); 61 | return buf; 62 | } 63 | 64 | bool peconv::free_aligned(peconv::ALIGNED_BUF buffer, size_t buffer_size) 65 | { 66 | if (buffer == nullptr) return true; 67 | if (!VirtualFree(buffer, 0, MEM_RELEASE)) { 68 | #ifdef _DEBUG 69 | std::cerr << "Releasing failed" << std::endl; 70 | #endif 71 | return false; 72 | } 73 | return true; 74 | } 75 | 76 | //----------------------------------------------------------------------------------- 77 | // 78 | // wrappers using appropriate buffer type according to the purpose: 79 | // 80 | 81 | // allocate a buffer for PE module: 82 | peconv::ALIGNED_BUF peconv::alloc_pe_buffer(size_t buffer_size, DWORD protect, void* desired_base) 83 | { 84 | return alloc_aligned(buffer_size, protect, desired_base); 85 | } 86 | 87 | // Free loaded PE module 88 | bool peconv::free_pe_buffer(peconv::ALIGNED_BUF buffer, size_t buffer_size) 89 | { 90 | return peconv::free_aligned(buffer, buffer_size); 91 | } 92 | 93 | -------------------------------------------------------------------------------- /libpeconv/src/caves.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/caves.h" 2 | #include "peconv/pe_hdrs_helper.h" 3 | #include "peconv/util.h" 4 | 5 | using namespace peconv; 6 | 7 | #ifdef _DEBUG 8 | #include 9 | #endif 10 | 11 | PBYTE peconv::find_ending_cave(BYTE*modulePtr, size_t moduleSize, const DWORD minimal_size, const DWORD req_charact) 12 | { 13 | size_t sec_count = peconv::get_sections_count(modulePtr, moduleSize); 14 | if (sec_count == 0) return nullptr; 15 | 16 | size_t last_sec = sec_count - 1; 17 | PIMAGE_SECTION_HEADER section_hdr = peconv::get_section_hdr(modulePtr, moduleSize, last_sec); 18 | if (section_hdr == nullptr) return nullptr; 19 | if (!(section_hdr->Characteristics & req_charact)) return nullptr; 20 | 21 | DWORD raw_size = section_hdr->SizeOfRawData; 22 | DWORD virtual_size = (DWORD)moduleSize - section_hdr->VirtualAddress; 23 | 24 | if (raw_size >= virtual_size) { 25 | #ifdef _DEBUG 26 | std::cout << "Last section's raw_size: " << std::hex << raw_size << " >= virtual_size: " << virtual_size << std::endl; 27 | #endif 28 | return nullptr; 29 | } 30 | DWORD cave_size = virtual_size - raw_size; 31 | if (cave_size < minimal_size) { 32 | #ifdef _DEBUG 33 | std::cout << "Cave is too small" << std::endl; 34 | #endif 35 | return nullptr; 36 | } 37 | PBYTE cave_ptr = modulePtr + section_hdr->VirtualAddress + section_hdr->SizeOfRawData; 38 | if (!validate_ptr(modulePtr, moduleSize, cave_ptr, minimal_size)) { 39 | #ifdef _DEBUG 40 | std::cout << "Invalid cave pointer" << std::endl; 41 | #endif 42 | return nullptr; 43 | } 44 | section_hdr->SizeOfRawData += minimal_size; //book this cave 45 | return cave_ptr; 46 | } 47 | 48 | PBYTE peconv::find_alignment_cave(BYTE* modulePtr, size_t moduleSize, const DWORD minimal_size, const DWORD req_charact) 49 | { 50 | DWORD alignment = peconv::get_sec_alignment(modulePtr, true); 51 | if (alignment == 0) return nullptr; 52 | 53 | size_t sec_count = peconv::get_sections_count(modulePtr, moduleSize); 54 | for (size_t i = 0; i < sec_count; i++) { 55 | PIMAGE_SECTION_HEADER section_hdr = peconv::get_section_hdr(modulePtr, moduleSize, i); 56 | if (section_hdr == nullptr) continue; 57 | if (!(section_hdr->Characteristics & req_charact)) continue; 58 | 59 | DWORD rem = section_hdr->SizeOfRawData % alignment; 60 | if (rem == 0) continue; 61 | 62 | DWORD div = (section_hdr->SizeOfRawData / alignment) + 1; 63 | DWORD new_size = div * alignment; 64 | DWORD cave_size = new_size - section_hdr->SizeOfRawData; 65 | if (cave_size < minimal_size) { 66 | #ifdef __DEBUG 67 | std::cout << "Cave is too small" << std::endl; 68 | #endif 69 | continue; 70 | } 71 | DWORD sec_start = section_hdr->PointerToRawData; 72 | if (sec_start == 0) continue; 73 | 74 | DWORD sec_end = sec_start + section_hdr->SizeOfRawData; 75 | #ifdef _DEBUG 76 | std::cout << "section: " << std::hex << sec_start << " : " << sec_end << std::endl; 77 | #endif 78 | PBYTE cave_ptr = modulePtr + sec_end; 79 | if (!validate_ptr(modulePtr, moduleSize, cave_ptr, minimal_size)) { 80 | #ifdef _DEBUG 81 | std::cout << "Invalid cave pointer" << std::endl; 82 | #endif 83 | continue; 84 | } 85 | section_hdr->SizeOfRawData += minimal_size; //book this cave 86 | return cave_ptr; 87 | } 88 | #ifdef _DEBUG 89 | std::cout << "Cave not found" << std::endl; 90 | #endif 91 | return nullptr; 92 | } 93 | 94 | PBYTE peconv::find_padding_cave(BYTE* modulePtr, size_t moduleSize, const size_t minimal_size, const DWORD req_charact) 95 | { 96 | size_t sec_count = peconv::get_sections_count(modulePtr, moduleSize); 97 | for (size_t i = 0; i < sec_count; i++) { 98 | PIMAGE_SECTION_HEADER section_hdr = peconv::get_section_hdr(modulePtr, moduleSize, i); 99 | if (section_hdr == nullptr) continue; 100 | if (!(section_hdr->Characteristics & req_charact)) continue; 101 | 102 | if (section_hdr->SizeOfRawData < minimal_size) continue; 103 | 104 | // we will be searching in the loaded, virtual image: 105 | DWORD sec_start = section_hdr->VirtualAddress; 106 | if (sec_start == 0) continue; 107 | 108 | DWORD sec_end = sec_start + section_hdr->SizeOfRawData; 109 | #ifdef _DEBUG 110 | std::cout << "section: " << std::hex << sec_start << " : " << sec_end << std::endl; 111 | #endif 112 | //offset from the end of the section: 113 | size_t cave_offset = section_hdr->SizeOfRawData - minimal_size; 114 | PBYTE cave_ptr = modulePtr + sec_start + cave_offset; 115 | if (!validate_ptr(modulePtr, moduleSize, cave_ptr, minimal_size)) { 116 | #ifdef _DEBUG 117 | std::cout << "Invalid cave pointer" << std::endl; 118 | #endif 119 | continue; 120 | } 121 | bool found = false; 122 | if (is_padding(cave_ptr, minimal_size, 0)) { 123 | found = true; 124 | } 125 | //if the section is code, check also code padding: 126 | if (section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) { 127 | if (is_padding(cave_ptr, minimal_size, 0xCC)) { 128 | found = true; 129 | } 130 | } 131 | if (found) { 132 | return cave_ptr; 133 | } 134 | } 135 | #ifdef _DEBUG 136 | std::cout << "Cave not found" << std::endl; 137 | #endif 138 | return nullptr; 139 | } 140 | -------------------------------------------------------------------------------- /libpeconv/src/delayed_imports_loader.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/delayed_imports_loader.h" 2 | #include "peconv/imports_loader.h" 3 | 4 | #include 5 | 6 | IMAGE_DELAYLOAD_DESCRIPTOR* peconv::get_delayed_imps(IN const BYTE* modulePtr, IN const size_t moduleSize, OUT size_t &dir_size) 7 | { 8 | dir_size = 0; 9 | IMAGE_DATA_DIRECTORY *d_imps_dir = peconv::get_directory_entry(modulePtr, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT); 10 | if (!d_imps_dir) { 11 | return nullptr; 12 | } 13 | BYTE* dimps_table = (BYTE*)((ULONGLONG) modulePtr + d_imps_dir->VirtualAddress); 14 | const size_t min_size = sizeof(IMAGE_DELAYLOAD_DESCRIPTOR); 15 | if (d_imps_dir->Size < min_size) { 16 | return nullptr; 17 | } 18 | if (!peconv::validate_ptr((LPVOID)modulePtr, moduleSize, dimps_table, min_size)) { 19 | return nullptr; 20 | } 21 | dir_size = d_imps_dir->Size; 22 | return reinterpret_cast (dimps_table); 23 | } 24 | 25 | template 26 | bool parse_delayed_desc(BYTE* modulePtr, const size_t moduleSize, 27 | const ULONGLONG img_base, 28 | LPSTR lib_name, 29 | const T_FIELD ordinal_flag, 30 | IMAGE_DELAYLOAD_DESCRIPTOR *desc, 31 | peconv::t_function_resolver* func_resolver 32 | ) 33 | { 34 | ULONGLONG iat_addr = desc->ImportAddressTableRVA; 35 | 36 | if (iat_addr > img_base) iat_addr -= img_base; // it may be either RVA or VA 37 | 38 | ULONGLONG thunk_addr = desc->ImportNameTableRVA; 39 | if (thunk_addr > img_base) thunk_addr -= img_base; // it may be either RVA or VA 40 | 41 | T_FIELD* record_va = (T_FIELD*)((ULONGLONG)modulePtr + iat_addr); 42 | T_IMAGE_THUNK_DATA* thunk_va = (T_IMAGE_THUNK_DATA*)((ULONGLONG)modulePtr + thunk_addr); 43 | 44 | for (; (*record_va) && thunk_va; record_va++, thunk_va++) { 45 | if (!peconv::validate_ptr(modulePtr, moduleSize, record_va, sizeof(T_FIELD))) { 46 | return false; 47 | } 48 | if (!peconv::validate_ptr(modulePtr, moduleSize, thunk_va, sizeof(T_FIELD))) { 49 | return false; 50 | } 51 | 52 | T_FIELD iat_va = *record_va; 53 | ULONGLONG iat_rva = (ULONGLONG)iat_va; 54 | if (iat_va > img_base) iat_rva -= img_base; // it may be either RVA or VA 55 | #ifdef _DEBUG 56 | std::cout << std::hex << iat_rva << " : "; 57 | #endif 58 | T_FIELD* iat_record_ptr = (T_FIELD*)((ULONGLONG)modulePtr + iat_rva); 59 | if (!peconv::validate_ptr(modulePtr, moduleSize, iat_record_ptr, sizeof(T_FIELD))) { 60 | return false; 61 | } 62 | FARPROC hProc = nullptr; 63 | if (thunk_va->u1.Ordinal & ordinal_flag) { 64 | T_FIELD raw_ordinal = thunk_va->u1.Ordinal & (~ordinal_flag); 65 | #ifdef _DEBUG 66 | std::cout << std::hex << "ord: " << raw_ordinal << " "; 67 | #endif 68 | hProc = func_resolver->resolve_func(lib_name, MAKEINTRESOURCEA(raw_ordinal)); 69 | } 70 | else { 71 | ULONGLONG name_rva = thunk_va->u1.AddressOfData; 72 | if (name_rva > img_base) { 73 | name_rva -= img_base; 74 | } 75 | PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + name_rva); 76 | LPSTR func_name = reinterpret_cast(by_name->Name); 77 | if (!peconv::is_valid_import_name(modulePtr, moduleSize, func_name)) { 78 | continue; 79 | } 80 | #ifdef _DEBUG 81 | std::cout << func_name << " "; 82 | #endif 83 | hProc = func_resolver->resolve_func(lib_name, func_name); 84 | } 85 | if (hProc) { 86 | //rather than loading it via proxy function, we just overwrite the thunk like normal IAT: 87 | *record_va = (T_FIELD) hProc; 88 | #ifdef _DEBUG 89 | std::cout << "[OK]\n"; 90 | #endif 91 | } 92 | else { 93 | #ifdef _DEBUG 94 | std::cout << "[NOPE]\n"; 95 | #endif 96 | } 97 | } 98 | return true; 99 | } 100 | 101 | bool peconv::load_delayed_imports(BYTE* modulePtr, ULONGLONG moduleBase, t_function_resolver* func_resolver) 102 | { 103 | if (!peconv::get_directory_entry(modulePtr, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT)) { 104 | return true; // nothing to resolve 105 | } 106 | const bool is_64bit = peconv::is64bit(modulePtr); 107 | bool is_loader64 = false; 108 | #ifdef _WIN64 109 | is_loader64 = true; 110 | #endif 111 | if (is_64bit != is_loader64) { 112 | std::cerr << "[ERROR] Loader/Payload bitness mismatch.\n"; 113 | return false; 114 | } 115 | 116 | const size_t module_size = peconv::get_image_size(modulePtr); 117 | default_func_resolver default_res; 118 | if (!func_resolver) { 119 | func_resolver = (t_function_resolver*)&default_res; 120 | } 121 | size_t table_size = 0; 122 | IMAGE_DELAYLOAD_DESCRIPTOR *first_desc = get_delayed_imps(modulePtr, module_size, table_size); 123 | if (!first_desc) { 124 | return false; 125 | } 126 | #ifdef _DEBUG 127 | std::cout << "OK, table_size = " << table_size << std::endl; 128 | #endif 129 | bool is_ok = true; 130 | size_t max_count = table_size / sizeof(IMAGE_DELAYLOAD_DESCRIPTOR); 131 | for (size_t i = 0; i < max_count; i++) { 132 | IMAGE_DELAYLOAD_DESCRIPTOR *desc = &first_desc[i]; 133 | if (!validate_ptr(modulePtr, module_size, desc, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) break; 134 | if (!desc->DllNameRVA) { 135 | break; 136 | } 137 | ULONGLONG dll_name_rva = desc->DllNameRVA; 138 | if (dll_name_rva > moduleBase) { 139 | dll_name_rva -= moduleBase; 140 | } 141 | char* dll_name = (char*)((ULONGLONG) modulePtr + dll_name_rva); 142 | if (!validate_ptr(modulePtr, module_size, dll_name, sizeof(char))) continue; 143 | #ifdef _DEBUG 144 | std::cout << dll_name << std::endl; 145 | #endif 146 | if (is_64bit) { 147 | #ifdef _WIN64 148 | is_ok = parse_delayed_desc(modulePtr, module_size, moduleBase, dll_name, IMAGE_ORDINAL_FLAG64, desc, func_resolver); 149 | #else 150 | return false; 151 | #endif 152 | } 153 | else { 154 | #ifndef _WIN64 155 | is_ok = parse_delayed_desc(modulePtr, module_size, moduleBase, dll_name, IMAGE_ORDINAL_FLAG32, desc, func_resolver); 156 | #else 157 | return false; 158 | #endif 159 | } 160 | } 161 | return is_ok; 162 | } 163 | -------------------------------------------------------------------------------- /libpeconv/src/file_util.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/file_util.h" 2 | #include "peconv/buffer_util.h" 3 | #include "peconv/util.h" 4 | 5 | #include 6 | #ifdef _DEBUG 7 | #include 8 | #endif 9 | 10 | //load file content using MapViewOfFile 11 | peconv::ALIGNED_BUF peconv::load_file(IN LPCTSTR filename, OUT size_t &read_size) 12 | { 13 | HANDLE file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 14 | if(file == INVALID_HANDLE_VALUE) { 15 | #ifdef _DEBUG 16 | std::cerr << "Could not open file!" << std::endl; 17 | #endif 18 | return nullptr; 19 | } 20 | HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0); 21 | if (!mapping) { 22 | #ifdef _DEBUG 23 | std::cerr << "Could not create mapping!" << std::endl; 24 | #endif 25 | CloseHandle(file); 26 | return nullptr; 27 | } 28 | BYTE *dllRawData = (BYTE*) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); 29 | if (!dllRawData) { 30 | #ifdef _DEBUG 31 | std::cerr << "Could not map view of file" << std::endl; 32 | #endif 33 | CloseHandle(mapping); 34 | CloseHandle(file); 35 | return nullptr; 36 | } 37 | size_t r_size = GetFileSize(file, 0); 38 | if (read_size != 0 && read_size <= r_size) { 39 | r_size = read_size; 40 | } 41 | if (peconv::is_bad_read_ptr(dllRawData, r_size)) { 42 | std::cerr << "[-] Mapping of " << filename << " is invalid!" << std::endl; 43 | UnmapViewOfFile(dllRawData); 44 | CloseHandle(mapping); 45 | CloseHandle(file); 46 | return nullptr; 47 | } 48 | peconv::UNALIGNED_BUF localCopyAddress = peconv::alloc_unaligned(r_size); 49 | if (localCopyAddress != nullptr) { 50 | memcpy(localCopyAddress, dllRawData, r_size); 51 | read_size = r_size; 52 | } else { 53 | read_size = 0; 54 | #ifdef _DEBUG 55 | std::cerr << "Could not allocate memory in the current process" << std::endl; 56 | #endif 57 | } 58 | UnmapViewOfFile(dllRawData); 59 | CloseHandle(mapping); 60 | CloseHandle(file); 61 | return localCopyAddress; 62 | } 63 | 64 | //load file content using ReadFile 65 | peconv::ALIGNED_BUF peconv::read_from_file(IN LPCTSTR in_path, IN OUT size_t &read_size) 66 | { 67 | HANDLE file = CreateFile(in_path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 68 | if (file == INVALID_HANDLE_VALUE) { 69 | #ifdef _DEBUG 70 | std::cerr << "Cannot open the file for reading!" << std::endl; 71 | #endif 72 | return nullptr; 73 | } 74 | DWORD r_size = GetFileSize(file, 0); 75 | if (read_size != 0 && read_size <= r_size) { 76 | r_size = MASK_TO_DWORD(read_size); 77 | } 78 | peconv::UNALIGNED_BUF buffer = peconv::alloc_unaligned(r_size); 79 | if (buffer == nullptr) { 80 | #ifdef _DEBUG 81 | std::cerr << "Allocation has failed!" << std::endl; 82 | #endif 83 | return nullptr; 84 | } 85 | DWORD out_size = 0; 86 | if (!ReadFile(file, buffer, r_size, &out_size, nullptr)) { 87 | #ifdef _DEBUG 88 | std::cerr << "Reading failed!" << std::endl; 89 | #endif 90 | peconv::free_file(buffer); 91 | buffer = nullptr; 92 | read_size = 0; 93 | } else { 94 | read_size = r_size; 95 | } 96 | CloseHandle(file); 97 | return buffer; 98 | } 99 | 100 | //save the given buffer into a file 101 | bool peconv::dump_to_file(IN LPCTSTR out_path, IN PBYTE dump_data, IN size_t dump_size) 102 | { 103 | if (!out_path || !dump_data || !dump_size) return false; 104 | 105 | HANDLE file = CreateFile(out_path, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 106 | if (file == INVALID_HANDLE_VALUE) { 107 | #ifdef _DEBUG 108 | std::cerr << "Cannot open the file for writing!" << std::endl; 109 | #endif 110 | return false; 111 | } 112 | DWORD written_size = 0; 113 | bool is_dumped = false; 114 | if (WriteFile(file, dump_data, (DWORD) dump_size, &written_size, nullptr)) { 115 | is_dumped = true; 116 | } 117 | #ifdef _DEBUG 118 | else { 119 | std::cerr << "Failed to write to the file : " << out_path << std::endl; 120 | } 121 | #endif 122 | CloseHandle(file); 123 | return is_dumped; 124 | } 125 | 126 | //free the buffer allocated by load_file/read_from_file 127 | void peconv::free_file(IN peconv::UNALIGNED_BUF buffer) 128 | { 129 | peconv::free_unaligned(buffer); 130 | } 131 | 132 | std::string peconv::get_file_name(IN const std::string str) 133 | { 134 | size_t found = str.find_last_of("/\\"); 135 | if (found == std::string::npos) { 136 | return str; 137 | } 138 | return str.substr(found + 1); 139 | } 140 | 141 | std::string peconv::get_directory_name(IN const std::string str) 142 | { 143 | size_t found = str.find_last_of("/\\"); 144 | if (found == std::string::npos) { 145 | return ""; 146 | } 147 | return str.substr(0, found); 148 | } 149 | 150 | size_t peconv::find_extension_pos(IN const std::string str) 151 | { 152 | size_t len = str.length(); 153 | size_t ext_pos = len; 154 | for (size_t k = len; k != 0; k--) { 155 | size_t i = k - 1; 156 | char c = str[i]; 157 | if (c == '.') { 158 | ext_pos = i; 159 | break; 160 | } 161 | } 162 | return ext_pos; 163 | } 164 | -------------------------------------------------------------------------------- /libpeconv/src/find_base.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace peconv { 9 | 10 | class CollectCodeRelocs : public RelocBlockCallback 11 | { 12 | public: 13 | CollectCodeRelocs(BYTE *pe_buffer, size_t buffer_size, IN bool _is64bit, OUT std::set &_relocs) 14 | : RelocBlockCallback(_is64bit), relocs(_relocs), 15 | peBuffer(pe_buffer), bufferSize(buffer_size) 16 | { 17 | codeSec = getCodeSection(peBuffer, bufferSize); 18 | } 19 | 20 | virtual bool processRelocField(ULONG_PTR relocField) 21 | { 22 | if (!codeSec) return false; 23 | 24 | ULONGLONG reloc_addr = (relocField - (ULONGLONG)peBuffer); 25 | const bool is_in_code = (reloc_addr >= codeSec->VirtualAddress) && (reloc_addr < codeSec->Misc.VirtualSize); 26 | if (!is64bit && !is_in_code) { 27 | // in case of 32 bit PEs process only the relocations form the code section 28 | return true; 29 | } 30 | ULONGLONG rva = 0; 31 | if (is64bit) { 32 | ULONGLONG* relocateAddr = (ULONGLONG*)((ULONG_PTR)relocField); 33 | rva = (*relocateAddr); 34 | //std::cout << std::hex << (relocField - (ULONGLONG)peBuffer) << " : " << rva << std::endl; 35 | } 36 | else { 37 | DWORD* relocateAddr = (DWORD*)((ULONG_PTR)relocField); 38 | rva = ULONGLONG(*relocateAddr); 39 | //std::cout << std::hex << (relocField - (ULONGLONG)peBuffer) << " : " << rva << std::endl; 40 | } 41 | relocs.insert(rva); 42 | return true; 43 | } 44 | 45 | static PIMAGE_SECTION_HEADER getCodeSection(BYTE *peBuffer, size_t bufferSize) 46 | { 47 | size_t sec_count = peconv::get_sections_count(peBuffer, bufferSize); 48 | for (size_t i = 0; i < sec_count; i++) { 49 | PIMAGE_SECTION_HEADER hdr = peconv::get_section_hdr(peBuffer, bufferSize, i); 50 | if (!hdr) break; 51 | if (hdr->VirtualAddress == 0 || hdr->SizeOfRawData == 0) { 52 | continue; 53 | } 54 | if (hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) { 55 | return hdr; 56 | } 57 | } 58 | return nullptr; 59 | } 60 | 61 | protected: 62 | std::set &relocs; 63 | PIMAGE_SECTION_HEADER codeSec; 64 | 65 | BYTE *peBuffer; 66 | size_t bufferSize; 67 | }; 68 | } 69 | 70 | ULONGLONG peconv::find_base_candidate(IN BYTE* modulePtr, IN size_t moduleSize) 71 | { 72 | if (moduleSize == 0) { 73 | moduleSize = peconv::get_image_size((const BYTE*)modulePtr); 74 | } 75 | if (moduleSize == 0) return 0; 76 | 77 | bool is64 = peconv::is64bit(modulePtr); 78 | std::set relocs; 79 | peconv::CollectCodeRelocs callback(modulePtr, moduleSize, is64, relocs); 80 | if (!peconv::process_relocation_table(modulePtr, moduleSize, &callback)) { 81 | return 0; 82 | } 83 | if (relocs.size() == 0) { 84 | return 0; 85 | } 86 | 87 | PIMAGE_SECTION_HEADER hdr = peconv::CollectCodeRelocs::getCodeSection(modulePtr, moduleSize); 88 | if (!hdr) { 89 | return 0; 90 | } 91 | const ULONGLONG mask = ~ULONGLONG(0xFFFF); 92 | std::mapbase_candidates; 93 | 94 | std::set::iterator itr = relocs.begin(); 95 | 96 | for (itr = relocs.begin(); itr != relocs.end(); ++itr) { 97 | const ULONGLONG guessed_base = (*itr) & mask; 98 | std::map::iterator found = base_candidates.find(guessed_base); 99 | if (found == base_candidates.end()) { 100 | base_candidates[guessed_base] = 0; 101 | } 102 | base_candidates[guessed_base]++; 103 | } 104 | ULONGLONG most_freqent = 0; 105 | size_t max_freq = 0; 106 | std::map::iterator mapItr; 107 | for (mapItr = base_candidates.begin(); mapItr != base_candidates.end(); ++mapItr) { 108 | if (mapItr->second >= max_freq) { 109 | most_freqent = mapItr->first; 110 | max_freq = mapItr->second; 111 | } 112 | } 113 | for (itr = relocs.begin(); itr != relocs.end(); ++itr) { 114 | ULONGLONG first = *itr; 115 | ULONGLONG first_base = first & mask; 116 | if (first_base > most_freqent) { 117 | break; 118 | } 119 | ULONGLONG delta = most_freqent - first_base; 120 | if (delta < moduleSize) { 121 | return first_base; 122 | } 123 | } 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /libpeconv/src/fix_dot_net_ep.cpp: -------------------------------------------------------------------------------- 1 | #include "fix_dot_net_ep.h" 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | class ListImportNames : public peconv::ImportThunksCallback 8 | { 9 | public: 10 | ListImportNames(BYTE* _modulePtr, size_t _moduleSize, std::map &name_to_addr) 11 | : ImportThunksCallback(_modulePtr, _moduleSize), nameToAddr(name_to_addr) 12 | { 13 | } 14 | 15 | virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr) 16 | { 17 | if (this->is64b) { 18 | IMAGE_THUNK_DATA64* desc = reinterpret_cast(origFirstThunkPtr); 19 | ULONGLONG* call_via = reinterpret_cast(firstThunkPtr); 20 | return processThunks_tpl(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG64); 21 | } 22 | IMAGE_THUNK_DATA32* desc = reinterpret_cast(origFirstThunkPtr); 23 | DWORD* call_via = reinterpret_cast(firstThunkPtr); 24 | return processThunks_tpl(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG32); 25 | } 26 | 27 | protected: 28 | template 29 | bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA* desc, T_FIELD* call_via, T_FIELD ordinal_flag) 30 | { 31 | DWORD call_via_rva = static_cast((ULONG_PTR)call_via - (ULONG_PTR)this->modulePtr); 32 | #ifdef _DEBUG 33 | std::cout << "via RVA: " << std::hex << call_via_rva << " : "; 34 | #endif 35 | bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0; 36 | if (!is_by_ord) { 37 | PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData); 38 | LPSTR func_name = reinterpret_cast(by_name->Name); 39 | #ifdef _DEBUG 40 | std::cout << "name: " << func_name << std::endl; 41 | #endif 42 | nameToAddr[func_name] = call_via_rva; 43 | } 44 | return true; 45 | } 46 | 47 | std::map &nameToAddr; 48 | }; 49 | 50 | DWORD find_corexemain(BYTE *buf, size_t buf_size) 51 | { 52 | std::map name_to_addr; 53 | ListImportNames callback(buf, buf_size, name_to_addr); 54 | if (!peconv::process_import_table(buf, buf_size, &callback)) return 0; 55 | 56 | std::map::iterator found = name_to_addr.find("_CorExeMain"); 57 | if (found != name_to_addr.end()) return found->second; 58 | 59 | found = name_to_addr.find("_CorDllMain"); 60 | if (found != name_to_addr.end()) return found->second; 61 | 62 | return 0; 63 | } 64 | 65 | BYTE* search_jump(BYTE *buf, size_t buf_size, const DWORD cor_exe_main_thunk, const ULONGLONG img_base) 66 | { 67 | // search the jump pattern, i.e.: 68 | //JMP DWORD NEAR [0X402000] : FF 25 00204000 69 | const size_t jmp_size = 2; 70 | const BYTE jmp_pattern[jmp_size] = { 0xFF, 0x25 }; 71 | 72 | const size_t arg_size = sizeof(DWORD); 73 | if ((jmp_size + arg_size) > buf_size) { 74 | return nullptr; 75 | } 76 | const size_t end_offset = buf_size - (jmp_size + arg_size); 77 | 78 | for (size_t i = end_offset; // search backwards 79 | (i + 1) != 0; // this is unsigned comparison, so we cannot do: i >= 0 80 | i--) // go back by one BYTE 81 | { 82 | if (buf[i] == jmp_pattern[0] && buf[i + 1] == jmp_pattern[1]) { // JMP 83 | DWORD* addr = (DWORD*)(&buf[i + jmp_size]); 84 | DWORD rva = static_cast((*addr) - img_base); 85 | if (rva == cor_exe_main_thunk) { 86 | #ifdef _DEBUG 87 | std::cout << "Found call to _CorExeMain\n"; 88 | #endif 89 | return buf + i; 90 | } 91 | else { 92 | std::cerr << "[!] Mismatch: " << std::hex << rva << " vs _CorExeMain: " << cor_exe_main_thunk << std::endl; 93 | } 94 | } 95 | } 96 | return nullptr; 97 | } 98 | 99 | bool fix_dot_net_ep(BYTE *pe_buffer, size_t pe_buffer_size) 100 | { 101 | if (!pe_buffer) return false; 102 | 103 | if (peconv::is64bit(pe_buffer)) { 104 | //64bit .NET files have EP=0 105 | peconv::update_entry_point_rva(pe_buffer, 0); 106 | return true; 107 | } 108 | 109 | DWORD ep_rva = peconv::get_entry_point_rva(pe_buffer); 110 | #ifdef _DEBUG 111 | std::cout << "[*] This is a .NET payload and may require Enty Point correction. Current EP: " << std::hex << ep_rva << "\n"; 112 | #endif 113 | PIMAGE_SECTION_HEADER sec_hdr = peconv::get_section_hdr(pe_buffer, pe_buffer_size, 0); 114 | if (!sec_hdr) { 115 | return false; 116 | } 117 | BYTE* sec_ptr = (BYTE*)((ULONG_PTR)pe_buffer + sec_hdr->VirtualAddress); 118 | if (!peconv::validate_ptr(pe_buffer, pe_buffer_size, sec_ptr, sec_hdr->SizeOfRawData)) { 119 | return false; 120 | } 121 | ULONGLONG img_base = peconv::get_image_base(pe_buffer); 122 | DWORD cor_exe_main_thunk = find_corexemain(pe_buffer, pe_buffer_size); 123 | if (!cor_exe_main_thunk) { 124 | return false; 125 | } 126 | BYTE* jump_ptr = search_jump(sec_ptr, sec_hdr->SizeOfRawData, cor_exe_main_thunk, img_base); 127 | if (!jump_ptr) { 128 | return false; 129 | } 130 | size_t offset = (ULONG_PTR)jump_ptr - (ULONG_PTR)pe_buffer; 131 | peconv::update_entry_point_rva(pe_buffer, static_cast(offset)); 132 | #ifdef _DEBUG 133 | std::cout << "[*] Found possible Entry Point: " << std::hex << offset << std::endl; 134 | #endif 135 | return true; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /libpeconv/src/fix_dot_net_ep.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | bool fix_dot_net_ep(BYTE *pe_buffer, size_t pe_buffer_size); 6 | 7 | BYTE* search_jump(BYTE *buf, size_t buf_size, const DWORD cor_exe_main_thunk, const ULONGLONG img_base); 8 | -------------------------------------------------------------------------------- /libpeconv/src/function_resolver.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/function_resolver.h" 2 | 3 | #include 4 | 5 | FARPROC peconv::default_func_resolver::resolve_func(LPCSTR lib_name, LPCSTR func_name) 6 | { 7 | HMODULE libBasePtr = LoadLibraryA(lib_name); 8 | if (libBasePtr == NULL) { 9 | std::cerr << "Could not load the library: " << lib_name << std::endl; 10 | return NULL; 11 | } 12 | FARPROC hProc = GetProcAddress(libBasePtr, func_name); 13 | if (hProc == NULL) { 14 | ULONGLONG func_val = (ULONGLONG)func_name; 15 | //is only the first WORD filled? 16 | bool is_ord = (func_val & (0x0FFFF)) == func_val; 17 | std::cerr << "Could not load the function: " << lib_name << "."; 18 | if (is_ord) { 19 | std::cerr << std::hex << "0x" << func_val; 20 | } 21 | else { 22 | std::cerr << func_name; 23 | } 24 | std::cerr << std::endl; 25 | return NULL; 26 | } 27 | return hProc; 28 | } 29 | -------------------------------------------------------------------------------- /libpeconv/src/load_config_util.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/load_config_util.h" 2 | #include "peconv/pe_hdrs_helper.h" 3 | 4 | BYTE* peconv::get_load_config_ptr(BYTE* buffer, size_t buf_size) 5 | { 6 | if (!buffer || !buf_size) return nullptr; 7 | IMAGE_DATA_DIRECTORY* dir = peconv::get_directory_entry(buffer, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG); 8 | if (!dir) { 9 | return 0; 10 | } 11 | DWORD entry_rva = dir->VirtualAddress; 12 | DWORD entry_size = dir->Size; 13 | if (!peconv::validate_ptr(buffer, buf_size, buffer + entry_rva, entry_size)) { 14 | return 0; 15 | } 16 | IMAGE_LOAD_CONFIG_DIRECTORY32* ldc = reinterpret_cast((ULONG_PTR)buffer + entry_rva); 17 | return reinterpret_cast(ldc); 18 | } 19 | 20 | peconv::t_load_config_ver peconv::get_load_config_version(BYTE* buffer, size_t buf_size, BYTE* ld_config_ptr) 21 | { 22 | if (!buffer || !buf_size || !ld_config_ptr) { 23 | return peconv::LOAD_CONFIG_NONE; 24 | } 25 | bool is64b = peconv::is64bit(buffer); 26 | 27 | if (!peconv::validate_ptr(buffer, buf_size, ld_config_ptr, sizeof(peconv::IMAGE_LOAD_CONFIG_DIR32_W7))) { 28 | return peconv::LOAD_CONFIG_NONE; 29 | } 30 | 31 | peconv::IMAGE_LOAD_CONFIG_DIR32_W7* smallest = (peconv::IMAGE_LOAD_CONFIG_DIR32_W7*)ld_config_ptr; 32 | const size_t curr_size = smallest->Size; 33 | 34 | if (is64b) { 35 | switch (curr_size) { 36 | case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR64_W7) : 37 | return peconv::LOAD_CONFIG_W7_VER; 38 | case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR64_W8) : 39 | return peconv::LOAD_CONFIG_W8_VER; 40 | case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR64_W10) : 41 | return peconv::LOAD_CONFIG_W10_VER; 42 | default: 43 | return LOAD_CONFIG_UNK_VER; 44 | } 45 | } 46 | else { 47 | switch (curr_size) { 48 | case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR32_W7) : 49 | return peconv::LOAD_CONFIG_W7_VER; 50 | case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR32_W8) : 51 | return peconv::LOAD_CONFIG_W8_VER; 52 | case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR32_W10) : 53 | return peconv::LOAD_CONFIG_W10_VER; 54 | default: 55 | return LOAD_CONFIG_UNK_VER; 56 | } 57 | } 58 | return LOAD_CONFIG_UNK_VER; 59 | } 60 | -------------------------------------------------------------------------------- /libpeconv/src/pe_dumper.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/pe_dumper.h" 2 | 3 | #include "peconv/pe_hdrs_helper.h" 4 | #include "peconv/pe_virtual_to_raw.h" 5 | #include "peconv/fix_imports.h" 6 | #include "peconv/file_util.h" 7 | #include "peconv/pe_mode_detector.h" 8 | #include "fix_dot_net_ep.h" 9 | #include 10 | 11 | using namespace peconv; 12 | 13 | t_pe_dump_mode peconv::detect_dump_mode(IN const BYTE* buffer, IN size_t mod_size) 14 | { 15 | const t_pe_dump_mode default_mode = peconv::PE_DUMP_UNMAP; 16 | if (peconv::is_pe_raw(buffer, mod_size)) { 17 | return peconv::PE_DUMP_VIRTUAL; 18 | } 19 | if (peconv::is_pe_expanded(buffer, mod_size)) { 20 | return peconv::PE_DUMP_REALIGN; 21 | } 22 | return default_mode; 23 | } 24 | 25 | bool peconv::dump_pe( 26 | IN LPCTSTR out_path, 27 | IN OUT BYTE *buffer, IN size_t mod_size, 28 | IN const ULONGLONG start_addr, 29 | IN OUT t_pe_dump_mode &dump_mode, 30 | IN OPTIONAL const peconv::ExportsMapper* exportsMap 31 | ) 32 | { 33 | // if the exportsMap is supplied, attempt to recover the (destroyed) import table: 34 | if (exportsMap != nullptr) { 35 | if (!peconv::fix_imports(buffer, mod_size, *exportsMap, NULL)) { 36 | std::cerr << "[-] Unable to fix imports!" << std::endl; 37 | } 38 | } 39 | if (dump_mode == PE_DUMP_AUTO || dump_mode >= PE_DUMP_MODES_COUNT) { 40 | dump_mode = detect_dump_mode(buffer, mod_size); 41 | } 42 | 43 | BYTE* dump_data = buffer; 44 | size_t dump_size = mod_size; 45 | size_t out_size = 0; 46 | BYTE* unmapped_module = nullptr; 47 | 48 | if (dump_mode == peconv::PE_DUMP_UNMAP || dump_mode == peconv::PE_DUMP_REALIGN) { 49 | //if the image base in headers is invalid, set the current base and prevent from relocating PE: 50 | if (peconv::get_image_base(buffer) == 0) { 51 | peconv::update_image_base(buffer, (ULONGLONG)start_addr); 52 | } 53 | if (is_dot_net(buffer, mod_size)) { 54 | fix_dot_net_ep(buffer, mod_size); 55 | } 56 | const ULONGLONG hdr_base = peconv::get_image_base(buffer); 57 | ULONGLONG target_base = start_addr; 58 | if (dump_mode != peconv::PE_DUMP_VIRTUAL && peconv::has_relocations(buffer)) { 59 | // enforce relocation to the original base 60 | target_base = hdr_base; 61 | peconv::update_image_base(buffer, (ULONGLONG)start_addr); 62 | } 63 | if (dump_mode == peconv::PE_DUMP_UNMAP) { 64 | unmapped_module = pe_virtual_to_raw(buffer, mod_size, (ULONGLONG)target_base, out_size, false); 65 | } 66 | else if (dump_mode == peconv::PE_DUMP_REALIGN) { 67 | unmapped_module = peconv::pe_realign_raw_to_virtual(buffer, mod_size, (ULONGLONG)target_base, out_size); 68 | } 69 | // unmap the PE file (convert from the Virtual Format into Raw Format) 70 | if (unmapped_module) { 71 | dump_data = unmapped_module; 72 | dump_size = out_size; 73 | } 74 | } 75 | // save the read module into a file 76 | const bool is_dumped = dump_to_file(out_path, dump_data, dump_size); 77 | 78 | peconv::free_pe_buffer(unmapped_module, mod_size); 79 | return is_dumped; 80 | } 81 | -------------------------------------------------------------------------------- /libpeconv/src/pe_loader.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/pe_loader.h" 2 | 3 | #include "peconv/relocate.h" 4 | #include "peconv/imports_loader.h" 5 | #include "peconv/buffer_util.h" 6 | #include "peconv/function_resolver.h" 7 | #include "peconv/exports_lookup.h" 8 | 9 | #include 10 | #include 11 | 12 | using namespace peconv; 13 | 14 | namespace peconv { 15 | BYTE* load_no_sec_pe(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, bool executable) 16 | { 17 | ULONG_PTR desired_base = 0; 18 | size_t out_size = (r_size < PAGE_SIZE) ? PAGE_SIZE : r_size; 19 | if (executable) { 20 | desired_base = get_image_base(dllRawData); 21 | out_size = peconv::get_image_size(dllRawData); 22 | } 23 | DWORD protect = (executable) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; 24 | BYTE* mappedPE = peconv::alloc_pe_buffer(out_size, protect, reinterpret_cast(desired_base)); 25 | if (!mappedPE) { 26 | return nullptr; 27 | } 28 | memcpy(mappedPE, dllRawData, r_size); 29 | v_size = out_size; 30 | return mappedPE; 31 | } 32 | }; 33 | 34 | BYTE* peconv::load_pe_module(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, bool executable, bool relocate, ULONG_PTR desired_base) 35 | { 36 | if (!peconv::get_nt_hdrs(dllRawData, r_size)) { 37 | return nullptr; 38 | } 39 | if (peconv::get_sections_count(dllRawData, r_size) == 0) { 40 | return load_no_sec_pe(dllRawData, r_size, v_size, executable); 41 | } 42 | // by default, allow to load the PE at the supplied base 43 | // if relocating is required, but the PE has no relocation table... 44 | if (relocate && !has_relocations(dllRawData)) { 45 | // ...enforce loading the PE image at its default base (so that it will need no relocations) 46 | desired_base = get_image_base(dllRawData); 47 | } 48 | // load a virtual image of the PE file at the desired_base address (random if desired_base is NULL): 49 | BYTE *mappedDLL = pe_raw_to_virtual(dllRawData, r_size, v_size, executable, desired_base); 50 | if (mappedDLL) { 51 | //if the image was loaded at its default base, relocate_module will return always true (because relocating is already done) 52 | if (relocate && !relocate_module(mappedDLL, v_size, (ULONGLONG)mappedDLL)) { 53 | // relocating was required, but it failed - thus, the full PE image is useless 54 | std::cerr << "[!] Could not relocate the module!\n"; 55 | free_pe_buffer(mappedDLL, v_size); 56 | mappedDLL = nullptr; 57 | } 58 | } else { 59 | std::cerr << "[!] Could not allocate memory at the desired base!\n"; 60 | } 61 | return mappedDLL; 62 | } 63 | 64 | BYTE* peconv::load_pe_module(LPCTSTR filename, OUT size_t &v_size, bool executable, bool relocate, ULONG_PTR desired_base) 65 | { 66 | size_t r_size = 0; 67 | BYTE *dllRawData = load_file(filename, r_size); 68 | if (!dllRawData) { 69 | #ifdef _DEBUG 70 | std::cerr << "Cannot load the file: " << filename << std::endl; 71 | #endif 72 | return nullptr; 73 | } 74 | BYTE* mappedPE = load_pe_module(dllRawData, r_size, v_size, executable, relocate, desired_base); 75 | free_file(dllRawData); 76 | return mappedPE; 77 | } 78 | 79 | BYTE* peconv::load_pe_executable(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, t_function_resolver* import_resolver, ULONG_PTR desired_base) 80 | { 81 | BYTE* loaded_pe = load_pe_module(dllRawData, r_size, v_size, true, true, desired_base); 82 | if (!loaded_pe) { 83 | std::cerr << "[-] Loading failed!\n"; 84 | return nullptr; 85 | } 86 | #if _DEBUG 87 | printf("Loaded at: %p\n", loaded_pe); 88 | #endif 89 | if (has_valid_import_table(loaded_pe, v_size)) { 90 | if (!load_imports(loaded_pe, import_resolver)) { 91 | printf("[-] Loading imports failed!"); 92 | free_pe_buffer(loaded_pe, v_size); 93 | return NULL; 94 | } 95 | } 96 | else { 97 | printf("[-] PE doesn't have a valid Import Table!\n"); 98 | } 99 | return loaded_pe; 100 | } 101 | 102 | 103 | BYTE* peconv::load_pe_executable(LPCTSTR my_path, OUT size_t &v_size, t_function_resolver* import_resolver) 104 | { 105 | #if _DEBUG 106 | _tprintf(TEXT("Module: %s\n"), my_path); 107 | #endif 108 | BYTE* loaded_pe = load_pe_module(my_path, v_size, true, true); 109 | if (!loaded_pe) { 110 | printf("[-] Loading failed!\n"); 111 | return NULL; 112 | } 113 | #if _DEBUG 114 | printf("Loaded at: %p\n", loaded_pe); 115 | #endif 116 | if (!load_imports(loaded_pe, import_resolver)) { 117 | printf("[-] Loading imports failed!"); 118 | free_pe_buffer(loaded_pe, v_size); 119 | return nullptr; 120 | } 121 | return loaded_pe; 122 | } 123 | -------------------------------------------------------------------------------- /libpeconv/src/pe_raw_to_virtual.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/pe_raw_to_virtual.h" 2 | 3 | #include "peconv/util.h" 4 | #include "peconv/pe_hdrs_helper.h" 5 | 6 | #include 7 | 8 | using namespace peconv; 9 | 10 | // Map raw PE into virtual memory of local process: 11 | bool sections_raw_to_virtual(IN const BYTE* payload, IN SIZE_T payloadSize, OUT BYTE* destBuffer, IN SIZE_T destBufferSize) 12 | { 13 | if (!payload || !destBuffer) return false; 14 | 15 | BYTE* payload_nt_hdr = get_nt_hdrs(payload, payloadSize); 16 | if (!payload_nt_hdr) { 17 | std::cerr << "Invalid PE: " << std::hex << (ULONGLONG) payload << std::endl; 18 | return false; 19 | } 20 | 21 | const bool is64b = is64bit(payload); 22 | 23 | IMAGE_FILE_HEADER *fileHdr = nullptr; 24 | DWORD hdrsSize = 0; 25 | void* secptr = nullptr; 26 | if (is64b) { 27 | IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr; 28 | fileHdr = &(payload_nt_hdr64->FileHeader); 29 | hdrsSize = payload_nt_hdr64->OptionalHeader.SizeOfHeaders; 30 | secptr = (void*)((ULONG_PTR)&(payload_nt_hdr64->OptionalHeader) + fileHdr->SizeOfOptionalHeader); 31 | } 32 | else { 33 | IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr; 34 | fileHdr = &(payload_nt_hdr32->FileHeader); 35 | hdrsSize = payload_nt_hdr32->OptionalHeader.SizeOfHeaders; 36 | secptr = (void*)((ULONG_PTR)&(payload_nt_hdr32->OptionalHeader) + fileHdr->SizeOfOptionalHeader); 37 | } 38 | DWORD first_raw = 0; 39 | //copy all the sections, one by one: 40 | for (WORD i = 0; i < fileHdr->NumberOfSections; i++) { 41 | PIMAGE_SECTION_HEADER next_sec = (PIMAGE_SECTION_HEADER)((ULONG_PTR)secptr + ((ULONG_PTR)IMAGE_SIZEOF_SECTION_HEADER * i)); 42 | if (!validate_ptr(static_cast(payload), payloadSize, next_sec, IMAGE_SIZEOF_SECTION_HEADER) // check if fits in the source size 43 | || !validate_ptr(static_cast(payload), destBufferSize, next_sec, IMAGE_SIZEOF_SECTION_HEADER)) // check if fits in the destination size 44 | { 45 | return false; 46 | } 47 | if (next_sec->PointerToRawData == 0 || next_sec->SizeOfRawData == 0) { 48 | continue; //skipping empty 49 | } 50 | void* section_mapped = destBuffer + next_sec->VirtualAddress; 51 | void* section_raw_ptr = (BYTE*)payload + next_sec->PointerToRawData; 52 | size_t sec_size = next_sec->SizeOfRawData; 53 | 54 | if ((next_sec->VirtualAddress + sec_size) > destBufferSize) { 55 | std::cerr << "[!] Virtual section size is out ouf bounds: " << std::hex << sec_size << std::endl; 56 | sec_size = (destBufferSize > next_sec->VirtualAddress) ? SIZE_T(destBufferSize - next_sec->VirtualAddress) : 0; 57 | std::cerr << "[!] Truncated to maximal size: " << std::hex << sec_size << ", buffer size:" << destBufferSize << std::endl; 58 | } 59 | if (next_sec->VirtualAddress >= destBufferSize && sec_size != 0) { 60 | std::cerr << "[-] VirtualAddress of section is out ouf bounds: " << std::hex << next_sec->VirtualAddress << std::endl; 61 | return false; 62 | } 63 | if (next_sec->PointerToRawData + sec_size > destBufferSize) { 64 | std::cerr << "[-] Raw section size is out ouf bounds: " << std::hex << sec_size << std::endl; 65 | return false; 66 | } 67 | 68 | // validate source: 69 | if (!validate_ptr(static_cast(payload), payloadSize, section_raw_ptr, sec_size)) { 70 | if (next_sec->PointerToRawData > payloadSize) { 71 | std::cerr << "[-] Section " << i << ": out ouf bounds, skipping... " << std::endl; 72 | continue; 73 | } 74 | // trim section 75 | sec_size = payloadSize - (next_sec->PointerToRawData); 76 | } 77 | // validate destination: 78 | if (!peconv::validate_ptr(destBuffer, destBufferSize, section_mapped, sec_size)) { 79 | std::cerr << "[-] Section " << i << ": out ouf bounds, skipping... " << std::endl; 80 | continue; 81 | } 82 | memcpy(section_mapped, section_raw_ptr, sec_size); 83 | if (first_raw == 0 || (next_sec->PointerToRawData < first_raw)) { 84 | first_raw = next_sec->PointerToRawData; 85 | } 86 | } 87 | 88 | //copy payload's headers: 89 | if (hdrsSize == 0) { 90 | hdrsSize= first_raw; 91 | #ifdef _DEBUG 92 | std::cout << "hdrsSize not filled, using calculated size: " << std::hex << hdrsSize << "\n"; 93 | #endif 94 | } 95 | if (!validate_ptr((const LPVOID)payload, destBufferSize, (const LPVOID)payload, hdrsSize)) { 96 | return false; 97 | } 98 | memcpy(destBuffer, payload, hdrsSize); 99 | return true; 100 | } 101 | 102 | BYTE* peconv::pe_raw_to_virtual( 103 | IN const BYTE* payload, 104 | IN size_t in_size, 105 | OUT size_t &out_size, 106 | IN OPTIONAL bool executable, 107 | IN OPTIONAL ULONG_PTR desired_base 108 | ) 109 | { 110 | //check payload: 111 | BYTE* nt_hdr = get_nt_hdrs(payload); 112 | if (!nt_hdr) { 113 | std::cerr << "Invalid PE: " << std::hex << (ULONG_PTR) payload << std::endl; 114 | return nullptr; 115 | } 116 | DWORD payloadImageSize = 0; 117 | 118 | const bool is64 = is64bit(payload); 119 | if (is64) { 120 | IMAGE_NT_HEADERS64* payload_nt_hdr = (IMAGE_NT_HEADERS64*)nt_hdr; 121 | payloadImageSize = payload_nt_hdr->OptionalHeader.SizeOfImage; 122 | } 123 | else { 124 | IMAGE_NT_HEADERS32* payload_nt_hdr = (IMAGE_NT_HEADERS32*)nt_hdr; 125 | payloadImageSize = payload_nt_hdr->OptionalHeader.SizeOfImage; 126 | } 127 | payloadImageSize = peconv::round_up_to_unit(payloadImageSize, (DWORD)PAGE_SIZE); 128 | 129 | DWORD protect = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; 130 | //first we will prepare the payload image in the local memory, so that it will be easier to edit it, apply relocations etc. 131 | //when it will be ready, we will copy it into the space reserved in the target process 132 | BYTE* localCopyAddress = alloc_pe_buffer(payloadImageSize, protect, reinterpret_cast(desired_base)); 133 | if (!localCopyAddress) { 134 | std::cerr << "Could not allocate memory in the current process" << std::endl; 135 | return NULL; 136 | } 137 | //printf("Allocated local memory: %p size: %x\n", localCopyAddress, payloadImageSize); 138 | if (!sections_raw_to_virtual(payload, in_size, localCopyAddress, payloadImageSize)) { 139 | std::cerr << "Could not copy PE file" << std::endl; 140 | return nullptr; 141 | } 142 | out_size = payloadImageSize; 143 | return localCopyAddress; 144 | } 145 | -------------------------------------------------------------------------------- /libpeconv/src/peb_lookup.cpp: -------------------------------------------------------------------------------- 1 | #include "ntddk.h" 2 | #ifdef _DEBUG 3 | #include 4 | #endif 5 | #include 6 | 7 | class SectionLocker { 8 | public: 9 | SectionLocker(RTL_CRITICAL_SECTION &_section) 10 | : section(_section) 11 | { 12 | RtlEnterCriticalSection(§ion); 13 | } 14 | 15 | ~SectionLocker() 16 | { 17 | RtlLeaveCriticalSection(§ion); 18 | } 19 | 20 | protected: 21 | RTL_CRITICAL_SECTION §ion; 22 | }; 23 | 24 | //here we don't want to use any functions imported form extenal modules 25 | 26 | typedef struct _LDR_MODULE { 27 | LIST_ENTRY InLoadOrderModuleList;// +0x00 28 | LIST_ENTRY InMemoryOrderModuleList;// +0x08 29 | LIST_ENTRY InInitializationOrderModuleList;// +0x10 30 | void* BaseAddress; // +0x18 31 | void* EntryPoint; // +0x1c 32 | ULONG SizeOfImage; 33 | UNICODE_STRING FullDllName; 34 | UNICODE_STRING BaseDllName; 35 | ULONG Flags; 36 | SHORT LoadCount; 37 | SHORT TlsIndex; 38 | HANDLE SectionHandle; 39 | ULONG CheckSum; 40 | ULONG TimeDateStamp; 41 | } LDR_MODULE, *PLDR_MODULE; 42 | 43 | inline PPEB get_peb() 44 | { 45 | #if defined(_M_AMD64) 46 | return (PPEB)__readgsqword(0x60); 47 | #elif defined(_M_ARM64) 48 | PPEB peb = (PPEB)(*(__getReg(18) + 0x60)); 49 | #ifdef _DEBUG 50 | std::cout << "[+] ARM64 TEB: " << __getReg(18) << " PEB: " << peb << "\n"; 51 | #endif 52 | return peb; 53 | #else 54 | return (PPEB)__readfsdword(0x30); 55 | /* 56 | //alternative way to fetch it: 57 | LPVOID PEB = NULL; 58 | __asm { 59 | mov eax, fs:[30h] 60 | mov PEB, eax 61 | }; 62 | return (PPEB)PEB; 63 | 64 | or: 65 | LPVOID PEB = RtlGetCurrentPeb(); 66 | */ 67 | #endif 68 | } 69 | 70 | inline WCHAR to_lowercase(WCHAR c1) 71 | { 72 | if (c1 <= L'Z' && c1 >= L'A') { 73 | c1 = (c1 - L'A') + L'a'; 74 | } 75 | return c1; 76 | } 77 | 78 | bool is_wanted_module(LPCWSTR curr_name, LPCWSTR wanted_name) 79 | { 80 | if (wanted_name == NULL || curr_name == NULL) return false; 81 | 82 | LPCWSTR curr_end_ptr = curr_name; 83 | while (*curr_end_ptr != L'\0') { 84 | curr_end_ptr++; 85 | } 86 | if (curr_end_ptr == curr_name) return false; 87 | 88 | LPCWSTR wanted_end_ptr = wanted_name; 89 | while (*wanted_end_ptr != L'\0') { 90 | wanted_end_ptr++; 91 | } 92 | if (wanted_end_ptr == wanted_name) return false; 93 | 94 | while ((curr_end_ptr != curr_name) && (wanted_end_ptr != wanted_name)) { 95 | 96 | if (to_lowercase(*wanted_end_ptr) != to_lowercase(*curr_end_ptr)) { 97 | return false; 98 | } 99 | wanted_end_ptr--; 100 | curr_end_ptr--; 101 | } 102 | return true; 103 | } 104 | 105 | HMODULE peconv::get_module_via_peb(IN OPTIONAL LPCWSTR module_name) 106 | { 107 | PPEB peb = get_peb(); 108 | if (!peb) { 109 | return NULL; 110 | } 111 | SectionLocker locker(*peb->LoaderLock); 112 | LIST_ENTRY head = peb->Ldr->InLoadOrderModuleList; 113 | 114 | const PLDR_MODULE first_module = *((PLDR_MODULE *)(&head)); 115 | PLDR_MODULE curr_module = first_module; 116 | if (!module_name) { 117 | return (HMODULE)(curr_module->BaseAddress); 118 | } 119 | 120 | // it is a cyclic list, so if the next record links to the initial one, it means we went throught the full loop 121 | do { 122 | // this should also work as a terminator, because the BaseAddress of the last module in the cycle is NULL 123 | if (curr_module == NULL || curr_module->BaseAddress == NULL) { 124 | break; 125 | } 126 | if (is_wanted_module(curr_module->BaseDllName.Buffer, module_name)) { 127 | return (HMODULE)(curr_module->BaseAddress); 128 | } 129 | curr_module = (PLDR_MODULE)curr_module->InLoadOrderModuleList.Flink; 130 | 131 | } while (curr_module != first_module); 132 | 133 | return NULL; 134 | } 135 | 136 | size_t peconv::get_module_size_via_peb(IN OPTIONAL HMODULE hModule) 137 | { 138 | PPEB peb = get_peb(); 139 | if (!peb) { 140 | return 0; 141 | } 142 | SectionLocker locker(*peb->LoaderLock); 143 | LIST_ENTRY head = peb->Ldr->InLoadOrderModuleList; 144 | 145 | const PLDR_MODULE first_module = *((PLDR_MODULE *)(&head)); 146 | PLDR_MODULE curr_module = first_module; 147 | if (!hModule) { 148 | return (size_t)(curr_module->SizeOfImage); 149 | } 150 | 151 | // it is a cyclic list, so if the next record links to the initial one, it means we went throught the full loop 152 | do { 153 | // this should also work as a terminator, because the BaseAddress of the last module in the cycle is NULL 154 | if (curr_module == NULL || curr_module->BaseAddress == NULL) { 155 | break; 156 | } 157 | if (hModule == (HMODULE)(curr_module->BaseAddress)) { 158 | return (size_t)(curr_module->SizeOfImage); 159 | } 160 | curr_module = (PLDR_MODULE)curr_module->InLoadOrderModuleList.Flink; 161 | 162 | } while (curr_module != first_module); 163 | 164 | return 0; 165 | } 166 | 167 | bool peconv::set_main_module_in_peb(HMODULE module_ptr) 168 | { 169 | PPEB peb = get_peb(); 170 | if (peb == NULL) { 171 | return false; 172 | } 173 | SectionLocker locker(*peb->FastPebLock); 174 | peb->ImageBaseAddress = module_ptr; 175 | return true; 176 | } 177 | 178 | HMODULE peconv::get_main_module_via_peb() 179 | { 180 | PPEB peb = get_peb(); 181 | if (peb == NULL) { 182 | return NULL; 183 | } 184 | SectionLocker locker(*peb->FastPebLock); 185 | return (HMODULE) peb->ImageBaseAddress; 186 | } 187 | -------------------------------------------------------------------------------- /libpeconv/src/resource_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/resource_parser.h" 2 | #include "peconv/pe_hdrs_helper.h" 3 | 4 | #ifdef _DEBUG 5 | #include 6 | #endif 7 | 8 | bool parse_resource_dir(BYTE* modulePtr, const size_t moduleSize, 9 | IMAGE_RESOURCE_DIRECTORY_ENTRY *root_dir, 10 | const IMAGE_RESOURCE_DIRECTORY *upper_dir, 11 | IMAGE_RESOURCE_DIRECTORY* curr_dir, 12 | peconv::t_on_res_entry_found on_entry); 13 | 14 | bool parse_resource_entry(BYTE* modulePtr, const size_t moduleSize, 15 | IMAGE_RESOURCE_DIRECTORY_ENTRY *root_dir, 16 | const IMAGE_RESOURCE_DIRECTORY *upper_dir, 17 | IMAGE_RESOURCE_DIRECTORY_ENTRY* entry, 18 | peconv::t_on_res_entry_found on_entry) 19 | { 20 | if (!entry->DataIsDirectory) { 21 | #ifdef _DEBUG 22 | std::cout << "Entry is NOT a directory\n"; 23 | #endif 24 | DWORD offset = entry->OffsetToData; 25 | #ifdef _DEBUG 26 | std::cout << "Offset: " << offset << std::endl; 27 | #endif 28 | IMAGE_RESOURCE_DATA_ENTRY *data_entry = (IMAGE_RESOURCE_DATA_ENTRY*)(offset + (ULONGLONG)upper_dir); 29 | if (!peconv::validate_ptr(modulePtr, moduleSize, data_entry, sizeof(IMAGE_RESOURCE_DATA_ENTRY))) { 30 | return false; 31 | } 32 | #ifdef _DEBUG 33 | std::cout << "Data Offset: " << data_entry->OffsetToData << " : " << data_entry->Size << std::endl; 34 | #endif 35 | BYTE* data_ptr = (BYTE*)((ULONGLONG)modulePtr + data_entry->OffsetToData); 36 | if (!peconv::validate_ptr(modulePtr, moduleSize, data_ptr, data_entry->Size)) { 37 | return false; 38 | } 39 | on_entry(modulePtr, root_dir, data_entry); 40 | return true; 41 | } 42 | #ifdef _DEBUG 43 | std::cout << "Entry is a directory\n"; 44 | #endif 45 | //else: it is a next level directory 46 | DWORD offset = entry->OffsetToDirectory; 47 | #ifdef _DEBUG 48 | std::cout << "Offset: " << offset << std::endl; 49 | #endif 50 | IMAGE_RESOURCE_DIRECTORY *next_dir = (IMAGE_RESOURCE_DIRECTORY*)(offset + (ULONGLONG)upper_dir); 51 | if (!peconv::validate_ptr(modulePtr, moduleSize, next_dir, sizeof(IMAGE_RESOURCE_DIRECTORY))) { 52 | return false; 53 | } 54 | return parse_resource_dir(modulePtr, moduleSize, root_dir, upper_dir, next_dir, on_entry); 55 | } 56 | 57 | bool parse_resource_dir(BYTE* modulePtr, const size_t moduleSize, 58 | IMAGE_RESOURCE_DIRECTORY_ENTRY *root_dir, 59 | const IMAGE_RESOURCE_DIRECTORY *upper_dir, 60 | IMAGE_RESOURCE_DIRECTORY* curr_dir, 61 | peconv::t_on_res_entry_found on_entry) 62 | { 63 | size_t total_entries = curr_dir->NumberOfIdEntries + curr_dir->NumberOfNamedEntries; 64 | IMAGE_RESOURCE_DIRECTORY_ENTRY* first_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)((ULONGLONG)&curr_dir->NumberOfIdEntries + sizeof(WORD)); 65 | for (size_t i = 0; i < total_entries; i++) { 66 | IMAGE_RESOURCE_DIRECTORY_ENTRY* entry = &first_entry[i]; 67 | #ifdef _DEBUG 68 | std::cout << "Entry:" << std::hex << i << " ; " << "Id: " << entry->Id << " ; dataOffset:" << entry->OffsetToData << "\n"; 69 | #endif 70 | if (root_dir == nullptr) { 71 | root_dir = entry; 72 | } 73 | parse_resource_entry(modulePtr, moduleSize, root_dir, upper_dir, entry, on_entry); 74 | } 75 | return true; 76 | } 77 | 78 | bool peconv::parse_resources(BYTE* modulePtr, t_on_res_entry_found on_entry) 79 | { 80 | const size_t module_size = peconv::get_image_size(modulePtr); 81 | IMAGE_DATA_DIRECTORY *dir = peconv::get_directory_entry(modulePtr, IMAGE_DIRECTORY_ENTRY_RESOURCE); 82 | if (!dir || dir->VirtualAddress == 0 || dir->Size == 0) { 83 | return false; 84 | } 85 | IMAGE_RESOURCE_DIRECTORY *res_dir = (IMAGE_RESOURCE_DIRECTORY*)(dir->VirtualAddress + (ULONGLONG)modulePtr); 86 | if (!peconv::validate_ptr(modulePtr, module_size, res_dir, sizeof(IMAGE_DEBUG_DIRECTORY))) { 87 | return false; 88 | } 89 | return parse_resource_dir(modulePtr, module_size, nullptr, res_dir, res_dir, on_entry); 90 | } 91 | -------------------------------------------------------------------------------- /libpeconv/src/resource_util.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/resource_util.h" 2 | 3 | #ifdef _DEBUG 4 | #include 5 | #endif 6 | 7 | HMODULE peconv::get_current_module_handle() 8 | { 9 | HMODULE hMod = NULL; 10 | GetModuleHandleExW( 11 | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 12 | reinterpret_cast(&peconv::get_current_module_handle), 13 | &hMod); 14 | return hMod; 15 | } 16 | 17 | peconv::ALIGNED_BUF peconv::load_resource_data(OUT size_t &out_size, int res_id, const LPSTR res_type, HMODULE hInstance) 18 | { 19 | if (hInstance == nullptr) { 20 | hInstance = GetModuleHandleA(NULL); 21 | } 22 | HRSRC res = FindResourceA(hInstance, MAKEINTRESOURCEA(res_id), res_type); 23 | if (!res) { 24 | #ifdef _DEBUG 25 | std::cerr << "Cannot find resource" << std::endl; 26 | #endif 27 | return nullptr; 28 | } 29 | HGLOBAL res_handle = LoadResource(hInstance, res); 30 | if (res_handle == nullptr) { 31 | #ifdef _DEBUG 32 | std::cerr << "Cannot get resource handle" << std::endl; 33 | #endif 34 | return nullptr; 35 | } 36 | BYTE* res_data = (BYTE*) LockResource(res_handle); 37 | size_t r_size = static_cast(SizeofResource(hInstance, res)); 38 | if (out_size != 0 && out_size <= r_size) { 39 | r_size = out_size; 40 | } 41 | 42 | peconv::ALIGNED_BUF out_buf = peconv::alloc_aligned(r_size, PAGE_READWRITE); 43 | if (out_buf != nullptr) { 44 | memcpy(out_buf, res_data, r_size); 45 | out_size = r_size; 46 | } else { 47 | out_size = 0; 48 | } 49 | FreeResource(res_handle); 50 | return out_buf; 51 | } 52 | 53 | void peconv::free_resource_data(peconv::ALIGNED_BUF buffer) 54 | { 55 | peconv::free_aligned(buffer); 56 | } 57 | -------------------------------------------------------------------------------- /libpeconv/src/tls_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/tls_parser.h" 2 | 3 | #include "peconv/pe_hdrs_helper.h" 4 | #include 5 | 6 | namespace peconv { 7 | 8 | 9 | template 10 | size_t fetch_callbacks_list(IN PVOID modulePtr, IN size_t moduleSize, IN DWORD callbacks_rva, OUT std::vector &tls_callbacks) 11 | { 12 | FIELD_T* callbacks_list_ptr = (FIELD_T*)(callbacks_rva + (BYTE*)modulePtr); 13 | if (!validate_ptr(modulePtr, moduleSize, callbacks_list_ptr, sizeof(FIELD_T))) { 14 | return 0; 15 | } 16 | 17 | const ULONGLONG img_base = (ULONGLONG)modulePtr; 18 | const DWORD img_size = peconv::get_image_size((BYTE*)modulePtr); 19 | size_t counter = 0; 20 | for (FIELD_T *next_callback = callbacks_list_ptr; 21 | validate_ptr(modulePtr, moduleSize, next_callback, sizeof(FIELD_T)); 22 | next_callback++) 23 | { 24 | FIELD_T value = *next_callback; 25 | if (value == 0) break; 26 | 27 | tls_callbacks.push_back(value); 28 | counter++; 29 | } 30 | return counter; 31 | } 32 | }; 33 | 34 | 35 | bool peconv::virtual_addr_to_rva(IN const ULONGLONG img_base, IN const DWORD img_size, IN ULONGLONG callback_addr, OUT DWORD &callback_rva) 36 | { 37 | if (!img_size || !callback_addr) return false; 38 | 39 | //check if VA: 40 | if (callback_addr >= img_base && callback_addr < (img_base + img_size)) { 41 | callback_rva = MASK_TO_DWORD(callback_addr - img_base); 42 | return true; 43 | } 44 | if (callback_addr < img_size) { 45 | callback_rva = MASK_TO_DWORD(callback_addr); 46 | return true; 47 | } 48 | // out of scope address 49 | return false; 50 | } 51 | 52 | size_t peconv::list_tls_callbacks(IN PVOID modulePtr, IN size_t moduleSize, OUT std::vector &tls_callbacks) 53 | { 54 | const ULONGLONG img_base = (ULONGLONG)modulePtr; 55 | const DWORD img_size = peconv::get_image_size((BYTE*)modulePtr); 56 | if (!img_size) return 0; // invalid image 57 | 58 | if (moduleSize == 0) { 59 | moduleSize = img_size; 60 | } 61 | IMAGE_TLS_DIRECTORY* tls_dir = peconv::get_type_directory((HMODULE)modulePtr, IMAGE_DIRECTORY_ENTRY_TLS); 62 | if (!tls_dir) return 0; 63 | 64 | ULONGLONG callbacks_addr = tls_dir->AddressOfCallBacks; 65 | if (!callbacks_addr) return 0; 66 | #ifdef _DEBUG 67 | std::cout << "TLS Callbacks Table: " << std::hex << callbacks_addr << std::endl; 68 | #endif 69 | DWORD callbacks_rva = 0; 70 | if (!virtual_addr_to_rva(img_base, img_size, callbacks_addr, callbacks_rva)) return 0; 71 | #ifdef _DEBUG 72 | std::cout << "TLS Callbacks RVA: " << std::hex << callbacks_rva << std::endl; 73 | #endif 74 | size_t counter = 0; 75 | if (peconv::is64bit((BYTE*)modulePtr)) { 76 | counter = fetch_callbacks_list(modulePtr, moduleSize, callbacks_rva, tls_callbacks); 77 | } 78 | else { 79 | counter = fetch_callbacks_list(modulePtr, moduleSize, callbacks_rva, tls_callbacks); 80 | } 81 | return counter; 82 | } 83 | 84 | size_t peconv::run_tls_callbacks(IN PVOID modulePtr, IN size_t moduleSize, IN DWORD dwReason) 85 | { 86 | const DWORD img_size = peconv::get_image_size((BYTE*)modulePtr); 87 | if (moduleSize == 0) { 88 | moduleSize = img_size; 89 | } 90 | std::vector tls_callbacks; 91 | if (!peconv::list_tls_callbacks(modulePtr, moduleSize, tls_callbacks)) { 92 | return 0; 93 | } 94 | std::vector::iterator itr; 95 | size_t i = 0; 96 | for (itr = tls_callbacks.begin(); itr != tls_callbacks.end(); ++itr, i++) { 97 | ULONGLONG callback_addr = *itr; 98 | DWORD rva = 0; //TLS callback can be defined as RVA or VA, so make sure it is in a consistent format... 99 | if (!peconv::virtual_addr_to_rva((ULONG_PTR)modulePtr, img_size, callback_addr, rva)) { 100 | // in some cases, TLS callbacks can lead to functions in other modules: we want to skip those, 101 | // keeping only addresses that are in the current PE scope 102 | continue; 103 | } 104 | #ifdef _DEBUG 105 | std::cout << std::hex << "TLS RVA:" << rva << std::endl; 106 | #endif 107 | ULONG_PTR callback_va = rva + (ULONG_PTR)modulePtr; 108 | if (!validate_ptr(modulePtr, moduleSize, (BYTE*)callback_va, sizeof(BYTE))) { 109 | // make sure that the address is valid 110 | continue; 111 | } 112 | void(NTAPI *callback_func)(PVOID DllHandle, DWORD dwReason, PVOID) = (void(NTAPI *)(PVOID, DWORD, PVOID)) (callback_va); 113 | #ifdef _DEBUG 114 | std::cout << "Calling TLS callback[" << i << "]:" << std::endl; 115 | #endif 116 | callback_func(modulePtr, dwReason, NULL); 117 | } 118 | return i; 119 | } 120 | -------------------------------------------------------------------------------- /libpeconv/src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "peconv/util.h" 2 | #include 3 | 4 | #define USE_OLD_BADPTR 5 | 6 | namespace peconv { 7 | 8 | HMODULE g_kernel32Hndl = nullptr; 9 | HMODULE g_ntdllHndl = nullptr; 10 | 11 | HMODULE get_kernel32_hndl() 12 | { 13 | if (g_kernel32Hndl == nullptr) { 14 | g_kernel32Hndl = LoadLibraryA("kernel32.dll"); 15 | } 16 | return g_kernel32Hndl; 17 | } 18 | 19 | HMODULE get_ntdll_hndl() 20 | { 21 | if (g_ntdllHndl == nullptr) { 22 | g_ntdllHndl = LoadLibraryA("ntdll.dll"); 23 | } 24 | return g_ntdllHndl; 25 | } 26 | }; 27 | 28 | DWORD ntdll_get_process_id(HANDLE hProcess) 29 | { 30 | #if !defined PROCESSINFOCLASS 31 | typedef LONG PROCESSINFOCLASS; 32 | #endif 33 | 34 | NTSTATUS(WINAPI *_ZwQueryInformationProcess)( 35 | IN HANDLE ProcessHandle, 36 | IN PROCESSINFOCLASS ProcessInformationClass, 37 | OUT PVOID ProcessInformation, 38 | IN ULONG ProcessInformationLength, 39 | OUT PULONG ReturnLength 40 | ) = NULL; 41 | 42 | HINSTANCE hNtDll = peconv::get_ntdll_hndl(); 43 | if (!hNtDll) { 44 | return 0; 45 | } 46 | 47 | FARPROC procPtr = GetProcAddress(hNtDll, "ZwQueryInformationProcess"); 48 | if (!procPtr) { 49 | return 0; 50 | } 51 | 52 | _ZwQueryInformationProcess = (NTSTATUS(WINAPI *)( 53 | HANDLE, 54 | PROCESSINFOCLASS, 55 | PVOID, 56 | ULONG, 57 | PULONG) 58 | ) procPtr; 59 | 60 | typedef struct _PROCESS_BASIC_INFORMATION { 61 | PVOID Reserved1; 62 | PVOID PebBaseAddress; 63 | PVOID Reserved2[2]; 64 | ULONG_PTR UniqueProcessId; 65 | PVOID Reserved3; 66 | } PROCESS_BASIC_INFORMATION; 67 | 68 | PROCESS_BASIC_INFORMATION pbi = { 0 }; 69 | if (_ZwQueryInformationProcess(hProcess, 0, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL) == S_OK) { 70 | const DWORD pid = static_cast(pbi.UniqueProcessId); 71 | return pid; 72 | } 73 | return 0; 74 | } 75 | 76 | DWORD peconv::get_process_id(HANDLE hProcess) 77 | { 78 | static DWORD(WINAPI *_GetProcessId)(IN HANDLE Process) = nullptr; 79 | 80 | DWORD processID = 0; 81 | if (!_GetProcessId) { 82 | HMODULE kernelLib = peconv::get_kernel32_hndl(); 83 | if (kernelLib) { 84 | FARPROC procPtr = GetProcAddress(kernelLib, "GetProcessId"); 85 | if (procPtr) { 86 | _GetProcessId = (DWORD(WINAPI *) (IN HANDLE))procPtr; 87 | } 88 | } 89 | } 90 | if (_GetProcessId) { 91 | processID = _GetProcessId(hProcess); 92 | } 93 | if (processID == 0) { 94 | //could not retrieve Pid using GetProcessId, try using NTDLL: 95 | processID = ntdll_get_process_id(hProcess); 96 | } 97 | return processID; 98 | } 99 | 100 | bool peconv::is_padding(const BYTE *cave_ptr, size_t cave_size, const BYTE padding) 101 | { 102 | for (size_t i = 0; i < cave_size; i++) { 103 | if (cave_ptr[i] != padding) { 104 | return false; 105 | } 106 | } 107 | return true; 108 | } 109 | 110 | bool peconv::is_mem_accessible(LPCVOID areaStart, SIZE_T areaSize, DWORD dwAccessRights) 111 | { 112 | if (!areaSize) return false; // zero-sized areas are not allowed 113 | 114 | const DWORD dwForbiddenArea = PAGE_GUARD | PAGE_NOACCESS; 115 | 116 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 117 | const size_t mbiSize = sizeof(MEMORY_BASIC_INFORMATION); 118 | 119 | SIZE_T sizeToCheck = areaSize; 120 | LPCVOID areaPtr = areaStart; 121 | 122 | while (sizeToCheck > 0) { 123 | //reset area 124 | memset(&mbi, 0, mbiSize); 125 | 126 | // query the next area 127 | if (VirtualQuery(areaPtr, &mbi, mbiSize) != mbiSize) { 128 | return false; // could not query the area, assume it is bad 129 | } 130 | // check the privileges 131 | bool isOk = (mbi.State & MEM_COMMIT) // memory allocated and 132 | && !(mbi.Protect & dwForbiddenArea) // access to page allowed and 133 | && (mbi.Protect & dwAccessRights); // the required rights 134 | if (!isOk) { 135 | return false; //invalid access 136 | } 137 | SIZE_T offset = (ULONG_PTR)areaPtr - (ULONG_PTR)mbi.BaseAddress; 138 | SIZE_T queriedSize = mbi.RegionSize - offset; 139 | if (queriedSize >= sizeToCheck) { 140 | return true; // it is fine 141 | } 142 | // move to the next region 143 | sizeToCheck -= queriedSize; 144 | areaPtr = LPCVOID((ULONG_PTR)areaPtr + queriedSize); 145 | } 146 | // by default assume it is inaccessible 147 | return false; 148 | } 149 | 150 | bool peconv::is_bad_read_ptr(LPCVOID areaStart, SIZE_T areaSize) 151 | { 152 | #ifdef USE_OLD_BADPTR // classic IsBadReadPtr is much faster than the version using VirtualQuery 153 | return (IsBadReadPtr(areaStart, areaSize)) ? true : false; 154 | #else 155 | const DWORD dwReadRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; 156 | bool isAccessible = peconv::is_mem_accessible(areaStart, areaSize, dwReadRights); 157 | if (isAccessible) { 158 | // the area has read access rights: not a bad read pointer 159 | return false; 160 | } 161 | return true; 162 | #endif 163 | } 164 | -------------------------------------------------------------------------------- /pe_unmapper/README.md: -------------------------------------------------------------------------------- 1 | # pe_unmapper 2 | 3 | Small tool to convert beteween the PE alignments (raw and virtual). 4 | 5 | Moved into a dedicated repository: 6 | + https://github.com/hasherezade/pe_unmapper 7 | -------------------------------------------------------------------------------- /run_pe/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | project (run_pe) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | message (STATUS "parser_dir='${PECONV_DIR}'") 7 | message (STATUS "parser_lib='${PECONV_LIB}'") 8 | 9 | include_directories ( ${PECONV_DIR}/include ) 10 | 11 | set (srcs 12 | main.cpp 13 | run_pe.cpp 14 | patch_ntdll.cpp 15 | ) 16 | 17 | set (hdrs 18 | run_pe.h 19 | patch_ntdll.h 20 | ) 21 | 22 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs} ) 23 | target_link_libraries ( ${PROJECT_NAME} ${PECONV_LIB} ) 24 | add_dependencies( ${PROJECT_NAME} libpeconv) 25 | 26 | if(PECONV_LIB_INSTALL) 27 | include(GNUInstallDirs) 28 | 29 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 30 | endif() 31 | -------------------------------------------------------------------------------- /run_pe/README.md: -------------------------------------------------------------------------------- 1 | # Demo: RunPE 2 | 3 | This is a demo project using _libpeconv_.
4 | RunPE (aka Process Hollowing) is a well known technique allowing to injecting a new PE into a remote processes, imprersonating this process. 5 | 6 | ![](https://blog.malwarebytes.com/wp-content/uploads/2018/08/hollowing1-1_.png) 7 | 8 | The given implementation works for PE 32bit as well as 64bit.
9 | 10 | Supported injections: 11 | - 12 | If the loader was built as 32 bit: 13 | ``` 14 | 32 bit payload -> 32 bit target 15 | ``` 16 | If the loader was built as 64 bit: 17 | ``` 18 | 64 bit payload -> 64 bit target 19 | 32 bit payload -> 32 bit target 20 | ``` 21 | 22 | How to use the app: 23 | - 24 | Supply 2 commandline arguments: 25 | 26 | ``` 27 | [payload_path] [target_path] 28 | ``` 29 | 30 | Payload is the PE to be executed impersonating the Target. 31 | -------------------------------------------------------------------------------- /run_pe/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "run_pe.h" 5 | 6 | LPCTSTR version = TEXT("0.2"); 7 | 8 | bool g_PatchRequired = false; 9 | 10 | bool isWindows1124H2OrLater() 11 | { 12 | NTSYSAPI NTSTATUS RtlGetVersion( PRTL_OSVERSIONINFOW lpVersionInformation ); 13 | 14 | RTL_OSVERSIONINFOW osVersionInfo = { 0 }; 15 | osVersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW); 16 | 17 | HMODULE hNtdll = GetModuleHandleA("ntdll"); 18 | if (!hNtdll) return false; // should never happen 19 | 20 | auto _RtlGetVersion = reinterpret_cast(GetProcAddress(hNtdll, "RtlGetVersion")); 21 | NTSTATUS status = _RtlGetVersion( 22 | &osVersionInfo 23 | ); 24 | if (status != S_OK) { 25 | std::cerr << "Failed to retrieve OS version information." << std::endl; 26 | return false; 27 | } 28 | // Check major version and build number for Windows 11 29 | if (osVersionInfo.dwMajorVersion > 10 || 30 | (osVersionInfo.dwMajorVersion == 10 && osVersionInfo.dwBuildNumber >= 26100)) { 31 | return true; 32 | } 33 | return false; 34 | } 35 | 36 | int _tmain(int argc, LPTSTR argv[]) 37 | { 38 | LPTSTR payload_path = NULL; 39 | LPTSTR target_path = NULL; 40 | if (isWindows1124H2OrLater()) { 41 | std::cout << "WARNING: Executing RunPE on Windows11 24H2 or above requires patching NTDLL.ZwQueryVirtualMemory\n"; 42 | g_PatchRequired = true; 43 | } 44 | if (argc < 3) { 45 | std::tcout << TEXT("[ run_pe v") << version << TEXT(" ]\n") 46 | << TEXT("Args: \n"); 47 | system("pause"); 48 | return -1; 49 | } 50 | 51 | payload_path = argv[1]; 52 | target_path = argv[2]; 53 | 54 | std::tstring cmdLine = GetCommandLine(); 55 | size_t found = cmdLine.find(target_path); 56 | 57 | // cut out the parameters that are dedicated to the run_pe app only 58 | std::tstring trimmedCmdLine = cmdLine.substr(found, cmdLine.length()); 59 | 60 | std::tcout << TEXT("Payload: ") << payload_path << TEXT("\n"); 61 | std::tcout << TEXT("Target: ") << target_path << TEXT("\n"); 62 | 63 | bool isOk = run_pe(payload_path, target_path, trimmedCmdLine.c_str()); 64 | if (!isOk) { 65 | std::cerr << "Failed!\n"; 66 | } 67 | else { 68 | std::cout << "Done!\n"; 69 | } 70 | return isOk ? 0 : (-1); 71 | } 72 | -------------------------------------------------------------------------------- /run_pe/patch_ntdll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | bool patch_NtManageHotPatch32(HANDLE hProcess); 6 | bool patch_NtManageHotPatch64(HANDLE hProcess); 7 | bool patch_ZwQueryVirtualMemory(HANDLE hProcess, LPVOID module_ptr); 8 | -------------------------------------------------------------------------------- /run_pe/run_pe.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** 6 | Perform the RunPE injection of the payload into the target. 7 | */ 8 | bool run_pe(IN LPCTSTR payloadPath, IN LPCTSTR targetPath, IN LPCTSTR cmdLine); 9 | 10 | BOOL update_remote_entry_point(PROCESS_INFORMATION& pi, ULONGLONG entry_point_va, bool is32bit); 11 | -------------------------------------------------------------------------------- /tests/greek_to_me.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/libpeconv/7083def3d43f19245b9d7310939f18c01ac4c004/tests/greek_to_me.bin -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "test_loading.h" 6 | #include "test_loading_imps.h" 7 | #include "test_crackme_f4_3.h" 8 | #include "test_hooking_imps.h" 9 | #include "test_crackme_f4_6.h" 10 | #include "test_load_ntdll.h" 11 | #include "test_replacing_func.h" 12 | #include "test_delayed_imps.h" 13 | #include "test_imp_list.h" 14 | #include "test_hooking_local.h" 15 | #include "test_peb_lookup.h" 16 | #include "test_imports_mix.h" 17 | #include "test_found_base.h" 18 | #include "test_fix_dotnet.h" 19 | #include "test_format_detect.h" 20 | #include "test_tls_callbacks.h" 21 | #include "test_exceptions.h" 22 | 23 | int make_test(int test_id, LPCTSTR test_arg) 24 | { 25 | switch (test_id) { 26 | case 1: return tests::load_self(); 27 | case 2: return tests::deploy_self(); 28 | case 3: return tests::brutforce_crackme_f4_3(); 29 | case 4: 30 | { 31 | peconv::export_based_resolver *exp_res = new peconv::export_based_resolver(); 32 | int res = tests::deploy_self_ex((peconv::t_function_resolver*)exp_res); 33 | delete exp_res; 34 | return res; 35 | } 36 | case 5: return tests::hook_testcase(test_arg); 37 | case 6: return tests::decode_crackme_f4_6(test_arg); 38 | case 7: return tests::test_ntdll(NULL); //manual test 39 | case 8: return tests::replace_func_testcase(test_arg); 40 | case 9: return tests::replace_delayed_imps(test_arg); 41 | case 10: return tests::imp_list(test_arg); //manual test 42 | case 11: return tests::hook_self_local(); 43 | case 12: return tests::check_modules(); 44 | case 13: return tests::imports_mix(test_arg); 45 | case 14: return tests::load_and_check_base(test_arg); 46 | case 15: return tests::check_finding_jumps(); 47 | case 16: return tests::check_pe_format(test_arg); 48 | case 17: return tests::test_load_with_tls_callbacks(test_arg); 49 | case 18: return tests::test_load_with_exception_table(test_arg); 50 | } 51 | return -1; 52 | } 53 | 54 | void print_banner() 55 | { 56 | printf("---------------\n"); 57 | printf("TESTS DEPLOYED!\n"); 58 | printf("---------------\n"); 59 | } 60 | 61 | int _tmain(int argc, LPCTSTR argv[]) 62 | { 63 | print_banner(); 64 | if (argc < 2) { 65 | printf("Supply the test id!\n"); 66 | return 0; 67 | } 68 | 69 | int test_id = _tstoi(argv[1]); 70 | printf("Test ID: %d\n", test_id); 71 | 72 | LPCTSTR test_arg = NULL; 73 | if (argc > 2) { 74 | test_arg = argv[2]; 75 | } 76 | int res = make_test(test_id, test_arg); 77 | 78 | if (res == 0) { 79 | printf("[+] Test passed!\n"); 80 | } 81 | return res; 82 | } 83 | -------------------------------------------------------------------------------- /tests/resource.h: -------------------------------------------------------------------------------- 1 | // resource.h 2 | 3 | #define CRACKME_F4_3_32 101 4 | -------------------------------------------------------------------------------- /tests/resource.rc: -------------------------------------------------------------------------------- 1 | // resource.rc : 2 | 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource.h" 6 | 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "windows.h" 13 | 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | 17 | ///////////////////////////////////////////////////////////////////////////// 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PLK) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""windows.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // RCDATA 51 | // 52 | 53 | CRACKME_F4_3_32 RCDATA "greek_to_me.bin" 54 | 55 | #endif 56 | ///////////////////////////////////////////////////////////////////////////// 57 | -------------------------------------------------------------------------------- /tests/shellc32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | unsigned char messageBox32bit_sc[] = { 4 | 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x1C, 0xE8, 0x1C, 0x00, 0x00, 0x00, 0x6B, 5 | 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x33, 6 | 0x00, 0x32, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x00, 7 | 0x00, 0x00, 0x00, 0xE8, 0x60, 0x02, 0x00, 0x00, 0x83, 0xC4, 0x04, 0x89, 8 | 0x45, 0xFC, 0x83, 0x7D, 0xFC, 0x00, 0x75, 0x0A, 0xB8, 0x01, 0x00, 0x00, 9 | 0x00, 0xE9, 0xEC, 0x00, 0x00, 0x00, 0xE8, 0x10, 0x00, 0x00, 0x00, 0x4C, 10 | 0x6F, 0x61, 0x64, 0x4C, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41, 0x00, 11 | 0x00, 0x00, 0x00, 0x8B, 0x45, 0xFC, 0x50, 0xE8, 0xD2, 0x00, 0x00, 0x00, 12 | 0x83, 0xC4, 0x08, 0x89, 0x45, 0xF8, 0x83, 0x7D, 0xF8, 0x00, 0x75, 0x0A, 13 | 0xB8, 0x02, 0x00, 0x00, 0x00, 0xE9, 0xB8, 0x00, 0x00, 0x00, 0xE8, 0x10, 14 | 0x00, 0x00, 0x00, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6F, 0x63, 0x41, 0x64, 15 | 0x64, 0x72, 0x65, 0x73, 0x73, 0x00, 0x00, 0x8B, 0x4D, 0xFC, 0x51, 0xE8, 16 | 0x9E, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x08, 0x89, 0x45, 0xF4, 0x83, 0x7D, 17 | 0xF4, 0x00, 0x75, 0x0A, 0xB8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0x84, 0x00, 18 | 0x00, 0x00, 0x8B, 0x55, 0xF8, 0x89, 0x55, 0xEC, 0x8B, 0x45, 0xF4, 0x89, 19 | 0x45, 0xE4, 0xE8, 0x0C, 0x00, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x33, 20 | 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x00, 0xFF, 0x55, 0xEC, 0x89, 0x45, 21 | 0xE8, 0xE8, 0x0C, 0x00, 0x00, 0x00, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 22 | 0x65, 0x42, 0x6F, 0x78, 0x57, 0x00, 0x8B, 0x4D, 0xE8, 0x51, 0xFF, 0x55, 23 | 0xE4, 0x89, 0x45, 0xF0, 0x83, 0x7D, 0xF0, 0x00, 0x75, 0x07, 0xB8, 0x04, 24 | 0x00, 0x00, 0x00, 0xEB, 0x39, 0x6A, 0x00, 0xE8, 0x0C, 0x00, 0x00, 0x00, 25 | 0x44, 0x00, 0x65, 0x00, 0x6D, 0x00, 0x6F, 0x00, 0x21, 0x00, 0x00, 0x00, 26 | 0xE8, 0x1A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 27 | 0x00, 0x6F, 0x00, 0x20, 0x00, 0x57, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x6C, 28 | 0x00, 0x64, 0x00, 0x21, 0x00, 0x00, 0x00, 0x6A, 0x00, 0xFF, 0x55, 0xF0, 29 | 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3, 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x3C, 30 | 0x8B, 0x45, 0x08, 0x89, 0x45, 0xEC, 0x8B, 0x4D, 0xEC, 0x0F, 0xB7, 0x11, 31 | 0x81, 0xFA, 0x4D, 0x5A, 0x00, 0x00, 0x74, 0x07, 0x33, 0xC0, 0xE9, 0x35, 32 | 0x01, 0x00, 0x00, 0x8B, 0x45, 0xEC, 0x8B, 0x4D, 0x08, 0x03, 0x48, 0x3C, 33 | 0x89, 0x4D, 0xE4, 0xBA, 0x08, 0x00, 0x00, 0x00, 0x6B, 0xC2, 0x00, 0x8B, 34 | 0x4D, 0xE4, 0x8D, 0x54, 0x01, 0x78, 0x89, 0x55, 0xE8, 0x8B, 0x45, 0xE8, 35 | 0x83, 0x38, 0x00, 0x75, 0x07, 0x33, 0xC0, 0xE9, 0x08, 0x01, 0x00, 0x00, 36 | 0x8B, 0x4D, 0xE8, 0x8B, 0x11, 0x89, 0x55, 0xE0, 0x8B, 0x45, 0xE0, 0x03, 37 | 0x45, 0x08, 0x89, 0x45, 0xF4, 0x8B, 0x4D, 0xF4, 0x8B, 0x51, 0x18, 0x89, 38 | 0x55, 0xDC, 0x8B, 0x45, 0xF4, 0x8B, 0x48, 0x1C, 0x89, 0x4D, 0xD0, 0x8B, 39 | 0x55, 0xF4, 0x8B, 0x42, 0x20, 0x89, 0x45, 0xD8, 0x8B, 0x4D, 0xF4, 0x8B, 40 | 0x51, 0x24, 0x89, 0x55, 0xD4, 0xC7, 0x45, 0xF8, 0x00, 0x00, 0x00, 0x00, 41 | 0xEB, 0x09, 0x8B, 0x45, 0xF8, 0x83, 0xC0, 0x01, 0x89, 0x45, 0xF8, 0x8B, 42 | 0x4D, 0xF8, 0x3B, 0x4D, 0xDC, 0x0F, 0x83, 0xB3, 0x00, 0x00, 0x00, 0x8B, 43 | 0x55, 0x08, 0x03, 0x55, 0xD8, 0x8B, 0x45, 0xF8, 0x8D, 0x0C, 0x82, 0x89, 44 | 0x4D, 0xC8, 0x8B, 0x55, 0x08, 0x03, 0x55, 0xD4, 0x8B, 0x45, 0xF8, 0x8D, 45 | 0x0C, 0x42, 0x89, 0x4D, 0xCC, 0x8B, 0x55, 0x08, 0x03, 0x55, 0xD0, 0x8B, 46 | 0x45, 0xCC, 0x0F, 0xB7, 0x08, 0x8D, 0x14, 0x8A, 0x89, 0x55, 0xC4, 0x8B, 47 | 0x45, 0xC8, 0x8B, 0x4D, 0x08, 0x03, 0x08, 0x89, 0x4D, 0xF0, 0xC7, 0x45, 48 | 0xFC, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 49 | 0xEB, 0x09, 0x8B, 0x55, 0xFC, 0x83, 0xC2, 0x01, 0x89, 0x55, 0xFC, 0x8B, 50 | 0x45, 0x0C, 0x03, 0x45, 0xFC, 0x0F, 0xBE, 0x08, 0x85, 0xC9, 0x74, 0x27, 51 | 0x8B, 0x55, 0xF0, 0x03, 0x55, 0xFC, 0x0F, 0xBE, 0x02, 0x85, 0xC0, 0x74, 52 | 0x1A, 0x8B, 0x4D, 0x0C, 0x03, 0x4D, 0xFC, 0x0F, 0xBE, 0x11, 0x8B, 0x45, 53 | 0xF0, 0x03, 0x45, 0xFC, 0x0F, 0xBE, 0x08, 0x3B, 0xD1, 0x74, 0x02, 0xEB, 54 | 0x02, 0xEB, 0xC3, 0x8B, 0x55, 0x0C, 0x03, 0x55, 0xFC, 0x0F, 0xBE, 0x02, 55 | 0x85, 0xC0, 0x75, 0x19, 0x8B, 0x4D, 0xF0, 0x03, 0x4D, 0xFC, 0x0F, 0xBE, 56 | 0x11, 0x85, 0xD2, 0x75, 0x0C, 0x8B, 0x45, 0xC4, 0x8B, 0x4D, 0x08, 0x03, 57 | 0x08, 0x8B, 0xC1, 0xEB, 0x07, 0xE9, 0x38, 0xFF, 0xFF, 0xFF, 0x33, 0xC0, 58 | 0x8B, 0xE5, 0x5D, 0xC3, 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x34, 0xC7, 0x45, 59 | 0xE4, 0x00, 0x00, 0x00, 0x00, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x89, 60 | 0x45, 0xE4, 0x8B, 0x4D, 0xE4, 0x8B, 0x51, 0x0C, 0x89, 0x55, 0xD8, 0x8B, 61 | 0x45, 0xD8, 0x8B, 0x48, 0x0C, 0x8B, 0x50, 0x10, 0x89, 0x4D, 0xCC, 0x89, 62 | 0x55, 0xD0, 0x8B, 0x45, 0xCC, 0x89, 0x45, 0xD4, 0x8B, 0x4D, 0xD4, 0x89, 63 | 0x4D, 0xE8, 0x83, 0x7D, 0xE8, 0x00, 0x0F, 0x84, 0x5A, 0x01, 0x00, 0x00, 64 | 0x8B, 0x55, 0xE8, 0x83, 0x7A, 0x18, 0x00, 0x0F, 0x84, 0x4D, 0x01, 0x00, 65 | 0x00, 0x8B, 0x45, 0xE8, 0x83, 0x78, 0x30, 0x00, 0x75, 0x02, 0xEB, 0xDE, 66 | 0x8B, 0x4D, 0xE8, 0x8B, 0x51, 0x30, 0x89, 0x55, 0xEC, 0xC7, 0x45, 0xF0, 67 | 0x00, 0x00, 0x00, 0x00, 0xC7, 0x45, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xEB, 68 | 0x09, 0x8B, 0x45, 0xF0, 0x83, 0xC0, 0x01, 0x89, 0x45, 0xF0, 0x8B, 0x4D, 69 | 0xF0, 0x8B, 0x55, 0x08, 0x0F, 0xB7, 0x04, 0x4A, 0x85, 0xC0, 0x0F, 0x84, 70 | 0xDD, 0x00, 0x00, 0x00, 0x8B, 0x4D, 0xF0, 0x8B, 0x55, 0xEC, 0x0F, 0xB7, 71 | 0x04, 0x4A, 0x85, 0xC0, 0x0F, 0x84, 0xCB, 0x00, 0x00, 0x00, 0x8B, 0x4D, 72 | 0xF0, 0x8B, 0x55, 0x08, 0x0F, 0xB7, 0x04, 0x4A, 0x83, 0xF8, 0x5A, 0x7F, 73 | 0x37, 0x8B, 0x4D, 0xF0, 0x8B, 0x55, 0x08, 0x0F, 0xB7, 0x04, 0x4A, 0x83, 74 | 0xF8, 0x41, 0x7C, 0x28, 0x8B, 0x4D, 0xF0, 0x8B, 0x55, 0x08, 0x0F, 0xB7, 75 | 0x04, 0x4A, 0x83, 0xC0, 0x20, 0x89, 0x45, 0xE0, 0x8B, 0x4D, 0xF0, 0x8B, 76 | 0x55, 0x08, 0x66, 0x8B, 0x45, 0xE0, 0x66, 0x89, 0x04, 0x4A, 0x66, 0x8B, 77 | 0x4D, 0xE0, 0x66, 0x89, 0x4D, 0xFE, 0xEB, 0x0E, 0x8B, 0x55, 0xF0, 0x8B, 78 | 0x45, 0x08, 0x66, 0x8B, 0x0C, 0x50, 0x66, 0x89, 0x4D, 0xFE, 0x66, 0x8B, 79 | 0x55, 0xFE, 0x66, 0x89, 0x55, 0xF8, 0x8B, 0x45, 0xF0, 0x8B, 0x4D, 0xEC, 80 | 0x0F, 0xB7, 0x14, 0x41, 0x83, 0xFA, 0x5A, 0x7F, 0x37, 0x8B, 0x45, 0xF0, 81 | 0x8B, 0x4D, 0xEC, 0x0F, 0xB7, 0x14, 0x41, 0x83, 0xFA, 0x41, 0x7C, 0x28, 82 | 0x8B, 0x45, 0xF0, 0x8B, 0x4D, 0xEC, 0x0F, 0xB7, 0x14, 0x41, 0x83, 0xC2, 83 | 0x20, 0x89, 0x55, 0xDC, 0x8B, 0x45, 0xF0, 0x8B, 0x4D, 0xEC, 0x66, 0x8B, 84 | 0x55, 0xDC, 0x66, 0x89, 0x14, 0x41, 0x66, 0x8B, 0x45, 0xDC, 0x66, 0x89, 85 | 0x45, 0xFC, 0xEB, 0x0E, 0x8B, 0x4D, 0xF0, 0x8B, 0x55, 0xEC, 0x66, 0x8B, 86 | 0x04, 0x4A, 0x66, 0x89, 0x45, 0xFC, 0x66, 0x8B, 0x4D, 0xFC, 0x66, 0x89, 87 | 0x4D, 0xF4, 0x0F, 0xB7, 0x55, 0xF8, 0x0F, 0xB7, 0x45, 0xF4, 0x3B, 0xD0, 88 | 0x74, 0x02, 0xEB, 0x05, 0xE9, 0x08, 0xFF, 0xFF, 0xFF, 0x8B, 0x4D, 0xF0, 89 | 0x8B, 0x55, 0x08, 0x0F, 0xB7, 0x04, 0x4A, 0x85, 0xC0, 0x75, 0x16, 0x8B, 90 | 0x4D, 0xF0, 0x8B, 0x55, 0xEC, 0x0F, 0xB7, 0x04, 0x4A, 0x85, 0xC0, 0x75, 91 | 0x08, 0x8B, 0x4D, 0xE8, 0x8B, 0x41, 0x18, 0xEB, 0x0F, 0x8B, 0x55, 0xE8, 92 | 0x8B, 0x02, 0x89, 0x45, 0xE8, 0xE9, 0x9C, 0xFE, 0xFF, 0xFF, 0x33, 0xC0, 93 | 0x8B, 0xE5, 0x5D, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 94 | }; 95 | -------------------------------------------------------------------------------- /tests/shellcodes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "shellc32.h" 4 | #include "shellc64.h" 5 | -------------------------------------------------------------------------------- /tests/test_case1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | project (test_case1) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | set (srcs 7 | main.cpp 8 | ) 9 | 10 | set (hdrs 11 | ) 12 | 13 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs}) 14 | 15 | #install 16 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 17 | -------------------------------------------------------------------------------- /tests/test_case1/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int popup_message1() 5 | { 6 | SYSTEMTIME SystemTime; 7 | GetSystemTime(&SystemTime); 8 | 9 | char pszDate[200]; 10 | GetDateFormatA( LOCALE_USER_DEFAULT, DATE_LONGDATE, &SystemTime, NULL, pszDate, 200 ); 11 | 12 | return MessageBoxA(NULL, pszDate, "Test Case 1", MB_OK); 13 | } 14 | 15 | int popup_message2() 16 | { 17 | return MessageBoxW(NULL, L"Checking wide strings", L"Test Case 1", MB_OK); 18 | } 19 | 20 | int main() 21 | { 22 | if (popup_message1() == 1337) { 23 | if (popup_message2() == 1338) { 24 | return MessageBox(NULL, TEXT("Hooking test passed"), TEXT("Test Case 1"), MB_OK); 25 | } 26 | } 27 | printf("Test Case 1 finished\n"); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /tests/test_case2/README.md: -------------------------------------------------------------------------------- 1 | # FlareOn2017 Challenge 6 2 | 3 | ### Writeup 4 | 5 | + [Solving Flare-On 2017, Challenge 6 with libPeConv](https://hshrzd.wordpress.com/2017/12/01/hook-the-planet-solving-flareon4-challenge6-with-libpeconv/) 6 | -------------------------------------------------------------------------------- /tests/test_case2/payload.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/libpeconv/7083def3d43f19245b9d7310939f18c01ac4c004/tests/test_case2/payload.dll -------------------------------------------------------------------------------- /tests/test_case3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | project (test_case3) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | set (srcs 7 | main.cpp 8 | checksum.cpp 9 | ) 10 | 11 | set (hdrs 12 | checksum.h 13 | ) 14 | 15 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs}) 16 | 17 | #install 18 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 19 | -------------------------------------------------------------------------------- /tests/test_case3/bin/test_case3_32.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/libpeconv/7083def3d43f19245b9d7310939f18c01ac4c004/tests/test_case3/bin/test_case3_32.exe -------------------------------------------------------------------------------- /tests/test_case3/bin/test_case3_64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/libpeconv/7083def3d43f19245b9d7310939f18c01ac4c004/tests/test_case3/bin/test_case3_64.exe -------------------------------------------------------------------------------- /tests/test_case3/checksum.cpp: -------------------------------------------------------------------------------- 1 | #include "checksum.h" 2 | 3 | inline DWORD rotl32a(DWORD x, DWORD n) 4 | { 5 | return (x << n) | (x >> (32 - n)); 6 | } 7 | 8 | inline char to_lower(char c) 9 | { 10 | if (c >= 'A' && c <= 'Z') { 11 | c = c - 'A' + 'a'; 12 | } 13 | return c; 14 | } 15 | 16 | DWORD calc_checksum(BYTE *str, size_t buf_size, bool enable_tolower) 17 | { 18 | if (str == NULL) return 0; 19 | 20 | DWORD checksum = 0; 21 | for (size_t i = 0; i < buf_size; i++) { 22 | checksum = rotl32a(checksum, 7); 23 | char c = str[i]; 24 | if (enable_tolower) { 25 | c = to_lower(c); 26 | } 27 | checksum ^= c; 28 | } 29 | return checksum; 30 | } 31 | 32 | DWORD calc_checksum(char *str, bool enable_tolower) 33 | { 34 | return calc_checksum((BYTE*)str, strlen(str), enable_tolower); 35 | } 36 | -------------------------------------------------------------------------------- /tests/test_case3/checksum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | DWORD calc_checksum(char *str, bool enable_tolower); 6 | DWORD calc_checksum(BYTE *str, size_t buf_size, bool enable_tolower); 7 | 8 | -------------------------------------------------------------------------------- /tests/test_case3/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "checksum.h" 6 | 7 | bool get_rand_string(char *buffer, size_t buffer_size) 8 | { 9 | const char charset[] = "ABCDEFGHIJKLMNOPQRSTUWVXYZabcdefghijklmnopqrstuwvxyz1234567890"; 10 | size_t charset_len = strlen(charset); 11 | 12 | srand(GetTickCount()); 13 | for (size_t i = 0; i < buffer_size - 1; i++) { 14 | size_t c_indx = rand() % charset_len; 15 | buffer[i] = charset[c_indx]; 16 | Sleep(1000); 17 | } 18 | buffer[buffer_size - 1] = '\0'; 19 | return true; 20 | } 21 | 22 | bool is_password_valid(char *str) 23 | { 24 | DWORD checksum = calc_checksum(str, true); 25 | if (checksum == 0x1f561e6a) { //calc_checksum("my_demo_password", true); 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | int main() 32 | { 33 | char str[14] = { 0 }; 34 | get_rand_string(str, 12); 35 | 36 | std::cout << str << std::endl; 37 | 38 | if (is_password_valid(str)) { 39 | MessageBoxA(NULL, "Passed!", "Test Case 3", MB_OK); 40 | } else { 41 | std::cout << "Failed!" << std::endl; 42 | MessageBoxA(NULL, "Failed!", "Test Case 3", MB_OK); 43 | } 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_case4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | project (test_case4) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | set(DELAYLOAD_FLAG "/DELAYLOAD:\"user32.dll\"" ) 6 | 7 | set (srcs 8 | main.cpp 9 | ) 10 | 11 | set (hdrs 12 | ) 13 | 14 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs} ) 15 | target_link_libraries(${PROJECT_NAME} "delayimp.lib" ) 16 | set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS " ${DELAYLOAD_FLAG}") 17 | 18 | #install 19 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 20 | -------------------------------------------------------------------------------- /tests/test_case4/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int popup_message1() 5 | { 6 | SYSTEMTIME SystemTime; 7 | GetSystemTime(&SystemTime); 8 | 9 | char pszDate[200]; 10 | GetDateFormatA( LOCALE_USER_DEFAULT, DATE_LONGDATE, &SystemTime, NULL, pszDate, 200 ); 11 | 12 | return MessageBoxA(NULL, pszDate, "Test Case 1", MB_OK); 13 | } 14 | 15 | int popup_message2() 16 | { 17 | return MessageBoxW(NULL, L"Checking wide strings", L"Test Case 1", MB_OK); 18 | } 19 | 20 | int main() 21 | { 22 | if (popup_message1() == 1337) { 23 | if (popup_message2() == 1338) { 24 | return MessageBox(NULL, TEXT("Hooking test passed"), TEXT("Test Case 1"), MB_OK); 25 | } 26 | } 27 | printf("Test Case 4 finished\n"); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /tests/test_case5/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | project (test_case5_exe) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | set (srcs 7 | main.cpp 8 | ) 9 | 10 | set (hdrs 11 | ) 12 | 13 | # libs 14 | add_subdirectory (test_case5_dll) 15 | include_directories ( test_case5_dll/include ) 16 | 17 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs} ) 18 | target_link_libraries(${PROJECT_NAME} "test_case5_dll" ) 19 | 20 | #install 21 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 22 | -------------------------------------------------------------------------------- /tests/test_case5/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "api.h" 4 | 5 | int main() 6 | { 7 | std::cout << "Test Case 5 started..." << std::endl; 8 | DWORD checks = test_checksum1(); 9 | checks += test_checksum2(); 10 | checks += test_checksum3(); 11 | checks += test_checksum4(); 12 | checks += test_checksum5(); 13 | 14 | std::cout << "Test Case 5 finished, checks: " << std::hex << checks << std::endl; 15 | return checks; 16 | } 17 | -------------------------------------------------------------------------------- /tests/test_case5/test_case5_dll/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 2.8...3.21 ) 2 | project (test_case5_dll) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | include_directories ( include ) 6 | 7 | set (srcs 8 | main.cpp 9 | ) 10 | 11 | set (dll_hdrs 12 | include/api.h 13 | ) 14 | 15 | add_library ( ${PROJECT_NAME} SHARED ${dll_hdrs} ${srcs} main.def) 16 | 17 | #install 18 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 19 | -------------------------------------------------------------------------------- /tests/test_case5/test_case5_dll/include/api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef DLL_EXPORTS 4 | #define DLL_API __declspec(dllexport) __stdcall 5 | #else 6 | #define DLL_API __declspec(dllimport) __stdcall 7 | #endif 8 | 9 | int DLL_API test_checksum1(); 10 | int DLL_API test_checksum2(); 11 | int DLL_API test_checksum3(); 12 | int DLL_API test_checksum4(); 13 | int DLL_API test_checksum5(); 14 | -------------------------------------------------------------------------------- /tests/test_case5/test_case5_dll/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define DLL_EXPORTS 5 | #include "api.h" 6 | 7 | //#define SHOW_MSGBOX 8 | 9 | inline DWORD rotl32a(DWORD x, DWORD n) 10 | { 11 | return (x << n) | (x >> (32 - n)); 12 | } 13 | 14 | inline char to_lower(char c) 15 | { 16 | if (c >= 'A' && c <= 'Z') { 17 | c = c - 'A' + 'a'; 18 | } 19 | return c; 20 | } 21 | 22 | DWORD calc_checksum(BYTE *str, size_t buf_size, bool enable_tolower) 23 | { 24 | if (str == NULL) return 0; 25 | 26 | DWORD checksum = 0; 27 | for (size_t i = 0; i < buf_size; i++) { 28 | checksum = rotl32a(checksum, 7); 29 | char c = str[i]; 30 | if (enable_tolower) { 31 | c = to_lower(c); 32 | } 33 | checksum ^= c; 34 | } 35 | return checksum; 36 | } 37 | 38 | int DLL_API test_checksum1() 39 | { 40 | char test1[] = "this is a test!"; 41 | DWORD checks = calc_checksum((BYTE*)test1, strlen(test1), true); 42 | std::cout << "Checks 1: " << std::hex << checks << std::endl; 43 | return checks; 44 | } 45 | 46 | int DLL_API test_checksum2() 47 | { 48 | wchar_t teststr[] = L"Checking wide strings"; 49 | DWORD checks = calc_checksum((BYTE*)teststr, sizeof(teststr), true); 50 | #ifdef SHOW_MSGBOX 51 | MessageBoxW(NULL, teststr, L"Test Case 5", MB_OK); 52 | #endif 53 | std::cout << "Checks 2: " << std::hex << checks << std::endl; 54 | return checks; 55 | } 56 | 57 | int DLL_API test_checksum4() 58 | { 59 | wchar_t teststr[] = L"Test checksum 4"; 60 | DWORD checks = calc_checksum((BYTE*)teststr, sizeof(teststr), true); 61 | #ifdef SHOW_MSGBOX 62 | MessageBoxW(NULL, teststr, L"Test Case 5", MB_OK); 63 | #endif 64 | std::cout << "Checks 4: " << std::hex << checks << std::endl; 65 | return checks; 66 | } 67 | 68 | int DLL_API test_checksum5() 69 | { 70 | wchar_t teststr[] = L"Yet another checksum test: 5"; 71 | DWORD checks = calc_checksum((BYTE*)teststr, sizeof(teststr), true); 72 | #ifdef SHOW_MSGBOX 73 | MessageBoxW(NULL, teststr, L"Test Case 5", MB_OK); 74 | #endif 75 | std::cout << "Checks 5: " << std::hex << checks << std::endl; 76 | return checks; 77 | } 78 | 79 | int DLL_API test_checksum3() 80 | { 81 | SYSTEMTIME SystemTime; 82 | GetSystemTime(&SystemTime); 83 | 84 | TCHAR pszDate[200]; 85 | GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &SystemTime, NULL, pszDate, 200); 86 | 87 | wchar_t teststr[] = L"Time func checksum"; 88 | DWORD checks = calc_checksum((BYTE*)teststr, sizeof(teststr), true); 89 | std::cout << "Checks 3: " << std::hex << checks << std::endl; 90 | #ifdef SHOW_MSGBOX 91 | MessageBox(NULL, pszDate, TEXT("Test Case 5"), MB_OK); 92 | #endif 93 | return checks; 94 | } 95 | 96 | BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 97 | { 98 | switch (fdwReason) 99 | { 100 | case DLL_PROCESS_ATTACH: 101 | printf("Test Case 5 DLL loaded\n"); 102 | break; 103 | case DLL_THREAD_ATTACH: 104 | case DLL_THREAD_DETACH: 105 | case DLL_PROCESS_DETACH: 106 | break; 107 | } 108 | return TRUE; 109 | } 110 | -------------------------------------------------------------------------------- /tests/test_case5/test_case5_dll/main.def: -------------------------------------------------------------------------------- 1 | LIBRARY test_case5_dll 2 | EXPORTS 3 | test_checksum1 4 | test_checksum2 @2 NONAME 5 | test_checksum3 6 | test_checksum4 @4 NONAME 7 | test_checksum5 8 | -------------------------------------------------------------------------------- /tests/test_case6/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | project (test_case6) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") 5 | 6 | set (srcs 7 | main.cpp 8 | sockets.cpp 9 | ) 10 | 11 | set (hdrs 12 | callback.h 13 | main.h 14 | sockets.h 15 | ) 16 | 17 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs} ) 18 | 19 | 20 | #install 21 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 22 | -------------------------------------------------------------------------------- /tests/test_case6/callback.h: -------------------------------------------------------------------------------- 1 | /** 2 | Example based on: White Rabbit crackme, stage 2 3 | more info: https://hshrzd.wordpress.com/2018/02/03/white-rabbit-crackme/ 4 | */ 5 | #pragma once 6 | #include 7 | #include 8 | #include 9 | #include "main.h" 10 | 11 | //#define TEST_WITH_SOCKETS - for manual tests 12 | 13 | #ifdef TEST_WITH_SOCKETS 14 | #include "sockets.h" 15 | #endif 16 | 17 | char junk_buf[0x100] = { 0 }; 18 | 19 | inline int junk_code() { 20 | srand(GetTickCount()); 21 | for (int i = 0; i < 10; i++) { 22 | junk_buf[i] = rand(); 23 | } 24 | return rand(); 25 | } 26 | 27 | bool check_condition(char buf[10], int number) 28 | { 29 | #ifdef TEST_WITH_SOCKETS 30 | return listen_for_connect(buf, number); 31 | #else 32 | switch (buf[0]) { 33 | case 0: 34 | buf[0] = '9'; break; 35 | case '9': 36 | buf[0] = '3'; break; 37 | case '3': 38 | buf[0] = '5'; break; 39 | default: 40 | buf[0] = 0; 41 | } 42 | return true; 43 | #endif 44 | } 45 | 46 | void NTAPI tls_callback1(PVOID DllHandle, DWORD dwReason, PVOID arg) 47 | { 48 | std::cout << __FUNCTION__ << ": TLS callback: dwReason: " << dwReason << "\n"; 49 | size_t pos = junk_code(); 50 | char pass[100] = { 0 }; 51 | junk_code(); 52 | if (strnlen(g_Pass, sizeof(g_Pass)) >= 10) return; 53 | size_t indx = 0; 54 | size_t indx2 = 1; 55 | char buf[10] = { 0 }; 56 | //"NR7YcqGFUn0"; 57 | if (check_condition(buf, 1337)) { 58 | pass[indx] = 0x87 - buf[0]; 59 | indx += 2; 60 | pass[indx2] = 0x8b - buf[0]; 61 | indx2 += 2; 62 | pass[indx] = 0x70 - buf[0]; 63 | indx += 2; 64 | pass[indx] = 0x9c - buf[0]; 65 | indx += 2; 66 | } 67 | 68 | junk_code(); 69 | junk_code(); 70 | junk_code(); 71 | if (check_condition(buf, 1338)) { 72 | pass[indx2] = 0x8c - buf[0]; 73 | indx2 += 2; 74 | pass[indx2] = 0xa4 - buf[0]; //'q' 75 | indx2 += 2; 76 | pass[indx] = 0x7a - buf[0]; //'G' 77 | indx += 2; 78 | } 79 | 80 | junk_code(); 81 | if (check_condition(buf, 1339)) { //"FUn0" 82 | pass[8] = 0x8a - buf[0]; //'U' 83 | junk_code(); 84 | pass[7] = 0x7b - buf[0]; //'F' 85 | pass[10] = 0x65 - buf[0]; // '0' 86 | junk_code(); 87 | pass[9] = 0xa3 - buf[0]; //'n' 88 | } 89 | 90 | g_pass_mutex = CreateMutexA(NULL, TRUE, NULL); 91 | WaitForSingleObject(g_pass_mutex, INFINITE); 92 | //copy to global: 93 | const size_t size = sizeof(pass) > sizeof(g_Pass) ? sizeof(g_Pass) : sizeof(pass); 94 | memcpy(g_Pass, pass, size); 95 | ReleaseMutex(g_pass_mutex); 96 | 97 | std::cout << __FUNCTION__ << ": TLS callback: finished\n"; 98 | } 99 | 100 | void NTAPI tls_callback2(PVOID DllHandle, DWORD dwReason, PVOID arg) 101 | { 102 | std::cout << __FUNCTION__ << ": TLS callback: dwReason: " << dwReason << "\n"; 103 | std::cout << __FUNCTION__ << ": TLS callback: finished\n"; 104 | } 105 | 106 | #ifdef _WIN64 107 | #pragma comment (linker, "/INCLUDE:_tls_used") 108 | #pragma comment (linker, "/INCLUDE:tls_callback_func1") 109 | #else 110 | #pragma comment (linker, "/INCLUDE:__tls_used") 111 | #pragma comment (linker, "/INCLUDE:_tls_callback_func1") 112 | #endif 113 | 114 | #ifdef _WIN64 115 | #pragma const_seg(".CRT$XLF") 116 | EXTERN_C const 117 | #else 118 | #pragma data_seg(".CRT$XLF") 119 | EXTERN_C 120 | #endif 121 | PIMAGE_TLS_CALLBACK tls_callback_func1 = tls_callback1; 122 | PIMAGE_TLS_CALLBACK tls_callback_func2 = tls_callback2; 123 | #ifdef _WIN64 124 | #pragma const_seg() 125 | #else 126 | #pragma data_seg() 127 | #endif //_WIN64 128 | -------------------------------------------------------------------------------- /tests/test_case6/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "callback.h" 6 | 7 | std::string get_pass() 8 | { 9 | std::string result = ""; 10 | while (g_pass_mutex == nullptr) { 11 | Sleep(10); 12 | } 13 | g_pass_mutex = CreateMutexA(NULL, FALSE, NULL); 14 | WaitForSingleObject(g_pass_mutex, INFINITE); 15 | result = g_Pass; 16 | ReleaseMutex(g_pass_mutex); 17 | return result; 18 | } 19 | 20 | int main() 21 | { 22 | std::cout << "Test case 6: Entry Point called!" << std::endl; 23 | std::cout << "Password: " << get_pass() << std::endl; 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/test_case6/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | char g_Pass[MAX_PATH] = { 0 }; 6 | HANDLE g_pass_mutex = nullptr; 7 | -------------------------------------------------------------------------------- /tests/test_case6/sockets.cpp: -------------------------------------------------------------------------------- 1 | #include "sockets.h" 2 | 3 | #pragma comment(lib,"Ws2_32.lib") 4 | #include 5 | 6 | bool switch_state(char *buf, char *resp) 7 | { 8 | switch (resp[0]) { 9 | case 0: 10 | if (buf[0] != '9') break; 11 | resp[0] = 'Y'; 12 | return true; 13 | case 'Y': 14 | if (buf[0] != '3') break; 15 | resp[0] = 'E'; 16 | return true; 17 | case 'E': 18 | if (buf[0] != '5') break; 19 | resp[0] = 'S'; 20 | return true; 21 | default: 22 | resp[0] = 0; break; 23 | } 24 | return false; 25 | } 26 | 27 | bool listen_for_connect(char buf[CONN_BUF_SIZE], int port) 28 | { 29 | bool got_resp = false; 30 | static char resp[4] = { 0 }; 31 | WSADATA wsaData = { 0 }; 32 | if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { 33 | return false; 34 | } 35 | struct sockaddr_in sock_config = { 0 }; 36 | 37 | SOCKET listen_socket = 0; 38 | if ((listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { 39 | std::cerr << "Creating the socket failed!\n"; 40 | WSACleanup(); 41 | return false; 42 | } 43 | sock_config.sin_family = AF_INET; 44 | const char *localhost = "127.0.0.1"; 45 | sock_config.sin_addr.s_addr = inet_addr(localhost); 46 | sock_config.sin_port = htons(port); 47 | 48 | if (bind(listen_socket, (SOCKADDR*)&sock_config, sizeof(sock_config)) != SOCKET_ERROR 49 | && listen(listen_socket, SOMAXCONN) != SOCKET_ERROR 50 | ) 51 | { 52 | std::cout << "The socket is listening...\n"; 53 | SOCKET conn_sock = SOCKET_ERROR; 54 | while ((conn_sock = accept(listen_socket, 0, 0)) != SOCKET_ERROR) { 55 | if (recv(conn_sock, buf, CONN_BUF_SIZE, 0) > 0) { 56 | got_resp = true; 57 | if (switch_state(buf, resp)) { 58 | send(conn_sock, resp, CONN_BUF_SIZE, 0); 59 | closesocket(conn_sock); 60 | break; 61 | } 62 | } 63 | closesocket(conn_sock); 64 | } 65 | } 66 | else { 67 | std::cerr << "Binding the socket failed!\n"; 68 | } 69 | closesocket(listen_socket); 70 | WSACleanup(); 71 | return got_resp; 72 | } 73 | -------------------------------------------------------------------------------- /tests/test_case6/sockets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define CONN_BUF_SIZE 4 5 | 6 | bool listen_for_connect(char buf[CONN_BUF_SIZE], int port); 7 | -------------------------------------------------------------------------------- /tests/test_case7/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | project (test_case7) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | set (srcs 7 | main.cpp 8 | ) 9 | 10 | set (hdrs 11 | ) 12 | 13 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs} ) 14 | 15 | #install 16 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 17 | -------------------------------------------------------------------------------- /tests/test_case7/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void make_exception1() 5 | { 6 | std::cout << __FUNCTION__ << ": Throwing exception:" << std::endl; 7 | __try { 8 | RaiseException(STATUS_BREAKPOINT, 0, 0, 0); 9 | } 10 | __except (EXCEPTION_EXECUTE_HANDLER) { 11 | std::cout << "Exception handled: STATUS_BREAKPOINT" << std::endl; 12 | } 13 | } 14 | 15 | void make_exception2() 16 | { 17 | std::cout << __FUNCTION__ << ": Throwing exception:" << std::endl; 18 | __try { 19 | RaiseException(STATUS_INTEGER_DIVIDE_BY_ZERO, 0, 0, 0); 20 | } 21 | __except (EXCEPTION_EXECUTE_HANDLER) { 22 | std::cout << "Exception handled: STATUS_INTEGER_DIVIDE_BY_ZERO" << std::endl; 23 | } 24 | } 25 | 26 | 27 | int main() 28 | { 29 | make_exception1(); 30 | make_exception2(); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /tests/test_crackme_f4_3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "test_crackme_f4_3.h" 5 | 6 | #include "resource.h" 7 | 8 | #include "peconv.h" 9 | 10 | namespace test3 { 11 | BYTE *g_Buffer = NULL; 12 | const size_t g_BufferLen = 0x79; 13 | 14 | BYTE g_Buffer2[g_BufferLen] = { 0 }; 15 | 16 | WORD (*calc_checksum) (BYTE *decoded_buffer, size_t buf_size) = NULL; 17 | 18 | bool test_val(BYTE xor_val) 19 | { 20 | for (size_t i = 0; i < g_BufferLen; i++) { 21 | BYTE val = g_Buffer[i]; 22 | g_Buffer2[i] = (xor_val ^ val) + 0x22; 23 | } 24 | WORD checksum = calc_checksum(g_Buffer2, g_BufferLen); 25 | if (checksum == 0xfb5e) { 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | BYTE brutforce() 32 | { 33 | BYTE xor_val = 0; 34 | do { 35 | xor_val++; 36 | } while (!test_val(xor_val)); 37 | return xor_val; 38 | } 39 | }; 40 | 41 | //--- 42 | 43 | int tests::brutforce_crackme_f4_3() 44 | { 45 | #ifdef _WIN64 46 | printf("Compile the loader as 32bit!\n"); 47 | return 0; 48 | #endif 49 | BYTE* loaded_pe = NULL; 50 | size_t v_size = 0; 51 | 52 | { //scope1 53 | size_t raw_size = 0; 54 | BYTE *raw_crackme = peconv::load_resource_data(raw_size, CRACKME_F4_3_32); 55 | if (!raw_crackme) { 56 | return -1; 57 | } 58 | loaded_pe = peconv::load_pe_module(raw_crackme, raw_size, v_size, true, false); 59 | if (!loaded_pe) { 60 | peconv::free_resource_data(raw_crackme); 61 | return -1; 62 | } 63 | peconv::free_resource_data(raw_crackme); 64 | }//!scope1 65 | 66 | test3::g_Buffer = (BYTE*) (0x107C + (ULONGLONG) loaded_pe); 67 | 68 | ULONGLONG func_offset = 0x11e6 + (ULONGLONG) loaded_pe; 69 | test3::calc_checksum = ( WORD (*) (BYTE *, size_t ) ) func_offset; 70 | 71 | BYTE found = test3::brutforce(); 72 | printf("Found: %x\n", found); 73 | int res = -1; 74 | if (found == 0xa2) { 75 | res = 0; 76 | } 77 | peconv::free_pe_buffer(loaded_pe, v_size); 78 | return res; 79 | } 80 | 81 | //For now this is for manual tests only: 82 | int tests::deploy_crackme_f4_3(peconv::t_function_resolver* func_resolver) 83 | { 84 | #ifdef _WIN64 85 | printf("Compile the loader as 32bit!\n"); 86 | return 0; 87 | #endif 88 | BYTE* loaded_pe = NULL; 89 | size_t v_size = 0; 90 | 91 | { //scope1 92 | size_t raw_size = 0; 93 | BYTE *raw_crackme = peconv::load_resource_data(raw_size, CRACKME_F4_3_32); 94 | if (!raw_crackme) { 95 | return -1; 96 | } 97 | loaded_pe = peconv::load_pe_executable(raw_crackme, raw_size, v_size, func_resolver); 98 | if (!loaded_pe) { 99 | peconv::free_resource_data(raw_crackme); 100 | return -1; 101 | } 102 | peconv::free_resource_data(raw_crackme); 103 | }//!scope1 104 | 105 | test3::g_Buffer = (BYTE*) (0x107C + (ULONGLONG) loaded_pe); 106 | 107 | ULONGLONG func_offset = 0x11e6 + (ULONGLONG) loaded_pe; 108 | test3::calc_checksum = ( WORD (*) (BYTE *, size_t ) ) func_offset; 109 | 110 | BYTE found = test3::brutforce(); 111 | printf("Found: %x\n", found); 112 | int res = -1; 113 | if (found != 0xa2) { 114 | peconv::free_pe_buffer(loaded_pe, v_size); 115 | return -1; 116 | } 117 | ULONGLONG ep_va = peconv::get_entry_point_rva(loaded_pe) + (ULONGLONG) loaded_pe; 118 | printf("Press any key to go to function's entry point\n"); 119 | system("pause"); 120 | //make pointer to the entry function: 121 | int (*loaded_pe_entry)(void) = (int (*)(void)) ep_va; 122 | res = loaded_pe_entry(); 123 | printf("Finished: %d\n", res); 124 | 125 | peconv::free_pe_buffer(loaded_pe, v_size); 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /tests/test_crackme_f4_3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peconv.h" 4 | 5 | namespace tests { 6 | // Loads the FlareOn4 Crackme 3, brutforces the key value using a function imported from the crackme and verifies it 7 | int brutforce_crackme_f4_3(); 8 | 9 | //For now this is for manual tests only: 10 | int deploy_crackme_f4_3(peconv::t_function_resolver* func_resolver); 11 | 12 | }; //namespace tests -------------------------------------------------------------------------------- /tests/test_crackme_f4_6.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peconv.h" 4 | 5 | namespace tests { 6 | 7 | int decode_crackme_f4_6(LPCTSTR path); 8 | 9 | }; //namespace tests -------------------------------------------------------------------------------- /tests/test_delayed_imps.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "test_delayed_imps.h" 5 | 6 | #include "peconv.h" 7 | using namespace peconv; 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace test9 { 14 | int _stdcall my_MessageBoxA( 15 | _In_opt_ HWND hWnd, 16 | _In_opt_ LPCSTR lpText, 17 | _In_opt_ LPCSTR lpCaption, 18 | _In_ UINT uType) 19 | { 20 | std::cout << "TITLE: [" << lpCaption << "]" << std::endl; 21 | std::cout << "MESSAGE: [" << lpText << "]" << std::endl; 22 | return 1337; 23 | } 24 | 25 | int _stdcall my_MessageBoxW( 26 | _In_opt_ HWND hWnd, 27 | _In_opt_ LPCWSTR lpText, 28 | _In_opt_ LPCWSTR lpCaption, 29 | _In_ UINT uType) 30 | { 31 | std::wcout << L"TITLE: [" << lpCaption << L"]" << std::endl; 32 | std::wcout << L"MESSAGE: [" << lpText << L"]" << std::endl; 33 | return 1338; 34 | } 35 | }; 36 | 37 | int tests::replace_delayed_imps(LPCTSTR path) 38 | { 39 | if (path == NULL) { 40 | std::cerr << "Supply the path to the app" << std::endl; 41 | return -1; 42 | } 43 | std::tcout << TEXT("Trying to load: ") << path << std::endl; 44 | size_t v_size = 0; 45 | 46 | peconv::hooking_func_resolver my_res; 47 | my_res.add_hook("MessageBoxA", (FARPROC)&test9::my_MessageBoxA); 48 | my_res.add_hook("MessageBoxW", (FARPROC)&test9::my_MessageBoxW); 49 | BYTE* loaded_pe = peconv::load_pe_executable(path, v_size); 50 | if (!loaded_pe) { 51 | return -1; 52 | } 53 | if (!peconv::load_delayed_imports(loaded_pe, (ULONGLONG)loaded_pe, (peconv::t_function_resolver*) &my_res)) { 54 | std::cout << "Failed loading delayed functions!" << std::endl; 55 | peconv::free_pe_buffer(loaded_pe, v_size); 56 | return -1; 57 | } 58 | ULONGLONG ep_exp_offset = (ULONGLONG)loaded_pe + peconv::get_entry_point_rva(loaded_pe); 59 | void(_cdecl *ep_func)() = (void(_cdecl *)()) (ep_exp_offset); 60 | std::cout << "Calling entry point:" << std::endl; 61 | ep_func(); 62 | peconv::free_pe_buffer(loaded_pe, v_size); 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /tests/test_delayed_imps.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peconv.h" 4 | 5 | namespace tests { 6 | 7 | int replace_delayed_imps(LPCTSTR path); 8 | 9 | }; //namespace tests 10 | -------------------------------------------------------------------------------- /tests/test_exceptions.cpp: -------------------------------------------------------------------------------- 1 | #include "test_exceptions.h" 2 | 3 | #include 4 | using namespace peconv; 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int tests::test_load_with_exception_table(LPCTSTR path) 11 | { 12 | if (path == NULL) { 13 | std::cerr << "Supply the path to the app" << std::endl; 14 | return -1; 15 | } 16 | std::wcout << "Trying to load: " << path << std::endl; 17 | size_t v_size = 0; 18 | 19 | BYTE* loaded_pe = peconv::load_pe_executable(path, v_size); 20 | if (!loaded_pe) { 21 | return -1; 22 | } 23 | std::wcout << "Trying to set up exceptions: " << path << std::endl; 24 | if (!peconv::setup_exceptions(loaded_pe, v_size)) { 25 | std::cerr << "[+] Failed to add the exception table\n"; 26 | } 27 | else { 28 | #ifdef _DEBUG 29 | std::wcout << "[+] The exception table was added\n"; 30 | #endif 31 | } 32 | std::wcout << __FUNCTION__ << ": Throwing exception:" << std::endl; 33 | __try { 34 | peconv::run_tls_callbacks(loaded_pe, v_size); 35 | 36 | ULONGLONG ep_exp_offset = (ULONGLONG)loaded_pe + peconv::get_entry_point_rva(loaded_pe); 37 | void(_cdecl *ep_func)() = (void(_cdecl *)()) (ep_exp_offset); 38 | std::wcout << "Calling entry point:" << std::endl; 39 | ep_func(); 40 | } 41 | __except (EXCEPTION_EXECUTE_HANDLER) { 42 | std::wcout << "Exception captured by the caller" << std::endl; 43 | } 44 | peconv::free_pe_buffer(loaded_pe, v_size); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_exceptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tests { 6 | 7 | int test_load_with_exception_table(LPCTSTR path); 8 | 9 | }; //namespace tests 10 | -------------------------------------------------------------------------------- /tests/test_fix_dotnet.cpp: -------------------------------------------------------------------------------- 1 | #include "test_fix_dotnet.h" 2 | 3 | #include 4 | #include "..\\libpeconv\src\fix_dot_net_ep.h" 5 | 6 | namespace tests { 7 | 8 | bool test_finding_offset(BYTE *buf, const size_t buf_size, size_t pattern_offset, bool direction_start=true) 9 | { 10 | const BYTE pattern[] = { 0xFF, 0x25, 0x00, 0x20, 0x40, 0x00 }; 11 | const ULONGLONG img_base = 0x400000; 12 | const DWORD cor_exe_main_thunk = 0x2000; 13 | 14 | // reset buffer: 15 | memset(buf, 0, buf_size); 16 | 17 | if (!direction_start) { 18 | pattern_offset = (buf_size - sizeof(pattern)) - pattern_offset; 19 | } 20 | if (buf_size < sizeof(pattern) || (pattern_offset + sizeof(pattern)) > buf_size) { 21 | std::cerr << __FILE__ << " incorrect test data!" << std::endl; 22 | return false; 23 | } 24 | memcpy(buf + pattern_offset, pattern, sizeof(pattern)); 25 | 26 | BYTE* found = search_jump(buf, buf_size, cor_exe_main_thunk, img_base); 27 | if (!found) { 28 | std::cout << "Not found!\n"; 29 | return false; 30 | } 31 | size_t diff = found - buf; 32 | std::cout << "Fount at offset: " << std::hex << diff << "\n"; 33 | if (diff == pattern_offset) { 34 | return true; 35 | } 36 | return false; 37 | } 38 | }; 39 | 40 | int tests::check_finding_jumps() 41 | { 42 | BYTE buf[0x100] = { 0 }; 43 | 44 | bool is_ok = test_finding_offset(buf, sizeof(buf), 0, true); 45 | if (is_ok) { 46 | std::cout << "Test 1 passed!\n"; 47 | } else { 48 | std::cout << "Test 1 failed!\n"; 49 | return 1; 50 | } 51 | is_ok = test_finding_offset(buf, sizeof(buf), 0, false); 52 | if (is_ok) { 53 | std::cout << "Test 2 passed!\n"; 54 | } 55 | else { 56 | std::cout << "Test 2 failed!\n"; 57 | return 1; 58 | } 59 | 60 | is_ok = test_finding_offset(buf, sizeof(buf), 30, false); 61 | if (is_ok) { 62 | std::cout << "Test 3 passed!\n"; 63 | } 64 | else { 65 | std::cout << "Test 2 failed!\n"; 66 | return 1; 67 | } 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /tests/test_fix_dotnet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tests { 6 | 7 | int check_finding_jumps(); 8 | 9 | }; //namespace tests 10 | -------------------------------------------------------------------------------- /tests/test_format_detect.cpp: -------------------------------------------------------------------------------- 1 | #include "test_format_detect.h" 2 | 3 | using namespace peconv; 4 | 5 | void printFromat(bool isRaw) 6 | { 7 | if (isRaw) { 8 | std::cout << "PE is in the RAW format\n"; 9 | } 10 | else { 11 | std::cout << "PE is in the VIRTUAL format\n"; 12 | } 13 | } 14 | 15 | int tests::check_pe_format(LPCTSTR my_path) 16 | { 17 | size_t pe_size = 0; 18 | std::cout << "Module: " << my_path << "\n"; 19 | // Load the current executable from the file with the help of libpeconv: 20 | BYTE* loaded_pe = peconv::load_file(my_path, pe_size); 21 | if (!loaded_pe) { 22 | std::cout << "Loading failed!\n"; 23 | return -1; 24 | } 25 | bool isRaw = peconv::is_pe_raw(loaded_pe, pe_size); 26 | bool isRaw2 = false; 27 | if (isRaw) { 28 | size_t v_size = 0; 29 | BYTE* virtual_pe = peconv::load_pe_module(my_path, v_size, false, false); 30 | if (!virtual_pe) { 31 | std::cout << "Mapping failed!\n"; 32 | return -1; 33 | } 34 | isRaw2 = peconv::is_pe_raw(virtual_pe, v_size); 35 | peconv::free_pe_buffer(virtual_pe); 36 | } 37 | peconv::free_pe_buffer(loaded_pe); 38 | 39 | std::cout << "Test 1:\n\t"; 40 | printFromat(isRaw); 41 | std::cout << "Test 2:\n\t"; 42 | printFromat(isRaw2); 43 | if (isRaw && !isRaw2) { 44 | return 0; // status OK 45 | } 46 | return 1; 47 | } 48 | -------------------------------------------------------------------------------- /tests/test_format_detect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tests { 6 | 7 | // check if the supplied PE is in raw or virtual format 8 | int check_pe_format(LPCTSTR path); 9 | 10 | }; //namespace tests 11 | -------------------------------------------------------------------------------- /tests/test_found_base.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "test_found_base.h" 6 | 7 | using namespace peconv; 8 | 9 | int tests::load_and_check_base(LPCTSTR path) 10 | { 11 | if (!path) { 12 | return -1; 13 | } 14 | size_t v_size = 0; 15 | BYTE* pe = peconv::load_pe_module(path, v_size, false, true); 16 | if (!pe) { 17 | return -2; 18 | } 19 | std::cout << "Loaded at: " < 5 | #include 6 | #include 7 | 8 | #include "test_hooking_imps.h" 9 | 10 | namespace test5 { 11 | int _stdcall my_MessageBoxA( 12 | _In_opt_ HWND hWnd, 13 | _In_opt_ LPCSTR lpText, 14 | _In_opt_ LPCSTR lpCaption, 15 | _In_ UINT uType) 16 | { 17 | std::cout << "TITLE: [" << lpCaption << "]" << std::endl; 18 | std::cout << "MESSAGE: [" << lpText << "]" < 4 | using namespace peconv; 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define FAKE_NAME "fake_module_name" 11 | 12 | namespace test11 { 13 | int _stdcall my_MessageBoxA( 14 | _In_opt_ HWND hWnd, 15 | _In_opt_ LPCSTR lpText, 16 | _In_opt_ LPCSTR lpCaption, 17 | _In_ UINT uType) 18 | { 19 | std::cout << "TITLE: [" << lpCaption << "]" << std::endl; 20 | std::cout << "MESSAGE: [" << lpText << "]" << std::endl; 21 | return 1337; 22 | } 23 | 24 | int _stdcall my_MessageBoxW( 25 | _In_opt_ HWND hWnd, 26 | _In_opt_ LPCWSTR lpText, 27 | _In_opt_ LPCWSTR lpCaption, 28 | _In_ UINT uType) 29 | { 30 | std::wcout << L"TITLE: [" << lpCaption << L"]" << std::endl; 31 | std::wcout << L"MESSAGE: [" << lpText << L"]" << std::endl; 32 | return 1338; 33 | } 34 | 35 | int __cdecl my_rand(void) 36 | { 37 | return 44; 38 | } 39 | 40 | DWORD 41 | WINAPI 42 | my_GetModuleFileNameA( 43 | IN OPTIONAL HMODULE hModule, 44 | OUT LPSTR lpFilename, 45 | IN DWORD nSize 46 | ) 47 | { 48 | const char fake_name[] = FAKE_NAME; 49 | size_t to_copy = strlen(fake_name); 50 | if (to_copy < nSize) to_copy = nSize; 51 | 52 | memcpy(lpFilename, fake_name, to_copy); 53 | return to_copy; 54 | } 55 | }; 56 | 57 | int tests::hook_self_local() 58 | { 59 | char normal_name[MAX_PATH] = { 0 }; 60 | GetModuleFileNameA(NULL, normal_name, MAX_PATH); 61 | 62 | HMODULE user32_lib = LoadLibraryA("user32.dll"); 63 | HMODULE kernel32_lib = LoadLibraryA("kernel32.dll"); 64 | PatchBackup backup; 65 | 66 | if ((user32_lib == nullptr) || (kernel32_lib == nullptr)) { 67 | std::cout << "Failed!"; 68 | return -5; 69 | } 70 | 71 | peconv::redirect_to_local(GetProcAddress(user32_lib, "MessageBoxA"), &test11::my_MessageBoxA); 72 | peconv::redirect_to_local(GetProcAddress(kernel32_lib, "GetModuleFileNameA"), &test11::my_GetModuleFileNameA, &backup); 73 | peconv::redirect_to_local(rand, &test11::my_rand); 74 | 75 | char module_name[MAX_PATH] = { 0 }; 76 | GetModuleFileNameA(NULL, module_name, MAX_PATH); 77 | MessageBoxA(0, module_name, "Module Name", MB_OK); 78 | 79 | if (strcmp(FAKE_NAME, module_name) != 0) { 80 | std::cout << "Failed!"; 81 | return -1; 82 | } 83 | srand(10000); 84 | int rand_val = rand(); 85 | if (rand_val != 44) { 86 | std::cout << "Failed: " << rand_val << "\n"; 87 | return -2; 88 | } 89 | if (!backup.applyBackup()) { 90 | std::cout << "Failed! Cannot apply backup."; 91 | return -3; 92 | } 93 | GetModuleFileNameA(NULL, module_name, MAX_PATH); 94 | MessageBoxA(0, module_name, "Module Name", MB_OK); 95 | 96 | if (strcmp(normal_name, module_name) != 0) { 97 | std::cout << "Failed!"; 98 | return -4; 99 | } 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /tests/test_hooking_local.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tests { 6 | 7 | int hook_self_local(); 8 | 9 | }; //namespace tests 10 | -------------------------------------------------------------------------------- /tests/test_imp_list.cpp: -------------------------------------------------------------------------------- 1 | #include "test_imp_list.h" 2 | 3 | using namespace peconv; 4 | 5 | class ListImportThunks : public ImportThunksCallback 6 | { 7 | public: 8 | ListImportThunks(BYTE* _modulePtr, size_t _moduleSize) 9 | : ImportThunksCallback(_modulePtr, _moduleSize) 10 | { 11 | } 12 | 13 | virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr) 14 | { 15 | if (this->is64b) { 16 | IMAGE_THUNK_DATA64* desc = reinterpret_cast(origFirstThunkPtr); 17 | ULONGLONG* call_via = reinterpret_cast(firstThunkPtr); 18 | return processThunks_tpl(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG64); 19 | } 20 | IMAGE_THUNK_DATA32* desc = reinterpret_cast(origFirstThunkPtr); 21 | DWORD* call_via = reinterpret_cast(firstThunkPtr); 22 | return processThunks_tpl(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG32); 23 | } 24 | 25 | protected: 26 | template 27 | bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA* desc, T_FIELD* call_via, T_FIELD ordinal_flag) 28 | { 29 | ULONG_PTR call_via_rva = (ULONG_PTR)call_via - (ULONG_PTR)this->modulePtr; 30 | std::cout << "via RVA: " << std::hex << call_via_rva << " : " << lib_name << " : "; 31 | 32 | bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0; 33 | if (is_by_ord) { 34 | T_FIELD raw_ordinal = desc->u1.Ordinal & (~ordinal_flag); 35 | std::cout << "ord: " << raw_ordinal << std::endl; 36 | } 37 | else { 38 | PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData); 39 | LPSTR func_name = reinterpret_cast(by_name->Name); 40 | std::cout << "name: " << func_name << std::endl; 41 | } 42 | return true; 43 | } 44 | }; 45 | 46 | bool list_imports(IN BYTE* modulePtr, IN size_t moduleSize) 47 | { 48 | if (moduleSize == 0) { 49 | moduleSize = peconv::get_image_size((const BYTE*)modulePtr); 50 | } 51 | if (moduleSize == 0) return false; 52 | 53 | ListImportThunks callback(modulePtr, moduleSize); 54 | return peconv::process_import_table(modulePtr, moduleSize, &callback); 55 | } 56 | 57 | int tests::imp_list(LPCTSTR my_path) 58 | { 59 | size_t v_size = 0; 60 | std::tcout << TEXT("Module: ") << my_path << ("\n"); 61 | // Load the current executable from the file with the help of libpeconv: 62 | BYTE* loaded_pe = load_pe_module(my_path, v_size, true, true); 63 | if (!loaded_pe) { 64 | std::cout << "Loading failed!\n"; 65 | return -1; 66 | } 67 | 68 | bool is_ok = list_imports(loaded_pe, v_size); 69 | 70 | peconv::free_pe_buffer(loaded_pe); 71 | return is_ok ? 0 : 1; 72 | } 73 | -------------------------------------------------------------------------------- /tests/test_imp_list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tests { 6 | 7 | int imp_list(LPCTSTR path); 8 | 9 | }; //namespace tests 10 | -------------------------------------------------------------------------------- /tests/test_imports_mix.cpp: -------------------------------------------------------------------------------- 1 | #include "test_imports_mix.h" 2 | 3 | using namespace peconv; 4 | 5 | int tests::imports_mix(LPCTSTR my_path) 6 | { 7 | size_t v_size = 0; 8 | std::cout << "Module: " << my_path << "\n"; 9 | // Load the current executable from the file with the help of libpeconv: 10 | BYTE* loaded_pe = peconv::load_pe_executable(my_path, v_size); 11 | if (!loaded_pe) { 12 | std::cout << "Loading failed!\n"; 13 | return -1; 14 | } 15 | 16 | //calculate the Entry Point of the manually loaded module 17 | DWORD ep_rva = peconv::get_entry_point_rva(loaded_pe); 18 | if (!ep_rva) { 19 | return -2; 20 | } 21 | ULONG_PTR ep_va = ep_rva + (ULONG_PTR)loaded_pe; 22 | //assuming that the payload is an EXE file (not DLL) this will be the simplest prototype of the main: 23 | int(*new_main)() = (int(*)())ep_va; 24 | 25 | //call the Entry Point of the manually loaded PE: 26 | new_main(); 27 | peconv::free_pe_buffer(loaded_pe); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /tests/test_imports_mix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tests { 6 | 7 | int imports_mix(LPCTSTR my_path); 8 | 9 | }; //namespace tests 10 | -------------------------------------------------------------------------------- /tests/test_load_ntdll.cpp: -------------------------------------------------------------------------------- 1 | #include "test_load_ntdll.h" 2 | 3 | #include "peconv.h" 4 | 5 | #include 6 | #include "shellcodes.h" 7 | 8 | int (_cdecl *ntdll_tolower) (int) = NULL; 9 | 10 | NTSTATUS (NTAPI *ntdll_ZwAllocateVirtualMemory)( 11 | _In_ HANDLE ProcessHandle, 12 | _Inout_ PVOID *BaseAddress, 13 | _In_ ULONG_PTR ZeroBits, 14 | _Inout_ PSIZE_T RegionSize, 15 | _In_ ULONG AllocationType, 16 | _In_ ULONG Protect 17 | ) = NULL; 18 | 19 | //For now this is for manual tests only: 20 | int tests::test_ntdll(LPCTSTR path) 21 | { 22 | TCHAR ntdllPath[MAX_PATH]; 23 | ExpandEnvironmentStrings(TEXT("%SystemRoot%\\system32\\ntdll.dll"), ntdllPath, MAX_PATH); 24 | 25 | size_t v_size = 0; 26 | // NTDLL does not need any imports, so we can load it by load_pe_module 27 | // in a normal case we should use load_pe_executable 28 | BYTE *ntdll_module = peconv::load_pe_module(ntdllPath, v_size, true, true); 29 | if (!ntdll_module) { 30 | return -1; 31 | } 32 | bool is64 = peconv::is64bit(ntdll_module); 33 | std::cout << "NTDLL loaded, is64: " << is64 << std::endl; 34 | FARPROC n_offset = peconv::get_exported_func(ntdll_module, "tolower"); 35 | if (n_offset == NULL) { 36 | return -1; 37 | } 38 | std::cout << "Got tolower: " << n_offset << std::endl; 39 | ntdll_tolower = (int (_cdecl *) (int)) n_offset; 40 | int out = ntdll_tolower('C'); 41 | std::cout << "To lower char: " << (char) out << std::endl; 42 | 43 | n_offset = peconv::get_exported_func(ntdll_module, "ZwAllocateVirtualMemory"); 44 | if (n_offset == NULL) { 45 | return -1; 46 | } 47 | PVOID base_addr = 0; 48 | SIZE_T buffer_size = 0x200; 49 | ntdll_ZwAllocateVirtualMemory = (NTSTATUS (NTAPI *)(HANDLE, PVOID *, ULONG_PTR, PSIZE_T, ULONG, ULONG)) n_offset; 50 | NTSTATUS status = ntdll_ZwAllocateVirtualMemory( 51 | GetCurrentProcess(), &base_addr, 0, &buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE 52 | ); 53 | 54 | if (status != S_OK) { 55 | return -1; 56 | } 57 | printf("allocated: %p\n", base_addr); 58 | #ifndef _WIN64 59 | memcpy(base_addr, messageBox32bit_sc, sizeof(messageBox32bit_sc)); 60 | #else 61 | memcpy(base_addr, messageBox64bit_sc, sizeof(messageBox64bit_sc)); 62 | #endif 63 | void (*shellc)(void) = (void (*)(void))base_addr; 64 | shellc(); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /tests/test_load_ntdll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peconv.h" 4 | 5 | namespace tests { 6 | 7 | //For now this is for manual tests only: 8 | int test_ntdll(LPCTSTR path); 9 | 10 | }; //namespace tests -------------------------------------------------------------------------------- /tests/test_loading.cpp: -------------------------------------------------------------------------------- 1 | #include "test_loading.h" 2 | 3 | #include 4 | #include 5 | using namespace peconv; 6 | 7 | int tests::load_self() 8 | { 9 | TCHAR my_path[MAX_PATH] = { 0 }; 10 | GetModuleFileName(NULL, my_path, MAX_PATH); 11 | size_t v_size = 0; 12 | std::cout << "Module: " << my_path << "\n"; 13 | // Load the current executable from the file with the help of libpeconv: 14 | BYTE* loaded_pe = load_pe_module(my_path, v_size, true, true); 15 | if (!loaded_pe) { 16 | std::cout << "Loading failed!\n"; 17 | return -1; 18 | } 19 | 20 | printf("Loaded at: %p\n", loaded_pe); 21 | 22 | // Now try to unmap the loaded image using libpeconv: 23 | size_t raw_size = 0; 24 | BYTE* unmapped = pe_virtual_to_raw(loaded_pe, v_size, (ULONGLONG)loaded_pe, raw_size, true); 25 | if (!unmapped || raw_size == 0) { 26 | std::cout << "Unmapping failed!\n"; 27 | return -1; 28 | } 29 | std::cout << "Unmapped at:" << std::hex << (ULONG_PTR)unmapped << "\n"; 30 | 31 | //Read the original file and compare it with the unmapped module: 32 | size_t read_size = 0; 33 | BYTE* file_content = load_file(my_path, read_size); 34 | if (file_content == NULL) { 35 | printf("Reading file failed!\n"); 36 | return -1; 37 | } 38 | std::cout << "Read size: " << std::dec << read_size << "\n"; 39 | std::cout << "Unmapped size: " << std::dec << raw_size << "\n"; 40 | size_t smaller_size = raw_size < read_size ? raw_size : read_size; 41 | int res = memcmp(unmapped, file_content, smaller_size); 42 | if (loaded_pe) { 43 | free_pe_buffer(loaded_pe, v_size); 44 | free_pe_buffer(unmapped, raw_size); 45 | std::cout << "Unloaded!\n"; 46 | } 47 | free_file(file_content); 48 | if (res != 0) { 49 | std::cout << "Unmapped module is NOT the same as the original!\n"; 50 | } 51 | return res; 52 | } 53 | -------------------------------------------------------------------------------- /tests/test_loading.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peconv.h" 4 | 5 | namespace tests { 6 | 7 | int load_self(); 8 | 9 | }; //namespace tests 10 | -------------------------------------------------------------------------------- /tests/test_loading_imps.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "test_loading_imps.h" 5 | 6 | int tests::deploy_self_ex(peconv::t_function_resolver* func_resolver) 7 | { 8 | char marker_path[] = "peconv_test_marker"; 9 | DWORD current_pid = GetCurrentProcessId(); 10 | 11 | printf("My PID: %d\n", current_pid); 12 | printf("My ptr: %p\n", &deploy_self_ex); 13 | 14 | char my_env[MAX_PATH] = { 0 }; 15 | if (GetEnvironmentVariableA(marker_path, my_env, MAX_PATH)) { 16 | int pid = atoi(my_env); 17 | if (pid == current_pid) { 18 | printf("Second iteration: marker found\n"); 19 | } 20 | return 0; 21 | } else { 22 | printf("First iteration: marker not found\n"); 23 | } 24 | 25 | TCHAR my_path[MAX_PATH] = { 0 }; 26 | GetModuleFileName(NULL, my_path, MAX_PATH); 27 | size_t v_size = 0; 28 | std::tcout << TEXT("Module: ") << my_path << std::endl; 29 | // Load the current executable from the file with the help of libpeconv: 30 | BYTE* loaded_pe = peconv::load_pe_executable(my_path, v_size, func_resolver); 31 | ULONGLONG ep = peconv::get_entry_point_rva(loaded_pe) + (ULONGLONG) loaded_pe; 32 | LPVOID ep_ptr = (LPVOID) ep; 33 | 34 | // Deploy itself! 35 | // read the Entry Point from the headers: 36 | int (*loaded_pe_entry)(void); 37 | loaded_pe_entry = (int (*)(void)) ep_ptr; 38 | 39 | _itoa_s(current_pid, my_env, 10); 40 | if (SetEnvironmentVariableA(marker_path, my_env)) { 41 | printf ("Env marker set!\n"); 42 | } 43 | 44 | //call the loaded PE's ep: 45 | printf("Calling the Entry Point of the loaded module:\n"); 46 | int ret_val = loaded_pe_entry(); 47 | return ret_val; 48 | } 49 | 50 | int tests::deploy_self() 51 | { 52 | return tests::deploy_self_ex(NULL); 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_loading_imps.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peconv.h" 4 | 5 | namespace tests { 6 | 7 | // Get the path of the current module and loads it by the custom loader. Then, deploys the module and checks if it runs properly. 8 | int deploy_self(); 9 | 10 | int deploy_self_ex(peconv::t_function_resolver* func_resolver); 11 | 12 | }; //namespace tests 13 | -------------------------------------------------------------------------------- /tests/test_peb_lookup.cpp: -------------------------------------------------------------------------------- 1 | #include "test_peb_lookup.h" 2 | 3 | #include 4 | 5 | namespace tests { 6 | 7 | int compare_modules_and_sizes(wchar_t* module_name = NULL) 8 | { 9 | std::wcout << "\n[*] Test: "; 10 | if (module_name == NULL) { 11 | std::wcout << "self"; 12 | } 13 | else { 14 | std::wcout << module_name; 15 | LoadLibraryW(module_name); 16 | } 17 | std::wcout << "\n"; 18 | HMODULE mod1 = peconv::get_module_via_peb(module_name); 19 | HMODULE mod2 = GetModuleHandleW(module_name); 20 | std::cout << "get_module_via_peb: " << std::hex << mod1 << "\n"; 21 | std::cout << "GetModuleHandle: " << std::hex << mod2 << "\n"; 22 | if (mod1 != mod2) { 23 | return false; 24 | } 25 | 26 | size_t size1 = peconv::get_image_size((BYTE*)mod1); 27 | size_t size2 = peconv::get_module_size_via_peb(mod2); 28 | std::cout << "get_image_size: " << std::hex << size1 << "\n"; 29 | std::cout << "get_module_size_via_peb: " << std::hex << size2 << "\n"; 30 | if (size1 != size2) { 31 | return false; 32 | } 33 | return true; 34 | } 35 | 36 | bool check_unexisting_module() 37 | { 38 | std::wcout << "\n[*] Test: unexisting module\n"; 39 | 40 | wchar_t* module_name = L"unexisting_module"; 41 | HMODULE mod1 = peconv::get_module_via_peb(module_name); 42 | HMODULE mod2 = GetModuleHandleW(module_name); 43 | 44 | std::cout << "get_module_via_peb: " << std::hex << mod1 << "\n"; 45 | std::cout << "GetModuleHandle: " << std::hex << mod2 << "\n"; 46 | if (mod1 != mod2) { 47 | return false; 48 | } 49 | return true; 50 | } 51 | 52 | }; 53 | 54 | int tests::check_modules() 55 | { 56 | if (!compare_modules_and_sizes(NULL)) { 57 | return 1; 58 | } 59 | if (!compare_modules_and_sizes(L"ntdll.dll")) { 60 | return 1; 61 | } 62 | if (!compare_modules_and_sizes(L"kernel32.dll")) { 63 | return 1; 64 | } 65 | if (!compare_modules_and_sizes(L"user32.dll")) { 66 | return 1; 67 | } 68 | if (!compare_modules_and_sizes(L"advapi32.dll")) { 69 | return 1; 70 | } 71 | if (!compare_modules_and_sizes(L"ws2_32.dll")) { 72 | return 1; 73 | } 74 | if (!check_unexisting_module()) { 75 | return 1; 76 | } 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /tests/test_peb_lookup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tests { 6 | 7 | int check_modules(); 8 | 9 | }; //namespace tests 10 | -------------------------------------------------------------------------------- /tests/test_replacing_func.cpp: -------------------------------------------------------------------------------- 1 | #include "test_replacing_func.h" 2 | 3 | #include "peconv.h" 4 | using namespace peconv; 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace test8 { 12 | int _stdcall my_MessageBoxA( 13 | _In_opt_ HWND hWnd, 14 | _In_opt_ LPCSTR lpText, 15 | _In_opt_ LPCSTR lpCaption, 16 | _In_ UINT uType) 17 | { 18 | std::cout << "TITLE: [" << lpCaption << "]" << std::endl; 19 | std::cout << "MESSAGE: [" << lpText << "]" < 4 | using namespace peconv; 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int tests::test_load_with_tls_callbacks(LPCTSTR path) 11 | { 12 | 13 | if (path == NULL) { 14 | std::cerr << "Supply the path to the app" << std::endl; 15 | return -1; 16 | } 17 | std::cout << "Trying to load: " << path << std::endl; 18 | size_t v_size = 0; 19 | 20 | BYTE* loaded_pe = peconv::load_pe_executable(path, v_size); 21 | if (!loaded_pe) { 22 | return -1; 23 | } 24 | 25 | run_tls_callbacks(loaded_pe, v_size); 26 | 27 | ULONGLONG ep_exp_offset = (ULONGLONG) loaded_pe + peconv::get_entry_point_rva(loaded_pe); 28 | void (_cdecl *ep_func)() = (void (_cdecl *)()) (ep_exp_offset); 29 | std::cout << "Calling entry point:" <