diff --git a/config/patches.json b/config/patches.json new file mode 100644 index 0000000..7205d64 --- /dev/null +++ b/config/patches.json @@ -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" +} diff --git a/core/adp_stub.c b/core/adp_stub.c new file mode 100644 index 0000000..732f3c2 --- /dev/null +++ b/core/adp_stub.c @@ -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 +#include +#include +#include + +#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"); +} diff --git a/core/maya_patcher.c b/core/maya_patcher.c new file mode 100644 index 0000000..983c276 --- /dev/null +++ b/core/maya_patcher.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 \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; +} diff --git a/patcher/apply_patches.py b/patcher/apply_patches.py new file mode 100755 index 0000000..6b5d7d3 --- /dev/null +++ b/patcher/apply_patches.py @@ -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 ") + 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() diff --git a/patcher/auto_patch.sh b/patcher/auto_patch.sh new file mode 100755 index 0000000..b34a192 --- /dev/null +++ b/patcher/auto_patch.sh @@ -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"