apply_a16_fix.sh 4.13 KB
#!/bin/bash
# LSPosed A16 Fix — apply the JNINativeInterface runtime-size patch
# Usage: bash apply_a16_fix.sh <path-to-LSPosed-repo>
# 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"