用 KGDB 來替 Android 除錯的實用技巧

內核(Kernel)除錯功能讓資安研究人員在進行分析時能夠監視和控制設備。在Windows、macOS和Linux等桌面平台上,這很容易進行。但要在Android設備(如Google Nexus 6P)上進行內核除錯則困難得多。在本文中,將介紹一種在Nexus 6P和Google Pixel上進行內核除錯的方法而無須特製的硬體。

Joshua J. Drake和Ryan Smith為此目的打造了一條UART除錯線,效果很好。然而對於不擅長製作硬體的人(像是筆者這樣的軟體工程師)來說,這可能很困難。而替代方案是透過serial-over-usb通道來進行內核(Kernel)除錯 也是可行的。

這個方法可以追溯到2010年,這表示部分介紹已經過時。但我發現的作法可以利用其關鍵要點,然後用在今日的Android設備上。讓研究人員可以使用除錯功能來確認CPU執行的當前狀態,讓分析更加快速。那該如何進行?

Android是用Linux內核打造的,其中包含了內建的內核除錯程式KGDB。KGDB依靠串列埠來連接除錯設備和目標設備。正常情況如下:

圖1、KGDB工作模式

 

目標和除錯設備透過串列線連接。使用者在除錯機上用GDB來附加串列設備檔案(如/dev/ttyS1),指令是target remote /dev/ttyS1。之後,GDB可以通過串列線和目標設備上的KGDB進行溝通。

KGDB核心元件處理實際的除錯工作,像是設置中斷點和取得記憶體內的資料。KGDB I/O元件可以連接KGDB核心元件和低階串列驅動程式以處理除錯資訊傳輸。

不過Android設備通常沒有硬體串列埠。首先要解決的就是找到一個通道讓KGDB可以將除錯資訊傳送到具有GDB的外部設備。使用過各種通道,但最實用的方法是用USB連接線。

Linux內核的USB驅動程式支援USB ACM類別設備,這可以用來模擬串列設備。也就是說,Android設備可以透過USB線連到串列設備。這些已經存在於Linux內核程式碼,所以我們不需要額外加入任何程式碼。以下是啟用此除錯功能的步驟:

 

  1. 用aosp_angler-eng和對應的linux內核來製作一個AOSP(Android開放原始碼專案)版本。請參考這裡
  2. 使用USB線連接目標設備與除錯機器。
  3. flashboot指令將映像寫入目標設備:fastboot flashall -w
  4. 執行adb指令來啟用adb網路服務:adb tcpip 6666
  5. 在除錯機器上執行adb來連接設備:adb connect <設備IP>:6666
  6. 執行adb shell:adb shell
  7. adb shell中,進入USB gadget驅動程式控制資料夾/sys/class/android_usb/android0/
  8. adb shell中使用下列指令啟用USB ACM功能:

echo 0 > enable //關閉USB連線

echo tty > f_acm/acm_transports //指定傳輸類型

echo acm > functions 啟用USB gadget驅動程式的ACM功能

echo 1 > enable //啟用USB連線

 

此時,USB ACM功能應該已經啟用。需要做兩件事來確認:首先,在adb shell輸入指令ls /dev/ttyGS*。應該存在一個設備檔案。第二,在除錯機器上使用指令ls /dev/ttyACM*。也應該有一個設備檔案。除錯機器可以用這兩個設備檔案和目標設備進行溝通。

第二項挑戰是KGDB需要一個較低階的通訊驅動程式(串列驅動程式或USB gadget驅動程式)來提供一個輪詢(polling)介面以取得和寫入字元。為什麼?因為KGDB的通訊管道需要在KGDB的內核異常處理程序中運作。在此種情況下,中斷被禁用,只有一個CPU可以執行此程式碼。低階驅動程式不依賴中斷,而需要主動輪詢暫存器變動或記憶體I/O空間的變動。在這種情況下不要使用sleep或spinlock。

Nexus 6P的USB連線使用DWC3控制器。此USB驅動程式不直接提供輪詢功能,因此我將此功能加入DWC3設備驅動程式中。這程式碼背後的概念很簡單:我移除了對中斷的依賴。另外使用迴圈查詢暫存器或記憶體空間中相應的變動。為了容易了解,我做的是下列事情:

 

  • 我將c內kgdboc_init_jack函式中的ACM設備檔案名稱寫死為/dev/ttyGS0
  • 我變更f_acm/acm_transports的處理功能來啟用KGDB。這讓KGDB可以簡單的用echo kgdb > f_acm/acm_transports來啟用

 

這將KGDB的工作模式變成如下:

圖2、新的KGDB工作模式

 

下面是將上述程式碼合併到內核程式碼的步驟。雖然你可以用任何內核程式碼,我是取自Google自己的Git repository,用的是分支 origin/android-msm-angler-3.10-nougat-hwbinder

 

  1. 將原始設定變更如下來進行內核編譯:
  2. CONFIG_KGDB=y

CONFIG_KGDB_SERIAL_CONSOLE=y

  1. CONFIG_MSM_WATCHDOG_V2=n

如果這被啟用,內核會將輪詢迴圈當作死迴圈並重新啟動設備。所以必須停用此功能。

  1. CONFIG_FORCE_PAGES=y

如果沒有啟用,則不會設置軟中斷點。

  1. fastboot指令將內核和Android映像刷新到目標設備上。
  2. 在除錯機器上啟動Android系統。執行以下指令來啟用adb網路服務:adb tcpip 6666
  3. 在除錯機器上執行下列指令:

adb connect <設備的IP地址>:6666

adb shell

  1. adb shell中執行以下指令:

echo 0 > enable

echo tty > f_acm/acm_transports

echo acm > functions

echo 1 > enable

echo kgdb > f_acm/acm_transports

  1. 在除錯機上執行gdb。Nexus 6P有一個aarch64內核,所以你需要gdb for aarch64

sudo <路徑> /aarch64-linux-android-gdb <路徑>/vmlinux

target remote /dev/ttyACM0

  1. 在adb shell中輸入以下指令:

echo g > /proc/sysrq-trigger

注意:步驟6的最後一個指令和步驟7之間的時間間隔不應太長。越短越好。

  1. 如果成功,GDB將輸出以下內容:

圖3、GDB輸出

 

我已經用這方法在Nexus 6P上進行了內核除錯。此方法應該也適用於Google Pixel,只要它們也用DWC3 USB控制器。

我們希望此技術的分享可以對其他Android研究人員有幫助,替研究團體提供新方法來更好地了解行動惡意軟體的行為。除錯功能能夠為研究人員帶來更好的逆向工程能力,讓他們更清楚地了解惡意軟體在Android設備內的行為。

 

@原文出處:Practical Android Debugging Via KGDB 作者:Jack Tang(威脅分析師)