主頁 >  其他 > 只因拎不清 插件化 和 熱修復 的實作原理,我痛失了阿里的offer

只因拎不清 插件化 和 熱修復 的實作原理,我痛失了阿里的offer

2020-11-07 03:08:54 其他

前言

聽堅守在本部的同事說,杭州阿里這邊,這段時間面了不下 30 個 因公司優化、而被迫離職的 Android 中高級開發,

但是由于Android崗位僧多肉少的緣故,同事便從進階基礎開始問,就比如插件化、組件化、熱修復的實作原理,沒想到這 30 個人里面,僅有 1 個勉強過關 🤔

考慮到接下來,還會有不少人會因為 掌握不透 插件化、組件化、熱修復的實作原理而面試遭拒,我便把插件化、組件化、熱修復寫一個專題,所以 掌握不透的朋友,請 務必 務必 務必 收藏好這一篇進階筆記!

看完本文可以達到什么程度

  1. 了解吃透插件化/熱修復常見的實作原理

閱讀前準備作業

  1. clone CommonTec 專案,其中 hotfix 和 patch 是熱修復代碼
    示例代碼基于 AndFix,NuWa,Robust 進行了調整,抽取主要部分用來講解原理,

文章概覽

PS:關于我


本人是一個擁有6年開發經驗的帥氣Android攻城獅,記得看完點贊,養成習慣,微信搜一搜「 程式猿養成中心 」關注這個喜歡寫干貨的程式員,

另外耗時兩年整理收集的Android一線大廠面試完整考點PDF出爐,資料【完整版】已更新在我的【Github】,有面試需要的朋友們可以去參考參考,如果對你有幫助,可以點個Star哦!

地址:【https://github.com/733gh/xiongfan】

干貨要來了!你準備好了嗎?

一、熱修復和插件化

插件化和熱修復的原理,都是動態加載 dex/apk 中的類/資源,兩者的目的不同,插件化目標在于加載 activity 等組件,達到動態下發組件的功能,熱修復目標在修復已有的問題,目標不同,也就導致其實作方式上的差別,由于目標是動態加載組件,所以插件化重在解決組件的生命周期,以及資源的問題,而熱修復重在解決替換已有的有問題的類/方法/資源等,
關于插件化,可以看前面分享的文章Android 插件化分析

二、使用 gradle 簡化插件開發流程

如果看過Android 插件化分析里的 gradle 簡化插件開發流程,這里可以略過~

在學習和開發熱修復的時候,我們需要動態去加載補丁 apk,所以開發程序中一般需要有兩個 apk,一個是宿主 apk,一個是補丁 apk,對應的就需要有宿主專案和補丁專案,

在 CommonTec 這里創建了 app 作為宿主專案,plugin 為插件專案,為了方便,我們直接把生成的插件 apk 放到宿主 apk 中的 assets 中,apk 啟動時直接放到內部存盤空間中方便加載,

這樣的專案結構,我們除錯問題時的流程就是下面這樣:

修改插件專案 -> 編譯生成插件 apk -> 拷貝插件 apk 到宿主 assets -> 修改宿主專案 -> 編譯生成宿主 apk -> 安裝宿主 apk -> 驗證問題

如果每次我們修改一個很小的問題,都經歷這么長的流程,那么耐心很快就耗盡了,最好是可以直接編譯宿主 apk 的時候自動打包插件 apk 并拷貝到宿主 assets 目錄下,這樣我們不管修改什么,都直接編譯宿主專案就好了,

如何實作呢?還記得我們之前講解過的 gradle 系列么?現在就是學以致用的時候了,

首先在 plugin 專案的 build.gradle 添加下面的代碼:

project.afterEvaluate {
    project.tasks.each {
        if (it.name == "assembleDebug") {
            it.doLast {
                copy {
                    from new File(project.getBuildDir(), 'outputs/patch/debug/patch-debug.apk').absolutePath
                    into new File(project.getRootProject().getProjectDir(), 'hotfix/src/main/assets')
                    rename 'patch-debug.apk', 'patch.apk'
                }
            }
        }
    }
}

這段代碼是在 afterEvaluate 的時候,遍歷專案的 task,找到打包 task 也就是 assembleDebug,然后在打包之后,把生成的 apk 拷貝到宿主專案的 assets 目錄下,并且重命名為 plugin.apk,

然后在 app 專案的 build.gradle 添加下面的代碼:

project.afterEvaluate {
    project.tasks.each {
        if (it.name == 'mergeDebugAssets') {
            it.dependsOn ':patch:assembleDebug'
        }
    }
}

找到宿主打包的 mergeDebugAssets 任務,依賴插件專案的打包,這樣每次編譯宿主專案的時候,會先編譯插件專案,然后拷貝插件 apk 到宿主 apk 的 assets 目錄下,以后每次修改,只要編譯宿主專案就可以了,

三、ClassLoader

如果看過Android 插件化分析里的 ClassLoader 分析,這里可以略過~

ClassLoader 是熱修復和插件化中必須要掌握的,因為插件是未安裝的 apk,系統不會處理其中的類,所以需要我們自己來處理,

3.1 java 中的 ClassLoader

BootstrapClassLoader
負責加載 JVM 運行時的核心類,比如 JAVA_HOME/lib/rt.jar 等等

ExtensionClassLoader
負責加載 JVM 的擴展類,比如 JAVA_HOME/lib/ext 下面的 jar 包

AppClassLoader
負責加載 classpath 里的 jar 包和目錄

3.2 android 中的 ClassLoader

在這里,我們統稱 dex 檔案,包含 dex 的 apk 檔案以及 jar 檔案為 dex 檔案

PathClassLoader

用來加載系統類和應用程式類,用來加載 dex 檔案,但是 dex2oat 生成的 odex 檔案只能放在系統的默認目錄,

DexClassLoader

用來加載 dex 檔案,可以從存盤空間加載 dex 檔案,可以指定 odex 檔案的存放目錄,

我們在插件化中一般使用的是 DexClassLoader,

3.3 雙親委派機制

每一個 ClassLoader 中都有一個 parent 物件,代表的是父類加載器,在加載一個類的時候,會先使用父類加載器去加載,如果在父類加載器中沒有找到,自己再進行加載,如果 parent 為空,那么就用系統類加載器來加載,通過這樣的機制可以保證系統類都是由系統類加載器加載的,

下面是 ClassLoader 的 loadClass 方法的具體實作,

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        // 先從父類加載器中進行加載
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // 沒有找到,再自己加載
                    c = findClass(name);
                }
            }
            return c;
    }

3.4 如何加載插件中的類

要加載插件中的類,我們首先要創建一個 DexClassLoader,先看下 DexClassLoader 的建構式需要那些引數,

public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        // ...
    }
}

建構式需要四個引數:

dexPath 是需要加載的 dex / apk / jar 檔案路徑
optimizedDirectory 是 dex 優化后存放的位置,在 ART 上,會執行 oat 對 dex 進行優化,生成機器碼,這里就是存放優化后的 odex 檔案的位置
librarySearchPath 是 native 依賴的位置
parent 就是父類加載器,默認會先從 parent 加載對應的類

創建出 DexClassLaoder 實體以后,只要呼叫其 loadClass(className) 方法就可以加載插件中的類了,具體的實作在下面:

    // 從 assets 中拿出插件 apk 放到內部存盤空間
    private fun extractPlugin() {
        var inputStream = assets.open("plugin.apk")
        File(filesDir.absolutePath, "plugin.apk").writeBytes(inputStream.readBytes())
    }

    private fun init() {
        extractPlugin()
        pluginPath = File(filesDir.absolutePath, "plugin.apk").absolutePath
        nativeLibDir = File(filesDir, "pluginlib").absolutePath
        dexOutPath = File(filesDir, "dexout").absolutePath
        // 生成 DexClassLoader 用來加載插件類
        pluginClassLoader = DexClassLoader(pluginPath, dexOutPath, nativeLibDir, this::class.java.classLoader)
    }

四、熱修復需要解決的難點

熱修復不同于插件化,不需要考慮各種組件的生命周期,唯一需要考慮的就是如何能將問題的方法/類/資源/so 替換為補丁中的新方法/類/資源/so,
其中最重要的是方法和類的替換,所以有不少熱修復框架只做了方法和類的替換,而沒有對資源和 so 進行處理,

五、主流的熱修復框架對比

這里選取幾個比較主流的熱修復框架進行對比

Qzone/NuwaAndFixRobustTinkerSophix
dex 修復yyyyy
so 修復nnnyy
資源修復nnnyy
全平臺支持ynyyy
即時生效nyyn同時支持
補丁包大小

上面是熱修復框架的一些對比,如果按照實作 dex 修復的原理來劃分的話,大概能分成下面幾種:

native hook
Andfix
dex 插樁
Qzone
Nuwa
InstantRun
Robust
Aceso
全量替換 dex
Tinker
混合方案
Sophix

下面對這幾種熱修復的方案進行詳細分析,

六、dex 熱修復方案

6.1 native hook 替換 ArtMethod 內容

6.1.1 原理

在解釋 native hook 原理之前,先介紹一下虛擬機的一些簡單實作,java 中的類,方法,變數,對應到虛擬機里的實作是 Class,ArtMethod,ArtField,以 Android N 為例,簡單看一下這幾個類的一些結構,

class Class: public Object {
public:
    // ...
    // classloader 指標
    uint32_t class_loader_;
    // 陣列的型別表示
    uint32_t component_type_;
    // 決議 dex 生成的快取
    uint32_t dex_cache_;
    // interface table,保存了實作的介面方法
    uint32_t iftable_;
    // 類描述符,例如:java.lang.Class
    uint32_t name_;
    // 父類
    uint32_t super_class_;
    // virtual method table,虛方法表,指令 invoke-virtual 會用到,保存著父類方法以及子類復寫或者覆寫的方法,是 java 多型的基礎
    uint32_t vtable_;
    // public private 
    uint32_t access_flags_;
    // 成員變數
    uint64_t ifields_;
    // 保存了所有方法,包括 static,final,virtual 方法
    uint64_t methods_;
    // 靜態變數
    uint64_t sfields_;
    // class 當前的狀態,加載,決議,初始化等等
    Status status_;
    static uint32_t java_lang_Class_;
};

class ArtField {
public:
    uint32_t declaring_class_;
    uint32_t access_flags_;
    uint32_t field_dex_idx_;
    uint32_t offset_;
};

class ArtMethod {
public:
    uint32_t declaring_class_;
    uint32_t access_flags_;
    // 方法位元組碼的偏移
    uint32_t dex_code_item_offset_;
    // 方法在 dex 中的 index
    uint32_t dex_method_index_;
    // 在 vtable 或者 iftable 中的 index
    uint16_t method_index_;
    // 方法的呼叫入口
    struct PACKED(4) PtrSizedFields {
        ArtMethod** dex_cache_resolved_methods_;
        GcRoot<mirror::Class>* dex_cache_resolved_types_;
        void* entry_point_from_jni_;
        void* entry_point_from_quick_compiled_code_;
    } ptr_sized_fields_;
};

上面列出了三個結構的一部分變數,其實從這些變數可以比較清楚的看到,Class 中的 iftable_,vtable_,methods_ 里面保存了所有的類方法,sfields_,ifields_ 保存了所有的成員變數,

而在 ArtMethod 中,ptr_sized_fields_ 變數指向了方法的呼叫入口,也就是執行位元組碼的地方,在虛擬機內部,呼叫一個方法的時候,可以簡單的理解為會找到 ptr_sized_fields_ 指向的位置,跳轉過去執行對應的方法位元組碼或者機器碼,

簡圖如下:

這里也順便說一下上面三個結構的內容是什么時候填充的,就是在 ClassLoader 加載類的時候,

簡圖如下:

其實到這里,我們就簡單理解了虛擬機的內部實作,也就很容易想到 native hook 的原理了,既然每次呼叫方法的時候,都是通過 ArtMethod 找到方法,然后跳轉到其對應的位元組碼/機器碼位置去執行,那么我們只要更改了跳轉的目標位置,那么自然方法的實作也就被改變了,

簡圖如下:

所以 native hook 的本質就是把舊方法的 ArtMethod 內容替換成新方法的 ArtMethod 內容,
具體的實作代碼在這里(只實作了 Android N 上的修復),下面看一些重點代碼,

6.1.2 實作代碼

  1. 首先要找到替換的舊方法和新方法,這一步在 java 中進行,直接通過反射獲取即可
// 創建補丁的 ClassLoader
pluginClassLoader = DexClassLoader(pluginPath, dexOutPath.absolutePath, nativeLibDir.absolutePath, this::class.java.classLoader)
// 通過補丁 ClassLoader 加載新方法
val toMethod = pluginClassLoader.loadClass("com.zy.hotfix.native_hook.PatchNativeHookUtils").getMethod("getMsg")
// 反射獲取到需要修改的舊方法
val fromMethod = nativeHookUtils.javaClass.getMethod("getMsg")

  1. 之后呼叫 native 方法替換 ArtMethod 內容
nativeHookUtils.patch(fromMethod, toMethod)

Java_com_zy_hotfix_native_1hook_NativeHookUtils_patch(JNIEnv* env, jobject clazz, jobject src, jobject dest) {
    // 獲取到 java 方法對應的 ArtMethod
    art::mirror::ArtMethod* smeth =
            (art::mirror::ArtMethod*) env->FromReflectedMethod(src);
    art::mirror::ArtMethod* dmeth =
            (art::mirror::ArtMethod*) env->FromReflectedMethod(dest);

    reinterpret_cast<art::mirror::Class*>(dmeth->declaring_class_)->clinit_thread_id_ =
            reinterpret_cast<art::mirror::Class*>(smeth->declaring_class_)->clinit_thread_id_;
    reinterpret_cast<art::mirror::Class*>(dmeth->declaring_class_)->status_ =
            static_cast<art::mirror::Class::Status>(reinterpret_cast<art::mirror::Class*>(smeth->declaring_class_)->status_ -1);
    //for reflection invoke
    reinterpret_cast<art::mirror::Class*>(dmeth->declaring_class_)->super_class_ = 0;

    // 替換方法中的內容
    smeth->declaring_class_ = dmeth->declaring_class_;
    smeth->access_flags_ = dmeth->access_flags_  | 0x0001;
    smeth->dex_code_item_offset_ = dmeth->dex_code_item_offset_;
    smeth->dex_method_index_ = dmeth->dex_method_index_;
    smeth->method_index_ = dmeth->method_index_;
    smeth->hotness_count_ = dmeth->hotness_count_;
    // 替換方法的入口
    smeth->ptr_sized_fields_.dex_cache_resolved_methods_ =
            dmeth->ptr_sized_fields_.dex_cache_resolved_methods_;
    smeth->ptr_sized_fields_.dex_cache_resolved_types_ =
            dmeth->ptr_sized_fields_.dex_cache_resolved_types_;
    smeth->ptr_sized_fields_.entry_point_from_jni_ =
            dmeth->ptr_sized_fields_.entry_point_from_jni_;
    smeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_ =
            dmeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_;
}

通過上述方法的替換,再次呼叫舊方法,就會跳轉到新方法的入口,自然也就執行新方法的邏輯了,

6.1.3 優缺點

優點
補丁可以實時生效
缺點

  1. 兼容性差,由于 Android 系統每個版本的實作都有差別,所以需要做很多的兼容,(這也就是為什么上面提供的 demo 代碼只能運行在 Android N 上,因為沒有對其他版本做兼容)
  2. 開發需要掌握 jni 相關知識

6.2 dex 插樁

6.2.1 原理

dex 插樁的實作,是 Qzone 團隊提出來的,Nuwa 框架采用這種實作并且開源,
系統默認使用的是 PathClassLoader,繼承自 BaseDexClassLoader,在 BaseDexClassLoader 里,有一個 DexPathList 變數,在 DexPathList 的實作里,有一個 Element[] dexElements 變數,這里面保存了所有的 dex,在加載 Class 的時候,就遍歷 dexElements 成員,依次查找 Class,找到以后就回傳,

下面是重點代碼:

public class PathClassLoader extends BaseDexClassLoader {
}

public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;
}

final class DexPathList {
    // 保存了 dex 的串列
    private Element[] dexElements;

    public Class findClass(String name, List<Throwable> suppressed) {
        // 遍歷 dexElements
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;

            if (dex != null) {
                // 從 DexFile 中查找 Class
                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        // ...
        return null;
    }
}

從上面 ClassLoader 的實作我們可以知道,查找 Class 的關鍵就是遍歷 dexElements,那么自然就想到了把補丁 dex 插入到 dexElements 最前面,這樣遍歷 dexElements 就會優先從補丁 dex 中查找 Class 了,

具體的實作在這里,下面放一些重點代碼,

6.2.2 實作代碼

    public static void injectDexAtFirst(String dexPath, String defaultDexOptPath) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        // 創建補丁 dex 的 classloader,目的是使用其中的補丁 dexElements
        DexClassLoader dexClassLoader = new DexClassLoader(dexPath, defaultDexOptPath, dexPath, getPathClassLoader());
        // 獲取到舊的 classloader 的 pathlist.dexElements 變數
        Object baseDexElements = getDexElements(getPathList(getPathClassLoader()));
        // 獲取到補丁 classloader 的 pathlist.dexElements 變數
        Object newDexElements = getDexElements(getPathList(dexClassLoader));
        // 將補丁 的 dexElements 插入到舊的 classloader.pathlist.dexElements 前面
        Object allDexElements = combineArray(newDexElements, baseDexElements);
    }

    private static PathClassLoader getPathClassLoader() {
        PathClassLoader pathClassLoader = (PathClassLoader) InsertDexUtils.class.getClassLoader();
        return pathClassLoader;
    }

    private static Object getDexElements(Object paramObject)
            throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
        return Reflect.on(paramObject).get("dexElements");
    }

    private static Object getPathList(Object baseDexClassLoader)
            throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        return Reflect.on(baseDexClassLoader).get("pathList");
    }

    private static Object combineArray(Object firstArray, Object secondArray) {
        Class<?> localClass = firstArray.getClass().getComponentType();
        int firstArrayLength = Array.getLength(firstArray);
        int allLength = firstArrayLength + Array.getLength(secondArray);
        Object result = Array.newInstance(localClass, allLength);
        for (int k = 0; k < allLength; ++k) {
            if (k < firstArrayLength) {
                Array.set(result, k, Array.get(firstArray, k));
            } else {
                Array.set(result, k, Array.get(secondArray, k - firstArrayLength));
            }
        }
        return result;
    }

6.2.3 優缺點

優點

  1. 實作簡單
  2. 不需要太多的適配

缺點

  1. 需要重新啟動補丁才能生效,因為在插樁之前加載的類是不會再重新加載的,所以需要重新啟動,讓已經加載過的 Class 重新加載才能應用到補丁
  2. class verify 問題,關于這個問題可以看Qzone 的解釋,這里就不詳細展開了
  3. Art 虛擬機上由于 oat 導致的地址偏移問題,可能會需要在補丁包中打入補丁無關的類,導致補丁包體積增大

6.3 dex 替換

dex 替換的方案,主要是 tinker 在使用,這里生成的補丁包不只是需要修改的類,而是包含了整個 app 所有的類,在替換時原理和 dex 插樁類似,也是替換掉 dexElements 中的內容即可,這里就不詳細說了,

6.4 InstantRun

6.4.1 原理

InstantRun 是 AndroidStudio 2.0 新增的功能,方便快速的增量編譯應用并部署,美團參照其原理實作了 Robust 熱修復框架,

其中的原理是,給每個 Class 中新增一個 changeQuickRedirect 的靜態變數,并在每個方法執行之前,對這個變數進行了判斷,如果這個變數被賦值了,就呼叫補丁類中的方法,如果沒有被賦值,還是呼叫舊方法,
原理比較簡單,下面看看實作,具體實作在這里,

6.4.2 實作代碼

public class InstantRunUtils {
    // 上文中說的 changeQuickRedirect 變數,改了一下名字
    public static PatchRedirect patchRedirect;

    // 需要補丁的方法
    public int getValue() {
        // 判斷 patchRedirect 是否為空
        if (patchRedirect != null) {
            // 不為空,說明方法需要打補丁,由于一個類中有很多方法,所以這里需要判斷此方法是否需要補丁
            if (patchRedirect.needPatch("getValue")) {
                // 需要補丁,就呼叫補丁中的方法
                return (String) patchRedirect.invokePatchMethod("getValue");
            }
        }
        return 100;
    }

    // 注入補丁
    public static void inject(ClassLoader classLoader) {
        try {
            // 獲取到補丁中的補丁資訊
            Class patchInfoClass = classLoader.loadClass("com.zy.hotfix.instant_run.PatchInfo");
            patchInfoClass.getMethod("init").invoke(null);
            // patchMap 中存著 className -> PatchRedirect,即需要補丁的類描述符和對應的 PatchRedirect
            Map<String, Object> patchMap = (Map<String, Object>) patchInfoClass.getField("patchMap").get(null);
            for (String key: patchMap.keySet()) {
                PatchRedirect redirect = (PatchRedirect) patchMap.get(key);
                Class clazz = Class.forName(key);
                // 替換 class 中的 PatchRedirect
                clazz.getField("patchRedirect").set(null, redirect);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然后我們看看補丁中的 PatchRefirect 是怎么實作的

public class InstantRunUtilsRedirect extends PatchRedirect {
    @Override
    public Object invokePatchMethod(String methodName, Object... params) {
        // 根據方法描述符呼叫對應的方法
        if (methodName.equals("getValue")) {
            return getValue();
        }
        return null;
    }

    @Override
    public boolean needPatch(String methodName) {
        // 判斷方法是否需要補丁
        if ("getValue".equals(methodName)) {
            return true;
        }
        return false;
    }

    // 補丁方法,回傳正確的值
    public int getValue() {
        return 200;
    }
}

6.4.3 優缺點

優點

  1. 使用 java 實作,開發方便
  2. 兼容性好
  3. 補丁實時生效

缺點

  1. 代碼是侵入比較高,需要在原有代碼中新增邏輯,而且需要對方法進行插樁,將這里邏輯自動化處理
  2. 增大包體積

七、資源熱修復方案

關于資源的修復方案,沒有像代碼修復一樣方法繁多,基本上集中在對 AssetManager 的修改上,

7.1 替換 AssetManager

這個是 InstantRun 采用的方案,就是構造一個新的 AssetManager,反射呼叫其 addAssetPath 函式,把新的補丁資源包添加到 AssetManager 中,從而得到含有完整補丁資源的 AssetManager,然后找到所有參考 AssetManager 的地方,通過反射將其替換為新的 AssetManager,

7.2 添加修改的資源到 AssetManager 中,并重新初始化

這個是 Sophix 采用的方案,原理是構造一個 package id 為 0x66 的資源包,只含有改變的資源,將其直接添加到原有的 AssetManager 中,這樣不會與原來的 package id 0x7f 沖突,然后將原來的 AssetManager 重新進行初始化即可,就不需要進行繁瑣的反射替換操作了,

八、so 熱修復方案

8.1 對加載程序進行封裝,替換 System.loadLibrary

在加載 so 庫的時候,系統提供了兩個介面

System.loadLibrary(String libName):用來加載已經安裝的 apk 中的 so
System.load(String pathName):可以加載自定義路徑下的 so

通過上面兩個方法,我們可以想到,如果有補丁 so 下發,我們就呼叫 System.load 去加載,如果沒有補丁 so 沒有下發,那么還是呼叫 System.loadLibrary 去加載系統目錄下的 so,原理比較簡單,但是我們需要再上面進行一層封裝,并對呼叫 System.loadLibrary 的地方都進行替換,

8.2 反射注入補丁 so 路徑

還記得上面 dex 插樁的原理么?在 DexPathList 中有 dexElements 變數,代表著所有 dex 檔案,其實 DexPathList 中還有另一個變數就是 Element[] nativeLibraryPathElements,代表的是 so 的路徑,在加載 so 的時候也會遍歷 nativeLibraryPathElements 進行加載,代碼如下:

    public String findLibrary(String libraryName) {
        String fileName = System.mapLibraryName(libraryName);
        // 遍歷 nativeLibraryPathElements 
        for (Element element : nativeLibraryPathElements) {
            String path = element.findNativeLibrary(fileName);

            if (path != null) {
                return path;
            }
        }
        return null;
    }

看到這里我們就知道如何去做了吧,就像 dex 插樁一樣的方法,將 so 的路徑插入到 nativeLibraryPathElements 之前即可,

九、總結

地址:【https://github.com/733gh/xiongfan】

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/205015.html

標籤:其他

上一篇:vscode上安裝 matplotlib失敗

下一篇:Android Jetpack- Paging的使用

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more