【发布时间】:2022-12-14 11:46:51
【问题描述】:
我在 NASM 中编写了一个简单的 Hello world 程序,然后出于好奇查看使用 objdump -d。程序如下:
BITS 64
SECTION .text
GLOBAL _start
_start:
mov rax, 0x01
mov rdi, 0x00
mov rsi, hello_world
mov rdx, hello_world_len
syscall
mov rax, 0x3C
syscall
SECTION .data
hello_world: db "Hello, world!", 0x0A
hello_world_len: equ $-hello_world
当我检查这个程序时,我发现这个程序的实际实现使用 movabs 和十六进制值 0x402000 代替名称,这是有道理的,除了这肯定意味着它知道 'Hello,世界!'每次程序运行时都会存储在0x402000,并且没有引用“Hello, world!” objdump -d hello_world 输出中的任何位置(我在下面提供的输出)。
我试着重写程序;这次我用 mov rsi, 0x402000 替换了第 8 行的 hello_world 并且程序仍然可以编译并完美运行。
我认为这可能是名称的某种编码,但是更改SECTION .data 中的文本“hello_world”也没有改变结果。
我比任何事情都更困惑 - 它如何在编译时知道地址,为什么它永远不会改变,即使在重新编译时也是如此?
(objdump -d hello_world 的输出)
./hello_world: file format elf64-x86-64
Disassembly of section .text:
0000000000401000 <_start>:
401000: b8 01 00 00 00 mov $0x1,%eax
401005: bf 00 00 00 00 mov $0x0,%edi
40100a: 48 be 00 20 40 00 00 movabs $0x402000,%rsi
401011: 00 00 00
401014: ba 0e 00 00 00 mov $0xe,%edx
401019: 0f 05 syscall
40101b: b8 3c 00 00 00 mov $0x3c,%eax
401020: bf 00 00 00 00 syscall
(如您所见,没有“.data 部分的反汇编”,这让我更加困惑)
【问题讨论】:
-
该字符串在编译时也是已知的。它静态存在于您的可执行文件中。编译器一开始就放在了地址,当然知道地址了! (在 ASLR 或 dylib 环境中这仍然适用,因为全部相对于模块的地址会根据需要移动,编译器会放置一个重定位条目,这样加载器就知道那里有一个地址引用需要修复,但它们之间的相对关系仍然保持不变。)
-
数据段的反汇编是矛盾的,数据段通常不包含可被反汇编的指令。
-
这是虚拟内存,所讨论的内存页面不必物理存在于内存中,它可以根据需要调入和调出,操作系统的内存管理器的工作是决定什么时候在物理内存中保留什么。尝试访问属于物理上不在内存中的页面的地址将使它在那个时间点透明地被内核调入页面。但是对于这么小的程序,很可能整个程序从一开始就在内存中。
-
在用户模式代码中,您通常永远不会看到物理内存地址。这完全被内核抽象掉了。
-
也使用
objdump -s转储数据部分。您应该在预期地址找到该字符串。
标签: pointers assembly x86-64 nasm reverse-engineering