-
-
[原创]Android逆向0基础入门-APK全面解析,动调与脱壳
-
发表于: 2025-3-7 11:48 3539
-
第一章:引言与背景
Android逆向工程,作为一种深入分析Android应用程序的技术,主要目的就是通过分析应用的代码、资源和行为来理解其功能、结构和潜在的安全问题。它不仅仅是对应用进行破解或修改,更重要的是帮助开发者、研究人员和安全人员发现并解决安全隐患。
本文主要对整个安卓逆向入门的体系进行整理,解决一些安卓逆向时候的痛点问题!每个知识点都会有对应的例题下载,或者前文对应刷题的链接,在刷题中学习,才可以更快的掌握知识!
下面是本文的关键点:
- APK逆向工具:工欲善其事,必先利其器(这部分可以掠过,只做了工具的下载地址和简单介绍)
- APK的基本结构(详细解析了每个文件的作用和目的)
- Android逆向分析中的基础(解决没有安卓开发基础学习安卓逆向时候的痛点问题)
- 如何找出APP的程序的入口点?
- 如何识别是否加壳,并且简单脱壳?
- 如何寻找一个页面的xml布局文件?
- 如何确定按钮所绑定的函数?
- 如何寻找app中涉及到的资源文件?
- 什么是java层逆向,什么是Native层逆向?
- 安卓系统中的四大组件是如何在逆向题目中被应用的?
- 最后进行了安卓体系的梳理!
第二章:APK逆向工具:工欲善其事,必先利其器
1.1 雷电模拟器运行APK
雷电模拟器,作为安卓模拟器的佼佼者,一直以来备受用户青睐。它不仅可以让你在PC上畅快运行安卓应用,还能提供与手机端接近的使用体验,让你在开发、调试乃至游戏娱乐中都能游刃有余。安装雷电模拟器其实并不复杂,但要确保顺利完成,还是有一些细节需要关注。
相关使用:雷电模拟器的使用 - 搜索
下载地址:雷电模拟器官网_安卓模拟器_电脑手游模拟器
1.2 ADB的使用
ADB(Android Debug Bridge),简直是安卓开发者和逆向工程师的“瑞士军刀”。无论是调试、安装应用,还是进行日志分析,ADB都是不可或缺的工具。你可能会认为ADB只是一个命令行工具,然而它的强大远超你的想象。
相关使用:ADB安装及使用详解(非常详细)从零基础入门到精通,看完这一篇就够了-CSDN博客
下载地址:https://dl.google.com/android/repository/platform-tools-latest-windows.zip
1.4 使用JADX反编译APK
JADX是一款非常流行且功能强大的APK反编译工具,它能够将APK中的DEX文件(即Dalvik Executable文件)反编译成可读的Java源代码。JADX的优势不仅仅在于它的易用性,还在于它的反编译效果非常优秀,能够清晰地显示反编译后的Java代码,帮助开发者和安全人员深入理解应用的内部逻辑。
JADX的优点:
- 易用性:图形界面的设计使得操作简单直观,适合初学者和经验丰富的开发者。
- 高效的反编译效果:JADX能够将DEX文件反编译成非常接近原始Java代码的源代码,对于Java层的应用分析尤为高效。
-
支持多种格式:除了反编译Java代码,JADX还支持查看APK中的资源文件(如图片、XML文件等),让你能够全面了解应用的构成。
相关使用:APK反编译工具jadx - chuyaoxin - 博客园
下载地址:https://down.52pojie.cn/Tools/Android_Tools/jadx-1.5.0.zip
1.5 使用GAD进行APK反汇编
GAD(Google Android Disassembler)是一个专注于APK底层字节码分析的工具。与JADX不同,GAD更多侧重于字节码级别的反汇编,它能够帮助安全研究人员和开发者深入到应用的最底层,查看其具体的机器码和执行逻辑。GAD特别适用于那些对字节码和汇编语言感兴趣的逆向工程师,它可以帮助我们获得应用中深层次的行为信息。
GAD的优点:
- 底层分析能力:GAD能够提供非常详细的底层字节码分析,帮助你更深入地理解应用的执行过程。
-
适用于高级分析:如果你需要分析复杂的应用行为或破解复杂的加密算法,GAD提供的反汇编信息可以帮助你做出准确的判断。
相关使用:[原创]GDA使用手册-Android安全-看雪-安全社区|安全招聘|kanxue.com
下载地址:https://down.52pojie.cn/Tools/Android_Tools/GDA4.11.zip
1.6 JEB进行APK反汇编
JEB的魅力在于其高精度的反汇编能力。它不仅能解析传统的DEX文件,还能处理各类复杂的文件格式,包括加固过的APK、经过混淆处理的代码,甚至是一些非标准的Android文件结构。它像一把锐利的刀刃,切开了应用的“外壳”,揭示其最核心的部分。
JEB的优点:
- 强大的反汇编能力:JEB不仅仅局限于常规的字节码反汇编,它能够对各种复杂和非标准格式的APK进行深入分析。
- 支持多种文件格式 :JEB支持广泛的文件格式解析,除了DEX文件,还包括PE(Portable Executable)、ELF、Java字节码等多种格式。
-
高级功能与插件支持 :JEB的插件架构极为强大,用户可以根据自己的需求,定制化扩展JEB的功能。
相关使用:第36讲: 使用Jeb工具反编译安卓APK_jeb反编译工具-CSDN博客
下载地址:https://down.52pojie.cn/Tools/Android_Tools/JEB_Decompiler_3.19.1_Professional.rar
1.7 IDA进行反汇编
进入IDA的世界,你将步入一个顶级的反汇编领域。IDA(Interactive Disassembler)是众多逆向工程师手中的“神器”,无论是操作系统、应用程序还是嵌入式系统,它都能提供无与伦比的反汇编支持。
IDA的优势:
- 精确的反汇编能力:IDA能反汇编几乎所有的二进制文件格式,展示底层机器码及其执行路径,让逆向分析更加精准。
- 高度可扩展:IDA支持插件开发,用户可以根据需求扩展其功能,实现个性化分析。
-
复杂任务支持:IDA特别适合进行复杂的逆向分析任务,如破解软件、分析恶意代码等。
相关使用:菜鸟的逆向工程学习之路——逆向工具IDA的使用_ida工具-CSDN博客
下载地址:https://down.52pojie.cn/Tools/Disassemblers/IDA_Pro_v8.3_Portable.zip
1.8 Frida脱壳工具的使用
Frida 是一个强大的动态分析工具,广泛应用于反向工程和安全测试中,尤其是在对 Android 应用进行脱壳(解除保护)时,它能够帮助研究人员通过动态注入脚本来分析应用程序的行为。以下是使用 Frida 进行脱壳的环境配置和基本步骤。
环境配置:
首先,确保你已经安装了 Frida 及其相关工具,可以通过以下命令进行安装:
1
2
3
|
pip install frida # 安装 Frida 主库
pip install frida-tools # 安装 Frida 的工具集,提供命令行工具
pip install frida-dexdump # 安装 frida-dexdump,用于分析 APK 文件的 dex 内容
|
这些工具将帮助你在 Android 环境中启动和操作 Frida Server,以及进行 APK 分析等操作。
Frida 的环境搭建并不复杂,特别是在虚拟设备(如雷电模拟器)和 Android Debug Bridge (ADB) 的支持下。具体的搭建流程可以参考以下链接:
-
Frida入门教程:基于逍遥模拟器和 ADB 环境搭建
这个教程将详细介绍如何安装和配置 Frida 环境,并提供如何在模拟器和实际设备上运行 Frida Server 和脚本的操作步骤。
第三章:APK解析基础
在深入理解Android应用的工作原理和内部结构之前,我们首先需要了解应用打包的核心文件——APK(Android Package)。APK 文件是Android操作系统中的应用程序包,它包含了应用的所有资源、代码和必要的配置文件。可以把APK看作一个容器,其中集成了Android应用的所有组成部分。
为了能够更深入地分析和理解Android应用的结构,我们可以将APK文件拆解为多个关键组件。每个组件在应用运行中都扮演着不同的角色,理解这些组件有助于我们全面掌握应用的运行机制,甚至为后续的逆向分析和漏洞挖掘打下基础。
APK的基本结构
实际上,APK 文件是以 ZIP 格式进行压缩打包的,因此,我们可以像操作普通的ZIP文件一样,使用解压工具对其进行解压。通过解压后查看APK文件的目录结构,我们能够清晰地了解每个组成部分的作用。以下是一个典型的APK文件的结构示例:
APK 文件通常包括以下几个主要部分:
- AndroidManifest.xml
- classes.dex
- resources.arsc
- assets/
- lib/
- res/
-
META-INF/
接下来,我们将详细解析这些文件和目录的作用及其内容。
将一个apk进行解压,可以发现如下结构的文件目录,测试案例下载:攻防世界-Mobile-easy-so
1.AndroidManifest.xml(关键核心)
AndroidManifest.xml 是每个Android应用不可或缺的配置文件,它包含了应用的关键信息。我们可以把它看作是应用的“蓝图”或“说明书”,它向系统声明了应用的基本属性、组件以及权限等。AndroidManifest.xml中包括以下重要部分:
- 应用的包名(package):每个Android应用都有一个唯一的包名,通过包名来区分不同的应用。
- 应用的组件(Activities, Services, Broadcast Receivers, Content Providers):声明应用包含哪些组件,以及这些组件的属性和功能。
- 权限声明:列出应用所需的权限,如访问网络、读取存储、使用相机等。
- 应用主题和图标:定义应用的UI样式、图标等。
-
最小SDK版本和目标SDK版本:确定应用能在什么版本的Android系统上运行。
详细解析Manifest中的关键字段
-
:包含整个应用的包信息及权限定义。-
package
: 定义了应用的包名,通常为反向域名格式,如com.example.app
。 -
android:versionCode
: 定义应用的版本号。 -
android:versionName
: 定义应用的版本名称。
-
-
:包含应用的核心配置,如主题、图标等。-
android:icon
: 定义应用的图标。 -
android:label
: 定义应用的名称。 -
android:theme
: 应用的UI主题。
-
-
:声明应用的各个界面(Activity),以及这些Activity的属性和行为。-
android:name
: Activity的类名。 -
android:label
: Activity的标签。 -
android:theme
: Activity特有的UI主题。
-
-
:声明应用所需要的权限,如访问网络、发送短信等。 -
:定义组件的功能和响应的事件,如Activity的启动方式或Broadcast Receiver接收的广播类型。
2. 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)策略。
这部分比较难可以拓展阅读一下,相关文档:
3. resources.arsc
resources.arsc 文件包含了应用程序的所有编译后的资源映射信息。这个文件并不存储实际的资源内容(如图片或字符串),而是存储资源与资源ID的映射关系。例如,它会保存应用中的字符串、颜色、尺寸、样式等信息以及这些资源的ID。通过这个文件,Android系统能够在应用运行时快速访问和加载所需的资源。
使用jadx可以看见:
4. assets/
assets/ 目录包含了应用程序的原始资源文件,这些资源不经过编译,直接以原始形式存储。通常,开发者可以在该目录中存放字体文件、音频文件、HTML文件等,应用在运行时通过API来读取这些资源。例如,游戏可能会将所有的地图文件或纹理图像存放在此目录中。通过AssetManager
API,应用可以访问这些文件。
5. lib/
lib/ 目录包含了本地库文件,通常是通过 JNI(Java Native Interface) 与C/C++编写的本地代码。这些库文件可以针对不同的硬件架构(如arm、x86等)进行编译,因此lib/
目录下通常会为每个架构创建相应的子目录。这个目录中存放的本地库可以通过Java代码调用JNI接口实现与系统底层的交互。
下面是一个案例进入lib进入到目录中得到以下目录结构,不同架构的手机拥有不同的操汇编代码所以使用四种架构的汇编分别实现一次:
1
2
3
4
5
6
7
8
9
10
11
|
├─arm64-v8a │ libcyberpeace.so │ ├─armeabi-v7a │ libcyberpeace.so │ ├─x86 │ libcyberpeace.so │ └─x86_64 libcyberpeace.so
|
6. 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里面查看:
7. META-INF/
META-INF/ 目录与Java的JAR文件类似,用于存放APK文件的元数据,如签名文件、校验信息等。此目录主要包括以下文件:
- MANIFEST.MF:存放APK的清单文件,包含关于APK文件本身的信息。
- CERT.RSA:包含APK文件的数字签名。
-
CERT.SF:存放APK文件的签名摘要。
这些文件确保了APK的完整性和安全性,保证APK文件没有被篡改,且来自合法的开发者。
下面是一个示例:
第四章:Android逆向分析中的基础
Android 逆向分析是一个深入挖掘应用内部工作原理的过程,通常用于漏洞挖掘、恶意软件分析或应用的安全性研究。在这章中,我们将深入探讨 Android APK 的反编译与结构分析,剖析壳分析与绕过技术,以及如何对资源与布局文件进行分析。我们还会涉猎 Java 层的逆向技巧,以及如何在 Native 层执行逆向工程。每一部分都将逐一分析和讲解,以帮助读者在 Android 逆向分析中取得更好的突破。
1. APK反编译与结构分析:如何找出程序的入口点
在进行 Android 逆向时,首先需要对 APK 文件进行反编译和结构分析。理解 APK 的基本结构至关重要,因为它帮助我们定位关键组件和入口点。一个典型的 APK 文件包含多个元素,如 AndroidManifest.xml
、DEX 文件、资源文件和库文件等。
定位入口点实战案例
目标: 学习如何反编译 APK 文件并分析其结构,找出应用程序的入口点。
文章: android apk入口分析_5.apk的程序入口界面 - CSDN博客
实战案例:BUU刷题-简单注册器
更详细的WP:BUUCTF之简单注册器(RE) - Eip的浪漫 - 博客园
首先梳理一下基本app的逆向流程:
1.将apk拖入JADX后寻找到AndroidManifest.xml文件:
下面给出AndroidManifest.xml文件的详细注释:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
|
在 Android 应用中,AndroidManifest.xml
文件是至关重要的,它定义了应用的所有组件以及组件之间的关系,包括应用的入口点。打开反编译后的 APK 中的 AndroidManifest.xml
文件,查找
标签,它们通常定义了应用的各个 Activity(包括启动 Activity)。
入口点通常由以下两个标记表示:
-
:标记这是应用的主入口。 -
:表示该 Activity 会出现在应用启动器中(即桌面)。
所以最终得出该app的程序入口点代码是在android:name="com.example.flag.MainActivity"
处!
2.成功寻找到activity的代码入口处,开始分析activity的生命执行流程:
根据android:name="com.example.flag.MainActivity"
字段成功找到Activity的功能实现代码位置,一个Activity的生命周期是:onCreate()
->onStart()
->onResume()
->onPause()
->onStop()
->onDestroy()
,所以可以先锁定omCreate函数,锁定app加载的主要逻辑!
3.开始分析按钮的逻辑代码,成功解析出需要输入的内容:
在主要逻辑中可以发现界面中的一个按钮绑定了一个onclick按钮点击事件:
该函数用于处理用户点击事件,验证输入框中的文本是否符合特定规则,如果符合规则,则对一个预定义的字符串进行一系列字符运算和逆序处理,最终显示一个特定格式的“flag”;否则,显示错误提示“输入注册码错误”。
也就是说我们只需要输入一个正确的字符串就可以成功拿到flag!
将代码提取出来分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
button.setOnClickListener( new View.OnClickListener() { // from class: com.example.flag.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
int flag = 1 ;
String xx = editview.getText().toString();
if (xx.length() != 32 || xx.charAt( 31 ) != 'a' || xx.charAt( 1 ) != 'b' || (xx.charAt( 0 ) + xx.charAt( 2 )) - 48 != 56 ) {
flag = 0 ;
}
if (flag == 1 ) {
char [] x = "dd2940c04462b4dd7c450528835cca15" .toCharArray();
x[ 2 ] = ( char ) ((x[ 2 ] + x[ 3 ]) - 50 );
x[ 4 ] = ( char ) ((x[ 2 ] + x[ 5 ]) - 48 );
x[ 30 ] = ( char ) ((x[ 31 ] + x[ 9 ]) - 48 );
x[ 14 ] = ( char ) ((x[ 27 ] + x[ 28 ]) - 97 );
for ( int i = 0 ; i < 16 ; i++) {
char a = x[ 31 - i];
x[ 31 - i] = x[i];
x[i] = a;
}
String bbb = String.valueOf(x);
textview.setText( "flag{" + bbb + "}" );
return ;
}
textview.setText( "输入注册码错误" );
}
});
} |
这里的逻辑代码就很清晰了:
1
2
3
4
5
|
int flag = 1 ;
String xx = editview.getText().toString(); if (xx.length() != 32 || xx.charAt( 31 ) != 'a' || xx.charAt( 1 ) != 'b' || (xx.charAt( 0 ) + xx.charAt( 2 )) - 48 != 56 ) {
flag = 0 ;
} |
- 获取我们的第一个输入并且判断他的长度和字符的条件要求满足!
- 这个条件意味着字符串的长度必须是 32。
- 这个条件意味着字符串的第32个字符(索引为31)必须 不是
'a'
。 - 这个条件意味着字符串的第2个字符(索引为1)必须 不是
'b'
。 - 字符串的第1个字符(索引为0)和第3个字符(索引为2)的 ASCII 值加起来减去 48 不等于 56。
- 例如,
xx.charAt(0)
为'x'
,xx.charAt(2)
为'y'
,其 ASCII 值分别为 120 和 121。所以(120 + 121) - 48 = 193
,这个结果必须 不等于 56。
1
2
|
(xx.charAt(0) + xx.charAt(2)) == 104 ! G |
!bGaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
满足条件的字符串:
1 |
!bGaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
想要详细的解析可以前往其他WP!
4.理清楚解题脚本后,使用python代码实现flag输出:
解题脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# ✅ 将字符串转换为字符数组 string = "dd2940c04462b4dd7c450528835cca15"
arr_c = list (string)
print (arr_c) # ️ ['j', 'i', 'y', 'i', 'k']
arr_c[ 2 ] = chr ( ord (arr_c[ 2 ]) + ord (arr_c[ 3 ]) - 50 )
arr_c[ 4 ] = chr ( ord (arr_c[ 2 ]) + ord (arr_c[ 5 ]) - 0x30 )
arr_c[ 30 ] = chr ( ord (arr_c[ 0x1F ]) + ord (arr_c[ 9 ]) - 0x30 )
arr_c[ 14 ] = chr ( ord (arr_c[ 27 ]) + ord (arr_c[ 28 ]) - 97 )
for i in range ( 16 ):
a = arr_c[ 0x1F - i];
arr_c[ 0x1F - i] = arr_c[i];
arr_c[i] = a;
for i in arr_c:
print (i,end = "")
|
成功得到flag!
寻找入口点流程梳理
每个 Android 应用的启动通常由一个 Activity
或者 Service
作为入口,通常位于 AndroidManifest.xml
中。我们可以通过以下步骤来快速找到入口点:
- **查看 **
AndroidManifest.xml
:这是每个 Android 应用的配置文件,包含应用的所有组件声明。入口Activity
通常会在intent-filter
中声明MAIN
action 和LAUNCHER
category。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
|
该 Activity
是应用的入口点。
-
反编译并查看
classes.dex
文件:通过工具(如 JADX 或者 JEB)反编译DEX
文件,我们可以进一步理解应用的控制流和逻辑。通过分析反编译后的代码,可以找到主"com.example.flag.MainActivity"
的onCreate()
方法,这是启动应用时的第一步。
2. 壳分析与绕过:简单分析梆梆免费加固
在 Android 安全分析中,许多应用都使用加固技术来防止反编译和分析。梆梆是一种常见的加固,主要通过修改 APK 的结构,注入防护代码来提高应用的安全性。
脱壳实战案例讲解
目标: 理解如何识别并绕过应用程序加壳保护。
加固与脱壳学习: 安卓逆向-脱壳学习记录 - Is Yang's Blog
实战案例:网鼎杯_2020_青龙组_bang
更详细的WP:
首先梳理一下加壳app的逆向流程:
1.将apk拖入JADX后寻找到AndroidManifest.xml文件:
虽然咋i这个xml文件中寻找到了Activity的MainActivity的方法,但是并无此com.example.how_debug.MainActivity的代码实现,首先判定该app进行了加壳操作!
还可以通过观察APK文件是否在AndroidManifest.xml配置Applicaiton信息来判定,该app实现了一个自定义操作将Application
类进行了自定义修改成了com.SecShell.SecShell.ApplicationWrapper
来实现自己的加壳逻辑.
注释:AndroidManifest.xml
文件中,明显可以看到一个与壳相关的线索:在
标签中,android:name="com.SecShell.SecShell.ApplicationWrapper"
指定了应用的 Application
类为 com.SecShell.SecShell.ApplicationWrapper
。这通常意味着应用使用了加壳技术,App通过自定义的 ApplicationWrapper
类来启动壳程序。
拓展:
梆梆加固原理:
根据APK文件是否在AndroidManifest.xml配置Applicaiton信息,梆梆加固会做不同的处理:
通过上传Applicaiton不同配置的APK文件:
- 当APK配置有Applicaition信息时,梆梆加固重写Application类
- 当APK未配置Application信息时,梆梆加固新建类,并在AndroidManifest.xml中配置自己Application类
详细介绍:梆梆加壳原理 - 徐小鱼 - 博客园
也可以简单来看看"com.SecShell.SecShell.ApplicationWrapper"
中的代码逻辑:
1
2
3
4
5
6
7
8
9
10
11
|
static {
d.a(); // 调用加壳相关的操作
System.loadLibrary( "SecShell" ); // 加载名为 "SecShell" 的本地库
if (Helper.PPATH != null ) {
System.load(Helper.PPATH); // 加载 Helper.PPATH 指定路径的本地库
}
if (Helper.J2CNAME.equals( "SECNEOJ2C" )) {
return ; // 如果 J2CNAME 为 "SECNEOJ2C",则跳过加载其他库
}
System.loadLibrary(Helper.J2CNAME); // 加载 Helper.J2CNAME 指定的本地库
} |
- 该静态代码块是应用启动时的第一个执行逻辑,属于加壳和初始化的关键部分。
-
System.loadLibrary("SecShell")
这行代码加载了一个名为SecShell
的本地库,该库通常是加壳的核心部分。它可能会执行一些关键的安全操作,如检查当前环境是否为调试状态、是否检测到被反编译等。
2.开始使用FRIDA-DEXDump工具进行简单的脱壳:
脱壳原理讲解:深入 FRIDA-DEXDump 中的矛与盾 (qq.com)
思维导图:frida-dexdump脱壳工具简单使用的思维导图 - 『移动安全区』
原理:
- 在进程的内存中搜索dex文件头
- 如果dex头被抹除,则需要开启深度搜索模式,搜索其他关键字段
- 如果dex的文件file_size字段被抹去,就需要搜索dex的尾部字段来判断是否是dex和dex的大小
先在模拟器中运行该APP,使用安卓7.1成功安装app:
将frida传入,启动fridaserver服务后frida才可以正常工作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 将`frida-server`文件推送到设备的`/data/local/tmp`目录 PS C:\Users\Administrator> adb push D:\CTF_Study\Reverse\AndroidWorkSpace\Frida_libc\frida-server-16.1.11-android-x86_64 /data/local/tmp # 进入Android设备的shell环境 PS C:\Users\Administrator> adb shell # 切换到`/data/local/tmp`目录 aosp:/ # cd /data/local/tmp # 查看目录下的文件,确认`frida-server`是否存在 aosp:/data/local/tmp # ls # 赋予`frida-server`文件可执行权限 aosp:/data/local/tmp # chmod 777 ./frida-server-16.1.11-android-x86_64 # 启动Frida Server aosp:/data/local/tmp # ./frida-server-16.1.11-android-x86_64 |
成功脱壳,寻找到两个dex文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 使用frida-dexdump(frida-dexdump -U -f 包名 -o 保存地址) # frida-dexdump -U -p port -o 保存地址 //通过端口 # frida-dexdump -U -n n1book1 -o 保存地址 //通过名字 PS D:\CTF_Study\Reverse\AndroidWorkSpace> frida-dexdump -U -n how_debug -o ./ ... Attaching... INFO:Agent:DexDumpAgent INFO:frida-dexdump:[+] Searching... INFO:frida-dexdump:[*] Successful found 2 dex, used 0 time. INFO:frida-dexdump:[+] Starting dump to './'... INFO:frida-dexdump:[+] DexMd5=5cc5b1ecee503082181d8ddae2f9c115, SavePath=./classes.dex, DexSize=0x1d60b8 INFO:frida-dexdump:[+] DexMd5=28514d4f7ccf16c6bb7ba28602b5d72f, SavePath=./classes02.dex, DexSize=0x446c INFO:frida-dexdump:[*] All done... |
3.开始分析脱壳出来后的DEX文件,成功寻找到activity的代码入口处:
脱壳出来的两个dex文件中偏大的就是我们需要分析的dex了:
直接拖入JADX开始分析工作,脱壳后成功找到了逻辑主要代码,之后就可以继续逆向分析了:
4.开始分析按钮的逻辑代码,成功解析出flag的内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class MainActivity$ 1 implements View$OnClickListener // class@000763 from classes.dex
{ final MainActivity this $ 0 ;
final EditText val$et1;
final EditText val$et2;
void MainActivity$ 1 (MainActivity p0,EditText p1,EditText p2){
this . this $ 0 = p0;
this .val$et1 = p1;
this .val$et2 = p2;
super ();
}
public void onClick(View p0){
String str = this .val$et1.getText().toString();
String str1 = this .val$et2.getText().toString();
if (str.equals(str1)) {
MainActivity.showmsg( "user is equal passwd" );
} else if ((str.equals( "admin" ) & str1.equals( "pass71487" ))){
MainActivity.showmsg( "success" );
MainActivity.showmsg( "flag is flag{borring_things}" );
} else {
MainActivity.showmsg( "wrong" );
}
}
} |
所以得出flag就是flag{borring_things}.
3. 资源与布局文件分析
A.如何寻找一个Activity页面的xml布局文件的位置?
目标: 掌握如何定位和分析 APK 中的资源文件
加固与脱壳学习: 安卓逆向-脱壳学习记录 - Is Yang's Blog
实战案例:攻防世界-基础Android
更详细的WP:
首先按照上文提到的寻找入口点的方法锁定Activity代码实现的位置的onCreate函数:
可以锁定到这个函数:
1 |
setContentView(0x7f04001a); // 加载activity的页面内容,资源文件的id是0x7f04001a |
setContentView()
是 Android 中的一个方法,用于设置当前 Activity 的界面视图(UI)。这个方法通常在 Activity
的 onCreate()
方法中调用,用来加载布局文件,并将其显示在当前界面上。
0x7f04001a
是一个整数值,代表一个资源的 ID。这个资源 ID 对应的是一个 XML 布局文件(通常位于 res/layout/
目录下),在编译过程中由 Android 构建工具自动生成。
所以我们可以手动寻找到布局文件通过jadx的搜索功能寻找到目标资源的名称:
1
2
|
com.example.test.ctf02.R.layout: public static final int acticity_main_1 = 0x7f04001a ;
|
成功获得资源的id和名称,接下来就是在布局文件中寻找了!
成功在res目录下的layout目录下找到了activity'的页面布局文件:
对比一下xml文件和运行后的app界面:
xml布局文件的寻找流程梳理
每个 Activity
在启动时通常会加载一个或多个布局文件。要找到与 Activity
关联的 XML 布局文件,可以按照以下步骤进行:
-
查找
setContentView()
调用:这是在Activity
中加载布局的标准方法。通过反编译的代码中查找setContentView()
,通常会传递一个布局文件的资源 ID。
例如:
1 |
setContentView(R.layout.activity_main); |
通过这个 ID(如 R.layout.activity_main
),你可以定位到 res/layout
目录下的 XML 布局文件。
-
手动分析布局文件:在 APK 中,所有的布局文件都存储在
res/layout
目录下。你可以查看这个目录,找到与Activity
相关联的 XML 文件,并分析其 UI 结构。
B.如何寻找按钮动态绑定和静态绑定的函数是什么?
在分析 Android 应用时,了解按钮(Button)是如何与函数进行绑定的非常重要。按钮的绑定方式通常有两种:动态绑定和静态绑定。
1.按钮动态绑定函数
动态绑定函数是指通过代码在运行时绑定事件处理程序(如点击事件)到 UI 元素(如按钮)上。通常,按钮的动态绑定是通过 setOnClickListener()
方法来实现的,这个方法为按钮设置了点击事件监听器。
目标: 掌握APP是如何动态的为按钮绑定函数的
实战案例:攻防世界-基础Android
更详细的WP:
以攻防世界-基础Android这道题目为例:
首先按照上文提到的寻找入口点的方法锁定Activity代码实现的位置的onCreate函数:
首先,在 onCreate()
方法中,我们可以找到动态绑定的实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class MainActivity extends AppCompatActivity {
private Button login;
private EditText passWord;
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView( 0x7f04001a ); // 加载activity的页面内容,资源文件的id是0x7f04001a
// 动态绑定 EditText 和 Button
this .passWord = (EditText) findViewById( 0x7f0b0055 ); // 获取 EditText 组件
this .login = (Button) findViewById( 0x7f0b0056 ); // 获取 Button 组件
// 给按钮设置点击事件监听器
this .login.setOnClickListener( new View.OnClickListener() {
@Override // android.view.View.OnClickListener
public void onClick(View v) {
String str = MainActivity. this .passWord.getText().toString(); // 获取密码输入框的内容
Check check = new Check(); // 调用 Check 类中的函数
// 处理逻辑...
}
});
}
} |
-
findViewById()
方法用于查找页面中的 UI 组件,这里查找的是Button
和EditText
组件。 -
setOnClickListener()
方法是动态绑定的核心,表示为按钮设置了点击事件监听器。当用户点击按钮时,onClick()
方法会被调用。 - 在
onClick()
方法内部,开发者可以实现点击事件的逻辑,例如获取输入框内容、进行检查等。
2.按钮静态绑定函数
目标: 掌握APP是如何为静态的为按钮绑定函数的
实战案例:BUUCTF在线-FlareOn6_FlareBear
更详细的WP:
静态绑定函数是通过 XML 文件静态地将按钮与事件处理函数关联,在 Android 中通常通过 android:onClick
属性来实现。
以BUUCTF在线-FlareOn6_FlareBear这道题目为例:
首先按照上文提到的如何寻找一个Activity页面的xml布局文件的位置的方法找到页面的xml文件:
可以在这个xml文件中发现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
< Button
android:textSize = "24sp"
android:id = "@+id/button2"
android:layout_width = "wrap_content"
android:layout_height = "80dp"
android:text = "Help"
android:onClick = "showHelp" //该按钮绑定了一个showHelp函数
android:fontFamily = "casual" />
< Button
android:textSize = "24sp"
android:id = "@+id/buttonCredits"
android:layout_width = "wrap_content"
android:layout_height = "80dp"
android:layout_marginLeft = "20dp"
android:text = "@string/text_credits"
android:onClick = "showCredits" //该按钮绑定了一个showCredits函数
android:fontFamily = "casual" />
|
根据函数名称可以在代码中寻找到函数的实现:
- 在 XML 文件中,
android:onClick="onLoginClick"
表示按钮的点击事件将绑定到onLoginClick()
方法。 - 在
Activity
中,onLoginClick()
方法的参数必须是View
类型,它会自动接收点击的视图(在这里是Button
)。 - 按钮的点击事件处理是通过静态绑定(XML 声明)完成的,事件处理函数在代码中定义好,并且由 Android 系统自动调用。
静态绑定 是通过 XML 文件中的 android:onClick
属性,将按钮与方法绑定,在应用启动时,系统会自动为按钮设置监听事件并调用绑定的函数。
3.安卓中按钮的动静态绑定函数的方法梳理
1.按钮动态绑定函数
动态绑定通常通过代码中的 setOnClickListener()
方法进行。以下是一个例子:
1
2
3
4
5
6
7
|
Button btn = findViewById(R.id.my_button); btn.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
// 触发的逻辑
}
}); |
在反编译的代码中,找到 setOnClickListener()
的实现,可以追踪到事件响应的函数。
2.按钮静态绑定函数
静态绑定通常通过在 XML 布局文件中直接指定一个函数来完成。例如:
1
2
3
4
|
< Button
android:id = "@+id/my_button"
android:text = "Click me"
android:onClick = "buttonClicked" />
|
在这个例子中,android:onClick
指定了 Activity
中的 buttonClicked(View view)
函数。反编译后,可以找到该函数并分析其逻辑。