文章目录

  • APK加固介绍
    • 使用proguard对apk中的源码进行混淆
      • proguard原理
    • 对APK反编译之后的smali进行混淆
      • 代码乱序原理
      • 乱序实例-Hello
    • 对APK中的字符串进行加密
      • 解决方案1-StringBuilder拼接
      • 解决方案2-编码混淆
      • 解决方案3-加密处理
    • 小结
    • 对APK中的文件进行校验
      • 对APK中的Dex文件进行校验
      • 对APK中的APK进行校验
      • 对APK中的签名进行校验
      • 小结

APK加固介绍

Android应用程序使用的开发语言是JAVA,由于采用的是这种解释型语言,所以代码可以被反编译。如果没有经过混淆或是加密,会非常容易让人分析。

为了使我们的APK更好的保护起来,Android的开发以及安全人员对APK进行了一系列的加固措施。从开发者而言,以下是常用的手段和方式:

  1. 使用proguard对apk中的源码进行混淆
  2. 对APK反编译之后的smali进行混淆
  3. 对APK中的字符串进行加密
  4. 对APK中的文件进行校验

使用proguard对apk中的源码进行混淆

Android Studio默认已经有proguard的支持,在项目目录app下的build.gradle中有配置信息,只需要将false改成true即可

默认只有release的配置,而工程默认是debug的,所以想要debug添加代码混淆,需要在release后面添加debug模式

编译以后可以看到混淆的效果

上面都是系统的一些类库,被混淆成了无意义的字符

反编译后的源码可以看到对类名和系统库进行了一定程度的混淆。

Proguard的混淆结果会输出到<module-name>/build/outputs/mapping/release/目录或是``/build/outputs/mapping/debug/`

一共有四个输出文件

  1. dump.txt:描述APK中所有类文件的内部结构
  2. mapping.txt:提供混淆前后的类名,方法名和成员变量名的对应关系
  3. seeds.txt:列出没有被混淆的类和成员
  4. usage.txt:列出从APK中移出的代码

proguard原理

proguard包括四个功能,shrinker(压缩),optimizer(优化),obfuscator(混淆),preverifier(预校验)

  • shrinker(压缩):检测并移除没有用到的类,变量,方法和属性
  • optimizer(优化):优化代码,非入口节点类会加上private/static/final,没有用到的参数会被删除,一些方法可能会变成内联代码
  • obfuscator(混淆):使用短又没有语义的名字重命名非入口类的类名,变量名和方法名。入口类的类名保持不变
  • preverify(预校验):预校验代码是否符合Javal.6或者更高的规范

一般用户可以选择默认的设置即可,如果想要自定义可以在proguard-rules.pro文件中进行配置 。

对APK反编译之后的smali进行混淆

源码经过混淆之后,在一个产品级的APK中对APK的保护是有一定帮助的,而对于一些有经验的破解者,他们还是会找到比较关键的地方,一般这种方式都是反编译之后进行分析的,那么如果我们效仿Windows下的保护软件,对smali代码进行混淆,某种程度上就加大了破解者的分析难度。比较方便的混淆方式就是代码乱序

代码乱序原理

我们在分析smali代码时,一般会借助于反编译工具反编译成java代码。代码乱序的目的就是想要将smali代码乱序后,使反编译的效果大打折扣,这样就会挡住很多菜鸟,至少可以减慢分析速度,增加破解难度。

乱序的基本原理如上图所示,将指令重新布局,并给每块指令赋予一个 label,在函数开头处使用 goto 跳到原先的第一条指令处,然后第一条指令处理完,再跳到 第二条指令,以此类推。

乱序实例-Hello

我们先从最简单的Hello开始

public class Hello{public static void main(String[] argc){String a="1";String b="2";String c=a+b;System.out.println(c);}
}

首先用Android Studio编译成APK,然后放到Android Killer中反编译

接着来修改smali代码

我们可以大致把整个代码分成三部分:

  1. 定义变量
  2. 字符串拼接
  3. 打印字符串

根据这三部分,我们可以根据上面的原理对代码中的三部分进行乱序,加上标签跟跳转

乱序后的代码如下

复制一份smali副本

将没有用的smali代码全部删除,只保留Hello.smali

先用smali.jar将整个文件夹转成dex文件,接着用dex2jar将dex文件转成jar文件

此时再来查看jd-gui,可以看到这个工具的反编译效果大打折扣

但是对jadx实际上是无效的,毕竟这个工具的实在是太强大了 。

同时proguard一个东家的dexguard,最初就采取类似的方法对代码进行保护,当然除了这种保护方式外,还有其他的,比如最常见的就是字符串加密。

对APK中的字符串进行加密

在开发过程中字符串的使用是不可避免的,但是这些字符串极可能是破解的关键点,比如服务器的地址和错误提示这些敏感的字符串信息。如果这些字符串采用硬编码的方式很容易通过静态分析获取

在java中定义一个字符串

public String normalString(){String str="Hello wordl";return str;
}

反编译的smali代码

.method public normalString()Ljava/lang/String;const-string v0, "Hello wordl".local v0, "str":Ljava/lang/String;return-object v0
.end method

可以看出const-string关键字后面就是定义的字符串值,甚至可以使用自动化分析工具批量提取出来

解决方案1-StringBuilder拼接

StringBuilder类通过append方法来构造需要的字符串,这种方式可以增加自动化分析的难度,如果要获取完整的字符串就必须进行相应的语法解析了

Java中拼接字符串代码

public String buildString(){StringBuilder builder=new StringBuilder();builder.append("Hello");builder.append(" ");builder.append("wordl");return builder.toString();
}

反编译的smali代码

.method public buildString()Ljava/lang/String;.locals 2.line 18new-instance v0, Ljava/lang/StringBuilder;invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V.line 19.local v0, "builder":Ljava/lang/StringBuilder;const-string v1, "Hello"invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;.line 20const-string v1, " "invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;.line 21const-string v1, "wordl"invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;.line 22invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;move-result-object v1return-object v1
.end method

可以看出反编译后的smali代码对破解增加了一定的难度,并不能一眼就识别出来

解决方案2-编码混淆

编码混淆是在硬编码的时候将字符串转换成16进制的数组或者Unicode编码,在使用的时候转回字符串。这种方式在反编译smali代码比StringBuilder方式更难直接识别

Java代码

public String encodingString() {byte[] strBytes = {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6f, 0x72, 0x6C, 0x64};String str = new String(strBytes);return str;
}

反编译的smali代码

.method public encodingString()Ljava/lang/String;.locals 2.line 26const/16 v0, 0xbnew-array v0, v0, [Bfill-array-data v0, :array_0.line 27.local v0, "strBytes":[Bnew-instance v1, Ljava/lang/String;invoke-direct {v1, v0}, Ljava/lang/String;-><init>([B)V.line 28.local v1, "str":Ljava/lang/String;return-object v1nop:array_0.array-data 10x48t0x65t0x6ct0x6ct0x6ft0x20t0x77t0x6ft0x72t0x6ct0x64t.end array-data
.end method

解决方案3-加密处理

加密处理是先将字符串在本地进行加密处理,后将密文硬编码进去,运行时再进行解密。加密步骤:

  1. 字符串加密
  2. 硬编码进程序
  3. 编译运行
  4. 解密密文

当然因为Java代码相对来说比较容易反编译,并且该方式需要将解密方法放在APK本地,所以我们可以将解密方法通过JNI实现,加大反编译难度

小结

任何一种加固方式都只是加大了破解的难度,并不能完全避免Android程序被破解。

对APK中的文件进行校验

在APK中包括代码和资源以及签名文件,我们一般会对可执行文件进行校验,还有证书的签名进行校验,以及apk本身的校验。

对APK中的Dex文件进行校验

classes.dex是Android虚拟机的可执行文件,我们所写的JAVA代码其实都在这里,很多应用程序的篡改都是针对classes.dex的。

代码比较简单,这里是通过计算好的CRC保存在string.xml里。本例可以直接运行代码校验,在监视器中查看到CRC的值,然后修改string.xml中对应的值即可。

校验代码:


private void VarifyDex(){//获取string.xml的valueLong dexCrc=Long.parseLong(this.getString(R.string.crc_value));String apkPath=this.getPackageCodePath();try {ZipFile zipFile=new ZipFile(apkPath);ZipEntry dexEntry=zipFile.getEntry("classes.dex");//计算class.dex的CRC值long dexEntryCrc=dexEntry.getCrc();//对比if (dexCrc==dexEntryCrc){Log.d("Dex","dex not been modify");}else{Log.d("Dex","dex has been modify");}}catch (IOException e){e.printStackTrace();} }

对APK中的APK进行校验

与Dex校验不同,APK校验必须把计算好的哈希值放在网络服务端,因为对APK的任何改动都会影响到最后的哈希值。

校验代码:

 private void VarifyApk() {//获取data/app/***/base.apkString apkPath=getPackageResourcePath();Log.d("GuiShou","apkPath:"+apkPath);MessageDigest msgDigest;try {//获取apk并计算MD5值msgDigest=MessageDigest.getInstance("MD5");byte[] bytes=new byte[4096];int count;FileInputStream fis;fis=new FileInputStream(new File(apkPath));while ((count=fis.read(bytes))>0){msgDigest.update(bytes,0,count);}//计算出md5值BigInteger bigInteger=new BigInteger(1,msgDigest.digest());String md5=bigInteger.toString(16);fis.close();Log.d("GuiShou","md5:"+md5);}catch (IOException e){e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}//获取服务端的md5进行对比.....}

对APK中的签名进行校验

每个APK都会经过开发者独有的证书进行签名,如果破解者对APK进行二次打包一般会用自己的签名证书进行打包。这时我们就可以通过校验签名证书的MD5值进行校验

校验代码

public void VerifySignatrue(){String packageName=this.getPackageName();PackageManager pm=this.getPackageManager();PackageInfo pi;String md5="";try {pi=pm.getPackageInfo(packageName,PackageManager.GET_SIGNATURES);Signature[] s=pi.signatures;//计算出MD5值MessageDigest messageDigest=MessageDigest.getInstance("MD5");messageDigest.reset();messageDigest.update(s[0].toByteArray());BigInteger bigInteger=new BigInteger(1,messageDigest.digest());md5=bigInteger.toString();} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}//---获取服务端的签名证书进行对比---}

获取签名证书md5可以解压APK,找到META-INF文件夹中的CERT.RSA,通过keytool工具就可以看到其中的md5等属性

小结

当然上述的保护方式容易被暴力破解,完整性检查最终还是通过返回true/false来控制后续代码逻辑的走向。如果攻击者直接修改代码逻辑,使完整性检查始终返回true,那这种方法就无效了,所以类似文件完整性的校验需要配合一些其他的方法,或者更为巧妙的方法实现。

017 Android加固之APK混淆和保护方式相关推荐

  1. 019 Android加固之APK加固的原理和实现

    文章目录 前言 加载Activity遇到的问题 APK的启动过程 替换ClassLoader流程 获取ActivityThread类对象 获取AppBindData类对象mBoundApplicati ...

  2. Android安全专项-Apk加固

    参考文章 Android APK加壳技术方案[1] Android APK加壳技术方案[2] Android中的Apk的加固(加壳)原理解析和实现 0x00 原理部分我不献丑了,上面3篇文章说的很清楚 ...

  3. android加固是什么意思,apk加固(乐固)

    最近项目要上市场了,虽然已经给apk加了混淆,但是听同事说,还有加固工具可以更好的保护apk.特地去用了一下apk加固. 目前加固工具挺多的,我听到的有腾讯的乐固和360安全加固. 这次选腾讯的乐固. ...

  4. Android加固调研

    简介               基本概念                      APK结构                      Dex结构                      APK ...

  5. eclipse java混淆打包,Android Studio和eclipse混淆打包总结

    最近项目有点闲,考虑到以前的项目没有做过混淆,只是用了加固软件进行加固,为了安全性,准备给项目加上,这里做个总结,都经本人亲自在项目实践,说是为了安全性,这好像说大了,一来项目中没用到什么特别的技术, ...

  6. android生成apk流程,Android Studio生成APK的基本流程

    Android Studio生成APK的基本流程 2020年08月14日 | 萬仟网移动技术 | 我要评论 Android Studio生成APK流程修改版本号和文件名安全加固(可选)生成签名apk修 ...

  7. android加固多渠道,Android 多渠道打包(使用友盟统计,结合360加固宝进行多渠道打包)...

    如有不正确的地方,望各路小哥哥.小姐姐指点,小女子在此谢过(*^__^*) 嘻嘻-- 一般app做推广的时候都需要渠道包,方便分析数据,查看渠道质量 一.先去友盟平台申请一个appkey a.   a ...

  8. android应用程序的混淆打包(转)

    android应用程序的混淆打包 1 . 在工程文件project.properties中加入下proguard.config=proguard.cfg , 如下所示: target=android- ...

  9. [转]Android 项目的代码混淆,Android proguard 使用说明

    简介 Java代码是非常容易反编译的.为了很好的保护Java源代码,我们往往会对编译好的class文件进行混淆处理. ProGuard是一个混淆代码的开源项目.它的主要作用就是混淆,当然它还能对字节码 ...

最新文章

  1. nyoj n-1位数
  2. CHIL-SQL-PRIMARY KEY 约束
  3. Spring事务管理TransactionManager
  4. MySQL读写分离介绍及搭建
  5. 执行Oracle中的sqlldr xxx.ctl命令导入数据时,当错误无法导入时,注意查看「xxx.log信息」
  6. SharePoint Foundation 2013安装-1:先决条件准备
  7. endnote正版软件多少钱_销售endnote软件到底价格可以购买 保证正版软件
  8. deep learning in NLP
  9. mongoose populate 填充
  10. jquery动态加载图片数据
  11. android 左测设置功能,iqoo手机左边按键有什么作用?iqoo手机左侧按键介绍
  12. 【归并排序】基础代码
  13. Stack Overflow 食用指南
  14. 动态规划-剪绳子问题
  15. 联合国 ITU 立项成功,DevOps 标准开启国际化模式!
  16. 【趣味AI】手把手教你用AI画画
  17. 欺诈、舞弊检测中人与人工智能的分工,共同节省数十亿美元
  18. java课程设计连连看_连连看_java课程设计报告
  19. 【JavaEE】社区版IDEA(2021.X版本及之前)创建SpringBoot项目
  20. 《我爱背单词》 Alpha版 发布说明

热门文章

  1. Py之huaweicloud-sdk-python:huaweicloud-sdk-python的简介、安装、使用方法之详细攻略
  2. 成功解决ImportError: cannot import name 'pywrap_tensorflow'
  3. 人工智能的时代?未来已至?
  4. 蓝桥杯_算法训练_关联矩阵
  5. array_map常用技巧
  6. js中的arguments 参数
  7. 关于Python课程
  8. 【bzoj5197】[CERC2017]Gambling Guide 期望dp+堆优化Dijkstra
  9. js格式化文件大小, 输出成带单位的字符串工具
  10. jdk7 for Mac