【问题标题】:How does this program know the exact location where this string is stored?这个程序如何知道这个字符串存储的确切位置?
【发布时间】:2019-09-01 19:49:36
【问题描述】:

我已经用 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 具有从行lea rdi, [0x000368ff] 传递给它的字符串"%d" 的地址。我假设0x000368ff 是可执行文件中"%d" 的位置,因为如果我在调试模式下重新启动Radare2 (r2 -d ./exec),那么lea rdi, [0x000368ff] 将被lea rdi, [someMemoryAddress] 替换。

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

【问题讨论】:

  • 搬迁地址可能吗?反汇编器显示一些相对地址,调试器显示真实重定位?

标签: c assembly x86-64 memory-address


【解决方案1】:

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,如果我们用hd查看二进制文件中的偏移量0x754

$ 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

【讨论】:

  • 措辞挑剔:LEA 实际上并不是负担。将静态地址放入寄存器只是地址计算。在被调用者取消引用其 arg 之前,不会从字符串加载。
  • 我的意思是“加载”字面意思是“加载有效地址[进入寄存器]”,这就是指令的作用。另外,“静态”是什么意思?地址是在运行时动态计算的。
  • 我的意思是静态分配存储的地址。即 C 调用的静态存储类,而不是堆栈上的本地地址(自动存储),或从某处加载指针。回复:“加载”这个词 - 它很容易产生误导,没有限定词“有效地址”,标准含义是“从内存加载”。我们谈论 x86 ISA 的事实并不意味着“load”在简单的英语句子中使用时不再具有其标准含义,无论是否存在 lea 和 lahf 等助记符使用它从源编写寄存器除了内存。
【解决方案2】:

完整的指令是

48 8d 3d f3 56 03 00

这条指令是字面意思

lea rdi, [rip + 0x000356f3]

使用rip 相对寻址模式。当指令执行时,指令指针rip 的值为0x0000120c,因此rdi 接收到所需的值0x000368ff

如果这不是真实地址,则您的程序可能是一个位置无关的可执行文件 (PIE),它会被重定位。由于地址是使用 rip-relative 寻址模式编码的,因此无论二进制文件加载到哪里,都不需要重定位并且地址是正确的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-03
    • 2017-06-19
    • 1970-01-01
    • 2019-02-06
    • 1970-01-01
    • 2019-10-22
    相关资源
    最近更新 更多