#!/bin/bash # LSPosed A16 Fix — apply the JNINativeInterface runtime-size patch # Usage: bash apply_a16_fix.sh # Example: bash apply_a16_fix.sh ~/LSPosed set -e REPO="${1:-.}" HEADER="$REPO/zygisk/src/main/cpp/include/ipc_bridge.h" CPP="$REPO/zygisk/src/main/cpp/ipc_bridge.cpp" if [ ! -f "$HEADER" ] || [ ! -f "$CPP" ]; then echo "ERROR: LSPosed source not found at $REPO" echo "Clone first: git clone --depth 1 --recursive https://github.com/JingMatrix/LSPosed.git" exit 1 fi echo "=== Applying A16 JNINativeInterface runtime-size fix ===" # === Patch 1: ipc_bridge.h — change stack-allocated table to pointer === echo "[1/2] Patching $HEADER ..." # Create backup cp "$HEADER" "$HEADER.bak" python3 -c " import re with open('$HEADER', 'r') as f: content = f.read() old = ' // A complete copy of the original JNI function table, with our hook installed.\n JNINativeInterface native_interface_hook_{};' new = ''' // A complete copy of the original JNI function table, with our hook installed. // Uses runtime-size allocation to support A16+ where ART added new JNI functions. JNINativeInterface* native_interface_hook_ = nullptr; size_t table_size_ = 0;''' if old in content: content = content.replace(old, new) with open('$HEADER', 'w') as f: f.write(content) print(' ✓ ipc_bridge.h patched') else: print(' ⚠ ipc_bridge.h — pattern not found, may already be patched') " # === Patch 2: ipc_bridge.cpp — use reserved0 for runtime size === echo "[2/2] Patching $CPP ..." cp "$CPP" "$CPP.bak" python3 -c " with open('$CPP', 'r') as f: content = f.read() old = ''' // --- Step 3: Install the hook --- // Make a full copy of the existing JNI function table. memcpy(&native_interface_hook_, env->functions, sizeof(JNINativeInterface)); // Store a pointer to the original function we are about to replace. call_boolean_method_v_backup_ = env->functions->CallBooleanMethodV; // Overwrite the function pointer in our copy with our hook. native_interface_hook_.CallBooleanMethodV = &CallBooleanMethodV_Hook; // Atomically swap the process's JNI function table with our modified one. set_table_override_func(&native_interface_hook_);''' new = ''' // --- Step 3: Install the hook --- // Get the ACTUAL runtime size of the JNI function table. // Android 16 ART adds new JNI functions that increase the table size. // Using compile-time sizeof(JNINativeInterface) truncates the copy, // causing crashes when ART calls new functions via garbage pointers. table_size_ = (size_t)env->functions->reserved0; if (table_size_ == 0 || table_size_ < sizeof(JNINativeInterface)) { table_size_ = sizeof(JNINativeInterface); } LOGI(\"JNI table runtime size: %zu (compile-time: %zu)\", table_size_, sizeof(JNINativeInterface)); // Allocate and copy with runtime size native_interface_hook_ = (JNINativeInterface*)malloc(table_size_); if (!native_interface_hook_) { LOGE(\"Failed to allocate JNI hook table.\"); return; } memcpy(native_interface_hook_, env->functions, table_size_); // Store a pointer to the original function we are about to replace. call_boolean_method_v_backup_ = native_interface_hook_->CallBooleanMethodV; // Overwrite the function pointer in our copy with our hook. native_interface_hook_->CallBooleanMethodV = &CallBooleanMethodV_Hook; // Atomically swap the process's JNI function table with our modified one. set_table_override_func(native_interface_hook_);''' if old in content: content = content.replace(old, new) with open('$CPP', 'w') as f: f.write(content) print(' ✓ ipc_bridge.cpp patched') else: print(' ⚠ ipc_bridge.cpp — pattern not found, may already be patched') " echo "" echo "=== Done. Building ===" echo "" echo "To build:" echo " export ANDROID_HOME=/path/to/android-sdk" echo " cd $REPO" echo " ./gradlew :zygisk:assembleRelease" echo "" echo "Output: zygisk/build/outputs/apk/release/zygisk-release.apk" echo "The fixed zygisk .so inside can replace:" echo " /data/adb/modules/zygisk_vector/zygisk/arm64-v8a.so"