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要满足如下条件:
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




