Отключить / проверить фиктивное местоположение (предотвратить спуфинг GPS)

92

Ищете лучший способ предотвратить / обнаружить спуфинг GPS на Android. Есть предложения о том, как этого добиться и что можно сделать, чтобы это остановить? Я предполагаю, что пользователь должен включить фиктивные местоположения, чтобы подделать GPS, если это будет сделано, тогда они могут подделать GPS?

Думаю, мне нужно просто определить, включены ли Mock Locations? Есть другие предложения?

Chrispix
источник
2
Я думаю, он спрашивает о функции подмены местоположения, доступной в представлении DDMS в Eclipse.
Шон Уолтон
2
У меня есть игра, основанная на местоположении, и я не хочу, чтобы люди обманывали ее, поэтому я хочу заблокировать спуфинг, я понимаю, что это может произойти одним из двух способов. Включая фиктивные местоположения и создавая пользовательское изображение, которое выполняет спуфинг низкого уровня и игнорирует настройку спуфинга в приложении настроек. Попытка найти поставщик Settings.System для MockLocations или посмотреть, включен ли он (со слушателем в середине приложения).
Chrispix

Ответы:

127

Я провел небольшое расследование и поделился своими результатами здесь, это может быть полезно для других.

Во-первых, мы можем проверить, включена ли опция 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.

Джамбааз
источник
Очень интересные наблюдения. Особенно последний +1 за то, что поделился этим.
ar-g
2
Примечание о методе removeTestProvider. Если вы разрешите диспетчеру местоположения работать в фоновом режиме, пользователь может перейти в фиктивное приложение и перезапустить фиктивное местоположение. После этого ваш менеджер местоположения начнет получать фиктивные местоположения, пока вы снова не вызовете removeTestProvider.
Timur_C
4
Также ваше приложение должно иметь android.permission.ACCESS_MOCK_LOCATIONразрешение на removeTestProviderработу, что, на мой взгляд, является самым большим недостатком.
Timur_C
18
Спасибо за ответ! просто точка: в Android 6.0 ALLOW_MOCK_LOCATION устарела. И на самом деле нет флажка для фиктивного местоположения. Проверить, является ли местоположение фальшивым или нет, можно из объекта местоположения
Silwester
3
@Blackkara В конце концов, я им не пользовался. Я использовал индивидуальные комбинации isMockSettingsON(), Location.isFromMockProvider()и areThereMockPermissionApps()с черным списком приложений. Есть много предустановленных системных приложений с ACCESS_MOCK_LOCATION разрешением, например на устройствах HTC и Samsung. Белый список всех законных приложений был бы лучше, но черный список самых популярных приложений для подмены местоположения в моем случае работал хорошо. И я также проверил, было ли устройство рутировано.
Timur_C
46

Начиная с 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");
}
Фернандо
источник
Я почти уверен, что ваше второе выражение обратное (верните true, когда оно должно вернуть false). Думаю, так и должно быть:isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
charles-allen
3
Пожалуйста. Спасибо, что разместили более современный ответ! Действительно, на сегодняшний день это правильный ответ.
charles-allen
1
Как мы можем обойтись без объекта "местоположение" для SDK старше 18?
Аджит Шарма
35

Похоже, что единственный способ сделать это - предотвратить подмену местоположения, предотвращающую 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;
Chrispix
источник
4
Однако это не доказательство от дурака. Пользователи на устройстве без рутирования могут по-прежнему устанавливать фиктивное местоположение в любое время в будущем, а затем отключать фиктивные местоположения, и фиктивное местоположение все еще остается активным. Хуже того, они могут называть фиктивное местоположение тем же именем провайдера, что и Network / Gps, и, по-видимому, оно
исходит
3
Кроме того, Fake GPS не требует настройки фиктивного местоположения на устройствах с root-доступом.
Пол Ламмертсма
Всегда можно проверить, не установлено ли приложение fake.gps :)
Chrispix,
11
Вы можете использовать return !xвместо if(x) return false; else return true.
CodesInChaos
Фактически, фиктивное местоположение изменит настройку фиктивного местоположения даже на устройствах с root-доступом.
PageNotFound
26

Наткнулся на эту ветку пару лет спустя. В 2016 году большинство устройств Android будут иметь уровень API> = 18 и, следовательно, должны полагаться на Location.isFromMockProvider (), как указал Фернандо .

Я много экспериментировал с фальшивыми / фиктивными локациями на разных устройствах и дистрибутивах Android. К сожалению .isFromMockProvider () не на 100% надежен. Время от времени поддельное местоположение не будет помечено как фиктивное . Похоже, это происходит из-за некоторой ошибочной внутренней логики объединения в Google Location API.

Я написал подробный пост об этом в блоге , если вы хотите узнать больше. Подводя итог, если вы подпишетесь на обновления местоположения из Location API, затем включите поддельное приложение GPS и распечатаете результат каждого Location.toString () на консоли, вы увидите что-то вроде этого:

введите описание изображения здесь

Обратите внимание, что в потоке обновлений местоположения одно местоположение имеет те же координаты, что и другие, но не помечено как фиктивное и имеет гораздо более низкую точность определения местоположения.

Чтобы решить эту проблему, я написал служебный класс, который надежно подавит фиктивные местоположения во всех современных версиях 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) это в основном это.

Конечно, с рутированной ОС вы все еще можете найти способы подделки информации о местоположении, которые обычные приложения не могут обнаружить.

KlaasNotFound
источник
Вы должны кратко описать связанный пост в блоге (где isFromMockProvider не работает).
charles-allen
@CodeConfident - спасибо за замечание! Не уверен, что я должен добавить. Второй абзац моего ответа - это резюме сообщения в блоге. .isFromMockProvider периодически и непредсказуемо дает сбой. В статье я просто более подробно описываю шаги, которые я предпринял, чтобы обнаружить и исправить это.
KlaasNotFound
Что ж, я был вынужден перейти к вашей статье, чтобы понять, что, на мой взгляд, идет вразрез с намерениями SO. Моим лучшим предложением было бы: (1) вставьте свою картинку, которая показывает изворотливое местоположение (не помечено как макет) и (2) быстро запишите свою логику для их устранения (игнорируйте в пределах 1 км от макета)
Чарльз-Аллен,
Хорошо, понял. Я думаю, что в контексте ОП специфика почему .isFromMockProvider () ненадежен, не слишком актуальны. Но я попытаюсь добавить детали, которые вы упомянули, для большей картины. Спасибо за ответ!
KlaasNotFound
1
Что делать, если у пользователя не установлен сервис Google Play?
Юрий Чернышов
6

Если вам известно общее расположение вышек сотовой связи, вы можете проверить, совпадает ли текущая вышка сотовой связи с заданным местоположением (в пределах погрешности чего-то большого, например, 10 или более миль).

Например, если ваше приложение открывает функции только в том случае, если пользователь находится в определенном месте (например, в вашем магазине), вы можете проверить GPS, а также вышки сотовой связи. В настоящее время ни одно приложение для спуфинга GPS также не подделывает вышки сотовой связи, поэтому вы можете увидеть, не пытается ли кто-то по всей стране просто обманом проникнуть в ваши специальные функции (например, я думаю о приложении Disney Mobile Magic).

Именно так приложение Llama управляет местоположением по умолчанию, поскольку проверка идентификаторов вышек сотовой связи требует гораздо меньшего расхода заряда батареи, чем gps. Это бесполезно для очень определенных мест, но если дом и работа находятся в нескольких милях от него, он может очень легко различить два основных местоположения.

Конечно, это потребует от пользователя вообще наличия сотового сигнала. И вам нужно будет знать идентификаторы всех вышек сотовой связи в этом районе - всех сетевых провайдеров - или вы рискуете получить ложноотрицательный результат.

Стивен Шраугер
источник
1
Спасибо, это неплохая идея. Возможно, мне придется разобраться в этом. Спасибо
Chrispix
3

попробуйте этот код, он очень простой и полезный

  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;
    }
Мостафа Пирхаяти
источник
Это гораздо лучшая версия метода isMockLocationEnabled, описанного выше.
setzamora
AppOpsManager.checkOp () выбрасывает SecurityException, если приложение было настроено на сбой в этой операции. Например: java.lang.SecurityException: packagename из uid 11151 не разрешено выполнять MOCK_LOCATION. Этот метод собирается определить, «может ли ваше приложение имитировать местоположение». Но не «если полученные локации высмеиваются».
Серхио
@Sergio, говоря «если ваше приложение может имитировать местоположения», вы имеете в виду, если у текущего приложения есть разрешение имитировать местоположения, верно?
Виктор Лаэрт
@VictorLaerte Я последний раз рассказал о контексте темы: / Верно. Но вопрос был в следующем: «как определить, является ли полученное местоположение ложным или полученное от ложного провайдера». Или как игнорировать фальшивые локации.
Серхио
2

Этот скрипт работает для всех версий 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
    }
Али
источник
1

Вы можете добавить дополнительную проверку на основе данных триангуляции вышек сотовой связи или информации о точках доступа 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();
}
yoAlex5
источник