Initial commit
This commit is contained in:
8
game_payload/include/ace.h
Normal file
8
game_payload/include/ace.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void ace_fake_driver_files();
|
||||
|
||||
HMODULE ace_load_base_module(const char *exeName);
|
||||
HMODULE ace_load_driver_module();
|
||||
24
game_payload/include/crc32.h
Normal file
24
game_payload/include/crc32.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
// Modified from https://stackoverflow.com/a/27950866
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* CRC-32C (iSCSI) polynomial in reversed bit order. */
|
||||
#define __POLY 0x82f63b78
|
||||
|
||||
static inline uint32_t crc32c(uint32_t crc, const unsigned char *buf, size_t len) {
|
||||
crc = ~crc;
|
||||
|
||||
while (len--) {
|
||||
crc ^= *buf++;
|
||||
for (int k = 0; k < 8; k++) {
|
||||
crc = crc & 1 ? (crc >> 1) ^ __POLY : crc >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
#undef __POLY
|
||||
6
game_payload/include/err.h
Normal file
6
game_payload/include/err.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
void err_mb_a(const char *format, ...);
|
||||
void err_mb_w(const wchar_t *format, ...);
|
||||
20
game_payload/include/game.h
Normal file
20
game_payload/include/game.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
enum game_id {
|
||||
GAME_INVALID,
|
||||
|
||||
GAME_HI3_GLB
|
||||
};
|
||||
|
||||
struct game_data {
|
||||
enum game_id id; // Temporary
|
||||
const char *name;
|
||||
const char *assembly_path;
|
||||
const wchar_t *assembly_name_lwr;
|
||||
const char *tp6_section_name; // Unused for now
|
||||
const char *tvm_section_name;
|
||||
};
|
||||
|
||||
void game_detect(struct game_data *buf);
|
||||
5
game_payload/include/hi3.h
Normal file
5
game_payload/include/hi3.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <game.h>
|
||||
|
||||
void hi3_fill_data(struct game_data *buf);
|
||||
36
game_payload/include/ntdll.h
Normal file
36
game_payload/include/ntdll.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/devnotes/ldrdllnotification
|
||||
typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
|
||||
ULONG Flags; //Reserved.
|
||||
PCUNICODE_STRING FullDllName; //The full path name of the DLL module.
|
||||
PCUNICODE_STRING BaseDllName; //The base file name of the DLL module.
|
||||
PVOID DllBase; //A pointer to the base address for the DLL in memory.
|
||||
ULONG SizeOfImage; //The size of the DLL image, in bytes.
|
||||
} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
|
||||
|
||||
typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA {
|
||||
ULONG Flags; //Reserved.
|
||||
PCUNICODE_STRING FullDllName; //The full path name of the DLL module.
|
||||
PCUNICODE_STRING BaseDllName; //The base file name of the DLL module.
|
||||
PVOID DllBase; //A pointer to the base address for the DLL in memory.
|
||||
ULONG SizeOfImage; //The size of the DLL image, in bytes.
|
||||
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
|
||||
|
||||
typedef union _LDR_DLL_NOTIFICATION_DATA {
|
||||
LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
|
||||
LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
|
||||
} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
|
||||
|
||||
typedef void (*LdrDllNotification_t)(ULONG reason, const PLDR_DLL_NOTIFICATION_DATA data, void *context);
|
||||
|
||||
typedef NTSTATUS (*LdrRegisterDllNotification_t)(ULONG flags, LdrDllNotification_t notificationFunction, void *context, void **cookie);
|
||||
typedef NTSTATUS (*LdrUnregisterDllNotification_t)(void *cookie);
|
||||
|
||||
extern LdrRegisterDllNotification_t LdrRegisterDllNotification;
|
||||
extern LdrUnregisterDllNotification_t LdrUnregisterDllNotification;
|
||||
|
||||
void ntdll_link();
|
||||
6
game_payload/include/pe.h
Normal file
6
game_payload/include/pe.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void pe_find_section(HMODULE module, const char *section, MEMORY_BASIC_INFORMATION *buf);
|
||||
void *pe_find_entry_point(HMODULE module);
|
||||
7
game_payload/include/tp6.h
Normal file
7
game_payload/include/tp6.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
void tp6_setup_patcher(struct game_data *game, HMODULE thisModule, HMODULE baseModule);
|
||||
5
game_payload/include/utils.h
Normal file
5
game_payload/include/utils.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t utils_file_crc32c(const char *filePath);
|
||||
34
game_payload/meson.build
Normal file
34
game_payload/meson.build
Normal file
@ -0,0 +1,34 @@
|
||||
# Input files
|
||||
sources = [
|
||||
'src/main.c',
|
||||
'src/ntdll.c',
|
||||
'src/ace.c',
|
||||
'src/pe.c',
|
||||
'src/game.c',
|
||||
'src/hi3.c',
|
||||
'src/utils.c',
|
||||
'src/err.c',
|
||||
|
||||
# File withheld to make abuse more difficult
|
||||
'src/tp6.c'
|
||||
]
|
||||
resources = [
|
||||
'res/hi3/glb/allocations.dat',
|
||||
'res/hi3/glb/entries.dat'
|
||||
]
|
||||
|
||||
# Generate resource files for ./res
|
||||
res_files = custom_target(
|
||||
'resources.[ho]',
|
||||
output: [ 'resources.o', 'resources.h' ],
|
||||
input: resources,
|
||||
command: [ gen_res, meson.current_source_dir(), '@OUTPUT0@', '@OUTPUT1@', '@INPUT@' ]
|
||||
)
|
||||
|
||||
shared_library(
|
||||
'game_payload',
|
||||
sources,
|
||||
res_files,
|
||||
include_directories: 'include',
|
||||
name_prefix: ''
|
||||
)
|
||||
BIN
game_payload/res/hi3/glb/allocations.dat
Normal file
BIN
game_payload/res/hi3/glb/allocations.dat
Normal file
Binary file not shown.
BIN
game_payload/res/hi3/glb/entries.dat
Normal file
BIN
game_payload/res/hi3/glb/entries.dat
Normal file
Binary file not shown.
91
game_payload/src/ace.c
Normal file
91
game_payload/src/ace.c
Normal file
@ -0,0 +1,91 @@
|
||||
#include <ntdll.h>
|
||||
#include <pe.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <ace.h>
|
||||
|
||||
static void _dll_notification(ULONG reason, const PLDR_DLL_NOTIFICATION_DATA data, void *context) {
|
||||
if (reason != 1) { // 1 - attach
|
||||
return;
|
||||
}
|
||||
|
||||
// context should be set to the target module name, lowercase
|
||||
wchar_t *targetModuleName = (wchar_t*)context;
|
||||
|
||||
wchar_t lwModuleName[MAX_PATH];
|
||||
wcscpy(lwModuleName, data->Loaded.BaseDllName->Buffer);
|
||||
_wcslwr(lwModuleName);
|
||||
|
||||
if (wcscmp(targetModuleName, lwModuleName) == 0) {
|
||||
// Replace entry point with a stub
|
||||
void *entryPoint = pe_find_entry_point(data->Loaded.DllBase);
|
||||
|
||||
const char ENTRY_POINT_STUB[] = {
|
||||
0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1
|
||||
0xC3 // ret
|
||||
};
|
||||
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(entryPoint, sizeof(ENTRY_POINT_STUB), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
memcpy(entryPoint, ENTRY_POINT_STUB, sizeof(ENTRY_POINT_STUB));
|
||||
|
||||
VirtualProtect(entryPoint, sizeof(ENTRY_POINT_STUB), oldProtect, &oldProtect);
|
||||
}
|
||||
}
|
||||
|
||||
void ace_fake_driver_files() {
|
||||
// They only report presence
|
||||
const char *wdDriverPath = "ACE-BASE.sys";
|
||||
const char *s32DriverPath = "C:\\windows\\system32\\drivers\\ACE-BASE.sys";
|
||||
|
||||
HANDLE wdDriverFile = CreateFileA(wdDriverPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!wdDriverFile) {
|
||||
err_mb_a("Could not create driver file: %s", wdDriverPath);
|
||||
}
|
||||
|
||||
// Just in case
|
||||
HANDLE s32DriverFile = CreateFileA(s32DriverPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!s32DriverFile) {
|
||||
err_mb_a("Could not create driver file: %s", s32DriverPath);
|
||||
}
|
||||
|
||||
CloseHandle(wdDriverFile);
|
||||
CloseHandle(s32DriverFile);
|
||||
}
|
||||
|
||||
HMODULE ace_load_base_module(const char *exeName) {
|
||||
wchar_t baseModuleName[MAX_PATH];
|
||||
swprintf(baseModuleName, MAX_PATH, L"%sbase.dll", exeName);
|
||||
wcslwr(baseModuleName);
|
||||
|
||||
void *cookie;
|
||||
LdrRegisterDllNotification(0, &_dll_notification, baseModuleName, &cookie);
|
||||
|
||||
HMODULE baseModule = LoadLibraryW(baseModuleName);
|
||||
if (!baseModule) {
|
||||
err_mb_w(L"Could not load base module: %ls", baseModuleName);
|
||||
}
|
||||
|
||||
// LoadLibraryA is synchronous; the notification function has already finished executing
|
||||
LdrUnregisterDllNotification(cookie);
|
||||
|
||||
return baseModule;
|
||||
}
|
||||
|
||||
HMODULE ace_load_driver_module() {
|
||||
const char *driverModulePath = "AntiCheatExpert/InGame/x64/ACE-DRV64.dll";
|
||||
|
||||
void *cookie;
|
||||
LdrRegisterDllNotification(0, &_dll_notification, L"ace-drv64.dll", &cookie);
|
||||
|
||||
HMODULE driverModule = LoadLibraryA(driverModulePath);
|
||||
if (!driverModule) {
|
||||
err_mb_a("Could not load driver module: %s", driverModulePath);
|
||||
}
|
||||
|
||||
// LoadLibraryA is synchronous; the notification function has already finished executing
|
||||
LdrUnregisterDllNotification(cookie);
|
||||
|
||||
return driverModule;
|
||||
}
|
||||
26
game_payload/src/err.c
Normal file
26
game_payload/src/err.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <err.h>
|
||||
|
||||
#define DEF_ERROR_FN(name, type, printfn, mbfn, projname) \
|
||||
void name(const type *format, ...) { \
|
||||
va_list args; \
|
||||
va_start(args, format); \
|
||||
\
|
||||
int count = printfn(NULL, 0, format, args) + 1; \
|
||||
\
|
||||
type *buf = malloc(count * sizeof(type)); \
|
||||
printfn(buf, count, format, args); \
|
||||
\
|
||||
mbfn(NULL, buf, projname, MB_OK | MB_ICONERROR); \
|
||||
\
|
||||
va_end(args); \
|
||||
\
|
||||
free(buf); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
|
||||
DEF_ERROR_FN(err_mb_a, char, _vsnprintf, MessageBoxA, "Jadeite Autopatcher")
|
||||
DEF_ERROR_FN(err_mb_w, wchar_t, _vsnwprintf, MessageBoxW, L"Jadeite Autopatcher")
|
||||
19
game_payload/src/game.c
Normal file
19
game_payload/src/game.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <err.h>
|
||||
#include <hi3.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
void game_detect(struct game_data *buf) {
|
||||
wchar_t exePath[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, exePath, MAX_PATH);
|
||||
|
||||
wchar_t *exeName = wcsrchr(exePath, L'\\') + 1;
|
||||
wcslwr(exeName);
|
||||
|
||||
// Only HI3 is supported for now
|
||||
if (wcscmp(exeName, L"bh3.exe") == 0) {
|
||||
hi3_fill_data(buf);
|
||||
} else {
|
||||
err_mb_w(L"Unknown game: %ls", exeName);
|
||||
}
|
||||
}
|
||||
44
game_payload/src/hi3.c
Normal file
44
game_payload/src/hi3.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include <utils.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <hi3.h>
|
||||
|
||||
const char *HI3_NAME = "BH3";
|
||||
const char *HI3_ASSEMBLY_PATH = "BH3_Data/Native/UserAssembly.dll";
|
||||
const wchar_t *HI3_ASSEMBLY_NAME_LWR = L"userassembly.dll";
|
||||
const char *HI3_TP6_SECTION_NAME = ".bh3";
|
||||
const char *HI3_TVM_SECTION_NAME = ".tvm0";
|
||||
|
||||
struct crc_id_pair {
|
||||
uint32_t crc;
|
||||
enum game_id id;
|
||||
};
|
||||
|
||||
const struct crc_id_pair HI3_REGIONS[] = {
|
||||
// Only glb for now
|
||||
// It may be possible to get rid of region-specific data altogether in the future
|
||||
|
||||
{ 0x34bdec99, GAME_HI3_GLB } // glb v6.6.0
|
||||
};
|
||||
|
||||
void hi3_fill_data(struct game_data *buf) {
|
||||
uint32_t crc = utils_file_crc32c("UnityPlayer.dll");
|
||||
|
||||
enum game_id id = GAME_INVALID;
|
||||
for (size_t i = 0; i < sizeof(HI3_REGIONS) / sizeof(struct crc_id_pair); i++) {
|
||||
if (HI3_REGIONS[i].crc == crc) {
|
||||
id = HI3_REGIONS[i].id;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == GAME_INVALID) {
|
||||
err_mb_a("Invalid UnityPlayer.dll checksum: %d", crc);
|
||||
}
|
||||
|
||||
buf->id = id;
|
||||
buf->name = HI3_NAME;
|
||||
buf->assembly_path = HI3_ASSEMBLY_PATH;
|
||||
buf->assembly_name_lwr = HI3_ASSEMBLY_NAME_LWR;
|
||||
buf->tp6_section_name = HI3_TP6_SECTION_NAME;
|
||||
buf->tvm_section_name = HI3_TVM_SECTION_NAME;
|
||||
}
|
||||
33
game_payload/src/main.c
Normal file
33
game_payload/src/main.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntdll.h>
|
||||
#include <ace.h>
|
||||
#include <game.h>
|
||||
#include <tp6.h>
|
||||
#include <utils.h>
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
|
||||
// Only listen to attach
|
||||
if (reason != DLL_PROCESS_ATTACH) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Dynamically link functions from ntdll
|
||||
ntdll_link();
|
||||
|
||||
// Detect which game the user is trying to run
|
||||
struct game_data game;
|
||||
game_detect(&game);
|
||||
|
||||
// Create fake ACE driver files
|
||||
ace_fake_driver_files();
|
||||
|
||||
// Load both ACE modules
|
||||
HMODULE baseModule = ace_load_base_module(game.name);
|
||||
ace_load_driver_module();
|
||||
|
||||
// ...magic
|
||||
tp6_setup_patcher(&game, instance, baseModule);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
11
game_payload/src/ntdll.c
Normal file
11
game_payload/src/ntdll.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <ntdll.h>
|
||||
|
||||
LdrRegisterDllNotification_t LdrRegisterDllNotification;
|
||||
LdrUnregisterDllNotification_t LdrUnregisterDllNotification;
|
||||
|
||||
void ntdll_link() {
|
||||
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
|
||||
|
||||
LdrRegisterDllNotification = (LdrRegisterDllNotification_t)GetProcAddress(ntdll, "LdrRegisterDllNotification");
|
||||
LdrUnregisterDllNotification = (LdrUnregisterDllNotification_t)GetProcAddress(ntdll, "LdrUnregisterDllNotification");
|
||||
}
|
||||
34
game_payload/src/pe.c
Normal file
34
game_payload/src/pe.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pe.h>
|
||||
|
||||
void pe_find_section(HMODULE module, const char *section, MEMORY_BASIC_INFORMATION *buf) {
|
||||
char *cModule = (char*)module;
|
||||
|
||||
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)module;
|
||||
IMAGE_NT_HEADERS64* ntHeaders = (IMAGE_NT_HEADERS64*)(cModule + dosHeader->e_lfanew);
|
||||
|
||||
uint16_t sectionCount = ntHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)(ntHeaders + 1);
|
||||
|
||||
void* targetAddress = 0x0;
|
||||
for (uint16_t i = 0; i < sectionCount; i++) {
|
||||
if (strncmp((char*)sectionHeader->Name, section, 8) == 0) {
|
||||
targetAddress = (void*)(cModule + sectionHeader->VirtualAddress);
|
||||
break;
|
||||
}
|
||||
|
||||
sectionHeader++;
|
||||
}
|
||||
|
||||
VirtualQuery(targetAddress, buf, sizeof(MEMORY_BASIC_INFORMATION));
|
||||
}
|
||||
|
||||
void *pe_find_entry_point(HMODULE module) {
|
||||
char *cModule = (char*)module;
|
||||
|
||||
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)module;
|
||||
IMAGE_NT_HEADERS64* ntHeaders = (IMAGE_NT_HEADERS64*)(cModule + dosHeader->e_lfanew);
|
||||
|
||||
return cModule + ntHeaders->OptionalHeader.AddressOfEntryPoint;
|
||||
}
|
||||
2
game_payload/src/tp6.md
Normal file
2
game_payload/src/tp6.md
Normal file
@ -0,0 +1,2 @@
|
||||
### 1.0.0
|
||||
- First version
|
||||
30
game_payload/src/utils.c
Normal file
30
game_payload/src/utils.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <crc32.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <utils.h>
|
||||
|
||||
uint32_t utils_file_crc32c(const char *filePath) {
|
||||
HANDLE file = CreateFileA(filePath, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!file) {
|
||||
err_mb_a("Could not open file: %s", filePath);
|
||||
}
|
||||
|
||||
LARGE_INTEGER fileSize;
|
||||
GetFileSizeEx(file, &fileSize);
|
||||
|
||||
HANDLE hMap = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
char *map = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!map) {
|
||||
err_mb_a("Could not create file mapping for %s", filePath);
|
||||
}
|
||||
|
||||
uint32_t crc = crc32c(0, (unsigned char*)map, fileSize.QuadPart);
|
||||
|
||||
UnmapViewOfFile(map);
|
||||
CloseHandle(hMap);
|
||||
CloseHandle(file);
|
||||
|
||||
return crc;
|
||||
}
|
||||
Reference in New Issue
Block a user