use cargo_emit::rerun_if_changed;
use noak::{writer::ClassWriter, AccessFlags};
use std::{collections::HashMap, env::var, fs::OpenOptions, io::Write, path::PathBuf};
pub fn gen_class(
class: &str,
interfaces: &[&str],
method_map: &HashMap<&str, (bool, String)>,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut bytes = Vec::new();
ClassWriter::new()
.version(noak::Version::V8)?
.access_flags(AccessFlags::PUBLIC | AccessFlags::SUPER)?
.this_class(class)?
.super_class("java/lang/Object")?
.interfaces(|faces| {
for interface in interfaces.iter() {
faces.begin(|i| i.interface(*interface))?;
}
Ok(())
})?
.fields(|_| Ok(()))?
.methods(|methods| {
for (name, (is_static, sig)) in method_map.iter() {
methods.begin(|m| {
let mut flags = AccessFlags::PUBLIC | AccessFlags::NATIVE;
if *is_static {
flags |= AccessFlags::STATIC;
}
m.access_flags(flags)?
.name(*name)?
.descriptor(sig.as_str())?
.attributes(|_| Ok(()))
})?;
}
Ok(())
})?
.attributes(|_| Ok(()))?
.write_bytes_to(&mut bytes)?;
Ok(bytes)
}
fn main() {
let target_os = var("CARGO_CFG_TARGET_OS").unwrap();
if target_os != "android" {
return;
}
rerun_if_changed!("build.rs");
let out_dir = PathBuf::from(var("OUT_DIR").unwrap());
const CLASS: &str = "rust/CallMethodHook";
let java_class_path = out_dir.join("CallMethodHook.class");
let mut java_class_file = OpenOptions::new()
.create(true)
.write(true)
.open(&java_class_path)
.unwrap();
let mut methods = HashMap::new();
methods.insert(
"invoke",
(
false,
"(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"
.to_string(),
),
);
let class = gen_class(CLASS, &["java/lang/reflect/InvocationHandler"], &methods).unwrap();
java_class_file.write_all(&class).unwrap();
let android_jar_path = android_build::android_jar(None).expect("Failed to find android.jar");
let d8_jar_path = android_build::android_d8_jar(None).expect("Failed to find d8.jar");
let _ = android_build::JavaRun::new()
.class_path(d8_jar_path)
.main_class("com.android.tools.r8.D8")
.arg("--classpath")
.arg(android_jar_path)
.arg("--output")
.arg(&out_dir)
.arg(&java_class_path)
.run()
.expect("failed to acquire exit status for java d8.jar invocation")
.success();
}