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() {}
|
||||
}
|
||||
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 {
|
||||
format!("end_reflection_instanciation_at_{}", "TODO_ADDR")
|
||||
format!("end_reflection_instanciation_at_{}", addr_label.clone())
|
||||
} else {
|
||||
panic!("Should not happen!")
|
||||
};
|
||||
|
|
@ -740,9 +740,9 @@ fn get_cnstr_new_inst_block(
|
|||
}
|
||||
|
||||
let abort_label = format!(
|
||||
"end_static_instance_with_{}_at_{}",
|
||||
"end_static_instance_with_{}_at_{:08X}",
|
||||
ref_data.constructor.try_to_smali()?,
|
||||
"TODO_ADDR"
|
||||
ref_data.addr
|
||||
);
|
||||
|
||||
let mut insns = test_cnstr(
|
||||
|
|
@ -854,9 +854,9 @@ fn get_class_new_inst_block(
|
|||
let class_reg = class_reg as u8;
|
||||
|
||||
let abort_label = format!(
|
||||
"end_static_instance_with_{}_at_{}",
|
||||
"end_static_instance_with_{}_at_{:08X}",
|
||||
ref_data.constructor.try_to_smali()?,
|
||||
"TODO_ADDR"
|
||||
ref_data.addr
|
||||
);
|
||||
|
||||
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]
|
||||
packages = [{include = "theseus_autopatcher", from = "src"}]
|
||||
include = [
|
||||
{ path = "src/theseus_autopatcher/patcher_86_64_musl", format = ["sdist", "wheel"] },
|
||||
]
|
||||
|
||||
|
||||
[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():
|
||||
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