我们知道在Linux中如果程序想要调用其他动态链接库的函数,必须要在程序加载的时候动态链接;在一个程序运行过程中,可能很多函数在程序执行完时都不会用到,比如一些错误处理函数或者一些用户很少用到的功能模块;所以ELF采用一种叫做延迟绑定(Lazy Binding)的做法,基本思想就是当函数第一次被调用的时候才进行绑定(符号查找、重定位等);
而在Linux 中是利用_dl_runtime_resolve(link_map_obj, reloc_index)函数来对动态链接的函数进行重定位的;
首先总的来说一下_dl_runtime_resolve函数如何使程序第一次调用一个函数:
现在我们通过一个实例来解释上面的过程:
任意打开一个ELF程序,这里我用XDCTF2015的pwn200中的strlen函数为例来看Linux中程序如何第一次调用一个函数;
在0x8048588下断点;
然后si进入call strlen@plt;
我们看到程序没有直接转到strlen函数,而是跳转到了_dl_runtime_resolve函数;并且push了两个参数:
刚好是_dl_runtime_resolve(link_map_obj, reloc_index)需要的参数;其中0x804a004就是link_map指针,然后0x10就是reloc_index;
我们来看看如何通过这两个参数找到strlen函数的;
首先找到link_map的地址0xf7ffd940:
然后通过link_map找到.dynamic的地址:
其中第三个地址就是.dynamic的地址,即0x08049f14;
然后通过.dynamic来找到.dynstr、 .dynsym、 .rel.plt的地址:
.dynamic的地址加0x44的位置是.dynstr;
.dynamic的地址加0x4c的位置是.dynsym;
.dynamic的地址加0x84的位置是.rel.plt;
然后用.rel.plt的地址加上参数reloc_index,即0x08048330 + 0x10找到函数的重定位表项Elf32_Rel的指针,记作rel;
这里rel为0x8048340,所以:
然后我们将r_info>>8,即0x00000407>>8 = 4作为.dynsym中的下标;
此时我们来到.dynsym的位置,去找找strlen函数的名字符串偏移;
注意是下标,不是相对与.dynsym地址的偏移,如果是找地址偏移需要下标乘以0x10;
所以这里的name_offset = 0x00000020;
然后用.dynstr的地址加上name_offset,就是这个函数的符号名字符串st_name;
最后在动态链接库查找这个函数的地址,并且把地址赋值给*rel->r_offset,即GOT表就可以了;
现在我们仍然利用这个程序来具体看看ret2_dl_runtime_resolve的利用手法;
事实上,虚拟地址是通过最后一个箭头,即从st_name得来的,只要我们能够修改这个st_name的内容就可以执行任意函数。比如把st_name的内容修改成为"system";
而index_arg即参数n是我们可以控制的,我们需要做的是通过一系列操作。把index_arg可控转化为st_name可控;我们需要在一个可写地址上构造一系列伪结构就可以完成利用或在条件允许的情况下直接修改.dynstr;
所以我们需要在程序中找一段空间start出来,放我们直接构造的fake_dynsym,fake_dynstr和fake_rel_plt等,然后利用栈迁移的手法将栈转移到start;
r_info的计算方法是:
0x8048579 <main+90> call setbuf@plt <0x8048390>
0x804857e <main+95> add esp, 0x10
0x8048581 <main+98> sub esp, 0xc
0x8048584 <main+101> lea eax, [ebp - 0x6c]
0x8048587 <main+104> push eax
► 0x8048588 <main+105> call strlen@plt <0x80483b0>
s: 0xffffbd8c ◂— 'Welcome to XDCTF2015~!\n'
0x804858d <main+110> add esp, 0x10
0x8048590 <main+113> sub esp, 4
0x8048593 <main+116> push eax
0x8048594 <main+117> lea eax, [ebp - 0x6c]
0x8048597 <main+120> push eax
0x80483b0 <strlen@plt> jmp dword ptr [_GLOBAL_OFFSET_TABLE_+20] <0x804a014>
0x80483b6 <strlen@plt+6> push 0x10 //参数n
0x80483bb <strlen@plt+11> jmp 0x8048380
↓
0x8048380 push dword ptr [_GLOBAL_OFFSET_TABLE_+4] <0x804a004>
0x8048386 jmp dword ptr [0x804a008] <0xf7fead80>
↓
0xf7fead80 <_dl_runtime_resolve> push eax
0xf7fead81 <_dl_runtime_resolve+1> push ecx
0xf7fead82 <_dl_runtime_resolve+2> push edx
push 0x10 //参数n
push dword ptr [_GLOBAL_OFFSET_TABLE_+4]<0x804a004>
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2019-4-10 17:51
被钞sir编辑
,原因: