Android模擬器常常被用來刷單,如何準確的識別模擬器成為App開發中的一個重要模組,目前也有專門的公司提供相應的SDK供開發者識別模擬器。 目前流行的Android模擬器主要分為兩種,一種是基於Qemu,另一類是基於Genymotion,網上現在流行用一些模擬器特徵進行鑑別,比如:
- 通過判斷IMEI是否全部為0000000000格式(>=6.0的國產ROM可能直接返回00000000000000,也要區分)
- 判斷Build中的一些模擬器特徵值
- 匹配Qemu的一些特徵檔案以及屬性
- 通過獲取cpu資訊,將x86的給過濾掉(真機一般都是基於ARM)
等等,不過裡面的很多手段都能通過改寫ROM或者Xposed作假,讓判斷的效能打折扣。其實,現在絕大部分手機都是基於ARM架構,可以將其他CPU架構給忽略不計,模擬器全部執行在PC上,因此,只需要判斷是執行的裝置否是ARM架構即可。
ARM與PC的X86在架構上有很大區別,ARM採用的哈弗架構將指令儲存跟資料儲存分開,與之對應的,ARM的一級快取分為I-Cache(指令快取)與D-Cahce(資料快取),而X86只有一塊快取,如果我們將一段程式碼可執行程式碼動態對映到記憶體,在執行的時候,X86架構上是可以修改這部分程式碼的,而ARM的I-Cache可以看做是隻讀,因為,當我們往PC對應的地址寫指令的時候,其實是往D-Cahce寫,而I-Cache中的指令並未被更新,這樣,兩端程式就會在ARM與x86上有不同的表現,根據計算結果便可以知道究竟是還在哪個平臺上執行。但是,無論是x86還是ARM,只要是靜態編譯的程式,都沒有修改程式碼段的許可權,所以,首先需要將上面的彙編程式碼翻譯成可執行檔案,再需要申請一塊記憶體,將可執行程式碼段對映過去,執行。 以下實現程式碼是測試程式碼的核心,主要就是將地址e2844001的指令add r4, r4, #1,在執行中動態替換為e2877001的指令add r7, r7, #1,這裡目標是ARM-V7架構的,要注意它採用的是三級流水,PC值=當前程式執行位置+8。通過arm交叉編譯鏈編譯出的可執行程式碼如下:
8410: e92d41f0 push {r4, r5, r6, r7, r8, lr}
8414: e3a07000 mov r7, #0
8418: e1a0800f mov r8, pc // 本平臺針對ARM7,三級流水 PC值=當前程式執行位置+8
841c: e3a04000 mov r4, #0
8420: e2877001 add r7, r7, #1
....
842c: e1a0800f mov r8, pc
8430: e248800c sub r8, r8, #12 // PC值=當前程式執行位置+8
8434: e5885000 str r5, [r8]
8438: e354000a cmp r4, #10
843c: aa000002 bge 844c <out>
.....
複製程式碼
如果是在ARM上執行,e2844001處指令無法被覆蓋,最終執行的是add r4,#1 ,而在x86平臺上,執行的是add r7,#1 ,程式碼執行完畢, r0的值在模擬器上是1,而在真機上是10。之後,將上述可執行程式碼通過mmap,對映到記憶體並執行即可,具體做法如下,將可執行的二進位制程式碼直接拷貝可執行程式碼區,去執行
void (*asmcheck)(void);
int detect() {
//可執行二進位制程式碼
char code[] =
"\xF0\x41\x2D\xE9"
"\x00\x70\xA0\xE3"
"\x0F\x80\xA0\xE1"
"\x00\x40\xA0\xE3"
"\x01\x70\x87\xE2"
"\x00\x50\x98\xE5"
"\x01\x40\x84\xE2"
....
// 對映一塊可執行記憶體 PROT_EXEC
void *exec = mmap(NULL, (size_t) getpagesize(), PROT_EXEC|PROT_WRITE|PROT_READ, MAP_ANONYMOUS | MAP_SHARED, -1, (off_t) 0);
memcpy(exec, code, sizeof(code) + 1);
//強制賦值到函式
asmcheck = (void *) exec;
//執行函式
asmcheck();
__asm __volatile (
"mov %0,r0 \n"
:"=r"(a)
);
munmap(exec, getpagesize());
return a;
}
複製程式碼
經驗證, 無論是Android自帶的模擬器,還是夜神模擬器,或者Genymotion造假的模擬器,都能準確識別。在32位真機上完美執行,但是在64位的真機上可能會存在相容性問題,可能跟arm64-v8a的指令集不同有關係,也希望人能指點。為了防止在真機上出現崩潰,最好還是單獨開一個程式服務,利用Binder實現模擬器鑑別的查詢。
另外,對於Qemu的模擬器還有一種任務排程的檢測方法,但是實驗過程中發現不太穩定,並且僅限Qemu,不做參考,不過這裡給出原文連結: DEXLabs
僅供參考,歡迎指正
作者:看書的小蝸牛 原文連結 Android模擬器識別技術