Add proper binary patcher with version-aware patching system

This commit is contained in:
mithral 2026-10-06 20:36:31 -04:00
parent 5da49a0d37
commit c3062b1d26
5 changed files with 669 additions and 0 deletions

62
config/patches.json Normal file
View file

@ -0,0 +1,62 @@
{
"versions": {
"2027": {
"library": "/usr/autodesk/maya2027/lib/libAdskLicensingSDK.so.10.0.1.66",
"patches": [
{
"description": "License validation check - always return valid",
"signature": "4889F54883EC20",
"replacement": "4889F5C3",
"type": 1
},
{
"description": "License expiry check - disable",
"signature": "7408",
"replacement": "9090",
"type": 0
},
{
"description": "Feature check bypass",
"signature": "0F8500000000",
"replacement": "909090909090",
"type": 0
},
{
"description": "Network license check - offline mode",
"signature": "E800000000",
"replacement": "9090909090",
"type": 0
}
]
},
"2026": {
"library": "/usr/autodesk/maya2026/lib/libAdskLicensingSDK.so.10.0.1.66",
"patches": [
{
"description": "License validation check - always return valid",
"signature": "4889F54883EC20",
"replacement": "4889F5C3",
"type": 1
},
{
"description": "License expiry check - disable",
"signature": "7408",
"replacement": "9090",
"type": 0
}
]
},
"2025": {
"library": "/usr/autodesk/maya2025/lib/libAdskLicensingSDK.so.10.0.1.66",
"patches": [
{
"description": "License validation check - always return valid",
"signature": "4889F54883EC20",
"replacement": "4889F5C3",
"type": 1
}
]
}
},
"update_url": "https://git.mithraic.cloud/ad3laid3/FUA.git/raw/branch/main/config/patches.json"
}

82
core/adp_stub.c Normal file
View file

@ -0,0 +1,82 @@
/*
* Autodesk Desktop Platform (ADP) Stub Library
* Provides minimal ADP implementation to prevent "ADP Failed to initialize" error
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#define DEBUG_LOG 1
#define LOG(fmt, ...) do { \
if (DEBUG_LOG) { \
fprintf(stderr, "[ADP-STUB] " fmt, ##__VA_ARGS__); \
fflush(stderr); \
} \
} while(0)
/* ADP Initialization function - return success */
int __attribute__((visibility("default"))) ADP_Initialize(void) {
LOG("ADP_Initialize() called - returning success\n");
return 0; /* Success */
}
void __attribute__((visibility("default"))) ADP_Shutdown(void) {
LOG("ADP_Shutdown() called\n");
}
/* ADP User/Session functions */
int __attribute__((visibility("default"))) ADP_IsUserLoggedIn(void) {
LOG("ADP_IsUserLoggedIn() called - returning true\n");
return 1; /* Logged in */
}
const char* __attribute__((visibility("default"))) ADP_GetUserName(void) {
LOG("ADP_GetUserName() called\n");
return "MayaUser";
}
const char* __attribute__((visibility("default"))) ADP_GetUserId(void) {
LOG("ADP_GetUserId() called\n");
return "maya-stub-user-id";
}
/* ADP License functions */
int __attribute__((visibility("default"))) ADP_IsLicenseValid(void) {
LOG("ADP_IsLicenseValid() called - returning true\n");
return 1; /* Valid */
}
int __attribute__((visibility("default"))) ADP_AcquireLicense(const char* product) {
LOG("ADP_AcquireLicense() called for: %s - returning success\n", product ? product : "unknown");
return 0; /* Success */
}
void __attribute__((visibility("default"))) ADP_ReleaseLicense(void) {
LOG("ADP_ReleaseLicense() called\n");
}
/* ADP Cloud/Service functions */
int __attribute__((visibility("default"))) ADP_IsCloudServiceAvailable(void) {
LOG("ADP_IsCloudServiceAvailable() called - returning false (offline mode)\n");
return 0; /* Not available - offline mode */
}
/* Generic success function for any unknown ADP calls */
int __attribute__((visibility("default"))) ADP_CheckStatus(void) {
LOG("ADP_CheckStatus() called - returning success\n");
return 0;
}
__attribute__((constructor))
void adp_stub_init(void) {
LOG("=== ADP Stub Library Loaded ===\n");
LOG("This stub provides minimal ADP functionality to prevent initialization errors\n");
}
__attribute__((destructor))
void adp_stub_fini(void) {
LOG("=== ADP Stub Library Unloaded ===\n");
}

297
core/maya_patcher.c Normal file
View file

@ -0,0 +1,297 @@
/*
* 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;
}

137
patcher/apply_patches.py Executable file
View file

@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""
Maya License Patcher - Python Implementation
Properly patches Maya licensing libraries with version-aware binary patches
"""
import sys
import os
import json
import struct
import shutil
from pathlib import Path
def log(msg):
print(f"[PATCHER] {msg}")
def hex_to_bytes(hex_str):
"""Convert hex string to bytes"""
return bytes.fromhex(hex_str.replace(':', '').replace(' ', ''))
def find_signature(buffer, signature):
"""Find signature in buffer, return offset or -1"""
sig = hex_to_bytes(signature)
for i in range(len(buffer) - len(sig) + 1):
if buffer[i:i+len(sig)] == sig:
return i
return -1
def apply_patch(buffer, patch):
"""Apply a single patch to buffer, return True if applied"""
sig = patch.get('signature', '')
repl = patch.get('replacement', '')
desc = patch.get('description', 'Unknown')
if not sig or not repl:
log(f" Skipping patch with missing data: {desc}")
return False
sig_bytes = hex_to_bytes(sig)
repl_bytes = hex_to_bytes(repl)
offset = find_signature(buffer, sig)
if offset == -1:
log(f" NOT FOUND: {desc}")
return False
# Apply patch
buffer[offset:offset+len(repl_bytes)] = repl_bytes
log(f" APPLIED: {desc} at offset 0x{offset:x}")
return True
def patch_library(lib_path, patches):
"""Patch a library file with given patches"""
log(f"Patching: {lib_path}")
# Read library
with open(lib_path, 'rb') as f:
buffer = bytearray(f.read())
log(f" File size: {len(buffer):,} bytes")
# Create backup
backup_path = f"{lib_path}.backup"
shutil.copy2(lib_path, backup_path)
log(f" Backup created: {backup_path}")
# Apply patches
applied = 0
not_found = 0
for patch in patches:
if apply_patch(buffer, patch):
applied += 1
else:
not_found += 1
# Write patched library
with open(lib_path, 'wb') as f:
f.write(buffer)
log(f" Patches applied: {applied}, not found: {not_found}")
return applied, not_found
def load_patch_db(db_path):
"""Load patch database from JSON"""
with open(db_path, 'r') as f:
return json.load(f)
def main():
if len(sys.argv) < 3:
print("Usage: apply_patches.py <version> <patch_db.json>")
sys.exit(1)
version = sys.argv[1]
patch_db_path = sys.argv[2]
log("=== Maya License Patcher ===")
log(f"Target version: {version}")
# Load patch database
db = load_patch_db(patch_db_path)
if version not in db.get('versions', {}):
log(f"ERROR: Version {version} not found in patch database")
sys.exit(1)
version_data = db['versions'][version]
lib_path = version_data.get('library')
patches = version_data.get('patches', [])
if not lib_path:
log("ERROR: No library path specified")
sys.exit(1)
if not os.path.exists(lib_path):
log(f"ERROR: Library not found: {lib_path}")
sys.exit(1)
log(f"Library: {lib_path}")
log(f"Patches to apply: {len(patches)}")
log("")
# Apply patches
applied, not_found = patch_library(lib_path, patches)
log("")
log("=== Patching Complete ===")
if applied > 0:
log(f"Successfully applied {applied} patches")
sys.exit(0)
else:
log("No patches were applied!")
sys.exit(1)
if __name__ == "__main__":
main()

91
patcher/auto_patch.sh Executable file
View file

@ -0,0 +1,91 @@
#!/bin/bash
#
# Maya Auto-Patcher
# Detects version, applies patches, verifies installation
#
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
PATCHER_BIN="$PROJECT_DIR/core/maya_patcher"
PATCH_DB="$PROJECT_DIR/config/patches.json"
echo "=== Maya Auto-Patcher ==="
echo ""
# Detect Maya version
echo "Step 1: Detecting Maya installation..."
MAYA_INFO=$(python3 "$PROJECT_DIR/updater/detect_maya.py" 2>/dev/null | python3 -c "
import sys, json
for line in sys.stdin:
if line.startswith('{'):
data = json.loads(line)
if data.get('found') and data.get('installations'):
inst = data['installations'][0]
print(f\"{inst['version']}|{inst['path']}\")
sys.exit(0)
print('NOT_FOUND|')
")
if [[ "$MAYA_INFO" == "NOT_FOUND|"* ]]; then
echo "ERROR: No Maya installation found!"
exit 1
fi
MAYA_VERSION=$(echo "$MAYA_INFO" | cut -d'|' -f1)
MAYA_PATH=$(echo "$MAYA_INFO" | cut -d'|' -f2)
echo " Found Maya $MAYA_VERSION at $MAYA_PATH"
echo ""
# Check if patcher is built
if [[ ! -f "$PATCHER_BIN" ]]; then
echo "Step 2: Building patcher..."
cd "$PROJECT_DIR/core"
gcc -Wall -o maya_patcher maya_patcher.c -ljson-c
echo " Patcher built successfully"
echo ""
fi
# Apply patches
echo "Step 3: Applying patches for Maya $MAYA_VERSION..."
$PATCHER_BIN "$MAYA_VERSION" "$PATCH_DB"
PATCH_RESULT=$?
if [[ $PATCH_RESULT -eq 0 ]]; then
echo ""
echo "✓ Patches applied successfully!"
else
echo ""
echo "✗ Some patches failed to apply"
exit 1
fi
# Verify patches
echo ""
echo "Step 4: Verifying patches..."
LIB_PATH="/usr/autodesk/maya${MAYA_VERSION}/lib/libAdskLicensingSDK.so.10.0.1.66"
if [[ -f "$LIB_PATH.backup" ]]; then
echo " Backup exists: $LIB_PATH.backup"
# Compare files to ensure they're different
if ! cmp -s "$LIB_PATH" "$LIB_PATH.backup"; then
echo " ✓ Library has been modified (different from backup)"
else
echo " ✗ Library appears unchanged!"
exit 1
fi
else
echo " Warning: No backup found"
fi
echo ""
echo "=== Auto-Patch Complete ==="
echo ""
echo "Maya $MAYA_VERSION has been patched."
echo "You can now launch Maya without license validation."
echo ""
echo "To restore original library:"
echo " cp $LIB_PATH.backup $LIB_PATH"