diff --git a/libs/build_deps.sh b/libs/build_deps.sh index fdb24d5..9840d3f 100755 --- a/libs/build_deps.sh +++ b/libs/build_deps.sh @@ -3,10 +3,25 @@ set -euo pipefail # Builds shadowhook as a static library for Android arm64. # Produces: libs/libshadowhook.a and libs/libshadowhook_nothing.so +# +# Skips the build entirely if both outputs already exist. Set FORCE_REBUILD=1 +# to rebuild anyway (e.g. after changing CFLAGS, the toolchain, or the +# shadowhook sources themselves). SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" BUILD_DIR="$PROJECT_DIR/build/deps" + +OUT_DIR="$SCRIPT_DIR" +SH="$SCRIPT_DIR/shadowhook/shadowhook/src/main/cpp" + +if [ "${FORCE_REBUILD:-0}" != "1" ] \ + && [ -f "$OUT_DIR/libshadowhook.a" ] \ + && [ -f "$OUT_DIR/libshadowhook_nothing.so" ]; then + echo "=== shadowhook already built, skipping (FORCE_REBUILD=1 to rebuild) ===" + exit 0 +fi + mkdir -p "$BUILD_DIR" # --- Auto-detect Android NDK (same logic as build.sh) --- @@ -47,81 +62,67 @@ TOOLCHAIN_DIR="$(dirname "$CC")" AR="$TOOLCHAIN_DIR/llvm-ar" CFLAGS="-O2 -ffunction-sections -fdata-sections -fPIC -DANDROID -Wall -Wextra -Wno-unused-parameter -Wno-unused-function" -OUT_DIR="$SCRIPT_DIR" +SH_SRCS=( + "$SH/sh_elf.c" + "$SH/sh_enter.c" + "$SH/sh_hub.c" + "$SH/sh_island.c" + "$SH/sh_linker.c" + "$SH/sh_recorder.c" + "$SH/sh_safe.c" + "$SH/sh_switch.c" + "$SH/sh_task.c" + "$SH/sh_xdl.c" + "$SH/shadowhook.c" + "$SH/arch/arm64/sh_a64.c" + "$SH/arch/arm64/sh_inst.c" + "$SH/arch/arm64/sh_glue.S" + "$SH/common/bytesig.c" + "$SH/common/sh_errno.c" + "$SH/common/sh_log.c" + "$SH/common/sh_ref.c" + "$SH/common/sh_trampo.c" + "$SH/common/sh_util.c" + "$SH/third_party/xdl/xdl.c" + "$SH/third_party/xdl/xdl_iterate.c" + "$SH/third_party/xdl/xdl_linker.c" + "$SH/third_party/xdl/xdl_lzma.c" + "$SH/third_party/xdl/xdl_util.c" +) + +SH_INCLUDES=( + "-I$SH" + "-I$SH/include" + "-I$SH/arch/arm64" + "-I$SH/common" + "-I$SH/third_party/xdl" + "-I$SH/third_party/bsd" + "-I$SH/third_party/lss" +) + +NOTHING_SRC="$SH/nothing/sh_nothing.c" echo "=== Building shadowhook (arm64-v8a) ===" -SH="$SCRIPT_DIR/shadowhook/shadowhook/src/main/cpp" - -SH_SRCS=" -$SH/sh_elf.c -$SH/sh_enter.c -$SH/sh_hub.c -$SH/sh_island.c -$SH/sh_linker.c -$SH/sh_recorder.c -$SH/sh_safe.c -$SH/sh_switch.c -$SH/sh_task.c -$SH/sh_xdl.c -$SH/shadowhook.c -$SH/arch/arm64/sh_a64.c -$SH/arch/arm64/sh_inst.c -$SH/arch/arm64/sh_glue.S -$SH/common/bytesig.c -$SH/common/sh_errno.c -$SH/common/sh_log.c -$SH/common/sh_ref.c -$SH/common/sh_trampo.c -$SH/common/sh_util.c -$SH/third_party/xdl/xdl.c -$SH/third_party/xdl/xdl_iterate.c -$SH/third_party/xdl/xdl_linker.c -$SH/third_party/xdl/xdl_lzma.c -$SH/third_party/xdl/xdl_util.c -" - -SH_INCLUDES=" --I$SH --I$SH/include --I$SH/arch/arm64 --I$SH/common --I$SH/third_party/xdl --I$SH/third_party/bsd --I$SH/third_party/lss -" - -SH_OBJS="" -for src in $SH_SRCS; do - [ -z "$src" ] && continue - src=$(echo "$src" | xargs) +SH_OBJS=() +for src in "${SH_SRCS[@]}"; do if [ ! -f "$src" ]; then echo "ERROR: shadowhook source not found: $src" exit 1 fi - basename=$(echo "$src" | sed "s|$SH/||" | tr '/' '_') - obj="$BUILD_DIR/sh_${basename%.c}.o" - obj="${obj%.S.o}.o" + rel="${src#"$SH"/}" + flat="$(echo "$rel" | tr '/' '_')" + obj="$BUILD_DIR/sh_${flat%.*}.o" - if [[ "$src" == *.S ]]; then - "$CC" $CFLAGS $SH_INCLUDES -c "$src" -o "$obj" - else - "$CC" $CFLAGS $SH_INCLUDES -c "$src" -o "$obj" - fi - SH_OBJS="$SH_OBJS $obj" + "$CC" $CFLAGS "${SH_INCLUDES[@]}" -c "$src" -o "$obj" + SH_OBJS+=("$obj") done -if [ -z "$SH_OBJS" ]; then - echo "ERROR: shadowhook produced no object files." - exit 1 -fi - -"$AR" rcs "$OUT_DIR/libshadowhook.a" $SH_OBJS +"$AR" rcs "$OUT_DIR/libshadowhook.a" "${SH_OBJS[@]}" SH_SIZE=$(wc -c < "$OUT_DIR/libshadowhook.a" 2>/dev/null || echo "unknown") echo " Built $OUT_DIR/libshadowhook.a ($SH_SIZE bytes)" -NOTHING_SRC="$SH/nothing/sh_nothing.c" if [ ! -f "$NOTHING_SRC" ]; then echo "ERROR: ShadowHook companion source not found: $NOTHING_SRC" exit 1 @@ -133,4 +134,4 @@ fi -Wl,--strip-all -Wl,--as-needed -Wl,--hash-style=sysv \ -Wl,--build-id=none \ -o "$OUT_DIR/libshadowhook_nothing.so" "$NOTHING_SRC" -echo " Built $OUT_DIR/libshadowhook_nothing.so" +echo " Built $OUT_DIR/libshadowhook_nothing.so" \ No newline at end of file diff --git a/script/build.sh b/script/build.sh index 4d2d022..8e3585c 100755 --- a/script/build.sh +++ b/script/build.sh @@ -95,6 +95,7 @@ echo "✅ Qt libraries ready at $QT_LIB_DIR" # --- Step 2: Compile native hooking library --- echo "[2/2] Compiling $SO_NAME..." +# shellcheck disable=SC2086 "$CC" -shared -fPIC -O2 -std=c++17 -stdlib=libc++ -fno-exceptions -fno-rtti \ -DQT_NO_VERSION_TAGGING \ -o "$BUILD_DIR/$SO_NAME" \ @@ -120,4 +121,4 @@ echo "📍 Library: $BUILD_DIR/$SO_NAME ($(ls -lh "$BUILD_DIR/$SO_NAME" | awk '{ echo "" echo "🚀 To inject into the reMarkable application:" echo " ./script/inject.sh" -echo "" +echo "" \ No newline at end of file diff --git a/script/inject.sh b/script/inject.sh index 26c123c..115f381 100755 --- a/script/inject.sh +++ b/script/inject.sh @@ -33,6 +33,17 @@ KEYSTORE="$PROJECT_DIR/config/rmhook.keystore" KEYSTORE_PASS="rmhook" KEY_ALIAS="rmhook" +# --- Portable in-place sed (GNU sed vs BSD/macOS sed take -i differently) --- +sed_inplace() { + if sed --version >/dev/null 2>&1; then + # GNU sed + sed -i "$@" + else + # BSD/macOS sed requires an explicit (possibly empty) backup suffix + sed -i '' "$@" + fi +} + usage() { echo "Usage: $0 [apk_dir]" echo "" @@ -56,6 +67,8 @@ fi # Clean and create output directory rm -rf "$OUTPUT_DIR" mkdir -p "$OUTPUT_DIR" +rm -rf "$BUILD_DIR/base" "$BUILD_DIR/split_arm64" "$BUILD_DIR/split_xxhdpi" +rm -f "$BUILD_DIR"/*.apk # --- Step 1: Decompile and patch base.apk (smali injection only, no resources) --- echo "[1/4] Patching base.apk (smali injection)..." @@ -69,7 +82,7 @@ if [ ! -f "$MAIN_SMALI" ]; then fi # Insert System.loadLibrary("rmhook") at the very start of -sed -i '' '/.method static constructor ()V/{ +sed_inplace '/.method static constructor ()V/{ n /.locals/{ a\ @@ -80,6 +93,12 @@ a\ } }' "$MAIN_SMALI" +if ! grep -q 'invoke-static {v0}, Ljava/lang/System;->loadLibrary' "$MAIN_SMALI"; then + echo "❌ Failed to inject loadLibrary(\"rmhook\") into MainActivity.smali" + echo " The block's shape may have changed - inspect $MAIN_SMALI manually." + exit 1 +fi + echo "✅ Injected System.loadLibrary(\"rmhook\") into MainActivity." apktool b "$BUILD_DIR/base" -o "$BUILD_DIR/base_patched.apk" @@ -100,8 +119,8 @@ cp "$LIBS_DIR/libshadowhook_nothing.so" "$BUILD_DIR/split_arm64/lib/arm64-v8a/" rm -rf "$BUILD_DIR/split_arm64/META-INF" -(cd "$BUILD_DIR/split_arm64" && zip -q -r -0 "$BUILD_DIR/split_arm64_raw.apk" . -x "*.so") -(cd "$BUILD_DIR/split_arm64" && zip -q -r "$BUILD_DIR/split_arm64_raw.apk" lib/) +# Store everything uncompressed (-0), including the native libraries. +(cd "$BUILD_DIR/split_arm64" && zip -q -r -0 "$BUILD_DIR/split_arm64_raw.apk" .) # --- Step 3: Align APKs --- echo "[3/4] Aligning APKs..." @@ -160,7 +179,8 @@ echo "" echo "🚀 To install on your reMarkable:" echo " adb uninstall com.remarkable.mobile" echo " adb install-multiple --no-streaming $OUTPUT_DIR/base.apk $OUTPUT_DIR/split_config.arm64_v8a.apk $OUTPUT_DIR/split_config.xxhdpi.apk" +echo " adb push rmhook.conf /sdcard/Android/data/com.remarkable.mobile/files/rmhook.conf" echo "" echo "🔍 To debug:" echo " adb logcat -s 'RMHook:*'" -echo "" +echo "" \ No newline at end of file