原文: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层都进行检测。当然,与黑产斗争的道路我们永远处于劣势。

Android多开/分身检测的更多相关文章

  1. Android混合开发之WebViewJavascriptBridge实现JS与java安全交互

    前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...

  2. Android混合开发之WebView使用总结

    前言: 今天修改项目中一个有关WebView使用的bug,激起了我总结WebView的动机,今天抽空做个总结. 混合开发相关博客: Android混合开发之WebView使用总结 Android混合开 ...

  3. Android安全开发之ZIP文件目录遍历

    1.ZIP文件目录遍历简介 因为ZIP压缩包文件中允许存在“../”的字符串,攻击者可以利用多个“../”在解压时改变ZIP包中某个文件的存放位置,覆盖掉应用原有的文件.如果被覆盖掉的文件是动态链接s ...

  4. Android安全开发之WebView中的地雷

    Android安全开发之WebView中的地雷 0X01 About WebView 在Android开发中,经常会使用WebView来实现WEB页面的展示,在Activiry中启动自己的浏览器,或者 ...

  5. Android 安全开发之 ZIP 文件目录遍历

    1.ZIP文件目录遍历简介 因为ZIP压缩包文件中允许存在"../"的字符串,攻击者可以利用多个"../"在解压时改变ZIP包中某个文件的存放位置,覆盖掉应用原 ...

  6. Android官方开发文档Training系列课程中文版:目录

    Android官方开发文档Training系列课程中文版:目录   引言 在翻译了一篇安卓的官方文档之后,我觉得应该做一件事情,就是把安卓的整篇训练课程全部翻译成英文,供国内的开发者使用,尤其是入门开 ...

  7. Android混合开发之WebView与Javascript交互

    前言: 最近公司的App为了加快开发效率选择了一部分功能采用H5开发,从目前市面的大部分App来讲,大致分成Native App.Web App.Hybrid App三种方式,个人觉得目前以Hybri ...

  8. Android驱动开发之Hello实例

    Android驱动开发之Hello实例:   驱动部分 modified:   kernel/arch/arm/configs/msm8909-1gb_w100_hd720p-perf_defconf ...

  9. 利用Android Studio、MAT对Android进行内存泄漏检测

    利用Android Studio.MAT对Android进行内存泄漏检测 Android开发中难免会遇到各种内存泄漏,如果不及时发现处理,会导致出现内存越用越大,可能会因为内存泄漏导致出现各种奇怪的c ...

随机推荐

  1. hdu6444 Neko's Loop

    要想A一道题,你需要正版ide,正版草稿纸,正版键盘,正版双手,但最重要的是正版大脑.(改编自wxf在微积分群的吐槽) 分析一下题意,选的所有数的下标模gcd(n,k)都是同余的,而且是在所有同余的下 ...

  2. Python之路:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

  3. 前端开发【第5篇:JavaScript进阶】

    语句 复合表达式和空语句 复合表达式意思是把多条表达式连接在一起形成一个表达式 { let a = 100; let b = 200; let c = a + b; } 注意这里不能再块级后面加分号, ...

  4. tomcat 性能调优

    1. 内存 windows在bin/catalina.bat的注释下第一行加入 set JAVA_OPTS=-Xms2048m -Xmx2048m -Xss128K -XX:PermSize=64m ...

  5. 构造方法和一般方法的区别(面试)-----java基础知识总结

    构造方法:对象创建时,就会调用与之对应的构造方法,对对象进行初始化: 一般方法:对象创建后,需要方法功能时才会调用. 构造方法,在对象创建时,会调用且只调用一次. 一般方法,对象创建后,可以被调用多次 ...

  6. python中的无参装饰器和有参装饰器

    python中的无参装饰器和有参装饰器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 装饰器特点: 1>.开放封闭原则,即对扩展是开放的,对修改时封闭的: 2>.装饰器 ...

  7. jdk1.8.0_45源码解读——HashMap的实现

    jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对 ...

  8. [整理]JS中的状态机

    /*StateMachine*/ var StateMachine = (function(){ function StateMachine(opts){ this.current = opts.in ...

  9. Export SQLite data to Excel in iOS programmatically(OC)

    //For the app I have that did this, the SQLite data was fairly large. Therefore, I used a background ...

  10. [R语言]关联规则2---考虑items之间严格的时序关系

    前面介绍了关联规则1---不考虑用户购买的items之间的时序关系,但在一些情况下用户购买item是有严格的次序关系了,比如在某些休闲游戏中,用户购买了道具A才能购买道具B,且道具A和B只能购买一次, ...