/** * */ package com.example.dex; import android.util.Log; import dalvik.system.DexClassLoader; import dalvik.system.PathClassLoader; import java.io.File; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class DexInjector { private static final String TAG = DexInjector.class.getSimpleName(); /** * inject your dex file to PathClassLoader * @param dexPath the list of jar/apk files containing classes and resources, delimited by File.pathSeparator, which defaults to ":" on Android * @param defaultDexOptPath directory where optimized dex files should be written; must <b>not</b> be null * @param nativeLibPath the list of directories containing native libraries, delimited by File.pathSeparator; may be null * @return whether success */ public static synchronized Boolean inject(String dexPath, String defaultDexOptPath, String nativeLibPath, String dummyClassName) { try { Class.forName("dalvik.system.LexClassLoader"); return injectInAliyunOs(dexPath, defaultDexOptPath, nativeLibPath, dummyClassName); } catch (ClassNotFoundException e) { } boolean hasBaseDexClassLoader = true; try { Class.forName("dalvik.system.BaseDexClassLoader"); } catch (ClassNotFoundException e) { hasBaseDexClassLoader = false; } if (!hasBaseDexClassLoader) { return injectBelowApiLevel14(dexPath, defaultDexOptPath, nativeLibPath, dummyClassName); } else { return injectAboveEqualApiLevel14(dexPath, defaultDexOptPath, nativeLibPath, dummyClassName); } } private static synchronized Boolean injectInAliyunOs( String dexPath, String defaultDexOptPath, String nativeLibPath, String dummyClassName) { Log.i(TAG, "-->injectInAliyunOs"); PathClassLoader localClassLoader = (PathClassLoader) DexInjector.class.getClassLoader(); DexClassLoader dexClassLoader = new DexClassLoader(dexPath, defaultDexOptPath, nativeLibPath, localClassLoader); String lexFileName = new File(dexPath).getName(); lexFileName = lexFileName.replaceAll("\\.[a-zA-Z0-9]+", ".lex"); try { dexClassLoader.loadClass(dummyClassName); Class<?> classLexClassLoader = Class.forName("dalvik.system.LexClassLoader"); Constructor<?> constructorLexClassLoader = classLexClassLoader.getConstructor( String.class, String.class, String.class, ClassLoader.class); Object localLexClassLoader = constructorLexClassLoader.newInstance( defaultDexOptPath + File.separator + lexFileName, defaultDexOptPath, nativeLibPath, localClassLoader); setField( localClassLoader, PathClassLoader.class, "mPaths", appendArray( getField(localClassLoader, PathClassLoader.class, "mPaths"), getField(localLexClassLoader, classLexClassLoader, "mRawDexPath"))); setField( localClassLoader, PathClassLoader.class, "mFiles", combineArray( getField(localClassLoader, PathClassLoader.class, "mFiles"), getField(localLexClassLoader, classLexClassLoader,"mFiles"))); setField( localClassLoader, PathClassLoader.class, "mZips", combineArray( getField(localClassLoader, PathClassLoader.class, "mZips"), getField(localLexClassLoader, classLexClassLoader, "mZips"))); setField( localClassLoader, PathClassLoader.class, "mLexs", combineArray( getField(localClassLoader, PathClassLoader.class, "mLexs"), getField(localLexClassLoader, classLexClassLoader, "mDexs"))); } catch (Throwable t) { t.printStackTrace(); return false; } Log.i(TAG, "<--injectInAliyunOs end."); return true; } private static synchronized Boolean injectBelowApiLevel14( String dexPath, String defaultDexOptPath, String nativeLibPath, String dummyClassName) { Log.i(TAG, "--> injectBelowApiLevel14"); PathClassLoader pathClassLoader = (PathClassLoader) DexInjector.class.getClassLoader(); DexClassLoader dexClassLoader = new DexClassLoader(dexPath, defaultDexOptPath, nativeLibPath, pathClassLoader); try { dexClassLoader.loadClass(dummyClassName); setField( pathClassLoader, PathClassLoader.class, "mPaths", appendArray( getField(pathClassLoader, PathClassLoader.class,"mPaths"), getField(dexClassLoader, DexClassLoader.class,"mRawDexPath"))); setField( pathClassLoader, PathClassLoader.class, "mFiles", combineArray( getField(pathClassLoader, PathClassLoader.class, "mFiles"), getField(dexClassLoader, DexClassLoader.class, "mFiles"))); setField( pathClassLoader, PathClassLoader.class, "mZips", combineArray( getField(pathClassLoader, PathClassLoader.class, "mZips"), getField(dexClassLoader, DexClassLoader.class, "mZips"))); setField( pathClassLoader, PathClassLoader.class, "mDexs", combineArray( getField(pathClassLoader, PathClassLoader.class, "mDexs"), getField(dexClassLoader, DexClassLoader.class, "mDexs"))); } catch (Throwable e) { e.printStackTrace(); return false; } Log.i(TAG, "<-- injectBelowApiLevel14"); return true; } private static synchronized Boolean injectAboveEqualApiLevel14( String dexPath, String defaultDexOptPath, String nativeLibPath, String dummyClassName) { Log.i(TAG, "--> injectAboveEqualApiLevel14"); PathClassLoader pathClassLoader = (PathClassLoader) DexInjector.class.getClassLoader(); DexClassLoader dexClassLoader = new DexClassLoader(dexPath, defaultDexOptPath, nativeLibPath, pathClassLoader); try { dexClassLoader.loadClass(dummyClassName); Object dexElements = combineArray( getDexElements(getPathList(pathClassLoader)), getDexElements(getPathList(dexClassLoader))); Object pathList = getPathList(pathClassLoader); setField(pathList, pathList.getClass(), "dexElements", dexElements); } catch (Throwable e) { e.printStackTrace(); return false; } Log.i(TAG, "<-- injectAboveEqualApiLevel14 End."); return true; } private static Object getPathList(Object baseDexClassLoader) throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException { return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList"); } private static Object getDexElements(Object paramObject) throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException { return getField(paramObject, paramObject.getClass(), "dexElements"); } private static Object getField(Object obj, Class<?> cl, String field) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field localField = cl.getDeclaredField(field); localField.setAccessible(true); return localField.get(obj); } private static void setField(Object obj, Class<?> cl, String field, Object value) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field localField = cl.getDeclaredField(field); localField.setAccessible(true); localField.set(obj, value); } private static Object combineArray(Object arrayLhs, Object arrayRhs) { Class<?> localClass = arrayLhs.getClass().getComponentType(); int i = Array.getLength(arrayLhs); int j = i + Array.getLength(arrayRhs); Object result = Array.newInstance(localClass, j); for (int k = 0; k < j; ++k) { if (k < i) { Array.set(result, k, Array.get(arrayLhs, k)); } else { Array.set(result, k, Array.get(arrayRhs, k - i)); } } return result; } private static Object appendArray(Object array, Object value) { Class<?> localClass = array.getClass().getComponentType(); int i = Array.getLength(array); int j = i + 1; Object localObject = Array.newInstance(localClass, j); for (int k = 0; k < j; ++k) { if (k < i) { Array.set(localObject, k, Array.get(array, k)); } else { Array.set(localObject, k, value); } } return localObject; } }