# 基礎知識

本章為您介紹 Android 自動化相關的基礎知識，請務必看完本章節的描述，後面不再贅述。Android 自動化與常規網頁自動化存在很大的區別，但是也有很多共通點。在常規的網頁自動化中，可以很簡單地透過 F12 開發者工具來查看到網頁的佈局以及元素 ID 等資訊，透過 XPATH 進而獲取元素進行點擊、等待等操作。當然在 Android 中也是類似的邏輯，您也可以透過名叫選擇器的東西來選定元素並進行點擊、判斷等操作，所以您不必擔心會難以入門。

## 行動與網頁自動化的共通點與區別

行動端的自動化與網頁之間有很多共通點，當然也有很多區別。我們以 Selenium 舉例，通常，使用 Selenium 進行網頁控制，需要您具備三樣東西：其一，瀏覽器；其二，WebDriver；其三，Selenium。當然，行動端也是一樣的，行動端的手機就相當於瀏覽器，FIRERPA 就相當於 WebDriver，FIRERPA 的 Python 函式庫，也就是 lamda 相當於 Selenium，並且它們目標一致，均是執行模擬使用者操作，實現測試、資料擷取或任務自動執行。並且均是透過腳本驅動、對元素進行定位、點擊、截圖、判斷等操作。這麼一看其實還是挺相像的。

但它們也是不同的，首先行動端的自動化需要您具備一台手機和一台電腦，而網頁自動化在您自己的電腦上即可進行。並且它們使用的並不是一套方案，對於網頁端，常見的工具如 Selenium、Puppeteer、Playwright。對於行動端，通常則是 FIRERPA、AutoJS、Appium、uiautomator 之類。

對於網頁端，常見的定位方式主要是基於 HTML DOM 結構的 XPath 或是 CSS Path，元素層級相對直觀。而對於行動端，常見的定位方式則是選擇器，當然，Android 應用介面也使用 XML 佈局，您也可以透過 XML 進行 XPath 選擇。通常，網頁端自動化無需考慮太多的相容性問題，大部分情況下可以透過固定瀏覽器以及啟動解析度大小來繞過大部分因裝置產生的相容性問題。但對於 Android 端，由於各種品牌型號的裝置各有不同，螢幕大小、系統版本等均可能對自動化程式碼的相容性產生影響。但是也請不要害怕，雖然有影響但是不大。

## 各種自動化工具的區別

上文所說，我們舉例的幾種常用 Android 端的自動化工具之間也有很多區別，我們先表明立場，FIRERPA 是所有自動化工具中最穩定，功能最全，最強大，最適合專案化管理和應用的。

```{note}
我們的立場不是偏見，而是基於 6 年來不斷的探索優化而形成，您走的路線踩的坑我們基本都走過。
```

### AutoJS

常用的 AutoJS 等相關衍生產品，屬於自我控制類型，需要在裝置上安裝 APK 並透過 JS 編寫腳本進行操作，通常 AutoJS 只能進行自動化級別的操作，優點是適合新手或業餘使用，入門門檻低。但是其本身的設計不適合用於大規模的腳本控制、管理、更新。處於去中心化，無管理的狀態。無法進行精確的大規模控制。

### Appium

測試人員常用的 Appium，屬於 C/S 架構，相對來說則比 AutoJS 更適合進行叢集控制，但是其有明顯的缺點，由於其適用於大部分系統的自動化，除了支援 Android 外也支援 iOS，導致體積龐大臃腫，非常不適合大規模的部署使用。

### 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 等等其他 API，您就可以實現一整個自動化操作流程了，看吧，沒這麼難。

## 介面佈局檢視

正常情況下，編寫自動化程式碼離不開介面佈局檢視，這也是您獲取選擇器條件的唯一路徑。首先您需要瀏覽器打開裝置的遠端桌面。隨後點擊遠端桌面右上角的眼睛圖示進入佈局檢視，此時您可以點擊左側螢幕上的虛線框來查看對應元素的資訊，您可以將其中的屬性作為選擇器的參數。再次點擊眼睛圖示將關閉佈局檢視，佈局檢視並不會隨著頁面的改變而刷新，它始終是您按下快捷鍵那一刻的螢幕佈局，如果需要刷新佈局請手動按下快捷鍵 `CTRL + R`。

![檢視元素](/assets/images/inspect-demo.gif)

```{hint}
您也可以在佈局檢視介面按下 TAB 鍵來遍歷查看所有元素。
```

## 介面選擇器

介面選擇器 (Selector) 用於操作 Android 元素，您也可以將其理解為 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) 是由您透過上文介面佈局檢視功能獲得的相關主要參數拼湊而成。
```

## 螢幕座標定義

自動化操作的過程中難免會碰到需要透過詳細座標或區域座標進行操作的情況。但是由於很多人可能並不是很清楚座標問題，所以我們在此介紹一下 Android 的螢幕座標知識。

眾所周知，圖片有解析度大小，螢幕也有。對於 Android 的螢幕，不論是橫向、縱向亦或是自動旋轉的螢幕，統一將左上角作為原點 (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)
```


## Android 應用的資料

每個 Android 應用在裝置上都有專用的目錄用於存放應用的資料，通常情況下應用的相關資料儲存於 /data 目錄下，您可以透過呼叫 d.application("com.example").info() API 來獲取應用的資料目錄，大部分情況下，您也可以直接 cd 至目錄 /data/user/0/com.example.test 來直接切換到使用者目錄。除了常規的 /data 目錄外，部分應用還會在 /sdcard/Android 目錄儲存多媒體等其他檔案。

### 查看簡訊資料庫

有時候，您可能想看看本機收到的簡訊都儲存在哪個位置，非常好的想法，而且非常簡單，甚至透過編寫 extension 您可以直接讀取內容並透過 HTTP API 即時獲取！
我們按照常規 Android 的思路來，如果您的不同，請自行發散思維。在 Android 上，簡訊應用的名稱應該為 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 ➥
 ```

當然讀取非常簡單，因為在 Android 下常規的應用資料庫均為 SQLite，不過一些安全性較高的應用通常自身資料庫也會加密，當然 FireRPA 這麼強怎麼會沒有呢，FireRPA 除了可以讀取常規 SQLite，同時支援**即時讀取**微信 (sqlcipher) aes-256、企業微信 aes-128、阿里系 sqlcrypto (aes-128) 等各種類型的資料庫（當然前提是您需要自己找到金鑰）。下面我們僅為您簡單示範讀取系統的簡訊內容。非常簡單，一條命令即可，當然你也可以編寫 extension 讀取。

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

當然，輸出是一大堆，不過您可以在裡面快速找到需要資料所在的資料表，然後自行 SQL 就行。這個方法適用於 98% 的 Android 應用，剩下 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}
Android 應用的資料庫也不一定非在 databases 目錄下哦。
```

### 查看其他資料

當然，應用資料目錄裡可不止有資料庫，其中還包含一些應用相關參數、設定、快取、檔案，如 shared_prefs (xml) 等，不過我們不過多介紹，您可以自行探索。

## 自動化輔助措施

在自動化業務中，並不是所有應用都適合使用選擇器進行定位，有些介面如遊戲由於是即時渲染，並不存在 Android 層面的頁面佈局。所以對於此類應用，您只能透過 OCR 或圖像比對進行操作判別。我們提供了完整的 OCR 輔助方案以及內建的圖像 SIFT、範本比對方案用以輔助實現這些業務目標。您可以在文件中找到相關的 API 以及其使用方法。

正在更新...