改机 - 从源码着手任意修改GPS地理位置
需求:随意修改定位
android在改机过程中,经常会遇到随意修改位置GPS的需求。
修改GPS的方式有很多种:
xposed hook
MockLocation
修改源码
以上三种方式都能修改gps随意修改gps坐标,各有优缺点:xposed隐藏不好,容易被发现;MockLocation容易在开发者模式和gps provider被识别;改源码,编译麻烦,而且不一定有源码;前两种方式具有普适性,改源码费时费力,局限性比较强;
秉承明知山有虎,偏向虎上行的心态,尝试阅读以下android的源码,并且修改gps部分的代码;
具体原理:切断hal层和framework之间的通讯,模仿硬件向framework通知硬件信息
样例:android 8.0
1. gps jni callbackstruct GnssCallback : public IGnssCallback {
Return gnssLocationCb(
const android::hardware::gnss::V1_0::GnssLocation& location) override; // gps位置变化回调函数
Return gnssStatusCb(const IGnssCallback::GnssStatusValue status) override; // gps状态变化回调函数
Return gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override; // 卫星状态变化回调函数
Return gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
Return gnssSetCapabilitesCb(uint32_t capabilities) override;
Return gnssAcquireWakelockCb() override;
Return gnssReleaseWakelockCb() override;
Return gnssRequestTimeCb() override;
Return gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override;
static GnssSvInfo sGnssSvList[static_cast(
android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)];
static size_t sGnssSvListSize;
static const char* sNmeaString;
static size_t sNmeaStringLength;
};
// 省略......
Return GnssCallback::gnssLocationCbImpl(const T& location) {
JNIEnv* env = getJniEnv();
jobject jLocation = translateGnssLocation(env, location);
env->CallVoidMethod(mCallbacksObj,
method_reportLocation, // frameworks
boolToJbool(hasLatLong(location)),
jLocation);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
env->DeleteLocalRef(jLocation);
return Void();
}
// 省略......
method_reportLocation = env->GetMethodID(clazz, "reportLocation",
"(ZLandroid/location/Location;)V");
GnssCallback的作用就是在gps模块位置、状态、精度等信息变化后,通知上层应用的回调函数类。
2. framework
可以发现,在gps硬件模块拿到新的位置时,通知framework调用java的函数是reportLocation, 源码如下:
/*
* @hasLatLong: 地理位置是否合法
* @loction: 地理位置
*/
private void reportLocation(boolean hasLatLong, Location location) {
if (location.hasSpeed()) {
mItarSpeedLimitExceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
}
if (mItarSpeedLimitExceeded) {
Log.i(TAG, "Hal reported a speed in excess of ITAR limit." +
" GPS/GNSS Navigation output blocked.");
mGnssMetrics.logReceivedLocationStatus(false);
return;
}
if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString());
synchronized (mLocation) {
mLocation = location;
// It would be nice to push the elapsed real-time timestamp
// further down the stack, but this is still useful
mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
mLocation.setExtras(mLocationExtras);
try {
mILocationManager.reportLocation(mLocation, false);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling reportLocation");
}
}
mGnssMetrics.logReceivedLocationStatus(hasLatLong);
if (hasLatLong) {
if (location.hasAccuracy()) {
mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy());
}
if (mTimeToFirstFix > 0) {
int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime);
mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
}
}
mLastFixTime = SystemClock.elapsedRealtime();
// report time to first fix
if (mTimeToFirstFix == 0 && hasLatLong) {
mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
// notify status listeners
mListenerHelper.onFirstFix(mTimeToFirstFix);
}
if (mSingleShot) {
stopNavigating();
}
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// we want to time out if we do not receive a fix
// within the time out and we are requesting infrequent fixes
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
mAlarmManager.cancel(mTimeoutIntent);
}
// send an intent to notify that the GPS is receiving fixes.
Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
updateStatus(LocationProvider.AVAILABLE, mSvCount, mMeanCn0, mMaxCn0);
}
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
if (DEBUG) Log.d(TAG, "got fix, hibernating");
hibernate();
}
}
自此,刨除hal层,android系统就获取到了一个地理位置。原理了解后,就可以动手修改源码了。
3. 切断hal层调用
GnssLocationProvider.cpp
#include
// ....... 省略
Return GnssCallback::gnssLocationCbImpl(const T& location) {
// 定义一个系统开关,可以自由控制
char property[PROP_VALUE_MAX];
int len = __system_property_get("gps.location.custom", property);
if(len > 0) {
if(strcmp(property, "1") == 0) return Void();
}
// ...... 省略
}
4. 在framework中添加一个public函数
LocationManager.java
// ...... 省略
public void reportCustomLocation(Location location) {
mService.reportLocation(true, location);
}
5. 编译rommake update-api # 在LocationManager中添加了新的接口
make -j32 # 炫耀一下我32核的的机器
5. 在APK中使用
MainActivity.java
// ...... 省略
Button btn = findViewById(R.id.test_btn);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
Location l = new Location("gps");
l.setLatitude(41.055962);
l.setLongitude(110.307711);
l.setAccuracy(2.0f);
l.setTime(System.currentTimeMillis());
l.setElapsedRealtimeNanos(System.currentTimeMillis());
l.setAccuracy(1.0f);
locationManager.reportCustomLocation(l);
Location lo = locationManager.getLastKnownLocation(provider);
Log.d(TAG, lo.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
最后于 -6-29 11:04
被neocanable编辑
,原因: markdown 好像不太好用