首页
社区
课程
招聘
[原创]dpt-shell源码详细解析(v1.11.3)
发表于: 2024-12-10 22:35 3827

[原创]dpt-shell源码详细解析(v1.11.3)

2024-12-10 22:35
3827

dpt-shell自22年以来进行了不少的调整,本文试对v1.11.3版的dpt-shell进行源码分析,补充作者在HowItWorks中未撰写出来的部分并作积累。

简而言之,dpt-shell可以分为两个模块,一个是Processeor模块,用于对原app进行指令抽空并构建新app;另一个是shell模块,用于在app运行时回填指令,顺利执行app的代码。以下是对这两个模块的详细分析

入口点在src\main\java\com\luoye\dpt\Dpt.java,解析用户的运行参数后进入apk.protect()进行抽取

extractDexCode:调用DexUtils.extractAllMethods获取List<Instruction> ret,最后将每个方法的字节码信息写入到assets/OoooooOooo文件

extractAllMethods:解析Dex文件的结构体,获取directMethods,virtualMethods,调用extractMethod来进行patch

extractMethod:一边用byteCode保存原字节码,一边用outRandomAccessFile.writeShort(0)写入nop

下面主要贴一下extractDexCode的源码好了

主要是保存了一下源ApplicationName和AppComponentFactory,后续壳加载的时候用

此外修改了程序的AMF.xml,替换为代理ApplicationName和代理AppComponentFactory

源dex压缩存放到assets/i11111i111.zip

combineDexZipWithShellDex:将壳文件添加到dex,并修复size、sha1、checksum

copyNativeLibs这里就没啥好说的,直接添加(shell-files/libs → assets/vwwwwwvwww)

encryptSoFiles这块对比于初始版本是新添加的功能:

zipalign,对zip进行对齐

对APK进行签名,assets/dpt.jks

signApkDebug

signApk,调用command来实现

完成!

这里的逻辑也会初始版本发生了一些变化,直接分析当前版本的

JniBridge.loadShellLibs(applicationInfo.dataDir,applicationInfo.sourceDir)

JniBridge.ia():init_app

JniBridge.cbde:combineDexElements,动态合并新的dex

先调用父类的onCreate,后面主要看replaceApplication,同样发生在native层

JniBridge.ra:replaceApplication,主要实例化了一个application,执行replaceApplicationOnLoadedApk和replaceApplicationOnActivityThread来替换这个实例

replaceApplicationOnLoadedApk

replaceApplicationOnActivityThread

这里做个区分:

JniBridge.craa:callRealApplicationAttach

JniBridge.craoc:callRealApplicationOnCreate

壳的so文件在init_array中优先调用

decrypt_bitcode

dpt_hook

函数路径位于src\main\cpp\dpt_hook.cpp

hook libc.so的execve

字符串子串匹配dex2oat,禁用dex2oat

hook libc.so的mmap

添加写权限,以便后续修改dex

hook DefineClass

这里通过DobbyHook库来对java层的代码进行hook

classloader在加载类的时候会调用defineClass,根据sdk版本选择合适的hook函数,如

patchClass**(指令回填的核心函数)**

patchMethod

createAntiRiskProcess

detectFrida

doPtrace

在系统需要创建组件实例时按需调用,通过代理的方式控制组件的实例化,优先使用目标 AppComponentFactory 创建组件,创建失败再用默认的

回看22年的帖子,当时作者使用LoadMethod作为hook和指令回填的目标,现在已经换成DefineClass,原因在HowItWorks.md也有解释

ClassDef这个结构还有一个特点,它是dex文件的结构,也就是说dex文件格式不变,它一般就不会变。还有,DefineClass函数的参数会改变吗?目前来看从Android M到现在没有变过。所以使用它不用太担心随着Android版本的升级而导致字段偏移的变化,也就是兼容性较强。这就是为什么用DefineClass作为Hook点。

dpt之前就是使用的LoadMethod函数作为Hook点,在LoadMethod函数里面做CodeItem填充操作。但是后来发现,LoadMethod函数参数不太固定,随着Android版本的升级可能要不断适配,而且每个函数都要填充,会影响一定的性能。

分享一个自己做的函数抽取壳 - 吾爱破解 - 52pojie.cn

https://github.com/luoyesiqiu/dpt-shell

private static void process(Apk apk){
    if(!new File("shell-files").exists()) {
        LogUtils.error("Cannot find shell files!");
        return;
    }
    File apkFile = new File(apk.getFilePath());
 
    if(!apkFile.exists()){
        LogUtils.error("Apk not exists!");
        return;
    }
 
    //apk extract path
    String apkMainProcessPath = apk.getWorkspaceDir().getAbsolutePath();
 
    LogUtils.info("Apk main process path: " + apkMainProcessPath);
 
    ZipUtils.unZip(apk.getFilePath(),apkMainProcessPath);
    String packageName = ManifestUtils.getPackageName(apkMainProcessPath + File.separator + "AndroidManifest.xml");
    apk.setPackageName(packageName);
    // 1. 指令抽空
    apk.extractDexCode(apkMainProcessPath);
    //  2. AMF的处理
    apk.saveApplicationName(apkMainProcessPath);    // 保存原始ApplicationName到assets/app_name
    apk.writeProxyAppName(apkMainProcessPath);  // 写入代理ApplicationName
    if(apk.isAppComponentFactory()){
        apk.saveAppComponentFactory(apkMainProcessPath);    // 保存原始AppComponentFactory到assets/app_acf
        apk.writeProxyComponentFactoryName(apkMainProcessPath); // 写入代理AppComponentFactory
    }
    if(apk.isDebuggable()) {
        LogUtils.info("Make apk debuggable.");
        apk.setDebuggable(apkMainProcessPath, true);
    }
 
    apk.setExtractNativeLibs(apkMainProcessPath);
    apk.addJunkCodeDex(apkMainProcessPath);
    // 3. 压缩源dex到新的路径并删除旧的路径
    apk.compressDexFiles(apkMainProcessPath);   // 源dex压缩存放到assets/i11111i111.zip
    apk.deleteAllDexFiles(apkMainProcessPath);
    // 4. 合并壳dex和原dex
    apk.combineDexZipWithShellDex(apkMainProcessPath);
    // 5. 复制壳的so文件,加密so文件
    apk.copyNativeLibs(apkMainProcessPath); // 复制壳的so文件
    apk.encryptSoFiles(apkMainProcessPath);
 
    // 6. 构建apk
    apk.buildApk(apkFile.getAbsolutePath(),apkMainProcessPath, FileUtils.getExecutablePath());
 
    File apkMainProcessFile = new File(apkMainProcessPath);
    if (apkMainProcessFile.exists()) {
        FileUtils.deleteRecurse(apkMainProcessFile);
    }
    LogUtils.info("All done.");
}
public void protect() {
    process(this);
}
private static void process(Apk apk){
    if(!new File("shell-files").exists()) {
        LogUtils.error("Cannot find shell files!");
        return;
    }
    File apkFile = new File(apk.getFilePath());
 
    if(!apkFile.exists()){
        LogUtils.error("Apk not exists!");
        return;
    }
 
    //apk extract path
    String apkMainProcessPath = apk.getWorkspaceDir().getAbsolutePath();
 
    LogUtils.info("Apk main process path: " + apkMainProcessPath);
 
    ZipUtils.unZip(apk.getFilePath(),apkMainProcessPath);
    String packageName = ManifestUtils.getPackageName(apkMainProcessPath + File.separator + "AndroidManifest.xml");
    apk.setPackageName(packageName);
    // 1. 指令抽空
    apk.extractDexCode(apkMainProcessPath);
    //  2. AMF的处理
    apk.saveApplicationName(apkMainProcessPath);    // 保存原始ApplicationName到assets/app_name
    apk.writeProxyAppName(apkMainProcessPath);  // 写入代理ApplicationName
    if(apk.isAppComponentFactory()){
        apk.saveAppComponentFactory(apkMainProcessPath);    // 保存原始AppComponentFactory到assets/app_acf
        apk.writeProxyComponentFactoryName(apkMainProcessPath); // 写入代理AppComponentFactory
    }
    if(apk.isDebuggable()) {
        LogUtils.info("Make apk debuggable.");
        apk.setDebuggable(apkMainProcessPath, true);
    }
 
    apk.setExtractNativeLibs(apkMainProcessPath);
    apk.addJunkCodeDex(apkMainProcessPath);
    // 3. 压缩源dex到新的路径并删除旧的路径
    apk.compressDexFiles(apkMainProcessPath);   // 源dex压缩存放到assets/i11111i111.zip
    apk.deleteAllDexFiles(apkMainProcessPath);
    // 4. 合并壳dex和原dex
    apk.combineDexZipWithShellDex(apkMainProcessPath);
    // 5. 复制壳的so文件,加密so文件
    apk.copyNativeLibs(apkMainProcessPath); // 复制壳的so文件
    apk.encryptSoFiles(apkMainProcessPath);
 
    // 6. 构建apk
    apk.buildApk(apkFile.getAbsolutePath(),apkMainProcessPath, FileUtils.getExecutablePath());
 
    File apkMainProcessFile = new File(apkMainProcessPath);
    if (apkMainProcessFile.exists()) {
        FileUtils.deleteRecurse(apkMainProcessFile);
    }
    LogUtils.info("All done.");
}
public void protect() {
    process(this);
}
private void  extractDexCode(String apkOutDir){
    List<File> dexFiles = getDexFiles(apkOutDir);
    Map<Integer,List<Instruction>> instructionMap = new HashMap<>();
    String appNameNew = "OoooooOooo";
    String dataOutputPath = getOutAssetsDir(apkOutDir).getAbsolutePath() + File.separator + appNameNew;
 
    CountDownLatch countDownLatch = new CountDownLatch(dexFiles.size());
    for(File dexFile : dexFiles) {
        ThreadPool.getInstance().execute(() -> {
            final int dexNo = getDexNumber(dexFile.getName());
            if(dexNo < 0){
                return;
            }
            String extractedDexName = dexFile.getName().endsWith(".dex") ? dexFile.getName().replaceAll("\\.dex$", "_extracted.dat") : "_extracted.dat";
            File extractedDexFile = new File(dexFile.getParent(), extractedDexName);
 
            List<Instruction> ret = DexUtils.extractAllMethods(dexFile, extractedDexFile, getPackageName(), isDumpCode());
            instructionMap.put(dexNo,ret);
 
            File dexFileRightHashes = new File(dexFile.getParent(),FileUtils.getNewFileSuffix(dexFile.getName(),"dat"));
            DexUtils.writeHashes(extractedDexFile,dexFileRightHashes);
            dexFile.delete();
            extractedDexFile.delete();
            dexFileRightHashes.renameTo(dexFile);
            countDownLatch.countDown();
        });
 
    }
 
    ThreadPool.getInstance().shutdown();
 
    try {
        countDownLatch.await();
    }
    catch (Exception ignored){
    }
 
    MultiDexCode multiDexCode = MultiDexCodeUtils.makeMultiDexCode(instructionMap);
 
    MultiDexCodeUtils.writeMultiDexCode(dataOutputPath,multiDexCode);
 
}
private void  extractDexCode(String apkOutDir){
    List<File> dexFiles = getDexFiles(apkOutDir);
    Map<Integer,List<Instruction>> instructionMap = new HashMap<>();
    String appNameNew = "OoooooOooo";
    String dataOutputPath = getOutAssetsDir(apkOutDir).getAbsolutePath() + File.separator + appNameNew;
 
    CountDownLatch countDownLatch = new CountDownLatch(dexFiles.size());
    for(File dexFile : dexFiles) {
        ThreadPool.getInstance().execute(() -> {
            final int dexNo = getDexNumber(dexFile.getName());
            if(dexNo < 0){
                return;
            }
            String extractedDexName = dexFile.getName().endsWith(".dex") ? dexFile.getName().replaceAll("\\.dex$", "_extracted.dat") : "_extracted.dat";
            File extractedDexFile = new File(dexFile.getParent(), extractedDexName);
 
            List<Instruction> ret = DexUtils.extractAllMethods(dexFile, extractedDexFile, getPackageName(), isDumpCode());
            instructionMap.put(dexNo,ret);
 
            File dexFileRightHashes = new File(dexFile.getParent(),FileUtils.getNewFileSuffix(dexFile.getName(),"dat"));
            DexUtils.writeHashes(extractedDexFile,dexFileRightHashes);
            dexFile.delete();
            extractedDexFile.delete();
            dexFileRightHashes.renameTo(dexFile);
            countDownLatch.countDown();
        });
 
    }
 
    ThreadPool.getInstance().shutdown();
 
    try {
        countDownLatch.await();
    }
    catch (Exception ignored){
    }
 
    MultiDexCode multiDexCode = MultiDexCodeUtils.makeMultiDexCode(instructionMap);
 
    MultiDexCodeUtils.writeMultiDexCode(dataOutputPath,multiDexCode);
 
}
private static boolean signApk(String apkPath, String keyStorePath, String signedApkPath,
                               String keyAlias,
                               String storePassword,
                               String KeyPassword) {
    ArrayList<String> commandList = new ArrayList<>();
 
    commandList.add("sign");
    commandList.add("--ks");
    commandList.add(keyStorePath);
    commandList.add("--ks-key-alias");
    commandList.add(keyAlias);
    commandList.add("--ks-pass");
    commandList.add("pass:" + storePassword);
    commandList.add("--key-pass");
    commandList.add("pass:" + KeyPassword);
    commandList.add("--out");
    commandList.add(signedApkPath);
    commandList.add("--v1-signing-enabled");
    commandList.add("true");
    commandList.add("--v2-signing-enabled");
    commandList.add("true");
    commandList.add("--v3-signing-enabled");
    commandList.add("true");
    commandList.add(apkPath);
 
    int size = commandList.size();
    String[] commandArray = new String[size];
    commandArray = commandList.toArray(commandArray);
 
    try {
        ApkSignerTool.main(commandArray);
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
    return true;
}
private static boolean signApk(String apkPath, String keyStorePath, String signedApkPath,
                               String keyAlias,
                               String storePassword,
                               String KeyPassword) {
    ArrayList<String> commandList = new ArrayList<>();
 
    commandList.add("sign");
    commandList.add("--ks");
    commandList.add(keyStorePath);
    commandList.add("--ks-key-alias");
    commandList.add(keyAlias);
    commandList.add("--ks-pass");
    commandList.add("pass:" + storePassword);
    commandList.add("--key-pass");
    commandList.add("pass:" + KeyPassword);
    commandList.add("--out");
    commandList.add(signedApkPath);
    commandList.add("--v1-signing-enabled");
    commandList.add("true");
    commandList.add("--v2-signing-enabled");
    commandList.add("true");
    commandList.add("--v3-signing-enabled");
    commandList.add("true");
    commandList.add(apkPath);
 
    int size = commandList.size();
    String[] commandArray = new String[size];
    commandArray = commandList.toArray(commandArray);
 
    try {
        ApkSignerTool.main(commandArray);
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
    return true;
}
@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);  // 先调用父类的attachBaseContext
    Log.d(TAG,"dpt attachBaseContext classloader = " + base.getClassLoader());
    realApplicationName = FileUtils.readAppName(this);
    if(!Global.sIsReplacedClassLoader) {
        ApplicationInfo applicationInfo = base.getApplicationInfo();
        if(applicationInfo == null) {
            throw new NullPointerException("application info is null");
        }
        FileUtils.unzipLibs(applicationInfo.sourceDir,applicationInfo.dataDir);
        JniBridge.loadShellLibs(applicationInfo.dataDir,applicationInfo.sourceDir);
        Log.d(TAG,"ProxyApplication init");
        JniBridge.ia();
        ClassLoader targetClassLoader = base.getClassLoader();
        JniBridge.cbde(targetClassLoader);
        Global.sIsReplacedClassLoader = true;
    }
}
@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);  // 先调用父类的attachBaseContext
    Log.d(TAG,"dpt attachBaseContext classloader = " + base.getClassLoader());
    realApplicationName = FileUtils.readAppName(this);
    if(!Global.sIsReplacedClassLoader) {
        ApplicationInfo applicationInfo = base.getApplicationInfo();
        if(applicationInfo == null) {
            throw new NullPointerException("application info is null");
        }
        FileUtils.unzipLibs(applicationInfo.sourceDir,applicationInfo.dataDir);
        JniBridge.loadShellLibs(applicationInfo.dataDir,applicationInfo.sourceDir);
        Log.d(TAG,"ProxyApplication init");
        JniBridge.ia();
        ClassLoader targetClassLoader = base.getClassLoader();
        JniBridge.cbde(targetClassLoader);
        Global.sIsReplacedClassLoader = true;
    }
}
DPT_ENCRYPT void init_app(JNIEnv *env, jclass __unused) {
    DLOGD("init_app!");
    clock_t start = clock();
 
    void *apk_addr = nullptr;
    size_t apk_size = 0;
    load_apk(env,&apk_addr,&apk_size);
 
    uint64_t entry_size = 0;
    if(codeItemFilePtr == nullptr) {
        read_zip_file_entry(apk_addr,apk_size,CODE_ITEM_NAME_IN_ZIP,&codeItemFilePtr,&entry_size);
    }
    else {
        DLOGD("no need read codeitem from zip");
    }
    readCodeItem((uint8_t *)codeItemFilePtr,entry_size);
 
    pthread_mutex_lock(&g_write_dexes_mutex);
    extractDexesInNeeded(env,apk_addr,apk_size);
    pthread_mutex_unlock(&g_write_dexes_mutex);
 
    unload_apk(apk_addr,apk_size);
    printTime("read apk data took =" , start);
}
DPT_ENCRYPT void init_app(JNIEnv *env, jclass __unused) {
    DLOGD("init_app!");
    clock_t start = clock();
 
    void *apk_addr = nullptr;
    size_t apk_size = 0;
    load_apk(env,&apk_addr,&apk_size);
 
    uint64_t entry_size = 0;
    if(codeItemFilePtr == nullptr) {
        read_zip_file_entry(apk_addr,apk_size,CODE_ITEM_NAME_IN_ZIP,&codeItemFilePtr,&entry_size);
    }
    else {
        DLOGD("no need read codeitem from zip");
    }
    readCodeItem((uint8_t *)codeItemFilePtr,entry_size);
 
    pthread_mutex_lock(&g_write_dexes_mutex);
    extractDexesInNeeded(env,apk_addr,apk_size);
    pthread_mutex_unlock(&g_write_dexes_mutex);
 
    unload_apk(apk_addr,apk_size);
    printTime("read apk data took =" , start);
}
DPT_ENCRYPT void combineDexElement(JNIEnv* env, jclass __unused, jobject targetClassLoader, const char* pathChs) {
    jobjectArray extraDexElements = makePathElements(env,pathChs);
 
    dalvik_system_BaseDexClassLoader targetBaseDexClassLoader(env,targetClassLoader);
 
    jobject originDexPathListObj = targetBaseDexClassLoader.getPathList();
 
    dalvik_system_DexPathList targetDexPathList(env,originDexPathListObj);
 
    jobjectArray originDexElements = targetDexPathList.getDexElements();
 
    jsize extraSize = env->GetArrayLength(extraDexElements);
    jsize originSize = env->GetArrayLength(originDexElements);
 
    dalvik_system_DexPathList::Element element(env, nullptr);
    jclass ElementClass = element.getClass();
    jobjectArray  newDexElements = env->NewObjectArray(originSize + extraSize,ElementClass, nullptr);
 
    for(int i = 0;i < originSize;i++) {
        jobject elementObj = env->GetObjectArrayElement(originDexElements, i);
        env->SetObjectArrayElement(newDexElements,i,elementObj);
    }
 
    for(int i = originSize;i < originSize + extraSize;i++) {
        jobject elementObj = env->GetObjectArrayElement(extraDexElements, i - originSize);
        env->SetObjectArrayElement(newDexElements,i,elementObj);
    }
 
    targetDexPathList.setDexElements(newDexElements);
 
    DLOGD("combineDexElement success");
}
DPT_ENCRYPT void combineDexElement(JNIEnv* env, jclass __unused, jobject targetClassLoader, const char* pathChs) {
    jobjectArray extraDexElements = makePathElements(env,pathChs);
 
    dalvik_system_BaseDexClassLoader targetBaseDexClassLoader(env,targetClassLoader);
 
    jobject originDexPathListObj = targetBaseDexClassLoader.getPathList();
 
    dalvik_system_DexPathList targetDexPathList(env,originDexPathListObj);
 
    jobjectArray originDexElements = targetDexPathList.getDexElements();
 
    jsize extraSize = env->GetArrayLength(extraDexElements);
    jsize originSize = env->GetArrayLength(originDexElements);
 
    dalvik_system_DexPathList::Element element(env, nullptr);
    jclass ElementClass = element.getClass();
    jobjectArray  newDexElements = env->NewObjectArray(originSize + extraSize,ElementClass, nullptr);
 
    for(int i = 0;i < originSize;i++) {
        jobject elementObj = env->GetObjectArrayElement(originDexElements, i);
        env->SetObjectArrayElement(newDexElements,i,elementObj);
    }
 
    for(int i = originSize;i < originSize + extraSize;i++) {
        jobject elementObj = env->GetObjectArrayElement(extraDexElements, i - originSize);
        env->SetObjectArrayElement(newDexElements,i,elementObj);
    }
 
    targetDexPathList.setDexElements(newDexElements);
 
    DLOGD("combineDexElement success");
}
private void replaceApplication() {
    if (Global.sNeedCalledApplication && !TextUtils.isEmpty(realApplicationName)) {
        realApplication = (Application) JniBridge.ra(realApplicationName);
        Log.d(TAG, "applicationExchange: " + realApplicationName+"  realApplication="+realApplication.getClass().getName());
 
        JniBridge.craa(getApplicationContext(), realApplicationName);
        JniBridge.craoc(realApplicationName);
        Global.sNeedCalledApplication = false;
    }
}
private void replaceApplication() {
    if (Global.sNeedCalledApplication && !TextUtils.isEmpty(realApplicationName)) {
        realApplication = (Application) JniBridge.ra(realApplicationName);
        Log.d(TAG, "applicationExchange: " + realApplicationName+"  realApplication="+realApplication.getClass().getName());
 
        JniBridge.craa(getApplicationContext(), realApplicationName);
        JniBridge.craoc(realApplicationName);
        Global.sNeedCalledApplication = false;
    }
}
DPT_ENCRYPT void replaceApplicationOnLoadedApk(JNIEnv *env, jclass __unused,jobject realApplication) {
    android_app_ActivityThread activityThread(env);
 
    jobject mBoundApplicationObj = activityThread.getBoundApplication();    // 获取 BoundApplication 对象
 
    android_app_ActivityThread::AppBindData appBindData(env,mBoundApplicationObj);
    jobject loadedApkObj = appBindData.getInfo();
 
    android_app_LoadedApk loadedApk(env,loadedApkObj);  // LoadedApk对象是APK文件在内存中的表示
 
    //make it null
    loadedApk.setApplication(nullptr);  // 以便可以替换为新的 Application 对象。
 
    jobject mAllApplicationsObj = activityThread.getAllApplication();
 
    java_util_ArrayList arrayList(env,mAllApplicationsObj);
 
    jobject removed = (jobject)arrayList.remove(0); // 移除原来的 Application 对象。
    if(removed != nullptr){
        DLOGD("replaceApplicationOnLoadedApk proxy application removed");
    }
 
    jobject ApplicationInfoObj = loadedApk.getApplicationInfo();    // 获取 ApplicationInfo 对象。
 
    android_content_pm_ApplicationInfo applicationInfo(env,ApplicationInfoObj);
 
    char applicationName[128] = {0};
    getClassName(env,realApplication,applicationName, ARRAY_LENGTH(applicationName));   // 获取真实 Application 对象的类名
 
    DLOGD("applicationName = %s",applicationName);
    char realApplicationNameChs[128] = {0};
    parseClassName(applicationName,realApplicationNameChs);     // 前面获取了类名了,现在解析类
    jstring realApplicationName = env->NewStringUTF(realApplicationNameChs);
    auto realApplicationNameGlobal = (jstring)env->NewGlobalRef(realApplicationName);
 
    android_content_pm_ApplicationInfo appInfo(env,appBindData.getAppInfo());
 
    //replace class name    替换类名
    applicationInfo.setClassName(realApplicationNameGlobal);
    appInfo.setClassName(realApplicationNameGlobal);
 
    DLOGD("replaceApplicationOnLoadedApk begin makeApplication!");
 
    // call make application
    loadedApk.makeApplication(JNI_FALSE,nullptr);
 
    DLOGD("replaceApplicationOnLoadedApk success!");
}
DPT_ENCRYPT void replaceApplicationOnLoadedApk(JNIEnv *env, jclass __unused,jobject realApplication) {
    android_app_ActivityThread activityThread(env);
 
    jobject mBoundApplicationObj = activityThread.getBoundApplication();    // 获取 BoundApplication 对象
 
    android_app_ActivityThread::AppBindData appBindData(env,mBoundApplicationObj);
    jobject loadedApkObj = appBindData.getInfo();
 
    android_app_LoadedApk loadedApk(env,loadedApkObj);  // LoadedApk对象是APK文件在内存中的表示
 
    //make it null
    loadedApk.setApplication(nullptr);  // 以便可以替换为新的 Application 对象。
 
    jobject mAllApplicationsObj = activityThread.getAllApplication();
 
    java_util_ArrayList arrayList(env,mAllApplicationsObj);
 
    jobject removed = (jobject)arrayList.remove(0); // 移除原来的 Application 对象。
    if(removed != nullptr){
        DLOGD("replaceApplicationOnLoadedApk proxy application removed");
    }
 
    jobject ApplicationInfoObj = loadedApk.getApplicationInfo();    // 获取 ApplicationInfo 对象。
 
    android_content_pm_ApplicationInfo applicationInfo(env,ApplicationInfoObj);
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 4
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//