我正在使用dexlib2进行某种dalvik字节码插装。然而,还有一些遗留问题。似乎发生在goto指令和catch块之后的寄存器类型合并,更准确地说是在相应的标签处,以某种方式派生了一个意外的寄存器类型,这反过来又破坏了插入指令的代码。
插入的指令如下所示:
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的寄存器类型,以便导出正确的移动指令,即宽移动、移动或移动对象。但是,当此代码包含在某个try-block中时,插装就会中断。baksmali (baksmali d -b "“--register-info ALL,FULLMERGE --offsets )的输出表明,const-string指令之后的v0类型(即Reference,L/java/lang/String)被视为发生在相应catch-block标签处的合并过程的输入。假设插入代码之前的类型是引用,[I (整型数组)结果类型现在是引用,L/java/lang/Object (这会产生验证错误),尽管最后的move指令恢复了原始寄存器类型。
现在来回答我的问题:
1)这种合并是什么时候真正发生的?
2)为什么合并过程要在const-string指令之后考虑v0的类型?它是否考虑每条指令修改任何寄存器的类型?
3)这个问题是否只与try-catch块有关?
4)在这个问题上,try-catch块有哪些限制?
5)除了为每个代码构造一个自己的方法来不带参数地注入之外,有没有什么解决方案?那么有没有可能使用额外的寄存器来解决这个问题?
6)我可以用dexlib2检测try-catch块并确定它们包含的指令集吗?
7)是否有讨论此问题的注释/文献,例如合并程序和相关技术问题,例如仪器的进一步限制/限制?
我非常感谢在这件事上的任何帮助。提前感谢!
发布于 2019-05-23 02:55:00
在catch块开始合并寄存器时,try块中可能抛出的每条指令都有一个传入边缘。只有某些指令可以抛出-由CAN_THROW操作码标志决定。
在您的特定示例中,const-string指令后的invoke-static指令可能会抛出,因此从该指令之前到catch块的开始处有一个边缘。
如果后退一步,执行可以从try块中可以抛出的任何指令跳到catch块的开头。因此,catch块中的代码必须准备好使寄存器处于与寄存器内容一致的状态,然后才能抛出任何这些指令。
因此,例如,如果有一个可能的“跳转”从try块到catch块,其中寄存器包含一个原始int类型,以及另一个可能的跳转,其中它包含一个对象,则该寄存器被认为是“冲突的”,因为寄存器在代码中可能包含任何一个类型,并且这两种类型彼此不兼容。例如,原语int永远不能传递给需要引用类型的对象,反之亦然。字节码中没有用于静态寄存器类型检查的机制。
一种可能的解决方案可能是在插入插装的地方拆分try块,这样插装本身就不会被try块覆盖,但原始代码的两端都会被覆盖。请记住,在字节码中,相同的catch块可以由多个try块使用,因此您可以将原始try块拆分为两个,并让这两个块都引用原始catch块。
否则,您将不得不找出一些适当管理寄存器的方法来避免这个问题。
至于6),请参见MethodImplementation.getTryBlocks(),它将为您提供该方法中的try块的列表。每个try块指定它从哪里开始,它涵盖了多少指令,以及与它相关的所有catch块(不同的catch块对应不同的异常)。
https://stackoverflow.com/questions/56256265
复制相似问题