Android虚拟定位

项目地址

GitHub地址

  • 下载方式

打开项目,点击右边的releases,听说先点个右上角的Star成功率会更高😁。
进入新的页面可以看到各个历史版本,选择最上面的(也就是最新的)版本的apk文件 单击下载。

655.png

原理分析

地图与定位

本APP地图与定位是使用的百度地图开放平台能力,目前国内的三家主流地图应用(百度、腾讯、高德)都有对个人提供免费的SDK。

模拟定位原理

本应用目前使用的是基于开发者模式的位置修改

  • 基于开发者模式 的位置修改

实现方式:使用 Android 系统自带的调试 API,模拟 GPS Provider 的结果,从而实现模拟位置的功能。即利用手机自带的位置模拟功能,修改用户获取的位置信息。

Android 系统,在开发者模式中开启允许模拟位置。Android 6.0 以上的系统中开发者模式中去除了”允许模拟器位置“选项,但是增加了”选择模拟位置信息应用“选项。可以通过该选项,选择要开启模拟位置的应用,便可以通过 addTestProvider 进行位置信息模拟。

这是目前最为简单的模拟定位方式,不要求root,缺点就是易于检测。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
try {
//开启模拟位置服务
mLocationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, false, false, false, true, true, true, 0, 5);
mLocationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);
} catch (Exception e) {
return false;
}

......
public Location getLocation() {
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
mLocationManager.addNmeaListener(mNmeaListener);
}
return mlocation;
}
  • 基于Hook方式 的位置修改

实现方式:对手机 Root 以后,利用 Hook 框架拦截系统或者地图类 SDK 获取位置信息的 API,并用伪造的位置数据替换原有的位置数据,从而实现模拟位置的功能。

以高德地图为例,通过 Xposed 框架对高德地图中获取位置信息的函数进行 Hook,从而达到修改位置信息的目的。

这种方法的缺点就是现在市面上购买的手机难以root。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
XposedHelpers.findAndHookMethod(Location.class, "getLongitude", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("修改经度坐标");
param.setResult(-122.4194); // 设置伪造的经度(例如:旧金山
}
});

XposedHelpers.findAndHookMethod(Location.class, "getLatitude", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("修改纬度坐标");
param.setResult(37.7749); // 设置伪造的纬度(例如:旧金山)
}
});

  • 基于虚拟容器 的位置修改
    实现方式:将 APP 安装在虚拟容器内,虚拟容器通过将应用注册到虚拟空间中。以 Xposedvirtual 为例,它是一套插件框架,允许应用以插件的方式运行在其构造的虚拟空间中,并自带 Root 权限和 Xposed Hook 框架,且宿主机无需 Root。这样便可以在任意的设备上利用 Hook 框架拦截系统或者地图类 SDK 获取位置信息的 API,并用伪造的数据替换原有的位置数据,从而实现模拟位置的功能。

  • 基于模拟器 的位置修改
    实现方式:将 APP 安装在模拟器内,利用模拟器提供的位置修改功能修改模拟器的位置信息,从而实现模拟位置的功能。

以夜神模拟器为例,修改模拟器的位置信息

5bd3be8debcf3f1f8cc964347aee274b.webp

  • 基于修改系统源码 的位置修改

实现方式:修改 Android 系统源码并编译打包,将系统中获取位置信息的 API 暴露出来,暴露的接口可以接收外部自定义的位置信息。当 APP 调用获取位置相关的 API 时,可以通过暴露的接口传入指定的位置信息,从而实现模拟位置的功能。

如何检测模拟定位

基于开发者模式的位置修改检测

  1. 最暴力,直接检测手机是否打开了开发者模式
1
2
3
4
5
6
7
8
9
10
11
12
private fun checkDeveloperMode() {
val isDeveloperModeEnabled = Settings.Secure.getInt(
contentResolver,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0
) != 0

if (isDeveloperModeEnabled) {
textView.append("开发者模式已开启\n")
} else {
textView.append("开发者模式未开启\n")
}
}
  1. 检测定位是否来自MockProvider
1
2
3
4
5
6
7
8
9
10
11
12
13
private fun checkMockLocation() {
val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
try {
val location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
if (location?.isFromMockProvider == true) {
textView.append("位置来自MockProvider\n")
} else {
textView.append("位置不来自MockProvider\n")
}
} catch (e: SecurityException) {
textView.append("无法获取位置信息: ${e.message}\n")
}
}
  1. 检查模拟定位开关
1
2
3
4
5
6
7
8
9
10
11
private fun checkMockLocationEnabled() {
val isMockLocationEnabled = Settings.Secure.getInt(
contentResolver,
Settings.Secure.ALLOW_MOCK_LOCATION, 0
) != 0
if (isMockLocationEnabled) {
textView.append("模拟定位已开启\n")
} else {
textView.append("模拟定位未开启\n")
}
}
  1. 检测应用列表,并检测是否有APP申请了模拟定位的权限android.permission.ACCESS_MOCK_LOCATION
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private fun checkInstalledAppsForMockPermission() {
    val packageManager = packageManager
    val installedPackages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS)
    val appsWithMockLocation = installedPackages.filter { packageInfo ->
    packageInfo.requestedPermissions?.contains("android.permission.ACCESS_MOCK_LOCATION") == true
    }
    if (appsWithMockLocation.isNotEmpty()) {
    textView.append("以下应用拥有模拟定位权限:\n")
    appsWithMockLocation.forEach { packageInfo ->
    textView.append("- ${packageInfo.packageName}\n")
    }
    } else {
    textView.append("没有应用拥有模拟定位权限\n")
    }
    }

基于 Hook 方式的位置修改

  • 思路:

Hook 方式通常通过 Xposed 或类似框架,拦截和修改系统 API 的返回值,比如 LocationManager、Location 对象的相关方法。因此,检测这种方式主要是识别这些框架的存在和使用。
可以检查系统中是否安装了 Xposed 或 LSPatch 之类的框架,或检测是否存在这些框架的迹象,如特定的系统属性或文件。
检测关键方法是否被 Hook,比如检测 LocationManager 的 getLastKnownLocation() 或 requestLocationUpdates() 的返回值是否被修改过。

  • 难易程度:

中等。虽然检测框架的存在相对容易,但如果开发者对 Hook 方法进行了深度隐藏(如通过 LSPatch 无模块显示等),检测难度会增加。

  • 注意事项:

要考虑检测方案的兼容性,以免误报合法的系统修改。
如果框架通过高级技术进行隐藏,检测可能会变得非常困难。

基于虚拟容器的位置修改

  • 思路:

虚拟容器技术(如 VirtualXposed)通过在虚拟环境中运行应用,拦截和修改系统 API 的返回值,而不需要设备 Root。
可以通过检测应用运行环境是否为虚拟容器来识别,例如检查进程名称、特定的虚拟容器标识文件或特征,或通过系统调用反查虚拟化环境(如查看文件系统特征)。
也可以监控关键系统服务的行为(如 LocationManager),并比对实际设备状态与应用内获取的信息是否一致。

  • 难易程度:

中等。虚拟容器环境可能存在一些独特的特征,通过仔细分析可以识别,但不同的虚拟容器实现可能各有差异。

  • 注意事项:

虚拟容器的多样性使得需要综合多种检测方法来提高检测的准确性。
容器可能会模拟原生环境,因此要防止虚假阴性(未检测到修改的情况)。

基于模拟器的位置修改

  • 思路:

模拟器通常用于开发和测试,具备一系列的虚拟化特征。可以通过检测设备信息是否与实际设备不符(如硬件信息、传感器信息、系统属性等)来识别模拟器。
检查系统属性(如 ro.product.model、ro.hardware)是否匹配常见的模拟器特征(如 “sdk_gphone_x86” 等)。
检测应用权限,模拟器环境中通常会有不同的权限和系统行为。

  • 难易程度:

简单到中等。模拟器的特征比较明显,通常很容易被检测到,但如果模拟器进行了深度仿真和伪装,检测难度会提高。

  • 注意事项:

需要兼容不同版本的模拟器,不同的模拟器可能会有不同的表现。
有些模拟器可能已经进行了特征伪装,因此检测策略需要持续更新。

基于修改系统源码的位置修改

  • 思路:

修改系统源码是通过修改 Android 系统的底层代码实现的,这种方式直接在系统级别修改 API 的行为,绕过了上层检测机制。
检测这种方式主要通过比对系统的关键组件是否被篡改,或使用信任链来验证系统文件的完整性,如验证签名、哈希值等。
检查系统的 SELinux 配置、验证系统文件的完整性(例如关键二进制文件的校验)也是一种方法。

  • 难易程度:

困难。这种方式修改较为隐蔽,且往往在较深的系统层面执行,难以通过常规的应用层检测手段识别。

  • 注意事项:

系统源码的修改通常非常隐蔽,除非有明确的系统文件完整性检查,否则很难检测。
可能需要结合设备的系统更新状态、签名验证和系统安全策略进行综合评估。

如何绕过的钉钉检测

目前来看,钉钉打卡的检测方式是基于应用列表的检测,可能是一种黑白名单机制,我们在基于发者模式的位置修改 中,只要把我们APP的包名修改为类似系统APP的应用,就能绕过钉钉检测。

不知道钉钉为什么只做这种粗略的检测方式,但是我们要知道,钉钉如果想检测开基于发者模式的位置修改就一定能检测到,所以要谨慎使用模拟定位软件打卡,强需求还是要以root方式去模拟定位。

补充,钉钉还会检测运行环境,上面提及的基于虚拟容器的位置修改 是绕不过钉钉检测的,但是也不一定全都绕不过,这本质就是一个持久对抗的过程,道高一尺魔高一丈。

TODO

目前还在开发Hook方式的位置修改,作为Xposed模块使用,还没有上线,初步测试以及可以修改微信的定位了。

效果展示:

免责声明

本虚拟定位软件的开发旨在提供技术学习与交流的途径。请勿将本软件用于任何形式的作弊、非法或不道德的活动。用户应自行承担因不当使用本软件而导致的所有后果。开发者不对任何因使用本软件产生的直接或间接损害承担责任。