Android基础:逆向准备
由于最近做的项目要求要会安卓逆向分析,于是我就来学学安卓逆向分析,毕竟技多不压身,万一比赛里 web 做不出来还能试试 reverse(当然,我并不希望发生这种情况)。学了一阵安卓开发基础之后发现其实有 Java web 开发的基础学起来就会比较简单。这篇文章总结一下正式学安卓逆向前做的准备工作,主要是安卓开发入门,毕竟学安全前必须学一点简单的开发,学点安卓开发有利于逆向的时候快速定位关键点(虽然有自动工具)。
ADB原理与基本命令
ADB (Android Debug Bridge) 是一个命令行工具,它允许开发者与设备进行通信,以在设备上执行各种操作,如安装和调试应用程序等。它由三个组件组成:
- 客户端:这是在开发机器上运行的命令行工具,您可以通过它发送命令。客户端向 ADB 服务器发送命令,然后由服务器执行这些命令。
- 服务器:它在您的开发机器上作为一个背景进程运行。服务器管理与设备的通信。
- **守护进程 (adbd)**:它在每个设备上作为一个背景进程运行。守护进程在设备上执行命令并返回结果。
使用 adb 操作真机或者虚拟机之前首先要使服务器连接到对应的 adbd ,推荐使用 USB 连接,非常简单,一接上就自动连接,并不需要手动 adb connect。
如果使用局域网连接(同一个 wifi 下),请参考这篇文章:
https://blog.csdn.net/weixin_45858545/article/details/120637586
adb 常见命令:
adb devices
:列出所有连接的设备和模拟器。adb install <path_to_apk>
:在设备上安装 APK。adb uninstall <package_name>
:卸载设备上的应用。adb push <local> <remote>
:将文件从开发机器复制到设备。adb pull <remote> <local>
:将文件从设备复制到开发机器。adb logcat
:查看设备的 logcat 日志。adb shell
:在设备上打开一个 shell。adb kill-server/adb start-server
: 重启 adb 服务器。
另外,adb shell 获得的 shell 执行命令的时候和 linux 命令行基本完全一样,故有必要掌握 linux 的常见命令。
Android常用目录
上面提到获得了 shell ,自然就要翻翻安卓机的目录(注意这里的 shell 已经获得了 root 权限,不然很多文件夹都没法访问,没有 root 的可以刷刷真机或者使用模拟器,模拟器默认就是 root )
data/data 目录,用来存放用户 apk 数据的目录,每个 apk 都有自己的目录,以包名为命名,这是一个私有目录,每一个 app 只能访问自己的目录,除非有 root 权限。
data/app 目录,用户自己安装的app都存放在这个目录下
可以看到直接访问看到的还不是包名,是一串随机生成的 base64,每次重新安装都会再次随机生成,往下访问,可以看到是包名加上一段随机生成的 base64,再往下访问可以看到 base.apk,这个就是这个软件本来的安装包,有时候可以直接拖出来(突然明白怎么获取已安装软件的安装包)。
首先通过 adb shell pm 命令,打印当前手机安装的所有apk的包名,再到 data/app目录下面找对应的 base.apk ,然后使用 adb pull 拖至本地即可。
data/local/tmp ,这是一个临时目录,拥有比较大的权限,很多时候push或者启动软件都可以在这个文件夹中进行,比较不会因为权限产生问题。
system/app 目录,这个目录用来存放系统自带的app,通常删除里面的文件需要root权限。
system/lib 目录和 system/lib64 目录,用来存放 app 中使用到的 so 文件
system/bin 用来存放 shell 命令(和linux一样)
system/framework 目录,存放了 Android系统所用到的框架,如一些 jar 文件
sd 卡目录,不管手机有没有存储卡其实都会有这个目录
访问的方式有很多种:
/sdcard-> /storage/self/primary
/mnt/sdcard
/storage/emulated/0
app使用这个文件夹需要申请权限。
AndroidStudio安装配置
在这个安装和配置上卡了很久。。。记录一下最后是怎么解决问题的,每个人的情况都不一样,仅供参考。
首先如果之前装过,但是有问题,先删干净再装,参考以下文章:
https://blog.csdn.net/Waterme10n/article/details/124251738
官网下载 AndroidStudio 安装包,然后安装的时候如果C盘位置充足建议选择默认安装,选择自定义安装有可能出现等下组件丢失之类的。参考官方安装文章:
如果安装完之后提示 HAXM Installation Failed (我出现了这个问题),可以参考这篇文章进行解决:
经过排查发现之前装 docker 的时候开启了 windows 的虚拟机平台服务,关掉服务重启就行,这个错误会导致你启动不了 AndroidStudio 里面的虚拟设备,当然如果你不需要这个虚拟设备,完全不用管这个错误。
安装完之后打开,先别急着新建项目,先配置一下,再下载一些关键的组件,当然如果已经开了项目也先别运行:
首先找到 Android SDK,直接搜索,有可能版本不同位置不一样,但是肯定有,如果 SDK 路径是空的,选择刚刚默认装的 SDK 位置,next 就行。
有路径之后,下面就会显示装了哪些组件,建议选多几个 SDK Platforms,然后再选一些 SDK Tools,点击 apply 自动等待安装即可:
这时候再点击运行,应该就能自动启动虚拟机然后运行 app 了,默认模版就是可以直接运行的,如果还是报错了,就自己上网查查怎么解决吧(
刚刚提到可以不用虚拟机,毕竟虚拟机吃性能,不用虚拟机就要用真机,可以买一台 pixel 或者 nexus ,不贵的,然后参考下面教程刷个机,换个系统版本并且获取 root 权限:
https://blog.csdn.net/qq_35481726/article/details/127386698?spm=1001.2014.3001.5502
然后下个 QtScrcpy 软件(用来投屏),USB 插上之后在 Android Studio 里面选择这个设备再运行即可。
AndroidStudio Project目录
在目录栏可以选择文件以 app 形式展示还是 project 形式,写过 java 项目的当然还是更喜欢 Project 形式,简单了解一下目录的构成和关键的文件:
这个 .gradle 和 .idea 都是自动生成的不用管,但是这里可以先了解一下 gradle
Gradle简介
Gradle 是一个开源的构建自动化工具,主要用于 Java、Groovy、Kotlin 和其他许多编程语言的项目。它引入了一个基于 Groovy 的特定领域语言(DSL),而不是传统的 XML,来声明项目设置,这使得构建脚本更加简洁和易于理解。
在 Android 开发中,Gradle 被用作默认的构建工具,用于自动化编译、打包、签名和发布应用的过程。Android 的 Gradle 插件添加了许多特定于 Android 的构建任务。
简单来说,Gradle 负责帮你构建 apk 。
可以看到,整个项目外面有一个 build.gradle 负责项目级别的应用构建配置,而 app 文件夹里面那个是模块级别的应用构建配置。
可以看到有很多配置信息。
在 gradle 文件夹下的 gradle-wrapper.properties 可以修改 gradle 的版本,记得和 buildtool 的版本相对应,不然可能会发生链接错误。
修改这个数字,然后 gradle sync 同步即可
另一个重要的文件夹就是 app 文件夹:
build 文件夹里面放了构建之后的输出,比如选择构建一个 apk ,apk 文件就会放在里面。
libs 文件夹里面用来放各种 so 文件(C或C++链接库)。
src 文件夹里面的 main 里面的 java 文件夹用来放 java 类文件,就是我们平时写代码的地方。
src 文件夹里面的 res 里面放的是一堆 xml 文件,用来对我们的 app 外形进行控制,需要关注的是 layout 文件夹下的 activity_main.xml ,其定义了我们 app 的主要页面的外形,并提供了两种查看方式,可以图形化看,通过拖拽控件来调整,也可以看代码版。
这些常用的控件的使用方法下文也会具体说明。
src 文件夹下面还有一个重要的文件:AndroidManifest.xml,这个文件下文也会具体说明。
了解这个 AndroidStudio Project目录结构很有意义,因为安卓的 app 基本都是用这个工具开发的,到时候反汇编出来项目结构也是这样的。
AndroidManifest.xml文件
AndroidManifest.xml
是 Android 应用程序的中枢,它包含了关于应用程序的必要信息,Android 系统必须先读取这些信息,才能运行任何应用程序中的代码。以下是一个一般性的 AndroidManifest.xml
1 |
|
节点解析
根节点是 manifest,根节点的 package 属性指定了包名,根节点下面又有若干子节点,
user-premission 声明 app 运行需要的权限
application 节点用来指定 app 自身属性,比较重要的是 android:name ,这是一个可选属性,一般加固的应用都会有这个,这里定义的类比activity里定义的类更先执行(也就是实际意义上的入口类)。
application 节点中还有若干的子节点,比如四大组件的注册(活动(<activity>
)、服务(<service>
)、广播接收者(<receiver>
)和内容提供者(<provider>
)),这里先主要分析用的最多的 activity ,其他的日后遇到再介绍。
activity 标签表示一个应用程序的单个屏幕,也就是用户可以与之交互的一个界面
<activity>
元素定义了一些重要的属性:
android:name
:这是 Activity 的全名,包括包名。如果 Activity 位于应用程序的根包下,那么可以使用.ActivityName
的形式。也就是说这个 Activity 所对应的 java 类android:label
:定义了 Activity 在设备的启动器中显示的标题。通常,这是一个指向字符串资源的引用。
<intent-filter>
元素是<activity>
元素的子元素,它用于指定 Activity 可以响应的 Intent 。这个<intent-filter>
元素包含了两个子元素:
<action android:name="android.intent.action.MAIN" />
:这个元素表示 Activity 应该被视为应用程序的主入口点,也就是说,用户可以直接从设备的启动器启动这个 Activity 。<category android:name="android.intent.category.LAUNCHER" />
:这个元素表示这个 Activity 是应用的入口点,也就是说,这个 Activity 将出现在设备的应用启动器中,用户可以从启动器中点击应用的图标来启动这个 Activity 。
突然想起来蓝帽杯 apk 取证里面就是通过搜索这两来寻找入口类。安卓程序执行的入口通常是被标记为主(Main)和启动器(Launcher)的 Activity。
根据需要,<activity>
标签还可以包含其他属性,如android:theme
(定义Activity的主题)、android:screenOrientation
(定义Activity的屏幕方向)、android:configChanges
(定义Activity如何响应设备配置的更改)等,但是这些属性对我们逆向来说都不重要。
回调方法
在 Android 应用程序中,Activity 的生命周期由一系列的回调方法控制。当用户打开应用程序时,以下的回调方法将按照顺序被调用:
onCreate()
: 这个方法在 Activity 第一次被创建时调用。你通常会在这个方法中进行初次初始化,比如创建用户界面,绑定数据到列表,初始化类成员等。onStart()
: 这个方法在 Activity 对用户可见之前调用,但用户还不能与其交互。在这个生命周期方法中,应用可以继续或开始进行一些用户可见的行为。onResume()
: 这个方法在 Activity 准备好与用户进行交互时调用。此时, Activity 位于应用程序堆栈的顶部,并且具有用户输入焦点。
当你的 Activity 正在运行时,如果发生某些事件(比如用户按下了 Home 键,或者有另一个 Activity 被启动),系统可能会调用其他的生命周期方法,如onPause()
和onStop()
,来暂停或停止你的 Activity 。如果你的 Activity 再次回到前台,onRestart()
, onStart()
, 和 onResume()
将会被再次调用。
MainActivity.java 的 onCreate 方法如下,创建这个 activity 的时候就会自动调用。
另外,这个 setContentView 方法就是用来呈现页面的,千万不要在这个方法前加一些需要用户交互的方法,否则直接启动失败(入口页面缺失)。
基本控件的使用
通过直接拖拽来在页面布局中添加控件,右边可以调整具体样式,同时可以看到代码里面多了一段对该控件属性的描述,需要关注的是 android:id,以后可以在 java 类中通过 findViewById(R.id.(android:id))
来找到这个对象,并进行事件的绑定或者增加属性。
常用的基本控件包括:
TextView: 用于显示文本。
1
2
3
4
5
6<TextView
android:id="@+id/my_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, World!"
/>在 Java 或 Kotlin 代码中设置文本:
1
2TextView textView = findViewById(R.id.my_textview);
textView.setText("Hello, Android!");ImageView: 用于显示图片。
1
2
3
4
5
6<ImageView
android:id="@+id/my_imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/my_image" // Assume you have an image named "my_image" in res/drawable
/>在 Java 或 Kotlin 代码中设置图片:
1
2ImageView imageView = findViewById(R.id.my_imageview);
imageView.setImageResource(R.drawable.another_image); // Assume you have another image named "another_image" in res/drawableButton: 用于执行点击操作。
1
2
3
4
5
6<Button
android:id="@+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click me"
/>在 Java 或 Kotlin 代码中设置点击事件:
1
2
3
4
5
6
7Button button = findViewById(R.id.my_button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Handle button click
}
});EditText: 用于输入文本。
1
2
3
4
5
6<EditText
android:id="@+id/my_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter text here"
/>在 Java 或 Kotlin 代码中获取输入:
1
2EditText editText = findViewById(R.id.my_edittext);
String input = editText.getText().toString();CheckBox: 用于表示选中/未选中状态。
1
2
3
4
5
6<CheckBox
android:id="@+id/my_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Check me"
/>在 Java 或 Kotlin 代码中检查状态:
1
2CheckBox checkBox = findViewById(R.id.my_checkbox);
boolean isChecked = checkBox.isChecked();
后记
安卓基础的入门就先到这了,下次再研究安卓逆向就可以开始抓包了,这几天学到了很多新东西,也能锻炼到自主学习和解决问题的能力。接着投身 web 大业,有时间再来接着研究安卓。
Android基础:逆向准备