The quiter you become,the more you are able to hear!

iOS虚拟定位技术探究

Author: geneblue

Blog: https://geneblue.github.io/

什么是虚拟定位

简而言之,利用软硬件更改手机系统的定位数据,从而让目标 app 获取错误的定位信息

虚拟定位常用场景

  • 上下班打卡
  • 代购
  • 朋友圈装逼
  • 打车软件司机端抢单
  • 社交类软件,附近人,把妹,钓凯子

pic

凡是和位置相关的应用,均有一定的应用场景,尽情发挥群众的智慧

虚拟定位原理

经过调研 iOS 虚拟定位大概有三类技术路线:

动态库注入

iOS 越狱后,安装使用具备虚拟定位功能的越狱插件。在上帝模式下,越狱插件可以肆意劫持系统函数。或者在非越狱环境下,砸壳目标 app ipa 文件,重打包注入动态库。这两种方式本质是一致的,都是为了 app 进程运行时,注入运行动态库。

劫持 CoreLocation 中以下常用的获取定位信息的函数,即可篡改正常的定位数据:

1
2
3
4
5
6
7
8
9
CLLocationManager->delegate()
CLLocationManager->initialize()
CLLocationManager->setDelegate()
CLLocationManager->startMonitoringSignificantLocationChanges()
CLLocationManager->startUpdatingHeading()
CLLocationManager->startUpdatingLocation()
CLLocationManager->stopMonitoringSignificantLocationChanges()
CLLocationManager->stopUpdatingHeading()
CLLocationManager->stopUpdatingLocation()

电脑端软件

做过 iOS 开发的同学,可以观察一下 xcode 上有一个 Simulate location 的调试功能,这是方便 app 开发过程中测试定位代码。

pic

我们可以使用这个功能来模拟定位。不过使用这种方式需要打开电脑,开启 xcode,然后再编写 GPX 文件,对于普通用户,这个门槛太高了。国内的一些 iOS 助手类软件,将 Simulate location 功能逆向出来,集成到自己的软件上,比如下面这个 iTools 软件:

pic

闪电口外设

典型产品如位移精灵,不但具备虚拟定位能力,还具备充电宝功能,多种用途,很方便啊,有木有。

pic

那这个东西是啥原理呢?

解释起来很简单,这也是苹果提供的功能,不是啥黑科技。通过苹果 MFi(Made For iOS) 认证厂商会获得 MFI Accessory Interface Specification 文档,其中描述了 USB 与外设之间的通讯协议 iAP2,该协议具备许多丰富的功能:

pic

可以看到其中就有 location 定位功能,按照协议格式,发送对应位置数据,就可以更改定位信息。电脑端 xcode 是不是也是使用 iAP2 协议呢?xcode 实现文档没有公开过,所以这个是未知的。

检测方法

知道了,这三种虚拟定位方式,我们只要找到正常定位与虚拟定位之间的差异就可以做到检测了

动态库注入

有几个明显特征: - 越狱环境,安装越狱插件,其动态库会被注入到目标进程 - 非越狱环境下,砸壳目标ipa,注入动态库 - 劫持(HOOK)定位函数

越狱环境可以检查设备是否越狱,非越狱可以检查 app 是否被重打包。如此,检查定位函数是否被劫持比较关键

函数 HOOK 涉及 OC 语言的动态特性,主要利用了 method_exchangeImplementations() 将函数实现替换掉。被 HOOK 的函数,其 IMP 实现会被劫持到注入的动态库中,所以我们检查函数 IMP 实现是否是原始 module 即可判断是否被 HOOK 了。

1
2
3
4
5
6
7
8
9
10
11
12
13
- (NSString *) getMethodIMPInfo:(NSString *) className, sel:(NSString *)sel {
NSString *ret;
Dl_info info;

IMP imp = class_getMethodImplementation(objc_getClass([className UTF8String]), sel_registerName([sel UTF8String]));
if ( dladdr(imp, &info)) {
ret = [NSString stringWithFormat:@"%@-%@-fname:%s-sname:%s-fbase:%p-saddr:%p", className, sel, info.dli_fname, info.dli_sname, info.dli_fbase, info.dli_saddr];
}

return ret;
}

NSString *ret = getMethodIMPInfo(@"CLLocationManager", @"delegate");
电脑端软件

使用电脑端软件虚拟定位时,app 获取定位信息与正常获取定位信息有差异。

正常获取定位的时代理方法会被执行 3 次:

1
2
3
4
5
6
7
8
9
2020-09-25 14:37:25.587317+0800 iOSApp[990:275020] 纬度为:22.996662, 经度为:113.328448
2020-09-25 14:38:00.158517+0800 iOSApp[990:275020] 高度为:23.031912
2020-09-25 14:38:04.869490+0800 iOSApp[990:275020] 水平精确度:65.000000, 垂直精确度:10.000000
2020-09-25 14:38:22.238887+0800 iOSApp[990:275020] 纬度为:22.996662, 经度为:113.328448
2020-09-25 14:38:23.784470+0800 iOSApp[990:275020] 高度为:23.031912
2020-09-25 14:38:25.844754+0800 iOSApp[990:275020] 水平精确度:65.000000, 垂直精确度:10.000000
2020-09-25 14:38:26.841710+0800 iOSApp[990:275020] 纬度为:22.996667, 经度为:113.328447
2020-09-25 14:38:26.842180+0800 iOSApp[990:275020] 高度为:23.047693
2020-09-25 14:38:26.842767+0800 iOSApp[990:275020] 水平精确度:65.000000, 垂直精确度:10.000000

使用xcode虚拟定位的 location 代理方法仅执行一次:

1
2
3
2020-09-25 07:41:57.466217+0100 iOSApp[1005:276293] 纬度为:51.509980, 经度为:-0.133700
2020-09-25 07:41:57.466404+0100 iOSApp[1005:276293] 高度为:0.000000
2020-09-25 07:41:57.466511+0100 iOSApp[1005:276293] 水平精确度:5.000000, 垂直精确度:-1.000000

多次实验后,发现以下特征:正常获取定位信息时,locationManager 代理方法会被回调 3 次,而虚拟定位,代理方法仅会被回调 1 次;正常定位可以获取海拔高度,水平精确度,垂直精确度,虚拟定位时,无法获取海拔高度,水平精确度,垂直精确度也是固定的错误值。以上两点可以作为检查依据。

闪电口外设

没有购买位移精灵,没有做测试,不过在 gayhub 上发现一份检查 iap2 外设定位的代码 iap2-location-checker

其主要思路也是检查定位数据的精度。还有一种思路是检查外设类型,获取外设的硬件信息,如 mac,manufacturer,brand 之类的信息,不过在高版本 iOS 中,需要额外申请权限,这里就没有实践了。

参考