安卓虚拟定位技术已经出现很多年了,对于现在来说并不是什么新鲜技术,但是实际上我自己并没有使用过,因为暂时没有需求。然而最近需求来了,想找个现成的工具居然要我开会员,无奈只好自己学习一下相关的知识。
很多人(包括我自己)可能想当然的认为安卓上的虚拟定位必须依赖于Root权限,其实不然,在非Root的情况下也能实现一定程度上的虚拟定位,那就是借助开发者模式中的模拟位置功能。可以参考这篇文章。
开发者模式中提供了模拟位置的接口,能够自己开发一个用于模拟位置的app,只要在Manifest中声明权限"android.permission.ACCESS_MOCK_LOCATION"
后,即可在开发者选项-选择模拟位置信息应用
中选择这个app,具体任何模拟位置则由app中的实现决定,我暂时没有亲自试过,可以知道的是,使用这种方法能够实现百度地图的虚拟定位,但是对于其他很多主流app是无法生效的,所以其实算是比较鸡肋,因为这个功能原来就是设计给开发者使用的。
在有Root的情况下,理论上虚拟定位能够对所有app生效。不讨论某些大厂的极端情况,一般来说,应用获取位置信息的来源有3个:
我们只要能够篡改这三个来源的数据就能实现虚拟定位的功能。我们需要知道的就是与这三个来源相关的所有API,并对其进行合理的篡改。参考这篇文章,我们可以知道很多相关的重要接口以及篡改思路,并且文末有个xposed插件,可以实现企业微信的虚拟定位。但是由于代码中写死了企业微信的包名,所以只能对企业微信生效,其实现在有LSPosed可以指定模块只对特定的app生效,所以已经没必要用以往的包名限定写法来写xposed插件了。这边我将其hook的对象包名改成com.tencent.mm
即微信,测试后证明了该插件经久不衰,即使时隔多年也依然能对最新版本的微信生效后,我阅读了它的源码,并参考其主要hook逻辑,实现了对应的frida版本,虽然没有100%对所有的API进行hook,但是已经能够生效。
代码如下:
var Latitude = 30
var Longitude = 120
Java.perform(()=>{
var telMng = Java.use("android.telephony.TelephonyManager")
var ArrayList = Java.use("java.util.ArrayList")
telMng.getCellLocation.implementation = function(){
console.log("getCellLocation")
return null
}
telMng.getNeighboringCellInfo.implementation = function(){
console.log("getNeighboringCellInfo")
return null
}
telMng.getAllCellInfo.implementation = function(){
console.log("getAllCellInfo")
return null
}
telMng.getPhoneCount.implementation = function(){
console.log("getPhoneCount")
return 1
}
var phoneStateListener = Java.use("android.telephony.PhoneStateListener")
phoneStateListener.onCellLocationChanged.implementation = function(){
console.log("onCellLocationChanged")
}
// phoneStateListener.onCellInfoChanged.implementation = function(){
// console.log("onCellInfoChanged")
// return this.onCellInfoChanged.apply(this,arguments)
// }
var WifiManager = Java.use("android.net.wifi.WifiManager")
WifiManager.getScanResults.implementation = function(){
console.log("getScanResults")
return ArrayList.$new()
}
WifiManager.getWifiState.implementation = function(){
console.log("getWifiState")
return 1
}
WifiManager.isWifiEnabled.implementation = function(){
console.log("isWifiEnabled")
return true
}
var WifiInfo = Java.use("android.net.wifi.WifiInfo")
WifiInfo.getMacAddress.implementation = function(){
console.log("getMacAddress")
return "00-00-00-00-00-00-00-00"
}
WifiInfo.getSSID.implementation = function(){
console.log("getSSID")
return null
}
WifiInfo.getBSSID.implementation = function(){
console.log("getBSSID")
return "00-00-00-00-00-00-00-00"
}
var NetworkInfo = Java.use("android.net.NetworkInfo")
NetworkInfo.getTypeName.implementation = function(){
console.log("getTypeName")
return "WIFI"
}
NetworkInfo.isConnectedOrConnecting.implementation = function(){
console.log("isConnectedOrConnecting")
return true
}
NetworkInfo.isConnected.implementation = function(){
console.log("isConnected")
return true
}
NetworkInfo.isAvailable.implementation = function(){
console.log("isAvailable")
return true
}
var CellInfo = Java.use("android.telephony.CellInfo")
CellInfo.isRegistered.implementation = function(){
console.log("isRegistered")
return true
}
var LocationManager = Java.use("android.location.LocationManager")
var Location = Java.use("android.location.Location")
LocationManager.getLastLocation.implementation = function(){
console.log("getLastLocation")
var location = Location.$new(LocationManager.GPS_PROVIDER.value)
location.setLatitude(Latitude)
location.setLongitude(Longitude)
location.setAccuracy(100)
location.setTime(new Date().getTime())
return location
}
LocationManager.getLastKnownLocation.implementation = function(){
console.log("getLastKnownLocation")
var location = Location.$new(LocationManager.GPS_PROVIDER.value)
location.setLatitude(Latitude)
location.setLongitude(Longitude)
location.setAccuracy(100)
location.setTime(new Date().getTime())
return location
}
LocationManager.getProviders.overload('boolean').implementation = function(){
console.log("getProviders1")
var array = ArrayList.$new()
array.add("gps")
return array
}
LocationManager.getProviders.overload('android.location.Criteria', 'boolean').implementation = function(){
console.log("getProviders2")
var array = ArrayList.$new()
array.add("gps")
return array
}
LocationManager.getBestProvider.implementation = function(){
console.log("getBestProvider")
return "gps"
}
LocationManager.addGpsStatusListener.implementation = function(args){
console.log("addGpsStatusListener")
var ret = this.addGpsStatusListener(args)
if(args != null){
args.onGpsStatusChanged(1)
args.onGpsStatusChanged(3)
}
return ret
}
LocationManager.addNmeaListener.overload('android.location.GpsStatus$NmeaListener').implementation = function(){
console.log("addNmeaListener")
return false
}
// LocationManager.getGpsStatus.implementation = function(){
// console.log("getGpsStatus")
// }
})