未加星标

如何进行Linux平台共享库替换

字体大小 | |
[系统(linux) 所属分类 系统(linux) | 发布者 店小二05 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

*本文原创作者:gaearrow,本文属CodeSec原创奖励计划,未经许可禁止转载

共享库基础知识

程序由源代码变成可执行文件,一般可以分解为四个步骤,分别是预处理(Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。预处理过程主要处理源代码中以“#”开始的预编译指令;编译过程把预处理完成的文件进行词法、语法、语义等分析并产生相应的汇编代码文件;汇编过程将汇编代码文件翻译成机器可以执行的目标文件;链接过程将汇编生成的目标文件集合相连接并生成最终的可执行文件。

链接方式分为静态链接和动态链接,静态链接分发程序只需要生成的可执行文件,动态链接分发程序不仅需要可执行文件,还要包含相应的库文件。该库文件在windows平台称为动态链接库(Dynamic-Link Library,DLL),在linux平台一般称为共享库(Shared Object,SO)。

Linux平台SO替换可以分为静态替换和动态替换:静态替换利用文件操作直接替换SO,新SO在下次加载时生效;动态替换利用代码注入替换目标进程内存空间,实现新SO的加载和替换,新SO立即生效。

静态替换

针对未被加载的SO,利用复制命令(cp new.so old.so)即可直接完成静态替换,新SO在下次加载时生效。对于已经加载的原SO,直接用新SO复制替换将会导致相应程序崩溃,此种情况可以使用删除原SO(rm -f old.so)或修改原SO名称(mv old.so oldx.so)后,再复制新SO的方法代替,新SO同样在下次加载时生效。

程序崩溃的原因是复制替换操作会破坏系统访问原SO的索引节点inode,导致系统找不到原SO。系统为每个加载到内存中的文件创建对应的inode,用来管理该文件,inode包含了文件的元信息,如文件字节数、拥有者ID、读写执行权限等。系统以inode标识程 序加载的SO,不再关心文件名,修改SO名称并未改变对应inode,因此程序可以继续正常运行;删除SO只是无法查看,系统直到程序释放SO后才真正删除SO和inode,因此程序也可以继续正常运行;但是在直接复制替换时,新SO将会继承原SO的inode,程序无法继续访问原SO,从而导致程序崩溃。

动态替换

针对已经被程序加载的SO,为了实现不停止程序,替换后的SO立即生效的目的,可以采用动态替换。

动态替换的对象既可以是SO整体,也可以是SO中的特定函数。两者的区别主要是整体替换需要在特定函数替换的基础上再增加SO加载及输出函数重定位等过程。由于时间有限, 本文仅介绍特定函数动态替换的基本原理和初步实现,SO整体替换感兴趣的 读者可以自行尝试。

SO特定函数动态替换主要包括三个关键过程:控制目标进程,构造替换内容和确定替换地址,实际上依次解决的就是利用什么替换、替换什么内容和替换到哪里的问题。

控制目标进程

为实现对其它进程空间或运行进行控制, Linux平台提供了进程跟踪函数Ptrace()(类似于Windows平台的创建远程线程函数CreateRemoteThread())。

Ptrace()函数引用头文件和参数形式如下:

#include<sys/ptrace.h>
int ptrace(int request,int pid,int addr,int data);

其中,request参数决定了该函数的调用功能。

PTRACE_ATTACH/ PTRACE_DETACH
int ptrace(PTRACE_ATTACH,Pid,NULL,NULL);
int ptrace(PTRACE_DETACH,Pid,NULL,NULL);

分别实现跟踪和结束跟踪目标进程功能。Pid表示目标进程标识符。成功跟踪后,被跟踪进程将成为当前进程的子进程,并进入中止状态。

PTRACE_POKEDATA
int ptrace(PTRACE_?POKEDATA,Pid,Addr,Data);

实现向目标进程内存中写入一个字节数据功能。Pid表示目标进程标识符,Addr存储写入的内存地址,Data为要写入的数据。

除了以上本文中用到的功能,Ptrace()函数还提供数据读取(PTRACE_PEEKDATA)、终止进程(PTRACE_KILL)和重新运行(PTRACE_CONT)等功能,针对Intel386平台还提供读取和设置寄存器等功能。


如何进行Linux平台共享库替换

图 1特定函数替换主要过程活动图

SO特定函数动态替换主要过程活动图如图1所示,其中跟踪和结束跟踪目标进程过程由Ptrace()函数直接完成,替换目标进程内存过程由Ptrace()配合构造的替换内容共同完成。

构造替换内容

构造替换内容主要包括两方面工作,一是分析被替换函数的特征,确定替换空间结构和堆栈恢复指令;二是完成替换函数的编译、Shellcode提取及再构造。

被替换函数特征分析

测试被替换函数hello()存在于SO的libfso.so中,由主程序main载入并以固定周期循环调用,其反汇编代码如图2所示。由图可知,方框标识的代码为堆栈平衡和函数返回指令,函数实际执行部分为地址hello+8至hello+60之间。因此包含堆栈恢复和函数返回指令(2字节)的最大可替换空间地址为hello+8至hello+61,共计54个字节。


如何进行Linux平台共享库替换
?

图 2测试被替换函数反汇编代码

替换函数编译再构造

测试替换函数汇编代码如图3所示,功能为输出Hello World!字符,其中方框标识的空指令是为堆栈恢复和函数返回指令预留的存储空间。

经过编译提取后可以得到46个字节的Shellcode,为了避免程序误将helloworld字符理解成指令执行导致程序崩溃,还需要将原函数的堆栈恢复和函数返回指令拷贝至预留的空指令位置,提前返回函数。成功替换后的被替换函数结构如图4所示,其中小方格内为堆栈平衡和函数返回指令,大方格内为构造的替换内容。


如何进行Linux平台共享库替换

图 3测试替换函数汇编代码


如何进行Linux平台共享库替换

图 4成功替换后实际结构

确定替换地址

由于地址加载随机化ASLR(Address space layout randomization)的影响,SO加载到内存的基地址并不固定,但是函数相对于基地址的偏移地址是固定不变的。因此对被替换函数所在SO进行反汇编分析,可以确定被替换函数替换位置首地址,再配合linux提供的进程虚拟地址空间查看命令得到SO加载基地址,简单相减即可得到被替换函数位置相对偏移地址。待准备实施替换时,只需再次利用进程虚拟地址空间查看命令获得基地址加上相对地址就可以确定实际替换首地址。

图5中地址0x2b6901e0f000为测试程序SO加载基地址,图6中0x2b6901e0f514为函数替换位置首地址,相减得到0×514,即为被替换函数替换起始位置的相对偏移地址。


如何进行Linux平台共享库替换

图 5共享库加载基地址


如何进行Linux平台共享库替换
?

图 6替换位置首地址

测试实例 测试环境

CentOS 6.6 (Final)Linux version 2.6.32

gcc version 4.4.7 20120313

nasm version 2.07

前文截图为64位系统情况,以下为32位系统情况。

被动态替换程序

【动态链接库】

//fso.c
//gcc -c -Wall -Werror -fPIC fso.c
//gcc -shared -o libfso.so fso.o
#include <stdio.h>
void hello()
{
int i = 0;
int j = 0;
printf("Hello Myboy!\n");
for(i=0; i<10000000; i++)
{
j++;
}
}

以上代码中的for循环,仅仅是为了确保Shellcode在替换时具有足够的存储空间。

【被动态替换程序】

//main.c
//gcc -L /home/mycos/so -Wall -o main main.c -lfso
//export LD_LIBRARY_PATH=/home/mycos/so :$LD_LIBRARY_PATH
#include <stdio.h>
extern void hello(void);
int main()
{
int i = 0;
printf("This is Main!\n");
while(1)
{
if(i%10 == 0) printf("\n");
sleep(1);
hello();
i++;
}
return 0;
}

详细信息或出现错误请参见 加载动态链接库so程序简单实例

Shellcode

【hello.asm】

; 32-bit "Hello World!" in CentOS 6 i686
; nasm -felf32 hello.asm -o hello.o
; ld -s -o hello hello.o
global _start
_start:
jmp string
code:
pop ecx
mov eax, 0x4
mov ebx, 0x1
mov edx, 0xD
int 0x80
nop
nop
nop
nop
nop
nop
nop
nop
string:
call code
db 'Hello world!',0x0a

以上空指令nop,是为堆栈恢复和函数返回指令预留的存储空间,预留空指令空间必须比被替换程序动态库堆栈恢复和函数返回指令占用的空间大。

【提取Shellcode】

for i in $(objdump -d hello |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo

执行以上命令得到如下Shellcode,共计49个字节,但是该Shellcode还不能直接使用,需要根据目标程序动态链接库实际情况进行修改。

\xe9\x1a\x00\x00\x00\x59\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xba\x0d\x00\x00\x00\xcd\x80\x90\x90\x90\x90\x90\x90\x90\x90\xe8\xdf\xff\xff\xff\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x21\x0a

【修改Shellcode】

利用GDB调试被替换程序,得到动态库中函数堆栈恢复和函数返回的代码。

[[email protected] so]$ gdb main
(gdb) b hello
Breakpoint 1 at 0x804842c
(gdb) r
Starting program: /home/mycos/so/main
This is Main!
Breakpoint 1, 0x00111434 in hello () from /home/mycos/so/libfso.so
(gdb) disas hello
Dump of assembler code for function hello:
0x00111430 <+0>: push %ebp
0x00111431 <+1>: mov %esp,%ebp
0x00111433 <+3>: push %ebx
=> 0x00111434 <+4>: sub $0x24,%esp
0x00111437 <+7>: call 0x111429 <__i686.get_pc_thunk.bx>
0x0011143c <+12>: add $0x1190,%ebx
0x00111442 <+18>: movl $0x0,-0x10(%ebp)
0x00111449 <+25>: movl $0x0,-0xc(%ebp)
0x00111450 <+32>: lea -0x10f8(%ebx),%eax
0x00111456 <+38>: mov %eax,(%esp)
0x00111459 <+41>: call 0x111334 <[email protected]>
0x0011145e <+46>: movl $0x0,-0x10(%ebp)
0x00111465 <+53>: jmp 0x11146f <hello+63>
0x00111467 <+55>: addl $0x1,-0xc(%ebp)
0x0011146b <+59>: addl $0x1,-0x10(%ebp)
0x0011146f <+63>: cmpl $0x98967f,-0x10(%ebp)
0x00111476 <+70>: jle 0x111467 <hello+55>
0x00111478 <+72>: add $0x24,%esp
0x0011147b <+75>: pop %ebx
0x0011147c <+76>: pop %ebp
0x0011147d <+77>: ret
End of assembler dump. (gdb) x /8xb 0x111478
0x111478 <hello+72>: 0x83 0xc4 0x24 0x5b 0x5d 0xc3 0x90 0x90

分析动态库hello函数的汇编代码,可以较容易的判断出恢复堆栈和函数返回的地址从0×00111478到0x0011147d共6个字节,且该函数实际可被shellcode覆盖填充的部分从0×00111437(函数开始的堆栈平衡处理部分不能覆盖,易导致异常)到0x0011147d共70个字节大于shellcode的49个字节,满足替换空间要求。因此最后得到的Shellcode如下:

\xe9\x1a\x00\x00\x00\x59\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xba\x0d\x

本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统

主题: LinuxWindows数据RY删除行权定位
分页:12
转载请注明
本文标题:如何进行Linux平台共享库替换
本站链接:http://www.codesec.net/view/484849.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 系统(linux) | 评论(0) | 阅读(27)