Desativar / verificar localização de simulação (evitar falsificação de GPS)

89

Procurando encontrar a melhor maneira de prevenir / detectar spoofing de GPS no Android. Alguma sugestão sobre como isso é feito e o que pode ser feito para impedir? Eu estou supondo que o usuário tem que ativar locais fictícios para falsificar o GPS. Se isso for feito, eles podem falsificar o GPS?

Eu acho que eu precisaria apenas detectar se Mock Locations estão habilitados? Alguma outra sugestão?

Chrispix
fonte
2
Acho que ele está perguntando sobre a função Location Spoofing disponível na visualização DDMS no Eclipse.
Shawn Walton
2
Eu tenho um jogo baseado em localização que eu não quero que as pessoas trapaceando, então eu procuro bloquear a falsificação, meu entendimento é que pode acontecer de duas maneiras. Ter locais simulados habilitados e construir uma imagem personalizada que faça spoofing de baixo nível e ignora a configuração de falsificação no aplicativo de configurações. Tentando encontrar o Provedor Settings.System para MockLocations, ou procurando ver se ele fica habilitado (com um ouvinte no meio do aplicativo).
Chrispix

Respostas:

124

Eu fiz algumas investigações e compartilhei meus resultados aqui, isso pode ser útil para outros.

Primeiro, podemos verificar se a opção MockSetting está ativada

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

Em segundo lugar, podemos verificar se existem outros aplicativos no dispositivo, que estão usando android.permission.ACCESS_MOCK_LOCATION(aplicativos de spoofing de localização)

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

Se os dois métodos acima, o primeiro e o segundo, forem verdadeiros, há boas chances de que o local seja falsificado ou falso.

Agora, o spoofing pode ser evitado usando a API do Location Manager.

Podemos remover o provedor de teste antes de solicitar as atualizações de localização de ambos os provedores (rede e 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);

Eu vi que removeTestProvider (~) funciona muito bem com Jelly Bean e versões posteriores. Este API parecia não ser confiável até o Ice Cream Sandwich.

Jambaaz
fonte
Observações muito interessantes. Especialmente o último +1 por compartilhar isso.
ar-g
2
Observação sobre o método removeTestProvider. Se você permitir que o Gerenciador de localização funcione em segundo plano, o usuário pode acessar o aplicativo simulado e reiniciar a simulação de localização. Seu gerenciador de localização começará a receber localizações fictícias até que você chame removeTestProvider novamente.
Timur_C
4
Além disso, seu aplicativo deve ter android.permission.ACCESS_MOCK_LOCATIONpermissão para removeTestProviderfuncionar, o que eu acho que é a maior desvantagem.
Timur_C
18
obrigado pela resposta! apenas um ponto: no Android 6.0, ALLOW_MOCK_LOCATION está obsoleto. E, na verdade, não há caixa de seleção para localização fictícia também. Pode-se verificar se a localização é falsa ou
incorreta
2
@Blackkara Afinal, eu não usei. Eu usei uma combinação personalizada de isMockSettingsON(), Location.isFromMockProvider()e areThereMockPermissionApps()com uma lista negra de aplicativos. Existem muitos aplicativos de sistema pré-instalados com ACCESS_MOCK_LOCATION permissão, por exemplo, em dispositivos HTC e Samsung. Uma lista de permissões de todos os aplicativos legítimos seria melhor, mas uma lista negra dos aplicativos de falsificação de localização mais populares funcionou bem no meu caso. E também verifiquei se o dispositivo estava enraizado.
Timur_C
45

Desde a API 18, o objeto Location tem o método .isFromMockProvider () para que você possa filtrar localizações falsas.

Se você quiser oferecer suporte a versões anteriores à 18, é possível usar algo assim:

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
fonte
Tenho quase certeza de que sua segunda expressão está invertida (retorne true quando deveria retornar false). Eu acho que deveria ser:isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
AjahnCharles
3
De nada. Obrigado por postar uma resposta mais moderna! Realmente, esta é a resposta correta até hoje.
AjahnCharles de
1
Como podemos fazer isso sem o objeto "local" para SDK acima de 18?
Ajit Sharma
35

Parece que a única maneira de fazer isso é evitar o spoofing de local que impede MockLocations. O lado negativo é que alguns usuários usam dispositivos GPS Bluetooth para obter um sinal melhor, eles não poderão usar o aplicativo, pois são obrigados a usar os locais fictícios.

Para fazer isso, fiz o seguinte:

// 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
fonte
4
Isso não é à prova de idiotas. Os usuários em um dispositivo sem acesso root ainda podem definir um local fictício, com um horário no futuro, e desativar os locais fictícios, e o local fictício ainda estará ativo. Pior ainda, eles podem chamar o local fictício com o mesmo nome de provedor de Rede / Gps e aparentemente puxa disso ..
Chrispix
3
Além disso, o Fake GPS não requer a configuração de localização simulada em dispositivos com acesso root.
Paul Lammertsma
Pode sempre verificar se o aplicativo fake.gps não está instalado :)
Chrispix
11
Você pode usar em return !xvez de if(x) return false; else return true.
CodesInChaos
Na verdade, a localização falsa mudará a configuração de localização simulada, mesmo em dispositivos com acesso root.
PageNotFound
24

Tropecei neste tópico alguns anos depois. Em 2016, a maioria dos dispositivos Android terá nível de API> = 18 e, portanto, deve contar com Location.isFromMockProvider () como apontado por Fernando .

Eu experimentei extensivamente com locais falsos / simulados em diferentes dispositivos e distros Android. Infelizmente .isFromMockProvider () não é 100% confiável. De vez em quando, um local falso não será rotulado como fictício . Isso parece ser devido a alguma lógica de fusão interna incorreta na API de localização do Google.

Eu escrevi uma postagem detalhada no blog sobre isso , se você quiser saber mais. Para resumir, se você se inscrever para receber atualizações de localização da API Location, ativar um aplicativo GPS falso e imprimir o resultado de cada Location.toString () no console, você verá algo assim:

insira a descrição da imagem aqui

Observe como, no fluxo de atualizações de localização, uma localização tem as mesmas coordenadas que as outras, mas não é sinalizada como uma simulação e tem uma precisão de localização muito pior.

Para resolver esse problema, eu escrevi uma classe de utilitário que vai locais fictícios de forma confiável suprimir em todas as versões do Android modernos (nível API 15 e acima):

LocationAssistant - atualizações de localização sem complicações no Android

Basicamente, ele "desconfia" de locais não fictícios que estão a 1km do último local fictício conhecido e também os rotula como fictícios. Ele faz isso até que um número significativo de locais não simulados chegue. O LocationAssistant pode não apenas rejeitar locais fictícios, mas também livra você da maior parte do incômodo de configurar e assinar atualizações de locais.

Para receber apenas atualizações de localização real (ou seja, suprimir simulações), use-o da seguinte maneira:

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()agora só será invocado com informações de localização real. Existem mais alguns métodos de ouvinte que você precisa implementar, mas no contexto da sua pergunta (como evitar spoofing de GPS), é basicamente isso.

Obviamente, com um sistema operacional enraizado, você ainda pode encontrar maneiras de falsificar informações de localização que são impossíveis de serem detectadas por apps normais.

KlaasNotFound
fonte
Você deve resumir brevemente a postagem do blog vinculada (onde isFromMockProvider falha).
AjahnCharles
@CodeConfident - obrigado pelo comentário! Não tenho certeza do que devo acrescentar. O segundo parágrafo da minha resposta é o resumo da postagem do blog. .isFromMockProvider falha esporadicamente e de forma imprevisível. No artigo, acabo de descrever com mais detalhes as etapas que tomei para descobrir e remediar isso.
KlaasNotFound
Bem, fui forçado a pular para o seu artigo para entender o que para mim parece ir contra a intenção de SO. Minha melhor sugestão seria: (1) insira sua foto que mostra o local duvidoso (não sinalizado como uma simulação) e (2) observe rapidamente sua lógica para eliminá-los (ignorar dentro de 1km de uma simulação)
AjahnCharles
Ok, entendi. Acho que no contexto do OP, os detalhes do motivo pelo qual .isFromMockProvider () não é confiável não são muito relevantes. Mas tentarei adicionar os detalhes que você mencionou para o quadro maior. Obrigado pelo feedback!
KlaasNotFound de
1
E se o usuário não tiver o Google Play Service instalado?
Yuriy Chernyshov
6

Se você souber a localização geral das torres de celular, poderá verificar se a torre de celular atual corresponde ao local fornecido (dentro de uma margem de erro de algo grande, como 10 milhas ou mais).

Por exemplo, se seu aplicativo desbloquear recursos apenas se o usuário estiver em um local específico (sua loja, por exemplo), você pode verificar GPS, bem como torres de celular. Atualmente, nenhum aplicativo de falsificação de GPS também falsifica as torres de celular, então você pode ver se alguém em todo o país está simplesmente tentando falsificar seus recursos especiais (estou pensando no aplicativo Disney Mobile Magic, por exemplo).

É assim que o aplicativo Llama gerencia a localização por padrão, uma vez que verificar as ids das torres de celular consome muito menos bateria do que o GPS. Não é útil para locais muito específicos, mas se casa e trabalho estiverem a vários quilômetros de distância, ele pode distinguir entre os dois locais gerais com muita facilidade.

Claro, isso exigiria que o usuário tivesse um sinal de celular. E você teria que saber todas as ids de torres de celular na área --em todos os provedores de rede - ou você correria o risco de um falso negativo.

Stephen Schrauger
fonte
1
Obrigado, é uma ideia muito boa. Eu posso ter que olhar para isso. Obrigado
Chrispix
3

tente este código é muito simples e útil

  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
fonte
Esta é uma versão muito melhor do método isMockLocationEnabled acima.
setzamora
AppOpsManager.checkOp () lança SecurityException Se o aplicativo foi configurado para travar neste op. Como: java.lang.SecurityException: packagename do uid 11151 não tem permissão para executar MOCK_LOCATION. Este método está prestes a detectar "se seu aplicativo pode simular locais". Mas não "se os locais recebidos forem ridicularizados".
Sergio
@Sergio por "se seu aplicativo pode simular locais", você quer dizer se o aplicativo atual tem permissão para simular locais, certo?
Victor Laerte
@VictorLaerte Durei um contexto de um tópico: / Certo. Mas a questão era: "como detectar se um local recebido é simulado ou de um provedor simulado". Ou como ignorar locais falsos.
Sergio
2

Este script está funcionando para todas as versões do Android e eu o encontrei após muitas pesquisas

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
    }
Todos
fonte
1

Você pode adicionar verificação adicional com base na triangulação da torre de celular ou nas informações dos pontos de acesso de Wi-Fi usando a Google Maps Geolocation API

A maneira mais simples de obter informações sobre 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();

Você pode comparar seus resultados com o site

Para obter informações sobre pontos de acesso 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
fonte