Android类似钉钉类的APP实现禁⽌⽤户虚拟定位
在应⽤开发中,如果有签到打卡之类的功能,我们肯定需要在项⽬中禁⽌⽤户开启虚拟定位,导致在***⽶之外的距离模拟定位然后进⾏了打卡操作!
(⼀)
⾸先:获取⽤户⼿机是否打开了  “允许模拟位置”  选项?
其实很简单,这些设置项,基本都是写在数据库⾥,所以只要看看setting的源码(或者查看logcat可能也可以得到些有⽤的信息),就能知道该配置是写了数据库的哪个字段。
boolean isOpen = ContentResolver(),Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0;
//很明显,Settings.Secure.ALLOW_MOCK_LOCATION 就是存放允许模拟位置的数据库字段了
//当isOpen为ture时,则是⽤户打开了允许模拟位置的选项,否则则没有开启!
(⼆)
其次:虚拟定位可以通过⼀些第三⽅应⽤,然后把我们⾃⼰的应⽤克隆⼀个。在启动的时候需要在第三⽅的应⽤⾥⾯来启动,这样的话在私有⽂件⾥⾯⽣成的包名势必会和直接启动⾃⼰的应⽤有区别,知道了这些,我们将通过以下三个⽅法来⼀⼀检测;
⾸先介绍⼀些那些使⽤应⽤分⾝双开和虚拟定位的应⽤和⾃⼰的应⽤在私有⽬录下⽣成的包名有什么区别:
我们知道App的私有⽬录是/data/data/包名/或/data/user/⽤户号/包名,通过FilesDir()⽅法可以拿到私有⽬录下的files⽬录。在多开环境下,获取到⽬录会变为/data/data/多开App的包名/xxxxxxxx或/data/user/⽤户号/多开App的包名/xxxxxxxx。
举个例⼦,在我⼿机上,正常使⽤App上⾯的代码获取到的路径为/data/user/0/top.darkness463.virtualcheck/files。在多开分⾝的多开环境下,路径为/data/user/o/virtual/data/user/0/top.darkness463.virtualcheck/files。
当然,多开软件是可以hook处理让你拿到正常的⽬录,但截⾄写这篇⽂章为⽌,市⾯上⼤部分多开App没有绕过这项检测,仅有360家的分⾝⼤师可以绕过。
下⾯开始正式检测:
1. ps检测(详见)
我们先通过执⾏对uid进⾏过滤,得到类似下⾯的结果
// 正常情况下
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的⽬录。看下具体代码实现;
public static boolean isRunInVirtual() {
String filter = getUidStrFormat();
String result = exec("ps");
if (result == null || result.isEmpty()) {
return false;
}
String[] lines = result.split("\n");
if (lines == null || lines.length <= 0) {
return false;
}
int exitDirCount = 0;
for (int i = 0; i < lines.length; i++) {
if (lines[i].contains(filter)) {
int pkgStartIndex = lines[i].lastIndexOf(" ");
String processName = lines[i].substring(pkgStartIndex <= 0
0 : pkgStartIndex + 1, lines[i].length());
File dataFile = new File(String.format("/data/data/%s",
processName, Locale.CHINA));
if (ists()) {
exitDirCount++;
}
}
}
return exitDirCount > 1;
}
这⾥的应⽤列表检测不是指简单的遍历应⽤列表判断是不是安装了多开App,我们并不阻⽌⽤户安装多开App并多开其他App,我们只是不希望⽤户多开我们⾃⼰的App,因此不能检测到⽤户安装了多开App就把他⼲掉。
2.应⽤列表检测
多开App都会对PackageName()进⾏处理,让这个⽅法返回原始App的包名,因此在被多开的App看来,多开App的包名和原始的那个App的包名⼀样,因此在多开环境下遍历应⽤列表时会发现包名等于原始App的包名的应⽤会有两个。
private boolean checkPkg(Context context) {
try {
if (context == null) {
return false;
}
int count = 0;
String packageName = PackageName();
PackageManager pm = PackageManager();
List<PackageInfo> pkgs = pm.getInstalledPackages(0);
for (PackageInfo info : pkgs) {
android模拟点击if (packageName.equals(info.packageName)) {
count++;
}
}
return count > 1;
} catch (Exception ignore) {}
return false;
}
3.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改个包名就失效了。
Set<String> virtualPkgs;  // 多开第三⽅App包名列表
private boolean check() {
BufferedReader bufr = null;
try {
bufr = new BufferedReader(new FileReader("/proc/self/maps"));
String line;
while ((line = adLine()) != null) {
for (String pkg : virtualPkgs) {
if (ains(pkg)) {
return true;
}
}
}
} catch (Exception ignore) {
} finally {
if (bufr != null) {
try {
bufr.close();
} catch (IOException e) {
}
}
}
return false;
}
以上三种检测⽅法有的会被第三⽅虚拟定位软件或者多开分⾝软件躲避掉,有的则不会,所以使⽤的时候建议三种⽅法全部⽤上。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。