MIPS 二进制中的奇怪指令模式 (lw)

逆向工程 拆卸 二元分析 米普
2021-06-18 08:45:22

我正在尝试反转二进制文件,但我无法理解在二进制文件中几乎一半的函数中不断显示的模式。

这是一个特定函数的样子:

在此处输入图片说明

为什么rsl_setNetCfgObj$v1任何地方被加载而不是“使用”(被调用)?我看到下一个操作码设置$a1为 的偏移量$v1,但这些地址没有指向任何有意义的内容。这种模式在我正在分析的许多函数中重复出现,我不确定这是 Binary Ninja 和 IDA Pro 中的一些错误(因为它们都显示相同),还是我遗漏了什么。

如果这不是错误,那么究竟rol_setNetCfgObj应该做什么?

1个回答

与许多 RISC 实现一样,MIPS 指令集使用固定宽度的 32 位指令,而指令只有 16 位用于偏移字段,这意味着您只能使用 16 位常量,从而为您提供 64KB 的寻址空间。但是,MIPS CPU 的实际地址空间是 4GB(32 位地址大小),那么您如何访问所有这些呢?好吧,可以选择使用部分 16 位移动在寄存器中构建 32 位地址,然后使用间接加载/存储来访问它。它通常看起来类似于:

lui  $r1, 0x0123
addi $r1, $r1, 0xabcd

或者

lui  $r1, 0x0123
ori  $r1, $r1, 0xabcd

这两个都加载0x0123abcdr1aka at(请参阅此处)。

但是,这需要在编译(或至少是链接)时了解地址。如果我们的二进制文件需要加载到不同的地址(通常是共享库的情况),则必须重新定位(需要修补指令)。修补需要时间,防止代码页共享并增加内存消耗,这就是为什么位置无关代码 (PIC) 比固定地址更受欢迎。

那么,我们如何独立于加载地址进行地址计算呢?好吧,我们只需要获取我们的执行地址并为其添加一个固定的增量(通常二进制文件作为一个块加载到内存中,因此从它的一部分到另一部分的偏移量是固定的)。MIPS ABI 规定,对于公共函数,$t9函数入口处的值应该等于它的运行时地址,我们可以看到使用这个事实的代码:

109BC: li $gp, 0x830e4
109C4: addu $gp, $gp, $t9

如果我们假设$t9等于0x109BC,我们得到:

gp = 0x830e4 + 0x109BC = 0x93AA0

gp代表“全局指针”,在函数执行期间不应该改变(通常假设它在程序的所有函数中具有相同的值)。编译器可以在涉及程序地址的所有其他计算中使用这一事实。例如,查看突出显示的区域:

lw $v1, -0x7fd4($gp) 

0x93AA0-0x7fd4 = 0x8BACC,显然程序存储0x70000在该地址,该地址用于下一条加载指令$a1

lw $a1, -0x1b4c($v1)

计算它:(-0x1b4c+0x70000=0x6E4B4由反汇编器显示很有帮助)。

因此,$v1(以及v0稍后)这里只是一个用于地址计算的中间变量(通常$at为此目的而保留,但一直重用它可能会导致代码变慢)。

顺便说一下,0x70000低 16 位全为零,这不是偶然的。它可能碰巧指向rsl_setNetCfgObj但它只是一个红鲱鱼。

如果您转到二进制文件的 .got 部分,您通常会看到如下内容:

.got:00515B40      .word 0
.got:00515B44      .word 0x80000000
.got:00515B48      .word 0x510000
.got:00515B4C      .word 0x4D0000
.got:00515B50      .word 0x420000
.got:00515B54      .word 0x4C0000
.got:00515B58      .word 0x520000
.got:00515B5C      .word 0x430000
.got:00515B60      .word 0x440000
.got:00515B64      .word 0x450000
.got:00515B68      .word 0x460000
.got:00515B6C      .word 0x470000
.got:00515B70      .word 0x480000
.got:00515B74      .word 0x490000
.got:00515B78      .word 0x4A0000
.got:00515B7C      .word 0x530000
.got:00515B80      .word 0x4B0000
.got:00515B84      .word 0
.got:00515B88      .word 0
.got:00515B8C      .word 0

这些是所谓的本地GOT 条目,编译器仅将其用于二进制内部的地址计算,而不是作为指向外部符号的指针。编译器在那里分配足够多的不同地址,因此它可以通过 16 位(有符号)偏移量到达本地二进制文件中的任何所需地址。gp本身通常设置为 GOT+ 7FF0,它允许编译器在一条指令中加载任何 GOT 条目(外部符号或本地地址以供进一步计算)(假设 GOT 不超过 64KB)。


因此,总而言之:您在这里看到的不是混淆或反汇编程序错误,而是展示 MIPS 指令集及其调用约定局限性的正常代码


顺便说一句,IDA 知道这些事情,并且默认情况下使用最终地址表示大多数此类引用。例如,来自示例二进制文件:

.text:0042C34C   la      $v0, dword_520000
.text:0042C350   lbu     $v0, (byte_5193CD - 0x520000)($v0)
.text:0042C354   beqz    $v0, loc_42C36C
.text:0042C358   li      $v1, 8
.text:0042C35C   li      $v1, 9
.text:0042C360   la      $v0, dword_520000
.text:0042C364   b       loc_42C380
.text:0042C368   sb      $v1, (sLastPayedFileInfo - 0x520000)($v0)

并关闭简化:

.text:0042C34C   lw      $v0, -0x7FD8($gp)
.text:0042C350   lbu     $v0, (byte_5193CD - 0x520000)($v0)
.text:0042C354   beq     $v0, $zero, loc_42C36C
.text:0042C358   addiu   $v1, $zero, 8
.text:0042C35C   addiu   $v1, $zero, 9
.text:0042C360   lw      $v0, -0x7FD8($gp)
.text:0042C364   beq     $zero, $zero, loc_42C380
.text:0042C368   sb      $v1, (sLastPayedFileInfo - 0x520000)($v0)

虽然你仍然可以看到发生了什么,但我个人更喜欢第一个。

有关 MIPS 的更多信息,我会推荐SweetmanSee MIPS Run手册,以及 MIPS ABI 规范(请参阅此处开始)。或者只是通过指令逐条阅读代码指令并尝试弄清楚它们的作用。