Шпаргалка по Smali на русском
Приветствую, любители реверс-инжинирить Android. Перед вами шпаргалка по Smali — аналогу ассемблера для Android-приложений.
Изначальный текст на русском взят отсюда. Там текст появился из машинного перевода официальной документации.
В итоге, оформил сухой текст + поправил небольшие опечатки и корявости перевода. По поводу замечаний и предложений можете писать либо мне в ЛС, либо оформлять PR на Gist.
Общая информация
Виды(Types)
Байт-код Dalvik имеет два основных класса типов: примитивные типы и ссылочные типы. Типы ссылок — это объекты и массивы, все остальное является примитивным.
Примитивы представлены одной буквой:
Объекты принимают форму Lpackage/name/ObjectName; — где ведущий L указывает, что это тип объекта, package/name/ — это пакет, в котором находится объект, ObjectName — это имя объекта и ; обозначает конец имени объекта.
Это эквивалентно имени package.name.ObjectName в java. Или, для более конкретного примера, Ljava/lang/String; эквивалентно java.lang.String
Вы также можете иметь массивы объектов, [Ljava/lang/String; будет массив строк.
Методы(Methods)
Методы всегда указываются в очень подробной форме, которая включает тип, содержащий метод, имя метода, типы параметров и тип возвращаемого значения. Вся эта информация необходима, чтобы виртуальная машина могла находить правильный метод и иметь возможность выполнять статический анализ на байт-коде
Они принимают форму Lpackage/name/ObjectName;->MethodName(III)Z
В этом примере вы должны распознать Lpackage/name/ObjectName; как тип. MethodName — это имя метода. (III)Z является сигнатурой метода. III — параметры (в данном случае 3 целых числа), а Z — тип возврата (bool).
Параметры метода перечислены один за другим, без разделителей между ними.
Вот более сложный пример:
В Java, это было бы
Поля(Fields)
Поля также всегда указывается в многословной форме, которая включает тип, содержащее поле, имя поля и тип поля. Опять же, это позволяет виртуальной машине находить правильное поле, а также выполнять статический анализ на байт-коде.
Они принимают форму Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;
Это должно быть довольно очевидно — это пакет и имя объекта, имя поля и тип поля соответственно
Регистры (Register)
Вступление
В байт-коделе dalvik регистры всегда 32 битные и могут содержать любой тип значения. 2 регистра используются для хранения 64-битных типов (длинного — Long и числа с плавающей точкой — Double).
Указание количества регистров в методе
Как параметры метода передаются в метод:
Первым параметром для нестатических методов ( non-static methods ) всегда является объект, на который вызывается метод ( this объект)
Для статических методов ( static methods ) это одно и то же, за исключением того, что неявный этот аргумент.
Регистрация имен (Register names)
Существует две схемы именования для регистров — обычная схема именования v# и схема именования p# для регистров параметров. Первый регистр в схеме именования p# является первым регистром параметров в методе.
Итак, вернемся к предыдущему примеру метода с 3-мя аргументами и 5-ю полными регистрами. В следующей таблице показано обычное имя v# для каждого регистра, за которым следует имя p# для регистров параметров ( parameter registers )
| v# | p# | |
|---|---|---|
| v0 | Первый локальный регистр | |
| v1 | Второй локальный регистр | |
| v2 | p0 | Первый регистр параметров |
| v3 | p1 | Второй регистр параметров |
| v4 | p2 | Третий регистр параметров |
Вы можете ссылаться на регистры параметров по имени — это не имеет никакого значения.
Введения регистров параметров ( parameter registers )
Но если схема именования p# использовалась для ссылки на регистры параметров во всем методе, вы можете легко изменить количество регистров в методе, не беспокоясь о перонумеровании любых существующих регистров.
Long/Double значения
Кроме того, когда вы вызываете метод позже, вам нужно указать оба регистра для любых аргументов с двойным расширением в списке регистров для инструкции типа invoke.
Array (массивы)
array-length vA, vB
Сохраняет длину (количество записей) указанного массива vB в vA
fill-array-data vA+, :target
Пример таблицы данных:
new-array vA+, vB, Lclass;->type
Создает новый массив указанного типа и размера. Тип должен быть типом массива.
filled-new-array < vA [ vB, v. vX ]>, Lclass;->type
Array Accessors (приемники массивов)
Сохраняет целое ( integer ) значение от vA в массиве, на который ссылается vB на индекс vC
Например: aget-objec (Получает объект( object ))
Сравнения
cmp-long vA, vB+, vC+
Сравнивает длинные (long) значения в исходных регистрах, сохраняя 0
cmpg-double vA, vB+, vC+
Сравнивает числа с плавающей точкой (double) значения в исходных регистрах, сохраняя 0
cmpg-float vA, vB, vC
Сравнивает значения с плавающей(float) запятой в исходных регистрах, сохраняя 0;
cmpl-double vA, vB+, vC+
Сравнивает double значения в исходных регистрах, сохраняя 0;
cmpl-float vA, vB, vC — выполняет указанное сравнение с плавающей(float) запятой, сохраняя 0;
Константы
const vAA, #+BBBBBBBB
const/16 vAA, #+BBBB
Заносит #+BBBB в регистр vAA
const/4 vA, #+B
const/high16 vAA, #+BBBB
const-class vAA, Lclass
const-string vAA, «BBBB»
Переместит ссылку на строку, указанную в регистре назначения vAA
const-string/jumbo vAA, «BBBBBBBB»
Переместит ссылку на строку, указанную в регистре назначения vAA
jumbo — обозначает, что значение будет «большим»
Еще примеры:
const-wide/16 vA+, #+BBBB
const-wide/high16 vA+, #+BBBB
const-wide vA+, #+BBBBBBBBBBBBBBBB
Go To
goto — Безусловный переход к :target.
Только замена не может производиться на оборот: goto не может заменить goto/16, а он в свою очередь не может заменить goto/32.
Условия
if — Если выполняется условие, происходит переход по метке
Примеры:
Invoke
Вызывает метод интерфейса ( interface method ) (то есть объект, чей конкретный класс неизвестен, используя метод, который ссылается на интерфейс):
Вызывает статический метод ( static method ) (который всегда считается прямым методом):
Вызывает виртуальный метод ( virtual method ) непосредственного родительского класса:
Вызывает виртуальный метод ( virtual method ) (метод, который не является статическим или окончательным, и не является конструктором):
Примечание:
Если метод возвращает значение ( R не является « V » для Void), он должен быть зафиксирован в следующей строке одним из операторов move-result или он будет потерян.
И так можно делать с любым выше перечисленным invoke:
Прочее
check-cast vAA, Lclass
instance-of vA, vB, Lclass
new-instance vAA, Lclass
Пустая команда/Нет операции
throw vAA
Прим: A: x bits. B: x bits не является частью кода. Добавил только для обозначения бит в регистрах
move vA, vB
Перемещает содержимого одного регистра не-объекта (non-object) к другому.
move/16 vAAAA, vBBBB
A: 16 bits. B: 16 bits
move/from16 vAA, vBBBB
A: 8 bits. B: 16 bits
move-exception vAA
move-object vA, vB
Перемещает содержимое одного объекта, несущего регистра в другой.
move-object/16 vAAAA, vBBBB
A: 16 bits. B: 16 bits
move-object/from16 vAA, vBBBB
A: 8 bits. B: 16 bits
move-result vAA
move-result-object vAA
Остальное кратко:
Операции
Оператор ADD
Cкладывает значения по обе стороны от оператора
add-double vA+, vB+, vC+
Вычисляет vB+ + vC+ и сохраняет результат в vA+
add-double/2addr vA+, vB+
Вычисляет vA + vB и сохраните результат в vA+
add-float vA, vB, vC
Вычисляет vB + vC и сохраняет результат в vA
add-float/2addr vA, vB
Вычисляет vA + vB и сохраняет результат в vA
add-int vA, vB, vC
Вычисляет vB + vC и сохраняет результат в vA
add-int/lit8 vA, vB, 0xC
Вычисляет vB + 0xC и сохраняет результат в vA
add-int/lit16 vA, vB, 0xC
Вычисляет vB + 0xC и сохраняет результат в vA
add-int/2addr vA, vB
Вычисляет vA + vB и сохраняет результат в vA
Оператор AND
Бинарный оператор копирует бит в результат, если он существует в обоих операндах.
Оператор DIV
Делит левый операнд на правый операнд
Оператор MUL
Умножает значения по обе стороны от оператора
Оператор OR
Копирует бит, если он существует в любом из операндов.
Оператор REM
Делит левый операнд на правый операнд и возвращает остаток
Оператор SHL
Значение левых операндов перемещается влево на количество бит, заданных правым операндом.
Оператор SHR
Значение правых операндов перемещается вправо на количество бит, заданных левых операндом.
Оператор SUB
Вычитает из правого операнда левый операнд
Оператор USHR
Оператор XOR
копирует бит, если он установлен в одном операнде, но не в обоих.
Return
Оператор return используется для выполнения явного возврата из метода. То есть он снова передает управление объекту, который вызвал данный метод. Оператор return предписывает интерпретатору остановить выполнение текущего метода. Если метод возвращает значение, оператор return сопровождается некоторым выражением. Значение этого выражения становится возвращаемым значением метода.
return vAA
Возврат из метода возвращаемого значения non-object со значением vAA.
return-object vAA
Возврат из метода object-returning с помощью object-reference в vAA.
return-void
Возврат из метода void без значения.
return-wide vA+
Switch-и
packed-switch vAA, :target
Легенда:
sparse-switch vAA, :target
Реализует оператор switch, где константы case не являются последовательными. В инструкции используется таблица поиска с константами case и смещениями для каждой константы случая. Если в таблице нет соответствия, выполнение продолжается в следующей команде (случай по умолчанию).
Начинаем со Smali. Как реверсить приложения для Android
Содержание статьи
Какое приложение мы будем препарировать? Я выбрал для своих целей VK Admin — программу для управления сообществами «ВКонтакте» со смартфона. В нем не предусмотрена темная тема, поэтому мы с тобой попробуем эту тему добавить.
WARNING
Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный изложенным материалом.
Собираем и разбираем
Сначала извлечем все ресурсы приложения, используя утилиту apktool — она распаковывает и запаковывает файлы пакетов APK, которые хранятся в сжатом, бинарном виде, и дизассемблирует программный код, заключенный в них.
Чтобы получить установочный пакет, можно воспользоваться Android Debugging Bridge — системой для отладки программ на устройстве. В *nix-подобных системах ADB ставится стандартно, с помощью пакетного менеджера, а в Windows — идет в составе Android Studio или Android SDK Platform Tools.
В первую очередь установим приложение из Google Play Store на смартфон, подключим его к компьютеру с помощью USB, затем воспользуемся ADB для переноса пакета приложения на компьютер и извлечем его содержимое.
$ adb shell pm path com.vk.admin
package:/data/app/com.vk.admin-Ka5KVtTbnGgxoRqnObb-pQ==/base.apk
$ adb pull /data/app/com.vk.admin-Ka5KVtTbnGgxoRqnObb-pQ==/base.apk com.vk.admin.apk
3881 KB/s (13414132 bytes in 3.374s)
$ apktool d com.vk.admin.apk
I: Using Apktool 2.4.0 on base.apk
I: Loading resource table.
I: Decoding AndroidManifest.xml with resources.
I: Regular manifest package.
I: Decoding file-resources.
I: Decoding values / XMLs.
I: Baksmaling classes.dex.
I: Copying assets and libs.
I: Copying unknown files.
I: Copying original files.
$ apktool b com.vk.admin/
I: Using Apktool 2.4.0
I: Checking whether resources has changed.
I: Checking whether sources has changed.
I: Smaling classes folder into classes.dex.
I: Building apk file.
I: Copying unknown files/dir.
I: Built apk.
Затем необходимо подписать приложение. Это нужно, чтобы и Google Play Store, и устройство не обновляли приложение, если подписи не совпадают. Поэтому не забудь удалить приложение с телефона, прежде чем установить свое, подписанное своим ключом. Для создания подписи в первый раз нужно воспользоваться утилитой keytool (входит в Java Development Kit):
Enter key password for
(RETURN if same as keystore password):
Re-enter new password:
Когда ты уже создал ключ, подпиши приложение.
После этого установи приложение на телефон.
$ adb install com.vk.admin.apk
3881 KB/s (13414132 bytes in 3.374s)
Success
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Начинаем со Smali. Как реверсить приложения для Android

Какое приложение мы будем препарировать? Я выбрал для своих целей VK Admin — программу для управления сообществами «ВКонтакте» со смартфона. В нем не предусмотрена темная тема, поэтому мы с тобой попробуем эту тему добавить.
Собираем и разбираем
Сначала извлечем все ресурсы приложения, используя утилиту apktool — она распаковывает и запаковывает файлы пакетов APK, которые хранятся в сжатом, бинарном виде, и дизассемблирует программный код, заключенный в них.
Чтобы получить установочный пакет, можно воспользоваться Android Debugging Bridge — системой для отладки программ на устройстве. В *nix-подобных системах ADB ставится стандартно, с помощью пакетного менеджера, а в Windows — идет в составе Android Studioили Android SDK Platform Tools.
В первую очередь установим приложение из Google Play Store на смартфон, подключим его к компьютеру с помощью USB, затем воспользуемся ADB для переноса пакета приложения на компьютер и извлечем его содержимое.
Затем необходимо подписать приложение. Это нужно, чтобы и Google Play Store, и устройство не обновляли приложение, если подписи не совпадают. Поэтому не забудь удалить приложение с телефона, прежде чем установить свое, подписанное своим ключом. Для создания подписи в первый раз нужно воспользоваться утилитой keytool (входит в Java Development Kit):
Когда ты уже создал ключ, подпиши приложение.
После этого установи приложение на телефон.
Меняем цветовые схемы
Цвета в приложении можно настроить несколькими способами:
Все эти варианты жизнеспособны и постоянно используются.
colors.xml
styles.xml
В файле com.vk.admin/res/values/styles.xml заданы цвета, но только некоторые. Этот файл используется самими разработчиками, когда они хотят сделать несколько цветовых схем приложения. Если же этих схем нет, работать приходится нам с тобой.
Layout
В папке com.vk.admin/res/ находятся описания экранов приложения.
Некоторые цвета приложения, чаще всего задний фон, можно найти здесь и изменить.
Smali
Машина Dalvik, в отличие от JVM, — регистровая, а не стековая. Регистры не имеют типов и могут хранить всё: числа, строки, экземпляры классов. При этом язык smali строго типизирован.
Вот небольшая последовательность инструкций, чтобы вывести содержимое регистра v0 в лог.
Если не удается понять, что значит тот или иной регистр или что делает та или иная функция, используй декомпилятор smali в Java — Jadx.

Использовать Jadx просто — нужно лишь выбрать файл и открыть его, код будет автоматически декомпилирован и показан.
Эта строка занимается изменением. Чтобы изменить цвет, нужно его записать в регистр, а затем передать в функцию вместо настоящего.
Теперь пройдись по всем файлам и замени цвет.
Бывают случаи на порядок проще.
Стоит быть внимательным и пытаться разобраться в коде: иногда оптимизатор кода будет перемещать инструкции const вверх, и ты можешь их не заметить.
setTextColor
На этом борьбу со светлым фоном и ярким экраном можно закончить. Большинство элементов экрана поменяли свой цвет на противоположный. Так ты можешь изменить любое приложение, особенно если ты любишь потыкать в телефон поздно ночью.

Меняем функциональность приложения
Чтобы ощутить всю мощь smali, давай попробуем изменить какую-то часть приложения. Например, обойдем проверку на внутренние покупки.
Возьмем другое приложение — ISS Detector. Оно показывает положение МКС над Землей. В нем можно купить расширения: показ комет, планет, Луны, телескопа «Хаббл».
Копируем все. При переустановке приложения файлы split_config.en.apk и split_config.xxhdpi.apk потеряются, а без них приложение будет вылетать. Поэтому их нужно будет переподписать: зайти в них как в архивы ZIP, удалить папку META_INF и прогнать через стандартную процедуру утилиты jarsigner. Основной же файл мы будем препарировать с помощью apktool.
Собираем приложение и устанавливаем его.
$ adb install-multiplie com.runar.issdetector.apk split_config.xxhdpi.apk split_config.en.apk
Success
С этого можно начать свой путь в реверс-инжиниринг. Я рассказал тебе основы, с которых начинал сам. Небольшие изменения могут улучшить твою жизнь — надеюсь, я смог тебе это показать.
Русские Блоги
Основы смальского языка
Что такое Смали?
Введение
Основы смальского языка
Следующее содержание относится к структуре и базовому синтаксису некоторых программ на Smali. Эти базовые синтаксисы необходимо использовать при использовании Smali для изменения логики приложения.
Smali файловая структура
основной тип
| Введите ключевые слова | Соответствует описанию типа в Java |
|---|---|
| V | void, может использоваться только для типа возврата |
| Z | boolean |
| B | byte |
| S | short |
| C | char |
| I | int |
| J | long (64 bits) |
| F | float |
| D | double (64 bits) |
объект
Тип объекта, то есть объект ссылочного типа, при обращении к нему начинается с L, за которым следует полное имя пакета, например: java.lang.String Соответствующий синтаксис Smali Ljava/lang/String
массив
Объявление метода и вызов
Шаблон справочного метода Smali, приведенный в официальной вики, выглядит следующим образом:
Lpackage/name/ObjectName;->MethodName(III)Z
Часть 1 Lpackage/name/ObjectName; Используется для объявления определенных типов для JVM, чтобы найти
Поскольку список параметров метода не разделен запятой, его можно разделить только слева направо в соответствии с определением типа. Это необходимо соблюдать осторожно.
Если вам нужно вызвать конструктор, MethodName:
Зарегистрировать декларацию и использовать
В Smali, если вам нужно хранить переменные, вы должны сначала объявить достаточное количество регистров, один регистр может хранить 32-битные типы длины, такие как Int, и два регистра могут хранить 64-битные типы данных, такие как Long или Double
В сочетании с обычно используемыми инструкциями Dalvik, вы можете достичь некоторых необходимых функций
Итак, как определить количество регистров, которые будут использоваться?
Из-за нестатического метода вам нужно занять регистр для сохранения этого указателя, тогда минимальное количество регистров этого типа метода равно 1, если вам нужно обработать входящие параметры, вам нужно добавить еще раз, в это время вы должны рассмотреть Double и Float Этот тип параметра должен занимать два регистра, например:
Если метод Java объявлен следующим образом:
myMethod(int p1, float p2, boolean p3)
Тогда соответствующий сомали это:
method LMyObject;->myMethod(IJZ)V
В настоящее время соответствующая ситуация с регистром выглядит следующим образом:
| Зарегистрировать имя | Соответствующая ссылка |
|---|---|
| p0 | this |
| p1 | параметр p1 типа int |
| p2, p3 | параметр типа p2 с плавающей точкой |
| p4 | логический параметр p3 |
Тогда минимальное количество необходимых регистров: 5
Если метод содержит определения констант, переменных и т. Д., Вам необходимо увеличить количество регистров в соответствии с ситуацией. Пока число соответствует требованиям, убедитесь, что значение, которое необходимо получить, не вымывается при последующем присвоении. После сохранения регистр можно переназначить) или занять реестр на долгое время
Набор инструкций Dalvik
Если вам нужно написать программу с использованием Smali, вам также необходимо освоить часто используемые инструкции виртуальной машины Dalvik, которые называются набором команд Dalvik. Эти инструкции в некоторой степени похожи на инструкции по сборке x86, но есть больше инструкций, и они очень просты и удобны в использовании. Для более подробного ознакомления вы можете обратиться к официальным документам Android, связанным с Dalvik:https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions
Вот также некоторые часто используемые инструкции, в сочетании с Smali для объяснения:
Используется для возврата значения, соответствующего оператору возврата в Java
Используется для объявления констант, таких как строковые константы (только объявления, String a = «abc» Такие заявления включают декларацию и уступку)
| инструкция | объяснение |
|---|---|
| if-eq v1,v2 | Определить, равны ли значения в двух регистрах |
| if-ne v1,v2 | Определите, не равны ли значения в двух регистрах |
| if-lt v1,v2 | Определите, меньше ли значение в регистре v1, чем значение в регистре v2 (lt == меньше чем) |
| if-ge v1,v2 | Определите, больше или равно ли значение в регистре v1 значению в регистре v2 (ge == больше или равно) |
| if-gt v1,v2 | Определите, больше ли значение в регистре v1, чем значение в регистре v2 (gt == great than) |
| if-le v1,v2 | Определите, является ли значение в регистре v1 меньше или равно значению в регистре v2 (le == меньше или равно) |
Следует отметить, что если операторы, написанные на Java, часто в соответствующих Smali, станут противоположной логикой суждения, как показано ниже:
Соблюдайте внимательно:
Атрибутивные операции делятся на: получить и положить
Целевой тип делится на: массив (массив), экземпляр (экземпляр) и статический (статический) три, соответствующий сокращенный префикс a, i, s
Типы длины делятся на: по умолчанию (ничего не писать), широкий (широкий, 64-битный) 、 объект 、 boolean 、 byte 、 char 、 short (Последние несколько не будут объяснены, в соответствии с Java)
Формат инструкции: [Имя команды] [регистр источника], [регистр объекта, в котором находится поле назначения], [указатель поля] Пример кода следующий: операция заключается в присвоении значения 100 переменной члена класса mIntA типа int:
Инструкции для поля экземпляра перечислены ниже, где я могу быть заменен на a или s, которые используются для манипулирования полями массива или статическими полями, соответственно
| инструкция | объяснение |
|---|---|
| iget | Значение, используемое для манипулирования типом значения int |
| iget-wide | Значение, используемое для работы широкого поля |
| iget-object | Значение, используемое для управления ссылками на объект |
| iget-boolean | Значение, используемое для работы с логическим типом |
| iget-byte | Значение, используемое для манипулирования типом байта |
| iget-char | Значение, используемое для манипулирования типом символа |
| iget-short | Значение, используемое для работы короткого типа |
| iput | Назначение, используемое для манипулирования типами значений, такими как int |
| iput-wide | Назначение, используется для работы широкого поля |
| iput-object | Назначение, используемое для управления ссылками на объекты |
| iput-boolean | Назначение, используемое для управления булевыми типами |
| iput-byte | Назначение, используемое для манипулирования типами байтов |
| iput-char | Назначение, используемое для управления типами символов |
| iput-short | Назначение, используется для работы короткого типа |
Следующий Java-код предназначен для самых простых операций присваивания и значения переменных класса.
Соответствующий код Smali выглядит следующим образом:
В соответствии со сравнением кода Java и Smali, стоит отметить, что метод получения переменных класса в Smali ближе к вызовам функций, но нет никаких параметров для вызовов функций
В дополнение к базовым инструкциям Dalvik, представленным выше, Dalvik также поддерживает два типа наборов команд: преобразование типов значений (например, int в float, double в float и т. Д.) И базовые операции (математические операции, логические операции, самоинкремент). Перечислите некоторые общие инструкции, другие могут ссылаться на официальные документы Google, упомянутые выше
Что может сделать Смали?
Хотя мы понимаем основной синтаксис Smali, мы, как правило, не пишем напрямую Smali для разработки функций, что является слишком дорогостоящим, и цель понимания Smali состоит в том, чтобы выполнить реверс-инжиниринг Android, такой как: анализ принципов APP, обнаружение уязвимостей, Конечно, вы также можете внести некоторые незначительные изменения в некоторые приложения (лучше не делать вещи, которые наносят ущерб закону, нарушают закон и дисциплину, и вредят другим)




