diff --git a/frida/.gitignore b/frida/.gitignore index 552d099..2489c7a 100644 --- a/frida/.gitignore +++ b/frida/.gitignore @@ -1,4 +1,3 @@ __pycache__ dist -theseus_frida/StackConsumer.dex.b64 consumer/build diff --git a/frida/README.md b/frida/README.md index 9e5d3bb..accbe97 100644 --- a/frida/README.md +++ b/frida/README.md @@ -2,3 +2,25 @@ Collect runtime information about reflection operation done by en application to feed them to the patcher. + +# Modifying StackConsumer.java + +The Frida hook uses a Java class to collect the stack information. If you modify it, before building/installing the python package, you need to build regenerate the base64 encoded class used by frida: + +```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 + +# Then you can build the package / install it +poetry build # / poetry install / pip install . +``` diff --git a/frida/consumer/StackConsumer.java b/frida/consumer/StackConsumer.java new file mode 100644 index 0000000..d291a01 --- /dev/null +++ b/frida/consumer/StackConsumer.java @@ -0,0 +1,29 @@ +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 new file mode 100644 index 0000000..363e6c4 --- /dev/null +++ b/frida/consumer/build.sh @@ -0,0 +1,32 @@ +#!/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/StackConsumer.dex.b64 b/frida/theseus_frida/StackConsumer.dex.b64 new file mode 100644 index 0000000..945889d --- /dev/null +++ b/frida/theseus_frida/StackConsumer.dex.b64 @@ -0,0 +1,49 @@ +ZGV4CjAzNQBSwVrBo2GZnSIiXXnINXUzvz6Ai4dYk7LgCgAAcAAAAHhWNBIAAAAAAAAAABwKAAA5 +AAAAcAAAABEAAABUAQAAEQAAAJgBAAADAAAAZAIAABUAAAB8AgAAAgAAACQDAAB8BwAAZAMAAG4F +AACABQAAkgUAALoFAAC/BQAAwgUAAMoFAADOBQAA0wUAAOcFAADqBQAA7QUAAPAFAAD0BQAA+QUA +AP0FAAABBgAABwYAAAwGAAArBgAAPwYAAGMGAAB6BgAAkQYAALQGAADTBgAA8gYAABUHAAA0BwAA +UwcAAG4HAACJBwAAxAcAAOUHAAD5BwAA/AcAAAAIAAAFCAAACAgAAAwIAAAhCAAARggAAE4IAABT +CAAAXAgAAGMIAABsCAAAcQgAAHYIAAB/CAAAiQgAAJ4IAAClCAAArAgAALUIAAC8CAAAyAgAAAkA +AAAKAAAAEgAAABMAAAAUAAAAFQAAABcAAAAYAAAAGgAAABsAAAAdAAAAHwAAACAAAAAiAAAAJQAA +ACcAAAAoAAAADwAAAAMAAAAcBQAADwAAAAcAAAAUBQAAEQAAAAcAAAAkBQAADAAAAAkAAAAsBQAA +DwAAAAkAAAAMBQAAEQAAAAkAAAA0BQAADgAAAAoAAAA8BQAAIgAAAA0AAAAAAAAAIwAAAA0AAAAc +BQAAIwAAAA0AAABEBQAAIwAAAA0AAAAUBQAAJAAAAA0AAABMBQAAJgAAAA4AAAAcBQAADwAAAA8A +AABUBQAACwAAABAAAAAAAAAADQAAABAAAABcBQAAEAAAABAAAABkBQAACwAMAC4AAAALAAAALwAA +AAwABQA0AAAAAwAHAAUAAAAFAAcABQAAAAUADAAqAAAABQANADUAAAAGAAIAAAAAAAgABQAAAAAA +CAAFAAEAAAAKAAoAMAAAAAoABgAzAAAACwALAAUAAAALAAQAKwAAAAsAAAAsAAAACwAEAC0AAAAM +ABAAAgAAAAwABwAFAAAADAAIACkAAAAMAAkAKQAAAAwAAQArAAAADAAOADEAAAAMAA8AMgAAAAwA +AwA3AAAACwAAABEQAAADAAAADAUAAAgAAAAAAAAAlgkAAAAAAAAMAAAAAAAAAAMAAAAUBQAAIQAA +APwJAAC0CQAAAAAAAAQAAgADAAAAAAAAAAsAAABUIAAAUiEBAB8DCgBxMA0AEAMMAxEDAAACAAIA +AgAAAAAAAAAFAAAAcSAFABAADAERAQAAAgACAAIAAAAAAAAABQAAAHEgBgAQAAwBEQEAAAMAAwAB +AAAAAAAAAAgAAABwEAAAAABbAQAAWQIBAA4AAwADAAMAAAAAAAAABQAAAHAwEwAQAgwAEQAAAAMA +AQACAAAA7AQAAAwAAABUIAIAEgEjERAAbiADABAADAAfABAAEQAFAAMAAwAAAPAEAAANAAAAgTBy +MAgABAEMA3IgBwAjAG4QEgACAAwDEQMAAAIAAgACAAAAAAAAAAUAAABxIAQAEAAMAREBAAADAAIA +AwAAAPYEAAAGAAAAIgALAHAwCQAQAhEAAgABAAEAAAD7BAAACwAAAHAQAAABACIABQBwEAEAAABb +EAIADgAAAAIAAgACAAAAAQUAAAYAAAAfAQQAbiAQABAADgADAAIAAgAAAAYFAAAGAAAAVBACAG4g +AgAgAA4AFwAOABsCAAAOABsBAA4ADQAOPHgACQEADgATAQAOWgABAAAACQAAAAEAAAAHAAAAAQAA +AAMAAAACAAAABwAHAAEAAAAAAAAAAgAAAAkACQABAAAAAQAAAAEAAAAEAAAAAgAAAAwAAAABAAAA +DwAAAAIAAAAAAAoAAwAAAAwAAAAKABAkZGVmYXVsdCRhbmRUaGVuABAkZGVmYXVsdCRjb21wb3Nl +ACYkcjgkbGFtYmRhJFpYZWVGQm9YWU03d2VwZjhsSjNzcnh6YnRlbwADKEkpAAEtAAY8aW5pdD4A +Aj47AAM+O1sAEkQ4JCRTeW50aGV0aWNDbGFzcwABSQABSgABTAACTEkAA0xJTAACTEoAAkxMAARM +TElMAANMTEwAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABJMamF2YS9sYW5nL09iamVj +dDsAIkxqYXZhL2xhbmcvU3RhY2tXYWxrZXIkU3RhY2tGcmFtZTsAFUxqYXZhL3V0aWwvQXJyYXlM +aXN0OwAVTGphdmEvdXRpbC9BcnJheUxpc3Q8ACFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVy +JC1DQzsAHUxqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7AB1MamF2YS91dGlsL2Z1bmN0aW9u +L0NvbnN1bWVyPAAhTGphdmEvdXRpbC9mdW5jdGlvbi9GdW5jdGlvbiQtQ0M7AB1MamF2YS91dGls +L2Z1bmN0aW9uL0Z1bmN0aW9uOwAdTGphdmEvdXRpbC9mdW5jdGlvbi9GdW5jdGlvbjwAGUxqYXZh +L3V0aWwvc3RyZWFtL1N0cmVhbTsAGUxqYXZhL3V0aWwvc3RyZWFtL1N0cmVhbTwAOUx0aGVzZXVz +L2FuZHJvaWQvU3RhY2tDb25zdW1lciQkRXh0ZXJuYWxTeW50aGV0aWNMYW1iZGEwOwAfTHRoZXNl +dXMvYW5kcm9pZC9TdGFja0NvbnN1bWVyOwASU3RhY2tDb25zdW1lci5qYXZhAAFWAAJWTAADVkxJ +AAFaAAJaTAATW0xqYXZhL2xhbmcvT2JqZWN0OwAjW0xqYXZhL2xhbmcvU3RhY2tXYWxrZXIkU3Rh +Y2tGcmFtZTsABmFjY2VwdAADYWRkAAdhbmRUaGVuAAVhcHBseQAHY29tcG9zZQADZiQwAANmJDEA +B2ZvckVhY2gACGdldFN0YWNrABNsYW1iZGEkd2Fsa05GcmFtZSQwAAVsaW1pdAAFc3RhY2sAB3Rv +QXJyYXkABXZhbHVlAAp3YWxrTkZyYW1lAJsBfn5EOHsiYmFja2VuZCI6ImRleCIsImNvbXBpbGF0 +aW9uLW1vZGUiOiJkZWJ1ZyIsImhhcy1jaGVja3N1bXMiOmZhbHNlLCJtaW4tYXBpIjoxLCJzaGEt +MSI6ImZhY2VkZjQxYmJkMjhiNTYzZDFlOWUwOWM1ZjcyZDdjNWNhNTk4ZDUiLCJ2ZXJzaW9uIjoi +OC4yLjItZGV2In0AAgIBNhwIFwMXHBcEFx4XFBcHFxQXBgICATYcAxcWFxQXBgICATYcBBcTFxkX +FBcGAAIBAwCRIAGRIAmBoATEBwqBIIwHARHkBgGBIKgHAAEDBQIBDYkg5AcBgYAEjAkFgiCoCA/B +ILQJAQHQCQGBINQIAQGACAIB8AgAAAAAAQAAAGYJAAABAAAAfAkAAAEAAACICQAA9AkAAAEAAAAB +AAAAAAAAAAIAAADsCQAAFAAAAOQJAAAQAAAAAAAAAAEAAAAAAAAAAQAAADkAAABwAAAAAgAAABEA +AABUAQAAAwAAABEAAACYAQAABAAAAAMAAABkAgAABQAAABUAAAB8AgAABgAAAAIAAAAkAwAAASAA +AAwAAABkAwAAAyAAAAYAAADsBAAAARAAAAwAAAAMBQAAAiAAADkAAABuBQAABCAAAAMAAABmCQAA +ACAAAAIAAACWCQAAAxAAAAQAAADgCQAABiAAAAEAAAD8CQAAABAAAAEAAAAcCgAA diff --git a/frida/theseus_frida/__init__.py b/frida/theseus_frida/__init__.py index 57dc94c..77820df 100644 --- a/frida/theseus_frida/__init__.py +++ b/frida/theseus_frida/__init__.py @@ -21,6 +21,7 @@ from loguru import logger # type: ignore logger.remove() # remove androguard logs FRIDA_SCRIPT = Path(__file__).parent / "hook.js" +STACK_CONSUMER_B64 = Path(__file__).parent / "StackConsumer.dex.b64" FRIDA_SERVER_BIN = Path(__file__).parent / "frida-server-16.7.4-android-x86_64.xz" FRIDA_SERVER_ANDROID_PATH = "/data/local/tmp/frida-server" @@ -347,6 +348,11 @@ def collect_runtime( 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 9277e29..da73698 100644 --- a/frida/theseus_frida/hook.js +++ b/frida/theseus_frida/hook.js @@ -26,6 +26,8 @@ function dump_classloaders() { }); } +/* ----- Frida Native Class Loading ----- + * Broken, for some ineffable frida-android reason. function registerStackConsumer() { const Consumer = Java.use('java.util.function.Consumer'); const Method = Java.use('java.lang.reflect.Method'); @@ -91,6 +93,19 @@ function registerStackConsumer() { console.log(Object.keys(spec.methods)); return Java.registerClass(spec); } +*/ + +// ----- InMemoryDexClassLoader class loading ----- +function registerStackConsumer() { + const InMemoryDexClassLoader = Java.use("dalvik.system.InMemoryDexClassLoader"); + const ByteBuffer = Java.use("java.nio.ByteBuffer"); + const Base64 = Java.use("android.util.Base64"); + let myClassLoader = InMemoryDexClassLoader.$new( + ByteBuffer.wrap(Base64.decode("", Base64.DEFAULT.value)), + null, + ); + return Java.ClassFactory.get(myClassLoader).use("theseus.android.StackConsumer"); +} // recv('dump-class-loaders', function onMessage(msg) {dump_classloaders()}); @@ -108,9 +123,8 @@ Java.perform(() => { const StackWalkerOptionsShowReflect = StackWalkerOptions.valueOf("SHOW_REFLECT_FRAMES"); const StackWalkerOptionsRetainClassReference = StackWalkerOptions.valueOf("RETAIN_CLASS_REFERENCE"); const StackFrame = Java.use('java.lang.StackWalker$StackFrame'); - const Base64 = Java.use("android.util.Base64"); - const InMemoryDexClassLoader = Java.use("dalvik.system.InMemoryDexClassLoader"); const ByteBuffer = Java.use("java.nio.ByteBuffer"); + const Base64 = Java.use("android.util.Base64"); const Method = Java.use("java.lang.reflect.Method"); const Class = Java.use("java.lang.Class"); const Constructor = Java.use("java.lang.reflect.Constructor"); @@ -122,13 +136,6 @@ 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 () {