移动安全---应用安全技术发展:如何进行安全对抗、挖掘安全技术
前言:首先声明,我是主做android安全的,对别的行业也有涉略,我想将一些问题简单化,然后找出解决他的方法,这是一篇我目前做安全中遇到的大多数问题,各个平台都通用,我整理了一下,我并没有直接给出某个问题的答案,但是我觉得我写了问题解决的思路。 应用安全核心宗旨应用安全甚至是软件含义行业已经很多年了,各个平台,各种工具,各种技术都不断的迭代,其实主要目的在防逆向。但是逆向从理论上来说其实是一个100%的问题,任何一个软件只要有足够的时间和精力都会被破解,在这种前提之下,安全的核心宗旨其实就是提高逆向成本,但是同时安全又是为产品服务的,要兼顾产品的需求。所以软件安全的核心宗旨就是在满足产品需求的情况下,尽可能的提高逆向成本。 应用安全技术发展一个平台应用安全的发展,完全是逆向工程催生的,安全产品的出现,也说明逆向工程已成规模,在这个平台已经有了一套逆向的工具和方法论,可以完成平台系统逆向基础工作,专注于对应用本身进行逆向。 逆向工程的发展各平台逆向工具和方法方法android逆向 apktool 反编译apk文件,smail可以单独反编译dex,so文件通过ida分析, java调试可以使用android studio 的smail插件借助as调试模块进行调试,so调试使用ida,gdb,lldb都可以 linux 和win 逆向 ida、od、ghidra的工具对文件进行分析,同时可以动态调试 unity 逆向 这个我很久不搞了,不知道是否有新方案了,当时有c#反编译工具的,后来c#语言变成了il2cpp,调试的时候我都是用的本地调试工具,不清楚c#是否有专有调试工具 前端js 执行文件为明文,不需要反编译,调试需要借助浏览器调试工具
通过以上这些不同的平台的逆向方法,我们发现已经为我们提供了平台逆向相应的工具,我们只需要对应用进行逆向就可以了。 应用逆向方法和工具分析应用是什么 应用对我们来说可能就是一个文件安装到系统中运行,但是对于这个系统来说它是“插件”,我们可以把它叫做系统功能扩展插件。这个文件就是插入到操作系统中的,依赖于操作系统提供的接口,实现这个应用的内容所编写的功能。 为什么应用可以运行 因为应用经过了这个系统平台的特定的编译器,或者说编译集成平台的编译,编译器是一种跟系统约定俗成的转化器,可以将我们人类写的代码转变成应用,系统平台和编译器是一一对应的关系。 应用逆向方法和工具的本质 应用逆向就是一个和开发完全反向的过程,在开发中,我们是需要依赖于编译器来对我们写的代码进行转化的,所以逆向也是这样需要反编译工具将代码反向转化回去。而这种反编译工具,就是逆向的基础工具。开发程序一般都比较简单,这是因为很多平台都有集成式ide,直接能编译出来就可以,但是逆向往往工序复杂,这是因为逆向工具完成度不够,基本没有集成ide那样,逆向工具很多都是靠开源项目,而开发ide,是有大公司来做的,即使是后续工具完善了,也往往存在缺陷需要我们通过方法手工避雷。
应用逆向方法和工具的诞生、发展在做逆向的时候,我们很少去考虑逆向工具的原理,就像开发的时候,我们不用考虑编译器,IDE的原理一样,这属于完全抽离出来的透明技术层。但是逆向跟开发不同的是,开发的IDE工具是由官方、社区、公司这些比较大的组织提供的,比较完善,有bug修复速度往往也很快,而且他们对于这些问题的解决方式和方法更加趋于正确,能够多方协调。逆向工具,往往都是由开源项目发起的,很多时候是某个技术大佬来做的,工具本身可能有bug,对于逆向问题的解决方式可能是片面的,更新不及时,做不到多技术融合的IDE平台等等很多问题。所以对于逆向,这些问题可能都需要靠我们自身的能力来解决。 应用文件格式解析 应用文件,是应用的载体,开发时,编译相关工具会将代码编译组合成某种格式,来存储应用相关信息,包括代码,变量、配置文件等等。andorid的apk文件,linux elf文件,windows pe文件,davilk dex文件,这些都有一定的格式,是需要进行格式解析的,前端js,php,这些脚本语言,文本格式,这些其实也是有格式的,但是不用解析的原因是系统自带了文本格式解析工具。文件格式解析是逆向工程静态分析的基础。apktool,其中包括了解析axml,dex等文件格式解析,readelf,解析elf文件格式,ida 支持解析多种文件格式。 代码还原(反编译、反汇编,还原成源码) 对于文件格式解析出来数据内容,进行解析还原成开发源码,比如反汇编,静态编译型语言,存储的都是字节码,这是需要先进行反编译,然后进行反汇编,比如c,c++ java 编译的程序,都是编译成字节码,通过字节码来运行,逆向的时候,需要先对字节码进行解析成汇编,然后通过汇编语言进行反汇编,还原成源码。反编译,这部分比较简单,对照手册和架构相关的文档说明,是可以完美还原的。反汇编,是专门针对一些有bytecode的中间代码,这类的应用,将这些bytecode或者中间代码,转变成开发代码,这种代码还原技术现在其实不算很成熟,高级语言,或者是经过编译器编译的代码进行还原效果还是不错的,但是经过人工编写,或者主动对抗的代码还原程度还是不高的。原因是编译器编译出来的代码符合一定的规则,不同工具编译出来的有可能规则不一样,像ida这种反编译工具,反编译的时候是可以选中编译器的,他应该是针对这些不同的编译器有不同的规则,或者说研究过不同编译编译出来的代码的特种,指定策略进行还原。 应用平台安全架构突破 为了保证应用的安全性,应用平台一般会有一些系统安全措施来保护应用不被攻击,而想要侵入应用进程,进行动态观测,读取修改内容,是需要绕过的,比如android ios签名问题,ios调试是需要修改配置的否则无法ptrace,android上java要么改系统rmo,要么改配置才能进行调试。还有比如selinux,mac这些安全机制,而且这种安全机制随着系统的更新,也会不断的更新的,需要不断兼容学习。 应用系统底层运行环境对接 一个应用的逆向,不只有文件分析部分,还需要应用动态执行的时候可以对他进行观察,而且,颗粒的越细越好。这方面,最好的就是调试系统,其次就是进程注入,调试系统很多平台会提供api,或者工具,也可以使用系统提供的api自己写,效果好,容易被检测和对抗。进程注入方法多种多样,因平台而已,不容易被检测,但是颗粒度不够细,比如不能单步,只能读写。 应用运行以后,会通过加载系统将文件加载到内存中,有可能不改变内存分布,也有可能改变内存分布,在前面我们写道要对应用进程进行观测,所以我们需要了解应用加载到内存中的分布,解析和使用,ida,附加的进程以后,会解析加载的所有so,可以查看他们的符号,和函数,查看内存分开,这就是进行了解析。 模块、库管理系统,不算是基础分析所需要的,但是提供给这个功能,是可以加速分析的,你可以快速的知道你的应用所使用的有那些库,然后你可以去对照着寻找开源项目,或者系统库说明,加快分析速度。android native 的solist,java层的类加载器等等,我们可以通过某些特有api或者特有函数,去对接库管理系统。
运行系统架构体系 cpu代码执行,android davlik指令执行,在我们分析代码的时候都会遇到,这就要求对指令熟悉,同时还有架构体系,比如arm 三级流水线,在某些特殊指令的时候,如果不了解,代码往哪里跑都不知道,还有一些特殊指令,不是常规的指令,ida前几个版本的时候,arm 有it,没法单步,就是因为不支持这个指令。 模块、库管理系统 模块、库管理系统,不算是基础分析所需要的,但是提供给这个功能,是可以加速分析的,你可以快速的知道你的应用所使用的有那些库,然后你可以去对照着寻找开源项目,或者系统库说明,加快分析速度。android native 的solist,java层的类加载器等等,我们可以通过某些特有api或者特有函数,去对接库管理系统。
应用文件加载系统 应用运行以后,会通过加载系统将文件加载到内存中,有可能不改变内存分布,也有可能改变内存分布,在前面我们写道要对应用进程进行观测,所以我们需要了解应用加载到内存中的分布,解析和使用,ida,附加的进程以后,会解析加载的所有so,可以查看他们的符号,和函数,查看内存分开,这就是进行了解析。
应用动态运行观测 一个应用的逆向,不只有文件分析部分,还需要应用动态执行的时候可以对他进行观察,而且,颗粒的越细越好。这方面,最好的就是调试系统,其次就是进程注入,调试系统很多平台会提供api,或者工具,也可以使用系统提供的api自己写,效果好,容易被检测和对抗。进程注入方法多种多样,因平台而已,不容易被检测,但是颗粒度不够细,比如不能单步,只能读写。
应用语言底层运行环境对接 对接开发语言所运行的环境,而不是前面提到的运行系统的底层系统语言对应的环境。比如逆向so,目标语言系统对应的是汇编,而我们可以用c语言环境对接,使用c中的变量,函数,这个可能看不出有多大改变,那就更进一步,用c++对接,有什么区别那,比如,c++使用的对象,我们hook或者某种方式,获取到这个c++的对象的地址,然后导出它对应函数成员的函数符号,用这个函数符号,和c++对象,就可以完成c++中的调用某个对象的成员函数。再举个例子unity游戏,经过保护以后,unity游戏全部变成了il2cpp,他的代码全部变成了native,但是我想在想用il2cpp编一个文件注入进去,然后调用它,这是可能的吗,说实话我目前没试过,但是这在理论上是可能的,只不过需要极为严格的环境对接,有个项目叫frida-il2cpp-bridge,他通过frida将unity的对象,类,这些从内存中解析从来,然后就可以直接通过frida调用它,这个也是个经典的例子。
应用安全保护 应用加固加固出现的主要原因,是因为有了静态应用文件分析工具,逆向分析已成规模,所以加固公司就可以宣扬应用文件的已经不安全。应用加固目前来看,主要有两个方面:文件格式加固,文件内容加固。 文件格式加固 针对于静态存储的文件,即使在运行以后这类文件也不会改变,只是加载到内存中解析,包括zip加密(zip打不开,需要密码),axml加密,但是这类技术往往不能够长时间奏效,主打时间差,利用逆向解析工具中的bug和对于文件解析中的漏洞来进行加固,阻止逆向程序工作。这类文件没必要保护文件内容,会被内存dump,因为文件内容是静态,dump一次即可,成本太高。 文件内容加固 当应用文件被逆向无法阻止的时候,于是就有了对文件的内容进行加固,就像http协议,通讯一定会被拦截,所有才有了https对于通讯内容加密。目前这种文件内容加固需要同时针对静态分析和动态dump,在静态分析的时候,让他无法找到正确的文件内容,但是动态运行以后这部分内容始终还是要解密出来的,要加载要运行,很多dump工具都是这个原理,所以对于动态运行的对抗方式就是在运行起来以后,自定义加载方式,不依赖与系统api,让dump无处下手,vmp,添加代码转化器,提供逆向难度,想要找到真正的代码,必须要先逆向vmp框架,代码混淆,即使找到了真正的文件内容,也难以理解代码的逻辑,无法使用。
运行对抗(防攻击、防侵入、防黑客工具)对于经过加固的应用,脱壳的主要方法就是运行时内存dump(静态还原也是可能的,不过难度太高,基本不用)。还有例如风控,设备指纹,用户画像等等,也是在运行时动态计算,而逆向工程是可以对于应用或者系统的安全进行突破的,所以安全公司针对这方面进行了防护。 特征检测 运行对抗主要是检测是否被攻击,无法进行防御,andriod上主动ptrace自己,兼容性差不说,对于从zygote注入的没有任何防御办法,pc也是一样,比如可以修改系统,等等方式,来完成攻击,所以运行对抗主要在检测上,而这种检测对抗,其实完全是一种特征对抗,比如ptrace的检测现在有很多种,这种我们可以说他是通用特征检测,像frida,xposed 这些,我们可以进行专用特征检测,比如so库名字,类名,符号,字符串,里面包含的库,只要开源了,基本都能被防御。 捕获能力 上面的特征检测,主要还是针对开源项目,和系统共用特征,但是有很多逆向工具并不是开源的,这种时候特征检测失效,是没办法进行安全处理的。这时候就需要进行捕获,首先是确定是否真的被攻击了,然后通过查看内存分布,系统库等情况,找到逆向工具然后dump。这个主要还是需要通用特征的检测点位足够多,并且和专用特征的系统完全分开,最好洒下鱼饵,比如像web的蜜罐那种操作。 自杀能力 操作系统本身是拥有调试功能和故障反馈的,甚至还有内存转储。在我们检测到已经被攻击了以后,势必要阻止程序继续运行,有自杀退出进程的,有卡死的,有无法联网的,有风控对抗的,很多种,但是阻止进程运行的手段一点不能触发系统的反馈功能,如自杀退出进程,经常会有大量堆栈返回,直接暴力出代码位置,卡死的,单线程退出的,在调试的时候,极容易被定为到现场位置,把现场暂停掉,还有风控对砍,功能确实无法使用,但是客户端本身是可以被主动调用的,再次调用进行分析的,问题多多,需要慎重。
代码分析对抗不管做了多少的保护,最终程序还是有可能被脱壳,或者在运行时找到真正的代码所在,而安全对抗的手段就是对于代码进行混淆、修改,使其在不改变原功能的基础上,变得难以理解,难以调试,难以还原等等。 反编译对抗 将一段二进制反编译成汇编,叫做反编译,很简单。但是这个简单是有前提的,比如如何分辨数据和指令,如何确定代码地址(x86架构忘得差不多了,不透的架构有不同的反编译问题),ida能够做到反编译成功的原因是这些其实都是没有反编译对抗的。比如我自己写个汇编,我把指令当数据,代码的执行顺序我用rop这种漏洞利用代码片段的方式,即使你反编译出来,实际的执行结果也是不同的。所以反编译是建立在程序是符合一定的标准上,国外有个开源项目,可以将二进制程序转变成汇编文件输出,还可以编译汇编文件,也是建立在这个基础上。 反汇编对抗 将汇编函数还原成源代码,这个跟反编译的思路是一样的。还原的基础建立在一定规则和标准上,比如,用汇编写裸函数,不符合c函数调用规则,函数举例长,函数和函数中间插入一个别的函数,都有可能造成无法分析。目前见过有的公司写了一套全局跳转汇编,将函数的顺序调用变成了通过参数调用全局跳转表,然后调用目标函数,估计还是用llvm写的。 混淆 目前这种方案很流行用的较多,主要原因是llvm解决了大部分底层问题,可以更简单的开发一个混淆编译器。混淆在pc上的时候就有了,但是当时我学艺不精,不知道到底是手工搞得,还是批量插入的,没有深入分析过。目前很多混淆方案都是基于llvm,但是目前开源的这些混淆方式和我逆向中遇到的一些我认为都不太完善,他确实对于代码进行了改变,但是通过人工分析加代码优化是可以去除的,主要问题是,无法真正的融入源代码中。比如ollvm,虽然miasm早年公布的一种还原ollvm的方法不够通用,对于定制化的ollvm无法还原,但是通过符号执行+静态代码分析的一些技术是可以对这种混淆方式进行通用还原的。我不知道是否真的有完美的混淆技术,但是如果能真正的融入代码中,和代码有所交互,保护效果会更好。 vmp 网上说vmp也是混淆的一种。vmp在逆向中,好像就是不可逆向的代名词。遇到vmp,就可以知难而退了。目前我也有幸遇到几个vmp,运气很好,难度都不算太高,而且还原了。对于vmp的逆向,需要首先对他的框架进行逆向,如果这部分完不成,基本毫无产出,这也是难点所在,很多人都是倒在这一步了。当对于框架结构完全逆向出来以后,就需要对于指令进行逆向,知道那些指令替换翻译成了什么,然后就可以还原了,所以vmp保护问题其实全在框架上,不要以为vmp就安全,如果没有对于vmp框架进行保护会降低分析难度。 动态分析运行对抗(指令trace) 在进行程序分析的时候,动态运行然后通过环境,结果进行代码分析的效果更好。目前我所知道的,frida的指令trace,调试器的指令trace,unicorn(unidbg)的指令trace,还有一个是有个朋友告诉我的网上大神写的虚拟vm项目(外部人员不懂,也不会用),其中unidbg效果是最好的,但是需要解决环境问题,frida这个没怎么用,调试器这个如果不遇到对抗效果也很好但是非常慢。ida的trace,很容易崩溃,很不稳定。 单独写这个的原因是逆向分析中,这种动态运行分析还是占大头的,而且指令trace分析vmp,分析算法,效果非常好。甚至可以搞一个指令trace+数据分析的软件,专门来进行代码分析。 目前对于trace的对抗我只研究了调试trace对砍,unidbg和别的还需要尝试。这种对抗方式主要还是在指令上,有些指令是存在特殊问题的,比如调试的时候代码会执行不过去,trace直接卡死,或者使用批量读写指令对于当前运行的内存段进行copy在运行,这种情况下是可以检测到断点的。别的方法记不住了。
最后:写的不算太好,体谅吧。想写好,但是东西有些多,只能不断的简化,这也导致了逻辑出现问题,只能一点一点罗列了,我也知道我写的不全,在后续我会慢慢更新,如果你有新的补充,比如具体到某一个行业,游戏软件安全,金融软件安全,也可以补充。 我准备开一个公众号,后面还有有一些,这算是个一些理论。
|