FUA/core/maya_patcher.c

297 lines
9.1 KiB
C

/*
* Maya License Patcher - Binary Patching Engine
*
* Properly patches Maya licensing libraries by:
* 1. Detecting Maya version
* 2. Loading version-specific patch signatures
* 3. Applying binary patches to licensing functions
* 4. Verifying patches were applied correctly
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <elf.h>
#include <json-c/json.h>
#define LOG(fmt, ...) printf("[PATCHER] " fmt, ##__VA_ARGS__)
/* Patch operation types */
typedef enum {
PATCH_NOP, /* Replace with NOP instructions */
PATCH_RET, /* Replace with immediate return */
PATCH_JUMP, /* Replace with unconditional jump */
PATCH_VALUE /* Replace specific bytes with new value */
} PatchType;
/* Single patch definition */
typedef struct {
const char* signature; /* Hex signature to find */
const char* replacement; /* Hex replacement bytes */
size_t offset; /* Optional: fixed offset from start */
PatchType type;
const char* description;
} Patch;
/* Version-specific patch set */
typedef struct {
const char* version;
const char* library_path;
Patch* patches;
int patch_count;
} PatchSet;
/* Load patch signatures from JSON */
PatchSet* load_patch_set(const char* json_file, const char* version) {
struct json_object* root = json_object_from_file(json_file);
if (!root) {
LOG("Failed to load patch signatures from %s\n", json_file);
return NULL;
}
struct json_object* versions = json_object_object_get(root, "versions");
if (!versions) {
LOG("No versions found in patch database\n");
json_object_put(root);
return NULL;
}
struct json_object* ver_obj = json_object_object_get(versions, version);
if (!ver_obj) {
LOG("Version %s not found in patch database\n", version);
json_object_put(root);
return NULL;
}
/* Parse version data */
struct json_object* lib_path_obj = json_object_object_get(ver_obj, "library");
struct json_object* patches_obj = json_object_object_get(ver_obj, "patches");
if (!lib_path_obj || !patches_obj) {
LOG("Invalid patch data for version %s\n", version);
json_object_put(root);
return NULL;
}
/* Allocate patch set */
int patch_count = json_object_array_length(patches_obj);
PatchSet* patch_set = malloc(sizeof(PatchSet));
patch_set->version = strdup(version);
patch_set->library_path = strdup(json_object_get_string(lib_path_obj));
patch_set->patches = malloc(sizeof(Patch) * patch_count);
patch_set->patch_count = patch_count;
/* Parse each patch */
for (int i = 0; i < patch_count; i++) {
struct json_object* patch_obj = json_object_array_get_idx(patches_obj, i);
struct json_object* sig_obj = json_object_object_get(patch_obj, "signature");
struct json_object* repl_obj = json_object_object_get(patch_obj, "replacement");
struct json_object* desc_obj = json_object_object_get(patch_obj, "description");
struct json_object* type_obj = json_object_object_get(patch_obj, "type");
Patch* p = &patch_set->patches[i];
p->signature = sig_obj ? strdup(json_object_get_string(sig_obj)) : NULL;
p->replacement = repl_obj ? strdup(json_object_get_string(repl_obj)) : NULL;
p->description = desc_obj ? strdup(json_object_get_string(desc_obj)) : "Unknown patch";
p->type = type_obj ? json_object_get_int(type_obj) : PATCH_NOP;
p->offset = 0;
struct json_object* offset_obj = json_object_object_get(patch_obj, "offset");
if (offset_obj) {
p->offset = json_object_get_int64(offset_obj);
}
}
json_object_put(root);
return patch_set;
}
/* Convert hex string to bytes */
int hex_to_bytes(const char* hex, unsigned char* bytes, size_t* len) {
*len = 0;
if (!hex || !*hex) return -1;
size_t hex_len = strlen(hex);
if (hex_len % 2 != 0) return -1;
*len = hex_len / 2;
for (size_t i = 0; i < hex_len; i += 2) {
unsigned int byte;
if (sscanf(hex + i, "%2x", &byte) != 1) return -1;
bytes[*len - (hex_len - i) / 2] = (unsigned char)byte;
}
return 0;
}
/* Find signature in buffer */
unsigned char* find_signature(unsigned char* buffer, size_t buf_len,
unsigned char* sig, size_t sig_len) {
if (sig_len > buf_len) return NULL;
for (size_t i = 0; i <= buf_len - sig_len; i++) {
if (memcmp(buffer + i, sig, sig_len) == 0) {
return buffer + i;
}
}
return NULL;
}
/* Apply a single patch to memory-mapped file */
int apply_patch(unsigned char* buffer, size_t buf_len, Patch* patch) {
if (!patch->signature || !patch->replacement) {
LOG(" Skipping patch with missing signature/replacement\n");
return 0;
}
unsigned char sig_bytes[256], repl_bytes[256];
size_t sig_len, repl_len;
if (hex_to_bytes(patch->signature, sig_bytes, &sig_len) < 0) {
LOG(" Invalid signature hex for: %s\n", patch->description);
return -1;
}
if (hex_to_bytes(patch->replacement, repl_bytes, &repl_len) < 0) {
LOG(" Invalid replacement hex for: %s\n", patch->description);
return -1;
}
/* Find signature */
unsigned char* location = find_signature(buffer, buf_len, sig_bytes, sig_len);
if (!location) {
LOG(" NOT FOUND: %s\n", patch->description);
return 0;
}
/* Apply patch */
memcpy(location, repl_bytes, repl_len);
LOG(" APPLIED: %s at offset 0x%lx\n", patch->description, location - buffer);
return 1;
}
/* Patch a Maya library file */
int patch_library(const char* lib_path, PatchSet* patch_set) {
LOG("Patching: %s\n", lib_path);
LOG("Version: %s\n", patch_set->version);
LOG("Applying %d patches...\n", patch_set->patch_count);
/* Open file */
int fd = open(lib_path, O_RDWR);
if (fd < 0) {
LOG("Failed to open %s: %s\n", lib_path, strerror(errno));
return -1;
}
/* Get file size */
struct stat st;
if (fstat(fd, &st) < 0) {
LOG("Failed to stat %s\n", lib_path);
close(fd);
return -1;
}
size_t file_size = st.st_size;
/* Memory map the file */
unsigned char* buffer = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buffer == MAP_FAILED) {
LOG("Failed to mmap %s\n", lib_path);
close(fd);
return -1;
}
/* Create backup */
char backup_path[512];
snprintf(backup_path, sizeof(backup_path), "%s.backup", lib_path);
if (copy_file(lib_path, backup_path) < 0) {
LOG("Warning: Failed to create backup at %s\n", backup_path);
} else {
LOG("Backup created: %s\n", backup_path);
}
/* Apply all patches */
int applied = 0, failed = 0;
for (int i = 0; i < patch_set->patch_count; i++) {
int result = apply_patch(buffer, file_size, &patch_set->patches[i]);
if (result < 0) failed++;
else if (result > 0) applied++;
}
/* Sync and cleanup */
msync(buffer, file_size, MS_SYNC);
munmap(buffer, file_size);
close(fd);
LOG("Patching complete: %d applied, %d failed, %d not found\n",
applied, failed, patch_set->patch_count - applied - failed);
return failed;
}
/* Copy file helper */
int copy_file(const char* src, const char* dst) {
int src_fd = open(src, O_RDONLY);
if (src_fd < 0) return -1;
int dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd < 0) {
close(src_fd);
return -1;
}
char buf[8192];
ssize_t n;
while ((n = read(src_fd, buf, sizeof(buf))) > 0) {
if (write(dst_fd, buf, n) != n) {
close(src_fd);
close(dst_fd);
return -1;
}
}
close(src_fd);
close(dst_fd);
return 0;
}
/* Main entry point */
int main(int argc, char* argv[]) {
if (argc < 3) {
printf("Usage: %s <version> <patch_db.json>\n", argv[0]);
return 1;
}
const char* version = argv[1];
const char* patch_db = argv[2];
LOG("=== Maya License Patcher ===\n");
LOG("Target version: %s\n", version);
/* Load patch set */
PatchSet* patch_set = load_patch_set(patch_db, version);
if (!patch_set) {
LOG("Failed to load patch set for version %s\n", version);
return 1;
}
/* Apply patches */
int result = patch_library(patch_set->library_path, patch_set);
/* Cleanup */
for (int i = 0; i < patch_set->patch_count; i++) {
free((void*)patch_set->patches[i].signature);
free((void*)patch_set->patches[i].replacement);
free((void*)patch_set->patches[i].description);
}
free(patch_set->patches);
free((void*)patch_set->library_path);
free((void*)patch_set->version);
free(patch_set);
LOG("=== Patching Complete ===\n");
return result;
}