glueware
This commit is contained in:
parent
bce66067b0
commit
8a192b0e1a
5 changed files with 273 additions and 7 deletions
|
|
@ -83,9 +83,9 @@ pub fn transform_method(
|
||||||
while move_ret.as_ref() != iter.next() {}
|
while move_ret.as_ref() != iter.next() {}
|
||||||
}
|
}
|
||||||
let end_label = if method == &*MTH_INVOKE {
|
let end_label = if method == &*MTH_INVOKE {
|
||||||
format!("end_reflection_call_at_{}", "TODO_ADDR")
|
format!("end_reflection_call_at_{}", addr_label.clone())
|
||||||
} else if method == &*CLASS_NEW_INST || method == &*CNSTR_NEW_INST {
|
} else if method == &*CLASS_NEW_INST || method == &*CNSTR_NEW_INST {
|
||||||
format!("end_reflection_instanciation_at_{}", "TODO_ADDR")
|
format!("end_reflection_instanciation_at_{}", addr_label.clone())
|
||||||
} else {
|
} else {
|
||||||
panic!("Should not happen!")
|
panic!("Should not happen!")
|
||||||
};
|
};
|
||||||
|
|
@ -740,9 +740,9 @@ fn get_cnstr_new_inst_block(
|
||||||
}
|
}
|
||||||
|
|
||||||
let abort_label = format!(
|
let abort_label = format!(
|
||||||
"end_static_instance_with_{}_at_{}",
|
"end_static_instance_with_{}_at_{:08X}",
|
||||||
ref_data.constructor.try_to_smali()?,
|
ref_data.constructor.try_to_smali()?,
|
||||||
"TODO_ADDR"
|
ref_data.addr
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut insns = test_cnstr(
|
let mut insns = test_cnstr(
|
||||||
|
|
@ -854,9 +854,9 @@ fn get_class_new_inst_block(
|
||||||
let class_reg = class_reg as u8;
|
let class_reg = class_reg as u8;
|
||||||
|
|
||||||
let abort_label = format!(
|
let abort_label = format!(
|
||||||
"end_static_instance_with_{}_at_{}",
|
"end_static_instance_with_{}_at_{:08X}",
|
||||||
ref_data.constructor.try_to_smali()?,
|
ref_data.constructor.try_to_smali()?,
|
||||||
"TODO_ADDR"
|
ref_data.addr
|
||||||
);
|
);
|
||||||
|
|
||||||
let obj_reg = match move_result {
|
let obj_reg = match move_result {
|
||||||
|
|
|
||||||
3
theseus_autopatcher/.gitignore
vendored
Normal file
3
theseus_autopatcher/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
dist
|
||||||
|
__pycache__
|
||||||
|
src/theseus_autopatcher/patcher_86_64_musl
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Android Theseus Patcher
|
||||||
|
|
||||||
|
This is mostly glueware between the [theseus frida](../frida) python package (used to get runtime information) and the [theseus patcher](../patcher) (rust binary used tp patch the apk).
|
||||||
|
|
||||||
|
This package embed the patcher binary for ease of use. The embedded version is build for linux x86_64, statically linked to musl. For other target platform (windows, arm, ect), a different patcher binary can provided at runtime.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
TODO: use nix build the project
|
||||||
|
|
||||||
|
Before building this package, the patcher binary must be built with the musl target. This require the `x86_64-unknown-linux-musl` to be installed, as well as `musl-gcc`:
|
||||||
|
|
||||||
|
```
|
||||||
|
rustup target add x86_64-unknown-linux-musl
|
||||||
|
doas pacman -S musl
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the patcher:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd ../patcher
|
||||||
|
cargo build --release --target=x86_64-unknown-linux-musl
|
||||||
|
cd -
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy to patcher to the python directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
cp ../patcher/target/x86_64-unknown-linux-musl/release/patcher src/theseus_autopatcher/patcher_86_64_musl
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the package:
|
||||||
|
|
||||||
|
```
|
||||||
|
poetry build
|
||||||
|
```
|
||||||
|
|
@ -13,6 +13,9 @@ dependencies = [
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
packages = [{include = "theseus_autopatcher", from = "src"}]
|
packages = [{include = "theseus_autopatcher", from = "src"}]
|
||||||
|
include = [
|
||||||
|
{ path = "src/theseus_autopatcher/patcher_86_64_musl", format = ["sdist", "wheel"] },
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,226 @@
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import which
|
||||||
|
|
||||||
|
from theseus_frida import collect_runtime
|
||||||
|
|
||||||
|
|
||||||
|
def get_android_sdk_path() -> Path | None:
|
||||||
|
if "ANDROID_HOME" in os.environ:
|
||||||
|
return os.environ["ANDROID_HOME"]
|
||||||
|
default = Path.home() / "Android" / "Sdk"
|
||||||
|
if default.exists():
|
||||||
|
return default
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_build_tools_path(toolname: str) -> Path | None:
|
||||||
|
def score_version(name: str):
|
||||||
|
score = []
|
||||||
|
for n in name.split("."):
|
||||||
|
if n.isdecimal():
|
||||||
|
score.append(int(n))
|
||||||
|
else:
|
||||||
|
score.append(-1)
|
||||||
|
return score
|
||||||
|
|
||||||
|
path = which(toolname)
|
||||||
|
if path is not None:
|
||||||
|
return path
|
||||||
|
path = which(toolname + ".exe")
|
||||||
|
if path is not None:
|
||||||
|
return path
|
||||||
|
|
||||||
|
sdk = get_android_sdk_path()
|
||||||
|
if sdk is None:
|
||||||
|
return None
|
||||||
|
tools = sdk / "build-tools"
|
||||||
|
if not tools.exists():
|
||||||
|
return None
|
||||||
|
options = []
|
||||||
|
for d in tools.iterdir():
|
||||||
|
if (d / toolname).exists():
|
||||||
|
options.append(d / toolname)
|
||||||
|
if (d / (toolname + ".exe")).exists():
|
||||||
|
options.append(d / (toolname + ".exe"))
|
||||||
|
if not options:
|
||||||
|
return None
|
||||||
|
return max(options, key=lambda d: score_version(d.parent.name))
|
||||||
|
|
||||||
|
|
||||||
|
def get_keytool_path() -> Path | None:
|
||||||
|
path = which("keytool")
|
||||||
|
if path is not None:
|
||||||
|
return path
|
||||||
|
return which("keytool.exe")
|
||||||
|
|
||||||
|
|
||||||
|
def gen_keystore(keytool: Path, storepath: Path):
|
||||||
|
print(f"{str(storepath)} does not exist, creating it.")
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
str(keytool),
|
||||||
|
"-genkeypair",
|
||||||
|
"-validity",
|
||||||
|
"1000",
|
||||||
|
"-dname",
|
||||||
|
"CN=SomeKey,O=SomeOne,C=FR",
|
||||||
|
"-keystore",
|
||||||
|
str(storepath),
|
||||||
|
"-alias",
|
||||||
|
"SignKey",
|
||||||
|
"-keyalg",
|
||||||
|
"RSA",
|
||||||
|
"-v",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PATCHER_BIN_PATH = Path(__file__).parent / "patcher_86_64_musl"
|
||||||
|
|
||||||
|
|
||||||
|
def patch_apk(
|
||||||
|
runtime_data: Path,
|
||||||
|
apk: Path,
|
||||||
|
apkout: Path,
|
||||||
|
zipalign: Path,
|
||||||
|
apksigner: Path,
|
||||||
|
keystore: Path,
|
||||||
|
):
|
||||||
|
def dbg(l):
|
||||||
|
print(" ".join(l))
|
||||||
|
return l
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
dbg(
|
||||||
|
[
|
||||||
|
str(PATCHER_BIN_PATH.absolute()),
|
||||||
|
"--runtime-data",
|
||||||
|
str(runtime_data.absolute()),
|
||||||
|
"--path",
|
||||||
|
str(apk.absolute()),
|
||||||
|
"--out",
|
||||||
|
str(apkout.absolute()),
|
||||||
|
"-k",
|
||||||
|
str(keystore.absolute()),
|
||||||
|
"-z",
|
||||||
|
str(zipalign.absolute()),
|
||||||
|
"-a",
|
||||||
|
str(apksigner.absolute()),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("hello void")
|
parser = argparse.ArgumentParser(prog="Android Theseus project")
|
||||||
|
parser.add_argument(
|
||||||
|
"-a", "--apk", required=True, help="Target application", type=Path
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--device",
|
||||||
|
default="",
|
||||||
|
help="The android device to connect to, eg: 'emulator-5554'",
|
||||||
|
type=str,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--output-apk",
|
||||||
|
required=True,
|
||||||
|
help="Where to write the repackaged apk",
|
||||||
|
type=Path,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--zipalign",
|
||||||
|
required=False,
|
||||||
|
help="Path to the zipalign executable to use",
|
||||||
|
type=Path,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--apksigner",
|
||||||
|
required=False,
|
||||||
|
help="Path to the apksigner executable to use",
|
||||||
|
type=Path,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-k",
|
||||||
|
"--keystore",
|
||||||
|
required=False,
|
||||||
|
help="Path to the apksigner executable to use",
|
||||||
|
type=Path,
|
||||||
|
default=Path(".") / "TheseusKey.keystore",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--keytool",
|
||||||
|
required=False,
|
||||||
|
help="Path to the keytool executable to use",
|
||||||
|
type=Path,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--patch",
|
||||||
|
required=False,
|
||||||
|
help="Path to the patcher executable to use. By default, use the one embeded with \
|
||||||
|
the package. (static x86_64 linux build with musl)",
|
||||||
|
type=Path,
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.zipalign is None:
|
||||||
|
zipalign = get_build_tools_path("zipalign")
|
||||||
|
else:
|
||||||
|
zipalign = args.zipalign
|
||||||
|
if args.apksigner is None:
|
||||||
|
apksigner = get_build_tools_path("apksigner")
|
||||||
|
else:
|
||||||
|
apksigner = args.apksigner
|
||||||
|
if args.keytool is None:
|
||||||
|
keytool = get_keytool_path()
|
||||||
|
else:
|
||||||
|
keytool = args.keytool
|
||||||
|
|
||||||
|
if zipalign is None:
|
||||||
|
print(
|
||||||
|
"Could not find zipalign, please install an android build-tools package. "
|
||||||
|
"If one is already installed, please use `--zipalign` to provide the path "
|
||||||
|
"to the zipalign executable."
|
||||||
|
)
|
||||||
|
exit(1)
|
||||||
|
if apksigner is None:
|
||||||
|
print(
|
||||||
|
"Could not find apksigner, please install an android build-tools package. "
|
||||||
|
"If one is already installed, please use `--apksigner` to provide the path "
|
||||||
|
"to the apksigner executable."
|
||||||
|
)
|
||||||
|
exit(1)
|
||||||
|
if keytool is None and not args.keystore.exists():
|
||||||
|
print(
|
||||||
|
f"Could not find keytool and {str(args.keystore)} does not exist. Either "
|
||||||
|
"provide an existing keystore with -k or install a JDK. If one is already installed, "
|
||||||
|
"please use --keytool to provide the path to the keytool executable."
|
||||||
|
)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if not args.keystore.exists():
|
||||||
|
gen_keystore(keytool, args.keystore)
|
||||||
|
|
||||||
|
tmpdname = "/tmp/tmp.xzq9jLxUbQ/tmp"
|
||||||
|
if True:
|
||||||
|
# with tempfile.TemporaryDirectory() as tmpdname:
|
||||||
|
tmpd = Path(tmpdname)
|
||||||
|
(tmpd / "dex").mkdir()
|
||||||
|
with (tmpd / "runtime.json").open("w") as fp:
|
||||||
|
collect_runtime(
|
||||||
|
apk=args.apk, device=args.device, file_storage=tmpd / "dex", output=fp
|
||||||
|
)
|
||||||
|
patch_apk(
|
||||||
|
runtime_data=tmpd / "runtime.json",
|
||||||
|
apk=args.apk,
|
||||||
|
apkout=args.output_apk,
|
||||||
|
zipalign=zipalign,
|
||||||
|
apksigner=apksigner,
|
||||||
|
keystore=args.keystore,
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue