├── .appveyor.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── injector ├── CMakeLists.txt ├── main.cpp ├── util.cpp └── util.h ├── loader_v1 ├── README.md ├── hldr32 │ ├── LICENSE.md │ ├── hldr32.asm │ ├── hldr32.inc │ ├── install.bat │ └── make.bat └── hldr64 │ ├── LICENSE.md │ ├── hldr64.asm │ ├── hldr64.inc │ ├── install.bat │ └── make.bat ├── loader_v2 ├── .gitignore ├── README.md ├── clean.bat ├── make_peloader32.bat ├── make_peloader64.bat ├── peb_lookup.h ├── peloader.cpp └── peloader.h ├── pe2shc ├── CMakeLists.txt ├── LICENSE ├── main.cpp ├── resource.h ├── resource1.rc ├── resource2.rc ├── stub1 │ ├── stub32.bin │ └── stub64.bin └── stub2 │ ├── stub32.bin │ └── stub64.bin ├── runshc ├── CMakeLists.txt ├── LICENSE └── main.cpp └── tests ├── CMakeLists.txt ├── dcp_test ├── CMakeLists.txt └── main.cpp ├── test_case1 ├── CMakeLists.txt └── main.cpp └── test_case2_dll ├── CMakeLists.txt ├── main.cpp └── main.def /.appveyor.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - Visual Studio 2015 3 | 4 | platform: x64 5 | - x64 6 | 7 | branches: 8 | only: 9 | - master 10 | 11 | install: 12 | - git submodule update --init --recursive 13 | - set PATH=C:\Program Files\CMake\bin;%PATH% 14 | 15 | build: 16 | verbosity: detailed 17 | 18 | configuration: 19 | - Release 20 | - Debug 21 | 22 | environment: 23 | artifactName: $(APPVEYOR_PROJECT_NAME)-$(APPVEYOR_REPO_COMMIT)-$(CONFIGURATION) 24 | matrix: 25 | - env_arch: "x64" 26 | - env_arch: "x86" 27 | 28 | before_build: 29 | - mkdir build 30 | - cd build 31 | - if [%env_arch%]==[x64] ( 32 | cmake .. -A x64 -DPE2SHC_BUILD_TESTING=ON ) 33 | - if [%env_arch%]==[x86] ( 34 | cmake .. -DPE2SHC_BUILD_TESTING=ON ) 35 | - cmake -DCMAKE_INSTALL_PREFIX:PATH=%APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT% .. 36 | 37 | build_script: 38 | - cmake --build . --config %CONFIGURATION% --target install 39 | - ctest -V 40 | 41 | after_build: 42 | - mkdir %artifactName% 43 | - cp %APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT%/* %artifactName% 44 | 45 | artifacts: 46 | - path: build\%artifactName% 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.aps 2 | *.exe 3 | *.bak 4 | 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libpeconv"] 2 | path = libpeconv 3 | url = https://github.com/hasherezade/libpeconv.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | 3 | # replace "peconv_project" by your own project name: 4 | project ( pe_to_shellcode ) 5 | 6 | option (PE2SHC_BUILD_TESTING "enable testing" OFF) 7 | 8 | # libs 9 | # modules: 10 | set ( M_PARSER "libpeconv/libpeconv" ) 11 | 12 | # modules paths: 13 | set (PECONV_DIR "${CMAKE_SOURCE_DIR}/${M_PARSER}" CACHE PATH "PEConv main path") 14 | add_subdirectory ( ${PECONV_DIR} ) 15 | set ( PECONV_LIB $ CACHE PATH "PEConvLib library path" ) 16 | 17 | # Add sub-directories 18 | # 19 | add_subdirectory ( pe2shc ) 20 | add_subdirectory ( runshc ) 21 | add_subdirectory ( injector ) 22 | 23 | # Setup testing 24 | if(PE2SHC_BUILD_TESTING) 25 | enable_testing() 26 | add_subdirectory ( tests ) 27 | endif() 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018-2020, hasherezade 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pe_to_shellcode 2 | [![Build status](https://ci.appveyor.com/api/projects/status/w3dy81u0k3up7459?svg=true)](https://ci.appveyor.com/project/hasherezade/pe-to-shellcode) 3 | [![GitHub release](https://img.shields.io/github/release/hasherezade/pe_to_shellcode.svg)](https://github.com/hasherezade/pe_to_shellcode/releases) 4 | [![Github All Releases](https://img.shields.io/github/downloads/hasherezade/pe_to_shellcode/total.svg)](https://github.com/hasherezade/pe_to_shellcode/releases) 5 | [![Github Latest Release](https://img.shields.io/github/downloads/hasherezade/pe_to_shellcode/latest/total.svg)](https://github.com/hasherezade/pe_to_shellcode/releases) 6 | 7 | Converts PE so that it can be then injected just like a normal shellcode.
8 | (At the same time, the output file remains to be a valid PE).
9 | Supports both 32 and 64 bit PEs 10 | 11 | *Authors: [@hasherezade](https://github.com/hasherezade) & [@hh86](https://github.com/86hh)* 12 | 13 | Objective 14 | - 15 | The goal of this project is to provide a possibility to generate PE files that can be injected with minimal effort. 16 | It is inspired by Stephen Fewer's [ReflectiveDLLInjection](https://github.com/stephenfewer/ReflectiveDLLInjection) - but the difference is that with pe2shc you can add the reflective loading stub post-compilation. Also, the header of the PE file is modified in such a way, that you can start executing the injected buffer from the very beginning - just like you would do with a shellcode. It will automatically find the stub, and continue loading the full PE. 17 | 18 | Scope of the project 19 | - 20 | 🟢 The stub supports only basic structures of PE format, such as: 21 | + relocations 22 | + imports 23 | + TLS callbacks (called once, before the Entry Point is executed) 24 | 25 | Please keep in mind, that although for the majority of PE files this is sufficient, some executables you encounter may be using other, more complex aspects of the PE format. It means, **not every PE can be successfuly converted to a shellcode**. 26 | 27 | 🚫 Examples of currently not supported elements: 28 | + exceptions (if the executable you converted will be run as a shellcode, and throw the exception, the appropriate exception handler will not be found, and the application will crash) 29 | + Delay Load Imports (only the basic Import Table support is implemented) 30 | + MUI files (if the executable you converted expects some elements of the GUI have to be loaded from a MUI file, it won't work) 31 | 32 | Builds 33 | - 34 | 📦 ⚙️ Download the latest [release](https://github.com/hasherezade/pe_to_shellcode/releases). 35 | 36 | Clone 37 | - 38 | Use recursive clone to get the repo together with all the submodules: 39 | 40 | ```console 41 | git clone --recursive https://github.com/hasherezade/pe_to_shellcode.git 42 | ``` 43 | 44 | How to use it 45 | - 46 | 1. Use **pe2shc.exe** to convert a PE of your choice: 47 | ``` 48 | pe2shc.exe [output path*] 49 | * - optional 50 | ``` 51 | If the PE was successfuly converted, **pe2shc** will let you know where the output was saved: 52 | ``` 53 | [+] Saved to file: 54 | ``` 55 | i.e. 56 | ``` 57 | [+] Saved to file: test_file.shc.exe 58 | ``` 59 | 2. Use **runshc.exe**(*) to run the output file and check if the conversion went fine. 60 | ``` 61 | runshc.exe 62 | ``` 63 | 64 | (*)Warning: remember to use the version of **runshc** with a bitness appropriate to your converted application (32 or 64 bit) - otherwise the application will crash! 65 | 66 | 3. If the file runs as the original PE, it confirms that the conversion was successful!
67 | Now you can use the converted PE just like you would use a shellcode: inject it to a target and execute from the beginning of the buffer. No additional PE loaders are required.
68 | At the same time, you can keep using the converted file as a regular PE. 69 | -------------------------------------------------------------------------------- /injector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | project (injector) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | set (srcs 7 | main.cpp 8 | util.cpp 9 | ) 10 | 11 | set (hdrs 12 | util.h 13 | ) 14 | 15 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs}) 16 | 17 | if(PE2SHC_BUILD_TESTING) 18 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 19 | endif() 20 | -------------------------------------------------------------------------------- /injector/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "util.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | if (argc < 3) { 8 | std::cout << "~ injector " 9 | #ifdef _WIN64 10 | << "(64-bit)" 11 | #else 12 | << "(32-bit)" 13 | #endif 14 | << " ~\n" 15 | << "Loads shellcode from a file, and injects it into a process with a given PID.\n"; 16 | 17 | std::cout << "Args: " << std::endl; 18 | system("pause"); 19 | return 0; 20 | } 21 | 22 | char *path = argv[1]; 23 | int pid = atoi(argv[2]); 24 | size_t shc_size = 0; 25 | BYTE *shellcode = util::load_file(path, shc_size); 26 | if (!shellcode) { 27 | std::cerr << "Could not load the shellcode file\n"; 28 | return -1; 29 | } 30 | std::cout << "Injecting to: " << pid << "\n"; 31 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 32 | if (hProcess == NULL) { 33 | std::cerr << "[ERROR] Could not open process : " << std::hex << GetLastError() << std::endl; 34 | return -1; 35 | } 36 | LPVOID remote_buf = VirtualAllocEx(hProcess, NULL, shc_size, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); 37 | if (remote_buf == NULL) { 38 | std::cerr << "[ERROR] Could not allocate a remote buffer : " << std::hex << GetLastError() << std::endl; 39 | return -1; 40 | } 41 | if (!WriteProcessMemory(hProcess, remote_buf, shellcode, shc_size, NULL)) { 42 | std::cerr << "[ERROR] WriteProcessMemory failed, status : " << std::hex << GetLastError() << std::endl; 43 | return -1; 44 | } 45 | HANDLE hMyThread = NULL; 46 | DWORD threadId = 0; 47 | if ((hMyThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)remote_buf, NULL, 0, &threadId)) == NULL) { 48 | std::cerr << "[ERROR] CreateRemoteThread failed, status : " << std::hex << GetLastError() << std::endl; 49 | return -1; 50 | } 51 | std::cout << "Injected, created Thread, id = " << threadId << "\n"; 52 | CloseHandle(hMyThread); 53 | CloseHandle(hProcess); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /injector/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | 4 | BYTE* util::alloc_aligned(size_t buffer_size, DWORD protect, ULONGLONG desired_base) 5 | { 6 | if (!buffer_size) return NULL; 7 | 8 | BYTE* buf = (BYTE*)VirtualAlloc((LPVOID)desired_base, buffer_size, MEM_COMMIT | MEM_RESERVE, protect); 9 | return buf; 10 | } 11 | 12 | bool util::free_aligned(BYTE* buffer) 13 | { 14 | if (buffer == nullptr) return true; 15 | if (!VirtualFree(buffer, 0, MEM_RELEASE)) { 16 | #ifdef _DEBUG 17 | std::cerr << "Releasing failed" << std::endl; 18 | #endif 19 | return false; 20 | } 21 | return true; 22 | } 23 | 24 | BYTE* util::load_file(IN const char *filename, OUT size_t &read_size) 25 | { 26 | HANDLE file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 27 | if (file == INVALID_HANDLE_VALUE) { 28 | #ifdef _DEBUG 29 | std::cerr << "Could not open file!" << std::endl; 30 | #endif 31 | return nullptr; 32 | } 33 | HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0); 34 | if (!mapping) { 35 | #ifdef _DEBUG 36 | std::cerr << "Could not create mapping!" << std::endl; 37 | #endif 38 | CloseHandle(file); 39 | return nullptr; 40 | } 41 | BYTE *dllRawData = (BYTE*)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); 42 | if (!dllRawData) { 43 | #ifdef _DEBUG 44 | std::cerr << "Could not map view of file" << std::endl; 45 | #endif 46 | CloseHandle(mapping); 47 | CloseHandle(file); 48 | return nullptr; 49 | } 50 | size_t r_size = GetFileSize(file, 0); 51 | if (read_size != 0 && read_size <= r_size) { 52 | r_size = read_size; 53 | } 54 | if (IsBadReadPtr(dllRawData, r_size)) { 55 | std::cerr << "[-] Mapping of " << filename << " is invalid!" << std::endl; 56 | UnmapViewOfFile(dllRawData); 57 | CloseHandle(mapping); 58 | CloseHandle(file); 59 | return nullptr; 60 | } 61 | BYTE* localCopyAddress = alloc_aligned(r_size, PAGE_READWRITE); 62 | if (localCopyAddress != nullptr) { 63 | memcpy(localCopyAddress, dllRawData, r_size); 64 | read_size = r_size; 65 | } 66 | else { 67 | read_size = 0; 68 | #ifdef _DEBUG 69 | std::cerr << "Could not allocate memory in the current process" << std::endl; 70 | #endif 71 | } 72 | UnmapViewOfFile(dllRawData); 73 | CloseHandle(mapping); 74 | CloseHandle(file); 75 | return localCopyAddress; 76 | } 77 | 78 | void util::free_file(BYTE* buffer) 79 | { 80 | free_aligned(buffer); 81 | } 82 | -------------------------------------------------------------------------------- /injector/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace util { 7 | 8 | BYTE* alloc_aligned(size_t buffer_size, DWORD protect, ULONGLONG desired_base=0); 9 | 10 | bool free_aligned(BYTE* buffer); 11 | 12 | BYTE* load_file(IN const char *filename, OUT size_t &read_size); 13 | 14 | void free_file(BYTE* buffer); 15 | } 16 | -------------------------------------------------------------------------------- /loader_v1/README.md: -------------------------------------------------------------------------------- 1 | Loader is the part dedicated to manual loading of a PE. It is automatically added to your executable during the process of conversion (shellcodification). So, when you use pe_to_shellcode, two main things are done to your exe: 2 | 1) the loader is appended 3 | 2) the header is modified, to redirect the execution to the loader - thanks to this, after the conversion the PE can be injected and executed starting from its beginning. 4 | 5 | Loader is built separately from the main executable. 6 | + Buiding requires [YASM](https://yasm.tortall.net/) 7 | + Run `make.bat`, appropriately for 32 and 64 bit version of the loader to compile 8 | + Run `install.bat`, to copy compiled module into the code directory of the main application. Now the main application can be compiled with the newly created loader. 9 | 10 | -------------------------------------------------------------------------------- /loader_v1/hldr32/LICENSE.md: -------------------------------------------------------------------------------- 1 | stub32.bin: CC-BY hh86 2 | -------------------------------------------------------------------------------- /loader_v1/hldr32/hldr32.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | %include "hldr32.inc" 3 | 4 | ;----------------------------------------------------------------------------- 5 | ;recover kernel32 image base 6 | ;----------------------------------------------------------------------------- 7 | 8 | hldr_begin: 9 | pushad ;must save ebx/edi/esi/ebp 10 | push tebProcessEnvironmentBlock 11 | pop eax 12 | fs mov eax, dword [eax] 13 | mov eax, dword [eax + pebLdr] 14 | mov esi, dword [eax + ldrInLoadOrderModuleList] 15 | lodsd 16 | xchg eax, esi 17 | lodsd 18 | mov ebp, dword [eax + mlDllBase] 19 | call parse_exports 20 | 21 | ;----------------------------------------------------------------------------- 22 | ;API CRC table, null terminated 23 | ;----------------------------------------------------------------------------- 24 | 25 | dd 0C97C1FFFh ;GetProcAddress 26 | dd 03FC1BD8Dh ;LoadLibraryA 27 | db 0 28 | 29 | ;----------------------------------------------------------------------------- 30 | ;parse export table 31 | ;----------------------------------------------------------------------------- 32 | 33 | parse_exports: 34 | pop esi 35 | mov ebx, ebp 36 | mov eax, dword [ebp + lfanew] 37 | add ebx, dword [ebp + eax + IMAGE_DIRECTORY_ENTRY_EXPORT] 38 | cdq 39 | 40 | walk_names: 41 | mov eax, ebp 42 | mov edi, ebp 43 | inc edx 44 | add eax, dword [ebx + _IMAGE_EXPORT_DIRECTORY.edAddressOfNames] 45 | add edi, dword [eax + edx * 4] 46 | or eax, -1 47 | 48 | crc_outer: 49 | xor al, byte [edi] 50 | push 8 51 | pop ecx 52 | 53 | crc_inner: 54 | shr eax, 1 55 | jnc crc_skip 56 | xor eax, 0edb88320h 57 | 58 | crc_skip: 59 | loop crc_inner 60 | inc edi 61 | cmp byte [edi], cl 62 | jne crc_outer 63 | not eax 64 | cmp dword [esi], eax 65 | jne walk_names 66 | 67 | ;----------------------------------------------------------------------------- 68 | ;exports must be sorted alphabetically, otherwise GetProcAddress() would fail 69 | ;this allows to push addresses onto the stack, and the order is known 70 | ;----------------------------------------------------------------------------- 71 | 72 | mov edi, ebp 73 | mov eax, ebp 74 | add edi, dword [ebx + _IMAGE_EXPORT_DIRECTORY.edAddressOfNameOrdinals] 75 | movzx edi, word [edi + edx * 2] 76 | add eax, dword [ebx + _IMAGE_EXPORT_DIRECTORY.edAddressOfFunctions] 77 | mov eax, dword [eax + edi * 4] 78 | add eax, ebp 79 | push eax 80 | lodsd 81 | sub cl, byte [esi] 82 | jnz walk_names 83 | 84 | ;----------------------------------------------------------------------------- 85 | ;save the pointers to the PE structure 86 | ;----------------------------------------------------------------------------- 87 | 88 | mov esi, dword [esp + krncrcstk_size + 20h + 4] 89 | mov ebp, dword [esi + lfanew] 90 | add ebp, esi 91 | 92 | push esi 93 | mov ebx, esp 94 | mov edi, esi 95 | 96 | ;----------------------------------------------------------------------------- 97 | ;import DLL 98 | ;----------------------------------------------------------------------------- 99 | 100 | pushad 101 | mov cl, IMAGE_DIRECTORY_ENTRY_IMPORT 102 | mov ebp, dword [ecx + ebp] 103 | test ebp, ebp ;check if PE has import table 104 | je import_popad ;if import table not found, skip loading 105 | add ebp, edi 106 | 107 | import_dll: 108 | mov ecx, dword [ebp + _IMAGE_IMPORT_DESCRIPTOR.idName] 109 | jecxz import_popad 110 | add ecx, dword [ebx] 111 | push ecx 112 | call dword [ebx + mapstk_size + krncrcstk.kLoadLibraryA] 113 | xchg ecx, eax 114 | mov edi, dword [ebp + _IMAGE_IMPORT_DESCRIPTOR.idFirstThunk] 115 | mov esi, dword [ebp + _IMAGE_IMPORT_DESCRIPTOR.idOriginalFirstThunk] 116 | test esi, esi ;if OriginalFirstThunk is NULL... 117 | cmove esi, edi ;use FirstThunk instead of OriginalFirstThunk 118 | add esi, dword [ebx] 119 | add edi, dword [ebx] 120 | 121 | import_thunks: 122 | lodsd 123 | test eax, eax 124 | je import_next 125 | btr eax, 31 126 | jc import_push 127 | add eax, dword [ebx] 128 | inc eax 129 | inc eax 130 | 131 | import_push: 132 | push ecx 133 | push eax 134 | push ecx 135 | call dword [ebx + mapstk_size + krncrcstk.kGetProcAddress] 136 | pop ecx 137 | stosd 138 | jmp import_thunks 139 | 140 | import_next: 141 | add ebp, _IMAGE_IMPORT_DESCRIPTOR_size 142 | jmp import_dll 143 | 144 | import_popad: 145 | popad 146 | 147 | ;----------------------------------------------------------------------------- 148 | ;apply relocations 149 | ;----------------------------------------------------------------------------- 150 | 151 | mov cl, IMAGE_DIRECTORY_ENTRY_RELOCS 152 | lea edx, dword [ebp + ecx] ;relocation entry in data directory 153 | add edi, dword [edx] 154 | xor ecx, ecx 155 | 156 | reloc_block: 157 | pushad 158 | mov ecx, dword [edi + IMAGE_BASE_RELOCATION.reSizeOfBlock] 159 | sub ecx, IMAGE_BASE_RELOCATION_size 160 | cdq 161 | 162 | reloc_addr: 163 | movzx eax, word [edi + edx + IMAGE_BASE_RELOCATION_size] 164 | push eax 165 | and ah, 0f0h 166 | cmp ah, IMAGE_REL_BASED_HIGHLOW << 4 167 | pop eax 168 | jne reloc_abs ;another type not HIGHLOW 169 | and ah, 0fh 170 | add eax, dword [edi + IMAGE_BASE_RELOCATION.rePageRVA] 171 | add eax, dword [ebx] ;new base address 172 | mov esi, dword [eax] 173 | sub esi, dword [ebp + _IMAGE_NT_HEADERS.nthOptionalHeader + _IMAGE_OPTIONAL_HEADER.ohImageBasex] 174 | add esi, dword [ebx] 175 | mov dword [eax], esi 176 | xor eax, eax 177 | 178 | reloc_abs: 179 | test eax, eax ;check for IMAGE_REL_BASED_ABSOLUTE 180 | jne hldr_exit ;not supported relocation type 181 | inc edx 182 | inc edx 183 | cmp ecx, edx 184 | jg reloc_addr 185 | popad 186 | add ecx, dword [edi + IMAGE_BASE_RELOCATION.reSizeOfBlock] 187 | add edi, dword [edi + IMAGE_BASE_RELOCATION.reSizeOfBlock] 188 | cmp dword [edx + 4], ecx 189 | jg reloc_block 190 | 191 | ;----------------------------------------------------------------------------- 192 | ;call entrypoint 193 | ; 194 | ;to a DLL main: 195 | ;push 0 196 | ;push 1 197 | ;push dword [ebx] 198 | ;mov eax, dword [ebp + _IMAGE_NT_HEADERS.nthOptionalHeader + _IMAGE_OPTIONAL_HEADER.ohAddressOfEntryPoint] 199 | ;add eax, dword [ebx] 200 | ;call eax 201 | ; 202 | ;to a RVA (an exported function's RVA, for example): 203 | ; 204 | ;mov eax, 0xdeadf00d ; replace with addr 205 | ;add eax, dword [ebx] 206 | ;call eax 207 | ;----------------------------------------------------------------------------- 208 | 209 | xor ecx, ecx 210 | mov eax, dword [ebp + _IMAGE_NT_HEADERS.nthOptionalHeader + _IMAGE_OPTIONAL_HEADER.ohAddressOfEntryPoint] 211 | add eax, dword [ebx] 212 | call eax 213 | 214 | ;----------------------------------------------------------------------------- 215 | ;if fails or returns from host, restore stack and registers and return (somewhere) 216 | ;----------------------------------------------------------------------------- 217 | 218 | hldr_exit: 219 | lea esp, dword [ebx + mapstk_size + krncrcstk_size] 220 | mov dword [esp+0x1c], eax ; write the current EAX value on the stack in order to preserve it 221 | popad 222 | ret 4 223 | hldr_end: 224 | 225 | -------------------------------------------------------------------------------- /loader_v1/hldr32/hldr32.inc: -------------------------------------------------------------------------------- 1 | CREATE_ALWAYS equ 2 2 | FILE_WRITE_DATA equ 2 3 | 4 | PAGE_EXECUTE_READWRITE equ 40h 5 | 6 | MEM_COMMIT equ 1000h 7 | MEM_RESERVE equ 2000h 8 | 9 | tebProcessEnvironmentBlock equ 30h 10 | pebLdr equ 0ch 11 | ldrInLoadOrderModuleList equ 0ch 12 | mlDllBase equ 18h 13 | 14 | lfanew equ 3ch 15 | 16 | IMAGE_DIRECTORY_ENTRY_EXPORT equ 78h 17 | IMAGE_DIRECTORY_ENTRY_IMPORT equ 80h 18 | IMAGE_DIRECTORY_ENTRY_RELOCS equ 0a0h 19 | 20 | IMAGE_REL_BASED_HIGHLOW equ 3 21 | 22 | struc mapstk 23 | .hImage: resd 1 24 | endstruc 25 | 26 | struc krncrcstk 27 | .kLoadLibraryA: resd 1 28 | .kGetProcAddress: resd 1 29 | endstruc 30 | 31 | struc _IMAGE_FILE_HEADER 32 | .fhMachine: resw 1 33 | .fhNumberOfSections: resw 1 34 | .fhTimeDateStamp: resd 1 35 | .fhPointerToSymbolTable: resd 1 36 | .fhNumberOfSymbols: resd 1 37 | .fhSizeOfOptionalHeader: resw 1 38 | .fhCharacteristics: resw 1 39 | endstruc 40 | 41 | struc _IMAGE_OPTIONAL_HEADER 42 | .ohMagic: resw 1 43 | .ohMajorLinkerVersion: resb 1 44 | .ohMinorLinkerVersion: resb 1 45 | .ohSizeOfCode: resd 1 46 | .ohSizeOfInitializedData: resd 1 47 | .ohSizeOfUninitializedData: resd 1 48 | .ohAddressOfEntryPoint: resd 1 49 | .ohBaseOfCode: resd 1 50 | .ohBaseOfData: resd 1 51 | .ohImageBasex: resd 1 52 | .ohSectionAlignment: resd 1 53 | .ohFileAlignment: resd 1 54 | .ohMajorOperatingSystemVersion: resw 1 55 | .ohMinorOperatingSystemVersion: resw 1 56 | .ohMajorImageVersion: resw 1 57 | .ohMinorImageVersion: resw 1 58 | .ohMajorSubsystemVersion: resw 1 59 | .ohMinorSubsystemVersion: resw 1 60 | .ohWin32VersionValue: resd 1 61 | .ohSizeOfImage: resd 1 62 | .ohSizeOfHeaders: resd 1 63 | endstruc 64 | 65 | struc _IMAGE_NT_HEADERS 66 | .nthSignature: resd 1 67 | .nthFileHeader: resb _IMAGE_FILE_HEADER_size 68 | .nthOptionalHeader: resb _IMAGE_OPTIONAL_HEADER_size 69 | endstruc 70 | 71 | struc _IMAGE_SECTION_HEADER 72 | .shName: resb 8 73 | .shVirtualSize: resd 1 74 | .shVirtualAddress: resd 1 75 | .shSizeOfRawData: resd 1 76 | .shPointerToRawData: resd 1 77 | .shPointerToRelocations: resd 1 78 | .shPointerToLinenumbers: resd 1 79 | .shNumberOfRelocations: resw 1 80 | .shNumberOfLinenumbers: resw 1 81 | .shCharacteristics: resd 1 82 | endstruc 83 | 84 | struc _IMAGE_IMPORT_DESCRIPTOR 85 | .idOriginalFirstThunk: resd 1 86 | .idTimeDateStamp: resd 1 87 | .idForwarderChain: resd 1 88 | .idName: resd 1 89 | .idFirstThunk: resd 1 90 | endstruc 91 | 92 | struc IMAGE_BASE_RELOCATION 93 | .rePageRVA: resd 1 94 | .reSizeOfBlock: resd 1 95 | endstruc 96 | 97 | struc _IMAGE_EXPORT_DIRECTORY 98 | .edCharacteristics: resd 1 99 | .edTimeDateStamp: resd 1 100 | .edMajorVersion: resw 1 101 | .edMinorVersion: resw 1 102 | .edName: resd 1 103 | .edBase: resd 1 104 | .edNumberOfFunctions: resd 1 105 | .edNumberOfNames: resd 1 106 | .edAddressOfFunctions: resd 1 107 | .edAddressOfNames: resd 1 108 | .edAddressOfNameOrdinals: resd 1 109 | endstruc -------------------------------------------------------------------------------- /loader_v1/hldr32/install.bat: -------------------------------------------------------------------------------- 1 | move stub32.bin ../../pe2shc/stub1/ 2 | -------------------------------------------------------------------------------- /loader_v1/hldr32/make.bat: -------------------------------------------------------------------------------- 1 | yasm -f bin hldr32.asm -o stub32.bin 2 | -------------------------------------------------------------------------------- /loader_v1/hldr64/LICENSE.md: -------------------------------------------------------------------------------- 1 | stub64.bin: CC-BY hh86 2 | -------------------------------------------------------------------------------- /loader_v1/hldr64/hldr64.asm: -------------------------------------------------------------------------------- 1 | bits 64 2 | 3 | %include "hldr64.inc" 4 | 5 | ;----------------------------------------------------------------------------- 6 | ;here begins HLDR64 7 | ;----------------------------------------------------------------------------- 8 | 9 | hldr64_begin: 10 | push rbx 11 | push rsi 12 | push rdi 13 | push r12 14 | push r8 15 | push r9 16 | regstksize equ 30h 17 | 18 | ;----------------------------------------------------------------------------- 19 | ;recover kernel32 image base 20 | ;----------------------------------------------------------------------------- 21 | 22 | push tebProcessEnvironmentBlock 23 | pop rsi 24 | gs lodsq 25 | mov rax, qword [rax + pebLdr] 26 | mov rsi, qword [rax + InMemoryOrderModuleList] 27 | lodsq 28 | xchg rax, rsi 29 | lodsq 30 | mov rbp, qword [rax + mDllBase] 31 | call parse_exports 32 | 33 | dd 0C97C1FFFh ;GetProcAddress 34 | dd 03FC1BD8Dh ;LoadLibraryA 35 | db 0 36 | 37 | ;----------------------------------------------------------------------------- 38 | ;parse export table 39 | ;----------------------------------------------------------------------------- 40 | 41 | parse_exports: 42 | pop rsi 43 | mov eax, dword [rbp + lfanew] 44 | mov ebx, dword [rbp + rax + IMAGE_DIRECTORY_ENTRY_EXPORT] 45 | add rbx, rbp 46 | cdq 47 | 48 | walk_names: 49 | inc edx 50 | mov eax, dword [rbx + _IMAGE_EXPORT_DIRECTORY.edAddressOfNames] 51 | add rax, rbp 52 | mov edi, dword [rax + rdx * 4] 53 | add rdi, rbp 54 | or eax, -1 55 | 56 | crc_outer: 57 | xor al, byte [rdi] 58 | push 8 59 | pop rcx 60 | 61 | crc_inner: 62 | shr eax, 1 63 | jnc crc_skip 64 | xor eax, 0edb88320h 65 | 66 | crc_skip: 67 | loop crc_inner 68 | inc rdi 69 | cmp byte [rdi], cl 70 | jne crc_outer 71 | not eax 72 | cmp dword [rsi], eax 73 | jne walk_names 74 | 75 | ;----------------------------------------------------------------------------- 76 | ;exports must be sorted alphabetically, otherwise GetProcAddress() would fail 77 | ;this allows to push addresses onto the stack, and the order is known 78 | ;----------------------------------------------------------------------------- 79 | 80 | mov edi, dword [rbx + _IMAGE_EXPORT_DIRECTORY.edAddressOfNameOrdinals] 81 | add rdi, rbp 82 | movzx edi, word [rdi + rdx * 2] 83 | mov eax, dword [rbx + _IMAGE_EXPORT_DIRECTORY.edAddressOfFunctions] 84 | add rax, rbp 85 | mov eax, dword [rax + rdi * 4] 86 | add rax, rbp 87 | push rax 88 | lodsd 89 | sub cl, byte [rsi] 90 | jne walk_names 91 | 92 | ;----------------------------------------------------------------------------- 93 | ;allocate space for mapstk, and make stack frame 94 | ;allocate space for shadow stack only once then align stack because at this time 95 | ;we don't know if aligned for sure 96 | ;----------------------------------------------------------------------------- 97 | 98 | push rcx 99 | push rcx 100 | push rsp 101 | pop rbx 102 | sub rsp, 40h ;only 20h bytes required for shadow stack 103 | and rsp, -10h ;align on 16-byte boundary 104 | 105 | ;----------------------------------------------------------------------------- 106 | ;save the pointers to the PE structure 107 | ;----------------------------------------------------------------------------- 108 | 109 | mov rsi, qword [rbx + mapstk_size + krncrcstk_size + regstksize + 8] 110 | mov ebp, dword [rsi + lfanew] 111 | add rbp, rsi 112 | mov rax, rsi 113 | mov qword [rbx], rsi 114 | mov rdi, rsi 115 | 116 | ;----------------------------------------------------------------------------- 117 | ;import DLL 118 | ;----------------------------------------------------------------------------- 119 | 120 | mov r12, rbp 121 | mov cl, IMAGE_DIRECTORY_ENTRY_IMPORT 122 | mov ebp, dword [rcx + rbp] 123 | test ebp, ebp ;check if PE has import table 124 | je import_poprbp ;if import table not found, skip loading 125 | add rbp, rax 126 | 127 | import_dll: 128 | mov ecx, dword [rbp + _IMAGE_IMPORT_DESCRIPTOR.idName] 129 | jecxz import_poprbp 130 | add rcx, qword [rbx] 131 | call qword [rbx + mapstk_size + krncrcstk.kLoadLibraryA] 132 | mov qword [rbx + mapstk.hModule], rax 133 | mov edi, dword [rbp + _IMAGE_IMPORT_DESCRIPTOR.idFirstThunk] 134 | mov esi, dword [rbp + _IMAGE_IMPORT_DESCRIPTOR.idOriginalFirstThunk] 135 | test esi, esi 136 | cmove esi, edi ;if OriginalFirstThunk is NULL, esi = edi = FirstThunk 137 | add rdi, qword [rbx] 138 | add rsi, qword [rbx] 139 | add rbp, _IMAGE_IMPORT_DESCRIPTOR_size 140 | 141 | import_thunks: 142 | lodsq 143 | test rax, rax 144 | je import_dll 145 | btr rax, 63 146 | jc import_mov 147 | add rax, qword [rbx] 148 | inc rax 149 | inc rax 150 | 151 | import_mov: 152 | push rax 153 | pop rdx 154 | mov rcx, qword [rbx + mapstk.hModule] 155 | call qword [rbx + mapstk_size + krncrcstk.kGetProcAddress] 156 | stosq 157 | jmp import_thunks 158 | 159 | import_poprbp: 160 | mov rbp, r12 ;restore because r12 uses prefix 161 | 162 | ;----------------------------------------------------------------------------- 163 | ;apply relocations 164 | ;----------------------------------------------------------------------------- 165 | 166 | mov edi, dword [rbp + IMAGE_DIRECTORY_ENTRY_RELOCS] 167 | add rdi, qword [rbx] 168 | 169 | reloc_block: 170 | push IMAGE_BASE_RELOCATION_size 171 | pop rdx 172 | 173 | reloc_addr: 174 | movzx rax, word [rdi + rdx] 175 | push rax 176 | and ah, 0f0h 177 | cmp ah, IMAGE_REL_BASED_DIR64 << 4 178 | pop rax 179 | jne reloc_abs ;another type not DIR64 180 | and ah, 0fh 181 | add eax, dword [rdi + IMAGE_BASE_RELOCATION.rePageRVA] 182 | add rax, qword [rbx] ;new base address 183 | mov rsi, qword [rax] 184 | sub rsi, qword [rbp + _IMAGE_NT_HEADERS.nthOptionalHeader + _IMAGE_OPTIONAL_HEADER.ohImageBasex] 185 | add rsi, qword [rbx] 186 | mov qword [rax], rsi 187 | xor eax, eax 188 | 189 | reloc_abs: 190 | test eax, eax ;check for IMAGE_REL_BASED_ABSOLUTE 191 | jne hldr_exit ;not supported relocation type 192 | inc edx 193 | inc edx 194 | cmp dword [rdi + IMAGE_BASE_RELOCATION.reSizeOfBlock], edx 195 | jg reloc_addr 196 | add ecx, edx 197 | add rdi, rdx 198 | cmp dword [rbp + IMAGE_DIRECTORY_ENTRY_RELOCS + 4], ecx 199 | jg reloc_block 200 | 201 | reloc_finished: 202 | ;----------------------------------------------------------------------------- 203 | ;call entrypoint 204 | ;----------------------------------------------------------------------------- 205 | mov eax, dword [rbp + _IMAGE_NT_HEADERS.nthOptionalHeader + _IMAGE_OPTIONAL_HEADER.ohAddressOfEntryPoint] 206 | add rax, qword [rbx] 207 | call rax 208 | 209 | ;----------------------------------------------------------------------------- 210 | ;if fails or returns from host, restore stack and registers and return (somewhere) 211 | ;----------------------------------------------------------------------------- 212 | 213 | hldr_exit: 214 | lea rsp, qword [rbx + mapstk_size + krncrcstk_size] 215 | pop r9 216 | pop r8 217 | pop r12 218 | pop rdi 219 | pop rsi 220 | pop rbx 221 | ret 8 222 | 223 | -------------------------------------------------------------------------------- /loader_v1/hldr64/hldr64.inc: -------------------------------------------------------------------------------- 1 | CREATE_ALWAYS equ 2 2 | FILE_WRITE_DATA equ 2 3 | 4 | PAGE_EXECUTE_READWRITE equ 40h 5 | 6 | MEM_COMMIT equ 1000h 7 | MEM_RESERVE equ 2000h 8 | 9 | tebProcessEnvironmentBlock equ 60h 10 | pebImagebase equ 10h 11 | pebLdr equ 18h 12 | InMemoryOrderModuleList equ 20h 13 | mDllBase equ 20h 14 | 15 | lfanew equ 3ch 16 | 17 | IMAGE_DIRECTORY_ENTRY_EXPORT equ 88h 18 | IMAGE_DIRECTORY_ENTRY_IMPORT equ 90h 19 | IMAGE_DIRECTORY_ENTRY_RELOCS equ 0b0h 20 | 21 | IMAGE_REL_BASED_DIR64 equ 0ah 22 | 23 | struc mapstk 24 | .hBaseImage: resq 1 25 | .hModule: resq 1 26 | endstruc 27 | 28 | struc krncrcstk 29 | .kLoadLibraryA: resq 1 30 | .kGetProcAddress: resq 1 31 | endstruc 32 | 33 | struc _IMAGE_FILE_HEADER 34 | .fhMachine: resw 1 35 | .fhNumberOfSections: resw 1 36 | .fhTimeDateStamp: resd 1 37 | .fhPointerToSymbolTable: resd 1 38 | .fhNumberOfSymbols: resd 1 39 | .fhSizeOfOptionalHeader: resw 1 40 | .fhCharacteristics: resw 1 41 | endstruc 42 | 43 | struc _IMAGE_OPTIONAL_HEADER 44 | .ohMagic: resw 1 45 | .ohMajorLinkerVersion: resb 1 46 | .ohMinorLinkerVersion: resb 1 47 | .ohSizeOfCode: resd 1 48 | .ohSizeOfInitializedData: resd 1 49 | .ohSizeOfUninitializedData: resd 1 50 | .ohAddressOfEntryPoint: resd 1 51 | .ohBaseOfCode: resd 1 52 | .ohImageBasex: resq 1 53 | .ohSectionAlignment: resd 1 54 | .ohFileAlignment: resd 1 55 | .ohMajorOperatingSystemVersion: resw 1 56 | .ohMinorOperatingSystemVersion: resw 1 57 | .ohMajorImageVersion: resw 1 58 | .ohMinorImageVersion: resw 1 59 | .ohMajorSubsystemVersion: resw 1 60 | .ohMinorSubsystemVersion: resw 1 61 | .ohWin32VersionValue: resd 1 62 | .ohSizeOfImage: resd 1 63 | .ohSizeOfHeaders: resd 1 64 | endstruc 65 | 66 | struc _IMAGE_NT_HEADERS 67 | .nthSignature: resd 1 68 | .nthFileHeader: resb _IMAGE_FILE_HEADER_size 69 | .nthOptionalHeader: resb _IMAGE_OPTIONAL_HEADER_size 70 | endstruc 71 | 72 | struc _IMAGE_SECTION_HEADER 73 | .shName: resb 8 74 | .shVirtualSize: resd 1 75 | .shVirtualAddress: resd 1 76 | .shSizeOfRawData: resd 1 77 | .shPointerToRawData: resd 1 78 | .shPointerToRelocations: resd 1 79 | .shPointerToLinenumbers: resd 1 80 | .shNumberOfRelocations: resw 1 81 | .shNumberOfLinenumbers: resw 1 82 | .shCharacteristics: resd 1 83 | endstruc 84 | 85 | struc _IMAGE_IMPORT_DESCRIPTOR 86 | .idOriginalFirstThunk: resd 1 87 | .idTimeDateStamp: resd 1 88 | .idForwarderChain: resd 1 89 | .idName: resd 1 90 | .idFirstThunk: resd 1 91 | endstruc 92 | 93 | struc IMAGE_BASE_RELOCATION 94 | .rePageRVA: resd 1 95 | .reSizeOfBlock: resd 1 96 | endstruc 97 | 98 | struc _IMAGE_EXPORT_DIRECTORY 99 | .edCharacteristics: resd 1 100 | .edTimeDateStamp: resd 1 101 | .edMajorVersion: resw 1 102 | .edMinorVersion: resw 1 103 | .edName: resd 1 104 | .edBase: resd 1 105 | .edNumberOfFunctions: resd 1 106 | .edNumberOfNames: resd 1 107 | .edAddressOfFunctions: resd 1 108 | .edAddressOfNames: resd 1 109 | .edAddressOfNameOrdinals: resd 1 110 | endstruc -------------------------------------------------------------------------------- /loader_v1/hldr64/install.bat: -------------------------------------------------------------------------------- 1 | move stub64.bin ../../pe2shc/stub1/ 2 | -------------------------------------------------------------------------------- /loader_v1/hldr64/make.bat: -------------------------------------------------------------------------------- 1 | yasm -f bin hldr64.asm -o stub64.bin 2 | -------------------------------------------------------------------------------- /loader_v2/.gitignore: -------------------------------------------------------------------------------- 1 | *.asm 2 | *.obj 3 | *.lnk 4 | -------------------------------------------------------------------------------- /loader_v2/README.md: -------------------------------------------------------------------------------- 1 | Loader is the part dedicated to manual loading of a PE. It is automatically added to your executable during the process of conversion (shellcodification). So, when you use pe_to_shellcode, two main things are done to your exe: 2 | 1) the loader is appended 3 | 2) the header is modified, to redirect the execution to the loader - thanks to this, after the conversion the PE can be injected and executed starting from its beginning. 4 | 5 | Loader is built separately from the main executable. 6 | + Buiding requires [masm_shc.exe](https://github.com/hasherezade/masm_shc/releases). 7 | + More details described [here](https://github.com/hasherezade/masm_shc). 8 | -------------------------------------------------------------------------------- /loader_v2/clean.bat: -------------------------------------------------------------------------------- 1 | del *.asm 2 | del *.obj 3 | del peloader*.exe 4 | -------------------------------------------------------------------------------- /loader_v2/make_peloader32.bat: -------------------------------------------------------------------------------- 1 | cl /c /GS- /FA /O1 peloader.cpp 2 | masm_shc.exe peloader.asm peloader1.asm 3 | ml peloader1.asm /link /entry:main 4 | -------------------------------------------------------------------------------- /loader_v2/make_peloader64.bat: -------------------------------------------------------------------------------- 1 | cl /c /GS- /FA /O1 peloader.cpp 2 | masm_shc.exe peloader.asm peloader2.asm 3 | ml64 peloader2.asm /link /entry:AlignRSP -------------------------------------------------------------------------------- /loader_v2/peb_lookup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // enhanced version of LDR_DATA_TABLE_ENTRY 7 | typedef struct _LDR_DATA_TABLE_ENTRY1 { 8 | LIST_ENTRY InLoadOrderLinks; 9 | LIST_ENTRY InMemoryOrderLinks; 10 | LIST_ENTRY InInitializationOrderLinks; 11 | void* DllBase; 12 | void* EntryPoint; 13 | ULONG SizeOfImage; 14 | UNICODE_STRING FullDllName; 15 | UNICODE_STRING BaseDllName; 16 | ULONG Flags; 17 | SHORT LoadCount; 18 | SHORT TlsIndex; 19 | HANDLE SectionHandle; 20 | ULONG CheckSum; 21 | ULONG TimeDateStamp; 22 | } LDR_DATA_TABLE_ENTRY1, * PLDR_DATA_TABLE_ENTRY1; 23 | 24 | 25 | template 26 | inline DWORD calc_checksum(CHAR_TYPE* curr_name, bool case_sensitive) 27 | { 28 | DWORD crc = 0xFFFFFFFF; 29 | size_t k; 30 | for (k = 0; curr_name[k] != 0; k++) { 31 | CHAR_TYPE ch = curr_name[k]; 32 | if (!case_sensitive) { 33 | ch = (ch <= 'Z' && ch >= 'A') ? (ch - 'A') + 'a' : ch; 34 | } 35 | for (size_t j = 0; j < 8; j++) { 36 | DWORD b = (ch ^ crc) & 1; 37 | crc >>= 1; 38 | if (b) crc = crc ^ 0xEDB88320; 39 | ch >>= 1; 40 | } 41 | } 42 | return ~crc; 43 | } 44 | 45 | inline LPVOID get_module_by_checksum(DWORD checksum) 46 | { 47 | PEB *peb; 48 | #if defined(_WIN64) 49 | peb = (PPEB)__readgsqword(0x60); 50 | #else 51 | peb = (PPEB)__readfsdword(0x30); 52 | #endif 53 | PEB_LDR_DATA *ldr = peb->Ldr; 54 | 55 | LIST_ENTRY *head = &ldr->InMemoryOrderModuleList; 56 | for(LIST_ENTRY *current = head->Flink; current != head; current = current->Flink){ 57 | LDR_DATA_TABLE_ENTRY1* entry = CONTAINING_RECORD(current, LDR_DATA_TABLE_ENTRY1, InMemoryOrderLinks); 58 | if (!entry || !entry->DllBase) break; 59 | 60 | WCHAR* curr_name = entry->BaseDllName.Buffer; 61 | if (!curr_name) continue; 62 | 63 | DWORD curr_crc = calc_checksum(curr_name, false); 64 | if (curr_crc == checksum) { 65 | //found 66 | return entry->DllBase; 67 | } 68 | } 69 | return nullptr; 70 | } 71 | 72 | inline LPVOID get_func_by_checksum(LPVOID module, DWORD checksum) 73 | { 74 | IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)module; 75 | if (idh->e_magic != IMAGE_DOS_SIGNATURE) { 76 | return nullptr; 77 | } 78 | IMAGE_NT_HEADERS* nt_headers = (IMAGE_NT_HEADERS*)((BYTE*)module + idh->e_lfanew); 79 | IMAGE_DATA_DIRECTORY* exportsDir = &(nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]); 80 | if (!exportsDir->VirtualAddress) { 81 | return nullptr; 82 | } 83 | 84 | DWORD expAddr = exportsDir->VirtualAddress; 85 | IMAGE_EXPORT_DIRECTORY* exp = (IMAGE_EXPORT_DIRECTORY*)(expAddr + (ULONG_PTR)module); 86 | SIZE_T namesCount = exp->NumberOfNames; 87 | 88 | DWORD funcsListRVA = exp->AddressOfFunctions; 89 | DWORD funcNamesListRVA = exp->AddressOfNames; 90 | DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals; 91 | 92 | //go through names: 93 | for (SIZE_T i = 0; i < namesCount; i++) { 94 | DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*)module + i * sizeof(DWORD)); 95 | WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*)module + i * sizeof(WORD)); 96 | DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*)module + (*nameIndex) * sizeof(DWORD)); 97 | 98 | LPSTR curr_name = (LPSTR)(*nameRVA + (BYTE*)module); 99 | DWORD curr_crc = calc_checksum(curr_name, true); 100 | if (curr_crc == checksum) { 101 | //found 102 | return (BYTE*)module + (*funcRVA); 103 | } 104 | } 105 | return nullptr; 106 | } 107 | -------------------------------------------------------------------------------- /loader_v2/peloader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "peb_lookup.h" 3 | #include "peloader.h" 4 | 5 | #define RELOC_32BIT_FIELD 3 6 | #define RELOC_64BIT_FIELD 0xA 7 | 8 | #ifdef _WIN64 9 | #define RELOC_FIELD RELOC_64BIT_FIELD 10 | typedef ULONG_PTR FIELD_PTR; 11 | #else 12 | #define RELOC_FIELD RELOC_32BIT_FIELD 13 | typedef DWORD_PTR FIELD_PTR; 14 | #endif 15 | 16 | typedef struct _BASE_RELOCATION_ENTRY { 17 | WORD Offset : 12; 18 | WORD Type : 4; 19 | } BASE_RELOCATION_ENTRY; 20 | 21 | #define CRC_kernel32 0x6AE69F02 22 | #define CRC_GetProcAddress 0xC97C1FFF 23 | #define CRC_LoadLibraryA 0x3FC1BD8D 24 | 25 | typedef struct 26 | { 27 | decltype(&LoadLibraryA) _LoadLibraryA; 28 | decltype(&GetProcAddress) _GetProcAddress; 29 | } t_mini_iat; 30 | 31 | bool init_iat(t_mini_iat &iat) 32 | { 33 | LPVOID base = get_module_by_checksum(CRC_kernel32); 34 | if (!base) { 35 | return false; 36 | } 37 | 38 | LPVOID load_lib = get_func_by_checksum((HMODULE)base, CRC_LoadLibraryA); 39 | if (!load_lib) { 40 | return false; 41 | } 42 | LPVOID get_proc = get_func_by_checksum((HMODULE)base, CRC_GetProcAddress); 43 | if (!get_proc) { 44 | return false; 45 | } 46 | 47 | iat._LoadLibraryA = reinterpret_cast(load_lib); 48 | iat._GetProcAddress = reinterpret_cast(get_proc); 49 | return true; 50 | } 51 | 52 | bool relocate(IMAGE_DATA_DIRECTORY& relocationsDirectory, BYTE* image, FIELD_PTR oldBase) 53 | { 54 | PIMAGE_BASE_RELOCATION ProcessBReloc = (PIMAGE_BASE_RELOCATION)(relocationsDirectory.VirtualAddress + (FIELD_PTR)image); 55 | // apply relocations: 56 | while (ProcessBReloc->VirtualAddress != 0) 57 | { 58 | const DWORD page = ProcessBReloc->VirtualAddress; 59 | if (ProcessBReloc->SizeOfBlock < sizeof(IMAGE_BASE_RELOCATION)) { 60 | continue; 61 | } 62 | size_t count = (ProcessBReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); 63 | BASE_RELOCATION_ENTRY* list = (BASE_RELOCATION_ENTRY*)(LPWORD)(ProcessBReloc + 1); 64 | for (size_t i = 0; i < count; i++) 65 | { 66 | if (list[i].Type == 0) break; 67 | if (list[i].Type != RELOC_FIELD) { 68 | return false; 69 | } 70 | DWORD rva = list[i].Offset + page; 71 | PULONG_PTR p = (PULONG_PTR)((LPBYTE)image + rva); 72 | //relocate the address 73 | *p = ((*p) - oldBase) + (FIELD_PTR)image; 74 | } 75 | ProcessBReloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)ProcessBReloc + ProcessBReloc->SizeOfBlock); 76 | } 77 | return true; 78 | } 79 | 80 | bool load_imports(t_mini_iat iat, IMAGE_DATA_DIRECTORY importsDirectory, BYTE* image) 81 | { 82 | PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (FIELD_PTR)image); 83 | 84 | while (importDescriptor->Name != NULL) 85 | { 86 | LPCSTR libraryName = (LPCSTR)((ULONG_PTR)importDescriptor->Name + (ULONG_PTR)image); 87 | HMODULE library = iat._LoadLibraryA(libraryName); 88 | if (!library) return false; 89 | 90 | PIMAGE_THUNK_DATA thunk = NULL; 91 | thunk = (PIMAGE_THUNK_DATA)((FIELD_PTR)image + importDescriptor->FirstThunk); 92 | 93 | while (thunk->u1.AddressOfData != NULL) 94 | { 95 | FIELD_PTR functionAddress = NULL; 96 | LPCSTR functionName = NULL; 97 | if (IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal)) { 98 | functionName = (LPCSTR)IMAGE_ORDINAL(thunk->u1.Ordinal); 99 | } 100 | else { 101 | PIMAGE_IMPORT_BY_NAME functionByName = (PIMAGE_IMPORT_BY_NAME)((FIELD_PTR)image + thunk->u1.AddressOfData); 102 | functionName = functionByName->Name; 103 | } 104 | if (!functionName) return false; 105 | 106 | functionAddress = (FIELD_PTR)iat._GetProcAddress(library, functionName); 107 | if (!functionAddress) return false; 108 | 109 | thunk->u1.Function = functionAddress; 110 | ++thunk; 111 | } 112 | importDescriptor++; 113 | } 114 | return (importDescriptor > 0); 115 | } 116 | 117 | bool run_tls_callbacks(IMAGE_DATA_DIRECTORY& tlsDir, BYTE* image) 118 | { 119 | PIMAGE_TLS_DIRECTORY tls_dir = (PIMAGE_TLS_DIRECTORY)(tlsDir.VirtualAddress + (FIELD_PTR)image); 120 | FIELD_PTR *callbacks_ptr = (FIELD_PTR*) tls_dir->AddressOfCallBacks; // this is VA... 121 | if (!callbacks_ptr) return true; 122 | 123 | while (callbacks_ptr != nullptr) { 124 | FIELD_PTR callback_va = *callbacks_ptr; 125 | if (!callback_va) break; 126 | 127 | void(NTAPI * callback_func)(PVOID DllHandle, DWORD dwReason, PVOID) 128 | = (void(NTAPI*)(PVOID, DWORD, PVOID)) callback_va; 129 | callback_func(image, DLL_PROCESS_ATTACH, NULL); 130 | 131 | callbacks_ptr++; 132 | } 133 | return true; 134 | } 135 | 136 | int __stdcall main(void *module_base) 137 | { 138 | t_mini_iat iat; 139 | if (!init_iat(iat)) { 140 | return (-1); 141 | } 142 | IMAGE_DOS_HEADER* mz = (IMAGE_DOS_HEADER*)module_base; 143 | if (mz->e_magic != IMAGE_DOS_SIGNATURE) { 144 | return (-2); 145 | } 146 | IMAGE_NT_HEADERS* pe = (IMAGE_NT_HEADERS*)(mz->e_lfanew + (ULONG_PTR)module_base); 147 | if (pe->Signature != IMAGE_NT_SIGNATURE) { 148 | return (-2); 149 | } 150 | 151 | min_hdr_t* my_hdr = (min_hdr_t*)module_base; 152 | if (my_hdr->load_status == LDS_RUN) { 153 | // do not allow to run again: 154 | return ERROR_ALREADY_INITIALIZED; 155 | } 156 | if (my_hdr->load_status == LDS_ATTACHED) { 157 | if ((pe->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) { 158 | // not a DLL, this should not happed: 159 | return ERROR_ALREADY_INITIALIZED; 160 | } 161 | DWORD ep_rva = pe->OptionalHeader.AddressOfEntryPoint; 162 | ULONG_PTR ep_va = (ULONG_PTR)module_base + ep_rva; 163 | BOOL(WINAPI * my_DllMain)(HINSTANCE, DWORD, LPVOID) 164 | = (BOOL(WINAPI*)(HINSTANCE, DWORD, LPVOID)) ep_va; 165 | BOOL is_ok = my_DllMain((HINSTANCE)module_base, DLL_PROCESS_DETACH, 0); 166 | if (is_ok) { 167 | // no longer attached: 168 | my_hdr->load_status = LDS_RUN; 169 | } 170 | return is_ok; 171 | } 172 | if (my_hdr->load_status == LDS_CLEAN) { 173 | IMAGE_DATA_DIRECTORY& relocDir = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 174 | if (!relocDir.VirtualAddress) { 175 | return (-3); 176 | } 177 | const ULONG_PTR oldBase = pe->OptionalHeader.ImageBase; 178 | if (!relocate(relocDir, (BYTE*)module_base, oldBase)) { 179 | return (-4); 180 | } 181 | IMAGE_DATA_DIRECTORY& importDir = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 182 | if (importDir.VirtualAddress) { 183 | if (!load_imports(iat, importDir, (BYTE*)module_base)) { 184 | return (-5); 185 | } 186 | } 187 | IMAGE_DATA_DIRECTORY& tlsDir = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; 188 | if (tlsDir.VirtualAddress) { 189 | run_tls_callbacks(tlsDir, (BYTE*)module_base); 190 | } 191 | } 192 | my_hdr->load_status = LDS_LOADED; 193 | 194 | DWORD ep_rva = pe->OptionalHeader.AddressOfEntryPoint; 195 | ULONG_PTR ep_va = (ULONG_PTR)module_base + ep_rva; 196 | BOOL is_ok = FALSE; 197 | 198 | my_hdr->load_status = LDS_RUN; 199 | if (pe->FileHeader.Characteristics & IMAGE_FILE_DLL) { 200 | BOOL(WINAPI * my_DllMain)(HINSTANCE, DWORD, LPVOID) 201 | = (BOOL(WINAPI*)(HINSTANCE, DWORD, LPVOID)) ep_va; 202 | is_ok = my_DllMain((HINSTANCE)module_base, DLL_PROCESS_ATTACH, 0); 203 | if (is_ok) { 204 | my_hdr->load_status = LDS_ATTACHED; 205 | } 206 | } 207 | else { 208 | int(*my_main)() = (int(*)()) (ep_va); 209 | is_ok = my_main(); 210 | } 211 | return is_ok; 212 | } 213 | -------------------------------------------------------------------------------- /loader_v2/peloader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define MAX_REDIR_SIZE 32 5 | 6 | #define LDS_CLEAN 0 7 | #define LDS_LOADED 1 8 | #define LDS_RUN 2 9 | #define LDS_ATTACHED 3 10 | 11 | typedef struct _min_hdr { 12 | BYTE redir[MAX_REDIR_SIZE]; 13 | BYTE load_status; 14 | } min_hdr_t; 15 | -------------------------------------------------------------------------------- /pe2shc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | 3 | project ( pe2shc ) 4 | 5 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 6 | 7 | option(OLD_LOADER "Build with the old loader stub" OFF) 8 | 9 | # include libpeconv headers: 10 | include_directories ( ${PECONV_DIR}/include ) 11 | 12 | set (srcs 13 | #put your sources here 14 | ) 15 | 16 | # general headers - they will be used for both EXE and DLL: 17 | set (hdrs 18 | resource.h 19 | ) 20 | 21 | if( OLD_LOADER ) 22 | set (rsrc 23 | resource1.rc 24 | ) 25 | add_definitions(-DOLD_LOADER) 26 | else() 27 | set (rsrc 28 | resource2.rc 29 | ) 30 | endif() 31 | 32 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs} ${rsrc} main.cpp ) 33 | 34 | # link with libpeconv.lib 35 | target_link_libraries ( ${PROJECT_NAME} ${PECONV_LIB} ) 36 | 37 | #dependencies: 38 | add_dependencies( ${PROJECT_NAME} libpeconv ) 39 | 40 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 41 | -------------------------------------------------------------------------------- /pe2shc/LICENSE: -------------------------------------------------------------------------------- 1 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 4 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 5 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 7 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 8 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 9 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 10 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /pe2shc/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "peconv.h" 5 | #include "resource.h" 6 | 7 | #define VERSION "1.2" 8 | #include "..\loader_v2\peloader.h" 9 | 10 | bool overwrite_hdr(BYTE *my_exe, size_t exe_size, DWORD raw, bool is64b) 11 | { 12 | const size_t value_pos = 8; 13 | size_t redir_size = 0; 14 | BYTE* redir_code = nullptr; 15 | 16 | BYTE redir_code32_64[] = "\x4D" //dec ebp 17 | "\x5A" //pop edx 18 | "\x45" //inc ebp 19 | "\x52" //push edx 20 | "\xE8\x00\x00\x00\x00" //call 21 | "\x5B" // pop ebx 22 | "\x48\x83\xEB\x09" // sub ebx,9 23 | "\x53" // push ebx (Image Base) 24 | "\x48\x81\xC3" // add ebx, 25 | "\x59\x04\x00\x00" // value 26 | "\xFF\xD3" // call ebx 27 | "\xc3"; // ret 28 | 29 | BYTE redir_code32[] = "\x4D" //dec ebp 30 | "\x5A" //pop edx 31 | "\x45" //inc ebp 32 | "\x52" //push edx 33 | "\xE8\x00\x00\x00\x00" //call 34 | "\x58" // pop eax 35 | "\x83\xE8\x09" // sub eax,9 36 | "\x50" // push eax (Image Base) 37 | "\x05" // add eax, 38 | "\x59\x04\x00\x00" // value 39 | "\xFF\xD0" // call eax 40 | "\xc3"; // ret 41 | 42 | BYTE redir_code64[] = "\x4D\x5A" //pop r10 43 | "\x45\x52" //push r10 44 | "\xE8\x00\x00\x00\x00" //call 45 | "\x59" // pop rcx 46 | "\x48\x83\xE9\x09" // sub rcx,9 (rcx -> Image Base) 47 | "\x48\x8B\xC1" // mov rax,rcx 48 | "\x48\x05" // add eax, 49 | "\x59\x04\x00\x00" // value 50 | "\xFF\xD0" // call eax 51 | "\xc3"; // ret 52 | 53 | #ifdef OLD_LOADER 54 | redir_code = redir_code32_64; 55 | redir_size = sizeof(redir_code32_64); 56 | #else 57 | redir_code = redir_code32; 58 | redir_size = sizeof(redir_code32); 59 | 60 | if (is64b) { 61 | redir_code = redir_code64; 62 | redir_size = sizeof(redir_code64); 63 | } 64 | #endif 65 | if (!redir_code) return false; 66 | if (redir_size > MAX_REDIR_SIZE) { 67 | std::cerr << "The selected redir stub exceed the maximal size: " << std::dec << MAX_REDIR_SIZE << "\n"; 68 | return false; 69 | } 70 | size_t offset = redir_size - value_pos; 71 | memcpy(redir_code + offset, &raw, sizeof(DWORD)); 72 | 73 | min_hdr_t* my_hdr = (min_hdr_t*)my_exe; 74 | memcpy(my_hdr->redir, redir_code, redir_size); 75 | my_hdr->load_status = LDS_CLEAN; 76 | return true; 77 | } 78 | 79 | BYTE* shellcodify(BYTE *my_exe, size_t exe_size, size_t &out_size, bool is64b) 80 | { 81 | out_size = 0; 82 | size_t stub_size = 0; 83 | int res_id = is64b ? STUB64 : STUB32; 84 | BYTE *stub = peconv::load_resource_data(stub_size, res_id); 85 | if (!stub) { 86 | std::cerr << "[ERROR] Stub not loaded" << std::endl; 87 | return nullptr; 88 | } 89 | size_t ext_size = exe_size + stub_size; 90 | BYTE *ext_buf = peconv::alloc_aligned(ext_size, PAGE_READWRITE); 91 | if (!ext_buf) { 92 | return nullptr; 93 | } 94 | memcpy(ext_buf, my_exe, exe_size); 95 | memcpy(ext_buf + exe_size, stub, stub_size); 96 | 97 | DWORD raw_addr = exe_size; 98 | overwrite_hdr(ext_buf, ext_size, raw_addr, is64b); 99 | 100 | out_size = ext_size; 101 | return ext_buf; 102 | } 103 | 104 | template 105 | bool has_tls_callbacks(BYTE *my_exe, size_t exe_size) 106 | { 107 | IMAGE_DATA_DIRECTORY* tls_dir = peconv::get_directory_entry(my_exe, IMAGE_DIRECTORY_ENTRY_TLS); 108 | if (!tls_dir) return false; 109 | 110 | IMAGE_TLS_DIRECTORY* tls = peconv::get_type_directory((HMODULE)my_exe, IMAGE_DIRECTORY_ENTRY_TLS); 111 | if (!tls) return false; 112 | 113 | ULONGLONG base = peconv::get_image_base(my_exe); 114 | ULONGLONG callback_rva = tls->AddressOfCallBacks; 115 | if (callback_rva > base) { 116 | callback_rva -= base; 117 | } 118 | if (!peconv::validate_ptr(my_exe, exe_size, my_exe + callback_rva, sizeof(ULONGLONG))) { 119 | return false; 120 | } 121 | ULONGLONG *callback_addr = (ULONGLONG *)(my_exe + callback_rva); 122 | if (callback_addr == 0) { 123 | return false; 124 | } 125 | if (*callback_addr == 0) { 126 | return false; 127 | } 128 | return true; 129 | } 130 | 131 | bool is_supported_pe(BYTE *my_exe, size_t exe_size) 132 | { 133 | if (!my_exe) return false; 134 | if (!peconv::has_relocations(my_exe)) { 135 | std::cerr << "[ERROR] The PE must have relocations!" << std::endl; 136 | return false; 137 | } 138 | if (peconv::get_subsystem(my_exe) != IMAGE_SUBSYSTEM_WINDOWS_GUI) { 139 | std::cout << "[INFO] This is a console application." << std::endl; 140 | } 141 | IMAGE_DATA_DIRECTORY* dotnet_dir = peconv::get_directory_entry(my_exe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR); 142 | if (dotnet_dir) { 143 | std::cerr << "[ERROR] .NET applications are not supported!" << std::endl; 144 | return false; 145 | } 146 | IMAGE_DATA_DIRECTORY* tls_dir = peconv::get_directory_entry(my_exe, IMAGE_DIRECTORY_ENTRY_TLS); 147 | if (tls_dir) { 148 | bool has_callback = false; 149 | if (!peconv::is64bit(my_exe)) { 150 | if (has_tls_callbacks(my_exe, exe_size)) { 151 | has_callback = true; 152 | } 153 | } 154 | else { 155 | if (has_tls_callbacks(my_exe, exe_size)) { 156 | has_callback = true; 157 | } 158 | } 159 | if (has_callback) { 160 | std::cout << "[INFO] This application has TLS callbacks." << std::endl; 161 | } 162 | } 163 | return true; 164 | } 165 | 166 | bool is_supported_pe(const std::string &in_path) 167 | { 168 | std::cout << "Reading module from: " << in_path << std::endl; 169 | size_t exe_size = 0; 170 | BYTE *my_exe = peconv::load_pe_module(in_path.c_str(), exe_size, false, false); 171 | if (!my_exe) { 172 | std::cerr << "[ERROR] Could not read the input file!" << std::endl; 173 | return false; 174 | } 175 | 176 | bool is_ok = is_supported_pe(my_exe, exe_size); 177 | peconv::free_pe_buffer(my_exe); 178 | 179 | if (!is_ok) { 180 | std::cerr << "[ERROR] Not supported input file!" << std::endl; 181 | return false; 182 | } 183 | return true; 184 | } 185 | 186 | std::string make_out_name(std::string input_file) 187 | { 188 | size_t found_indx = input_file.find_last_of("."); 189 | std::string ext = input_file.substr(found_indx + 1); 190 | std::string name = input_file.substr(0, found_indx); 191 | return name + ".shc." + ext; 192 | } 193 | 194 | int main(int argc, char *argv[]) 195 | { 196 | if (argc < 2) { 197 | std::cout << "~ pe2shc v." << VERSION << " ~\n" 198 | << "Converts PE into shellcode.\nFor 32 & 64 bit PEs.\n"; 199 | std::cout << "Args: [output_file]" << std::endl; 200 | system("pause"); 201 | return 0; 202 | } 203 | #ifdef OLD_LOADER 204 | std::cout << "Using: Loader v1\n"; 205 | #else 206 | std::cout << "Using: Loader v2\n"; 207 | #endif 208 | std::string in_path = argv[1]; 209 | std::string out_str = make_out_name(in_path); 210 | if (argc > 2) { 211 | out_str = argv[2]; 212 | } 213 | 214 | if (!is_supported_pe(in_path)) { 215 | return -2; 216 | } 217 | 218 | size_t exe_size = 0; 219 | BYTE *my_exe = peconv::load_pe_module(in_path.c_str(), exe_size, false, false); 220 | if (!my_exe) { 221 | std::cout << "[-] Could not read the input file!" << std::endl; 222 | return -1; 223 | } 224 | 225 | bool is64b = peconv::is64bit(my_exe); 226 | size_t ext_size = 0; 227 | BYTE *ext_buf = shellcodify(my_exe, exe_size, ext_size, is64b); 228 | if (!ext_buf) { 229 | std::cerr << "[ERROR] Adding the stub failed!" << std::endl; 230 | peconv::free_pe_buffer(my_exe); 231 | return -3; 232 | } 233 | // remap pe to raw == virtual, so that remapping on load will not be required 234 | peconv::t_pe_dump_mode dump_mode = peconv::PE_DUMP_REALIGN; 235 | ULONGLONG current_base = peconv::get_image_base(ext_buf); 236 | if (peconv::dump_pe(out_str.c_str(), ext_buf, ext_size, current_base, dump_mode)) { 237 | std::cout << "[INFO] Saved as: " << out_str << std::endl; 238 | } 239 | else { 240 | std::cerr << "[ERROR] Failed to save the output!" << std::endl; 241 | } 242 | peconv::free_pe_buffer(my_exe); 243 | peconv::free_aligned(ext_buf); 244 | return 0; 245 | } 246 | -------------------------------------------------------------------------------- /pe2shc/resource.h: -------------------------------------------------------------------------------- 1 | // resource.h 2 | 3 | #define STUB32 101 4 | #define STUB64 102 5 | -------------------------------------------------------------------------------- /pe2shc/resource1.rc: -------------------------------------------------------------------------------- 1 | // resource.rc : 2 | 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource.h" 6 | 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "windows.h" 13 | 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | 17 | ///////////////////////////////////////////////////////////////////////////// 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PLK) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""windows.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // RCDATA 51 | // 52 | 53 | STUB32 RCDATA "stub1\stub32.bin" 54 | STUB64 RCDATA "stub1\stub64.bin" 55 | #endif 56 | ///////////////////////////////////////////////////////////////////////////// 57 | -------------------------------------------------------------------------------- /pe2shc/resource2.rc: -------------------------------------------------------------------------------- 1 | // resource.rc : 2 | 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource.h" 6 | 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "windows.h" 13 | 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | 17 | ///////////////////////////////////////////////////////////////////////////// 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PLK) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""windows.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // RCDATA 51 | // 52 | 53 | STUB32 RCDATA "stub2\stub32.bin" 54 | STUB64 RCDATA "stub2\stub64.bin" 55 | #endif 56 | ///////////////////////////////////////////////////////////////////////////// 57 | -------------------------------------------------------------------------------- /pe2shc/stub1/stub32.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/pe_to_shellcode/a2458c96619b721677af0f9b906cd6a229364b4c/pe2shc/stub1/stub32.bin -------------------------------------------------------------------------------- /pe2shc/stub1/stub64.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/pe_to_shellcode/a2458c96619b721677af0f9b906cd6a229364b4c/pe2shc/stub1/stub64.bin -------------------------------------------------------------------------------- /pe2shc/stub2/stub32.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/pe_to_shellcode/a2458c96619b721677af0f9b906cd6a229364b4c/pe2shc/stub2/stub32.bin -------------------------------------------------------------------------------- /pe2shc/stub2/stub64.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/pe_to_shellcode/a2458c96619b721677af0f9b906cd6a229364b4c/pe2shc/stub2/stub64.bin -------------------------------------------------------------------------------- /runshc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | 3 | project ( runshc ) 4 | 5 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 6 | 7 | # include libpeconv headers: 8 | include_directories ( ${PECONV_DIR}/include ) 9 | 10 | set (srcs 11 | #put your sources here 12 | ) 13 | 14 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs} ${rsrc} main.cpp ) 15 | 16 | 17 | # link with libpeconv.lib 18 | target_link_libraries ( ${PROJECT_NAME} ${PECONV_LIB} ) 19 | 20 | #dependencies: 21 | add_dependencies( ${PROJECT_NAME} libpeconv ) 22 | 23 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 24 | -------------------------------------------------------------------------------- /runshc/LICENSE: -------------------------------------------------------------------------------- 1 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 4 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 5 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 7 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 8 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 9 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 10 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /runshc/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #define VERSION "1.2" 7 | #include "..\loader_v2\peloader.h" 8 | 9 | typedef struct { 10 | BYTE* my_exe; 11 | size_t exe_size; 12 | bool is_run; 13 | } t_module_params; 14 | 15 | bool load_and_run(t_module_params& args) 16 | { 17 | BYTE* test_buf = peconv::alloc_aligned(args.exe_size, PAGE_EXECUTE_READWRITE); 18 | if (!test_buf) { 19 | std::cerr << "[ERROR] Allocating buffer failed" << std::endl; 20 | return false; 21 | } 22 | 23 | //copy file content into executable buffer: 24 | memcpy(test_buf, args.my_exe, args.exe_size); 25 | 26 | //free the original buffer: 27 | peconv::free_file(args.my_exe); 28 | args.my_exe = nullptr; 29 | 30 | std::cout << "[*] Running the shellcode [" << std::hex << (ULONG_PTR) test_buf << " - " << (ULONG_PTR)(test_buf + args.exe_size) << "]" << std::endl; 31 | //run it: 32 | int (*my_main)() = (int(*)()) ((ULONGLONG)test_buf); 33 | int ret_val = my_main(); 34 | args.is_run = true; 35 | min_hdr_t *my_hdr = (min_hdr_t*)test_buf; 36 | if (my_hdr->load_status == LDS_ATTACHED) { 37 | //run again to unload DLL: 38 | std::cout << "[*] Running again to unload the DLL...\n"; 39 | my_main(); 40 | std::cout << "[*] Load status: " << (int)my_hdr->load_status << "\n"; 41 | } 42 | peconv::free_aligned(test_buf, args.exe_size); 43 | std::cout << "[+] The shellcode finished with a return value: " << std::hex << ret_val << std::endl; 44 | return true; 45 | } 46 | 47 | 48 | DWORD WINAPI mod_runner(LPVOID lpParam) 49 | { 50 | t_module_params* args = static_cast(lpParam); 51 | if (!args) { 52 | return ERROR_BAD_ARGUMENTS; 53 | } 54 | args->is_run = false; 55 | load_and_run(*args); 56 | return S_OK; 57 | } 58 | 59 | bool run_in_new_thread(t_module_params &args) 60 | { 61 | std::cout << ">>> Creating a new thread...\n"; 62 | HANDLE hThead = CreateThread( 63 | NULL, // default security attributes 64 | 0, // use default stack size 65 | mod_runner, // thread function name 66 | &args, // argument to thread function 67 | 0, // use default creation flags 68 | 0); // returns the thread identifier 69 | 70 | if (!hThead) { 71 | std::cerr << "Failed to created the thread!\n"; 72 | return false; 73 | } 74 | DWORD wait_result = WaitForSingleObject(hThead, INFINITE); 75 | return (args.is_run); 76 | } 77 | 78 | bool run_in_curr_thread(t_module_params &args) 79 | { 80 | std::cout << ">>> Running in a current thread...\n"; 81 | load_and_run(args); 82 | return (args.is_run); 83 | } 84 | 85 | #define NEW_THREAD 86 | 87 | int main(int argc, char *argv[]) 88 | { 89 | if (argc < 2) { 90 | std::cout << "~ runshc v." << VERSION << " ~\n" 91 | << "Run shellcode: loads and deploys shellcode file.\n"; 92 | #ifdef _WIN64 93 | std::cout << "For 64-bit shellcodes.\n"; 94 | #else 95 | std::cout << "For 32-bit shellcodes.\n"; 96 | #endif 97 | std::cout << "Args: " << std::endl; 98 | system("pause"); 99 | return 0; 100 | } 101 | 102 | size_t exe_size = 0; 103 | char* in_path = argv[1]; 104 | 105 | std::cout << "[*] Reading module from: " << in_path << std::endl; 106 | BYTE *my_exe = peconv::load_file(in_path, exe_size); 107 | if (!my_exe) { 108 | std::cerr << "[ERROR] Loading file failed" << std::endl; 109 | return -1; 110 | } 111 | // if the shellcode is a converted PE, check its bitness before running... 112 | const WORD arch = peconv::get_nt_hdr_architecture(my_exe); 113 | if (arch) { 114 | #ifdef _WIN64 115 | if (arch != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { 116 | std::cerr << "[ERROR] Bitness mismatch: the given payload is not compatibilie with this loader\n"; 117 | return 0; 118 | } 119 | #else 120 | if (arch != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { 121 | std::cerr << "[ERROR] Bitness mismatch: the given payload is not compatibilie with this loader\n"; 122 | return 0; 123 | } 124 | #endif 125 | } 126 | 127 | t_module_params args = { 0 }; 128 | args.my_exe = my_exe; 129 | args.exe_size = exe_size; 130 | args.is_run = false; 131 | 132 | #ifdef NEW_THREAD 133 | bool res = run_in_new_thread(args); 134 | #else 135 | bool res = run_in_curr_thread(args); 136 | #endif 137 | if (args.my_exe) { 138 | peconv::free_file(args.my_exe); 139 | args.my_exe = nullptr; 140 | my_exe = nullptr; 141 | } 142 | std::cout << ">>> FINISHED.\n"; 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | project (tests) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | #add the application that will be used for tests: 7 | add_subdirectory ( test_case1 ) 8 | add_subdirectory ( dcp_test ) 9 | add_subdirectory ( test_case2_dll ) 10 | enable_testing() 11 | 12 | #WARNING: in order for tests to work, all the binaries must be installed by: cmake --build . --target install 13 | 14 | # 0) does pe2shc run 15 | add_test(RunPe2Shc "${CMAKE_INSTALL_PREFIX}//pe2shc.exe" "${CMAKE_INSTALL_PREFIX}//pe2shc.exe" "${CMAKE_INSTALL_PREFIX}//demo.shc.exe") 16 | set_tests_properties(RunPe2Shc PROPERTIES DEPENDS pe2shc) 17 | 18 | # 1) does runshc run 19 | add_test(RunRunShc "${CMAKE_INSTALL_PREFIX}//runshc.exe" "${CMAKE_INSTALL_PREFIX}//demo.shc.exe") 20 | set_tests_properties(RunRunShc PROPERTIES PASS_REGULAR_EXPRESSION "Running the shellcode") 21 | set_tests_properties(RunPe2Shc PROPERTIES DEPENDS pe2shc) 22 | set_tests_properties(RunPe2Shc PROPERTIES DEPENDS runshc) 23 | 24 | # 2) does conversion of the test application work 25 | add_test(ConvTestCase1 "${CMAKE_INSTALL_PREFIX}//pe2shc.exe" "${CMAKE_INSTALL_PREFIX}//test_case1.exe" "${CMAKE_INSTALL_PREFIX}//test_case1.shc.exe") 26 | set_tests_properties(ConvTestCase1 PROPERTIES PASS_REGULAR_EXPRESSION "Saved as:") 27 | 28 | # 3) does converted application run properly 29 | add_test(RunTestCase1 "${CMAKE_INSTALL_PREFIX}//runshc.exe" "${CMAKE_INSTALL_PREFIX}//test_case1.shc.exe") 30 | set_tests_properties(RunTestCase1 PROPERTIES DEPENDS test_case1) 31 | set_tests_properties(RunTestCase1 PROPERTIES PASS_REGULAR_EXPRESSION "Test passed!") 32 | 33 | # 4) convert DLL 34 | add_test(ConvTestCase2 "${CMAKE_INSTALL_PREFIX}//pe2shc.exe" "${CMAKE_INSTALL_PREFIX}//test_case2.dll" "${CMAKE_INSTALL_PREFIX}//test_case2.shc.dll") 35 | set_tests_properties(ConvTestCase2 PROPERTIES PASS_REGULAR_EXPRESSION "Saved as:") 36 | set_tests_properties(RunPe2Shc PROPERTIES DEPENDS pe2shc) 37 | 38 | # 5) does converted DLL loads & unloads properly 39 | add_test(RunTestCase2 "${CMAKE_INSTALL_PREFIX}//runshc.exe" "${CMAKE_INSTALL_PREFIX}//test_case2.shc.dll") 40 | set_tests_properties(RunTestCase2 PROPERTIES DEPENDS test_case2_dll) 41 | set_tests_properties(RunTestCase2 PROPERTIES DEPENDS runshc) 42 | set_tests_properties(RunTestCase2 PROPERTIES PASS_REGULAR_EXPRESSION ">>> FINISHED.") 43 | -------------------------------------------------------------------------------- /tests/dcp_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | project (dcp_test) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | set (srcs 7 | main.cpp 8 | ) 9 | 10 | set (hdrs 11 | ) 12 | 13 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs}) 14 | 15 | if(PE2SHC_BUILD_TESTING) 16 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 17 | endif() 18 | -------------------------------------------------------------------------------- /tests/dcp_test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | PROCESS_MITIGATION_DYNAMIC_CODE_POLICY dcp = {}; 7 | dcp.ProhibitDynamicCode = 1; 8 | SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &dcp, sizeof(dcp)); 9 | std::cout << "PID: " << std::dec << GetCurrentProcessId() << "\n"; 10 | std::cout << "PROCESS_MITIGATION_DYNAMIC_CODE_POLICY enabled...\n"; 11 | while (true) 12 | { 13 | Sleep(6000); 14 | } 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/test_case1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | project (test_case1) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | 6 | set (srcs 7 | main.cpp 8 | ) 9 | 10 | set (hdrs 11 | ) 12 | 13 | add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs}) 14 | 15 | if(PE2SHC_BUILD_TESTING) 16 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 17 | endif() 18 | -------------------------------------------------------------------------------- /tests/test_case1/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //#define WITH_MESSAGE //only for manual tests 5 | 6 | int get_date() 7 | { 8 | SYSTEMTIME SystemTime; 9 | GetSystemTime(&SystemTime); 10 | 11 | char pszDate[200]; 12 | GetDateFormatA( LOCALE_USER_DEFAULT, DATE_LONGDATE, &SystemTime, NULL, pszDate, 200 ); 13 | std::cout << "Current date: " << pszDate << std::endl; 14 | return 1337; 15 | } 16 | 17 | int main() 18 | { 19 | if (get_date() == 1337) { 20 | std::cout << "Test passed!\n"; 21 | } 22 | #ifdef WITH_MESSAGE 23 | MessageBoxW(0, L"Hello World!", L"Demo!", MB_OK); 24 | #endif 25 | std::cout << "Test Case 1 finished\n"; 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/test_case2_dll/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | project (test_case2) 3 | 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 5 | include_directories ( include ) 6 | 7 | set (srcs 8 | main.cpp 9 | ) 10 | 11 | set (hdrs 12 | ) 13 | 14 | add_library ( ${PROJECT_NAME} SHARED ${hdrs} ${srcs} main.def) 15 | 16 | #install 17 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 18 | -------------------------------------------------------------------------------- /tests/test_case2_dll/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define TEST_NAME "Test Case 2" 5 | inline DWORD rotl32a(DWORD x, DWORD n) 6 | { 7 | return (x << n) | (x >> (32 - n)); 8 | } 9 | 10 | inline char to_lower(char c) 11 | { 12 | if (c >= 'A' && c <= 'Z') { 13 | c = c - 'A' + 'a'; 14 | } 15 | return c; 16 | } 17 | 18 | DWORD calc_checksum(BYTE *str, size_t buf_size, bool enable_tolower) 19 | { 20 | if (str == NULL) return 0; 21 | 22 | DWORD checksum = 0; 23 | for (size_t i = 0; i < buf_size; i++) { 24 | checksum = rotl32a(checksum, 7); 25 | char c = str[i]; 26 | if (enable_tolower) { 27 | c = to_lower(c); 28 | } 29 | checksum ^= c; 30 | } 31 | return checksum; 32 | } 33 | 34 | int test_checksum1() 35 | { 36 | char test1[] = "this is a test!"; 37 | DWORD checks = calc_checksum((BYTE*)test1, strlen(test1), true); 38 | std::cout << "Checks 1: " << std::hex << checks << std::endl; 39 | return checks; 40 | } 41 | 42 | BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 43 | { 44 | std::cout << __FUNCTION__ << std::endl; 45 | switch (fdwReason) 46 | { 47 | case DLL_PROCESS_ATTACH: 48 | std::cout << TEST_NAME << " DLL loaded\n"; 49 | test_checksum1(); 50 | break; 51 | case DLL_THREAD_ATTACH: 52 | case DLL_THREAD_DETACH: 53 | case DLL_PROCESS_DETACH: 54 | std::cout << TEST_NAME << " DLL unloaded\n"; 55 | break; 56 | } 57 | return TRUE; 58 | } 59 | -------------------------------------------------------------------------------- /tests/test_case2_dll/main.def: -------------------------------------------------------------------------------- 1 | LIBRARY test_case2 2 | --------------------------------------------------------------------------------