collect classloaders on the fly

This commit is contained in:
Jean-Marie Mineau 2025-04-04 11:34:57 +02:00
parent b86bf08229
commit ba02e70dcc
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
5 changed files with 58 additions and 26 deletions

View file

@ -3,7 +3,7 @@ name = "theseus-frida"
version = "0.1.0"
description = ""
authors = [
{name = "Jean-Marie Mineau",email = "jean-marie.mineau@inria.fr"}
{name = "Jean-Marie Mineau",email = "jean-marie.mineau@centralesupelec.fr"}
]
readme = "README.md"
requires-python = ">=3.13,<4.0.0"

View file

@ -12,6 +12,8 @@ import lzma
from pathlib import Path
from typing import TextIO, Any
from .app_exploration import explore_app
import frida # type: ignore
from androguard.core.apk import get_apkid # type: ignore
from loguru import logger # type: ignore
@ -76,6 +78,7 @@ def cl_id_to_string(classloader: int) -> str | None:
def handle_classloader_data(data: dict, data_storage: dict):
data["id"] = cl_id_to_string(data["id"])
data["parent_id"] = cl_id_to_string(data["parent_id"])
print(f"[+] Got classloader {data['id']}({data['str']})")
data_storage["classloaders"].append(data)
@ -363,21 +366,21 @@ def collect_runtime(apk: Path, device_name: str, file_storage: Path, output: Tex
# Resume the execution of the APK
device.resume(pid)
print("==> Press ENTER to end the analysis <==")
input()
# Dump all known classloaders
global CLASSLOADER_DONE
CLASSLOADER_DONE = False
# Don't wait for confirmation that all cl were sended
# global CLASSLOADER_DONE
# CLASSLOADER_DONE = False
script.post({"type": "dump-class-loaders"})
t = spinner()
while not CLASSLOADER_DONE:
print(
f"[{t.__next__()}] Waiting for the list of classloaders to be sent",
end="\r",
)
time.sleep(0.3)
print(f"[*] Classloader list received" + " " * 20)
# t = spinner()
# while not CLASSLOADER_DONE:
# print(
# f"[{t.__next__()}] Waiting for the list of classloaders to be sent",
# end="\r",
# )
# time.sleep(0.3)
# print(f"[*] Classloader list received" + " " * 20)
explore_app()
# Try to find the Main class loader
main_class_loader: str | None = None

View file

@ -0,0 +1,7 @@
def explore_app():
manual_exploration()
def manual_exploration():
print("==> Press ENTER to end the analysis <==")
input()

View file

@ -1,14 +1,26 @@
function dump_classloaders() {
Java.perform(() => {
const sended_class_loaders = new Set();
function send_class_loader(cl) {
const System = Java.use('java.lang.System');
var class_loader = Java.enumerateClassLoadersSync();
for (var cl of class_loader) {
let cl_id = System.identityHashCode(cl);
while (cl != null && !sended_class_loaders.has(cl_id)) {
let parent_ = cl.getParent();
send({"type": "classloader", "data": {
"id": System.identityHashCode(cl),
"parent_id": System.identityHashCode(cl.getParent()),
"id": cl_id,
"parent_id": System.identityHashCode(parent_),
"str": cl.toString(),
"cname": cl.$className
}});
sended_class_loaders.add(cl_id);
cl = parent_;
}
}
function dump_classloaders() {
Java.perform(() => {
var class_loader = Java.enumerateClassLoadersSync();
for (var cl of class_loader) {
send_class_loader(cl);
}
send({"type": "classloader-done"})
});
@ -125,11 +137,13 @@ Java.perform(() => {
var stack = stackConsumer.getStack()
//send({"type": "stack", "data": stackConsumer.getStack()});
return stack.map((frame) => {
let cl = frame.getDeclaringClass().getClassLoader();
send_class_loader(cl);
return {
"bytecode_index": frame.getByteCodeIndex(),
"is_native": frame.isNativeMethod(),
"method": frame.getDeclaringClass().descriptorString() + "->" + frame.getMethodName() + frame.getDescriptor(),
"cl_id": System.identityHashCode(frame.getDeclaringClass().getClassLoader()),
"cl_id": System.identityHashCode(cl),
//{
//"descriptor": frame.getDescriptor(),
//"name": frame.getMethodName(),
@ -170,11 +184,13 @@ Java.perform(() => {
Method.invoke.overload(
"java.lang.Object", "[Ljava.lang.Object;" // the Frida type parser is so cursted...
).implementation = function (obj, args) {
let cl = this.getDeclaringClass().getClassLoader();
send_class_loader(cl);
send({
"type": "invoke",
"data": {
"method": get_method_dsc(this),
"method_cl_id": System.identityHashCode(this.getDeclaringClass().getClassLoader()),
"method_cl_id": System.identityHashCode(cl),
/*{
"name": this.getName(),
"class": this.getDeclaringClass().getName(),
@ -193,11 +209,13 @@ Java.perform(() => {
// Class.newInstance()
Class.newInstance.overload(
).implementation = function () {
let cl = this.getClassLoader();
send_class_loader(cl);
send({
"type": "class-new-inst",
"data": {
"constructor": this.descriptorString() + "-><init>()V",
"constructor_cl_id": System.identityHashCode(this.getClassLoader()),
"constructor_cl_id": System.identityHashCode(cl),
/*{
"name": "<init>",
"class": this.getName(),
@ -215,11 +233,13 @@ Java.perform(() => {
Constructor.newInstance.overload(
"[Ljava.lang.Object;"
).implementation = function (args) {
let cl = this.getDeclaringClass().getClassLoader();
send_class_loader(cl);
send({
"type": "cnstr-new-isnt",
"data": {
"constructor": get_constr_dsc(this),
"constructor_cl_id": System.identityHashCode(this.getDeclaringClass().getClassLoader()),
"constructor_cl_id": System.identityHashCode(cl),
/*
{
"name": "<init>",
@ -262,6 +282,7 @@ Java.perform(() => {
let classloader_class = null;
let classloader_id = System.identityHashCode(loader);
if (loader !== null) {
send_class_loader(loader);
classloader_class = loader.getClass().descriptorString();
}
send({
@ -334,6 +355,7 @@ Java.perform(() => {
let classloader_id = System.identityHashCode(loader);
if (loader !== null) {
classloader_class = loader.getClass().descriptorString();
send_class_loader(loader);
}
send({
"type": "load-dex",

View file

@ -3,7 +3,7 @@ name = "theseus-autopatcher"
version = "0.1.0"
description = ""
authors = [
{name = "Jean-Marie 'Histausse' Mineau",email = "histausse@protonmail.com"}
{name = "Jean-Marie MINEAU",email = "jean-marie.mineau@centralesupelec.fr"}
]
readme = "README.md"
requires-python = ">=3.13,<4.0.0"