Rust NDK 開發(fā)#2 - 簽名校驗

[TOC]

  • 開發(fā)工具:Android Studio 2020.3.1
  • Rust 版本:1.57.0
  • NDK 版本: 21.4.7075529
  • 電腦系統(tǒng):Windows 10 家庭中文版 (21H1) 64位

在開發(fā)中则北,為了防止別人對我們的apk進(jìn)行二次打包,都會加上簽名校驗的操作朋魔,這部分代碼寫在java層對于破解來說形同虛設(shè),寫在NDK可以一定程度上的增加破解的難度显押。使用 C/C++ 寫的簽名校驗例子很多,下面分享下關(guān)于使用 Rust 編寫的校驗代碼傻挂。如果只想看代碼乘碑,可直接訪問gitee倉庫

Java 代碼:hijack/src/main/java/com/hulytu/android/hijack/AntiHijack.java · Chris/ThinDroid

Rust 代碼:hijack_rust · Chris/ThinDroid

# 準(zhǔn)備工作

  • Android studioIntelliJ IDEA 建議安裝下 Rust 這個插件,安裝完會提示安裝 Toml 金拒,也一并安裝上兽肤。
  • Rustcargo 的安裝,可參照上一節(jié)進(jìn)行绪抛。

一资铡、 Java 代碼

`Java` 層代碼比較簡單,比如我在 `NDK` 初始化的時候添加簽名校驗
package com.hulytu.android.hijack;

import android.content.Context;

public class AntiHijack {

    static {
        System.loadLibrary("hijack");
    }

    public static native boolean init(Context context);
}

二幢码、 Rust 實現(xiàn)

  1. rust 項目創(chuàng)建

    上一節(jié)我講了cargo-ndk 的安裝笤休,這節(jié)就直接使用 cargo 來創(chuàng)建一個項目,這個目錄可以放在和當(dāng)前項目目錄下症副,也可以放在其他地方店雅,建議和當(dāng)前項目放在一起。

    • 可以使用 Android studio 自帶的控制臺(Terminal 一般在底部位置)進(jìn)入到當(dāng)前項目目錄下贞铣,我的項目叫ThinDroid 那打開的效果是這樣的:
rust_ndk_2_terminal.png
  • 然后輸入命令

    cargo new hijack_rust --lib
    

    然后會在當(dāng)前目錄下生成 hijack_rust 這個目錄闹啦,名字可以取其他的。

  • 添加jni依賴

    進(jìn)入 hijack_rust 目錄下辕坝,打開 Cargo.toml 窍奋,在 dependencies 節(jié)點下添加

    jni = "0.19.0"
    

    添加 lib 節(jié)點,其中 name 表示生成 so 文件的名字

    [lib]
    name = "hijack"
    crate_type = ["dylib"]
    
  • 完整Cargo.toml

    [package]
    name = "hijack_rust"
    version = "0.1.0"
    edition = "2021"
    
    [lib]
    name = "hijack"
    crate_type = ["dylib"]
    
    [dependencies]
    jni = "0.19.0"
    
  1. 編寫 Rust 代碼

    打開 src/lib.rs 文件酱畅,刪除原來的代碼

    先放完整代碼费变,后面進(jìn)行拆解

    use std::ffi::c_void;
    
    use jni::{JavaVM, JNIEnv, NativeMethod};
    use jni::objects::{JObject, JString, JValue};
    use jni::strings::JNIString;
    use jni::sys::*;
    
    // 校驗的包名
    macro_rules! app_package { () => { "com.hulytu.android" }; }
    
    // 檢驗的簽名 hash-code 獲取方式可使用 com.hulytu.android.hijack.Utils.getSignInfoHashCode 方式獲取
    macro_rules! signature { () => { -779219788 }; }
    
    #[no_mangle]
    #[allow(non_snake_case)]
    fn JNI_OnLoad(jvm: JavaVM, _reserved: *mut c_void) -> jint {
        let methods = [
            NativeMethod { name: JNIString::from("init"), sig: JNIString::from("(Landroid/content/Context;)Z"), fn_ptr: init as *mut c_void },
        ];
    
        let env = jvm.get_env().unwrap();
        let version = env.get_version().unwrap();
    
        // 注冊native 方法
        let cls = match env.find_class("com/hulytu/android/hijack/AntiHijack") {
            Ok(clazz) => clazz,
            Err(_) => { return JNI_ERR; }
        };
    
        let result = env.register_native_methods(cls, &methods);
    
        return if result.is_ok() { version.into() } else { JNI_ERR };
    }
    
    
    fn init(env: JNIEnv, _: jclass, context: JObject) -> jboolean {
        if !is_valid_package(&env, &context) {
            return JNI_FALSE;
        }
    
        JNI_TRUE
    }
    
    
    fn is_valid_package(env: &JNIEnv, context: &JObject) -> bool {
    
        // #1
        let package_name = env.call_method(*context, "getPackageName", "()Ljava/lang/String;", &[]).unwrap();
    
        // app: com.hulytu.android
        let pkg = app_package!();
        let expect_package = env.new_string(pkg)
            .expect("load string error.");
    
        // 暫時沒想到好的比較方案 臨時解決辦法
        let obj = JObject::from(expect_package);
        let equals = env.call_method(obj, "equals", "(Ljava/lang/Object;)Z", &[package_name]).unwrap();
    
        // 比較包名
        if !equals.z().unwrap() {
            return false;
        }
    
        // #2
        // 獲取簽名
        let manager = env.call_method(*context,
                                      "getPackageManager",
                                      "()Landroid/content/pm/PackageManager;",
                                      &[]).unwrap().l();
    
        let version_clazz = env.find_class("android/os/Build$VERSION").unwrap();
        let sdk_int = env.get_static_field(version_clazz,
                                           "SDK_INT",
                                           "I").unwrap().i().unwrap();
    
    
        let args = [package_name, JValue::Int(if sdk_int >= 28 { 0x08000000 } else { 64 })];
        let result = env.call_method(manager.unwrap(), "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;", &args);
    
        let pkg_info = match result {
            Ok(info) => info,
            Err(_) => return false,
        };
    
        let signatures: JValue;
    
        if sdk_int >= 28 {
            let result = env.get_field(pkg_info.l().unwrap(), "signingInfo", "Landroid/content/pm/SigningInfo;");
            let info = match result {
                Ok(sif) => sif,
                Err(_) => return false,
            };
    
            let result = env.call_method(info.l().unwrap(), "getApkContentsSigners", "()[Landroid/content/pm/Signature;", &[]);
            signatures = match result {
                Ok(rs) => rs,
                Err(_) => return false,
            };
        } else {
            // 低于 28 獲取方式
            let result = env.get_field(pkg_info.l().unwrap(), "signatures", "[Landroid/content/pm/Signature;");
    
            signatures = match result {
                Ok(rs) => rs,
                Err(_) => return false,
            };
        }
    
        let signatures = signatures.l().unwrap();
    
        if signatures.is_null() {
            return false;
        }
    
        // #3
        let array = jobjectArray::from(*signatures);
        if env.get_array_length(array).unwrap() == 0 { return false; }
        let signature = env.get_object_array_element(array, jsize::from(0)).unwrap();
    
        if signature.is_null() { return false; }
    
        let hash = env.call_method(signature, "hashCode", "()I", &[]).unwrap().i().unwrap();
    
        hash == signature!()
    }
    
  1. 動態(tài)注冊

    這部分我使用動態(tài)注冊的方式來綁定到 com.hulytu.android.hijack.AntiHijack#init(Context)方法。

    #[no_mangle]
    #[allow(non_snake_case)]
    fn JNI_OnLoad(jvm: JavaVM, _reserved: *mut c_void) -> jint {
            let methods = [
            NativeMethod { name: JNIString::from("init"), sig: JNIString::from("(Landroid/content/Context;)Z"), fn_ptr: init as *mut c_void },
        ];
    
        let env = jvm.get_env().unwrap();
        let version = env.get_version().unwrap();
    
        // 注冊native 方法
        let cls = match env.find_class("com/hulytu/android/hijack/AntiHijack") {
            Ok(clazz) => clazz,
            Err(_) => { return JNI_ERR; }
        };
    
        let result = env.register_native_methods(cls, &methods);
    
        return if result.is_ok() { version.into() } else { JNI_ERR };
    }
    
  1. 簽名校驗

    fn is_valid_package(env: &JNIEnv, context: &JObject) -> bool {
    
        // #1
        let package_name = env.call_method(*context, "getPackageName", "()Ljava/lang/String;", &[]).unwrap();
    
        // app: com.hulytu.android
        let pkg = app_package!();
        let expect_package = env.new_string(pkg)
            .expect("load string error.");
    
        // 暫時沒想到好的比較方案 臨時解決辦法
        let obj = JObject::from(expect_package);
        let equals = env.call_method(obj, "equals", "(Ljava/lang/Object;)Z", &[package_name]).unwrap();
    
        // 比較包名
        if !equals.z().unwrap() {
            return false;
        }
    
        // #2
        // 獲取簽名
        let manager = env.call_method(*context,
                                      "getPackageManager",
                                      "()Landroid/content/pm/PackageManager;",
                                      &[]).unwrap().l();
    
        let version_clazz = env.find_class("android/os/Build$VERSION").unwrap();
        let sdk_int = env.get_static_field(version_clazz,
                                           "SDK_INT",
                                           "I").unwrap().i().unwrap();
    
    
        let args = [package_name, JValue::Int(if sdk_int >= 28 { 0x08000000 } else { 64 })];
        let result = env.call_method(manager.unwrap(), "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;", &args);
    
        let pkg_info = match result {
            Ok(info) => info,
            Err(_) => return false,
        };
    
        let signatures: JValue;
    
        if sdk_int >= 28 {
            let result = env.get_field(pkg_info.l().unwrap(), "signingInfo", "Landroid/content/pm/SigningInfo;");
            let info = match result {
                Ok(sif) => sif,
                Err(_) => return false,
            };
    
            let result = env.call_method(info.l().unwrap(), "getApkContentsSigners", "()[Landroid/content/pm/Signature;", &[]);
            signatures = match result {
                Ok(rs) => rs,
                Err(_) => return false,
            };
        } else {
            // 低于 28 獲取方式
            let result = env.get_field(pkg_info.l().unwrap(), "signatures", "[Landroid/content/pm/Signature;");
    
            signatures = match result {
                Ok(rs) => rs,
                Err(_) => return false,
            };
        }
    
        let signatures = signatures.l().unwrap();
    
        if signatures.is_null() {
            return false;
        }
    
        // #3
        let array = jobjectArray::from(*signatures);
        if env.get_array_length(array).unwrap() == 0 { return false; }
        let signature = env.get_object_array_element(array, jsize::from(0)).unwrap();
    
        if signature.is_null() { return false; }
    
        let hash = env.call_method(signature, "hashCode", "()I", &[]).unwrap().i().unwrap();
    
        hash == signature!()
    }
    
  2. 宏定義

    上面使用了兩個宏

    // 校驗的包名
    macro_rules! app_package { () => { "com.hulytu.android" }; }
    
    // 檢驗的簽名 hash-code
    macro_rules! signature { () => { -779219788 }; }
    

    主要是方便在修改代碼的時候不需要去到具體方法里面去修改

  1. 構(gòu)建打包圣贸,如構(gòu)建 armabi-v7 命令
    cargo ndk -t armeabi-v7a -o ./jniLibs build --release
    

正常構(gòu)建完成后挚歧,會在當(dāng)前目錄下生成 jniLibs ,把此目錄復(fù)制到項目中吁峻,如 app/src/main/jniLibs滑负;在項目中需要的地方調(diào)用。


后面計劃:gitee 上代碼已經(jīng)完成用含,博客有延后

  • mt 管理器 一鍵過簽名校驗
  • 使用 rust 對字符串進(jìn)行簽名

本文同步發(fā)布到 Rust NDK 開發(fā)#2 - 簽名校驗 - 八阿哥客棧 (hulytu.com)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矮慕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子啄骇,更是在濱河造成了極大的恐慌痴鳄,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缸夹,死亡現(xiàn)場離奇詭異痪寻,居然都是意外死亡螺句,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門橡类,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛇尚,“玉大人,你說我怎么就攤上這事顾画∪〗伲” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵研侣,是天一觀的道長谱邪。 經(jīng)常有香客問我,道長庶诡,這世上最難降的妖魔是什么惦银? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮灌砖,結(jié)果婚禮上璧函,老公的妹妹穿的比我還像新娘傀蚌。我一直安慰自己基显,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布善炫。 她就那樣靜靜地躺著撩幽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪箩艺。 梳的紋絲不亂的頭發(fā)上窜醉,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音艺谆,去河邊找鬼榨惰。 笑死,一個胖子當(dāng)著我的面吹牛静汤,可吹牛的內(nèi)容都是我干的琅催。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼虫给,長吁一口氣:“原來是場噩夢啊……” “哼藤抡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抹估,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缠黍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后药蜻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓷式,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡替饿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒿往。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盛垦。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瓤漏,靈堂內(nèi)的尸體忽然破棺而出腾夯,到底是詐尸還是另有隱情,我是刑警寧澤蔬充,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布蝶俱,位于F島的核電站,受9級特大地震影響饥漫,放射性物質(zhì)發(fā)生泄漏榨呆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一庸队、第九天 我趴在偏房一處隱蔽的房頂上張望积蜻。 院中可真熱鬧,春花似錦彻消、人聲如沸竿拆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丙笋。三九已至,卻和暖如春煌贴,著一層夾襖步出監(jiān)牢的瞬間御板,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工牛郑, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留怠肋,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓淹朋,卻偏偏與公主長得像笙各,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瑞你,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355