最近,在学习BCTF和0CTF的writeup时,注意到了一种通过DT_DEBUG来获得库的基址的方式:BCTF里的pattern用这一方法来获得ld-linux.so的地址,0CTF里的sandbox用这一方法来获得sandbox.so的基址。之前面对ASLR,我只知道可以通过GOT来获取libc.so的地址,而其他库的地址还不清楚应该怎样取得。于是,我稍微研究了下,在此记录。

首先,通过readelf -d,可以得到.dynamic的信息。而有些二进制文件里的.dynamic里包含DT_DEBUG:

Dynamic section at offset 0x7c8 contains 20 entries:

Tag Type Name/Value

...

0x0000000000000015 (DEBUG) 0x0

...

这里DT_DEBUG的值是0。在实际运行时,DT_DEBUG的值是指向struct r_debug的指针。其定义如下:

/* Rendezvous structure used by the run-time dynamic linker to communicate

details of shared object loading to the debugger. If the executable's

dynamic section has a DT_DEBUG element, the run-time linker sets that

element's value to the address where this structure can be found. */

struct r_debug

{

int r_version; /* Version number for this protocol. */

struct link_map *r_map; /* Head of the chain of loaded objects. */

/* This is the address of a function internal to the run-time linker,

that will always be called when the linker begins to map in a

library or unmap it, and again when the mapping change is complete.

The debugger can set a breakpoint at this address if it wants to

notice shared object mapping changes. */

ElfW(Addr) r_brk;

enum

{

/* This state value describes the mapping change taking place when

the `r_brk' address is called. */

RT_CONSISTENT, /* Mapping change is complete. */

RT_ADD, /* Beginning to add a new object. */

RT_DELETE /* Beginning to remove an object mapping. */

} r_state;

ElfW(Addr) r_ldbase; /* Base address the linker is loaded at. */

};

可以看到,其第二个元素是指向struct link_map的指针。其定义如下:

/* Structure describing a loaded shared object. The `l_next' and `l_prev'

members form a chain of all the shared objects loaded at startup.

These data structures exist in space used by the run-time dynamic linker;

modifying them may have disastrous results. */

struct link_map

{

/* These first few members are part of the protocol with the debugger.

This is the same format used in SVR4. */

ElfW(Addr) l_addr; /* Difference between the address in the ELF

file and the addresses in memory. */

char *l_name; /* Absolute file name object was found in. */

ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */

struct link_map *l_next, *l_prev; /* Chain of loaded objects. */

};

于是,遍历link_map,对比l_name,找到目标之后,就可以通过l_addr获得那个库的基址。

实例如下,比如说我们要找ld-linux.so的基址。首先,检查.dynamic的内容:

gdb-peda$x/20gx 0x6007c8

0x6007c8: 0x0000000000000001 0x0000000000000010

0x6007d8: 0x000000000000000c 0x0000000000400400

0x6007e8: 0x000000000000000d 0x00000000004006d8

0x6007f8: 0x000000006ffffef5 0x0000000000400260

0x600808: 0x0000000000000005 0x0000000000400310

0x600818: 0x0000000000000006 0x0000000000400280

0x600828: 0x000000000000000a 0x000000000000004b

0x600838: 0x000000000000000b 0x0000000000000018

0x600848: 0x0000000000000015 0x00007f38cd4bd140

0x600858: 0x0000000000000003 0x0000000000600960

DT_DEBUG是0x15,所以0x600848那里就是DT_DEBUG的条目,其值是0x00007f38cd4bd140,即struct r_debug的地址。

gdb-peda$x/6x 0x00007f38cd4bd140

0x7f38cd4bd140 <_r_debug>: 0x0000000000000001 0x00007f38cd4bd168

0x7f38cd4bd150 <_r_debug>: 0x00007f38cd2aba90 0x0000000000000000

0x7f38cd4bd160 <_r_debug>: 0x00007f38cd29c000 0x0000000000000000

对照定义,我们知道第二个元素,0x00007f38cd4bd168是link_map链表的第一个元素的地址。

gdb-peda$x/6x 0x00007f38cd4bd168

0x7f38cd4bd168: 0x0000000000000000 0x00007f38cd2b69fb

0x7f38cd4bd178: 0x00000000006007c8 0x00007f38cd4bd700

0x7f38cd4bd188: 0x0000000000000000 0x00007f38cd4bd168

gdb-peda$x/s 0x00007f38cd2b69fb

0x7f38cd2b69fb: ""

而这里第二个元素l_name是空,不是我们要找的库。于是通过第四个元素l_next,即0x00007f38cd4bd700,来看下一个

gdb-peda$x/6x 0x00007f38cd4bd700

0x7f38cd4bd700: 0x00007fff09fe9000 0x00007f38cd2b69fb

0x7f38cd4bd710: 0x00007fff09fe9318 0x00007f38cd4ba658

0x7f38cd4bd720: 0x00007f38cd4bd168 0x00007f38cd4bd700

gdb-peda$x/s 0x00007f38cd2b69fb

0x7f38cd2b69fb: ""

同理,这里也不是。继续看下一个0x00007f38cd4ba658

gdb-peda$x/6x 0x00007f38cd4ba658

0x7f38cd4ba658: 0x00007f38ccede000 0x00007f38cd4ba640

0x7f38cd4ba668: 0x00007f38cd294b40 0x00007f38cd4bc998

0x7f38cd4ba678: 0x00007f38cd4bd700 0x00007f38cd4ba658

gdb-peda$x/s 0x00007f38cd4ba640

0x7f38cd4ba640: "/lib64/libc.so.6"

这里是libc.so,那么我们继续看下一个0x00007f38cd4bc998

gdb-peda$x/6x 0x00007f38cd4bc998

0x7f38cd4bc998 <_rtld_local>: 0x00007f38cd29c000 0x0000000000400200

0x7f38cd4bc9a8 <_rtld_local>: 0x00007f38cd4bbe10 0x0000000000000000

0x7f38cd4bc9b8 <_rtld_local>: 0x00007f38cd4ba658 0x00007f38cd4bc998

gdb-peda$x/s 0x0000000000400200

0x400200: "/lib64/ld-linux-x86-64.so.2"

OK,这里就是ld-linux.so了。l_addr的值是0x00007f38cd29c000,我这里开了ASLR,而ld-linux.so的基址就正好是0x00007f38cd29c000

gdb-peda$vmmap

Start End Perm Name

0x00400000 0x00401000 r-xp /root/heap.out

0x00600000 0x00601000 rw-p /root/heap.out

0x00007f38ccede000 0x00007f38cd092000 r-xp /usr/lib64/libc-2.18.so

0x00007f38cd092000 0x00007f38cd291000 ---p /usr/lib64/libc-2.18.so

0x00007f38cd291000 0x00007f38cd295000 r--p /usr/lib64/libc-2.18.so

0x00007f38cd295000 0x00007f38cd297000 rw-p /usr/lib64/libc-2.18.so

0x00007f38cd297000 0x00007f38cd29c000 rw-p mapped

0x00007f38cd29c000 0x00007f38cd2bc000 r-xp /usr/lib64/ld-2.18.so

0x00007f38cd4ae000 0x00007f38cd4b1000 rw-p mapped

0x00007f38cd4ba000 0x00007f38cd4bb000 rw-p mapped

0x00007f38cd4bb000 0x00007f38cd4bc000 r--p /usr/lib64/ld-2.18.so

0x00007f38cd4bc000 0x00007f38cd4bd000 rw-p /usr/lib64/ld-2.18.so

0x00007f38cd4bd000 0x00007f38cd4be000 rw-p mapped

0x00007fff09f9f000 0x00007fff09fc0000 rw-p [stack]

0x00007fff09fe7000 0x00007fff09fe9000 r--p [vvar]

0x00007fff09fe9000 0x00007fff09feb000 r-xp [vdso]

0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall]

由此,我们得到了ld-linux.so的地址。同样的道理,我们通过遍历link_map,就可以得到所有库的地址。当然,前提是二进制文件需要有DT_DEBUG。

Edit: 最近研究才意识到,不一定需要从DT_DEBUG去获得link_map的地址。事实上,.got.plt的前3项,分别是.dynamic的地址,link_map的地址和_dl_runtime_resolve的地址。在解析函数地址时,link_map会被作为参数推到栈上传递给_dl_runtime_resolve。关于这一过程及利用方式,详情可见http://rk700.github.io/article/2015/08/09/return-to-dl-resolve

linux下获取模块基址,通过DT_DEBUG来获得各个库的基址相关推荐

  1. linux怎么获取当前路径,linux 下获取当前工作路径的实例

    获取工作路径这里介绍两种方法: 1.使用getcwd()函数. 头文件:#include 定义函数:char * getcwd(char * buf, size_t size); 函数说明:getcw ...

  2. linux下获取某文件的总行数

    为什么80%的码农都做不了架构师?>>>    需求: linux下获取某文件的总行数 实现: 方案一: echo cat logfile.txt | wc -l 方案二: more ...

  3. Linux下获取毫秒级时间差

    Linux下获取毫秒级时间差 使用Linux的gettimeofday函数可以达到这个目的  其中t1=t_start.tv_sec是公元1970年至今的时间(换算为秒)  t2=t_start.tv ...

  4. linux下获取占用CPU资源最多的10个进程,可以使用如下命令组合: ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head linux下

    linux下获取占用CPU资源最多的10个进程,可以使用如下命令组合: ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head linux下获取占用 ...

  5. Linux下获取xml调试信息等级

    Linux下获取XML调试信息等级 #ifndef _LOG_H_ #define _LOG_H_#include <stdio.h> #include <string.h> ...

  6. .net core在Linux下获取AD域信息

    .net core在Linux下获取AD域信息 .net Core 2.1.4 .net core现在System.DirectoryServices只支持Windows平台下使用. 参考: http ...

  7. linux 权限 c,Linux下获取root权限的c程序

    Linux下获取root权限的c程序 传递euid和egid给脚本,使脚本具有特殊用户的权限 使脚本实现类于设置了stick位的效果 shell, python, perl等脚本.程序不能取得suid ...

  8. linux c 获得root权限,Linux下获取root权限的c程序

    Linux下获取root权限的c程序 传递euid和egid给脚本,使脚本具有特殊用户的权限 使脚本实现类似于设置了stick位的效果 shell, python, perl等脚本.程序不能取得sui ...

  9. gsm基于linux程序,基于嵌入式Linux下GSM模块的短信收发系统设计

    摘要:移动通信中的短消息业务以其方便.可靠和价廉得到了广泛应用,本文在嵌入式Linux系统中,通过西门子MC35模块,实现了短信收发系统,该系统采用PDU短信模式,能支持中文短信.程序设计采用异步事件 ...

最新文章

  1. 《编程之美》读书笔记08:2.9 Fibonacci序列
  2. 用宏定义实现函数值互换
  3. 计算机文献检索综合性实验报告,文献检索综合性实验报告模板.doc
  4. javascript --- 原生的拖拽功能实现
  5. iOS端(腾讯Bugly)闪退异常上报扑获日志集成与使用指南
  6. C++之 伪随机数的生成
  7. chroot--实现系统普通用户在限定目录下活动
  8. 西门子 dp通讯测试软件,西门子 PLC DP通讯
  9. IDA Pro使用技巧及大杂烩
  10. python爬虫 爬取视频 练习
  11. 诺禾-蛋白表达纯化之通关技巧
  12. 三星Cortex-A53八核6818核心板
  13. [概率练习] n个小球放入m个盒子(8大类)
  14. linux 编译器制作,Linux交叉編譯器的制作(一)
  15. 快来开建春晚红包信息群吧!
  16. 怎么识别手写的文字?办公常备软件说明
  17. Win7系统网络连接图标显示红叉但可以正常上网怎么办
  18. STARK STARK_LT 复现(2021)
  19. 利用Python进行市场购物篮分析——入门篇
  20. Unity--分场景

热门文章

  1. 集线器(Hub)、交换机(SW)、路由器(router)对比区别
  2. python列表常用方法实践_Python 之列表的常用方法
  3. 计算机英语常用词语,计算机英语 常见计算机英语词汇解释(1)
  4. 无线远距离WiFi模组,CV5200远程实时传输应用,无线mesh自组网方案
  5. python将txt文件转化为矩阵_python 读文件,然后转化为矩阵的实例
  6. Python文件相关函数-----23
  7. anaconda pycharm_python入门必备干货 | python,pycharm,anaconda区别与联系
  8. 云健康实名认证_秒懂云通信:如何用阿里云语音通知
  9. 用python绘制一条红色蟒蛇_实例:用 Python 绘制一套“会跳舞”的动态图形
  10. mips汇编计算开方_全国计算机一级考试模拟题(11)