我已经用Radare2反汇编了一个C程序。在此程序中,有许多对scanf的调用,如下所示:

0x000011fe      488d4594       lea rax, [var_6ch]
0x00001202      4889c6         mov rsi, rax
0x00001205      488d3df35603.  lea rdi, [0x000368ff]       ; "%d" ; const char *format
0x0000120c      b800000000     mov eax, 0
0x00001211      e86afeffff     call sym.imp.__isoc99_scanf ; int scanf(const char *format)
0x00001216      8b4594         mov eax, dword [var_6ch]
0x00001219      83f801         cmp eax, 1                  ; rsi ; "ELF\x02\x01\x01"
0x0000121c      740a           je 0x1228

这里scanf具有从"%d"行传递给它的字符串lea rdi, [0x000368ff]的地址。我假设0x000368ff是可执行表文件中"%d"的位置,因为如果以 Debug模式(r2 -d ./exec)重新启动Radare2,则lea rdi, [0x000368ff]将被lea rdi, [someMemoryAddress]替换。

如果lea rdi, [0x000368ff]是文件中的硬编码内容,那么该指令在运行时如何更改为实际的内存地址?

最佳答案

Radare欺骗了您,您所看到的并不是真正的说明,它已为您简化了。

真正的指示是:

0x00001205    488d3df3560300    lea rdi, qword [rip + 0x356f3]
0x0000120c    b800000000        mov eax, 0

这是典型的与位置无关的 lea 。要使用的字符串存储在二进制文件中,偏移量为0x000368ff,但是由于可执行文件与位置无关,因此需要在运行时计算实际地址。由于下一条指令的偏移量为0x0000120c,因此您知道,无论二进制文件在内存中的何处加载,您想要的地址都是rip + (0x000368ff - 0x0000120c) = rip + 0x356f3,即您在上面看到的。

在进行静态分析时,由于Radare不知道内存中二进制文件的基址,因此它仅计算0x0000120c + 0x356f3 = 0x000368ff。这使逆向工程更加容易,但是由于实际的指令是不同的,因此可能会造成混淆。

例如,以下程序:
int main(void) {
    puts("Hello world!");
}

编译后产生:

  6b4:   48 8d 3d 99 00 00 00    lea    rdi,[rip+0x99]
  6bb:   e8 a0 fe ff ff          call   560 <puts@plt>

因此,rip + 0x99 = 0x6bb + 0x99 = 0x754,如果我们用0x754查看二进制文件中的偏移量hd:

$ hd -s 0x754 -n 16 a.out
00000754  48 65 6c 6c 6f 20 77 6f  72 6c 64 21 00 00 00 00  |Hello world!....|
00000764

关于c - 该程序如何知道此字符串的确切存储位置?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57748994/

10-11 15:37