This commit is contained in:
Jean-Marie Mineau 2025-03-26 11:56:10 +01:00
parent 2d7c69cb05
commit e34415857d
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
9 changed files with 116 additions and 18 deletions

View file

@ -1,4 +1,4 @@
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::fs::File; use std::fs::File;
use androscalpel::{Apk, IdType}; use androscalpel::{Apk, IdType};
@ -38,13 +38,16 @@ fn insert_code_naive(apk: &mut Apk, data: &RuntimeData) -> Result<()> {
} }
fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<()> { fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<()> {
let mut class_defined = apk.list_classes();
let mut class_loaders = HashMap::new(); let mut class_loaders = HashMap::new();
class_loaders.insert( class_loaders.insert(
"MAIN".to_string(), "MAIN".to_string(),
ClassLoader { ClassLoader {
id: "MAIN".to_string(),
parent: None, parent: None,
class: IdType::from_smali("Ljava/lang/Boolean;").unwrap(), class: IdType::from_smali("Ljava/lang/Boolean;").unwrap(),
apk: ApkOrRef::Ref(apk), apk: ApkOrRef::Ref(apk),
renamed_classes: HashSet::new(),
}, },
); );
for dyn_data in &data.dyn_code_load { for dyn_data in &data.dyn_code_load {
@ -54,17 +57,25 @@ fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<
let file = File::open(file)?; let file = File::open(file)?;
apk.add_code(file, crate::labeling, false)?; apk.add_code(file, crate::labeling, false)?;
} }
assert!(!class_loaders.contains_key(&dyn_data.classloader)); assert!(!class_loaders.contains_key(&dyn_data.classloader));
class_loaders.insert(
dyn_data.classloader.clone(), let classes = apk.list_classes();
ClassLoader { let mut class_loader = ClassLoader {
parent: None, id: dyn_data.classloader.clone(),
class, parent: None,
apk: ApkOrRef::Owned(apk), class,
}, apk: ApkOrRef::Owned(apk),
); renamed_classes: HashSet::new(),
};
let collisions = class_defined.intersection(&classes);
for cls in collisions {
class_loader.rename_classdef(cls);
}
class_defined.extend(classes);
class_loaders.insert(dyn_data.classloader.clone(), class_loader);
} }
// TODO: list colliding classes
// TODO: rename colliding classes according to class laoder // TODO: rename colliding classes according to class laoder
// TODO: get the ClassLoader::parent values... // TODO: get the ClassLoader::parent values...
// TODO: model the delegation behavior and rename ref to class accordingly // TODO: model the delegation behavior and rename ref to class accordingly
@ -75,9 +86,11 @@ fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<
/// Structure modelizing a class loader. /// Structure modelizing a class loader.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
struct ClassLoader<'a> { struct ClassLoader<'a> {
pub id: String,
pub parent: Option<String>, pub parent: Option<String>,
pub class: IdType, pub class: IdType,
pub apk: ApkOrRef<'a>, pub apk: ApkOrRef<'a>,
pub renamed_classes: HashSet<IdType>,
} }
impl ClassLoader<'_> { impl ClassLoader<'_> {
@ -87,6 +100,16 @@ impl ClassLoader<'_> {
ApkOrRef::Ref(ref mut apk) => apk, ApkOrRef::Ref(ref mut apk) => apk,
} }
} }
pub fn rename_classdef(&mut self, cls: &IdType) {
use androscalpel::SmaliName;
println!(
"TODO: rename {} -> {}_{}",
cls.try_to_smali().unwrap(),
cls.try_to_smali().unwrap(),
self.id
);
}
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]

1
patcher/tst.sh Normal file
View file

@ -0,0 +1 @@
cargo run --bin patcher -- --code-loading-patch-strategy model-class-loaders --runtime-data ./tst/runtime.json --path ../test_apks/dynloading/build/test_dynloading.apk --out /tmp/patched_dynloading.apk -k ../test_apks/dynloading/ToyKey.keystore -z $(which zipalign) -a $(which apksigner)

71
patcher/tst/runtime.json Normal file
View file

@ -0,0 +1,71 @@
{
"invoke_data": [
{
"method": "Lcom/example/theseus/dynloading/AMain;->getColliderId()Ljava/lang/String;",
"caller_method": "Lcom/example/theseus/dynloading/MainActivity;->indirectWithoutParent()V",
"addr": 33,
"is_static": true
},
{
"method": "Lcom/example/theseus/dynloading/AMain;->getColliderId()Ljava/lang/String;",
"caller_method": "Lcom/example/theseus/dynloading/MainActivity;->indirectWithParent()V",
"addr": 39,
"is_static": true
},
{
"method": "Lcom/example/theseus/dynloading/Collider;->getColliderId()Ljava/lang/String;",
"caller_method": "Lcom/example/theseus/dynloading/MainActivity;->directWithoutParent()V",
"addr": 33,
"is_static": true
},
{
"method": "Lcom/example/theseus/dynloading/Collider;->getColliderId()Ljava/lang/String;",
"caller_method": "Lcom/example/theseus/dynloading/MainActivity;->directWithParent()V",
"addr": 39,
"is_static": true
}
],
"class_new_inst_data": [
{
"constructor": "Landroid/app/Application;-><init>()V",
"caller_method": "Landroid/app/AppComponentFactory;->instantiateApplication(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/app/Application;",
"addr": 4
},
{
"constructor": "Lcom/example/theseus/dynloading/MainActivity;-><init>()V",
"caller_method": "Landroid/app/AppComponentFactory;->instantiateActivity(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/app/Activity;",
"addr": 4
}
],
"cnstr_new_inst_data": [],
"dyn_code_load": [
{
"classloader_class": "Ldalvik/system/PathClassLoader;",
"classloader": "0dfe1ce0",
"files": [
"./tst/dex/PathClassLoader_0dfe1ce0_09d17bb5de42d5d9.bytecode"
]
},
{
"classloader_class": "Ldalvik/system/PathClassLoader;",
"classloader": "0b4ac535",
"files": [
"./tst/dex/PathClassLoader_0b4ac535_09d17bb5de42d5d9.bytecode"
]
},
{
"classloader_class": "Ldalvik/system/PathClassLoader;",
"classloader": "0ceee91e",
"files": [
"./tst/dex/PathClassLoader_0ceee91e_09d17bb5de42d5d9.bytecode"
]
},
{
"classloader_class": "Ldalvik/system/PathClassLoader;",
"classloader": "0545b1a9",
"files": [
"./tst/dex/PathClassLoader_0545b1a9_09d17bb5de42d5d9.bytecode"
]
}
]
}

View file

@ -1,3 +1,2 @@
dist # Created by venv; see https://docs.python.org/3/library/venv.html
__pycache__ *
src/theseus_autopatcher/patcher_86_64_musl

View file

@ -10,7 +10,7 @@ from theseus_frida import collect_runtime
def get_android_sdk_path() -> Path | None: def get_android_sdk_path() -> Path | None:
if "ANDROID_HOME" in os.environ: if "ANDROID_HOME" in os.environ:
return os.environ["ANDROID_HOME"] return Path(os.environ["ANDROID_HOME"])
default = Path.home() / "Android" / "Sdk" default = Path.home() / "Android" / "Sdk"
if default.exists(): if default.exists():
return default return default
@ -29,10 +29,10 @@ def get_build_tools_path(toolname: str) -> Path | None:
path = which(toolname) path = which(toolname)
if path is not None: if path is not None:
return path return Path(path)
path = which(toolname + ".exe") path = which(toolname + ".exe")
if path is not None: if path is not None:
return path return Path(path)
sdk = get_android_sdk_path() sdk = get_android_sdk_path()
if sdk is None: if sdk is None:
@ -54,8 +54,12 @@ def get_build_tools_path(toolname: str) -> Path | None:
def get_keytool_path() -> Path | None: def get_keytool_path() -> Path | None:
path = which("keytool") path = which("keytool")
if path is not None: if path is not None:
return path return Path(path)
return which("keytool.exe") path = which("keytool.exe")
if path is not None:
return Path(path)
else:
return None
def gen_keystore(keytool: Path, storepath: Path): def gen_keystore(keytool: Path, storepath: Path):