利用加密算法将原始APK文件加密保护起来,然后将加密后的数据附加到壳尾部或其他地方,当运行起来的时候,壳会把动态解密加载起来。
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文件手动上传到/data/data/<packagename>/app-libs目录下等待壳App进行加载</packagename>
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
转自先社区
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.