Android 定位信息

2,473 阅读7分钟

Android 定位方式

GPS定位

定义:全球卫星定位系统,直接和卫星交互,获取设备经纬度

优点:

  • 走卫星通信通道,无需打开Wifi或流量就能获得位置信息
  • 精确度最高,几米到几十米

缺点:

  • 需要手机支持GPS模块(大部分智能机基本都有)
  • 耗电量大
  • 大部分用户可能关闭GPS
  • 定位慢,特别是从GPS模块启动到获取到第一次定位数据,需要的时间较长
  • 室内、地下室基本无法使用
var listener: LocationListener? = null

fun gpsLocation(completion: (Boolean, String?, Location?) -> Unit) {
    PermissionUtil.guardPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) { success ->
        if (success) {
            val locationManager = AppUtils.application.applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
            listener = object : LocationListener {
                override fun onLocationChanged(location: Location?) {
                    completion(true, null, location)
                    listener?.let {
                        locationManager.removeUpdates(it)
                        listener = null
                    }
                }

                override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {

                }

                override fun onProviderDisabled(provider: String?) {

                }

                override fun onProviderEnabled(provider: String?) {

                }
            }
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0.0f, listener)
        } else {
            completion(false, "Permission Denied", null)
        }
    }
}

Network定位

基站定位

方式:

  • 三角定位:由于每个基站的位置是固定的,手机通过扫描到附近的三个基站的信号,因其必然在这三个基站的共同覆盖范围内,从而利用电磁波在这三个基站间中转所需要时间,来算出手机所在的准确坐标。但因大部分手机都只能获取到单个基站的ID,所以此方法基本无法实行
  • 单基站定位:利用获取最近的基站的信息,其中包括基站ID,lac、mcc、mnc和信号强度,将这些数据发送到Google的定位Web服务里,就能拿到当前所在的位置信息,误差一般在几十米到几百米之内。其中信号强度这个数据很重要
mcc mobile country code 移动电话基站的国家代码,中国460
mnc mobile network code 移动电话基站的网络类型:0移动GSM,1联通WCDMA(电信CDMA对应sid),十进制
lac location area code 电信对应nid,十进制

优点:

  • 只要网络通畅即可定位
  • 可以在室内使用

缺点:

  • 精确度很差,一般有几百米,甚至上千米的误差。误差和基站的信号覆盖半径有关,Wifi覆盖半径大概100米左右,而基站则到千米级别

WIFI定位

定义:根据一个固定的WifiMAC地址,通过收集到的该Wifi热点的位置,然后访问网络上的定位服务以获得经纬度坐标。因为它和基站定位其实都需要使用网络,所以在Android也统称为Network方式。

一说到wifi定位,很多人觉得奇怪,Wifi怎么能知道我的位置呢,Wifi硬件会返回位置吗?其实这些Wifi都不能做到。Wifi定位的原理是,我们在室外的时候,手机能接收到GPS位置信息,也能扫描到Wifi,当手机把GPS位置和Wifi传到后端服务器,Wifi和GPS位置就建立了映射关系,当手机在室内无法接收到GPS时,却能扫描Wifi,手机把Wifi传到服务器查询出对应的GPS位置,然后进行计算可以得到位置结果(经纬度),其中映射和计算是很复杂的过程。

locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0.0f, listener)

优点:

  • 精度相对GPS差一点,但也能到十几米、几十米的误差
  • 低耗电

被动定位

读取其他应用保存的定位信息。当其它应用使用定位更新定位信息后,系统会被保存下来,被动定位就是读取的这些信息。如果系统中已经安装了百度地图、高德地图之类的应用,你只要使用他们定位过后,使用被动定位就可以拿到比较精确的定位信息了。局限性很大,依赖于别的应用。

locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0.0f, listener)

IP定位

定义:根据设备上获得的IP地址,访问网络上的定位服务以获得经纬度坐标

AGPS定位

定义:辅助全球卫星定位系统,结合GSM或GPRS与传统卫星定位,利用基站代送辅助卫星信息,缩减GPS芯片获取卫星信号的延迟时间,受遮盖的室内也能借基站讯号弥补,减轻GPS芯片对卫星的依赖度

优点:

  • 在室外等空旷地区,其精度在正常的GPS工作环境下,可达10米左右,堪称目前定位精度最高的一种定位技术
  • 首次捕获GPS信号的时间一般仅需几秒,不像GPS的首次捕获时间可能要2~3分钟

缺点:

  • 需要在手机内增加GPS接收机模块,并改造手机天线,同时要在移动网络上加建位置服务器、差分GPS基准站等设备。如果要提高该方案在室内等GPS信号屏蔽地区的定位有效性,该方案还提出需要增添类似于EOTD方案中的位测量单元(LMU)。

Android 模拟位置方式

GPS伪造

GPS信号可以通过编程修改GPS芯片驱动,来改变获取到的位置,此方法成本非常昂贵。

GSM伪造

通过修改设备硬件,或者伪造基站来伪造定位,成本也相对较高。并且一般通信都是加密的,也会造成不少困扰。

编译时就修改NLP结果

思路:修改NLP部分源码,重编系统

难度:非常大

运行时修改LocationManager结果

1. 使用Android自带调试Api,模拟定位结果

打开开发者选项,然后在设置中打开允许模拟位置的开关。

要模拟位置时,使用以下代码:

fun mock(providerName: String, location: Location) {
    val locationManager = AppUtils.application.applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    locationManager.addTestProvider(providerName, false, true, true, false, true, true, true, android.location.Criteria.POWER_HIGH, android.location.Criteria.ACCURACY_FINE)
    locationManager.setTestProviderEnabled(providerName, true)
    locationManager.setTestProviderStatus(providerName, LocationProvider.AVAILABLE, null, System.currentTimeMillis())
    locationManager.setTestProviderLocation(providerName, location)
}

有一些应用会在定位前检测是否开启了虚拟定位,如果想欺骗他们,需要在虚拟定位之前,把开关打开,定位后立即把开关关上,这样短暂的时间让其他程序基本看不到ALLOW_MOCK_LOCATION的变化。或者是直接Hook是否开启虚拟定位的接口,始终返回False。

private fun restoreMockLocationSettings(restore_value: Int) {
    try {
        Settings.Secure.putInt(getContentResolver(),
                Settings.Secure.ALLOW_MOCK_LOCATION, restore_value)
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

当然,这两种方法都需要Root权限。

2. 使用xposed,对app_process进行注入

xposed号称Android上最强大的神器,它通过替换/system/bin/app_precesss程序控制zygote进程,使得它在系统启动的过程中会加载Xposed framework的一个jar文件即XposedBridge.jar,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持,并且能够允许开发者独立的替代任何class,例如framework本身,系统UI又或者随意的一个app。

安装xposed框架需要Root权限。

Android 定位反作弊方式

使用removeTestProvider

在定位前先调用removeTestProvider方法,移除虚拟定位。

检测NMEA data

NMEA data是用于通信或接收GPS数据的标准,在真实的GPS定位中,每次取得位置信息,都会回调NmeaListener的onNmeaReceived方法,而虚拟定位则不会。

我们可以通过监听NmeaListener是否改变来判断此次的定位是否是真实的:

locationManager.addNmeaListener(nmeaListener);

检测虚拟位置开关

开发者选项中有模拟位置的选项可选,开发者可以用过模拟位置来伪造地理位置信息。这种方式可以被很多方法检测到:

fun isMockLocationOn(location: Location, context: Context): Boolean {
    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        return location.isFromMockProvider
    } else {
        var mockLocation = "0"
        try {
            mockLocation = Settings.Secure.getString(context.contentResolver, Settings.Secure.ALLOW_MOCK_LOCATION)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return mockLocation != "0"
    }
}

检测安装的应用中是否有使用了ACCESS_MOCK_LOCATION权限

fun areThereMockPermissionApps(context: Context): Boolean {
    var count = 0
    val pm = context.packageManager
    val packages = pm.getInstalledApplications(PackageManager.GET_META_DATA)
    for (applicationInfo in packages) {
        try {
            val packageInfo = pm.getPackageInfo(applicationInfo.packageName,
                    PackageManager.GET_PERMISSIONS)
            // Get Permissions
            val requestedPermissions = packageInfo.requestedPermissions
            if (requestedPermissions != null) {
                for (i in requestedPermissions.indices) {
                    if (requestedPermissions[i] == "android.permission.ACCESS_MOCK_LOCATION" && applicationInfo.packageName != context.packageName) {
                        count++
                    }
                }
            }
        } catch (e: NameNotFoundException) {
            Log.e("Got exception " + e.getMessage())
        }

    }
    return count > 0
}

检测手机是否Root

对于root机型,几乎无法从根源上反作弊,对于出来四年的Ingress也几乎无法避免作弊的问题,目前可知的Ingress对于位置欺骗的反作弊方式有:

  • 速度限制, 35 miles per hour 大概是 55km/h
  • 交叉验证 SSID,通过匹配数据库中记录的 SSID 来确定位置

而对于这两种方式Ingress官方并没有明确的文档指出,基本通过玩家黑盒测试得出。而对于最新出的Pokemon Go,在全球作弊成风的情况下官方直接禁用了Root机型。可见目前对于Root机型反作弊还没有一个切实有效的方法检测。

参考文章