0x80483d1
0x80483d3
0x80483d8
0x80483dd
0x80483e0
0x80483e2
0x80483e4
0x80483e5
...
0x80483ef
end of assembler dump.
(gdb) b * 0x80483d8
breakpoint 1 at 0x80483d8
(gdb) r
starting program: /home/alert7/test
breakpoint 1, 0x80483d8 in main ()
(gdb) disass 0x8048308①⑴
dump of assembler code for function printf:
/****************************************///plt4:
0x8048308
? //此时,got[6]中存在的是0x804830e
0x804830e
0x8048313
? //plt0处存放着调用rtld函数的指令
? //当函数返回时候,把got[6]修改为真正的
? //printf函数地址,然后直接跳到printf函数
? //执行。
该部分为plt的一部分
/****************************************/
end of assembler dump.
(gdb) x 0x8049488
0x8049488 <_global_offset_table_+24>: 0x0804830e
080482c8 <.plt>:②//plt0:
80482c8: ff 35 74 94 04 08 pushl0x8049474//pushl got[1]地址
? //got[1]是一个鉴别信息,是link_map类型的一个指针
80482ce: ff 25 78 94 04 08 jmp*0x8049478//jmp got[2]
? //跳到动态连接器解析函数执行
80482d4: 00 00 add%al,(%eax)
80482d6: 00 00 add%al,(%eax)
80482d8: ff 25 7c 94 04 08 jmp*0x804947c//plt1:
80482de: 68 00 00 00 00push $0x0
80482e3: e9 e0 ff ff ffjmp80482c8 <_init+0x30>
80482e8: ff 25 80 94 04 08 jmp*0x8049480//plt2:
80482ee: 68 08 00 00 00push $0x8
80482f3: e9 d0 ff ff ffjmp80482c8 <_init+0x30>
80482f8: ff 25 84 94 04 08 jmp*0x8049484//plt3:
80482fe: 68 10 00 00 00push $0x10
8048303: e9 c0 ff ff ffjmp80482c8 <_init+0x30>
8048308: ff 25 88 94 04 08 jmp*0x8049488//plt4:
804830e: 68 18 00 00 00push $0x18
8048313: e9 b0 ff ff ffjmp80482c8 <_init+0x30>
(gdb) b * 0x80482c8
breakpoint 2 at 0x80482c8
(gdb) c
continuing.
breakpoint 2, 0x80482c8 in _init ()
(gdb) x/8x 0x8049470
0x8049470 <_global_offset_table_>:0x080494900x40013ed00x4000a9600x400fa550
0x8049480 <_global_offset_table_+16>: 0x080482ee0x400328cc0x0804830e0x00000000
(gdb) x/50x 0x40013ed0 ( * link_map类型)
0x40013ed0: 0x000000000x40010c270x080494900x400143e0
0x40013ee0: 0x000000000x400141000x000000000x08049490
0x40013ef0: 0x080494e00x080494d80x080494a80x080494b0
0x40013f00: 0x080494b80x000000000x000000000x00000000
0x40013f10: 0x080494c00x080494c80x080494980x080494a0
0x40013f20: 0x000000000x000000000x000000000x080494f8
0x40013f30: 0x080495000x080495080x080494e80x080494d0
0x40013f40: 0x000000000x080494f00x000000000x00000000
0x40013f50: 0x000000000x000000000x000000000x00000000
0x40013f60: 0x000000000x000000000x000000000x00000000
(gdb) disass 0x4000a960③
dump of assembler code for function _dl_runtime_resolve:
0x4000a960 <_dl_runtime_resolve>: push %eax
0x4000a961 <_dl_runtime_resolve+1>: push %ecx
0x4000a962 <_dl_runtime_resolve+2>: push %edx
0x4000a963 <_dl_runtime_resolve+3>: mov0x10(%esp,1),%edx
0x4000a967 <_dl_runtime_resolve+7>: mov0xc(%esp,1),%eax
0x4000a96b <_dl_runtime_resolve+11>:call 0x4000a740
? //调用真正的解析函数fixup(),修正got[6],使它指向真正的printf函数地址
0x4000a970 <_dl_runtime_resolve+16>:pop%edx
0x4000a971 <_dl_runtime_resolve+17>:pop%ecx
0x4000a972 <_dl_runtime_resolve+18>:xchg %eax,(%esp,1)
0x4000a975 <_dl_runtime_resolve+21>:ret$0x8//跳到printf函数地址执行
0x4000a978 <_dl_runtime_resolve+24>:nop
0x4000a979 <_dl_runtime_resolve+25>:lea0x0(%esi,1),%esi
end of assembler dump.
(gdb) b * 0x4000a972
breakpoint 4 at 0x4000a972: file dl-runtime.c, line 182.
(gdb) c
continuing.
breakpoint 4, 0x4000a972 in _dl_runtime_resolve () at dl-runtime.c:182
182 in dl-runtime.c
(gdb) i reg $eax $esp
eax0x4006804c 1074167884
esp0xbffffb64 -1073743004
(gdb) b *0x4000a975
breakpoint 5 at 0x4000a975: file dl-runtime.c, line 182.
(gdb) c
continuing.
breakpoint 5, 0x4000a975 in _dl_runtime_resolve () at dl-runtime.c:182
182 in dl-runtime.c
(gdb) si
printf (format=0x1 ) at printf.c:26
26printf.c: no such file or directory.
(gdb) disass④⑵
dump of assembler code for function printf:
0x4006804c
0x4006804d
0x4006804f
0x40068050
0x40068055
0x40068056
0x4006805c
0x4006805f
0x40068060
0x40068063
0x40068069
0x4006806b
0x40068070
0x40068073
0x40068074
end of assembler dump.
(gdb) x/8x 0x8049470
0x8049470 <_global_offset_table_>:0x080494900x40013ed00x4000a9600x400fa550
0x8049480 <_global_offset_table_+16>: 0x080482ee0x400328cc0x4006804c0x00000000
got[6]已经被修正为0x4006804c了
第一次调用printf()的时候需要经过①->②->③->④
以后调用printf()的时候就不需要这么复杂了,只要经过⑴->⑵就可以了
我们来看看到底是如何修正got[6]的,也是就说如何找到要修正的地址的
(以前我在这点理解上发生了一些比较大的误解,误导各位的地方还请包涵:) )
1:
进入plt4的时候 push $0x18 ,该$0x18为printf重定位入口在jmprel section中的偏移量
2:
printf重定位地址为jmprel+$0x18/* elf32_rel * reloc = jmprel + reloc_offset; */
(gdb) x/8x 0x8048278+0x18
0x8048290:0x080494880x000004070x53e589550x000000e8
0x80482a0 <_init+8>:0xc3815b000x000011cf0x001cbb830x74000000
typedef struct {
? elf32_addrr_offset;
? elf32_wordr_info;
? } elf32_rel;
也就是说printf重定位printf_retloc.r_offset=0x08049488;
? printf_retloc.r_info=0x00000407;
再看看0x08049488是什么地方
(gdb) x 0x08049488
0x8049488 <_global_offset_table_+24>: 0x4006804c
也就是got[6]
3:
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
对一个可执行文件 或一个共享目标而言,rel_addr就等于reloc->r_offset
所以rel_addr=0x08049488=got[6];
4:
*reloc_addr = value;
修正了rel_addr也就是got[6]
至于value是如何计算的,请参考下面的源代码
同时r_info又关联着一个符号
elf32_sym * sym = &symtab[ elf32_r_sym (reloc->r_info) ];
sym=0x8048158+0x00000407;
? typedef struct {
? elf32_wordst_name;
? elf32_addrst_value;
? elf32_wordst_size;
? unsigned charst_info;
? unsigned charst_other;
? elf32_halfst_shndx;
? } elf32_sym;
(gdb) x/10x 0x8048158+0x00000407
0x804855f:0x00003a000x000080000x000000000x00006900
0x804856f:0x000080000x000000000x000083000x00008000
0x804857f:0x000000000x0000b700
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.
?this data structure might change in future, if necessary.user-level
?programs must avoid defining objects of this type.*/
★★ glibc中动态解析符号的源代码(glibc 2.1.3的实现)
? .text
? .globl _dl_runtime_resolve
? .type _dl_runtime_resolve, @function
? .align 16
_dl_runtime_resolve:
? pushl %eax# preserve registers otherwise clobbered.
? pushl %ecx
? pushl %edx
? movl 16(%esp), %edx# copy args pushed by plt in register.note
? movl 12(%esp), %eax# that `fixup' takes its parameters in regs.
? call fixup# call resolver.
? popl %edx# get register content back.
? popl %ecx
? xchgl %eax, (%esp)# get %eax contents end store function address.
? ret $8# jump to function address.
static elfw(addr) __attribute__ ((unused))
fixup (
# ifdef elf_machine_runtime_fixup_args
? elf_machine_runtime_fixup_args,
# endif
?struct link_map *l, elfw(word) reloc_offset)
{
? const elfw(sym) *const symtab
? = (const void *) l->l_info[dt_symtab]->d_un.d_ptr;
? const char *strtab = (const void *) l->l_info[dt_strtab]->d_un.d_ptr;
? const pltrel *const reloc/*计算函数重定位人口*/
? = (const void *) (l->l_info[dt_jmprel]->d_un.d_ptr + reloc_offset);
? /*l->l_info[dt_jmprel]->d_un.d_ptr 为jmprel section的地址*/
? const elfw(sym) *sym = &symtab[elfw(r_sym) (reloc->r_info)];/*计算函数symtab入口*/
? void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);/*重定向符号的绝对地址*/
?
? elfw(addr) value;
? /* the use of `alloca' here looks ridiculous but it helps.the goal is
?to prevent the function from being inlined and thus optimized out.
?there is no official way to do this so we use this trick.gcc never
?inlines functions which use `alloca'.*/
? alloca (sizeof (int));
? /* sanity check that we're really looking at a plt relocation.*/
? assert (elfw(r_type)(reloc->r_info) == elf_machine_jmp_slot);/*健壮性检查*/
?/* look up the target symbol.*/
? switch (l->l_info[versymidx (dt_versym)] != null)
? {
? default:
? {
? const elfw(half) *vernum =
? (const void *) l->l_info[versymidx (dt_versym)]->d_un.d_ptr;
? elfw(half) ndx = vernum[elfw(r_sym) (reloc->r_info)];
? const struct r_found_version *version = &l->l_versions[ndx];
? if (version->hash != 0)
? {
? value = _dl_lookup_versioned_symbol(strtab + sym->st_name,
? &sym, l->l_scope, l->l_name,
? version, elf_machine_jmp_slot);
? break;
? }
? }
? case 0:
? value = _dl_lookup_symbol (strtab + sym->st_name, &sym, l->l_scope,
?l->l_name, elf_machine_jmp_slot);
? }
?/*此时value为object装载的基地址*/
? /* currently value contains the base load address of the object
?that defines sym.now add in the symbol offset.*/
? value = (sym ? value + sym->st_value : 0);/*函数的绝对地址*/
? /* and now perhaps the relocation addend.*/
? value = elf_machine_plt_value (l, reloc, value);/*可能还需要一下重定位*/
? /* finally, fix up the plt itself.*/
? elf_machine_fixup_plt (l, reloc, rel_addr, value);/*修正rel_addr,一般来说是got[n]*/
? return value;
}
static inline elf32_addr
elf_machine_plt_value (struct link_map *map, const elf32_rela *reloc,
?elf32_addr value)
{
? return value + reloc->r_addend;
}
/* fixup a plt entry to bounce directly to the function at value.*/
static inline void
elf_machine_fixup_plt (struct link_map *map, const elf32_rel *reloc,
?elf32_addr *reloc_addr, elf32_addr value)
{
? *reloc_addr = value;
}
参考资料:
1.glibc 2.1.3 src
2.<
3.<
4.linux动态链接技术
? http://www.linuxforum.net/forum/showflat.php?cat=&;board=kstudy&number=102793&page=1&view=collapsed&sb=5&o=31&part=
5.p58-0x04by nergal
? << the advanced return-into-lib(c) exploits >>


