包裝殼層指令碼

在透過原生程式碼對應用程式進行偵錯及分析時,使用偵錯工具通常很有幫助,但您需要在程序啟動時啟用這些工具。這就需要您在新程序中執行應用程式,而非從 zygote 複製。例如:

使用包裝殼層指令碼

wrap.sh 使用起來很簡單:

  1. 編譯可進行偵錯的自訂 APK,其中包含以下內容:
  2. 在裝置上安裝可進行偵錯的 APK。
  3. 啟動應用程式。

建立包裝殼層指令碼

當您啟動包含 wrap.sh 的可進行偵錯 APK 時,系統會執行該指令碼,並傳遞做為引數的啟動應用程式指令。這個指令碼負責啟動應用程式,但也可以變更任何環境或引數。此外,這個指令碼應遵循 MirBSD Korn shell (mksh) 語法。

下列程式碼片段示範如何編寫僅用於啟動應用程式的簡單 wrap.sh 檔案:

#!/system/bin/sh
exec "$@"

Malloc debug

如要透過 wrap.sh 使用 malloc debug,請加入以下這行:

#!/system/bin/sh
LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper "$@"

ASan

請參閱 ASan 說明文件中的範例,瞭解如何為 ASan 執行這項操作。

wrap.sh 套件

為充分運用 wrap.sh,您的 APK 必須可進行偵錯。確認 Android 資訊清單中的 <application> 元素已設定 android:debuggable="true";或者,如果您使用的是 Android Studio,請確認已在 build.gradle 檔案中設定偵錯版本。

此外,您必須在應用程式的 build.gradle 檔案中將 useLegacyPackaging 設為 true。在大多數情況下,這個選項會預設為 false,因此建議您將這項設定明確設為 true,以避免任何意外發生。

您必須將 wrap.sh 指令碼與應用程式的原生資料庫封裝在一起。如果您的應用程式不包含原生資料庫,請手動將 lib 目錄新增至專案目錄。針對您的應用程式支援的每個架構,您必須在該原生資料庫目錄下提供包裝殼層指令碼的副本。

以下範例展示同時支援 ARMv8 和 x86-64 架構的檔案版面配置:

# App Directory
|- AndroidManifest.xml
|- …
|- lib
   |- arm64-v8a
      |- ...
      |- wrap.sh
   |- x86_64
      |- ...
      |- wrap.sh

Android Studio 僅封裝 lib/ 目錄中的 .so 檔案,因此如果您使用 Android Studio,便需要將 wrap.sh 檔案置於 src/main/resources/lib/* 目錄中,藉此確保檔案可正確封裝。

請注意,resources/lib/x86 在 UI 中會顯示為 lib.x86,但它實際上應該是子目錄:

Android Studio 中的 wrap.sh 封裝範例

在使用 wrap.sh 時進行偵錯

如要在使用 wrap.sh 時附加偵錯工具,您必須為殼層指令碼手動啟用偵錯功能。執行這項操作的方式因版本而異,因此以下範例示範如何為所有支援 wrap.sh 的版本新增適當的選項:

#!/system/bin/sh

cmd=$1
shift

os_version=$(getprop ro.build.version.sdk)

if [ "$os_version" -eq "27" ]; then
  cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
elif [ "$os_version" -eq "28" ]; then
  cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
else
  cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
fi

exec $cmd