我正在学习精灵二进制文件。我想手动验证程序中编写的代码是否在可执行的内存区域(对于要链接的共享库的代码也一样)。
我有一个简单的程序:
int main() { return 0; }
当我这样做时:
readelf -a myprog
我得到以下内容:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481cc 0001cc 000040 10 A 6 1 4
[ 6] .dynstr STRTAB 0804820c 00020c 000045 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048252 000252 000008 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0804825c 00025c 000020 00 A 6 1 4
[ 9] .rel.dyn REL 0804827c 00027c 000008 08 A 5 0 4
[10] .rel.plt REL 08048284 000284 000010 08 A 5 12 4
[11] .init PROGBITS 08048294 000294 00002e 00 AX 0 0 4
[12] .plt PROGBITS 080482d0 0002d0 000030 04 AX 0 0 16
[13] .text PROGBITS 08048300 000300 00016c 00 AX 0 0 16
[14] .fini PROGBITS 0804846c 00046c 00001a 00 AX 0 0 4
[15] .rodata PROGBITS 08048488 000488 000008 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 08048490 000490 000034 00 A 0 0 4
[17] .eh_frame PROGBITS 080484c4 0004c4 0000c4 00 A 0 0 4
[18] .ctors PROGBITS 08049f14 000f14 000008 00 WA 0 0 4
[19] .dtors PROGBITS 08049f1c 000f1c 000008 00 WA 0 0 4
[20] .jcr PROGBITS 08049f24 000f24 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049f28 000f28 0000c8 08 WA 6 0 4
[22] .got PROGBITS 08049ff0 000ff0 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 08049ff4 000ff4 000014 04 WA 0 0 4
[24] .data PROGBITS 0804a008 001008 000008 00 WA 0 0 4
[25] .bss NOBITS 0804a010 001010 000008 00 WA 0 0 4
[26] .comment PROGBITS 00000000 001010 00002a 01 MS 0 0 1
[27] .shstrtab STRTAB 00000000 00103a 0000fc 00 0 0 1
[28] .symtab SYMTAB 00000000 0015e8 000400 10 29 45 4
[29] .strtab STRTAB 00000000 0019e8 0001ea 00 0 0 1
要检查程序代码是否可执行,我可以看到标有.text
的{{1}}部分。这里的AX
是否可以使X
函数中的代码可执行?
当程序在运行时动态链接时,将加载共享库(例如glibc)的哪个部分?我在网上找到了解释在动态链接的背景下使用GOT,PLT的解释。标记为main()
的唯一部分为X
,init
和plt
(除fini
之外)。共享库是否链接到其中一个部分,以确保在程序开始执行时它们的代码是可执行的?
(如果在回答上述内容时可以指出一些参考文件会很棒)
答案 0 :(得分:2)
X
这里是否可以执行我的main()
函数中的代码?
正确。虽然涉及的不仅仅是main()
函数;还有一些其他代码链接到您的可执行文件中,该代码也包含在本节中。
当程序在运行时动态链接时,将加载共享库(例如glibc)的哪个部分?
他们都不是。共享库是一个单独的ELF对象,它有自己的部分,其中一些是可执行的;它本质上是与一起加载,而不是加载到中。也就是说,内存中生成的图像将包含可执行文件的.text
部分以及它已加载的所有共享库中的许多其他.text
(以及其他类型的)部分。
例如,以下是我的Linux系统上正在运行的/proc/self/maps
进程的/bin/cat
内容。此输出的格式与readelf
向您显示的格式不同,但是一些相似之处应该变得明显:
00400000-0040b000 r-xp 00000000 08:00 32968 /bin/cat 0060a000-0060b000 r--p 0000a000 08:00 32968 /bin/cat 0060b000-0060c000 rw-p 0000b000 08:00 32968 /bin/cat 0242a000-0244b000 rw-p 00000000 00:00 0 [heap] 7fdf299a2000-7fdf29c6b000 r--p 00000000 08:00 949 /usr/lib/locale/locale-archive 7fdf29c6b000-7fdf29e26000 r-xp 00000000 08:00 18508 /lib/x86_64-linux-gnu/libc-2.19.so 7fdf29e26000-7fdf2a025000 ---p 001bb000 08:00 18508 /lib/x86_64-linux-gnu/libc-2.19.so 7fdf2a025000-7fdf2a029000 r--p 001ba000 08:00 18508 /lib/x86_64-linux-gnu/libc-2.19.so 7fdf2a029000-7fdf2a02b000 rw-p 001be000 08:00 18508 /lib/x86_64-linux-gnu/libc-2.19.so 7fdf2a02b000-7fdf2a030000 rw-p 00000000 00:00 0 7fdf2a030000-7fdf2a053000 r-xp 00000000 08:00 18255 /lib/x86_64-linux-gnu/ld-2.19.so 7fdf2a246000-7fdf2a249000 rw-p 00000000 00:00 0 7fdf2a250000-7fdf2a252000 rw-p 00000000 00:00 0 7fdf2a252000-7fdf2a253000 r--p 00022000 08:00 18255 /lib/x86_64-linux-gnu/ld-2.19.so 7fdf2a253000-7fdf2a254000 rw-p 00023000 08:00 18255 /lib/x86_64-linux-gnu/ld-2.19.so 7fdf2a254000-7fdf2a255000 rw-p 00000000 00:00 0 7ffd516aa000-7ffd516cb000 rw-p 00000000 00:00 0 [stack] 7ffd517f9000-7ffd517fb000 r--p 00000000 00:00 0 [vvar] 7ffd517fb000-7ffd517fd000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
特别是,您可以看到从顶部的/bin/cat
加载了三个部分:第一部分是可执行的(r-xp
),第二部分是只读的(r--p
) ,第三个是读写(rw-p
)。此外,还有许多可执行和不可执行的段从libc-2.19.so
以及ld-2.19.so
(动态链接器)映射。
(在这个转储中也出现了一些有些神秘的段,包括[vdso]
和[vsyscall]
。这些段由内核映射到进程中,很难解释;我不会得到进入他们。)