From 28f5ac772c1f09361be130fd58c6c85e77c60188 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Wed, 2 Apr 2025 14:26:14 +0200 Subject: [PATCH] directly define StackConsumer in frida --- frida/README.md | 17 ---------- frida/consumer/StackConsumer.java | 29 ---------------- frida/consumer/build.sh | 32 ------------------ frida/theseus_frida/__init__.py | 5 --- frida/theseus_frida/hook.js | 55 ++++++++++++++++++++++++++++++- 5 files changed, 54 insertions(+), 84 deletions(-) delete mode 100644 frida/consumer/StackConsumer.java delete mode 100644 frida/consumer/build.sh diff --git a/frida/README.md b/frida/README.md index 1d07d23..9e5d3bb 100644 --- a/frida/README.md +++ b/frida/README.md @@ -2,20 +2,3 @@ Collect runtime information about reflection operation done by en application to feed them to the patcher. -The Frida hook uses a Java class to collect the stack information, before building/installing the python package, you need to build the class: - -```shell - -# If the default values do not match, set the variables: -# -# JAVAC: path to the java compiler -# ANDROID_SDK: path to the android sdk -# VERSION: Android SDK version to use -# D8: path to the d8 executable -# ANDROID_JAR: path to the android.jar file to link -# BUILD_F: build folder (will be delated if exist) -# OUT_FILE: the file where to put the b64 of the compiled dex - -bash consumer/build.sh -poetry build # / poetry install / pip install . -``` diff --git a/frida/consumer/StackConsumer.java b/frida/consumer/StackConsumer.java deleted file mode 100644 index d291a01..0000000 --- a/frida/consumer/StackConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package theseus.android; - -import java.util.function.Consumer; -import java.lang.StackWalker.StackFrame; -import java.util.ArrayList; -import java.util.function.Function; -import java.util.stream.Stream; - -class StackConsumer implements Consumer { - - public ArrayList stack; - - public StackConsumer() { - this.stack = new ArrayList(); - } - - @Override - public void accept(StackFrame frame) { - stack.add(frame); - } - - public StackFrame[] getStack() { - return this.stack.toArray(new StackFrame[] {}); - } - - public Function, StackFrame[]> walkNFrame(int n) { - return s -> { s.limit(n).forEach(this); return this.getStack(); }; - } -} diff --git a/frida/consumer/build.sh b/frida/consumer/build.sh deleted file mode 100644 index 363e6c4..0000000 --- a/frida/consumer/build.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - - -JAVAC="${JAVAC:-/usr/lib/jvm/java-17-openjdk/bin/javac}" -ANDROID_SDK="${ANDROID_SDK:-${HOME}/Android/Sdk/}" -VERSION="${VERSION:-34.0.0}" -D8="${D8:-${ANDROID_SDK}/build-tools/${VERSION}/d8}" -VERSION_B="${VERSION_B:-$(echo "${VERSION}" | sed 's/\..*//')}" -ANDROID_JAR="${ANDROID_JAR:-${ANDROID_SDK}/platforms/android-${VERSION_B}/android.jar}" - -FOLDER=$(dirname "$(realpath $0)") -BUILD_F="${BUILD_F:-${FOLDER}/build}" -OUT_FILE="${OUT_FILE:-${FOLDER}/../theseus_frida/StackConsumer.dex.b64}" - -echo "JAVAC = ${JAVAC}" -echo "ANDROID_SDK = ${ANDROID_SDK}" -echo "VERSION = ${VERSION}" -echo "D8 = ${D8}" -# echo "VERSION_B = ${VERSION_B}" -echo "ANDROID_JAR = ${ANDROID_JAR}" -echo "BUILD_F = ${BUILD_F}" -echo "OUT_FILE = ${OUT_FILE}" - -rm -r "${BUILD_F}" -mkdir "${BUILD_F}" - -"${JAVAC}" -Xlint -d "${BUILD_F}" -classpath "${ANDROID_JAR}" "${FOLDER}/StackConsumer.java" - -mkdir "${BUILD_F}/classes" -"${D8}" --classpath "${ANDROID_JAR}" "${BUILD_F}/theseus/android/StackConsumer.class" --output "${BUILD_F}/classes" - -base64 "${BUILD_F}/classes/classes.dex" > "${OUT_FILE}" diff --git a/frida/theseus_frida/__init__.py b/frida/theseus_frida/__init__.py index 8c17877..4837be8 100644 --- a/frida/theseus_frida/__init__.py +++ b/frida/theseus_frida/__init__.py @@ -330,11 +330,6 @@ def collect_runtime(apk: Path, device_name: str, file_storage: Path, output: Tex with FRIDA_SCRIPT.open("r") as file: jsscript = file.read() - with STACK_CONSUMER_B64.open("r") as file: - jsscript = jsscript.replace( - "", - file.read().replace("\n", "").strip(), - ) pid = device.spawn([app]) session = device.attach(pid) diff --git a/frida/theseus_frida/hook.js b/frida/theseus_frida/hook.js index cdf3e1a..1b463dd 100644 --- a/frida/theseus_frida/hook.js +++ b/frida/theseus_frida/hook.js @@ -14,6 +14,57 @@ function dump_classloaders() { }); } +function registerStackConsumer() { + const Consumer = Java.use('java.util.function.Consumer'); + const Method = Java.use('java.lang.reflect.Method'); + const ArrayList = Java.use('java.util.ArrayList'); + const StackFrame = Java.use('java.lang.StackWalker$StackFrame'); + + // Finding r8 optimized method for the Consumer interface + let requiredMethods = Consumer.class.getDeclaredMethods(); + var lambdamethod = ''; + requiredMethods.forEach(m => { + var meth = Java.cast(m, Method); + let methodname = meth.getName(); + if (methodname.startsWith("$r8$lambda$")) { + lambdamethod = methodname; + }; + }); + + return Java.registerClass({ + name: "theseus.android.StackConsumer", + implements: [Consumer], + fields: { + stack: 'java.util.ArrayList', + }, + methods: { + '': [{ + returnType: 'void', + argumentTypes: [], + implementation: function () { + this.stack.value = ArrayList.$new(); + } + }], + accept(frame) { + var castedFrame = Java.cast(frame, StackFrame); + this.stack.value.add(castedFrame); + }, + getStack: [{ + returnType: '[Ljava.lang.StackWalker$StackFrame;', + argumentTypes: [], + implementation: function () { + return this.stack.value.toArray(Java.array('java.lang.StackWalker$StackFrame', [])); + }, + }], + andThen(cons) { + return this.$super.andThen(cons); + }, + lambda$andThen$0(consumer, obj) {}, + ['_' + lambdamethod]: function (cons1, cons2, obj) {} + }, + }); +} + // recv('dump-class-loaders', function onMessage(msg) {dump_classloaders()}); Java.perform(() => { @@ -44,12 +95,14 @@ Java.perform(() => { const System = Java.use('java.lang.System'); const Arrays = Java.use('java.util.Arrays'); - + /* const myClassLoader = InMemoryDexClassLoader.$new( ByteBuffer.wrap(Base64.decode("", Base64.DEFAULT.value)), null ); const StackConsumer = Java.ClassFactory.get(myClassLoader).use("theseus.android.StackConsumer"); + */ + const StackConsumer = registerStackConsumer(); const get_stack = function () { // console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));