Android多開/分身檢測


原文:https://blog.darkness463.top/2018/05/04/Android-Virtual-Check/

多開/分身原本用於方便有多個微信/QQ解決同時登錄的問題,但近來年被各種黑產所利用,多見於薅羊毛,部分多開App甚至提供了篡改功能。對於普通用戶根本不會有多開的需求的App,一旦檢測到當前運行在多開環境下,有理由限制該用戶的后續行為。

在嘗試了目前市面上多款多開App后,總結了幾種檢測方案。

多開原理

目前市面上的多開App的原理類似,都是以新進程運行被多開的App,並hook各類系統函數,使被多開的App認為自己是一個正常的App在運行。

從形式上來說多開App有2種形式,一種是從多開App中直接加載被多開的App,如平行空間、VirtualApp等,另一種是讓用戶新安裝一個App,但這個App本質上就是一個殼,用來加載被多開的App,其原理和前一種是一樣的,市面上多開分身這款App是用的這種形式,用戶每分身一個App需新安裝一個包名為dkmodel.xxx.xxx的App。

檢測方案

檢測files目錄路徑

我們知道App的私有目錄是/data/data/包名//data/user/用戶號/包名,通過Context.getFilesDir()方法可以拿到私有目錄下的files目錄。在多開環境下,獲取到目錄會變為/data/data/多開App的包名/xxxxxxxx/data/user/用戶號/多開App的包名/xxxxxxxx

舉個例子,在我手機上,正常使用App上面的代碼獲取到的路徑為/data/user/0/top.darkness463.virtualcheck/files。在多開分身的多開環境下,路徑為/data/user/0/dkmodel.zom.rxo/virtual/data/user/0/top.darkness463.virtualcheck/files

當然,多開軟件是可以hook處理讓你拿到正常的目錄,但截至寫這篇文章為止,市面上大部分多開App沒有繞過這項檢測,僅有360家的分身大師可以繞過。

ps檢測

這種檢測方法見這篇文章。這里就不重復了,我簡單說一下原理。

我們先通過執行ps命令並以自己的uid進行過濾,得到類似下面的結果。

1
2
3
4
5
6
7
// 正常情況下
u0_a148 8162 423 1806036 56368 SyS_epoll+ 0 S top.darkness463.virtualcheck

// 多開環境下
u0_a155 19752 422 4437612 62752 SyS_epoll+ 0 S top.darkness463.virtualcheck
u0_a155 19758 422 564234 54356 SyS_epoll+ 0 S com.lbe.parallel
u0_a155 19747 422 734562 24542 SyS_epoll+ 0 S com.lbe.parallel:mdserver

可以看到在多開環境下,會獲取到自己的包名和多開App的包名這2個包名,通過這些包名去/data/data/下找會找到2個目錄,而正常情況下只能在/data/data/下找到自己的App的目錄。

具體的實現原文已經貼了,這里也不重復了。目前未發現有多開App能繞過該項檢測,但在Google Pixel Android 8.0下執行ps獲取不到進程信息,在Android 8.1的類AOSP rom下都能正常獲取,不知道那個手機什么情況。

應用列表檢測

這里的應用列表檢測不是指簡單的遍歷應用列表判斷是不是安裝了多開App,我們並不阻止用戶安裝多開App並多開其他App,我們只是不希望用戶多開我們自己的App,因此不能檢測到用戶安裝了多開App就把他干掉。

多開App都會對context.getPackageName()進行處理,讓這個方法返回原始App的包名,因此在被多開的App看來,多開App的包名和原始的那個App的包名一樣,因此在多開環境下遍歷應用列表時會發現包名等於原始App的包名的應用會有兩個。

代碼如下。只對部分多開App有效,例如360的分身大師,不少多開App會繞過這項檢測。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private boolean checkPkg(Context context) {
try {
if (context == null) {
return false;
}
int count = 0;
String packageName = context.getPackageName();
PackageManager pm = context.getPackageManager();
List<PackageInfo> pkgs = pm.getInstalledPackages(0);
for (PackageInfo info : pkgs) {
if (packageName.equals(info.packageName)) {
count++;
}
}
return count > 1;
} catch (Exception ignore) {}
return false;
}

maps檢測

讀取/proc/self/maps,多開App會加載一些自己的so到內存空間,舉個例子,360的分身大師加載了其目錄下的某個so,/data/app/com.qihoo.magic-gdEsg8KRAuJy0MuY18BlqQ==/lib/arm/libbreakpad-jni-1.5.so,通過對各種多開App的包名的匹配,如果maps中有多開App的包名的東西,那么當前就是運行在多開環境下。

目前沒有發現多開App繞過該項檢測,但缺點是需要收集所有多開App的包名,一旦多開App改個包名就失效了。

偽代碼如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

Set<String> virtualPkgs; // 多開App包名列表
private boolean check() {
BufferedReader bufr = null;
try {
bufr = new BufferedReader(new FileReader("/proc/self/maps"));
String line;
while ((line = bufr.readLine()) != null) {
for (String pkg : virtualPkgs) {
if (line.contains(pkg)) {
return true;
}
}
}
} catch (Exception ignore) {

} finally {
if (bufr != null) {
try {
bufr.close();
} catch (IOException e) {

}
}
}
return false;
}

總結

這些方法有的已經被部分多開App繞過了,有的暫時還未繞過,建議所有的檢測都加上,而且可以Java層和C層都進行檢測。當然,與黑產斗爭的道路我們永遠處於劣勢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM