297 lines
9.1 KiB
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;
|
|
}
|