20165320 毕业设计 第六周总结

20165320 毕业设计 第六周总结

任务及完成情况

周一 周二 周三 周四 周五 周六 周天
Android系统安全机制学习
Dalvik指令的学习
静态恶意代码注入
动态调试Smail代码
Hook学习与实现
文献翻译
撰写总结博客
已完成
已完成
已完成
已完成
已完成
已完成
已完成

内容总结

一、Android系统安全机制

1、沙箱隔离机制
  • Android系统以Linux系统中的用户权限机制为基础,一个APP对应一个用户,Linux中的用户隔离转变成Android系统中的应用隔离。

  • Android用户在设备上安装时被赋予独特的用户标识(UID),同时也会为该应用程序下的所有文件与所有的操作都配置相同的权限,只有相同UID的应用程序(前提是签名相同)才能对这些文件资源进行操作,未经授权不能访问。同时,每一个Android应用程序运行时Zygote进程会为其单独生成一个Dalvik虚拟机实例,应用程序运行于各自的Dalvik虚拟机中,这就是Android中的沙箱隔离机制。

2、应用权限机制
  • Android系统中的沙箱机制能很好地将互不信任的应用隔离开,同时互相信任的应用程序可以通过设置相同的UID来实现资源共享。但是应用访问Android系统中的资源需要在AndroidManifest.xml文件中写入相应权限的声明,安装时用户根据需要决定是否授予应用权限,同时安装后也能随时根据需要更改应用权限。
3、应用程序签名
  • Android应用程序在打包发布之前,开发人员需要使用自己的私钥对APK文件进行签名,生成的签名信息保存在META-INF文件夹中。通过该签名,可以验证应用程序的完整性与真实性,如果是通过逆向技术重新打包生成的APK文件,签名信息一定会发生改变,所以可以利用这一点在应用程序中加入相应的检验逻辑。同时,应用程序想要完成升级更新操作时,只有签名信息相同时系统才会认定为同一应用程序。包含相同签名信息的不同应用程序可以通过设置相同的UID来达到资源共享的目的。
4、文件权限
  • Android底层是Linux内核层,其核心的文件权限机制也被Android继承。Android系统中的每一个文件或者目录都具有相应的访问权限,包括只读、只写和可执行三种。同时每个文件或目录都有其所属的用户或者用户组。其具体访问权限由三组权限描述符声明,分别对应文件所有者、同组用户、其它用户对于该文件或目录所具有的权限。在Android系统中,一个APP就相当于一个用户。

二、Dalvik虚拟机与Smail代码学习

1、Dalvik虚拟机
1.1 简介
  • 在Android 4.4之前的版本中,为了解决Android应用在移动设备上运行效率的问题,也可能是为了与JVM相区分以避免版权纠纷,谷歌为Android专门设计了Dalvik虚拟机用来运行APP。在Android 4.4版本之后,为了提升性能,引入了全新的ART(Android RunTime)虚拟机,给用户带来流畅的体验的同时,能够很好地兼容在Dalvik虚拟机中运行的Dex文件。
1.2 Dalvik虚拟机特点
  • 体积小,占用内存小。
  • 可执行文件格式为Dex,执行速度快。
  • 常量池采用32位索引值,寻址速度快。
  • 基于寄存器架构,同时拥有一套完整的指令系统。
  • 每一个Android应用程序运行时对应一个Dalvik虚拟机实例。
1.3 与Java虚拟机的区别
  • 运行的字节码不同:

    • Java程序通过经过相应的编译环节生成对应的.class文件,Java虚拟机再通过.class文件读取相应的Java字节码运行程序。

    • Dalvik虚拟机运行的Dex文件是通过.class文件打包生成的,核心环节就是Java字节码到Dalvik字节码的转换。

  • 可执行文件体积不同:

    • Java类文件中的方法如果被其它类引用,相应的方法签名会被复制到其他类文件当中,导致不同的类文件中存在大量相同的常量,造成信息冗余。

    • Android SDK中的dx工具负责将Java字节码转换成Dalvik字节码,它能够提取所有Java类文件中的常量池,消除其中冗余的信息,再重新组合生成一个新的共享常量池。

  • 架构不同:

    • Java虚拟机基于栈架构,在运行过程中会对栈进行频繁的数据读写操作,效率低。
    • Dalvik虚拟机基于寄存器架构,运行过程中数据的访问直接通过寄存器,效率高。
1.4 虚拟机执行流程
  • Android系统启动后,会首先启动init进程完成初始化工作,然后读取init.rc文件启动Zygote进程。

  • Zygote进程是Android系统的孵化进程,启动后会初始化Dalvik虚拟机、启动Server进程,等待执行命令。

  • 用户启动一个Android应用时,Server进程会通过Android系统中的Binder方式接收到一条执行请求,随后通过将执行请求通过Socket的方式发送给Zygote进程。

  • Zygote进程收到执行请求后,会创建出一个Dalvik虚拟机实例来执行应用程序,完成整个启动过程。

  • Dalvik虚拟机执行应用程序时,首先完成类的装载工作,通过一张全局哈希表来实现装载类的查询存储。

  • 类的装载工作完成之后,使用字节码验证器对所有代码进行验证,校验完成之后调用相关函数在全局哈希表中查找main方法类,并执行。

1.5 虚拟机执行方式
  • Dalvik虚拟机使用JIT方式,当程序运行时将Dex可执行文件中的字节码转换成机器码。主要包括两种方式:

    • method方式:以函数方法为单位进行编译。
    • trace方式:首先获取执行频繁的路径代码,减少编译过程中的内存消耗。目前Dalvik虚拟机默认的编译方式。
2、Smali语言
2.1 简介
  • Smali是Dalvik虚拟机的寄存器语言,个人理解它与汇编语言有异曲同工之妙,都是通过可执行程序的反汇编获得。Android应用逆向攻击最常见的方式就是通过APKTool反汇编APK文件,再修改Samli文件改变程序运行逻辑。
2.2 数据类型
  • Smail中分基本数据类型与引用类型,其具体表现形式如下:

  • 基本数据类型:

    语法
    含义
    v
    void,用于返回值类型
    Z
    boolean
    B
    byte
    S
    short
    C
    char
    I
    int
    L
    long
    F
    float
    D
    double
    L
    Java类
    [
    数组类型
  • L类型可以表示Java类型中的任何类,在Smail具体表现形式为 "Lpackage/name/ObjectName;" package/name表示类所在包名,ObjectName表示对象名。比如:Ljava/lang/String;相当于java.lang.String

  • [类型可以表示所有基本类型的数组。"[I"表示int型的一维数组,相当于Java中的int[],多个"["可以表示多维数组。

2.3 方法与字段
  • 在Smail中,方法以.method指令开始,以.end method指令结束,具体表现形式如下:

      .method Lpackage/name/ObjectName;->MethodName(III)Z;
      .locals
      .prologue
    .end method
    
    • Lpackage/name/ObjectName:方法所在类。
    • .locals:
    • MethodName:具体的方法名。
    • (III):方法参数。
    • Z:返回值类型。
      PS:以上两部分构成方法的签名
    • .prologue:方法开始处。
  • 字段与方法类似,只是将参数与返回值改成字段名与类型。

      .field Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;
    
2.4 类相关信息
  • 每一个Smail文件对应Java中的一个类,包括内部类。只是内部类的文件名形式为[外部类]$[内部类].smali ,格式如下:

      .class < 访问权限> [ 修饰关键字] < 类名>
      
    .super < 父类名>
      
    .source <源文件名>
    
2.5 常见指令

三、基于Android的逆向静态攻击

1、简介
  • 静态分析是指在不运行代码的情况下,采用词法分析,语法分析等各种技术手段对程序文件进行扫描,从而生成程序的反汇编代码,然后阅读反汇编代码来掌握程序功能的一种技术。

  • Android Java层静态分析的两种方法:

    • 一种方法是是阅读通过ApkTool等工具对APK文件进行反编译,这一步最重要的就是通过baksmali反编译dex生成smali文件,攻击者在有一定Smali代码阅读能力的基础上,可以得到许多关键信息。

    • 第二种方法就是使用Dex2jar生成反汇编的.jar文件,然后再通过jd-gui来阅读反汇编的Java代码。

  • 目前对Android应用的反汇编以及静态分析,主要使用集成化的工具,这里我使用的是AndroidKiller。

2、分析技巧
  • 再对Android应用进行逆向攻击时,如果直接反编译,一行一行地阅读反汇编代码,工作量非常大,效率低。为了节省时间,常见的攻击思路如下:

    • 首先通过ApkTool反汇编APK文件,获取AndroidManifet.xml文件,该文件相当于是Android应用的配置文件,包含了整个应用组件的信息,以及一些关键的属性值,比如应用申请的一些权限,应用程序的主入口,包名等。

    • 信息反馈:运行目标程序,从程序运行过程中的反馈信息获取突破口。通常Android应用程序中用到的字符串数据会被存储在string.xml文件中,调用的时候是通过id值进行访问,可以通过直接搜索字符串的id值或者字符串来定位。

    • 特征函数法:与信息反馈类似,Android SDK提供的API,可以为我们提供便利。比如应用程序中弹出的对话框,需要调用Android中的Toast.MakeText().Show()方法,我们可以直接在反汇编后的代码中搜索相应的API,可以找到对应调用的位置。

    • 顺序查看法:从应用启动开始,一行一行的代码分析,也是最耗时最原始的办法,需要攻击者具有非常好的编程基础。

3、攻击实现
  • 首先运行程序,寻找一个突破口。发现当注册失败时,会有对话框弹出。

  • 然后使用AndroidKiller反汇编攻击程序,进入AndroidManifet.xml文件中。找到对应的主函数入口。

  • 利用应用程序运行时弹出的提示信息“无效注册码”,在项目工程中查找相关内容。能够在String.xml文件中找到“注册成功”的字符信息,很明显这就是我们想要的结果。

  • 查找succeed对应的id值,并检索其调用位置。

  • 根据id值,定位到具体的smali文件中。

  • 使用Dex2jar工具将Smail文件反编译生成Jar文件,再使用jd-gui阅读Jar文件代码,找到关键的Toast函数调用,并进行初步分析:

  • 返回Smali文件中,修改核心的判断逻辑。

  • 重新编译运行:

4、逆向静态分析攻击总结
  • 核心步骤:

    • 运行源程序,找到关键信息输出。
    • 使用Apktool工具反编译.APK文件,得到整个应用项目反汇编后的smali代码。
    • 通过关键信息找到应用的关键代码,并定位到相应smali文件。
    • 通过Dex2jar将smali文件转化成.jar阅读反汇编代码,得到程序执行的核心逻辑。
    • 返回Smali文件修改程序,并重新编译打包。
  • 危害性:静态分析常常与Android应用程序的重打包相关联,攻击者可以通过插入恶意代码感染合法应用(破解版软件可能存在的安全风险),步骤如下:

    • 使用Apktool得到原始应用和恶意应用的smali代码。
    • 将恶意应用的smali文件添加到原始应用smali文件夹下。
    • 将恶意应用的所有配置修改到原始应用。
    • 将恶意应用所需要的权限添加至原始应用的AndroidManifest.xml文件中。
    • 如果有需要,声明诸如广播接收器、服务等组件。
    • 使用ApkTool工具重新打包。
    • 使用keytool和Jarsigner工具对新生成的APK文件签名。
    • 发布到各大应用市场。

5、针对静态攻击的防护方案
  • 由于Java代码被反编译的难度很小,所以针对Java层的代码保护,主要是通过代码混淆来实现,目前常用的是Android Studio自带的混淆插件,只需要配置相关文件就行。

  • 静态修改Smali代码后,攻击者需要重新打包再使用攻击者的私钥对APK进行签名,所以二次打包后的签名信息一定会发生改变,所以可以在代码中添加相应的检测逻辑。

  • 针对静态分析最有效的办法还是加壳,攻击者反汇编后不能直接得到源程序的代码,无从下手。

四、基于Android的逆向动态攻击

1、简介
  • 在分析经过加密混淆的Android程序时,如果只是使用静态分析技术往往达不到理想的效果,所以往往需要经过对应用程序的动态调试,设置断点,理清程序的执行逻辑,找到关键的突破口。
2、动态分析技巧
  • 代码注入法:Android应用程序在正式发布后一般不会保留Log输出信息,如果想观察到特定位置的输出,此时需要手动像反汇编的Smali代码中注入Log调用的模块,重新打包签名运行,随后使用adb logcat -s SN命令查看日志输出信息。这种方法还是需要攻击者具有良好的Smali语言基础。
  • 栈跟踪法:原理与代码注入法一样,不同点在于输出的是栈跟踪信息的代码,且得到的反馈信息比Log注入要详细很多。
  • 动态调试Smail代码:Android Studio在安装了Smali插件后,可以动态调试Android应用程序,并插入相关断点实施查看内存中的变量值,非常高效。
3、攻击实现
  • 这里我以Android Studio动态调试为例,源程序为静态分析中的注册验证程序。

  • 首先需要在Android Studio中安装smalidea插件,源zip可以百度搜一下。安装成功后重启。

  • 将Android Killer反编译后生成的Smali文件打包存放在一个文件夹下。

  • 将该文件导入Android Studio工程,选择Import project选项。

  • 选中文件夹,右键Make Directory As -> Source Root命令,让Smali代码加载进来。

  • 执行File->Project Structure命令,选择SDK1.8。

  • 命令行使用am命令,以debug模式启动应用:adb shell am start -D -n com.XXX.XXX包名/.MainActivity,模拟器应用会进入等待调试状态。

  • 打开DDMS查看程序运行的端口。

  • 修改Debug设置中的端口号。

  • 执行Debug命令。

  • 命令行会提示建立Socket连接。

  • 在关键的Equals函数处设置断点。

  • 通过查看变量值,发现一个16位的字符串,直接猜测这就是正确的注册码。

  • 直接尝试使用同样的用户名,注册码填入获得的16位字符串。

4、逆向动态调试攻击总结
  • 核心思路

    • 获取待调试应用程序的反汇编Smali代码。

    • 导入Android Studio之类的调试器中,设置相关参数。

    • 在模拟器中以debug模式启动应用:adb shell am start -D -n -com.XXX.XXX(包名)/.主Activity。

      • 还有一种思路是在反汇编后,在AndroidManifest.xml文件中修改Debug属性为true,以及程序oncreate入口插入waitForDebug代码达到同样的效果。
    • 打开DDMS监视器,找到对应的调试端口号。

    • 在调试器Debug Configuration中设置远程调试项目,运行debug,如果DDMS中的小蜘蛛从红色变绿了,表明连接成功。

    • 在调试器中相应的关键代码设置断点,查看相应的变量值,这个过程主要看攻击者自身的分析能力。

    • 最后就是根据调试过程得到的信息,修改Smali代码或者其它手段达到攻击的效果。

  • 危害性:目前针对Android应用程序的动态调试攻击,不仅仅是对反汇编的Smali代码,还有Native层的So文件,通过IDA可以对So文件进行调试。而且目前的应用程序大部分都是经过加固之后才能上架,此时静态分析攻击基本上没有什么效果。所以目前针对Android应用的攻击思路首先是以动态调试,在Dexload函数处下断点,得到源Dex文件开始。

5、反调试方案
  • 反调试的目的就是为了防止应用程序被第三方的调试器分析,目前反调试的思路主要有三个方向:

    • 调试器状态检测:Android SDK中提供了android.os.Debug.isDebuggerConnected()方法来判断程序是否被调试器调试。同时AndroidManifest.xml文件中,可以查看Application标签中是否有android:debuggable属性,所以可以在代码中检测它的属性值。

    • 调试器端口检测:在使用调试器远程调试程序时,调试服务端会在Android设备上运行,然后等待调试器连接,所以可以根据调试器常用的端口号来检测是否被调试。最简单的方法就是用Socket连接需要检测的端口。

    • 进程状态检测:当应用程序被调试时,进程状态与其正常运行时有不同,可以通过检测进程状态的差异判断应用程序是否被调试。

五、Hook攻击

1、简介
  • Hook技术简称为“钩子“技术,首先需要将需要修改的核心函数找到,然后使用自己编写的函数替换掉应用中的源函数,被攻击的应用程序运行时就会执行攻击者自定义的模块,最后达到动态修改的目的。
2、原理
  • Android系统中使用的最多的Hook框架就是Xposed框架了,支持Java层的Dalvik Hook和ART Hook,所以主要针对其背后的运行原理进行研究。

  • Xposed框架的安装需要从网上下载一个Xposed Installer App,按照操作提示完成安装,前提是设备必须经过root,这里我选择的是模拟器。通过安装的脚本可以发现安装Xposed框架后的Android系统Zygote进程被替换,启动的是Xposed版的Zygote进程。

  • 在Android系统启动的时候,Zygote进程加载XposedBridge.jar,将所有需要替换的Method通过JNI方法hookMethodNative指向Native方法xposedCallHandler,这个方法再通过调用handleHookedMethod这个Java方法来调用被劫持的方法转入Hook逻辑。简而言之,就是在应用不知情的情况下修改了被劫持方法的执行逻辑,也就是偷梁换柱。

3、攻击实现
  • 目标:还是以上一节的注册应用为例,修改CheckSN()方法的执行逻辑,实现攻击。

  • 首先需要安装相应的Xposed框架,效果如下:

  • 随后使用Android Studio新建一个hook工程,新建一个Java类,添加对被攻击应用程序进程的过滤:

      public class hooks implements IXposedHookLoadPackage {
      final String packageName = "com.droider.crackme0201";  //被劫持的应用包名
    
          @Override
          public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
              if (!lpparam.packageName.equals(packageName)) {
                  return;
              }
      
              new CheckSNHook(lpparam.classLoader);  //如果发现进程中有该应用,则新建一个CheckSNHook类对应用进行hook。
          }
      }
    
  • 之后有两种攻击思路:

    • 1、编写核心的CheckSNhook方法,首先找到应用程序CheckSN方法的位置,进行定位。

       Class clz = (Class<?>) XposedHelpers.findClass("com.droider.crackme0201.MainActivity", cl);    //找到MainActivity类
        XposedHelpers.findAndHookMethod(clz,"checkSN",
        String.class, String.class,
        new XC_MethodHook(){
            // 找到需要Hook的方法,并写入hook后的方法逻辑
        }
      
    • 随后在XC_MethodHook中编写afterHookedMethod()方法,将结果置为true。

                @Override
                protected void afterHookedMethod(MethodHookParam param)
                    throws Throwable {
                    XposedBridge.log("CheckSN afterHookedMethod called.");
                    String s1 = (String) param.args[0];
                    String s2 = (String) param.args[1];
                    param.setResult(true);super.afterHookedMethod(param);
                            }
      
      • 2、使用XC_MethodReplacement,该类配合XposedBridge类的hookAllMethods()方法可以hook所有名为checkSN的方法,并可以设置相应的返回值为true。

          final XC_MethodReplacement replacementTrue = XC_MethodReplacement.returnConstant(true);
          Class clz = (Class<?>) XposedHelpers.findClass("com.droider.crackme0201.MainActivity", cl);
          XposedBridge.hookAllMethods(clz, "checkSN", replacementTrue);
        
  • hook模块编写完成后,需要在项目src目录下新建assets目录,再新建一个xposed_init的文本文件,XposedBrige会从该文件找到hook工具类。

  • 同时需要在AndroidManifest.xml文件中添加Xposed模块的声明:

       <meta-data android:value="true" android:name="xposedmodule"/>
      <meta-data android:value="53" android:name="xposedminversion"/>
      <meta-data android:name="xposeddescription" android:value="Crackme0201 checkSN hooker"/>
    
  • 编译安装好插件之后,在设备的Xposed Installer中启用该框架,重新开机。

  • 运行被攻击程序,随便输入用户名和密码,都会提示注册成功。

4、Hook攻击总结
  • 核心思路:

    • 首先需要一台root后的设备,可以是真机也可以是模拟器。
    • 其次需要根据设备的系统版本,有选择的下载相应Xposed框架(这一块花了很多时间)。
    • 在编写相应的攻击插件前,需要对被攻击应用程序进行静态分析,动态调试找到核心的方法,然后进行Hook,达到修改程序运行流程的目的。
    • Xposed框架只适用于Java层,对于Native层无能为力,但是可以用其它Hook框架弥补。
  • 危害性:

    • Xposed框架能够Hook系统以及APK的任意Java层代码,使得Java层的许多防逆向检测代码很难达到相应的效果。
    • 目前针对加壳程序的脱壳方案,核心也是找到关键的脱壳点,Hook相应的dexload方法。
5、反Hook方案
  • 调用Android提供的PackageManager来遍历整个系统的应用,查看是否有Xposed等hook框架。
  • 安装Xposed框架的情况下,打印方法的栈跟踪信息可以发现Hook的迹象。
  • Java层代码混淆,让攻击者找不到核心的函数。

待解决的问题&下周的计划

  • 待解决的问题:目前的攻击主要是针对Java层的,Native层由于C++没有基础,目前都还没有涉及。
  • 下周的计划:开始设计实现Android应用保护方案,目前暂定是防逆向这方面的。

猜你喜欢

转载自www.cnblogs.com/Gst-Paul/p/12637346.html