Погружение в Smali. Как выглядят Java и Kotlin со стороны

от автора

Эта статья — оригинальная, а не копипаст и не машинный перевод. При копировании прошу ссылаться…

Статья для начинающих исследователей файлов APK. Хотя почему только для начинающих? На некоторые моменты не обращаешь внимания, пока не разложишь все по полочкам.

Наверное есть такие люди, которые думают, что все так просто: взял APK, декомпилировал в Java и все — можно править код на понятном, высокоуровневом языке.
Таких оптимистов спешу расстроить: по моим личным наблюдениям, максимальное количество работоспособного кода Java, которое можно получить в результате декомпиляции APK не более 50%. Это конечно относится к приложениям из реального мира, а не к учебным примерам. Такие вещи, как обфускация кода и ресурсов я пока не затрагиваю — это материал для отдельных публикаций. Но, как бы вы не изворачивались, а все равно, в итоге придете к тому, что самый лучший вариант — читать код в Smali. Сначала это кажется довольно трудным занятием. Но, это — чисто психологические трудности, пока не наберетесь опыта.

В этой статье я хочу привести примеры типовых конструкций, применяемых в языках программирования. Мы посмотрим как выглядят эти конструкции в Java, Kotlin и Smali. Начнем пожалуй.

1. Операторы присваивания и арифметические операции

Java:

public void exampleOps() {     int a = 2;     int b = 1;     int c = 3;      c = a + b;     c = a - b;     c = a * b;     c = a / b; } 

Java -> Smali:

.method public exampleOps()V     .locals 3      .line 8     const/4 v0, 0x2      .line 9     .local v0, "a":I     const/4 v1, 0x1      .line 10     .local v1, "b":I     const/4 v2, 0x3      .line 12     .local v2, "c":I     add-int v2, v0, v1      .line 13     sub-int v2, v0, v1      .line 14     mul-int v2, v0, v1      .line 15     div-int v2, v0, v1      .line 16     return-void .end method 

Kotlin:

fun exampleOps() {     var a = 2     var b = 1     var c = 3      c = a + b     c = a - b     c = a * b     c = a / b } 

Kotlin -> Smali:

.method public final exampleOps()V     .locals 3      .line 6     const/4 v0, 0x2      .line 7     .local v0, "a":I     const/4 v1, 0x1      .line 8     .local v1, "b":I     const/4 v2, 0x3      .line 10     .local v2, "c":I     add-int v2, v0, v1      .line 11     sub-int v2, v0, v1      .line 12     mul-int v2, v0, v1      .line 13     div-int v2, v0, v1      .line 14     return-void .end method 

2. Условный оператор if..else.

Java:

public void exampleIf() {     int a = 1;     int b = 1;     if (a == b) {         b = a;     } else {         b = 0;     } } 

Java -> Smali:

.method public exampleIf()V     .locals 2      .line 19     const/4 v0, 0x1      .line 20     .local v0, "a":I     const/4 v1, 0x1      .line 21     .local v1, "b":I     if-ne v0, v1, :cond_0      .line 22     move v1, v0      goto :goto_0      .line 24     :cond_0     const/4 v1, 0x0      .line 26     :goto_0     return-void .end method 

Kotlin:

fun exampleIf() {     val a = 1     var b = 1     if (a == b) {         b = a     } else {         b = 0     } } 

Kotlin -> Smali:

.method public final exampleIf()V     .locals 2      .line 17     const/4 v0, 0x1      .line 18     .local v0, "a":I     const/4 v1, 0x1      .line 19     .local v1, "b":I     nop      .line 20     move v1, v0      .line 24     return-void .end method 

3. Оператор цикла for

Java:

private void exampleFor() {     int b = 0;     for (int i = 0; i < 10; i++) {         b++;     } } 

Java -> Smali:

.method private exampleFor()V     .locals 3      .line 48     const/4 v0, 0x0      .line 49     .local v0, "b":I     const/4 v1, 0x0      .local v1, "i":I     :goto_0     const/16 v2, 0xa      if-ge v1, v2, :cond_0      .line 50     add-int/lit8 v0, v0, 0x1      .line 49     add-int/lit8 v1, v1, 0x1      goto :goto_0      .line 52     .end local v1    # "i":I     :cond_0     return-void .end method 

Kotlin:

private fun exampleFor() {     var b = 0     for (i in 0..10) {         b++     } } 

Kotlin -> Smali:

.method private final exampleFor()V     .locals 3      .line 39     const/4 v0, 0x0      .line 40     .local v0, "b":I     const/4 v1, 0x0      .local v1, "i":I     :goto_0     const/16 v2, 0xb      if-ge v1, v2, :cond_0      .line 41     add-int/lit8 v0, v0, 0x1      .line 40     add-int/lit8 v1, v1, 0x1      goto :goto_0      .line 43     .end local v1    # "i":I     :cond_0     return-void 

4. Перечисление forEach

Java:

private void exampleForEach() {     ArrayList<Integer> a = new ArrayList<>();     int b = 0;     for (Integer it : a) {         b += it;     } } 

Java -> Smali:

.method private exampleForEach()V     .locals 5      .line 55     new-instance v0, Ljava/util/ArrayList;      invoke-direct {v0}, Ljava/util/ArrayList;-><init>()V      .line 56     .local v0, "a":Ljava/util/ArrayList;, "Ljava/util/ArrayList<Ljava/lang/Integer;>;"     const/4 v1, 0x0      .line 57     .local v1, "b":I     invoke-virtual {v0}, Ljava/util/ArrayList;->iterator()Ljava/util/Iterator;      move-result-object v2      :goto_0     invoke-interface {v2}, Ljava/util/Iterator;->hasNext()Z      move-result v3      if-eqz v3, :cond_0      invoke-interface {v2}, Ljava/util/Iterator;->next()Ljava/lang/Object;      move-result-object v3      check-cast v3, Ljava/lang/Integer;      .line 58     .local v3, "it":Ljava/lang/Integer;     invoke-virtual {v3}, Ljava/lang/Integer;->intValue()I      move-result v4      add-int/2addr v1, v4      .line 59     .end local v3    # "it":Ljava/lang/Integer;     goto :goto_0      .line 60     :cond_0     return-void .end method 

Kotlin:

private fun exampleForEach() {     val a = arrayListOf<Int>()     var b = 0     a.forEach { b += it } } 

Kotlin -> Smali:

.method private final exampleForEach()V     .locals 8      .line 46     new-instance v0, Ljava/util/ArrayList;      invoke-direct {v0}, Ljava/util/ArrayList;-><init>()V      .line 47     .local v0, "a":Ljava/util/ArrayList;     const/4 v1, 0x0      .line 48     .local v1, "b":I     move-object v2, v0      check-cast v2, Ljava/lang/Iterable;      .local v2, "$this$forEach$iv":Ljava/lang/Iterable;     const/4 v3, 0x0      .line 57     .local v3, "$i$f$forEach":I     invoke-interface {v2}, Ljava/lang/Iterable;->iterator()Ljava/util/Iterator;      move-result-object v4      :goto_0     invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z      move-result v5      if-eqz v5, :cond_0      invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;      move-result-object v5      .local v5, "element$iv":Ljava/lang/Object;     move-object v6, v5      check-cast v6, Ljava/lang/Number;      invoke-virtual {v6}, Ljava/lang/Number;->intValue()I      move-result v6      .local v6, "it":I     const/4 v7, 0x0      .line 48     .local v7, "$i$a$-forEach-ExamplesKotlin$exampleForEach$1":I     add-int/2addr v1, v6      .line 57     .end local v6    # "it":I     .end local v7    # "$i$a$-forEach-ExamplesKotlin$exampleForEach$1":I     nop      .end local v5    # "element$iv":Ljava/lang/Object;     goto :goto_0      .line 58     :cond_0     nop      .line 49     .end local v2    # "$this$forEach$iv":Ljava/lang/Iterable;     .end local v3    # "$i$f$forEach":I     return-void .end method 

5. Операторы switch…case (Java) и when() (Kotlin)

Java:

private void exampleSwitch() {     int a = 1;     int b = a;     int c = 0;     switch (b) {         case 0:             c = a + 1;             break;         case 1:             c = a + 2;             break;         case 2:             c = a + 3;             break;         default:             break;     } } 

Java -> Smali:

.method private exampleSwitch()V     .locals 3      .line 29     const/4 v0, 0x1      .line 30     .local v0, "a":I     move v1, v0      .line 31     .local v1, "b":I     const/4 v2, 0x0      .line 32     .local v2, "c":I     packed-switch v1, :pswitch_data_0      goto :goto_0      .line 40     :pswitch_0     add-int/lit8 v2, v0, 0x3      .line 41     goto :goto_0      .line 37     :pswitch_1     add-int/lit8 v2, v0, 0x2      .line 38     goto :goto_0      .line 34     :pswitch_2     add-int/lit8 v2, v0, 0x1      .line 35     nop      .line 45     :goto_0     return-void      nop      :pswitch_data_0     .packed-switch 0x0         :pswitch_2         :pswitch_1         :pswitch_0     .end packed-switch .end method 

Kotlin:

private fun exampleSwitch() {     var a = 1     var b = a     var c = 0     when (b) {         0 -> c = a + 1         1 -> c = a + 2         2 -> c = a + 3         else -> c = 0     } } 

Kotlin -> Smali:

.method private final exampleSwitch()V     .locals 3      .line 27     const/4 v0, 0x1      .line 28     .local v0, "a":I     move v1, v0      .line 29     .local v1, "b":I     const/4 v2, 0x0      .line 30     .local v2, "c":I     packed-switch v1, :pswitch_data_0      .line 34     const/4 v2, 0x0      goto :goto_0      .line 33     :pswitch_0     add-int/lit8 v2, v0, 0x3      goto :goto_0      .line 32     :pswitch_1     add-int/lit8 v2, v0, 0x2      goto :goto_0      .line 31     :pswitch_2     add-int/lit8 v2, v0, 0x1      .line 36     :goto_0     return-void      nop      :pswitch_data_0     .packed-switch 0x0         :pswitch_2         :pswitch_1         :pswitch_0     .end packed-switch .end method 

6. Преобразование переменной из Integer в String

Java:

private void exampleCast() {     int a = 1;     String b = "";     b = String.valueOf(a); } 

Java -> Smali:

.method private exampleCast()V     .locals 2      .line 63     const/4 v0, 0x1      .line 64     .local v0, "a":I     const-string v1, ""      .line 65     .local v1, "b":Ljava/lang/String;     invoke-static {v0}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;      move-result-object v1      .line 66     return-void .end method 

Kotlin:

private fun exampleCast() {     val a = 1     var b = ""     b = a.toString() } 

Kotlin -> Smali:

.method private final exampleCast()V     .locals 2      .line 52     const/4 v0, 0x1      .line 53     .local v0, "a":I     const-string v1, ""      .line 54     .local v1, "b":Ljava/lang/String;     invoke-static {v0}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;      move-result-object v1      .line 55     return-void .end method 

Заключение

На этом на сегодня — все. Вывод можно сделать такой, что как бы Kotlin не отгораживался от Java, а в байт-коде, на уровне простейших операций, они практически равны. Разве что Kotlin больше внимания уделяет оптимизации. В следующих статьях посмотрим на объявления классов и интерфейсов, создание объектов, а так же на то, как выглядят в Smali некоторые идиомы Kotlin. Посмотрим с боку на Corutines ,Rx, Architecture Components и DI. Отдельно поговорим про ресурсы (шаблоны XML и Compose).

Нужно еще сказать, что по сути, фрагменты кода в Smali довольно типовые, поэтому со временем ваш глаз научится сам находить нужный фрагмент на лету, не останавливая прокрутки экрана. Как в «Матрице»)).

P.S.
Я за чистоту Русского Языка, и терпеть не могу, когда иностранные слова используют в кириллице, типа «кейс» или «сниппет» и вставляют их в устную речь, даже когда общаются с широкой и разнородной аудиторией, искажая Великий и Могучий. Считаю это признаком недалекости ума (Личное мнение автора статьи).


ссылка на оригинал статьи https://habr.com/ru/articles/774758/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *