Android基础:逆向准备

文章发布时间:

最后更新时间:

文章总字数:
4k

预计阅读时间:
15 分钟

Android基础:逆向准备

由于最近做的项目要求要会安卓逆向分析,于是我就来学学安卓逆向分析,毕竟技多不压身,万一比赛里 web 做不出来还能试试 reverse(当然,我并不希望发生这种情况)。学了一阵安卓开发基础之后发现其实有 Java web 开发的基础学起来就会比较简单。这篇文章总结一下正式学安卓逆向前做的准备工作,主要是安卓开发入门,毕竟学安全前必须学一点简单的开发,学点安卓开发有利于逆向的时候快速定位关键点(虽然有自动工具)。

ADB原理与基本命令

ADB (Android Debug Bridge) 是一个命令行工具,它允许开发者与设备进行通信,以在设备上执行各种操作,如安装和调试应用程序等。它由三个组件组成:

  1. 客户端:这是在开发机器上运行的命令行工具,您可以通过它发送命令。客户端向 ADB 服务器发送命令,然后由服务器执行这些命令。
  2. 服务器:它在您的开发机器上作为一个背景进程运行。服务器管理与设备的通信。
  3. **守护进程 (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 )

  1. data/data 目录,用来存放用户 apk 数据的目录,每个 apk 都有自己的目录,以包名为命名,这是一个私有目录,每一个 app 只能访问自己的目录,除非有 root 权限。

    image-20230919215006449

  2. data/app 目录,用户自己安装的app都存放在这个目录下

    image-20230919215823579

    可以看到直接访问看到的还不是包名,是一串随机生成的 base64,每次重新安装都会再次随机生成,往下访问,可以看到是包名加上一段随机生成的 base64,再往下访问可以看到 base.apk,这个就是这个软件本来的安装包,有时候可以直接拖出来(突然明白怎么获取已安装软件的安装包)。

    首先通过 adb shell pm 命令,打印当前手机安装的所有apk的包名,再到 data/app目录下面找对应的 base.apk ,然后使用 adb pull 拖至本地即可。

  3. data/local/tmp ,这是一个临时目录,拥有比较大的权限,很多时候push或者启动软件都可以在这个文件夹中进行,比较不会因为权限产生问题。

  4. system/app 目录,这个目录用来存放系统自带的app,通常删除里面的文件需要root权限。

  5. system/lib 目录和 system/lib64 目录,用来存放 app 中使用到的 so 文件

  6. system/bin 用来存放 shell 命令(和linux一样)

  7. system/framework 目录,存放了 Android系统所用到的框架,如一些 jar 文件

    image-20230919221907675

  8. sd 卡目录,不管手机有没有存储卡其实都会有这个目录

    访问的方式有很多种:

    /sdcard-> /storage/self/primary

    /mnt/sdcard

    /storage/emulated/0

    app使用这个文件夹需要申请权限。

AndroidStudio安装配置

在这个安装和配置上卡了很久。。。记录一下最后是怎么解决问题的,每个人的情况都不一样,仅供参考。

首先如果之前装过,但是有问题,先删干净再装,参考以下文章:

https://blog.csdn.net/Waterme10n/article/details/124251738

官网下载 AndroidStudio 安装包,然后安装的时候如果C盘位置充足建议选择默认安装,选择自定义安装有可能出现等下组件丢失之类的。参考官方安装文章:

https://developer.android.com/codelabs/basic-android-kotlin-compose-install-android-studio?hl=zh-cn#2

如果安装完之后提示 HAXM Installation Failed (我出现了这个问题),可以参考这篇文章进行解决:

https://www.tektutorialshub.com/android-studio/haxm-installation-failed-install-intel-haxm-in-windows/

经过排查发现之前装 docker 的时候开启了 windows 的虚拟机平台服务,关掉服务重启就行,这个错误会导致你启动不了 AndroidStudio 里面的虚拟设备,当然如果你不需要这个虚拟设备,完全不用管这个错误。

安装完之后打开,先别急着新建项目,先配置一下,再下载一些关键的组件,当然如果已经开了项目也先别运行:

首先找到 Android SDK,直接搜索,有可能版本不同位置不一样,但是肯定有,如果 SDK 路径是空的,选择刚刚默认装的 SDK 位置,next 就行。

image-20230922171112957

有路径之后,下面就会显示装了哪些组件,建议选多几个 SDK Platforms,然后再选一些 SDK Tools,点击 apply 自动等待安装即可:

image-20230922171516357

这时候再点击运行,应该就能自动启动虚拟机然后运行 app 了,默认模版就是可以直接运行的,如果还是报错了,就自己上网查查怎么解决吧(

刚刚提到可以不用虚拟机,毕竟虚拟机吃性能,不用虚拟机就要用真机,可以买一台 pixel 或者 nexus ,不贵的,然后参考下面教程刷个机,换个系统版本并且获取 root 权限:

https://blog.csdn.net/qq_35481726/article/details/127386698?spm=1001.2014.3001.5502

然后下个 QtScrcpy 软件(用来投屏),USB 插上之后在 Android Studio 里面选择这个设备再运行即可。

image-20230922172326030

AndroidStudio Project目录

在目录栏可以选择文件以 app 形式展示还是 project 形式,写过 java 项目的当然还是更喜欢 Project 形式,简单了解一下目录的构成和关键的文件:

image-20230922180756151

这个 .gradle 和 .idea 都是自动生成的不用管,但是这里可以先了解一下 gradle

Gradle简介

Gradle 是一个开源的构建自动化工具,主要用于 Java、Groovy、Kotlin 和其他许多编程语言的项目。它引入了一个基于 Groovy 的特定领域语言(DSL),而不是传统的 XML,来声明项目设置,这使得构建脚本更加简洁和易于理解。

在 Android 开发中,Gradle 被用作默认的构建工具,用于自动化编译、打包、签名和发布应用的过程。Android 的 Gradle 插件添加了许多特定于 Android 的构建任务。

简单来说,Gradle 负责帮你构建 apk 。

可以看到,整个项目外面有一个 build.gradle 负责项目级别的应用构建配置,而 app 文件夹里面那个是模块级别的应用构建配置。

image-20230922185956850

可以看到有很多配置信息。

在 gradle 文件夹下的 gradle-wrapper.properties 可以修改 gradle 的版本,记得和 buildtool 的版本相对应,不然可能会发生链接错误。

image-20230922193635246

修改这个数字,然后 gradle sync 同步即可

另一个重要的文件夹就是 app 文件夹:

build 文件夹里面放了构建之后的输出,比如选择构建一个 apk ,apk 文件就会放在里面。

libs 文件夹里面用来放各种 so 文件(C或C++链接库)。

src 文件夹里面的 main 里面的 java 文件夹用来放 java 类文件,就是我们平时写代码的地方。

src 文件夹里面的 res 里面放的是一堆 xml 文件,用来对我们的 app 外形进行控制,需要关注的是 layout 文件夹下的 activity_main.xml ,其定义了我们 app 的主要页面的外形,并提供了两种查看方式,可以图形化看,通过拖拽控件来调整,也可以看代码版。

image-20230922194806928

这些常用的控件的使用方法下文也会具体说明。

src 文件夹下面还有一个重要的文件:AndroidManifest.xml,这个文件下文也会具体说明。

了解这个 AndroidStudio Project目录结构很有意义,因为安卓的 app 基本都是用这个工具开发的,到时候反汇编出来项目结构也是这样的。

AndroidManifest.xml文件

AndroidManifest.xml 是 Android 应用程序的中枢,它包含了关于应用程序的必要信息,Android 系统必须先读取这些信息,才能运行任何应用程序中的代码。以下是一个一般性的 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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApp">

<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>

节点解析

根节点是 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 的生命周期由一系列的回调方法控制。当用户打开应用程序时,以下的回调方法将按照顺序被调用:

  1. onCreate(): 这个方法在 Activity 第一次被创建时调用。你通常会在这个方法中进行初次初始化,比如创建用户界面,绑定数据到列表,初始化类成员等。
  2. onStart(): 这个方法在 Activity 对用户可见之前调用,但用户还不能与其交互。在这个生命周期方法中,应用可以继续或开始进行一些用户可见的行为。
  3. onResume(): 这个方法在 Activity 准备好与用户进行交互时调用。此时, Activity 位于应用程序堆栈的顶部,并且具有用户输入焦点。

当你的 Activity 正在运行时,如果发生某些事件(比如用户按下了 Home 键,或者有另一个 Activity 被启动),系统可能会调用其他的生命周期方法,如onPause()onStop(),来暂停或停止你的 Activity 。如果你的 Activity 再次回到前台,onRestart(), onStart(), 和 onResume() 将会被再次调用。

MainActivity.java 的 onCreate 方法如下,创建这个 activity 的时候就会自动调用。

image-20230922200609572

另外,这个 setContentView 方法就是用来呈现页面的,千万不要在这个方法前加一些需要用户交互的方法,否则直接启动失败(入口页面缺失)。

基本控件的使用

通过直接拖拽来在页面布局中添加控件,右边可以调整具体样式,同时可以看到代码里面多了一段对该控件属性的描述,需要关注的是 android:id,以后可以在 java 类中通过 findViewById(R.id.(android:id)) 来找到这个对象,并进行事件的绑定或者增加属性。

常用的基本控件包括:

  1. 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
    2
    TextView textView = findViewById(R.id.my_textview);
    textView.setText("Hello, Android!");
  2. 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
    2
    ImageView imageView = findViewById(R.id.my_imageview);
    imageView.setImageResource(R.drawable.another_image); // Assume you have another image named "another_image" in res/drawable
  3. Button: 用于执行点击操作。

    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
    7
    Button button = findViewById(R.id.my_button);
    button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    // Handle button click
    }
    });
  4. 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
    2
    EditText editText = findViewById(R.id.my_edittext);
    String input = editText.getText().toString();
  5. 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
    2
    CheckBox checkBox = findViewById(R.id.my_checkbox);
    boolean isChecked = checkBox.isChecked();

后记

安卓基础的入门就先到这了,下次再研究安卓逆向就可以开始抓包了,这几天学到了很多新东西,也能锻炼到自主学习和解决问题的能力。接着投身 web 大业,有时间再来接着研究安卓。