android hook 框架 xposed 如何實現注入


Android so注入-libinject2 簡介、編譯、運行

Android so注入-libinject2  如何實現so注入

Android so注入-Libinject 如何實現so注入

Android so注入掛鈎-Adbi 框架簡介、編譯、運行

Android so注入掛鈎-Adbi 框架如何實現so注入

Android so注入掛鈎-Adbi 框架如何實現so函數掛鈎

Android so注入掛鈎-Adbi 框架如何實現dalvik函數掛鈎

Android dalvik掛鈎-Xposed框架如何實現注入

Android dalvik掛鈎-Xposed框架如何實現掛鈎

 

前面分析的adbi框架和libinject都是使用so注入的方式,實現將指定代碼裝入目標進程,這種方式有幾個特點:

1. 是動態的,需要目標進程已經啟動

2. 無法影響全局,比如注入A進程掛鈎里邊libc.so的open函數,此時,B進程使用的libc.so的open函數還是老函數,linux系統通過COW機制,在你注入A進程並執行對open的掛鈎的時候,拷貝了新的頁面,放入新的函數。如果要影響全局,應該注入到類似 Zygote 這樣的進程,且應該在zygote進程啟動之后馬上注入,這樣后續zygote進程生成子進程時就能使用掛鈎后的函數

3. 需要依賴ptrace機制,某些情況下,目標進程無法被執行ptrace,則這種方式會失效

這一篇我們分析另外一種方式,是著名的xposed框架使用的方式,不需要動態注入,而是直接替換android系統的一個可執行程序。

 

一,android應用層進程啟動最初始的幾步

linux系統裝載並初始化各個子系統完畢后,執行第一個應用層程序init, android 的 init 程序是自己定制的,與其它linux發行版不一樣,它同樣會解析並執行 init.rc 配置文件。其中,有一步如下,調用 app_process 程序啟動 zygote 進程,xposed 替換的就是這個  /system/bin/app_process 程序

 

system/core/rootdir/init.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

 

android\frameworks\base\cmds\app_process\app_main.cpp  : main 函數

  if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // Remainder of args get passed to startup class main()
        runtime.mClassName = className;
        runtime.mArgC = argc - i;
        runtime.mArgV = argv + i;
        runtime.start("com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    } else {

app_process 是native世界進入java世界的入口,它初始化了虛擬機的執行時環境,並根據不同的參數,調用 com.android.internal.os.ZygoteInit 或者 com.android.internal.os.RuntimeInit 這兩個java類的main函數,如果是前者,則進入的 zygote 的世界。

 

Xposed\app_main.cpp : main 函數

if (zygote) {
        runtime.start(keepLoadingXposed ? XPOSED_CLASS_DOTS : "com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // Remainder of args get passed to startup class main()
        runtime.mClassName = className;
        runtime.mArgC = argc - i;
        runtime.mArgV = argv + i;
        runtime.start(keepLoadingXposed ? XPOSED_CLASS_DOTS : "com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    } else {

#define XPOSED_CLASS_DOTS "de.robv.android.xposed.XposedBridge"

與標准流程不一樣的地方,如果檢測到android版本支持xposed且已經安裝了Xposed,則 runtime.start 啟動的是  de.robv.android.xposed.XposedBridge 的main函數,進入了 xposed 的世界

 

xposedbridge.java

private static void main(String[] args) {
        // the class the VM has been created for or null for the Zygote process
        String startClassName = getStartClassName();

        // initialize the Xposed framework and modules
        try {
            // initialize log file
            try {
                logFile = new File(BASE_DIR + "log/error.log");
                if (startClassName == null && logFile.length() > MAX_LOGFILE_SIZE_SOFT)
                    logFile.renameTo(new File(BASE_DIR + "log/error.log.old"));
                logWriter = new PrintWriter(new FileWriter(logFile, true));
                logFile.setReadable(true, false);
                logFile.setWritable(true, false);
            } catch (IOException ignored) {}

            String date = DateFormat.getDateTimeInstance().format(new Date());
            determineXposedVersion();
            log("-----------------\n" + date + " UTC\n"
                    + "Loading Xposed v" + XPOSED_BRIDGE_VERSION
                    + " (for " + (startClassName == null ? "Zygote" : startClassName) + ")...");
            if (startClassName == null) {
                // Zygote
                log("Running ROM '" + Build.DISPLAY + "' with fingerprint '" + Build.FINGERPRINT + "'");
            }

            if (initNative()) {
                if (startClassName == null) {
                    // Initializations for Zygote
                    initXbridgeZygote();
                }

                loadModules(startClassName);
            } else {
                log("Errors during native Xposed initialization");
            }
        } catch (Throwable t) {
            log("Errors during Xposed initialization");
            log(t);
            disableHooks = true;
        }

        // call the original startup code
        if (startClassName == null)
            ZygoteInit.main(args);
        else
            RuntimeInit.main(args);
    }

xposedbridge 類先初始化xposed需要的環境,然后加載注冊到xposed框架里的 xposed 模塊,這一步執行完后,所以 xposed 對虛擬機的掛鈎已經完成,mian 函數最后,執行  ZygoteInit.main 或者 RuntimeInit.main ,  進入正常的流程

 

從這里可以看出,xposed 對虛擬機的注入采用的是比動態注入更優雅的方式,有幾個特點:

 

1. 由於替換了 app_process ,替換后的app_process 肯定是先啟動 xposed 然后再進入 zygote ,而其他app都是 zygote 創建的,這樣xposed 的掛鈎一定的全局性的,所有app都會被影響

2. 只需要安裝xposed時擁有root權限以替換系統的 app_process , 之后不再需要root權限,而前面采用 so動態注入的方式,每次要掛鈎都需要注入,每次注入zygote 都需要root權限

3. 不需要依賴 ptrace 等機制


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM