App攻防

App攻防技术发展

  随Smali工具(将dex反编译为smali语言的工具)以及baksmali工具(将Smali语言编译为dex二进制文件的工具)出现,Android安全起步。
  Android中的代码是运行在Dalvik或Art虚拟机中的,与ARM汇编类似,Smali语言可以当作Android虚拟机的汇编语言。
  

代码混淆

  如使用Google自带的混淆器ProGuard。ProGuard的作用是更改类名、函数名、变量名。好处就是增加攻击者在破解软件时猜到相应类的实际作用的难度;压缩文件大小。
  在Android中启用ProGuard,只需修改项目根目录下的App/build.gradle文件,将其中的buildTypes层级中的minifyEnable对应的值从false更改为true即可,这样最终编译生成的release版本便会启用代码混淆。

还有一个“代码混淆”工具——DexGuard,功能更丰富,不过它是个收费商业软件。

动态加载

  将需要保护的代码单独编译成一个二进制文件,将其进行加密后保存在一个外部的二进制文件中。在外部程序运行的过程中,再将被保护的二进制文件进行解密并使用ClassLoader类加载器来动态加载和运行被保护的代码。
  Android中每一个Java类都是由ClassLoader类加载器进行加载和运行的。
  由于Android中使用Java编写的App很容易被破解,故将所有的核心代码使用NDK套件(在Android中,NDK实际上是一个工具集,可以让开发者使用C/C++语言实现应用的各个部分)进行开发,结果就是外部的Java代码最终只是充当二进制文件装载器的角色,实际的业务逻辑都被放置在了更难破解的so文件中。甚至有的基于“放置在客户端”不可靠的原则,为了App安全,将重要功能和数据放置到云端,在客户端尽量只进行结果的展示。
  在这些保护手段下,单纯依靠apktool和Jadx这类静态反编译工具对App进行静态分析
已无法满足破解者要求,这时动态分析成为了主流。
  动态分析是通过附加调试或者注入进程来进行的分析。有Android Studio或者Jeb对App的dex进行调试,还有IDA、GDB等Native调试器对so文件进行单步调试,以及Hook和trace,也是动态分析手段。
  基于动态分析,动态加载保护就成了最脆弱的保护方式。只要通过动态分析,不管是在加载后的函数中设置断点以便从内存中dump出来被保护的内容,还是直接搜索进程的内存空间以便根据特征找到真实的dex文件并dump下来,都能应对这种保护方式。另外针对其他静态保护方式,由于动态分析是基于进程所处的运行状态,因此相比于在静态分析时得到的无意义代码而在动态分析时包含的都是真实的数据信息,使得破解者在逆向分析过程中有了很多真实数据,从而削减代码保护的作用。针对动态分析的对抗手段发展。
  在动态分析过程中,首先将调试器附加上进程或者通过注入将指令代码和数据注入目标进程中,然后才能对目标进行调试与内存监控。做到这个,最基本的方法就是调用ptrace()函数对进程进行附加或者基于二次打包的方式对程序进行修改,而对抗分两种:运行时检测和实现阻止。
  调用ptrace()函数对进程进行附加,会存在相应的特征点,比如/proc//status文件中的TracePid变量在进程被附加后,会由0变为附加进程的pid。如果此时代码本身单开一个线程对这个文件的TracePid值进行循环检测,检测到异常时就会自动退出进程,就可以做到阻止进程被破解者调试。
  App逆向分析过程中最难绕过的保护手段就是App加固。其逻辑和上面的动态加载类似,用加固厂商的壳程序包裹真实的App,在真实动态运行时再通过壳程序执行释放出来的真正App。App加固按照不同阶段加固特性重新分为三个不同阶段。
  第一阶段被认为是DEX整体加固,这是App加固初期。核心原理是将DEX整体加密后动态加载,刚开始App整体加固是需要先解密文件并在解密完成后写入到另外一个文件中,在解密完毕后调用DexClassLoader或者其他类加载器来加载解密后的文件。后来由于文件操作过于明显,进一步发展出将加密的DEX在内存中解密并直接在内存中进行加载的加固技术,这一阶段的加固技术没有明显写文件操作但同样无法阻止动态分析。后来为了防止根据特征进行内存搜索的方式,还出现了加载后抹去DEX文件头的手段,但都无法阻止设置断点和Hook的动态分析手段。DEX整体加固的致命之处在于,代码数据总是完整地存储在一段内存中,一旦反注入、反调试等措施被破解,保护就会门户洞开。
  第二阶段习惯上称为代码抽取保护。这一阶段App加固的关键在于真正的代码数据并不与DEX的整体结构数据存储在一起,就算DEX被完整地从内存中dump(转储)出来,也无法看到真正的函数代码。这种加固的核心原理是利用私有函数,通过对自身进程的Hook来拦截函数被调用时的路径,在抽取的函数被真实调用前,将无意义的代码数据填充到对应的代码区中。
  第三阶段加壳保护,将所有的Java代码变成最终的Native层代码。VMP与Dex2C。VMP加固技术最早起源于PC的虚拟机加固,其核心逻辑是将所有的代码使用自定义的解释器执行。代码不依赖于系统本身,即使获得所有的函数内容,也貌合神离,不知所云。这时唯一的解决方案可能是逆向对应解释器,找到与系统解释器的映射关系。Dex2C技术则是通过编译原理相关知识将原本的Java代码转化为native层代码,因为native层的二进制编码相比Java字节码更不容易被逆向。但实际上,具有C/C++ni’xai’g’n层的二进制编码相比Java字节码更不容易被逆向。但实际上,具有C/C++逆向经验的逆向开发和分析人员还是可以完成的。