我们已经讨论过 偏移量表达式,这些表达式可以适应单个指令操作数或数据值。但情况并不总是如此,所以让我们看看IDA如何处理可能由多个部分构成的偏移量。
8位处理器
尽管逐渐被淘汰,8位处理器——尤其是经典的8051——仍然可能出现在当前的硬件中,当然我们还将处理遗留系统很多年。即使它们的寄存器只能存储8位数据,但大多数可以寻址16位(64KiB)或更多的内存,这意味着地址可能需要分段构建。
例如,考虑以下来自8051固件的指令序列:
ounter(lineounter(lineounter(lineounter(line
mov R3, #0xFF
mov R2, #0xF6
mov R1, #0xA6
sjmp code_CF36
8051的代码通常使用Keil C51编译器编译,这种模式是初始化通用代码内存指针的典型方式。被引用的地址是0xF6A6
,但我们能否让指令看起来“漂亮”并创建交叉引用呢?
一种可能性是对最后一个移动使用自定义基址偏移量,并指定基址为0xF600
:
这确实计算了最终地址并创建了交叉引用,但代码看起来不太“漂亮”,其他指令仍然是普通数字:
实际上,更好的选择是对这两个指令使用高8/低8偏移量。因为每个指令仅提供完整偏移量的一部分,单独不能被IDA用于计算完整地址,这需要用户提供。
R2提供了地址的高8位,所以我们应该为其使用HIGH8
偏移类型。我们还需要在目标地址字段中填写完整地址(0xF6A6
)。基址应重置为0。
对于R1,可以使用LOW8
和相同的目标:
在应用了这两个偏移量后,IDA使用匹配的汇编操作符显示它们:
RISC处理器
RISC处理器通常使用固定宽度指令,可能无法在指令中有限的立即操作数空间内达到地址空间的全部范围。这包括SPARC、MIPS、PowerPC等。作为例子,让我们看看这个PowerPC VLE代码片段:
ounter(lineounter(lineounter(lineounter(line
e_lis r3, 1 # 加载立即数移位
e_add16i r3, r3, -0x1650 # 0xE9B0
se_mtlr r3
se_blrl
代码在r3
中计算一个函数的地址,然后调用它。IDA在注释中帮助显示了最终地址,但我们也可以使用自定义偏移量来更好地表示它们。对于e_add16i
指令,我们可以使用LOW16
类型,但在e_lis
的情况下,应该使用处理器特定类型HIGHA16
而不是HIGH16
。这是因为低16位在这里不是直接使用,而是作为符号扩展的加数,最终地址的高16位在加法后变为0(0x10000-0x1650=0xE9B0)。
在转换了这两个部分后,IDA使用特殊的汇编操作符显示最终地址:
现在我们可以转到目标并在那里创建一个函数。
注意:特别是对于PowerPC,如果目标地址存在并有指令或数据,IDA会自动将这种序列转换为偏移表达式。但手动方法对于其他处理器或复杂情况(例如,两条指令相距太远)仍然有用。
学习资源
立即关注【二进制磨剑】公众号
原文始发于微信公众号(二进制磨剑):IDA技巧(114)分割偏移量
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论