-
-
PWN入门-5-大战Full-RELRO
-
发表于: 2024-7-15 20:01 8333
-
GCC编译器允许使用下面的选项将RELRO全部开启.
开启以后检测ELF文件的安全属性可以看到RELRO的属性从Partial RELRO
变成了Full RELRO
。
不过我们现在还对RELRO一无所知,它为什么要存在,又能干什么呢?
首先观察RELRO开启前后的节信息。
对比上面的信息可以发现,当RELRO全部开启时.got.plt
节消失了,要知道该节位于可读可写段内,是为了延迟加载而存在的,当被调用函数位于动态链接库内且尚未被加载时,.plt
节中首表项会发挥作用,因为它里面存储着_dl_runtime_resolve_fxsave
函数的位置,_dl_runtime_resolve_fxsave
函数会对动态链接库中的函数进行解析,然后修改.got.plt
节中表项。.plt
节中其余表项的首条jmp
指令会指向.got.plt
节中对应表项,如果函数尚未被解析,那么.got.plt
节中对应表项存放的就是.plt
节中表项的第二条push
指令,继续运行第三条jmp
指令时,该指令会指向.plt
节中首表项,程序会开始运行_dl_runtime_resolve_fxsave
函数,然后它解析调用动态链接库中的函数并让主程序调用。当函数再次被调用时,.got.plt
节中对应表项存放的就是该函数的地址了,程序会使用该地址直接对函数进行调用。
此时.got.plt
节的消失,是否也标志着延迟绑定的功能不在存在了呢?
添加-z relro -z now
参数编译出来的程序,在其动态链接节中会出现BIND_NOW
标志位。
将程序挂到GDB上后发现,当程序首次调用动态链接库LibC中的printf
函数时,.plt
节会直接指向printf
函数的所在位置,进而直接对函数进行调用。
显然这时候已经没有延迟绑定了。在上面展示的信息中仍可以看到.got.plt
的身影存在,它为什么会存在呢?
通过在_start
函数设置断点可以发现,LD的_start
函数被命中时,got.plt
中内容还尚未被解析,但当主程序的_start函数被命中时,got.plt
中的内容已经变成了printf
函数的地址,因此这里猜测重定位的工作在程序正式运行前就由LD完成了。
对0x555555557fd0
地址设置观察点后,发现该地址上的数据对被elf_dynamic_do_Rela
函数改变(下面展示了完整的调用栈)。
dl_main
函数是LD的主要组成部分,其主要作用是完成动态链接库的解析及管理任务。
在dl_main
函数内部首先判断的是user_entry
,由于LD本身可以作为程序运行并支持通过LD运行程序,所以当发现user_entry
就是LD自身的入口地址时,就说明此时运行的程序是LD,后续会对传给LD的参数进行处理。如果允许的程序不是动态链接器自身,那么LD接下来就会开始对指定程序进行处理。
程序地址0x0000555555555050
与起始地址0x555555554000
的偏移是0x1050,LD地址0x00007ffff7fe5740
与起始地址0x7ffff7fc9000
的偏移是0x1c740,刚好可以和ELF头信息中记录的入口点对应上。
接下来继续将视线转移到程序独立执行时的代码部分,此时代码会对main_map
进行设置,这个变量属于link_map
结构体,用于记录程序的信息,其中main_map
是链表头,用于记录程序本身的信息,而链表上的其他节点则会记录动态链接信息。
下面代码展示了main_map
的创建及设置段头信息的过程。
之后代码会通过rtld_is_main
判断LD是否作为独立的程序运行,如果不是,就会处理主程序中的动态链接信息。
从下面的信息中可以看到,elf_get_dynamic_info
函数的主要作用就是从程序的ELF文件内提取动态链接节信息,用于后续访问。
完成.dynamic
节信息的提取后,就是设置vDSO。
接下来继续设置link_map
的链表,建立主程序和LD间的关系。
紧接着,LD会处理预加载信息。Linux中提供动态链接库预加载机制,使得用户可以自定义动态链接库,当自定义的动态链接库中存在与默认动态链接库中相同的函数时,会优先使用预加载库中的函数。
从下面可以看到检测是否存在预加载库的方式有三种,一是解析配置文件/etc/ld.so.preload
,二是解析LD_PRELOAD
环境变量,三是检测--preload
参数。
处理好预加载信息后,就会通过_dl_map_object_deps
函数将动态链接库加载进入内存,再设置线程信息。进行_dl_map_object_deps
之前,会发现处理程序相关的动态链接库(vDSO、LD除外)都还没有被加载进入内存。
当程序运行的所需信息都完成加载后,接下来要进行的就是重要的重定位操作。
首先见到与延迟绑定相关的就是下面两条语句,它要求程序被分析时必须进行延迟重定位,GCC编译器中可以使用-pg
选项开启分析功能,然后使用gprof
工具对生成的分析信息进行解析。
当分析功能开启时,程序必须进行延迟重定位。重定位的具体操作是由_dl_relocate_object
函数负责的,它会根据dl_lazy
的数值决定是否进行延迟重定位。
进入_dl_relocate_object
函数内观察link_map
可以发现,程序所有需要的动态链接信息均已经是设置还好的,每次重定位元素都是当前指针指向的链表元素。
重定位过程中,首先需要修正的是DT_TEXTREL
,这是因为ELF文件内可能存在使用绝对地址的情况,当LD检测到DT_TEXTREL
标志位时,就会先修正那些编译时确立的绝对地址(编译器无法预知运行时的状况,原本的地址可能是无法使用的)。
这段代码算是历史遗留,因为现在基本是看不到直接使用绝对地址的情况了。
再之后,要进行重定位的就是GOT表,GOT表中使用的地址都是相对地址,所以运行在运行期通过ELF_DYNAMIC_RELOCATE
进行修改。
在这段宏定义中,elf_machine_runtime_setup
适用于判断是否需要延迟加载的,而宏ELF_DYNAMIC_DO_REL
、ELF_DYNAMIC_DO_RELA
是非常相近似的,它们分别对应不同的重定位表,从实际上来讲,这些实际对应的仍是_ELF_DYNAMIC_DO_RELOC
。
从下面的宏中可以看到,ranges
变量定义两项,该变量会记录需要重定位信息,最后一位区分是否进行延迟重定位。该变量会从重定位节中获取数据,元素0中记录重定位信息表.rela.dyn
的信息,元素1记录重定位链接表.rela.plt
的信息。
宏读取完重定位信息后,会调用elf_dynamic_do_Rela
函数正式开始改写数据。
完成重定位之后,就会运行LibC的早期初始化函数以及进行清理信息等等操作,dl_main
负责的启动任务也告一段落。
通过对比查找动态链接库函数的地址0x6347e3701fc8
和程序的起始地址0x6347e36fe000
,可以发现其偏移值为3fc8
,它位于.got
节中,该节又位于可读可写段中。
.plt
节中的push
指令根据函数在.rela.plt
节中的索引值压入栈内。
从上面可以看到,尽管当full reloro
开启时,使得程序不再进行延迟绑定以及.got.plt
节也随之消失,但是动态链接库只是从调用时加变成了运行前加载,.got.plt
节也只是融入到.got
内,实际函数调用时还是先通过.plt
节中的jmp
指令到.got
内查找地址。
.got
既然始终可写,那这开不开full reloro
好像没有意义啊!
经过进一步排查,会发现ELF文件中的确是如此定义,但当运行时就会从5个段变成4个段,增加了一个不可写的段,查询的被调用函数地址也位于该段中。
这个段是什么时候添加上去的呢?
通过在_start
处设置断点,可以发现当LD准备开始运行时,还是遵照ELF文件的约定保持4个段。当主程序准备开始运行时,最后一个段就会分裂出一个不可写的段。
前面说过重定位操作是dl_main
函数中的_dl_relocate_object
操作的,查看代码可以知道,在_dl_relocate_object
函数的最后会检查l_relro_size
变量的数值,如果不为0就调用_dl_protect_relro
函数。
_dl_protect_relro
函数会根据受保护地址的偏移值l->l_relro_addr
重新划分出一个新段,这个新段的属性是只读的。
主程序的l_relro_size
和l_relro_addr
是由dl_main
中的rtld_setup_main_map
函数设置的,该函数内部会遍历ELF的段,其中就包含根据PT_GNU_RELRO
段。
所有只读的重定位信息都会位于PT_GNU_RELRO
段内,PT_GNU_RELRO
段会将可读可写的LOAD
段分配出一段只读空间,内当Full RELRO
开启时,被调用函数的查询地址会被编译器分配到.got
节内,.got
节位于只读段的范围内,但当程序只是Partial RELRO
时,被调用函数的查询地址会被编译器分配到.got.plt
中,.got.plt
节则位于可读可写段的范围内。
RELRO的开启标志着重定位信息是否只读,Partial RELRO
为了延迟绑定而存在,它允许.got.plt
节存在且可读,保障运行时进行重定位可以改写被查找的调用函数地址。Full RELRO
则将全部的重定位信息设置为只读状态,在编译阶段,仍会将待运行时重定位的函数放入可可段内,但会添加BIND_NOW
标志进行标识,在运行阶段,LD会在程序正式运行前将函数重定位好,并从可写段中将重定位信息划分到只读段内,保障重定位信息后续不会被修改。
不管是静态链接的函数还是动态链接的函数,其具体实现都会位于可执行段中,区别在于调用它们的方式会有所差别,静态链接的函数会根据相对地址直接进行调用,而动态链接函数则需要根据PLT指示查找对应函数然后调用。当程序的重定位信息只是部分只读Partial RELRO
时,程序会使用延迟重定位,此时保持函数解析方式或所在地址的.got.plt
节是可写,仍具备掉包动态链接函数的可能性。而当重定位信息变成全部只读Full RELRO
时,函数地址会被解析在.got
节内,就无法对动态链接函数进行掉包了。
首先前面提到过dl_main
函数在初始化时会检查当前是否设置了预加载库,当预加载库存在时,程序会优先使用预加载库中的函数,此时就可以实现动态链接的函数掉包。
当存在程序中存在自定义格式化字符串及设置缓冲区变量内容的机会时,可以将金丝雀及LibC的基地址泄露出来,并在栈上构造Shellcode,此时无需改写重定位信息,也可以借助函数的正常返回逻辑达到利用的效果。
首先让我们回忆一下PWN一路以来都发生过什么。
首先是没有任何保护的程序,所有的利用点都对我们开放,这时候可以肆无忌惮的完成PWN。
当数据执行保护开启后,栈上的Shellcode已经不能运行了,但此时ASLR尚未开启,在内存布局情况已知的情况下,PWN难度仍算太高,我们既可以直接改写.got.plt
,也可以函数的返回逻辑及LibC中可执行代码构造ROP,并调用Shell。
当金丝雀开启时,我们已经无法直接对栈上的数据进行覆盖,这个时候需要在泄露金丝雀数值或改写金丝雀数值上下一番功夫,才可以构造ROP调用Shell。当然想忽视金丝雀也可以,在程序调用动态链接的函数前提下,对.got.plt
进行改写。
随着ASLR的全面启用,就逼着我们利用格式化字符串漏洞对信息进行泄露,否则就无法成功进行PWN。
当重定位信息变成完全只读后(此时所有保护全部打开),留给我们的路就只剩了借助预加载机制调包动态链接的函数,以及利用格式化字符串漏洞泄露信息后在进行PWN。
成功PWN的道路眼见着越来越窄,可以利用的点也只剩下了零星的几个,在真实环境中,这些苛刻的利用条件真能全部满足吗?此时如同来到了悬崖一般,前方似乎看不到前进的道路。
难道这就是终点吗?没有其他更加高级的突破方式了吗?
在示例程序的反汇编结果内可以发现,程序会调用LibC中的printf
函数,因此我们可以自定义一个printf
函数,并进行预加载,掉包LibC中的printf
函数,示例动态链接库如下所示。
前面分析dl_main
函数时,得到想要预加载机制运行有三种途径,一是借助LD提供的--preload
参数,二是借助环境遍历LD_PRELOAD
,三是借助配置文件/etc/ld.so.preload
,下面会对1、2两种方式进行演示。
借助LD的--preload
参数:
借助环境变量LD_PRELOAD
:
使用环境变量时需要注意它的有效范围,通过export
声明的环境变量只对当前Shell有效。比如上面的示例,只要新打开一个Shell,再运行./example
就会发现它是会调用正确的printf
函数。
将export
声明的环境变量放入用户目录下~
的.bashrc
和.bash_profile
时,也只能对对应用户的Shell生效,这是因为用户每打开一个Shell都会运行上面的文件,这样对应用户的Shell环境变量就一定会声明。Linux下通常会都会使用用户目录下~
区分不同用户的配置,它都会作为隐藏目录.xxx
存在,比如VIM的编辑信息.viminfo
,用户登录时的自动启动程序.config/autostart/
,以及设置信息.config/dconf/user
(设置程序的信息会以数据库的形式保存)等等。
想要声明的环境变量永久生效时,则应该借助Linux通用的配置文件目录/etc
中的profile
文件,这样因为/etc
下面设置是对整个系统生效的。比如上方提到的自动启动程序,也可也通过/etc/xdg/autostart
目录设置所有用户都需要的自动启动程序。
通过将PWN入门-4-大闹ASLR
中的示例程序添加-z relro -z now
的链接选项,再次运行exploit,就会发现仍然是可以正常获取到Shell的。
这是当然的,毕竟原本也没有去改写.got.plt
表,而是借助函数的返回逻辑构造ROP,运行栈上的Shellcode。
关于如何利用格式化字符串漏洞进行绕过的方式,PWN入门-4-大闹ASLR
中已经进行过描述,在此就不过多赘述了。
-
z relro
-
z now
-
z relro
-
z now
Arch: amd64
-
64
-
little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Arch: amd64
-
64
-
little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RELRO全部开启:
Section to Segment mapping:
Segment Sections...
00
01
.interp
02
.interp .note.gnu.
property
.note.gnu.build
-
id
.note.ABI
-
tag .gnu.
hash
.dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03
.init .plt .text .fini
04
.rodata .eh_frame_hdr .eh_frame
05
.init_array .fini_array .dynamic .got .data .bss
06
.dynamic
07
.note.gnu.
property
08
.note.gnu.build
-
id
.note.ABI
-
tag
09
.note.gnu.
property
10
.eh_frame_hdr
11
12
.init_array .fini_array .dynamic .got
RELRO部分开启:
Section to Segment mapping:
Segment Sections...
00
01
.interp
02
.interp .note.gnu.
property
.note.gnu.build
-
id
.note.ABI
-
tag .gnu.
hash
.dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03
.init .plt .text .fini
04
.rodata .eh_frame_hdr .eh_frame
05
.init_array .fini_array .dynamic .got .got.plt .data .bss
06
.dynamic
07
.note.gnu.
property
08
.note.gnu.build
-
id
.note.ABI
-
tag
09
.note.gnu.
property
10
.eh_frame_hdr
11
12
.init_array .fini_array .dynamic .got
RELRO全部开启:
Section to Segment mapping:
Segment Sections...
00
01
.interp
02
.interp .note.gnu.
property
.note.gnu.build
-
id
.note.ABI
-
tag .gnu.
hash
.dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03
.init .plt .text .fini
04
.rodata .eh_frame_hdr .eh_frame
05
.init_array .fini_array .dynamic .got .data .bss
06
.dynamic
07
.note.gnu.
property
08
.note.gnu.build
-
id
.note.ABI
-
tag
09
.note.gnu.
property
10
.eh_frame_hdr
11
12
.init_array .fini_array .dynamic .got
RELRO部分开启:
Section to Segment mapping:
Segment Sections...
00
01
.interp
02
.interp .note.gnu.
property
.note.gnu.build
-
id
.note.ABI
-
tag .gnu.
hash
.dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03
.init .plt .text .fini
04
.rodata .eh_frame_hdr .eh_frame
05
.init_array .fini_array .dynamic .got .got.plt .data .bss
06
.dynamic
07
.note.gnu.
property
08
.note.gnu.build
-
id
.note.ABI
-
tag
09
.note.gnu.
property
10
.eh_frame_hdr
11
12
.init_array .fini_array .dynamic .got
Dynamic section at offset
0x2dc0
contains
27
entries:
0x000000000000001e
(FLAGS) BIND_NOW
Dynamic section at offset
0x2dc0
contains
27
entries:
0x000000000000001e
(FLAGS) BIND_NOW
1
: x
/
i $rip
=
>
0x5555555551ad
<vuln
+
100
>: call
0x555555555040
<printf@plt>
(gdb)
0x0000555555555040
in
printf@plt ()
1
: x
/
i $rip
=
>
0x555555555040
<printf@plt>:
jmp
*
0x2f8a
(
%
rip)
# 0x555555557fd0 <printf@got.plt>
(gdb)
__printf (
format
=
0x555555556008
"hello, I want to bypass full-relro %s\n"
)
at printf.c:
28
1
: x
/
i $rip
=
>
0x7ffff7e09c40
<__printf>: endbr64
1
: x
/
i $rip
=
>
0x5555555551ad
<vuln
+
100
>: call
0x555555555040
<printf@plt>
(gdb)
0x0000555555555040
in
printf@plt ()
1
: x
/
i $rip
=
>
0x555555555040
<printf@plt>:
jmp
*
0x2f8a
(
%
rip)
# 0x555555557fd0 <printf@got.plt>
(gdb)
__printf (
format
=
0x555555556008
"hello, I want to bypass full-relro %s\n"
)
at printf.c:
28
1
: x
/
i $rip
=
>
0x7ffff7e09c40
<__printf>: endbr64
0x00005555555551ad
<
+
100
>: call
0x555555555040
<printf@plt>
x
/
i
0x555555555040
0x555555555040
<printf@plt>: jmp
*
0x2f8a
(
%
rip)
# 0x555555557fd0 <printf@got.plt>
ld的_start函数被命中时plt的内容
(gdb) x
/
10wx
0x555555557fd0
0x555555557fd0
<printf@got.plt>:
0x00001046
0x00000000
0x00000000
0x00000000
0x555555557fe0
:
0x00000000
0x00000000
0x00000000
0x00000000
0x555555557ff0
:
0x00000000
0x00000000
主程序的_start函数被命中时plt的内容
(gdb) x
/
10wx
0x555555557fd0
0x555555557fd0
<printf@got.plt>:
0xf7e09c40
0x00007fff
0xf7dd7cc0
0x00007fff
0x555555557fe0
:
0x00000000
0x00000000
0x00000000
0x00000000
0x555555557ff0
:
0x00000000
0x00000000
0x00005555555551ad
<
+
100
>: call
0x555555555040
<printf@plt>
x
/
i
0x555555555040
0x555555555040
<printf@plt>: jmp
*
0x2f8a
(
%
rip)
# 0x555555557fd0 <printf@got.plt>
ld的_start函数被命中时plt的内容
(gdb) x
/
10wx
0x555555557fd0
0x555555557fd0
<printf@got.plt>:
0x00001046
0x00000000
0x00000000
0x00000000
0x555555557fe0
:
0x00000000
0x00000000
0x00000000
0x00000000
0x555555557ff0
:
0x00000000
0x00000000
主程序的_start函数被命中时plt的内容
(gdb) x
/
10wx
0x555555557fd0
0x555555557fd0
<printf@got.plt>:
0xf7e09c40
0x00007fff
0xf7dd7cc0
0x00007fff
0x555555557fe0
:
0x00000000
0x00000000
0x00000000
0x00000000
0x555555557ff0
:
0x00000000
0x00000000
(gdb) awatch
*
0x555555557fd0
Old value
=
4166
New value
=
-
136274880
elf_dynamic_do_Rela (
map
=
0x7ffff7ffe2e0
, scope
=
<optimized out>, reladdr
=
<optimized out>, relsize
=
<optimized out>, nrelative
=
<optimized out>, lazy
=
<optimized out>,
skip_ifunc
=
<optimized out>) at ..
/
sysdeps
/
x86_64
/
dl
-
machine.h:
413
(gdb) bt
#0 elf_dynamic_do_Rela (map=0x7ffff7ffe2e0, scope=<optimized out>, reladdr=<optimized out>, relsize=<optimized out>, nrelative=<optimized out>, lazy=<optimized out>,
skip_ifunc
=
<optimized out>) at ..
/
sysdeps
/
x86_64
/
dl
-
machine.h:
413
#1 _dl_relocate_object (l=l@entry=0x7ffff7ffe2e0, scope=<optimized out>, reloc_mode=<optimized out>, consider_profiling=<optimized out>, consider_profiling@entry=0) at dl-reloc.c:301
#2 0x00007ffff7fe8891 in dl_main (phdr=<optimized out>, phnum=<optimized out>, user_entry=<optimized out>, auxv=<optimized out>) at rtld.c:2311
#3 0x00007ffff7fe5146 in _dl_sysdep_start (start_argptr=start_argptr@entry=0x7fffffffe170, dl_main=dl_main@entry=0x7ffff7fe6cc0 <dl_main>) at ../sysdeps/unix/sysv/linux/dl-sysdep.c:140
#4 0x00007ffff7fe69be in _dl_start_final (arg=0x7fffffffe170) at rtld.c:494rr
#5 _dl_start (arg=0x7fffffffe170) at rtld.c:581
#6 0x00007ffff7fe5748 in _start () from /lib64/ld-linux-x86-64.so.2
#7 0x0000000000000001 in ?? ()
#8 0x00007fffffffe468 in ?? ()
#9 0x0000000000000000 in ?? ()
(gdb)
(gdb) awatch
*
0x555555557fd0
Old value
=
4166
New value
=
-
136274880
elf_dynamic_do_Rela (
map
=
0x7ffff7ffe2e0
, scope
=
<optimized out>, reladdr
=
<optimized out>, relsize
=
<optimized out>, nrelative
=
<optimized out>, lazy
=
<optimized out>,
skip_ifunc
=
<optimized out>) at ..
/
sysdeps
/
x86_64
/
dl
-
machine.h:
413
(gdb) bt
#0 elf_dynamic_do_Rela (map=0x7ffff7ffe2e0, scope=<optimized out>, reladdr=<optimized out>, relsize=<optimized out>, nrelative=<optimized out>, lazy=<optimized out>,
skip_ifunc
=
<optimized out>) at ..
/
sysdeps
/
x86_64
/
dl
-
machine.h:
413
#1 _dl_relocate_object (l=l@entry=0x7ffff7ffe2e0, scope=<optimized out>, reloc_mode=<optimized out>, consider_profiling=<optimized out>, consider_profiling@entry=0) at dl-reloc.c:301
#2 0x00007ffff7fe8891 in dl_main (phdr=<optimized out>, phnum=<optimized out>, user_entry=<optimized out>, auxv=<optimized out>) at rtld.c:2311
#3 0x00007ffff7fe5146 in _dl_sysdep_start (start_argptr=start_argptr@entry=0x7fffffffe170, dl_main=dl_main@entry=0x7ffff7fe6cc0 <dl_main>) at ../sysdeps/unix/sysv/linux/dl-sysdep.c:140
#4 0x00007ffff7fe69be in _dl_start_final (arg=0x7fffffffe170) at rtld.c:494rr
#5 _dl_start (arg=0x7fffffffe170) at rtld.c:581
#6 0x00007ffff7fe5748 in _start () from /lib64/ld-linux-x86-64.so.2
#7 0x0000000000000001 in ?? ()
#8 0x00007fffffffe468 in ?? ()
#9 0x0000000000000000 in ?? ()
(gdb)
if
(
*
user_entry
=
=
(ElfW(Addr)) ENTRY_POINT)
if
(
*
user_entry
=
=
(ElfW(Addr)) ENTRY_POINT)
直接运行程序时,user_entry保存的地址是对应程序可执行段中的地址
(gdb) x
/
gx
0x7fffffffde60
0x7fffffffde60
:
0x0000555555555050
LOAD
0x0000000000001000
0x0000000000001000
0x0000000000001000
0x0000000000026f55
0x0000000000026f55
R E
0x1000
555555554000
-
555555555000
r
-
-
p
00000000
08
:
01
2507248
/
home
/
test
/
labs
/
example
555555555000
-
555555556000
r
-
xp
00001000
08
:
01
2507248
/
home
/
test
/
labs
/
example
使用LD运行程序时,user_entry保存的地址是LD可执行段中的地址
x
/
20x
0x7fffffffde90
0x7fffffffde90
:
0x00007ffff7fe5740
LOAD
0x0000000000001000
0x0000000000001000
0x0000000000001000
0x0000000000026f55
0x0000000000026f55
R E
0x1000
7ffff7fc9000
-
7ffff7fca000
r
-
-
p
00000000
08
:
01
7351295
/
usr
/
lib
/
ld
-
linux
-
x86
-
64.so
.
2
7ffff7fca000
-
7ffff7ff1000
r
-
xp
00001000
08
:
01
7351295
/
usr
/
lib
/
ld
-
linux
-
x86
-
64.so
.
2
直接运行程序时,user_entry保存的地址是对应程序可执行段中的地址
(gdb) x
/
gx
0x7fffffffde60
0x7fffffffde60
:
0x0000555555555050
LOAD
0x0000000000001000
0x0000000000001000
0x0000000000001000
0x0000000000026f55
0x0000000000026f55
R E
0x1000
555555554000
-
555555555000
r
-
-
p
00000000
08
:
01
2507248
/
home
/
test
/
labs
/
example
555555555000
-
555555556000
r
-
xp
00001000
08
:
01
2507248
/
home
/
test
/
labs
/
example
使用LD运行程序时,user_entry保存的地址是LD可执行段中的地址
x
/
20x
0x7fffffffde90
0x7fffffffde90
:
0x00007ffff7fe5740
LOAD
0x0000000000001000
0x0000000000001000
0x0000000000001000
0x0000000000026f55
0x0000000000026f55
R E
0x1000
7ffff7fc9000
-
7ffff7fca000
r
-
-
p
00000000
08
:
01
7351295
/
usr
/
lib
/
ld
-
linux
-
x86
-
64.so
.
2
7ffff7fca000
-
7ffff7ff1000
r
-
xp
00001000
08
:
01
7351295
/
usr
/
lib
/
ld
-
linux
-
x86
-
64.so
.
2
程序中的头信息:
Entry point address:
0x1050
LD中的头信息:
Entry point address:
0x1c740
程序中的头信息:
Entry point address:
0x1050
LD中的头信息:
Entry point address:
0x1c740
main_map
=
_dl_new_object((char
*
)"
", "
", lt_executable, NULL, __RTLD_OPENEXEC, LM_ID_BASE);
assert
(main_map !
=
NULL);
main_map
-
>l_phdr
=
phdr;
段头
main_map
-
>l_phnum
=
phnum;
段头数量
main_map
-
>l_entry
=
*
user_entry;
入口地址
_dl_add_to_namespace_list(main_map, LM_ID_BASE);
将main_map添加到全局链表内
main_map
=
_dl_new_object((char
*
)"
", "
", lt_executable, NULL, __RTLD_OPENEXEC, LM_ID_BASE);
assert
(main_map !
=
NULL);
main_map
-
>l_phdr
=
phdr;
段头
main_map
-
>l_phnum
=
phnum;
段头数量
main_map
-
>l_entry
=
*
user_entry;
入口地址
_dl_add_to_namespace_list(main_map, LM_ID_BASE);
将main_map添加到全局链表内
if
(!rtld_is_main)
{
elf_get_dynamic_info (main_map, false, false);
if
(main_map
-
>l_info[DT_SONAME] !
=
NULL
&& (strcmp (((const char
*
) D_PTR (main_map, l_info[DT_STRTAB])
+
main_map
-
>l_info[DT_SONAME]
-
>d_un.d_val), LIBC_SO)
=
=
0
))
GL(dl_ns)[LM_ID_BASE].libc_map
=
main_map;
_dl_setup_hash(main_map);
}
if
(!rtld_is_main)
{
elf_get_dynamic_info (main_map, false, false);
if
(main_map
-
>l_info[DT_SONAME] !
=
NULL
&& (strcmp (((const char
*
) D_PTR (main_map, l_info[DT_STRTAB])
+
main_map
-
>l_info[DT_SONAME]
-
>d_un.d_val), LIBC_SO)
=
=
0
))
GL(dl_ns)[LM_ID_BASE].libc_map
=
main_map;
_dl_setup_hash(main_map);
}
elf_get_dynamic_info中枚举的动态链接信息:
{d_tag
=
1
, d_un
=
{d_val
=
58
, d_ptr
=
58
}}
{d_tag
=
12
, d_un
=
{d_val
=
4096
, d_ptr
=
4096
}}
{d_tag
=
13
, d_un
=
{d_val
=
4572
, d_ptr
=
4572
}}
{d_tag
=
25
, d_un
=
{d_val
=
15792
, d_ptr
=
15792
}}
......
ELF文件中读取的动态链接信息:
Dynamic section at offset
0x2dc0
contains
27
entries:
Tag
Type
Name
/
Value
0x0000000000000001
(NEEDED) Shared library: [libc.so.
6
]
0x000000000000000c
(INIT)
0x1000
0x000000000000000d
(FINI)
0x11dc
0x0000000000000019
(INIT_ARRAY)
0x3db0
0x000000000000001b
(INIT_ARRAYSZ)
8
(bytes)
0x000000000000001a
(FINI_ARRAY)
0x3db8
0x000000000000001c
(FINI_ARRAYSZ)
8
(bytes)
0x000000006ffffef5
(GNU_HASH)
0x3c0
0x0000000000000005
(STRTAB)
0x4a0
0x0000000000000006
(SYMTAB)
0x3e0
0x000000000000000a
(STRSZ)
170
(bytes)
0x000000000000000b
(SYMENT)
24
(bytes)
0x0000000000000015
(DEBUG)
0x0
0x0000000000000003
(PLTGOT)
0x3fb0
0x0000000000000002
(PLTRELSZ)
48
(bytes)
0x0000000000000014
(PLTREL) RELA
0x0000000000000017
(JMPREL)
0x660
0x0000000000000007
(RELA)
0x5a0
0x0000000000000008
(RELASZ)
192
(bytes)
0x0000000000000009
(RELAENT)
24
(bytes)
0x000000000000001e
(FLAGS) BIND_NOW
0x000000006ffffffb
(FLAGS_1) Flags: NOW PIE
0x000000006ffffffe
(VERNEED)
0x560
0x000000006fffffff
(VERNEEDNUM)
1
0x000000006ffffff0
(VERSYM)
0x54a
0x000000006ffffff9
(RELACOUNT)
3
0x0000000000000000
(NULL)
0x0
elf_get_dynamic_info中枚举的动态链接信息:
{d_tag
=
1
, d_un
=
{d_val
=
58
, d_ptr
=
58
}}
{d_tag
=
12
, d_un
=
{d_val
=
4096
, d_ptr
=
4096
}}
{d_tag
=
13
, d_un
=
{d_val
=
4572
, d_ptr
=
4572
}}
{d_tag
=
25
, d_un
=
{d_val
=
15792
, d_ptr
=
15792
}}
......
ELF文件中读取的动态链接信息:
Dynamic section at offset
0x2dc0
contains
27
entries:
Tag
Type
Name
/
Value
0x0000000000000001
(NEEDED) Shared library: [libc.so.
6
]
0x000000000000000c
(INIT)
0x1000
0x000000000000000d
(FINI)
0x11dc
0x0000000000000019
(INIT_ARRAY)
0x3db0
0x000000000000001b
(INIT_ARRAYSZ)
8
(bytes)
0x000000000000001a
(FINI_ARRAY)
0x3db8
0x000000000000001c
(FINI_ARRAYSZ)
8
(bytes)
0x000000006ffffef5
(GNU_HASH)
0x3c0
0x0000000000000005
(STRTAB)
0x4a0
0x0000000000000006
(SYMTAB)
0x3e0
0x000000000000000a
(STRSZ)
170
(bytes)
0x000000000000000b
(SYMENT)
24
(bytes)
0x0000000000000015
(DEBUG)
0x0
0x0000000000000003
(PLTGOT)
0x3fb0
0x0000000000000002
(PLTRELSZ)
48
(bytes)
0x0000000000000014
(PLTREL) RELA
0x0000000000000017
(JMPREL)
0x660
0x0000000000000007
(RELA)
0x5a0
0x0000000000000008
(RELASZ)
192
(bytes)
0x0000000000000009
(RELAENT)
24
(bytes)
0x000000000000001e
(FLAGS) BIND_NOW
0x000000006ffffffb
(FLAGS_1) Flags: NOW PIE
0x000000006ffffffe
(VERNEED)
0x560
0x000000006fffffff
(VERNEEDNUM)
1
0x000000006ffffff0
(VERSYM)
0x54a
0x000000006ffffff9
(RELACOUNT)
3
0x0000000000000000
(NULL)
0x0
setup_vdso (main_map, &first_preload);
setup_vdso_pointers ();
call_init_paths (&state);
setup_vdso (main_map, &first_preload);
setup_vdso_pointers ();
call_init_paths (&state);
main_map
-
>l_next
=
&GL(dl_rtld_map);
GL(dl_rtld_map).l_prev
=
main_map;
+
+
GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
+
+
GL(dl_load_adds);
main_map
-
>l_next
=
&GL(dl_rtld_map);
GL(dl_rtld_map).l_prev
=
main_map;
+
+
GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
+
+
GL(dl_load_adds);
assert
(
*
first_preload
=
=
NULL);
检测是否存在预加载库
if
(__glibc_unlikely (state.preloadlist !
=
NULL))
......
if
(__glibc_unlikely (state.preloadarg !
=
NULL))
......
检测指定文件内,是否存在预加载库信息
static const char preload_file[]
=
"/etc/ld.so.preload"
;
if
(__glibc_unlikely (__access (preload_file, R_OK)
=
=
0
))
......
if
(__glibc_unlikely (
*
first_preload !
=
NULL))
......
assert
(
*
first_preload
=
=
NULL);
检测是否存在预加载库
if
(__glibc_unlikely (state.preloadlist !
=
NULL))
......
if
(__glibc_unlikely (state.preloadarg !
=
NULL))
......
检测指定文件内,是否存在预加载库信息
static const char preload_file[]
=
"/etc/ld.so.preload"
;
if
(__glibc_unlikely (__access (preload_file, R_OK)
=
=
0
))
......
if
(__glibc_unlikely (
*
first_preload !
=
NULL))
......
int
consider_profiling
=
GLRO(dl_profile) !
=
NULL;
GLRO(dl_lazy) |
=
consider_profiling;
int
consider_profiling
=
GLRO(dl_profile) !
=
NULL;
GLRO(dl_lazy) |
=
consider_profiling;
if
(GL(dl_ns)[LM_ID_BASE].libc_map !
=
NULL)
_dl_relocate_object (GL(dl_ns)[LM_ID_BASE].libc_map,
GL(dl_ns)[LM_ID_BASE].libc_map
-
>l_scope,
GLRO(dl_lazy) ? RTLD_LAZY :
0
, consider_profiling);
if
(GL(dl_ns)[LM_ID_BASE].libc_map !
=
NULL)
_dl_relocate_object (GL(dl_ns)[LM_ID_BASE].libc_map,
GL(dl_ns)[LM_ID_BASE].libc_map
-
>l_scope,
GLRO(dl_lazy) ? RTLD_LAZY :
0
, consider_profiling);
(gdb) p
*
l
$
2
=
{l_addr
=
140737351720960
, l_name
=
0x7ffff7ffef50
"/usr/lib/libc.so.6"
,
l_ld
=
0x7ffff7f93960
, l_next
=
0x7ffff7ffdab0
<_rtld_global
+
2736
>,
......
}
(gdb) p
*
l
-
>l_next
$
3
=
{l_addr
=
140737353912320
,
l_name
=
0x555555554318
"/lib64/ld-linux-x86-64.so.2"
, l_ld
=
0x7ffff7ffce68
,
l_next
=
0x0
, l_prev
=
0x7ffff7f9e000
,
l_real
=
0x7ffff7ffdab0
<_rtld_global
+
2736
>,
......
}
(gdb) p
*
l
-
>l_prev
$
5
=
{l_addr
=
140737353904128
, l_name
=
0x7ffff7fc7371
"linux-vdso.so.1"
,
l_ld
=
0x7ffff7fc73e0
, l_next
=
0x7ffff7f9e000
, l_prev
=
0x7ffff7ffe2e0
,
......
}
(gdb) p
*
l
-
>l_prev
-
>l_prev
$
8
=
{l_addr
=
93824992231424
, l_name
=
0x7ffff7ffe8b8
"", l_ld
=
0x555555557dc0
,
l_next
=
0x7ffff7ffe8c0
, l_prev
=
0x0
, l_real
=
0x7ffff7ffe2e0
, l_ns
=
0
,
......
}
(gdb) p
*
l
$
2
=
{l_addr
=
140737351720960
, l_name
=
0x7ffff7ffef50
"/usr/lib/libc.so.6"
,
l_ld
=
0x7ffff7f93960
, l_next
=
0x7ffff7ffdab0
<_rtld_global
+
2736
>,
......
}
(gdb) p
*
l
-
>l_next
$
3
=
{l_addr
=
140737353912320
,
l_name
=
0x555555554318
"/lib64/ld-linux-x86-64.so.2"
, l_ld
=
0x7ffff7ffce68
,
l_next
=
0x0
, l_prev
=
0x7ffff7f9e000
,
l_real
=
0x7ffff7ffdab0
<_rtld_global
+
2736
>,
......
}
(gdb) p
*
l
-
>l_prev
$
5
=
{l_addr
=
140737353904128
, l_name
=
0x7ffff7fc7371
"linux-vdso.so.1"
,
l_ld
=
0x7ffff7fc73e0
, l_next
=
0x7ffff7f9e000
, l_prev
=
0x7ffff7ffe2e0
,
......
}
(gdb) p
*
l
-
>l_prev
-
>l_prev
$
8
=
{l_addr
=
93824992231424
, l_name
=
0x7ffff7ffe8b8
"", l_ld
=
0x555555557dc0
,
l_next
=
0x7ffff7ffe8c0
, l_prev
=
0x0
, l_real
=
0x7ffff7ffe2e0
, l_ns
=
0
,
......
}
if
(__glibc_unlikely (l
-
>l_info[DT_TEXTREL] !
=
NULL))
......
if
(__glibc_unlikely (l
-
>l_info[DT_TEXTREL] !
=
NULL))
......
# define ELF_DYNAMIC_RELOCATE(map, scope, lazy, consider_profile, skip_ifunc) \
do { \
int
edr_lazy
=
elf_machine_runtime_setup ((
map
), (scope), (lazy), \
(consider_profile)); \
if
(((
map
) !
=
&GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP)) \
ELF_DYNAMIC_DO_RELR (
map
); \
ELF_DYNAMIC_DO_REL ((
map
), (scope), edr_lazy, skip_ifunc); \
ELF_DYNAMIC_DO_RELA ((
map
), (scope), edr_lazy, skip_ifunc); \
ELF_DYNAMIC_AFTER_RELOC ((
map
), (edr_lazy)); \
}
while
(
0
)
ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
# define ELF_DYNAMIC_RELOCATE(map, scope, lazy, consider_profile, skip_ifunc) \
do { \
int
edr_lazy
=
elf_machine_runtime_setup ((
map
), (scope), (lazy), \
(consider_profile)); \
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!