(ART/DVM) Вставка инструкции непосредственно перед инструкцией :try_end приводит к ошибке VerifyError.

Я работаю с smali/baksmali уже некоторое время, и я понимаю инструкции в разумной степени. В последнее время я столкнулся с ошибкой, которая действительно странная. Вставка инструкции после выровненного блока try-catch приводит к ошибке VerifyError типа Conflicted во время выполнения. Я сделал несколько предположений о том, в чем может быть причина, в том числе предположил, что ART не принимает вызовы, ожидающие перемещаемых результатов, но это не так. Я также пытался вставить инструкцию sput, чтобы проверить, будет ли она работать, но это не так. Но для некоторых методов, которые я видел, put инструкции, вставленные в одну и ту же позицию, работают довольно хорошо, что наводит меня на мысль, что это проблема выравнивания. Но я не знаю, на каком правиле это основано.

Насколько я понимаю, ошибка конфликтующего типа возникает, когда тип регистра изменяется, вероятно, путем перехода к другим условным операторам или операторам goto. Но в этом случае я не могу понять, где скачок, который оправдывает это.

Возьмем, к примеру, этот метод

.method private static zzd(Ljava/lang/String;)I
    .locals 7

    const/4 v0, 0x0

    const/4 v1, 0x2

    const/4 v2, 0x3

    const/4 v3, 0x1

    :try_start_0
    const-string v4, "[.-]"

    .line 19
    invoke-static {v4}, Lcom/a/b/c/d/zzax;->zza(Ljava/lang/String;)Lcom/a/b/c/d/zzax;

    move-result-object v4

    invoke-virtual {v4, p0}, Lcom/a/b/c/d/zzax;->zza(Ljava/lang/CharSequence;)Ljava/util/List;

    move-result-object v4

    .line 20
    invoke-interface {v4}, Ljava/util/List;->size()I

    move-result v5

    if-ne v5, v3, :cond_0

    .line 21
    invoke-static {p0}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I

    move-result p0

    return p0

    .line 22
    :cond_0
    invoke-interface {v4}, Ljava/util/List;->size()I

    move-result v5

    if-lt v5, v2, :cond_1

    .line 23
    invoke-interface {v4, v0}, Ljava/util/List;->get(I)Ljava/lang/Object;

    move-result-object v5

    check-cast v5, Ljava/lang/String;

    invoke-static {v5}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I

    move-result v5

    const v6, 0xf4240

    mul-int v5, v5, v6

    .line 24
    invoke-interface {v4, v3}, Ljava/util/List;->get(I)Ljava/lang/Object;

    move-result-object v6

    check-cast v6, Ljava/lang/String;

    invoke-static {v6}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I

    move-result v6

    mul-int/lit16 v6, v6, 0x3e8

    add-int/2addr v5, v6

    .line 25
    invoke-interface {v4, v1}, Ljava/util/List;->get(I)Ljava/lang/Object;

    move-result-object v4

    check-cast v4, Ljava/lang/String;

    invoke-static {v4}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I

    move-result p0

    # Inserting any instruction here causes a type p0 to become a conflicted type

    :try_end_0
    .catch Ljava/lang/IllegalArgumentException; {:try_start_0 .. :try_end_0} :catch_0

    add-int/2addr v5, p0

    return v5

    :catch_0
    move-exception v4

    const-string v5, "LibraryVersionContainer"

    .line 29
    invoke-static {v5, v2}, Landroid/util/Log;->isLoggable(Ljava/lang/String;I)Z

    move-result v2

    if-eqz v2, :cond_1

    new-array v1, v1, [Ljava/lang/Object;

    # Trying to insert the value of p0 into the object array causes the VerifyError

    aput-object p0, v1, v0

    aput-object v4, v1, v3

    const-string p0, "Version code parsing failed for: %s with exception %s."

    .line 31
    invoke-static {p0, v1}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;

    move-result-object p0

    .line 32
    invoke-static {v5, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    :cond_1
    const/4 p0, -0x1

    return p0
.end method

person BluRe.CN    schedule 30.12.2020    source источник


Ответы (1)


Флаг --register-info от baksmali — ваш друг. Он добавит комментарии для каждой инструкции с подробной информацией о типах регистров. Он должен показать вам, где именно тип регистра становится конфликтующим, включая входящие ребра и входящие типы от каждого ребра.

baksmali d --off --register-info ARGS,DEST,FULLMERGE -b "" blah.dex

например вот комментарий относительно p0 из вашего исходного источника, где возникает конфликт в блоке catch после добавления инструкции (например, invoke-static {v4}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I) непосредственно перед try_end.

#p0=(Conflicted):merge{0x3:(Reference,Ljava/lang/String;),0x4:(Reference,Ljava/lang/String;),0x9:(Reference,Ljava/lang/String;),0xd:(Reference,Ljava/lang/String;),0x12:(Reference,Ljava/lang/String;),0x1d:(Reference,Ljava/lang/String;),0x22:(Reference,Ljava/lang/String;),0x23:(Reference,Ljava/lang/String;),0x2c:(Reference,Ljava/lang/String;),0x31:(Reference,Ljava/lang/String;),0x32:(Reference,Ljava/lang/String;),0x3a:(Reference,Ljava/lang/String;),0x3e:(Reference,Ljava/lang/String;),0x3f:(Reference,Ljava/lang/String;),0x44:(Integer)}

Каждое из этих входящих краев исходит из инструкций в блоке try, которые потенциально могут вызвать исключение. Добавляя новую инструкцию, которая может генерировать исключение после инструкции move-result p0 в конце блока try, вы добавляете новое входящее ребро в блок catch, и значение p0 из этого ребра (Integer) несовместимо с типы со всех других ребер, поэтому объединенный тип считается CONFLICTED.

Числа в комментарии слияния относятся к смещению кода входящего ребра. Когда вы выполняете дизассемблирование с опцией --code-offsets (--off является сокращением для этого), baksmali добавит комментарий со смещением кода для каждой инструкции, так что вы сможете сопоставить смещение кода из комментария с информацией о регистре обратно к собственно инструкция.

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

person JesusFreke    schedule 31.12.2020
comment
Спасибо за подробное объяснение. Теперь это действительно имеет больше смысла, я ценю ваш ответ. - person BluRe.CN; 31.12.2020
comment
Пожалуйста, у меня есть вопрос, есть ли способ заставить ART выполнить метод, несмотря на VerifyError? Используя приведенный выше пример, если я вставлю две следующие инструкции const/4 v2, 0x5 и invoke-static{v2}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;, мы точно знаем, что этот вызов никогда не может вызвать исключение, по крайней мере, в нормальной среде, и это означает, что поток выполнения никогда не доберется до блока catch где это становится конфликтным. Почему мы не можем заверить АРТ, что знаем, что произойдет? - person BluRe.CN; 12.04.2021