精通solaris的,来讨论讨论solaris sparc的共享库重定向技术

发表于:2007-06-09来源:作者:点击数: 标签:
SharedLibraryInjectionandRedirection ---|简介 Phrack56-9Backdooringbinaryobjects一文中介绍了利用BFD向SharedLibrary中插入代码,并修改SharedLibrary中的一个函数地址指向插入的代码.但是,插入的代码必须以汇编书写,并且要手工计算一些地址.本文描述如

Shared Library Injection and Redirection 

---| 简介 

Phrack 56-9 Backdooring binary objects 一文中介绍了利用BFD向Shared Library中插入代码,并修改Shared Library中的一个函数地址指向插入的代码. 但是,插入的代码必须以汇编书写,并且要手工计算一些地址. 本文描述如何在SPARC Solaris 8下实现这种技术. 而且进一步做了重定向工作,使插入代码可以用 c 书写. 考虑到BFD接口复杂,文档混乱, 我们不使用BFD做ELF文件操作. 

---| 准备工作 

本文中程序的编译和调试都在如下环境进行: 
bash-2.03$ uname -a 
SunOS LabSolaris 5.8 Generic_108528-09 sun4u sparc SUNW,Ultra-5_10 
bash-2.03$ gcc -v 
Reading specs from /opt/gnu32/lib/gcc-lib/sparc-sun-solaris2.8/3.0/specs 
Configured with: ./configure --prefix=/opt/gnu32 : (reconfigured) ./configure --prefix=/opt/gnu32 --enable-languages=c,c++ 
Thread model: posix 
gcc version 3.0 
bash-2.03$ objdump -V 
GNU objdump 2.11.2 
Copyright 1997, 98, 99, 2000, 2001 Free Software Foundation, Inc. 
This program is free software; you may redistribute it under the terms of the GNU General Public License. This program has absolutely no warranty. 

需要准备如下程序: 
/* haha.c */ 
#include <stdio.h> 
void haha(void) 

printf("haha\n"


/* huhu.c */ 
void huhu(void) 

printf("huhu\n"

编译成动态库: 
bash-2.03$ gcc -fPIC -G -nostdlib -o libtst.so haha.c huhu.c 

再准备如下 c 程序: 
/* hehe.c */ 
void hehe(void) 

haha(); 

编译成 object 文件: 
bash-2.03$ gcc -fpic -c hehe.c 

准备如下测试程序: 
/* t.c */ 
int main(int argc,char **argv) 

huhu(); 
return 0; 


---| SPARC下的ELF 

一个ELF动态库(SPARC)通常包括以下区(Section): 
.hash : hash table 
.interp: ELF interpreter 
.dynsym : dynamic symbol table 
.dynstr : dynamic string table 
.rela.* : relocation section 
.text : code section 
.rodata : readonly data section 
上面几个区共同组成了代码段(text segment),在内存中映象可读,可执行, 
但不可写。而且,代码段被进程共享。 
.got : 全局偏移表(Global Offset Table),是PIC(Position Independent 
Code) 的重要组成部分,.text区中对绝对地址的引用被转换成对.got中偏移 
的引用,dynamic linker修改.got使其指向绝对地址。 
.plt : 程序联接表(Procedure Linkage Table),是PIC的重要组成部分。 
.plt和.got类似,不过是把函数调用转换成对.plt的调用。在SPARC下,每个 
.plt表项为12字节长,也就是说,可以容纳 3 条指令。.plt的前 4 个表项为 
系统保留。dynamic linker会修改plt表项使其指向实际的函数地址。 
.dynamic : 保存动态连接信息 

---| 第一步:插入代码到动态库中 

这一步要实现插入代码到动态库中,同时保证不影响该动态库的使用。第一 
个问题是:应该插入动态库的哪个位置?Phrack 56-9 <<Backdooring binary 
objects>>建议插入.got中,这样容易实现,但也使插入的代码可能被用户修改。 
合理的插入位置应该是.text中。我们采用插入.got中的做法。具体步骤如下: 

1. 从ELF头中找到区头表的偏移和区名区的索引。 
2. 在动态库的区头表中(Section Header Table)中找到.got和紧跟.got的 
区(假设是.dynamic)的区头项。 
3. 修改.got区的大小,加上插入代码的长度(要求是 4 的倍数). 
4. 修改.got后面区的偏移,加上插入代码的长度。 
5. 修改程序头表中相应段的偏移和大小。 
6. 修改ELF头中区头表的偏移,加上插入代码的长度。 
7. 将代码从.dynamic的偏移处插入。 

这样,尽管一些符号的偏移还没有修改,但被修改的动态库依然可以正常 
使用。 

测试(假设修改后的动态库文件名为libnew.so): 
bash-2.03$ gcc -o t t.c -L . -lnew 
bash-2.03$ export LD_LIBRARY_PATH=. 
bash-2.03$ ./t 
huhu 
bash-2.03$ 

---| 第二步:动态库符号重定向 

这一步实现修改原动态库中的的一个函数指向新插入的代码。动态库"export"出的函数都在动态符号表(.dynsym)中描述,包括函数地址,代码长度等信息。修改原函数的地址,大小为新插入代码的地址,大小等属性即可。对有近2000个符号的标准 C 库,通过符号名找到符号比较耗时,这可以通过符号名hash表(.hash区)加快查询。 

通常,只修改动态符号表中的符号就可以完成重定位,保证连接该动态库的 
程序正常运行,虽然这时候符号表中的相应符号地址没有修改。但是,一些程序 
可能依靠符号表中的符号工作,例如gdb,它的disass sym指令就从符号表中符号指向地址处开始反汇编。因此,修改动态符号表中符号后,也应该修改符号表中的相应符号。 

---| 第三步:重定位 

如果插入的代码调用了动态库中的函数,那么这个函数调用会被编译成0x40000000,即call 0,因此必需对call的偏移做重定位。对用-fpic编译出的代码,被调用函数的重定位类型是R_SPARC_WPLT30,它指示连接器生成.plt表项,并计算该.plt表项到call指令偏移作为call指令的偏移;对不用-fpic编译出的代码,被调用函数的重定位类型是R_SPARC_WDISP30,这通过计算调用函数相对call指令的偏移得到。注意这个偏移值单位为指令长度(4个字节)。 

测试: 
bash-2.03$ gcc -fpic -c hehe.c 
bash-2.03$ ./redir hehe.o libtst.so 
bash-2.03$ gcc -o t t.c -L . -lnew 
bash-2.03$ export LD_LIBRARY_PATH=. 
bash-2.03$ ./t 
haha 
bash-2.03$ 

---| 总结 

这种插入方式只能插入一个.o文件到.so中,而且插入的.o要满足如下条件: 
1. 不能有.rodata区,data,bss大小为0。 
2. 只能调用要插入动态库中的函数。 

---| 参考资料 

1. ELF Specification 1.2 
2. SYSTEM V ABI: SPARC Processor Supplyment 
3. The SPARC Architecture Manual,version 8 
4. Phrack 56-9: Backdooring binary objects 
5. nsfocus: <<如何修改动态库符号表>>,author:wangdb 

---| 实现程序 
/* 
* redir.c 
* expand .got section of a shared library 

* copyright (c) 2002, lgx@venuslab 

* Compile: gcc -o redir redir.c 
* Usage: redir code.o libtest.so 

* history: 
* 1. Insert code to .got section 

*/ 

#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <elf.h> 
#include <sys/mman.h> 
#ifdef sun 
#include <sys/elf_SPARC.h> 
#include <sys/elf_386.h> 
#endif 
#include <stdlib.h> 
#include <limits.h> 

#define DEBUG 

/* 符号名hash表 */ 
static unsigned long nbucket = 0,nchain = 0,*buckets = NULL,*chains = NULL; 

/* 需要做重定位的特殊符号 */ 
static const char *spsym[] = {"_DYNAMIC","__bss_start","_edata","_end",NULL}; 

/* 符号名 ==> hash值 */ 
static unsigned long elf_hash(const unsigned char *name) 

unsigned long h = 0, g; 

while (*name){ 
h = (h << 4) + *name++; 
if (g = h & 0xf0000000) 
h ^= g >> 24; 
h &= ~g; 

return h; 


/* hash符号搜索 */ 
static unsigned long lookup_sym(Elf32_Sym *libsym,const char *libstr, 
const char *symname) 

unsigned long hash = elf_hash(symname); 
unsigned long idx = buckets[hash % nbucket]; 

#ifdef DEBUG 
fprintf(stderr,"Lookup sym for: %s\n",symname); 
#endif 

if (!strcmp(libsym[idx].st_name + libstr,symname)) { 
#ifdef DEBUG 
fprintf(stderr,"lookup_sym: Found sym: %s,idx=%d\n",symname,idx); 
return idx; 
#endif 


while (1) { 
idx = chains[idx]; 
if (idx == STN_UNDEF) { 
#ifdef DEBUG 
fprintf(stderr,"lookup_sym: sym: %s undef\n",symname,idx); 
#endif 
break; 

if (!strcmp(libsym[idx].st_name + libstr,symname)) { 
#ifdef DEBUG 
fprintf(stderr,"lookup_sym: Found sym: %s,idx=%d\n",symname,idx); 
#endif 
break; 


return idx; 


int main(int argc,char **argv) 

/* 
* 变量命名规则: 
* 对象前缀(obj:要插入的.o文件; lib:被插入的动态库; tmp:生成的动态库) 
* + 变量描述属性(eh:ELF头;sh:区头;sym:符号表;sn:区名表; 
* ph:程序头表;str:字符串表;data:文件原始数据; 
*/ 
Elf32_Ehdr *libeh = NULL,*tmpeh = NULL,*objeh = NULL; 
char *libfile = NULL,*objfile = NULL,*tmpfile = "libnew.so"; 
Elf32_Shdr *libsh = NULL,*tmpsh = NULL,*objsh = NULL; 
Elf32_Phdr *ph = NULL,*tmpph = NULL; 
Elf32_Rela *obj_rela_text = NULL; 
Elf32_Sym *objsym = NULL,*libsym = NULL,*tmpsym = NULL; 
struct stat objst,libst; 
unsigned char *libdata = NULL,*objdata = NULL,*tmpdata = NULL; 
char *libsn = NULL,*objstr = NULL,*objsn = NULL,*libstr = NULL; 
int i=0,objoff = 0,objlen = 0,fd,nobj_rela_text = 0; 
int lib_got_ndx = 0,lib_plt_ndx = 0,lib_dynsym_ndx = 0, 
lib_dynstr_ndx = 0,lib_hash_ndx = 0; 
int obj_rela_text_ndx = 0,obj_symtab_ndx = 0,obj_strtab_ndx = 0, 
obj_text_ndx = 0; 
char *p = NULL; 
unsigned long idx = 0; 

if (argc < 3) { 
fprintf(stderr,"Usage: %s objfile libfile\n",argv[0]); 
fprintf(stderr," objfile : code will be inserted into libfile\n"
fprintf(stderr," libfile : Library file name\n"
return -1; 


/* Open & mmap object file */ 
objfile = argv[1]; 
if ((fd=open(objfile,O_RDONLY)) < 0) { 
perror("Open object file"
return 1; 

fstat(fd,&objst); 
if ((objdata=mmap(NULL,objst.st_size,PROT_READ,MAP_SHARED,fd,0)) == NULL){ 
perror("mmap"
return -1; 

close(fd); 

/* Open & mmap library file */ 
libfile = argv[2]; 
if ((fd=open(libfile,O_RDONLY)) < 0) { 
perror("Open Library file"
return 1; 

fstat(fd,&libst); 
if ((libdata=mmap(NULL,libst.st_size,PROT_READ,MAP_SHARED,fd,0)) == NULL){ 
perror("mmap library file"
return -1; 

close(fd); 

/* Now get objfile infomation */ 
fprintf(stderr,"\nNow collecting obj file infomation .....\n"
objeh = (Elf32_Ehdr*)objdata; 
#ifdef DEBUG 
printf("obj: e_shoff:%#x,e_shnum:%d,e_shentsize:%d\n", 
objeh->e_shoff,objeh->e_shnum,objeh->e_shentsize); 
printf("obj: e_shstrndx:%d,e_phoff:%#x\n", 
objeh->e_shstrndx,objeh->e_phoff); 
#endif 
objsh = (Elf32_Shdr*)(objdata + objeh->e_shoff); 
objsn = (char*)(objdata + objsh[objeh->e_shstrndx].sh_offset); 

Elf32_Shdr *tmp = objsh; 
for (i=0; i<objeh->e_shnum; i++,tmp++) { 
printf("%d off:%#10x size:%#10x entsize:%#10x %s\n", 
i,tmp->sh_offset,tmp->sh_size, 
tmp->sh_entsize,objsn + tmp->sh_name); 
#ifdef sun 
if (!strncmp(objsn + tmp->sh_name,".rela.text",10)) { 
#else 
if (!strncmp(objsn + tmp->sh_name,".rel.text",9)) { 
#endif 
obj_rela_text_ndx = i; 

else if (!strncmp(objsn + tmp->sh_name,".symtab",7)) { 
obj_symtab_ndx = i; 

else if (!strncmp(objsn + tmp->sh_name,".strtab",7)) { 
obj_strtab_ndx = i; 

else if (!strncmp(objsn + tmp->sh_name,".text",5)) { 
obj_text_ndx = i; 



if (!obj_rela_text_ndx || !obj_symtab_ndx || 
!obj_strtab_ndx || !obj_text_ndx) { 
fprintf(stderr, 
"obj:One of .text,.rela.text,.symtab,.strtab not found.\n"
return -1; 

printf("obj: .text index is : %d\n",obj_text_ndx); 
objoff = objsh[obj_text_ndx].sh_offset; 
objlen = objsh[obj_text_ndx].sh_size; 
printf("obj: objoff:%#x, objlen:%#x\n",objoff,objlen); 
obj_rela_text = 
(Elf32_Rela *)(objdata + objsh[obj_rela_text_ndx].sh_offset); 
nobj_rela_text = 
objsh[obj_rela_text_ndx].sh_size / objsh[obj_rela_text_ndx].sh_entsize; 
printf("obj: .rela.text index is : %d,number=%d\n", 
obj_rela_text_ndx,nobj_rela_text); 
printf("obj: .symtab index is : %d\n",obj_symtab_ndx); 
objsym = (Elf32_Sym *)(objdata + objsh[obj_symtab_ndx].sh_offset); 
printf("obj: .strtab index is : %d\n",obj_strtab_ndx); 
objstr = (char*)(objdata + objsh[obj_strtab_ndx].sh_offset); 

/* Open & mmap tmp file */ 
if ((fd=open(tmpfile,O_RDWR|O_CREAT|O_TRUNC,S_IRWXU)) < 0) { 
perror("open tmpfile"
return -1; 

lseek(fd,libst.st_size + objlen -1,SEEK_SET); 
write(fd,(unsigned char*)&i,1); 
if ((tmpdata=mmap(NULL,libst.st_size + objlen, 
PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == NULL) { 
perror("mmap tmpfile"
return -1; 

close(fd); 
bzero(tmpdata,libst.st_size + objlen); 


fprintf(stderr,"Now collecting lib file infomation .....\n"
libeh = (Elf32_Ehdr*)libdata; 
#ifdef DEBUG 
printf("lib: e_shoff:%#x,e_shnum:%d,e_shentsize:%d\n", 
libeh->e_shoff,libeh->e_shnum,libeh->e_shentsize); 
printf("lib: e_shstrndx:%d,e_phoff:%#x\n", 
libeh->e_shstrndx,libeh->e_phoff); 
#endif 
/* Find .got section index,will insert code there */ 
libsh = (Elf32_Shdr*)(libdata + libeh->e_shoff); 
libsn = (char*)(libdata + libsh[libeh->e_shstrndx].sh_offset); 

Elf32_Shdr *tmp = libsh; 
for (lib_got_ndx=-1,i=0; i<libeh->e_shnum; i++,tmp++) { 
#ifdef DEBUG 
printf("lib: %d %#x %#x %s\n", 
i,tmp->sh_offset,tmp->sh_size,libsn + tmp->sh_name); 
#endif 
if (!strncmp(libsn + tmp->sh_name,".got",4)) { 
lib_got_ndx = i; 

else if (!strncmp(libsn + tmp->sh_name,".dynsym",7)) { 
lib_dynsym_ndx = i; 

else if (!strncmp(libsn + tmp->sh_name,".dynstr",7)) { 
lib_dynstr_ndx = i; 

else if (!strncmp(libsn + tmp->sh_name,".plt",4)) { 
lib_plt_ndx = i; 

else if (!strncmp(libsn + tmp->sh_name,".hash",4)) { 
lib_hash_ndx = i; 



if (!lib_got_ndx || !lib_plt_ndx || !lib_dynsym_ndx || 
!lib_dynstr_ndx || !lib_hash_ndx) { 
fprintf(stderr, 
"Lib: one of .got,.plt,.dynsym,.dynstr,.hash not found.\n"
return -1; 

fprintf(stderr,"Lib: got:%d,plt:%d,dynsym:%d,dynstr:%d,hash:%d\n", 
lib_got_ndx,lib_plt_ndx,lib_dynsym_ndx,lib_dynstr_ndx,lib_hash_ndx 
); 

unsigned long *l = 
(unsigned long*)(libdata + libsh[lib_hash_ndx].sh_offset); 
nbucket = *l++; 
nchain = *l++; 
fprintf(stderr,"Lib: nbucket=%d,nchain=%d\n",nbucket,nchain); 
buckets = l; 
chains = l + nbucket; 

libsym = (Elf32_Sym*)(libdata + libsh[lib_dynsym_ndx].sh_offset); 
libstr = (char *)(libdata + libsh[lib_dynstr_ndx].sh_offset); 

/* Now create output library,copy origin library data */ 
memcpy(tmpdata,libdata,libsh[lib_got_ndx+1].sh_offset); 
memcpy(tmpdata + libsh[lib_got_ndx+1].sh_offset, 
objdata + objoff, 
objlen 
); 
memcpy(tmpdata + libsh[lib_got_ndx+1].sh_offset + objlen, 
libdata + libsh[lib_got_ndx+1].sh_offset, 
libst.st_size - libsh[lib_got_ndx+1].sh_offset 
); 

tmpeh = (Elf32_Ehdr*)tmpdata; 
/* Because e_shoff > got_off, so we must adjust it */ 
tmpeh->e_shoff = libeh->e_shoff + objlen; 
/* Adjust .got size and adjust offset of sections beyond .got */ 
tmpsh = (Elf32_Shdr*)(tmpdata + tmpeh->e_shoff); 
tmpsym = (Elf32_Sym*)(tmpdata + tmpsh[lib_dynsym_ndx].sh_offset); 
#ifdef DEBUG 
printf("tmpgot off: %#x,size: %#x\n", 
tmpsh[lib_got_ndx].sh_offset,libsh[lib_got_ndx].sh_size); 
#endif 
tmpsh[lib_got_ndx].sh_size += objlen; 

for (i = lib_got_ndx + 1; i<tmpeh->e_shnum; i++) { 
tmpsh[i].sh_offset += objlen; 
if (tmpsh[i].sh_addr) 
tmpsh[i].sh_addr += objlen; 


for (i=0; spsym[i]; i++) { 
if ((idx = lookup_sym(libsym,libstr,spsym[i])) != STN_UNDEF) { 
Elf32_Sym *sym = &libsym[idx]; 
printf("value: %#x,size: %#x,bind:%d,type:%d,shndx: %d\n", 
sym->st_value,sym->st_size, 
ELF32_ST_BIND(sym->st_info), 
ELF32_ST_TYPE(sym->st_info), 
sym->st_shndx 
); 
sym = &tmpsym[idx]; 
sym->st_value += objlen; 

else 
fprintf(stderr,"Relocate: symbox %s not found\n",spsym[i]); 


/* Adjust Program header table */ 
#ifdef DEBUG 
printf("e_phoff:%#x, e_phnum:%d, e_phentsize:%d\n", 
tmpeh->e_phoff,tmpeh->e_phnum,tmpeh->e_phentsize); 
#endif 
tmpph = (Elf32_Phdr*)(tmpdata + tmpeh->e_phoff); 
for (i=0; i<tmpeh->e_phnum; i++,tmpph++) { 
#ifdef DEBUG 
printf("p offset: %#x,vaddr: %#x,paddr: %#x,filesz: %#x,memsz: %#x\n", 
tmpph->p_offset,tmpph->p_vaddr,tmpph->p_paddr, 
tmpph->p_filesz,tmpph->p_memsz); 
#endif 
if (tmpph->p_offset > tmpsh[lib_got_ndx].sh_offset) { 
tmpph->p_offset += objlen; 
tmpph->p_vaddr += objlen; 
tmpph->p_paddr = tmpph->p_vaddr; 

else if (tmpph->p_offset + tmpph->p_filesz > 
tmpsh[lib_got_ndx].sh_offset) { 
tmpph->p_filesz += objlen; 
tmpph->p_memsz += objlen; 
tmpph->p_flags = (PF_R | PF_W | PF_X); 



/* Now relocate function references */ 
for (i=0; i<nobj_rela_text; i++) { 
Elf32_Sym *sym = &objsym[ELF32_R_SYM(obj_rela_text[i].r_info)]; 
char *symname = sym->st_name + objstr; 
unsigned long *reloff = NULL; 
fprintf(stderr,"Relocate symbol: %s,offset:%#x,info:%#x,addend:%#x\n", 
symname,obj_rela_text[i].r_offset, 
obj_rela_text[i].r_info,obj_rela_text[i].r_addend 
); 
if ((idx = lookup_sym(libsym,libstr,symname)) != STN_UNDEF) { 
sym = &libsym[idx]; 
printf("value: %#x,size: %#x,bind:%d,type:%d,shndx: %d\n", 
sym->st_value,sym->st_size, 
ELF32_ST_BIND(sym->st_info), 
ELF32_ST_TYPE(sym->st_info), 
sym->st_shndx 
); 

else { 
fprintf(stderr,"Symbol %s not found in lib,continue anyway.\n", 
symname); 
continue; 

if (ELF32_ST_TYPE(libsym[idx].st_info) == STT_FUNC) { 
reloff = (unsigned long*)(tmpdata + 
libsh[lib_got_ndx+1].sh_offset + obj_rela_text[i].r_offset); 
if (ELF32_R_TYPE(obj_rela_text[i].r_info) == R_SPARC_WPLT30){ 
unsigned long disp30 = 0; 
printf("*reloff:%#x\n",*reloff); 
disp30 = libsh[lib_got_ndx+1].sh_addr + 
obj_rela_text[i].r_offset - sym->st_value; 
printf("base=%#x,disp30=%#x,disp30=%#x\n", 
libsh[lib_got_ndx+1].sh_addr,disp30,disp30 >> 2); 
disp30 = ~(disp30 >> 2); 
disp30 &= 0x3fffffff; 
disp30 |= 0x40000000; 
printf("final *reloff: %#x\n",disp30); 
*reloff = disp30; 

else if (ELF32_R_TYPE(obj_rela_text[i].r_info) == R_386_PC32) { 
unsigned long dest = sym->st_value - 4 - 
(libsh[lib_got_ndx+1].sh_addr + obj_rela_text[i].r_offset); 
printf("dest: %x\n",dest); 
*reloff = dest; 




/* Hook function */ 
if ((idx = lookup_sym(libsym,libstr,"huhu") != STN_UNDEF) { 
Elf32_Sym *sym = &libsym[idx]; 
printf("value: %#x,size: %#x,bind:%d,type:%d,shndx: %d\n", 
sym->st_value,sym->st_size, 
ELF32_ST_BIND(sym->st_info), 
ELF32_ST_TYPE(sym->st_info), 
sym->st_shndx 
); 
sym = &tmpsym[idx]; 
printf("value: %#x,size: %#x,bind:%d,type:%d,shndx: %d\n", 
sym->st_value,sym->st_size, 
ELF32_ST_BIND(sym->st_info), 
ELF32_ST_TYPE(sym->st_info), 
sym->st_shndx 
); 
sym->st_value = libsh[lib_got_ndx+1].sh_addr; 
sym->st_size = objlen; 
sym->st_shndx = lib_got_ndx; 


munmap(libdata,libst.st_size); 
munmap(tmpdata,libst.st_size + objlen); 
munmap(objdata,objst.st_size); 
return 0; 




原文转自:http://www.ltesting.net