Следующая новость
Предыдущая новость

За семью замками. Защищаем приложение для Android от отладчиков, эмуляторов и Frida

09.12.2020 14:02

Содержание статьи

  • Root
  • Magisk
  • Эмулятор
  • Отладчик
  • Xposed
  • Frida
  • Клонирование
  • Выводы

Ког­да задумы­ваешь­ся о защите при­ложе­ния от ревер­са, в пер­вую оче­редь на ум при­ходят такие сло­ва, как обфуска­ция и шиф­рование. Но это толь­ко часть решения проб­лемы. Вто­рая полови­на — это детект и защита от самих инс­тру­мен­тов ревер­са: отладчи­ков, эму­лято­ров, Frida и так далее. В этой статье мы рас­смот­рим тех­ники, которые мобиль­ный софт и злов­реды исполь­зуют, что­бы спря­тать­ся от этих инс­тру­мен­тов.

warning

Не сто­ит вос­при­нимать при­веден­ную в статье информа­цию как рецепт абсо­лют­ной защиты. Такого рецеп­та нет. Мы все­го лишь даем себе отсроч­ку, затор­мажива­ем иссле­дова­ние, но не дела­ем его невоз­можным. Все это — бес­конеч­ная игра в кош­ки‑мыш­ки, ког­да иссле­дова­тель взла­мыва­ет оче­ред­ную защиту, а раз­работ­чик при­думы­вает ей более изощ­ренную замену.

Важ­ный момент: я при­веду мно­жес­тво раз­ных тех­ник защиты, и у тебя может воз­никнуть соб­лазн запих­нуть их все в один класс (или натив­ную биб­лиоте­ку) и с удобс­твом для себя запус­кать один раз при стар­те при­ложе­ния. Так делать не сто­ит, механиз­мы защиты дол­жны быть раз­бро­саны по при­ложе­нию и стар­товать в раз­ное вре­мя. Так ты сущес­твен­но усложнишь жизнь взлом­щику, который в про­тив­ном слу­чае мог бы опре­делить наз­начение клас­са/биб­лиоте­ки и целиком заменить его на одну боль­шую заг­лушку.

Root

Пра­ва root — один из глав­ных инс­тру­мен­тов ревер­сера. Root поз­воля­ет запус­кать Frida без пат­чинга при­ложе­ний, исполь­зовать модули Xposed для изме­нения поведе­ния при­ложе­ния и трей­син­га при­ложе­ний, менять низ­коуров­невые парамет­ры сис­темы. В целом наличие root чет­ко говорит о том, что окру­жению исполне­ния доверять нель­зя. Но как его обна­ружить?

Са­мый прос­той вари­ант — поис­кать исполня­емый файл su в одном из сис­темных катало­гов:

  • /sbin/su
  • /system/bin/su
  • /system/bin/failsafe/su
  • /system/xbin/su
  • /system/sd/xbin/su
  • /data/local/su
  • /data/local/xbin/su
  • /data/local/bin/su

Би­нар­ник su всег­да при­сутс­тву­ет на рутован­ном устрой­стве, ведь имен­но с его помощью при­ложе­ния получа­ют пра­ва root. Най­ти его мож­но с помощью при­митив­ного кода на Java:

private static boolean findSu() { String[] paths = { "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su" }; for (String path : paths) { if (new File(path).exists()) return true; } return false;}

Ли­бо исполь­зовать такую фун­кцию, поза­имс­тво­ван­ную из при­ложе­ния rootinspector:

jboolean Java_com_example_statfile(JNIEnv * env, jobject this, jstring filepath) { jboolean fileExists = 0; jboolean isCopy; const char * path = (*env)->GetStringUTFChars(env, filepath, &isCopy); struct stat fileattrib; if (stat(path, &fileattrib) < 0) { __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NATIVE: stat error: [%s]", strerror(errno)); } else { __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NATIVE: stat success, access perms: [%d]", fileattrib.st_mode); return 1; } return 0;}

Еще один вари­ант — поп­робовать не прос­то най­ти, а запус­тить бинар­ник su:

try { Runtime.getRuntime().exec("su");} catch (IOException e) { // Телефон не рутован}

Ес­ли его нет, сис­тема выдаст IOException. Но здесь нуж­но быть осто­рож­ным: если устрой­ство все‑таки име­ет пра­ва root, поль­зователь уви­дит на экра­не зап­рос этих самых прав.

Еще один вари­ант — най­ти сре­ди уста­нов­ленных на устрой­ство при­ложе­ний менед­жер прав root. Он как раз и отве­чает за диалог пре­дос­тавле­ния прав:

  • com.thirdparty.superuser
  • eu.chainfire.supersu
  • com.noshufou.android.su
  • com.koushikdutta.superuser
  • com.zachspong.temprootremovejb
  • com.ramdroid.appquarantine
  • com.topjohnwu.magisk

Для поис­ка мож­но исполь­зовать такой метод:

private static boolean isPackageInstalled(String packagename, Context context) { PackageManager pm = context.getPackageManager(); try { pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES); return true; } catch (NameNotFoundException e) { return false; }}

Ис­кать мож­но и по кос­венным приз­накам. Нап­ример, SuperSU, неког­да популяр­ное решение для получе­ния прав root, име­ет нес­коль­ко фай­лов в фай­ловой сис­теме:

  • /system/etc/init.d/99SuperSUDaemon
  • /system/xbin/daemonsu — SuperSU

Еще один кос­венный приз­нак — про­шив­ка, под­писан­ная тес­товыми клю­чами. Это не всег­да под­твержда­ет наличие root, но точ­но говорит о том, что на устрой­стве уста­нов­лен кас­том:

private boolean isTestKeyBuild() { String buildTags = android.os.Build.TAGS; return buildTags != null && buildTags.contains("test-keys");}

Magisk

Все эти методы детек­та root отлично работа­ют до тех пор, пока ты не стол­кнешь­ся с устрой­ством, рутован­ным с помощью Magisk. Это так называ­емый systemless-метод рутин­га, ког­да вмес­то раз­мещения ком­понен­тов для root-дос­тупа в фай­ловой сис­теме поверх нее под­клю­чают дру­гую фай­ловую сис­тему (овер­лей), содер­жащую эти ком­понен­ты.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

1 год

7690 р.

1 месяц

720 р.

Я уже участник «Xakep.ru»

Источник

Последние новости