rollback frida 'Java.registerClass'

This commit is contained in:
Jean-Marie Mineau 2025-04-07 10:26:59 +02:00
parent 6ee51c13d6
commit 30c1abe1ea
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
7 changed files with 154 additions and 10 deletions

1
frida/.gitignore vendored
View file

@ -1,4 +1,3 @@
__pycache__ __pycache__
dist dist
theseus_frida/StackConsumer.dex.b64
consumer/build consumer/build

View file

@ -2,3 +2,25 @@
Collect runtime information about reflection operation done by en application to feed them to the patcher. 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 .
```

View file

@ -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<StackFrame> {
public ArrayList<StackFrame> stack;
public StackConsumer() {
this.stack = new ArrayList<StackFrame>();
}
@Override
public void accept(StackFrame frame) {
stack.add(frame);
}
public StackFrame[] getStack() {
return this.stack.toArray(new StackFrame[] {});
}
public Function<? super Stream<StackWalker.StackFrame>, StackFrame[]> walkNFrame(int n) {
return s -> { s.limit(n).forEach(this); return this.getStack(); };
}
}

32
frida/consumer/build.sh Normal file
View file

@ -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}"

View file

@ -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

View file

@ -21,6 +21,7 @@ from loguru import logger # type: ignore
logger.remove() # remove androguard logs logger.remove() # remove androguard logs
FRIDA_SCRIPT = Path(__file__).parent / "hook.js" 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_BIN = Path(__file__).parent / "frida-server-16.7.4-android-x86_64.xz"
FRIDA_SERVER_ANDROID_PATH = "/data/local/tmp/frida-server" FRIDA_SERVER_ANDROID_PATH = "/data/local/tmp/frida-server"
@ -347,6 +348,11 @@ def collect_runtime(
with FRIDA_SCRIPT.open("r") as file: with FRIDA_SCRIPT.open("r") as file:
jsscript = file.read() jsscript = file.read()
with STACK_CONSUMER_B64.open("r") as file:
jsscript = jsscript.replace(
"<PYTHON REPLACE StackConsumer.dex.b64>",
file.read().replace("\n", "").strip(),
)
pid = device.spawn([app]) pid = device.spawn([app])
session = device.attach(pid) session = device.attach(pid)

View file

@ -26,6 +26,8 @@ function dump_classloaders() {
}); });
} }
/* ----- Frida Native Class Loading -----
* Broken, for some ineffable frida-android reason.
function registerStackConsumer() { function registerStackConsumer() {
const Consumer = Java.use('java.util.function.Consumer'); const Consumer = Java.use('java.util.function.Consumer');
const Method = Java.use('java.lang.reflect.Method'); const Method = Java.use('java.lang.reflect.Method');
@ -91,6 +93,19 @@ function registerStackConsumer() {
console.log(Object.keys(spec.methods)); console.log(Object.keys(spec.methods));
return Java.registerClass(spec); 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("<PYTHON REPLACE StackConsumer.dex.b64>", Base64.DEFAULT.value)),
null,
);
return Java.ClassFactory.get(myClassLoader).use("theseus.android.StackConsumer");
}
// recv('dump-class-loaders', function onMessage(msg) {dump_classloaders()}); // recv('dump-class-loaders', function onMessage(msg) {dump_classloaders()});
@ -108,9 +123,8 @@ Java.perform(() => {
const StackWalkerOptionsShowReflect = StackWalkerOptions.valueOf("SHOW_REFLECT_FRAMES"); const StackWalkerOptionsShowReflect = StackWalkerOptions.valueOf("SHOW_REFLECT_FRAMES");
const StackWalkerOptionsRetainClassReference = StackWalkerOptions.valueOf("RETAIN_CLASS_REFERENCE"); const StackWalkerOptionsRetainClassReference = StackWalkerOptions.valueOf("RETAIN_CLASS_REFERENCE");
const StackFrame = Java.use('java.lang.StackWalker$StackFrame'); 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 ByteBuffer = Java.use("java.nio.ByteBuffer");
const Base64 = Java.use("android.util.Base64");
const Method = Java.use("java.lang.reflect.Method"); const Method = Java.use("java.lang.reflect.Method");
const Class = Java.use("java.lang.Class"); const Class = Java.use("java.lang.Class");
const Constructor = Java.use("java.lang.reflect.Constructor"); const Constructor = Java.use("java.lang.reflect.Constructor");
@ -122,13 +136,6 @@ Java.perform(() => {
const System = Java.use('java.lang.System'); const System = Java.use('java.lang.System');
const Arrays = Java.use('java.util.Arrays'); const Arrays = Java.use('java.util.Arrays');
/*
const myClassLoader = InMemoryDexClassLoader.$new(
ByteBuffer.wrap(Base64.decode("<PYTHON REPLACE StackConsumer.dex.b64>", Base64.DEFAULT.value)),
null
);
const StackConsumer = Java.ClassFactory.get(myClassLoader).use("theseus.android.StackConsumer");
*/
const StackConsumer = registerStackConsumer(); const StackConsumer = registerStackConsumer();
const get_stack = function () { const get_stack = function () {