(ART/DVM) 在 :try_end 指令之前插入一条指令导致 VerifyError
(ART/DVM) Inserting an instruction just before the :try_end instruction results in VerifyError
我使用 smali/baksmali 已经有一段时间了,我在一定程度上理解了这些说明。最近,我遇到了一个非常奇怪的错误。
在对齐的 try-catch
块之后插入一条指令会在运行时导致冲突类型 VerifyError。
我对可能的原因做了一些假设,包括假设 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
baksmali 的 --register-info 标志是你的朋友。它将为每条指令添加注释,其中包含有关寄存器类型的详细信息。它应该向您准确显示寄存器类型发生冲突的位置,包括传入边缘和来自每个边缘的传入类型。
baksmali d --off --register-info ARGS,DEST,FULLMERGE -b "" blah.dex
例如这是来自原始来源的关于 p0
的评论,在 try_end.
之前添加指令(例如 invoke-static {v4}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
)后,它在 catch 块中发生冲突
#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 块中可能引发异常的指令。通过在 try 块末尾的 move-result p0
指令之后添加可以抛出异常的新指令,您将向 catch 块添加一个新的传入边,并从中添加 p0
的值边 (Integer
) 与所有其他边的类型不兼容,因此合并后的类型被认为是 CONFLICTED
.
合并注释中的数字是指入边的代码偏移量。当您使用 --code-offsets 选项反汇编时(--off 是它的快捷方式),baksmali 将为每条指令添加一个带有代码偏移量的注释,因此您可以将寄存器信息注释中的代码偏移量匹配回实际说明。
值得注意的是,合并注释中的代码偏移指的是实际可以抛出的指令之前的指令。如果抛出异常,则抛出的指令不会影响任何寄存器,因此传入类型来自 post-指令类型,来自之前的指令。
我使用 smali/baksmali 已经有一段时间了,我在一定程度上理解了这些说明。最近,我遇到了一个非常奇怪的错误。
在对齐的 try-catch
块之后插入一条指令会在运行时导致冲突类型 VerifyError。
我对可能的原因做了一些假设,包括假设 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
baksmali 的 --register-info 标志是你的朋友。它将为每条指令添加注释,其中包含有关寄存器类型的详细信息。它应该向您准确显示寄存器类型发生冲突的位置,包括传入边缘和来自每个边缘的传入类型。
baksmali d --off --register-info ARGS,DEST,FULLMERGE -b "" blah.dex
例如这是来自原始来源的关于 p0
的评论,在 try_end.
invoke-static {v4}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
)后,它在 catch 块中发生冲突
#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 块中可能引发异常的指令。通过在 try 块末尾的 move-result p0
指令之后添加可以抛出异常的新指令,您将向 catch 块添加一个新的传入边,并从中添加 p0
的值边 (Integer
) 与所有其他边的类型不兼容,因此合并后的类型被认为是 CONFLICTED
.
合并注释中的数字是指入边的代码偏移量。当您使用 --code-offsets 选项反汇编时(--off 是它的快捷方式),baksmali 将为每条指令添加一个带有代码偏移量的注释,因此您可以将寄存器信息注释中的代码偏移量匹配回实际说明。
值得注意的是,合并注释中的代码偏移指的是实际可以抛出的指令之前的指令。如果抛出异常,则抛出的指令不会影响任何寄存器,因此传入类型来自 post-指令类型,来自之前的指令。