Android Apk通过代理Application解密,还原正式的Application,AES加密Apk

apk通过代理application解密,还原

1.manifest里添加

Android Apk通过代理Application解密,还原正式的Application,AES加密Apk

Android Apk通过代理Application解密,还原正式的Application,AES加密Apk

2.1解密加密后的Apk里面的.dex文件,还原正式的Application

import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class ProxyApp extends Application {

    //定义好解密后的文件的存放路径
    private String app_name;
    private String app_version;

    /**
     * ActivityThread创建Application之后调用的第一个方法
     * 可以在这个方法中进行解密,同时把dex交给android去加载
     */
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        //获取用户填入的metadata
        getMetaData();

        //得到当前加密了的APK文件
        File apkFile = new File(getApplicationInfo().sourceDir);

        //把apk解压   app_name+"_"+app_version目录中的内容需要boot权限才能用
        File versionDir = getDir(app_name + "_" + app_version, MODE_PRIVATE);
        File appDir = new File(versionDir, "app");
        File dexDir = new File(appDir, "dexDir");


        //得到我们需要加载的Dex文件
        List<File> dexFiles = new ArrayList<>();
        //进行解密(最好做MD5文件校验)
        if (!dexDir.exists() || dexDir.list().length == 0) {
            //把apk解压到appDir
            Zip.unZip(apkFile, appDir);
            //获取目录下所有的文件
            File[] files = appDir.listFiles();
            for (File file : files) {
                String name = file.getName();
                if (name.endsWith(".dex") && !TextUtils.equals(name, "classes.dex")) {
                    try {
                        AES.init(AES.DEFAULT_PWD);
                        //读取文件内容
                        byte[] bytes = Utils.getBytes(file);
                        //解密
                        byte[] decrypt = AES.decrypt(bytes);
                        //写到指定的目录
                        FileOutputStream fos = new FileOutputStream(file);
                        fos.write(decrypt);
                        fos.flush();
                        fos.close();
                        dexFiles.add(file);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } else {
            for (File file : dexDir.listFiles()) {
                dexFiles.add(file);
            }
        }

        try {
            //2.把解密后的文件加载到系统
            loadDex(dexFiles, versionDir);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private void loadDex(List<File> dexFiles, File versionDir) throws Exception {
        //1.获取pathlist
        Field pathListField = Utils.findField(getClassLoader(), "pathList");
        Object pathList = pathListField.get(getClassLoader());
        //2.获取数组dexElements
        Field dexElementsField = Utils.findField(pathList, "dexElements");
        Object[] dexElements = (Object[]) dexElementsField.get(pathList);
        //3.反射到初始化dexElements的方法
        Method makeDexElements = Utils.findMethod(pathList, "makePathElements", List.class, File.class, List.class);

        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        Object[] addElements = (Object[]) makeDexElements.invoke(pathList, dexFiles, versionDir, suppressedExceptions);

        //合并数组
        Object[] newElements = (Object[]) Array.newInstance(dexElements.getClass().getComponentType(), dexElements.length + addElements.length);
        System.arraycopy(dexElements, 0, newElements, 0, dexElements.length);
        System.arraycopy(addElements, 0, newElements, dexElements.length, addElements.length);

        //替换classloader中的element数组
        dexElementsField.set(pathList, newElements);
    }

    private void getMetaData() {
        try {
            ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(
                    getPackageName(), PackageManager.GET_META_DATA);
            Bundle metaData = applicationInfo.metaData;
            if (null != metaData) {
                if (metaData.containsKey("app_name")) {
                    app_name = metaData.getString("app_name");
                }
                if (metaData.containsKey("app_version")) {
                    app_version = metaData.getString("app_version");
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 开始替换application
     */
    @Override
    public void onCreate() {
        super.onCreate();
        try {
            bindRealApplicatin();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 让代码走入if中的第三段中
     * @return
     */
    @Override
    public String getPackageName() {
        if(!TextUtils.isEmpty(app_name)){
            return "";
        }
        return super.getPackageName();
    }

    @Override
    public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException {
        if(TextUtils.isEmpty(app_name)){
            return super.createPackageContext(packageName, flags);
        }
        try {
            bindRealApplicatin();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return delegate;

    }

    boolean isBindReal;
    Application delegate;

    private void bindRealApplicatin() throws Exception {
        if (isBindReal) {
            return;
        }
        if (TextUtils.isEmpty(app_name)) {
            return;
        }
        //得到attachBaseContext(context) 传入的上下文 ContextImpl
        Context baseContext = getBaseContext();
        //创建用户真实的application (MyApplication)
        Class<?> delegateClass = Class.forName(app_name);
        delegate = (Application) delegateClass.newInstance();
        //得到attach()方法
        Method attach = Application.class.getDeclaredMethod("attach", Context.class);
        attach.setAccessible(true);
        attach.invoke(delegate, baseContext);


//        ContextImpl---->mOuterContext(app)   通过Application的attachBaseContext回调参数获取
        Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
        //获取mOuterContext属性
        Field mOuterContextField = contextImplClass.getDeclaredField("mOuterContext");
        mOuterContextField.setAccessible(true);
        mOuterContextField.set(baseContext, delegate);

//        ActivityThread--->mAllApplications(ArrayList)       ContextImpl的mMainThread属性
        Field mMainThreadField = contextImplClass.getDeclaredField("mMainThread");
        mMainThreadField.setAccessible(true);
        Object mMainThread = mMainThreadField.get(baseContext);

//        ActivityThread--->>mInitialApplication
        Class<?> activityThreadClass=Class.forName("android.app.ActivityThread");
        Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
        mInitialApplicationField.setAccessible(true);
        mInitialApplicationField.set(mMainThread,delegate);
//        ActivityThread--->mAllApplications(ArrayList)       ContextImpl的mMainThread属性
        Field mAllApplicationsField = activityThreadClass.getDeclaredField("mAllApplications");
        mAllApplicationsField.setAccessible(true);
        ArrayList<Application> mAllApplications =(ArrayList<Application>) mAllApplicationsField.get(mMainThread);
        mAllApplications.remove(this);
        mAllApplications.add(delegate);

//        LoadedApk------->mApplication                      ContextImpl的mPackageInfo属性
        Field mPackageInfoField = contextImplClass.getDeclaredField("mPackageInfo");
        mPackageInfoField.setAccessible(true);
        Object mPackageInfo=mPackageInfoField.get(baseContext);

        Class<?> loadedApkClass=Class.forName("android.app.LoadedApk");
        Field mApplicationField = loadedApkClass.getDeclaredField("mApplication");
        mApplicationField.setAccessible(true);
        mApplicationField.set(mPackageInfo,delegate);

        //修改ApplicationInfo className   LooadedApk
        Field mApplicationInfoField = loadedApkClass.getDeclaredField("mApplicationInfo");
        mApplicationInfoField.setAccessible(true);
        ApplicationInfo mApplicationInfo = (ApplicationInfo)mApplicationInfoField.get(mPackageInfo);
        mApplicationInfo.className=app_name;

        delegate.onCreate();
        isBindReal = true;
    }
}

2,Apk加密,对齐,签名

 public static void main(String[] args) throws Exception {

        //分段执行,建议使用电脑cmd
        //

        /**
         * 1.制作只包含解密代码的dex文件
         */
//        File aarFile = new File("proxy_core/build/outputs/aar/proxy_core-debug.aar");
//        File aarTemp = new File("proxy_utils/temp");
//        Zip.unZip(aarFile,aarTemp);
//        File classesJar = new File(aarTemp,"classes.jar");
//        File calssesDex = new File(aarTemp,"classes.dex");
//
//        // /c = C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\28.0.3
//        //dx --dex --output out.dex  in.jar
//        String execo = "cmd c:/Users/Administrator/AppData/Local/Android/Sdk/build-tools/28.0.2 dx --dex --output "+calssesDex.getAbsolutePath()
//                +" "+classesJar.getAbsolutePath();
//        System.out.println("execo:"+execo);
//        Process process=Runtime.getRuntime().exec(execo);
//
//        process.waitFor();
//        if (process.exitValue() !=0){
//            throw  new RuntimeException("dex error");
//        }

        /**
         * 2.加密APK中所有的dex文件
         */
//        File apkFile=new File("app/build/outputs/apk/debug/app-debug.apk");
//        File apkTemp=new File("app/build/outputs/apk/debug/temp");
//        Zip.unZip(apkFile,apkTemp);
//////        //只要dex文件拿出来加密
//        File[] dexFiles=apkTemp.listFiles(new FilenameFilter() {
//            @Override
//            public boolean accept(File file, String s) {
//                return s.endsWith(".dex");
//            }
//        });
//        //AES加密了
//        AES.init(AES.DEFAULT_PWD);
//        for (File dexFile : dexFiles) {
//            byte[] bytes = Utils.getBytes(dexFile);
//            byte[] encrypt = AES.encrypt(bytes);
//            FileOutputStream fos=new FileOutputStream(new File(apkTemp,
//                    "secret-"+dexFile.getName()));
//            fos.write(encrypt);
//            fos.flush();
//            fos.close();
//            dexFile.delete();
//        }

        /**
         * 3.把dex放入apk解压目录,重新压成apk文件
         */
        File aarTemp = new File("proxy_utils/temp");
        File calssesDex = new File(aarTemp,"classes.dex");
        File apkTemp=new File("app/build/outputs/apk/debug/temp");
        calssesDex.renameTo(new File(apkTemp,"classes.dex"));
        File unSignedApk=new File("app/build/outputs/apk/debug/app-unsigned.apk");
        Zip.zip(apkTemp,unSignedApk);
        System.out.println("unSignedApk:"+unSignedApk.getAbsolutePath());


        /**
         * 4.对齐和签名
         */
//        zipalign -v -p 4 my-app-unsigned.apk my-app-unsigned-aligned.apk
//        File unSignedApk=new File("app/build/outputs/apk/debug/app-unsigned.apk");
//        File alignedApk=new File("app/build/outputs/apk/debug/app-unsigned-aligned.apk");
//
//        String execs1 = "cmd c:/Users/Administrator/AppData/Local/Android/Sdk/build-tools/25.0.0 zipalign -v -p 4 "+unSignedApk.getAbsolutePath()
//                +" "+alignedApk.getAbsolutePath();
//        System.out.println("execs1:"+execs1);
//        Process process=
//                Runtime.getRuntime().exec("cmd c:/Users/Administrator/AppData/Local/Android/Sdk/build-tools/23.0.1 zipalign -v -p 4 "+unSignedApk.getAbsolutePath()
//                +" "+alignedApk.getAbsolutePath());
//        process.waitFor();
//        if(process.exitValue()!=0){
//            throw new RuntimeException("dex error");
//        }

        /**
         * 4.2签名
         */
  //      Failed to load signer "signer #1": D:\AndroidProject\DnStudy\BigViewDemo\proxy_utils\forest.jks entry "forest" does not contain a key
//        apksigner sign --ks my-release-key.jks --out my-app-release.apk my-app-unsigned-aligned.apk
//        apksigner sign  --ks jks文件地址 --ks-key-alias 别名 --ks-pass pass:jsk密码 --key-pass pass:别名密码 --out  out.apk in.apk

//        File alignedApk=new File("app/build/outputs/apk/debug/app-unsigned-aligned.apk");
//        File signedApk=new File("app/build/outputs/apk/debug/app-signed-aligned.apk");
//        File jks=new File("proxy_utils/forestd.jks");
//
//        String execs = "cmd c:/Users/Administrator/AppData/Local/Android/Sdk/build-tools/25.0.0 apksigner sign --ks "+jks.getAbsolutePath()
//                +" --ks-key-alias forest --ks-pass pass:123456 --key-pass pass:123456 --out "
//                +signedApk.getAbsolutePath()+" "+alignedApk.getAbsolutePath();
//        System.out.println("execs:"+execs);
//        Process process=Runtime.getRuntime().exec(execs);
//
//
//        process.waitFor();
//        if(process.exitValue()!=0){
//            throw new RuntimeException("dex error");
//        }
        System.out.println("执行成功");

    }

Android Apk通过代理Application解密,还原正式的Application,AES加密Apk

最终得到的app-signed-aligned.apk就是加密后的