赞
踩
上一章我们介绍了rust
通过调用 Android
api
实现签名验证的方案.本章再介绍另一种方式,通过解压base.apk
读取META-INF
文件夹下的RSA
文件,并用openssl
进行验证.
Cargo.toml
openssl = {version="0.10.43", features = ["vendored"]}
zip = "0.6.3"
zip
库是用来解压base.apk
的.openssl
库则为我们提供了本章的核心功能------读取RSA
文件.在openssl
中启用vendored
feature
可以免去我们自行用ndk
编译openssl
源码的步骤.
#[no_mangle] pub extern "system" fn Java_com_jni_rust_RustNative_getSignatureOpenssl(env: JNIEnv, _: JClass) -> jstring { let activity_thread_clz = match env.find_class("android/app/ActivityThread") { Ok(activity_thread_clz) => activity_thread_clz, Err(_) => panic!() }; let application_value = match env.call_static_method(activity_thread_clz, "currentApplication", "()Landroid/app/Application;", &[]) { Ok(application_value) => { application_value } Err(_) => { panic!() } }; let application = match application_value.l() { Ok(application) => { application } Err(_) => { panic!() } }; let package_code_path = match env.call_method(application, "getPackageCodePath", "()Ljava/lang/String;", &[]) { Ok(package_code_path) => { package_code_path } Err(_) => { panic!() } }; let package_code_path = match package_code_path.l() { Ok(package_code_path) => { package_code_path } Err(_) => { panic!() } }; let package_code_path = JString::from(package_code_path); let package_code_path = match env.get_string(package_code_path) { Ok(package_code_path) => { package_code_path } Err(_) => { panic!() } }; let package_code_path: String = package_code_path.into(); log::d("openssl".to_string(), package_code_path.to_string()); let zip_file = match fs::File::open(package_code_path) { Ok(zip_file) => { zip_file } Err(_) => { panic!() } }; let mut zip = match zip::ZipArchive::new(zip_file) { Ok(zip) => { zip } Err(_) => { panic!() } }; for i in 0..zip.len() { let mut file = match zip.by_index(i) { Ok(file) => { file } Err(_) => { panic!() } }; if file.is_file() { if file.name().contains("META-INF") && file.name().contains(".RSA") { log::d("openssl".to_string(), file.name().to_string()); let mut file_bytes: Vec<u8> = vec![]; let _ = match file.read_to_end(&mut file_bytes) { Ok(_) => {} Err(_) => { panic!() } }; let pkcs7 = match Pkcs7::from_der(file_bytes.as_slice()) { Ok(pkcs7) => { pkcs7 } Err(_) => { panic!() } }; let empty_stack: Stack<X509> = match Stack::new() { Ok(empty_stack) => { empty_stack } Err(_) => { panic!() } }; let pkcs7_flags = Pkcs7Flags::STREAM; let stack = match pkcs7.signers(&empty_stack, pkcs7_flags) { Ok(stack) => { stack } Err(_) => { panic!() } }; let x509_ref = match stack.get(0) { None => { panic!() } Some(x509_ref) => { x509_ref } }; let digest_bytes = match x509_ref.digest(MessageDigest::md5()) { Ok(digest_bytes) => { digest_bytes } Err(_) => { panic!() } }; let sign_bytes = digest_bytes.to_vec(); let hex_sign: String = sign_bytes.iter() .map(|b| format!("{:02x}", b).to_string()) .collect::<Vec<String>>().join(""); log::d("openssl".to_string(), hex_sign.to_string()); let hex_sign = JNIString::from(hex_sign); let hex_sign = env.new_string(hex_sign).unwrap(); return hex_sign.into_raw(); } } } log::e("openssl".to_string(),"no signature found".to_string()); let result = JNIString::from("no signature found"); let result = env.new_string(result).unwrap(); result.into_raw() }
代码的整体流程是通过context.getPackageCodePath()
先获取到base.apk
的文件位置,然后通过zip
解压,找到META-INF
文件夹下的RSA
文件,再调用openssl
的api
读取这个RSA
文件.这段代码唯一的难点应该是大家可能对C
语言版的openssl
用法(api
)比较熟悉,但是不太熟悉rust
版的openssl
把对应api
给封装成什么名字了,所以调用起来会有不知道使用哪个api
的情况.这种情况我大概说下相关思路.
我们来大体过一下自己查看源码这个思路.
前提,我们应该知道openssl
的相关api
. 在我们想要读取一个.RSA
文件的时候,其方法为PKCS7 *p7 = d2i_PKCS7(NULL, &signature_msg, length);
.
rust openssl
中找一个名为d2i_PKCS7
的方法.d2i_PKCS7
是C
的方法,rust
调用C
的话,调用格式如下:extern "C" {
pub fn d2i_PKCS7(...)
}
所以我们有关键字d2i_PKCS7
,extern "C"
.
3. 源码中搜索,果然找到了一段代码:
extern "C" {
pub fn d2i_PKCS7(a: *mut *mut PKCS7, pp: *mut *const c_uchar, length: c_long) -> *mut PKCS7;
}
rust openssl
一定是在哪里封装了这个方法.所以查找调用的部分(我用的Clion
,快捷键ctrl+B
).Pkcs7
结构体的from_der
,代码如下:from_der! {
/// Deserializes a DER-encoded PKCS#7 signature
#[corresponds(d2i_PKCS7)]
from_der,
Pkcs7,
ffi::d2i_PKCS7
}
所以,就有了本段中的代码:
let pkcs7 = match Pkcs7::from_der(file_bytes.as_slice()) {
Ok(pkcs7) => { pkcs7 }
Err(_) => { panic!() }
};
成功读取到了Pkcs7
.
按照这个思路,可以查找到大多数XXX bindings for Rust
的具体api
,如果没找到的话也没关系,自己extern "C" ...
加上就好了.
error occurred: Failed to find tool. Is `aarch64-linux-android-clang` installed?
解决方案:
我们已经在~/.cargo/config
文件中配置过linker
和ar
了.但是如果编译的代码包含C
语言代码的话还需要进一步配置.在我的linux
环境下,配置CC
环境变量
export CC=<path>/NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang
error occurred: Failed to find tool. Is `aarch64-linux-android-ar` installed?
则还需配置环境变量:
export AR=<path>/NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar
cargo-bloat
可以帮助我们粗略的分析可执行文件中各个库的空间占用,github地址:https://github.com/RazrFalcon/cargo-bloat
cargo install cargo-bloat
so
文件cargo-bloat
时,需要先把Cargo.toml
中的strip = true
配置去掉cargo bloat --release --crates --target aarch64-linux-android
输出结果
File .text Size Crate 11.0% 34.4% 437.4KiB openssl_sys 8.8% 27.7% 352.0KiB [Unknown] 4.5% 14.1% 179.4KiB std 1.9% 6.0% 76.3KiB zstd_sys 0.5% 1.6% 20.0KiB miniz_oxide 0.3% 1.0% 12.9KiB bzip2_sys 0.3% 1.0% 12.7KiB jni 0.3% 0.8% 10.3KiB zip 0.1% 0.4% 4.6KiB sha1 0.1% 0.3% 3.9KiB combine 0.0% 0.1% 1.1KiB cesu8 0.0% 0.1% 976B openssl 0.0% 0.1% 944B flate2 0.0% 0.0% 624B bzip2 0.0% 0.0% 612B byteorder 0.0% 0.0% 436B crc32fast 0.0% 0.0% 400B android_logger_lite 0.0% 0.0% 400B bytes 0.0% 0.0% 260B zstd 0.0% 0.0% 232B adler 0.0% 0.0% 204B And 5 more crates. Use -n N to show more. 31.8% 100.0% 1.2MiB .text section size, the file size is 3.9MiB Note: numbers above are a result of guesswork. They are not 100% correct and never will be.
std
之后的结果 cargo bloat --release --crates --target aarch64-linux-android -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort
输出结果:
File .text Size Crate 19.2% 40.3% 437.3KiB openssl_sys 14.8% 31.0% 336.1KiB [Unknown] 3.4% 7.0% 76.3KiB zstd_sys 0.6% 1.3% 13.8KiB core 0.6% 1.2% 12.8KiB bzip2_sys 0.5% 1.0% 10.7KiB jni 0.4% 0.9% 9.6KiB zip 0.4% 0.9% 9.4KiB std 0.3% 0.7% 7.7KiB miniz_oxide 0.2% 0.4% 4.5KiB sha1 0.2% 0.4% 4.0KiB alloc 0.2% 0.4% 3.9KiB combine 0.0% 0.1% 1.0KiB hashbrown 0.0% 0.1% 728B flate2 0.0% 0.1% 684B cesu8 0.0% 0.1% 568B openssl 0.0% 0.0% 416B crc32fast 0.0% 0.0% 416B bzip2 0.0% 0.0% 280B byteorder 0.0% 0.0% 188B android_logger_lite 0.0% 0.0% 372B And 6 more crates. Use -n N to show more. 47.7% 100.0% 1.1MiB .text section size, the file size is 2.2MiB Note: numbers above are a result of guesswork. They are not 100% correct and never will be.
本章我们了解了使用openssl
进行android
签名验证的方法,虽然本方法放在整个系列的最后来讲,但并不代表它就是个完全安全的方案,只是实现签名验证的另一种思路而已,依旧存在被破解的风险.本系列文章的目标是为了让大家更加了解rust
与android
协同配合的方案,也是相比于以前只能用C/C++
,现在多了一个选择------rust
Android
项目地址:https://github.com/tangxuesong6/Android_Rust_JNI_Demo
rust
项目地址:https://github.com/tangxuesong6/Rust_JNI_Demo
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。