Android so注入-libinject2 簡介、編譯、運行html
Android so注入-libinject2 如何實現so注入java
Android so注入-Libinject 如何實現so注入linux
Android so注入掛鉤-Adbi 框架簡介、編譯、運行android
Android so注入掛鉤-Adbi 框架如何實現so注入app
Android so注入掛鉤-Adbi 框架如何實現so函數掛鉤框架
Android so注入掛鉤-Adbi 框架如何實現dalvik函數掛鉤socket
Android dalvik掛鉤-Xposed框架如何實現注入函數
Android dalvik掛鉤-Xposed框架如何實現掛鉤ui
前面分析的adbi框架和libinject都是使用so注入的方式,實現將指定代碼裝入目標進程,這種方式有幾個特色:spa
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 等機制