diff --git a/docs/source/app_decoding.rst b/docs/source/app_decoding.rst
index a5261d2..39db2d2 100644
--- a/docs/source/app_decoding.rst
+++ b/docs/source/app_decoding.rst
@@ -100,7 +100,7 @@ To manually capture the logs follow these steps:
6. Perform the actions required to get the packets needed for analysis (add device, send commands, etc).
.. note::
- This process is incredibly time sensitive, it may take multiple attempts to get it right.
+ This process is incredibly time sensitive, it may take multiple attempts (10+) to get it right.
run.sh
@@ -111,7 +111,7 @@ and in addition writes the logs to a file as well as the console. This is
the recommend approach.
.. note::
- The app must not be running when you start the script.
+ The app must **not** be running when you start the script.
This script performs the following actions:
diff --git a/scripts/frida.js b/scripts/frida.js
index c8bac48..56387f2 100644
--- a/scripts/frida.js
+++ b/scripts/frida.js
@@ -2,33 +2,15 @@
* Script name : frida.js
* Description : Frida script for preventing exit and extracting shared preferences
* Author : Harvey Lelliott (@flip-dots)
- * Date : 23/03/26
+ * Date : 16/07/26
*
* License : MIT
- * Revision : 1.0.0
+ * Revision : 1.1.0
*
* This script is based off https://codeshare.frida.re/@ninjadiary/frinja---sharedpreferences/
* but with additional code to prevent the anti-tamper mechanism from killing the app.
*/
-// Prevent anti-tamper mechanism from fully killing app
-setImmediate(function() {
- Java.perform(function() {
- var System = Java.use('java.lang.System');
- var Process = Java.use('android.os.Process');
-
- // Stop System.exit()
- System.exit.implementation = function(code) {
- console.log("[!] Intercepted exit (" + code + ")");
- };
-
- // Stop Process.killProcess()
- Process.killProcess.implementation = function(pid) {
- console.log("[!] Intercepted killProcess for PID: " + pid);
- };
- });
-});
-
// Log any reads/writes to shared preferences
// From: https://codeshare.frida.re/@ninjadiary/frinja---sharedpreferences/
setImmediate(function() {
@@ -181,3 +163,84 @@ Java.perform(function () {
};
});
});
+
+
+/**********************
+ * *
+ * Anti-Tamper Bypass *
+ * *
+ **********************/
+
+
+// Prevent anti-tamper mechanism from killing the app in Java
+setImmediate(function() {
+ Java.perform(function() {
+ console.log("Tampering with anti-tamper protection using Java API...");
+
+ var System = Java.use('java.lang.System');
+ var Process = Java.use('android.os.Process');
+
+ // Stop System.exit()
+ System.exit.implementation = function(code) {
+ console.log("[!] Intercepted exit (" + code + ")");
+ };
+
+ // Stop Process.killProcess()
+ Process.killProcess.implementation = function(pid) {
+ console.log("[!] Intercepted killProcess for PID: " + pid);
+ };
+ });
+});
+
+
+// Prevent anti-tamper mechanism from killing the app in C
+function blockNativeExit() {
+ console.log("Tampering with anti-tamper protection using C API...");
+
+ let usleepAddr = null;
+ try {
+ usleepAddr = Module.findGlobalExportByName("usleep") || Module.findExportByName("usleep");
+ } catch (e) {
+ console.log("[-] Could not find usleep globally: " + e.message);
+ }
+
+ let nativeUsleep = null;
+ if (usleepAddr) {
+ nativeUsleep = new NativeFunction(usleepAddr, 'int', ['uint32']);
+ }
+
+ // Standard libc functions used by packers to terminate processes
+ const exitSymbols = ["exit", "_exit", "abort", "kill", "pthread_exit"];
+
+ exitSymbols.forEach(function (symbol) {
+ let address = null;
+ try {
+ // Find global exports using Frida 17 APIs
+ address = Module.findGlobalExportByName(symbol) || Module.findExportByName(symbol);
+ } catch (e) {
+ // Ignore search errors
+ }
+
+ if (address) {
+ try {
+ Interceptor.attach(address, {
+ onEnter: function (args) {
+ console.log("[!] Native anti-debug triggered " + symbol + "(). Freezing thread to prevent crash...");
+
+ // Infinite loop using the native OS sleep to halt the calling thread
+ while (true) {
+ if (nativeUsleep) {
+ nativeUsleep(100000); // Sleep for 100ms per iteration safely
+ }
+ }
+ }
+ });
+ console.log("[+] Hooked native function: " + symbol);
+ } catch (e) {
+ console.log("[-] Failed to hook: " + symbol + " (" + e.message + ")");
+ }
+ }
+ });
+}
+
+blockNativeExit();
diff --git a/scripts/frida_2.js b/scripts/frida_2.js
index ee27c25..7b7b47e 100644
--- a/scripts/frida_2.js
+++ b/scripts/frida_2.js
@@ -1,10 +1,10 @@
/*
- * Script name : frida.js
+ * Script name : frida_2.js
* Description : Frida script for preventing exit, extracting shared preferences, and tracing crypto/BLE with timestamps
* Author : Harvey Lelliott (@flip-dots) / Enhanced
- * Date : 23/03/26
+ * Date : 16/07/26
* * License : MIT
- * Revision : 1.1.1
+ * Revision : 1.2.1
*/
// --- UTILITY FUNCTIONS ---
@@ -293,4 +293,56 @@ setImmediate(function() {
log("[-] Error hooking Bluetooth: " + e);
}
});
-});
\ No newline at end of file
+});
+
+// Prevent anti-tamper mechanism from killing the app in C
+function blockNativeExit() {
+ console.log("Tampering with anti-tamper protection using C API...");
+
+ let usleepAddr = null;
+ try {
+ usleepAddr = Module.findGlobalExportByName("usleep") || Module.findExportByName("usleep");
+ } catch (e) {
+ console.log("[-] Could not find usleep globally: " + e.message);
+ }
+
+ let nativeUsleep = null;
+ if (usleepAddr) {
+ nativeUsleep = new NativeFunction(usleepAddr, 'int', ['uint32']);
+ }
+
+ // Standard libc functions used by packers to terminate processes
+ const exitSymbols = ["exit", "_exit", "abort", "kill", "pthread_exit"];
+
+ exitSymbols.forEach(function (symbol) {
+ let address = null;
+ try {
+ // Find global exports using Frida 17 APIs
+ address = Module.findGlobalExportByName(symbol) || Module.findExportByName(symbol);
+ } catch (e) {
+ // Ignore search errors
+ }
+
+ if (address) {
+ try {
+ Interceptor.attach(address, {
+ onEnter: function (args) {
+ console.log("[!] Native anti-debug triggered " + symbol + "(). Freezing thread to prevent crash...");
+
+ // Infinite loop using the native OS sleep to halt the calling thread
+ while (true) {
+ if (nativeUsleep) {
+ nativeUsleep(100000); // Sleep for 100ms per iteration safely
+ }
+ }
+ }
+ });
+ console.log("[+] Hooked native function: " + symbol);
+ } catch (e) {
+ console.log("[-] Failed to hook: " + symbol + " (" + e.message + ")");
+ }
+ }
+ });
+}
+
+blockNativeExit();
diff --git a/scripts/patch.sh b/scripts/patch.sh
index 9432d83..a34dabe 100755
--- a/scripts/patch.sh
+++ b/scripts/patch.sh
@@ -3,11 +3,11 @@
# Script name : patch.sh
# Description : Script for injecting Frida gadget into Anker Android app
# Author : Harvey Lelliott (@flip-dots)
-# Date : 23/03/26
+# Date : 16/07/26
# Usage : ./patch.sh [ADB device (e.g 192.168.1.1:1234)]
#
# License : MIT
-# Revision : 1.0.0
+# Revision : 1.1.0
#
set -euxo pipefail
@@ -16,7 +16,7 @@ set -euxo pipefail
# Constants #
#############
-FRIDA_VERSION="17.8.2"
+FRIDA_VERSION="17.8.3"
UBER_APK_SIGNER_VERSION="1.3.0"
@@ -53,7 +53,7 @@ WORKING_FOLDER=$(pwd)
# Folder to put all data in
DATA_FOLDER="${WORKING_FOLDER}/data"
-mkdir -p $DATA_FOLDER
+rm -rf $DATA_FOLDER && mkdir -p $DATA_FOLDER
# Folder to put source APK inside
APK_SOURCE_FOLDER="${DATA_FOLDER}/source_apks"
@@ -73,7 +73,7 @@ mkdir -p $APK_SIGNED_FOLDER
# Folder to put tools in
TOOLS_FOLDER="${WORKING_FOLDER}/tools"
-mkdir -p $TOOLS_FOLDER
+rm -rf $TOOLS_FOLDER && mkdir -p $TOOLS_FOLDER
# Folder to put Frida gadgets in
FRIDA_FOLDER="${TOOLS_FOLDER}/frida"
@@ -124,10 +124,11 @@ cp "${WORKING_FOLDER}/frida_config.json" "${APK_DECOMPILED_FOLDER}/lib/armeabi-v
cp "${WORKING_FOLDER}/frida_config.json" "${APK_DECOMPILED_FOLDER}/lib/arm64-v8a/libnative-utils.config.so"
# Add Frida gadget to app startup
-sed -i '' '34c \
-const-string v0, "native-utils"\
-invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\
-' "${APK_DECOMPILED_FOLDER}/smali/s/h/e/l/l/A.smali"
+sed -i '' 's/\.locals 5/\.locals 6/g' "${APK_DECOMPILED_FOLDER}/smali/s/h/e/l/l/S.smali"
+sed -i '' '2386c \
+const-string v5, "native-utils"\
+invoke-static {v5}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\
+' "${APK_DECOMPILED_FOLDER}/smali/s/h/e/l/l/S.smali"
# Modify manifest to enable Frida loading
sed -i '' '// s/android:allowBackup="false"/android:allowBackup="true"/' "${APK_DECOMPILED_FOLDER}/AndroidManifest.xml"
diff --git a/scripts/patch_2.sh b/scripts/patch_2.sh
new file mode 100755
index 0000000..b1d873e
--- /dev/null
+++ b/scripts/patch_2.sh
@@ -0,0 +1,182 @@
+#!/bin/bash
+#
+# Script name : patch_2.sh
+# Description : Script for injecting Frida gadget into Anker Android app
+# Author : Harvey Lelliott (@flip-dots)
+# Date : 16/07/26
+# Usage : ./patch_2.sh [ADB device (e.g 192.168.1.1:1234)]
+#
+# License : MIT
+# Revision : 1.0.0
+#
+set -euxo pipefail
+
+
+#############
+# Constants #
+#############
+
+FRIDA_VERSION="17.8.3"
+
+UBER_APK_SIGNER_VERSION="1.3.0"
+
+
+##################################
+# Environment and arg validation #
+##################################
+echo "Checking environment/tools..."
+
+# Validate arguments
+if [ $# -lt 1 ]; then
+ echo "Missing device argument (e.g 192.168.1.1:1234)!"
+ exit 2
+fi
+
+# The device for use with ADB
+DEVICE=$1
+
+# Check tools
+command -v wget >/dev/null 2>&1 || { echo >&2 "wget is required!"; exit 1; }
+command -v adb >/dev/null 2>&1 || { echo >&2 "adb is required!"; exit 1; }
+command -v apktool >/dev/null 2>&1 || { echo >&2 "apktool is required!"; exit 1; }
+command -v java >/dev/null 2>&1 || { echo >&2 "java is required!"; exit 1; }
+command -v frida >/dev/null 2>&1 || { echo >&2 "frida is required!"; exit 1; }
+
+
+################
+# Folder setup #
+################
+echo "Setting up folders..."
+
+# The current folder
+WORKING_FOLDER=$(pwd)
+
+# Folder to put all data in
+DATA_FOLDER="${WORKING_FOLDER}/data"
+mkdir -p $DATA_FOLDER
+
+# Folder to put source APK inside
+APK_SOURCE_FOLDER="${DATA_FOLDER}/source_apks"
+mkdir -p $APK_SOURCE_FOLDER
+
+# Folder to put the main decompiled APK inside
+APK_MAIN_DECOMPILED_FOLDER="${DATA_FOLDER}/base_apk_decompiled"
+mkdir -p $APK_MAIN_DECOMPILED_FOLDER
+
+# Folder to put the flutter decompiled APK inside
+APK_FLUTTER_DECOMPILED_FOLDER="${DATA_FOLDER}/flutter_apk_decompiled"
+mkdir -p $APK_FLUTTER_DECOMPILED_FOLDER
+
+# Folder to put patched APKs inside
+APK_PATCHED_FOLDER="${DATA_FOLDER}/patched"
+mkdir -p $APK_PATCHED_FOLDER
+
+# Folder to put signed APKs inside
+APK_SIGNED_FOLDER="${DATA_FOLDER}/signed"
+mkdir -p $APK_SIGNED_FOLDER
+
+# Folder to put tools in
+TOOLS_FOLDER="${WORKING_FOLDER}/tools"
+mkdir -p $TOOLS_FOLDER
+
+# Folder to put Frida gadgets in
+FRIDA_FOLDER="${TOOLS_FOLDER}/frida"
+mkdir -p $FRIDA_FOLDER
+
+
+
+#######################
+# Download deps/tools #
+#######################
+echo "Downloading dependencies/tools"
+
+cd $FRIDA_FOLDER && wget "https://github.com/zer0def/undetected-frida/releases/download/${FRIDA_VERSION}/undetected-frida-gadget-${FRIDA_VERSION}-android-arm.so.xz"
+cd $FRIDA_FOLDER && wget "https://github.com/zer0def/undetected-frida/releases/download/${FRIDA_VERSION}/undetected-frida-gadget-${FRIDA_VERSION}-android-arm64.so.xz"
+cd $FRIDA_FOLDER && unxz -f "undetected-frida-gadget-${FRIDA_VERSION}-android-arm.so.xz"
+cd $FRIDA_FOLDER && unxz -f "undetected-frida-gadget-${FRIDA_VERSION}-android-arm64.so.xz"
+
+cd $TOOLS_FOLDER && wget "https://github.com/patrickfav/uber-apk-signer/releases/download/v${UBER_APK_SIGNER_VERSION}/uber-apk-signer-${UBER_APK_SIGNER_VERSION}.jar"
+
+
+########################
+# Pull and extract APK #
+########################
+
+# Pull Anker APKs from Phone
+echo "Extracting original APKs from phone..."
+adb -s $DEVICE shell pm path com.anker.charging | sed 's/^package://' | tr -d '\r' | xargs -I {} adb -s $DEVICE pull {} $APK_SOURCE_FOLDER
+
+
+################
+# Inject Frida #
+################
+echo "Injecting Frida gadget into flutter APK..."
+
+# Extract Flutter library from Flutter APK
+mkdir -p "${APK_FLUTTER_DECOMPILED_FOLDER}/lib/arm64-v8a"
+unzip -p "${APK_SOURCE_FOLDER}/split_config.arm64_v8a.apk" "lib/arm64-v8a/libflutter.so" > "${APK_FLUTTER_DECOMPILED_FOLDER}/lib/arm64-v8a/libflutter.so"
+
+# Use LIEF to inject the Frida gadget as a dependency of Flutter
+python3 -c "
+import lief
+import sys
+
+target_path = '${APK_FLUTTER_DECOMPILED_FOLDER}/lib/arm64-v8a/libflutter.so'
+
+try:
+ # Parse the ELF binary
+ binary = lief.parse(target_path)
+
+ # Inject the dependency
+ binary.add_library('libnative-utils.so')
+ binary.write(target_path)
+ print(f'Successfully injected Frida into Flutter')
+except Exception as e:
+ print(f'Failed to inject Frida into Flutter: {e}')
+ sys.exit(1)
+"
+
+# Copy Frida gadget binaries into Flutter APK
+cp "${FRIDA_FOLDER}/undetected-frida-gadget-${FRIDA_VERSION}-android-arm64.so" "${APK_FLUTTER_DECOMPILED_FOLDER}/lib/arm64-v8a/libnative-utils.so"
+cp "${WORKING_FOLDER}/frida_config.json" "${APK_FLUTTER_DECOMPILED_FOLDER}/lib/arm64-v8a/libnative-utils.config.so"
+
+
+# Decompile the main APK
+echo "Decompiling main APK..."
+apktool d "${APK_SOURCE_FOLDER}/base.apk" -o $APK_MAIN_DECOMPILED_FOLDER -f
+
+# Modify manifest of main APK to enable Frida loading
+sed -i '' '// s/android:allowBackup="false"/android:allowBackup="true"/' "${APK_MAIN_DECOMPILED_FOLDER}/AndroidManifest.xml"
+sed -i '' '// s/android:extractNativeLibs="false"/android:extractNativeLibs="true" android:debuggable="true"/' "${APK_MAIN_DECOMPILED_FOLDER}/AndroidManifest.xml"
+
+
+
+##########################
+# Re-package and re-sign #
+##########################
+echo "Re-packaging and re-signing APKs..."
+
+# Re-package APKs
+apktool b -o "${APK_PATCHED_FOLDER}/base.apk" ${APK_MAIN_DECOMPILED_FOLDER}
+
+cp "${APK_SOURCE_FOLDER}/split_config.arm64_v8a.apk" "${APK_PATCHED_FOLDER}/split_config.arm64_v8a.apk"
+cd "${APK_FLUTTER_DECOMPILED_FOLDER}" && zip -ur "${APK_PATCHED_FOLDER}/split_config.arm64_v8a.apk" lib/
+
+# Re-sign APKs
+java -jar "${TOOLS_FOLDER}/uber-apk-signer-${UBER_APK_SIGNER_VERSION}.jar" -o $APK_SIGNED_FOLDER --allowResign --apks \
+ "${APK_PATCHED_FOLDER}/base.apk" \
+ "${APK_PATCHED_FOLDER}/split_config.arm64_v8a.apk" \
+ "${APK_SOURCE_FOLDER}/split_config.en.apk" \
+ "${APK_SOURCE_FOLDER}/split_config.xxhdpi.apk" \
+ "${APK_SOURCE_FOLDER}/split_flutter_assets_pack.apk"
+
+# Uninstall existing Anker app
+adb -s $DEVICE uninstall com.anker.charging
+
+# Install patched APKs
+adb -s $DEVICE install-multiple \
+ "${APK_SIGNED_FOLDER}/base-aligned-debugSigned.apk" \
+ "${APK_SIGNED_FOLDER}/split_config.arm64_v8a-aligned-debugSigned.apk" \
+ "${APK_SIGNED_FOLDER}/split_config.en-aligned-debugSigned.apk" \
+ "${APK_SIGNED_FOLDER}/split_config.xxhdpi-aligned-debugSigned.apk" \
+ "${APK_SIGNED_FOLDER}/split_flutter_assets_pack-aligned-debugSigned.apk"
diff --git a/scripts/run.sh b/scripts/run.sh
index 3dcd6f5..90f86e0 100755
--- a/scripts/run.sh
+++ b/scripts/run.sh
@@ -3,11 +3,11 @@
# Script name : run.sh
# Description : Script for executing patched Anker app and starting Frida.
# Author : Harvey Lelliott (@flip-dots)
-# Date : 23/03/26
+# Date : 16/07/26
# Usage : ./run.sh [ADB device (e.g 192.168.1.1:1234)]
#
# License : MIT
-# Revision : 1.0.0
+# Revision : 1.1.0
#
set -euxo pipefail
@@ -55,12 +55,16 @@ echo "Starting execution..."
# Port forward the port used by Frida gadget
adb -s $DEVICE forward tcp:49152 tcp:49152
-# In 5 seconds open the app (non-blocking)
-(sleep 5 && adb -s $DEVICE shell monkey -p com.anker.charging 1 && echo "Restarted app!") &
+# Kill app
+adb -s "$DEVICE" shell am force-stop com.anker.charging
+sleep 1
-# Open the app and execute the Frida script
-adb -s $DEVICE shell monkey -p com.anker.charging 1 \
- && frida -H 127.0.0.1:49152 -n Gadget -l frida.js \
+# Open app
+adb -s $DEVICE shell monkey -p com.anker.charging 1
+sleep 1
+
+# Execute Frida script
+frida -H 127.0.0.1:49152 -n Gadget -l frida.js \
2>&1 | tee -a "${LOG_FOLDER}/${TIMESTAMP}.log"
echo "Done!"