improvement and add is_static data

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2025-03-04 17:38:35 +01:00
parent a365022185
commit 91fd0137d8
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
5 changed files with 57 additions and 30 deletions

View file

@ -1,3 +1,21 @@
# Theseus Data Collector # Theseus Data Collector
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.
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 .
```

View file

@ -9,7 +9,7 @@ class StackConsumer implements Consumer<StackFrame> {
public ArrayList<StackFrame> stack; public ArrayList<StackFrame> stack;
public StackConsumer() { public StackConsumer() {
this.stack = new ArrayList(); this.stack = new ArrayList<StackFrame>();
} }
@Override @Override

View file

@ -1,20 +1,30 @@
#!/usr/bin/env bash #!/usr/bin/env bash
JAVAC='/usr/lib/jvm/java-17-openjdk/bin/javac' JAVAC="${JAVAC:-/usr/lib/jvm/java-17-openjdk/bin/javac}"
SDK_TOOLS="${HOME}/Android/Sdk/" ANDROID_SDK="${ANDROID_SDK:-${HOME}/Android/Sdk/}"
VERSION='34.0.0' VERSION="${VERSION:-34.0.0}"
D8="${SDK_TOOLS}/build-tools/${VERSION}/d8" D8="${D8:-${ANDROID_SDK}/build-tools/${VERSION}/d8}"
VERSION_B=$(echo "${VERSION}" | sed 's/\..*//') VERSION_B="${VERSION_B:-$(echo "${VERSION}" | sed 's/\..*//')}"
ANDROID_JAR="${SDK_TOOLS}/platforms/android-${VERSION_B}/android.jar" ANDROID_JAR="${ANDROID_JAR:-${ANDROID_SDK}/platforms/android-${VERSION_B}/android.jar}"
FOLDER=$(dirname "$(realpath $0)") FOLDER=$(dirname "$(realpath $0)")
BUILD_F="${FOLDER}/build" BUILD_F="${BUILD_F:-${FOLDER}/build}"
OUT_FILE="${FOLDER}/../theseus_frida/StackConsumer.dex.b64" 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}" rm -r "${BUILD_F}"
mkdir "${BUILD_F}" mkdir "${BUILD_F}"
"${JAVAC}" -d "${BUILD_F}" -classpath "${ANDROID_JAR}" "${FOLDER}/StackConsumer.java" "${JAVAC}" -Xlint -d "${BUILD_F}" -classpath "${ANDROID_JAR}" "${FOLDER}/StackConsumer.java"
mkdir "${BUILD_F}/classes" mkdir "${BUILD_F}/classes"
"${D8}" --classpath "${ANDROID_JAR}" "${BUILD_F}/theseus/android/StackConsumer.class" --output "${BUILD_F}/classes" "${D8}" --classpath "${ANDROID_JAR}" "${BUILD_F}/theseus/android/StackConsumer.class" --output "${BUILD_F}/classes"

View file

@ -7,6 +7,9 @@ from pathlib import Path
import frida # type: ignore import frida # type: ignore
from androguard.core.apk import get_apkid # type: ignore from androguard.core.apk import get_apkid # type: ignore
from loguru import logger # type: ignore
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" STACK_CONSUMER_B64 = Path(__file__).parent / "StackConsumer.dex.b64"
@ -35,32 +38,25 @@ def print_stack(stack, prefix: str):
print(f"{prefix}{frame['method']}:{frame['bytecode_index']}{native}") print(f"{prefix}{frame['method']}:{frame['bytecode_index']}{native}")
# def get_ty(java_name: str) -> str:
# """Return the android name from the java name of a class / type"""
# # TODO: array
# # TODO: scalar
# if java_name == "V": # tmp stub
# return "V"
# return f"L{java_name.replace('.', '/')};"
# def get_method_id(method_data) -> str:
# """Get a method descriptor from the different elements collected from the methods."""
# name = method_data["name"]
# ret = get_ty(method_data["ret"])
# cls = get_ty(method_data["class"])
# args = "".join(map(get_ty, method_data["args"]))
# return f"{cls}->{name}({args}){ret}"
def handle_invoke_data(data, data_storage: dict): def handle_invoke_data(data, data_storage: dict):
method = data["method"] method = data["method"]
# TODO: good idea?
if method in [
"Landroid/view/View;->getTranslationZ()F",
"Landroid/view/View;->getElevation()F",
]:
return
if len(data["stack"]) == 0: if len(data["stack"]) == 0:
return return
caller_method = data["stack"][0]["method"] caller_method = data["stack"][0]["method"]
addr = data["stack"][0]["bytecode_index"] addr = data["stack"][0]["bytecode_index"]
is_static = data["is_static"]
if is_static:
is_static_str = " (static)"
else:
is_static_str = ""
print("Method.Invoke:") print("Method.Invoke:")
print(f" called: {method}") print(f" called: {method}{is_static_str}")
print(f" by: {caller_method}") print(f" by: {caller_method}")
print(f" at: 0x{addr:08x}") print(f" at: 0x{addr:08x}")
# print(f" stack:") # print(f" stack:")
@ -72,6 +68,7 @@ def handle_invoke_data(data, data_storage: dict):
"method": method, "method": method,
"caller_method": caller_method, "caller_method": caller_method,
"addr": addr, "addr": addr,
"is_static": is_static,
} }
) )

View file

@ -67,6 +67,7 @@ Java.perform(() => {
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");
const Modifier = Java.use("java.lang.reflect.Modifier");
Method.invoke.overload( Method.invoke.overload(
"java.lang.Object", "[Ljava.lang.Object;" // the Frida type parser is so cursted... "java.lang.Object", "[Ljava.lang.Object;" // the Frida type parser is so cursted...
).implementation = function (obj, args) { ).implementation = function (obj, args) {
@ -80,7 +81,8 @@ Java.perform(() => {
"args": this.getParameterTypes().map((argty) => argty.getName() ), "args": this.getParameterTypes().map((argty) => argty.getName() ),
"ret": this.getReturnType().getName(), "ret": this.getReturnType().getName(),
},*/ },*/
"stack": get_stack() "stack": get_stack(),
"is_static": Modifier.isStatic(this.getModifiers()),
} }
}); });
return this.invoke(obj, args); return this.invoke(obj, args);