Android P非SDK介面的限制分析
背景
Android系統幾乎每年都在釋出新版本,在Android 9上,針對非SDK介面使用的警示需要開發者提前考慮。
Android 9(API 級別 28)引入了針對非 SDK 介面的使用限制,無論是直接使用還是通過反射或 JNI 間接使用。 無論應用是引用非 SDK 介面還是嘗試使用反射或 JNI 獲取其控制代碼,均適用這些限制。 有關此決定的詳細資訊,請參閱通過減少使用非 SDK 介面提升穩定性。 一般來說,應用應當僅使用 SDK 中正式記錄的類。 特別是,這意味著,在您通過反射之類的語義來操作某個類時,不應打算訪問 SDK 中未列出的函式或欄位。 使用此類函式或欄位很可能會破壞您的應用。
靜態檢測
如果你的應用使用了限制介面,在除錯包執行時,會彈出警告框,按圖索驥,核查一下官方說明: https://g.co/dev/appcompat
Google提供了靜態檢測工具 veridex ,用於檢測apk是否使用了限制api。
veridex使用
該工具是一個*nix下的二進位制可執行指令碼,可以在macOS和linux下使用,Google官方已經提供了一套預編譯好的指令碼,如果有linux開發環境,可以閱讀README.md執行響應入口指令碼:
./appcompat.sh --dex-file=test.apk
如果執行失敗,需要確認一下環境問題。
筆者的本地為macOS環境,除此遇到了執行失敗,日誌類似:
NOTE: appcompat.sh is still under development. It can report API uses that do not execute at runtime, and reflection uses that do not exist. It can also miss on reflection uses. ./appcompat.sh: line 28: /Users/aven/Desktop/runtime-master-appcompat/veridex: cannot execute binary file ./appcompat.sh: line 28: /Users/aven/Desktop/runtime-master-appcompat/veridex: Undefined error: 0
分析日子可知veridex檔案無法執行,主要原因是系統不相容,他是linux下的可執行檔案,不是macOS的。
aven-mac-pro-2:runtime-master-appcompat aven$ file veridex veridex: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, with debug_info, not stripped
解決辦法有兩個,一是自行編譯veridex,這個成本比較大,需要安裝系列的編譯依賴庫。 第二個辦法是,搭建一套可用的linux環境,這個相對簡單。
如果你有在使用Docker,可以嘗試已經存在一些映象,如果沒有,直接去Docker Hub找一個基於linux 2.6.24以上的映象,然後在docker映象內執行veridex即可
#132: Reflection greylist Lsun/misc/Unsafe;->allocateInstance use(s): Lcom/google/gson/internal/UnsafeAllocator;->create()Lcom/google/gson/internal/UnsafeAllocator; Lcom/networkbench/com/google/gson/internal/h;->sf()Lcom/networkbench/com/google/gson/internal/h; Lcom/wuba/certify/x/ad;->a()Lcom/wuba/certify/x/ad; #133: Reflection greylist Lsun/misc/Unsafe;->theUnsafe use(s): Lcom/google/gson/internal/UnsafeAllocator;->create()Lcom/google/gson/internal/UnsafeAllocator; Lrx/internal/util/unsafe/UnsafeAccess;-><clinit>()V Lcom/networkbench/com/google/gson/internal/h;->sf()Lcom/networkbench/com/google/gson/internal/h; Lcom/wuba/certify/x/ad;->a()Lcom/wuba/certify/x/ad; 133 hidden API(s) used: 24 linked against, 109 through reflection 126 in greylist 2 in blacklist 3 in greylist-max-o 2 in greylist-max-p
檢測結果將列出名字黑名單,灰名單,深灰名單的各種情況。後面就是依據不同限制名單注意核對原始碼作調整。
檢測分析
前面我們根據官方提示,下載了指令碼工具的壓縮包,裡面包含了很多資源。結合appcompact.sh的內容,在呼叫veridex時傳遞了很多引數:
# First check if the script is invoked from a prebuilts location. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" if [[ -e ${SCRIPT_DIR}/veridex && \ -e ${SCRIPT_DIR}/hiddenapi-flags.csv && \ -e ${SCRIPT_DIR}/org.apache.http.legacy-stubs.zip && \ -e ${SCRIPT_DIR}/system-stubs.zip ]]; then exec ${SCRIPT_DIR}/veridex \ --core-stubs=${SCRIPT_DIR}/system-stubs.zip:${SCRIPT_DIR}/org.apache.http.legacy-stubs.zip \ --api-flags=${SCRIPT_DIR}/hiddenapi-flags.csv \ $@ fi
hiddenapi-flags.csv
其中有個excel檔案,開啟可以看到是一個白名單清單列表,配置了哪些特徵值屬於哪個級別的列表:
org.apache.http.legacy-stubs.zip
另外還有一個 org.apache.http.legacy-stubs.zip
,裡面是一個classes.dex,在嘗試反編譯後失敗了:
aven-mac-pro-2:runtime-master-appcompat aven$ /Users/aven/Android/sdk/build-tools/28.0.3/dexdump classes.dex Processing 'classes.dex'... E/libdex(53872): ERROR: unsupported dex version (30 33 39 00) E/libdex(53872): ERROR: Byte swap + verify failed ERROR: Failed structural verification of 'classes.dex'
根據 DexFormat.java 的定義,Dex 039是Android 9使用的
/** dex file version number for API level 28 and earlier */ public static final String VERSION_FOR_API_28 = "039"; /** dex file version number for API level 26 and earlier */ public static final String VERSION_FOR_API_26 = "038"; /** dex file version number for API level 24 and earlier */ public static final String VERSION_FOR_API_24 = "037"; /** dex file version number for API level 13 and earlier */ public static final String VERSION_FOR_API_13 = "035";
雖然Android自帶的dexdump無法解析這個dex,但是jadx任然可以,升級到jadx 0.9.0
分析可知,實際上這個dex是一些類的集合,這些類和老版本中sdk具備的API同名,但是都沒有實現具體邏輯,推測功能主要是為了類檢測校驗之類,本身也不是為了被使用,因此不需要引入實現體,如此可以避開實現體內引用其他類的情況。
public FilePart(String name, PartSource partSource, String contentType, String charset) { String str = (String) null; super(str, str, str, str); throw new RuntimeException("Stub!"); }
system-stubs.zip
類似org.apache.http.legacy-stubs.zip,這個裡面也是一個classes.dex