dalvik 字节码验证 - dex2oat
dalvik bytecode verification - dex2oat
我在字节码级别检测了一个应用程序,并从 dex2oat 得到以下验证错误:
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: Verification error in int a.d.cg.b(byte[], int, int)
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int): [0x25]
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int): [0x27]
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int) failed to verify: int a.d.cg.b(byte[], int, int): [0x2C] monitor-exit on non-object (Undefined)
此方法的 smali 表示如下:
.method b([BII)I
.locals 4
move-object/from16 v2, p0
move-object/from16 v3, p1
move/from16 p0, p2
move/from16 p1, p3
iget-object v0, v2, La/d/cg;->a:La/d/bj;
iget-object v0, v0, La/d/bj;->p:Ljava/io/InputStream;
if-eqz v0, :cond_0
const-string p2, "La/d/cg;->b([BII)I->3"
invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V
iget-object v0, v2, La/d/cg;->a:La/d/bj;
iget-object v0, v0, La/d/bj;->p:Ljava/io/InputStream;
check-cast v0, La/d/cn;
iget-object v1, v0, La/d/cn;->b:Ljava/lang/Object;
monitor-enter v1
:try_start_0
invoke-virtual {v0, v3, p0, p1}, La/d/cn;->b([BII)I
iget-object v0, v0, La/d/cn;->b:Ljava/lang/Object;
invoke-virtual {v0}, Ljava/lang/Object;->notify()V
monitor-exit v1
:cond_0
const-string p2, "La/d/cg;->b([BII)I->12"
invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V
return p1
:catchall_0
move-exception v0
monitor-exit v1
:try_end_0
.catchall {:try_start_0 .. :try_end_0} :catchall_0
throw v0
.end method
未检测的版本不包含跟踪器的调用和前面定义跟踪字符串的 const-string 指令。此外,前四个移动指令在原始版本中也没有出现;它们用于在 'end' 获得免费寄存器。
我也给smali文件注解了寄存器类型信息,输出如下(只有验证错误描述的有意思的部分):
#@1b
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
monitor-enter v1
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@1c
:try_start_1c
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
invoke-virtual {v0, v3, p0, p1}, La/d/cn;->b([BII)I
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@1f
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
iget-object v0, v0, La/d/cn;->b:Ljava/lang/Object;
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@21
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
invoke-virtual {v0}, Ljava/lang/Object;->notify()V
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@24
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
monitor-exit v1
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@25
:cond_25
#v0=(Reference,Ljava/lang/Object;):merge{0xc:(Reference,Ljava/io/InputStream;),0x24:(Reference,Ljava/lang/Object;)}
#v1=(Conflicted):merge{0xc:(Uninit),0x24:(Reference,Ljava/lang/Object;)}
#v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);
#p2=(Conflicted):merge{0xc:(Integer),0x24:(Reference,Ljava/lang/String;)}
#p3=(Integer);
const-string p2, "La/d/cg;->b([BII)I->12"
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@27
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@2a
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
return p1
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@2b
:catchall_2b
#v0=(Reference,Ljava/lang/Object;):merge{0xc:(Reference,Ljava/io/InputStream;),0x1b:(Reference,La/d/cn;),0x1c:(Reference,La/d/cn;),0x1f:(Reference,Ljava/lang/Object;),0x21:(Reference,Ljava/lang/Object;),0x24:(Reference,Ljava/lang/Object;),0x25:(Reference,Ljava/lang/Object;),0x2b:(Reference,Ljava/lang/Throwable;)}
#v1=(Conflicted):merge{0xc:(Uninit),0x1b:(Reference,Ljava/lang/Object;),0x1c:(Reference,Ljava/lang/Object;),0x1f:(Reference,Ljava/lang/Object;),0x21:(Reference,Ljava/lang/Object;),0x24:(Reference,Ljava/lang/Object;),0x25:(Conflicted),0x2b:(Conflicted)}
#v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);
#p2=(Conflicted):merge{0xc:(Integer),0x1b:(Reference,Ljava/lang/String;),0x1c:(Reference,Ljava/lang/String;),0x1f:(Reference,Ljava/lang/String;),0x21:(Reference,Ljava/lang/String;),0x24:(Reference,Ljava/lang/String;),0x25:(Reference,Ljava/lang/String;),0x2b:(Conflicted)}
#p3=(Integer);
move-exception v0
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
#@2c
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
monitor-exit v1
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
:try_end_2d
.catchall {:try_start_1c .. :try_end_2d} :catchall_2b
#@2d
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
throw v0
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
.end method
当查看位置 [0x2C] 时,我只观察到 v1 处于冲突状态并且 [0x2B] 处描述的合并告诉我 uninit 与引用类型合并。我认为这是问题所在并导致验证错误 (https://android.googlesource.com/platform/art/+/master/runtime/verifier/register_line.cc#367)。但是,当考虑附有寄存器类型信息的原始 smali 文件时,我观察到 v1 永远不会处于冲突状态。此外,奇怪的是 - 至少对我来说 - 是我的仪器从未接触过寄存器 v1,因此怎么会发生这种冲突?
问题在于,通过在 try 块中添加对跟踪函数的调用,您将从该位置向 catch-all 异常处理程序添加边。
有些指令能够抛出异常,有些则不能。例如return 指令不能抛出异常,而 invoke 指令可以。因此,对于 try 块中可以抛出的任何指令,将向该 try 块的任何异常处理程序添加一个边缘。
在原来的方法中,方法开始附近的条件(if-eqz v0, :cond_0
)直接跳转到了return语句,所以异常处理程序没有优势,因为它不能抛出异常。因此,到达该异常处理程序的唯一方法是通过已设置 v1
的执行路径。
但是,通过添加 invoke 指令,您从那里向异常处理程序添加了一条边,因此现在有一条指向未设置 v1
的异常处理程序的执行路径。
所以基本上,考虑 v0
在条件条件下为 null 的情况(因此采用跳转),然后跟踪函数抛出异常。将调用异常处理程序,但尚未设置 v1
。
我在字节码级别检测了一个应用程序,并从 dex2oat 得到以下验证错误:
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: Verification error in int a.d.cg.b(byte[], int, int)
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int): [0x25]
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int): [0x27]
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int) failed to verify: int a.d.cg.b(byte[], int, int): [0x2C] monitor-exit on non-object (Undefined)
此方法的 smali 表示如下:
.method b([BII)I
.locals 4
move-object/from16 v2, p0
move-object/from16 v3, p1
move/from16 p0, p2
move/from16 p1, p3
iget-object v0, v2, La/d/cg;->a:La/d/bj;
iget-object v0, v0, La/d/bj;->p:Ljava/io/InputStream;
if-eqz v0, :cond_0
const-string p2, "La/d/cg;->b([BII)I->3"
invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V
iget-object v0, v2, La/d/cg;->a:La/d/bj;
iget-object v0, v0, La/d/bj;->p:Ljava/io/InputStream;
check-cast v0, La/d/cn;
iget-object v1, v0, La/d/cn;->b:Ljava/lang/Object;
monitor-enter v1
:try_start_0
invoke-virtual {v0, v3, p0, p1}, La/d/cn;->b([BII)I
iget-object v0, v0, La/d/cn;->b:Ljava/lang/Object;
invoke-virtual {v0}, Ljava/lang/Object;->notify()V
monitor-exit v1
:cond_0
const-string p2, "La/d/cg;->b([BII)I->12"
invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V
return p1
:catchall_0
move-exception v0
monitor-exit v1
:try_end_0
.catchall {:try_start_0 .. :try_end_0} :catchall_0
throw v0
.end method
未检测的版本不包含跟踪器的调用和前面定义跟踪字符串的 const-string 指令。此外,前四个移动指令在原始版本中也没有出现;它们用于在 'end' 获得免费寄存器。 我也给smali文件注解了寄存器类型信息,输出如下(只有验证错误描述的有意思的部分):
#@1b
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
monitor-enter v1
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@1c
:try_start_1c
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
invoke-virtual {v0, v3, p0, p1}, La/d/cn;->b([BII)I
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@1f
#v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
iget-object v0, v0, La/d/cn;->b:Ljava/lang/Object;
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@21
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
invoke-virtual {v0}, Ljava/lang/Object;->notify()V
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@24
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
monitor-exit v1
#v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@25
:cond_25
#v0=(Reference,Ljava/lang/Object;):merge{0xc:(Reference,Ljava/io/InputStream;),0x24:(Reference,Ljava/lang/Object;)}
#v1=(Conflicted):merge{0xc:(Uninit),0x24:(Reference,Ljava/lang/Object;)}
#v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);
#p2=(Conflicted):merge{0xc:(Integer),0x24:(Reference,Ljava/lang/String;)}
#p3=(Integer);
const-string p2, "La/d/cg;->b([BII)I->12"
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@27
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@2a
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
return p1
#v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
#@2b
:catchall_2b
#v0=(Reference,Ljava/lang/Object;):merge{0xc:(Reference,Ljava/io/InputStream;),0x1b:(Reference,La/d/cn;),0x1c:(Reference,La/d/cn;),0x1f:(Reference,Ljava/lang/Object;),0x21:(Reference,Ljava/lang/Object;),0x24:(Reference,Ljava/lang/Object;),0x25:(Reference,Ljava/lang/Object;),0x2b:(Reference,Ljava/lang/Throwable;)}
#v1=(Conflicted):merge{0xc:(Uninit),0x1b:(Reference,Ljava/lang/Object;),0x1c:(Reference,Ljava/lang/Object;),0x1f:(Reference,Ljava/lang/Object;),0x21:(Reference,Ljava/lang/Object;),0x24:(Reference,Ljava/lang/Object;),0x25:(Conflicted),0x2b:(Conflicted)}
#v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);
#p2=(Conflicted):merge{0xc:(Integer),0x1b:(Reference,Ljava/lang/String;),0x1c:(Reference,Ljava/lang/String;),0x1f:(Reference,Ljava/lang/String;),0x21:(Reference,Ljava/lang/String;),0x24:(Reference,Ljava/lang/String;),0x25:(Reference,Ljava/lang/String;),0x2b:(Conflicted)}
#p3=(Integer);
move-exception v0
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
#@2c
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
monitor-exit v1
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
:try_end_2d
.catchall {:try_start_1c .. :try_end_2d} :catchall_2b
#@2d
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
throw v0
#v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
.end method
当查看位置 [0x2C] 时,我只观察到 v1 处于冲突状态并且 [0x2B] 处描述的合并告诉我 uninit 与引用类型合并。我认为这是问题所在并导致验证错误 (https://android.googlesource.com/platform/art/+/master/runtime/verifier/register_line.cc#367)。但是,当考虑附有寄存器类型信息的原始 smali 文件时,我观察到 v1 永远不会处于冲突状态。此外,奇怪的是 - 至少对我来说 - 是我的仪器从未接触过寄存器 v1,因此怎么会发生这种冲突?
问题在于,通过在 try 块中添加对跟踪函数的调用,您将从该位置向 catch-all 异常处理程序添加边。
有些指令能够抛出异常,有些则不能。例如return 指令不能抛出异常,而 invoke 指令可以。因此,对于 try 块中可以抛出的任何指令,将向该 try 块的任何异常处理程序添加一个边缘。
在原来的方法中,方法开始附近的条件(if-eqz v0, :cond_0
)直接跳转到了return语句,所以异常处理程序没有优势,因为它不能抛出异常。因此,到达该异常处理程序的唯一方法是通过已设置 v1
的执行路径。
但是,通过添加 invoke 指令,您从那里向异常处理程序添加了一条边,因此现在有一条指向未设置 v1
的异常处理程序的执行路径。
所以基本上,考虑 v0
在条件条件下为 null 的情况(因此采用跳转),然后跟踪函数抛出异常。将调用异常处理程序,但尚未设置 v1
。