关于android加固的简单实现------Application替换
来源:互联网 发布:盐枪 淘宝 编辑:程序博客网 时间:2024/06/06 18:45
最近大神同事带我一起做gradle加固,我当然满怀信心的想和他一起做,毕竟一直感觉这是个高大上的事,下面说说具体方案:
注:壳app==壳 加固app == app
- 我们准备了一个加固的壳,这个壳是用来做替换的,也就是说我们安装后打开的是壳,然后会在壳里面解析出真正的app。
- 自定义gradle插件,在编译的时候我们去捕获dex的生成的过程,在app生成dex的最后,我们先将app的dex剪切(没错就是剪切,可以理解为copy,删除)到asset目录下并进行加密,然后我们把壳的dex拷贝到app生成的dex目录下,这样壳的dex就成了我们app安装后会去寻找并执行的dex。
- 打开程序后会执行壳app的application,当然了壳dex里面也只有自定义的application一个类,用来替换成真实的application
- 然后用自定义classLoader将之前存放到asset下的app的dex(可能有多个)的path加到classLoader中,这样classloader就具有了加载真实app的dex的能力了,然后再把classloader设置到loadedApk中去,因为像我们context.getClassLoader()的获取方法最终都是到loadedApk中获取的,代码如下:
public static ClassLoader getClassLoader(ClassLoader loader, File dexDir, List<File> files) { StringBuilder sb = new StringBuilder(); for (File file : files) { Log.e("ggg", "ggg shell dexpath = " + file.getAbsolutePath()); sb.append(file.getAbsolutePath()) .append(File.pathSeparator); } DexClassLoader classLoader = new DexClassLoader(sb.toString(), baseContext.getDir("out", Context.MODE_PRIVATE).getAbsolutePath(), baseContext.getApplicationInfo().nativeLibraryDir, loader); return classLoader; }Reflect.on(loadedApk()).set("mClassLoader", classLoader);
壳和app的dex路径都在如下目录下可以找到
build\intermediates\transforms\dex\debug\folders\1000\1f\main\classes.dex
那么问题来了,这次先说application替换吧,前面说到app打开后运行的是壳中的application的生命周期,所以我们要做的当然是去运行app中application的生命周期啦,application中必要执行两个生命周期方法应该是attach和oncreate吧,所以我就想怎么样才能执行这两个方法呢,所以我们去看了ActivityThread中的handleBindApplication,了解一点framework的同学就会知道最后会在loadedApk执行makeApplication()方法,生成一个application并调用他的attach方法,然后我们就像我们能不能也调用这个方法去生成呢,事实证明是可以的,为了保证生命周期,于是我们在壳application中attach方法中执行如下代码
Object currentActivityThread = currentActivityThread();Object mBoundApplication = Reflect.on(currentActivityThread).get("mBoundApplication");Object loadedApk = Reflect.on(mBoundApplication).get("info");//把当前进程的mApplication 设置成了nullReflect.on(loadedApkInfo).set("mApplication", null);Object oldApplication = Reflect.on(currentActivityThread).get("mInitialApplication");//http://www.codeceo.com/article/android-context.htmlArrayList<Application> mAllApplications = Reflect.on(currentActivityThread).field("mAllApplications").get();mAllApplications.remove(oldApplication);//删除oldApplicationApplicationInfo loadedApk = Reflect.on(loadedApkInfo).get("mApplicationInfo");ApplicationInfo appBindData = Reflect.on(mBoundApplication).get("appInfo");loadedApk.className = appClassName;appBindData.className = appClassName;//执行 makeApplication(false,null)Application app = Reflect.on(loadedApkInfo).call("makeApplication", new Object[]{false, null}).get();
上面代码可以看到会把loadapk中application这个对象的className替换成真实app的application,然后设置进行替换
//把当前进程的mApplication 设置成了nullReflect.on(loadedApkInfo).set("mApplication", app); Reflect.on(currentActivityThread).set("mInitialApplication", app);
满心欢喜的去执行代码,安装好app后,你会发现获取到的application依然是壳application,这就很坑了,为了找出原因,于是去查看源码loadedApk,发现在makeAppliaction中执行会发生如下情况
try { java.lang.ClassLoader cl = getClassLoader(); if (!mPackageName.equals("android")) { initializeJavaContextClassLoader(); } ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { if (!mActivityThread.mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to instantiate application " + appClass + ": " + e.toString(), e); } } mActivityThread.mAllApplications.add(app); mApplication = app;
壳application中的attach方法是在mActivityThread.mInstrumentation.newApplication这个方法中走的,也就是说我们在执行完替换后,下面的方法还会继续走,那么上面代码最后的两行依然会走
mActivityThread.mAllApplications.add(app);mApplication = app;
这两行代码就会导致,mApplication依然被赋值成了壳的application,哇,真的是好坑,为了解决这个问题,我和同事讨论,我们想把替换工作的放到壳application的onCreate()方法中执行,但是makeApplication方法依然放在attach中执行:
Application app;@Overrideprotected void attachBaseContext(Context base) { super.attachBaseContext(base); ... ... app = Reflect.on(loadedApkInfo).call("makeApplication", new Object[]{false, null}).get();}@Overridepublic void onCreate() { super.onCreate(); Object currentActivityThread = currentActivityThread(); Object mBoundApplication = Reflect.on(currentActivityThread).get("mBoundApplication"); Object loadedApkInfo = Reflect.on(mBoundApplication).get("info"); Reflect.on(loadedApkInfo).set("mApplication", app); Reflect.on(currentActivityThread).set("mInitialApplication", app);}
这样试了一下,果然错误不出现了,但是又有了一个新坑,那就是provider的问题
if (!data.restrictedBackupMode) { List<ProviderInfo> providers = data.providers; if (providers != null) { installContentProviders(app, providers); // For process that contains content providers, we want to // ensure that the JIT is enabled "at some point". mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } }
这个是ActivityThread中的安装provider的源码,问题出就出在安装provider的时候loadedApk中的mApplication还没有被替换掉,用的还是壳application,继续在填坑的路上不断前进,经过多次的方案推翻又重来,我们最后的做法,是用app的application手动去调用installProvider方法,然后把data.providers置空,避免后来壳application再次调用这个方法,试了一下,终于可行。可能说的比较乱,就将有着看吧,后面再进行改进,把其他实现也贴出来,完成代码:
Application app;@Overrideprotected void attachBaseContext(Context base) { super.attachBaseContext(base); ... ... app = Reflect.on(loadedApkInfo).call("makeApplication", new Object[]{false, null}).get(); List<ProviderInfo> providers = Reflect.on(getBoundApplication()).field("providers").get();Log.e("ggg shell", "ggg providers = " + providers);if (providers != null) { Reflect.on(currentActivityThread).call("installContentProviders", app, providers); providers.clear(); }}@Overridepublic void onCreate() { super.onCreate(); Object currentActivityThread = currentActivityThread(); Object mBoundApplication = Reflect.on(currentActivityThread).get("mBoundApplication"); Object loadedApkInfo = Reflect.on(mBoundApplication).get("info"); Reflect.on(loadedApkInfo).set("mApplication", app); Reflect.on(currentActivityThread).set("mInitialApplication", app);}
最后感谢 https://github.com/godlikewangjun/dexknife-wj.git 的分享让我们有了入坑的资本。
- 关于android加固的简单实现------Application替换
- Android so加固的简单脱壳
- 关于Android APP的打包和加固
- Android So简单加固
- Android So简单加固
- Android中的Apk的加固实现原理
- 关于android的Application
- Android Apk加固的初步实现思路(dex整体加固)
- 简单的几行小代码,实现Android程序布局替换。
- 简单高效的实现Android App全局字体替换
- 简单高效的实现Android App全局字体替换
- 简单高效的实现Android App全局字体替换
- 阿里早期Android加固代码的实现分析
- 关于Android的application类
- 关于android application的讲解
- android程序的加固解析
- 关于Android Application & Android Framework 的学习计划
- PHP:简单实现ASP的Application对象
- Python-Redis操作
- android调试出现:Installation failed with message Failed to establish session
- 51Nod 1003 阶乘后面0的数量(后置0的个数)
- opensuse42.2和centos7设置开机进入命令行界面和切换命令行界面和图形界面
- CSS3新样式
- 关于android加固的简单实现------Application替换
- springBoot学习之常用注解 --1
- fiddler更新后证书导出和报错的坑
- C语言中的 static变量、static函数
- 三.Spring中IOC与DI的的区别
- String、StringBuffer、StringBuilder的区别
- radio或者checkbox
- 防止SQL注入的若干笔记
- 访问修饰符