Напомню, что dex файл содержит скомпилированные классы андроид приложения. В ранних версиях андроида, код в dex исполнялся на Dalvik. В современных версиях, вместо этого используется ART (https://source.android.com/devices/tech/dalvik). Эта рантайм система более совершенна, в отличии от JIT, ART добавляет фичу AOT.
AOT (Ahead-Of-Time) - фича, которая прекомпилирует куски кода приложения, для дальнейшего исполнения. Делает это она, с помощью утилиты dex2oat. Эта утилита запускается, когда вы открываете приложение на телефоне. В этот момент, она прекомпилирует dex файл в oat файл. Данная утилита является простым бинарником и лежит в /system/xbin.
Как это относится к теме модификации андроид приложений? Дело в том, что когда вы редактируете dex файлы, именно dex2oat производит первичную верификацию - берет из хэдера значения хэша, чексумы, длины, оффсеты и т.д. Чтобы быстрее проверить модифицированное приложение, раньше я полностью собирал apk, закидывал в эмулятор и смотрел logcat и изучал ошибки, связанные с верификацией dex. Теперь же, можно не париться со сборкой, подписанием apk, а напрямую использовать бинарник dex2oat, для проверки корректности изменений.
Можно пойти еще дальше и открыть исходники это проверки (https://android.googlesource.com/platform/art/+/master/libdexfile/dex/dex_file_verifier.cc). Это сложнее, но надеюсь в ближайшем будущем взять данный исходник, переписать его на более удобный язык и создать тем самым первичный и очень небольшой верификатор (в сети не нашел). Такой верификатор dex файлов пригодился бы при фазинге важнейших систем андроида.
Я это к чему, нашел вот такой классный доклад с Black Hat Europe (https://www.blackhat.com/docs/eu-15...es-Inside-System-Components-In-Android-wp.pdf), прочитав который я понял, что такой мини верификатор очень бы пригодился, так как они фазили и смотрели логи на самом андроиде.
Продолжение темы реверса dex файлов
Сразу к делу. Мы создадим приложение с тремя активити, назовем их MainActivity, FirstActivity и SecondActivity. MainActivity будет стартовым, наследоваться от FirstActivity и тупо его запускать. SecondActivity просто находится в приложении и никак не вызывается. Цель - пропатчить такое готовое приложение так, чтобы запускалось SecondActivity. Для начала мы создаем проект с EmptyActivity
Назовем его ,что-то вроде TestingDEX, так как эт освязанно с DEX
Мы просто берем и создаем вышеупомянутые три активити класса
Как видите, мы тупо наследуемся от FirstActivity и вызыаем OnCreate(). Теперь собираем нашу apk, делаем unzip, в итоговой папке у нас лежит classes.dex со скомпилированными классами, открываем его в hex редакторе.
Обратите внимание на его размер, он получился просто огромным, его тяжело анализировать и для этого мы применяем знания из статьи об уменьшении размера апк, чтобы наш DEX файл был чистеньким и готовым к реверсу. Первым делом включаем ProGurd и выкидываем ненужные ресурсы из apk
Прописываем такое и наш dex становиться меньше!
Еще у нашего приложения, из-за совместимости есть лишние библиотеки, выпилиываем их
В итоге вот так
Соответственно мы теперь наследуемся не от AppCompatActivity, а от просто Activity
Layout тоже чистим:
Также удалям XML файлы
Теперь, чтобы наш активити хоть както функционировал, пишем такой код
Правим манифест, было
Стало
Собираем приложени, посмотрите какой чистенький DEX мы получили. Маленький и понятный
Изначально, я всю эту дичь анализировал в простом HEX редакторе. Одним глазом смотрел в [https://source.android.com/devices/tech/dalvik/dex-format#method-id-item](Dalvik Executable format doc), другим в сырой hex редактор. Потом я вспомнил, что существует ИДА! Напомню, что нам необходимо перенаправить открытие FirstActivity на SecondActivity. Как вы поняли, мы собрали приложение ,которое наследуется и открывает FirstActivity. Теперь, просто меняем наследование от SecondActivity и собираем заново. Выглядить MainActivity будет так
Теперь у нас есть два приложения, которые унаследованы от разных классов. Теперь мы распаковываем их и делаем diff dex файлов, чтобы понять, что поменялось в бинарной структуре. Diff делал через WinHex, он выдает байты, которые поменялись, вот они
Байты с 8-31 это чексума и хэш dex файла, на нее не обращаем внимания, так как она и так всегда меняется. Нас интересуют последнии три строки. Почему байт 06 поменялся на 08, по оффсету 452=1С4? Дело в том, что на этом оффсете, в секции CLASSDEF лежит инфа о классах в DEX и так как у нас поменялся parent с FirstActivity на SecondActivity, мы видим изменение поля superclass
Смотрим следующий диф по офсету. У нас изменился вызов init у родителя ,что неудивительно
Следующий дифф, напомню, изменился 570-ый байт (10).
Тут видно, что поменялся вызов OnCreate(), так как мы теперь вызываем этот метод не из FirstActivity, а из SecondActivity. Итого, чтобы вызывать другой суперкласс из того-же DEX файла, нам надо поменять лишь 3 байта и понимать к чему они относятся. А что если класс находится в другом DEX'е? Ответ на этот вопрос будет в следующей части
Продолжение темы реверса dex файлов. Следующая часть.
Как и раньше, мы будем искать самый эффективный способ заражения легитимных андроид приложений. В данной части, мы создадим свой dex файл, подложим его в целевую апк и сделаем так, чтобы приложение работало дальше, как и было задумано. Рассмотрим на примере Color by Number!
Мы будем подкладывать свой DEX файл, который будет запускать наш пейлоад и потом запускать изначальный launcher активити приложения. Манифест выглядит так:
Было:
Стало:
Как видите, изначально у приложения launcher активити был "com.slimfitgames.drawcolor.AndroidLauncher" и стал "com.slimfitgames.drawcolor.MainActivity" - нашим внедренным классом.
После изменения манифеста, мы собираем наше приложение в нормальное apk.
Теперь открываем Android Studio и пишем наш payload, пользуясь техниками из второй части (урезаем до минимума).
Создаем свое приложение, с точно таким же package name, как и у целевого. Создаем активити, с точно таким же именем, как и у целевого, но с измененной последнй буквой. Именно эту букву мы будем патчить в DEX файле. То есть, в оригинале - AndroidLauncher, а мы назовем AndroidLaunches. В MainActivity, выполняем наш пейлоад (Log.d()), а далее, с помощью startActivity(), запускаем AndroidLaunches.
Теперь делаем Build project, нам необходимо, чтобы Android studio создала .class файлы для нас. После этого, в папке ХХХХХХХХХХХХХХХХ\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes\com\slimfitgames\drawcolor будут наши .class файлы. Нам это необходимо, чтобы отдельно скомпилировать класс в один dex файл.
min-api обязательно должен быть такой же, как и у целевого приложения. Посмотреть его можно в apktool.yml, после декодирования.
Полученый DEX файл мы просто подкладываем в целевую apk.
АВТОР @OrderOfSixAngles
https://github.com/thatskriptkid
Орфография и пунктуация автора сохранены
AOT (Ahead-Of-Time) - фича, которая прекомпилирует куски кода приложения, для дальнейшего исполнения. Делает это она, с помощью утилиты dex2oat. Эта утилита запускается, когда вы открываете приложение на телефоне. В этот момент, она прекомпилирует dex файл в oat файл. Данная утилита является простым бинарником и лежит в /system/xbin.
Как это относится к теме модификации андроид приложений? Дело в том, что когда вы редактируете dex файлы, именно dex2oat производит первичную верификацию - берет из хэдера значения хэша, чексумы, длины, оффсеты и т.д. Чтобы быстрее проверить модифицированное приложение, раньше я полностью собирал apk, закидывал в эмулятор и смотрел logcat и изучал ошибки, связанные с верификацией dex. Теперь же, можно не париться со сборкой, подписанием apk, а напрямую использовать бинарник dex2oat, для проверки корректности изменений.
Можно пойти еще дальше и открыть исходники это проверки (https://android.googlesource.com/platform/art/+/master/libdexfile/dex/dex_file_verifier.cc). Это сложнее, но надеюсь в ближайшем будущем взять данный исходник, переписать его на более удобный язык и создать тем самым первичный и очень небольшой верификатор (в сети не нашел). Такой верификатор dex файлов пригодился бы при фазинге важнейших систем андроида.
Я это к чему, нашел вот такой классный доклад с Black Hat Europe (https://www.blackhat.com/docs/eu-15...es-Inside-System-Components-In-Android-wp.pdf), прочитав который я понял, что такой мини верификатор очень бы пригодился, так как они фазили и смотрели логи на самом андроиде.
Продолжение темы реверса dex файлов
Сразу к делу. Мы создадим приложение с тремя активити, назовем их MainActivity, FirstActivity и SecondActivity. MainActivity будет стартовым, наследоваться от FirstActivity и тупо его запускать. SecondActivity просто находится в приложении и никак не вызывается. Цель - пропатчить такое готовое приложение так, чтобы запускалось SecondActivity. Для начала мы создаем проект с EmptyActivity
Назовем его ,что-то вроде TestingDEX, так как эт освязанно с DEX
Мы просто берем и создаем вышеупомянутые три активити класса
Как видите, мы тупо наследуемся от FirstActivity и вызыаем OnCreate(). Теперь собираем нашу apk, делаем unzip, в итоговой папке у нас лежит classes.dex со скомпилированными классами, открываем его в hex редакторе.
Обратите внимание на его размер, он получился просто огромным, его тяжело анализировать и для этого мы применяем знания из статьи об уменьшении размера апк, чтобы наш DEX файл был чистеньким и готовым к реверсу. Первым делом включаем ProGurd и выкидываем ненужные ресурсы из apk
Код:
android {
`buildTypes {`
`release {`
`minifyEnabled true`
`shrinkResources true`
`proguardFiles getDefaultProguardFile(`
`'proguard-android.txt'), 'proguard-rules.pro'`
`}`
`}`
}
Прописываем такое и наш dex становиться меньше!
Еще у нашего приложения, из-за совместимости есть лишние библиотеки, выпилиываем их
В итоге вот так
Соответственно мы теперь наследуемся не от AppCompatActivity, а от просто Activity
Layout тоже чистим:
Также удалям XML файлы
Теперь, чтобы наш активити хоть както функционировал, пишем такой код
Код:
TextView textView = new TextView(this); textView.setText("Hello World!"); setContentView(textView);
Правим манифест, было
Стало
Собираем приложени, посмотрите какой чистенький DEX мы получили. Маленький и понятный
Изначально, я всю эту дичь анализировал в простом HEX редакторе. Одним глазом смотрел в [https://source.android.com/devices/tech/dalvik/dex-format#method-id-item](Dalvik Executable format doc), другим в сырой hex редактор. Потом я вспомнил, что существует ИДА! Напомню, что нам необходимо перенаправить открытие FirstActivity на SecondActivity. Как вы поняли, мы собрали приложение ,которое наследуется и открывает FirstActivity. Теперь, просто меняем наследование от SecondActivity и собираем заново. Выглядить MainActivity будет так
Теперь у нас есть два приложения, которые унаследованы от разных классов. Теперь мы распаковываем их и делаем diff dex файлов, чтобы понять, что поменялось в бинарной структуре. Diff делал через WinHex, он выдает байты, которые поменялись, вот они
Байты с 8-31 это чексума и хэш dex файла, на нее не обращаем внимания, так как она и так всегда меняется. Нас интересуют последнии три строки. Почему байт 06 поменялся на 08, по оффсету 452=1С4? Дело в том, что на этом оффсете, в секции CLASSDEF лежит инфа о классах в DEX и так как у нас поменялся parent с FirstActivity на SecondActivity, мы видим изменение поля superclass
Смотрим следующий диф по офсету. У нас изменился вызов init у родителя ,что неудивительно
Следующий дифф, напомню, изменился 570-ый байт (10).
Тут видно, что поменялся вызов OnCreate(), так как мы теперь вызываем этот метод не из FirstActivity, а из SecondActivity. Итого, чтобы вызывать другой суперкласс из того-же DEX файла, нам надо поменять лишь 3 байта и понимать к чему они относятся. А что если класс находится в другом DEX'е? Ответ на этот вопрос будет в следующей части
Продолжение темы реверса dex файлов. Следующая часть.
Как и раньше, мы будем искать самый эффективный способ заражения легитимных андроид приложений. В данной части, мы создадим свой dex файл, подложим его в целевую апк и сделаем так, чтобы приложение работало дальше, как и было задумано. Рассмотрим на примере Color by Number!
Мы будем подкладывать свой DEX файл, который будет запускать наш пейлоад и потом запускать изначальный launcher активити приложения. Манифест выглядит так:
Было:
Код:
<activity android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize" android:label="@string/app_name" android:name="com.slimfitgames.drawcolor.AndroidLauncher" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
Код:
<activity android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize" android:label="@string/app_name" android:name="com.slimfitgames.drawcolor.MainActivity" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize" android:label="@string/app_name" android:name="com.slimfitgames.drawcolor.AndroidLauncher" android:screenOrientation="portrait" />
После изменения манифеста, мы собираем наше приложение в нормальное apk.
Теперь открываем Android Studio и пишем наш payload, пользуясь техниками из второй части (урезаем до минимума).
Создаем свое приложение, с точно таким же package name, как и у целевого. Создаем активити, с точно таким же именем, как и у целевого, но с измененной последнй буквой. Именно эту букву мы будем патчить в DEX файле. То есть, в оригинале - AndroidLauncher, а мы назовем AndroidLaunches. В MainActivity, выполняем наш пейлоад (Log.d()), а далее, с помощью startActivity(), запускаем AndroidLaunches.
Теперь делаем Build project, нам необходимо, чтобы Android studio создала .class файлы для нас. После этого, в папке ХХХХХХХХХХХХХХХХ\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes\com\slimfitgames\drawcolor будут наши .class файлы. Нам это необходимо, чтобы отдельно скомпилировать класс в один dex файл.
Код:
d8 --release --min-api 16 --no-desugaring MainActivity.class --output .
min-api обязательно должен быть такой же, как и у целевого приложения. Посмотреть его можно в apktool.yml, после декодирования.
Полученый DEX файл мы просто подкладываем в целевую apk.
АВТОР @OrderOfSixAngles
https://github.com/thatskriptkid
Орфография и пунктуация автора сохранены