首页
社区
课程
招聘
一招一式讲Android安全--【加固】运行时加载隐藏dex
发表于: 2021-4-14 21:54 14103

一招一式讲Android安全--【加固】运行时加载隐藏dex

2021-4-14 21:54
14103

一招一式讲Android安全--【加固】运行时加载隐藏dex

本系列会把Android加固一系列的保护原理和思路讲解一遍,仅作为归档整理。
包括dex、so、反调试等。

本文主要以Android 9.0代码为蓝本进行研究。
希望把加载隐藏dex的思路和原理讲明白,详细的细节,各个步奏,等等。
如有错误的地方,请联系我改正,谢谢。

本文所有权益归chinamima@163.com所有,如需转载请发邮件。
昵称:chinamima
邮箱:chinamima@163.com
经验:从事Android行业10年,Android安全7年,网络安全3年

ClassLoader类关系
ClassLoader类关系

optimizedDirectory为空,则会在默认位置生成.oat.odex文件,因此源码里用PathClassLoader作为ClassLoader的实例化类。

BaseDexClassLoader的构造函数并没有把optimizedDirectory传递给DexPathList

调用createOrUpdateClassLoaderLocked

调用ApplicationLoaders.getClassLoader获取ClassLoader

mLoader已存在,则直接返回。
mLoader不存在,则调用ClassLoaderFactory.createClassLoader创建。

新建一个PathClassLoader实例。

java/dalvik/system/DexFile.java

/art/runtime/native/dalvik_system_DexFile.cc

cookie通过ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)转换成DexFile*实例。
再通过class_linker->DefineClassDexFile*里加载类。

可以看到DexFile_defineClassNative里的ConvertJavaArrayToDexFiles函数把cookiedex_filesoat_file关联了起来。

/art/runtime/native/dalvik_system_DexFile.cc

从这里可以猜测得出,cookiedex_filesoat_file是一对一关系的。
因此,只需要把cookie替换成另一个dex的值,就可以加载另一个dex的类。

替换cookie值在native层比较难操作,因此我们把目光放到java层。

java/dalvik/system/DexFile.java

DexFile里也有一个mCookie,通过defineClassNative传参到DexFile_defineClassNative的。
也就是替换mCookie即可达成目的--加载其他dex。

根据DexFile的构造函数可知。
调用DexFileopenDexFile得到一个新的cookie,用于替换原始的mCookie

RePlugin的全局只hook一处的方案

初始化ClassLoader的调用链路可知,LoadedApk保存了ClassLoader的实例,实际上这个实例就是findClass时用到的PathClassLoader
因此,新new一个ClassLoader并替换掉LoadedApk里的mClassLoader即能实现替换dex效果。

从上面代码可知,ClassLoader是从DexPathList pathList里加载class的。
DexPathList是由Element[] dexElements组成的,在findClass最先从数组最前面取Element
因此,我们可以在DexPathList.dexElements的前面插入隐藏dex的Element,从而实现加载隐藏dex。

发现DexFile.openInMemoryDexFile可以直接加载buffer,因此可以从内存中加载dex。

关键点在于ArtDexFileLoader::open,此处传递了一个kNoOatDexFile参数,明显是无oat文件的意思。

dalvik_system_DexFile.cc

art_dex_file_loader.cc

 
// DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}
 
// PathClassLoader.java
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }
 
    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
}
// DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}
 
// PathClassLoader.java
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }
 
    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
}
//android 8.0
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
}
 
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent, boolean isTrusted) {
    super(parent);
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
 
    if (reporter != null) {
        reportClassLoaderChain();
    }
}
//android 8.0
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
}
 
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent, boolean isTrusted) {
    super(parent);
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
 
    if (reporter != null) {
        reportClassLoaderChain();
    }
}
ActivityThread.performLaunchActivity //启动activity
 ├─ ActivityThread.createBaseContextForActivity //创建ContextImpl,并作为app的Context实现
 │    └─ ContextImpl.createActivityContext //从LoadedApk获取ClassLoader,并创建ContextImpl
 │        └─ LoadedApk.getClassLoader //判断是否已有ClassLoader,如无则调用createOrUpdateClassLoaderLocked
 │            └─ LoadedApk.createOrUpdateClassLoaderLocked
 │                └─ ApplicationLoaders.getClassLoader
 │                    └─ ClassLoaderFactory.createClassLoader
 │                        └─ new PathClassLoader
 └─ ContextImpl.getClassLoader //LoadedApk.getClassLoader已经生成了ClassLoader,并存于ContextImpl的mClassLoader中
ActivityThread.performLaunchActivity //启动activity
 ├─ ActivityThread.createBaseContextForActivity //创建ContextImpl,并作为app的Context实现
 │    └─ ContextImpl.createActivityContext //从LoadedApk获取ClassLoader,并创建ContextImpl
 │        └─ LoadedApk.getClassLoader //判断是否已有ClassLoader,如无则调用createOrUpdateClassLoaderLocked
 │            └─ LoadedApk.createOrUpdateClassLoaderLocked
 │                └─ ApplicationLoaders.getClassLoader
 │                    └─ ClassLoaderFactory.createClassLoader
 │                        └─ new PathClassLoader
 └─ ContextImpl.getClassLoader //LoadedApk.getClassLoader已经生成了ClassLoader,并存于ContextImpl的mClassLoader中
package android.app;
 
public final class ActivityThread extends ClientTransactionHandler {
    /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //...省略,chinamima
        // ==========>>> 创建ContextImpl <<<==========
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            //从ContextImpl里获取ClassLoader
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
             //...省略,chinamima
        } catch (Exception e) {
            //...省略,chinamima
        }
 
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            //...省略,chinamima
            if (activity != null) {
                //...省略,chinamima
                appContext.setOuterContext(activity);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
                //...省略,chinamima
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                //...省略,chinamima
                r.activity = activity;
            }
            mActivities.put(r.token, r);
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
            //...省略,chinamima
        }
 
        return activity;
    }
}
package android.app;
 
public final class ActivityThread extends ClientTransactionHandler {
    /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //...省略,chinamima
        // ==========>>> 创建ContextImpl <<<==========
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            //从ContextImpl里获取ClassLoader
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
             //...省略,chinamima
        } catch (Exception e) {
            //...省略,chinamima
        }
 
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            //...省略,chinamima
            if (activity != null) {
                //...省略,chinamima
                appContext.setOuterContext(activity);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
                //...省略,chinamima
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                //...省略,chinamima
                r.activity = activity;
            }
            mActivities.put(r.token, r);
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
            //...省略,chinamima
        }
 
        return activity;
    }
}
package android.app;
 
public final class ActivityThread extends ClientTransactionHandler {
 
    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        int displayId = ActivityManager.getService().getActivityDisplayId(r.token);
        //...省略,chinamima
        // ==========>>> 生成ContextImpl实例,里面会生成一个默认ClassLoader成员变量 <<<==========
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
        //...省略,chinamima
        return appContext;
    }
}
package android.app;
 
public final class ActivityThread extends ClientTransactionHandler {
 
    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        int displayId = ActivityManager.getService().getActivityDisplayId(r.token);
        //...省略,chinamima
        // ==========>>> 生成ContextImpl实例,里面会生成一个默认ClassLoader成员变量 <<<==========
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
        //...省略,chinamima
        return appContext;
    }
}
package android.app;
 
class ContextImpl extends Context {
    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        //...省略,chinamima
        // ==========>>> 从LoadedApk里获取ClassLoader <<<==========
        ClassLoader classLoader = packageInfo.getClassLoader();
        //...省略,chinamima
        //创建一个ContextImpl实例
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader);
        //...省略,chinamima
        return context;
    }
}
package android.app;
 
class ContextImpl extends Context {
    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        //...省略,chinamima
        // ==========>>> 从LoadedApk里获取ClassLoader <<<==========
        ClassLoader classLoader = packageInfo.getClassLoader();
        //...省略,chinamima
        //创建一个ContextImpl实例
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader);
        //...省略,chinamima
        return context;
    }
}
package android.app;
public final class LoadedApk {
 
    private ClassLoader mClassLoader;
 
    public ClassLoader getClassLoader() {
        synchronized (this) {
            if (mClassLoader == null) {
                // ==========>>> 关键步奏 <<<==========
                createOrUpdateClassLoaderLocked(null /*addedPaths*/);
            }
            return mClassLoader;
        }
    }
}
package android.app;
public final class LoadedApk {
 
    private ClassLoader mClassLoader;
 
    public ClassLoader getClassLoader() {
        synchronized (this) {
            if (mClassLoader == null) {
                // ==========>>> 关键步奏 <<<==========
                createOrUpdateClassLoaderLocked(null /*addedPaths*/);
            }
            return mClassLoader;
        }
    }
}
package android.app;
public final class LoadedApk {
 
    private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
        //...省略,chinamima
 
        // If we're not asked to include code, we construct a classloader that has
        // no code path included. We still need to set up the library search paths
        // and permitted path because NativeActivity relies on it (it attempts to
        // call System.loadLibrary() on a classloader from a LoadedApk with
        // mIncludeCode == false).
        if (!mIncludeCode) {
            if (mClassLoader == null) {
                //...省略,chinamima
                // ==========>>> 关键步奏 <<<==========
                mClassLoader = ApplicationLoaders.getDefault().getClassLoader(
                        "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
                        librarySearchPath, libraryPermittedPath, mBaseClassLoader,
                        null /* classLoaderName */);
                //...省略,chinamima
            }
 
            return;
        }
        //...省略,chinamima
    }
}
package android.app;
public final class LoadedApk {
 
    private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
        //...省略,chinamima
 
        // If we're not asked to include code, we construct a classloader that has
        // no code path included. We still need to set up the library search paths
        // and permitted path because NativeActivity relies on it (it attempts to
        // call System.loadLibrary() on a classloader from a LoadedApk with
        // mIncludeCode == false).
        if (!mIncludeCode) {
            if (mClassLoader == null) {
                //...省略,chinamima
                // ==========>>> 关键步奏 <<<==========
                mClassLoader = ApplicationLoaders.getDefault().getClassLoader(
                        "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
                        librarySearchPath, libraryPermittedPath, mBaseClassLoader,
                        null /* classLoaderName */);
                //...省略,chinamima
            }
 
            return;
        }
        //...省略,chinamima
    }
}
package android.app;
 
public class ApplicationLoaders {
    public static ApplicationLoaders getDefault() {
        return gApplicationLoaders;
    }
 
    ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                               String librarySearchPath, String libraryPermittedPath,
                               ClassLoader parent, String classLoaderName) {
        // For normal usage the cache key used is the same as the zip path.
        // ==========>>> 调用另一个getClassLoader <<<==========
        return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
                              libraryPermittedPath, parent, zip, classLoaderName);
    }
 
    private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                                       String librarySearchPath, String libraryPermittedPath,
                                       ClassLoader parent, String cacheKey,
                                       String classLoaderName) {
        /*
         * This is the parent we use if they pass "null" in.  In theory
         * this should be the "system" class loader; in practice we
         * don't use that and can happily (and more efficiently) use the
         * bootstrap class loader.
         */
        ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();
 
        synchronized (mLoaders) {
            if (parent == null) {
                parent = baseParent;
            }
 
            /*
             * If we're one step up from the base class loader, find
             * something in our cache.  Otherwise, we create a whole
             * new ClassLoader for the zip archive.
             */
            if (parent == baseParent) {
                ClassLoader loader = mLoaders.get(cacheKey);
                if (loader != null) {
                    return loader;
                }
                //...省略,chinamima
                // ==========>>> 工厂模式生成PathClassLoader实例 <<<==========
                ClassLoader classloader = ClassLoaderFactory.createClassLoader(
                        zip,  librarySearchPath, libraryPermittedPath, parent,
                        targetSdkVersion, isBundled, classLoaderName);
                //...省略,chinamima
                mLoaders.put(cacheKey, classloader);
                return classloader;
            }
 
            // ==========>>> 工厂模式生成PathClassLoader实例 <<<==========
            ClassLoader loader = ClassLoaderFactory.createClassLoader(
                    zip, null, parent, classLoaderName);
            return loader;
        }
    }
 
    private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<>();
 
    private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders();
package android.app;
 
public class ApplicationLoaders {
    public static ApplicationLoaders getDefault() {
        return gApplicationLoaders;
    }
 
    ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                               String librarySearchPath, String libraryPermittedPath,
                               ClassLoader parent, String classLoaderName) {
        // For normal usage the cache key used is the same as the zip path.
        // ==========>>> 调用另一个getClassLoader <<<==========
        return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
                              libraryPermittedPath, parent, zip, classLoaderName);
    }
 
    private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                                       String librarySearchPath, String libraryPermittedPath,
                                       ClassLoader parent, String cacheKey,
                                       String classLoaderName) {
        /*
         * This is the parent we use if they pass "null" in.  In theory
         * this should be the "system" class loader; in practice we
         * don't use that and can happily (and more efficiently) use the
         * bootstrap class loader.
         */
        ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();
 
        synchronized (mLoaders) {
            if (parent == null) {
                parent = baseParent;
            }
 
            /*
             * If we're one step up from the base class loader, find
             * something in our cache.  Otherwise, we create a whole
             * new ClassLoader for the zip archive.
             */
            if (parent == baseParent) {
                ClassLoader loader = mLoaders.get(cacheKey);
                if (loader != null) {
                    return loader;
                }
                //...省略,chinamima
                // ==========>>> 工厂模式生成PathClassLoader实例 <<<==========
                ClassLoader classloader = ClassLoaderFactory.createClassLoader(
                        zip,  librarySearchPath, libraryPermittedPath, parent,
                        targetSdkVersion, isBundled, classLoaderName);
                //...省略,chinamima
                mLoaders.put(cacheKey, classloader);
                return classloader;
            }
 
            // ==========>>> 工厂模式生成PathClassLoader实例 <<<==========
            ClassLoader loader = ClassLoaderFactory.createClassLoader(
                    zip, null, parent, classLoaderName);
            return loader;
        }
    }
 
    private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<>();
 
    private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders();
package com.android.internal.os;
 
public class ClassLoaderFactory {
 
    public static ClassLoader createClassLoader(String dexPath,
            String librarySearchPath, ClassLoader parent, String classloaderName) {
        if (isPathClassLoaderName(classloaderName)) {
            // ==========>>> 新建PathClassLoader实例 <<<==========
            return new PathClassLoader(dexPath, librarySearchPath, parent);
        } else if (isDelegateLastClassLoaderName(classloaderName)) {
            return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);
        }
 
        throw new AssertionError("Invalid classLoaderName: " + classloaderName);
    }
 
    public static ClassLoader createClassLoader(String dexPath,
            String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
            int targetSdkVersion, boolean isNamespaceShared, String classloaderName) {
 
        // ==========>>> 调用另一个createClassLoader <<<==========
        final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
                classloaderName);
        //...省略,chinamima
        return classLoader;
    }
}
package com.android.internal.os;
 
public class ClassLoaderFactory {
 
    public static ClassLoader createClassLoader(String dexPath,
            String librarySearchPath, ClassLoader parent, String classloaderName) {
        if (isPathClassLoaderName(classloaderName)) {
            // ==========>>> 新建PathClassLoader实例 <<<==========
            return new PathClassLoader(dexPath, librarySearchPath, parent);
        } else if (isDelegateLastClassLoaderName(classloaderName)) {
            return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);
        }
 
        throw new AssertionError("Invalid classLoaderName: " + classloaderName);
    }
 
    public static ClassLoader createClassLoader(String dexPath,
            String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
            int targetSdkVersion, boolean isNamespaceShared, String classloaderName) {
 
        // ==========>>> 调用另一个createClassLoader <<<==========
        final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
                classloaderName);
        //...省略,chinamima
        return classLoader;
    }
}
package dalvik.system;
public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;
 
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent, boolean isTrusted) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
        //...省略,chinamima
    }
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // ==========>>> 从DexPathList里找类 <<<==========
        Class c = pathList.findClass(name, suppressedExceptions);
        //...省略,chinamima
        return c;
    }   
 
}
package dalvik.system;
public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;
 
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent, boolean isTrusted) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
        //...省略,chinamima
    }
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // ==========>>> 从DexPathList里找类 <<<==========
        Class c = pathList.findClass(name, suppressedExceptions);
        //...省略,chinamima
        return c;
    }   
 
}
package dalvik.system;
final class DexPathList {
    private Element[] dexElements;
 
    public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            // ==========>>> 从Element里找类 <<<==========
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
        //...省略,chinamima
        return null;
    }
}
package dalvik.system;
final class DexPathList {
    private Element[] dexElements;
 
    public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            // ==========>>> 从Element里找类 <<<==========
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
        //...省略,chinamima
        return null;
    }
}
static class Element {
    private final File path;
    private final DexFile dexFile;
 
    public Element(DexFile dexFile, File dexZipPath) {
        this.dexFile = dexFile;
        this.path = dexZipPath;
    }
 
    public Class<?> findClass(String name, ClassLoader definingContext,
            List<Throwable> suppressed) {
        // ==========>>> 从DexFile里找类 <<<==========
        return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                : null;
    }
}
static class Element {
    private final File path;

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-4-21 10:59 被chinamima编辑 ,原因: 调整格式,提示关键步奏
收藏
免费 10
支持
分享
最新回复 (13)
雪    币: 3064
活跃值: (7808)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
写的很不错  哥哥 
2021-4-15 11:06
0
雪    币: 6573
活跃值: (3873)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
3
整理的真好
2021-4-15 11:11
0
雪    币: 2291
活跃值: (2185)
能力值: (RANK:400 )
在线值:
发帖
回帖
粉丝
4
学习了。
2021-4-16 10:16
0
雪    币: 14
活跃值: (193)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
哥哥 写的真好!
2021-4-16 10:24
0
雪    币: 2971
活跃值: (117422)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
写的挺好的一篇文章
2021-4-16 18:01
0
雪    币: 116
活跃值: (1012)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
优秀
2021-4-17 01:12
0
雪    币: 195
活跃值: (108)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
想努力看懂
2021-4-17 17:37
0
雪    币: 11
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
我想知道这有用吗?全部载入内存后一下子dump出来了
2021-4-19 13:59
0
雪    币: 216
活跃值: (585)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
huangbof 我想知道这有用吗?全部载入内存后一下子dump出来了
防dump那是下一步的加固方案了,只要是在内存中还是完整的dex,就能dump
2021-4-21 09:47
0
雪    币: 5330
活跃值: (5464)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
11
学习了!!
2021-4-23 11:19
0
雪    币: 417
活跃值: (521)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
加载内存里的 dex 直接 InMemoryDexClassLoader 不就好?再者,mCookie 和 DexFile 等接口已经被加入隐藏 API 列表了,要用还得考虑绕过隐藏 API 限制呢。
2021-4-23 19:34
0
雪    币: 1709
活跃值: (830)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
加载内存里的 dex 直接 InMemoryDexClassLoader 不就好?再者,mCookie 和 DexFile 等接口已经被加入隐藏 API 列表了,要用还得考虑绕过隐藏 API 限制呢。
2021-4-23 19:43
0
雪    币: 208
活跃值: (387)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
加载内存里的 dex 直接 InMemoryDexClassLoader 不就好?再者,mCookie 和 DexFile 等接口已经被加入隐藏 API 列表了,要用还得考虑绕过隐藏 API 限制呢。
2021-4-25 18:54
0
游客
登录 | 注册 方可回帖
返回
//