前端时间写了一篇blog,讲了一下如何使用frida工具来绕过安卓APP的ssl固定,并使用工具进行抓包,以找到所需要的接口。但实际上之前的步骤基本上没有涉及到什么逆向分析相关的内容,frida的玩法还有很多,这篇文章我们会了解到如何使用这个工具对APP进行进一步的分析,来达到我们需要的采集数据的目的。

注意:


本文有小部分内容由AI辅助创作,部分内容的真实性存疑,请自行判断。
本文会使用到一些方法绕过GFW,请在使用互联网时遵守当地的相关法律法规。
本文涉及到软件逆向分析,请注意在实践过程中的法律问题。
根据《中华人民共和国刑法》第286条:
违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行,后果严重的,处5年以下有期徒刑或者拘役;后果特别严重的,处5年以上有期徒刑。
违反国家规定,对计算机信息系统中存储、处理或者传输的数据和应用程序进行删除、修改、增加的操作,后果严重的,依照前款的规定处罚。
故意制作、传播计算机病毒等破坏性程序,影响计算机系统正常运行,后果严重的,依照第1款的规定处罚。

如果你已经忘了之前的内容,可以先去复习一下:

https://kalinote.top/index.php/2024/07/09/%e4%bb%a5xtwitter%e4%b8%ba%e4%be%8b%e7%9a%84%e5%ae%89%e5%8d%93%e5%b9%b3%e5%8f%b0%e8%bd%af%e4%bb%b6%e9%80%86%e5%90%91%e5%8f%8a%e6%95%b0%e6%8d%ae%e9%87%87%e9%9b%86
温故而知新

书接上回

在之前的逆向分析工作当中,我们提到了有关请求中authorization头的问题1,当时只是说到,在X的开发者文档中有auth头的算法,我们可以直接通过这个算法来构造auth头。然而在实际的逆向工作中,很多时候官方并不会给出这些参数的算法,我们只能靠自己去寻找其构造方法,今天我们主要的目的就是通过反编译等手段,在不依赖官方文档的情况下,尝试找到authorization头的构造方法。

需要提前了解的信息

注意: 下面所说的x86平台/x86平台软件皆指Windows NT或linux软件(不包括基于Linux内核开发的AOSP),并非所有x86平台的软件(因为安卓也有x86版本)我将其称为x86只是为了方便,明白其意思就行。

不同平台软件的编译流程

在对安卓软件进行逆向分析之前,我们需要先了解其软件结构,以及其结构上与普通x86平台(Windows NT或linux)软件结构的差异。

x86软件编译流程

我们知道,一个软件从(高级编译型语言)源代码到最终的可执行程序(exe或elf)分为以下几步:

  1. 编写源代码: 使用C/C++或其他类似的高级编程语言来编写软件运行的逻辑的代码。
  2. 预处理: 完成代码编写后,通过编译器编译代码,编译器首先会对源代码进行预处理工作,比如处理C语言中的包引入或宏定义(#include和#define)等。这一步会将源代码转换成预处理代码,并继续由编译器进行下一步处理。
  3. 编译: 编译器获得预处理的代码后,会开始进行编译工作,这一步中编译器会将经过预处理的高级编程语言代码转换成相对来说较为底层的汇编代码。这一过程包含词法分析、代码优化、代码翻译等。
  4. 汇编编译: 编译得到的汇编语言由汇编器进一步翻译成可由CPU识别的机器码,最后生成得到的文件叫目标文件2
  5. 链接: 链接分为静态链接和动态链接,其中静态链接是将上一步得到的多个目标文件和静态库合并到一起;动态链接指的是在软件运行时加载动态库3。除此之外,链接程序还可以分配可执行文件的堆栈、代码段位置等。这一步最终得到的是可执行文件4

这里只是简单地对流程进行了描述,有关更加详细的编译流程,以及每个过程中编译器都干了些什么,我有可能会在操作系统开发系列文章中写到(现在还没有,以后不知道会不会补上)。

安卓软件编译流程

因为本文主要研究的是安卓软件的逆向分析,所以这一部分相对来说更加重要一些,x86平台软件的编译流程只需要稍作了解即可,因为安卓软件和x86平台的编译过程几乎完全不同,步骤上相对来说也会更多一些。

安卓软件大多由Java或Kotlin语言开发,这两种语言最后都需要编译成Dalvik可执行格式,安卓系统的软件运行机制,决定了安卓软件结构注定与ELF或PE不同。

安卓软件的编译流程如下:

  1. 编写源代码: 与x86软件相同,安卓软件也需要编写源代码(废话),但是安卓大多使用Java语言或Kotlin(从某种意义上来说算是Java的超集)。不过在需要高性能计算,或对底层硬件进行操作时,也会直接使用C/C++进行开发,C/C++通过JNI(Java Native Interface)来调用,使用C/C++开发需要用到NDK(Native Development Kit)。
  2. 代码编译: 对于Java或者Kotlin代码来说,编译器会将源代码编译成JVM的字节码,并保存到文件中,这些文件的后缀一般是.class;对于C或C++代码来说,编译器会使用NDK中的交叉编译工具链,并将源代码编译成对应架构的机器码,其中的步骤与上面提到的x86软件编译流程类似,但不完全相同,最后得到的是动态库(.so)文件,这些文件会被安卓程序运行时动态调用。
  3. 转换字节码: 这一步会通过dx或d8工具将字节码转换成Dalvik格式,该格式的文件后缀名通常为.dex。该格式的字节码可以在Dalvik虚拟机或ART(安卓运行时 Android Run-time)运行。
  4. 资源打包: 要生成可以用于安装的apk文件(或xapk文件),还需要将资源文件(图片、文本字符串以及其他资源文件)和XML布局文件进行打包,这一步编译器会将资源的资源id生成到R.java文件中,这个文件最终会被编译到classes.dex中(字节码文件),最后将所有的字节码和资源打包到apk文件中。
  5. 签名与对齐: 经过上面四个步骤,就已经可以得到最终的apk文件了,但是实际上还需要对apk进行签名,这一步是防止apk被恶意修改,以及保证apk的完整性;对apk进行对齐操作,可以提高apk的运行效率。

经过上述步骤得到的apk文件,可以被安装到安卓系统中并运行。

在软件第一次运行时,还会进行一次编译,安卓运行时在首次运行软件时,会将.dex字节码进行AOT(Ahead-of-Time)编译,转换为本地机器码,存储为.oat文件,以提高运行性能。所以有一部分软件,刚装上启动会比较慢,但后续再使用就会变快。

与x86软件直接运行于CPU不同,安卓软件一般运行于虚拟机之上,或经过AOT编译的环境中。从这一点原理上来看,安卓软件的效率无法达到100%。

安卓软件的基本结构

安卓软件从结构上来说,与x86软件也有较大不同。

应用框架和组件化设计

安卓应用遵循特定的生命周期框架以及组件化设计,比如ActivityServiceBroadcastReceiverContentProvider等组件。其中,一个Activity就是一个单独的屏幕或用户界面(UI)。它负责与用户进行交互,展示界面元素,并响应用户的操作。每个Activity都有自己的生命周期(创建、启动、暂停、销毁等),一个app可以包含多个Activity,不同的Activity之间通过Intent进行导航;Service是一种后台执行长时间运行操作的组件,其只负责任务执行,Service可以在应用退出(到后台)后保持运行,确保后台任务不会中断,比较常见的例子是音乐播放器,不会因为退出app而中断音乐。BroadcastReceiver是用于接收和响应系统或应用程序发送的广播消息的组件,app通过该组件接收系统发送的广播消息(比如设备启动、网络状态变化、电量低等);ContentProvider是用于在不同应用程序之间共享数据的组件。它定义了一套标准的接口,允许其他应用按照安全的方式访问和修改数据。ContentProvider管理的数据通常存储在文件、数据库或其他持久化存储中。

上面四个是在安卓开发中比较常用的四个组件,其余的可以在Google官方的Android开发文档中查到。

而在x86软件方面,就没有严格的生命周期规定,各软件遵循自己的生命周期管理方式,这一点不同软件根据其开发思路和运行的系统平台可能有所不同。

软件资源管理

在资源管理方面,安卓软件高度依赖资源文件,软件使用XML进行布局,其描述了界面组件如何在屏幕上排列、显示和交互。为了对不同端设备分辨率进行匹配,安卓可以同时保存多套分辨率的布局,并根据设备实际情况进行调整。

而x86软件则没有固定的资源管理要求,有可能通过文件系统管理资源,也有可能直接通过代码来生成用户界面,或者使用类似QT这种第三方的用户界面库。总之,x86平台没有一套标准的资源和界面管理规定。

用户操作与运行机制

除了部分辅助类软件外,安卓软件大多数都依赖于用户界面,用户通过界面进行操作,而不像x86平台,除了应用程序界面外,软件还可能以命令行或后台服务等形式存在和运行。

在软件运行方面,安卓软件运行在沙盒环境中,权限管理严格,各APP相互相对独立,应用间共享依赖于Intent机制,对设备权限也需要提前通过AndroidManifest.xml向安卓系统进行权限申请;而x86软件则相对更自由,没有太多的强制性的标准规则,也可以进行更底层的操作。

虽然安卓系统也支持PC或IOT设备,但安卓软件的各方面框架设计更加偏向于类似手机或平板类的移动设备。

安卓逆向分析工具

我们可以使用逆向分析工具,对安卓apk进行解包,反编译和动态调试。

每个安卓apk包实际上就是一个ZIP文件,如下图所示,文件头为504B(PK,ZIP文件的标准文件头):

X.apk文件的十六进制代码

所以在逆向分析时会先对apk进行解包,并获取其中的dex文件和资源文件。

在获取到dex文件后,逆向分析工具会对其进行反编译,将机器码反编译成汇编代码,部分逆向分析工具还支持将汇编代码翻译成Java伪代码,这一步我们在后续实际操作中还会详细介绍到。

除此之外,部分逆向分析工具还可以对资源进行提取和修改、进行动态分析和调试、以及对代码进行注入和修改等。

除了之前已经提到的frida以外,常用的逆向分析工具是APKTool,该工具可以对apk进行反编译,在修改代码后回编成apk并签名发布。但是本文不涉及代码的修改,只需要分析代码,所以用不上这个工具。

本文会主要使用的JEB,该工具也主要用于对安卓软件的反编译和代码分析,并且也有动态断点调试功能。该软件也支持一些脚本和插件类的开发,但是本文也用不上。这个软件的Pro版是付费的,但是网上可以找到“学习版本”,当然,也可以使用免费的社区版。

了解了这些前置信息后,我们可以尝试对X进行逆向分析。

着手开始逆向分析

环境准备

按照之前文章的步骤,准备好firda和模拟器环境。然后准备好JEB软件,启动JEB,将apk文件拖入窗口,JEB会自动开始反编译,整个过程可能会卡住一段时间,不用操作,等待反编译完成即可。在反编译之前可能会弹出一个设置弹窗,不需要修改设置,直接点完成即可。

长时间卡在这个界面是正常的