# 基础知识

本章为您介绍安卓自动化相关的基础知识，请务必看完本章节的描述，后面不再赘述。安卓自动化与常规网页自动化存在很大的区别但是也有很多共性。在常规的网页自动化中，可以很简单的通过 F12 开发者工具来查看到网页的布局以及元素ID等信息，通过 XPATH 继而获取元素进行点击等待等操作。当然在安卓中也是类似的逻辑，您也可以通过名叫选择器的东西来选定元素并进行点击判断等操作，所以您不必担心会难以入门。

## 移动与网页自动化的共性与区别

移动端的自动化与网页之间有很多共性，当然也有很多区别。我们以 selenium 举例，通常，使用 selenium 进行网页控制，需要您具备三样东西，其一 浏览器，其二 webdriver，其三 selenium。当然，移动端也是一样的，移动端的手机就相当于浏览器，FIRERPA 就相当于 webdriver，FIRERPA 的 Python 库，也就是 lamda 相当于 selenium，并且他们目标一致，均是执行模拟用户操作，实现测试、数据采集或任务自动执行。并且均是通过脚本驱动、对元素进行定位点击截图判断等操作。这么一看其实还是挺相像的。

但他们也是不同的，首先移动端的自动化需要您具备一台手机和一台电脑，而网页自动化在您自己的电脑上即可进行。并且他们使用的并不是一套方案，对于网页端，常见的工具如 Selenium、Puppeteer、Playwright。对于移动端，通常则是 FIRERPA、AutoJS、Appium、uiautomator 之类。

对于网页端，常见的定位方式主要是基于 HTML DOM 结构的 xpath 或是 csspath，元素层级相对直观。而对于移动端，常见的定位方式则是选择器，当然，安卓应用界面也使用 XML 布局，您也可以通过 XML 进行 xpath 选择。通常，网页端自动化无需考虑太多的兼容性问题，大部分情况下可以通过固定浏览器以及启动分辨率大小来绕过大部分因设备产生的兼容性问题。但对于安卓端，由于各种品牌型号的设备各有不同，屏幕大小，系统版本等均可能对自动化代码的兼容性产生影响。但是也请不要害怕，虽然有影响但是不大。

## 各种自动化工具的区别

上文所说，我们举例的几种常用安卓端的自动化工具之间也有很多区别，我们先表明立场，FIRERPA 是所有自动化工具中最稳定，功能最全，最强大，最适合项目化管理和应用的。

```{note}
我们的立场不是偏见，而是基于 6 年来不断的探索优化而形成，您走的路线踩的坑我们基本都走过。
```

::: tabs
@tab AutoJS

常用的 AutoJS 等相关衍生产品，属于自身控制自身类，需要在设备上安装 APK 并通过 js 编写脚本进行操作，通常 AutoJS 只能进行自动化级别的操作，优点是适合新手或者业余使用，入门门槛低。但是其本身的设计不适合用于大规模的脚本控制，管理，更新。处于去中心化，无管理的状态。无法进行精确的大规模控制。

@tab Appium

测试人员常用的 Appium，属于 C/S 架构，相对来说则比 AutoJS 更适合进行集群控制，但是其有明显的缺点，由于其适用于大部分系统的自动化，除了支持安卓外也支持 IOS，导致体积庞大臃肿，非常不适合大规模的部署使用。

@tab u2

最后 uiautomator2，也属 C/S 架构，相比 Appium 来说更加精确，达到了足够精简的地步，没有太多多余的东西，功能也都刚刚好。但是我们为什么抛弃，主要原因是在多设备情况下不是很稳定。其次，自动安装相关逻辑适合新手但是对于专业的集群控制，显得多余不容易掌控，并且没有维护。

当然，他们都是很适合常规使用的。但是偏偏我们走的不是很常规，因为在业务中，通常是不可能只进行自动化。就例如现在有个任务，测试某 APP，需要将请求和响应，请求时间等记录在案。想一想您会怎么做，我认为您的方案里，要不是包含很多额外的手动操作，要不就是极具不稳定或兼容性。而在 FIRERPA 的世界里，您可以将所有的操作都使用代码进行，您所接触的只有代码，稳定性和兼容性，交给 FIRERPA。其实我们不应该和这些进行对比，因为在功能上，FIRERPA 是上面所有方案的超集，它包含了我们所有踩过的坑走过的路。
:::

## 自动化基本流程

通常您需要预研方案，是只进行常规自动化还是需要自动化的同时进行应用运行数据攫取。通常情况下，您有两种方案进行数据攫取，其一 通过中间人进行 HTTP/s 通信截获，其二 通过 Hook 进行数据截获，中间人方式较为简单适合常规使用，但是对部分应用可能不起作用。Hook 方案需要大量的逆向知识，入门困难不适合新手，适合在边缘场合使用。

### 中间人攫取

中间人方案较为简单，您只需要在文档中找到 安装中间人证书、设置代理的相关内容，配合 mitmproxy 即可实现。如果您完全不清楚，您可以再参考我们官方的 startmitm.py 脚本，里面已经为您写好了所有的逻辑您可以随时复制或者复用。

### Hook 攫取

Hook 方案需要您具备初级及以上的逆向相关能力，如果此前并未听过，则当前您无需考虑此方案。总的来说 Hook 方案就是通过编写 frida 脚本，钩住相关的函数调用，获取参数获返回值并提交，注入应用这些流程。您可在 使用 Frida 上报数据 章节了解简单 Demo 和使用方法。

### 自动化代码

当然自动化代码也是不可或缺的一员，因为您需要通过自动化来触发相关逻辑。对于编写自动化代码，通常您应该是如下的流程。首先您应该打开 FIREPRA 的远程桌面。您应该看到如下的界面。

![远程桌面](/assets/images/frida-remote-desktop.png)

现在，请打开您想要自动化操作的 APP，随后点击远程桌面右上角的 小眼睛 图标，您将会看到如下的界面，此时选择您想要操作的元素，点击即可查看元素信息。

```{tip}
当然也能用代码打开，这些后面都会写的。
```

![选择元素](/assets/images/auto-eyeselect.png)

您可以看到，右侧的元素信息，比如 text、resourceId 等信息，现在我们想点击这个元素，那么编写如下代码，这段代码的意义是 "点击 text 为 '同意' 的元素"。

```python
d(text="同意").click()
```

```{note}
这只是示例，选择器写法有很多，示例只介绍最简单的一种写法。
```

好了，最简单的写法您已经了解了，现在请编写 if else 等控制逻辑，配合 exists 等等其他接口，您就可以实现一整个自动化操作流程了，看吧，没这么难。

## 界面布局检视

正常情况下，编写自动化代码离不开界面布局检视，这也是您获取选择器条件的唯一路径，首先您需要浏览器打开设备的远程桌面。随后点击远程桌面右上角的眼睛图标进入布局检视，此时您可以点击左侧屏幕上的虚线框来查看对应元素的信息，您可以将其中的属性作为选择器的参数。再次点击眼睛图标将关闭布局检视，布局检视并不会随着页面的改变而刷新，它始终是您按下快捷键那一刻的屏幕布局，如果需要刷新布局请手动按下快捷键 `CTRL + R`。

![检视元素](/assets/images/inspect-demo.gif)

```{hint}
您也可以在布局检视界面按下 TAB 键来遍历查看所有元素。
```

## 界面选择器

界面选择器 Selector 用于操作安卓元素，您也可以将其理解为 Xpath 规则，虽然不同但是大概的用途都是相同的，在 FIRERPA 中，选择器为 `Selector`，大部分情况下，您不需要直接接触到这个类。在上文中，您应该见过这个东西。完整的他包含了如下的可选参数。

| 匹配类型              | 说明            |
|----------------------|---------------|
| text              | 文本完全匹配   |
| textContains      | 文本包含匹配   |
| textStartsWith    | 文本起始匹配   |
| className         | 类名匹配       |
| description       | 描述完全匹配   |
| descriptionContains | 描述包含匹配 |
| descriptionStartsWith | 描述起始匹配 |
| clickable         | 可以点击       |
| longClickable     | 可以长按       |
| scrollable        | 可滚动         |
| resourceId        | 资源ID匹配     |

大部分情况下只使用 resourceId, text, description, textContains 等作为参数，如果元素存在正常的 resourceId，优先使用其作为 Selector，如 `d(resourceId="com.xxx:id/mobile_signal")`。否则则会使用 text，如 `d(text="点击进入")`，或者更模糊一点 `d(textContains="点击")`，description 与 text 同理，但是 description 用的会比较少。

```{hint}
选择器 Selector 是由您通过上文界面布局监视功能获得的相关主要参数拼构而成。
```

## 屏幕坐标定义

自动化操作的过程中难免会碰到需要通过详细坐标或者区域坐标进行操作的情况。但是由于很多人可能并不是很清楚坐标问题所以我们在此介绍一下安卓的屏幕坐标知识。

众所周知，图片有分辨率大小，屏幕也有。对于安卓的屏幕，不论是横屏竖屏亦或是自动旋转的屏幕，统一将左上角作为原点 0,0 并且向右以及向下扩展的坐标系，X 为横轴，Y 为竖轴，如图。

<p align="center">
<img src="/assets/images/screen-rect.png" alt="屏幕坐标" width="200">
</p>

由上图可知，屏幕左上角的坐标为 0,0，右上角的坐标为 1080,0，左下角为 0,1920，右下角为 1080,1920，您可以根据这个信息来换算屏幕上任意坐标点的坐标。

```{note}
无论屏幕原生是竖屏还是横屏，亦或是自动旋转，统一以屏幕当前方向的左上角作为原点。
```

### 屏幕上的点

在 FIRERPA 中，有两种关于屏幕的定义，一些操作如点击或者截图等需要您提供区域或坐标信息。对于常见的坐标点，我们使用如下定义，此定义代表屏幕坐标为 100,100 的点。

```python
Point(x=100, y=100)
```

### 区域的定义

区域的定义为屏幕上一片矩形的区域，他的定义稍微繁琐请认真阅读。我们使用 Bound 来代表屏幕上的一块区域，他需要您提供四个参数，分别为 `top`，`left`，`bottom`，`right`。您可能会有点迷惑，下面请务必仔细理解：`top` 代表矩形顶部距离屏幕顶部的像素距离，`left` 代表矩形左边距离屏幕左边的像素距离，`right` 代表矩形右边距离屏幕左边的距离，`bottom` 代表矩形底部距离屏幕顶部的距离。总之您可以理解所有距离均是以原点辐射的 XY 轴的距离。下面我们使用一张图来配合您的理解，手机的屏幕仍然是 1080x1920，设备当前为竖屏状态。

<p align="center">
<img src="/assets/images/screen-bound.png" alt="屏幕区域" width="200">
</p>

现在我们假设屏幕是四等分的，需要获取如图所示的左上右下两个区域的定义。根据规则，区域 1 矩形顶部距离屏幕顶部 0 像素，矩形左边距离屏幕左边 0 像素，矩形底部距离屏幕顶部 960 像素（1920÷2），矩形右边距离屏幕左边 540 像素（1080÷2），所以他的定义应该为

```python
Bound(top=0, left=0, right=540, bottom=960)
```

同理，区域 2 的矩形顶部距离屏幕顶部 960 个像素，矩形左边距离屏幕左边 540 个像素，矩形右边距离屏幕左边 1080 个像素，矩形顶部距离屏幕顶部 1920 个像素，可知第二个矩形定义为


```python
Bound(top=960, left=540, right=1080, bottom=1920)
```


## 安卓应用的数据

每个安卓应用在设备上都有专用的目录用于存放应用的数据，通常情况下应用的相关数据存储于 /data 目录下，您可以通过调用 d.application("com.example").info() 接口来获取应用的数据目录，大部分情况下，您也可以直接 cd 至目录 /data/user/0/com.example.test 来直接切到用户目录。除了常规的 /data 目录外，部分应用还会再 /sdcard/Android 目录存储多媒体等其他文件。

### 查看短信数据库

有时候，您可能想看看本机收到的短信都存储在哪个位置，非常好的想法，而且非常简单，甚至通过编写 extension 您可以直接读取内容并通过 HTTP 接口实时获取！
我们按照常规安卓的思路来，如果您的不同，请自行发散思维。在安卓上，短信应用的名称应该为 com.android.mms，所以我们可以直接切换到目录 /data/user/0/com.android.mms。通过我们下面的操作，您可以看到 databases 目录下有几个数据库，这里的 mmssms.db 即为我们的目标。

```text
 λ 10:12 /data/user/0/com.android.mms ➥ ls -la
total 82
drwx------    7 u0_a78   u0_a78        3452 Jan  2  2021 .
drwxrwx--x  381 system   system       53248 May  2 16:46 ..
drwxrws--x    3 u0_a78   u0_a78_c      3452 Jan  2  2021 cache
drwxrws--x    2 u0_a78   u0_a78_c      3452 Jan  2  2021 code_cache
drwxrwx--x    2 u0_a78   u0_a78        3452 Jan  2  2021 databases
drwxrwx--x    7 u0_a78   u0_a78       24576 Feb 26 13:43 files
drwxrwx--x    2 u0_a78   u0_a78        3452 May  4 10:12 shared_prefs
 λ 10:12 /data/user/0/com.android.mms ➥ ls -l databases/
total 504
-rw-rw----    1 u0_a78   u0_a78       24576 Jan  2  2021 dynamic_bubble
-rw-------    1 u0_a78   u0_a78           0 Jan  2  2021 dynamic_bubble-journal
-rw-rw----    1 u0_a78   u0_a78      491520 Feb 27 04:18 mmssms.db
-rw-------    1 u0_a78   u0_a78           0 Jan  2  2021 mmssms.db-journal
 λ 10:12 /data/user/0/com.android.mms ➥
 ```

当然读取非常简单，因为在安卓下常规的应用数据库均为 SQLite，不过一些安全性较高的应用通常自身数据库也会加密，当然 FireRPA 这么强怎么会没有呢，FireRPA 除了可以读取常规 SQLite，同时支持**实时读取**微信 (sqlcipher) aes-256、企业微信 aes-128、阿里系 sqlcrypto (aes-128) 等各种类型的数据库（当然前提是您需要自己找到秘钥）。下面我们进为您简单演示读取系统的短信内容。非常简单，一条命令即可，当然你也可以编写 extension 读取。

```bash
sqlite3 databases/mmssms.db .dump
```

当然，输出是一大堆，不过您可以在里面快速找到需要数据所在的表，然后自行 SQL 就行。这个方法适用于 98% 的安卓应用，剩下 2% 是加密数据库。

### 查看加密数据库

对于这些加密了的数据库。需要您自己找到数据库秘钥或者其计算生成方式。下面为您简单介绍如何读取相关软件的数据库，我们只介绍如何使用 PRAGMA 预设秘钥，如果您不明白这是什么请先去了解 sqlite。

> 微信系 (sqlcipher)

```sql
PRAGMA cipher = "sqlcipher";
PRAGMA legacy = 1;
PRAGMA key = "database-key";
```

> 企业微信 (wxsqlite)

```sql
PRAGMA cipher = "aes128cbc";
PRAGMA hexkey = "database-key"
```

> 阿里系 (sqlcrypto)

```sql
PRAGMA cipher = "sqlcrypto";
PRAGMA key = "database-key"
```

```{hint}
安卓应用的数据库也不一定是非在 databases 目录下哦。
```

### 查看其他数据

当然，应用数据目录里可不止有数据库，其中还包含一些应用相关参数、配置、缓存、文件，如 shared_prefs (xml) 等，不过我们不过多介绍，您可以自行探索。

## 自动化辅助措施

在自动化业务中，并不是所有应用都适合使用选择器进行定位，有些界面如游戏由于是实时渲染，并不存在安卓层面的页面布局。所以对于此类应用，您只能通过 OCR 或者图像匹配进行操作判别。我们提供了完整的 OCR 辅助方案以及内置的图像 SIFT、模板匹配方案用以辅助实现这些业务目标。您可以在文档中找到相关的接口以及其使用方法。

正在更新...
