这个程序如何知道存储这个字符串的确切位置?
How does this program know the exact location where this string is stored?
我用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"
的位置,因为如果我在调试模式 (r2 -d ./exec
) 中重新启动 Radare2,那么 lea rdi, [0x000368ff]
将被 lea rdi, [someMemoryAddress]
.
如果 lea rdi, [0x000368ff]
是文件中硬编码的内容,那么当 运行 时指令如何更改为实际内存地址?
完整的说明是
48 8d 3d f3 56 03 00
这条指令是字面意思
lea rdi, [rip + 0x000356f3]
使用 rip
相对寻址模式。执行指令时,指令指针rip
的值为0x0000120c
,因此rdi
接收到想要的值0x000368ff
。
如果这不是真实地址,则您的程序可能是位置无关的可执行文件 (PIE),需要重新定位。由于地址是使用 rip-relative 寻址模式编码的,因此不需要重定位并且地址是正确的,无论二进制文件加载到哪里。
雷达在骗你,你看到的不是真正的指令,它已经为你简化了。
真正的指令是:
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
我用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"
的位置,因为如果我在调试模式 (r2 -d ./exec
) 中重新启动 Radare2,那么 lea rdi, [0x000368ff]
将被 lea rdi, [someMemoryAddress]
.
如果 lea rdi, [0x000368ff]
是文件中硬编码的内容,那么当 运行 时指令如何更改为实际内存地址?
完整的说明是
48 8d 3d f3 56 03 00
这条指令是字面意思
lea rdi, [rip + 0x000356f3]
使用 rip
相对寻址模式。执行指令时,指令指针rip
的值为0x0000120c
,因此rdi
接收到想要的值0x000368ff
。
如果这不是真实地址,则您的程序可能是位置无关的可执行文件 (PIE),需要重新定位。由于地址是使用 rip-relative 寻址模式编码的,因此不需要重定位并且地址是正确的,无论二进制文件加载到哪里。
雷达在骗你,你看到的不是真正的指令,它已经为你简化了。
真正的指令是:
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