Dex简单保护

2019-07-30 约 316 字 预计阅读 2 分钟

声明:本文 【Dex简单保护】 由作者 yong夜 于 2019-07-30 09:10:00 首发 先知社区 曾经 浏览数 103 次

感谢 yong夜 的辛苦付出!

[TOC]

加固原理

利用加密算法将原始APK文件加密保护起来,然后将加密后的数据附加到壳尾部或其他地方,当运行起来的时候,壳会把动态解密加载起来。

加固流程

  1. 编写解密并加载原始APK的壳App,生成对应的壳Dex
  2. 加密原始APK/DEX文件,写入到壳Dex尾部或者存放到其他目录下
  3. 修改壳Dex相应字段和被加固App的AndroidManifest.xml文件添加Application组件从壳App启动
  4. 删除签名文件、替换dex文件、添加so文件等

壳App

1.解密出Dex/APK/JAR文件(为了简单化,这里暂时不用加密)

2.替换dexclassloader对象

3.执行原始APK的Application类

package com.stub;

import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.Log;

import com.stub.utils.FileUtils;
import com.stub.utils.RefInvoke;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
import dalvik.system.DexClassLoader;

public class shell extends Application {

    private Application mApp = this;
    private File LIBS;
    private static final String DEFAULT_APPLICATION = "android.app.Application";


    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        /*1. 获取原始Dex文件,这里手动将classes.dex传到app-libs目录下*/
        LIBS = mApp.getDir("libs", Context.MODE_PRIVATE);
        StringBuffer dexPathList = new StringBuffer();
        dexPathList.append(LIBS);
        dexPathList.append("/");
        dexPathList.append("classes.dex");
        System.out.printf("load classes.dex in %s", dexPathList);
        Log.i("shell", "load classes.dex in " + dexPathList);


        /* 2. 加载Dex文件*/
        String classActivityThread = "android.app.ActivityThread";
        String classLoadedApk = "android.app.LoadedApk";
        DexClassLoader loader;
        File nativeLib = new File(FileUtils.getParent(LIBS), "lib");

        Object activityThread = RefInvoke.invokeStaticMethod(classActivityThread, "currentActivityThread", new Class[]{}, new Object[]{});

        String packageName = this.getPackageName();//当前apk的包名

        Map<?,?> mPackage = (Map<?,?>)RefInvoke.getField(activityThread, classActivityThread, "mPackages");

        WeakReference<?> wr = (WeakReference<?>) mPackage.get(packageName);

        loader = new DexClassLoader(dexPathList.toString(), mApp.getCacheDir().getAbsolutePath(), nativeLib.getAbsolutePath(), (ClassLoader) RefInvoke.getField(wr.get(), "android.app.LoadedApk", "mClassLoader"));
        RefInvoke.setField(wr.get(), classLoadedApk, "mClassLoader", loader);

        /* 2. 执行application*/

        String main = "com.scoreloop.games.gearedub.GearApplication";
        String applicationName = main;
        //ActivityThread.currentActivityThread().mBoundApplication.info.mApplication = null;
        Object currentActivityThread = RefInvoke.invokeStaticMethod(classActivityThread, "currentActivityThread", new Class[]{}, new Object[]{});
        Object mBoundApplication = RefInvoke.getField(currentActivityThread, classActivityThread, "mBoundApplication");
        Object loadedApkInfo = RefInvoke.getField(mBoundApplication, classActivityThread+"$AppBindData", "info");
        RefInvoke.setField(loadedApkInfo, classLoadedApk, "mApplication", null);

        //currentActivityThread.mAllApplications.remove(currentActivityThread.mInitialApplication)
        Object mInitApplication = RefInvoke.getField(currentActivityThread, classActivityThread, "mInitialApplication");
        List<Application> mAllApplications = (List<Application>) RefInvoke.getField(currentActivityThread, classActivityThread, "mAllApplications");
        mAllApplications.remove(mInitApplication);

        //(LoadedApk) loadedApkInfo.mApplicationInfo.className = applicationName
        ((ApplicationInfo) RefInvoke.getField(loadedApkInfo, classLoadedApk, "mApplicationInfo")).className = applicationName;

        //(ActivityThread$AppBindData) mBoundApplication.appInfo.className = applicationName
        ((ApplicationInfo) RefInvoke.getField(mBoundApplication, classActivityThread+"$AppBindData", "appInfo")).className = applicationName;

        //currentActivityThread.mInitApplication = loadedApkInfo.makeApplication(false, null)
        Application makeApplication = (Application) RefInvoke.invokeMethod(loadedApkInfo, classLoadedApk, "makeApplication", new Class[]{boolean.class, Instrumentation.class}, new Object[]{false, null});
        RefInvoke.setField(currentActivityThread, classActivityThread, "mInitialApplication", makeApplication);

        //currentActivityThread.mProviderMap
        Map<?,?> mProviderMap = (Map<?,?>) RefInvoke.getField(currentActivityThread, classActivityThread, "mProviderMap");
        for (Map.Entry<?, ?> entry : mProviderMap.entrySet()) {
            Object providerClientRecord = entry.getValue();
            Object mLocalProvider = RefInvoke.getField(providerClientRecord, classActivityThread+"$ProviderClientRecord", "mLocalProvider");
            RefInvoke.setField(mLocalProvider, "android.content.ContentProvider", "mContext", makeApplication);
        }
        makeApplication.onCreate();
    }
}

加密Dex文件

这里为了方便起见,不进行加密操作,直接将原始dex文件手动上传到/data/data/<packagename>/app-libs目录下等待壳App进行加载</packagename>

修改AndroidManifest.xml文件

1.使用apktool将原始apk进行反编译

2.修改Manifest文件中的application组件的android:name属性值为壳App的application

3.重打包、签名

<application android:description="@string/app_description" android:icon="@drawable/icon" android:label="@string/app_label" android:name="com.stub.shell" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">

结果

加固成功,应用正常打开执行

参考

【1】 github加壳开源项目:https://github.com/kavmors/ApkSheller
【2】 CSDN加壳原理:https://blog.csdn.net/jiangwei0910410003/article/details/48415225
【3】 详细加壳步骤https://github.com/Herrrb/DexShell

关键词:[‘安全技术’, ‘移动安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now