Dalvik 字节码检测 - 寄存器类型合并

Dalvik bytecode instrumentation - register type merging

我正在使用 dexlib2 进行某种 dalvik 字节码检测。 但是,还有几个遗留问题。 似乎在goto指令之后发生的寄存器类型合并 并以某种方式捕获块,更准确地说是在相应的标签处 派生出意外的寄存器类型,这又会破坏检测代码。

插入的指令如下所示:

move(-wide,-object,/16,/from16) vNew, v0
const-string v0, "some string"
invoke-static, {v0}, LPathToSomeClass;->SomeMethod(Ljava/lang/String;)V
move(..) v0, vNew

因此,v0 用于保存静态函数调用的一些参数,而 vNew 是一个新的(本地)寄存器,用于存储和恢复 v0 的原始内容。提前导出v0的寄存器类型,以便导出正确 移动指令,即 move-wide、move 或 move-object。但是,当此代码包含在某些 try 块中时,检测会中断。的输出 baksmali (baksmali d -b "" --register-info ALL,FULLMERGE --offsets ) 显示 const-string 指令后的 v0 类型(即 Reference,L/java/lang/String)被视为发生在相应 catch 块标签处的合并过程的输入。假设插入代码之前的类型是Reference,[I(int array)结果 类型现在是 Reference,L/java/lang/Object(这会产生验证错误),尽管最后的移动指令恢复了原始寄存器类型。

现在回答我的问题:

1) 这种合并实际发生在什么时候?

2) 为什么合并过程在const-string 指令之后考虑v0 的类型?是否考虑每条指令修改任何寄存器的类型?

3) 这个问题是否只与 try-catch 块有关?

4) try-catch块在这件事上有什么限制?

5) 除了为每个代码构造一个自己的方法以进行无参数注入之外,是否有解决此问题的方法?那么是否可以使用额外的寄存器来解决这个问题呢?

6) 我可以使用 dexlib2 try-catch 块进行检测并确定它们包含的指令集吗?

7) 是否有 notes/literature 讨论这个问题,例如合并程序和相关技术细节,例如进一步 limitations/restrictions 对于仪器?

我非常感谢在这件事上的任何帮助。提前致谢!

在 catch 块的开头合并寄存器时,try 块中的每条指令都有一个传入边沿可以抛出。只有某些指令可以抛出 - 由 CAN_THROW 操作码标志决定。

在您的特定示例中,const-string 指令之后的 invoke-static 指令可以抛出异常,因此从该指令之前到 catch 块的开头有一条边。

如果后退一步,执行可以从 try 块中任何可以抛出异常的指令跳转到 catch 块的开头。因此,catch 块中的代码必须准备好让寄存器处于与寄存器内容一致的状态,就在任何可能抛出的指令之前。

因此,例如,如果有一种可能 "jump" 从 try 块到寄存器包含原始 int 类型的 catch 块,以及另一种可能的跳转,其中它包含一个对象,则该寄存器是考虑 "conflicted",因为寄存器在代码中的那个点可能包含任何一种类型,并且这两种类型彼此不兼容。例如。原始 int 永远不能传递给需要引用类型的东西,反之亦然。并且字节码中没有用于静态寄存器类型检查的机制。

一种可能的解决方案可能是在插入检测的位置拆分 try 块,这样检测本身就不会被 try 块覆盖,但原始代码的 "sides" 都被覆盖了。请记住,在字节码中,同一个 catch 块可以被多个 try 块使用,因此您可以将原始 try 块拆分为两个,并且都引用原始 catch 块。

否则,您将不得不想出一些方法来适当地管理寄存器来避免这个问题。

至于 6),请参阅 MethodImplementation.getTryBlocks(),它将为您提供该方法中的 try 块列表。每个 try 块指定它从哪里开始,它覆盖了多少指令,以及与之关联的所有 catch 块(不同的 catch 块用于不同的异常)。