1.app加固需要3个对象:

1)需要加密的源apk

2)壳程序apk(负责解密源apk)

3)加密工具(将源apk进行加密;和壳Dex合成新的Dex)

2.app加固需要用到的知识点:

1)对dex文件格式的理解和熟悉

2)熟悉apk打包流程

3)熟悉反编译的流程

4)对android虚拟机底层有一定的理解和apk在android平台上的加载机制

以下是参考大神博客,并且已经成功运行:

首先看源apk:ForceApkObj

public class MyApplication extends Application{@Overridepublic void onCreate() {super.onCreate();Log.i("log", "source apk onCreate:"+this);}
}
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);TextView content = new TextView(this);content.setText("I am Source Apk");content.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View arg0) {Intent intent = new Intent(MainActivity.this, SubActivity.class);startActivity(intent);}});setContentView(content);Log.i("log", "app:"+getApplicationContext());}
}
public class SubActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);TextView content = new TextView(this);content.setText("I am Source Apk SubMainActivity");setContentView(content);Log.i("log", "app:"+getApplicationContext());}
}

然后再看壳程序apk:ReforceApk

<applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:name="com.example.reforceapk.ProxyApplication"><meta-data android:name="APPLICATION_CLASS_NAME" android:value="com.example.forceapkobj.MyApplication"/><activityandroid:name="com.example.forceapkobj.MainActivity"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activityandroid:name="com.example.forceapkobj.SubActivity"></activity></application>
public class ProxyApplication extends Application {private static final String appkey = "APPLICATION_CLASS_NAME";private String apkFileName;private String odexPath;private String libPath;//这是context 赋值@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);try {//创建两个文件夹payload_odex,payload_lib 私有的,可写的文件目录File odex = this.getDir("payload_odex", MODE_PRIVATE);File libs = this.getDir("payload_lib", MODE_PRIVATE);odexPath = odex.getAbsolutePath();libPath = libs.getAbsolutePath();apkFileName = odex.getAbsolutePath() + "/payload.apk";File dexFile = new File(apkFileName);Log.i("demo", "apk size:" + dexFile.length());if (!dexFile.exists()) {dexFile.createNewFile();  //在payload_odex文件夹内,创建payload.apk// 读取程序classes.dex文件byte[] dexdata = this.readDexFileFromApk();// 分离出解壳后的apk文件已用于动态加载this.splitPayLoadFromDex(dexdata);}// 配置动态加载环境Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread",new Class[]{}, new Object[]{});//获取主线程对象 String packageName = this.getPackageName();//当前apk的包名//下面两句不是太理解ArrayMap mPackages = (ArrayMap) RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mPackages");WeakReference wr = (WeakReference) mPackages.get(packageName);//创建被加壳apk的DexClassLoader对象  加载apk内的类和本地代码(c/c++代码)DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,libPath, (ClassLoader) RefInvoke.getFieldOjbect("android.app.LoadedApk", wr.get(), "mClassLoader"));//base.getClassLoader(); 是不是就等同于 (ClassLoader) RefInvoke.getFieldOjbect()? 有空验证下//?//把当前进程的DexClassLoader 设置成了被加壳apk的DexClassLoader  ----有点c++中进程环境的意思~~RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader",wr.get(), dLoader);Log.i("demo", "classloader:" + dLoader);try {Object actObj = dLoader.loadClass("com.example.forceapkobj.MainActivity");Log.i("demo", "actObj:" + actObj);} catch (Exception e) {Log.i("demo", "activity:" + Log.getStackTraceString(e));}} catch (Exception e) {Log.i("demo", "error:" + Log.getStackTraceString(e));e.printStackTrace();}Log.i("log", "attachBaseContext");}@Overridepublic void onCreate() {{//loadResources(apkFileName);Log.i("demo", "onCreate");// 如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。String appClassName = null;try {//组件元素可以包含任意数量的<meta-data>子元素。系统将meta-data配置的数据存储于一个Bundle对象中,可以通过PackageItemInfo.metaData字段获取。//ApplicationInfo是PackageItemInfo的字类ApplicationInfo ai = this.getPackageManager().getApplicationInfo(this.getPackageName(),PackageManager.GET_META_DATA);Bundle bundle = ai.metaData;if (bundle != null && bundle.containsKey("APPLICATION_CLASS_NAME")) {appClassName = bundle.getString("APPLICATION_CLASS_NAME");//className 是配置在xml文件中的。Log.i("demo", appClassName);} else {Log.i("demo", "have no application class name");return;}} catch (NameNotFoundException e) {Log.i("demo", "error:" + Log.getStackTraceString(e));e.printStackTrace();}//有值的话调用该ApplicaitonObject currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread",new Class[]{}, new Object[]{});Object mBoundApplication = RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mBoundApplication");Object loadedApkInfo = RefInvoke.getFieldOjbect("android.app.ActivityThread$AppBindData",mBoundApplication, "info");//把当前进程的mApplication 设置成了nullRefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication",loadedApkInfo, null);Object oldApplication = RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mInitialApplication");ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke.getFieldOjbect("android.app.ActivityThread",currentActivityThread, "mAllApplications");mAllApplications.remove(oldApplication);//删除oldApplicationApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke.getFieldOjbect("android.app.LoadedApk", loadedApkInfo,"mApplicationInfo");ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke.getFieldOjbect("android.app.ActivityThread$AppBindData",mBoundApplication, "appInfo");appinfo_In_LoadedApk.className = appClassName;appinfo_In_AppBindData.className = appClassName;Application app = (Application) RefInvoke.invokeMethod("android.app.LoadedApk", "makeApplication", loadedApkInfo,new Class[]{boolean.class, Instrumentation.class},new Object[]{false, null});//执行 makeApplication(false,null)RefInvoke.setFieldOjbect("android.app.ActivityThread","mInitialApplication", currentActivityThread, app);ArrayMap mProviderMap = (ArrayMap) RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mProviderMap");Iterator it = mProviderMap.values().iterator();while (it.hasNext()) {Object providerClientRecord = it.next();Object localProvider = RefInvoke.getFieldOjbect("android.app.ActivityThread$ProviderClientRecord",providerClientRecord, "mLocalProvider");RefInvoke.setFieldOjbect("android.content.ContentProvider","mContext", localProvider, app);}Log.i("demo", "app:" + app);app.onCreate();Log.i("log", "onCreate");}}private void splitPayLoadFromDex(byte[] apkdata) throws IOException {int ablen = apkdata.length;//取被加壳apk的长度   这里的长度取值,对应加壳时长度的赋值都可以做些简化byte[] dexlen = new byte[4];System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4);ByteArrayInputStream bais = new ByteArrayInputStream(dexlen);DataInputStream in = new DataInputStream(bais);int readInt = in.readInt();System.out.println(Integer.toHexString(readInt));byte[] newdex = new byte[readInt];//把被加壳apk内容拷贝到newdex中System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt);//这里应该加上对于apk的解密操作,若加壳是加密处理的话//?//对源程序Apk进行解密newdex = decrypt(newdex);//写入apk文件File file = new File(apkFileName);try {FileOutputStream localFileOutputStream = new FileOutputStream(file);localFileOutputStream.write(newdex);localFileOutputStream.close();} catch (IOException localIOException) {throw new RuntimeException(localIOException);}//分析被加壳的apk文件ZipInputStream localZipInputStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(file)));while (true) {ZipEntry localZipEntry = localZipInputStream.getNextEntry();//不了解这个是否也遍历子目录,看样子应该是遍历的if (localZipEntry == null) {localZipInputStream.close();break;}//取出被加壳apk用到的so文件,放到 libPath中(data/data/包名/payload_lib)String name = localZipEntry.getName();if (name.startsWith("lib/") && name.endsWith(".so")) {File storeFile = new File(libPath + "/"+ name.substring(name.lastIndexOf('/')));storeFile.createNewFile();FileOutputStream fos = new FileOutputStream(storeFile);byte[] arrayOfByte = new byte[1024];while (true) {int i = localZipInputStream.read(arrayOfByte);if (i == -1)break;fos.write(arrayOfByte, 0, i);}fos.flush();fos.close();}localZipInputStream.closeEntry();}localZipInputStream.close();}private byte[] readDexFileFromApk() throws IOException {ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream();ZipInputStream localZipInputStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(this.getApplicationInfo().sourceDir)));while (true) {ZipEntry localZipEntry = localZipInputStream.getNextEntry();if (localZipEntry == null) {localZipInputStream.close();break;}if (localZipEntry.getName().equals("classes.dex")) {byte[] arrayOfByte = new byte[1024];while (true) {int i = localZipInputStream.read(arrayOfByte);if (i == -1)break;dexByteArrayOutputStream.write(arrayOfByte, 0, i);}}localZipInputStream.closeEntry();}localZipInputStream.close();return dexByteArrayOutputStream.toByteArray();}// //直接返回数据,读者可以添加自己解密方法private byte[] decrypt(byte[] srcdata) {for (int i = 0; i < srcdata.length; i++) {srcdata[i] = (byte) (0xFF ^ srcdata[i]);}return srcdata;}//以下是加载资源protected AssetManager mAssetManager;//资源管理器protected Resources mResources;//资源protected Theme mTheme;//主题protected void loadResources(String dexPath) {try {AssetManager assetManager = AssetManager.class.newInstance();Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);addAssetPath.invoke(assetManager, dexPath);mAssetManager = assetManager;} catch (Exception e) {Log.i("inject", "loadResource error:" + Log.getStackTraceString(e));e.printStackTrace();}Resources superRes = super.getResources();superRes.getDisplayMetrics();superRes.getConfiguration();mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());mTheme = mResources.newTheme();mTheme.setTo(super.getTheme());}@Overridepublic AssetManager getAssets() {return mAssetManager == null ? super.getAssets() : mAssetManager;}@Overridepublic Resources getResources() {return mResources == null ? super.getResources() : mResources;}@Overridepublic Theme getTheme() {return mTheme == null ? super.getTheme() : mTheme;}
}

最后看dex加密工具:DexShellTools

加密之前需要两个东西:

1)源程序的apk

2)壳程序的Dex文件

最后得到的是一个经过简单加密的dex文件,如下:

public class mymain {/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubtry {File payloadSrcFile = new File("force/ForceApkObj.apk");   //需要加壳的程序System.out.println("apk size:"+payloadSrcFile.length());File unShellDexFile = new File("force/ForceApkObj.dex"); //解客dexbyte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));//以二进制形式读出apk,并进行加密处理//对源Apk进行加密操作byte[] unShellDexArray = readFileBytes(unShellDexFile);//以二进制形式读出dexint payloadLen = payloadArray.length;int unShellDexLen = unShellDexArray.length;int totalLen = payloadLen + unShellDexLen +4;//多出4字节是存放长度的。byte[] newdex = new byte[totalLen]; // 申请了新的长度//添加解壳代码//System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);//src - 源数组。//srcPos - 源数组中的起始位置。//dest - 目标数组。//destPos - 目标数据中的起始位置。//length - 要复制的数组元素的数量System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);//先拷贝dex内容//添加加密后的解壳数据System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen);//再在dex内容后面拷贝apk的内容//添加解壳数据长度System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4);//最后4为长度//修改DEX file size文件头fixFileSizeHeader(newdex);//修改DEX SHA1 文件头fixSHA1Header(newdex);//修改DEX CheckSum文件头fixCheckSumHeader(newdex);String str = "force/classes.dex";File file = new File(str);if (!file.exists()) {file.createNewFile();}FileOutputStream localFileOutputStream = new FileOutputStream(str);localFileOutputStream.write(newdex);localFileOutputStream.flush();localFileOutputStream.close();} catch (Exception e) {e.printStackTrace();}}//直接返回数据,读者可以添加自己加密方法private static byte[] encrpt(byte[] srcdata){for(int i = 0;i<srcdata.length;i++){srcdata[i] = (byte)(0xFF ^ srcdata[i]);}return srcdata;}/*** 修改dex头,CheckSum 校验码* @param dexBytes*/private static void fixCheckSumHeader(byte[] dexBytes) {Adler32 adler = new Adler32();adler.update(dexBytes, 12, dexBytes.length - 12);//从12到文件末尾计算校验码long value = adler.getValue();int va = (int) value;byte[] newcs = intToByte(va);//高位在前,低位在前掉个个byte[] recs = new byte[4];for (int i = 0; i < 4; i++) {recs[i] = newcs[newcs.length - 1 - i];System.out.println(Integer.toHexString(newcs[i]));}System.arraycopy(recs, 0, dexBytes, 8, 4);//效验码赋值(8-11)System.out.println(Long.toHexString(value));System.out.println();}/*** int 转byte[]* @param number* @return*/public static byte[] intToByte(int number) {byte[] b = new byte[4];for (int i = 3; i >= 0; i--) {b[i] = (byte) (number % 256);number >>= 8;}return b;}/*** 修改dex头 sha1值* @param dexBytes* @throws NoSuchAlgorithmException*/private static void fixSHA1Header(byte[] dexBytes)throws NoSuchAlgorithmException {MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(dexBytes, 32, dexBytes.length - 32);//从32为到结束计算sha--1byte[] newdt = md.digest();System.arraycopy(newdt, 0, dexBytes, 12, 20);//修改sha-1值(12-31)//输出sha-1值,可有可无String hexstr = "";for (int i = 0; i < newdt.length; i++) {hexstr += Integer.toString((newdt[i] & 0xff) + 0x100, 16).substring(1);}System.out.println(hexstr);}/*** 修改dex头 file_size值* @param dexBytes*/private static void fixFileSizeHeader(byte[] dexBytes) {//新文件长度byte[] newfs = intToByte(dexBytes.length);System.out.println(Integer.toHexString(dexBytes.length));byte[] refs = new byte[4];//高位在前,低位在前掉个个for (int i = 0; i < 4; i++) {refs[i] = newfs[newfs.length - 1 - i];System.out.println(Integer.toHexString(newfs[i]));}System.arraycopy(refs, 0, dexBytes, 32, 4);//修改(32-35)}/*** 以二进制读出文件内容* @param file* @return* @throws IOException*/private static byte[] readFileBytes(File file) throws IOException {byte[] arrayOfByte = new byte[1024];ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();FileInputStream fis = new FileInputStream(file);while (true) {int i = fis.read(arrayOfByte);if (i != -1) {localByteArrayOutputStream.write(arrayOfByte, 0, i);} else {return localByteArrayOutputStream.toByteArray();}}}
}

将得到的classes.dex文件替换掉壳程序中的classes.dex文件即可

android中的app加固相关推荐

  1. Android 中如何计算 App 的启动时间?

    (转载) 已知的两种方法貌似可以获取,但是感觉结果不准确:一种是,adb shell am start -w packagename/activity,这个可以得到两个值,ThisTime和Total ...

  2. Android 系统优化(35)---Android 中如何计算 App 的启动时间?

    Android 中如何计算 App 的启动时间? 1 应用启动场景 事实上 Android 中一个 App 的启动时间可以准确计算的.但是要分场景.也就是说要分开游戏和应用. 大家都知道,在Andro ...

  3. Android安全性优化——APP加固

    现今移动应用市场火爆,APP数量呈爆发式增长,随着5G的逐渐广泛应用,APP增长趋势不断.正因APP的泛滥,网络攻击者的目标也在逐渐转移,数亿的移动互联网用户暴露在病毒攻击的范围之内,也使得手机APP ...

  4. Android中如何APP视屏如何去除广告

    生死看淡,不服就干! http://www.wjdiankong.cn 目录视图 摘要视图 订阅 微信小程序实战项目--点餐系统 程序员11月书讯,评论得书啦 Get IT技能知识库,50个领域一键直 ...

  5. Android中实现APP文本内容的分享发送与接收方法简述

    谨记(指定选择器Intent.createChooser()) 开始今天的内容前,先闲聊一下: (1)突然有一天头脑风暴,对很多问题有了新的看法和见解,迫不及待的想要分享给大家,文档已经写好了,我需要 ...

  6. Android中一个app启动另一个app|从浏览器打开app

    文章目录 一.应用A中点击按钮,跳转到应用B 二.应用A中点击按钮,跳转到应用B中的指定Activity--(scheme方式) 1.应用A中,点击按钮 2.应用B中,AndroidManifest. ...

  7. android中的so加固,so加固-加密特定section中的内容

    Android逆向之旅-基于对so中的section加密技术实现so加固 这篇文章写得真心好,建议先阅读一下原著,这里只是自己的实践过程(纸上得来终觉浅,绝知此事要躬行),和一些更细节的解释罢了. 一 ...

  8. Android中一个APP启动另一个APP并传递参数

    被调用(启动)的APP: 项目名字:Demo_ybs 项目包名:com.ybs.demo_ybs 被调用APP中获取调用者的传递数据: package com.ybs.demo_ybs;import ...

  9. Android 中监控APP「进入后台」「进入前台」

    利用ActivityLifecycleCallbacks监听所有activity的生命周期 解释下registerActivityLifecycleCallbacks这个方法,只要app中有一个act ...

最新文章

  1. xftp连接海康摄像头报错:sftp子系统申请已拒绝 请确保ssh连接的sftp子系统设置有效
  2. 微信小程序~自定义属性设置和获取(data-)
  3. Study Linux --- Shell Script
  4. 采购订单接收备注为必输项
  5. WSAGetLastError
  6. Ambari删除服务报错之CSRF protection is turned on
  7. golang rpc单参数调用实例
  8. linux----LAMP之编译安装apache
  9. 宽带和流量是分开的吗_宽带
  10. react-native 学习
  11. c++ GUI轻量工具包FLTK介绍 (1)
  12. 多媒体计算机网络机房方案,学校多媒体教室及计算机机房方案1.doc
  13. JS 阻止浮层弹窗下滚动
  14. html怎么填充单元格颜色,PPT表格单元格怎么填充颜色 PPT填充表格单元格颜色的详细教程...
  15. 网络编程day1-本地信息的获取
  16. PX4自主设置飞行模式
  17. 1-8代酷睿全部中招,英特尔处理器再曝漏洞
  18. 计算机网络的发展过程大致可以分为几个,计算机网络发展过程分几个阶段
  19. 利用canvas开发一个绘图板
  20. html页面计算圆的周长和面积,计算圆的周长和面积(VB)

热门文章

  1. 【为什么是一堆线头?理论指导实践】浴霸电路接线安装走线详解
  2. 阻容感基础03:电阻器分类(2)-膜式电阻器
  3. 重磅!中国工程院撤销李宁院士称号
  4. springboot+vue网上家电数码商城vue家用电器购物商城系统多商家
  5. DevOps学习总结
  6. oracle报01031错误,Oracle数据库shutdown报ORA-01031: insufficient privileg
  7. 从胡紫薇“闹场”事件看中国妇女的社会地位
  8. java计算机毕业设计敬老福利院管理源码+mysql数据库+系统+lw文档+部署
  9. 【爬虫实践】记一次Scrapy框架入门使用爬取豆瓣电影数据
  10. Yii2 模块化开发 配置