Désactiver / vérifier l'emplacement simulé (empêcher l'usurpation d'identité GPS)

90

Vous cherchez à trouver le meilleur moyen de prévenir / détecter l'usurpation GPS sur Android. Avez-vous des suggestions sur la façon dont cela est accompli et que peut-on faire pour y mettre fin? Je suppose que l'utilisateur doit activer les emplacements fictifs pour usurper le GPS, si cela est fait, alors ils peuvent usurper le GPS?

Je suppose que je devrais simplement détecter si les emplacements simulés sont activés? D'autres suggestions?

Chrispix
la source
2
Je pense qu'il pose des questions sur la fonction Spoofing d'emplacement disponible dans la vue DDMS d'Eclipse.
Shawn Walton
2
J'ai un jeu basé sur la géolocalisation sur lequel je ne veux pas que les gens trichent, alors je me permets de bloquer l'usurpation d'identité, je crois comprendre que cela peut se produire de deux manières. Activer les emplacements fictifs et créer une image personnalisée qui effectue une usurpation de bas niveau et ignore le paramètre d'usurpation d'identité dans l'application des paramètres. Essayer de trouver le fournisseur Settings.System pour MockLocations, ou chercher à voir s'il est activé (avec un écouteur au milieu de l'application).
Chrispix

Réponses:

125

J'ai fait quelques recherches et partage mes résultats ici, cela peut être utile pour d'autres.

Tout d'abord, nous pouvons vérifier si l'option MockSetting est activée

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;
}

Deuxièmement, nous pouvons vérifier s'il existe d'autres applications dans l'appareil qui utilisent android.permission.ACCESS_MOCK_LOCATION(Applications de spoofing de localisation)

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;
}

Si les deux méthodes ci-dessus, la première et la deuxième sont vraies, il y a de bonnes chances que l'emplacement soit usurpé ou faux.

Désormais, l'usurpation d'identité peut être évitée en utilisant l'API de Location Manager.

Nous pouvons supprimer le fournisseur de test avant de demander les mises à jour de localisation aux deux fournisseurs (réseau et 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);

J'ai vu que removeTestProvider (~) fonctionne très bien sur Jelly Bean et les versions ultérieures. Cette API semblait peu fiable jusqu'à Ice Cream Sandwich.

Jambaaz
la source
Observations très intéressantes. Surtout le dernier +1 pour avoir partagé cela.
ar-g
2
Remarque sur la méthode removeTestProvider. Si vous autorisez le gestionnaire d'emplacement à fonctionner en arrière-plan, l'utilisateur peut accéder à l'application fictive et redémarrer l'emplacement moqueur. Votre gestionnaire d'emplacement commencera alors à recevoir des emplacements fictifs jusqu'à ce que vous appeliez à nouveau removeTestProvider.
Timur_C
4
De plus, votre application doit avoir l' android.permission.ACCESS_MOCK_LOCATIONautorisation removeTestProviderde fonctionner, ce qui, à mon avis, est le plus gros inconvénient.
Timur_C
18
Merci d'avoir répondu! juste un point: dans Android 6.0, ALLOW_MOCK_LOCATION est obsolète. Et en fait, il n'y a pas non plus de case à cocher pour l'emplacement simulé. On peut vérifier si l'emplacement est faux ou pas directement à partir de l'objet de localisation: location.isFromMockProvider ()
Silwester
2
@Blackkara Je ne l'ai pas utilisé après tout. J'ai utilisé une combinaison personnalisée de isMockSettingsON(), Location.isFromMockProvider()et areThereMockPermissionApps()avec une liste noire des applications. Il existe de nombreuses applications système préinstallées avec ACCESS_MOCK_LOCATION autorisation, par exemple sur les appareils HTC et Samsung. Une liste blanche de toutes les applications légitimes serait préférable, mais une liste noire des applications d'usurpation de localisation les plus populaires fonctionnait bien dans mon cas. Et j'ai également vérifié si l'appareil était enraciné.
Timur_C
45

Depuis l'API 18, l'objet Location a la méthode .isFromMockProvider () afin que vous puissiez filtrer les faux emplacements.

Si vous souhaitez prendre en charge les versions antérieures à 18, il est possible d'utiliser quelque chose comme ceci:

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");
}
Fernando
la source
Je suis assez sûr que votre deuxième expression est à l'envers (retourne true alors qu'elle doit renvoyer false). Je pense que ça devrait être:isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
AjahnCharles
3
Je vous en prie. Merci d'avoir publié une réponse plus moderne! C'est vraiment la bonne réponse à partir d'aujourd'hui.
AjahnCharles
1
Comment pouvons-nous le faire sans objet "location" pour le SDK supérieur à 18?
Ajit Sharma
35

Il semble que le seul moyen de le faire est d'empêcher l'usurpation d'emplacement d'empêcher les MockLocations. L'inconvénient est que certains utilisateurs utilisent des appareils GPS Bluetooth pour obtenir un meilleur signal, ils ne pourront pas utiliser l'application car ils doivent utiliser les emplacements simulés.

Pour ce faire, j'ai fait ce qui suit:

// 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
la source
4
Ce n'est cependant pas une preuve infaillible. Les utilisateurs d'un appareil non rooté peuvent toujours définir un emplacement fictif, avec une heure dans le futur, puis désactiver les emplacements fictifs et l'emplacement fictif est toujours actif. Pire encore, ils peuvent appeler l'emplacement fictif du même nom de fournisseur que Network / Gps et cela tire apparemment de là ...
Chrispix
3
De plus, Fake GPS ne nécessite pas le paramètre de localisation fictive sur les appareils enracinés.
Paul Lammertsma
Pourrait toujours vérifier que l'application fake.gps n'est pas installée :)
Chrispix
11
Vous pouvez utiliser à la return !xplace de if(x) return false; else return true.
CodesInChaos
En fait, le faux emplacement modifiera le paramètre de l'emplacement fictif même sur les appareils enracinés.
PageNotFound
25

Je suis tombé sur ce fil quelques années plus tard. En 2016, la plupart des appareils Android auront un niveau d'API> = 18 et devraient donc s'appuyer sur Location.isFromMockProvider () comme l'a souligné Fernando .

J'ai beaucoup expérimenté avec des emplacements faux / simulés sur différents appareils et distributions Android. Malheureusement .isFromMockProvider () n'est pas fiable à 100%. De temps en temps, un faux emplacement ne sera pas qualifié de faux . Cela semble être dû à une logique de fusion interne erronée dans l'API Google Location.

J'ai écrit un article de blog détaillé à ce sujet , si vous voulez en savoir plus. Pour résumer, si vous vous abonnez aux mises à jour de localisation à partir de l'API Location, puis activez une fausse application GPS et imprimez le résultat de chaque Location.toString () sur la console, vous verrez quelque chose comme ceci:

entrez la description de l'image ici

Remarquez comment, dans le flux de mises à jour d'emplacement, un emplacement a les mêmes coordonnées que les autres, mais n'est pas signalé comme une simulation et a une précision de localisation beaucoup plus faible.

Pour remédier à ce problème, je l' ai écrit une classe utilitaire qui va Suppress les positions fictives de manière fiable dans toutes les versions Android modernes (niveau API 15 et plus):

LocationAssistant - Mises à jour de localisation sans tracas sur Android

Fondamentalement, il «se méfie» des emplacements non simulés qui se trouvent à moins de 1 km du dernier emplacement fictif connu et les qualifie également de simulacre. Il le fait jusqu'à ce qu'un nombre significatif d'emplacements non simulés soit arrivé. L' assistant de localisation peut non seulement rejeter des emplacements fictifs, mais également vous soulager de la plupart des tracas liés à la configuration et à l'abonnement aux mises à jour de localisation.

Pour recevoir uniquement des mises à jour de localisation réelle (c'est-à-dire supprimer les simulations), utilisez-le comme suit:

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()ne sera désormais invoqué qu'avec des informations de localisation réelles. Il y a d'autres méthodes d'écoute que vous devez implémenter, mais dans le contexte de votre question (comment empêcher l'usurpation GPS), c'est essentiellement cela.

Bien sûr, avec un système d'exploitation enraciné, vous pouvez toujours trouver des moyens d'usurper les informations de localisation qui sont impossibles à détecter pour les applications normales.

KlaasNotFound
la source
Vous devez résumer brièvement le billet de blog lié (où échoue isFromMockProvider).
AjahnCharles
@CodeConfident - merci pour la remarque! Je ne sais pas ce que je devrais ajouter cependant. Le deuxième paragraphe de ma réponse est le résumé du billet de blog. .isFromMockProvider échoue de manière sporadique et imprévisible. Dans l'article, je viens de décrire plus en détail les mesures que j'ai prises pour découvrir et y remédier.
KlaasNotFound
Eh bien, j'ai été obligé de sauter à votre article pour comprendre ce qui me semble aller à l'encontre de l'intention de SO. Ma meilleure suggestion serait: (1) insérez votre photo qui montre l'emplacement douteux (non signalé comme une maquette) et (2) notez rapidement votre logique pour les éliminer (ignorez à moins de 1 km d'une maquette)
AjahnCharles
Ok, gotcha. Je pense que dans le contexte du PO, les spécificités de la raison pour laquelle .isFromMockProvider () n'est pas fiable ne sont pas trop pertinentes. Mais je vais essayer d'ajouter les détails que vous avez mentionnés pour une vue d'ensemble. Merci pour les commentaires!
KlaasNotFound
1
Que faire si l'utilisateur n'a pas installé le service Google Play?
Yuriy Chernyshov
6

Si vous connaissez l'emplacement général des tours cellulaires, vous pouvez vérifier si la tour cellulaire actuelle correspond à l'emplacement indiqué (dans une marge d'erreur de quelque chose de grand, comme 10 miles ou plus).

Par exemple, si votre application ne déverrouille des fonctionnalités que si l'utilisateur se trouve dans un emplacement spécifique (votre magasin, par exemple), vous pouvez vérifier le GPS ainsi que les tours de téléphonie cellulaire. Actuellement, aucune application d'usurpation de GPS n'utilise également les tours de téléphonie cellulaire, vous pouvez donc voir si quelqu'un à travers le pays essaie simplement de se frayer un chemin dans vos fonctionnalités spéciales (je pense à l'application Disney Mobile Magic, par exemple).

C'est ainsi que l'application Llama gère l'emplacement par défaut, car la vérification des identifiants des tours cellulaires est beaucoup moins gourmande en batterie que le GPS. Ce n'est pas utile pour des endroits très spécifiques, mais si la maison et le travail sont à plusieurs kilomètres, il peut très facilement faire la distinction entre les deux emplacements généraux.

Bien entendu, cela exigerait que l'utilisateur ait un signal de cellule. Et vous devrez connaître tous les identifiants des tours cellulaires de la région - sur tous les fournisseurs de réseau - ou vous courriez le risque d'un faux négatif.

Stephen Schrauger
la source
1
Merci, c'est une très bonne idée. Je devrai peut-être examiner cela. Merci
Chrispix
3

essayez ce code, c'est très simple et utile

  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;
    }
Mostafa Pirhayati
la source
Il s'agit d'une bien meilleure version de la méthode isMockLocationEnabled ci-dessus.
setzamora
AppOpsManager.checkOp () lève SecurityException Si l'application a été configurée pour se bloquer sur cette opération. Comme: java.lang.SecurityException: le nom du paquet de l'uid 11151 n'est pas autorisé à effectuer MOCK_LOCATION. Cette méthode est sur le point de détecter "si votre application peut simuler des emplacements". Mais pas "si les emplacements reçus sont moqués".
Sergio
@Sergio par "si votre application peut simuler des emplacements", vous voulez dire si l'application actuelle a l'autorisation de simuler des emplacements, n'est-ce pas?
Victor Laerte le
@VictorLaerte J'ai dernier un contexte d'un sujet: / Droite. Mais la question était: "comment détecter si un emplacement reçu est simulé ou provient d'un fournisseur fictif". Ou comment ignorer les faux emplacements.
Sergio le
2

Ce script fonctionne pour toutes les versions d'Android et je le trouve après de nombreuses recherches

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
    }
Ali
la source
1

Vous pouvez ajouter une vérification supplémentaire en fonction de la triangulation de la tour cellulaire ou des informations sur les points d'accès Wifi en utilisant API de géolocalisation de Google Maps

Le moyen le plus simple d'obtenir des informations sur 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();

Vous pouvez comparer vos résultats avec site

Pour obtenir des informations sur les points d'accès Wifi

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
la source