package com.qianfandu.utils; import android.content.Context; import android.util.Log; import com.qianfandu.DynamicDexClassLoder; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.channels.FileChannel; import java.util.Map; import dalvik.system.DexClassLoader; import static android.transition.Fade.IN; /** * Dex文件保护工具类 * * @author linchaolong */ public class DexProtector { private static final int BUFF_SIZE = 1024 * 1024 * 5; //5MB private static final String TAG = "ApkProtect"; private Context context = null; private Class<?> loadedApkClass = null; private WeakReference<?> loadedApkRef = null; public DexProtector(Context ctx) { this.context = ctx; try { // 初始化 // 1. 得到当前的ActivityThread // 加载ActivityThread的字节码 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); // 利用反射得到currentActivityThread方法 Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); // 调用currentActivityThread方法得到当前ActivityThread对象 Object activityThread = currentActivityThreadMethod.invoke(null); // 2. 得到LoadedApk的弱引用 //final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<String, WeakReference<LoadedApk>>(); // 得到LoadedApk对象 Field mPackagesField = activityThreadClass.getDeclaredField("mPackages"); mPackagesField.setAccessible(true); //取消默认 Java 语言访问控制检查的能力(暴力反射) Map mPackages = (Map) mPackagesField.get(activityThread); // 得到LoadedApk对象的弱引用 loadedApkRef = (WeakReference) mPackages.get(ctx.getPackageName()); // 3. 修改LoadedApk对象中mClassLoader字段 loadedApkClass = Class.forName("android.app.LoadedApk"); // Field mClassLoaderField = loadedApkClass.getDeclaredField("mClassLoader"); // mClassLoaderField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } } /** * 得到系统ClassLoader * * @return */ public ClassLoader getAppClassLoader() { try { Field mClassLoaderField = loadedApkClass.getDeclaredField("mClassLoader"); mClassLoaderField.setAccessible(true); return (ClassLoader) mClassLoaderField.get(loadedApkRef.get());// 得到系统ClassLoader } catch (Exception e) { e.printStackTrace(); } return null; } /** * 修改当前应用ClassLoader * * @param newClassLoader */ private boolean setAppClassLoader(ClassLoader newClassLoader) { try { // 3. 修改LoadedApk对象中mClassLoader字段 Field mClassLoaderField = loadedApkClass.getDeclaredField("mClassLoader"); mClassLoaderField.setAccessible(true); mClassLoaderField.set(loadedApkRef.get(), newClassLoader); // 修改当前ClassLoader为自定义ClassLoader return true; } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } return false; } /** * 加载加密后的dex文件 * * @param dexInput 加密的dex文件的输入流 * @return ClassLoader */ public ClassLoader loadEncryptDex(String[] dexName) { try { //通过反射修改ActivityThread中LoadedApk的ClassLoader字段 ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); String pahts=""; for (int i = 0; i < dexName.length; i++) { pahts+=createEncryptDexLoader(context.getAssets().open("jiagu_data.bin/" + dexName[i]), dexName[i], appClassLoader)+":"; // 优先系统的 } // 经过优化的dex输出目录 File odexDir = context.getDir("apktoolplus_odex", Context.MODE_PRIVATE); // libs目录 String libPath = context.getApplicationInfo().nativeLibraryDir; // 创建类加载器,加载解密后的dex文件 ClassLoader dexClassLoader; if (appClassLoader != null) { dexClassLoader = new DexClassLoader(pahts, odexDir.getAbsolutePath(), libPath, appClassLoader); } else { dexClassLoader = new DexClassLoader(pahts, odexDir.getAbsolutePath(), libPath, context.getClassLoader()); } setAppClassLoader(dexClassLoader);// 修改当前ClassLoader为自定义ClassLoader // 删除解密后的dex文件 deleteDir(context.getDir("apktoolplus_dex", Context.MODE_PRIVATE)); return dexClassLoader; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 在内存在加载加密的dex文件 * * @param in 加密的dex文件的输入流 * @return ClassLoader */ public ClassLoader loadEncryptDexMemory(FileInputStream in) { try { // 读取dex文件数据 ByteArrayOutputStream outBuff = new ByteArrayOutputStream(); byte[] temp = new byte[BUFF_SIZE]; int len = -1; while ((len = in.read(temp)) != -1) { outBuff.write(temp, 0, len); } return loadEncryptDexMemory(outBuff.toByteArray()); } catch (Exception e) { e.printStackTrace(); } finally { IO.close(in); } return null; } /** * 在内存在加载加密的dex文件 * * @param dexBytes 加密的dex文件的字节数据 * @return ClassLoader */ public ClassLoader loadEncryptDexMemory(byte[] dexBytes) { try { ClassLoader appClassLoader = getAppClassLoader(); DynamicDexClassLoder newClassLoader = new DynamicDexClassLoder( context, dexBytes, null, appClassLoader, context.getPackageResourcePath(), context.getDir(".dex", Context.MODE_PRIVATE).getAbsolutePath()); setAppClassLoader(newClassLoader);// 修改当前ClassLoader为自定义ClassLoader return newClassLoader; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 创建一个加密dex文件的DexLoader * * @param in 加密的dex文件的输入流 * @param outFileName 解密文件输入名称 * @param parent 父类加载器 * @return ClassLoader */ public String createEncryptDexLoader(InputStream in, String outFileName, ClassLoader parent) { // 解密dex文件的File对象 File decryptFile = new File(context.getDir("apktoolplus_dex", Context.MODE_PRIVATE), outFileName); // 解密dex文件 DexProtector.decryptDex(in, decryptFile); if (!decryptFile.exists()) { Log.e(TAG, "dex decrypt failure!!!"); } // 使用优化过的odex文件替换解密的dex // File odexFile = new File(odexDir, outFileName); // repleceFile(odexFile, decryptFile); return decryptFile.getPath(); } private void deleteDir(File dir) { for (File file : dir.listFiles()) { file.delete(); } dir.delete(); } private void repleceFile(File src, File dst) { if (src.exists()) { try { copyFile(src, dst); } catch (IOException e) { e.printStackTrace(); } } } private void copyFile(File src, File dst) throws IOException { if (dst.exists()) { dst.delete(); } FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst); FileChannel inChannel = inStream.getChannel(); FileChannel outChannel = outStream.getChannel(); inChannel.transferTo(0, inChannel.size(), outChannel); inStream.close(); outStream.close(); } /** * 解密Dex文件 * * @param in 加密dex文件输入流 * @param outFile 输出解密文件 * @return 是否解密成功 */ public static boolean decryptDex(InputStream in, File outFile) { // 加密dex文件所在目录 File outDir = outFile.getParentFile(); // 如果目录不存在则创建 if (!outDir.exists() && outDir.isDirectory()) { outDir.mkdirs(); } FileOutputStream out = null; try { if (outFile.exists()) { outFile.delete(); } out = new FileOutputStream(outFile); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); // 读取加密数据 byte[] buff = new byte[BUFF_SIZE]; int len; while ((len = in.read(buff)) != -1) { byteOutput.write(buff, 0, len); } // 调用native方法解密dex文件数据 byte[] dexBytes = byteOutput.toByteArray(); byte[] decryptBuff = ApkToolPlus.decrypt(dexBytes); out.write(decryptBuff, 0, decryptBuff.length); out.flush(); return true; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 释放资源 IO.close(in); IO.close(out); } return false; } /** * 解密Dex文件 * * @param encryptFile 加密文件 * @param outFile 输出解密文件 * @return 是否解密成功 */ public static boolean decryptDex(File encryptFile, File outFile) { if (!encryptFile.exists()) { Log.e(TAG, "加密文件 '" + encryptFile.getPath() + " ''不存在"); return false; } try { return decryptDex(new FileInputStream(encryptFile), outFile); } catch (FileNotFoundException e) { e.printStackTrace(); } return false; } }