Отключить / проверить на ложное местоположение (предотвратить подделку GPS)
Ищете лучший способ предотвратить / обнаружить спуфинг GPS на Android. Любые предложения о том, как это сделать, и что можно сделать, чтобы остановить это? Я предполагаю, что пользователь должен включить фиктивные местоположения, чтобы обмануть GPS, если это будет сделано, то они могут обмануть GPS?
Я думаю, мне нужно было бы просто определить, включены ли Mock Locations? Любые другие предложения?
10 ответов
Я провел некоторое исследование и поделился своими результатами здесь, это может быть полезно для других.
Во-первых, мы можем проверить, включена ли опция MockSetting
public static boolean isMockSettingsON(Context context) {
// returns true if mock location enabled, false if not enabled.
if (Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
}
Во-вторых, мы можем проверить, есть ли в устройстве другие приложения, которые используют android.permission.ACCESS_MOCK_LOCATION
(Расположение спуфинга приложений)
public static boolean areThereMockPermissionApps(Context context) {
int count = 0;
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> packages =
pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : packages) {
try {
PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName,
PackageManager.GET_PERMISSIONS);
// Get Permissions
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i]
.equals("android.permission.ACCESS_MOCK_LOCATION")
&& !applicationInfo.packageName.equals(context.getPackageName())) {
count++;
}
}
}
} catch (NameNotFoundException e) {
Log.e("Got exception " , e.getMessage());
}
}
if (count > 0)
return true;
return false;
}
Если оба вышеуказанных метода, первый и второй, верны, то есть хорошие шансы, что местоположение может быть подделано или подделано.
Теперь спуфинга можно избежать с помощью API-интерфейса Location Manager.
Мы можем удалить тестового провайдера, прежде чем запрашивать обновления местоположения у обоих провайдеров (сеть и GPS)
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
try {
Log.d(TAG ,"Removing Test providers")
lm.removeTestProvider(LocationManager.GPS_PROVIDER);
} catch (IllegalArgumentException error) {
Log.d(TAG,"Got exception in removing test provider");
}
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, locationListener);
Я видел, что removeTestProvider(~) очень хорошо работает над Jelly Bean и более поздними версиями. Этот API оказался ненадежным до Ice Cream Sandwich.
Начиная с API 18, объект Location имеет метод .isFromMockProvider(), так что вы можете отфильтровывать поддельные местоположения.
Если вы хотите поддерживать версии до 18, можно использовать что-то вроде этого:
boolean isMock = false;
if (android.os.Build.VERSION.SDK_INT >= 18) {
isMock = location.isFromMockProvider();
} else {
isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
}
Кажется, что единственный способ сделать это - предотвратить подмену местоположения, предотвращая MockLocations. С другой стороны, есть некоторые пользователи, которые используют Bluetooth GPS-устройства для получения лучшего сигнала, они не смогут использовать приложение, так как они обязаны использовать макеты.
Для этого я сделал следующее:
// returns true if mock location enabled, false if not enabled.
if (Settings.Secure.getString(getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else return true;
Stumbled upon this thread a couple years later. In 2016, most Android devices will have API level >= 18 and should thus rely on Location.isFromMockProvider() as pointed out by Fernando.
I extensively experimented with fake/mock locations on different Android devices and distros. Unfortunately .isFromMockProvider() is not 100% reliable. Every once in a while, a fake location will not be labeled as mock. This seems to be due to some erroneous internal fusion logic in the Google Location API.
Я написал подробный пост в блоге об этом, если вы хотите узнать больше. Подводя итог, если вы подписываетесь на обновления местоположения из Location API, затем включаете поддельное приложение GPS и распечатываете результат каждого Location.toString() на консоль, вы увидите что-то вроде этого:
Обратите внимание, что в потоке обновлений местоположений одно местоположение имеет те же координаты, что и другие, но не помечается как макет и имеет гораздо более низкую точность определения местоположения.
Чтобы исправить эту проблему, я написал служебный класс, который будет надежно подавлять местоположения Mock во всех современных версиях Android (API уровня 15 и выше):
LocationAssistant - без проблем обновления местоположения на Android
По сути, он "не доверяет" не фиктивным местам, которые находятся в пределах 1 км от последнего известного макета, а также помечает их как макет. Это происходит до тех пор, пока не появится значительное количество не фиктивных мест. LocationAssistant может не только отклонять фиктивные местоположения, но и освобождает вас от большинства хлопот, связанных с настройкой и подпиской на обновления местоположений.
Для получения только реальных обновлений местоположения (т. Е. Подавления имитаций) используйте его следующим образом
public class MyActivity extends Activity implements LocationAssistant.Listener {
private LocationAssistant assistant;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// You can specify a different accuracy and interval here.
// The last parameter (allowMockLocations) must be 'false' to suppress mock locations.
assistant = new LocationAssistant(this, this, LocationAssistant.Accuracy.HIGH, 5000, false);
}
@Override
protected void onResume() {
super.onResume();
assistant.start();
}
@Override
protected void onPause() {
assistant.stop();
super.onPause();
}
@Override
public void onNewLocationAvailable(Location location) {
// No mock locations arriving here
}
...
}
onNewLocationAvailable()
теперь будет вызываться только с информацией о реальном местоположении. Есть еще несколько методов прослушивания, которые вам нужно реализовать, но в контексте вашего вопроса (как предотвратить подделку GPS) это в основном так.
Конечно, с рутованной ОС вы все еще можете найти способы подделки информации о местоположении, которые невозможно обнаружить обычным приложениям.
Если вам известно общее местоположение вышек сотовой связи, вы можете проверить, соответствует ли текущая вышка сотовой связи указанному местоположению (в пределах погрешности чего-то большого, например, 10 или более миль).
Например, если ваше приложение разблокирует функции, только если пользователь находится в определенном месте (например, в вашем магазине), вы можете проверить gps, а также вышки сотовой связи. В настоящее время ни одно приложение gps-спуфинга также не подделывает сотовые вышки, поэтому вы можете увидеть, просто ли кто-то по всей стране пытается подделать ваши специальные функции (например, я имею в виду приложение Disney Mobile Magic).
Именно так приложение Llama управляет местоположением по умолчанию, поскольку проверка идентификаторов вышек сотовой связи требует гораздо меньше заряда аккумулятора, чем GPS. Это бесполезно для очень специфических мест, но если дом и работа находятся на расстоянии нескольких миль, он может очень легко отличить два основных места.
Конечно, это потребует от пользователя вообще иметь сотовый сигнал. И вам нужно будет знать все идентификаторы вышек сотовой связи в этом районе - у всех сетевых провайдеров - или вы рискуете получить ложный отрицательный результат.
Попробуйте этот код очень просто и полезно
public boolean isMockLocationEnabled() {
boolean isMockLocation = false;
try {
//if marshmallow
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AppOpsManager opsManager = (AppOpsManager) getApplicationContext().getSystemService(Context.APP_OPS_SERVICE);
isMockLocation = (opsManager.checkOp(AppOpsManager.OPSTR_MOCK_LOCATION, android.os.Process.myUid(), BuildConfig.APPLICATION_ID)== AppOpsManager.MODE_ALLOWED);
} else {
// in marshmallow this will always return true
isMockLocation = !android.provider.Settings.Secure.getString(getApplicationContext().getContentResolver(), "mock_location").equals("0");
}
} catch (Exception e) {
return isMockLocation;
}
return isMockLocation;
}
Этот скрипт работает для всех версий Android, и я нахожу его после многих поисков
LocationManager locMan;
String[] mockProviders = {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER};
try {
locMan = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
for (String p : mockProviders) {
if (p.contentEquals(LocationManager.GPS_PROVIDER))
locMan.addTestProvider(p, false, false, false, false, true, true, true, 1,
android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_HIGH);
else
locMan.addTestProvider(p, false, false, false, false, true, true, true, 1,
android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_LOW);
locMan.setTestProviderEnabled(p, true);
locMan.setTestProviderStatus(p, android.location.LocationProvider.AVAILABLE, Bundle.EMPTY,
java.lang.System.currentTimeMillis());
}
} catch (Exception ignored) {
// here you should show dialog which is mean the mock location is not enable
}
Вы можете добавить дополнительную проверку на основе триангуляции вышек сотовой связи или информации о точках доступа Wi-Fi, используя Google Maps Geolocation API
Самый простой способ получить информацию о CellTowers
final TelephonyManager telephonyManager = (TelephonyManager) appContext.getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = telephonyManager.getNetworkOperator();
int mcc = Integer.parseInt(networkOperator.substring(0, 3));
int mnc = Integer.parseInt(networkOperator.substring(3));
String operatorName = telephonyManager.getNetworkOperatorName();
final GsmCellLocation cellLocation = (GsmCellLocation) telephonyManager.getCellLocation();
int cid = cellLocation.getCid();
int lac = cellLocation.getLac();
Вы можете сравнить свои результаты с сайтом
Чтобы получить информацию о точках доступа Wi-Fi
final WifiManager mWifiManager = (WifiManager) appContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if (mWifiManager != null && mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
// register WiFi scan results receiver
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
List<ScanResult> results = mWifiManager.getScanResults();//<-result list
}
};
appContext.registerReceiver(broadcastReceiver, filter);
// start WiFi Scan
mWifiManager.startScan();
}
Ниже подход работает для меня, чтобы правильно определить фиктивное местоположение
@Override
public void onLocationChanged (Location location){
boolean isMockLocation = location.isFromMockProvider();
}
Вставьте это в свою деятельность / где вы хотите проверить поддельный / фиктивный gps
try {
if (areThereMockPermissionApps(mContext)) {
Log.e(TAG, " - " + "Yup its use fake gps");
List<String> mFakeList = new ArrayList<>();
mFakeList = getListOfFakeLocationAppsInstalled(mContext); // this will return the fake app list
for (int a = 0; a < mFakeList.size(); a++) {
Log.e(TAG, mFakeList.size() + " - " + "NameList ----- " + mFakeList.get(a));
}
} else
Log.e(TAG, " - " + "Nope its not use fake gps");
} catch (Exception w) {
w.printStackTrace();
}
Здесь вы можете получить список установленных на вашем устройстве фальшивых / фиктивных приложений.
private List<String> getListOfFakeLocationAppsInstalled(Context context) {
List<String> fakeApps = new ArrayList<>();
try {
List<String> runningApps = new ArrayList<>();
final PackageManager pm = getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo packageInfo : packages) {
runningApps.add(packageInfo.packageName);
} // the getLaunchIntentForPackage returns an intent that you can use with startActivity()
for (String app : runningApps) {
if (!isSystemPackage(context, app) && hasAppPermission(context, app, "android.permission.ACCESS_MOCK_LOCATION")) {
fakeApps.add(getApplicationName(context, app));
}
}
} catch (Exception w) {
w.printStackTrace();
}
return fakeApps;
}
Вставьте этот метод в свой помощник / тот же класс
public static boolean areThereMockPermissionApps(Context context) {
int count = 0;
try {
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> packages =
pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : packages) {
try {
PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName,
PackageManager.GET_PERMISSIONS);
// Get Permissions
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i]
.equals("android.permission.ACCESS_MOCK_LOCATION")
&& !applicationInfo.packageName.equals(context.getPackageName())) {
count++;
}
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.e("MockDeductionAgilanbu", "Got exception --- " + e.getMessage());
}
}
} catch (Exception w) {
w.printStackTrace();
}
if (count > 0)
return true;
return false;
}