diff --git a/.gitignore b/.gitignore index 1d79fd0..a384fda 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,7 @@ /venv_test /test.apk /androguard_test.py +*.apk +*.dex +*.idsig +*.jks diff --git a/Cargo.lock b/Cargo.lock index dba09e1..b0dd360 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,7 @@ dependencies = [ "adler", "androscalpel_serializer", "anyhow", + "apk_frauder", "log", "pyo3 0.20.0", "pyo3-log", diff --git a/androscalpel/Cargo.toml b/androscalpel/Cargo.toml index 6393e70..6b39ffa 100644 --- a/androscalpel/Cargo.toml +++ b/androscalpel/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["cdylib"] adler = "1.0.2" androscalpel_serializer = { version = "0.1.0", path = "../androscalpel_serializer" } anyhow = { version = "1.0.75", features = ["backtrace"] } +apk_frauder = { version = "0.1.0", path = "../apk_frauder" } log = "0.4.20" pyo3 = { version = "0.20.0", features = ["anyhow"] } pyo3-log = "0.8.3" diff --git a/androscalpel/src/py_utils.rs b/androscalpel/src/py_utils.rs index 5c2fee5..2d7a61a 100644 --- a/androscalpel/src/py_utils.rs +++ b/androscalpel/src/py_utils.rs @@ -3,6 +3,9 @@ use pyo3::prelude::*; use pyo3::types::PyBytes; +use std::io::Cursor; +use std::path::PathBuf; + use crate::Result; use androscalpel_serializer::{Serializable, Sleb128, Uleb128, Uleb128p1}; @@ -41,6 +44,27 @@ pub fn sleb128_to_int(b: &[u8]) -> Result { Ok(Sleb128::deserialize_from_slice(b)?.0) } +/// Replace the dex files a an apk an resigned the apk. +/// +/// # Warning +/// +/// For now, only jks keystore are allowed. +/// +/// The `zipalign` and `apksigner` args allow to use a specific version of the +/// tools instead of the one in the PATH (if it even exist) +#[pyfunction] +pub fn replace_dex( + apk: PathBuf, + dst: PathBuf, + dexfiles: Vec<&[u8]>, + keystore: PathBuf, + zipalign: Option, + apksigner: Option, +) { + let mut dexfiles: Vec<_> = dexfiles.iter().map(Cursor::new).collect(); + apk_frauder::replace_dex(apk, dst, &mut dexfiles, keystore, zipalign, apksigner) +} + /// export the function in a python module pub(crate) fn export_module(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(int_to_uleb128, m)?)?; @@ -49,5 +73,6 @@ pub(crate) fn export_module(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(uleb128_to_int, m)?)?; m.add_function(wrap_pyfunction!(uleb128p1_to_int, m)?)?; m.add_function(wrap_pyfunction!(sleb128_to_int, m)?)?; + m.add_function(wrap_pyfunction!(replace_dex, m)?)?; Ok(()) } diff --git a/test.py b/test.py index 76db279..cd56bd7 100644 --- a/test.py +++ b/test.py @@ -8,9 +8,10 @@ import json import androscalpel as asc import zipfile as z from androscalpel import * +from pathlib import Path -# APK_NAME = "test.apk" -APK_NAME = __file__.removesuffix("test.py") + "/apk_frauder/app-release.apk" +# APK_NAME = Path(__file__).parent / "test.apk" +APK_NAME = Path(__file__).parent / "app-release.apk" DEX_NAME = "classes.dex" with z.ZipFile(APK_NAME) as zipf: @@ -74,15 +75,27 @@ for i in code.insns: print("[+] Recompile") dex_raw = apk.gen_raw_dex() -assert len(dex_raw) == 1 -with open(DEX_NAME, "wb") as file: - file.write(dex_raw[0]) + +utils.replace_dex( + APK_NAME, + APK_NAME.parent / "app-instrumented.apk", + dex_raw, + Path().parent / "my-release-key.jks", + zipalign=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "zipalign", + apksigner=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "apksigner", +) + +# assert len(dex_raw) == 1 +# with open(DEX_NAME, "wb") as file: +# file.write(dex_raw[0]) +# +# with open(DEX_NAME, "rb") as file: +# dex = file.read() print("[+] Load new dex") -with open(DEX_NAME, "rb") as file: - dex = file.read() new_apk = asc.Apk() -new_apk.add_dex_file(dex) +for dex in dex_raw: + new_apk.add_dex_file(dex) clazz = new_apk.classes[clazz_id] method = clazz.virtual_methods[method_id]