Android逆向工程学习笔记(一)
mobile笔记
参考文章,看雪论坛
在深入理解Android应用的工作原理和内部结构之前,我们首先需要了解应用打包的核心文件——APK(Android Package)。
APK 文件是Android操作系统中的应用程序包,它包含了应用的所有资源、代码和必要的配置文件。可以把APK看作一个容器,其中集成了Android应用的所有组成部分。
把APK拆成多个组件来研究学习,每个组件在应用运行中都扮演着不同的角色,理解这些组件有助于我们全面掌握应用的运行机制,甚至为后续的逆向分析和漏洞挖掘打下基础。
APK的基本结构
实际上,APK 文件是以 ZIP 格式进行压缩打包的,因此,我们可以像操作普通的ZIP文件一样,使用解压工具对其进行解压。通过解压后查看APK文件的目录结构,我们能够清晰地了解每个组成部分的作用。
APK 文件通常包括以下几个主要部分:
- AndroidManifest.xml
- classes.dex
- resources.arsc
- assets/
- lib/
- res/
- META-INF/
AndroidManifest.xml
AndroidManifest.xml 是每个Android应用不可或缺的配置文件,它包含了应用的关键信息。它向系统声明了应用的基本属性、组件以及权限等。AndroidManifest.xml中包括以下重要部分:
应用的包名(package):每个Android应用都有一个唯一的包名,通过包名来区分不同的应用。
应用的组件(Activities, Services, Broadcast Receivers, Content Providers):声明应用包含哪些组件,以及这些组件的属性和功能。
权限声明:列出应用所需的权限,如访问网络、读取存储、使用相机等。
应用主题和图标:定义应用的UI样式、图标等。
最小SDK版本和目标SDK版本:确定应用能在什么版本的Android系统上运行。

``:包含整个应用的包信息及权限定义。
package: 定义了应用的包名,通常为反向域名格式,如com.example.app。android:versionCode: 定义应用的版本号。android:versionName: 定义应用的版本名称。
<application>:包含应用的核心配置,如主题、图标等。
android:icon: 定义应用的图标。android:label: 定义应用的名称。android:theme: 应用的UI主题。
<activity>:声明应用的各个界面(Activity),以及这些Activity的属性和行为。
android:name: Activity的类名。android:label: Activity的标签。android:theme: Activity特有的UI主题。
<uses-permission>:声明应用所需要的权限,如访问网络、发送短信等。
<intent-filter>:定义组件的功能和响应的事件,如Activity的启动方式或Broadcast Receiver接收的广播类型。
以下面这个程序为例子,分析一下它的AndroidManifest.xml
题目来源:NSSCTF
题目名称:[CISCN 2022 东北]crackme_Android
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" android:compileSdkVersion="32" android:compileSdkVersionCodename="12" package="com.example.crackme1" platformBuildVersionCode="32" platformBuildVersionName="12">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="32"/>
<application android:theme="@style/Theme.CrackMe1" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:debuggable="true" android:allowBackup="true" android:supportsRtl="true" android:fullBackupContent="@xml/backup_rules" android:roundIcon="@mipmap/ic_launcher_round" android:appComponentFactory="androidx.core.app.CoreComponentFactory" android:dataExtractionRules="@xml/data_extraction_rules">
<activity android:name="com.example.crackme1.MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
package="com.example.crackme1",包名com.example.crackme1"
android:versionCode="1",版本号 1
android:versionName="1.0",版本名 1.0
android:icon="@mipmap/ic_launcher",定义应用的图标
android:label="@string/app_name" ,定义应用的名称
android:theme="@style/Theme.CrackMe1",定义应用的UI主题
android:name="com.example.crackme1.MainActivity",Activity(界面)的类名
android:label="@string/app_name",Activity(界面)的标签
android:theme="@style/Theme.CrackMe1",Activity(界面)的UI主题
<uses-permission>段:声明应用所需要的权限,如访问网络、发送短信等。该程序无体现
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
注意,这通常是逆向工程应用入口点的核心
入口点通常由以下两个标记表示:
<action android:name="android.intent.action.MAIN" />:标记这是应用的主入口。<category android:name="android.intent.category.LAUNCHER" />:表示该 Activity 会出现在应用启动器中(即桌面)。
<activity android:name="com.example.crackme1.MainActivity" android:exported="true">
这里可以得出,应用入口点在com.example.crackme1.MainActivity
com.example.crackme1.MainActivity是一个Java类的完整名称
<activity> 是一个 Activity 组件声明,android:name 是这个声明中的类名属性。
<activity android:name="com.example.crackme1.MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
这一整段可以这么理解
向 Android 系统注册一个 Activity,并指定它由 com.example.crackme1.MainActivity 这个 Java 类实现;同时声明这个 Activity 可以匹配 MAIN + LAUNCHER 类型的 Intent,因此系统 Launcher 可以将它作为应用图标对应的启动 Activity。
classes.dex
classes.dex 文件包含了应用程序的可执行代码。它是应用的Dalvik字节码文件,也是Android应用在运行时通过 Dalvik虚拟机 或 ART(Android Runtime) 解释执行的核心文件。每个Android应用中,所有的Java源代码都经过编译后形成一个或多个DEX(Dalvik Executable)文件,这些文件包含了应用的业务逻辑和代码实现。
在Android 5.0(Lollipop)之后,Google引入了 ART(Android Runtime) 代替了传统的Dalvik虚拟机,ART的执行方式比Dalvik更高效,支持Ahead-of-Time(AOT)编译和即时编译(JIT)策略。
这部分比较难可以拓展阅读一下,
相关文档:
JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对比_java dalvik-CSDN博客
安卓逆向学习----smali,dex,java等文件之间转换关系_dex与smail-CSDN博客
其实我也没细看这部分内容,目前主要知道以下内容即可
classes.dex 文件包含了应用程序的可执行代码。它是应用的Dalvik字节码文件,也是Android应用在运行时通过 Dalvik虚拟机 或 ART(Android Runtime) 解释执行的核心文件。
resources.arsc
resources.arsc 文件包含了应用程序的所有编译后的资源映射信息。这个文件并不存储实际的资源内容(如图片或字符串),而是存储资源与资源ID的映射关系。例如,它会保存应用中的字符串、颜色、尺寸、样式等信息以及这些资源的ID。通过这个文件,Android系统能够在应用运行时快速访问和加载所需的资源。
使用jadx查看:

classes.dex
↓
Java/Kotlin代码
AndroidManifest.xml
↓
APP组件、权限、Activity等声明
res/
↓
图片、布局、字符串、颜色等资源
resources.arsc
↓
这些资源的“索引和映射信息”
逆向工程中也需要关注这个resources.arsc(资源索引/资源表数据库)
很有可能key或者flag存储在这里
resources.arsc可以包含大量资源相关信息,例如:
字符串
颜色
尺寸
样式
主题
资源ID
资源名称
不同语言版本
不同屏幕配置
不同Android配置
assets/
assets/ 目录包含了应用程序的原始资源文件,这些资源不经过编译,直接以原始形式存储。通常,开发者可以在该目录中存放字体文件、音频文件、HTML文件等,应用在运行时通过API来读取这些资源。例如,游戏可能会将所有的地图文件或纹理图像存放在此目录中。通过AssetManager API,应用可以访问这些文件。
该程序没有
lib/
lib/ 目录包含了本地库文件,通常是通过 JNI(Java Native Interface) 与C/C++编写的本地代码。这些库文件可以针对不同的硬件架构(如arm、x86等)进行编译,因此lib/目录下通常会为每个架构创建相应的子目录。这个目录中存放的本地库可以通过Java代码调用JNI接口实现与系统底层的交互。
该程序没有
res/
res/ 目录包含了Android应用所需的所有资源文件。与 assets/ 目录不同,res/ 目录中的资源文件是经过编译的,按照不同类型的资源进行组织,例如:
- drawable/:存放图像资源(如PNG、JPEG等格式的图片)。
- layout/:存放XML格式的布局文件,定义界面的结构。
- values/:存放各种配置文件,定义应用的常量、颜色、字符串等资源。例如:
strings.xml:存储应用的文本字符串。colors.xml:存储应用使用的颜色资源。styles.xml:存储样式资源。
在values/目录下,除了strings.xml、colors.xml等常见资源文件,还会有像dimens.xml(尺寸定义文件)和attrs.xml(自定义属性)等资源文件。
可以在文件夹目录中找到也可以在jadx里面查看:


META-INF/
META-INF/ 目录与Java的JAR文件类似,用于存放APK文件的元数据,如签名文件、校验信息等。此目录主要包括以下文件:
- MANIFEST.MF:存放APK的清单文件,包含关于APK文件本身的信息。
- CERT.RSA:包含APK文件的数字签名。
- CERT.SF:存放APK文件的签名摘要。
这些文件确保了APK的完整性和安全性,保证APK文件没有被篡改,且来自合法的开发者。

APK基本结构的介绍就到此结束了
Android逆向分析
依旧是例题[CISCN 2022 东北]crackme_Android
其实建议有点java基础来分析,能看懂程序就差不多了,如果你有cpp等面向对象语言的开发经验,那么其实上手java也会非常快
逆向分析很重要的一点就是找到程序主入口点
我们在APK的基本结构中有提到如何去查找Android程序的主入口点
AndroidManifest.xml是非常重要的,初级逆向分析建议先从这里入手查找入口点
我们使用jadx进行反编译,没有jadx的师傅请自行上网查找一下

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" android:compileSdkVersion="32" android:compileSdkVersionCodename="12" package="com.example.crackme1" platformBuildVersionCode="32" platformBuildVersionName="12">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="32"/>
<application android:theme="@style/Theme.CrackMe1" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:debuggable="true" android:allowBackup="true" android:supportsRtl="true" android:fullBackupContent="@xml/backup_rules" android:roundIcon="@mipmap/ic_launcher_round" android:appComponentFactory="androidx.core.app.CoreComponentFactory" android:dataExtractionRules="@xml/data_extraction_rules">
<activity android:name="com.example.crackme1.MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
我们先主要看
<activity android:name="com.example.crackme1.MainActivity" android:exported="true">
可以看出java类名为com.example.crackme1.MainActivity是程序的主入口点
前往分析

一个MD5
MessageDigest
这是java内置的加密类,作用是创建一个哈希计算器
程序主要进行的是,对flag进行首位检测,去除首位检测的话一共是32字节,每四字节进行一次MD5
最后与拼接起来的hash值进行校验,正确则输出right,错误输出error
由于是对每四字节进行处理,那么我们完全可以进行爆破
本题需要利用python,hashlib库进行爆破,hashlib库是python自带的,不需要额外下载
完整的exp
from hashlib import md5
md5s = '8393931a16db5a00f464a24abe24b17a9040b57d9cb2cbfa6bdc61d12e9b51f2789e8a8ae9406c969118e75e9bc65c4327fbc7c3accdf2c54675b0ddf3e0a6099b1b81046d525495e3a14ff6eae76eddfa1740cd6bd483da0f7684b2e4ec84b371f07bf95f0113eefab12552181dd832af8d1eb220186400c494db7091e402b0'
flag = []
md5l = []
for i in range(0,len(md5s),32):
md5l.append(md5s[i:i+32])
space='abcdef01234567890-'
for n1 in space:
for n2 in space:
for n3 in space:
for n4 in space:
num5=n1+n2+n3+n4
if md5(num5.encode()).hexdigest() in md5l:
flag.append(num5)
for md5_ in md5l:
for i in flag:
if md5(i.encode()).hexdigest() == md5_:
print(i,end='')

浙公网安备 33010602011771号