什么时候在程序中指定共享对象的地址?在链接时?加载时?如果我想在程序的libc中找到system命令的内存地址,我可以在gdb中很容易地找到它,但是如果我不想把程序带到调试器中呢?这个地址会随着运行而改变吗?是否有其他静态分析工具允许查看库或函数在运行时加载到程序内存空间的位置?我希望在程序之外获得此信息(即使用objdump等实用程序收集信息)
libc
system
gdb
objdump
tmb3ates1#
库由ld.so(动态链接器或运行时链接器,在Linux中称为rtld、ld-linux.so.2或ld-linux.so.*)加载; glibc的一部分)。它被声明为“解释器”(INTERP; .interp部分)。因此,当您启动程序时,Linux将启动一个ld.so(加载到内存中并跳转到其入口点),然后ld.so将您的程序加载到内存中,准备它,然后运行它。
ld.so
ld-linux.so.2
ld-linux.so.*
.interp
/lib/ld-linux.so.2 ./your_program your_prog_params
ld.so执行所有需要的ELF文件的实际open和mmap,包括程序的ELF文件和所有需要的库的ELF文件。此外,它填充GOT和PLT表,并执行重定位解析(它将函数的地址从库写入调用点,在许多情况下使用间接调用)。使用ldd实用程序可以获得某个库的典型加载地址。它实际上是一个bash脚本,它设置了一个调试环境变量ld.so(在glibc的rtld中实际上是LD_TRACE_LOADED_OBJECTS=1)并启动一个程序。您甚至可以自己完成,而不需要脚本,例如使用bash轻松更改单次运行的环境变量:
open
mmap
ldd
LD_TRACE_LOADED_OBJECTS=1
LD_TRACE_LOADED_OBJECTS=1 /bin/echo
ld.so将看到这个变量,并将解析所有需要的库和打印它们的加载地址。(不确定程序或库的静态构造函数)。如果ASLR feature被禁用,加载地址大多数时候都是相同的。现代Linux经常启用ASLR,所以要禁用它,使用echo 0 | sudo tee /proc/sys/kernel/randomize_va_space。你可以用binutils的nm实用程序在libc.so中找到system函数的偏移量。我认为,你应该使用nm -D /lib/libc.so或objdump -T /lib/libc.so和grep输出。
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
nm
libc.so
nm -D /lib/libc.so
objdump -T /lib/libc.so
qlvxas9a2#
“直奔源头问马......”Drepper - How To Write Shared LibrariesLinux库编写者必读的文档。详细解释了加载的机制。
wgmfuz8q3#
如果你只需要函数的地址而不需要硬编码函数名,你可以dlopen()主程序:
dlopen()
void *self = dlopen(NULL, RTLD_NOW); dlsym(self, "system"); // returns the pointer to the system() function
如果只需要在编译时知道名称的函数的地址,只需使用void *addr = &system;
void *addr = &system;
pod7payv4#
在libc.so上使用的nm命令将显示system符号在libc.so中的位置。但是,如果启用了ASLR,则加载地址libc.so,因此每次运行程序时,system的最终地址将随机变化。即使没有ASLR,您需要确定libc.so加载的地址,并将system的地址偏移该值。
vvppvyoh5#
我建议您的环境具有LD_LIBRARY_PATH路径。这定义了在哪里可以找到共享库。您可能还需要查看/etc/ld.so.conf。查看此帖子http://www.google.com/url?sa=t&source=web&cd=3&ved=0CCQQFjAC&url=http%3A%2F%2Fubuntuforums.org%2Fshowthread.php%3Ft%3D324660&ei=KqJpTey7JofEsAPE9_imBA&usg=AFQjCNEIbGGrTHp4fufRuj4Yfc58RTHcag&sig2=_9tdlyadMbPc-FcOdCko-w
5条答案
按热度按时间tmb3ates1#
库由
ld.so
(动态链接器或运行时链接器,在Linux中称为rtld、ld-linux.so.2
或ld-linux.so.*
)加载; glibc的一部分)。它被声明为“解释器”(INTERP;.interp
部分)。因此,当您启动程序时,Linux将启动一个ld.so
(加载到内存中并跳转到其入口点),然后ld.so
将您的程序加载到内存中,准备它,然后运行它。ld.so
执行所有需要的ELF文件的实际open
和mmap
,包括程序的ELF文件和所有需要的库的ELF文件。此外,它填充GOT和PLT表,并执行重定位解析(它将函数的地址从库写入调用点,在许多情况下使用间接调用)。使用
ldd
实用程序可以获得某个库的典型加载地址。它实际上是一个bash脚本,它设置了一个调试环境变量ld.so(在glibc的rtld中实际上是LD_TRACE_LOADED_OBJECTS=1
)并启动一个程序。您甚至可以自己完成,而不需要脚本,例如使用bash轻松更改单次运行的环境变量:ld.so
将看到这个变量,并将解析所有需要的库和打印它们的加载地址。(不确定程序或库的静态构造函数)。如果ASLR feature被禁用,加载地址大多数时候都是相同的。现代Linux经常启用ASLR,所以要禁用它,使用echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
。你可以用binutils的
nm
实用程序在libc.so
中找到system
函数的偏移量。我认为,你应该使用nm -D /lib/libc.so
或objdump -T /lib/libc.so
和grep输出。qlvxas9a2#
“直奔源头问马......”
Drepper - How To Write Shared Libraries
Linux库编写者必读的文档。详细解释了加载的机制。
wgmfuz8q3#
如果你只需要函数的地址而不需要硬编码函数名,你可以
dlopen()
主程序:如果只需要在编译时知道名称的函数的地址,只需使用
void *addr = &system;
pod7payv4#
在
libc.so
上使用的nm
命令将显示system
符号在libc.so
中的位置。但是,如果启用了ASLR,则加载地址libc.so
,因此每次运行程序时,system
的最终地址将随机变化。即使没有ASLR,您需要确定libc.so
加载的地址,并将system
的地址偏移该值。vvppvyoh5#
我建议您的环境具有LD_LIBRARY_PATH路径。这定义了在哪里可以找到共享库。您可能还需要查看/etc/ld.so.conf。查看此帖子http://www.google.com/url?sa=t&source=web&cd=3&ved=0CCQQFjAC&url=http%3A%2F%2Fubuntuforums.org%2Fshowthread.php%3Ft%3D324660&ei=KqJpTey7JofEsAPE9_imBA&usg=AFQjCNEIbGGrTHp4fufRuj4Yfc58RTHcag&sig2=_9tdlyadMbPc-FcOdCko-w