Как подменить геолокацию Android

Как подменить геолокацию Android телефона.

Гелокация в телефоне отличный инструмент. Очень часто он помогает нам найти нужное место или просто понять где ты находишся. Но кроме явного преимущество, геолокационные сервисы выдают различным приложениям ваши координаты. Сегодня мы рассмотрим как подменить геолокацию android телефона.

Как подменить геолокацию Android

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

С точки зрения юзера все очень просто: нужно лишь установить специальное приложение, затем включить в настройках режим разработчика и выбрать установленное приложение в качестве поставщика фиктивного местоположения. Таких программ великое множество — от простеньких до довольно развесистых, умеющих не только подменять координаты на заданные, но и менять их по расписанию или проигрывать заранее записанные треки, чтобы имитировать движение телефона по какому-то маршруту. В общем, вбивай запрос «Fake GPS» и выбирай по вкусу.

 

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

Я же захотел разобраться, как именно работает этот механизм, и создать собственное приложение для спуфинга. А начал я с того, что посмотрел, как этот алгоритм реализован в одном из бесплатных приложений. Не читать же документацию, верно?

 

Реверсим FakeGPS

В качестве подопытного кролика было взято приложение FakeGPS 5.0.0. Внешне приложение представляет собой карту, на которой можно установить маркер в произвольную точку и с помощью кнопок «Старт» и «Стоп» запускать или останавливать трансляцию координат выбранной точки.

Вооружившись JEB Decompiler, открываем и смотрим. Первое, что бросается в глаза, — это наличие в манифесте пермишена android.permission.ACCESS_MOCK_LOCATION.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="com.android.vending.BILLING" />

В основной активити ничего интересного не обнаружено, обычная инициализация и настройка, но есть сервис с говорящим названием FakeGPSService.

Попытаемся прорваться сквозь дебри обфускации и посмотреть, что в нем есть интересного.

В методе onCreate имеется такой код:

this.f = "gps";
this.d = (LocationManager)this.getSystemService("location");
try {
  if(this.d == null) {
    goto label_46;
  }
  this.d.removeTestProvider(this.f);
  goto label_46;
} catch(IllegalArgumentException | NullPointerException unused_ex) {
  goto label_46;
}

label_46:
if(this.d != null) {
  this.d.addTestProvider(this.f, false, false, false, false, true, false, false, 0, 5);
  this.d.setTestProviderEnabled(this.f, true);
}

Если проще, то инициализируем LocationManager значением this.getSystemService("location"), затем удаляем тестового провайдера "gps" функцией removeTestProvider и добавляем заново с помощью функции addTestProvider, не забывая после этого включить его функцией setTestProviderEnabled("gps", true). Всё, тестовый провайдер добавлен и включен. А далее при изменении пользователем координат создаем и устанавливаем новое местоположение в функции onEventMainThread:

// Создаем
long v1 = System.currentTimeMillis();
Location v3 = new Location("");
v3.setProvider("gps");
v3.setLatitude(arg10.latitude);
v3.setLongitude(arg10.longitude);
v3.setAltitude(((double)FakeGPSService.p));
v3.setBearing(((float)FakeGPSService.q));
v3.setTime(v1);
v3.setAccuracy(((float)FakeGPSService.o));
v3.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());

// И устанавливаем
try {
  this.d.setTestProviderLocation(this.f, v3);
  Log.d("GpsMockProvider", v3.toString());
} catch(IllegalArgumentException unused_ex) {
}

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

 

Пишем код

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

Итак, запускаем Android Studio и создаем проект с пустой активити.

Добавляем в манифест android.permission.ACCESS_MOCK_LOCATION, после чего «Студия» начинает ругаться, что это разрешение доступно только системным приложениям, да еще может быть добавлено только в тестовый манифест. Тут можно не заморачиваться, а просто понажимать кнопки Alt+Shift+Enter и Alt-Enter, следуя подсказкам, и «Студия» сама все сделает за нас. Затем добавляем на стартовую активити две кнопки.

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="vertical">
  <Button
    android:id="@+id/btnDelGPS"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="DelGPS"
    android:text="Удалить провайдер GPS" />
  <Button
    android:id="@+id/btnAddGPS"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="AddGPS"
    android:text="Добавить провайдер GPS" />     
</LinearLayout>

И добавляем соответствующий код.

public class MainActivity extends Activity {
  LocationManager mLocationManager;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // Инициализируем LocationManager
    mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
  }
  public void AddGPS(View view) {
    // Добавляем тестовый провайдер
    mLocationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, false,
      false, false, true, true,
      true, android.location.Criteria.POWER_LOW, android.location.Criteria.ACCURACY_FINE);

    // Включаем тестовый провайдер
    mLocationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);

    // Задаем фиктивную точку
    Location newLocation = new Location(LocationManager.GPS_PROVIDER);
    newLocation.setLatitude(55.75578);
    newLocation.setLongitude(37.61786);
    newLocation.setTime(System.currentTimeMillis());
    newLocation.setAccuracy(25);
    newLocation.setElapsedRealtimeNanos(System.nanoTime());
    mLocationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, newLocation);
  }
  public void DelGPS(View view) {
    // Удаляем наш тестовый провайдер
    mLocationManager.removeTestProvider(LocationManager.GPS_PROVIDER);
  }
}

Компилируем и устанавливаем. Затем идем в настройки разработчика на телефоне, выбираем наше приложение в качестве поставщика фиктивных местоположений, трясущимися от волнения (как же, мы же только что написали собственный поставщик местоположений!) руками запускаем наше приложение и жмем кнопку «Добавить провайдер GPS».

 
 

Ничего не происходит. Ничего, собственно, и не должно происходить.

Если приложение запускается не первый раз, то возможно падение из-за того, что тестовый провайдер с таким именем уже создан. Тогда надо повторно запустить приложение и удалить провайдер кнопкой «Удалить провайдер GPS», а затем создать его заново кнопкой «Добавить провайдер GPS».

Я намеренно не стал добавлять обработку таких ошибок, чтобы не засорять код, мы же пишем макет, а не финальную версию. Впрочем, можешь присылать пулл-реквесты, ссылка на GitHub будет в конце статьи 🙂

 

Как подменить геолокацию Android — тесты

Сворачиваем приложение и приступаем к тестам. Запускаем «Яндекс Карты» и попадаем точно на Красную площадь, в нулевой километр, как и было задумано. Ура, получилось!

 

Ну, почти получилось. Если попробовать запустить Google Maps, почему-то мы попадаем на площадь Комсомола в городе Урюпинске. Вернее, понятно, почему попадаем, непонятно, почему не работает наш TestProvider.

Скажу честно, ответ на этот вопрос я искал несколько дней, но все оказалось довольно просто — надо отключить в настройках телефона геолокацию Google. Эта настройка позволяет не заморачиваться с выбором провайдера: телефон сам решает, из какого источника брать координаты. Начинает с самых точных, то есть GPS, затем, если спутниковое позиционирование недоступно, переходит к базовым станциям, затем по сетям Wi-Fi, а затем якобы даже использует акселерометр, чтобы найти себя в пространстве.

Итак, пробуем — отключаем геолокацию Google и запускаем «Карты».

 

Сработало, мы снова на Красной площади.

Итак, этот способ позволяет подменять реальные координаты GPS фиктивными, но хотелось бы решить задачу полностью — подменять местоположение начисто, без всяких скидок. Что интересно, наш подопытный кролик FakeGPS работает корректно независимо от настроек геолокации Google. Что ж, будем ковырять дальше.

 

Исправляем ошибки

Присмотревшись к сервису FakeGPSService чуть более внимательно, я заметил, что там еще используется некий GoogleApiClient. Признаюсь, при первичном анализе я с ходу решил, что он нужен для рекламы, и не стал больше обращать на него внимание. А еще там есть вот эти два метода:

LocationServices.FusedLocationApi.setMockMode()
LocationServices.FusedLocationApi.setMockLocation()

Кажется, это то, что нужно. Погуглив документацию (от нее все же не уйти!), выясняем, что FusedLocationApi немножко устарел и вместо него рекомендуется использовать FusedLocationProviderClient.

Что же, попробуем. Добавляем в раздел dependencies файла build.gradle такую строчку:

implementation 'com.google.android.gms:play-services-location:17.0.0'

Интересно, что после добавления этой строчки объем приложения вырастает с 11 Кбайт до 1 Мбайта с хвостиком.

В конец функции AddGPS дописываем пару строк.

LocationServices.getFusedLocationProviderClient(this).setMockMode(true);
LocationServices.getFusedLocationProviderClient(this).setMockLocation(newLocation);

Компилируем и запускаем — теперь нормально работает и в Google Maps с включенной геолокацией, и в «Яндекс Картах». Победа!

Click to rate this post!
[Total: 3 Average: 5]

Специалист в области кибер-безопасности. Работал в ведущих компаниях занимающихся защитой и аналитикой компьютерных угроз. Цель данного блога - простым языком рассказать о сложных моментах защиты IT инфраструктур и сетей.

Leave a reply:

Your email address will not be published.