put all the dex swapping & resigning shenanigans in a function
This commit is contained in:
parent
86a028f0bd
commit
cb34f76063
5 changed files with 160 additions and 73 deletions
54
Cargo.lock
generated
54
Cargo.lock
generated
|
|
@ -65,6 +65,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"androscalpel_serializer",
|
"androscalpel_serializer",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -182,6 +183,17 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.28.1"
|
version = "0.28.1"
|
||||||
|
|
@ -324,6 +336,12 @@ dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.78"
|
version = "1.0.78"
|
||||||
|
|
@ -436,6 +454,36 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
@ -561,6 +609,12 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
||||||
1
apk_frauder/.gitignore
vendored
1
apk_frauder/.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
*.zip
|
*.zip
|
||||||
*.dex
|
*.dex
|
||||||
*.idsig
|
*.idsig
|
||||||
|
*.jks
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
androscalpel_serializer = { version = "0.1.0", path = "../androscalpel_serializer" }
|
androscalpel_serializer = { version = "0.1.0", path = "../androscalpel_serializer" }
|
||||||
flate2 = { version = "1.0.28", features = ["rust_backend"] }
|
flate2 = { version = "1.0.28", features = ["rust_backend"] }
|
||||||
|
rand = "0.8.5"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
use androscalpel_serializer::Serializable;
|
use androscalpel_serializer::Serializable;
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Seek};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
pub mod apk_signing_block;
|
pub mod apk_signing_block;
|
||||||
pub mod compression;
|
pub mod compression;
|
||||||
|
|
@ -132,3 +138,87 @@ impl FileInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
pub fn replace_dex(
|
||||||
|
apk: impl AsRef<Path>,
|
||||||
|
dst: impl AsRef<Path>,
|
||||||
|
dexfiles: &mut [impl Read + Seek],
|
||||||
|
keystore: impl AsRef<Path>, // TODO enum for handling p11 and generating a random cert
|
||||||
|
// `keytool -genkey -v -keystore KEYSTORE.jks -keyalg RSA -keysize
|
||||||
|
// 2048 -validity 10000 -alias ALIAS`
|
||||||
|
zipalign: Option<impl AsRef<Path>>,
|
||||||
|
apksigner: Option<impl AsRef<Path>>,
|
||||||
|
) {
|
||||||
|
let zipalign = if let Some(path) = &zipalign {
|
||||||
|
path.as_ref().as_os_str()
|
||||||
|
} else {
|
||||||
|
"zipalign".as_ref()
|
||||||
|
};
|
||||||
|
let apksigner = if let Some(path) = &apksigner {
|
||||||
|
path.as_ref().as_os_str()
|
||||||
|
} else {
|
||||||
|
"apksigner".as_ref()
|
||||||
|
};
|
||||||
|
let tmp_dir = env::temp_dir().join(format!("apk_frauder_{:x}", rand::random::<u128>()));
|
||||||
|
let unaligned_path = tmp_dir.join("stripped.apk");
|
||||||
|
let aligned_path = tmp_dir.join("aligned.apk");
|
||||||
|
fs::create_dir_all(&tmp_dir).expect("Failed to create temporary directory");
|
||||||
|
|
||||||
|
let file = File::open(apk).expect("failed to open file");
|
||||||
|
let mut apk = ZipFileReader::new(file);
|
||||||
|
let file = File::create(&unaligned_path).expect("failed to create file");
|
||||||
|
let mut apk_out = ZipFileWriter::new(file, apk.zip64_end_of_central_directory.clone());
|
||||||
|
|
||||||
|
let mut file_info_ref = (*apk
|
||||||
|
.get_classes_file_info()
|
||||||
|
.first()
|
||||||
|
.expect("No dex file found in apk"))
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
apk.unlink_signature_files();
|
||||||
|
apk.unlink_bytecode_files();
|
||||||
|
for f in apk.files.clone() {
|
||||||
|
apk_out.insert_file_from_zip(f, &mut apk);
|
||||||
|
}
|
||||||
|
for (i, mut dex) in dexfiles.iter_mut().enumerate() {
|
||||||
|
if i == 0 {
|
||||||
|
file_info_ref.set_name("classes.dex");
|
||||||
|
} else {
|
||||||
|
file_info_ref.set_name(&format!("classes{}.dex", i + 1));
|
||||||
|
}
|
||||||
|
apk_out.insert_file(
|
||||||
|
&mut dex,
|
||||||
|
file_info_ref.header.clone(),
|
||||||
|
Some(file_info_ref.local_header.clone()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
apk_out.write_central_directory();
|
||||||
|
// TODO: we can probably do that ourself an spare ourself the trouble of finding zipalign
|
||||||
|
Command::new(zipalign)
|
||||||
|
.arg("-v")
|
||||||
|
.arg("-p")
|
||||||
|
.arg("4")
|
||||||
|
.arg(unaligned_path.as_os_str())
|
||||||
|
.arg(aligned_path.as_os_str())
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Command::new(apksigner)
|
||||||
|
.arg("sign")
|
||||||
|
.arg("--ks")
|
||||||
|
.arg(keystore.as_ref().as_os_str())
|
||||||
|
.arg("--out")
|
||||||
|
.arg(dst.as_ref().as_os_str())
|
||||||
|
.arg(aligned_path.as_os_str())
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
fs::remove_dir_all(tmp_dir).expect("Failled to remove tmp dir");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,78 +1,19 @@
|
||||||
use apk_frauder::ZipFileReader;
|
use std::env;
|
||||||
use apk_frauder::ZipFileWriter;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Remove previous generation
|
apk_frauder::replace_dex(
|
||||||
println!("Remove files from previous run");
|
"app-release.apk",
|
||||||
Command::new("rm").arg("app-striped.apk").status().unwrap();
|
"app-instrumented.apk",
|
||||||
Command::new("rm")
|
&mut [File::open("classes.dex").expect("failed to open file")],
|
||||||
.arg("app-instrumented.apk")
|
"my-release-key.jks",
|
||||||
.status()
|
Some(&format!(
|
||||||
.unwrap();
|
"{}/Android/Sdk/build-tools/34.0.0/zipalign",
|
||||||
Command::new("rm")
|
env::var("HOME").expect("$HOME not set")
|
||||||
.arg("app-instrumented-signed.apk")
|
)),
|
||||||
.status()
|
Some(&format!(
|
||||||
.unwrap();
|
"{}/Android/Sdk/build-tools/34.0.0/apksigner",
|
||||||
Command::new("rm")
|
env::var("HOME").expect("$HOME not set")
|
||||||
.arg("app-instrumented-aligned.apk")
|
)),
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!("Read the headers of the existing dex file and strip the apk signature and dex files");
|
|
||||||
let file = File::open("app-release.apk").expect("failed to open file");
|
|
||||||
let mut apk = ZipFileReader::new(file);
|
|
||||||
|
|
||||||
let mut file_info_ref = (*apk.get_classes_file_info().first().unwrap()).clone();
|
|
||||||
|
|
||||||
apk.unlink_signature_files();
|
|
||||||
apk.unlink_bytecode_files();
|
|
||||||
|
|
||||||
let out_file = File::create("app-striped.apk").expect("failed to create file");
|
|
||||||
let mut apk_out = ZipFileWriter::new(out_file, apk.zip64_end_of_central_directory.clone());
|
|
||||||
for f in apk.files.clone() {
|
|
||||||
apk_out.insert_file_from_zip(f, &mut apk);
|
|
||||||
}
|
|
||||||
apk_out.write_central_directory();
|
|
||||||
|
|
||||||
println!("Insert the dex file to the stripped apk with the header of the original dex file");
|
|
||||||
let file = File::open("app-striped.apk").expect("failed to open file");
|
|
||||||
let mut bytecodes = File::open("classes.dex").expect("failed to open file");
|
|
||||||
let mut apk = ZipFileReader::new(file);
|
|
||||||
let out_file = File::create("app-instrumented.apk").expect("failed to create file");
|
|
||||||
let mut apk_out = ZipFileWriter::new(out_file, apk.zip64_end_of_central_directory.clone());
|
|
||||||
for f in apk.files.clone() {
|
|
||||||
apk_out.insert_file_from_zip(f, &mut apk);
|
|
||||||
}
|
|
||||||
file_info_ref.set_name("classes.dex");
|
|
||||||
apk_out.insert_file(
|
|
||||||
&mut bytecodes,
|
|
||||||
file_info_ref.header,
|
|
||||||
Some(file_info_ref.local_header),
|
|
||||||
);
|
);
|
||||||
apk_out.write_central_directory();
|
|
||||||
|
|
||||||
println!("Align the apk");
|
|
||||||
// This could probably be performed by ourself
|
|
||||||
Command::new("/home/histausse/Android/Sdk/build-tools/34.0.0/zipalign")
|
|
||||||
.arg("-v")
|
|
||||||
.arg("-p")
|
|
||||||
.arg("4")
|
|
||||||
.arg("app-instrumented.apk")
|
|
||||||
.arg("app-instrumented-aligned.apk")
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!("Sign the apk");
|
|
||||||
Command::new("/home/histausse/Android/Sdk/build-tools/34.0.0/apksigner")
|
|
||||||
.arg("sign")
|
|
||||||
.arg("--ks")
|
|
||||||
.arg("/home/histausse/AndroidStudioProjects/TestApplication/my-release-key.jks")
|
|
||||||
.arg("--out")
|
|
||||||
.arg("app-instrumented-signed.apk")
|
|
||||||
.arg("app-instrumented-aligned.apk")
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue