此文的源码测试过,除了下面这个问题之外,基本没有什么大问题:
package com.android.dexunshell; import java.io.IOException; import java.net.URL; import java.util.Enumeration; import com.eebbk.mingming.k7utils.ReflectUtils; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import dalvik.system.DexClassLoader; import dalvik.system.DexFile; public class DynamicDexClassLoder extends DexClassLoader { private static final String TAG = DynamicDexClassLoder.class.getName(); private int cookie; private Context mContext; /** * 原构造 * * @param dexPath * @param optimizedDirectory * @param libraryPath * @param parent */ public DynamicDexClassLoder(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) { super(dexPath, optimizedDirectory, libraryPath, parent); } /** * 直接从内存加载 新构造 * * @param dexBytes * @param libraryPath * @param parent * @throws Exception */ public DynamicDexClassLoder(Context context, byte[] dexBytes, String libraryPath, ClassLoader parent, String oriPath, String fakePath) { super(oriPath, fakePath, libraryPath, parent); setContext(context); setCookie(NativeTool.loadDex(dexBytes, dexBytes.length)); } private void setCookie(int kie) { cookie = kie; } private void setContext(Context context) { mContext = context; } private String[] getClassNameList(int cookie) { return (String[]) ReflectUtils.invokeStaticMethod(DexFile.class, "getClassNameList", new Class[] { int.class }, new Object[] { cookie }); } private Class defineClass(String name, ClassLoader loader, int cookie) { return (Class) ReflectUtils.invokeStaticMethod(DexFile.class, "defineClass", new Class[] { String.class, ClassLoader.class, int.class }, new Object[] { name, loader, cookie }); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Log.d(TAG, "findClass-" + name); Class<?> cls = null; String as[] = getClassNameList(cookie); for (int z = 0; z < as.length; z++) { if (as[z].equals(name)) { cls = defineClass(as[z].replace('.', '/'), mContext.getClassLoader(), cookie); break; } else { defineClass(as[z].replace('.', '/'), mContext.getClassLoader(), cookie); } } if (null == cls) { cls = super.findClass(name); } return cls; } @Override protected URL findResource(String name) { Log.d(TAG, "findResource-" + name); return super.findResource(name); } @Override protected Enumeration<URL> findResources(String name) { Log.d(TAG, "findResources ssss-" + name); return super.findResources(name); } @Override protected synchronized Package getPackage(String name) { Log.d(TAG, "getPackage-" + name); return super.getPackage(name); } @Override protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { Log.d(TAG, "loadClass-" + className + " resolve " + resolve); Class<?> clazz = super.loadClass(className, resolve); if (null == clazz) { Log.e(TAG, "loadClass fail,maybe get a null-point exception."); } return clazz; } @Override protected Package[] getPackages() { Log.d(TAG, "getPackages sss-"); return super.getPackages(); } @Override protected Package definePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) throws IllegalArgumentException { Log.d(TAG, "definePackage" + name); return super.definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); } }
注意:函数findClass处的改动!
已经定义过的类不需要重新再定义,否则会导致app陷入无限循环,启动时有卡死状!
在做ndk dex加密时,壳与被加密程序在通常是捆绑在一个APK包中。如果APP中libs目录下有so库,此时需要重写findLibrary函数,从getClass().getClassLoader()获取路径!否则,你需要自己把.so文件存到手机某个地方,然后把路径返回给出来!
ndk解壳形式安全性比较高,但是内存加载dex所依赖的底层方法,只在4.0以上几个版本存在,5.0没有查询还是未知数,还没能满足通用性的要求,要需要进一步寻找方案。