博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
调试LD_PRELOAD注入的代码
阅读量:4994 次
发布时间:2019-06-12

本文共 10487 字,大约阅读时间需要 34 分钟。

    LD_PRELOAD提供了平民化的注入方式固然方便,同一时候也有不便:注入库出错后调试比較困难。

我琢磨了几天找到了可行的调试方法,当然未必是最有效的办法。抛出陋文,希望引来美玉~

    首先。写一段代码作为普通的动态库,公开接口。供人调用。例如以下:

//true.cint fake(const char* s1,const char* s2){	return 0;}gcc -g3 -O0 -o libtrue.so true.c -fPIC -sharedecho "/root/Desktop">>/etc/ld.so.confldconfig
这差点儿相同是个空函数。

    以下是LD_PRELOAD将要注入的代码:

//fake.c#include 
#include
int fake(const char* s1,const char* s2){ printf("s1:%s-s2:%s\n",s1,s2); while(1) sleep(1); return 0;} Makefileall:    gcc -g3 -O0 -fPIC -shared -Wa,-adlhn -c fake.c -fno-builtin-strcmp > fake.cod    gcc -g3 -O0 -fPIC -shared -o fake.so fake.o -Wl,-Map,Sym.map
fake.c除了生成调试信息以外,同一时候生成符号映射文件,why?

    先不解释为什么。先来看下Sym.map中有什么:

.init           0x0000000000000498       0x18 *(.init) .init          0x0000000000000498        0x9 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crti.o                0x0000000000000498                _init .init          0x00000000000004a1        0x5 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtbeginS.o .init          0x00000000000004a6        0x5 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtendS.o .init          0x00000000000004ab        0x5 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crtn.o.plt            0x00000000000004b0       0x40 *(.plt) .plt           0x00000000000004b0       0x40 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crti.o *(.iplt).text           0x00000000000004f0      0x148 *(.text.unlikely .text.*_unlikely) *(.text .stub .text.* .gnu.linkonce.t.*) .text          0x00000000000004f0       0x17 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crti.o *fill*         0x0000000000000507        0x9 90909090 .text          0x0000000000000510       0xaa /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtbeginS.o *fill*         0x00000000000005ba        0x2 90909090 .text          0x00000000000005bc       0x40 fake.o                0x00000000000005bc                fake *fill*         0x00000000000005fc        0x4 90909090 .text          0x0000000000000600       0x36 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtendS.o *fill*         0x0000000000000636        0x2 90909090 .text          0x0000000000000638        0x0 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crtn.o *(.gnu.warning).fini           0x0000000000000638        0xe *(.fini) .fini          0x0000000000000638        0x4 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crti.o                0x0000000000000638                _fini .fini          0x000000000000063c        0x5 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtbeginS.o .fini          0x0000000000000641        0x5 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crtn.o                0x0000000000000646                PROVIDE (__etext, .)                0x0000000000000646                PROVIDE (_etext, .)                0x0000000000000646                PROVIDE (etext, .)
这里仅截取了Sym.map代码段。代码段中有.init节.text节.fini节。每节中又由诺干.o文件组成。如crt执行时库相关的crti.o以及fake.c编译后生成的fake.o。

编译器将源代码编译为.o中间文件后,还须要把全部的中间文件按同样的页面属性连接到一起,并分配链接地址(关于编译连接的具体介绍能够參看<程序猿的自我修养>)。sym.map文件显示了链接时,各个.o文件在整个fake.so文件里的偏移:如fake.o在文件里的偏移是0x5bc

    如今说明一下须要sym.map的原因:由于fake.so是动态库,程序执行时,载入到内存中的位置不固定。由于他的不固定性。所以非常难下断点或者反汇编。可是。程序执行起来后,能够通过cat /proc/pidnum/maps查看进程内存载入的情况,并获得fake.so载入的基址。

有了基址,加上偏移,就能够确定fake.c提供的代码在进程空间中的详细地址:

    图中显示fake.so被载入到地址7ffff7deb000-7ffff7dee000,当中7ffff7deb000是基址。加上前面sym.map显示的偏移,如今能够预知fake提供的函数在地址7ffff7deb5bc处。

    继续往下,有了动态库,还要有測试文件调用动态库接口:

//test.c#include 
#include
extern int fake(const char* s1,const char* s2);int main(){ if(fake("1","2") == 0) { printf("nothing\n"); } return 0;}gcc -o test test.cstrip --strip-all test
为了比較真实的模拟由他人公布的程序的环境,对于test.c不仅不生成调试信息,同一时候还剥离符号表。
以下開始注入并启动调试。

export LD_PRELOAD=/root/Desktop/fake.sogdb test
因为没有调试信息,gdb找不到main函数,因此无法在main函数下断点,直接导致start后程序跑飞了:

(gdb) start Function "main" not defined.Make breakpoint pending on future shared library load?

(y or [n]) n Starting program: /root/Desktop/test s1:1-s2:2

好吧,眼下仅仅能又一次调试它,并尝试找到程序从哪開始的:

[root@localhost Desktop]# gdb test(gdb) info files Symbols from "/root/Desktop/test".Local exec file:    `/root/Desktop/test', file type elf64-x86-64.    Entry point: 0x400520

这样就得到了程序的入口,并于此下断点然后start执行,程序在0x400520处停下:

(gdb) b *0x400520Breakpoint 1 at 0x400520(gdb) start Function "main" not defined.Make breakpoint pending on future shared library load?

(y or [n]) n Starting program: /root/Desktop/test Breakpoint 1, 0x0000000000400520 in ?? () Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6_5.3.x86_64 (gdb)

这时。进程的依赖的各个动态库也业已完毕载入,能够查看内存载入情况:

[root@localhost ~]# ps x|grep  test 4352 pts/8    S+     0:00 gdb test 4381 pts/8    T      0:00 /root/Desktop/test[root@localhost ~]# cat /proc/4381/maps 00400000-00401000 r-xp 00000000 fd:00 131451                             /root/Desktop/test00600000-00601000 rw-p 00000000 fd:00 131451                             /root/Desktop/test3326400000-3326420000 r-xp 00000000 fd:00 75830                          /lib64/ld-2.12.so332661f000-3326620000 r--p 0001f000 fd:00 75830                          /lib64/ld-2.12.so3326620000-3326621000 rw-p 00020000 fd:00 75830                          /lib64/ld-2.12.so3326621000-3326622000 rw-p 00000000 00:00 0 3326c00000-3326d8b000 r-xp 00000000 fd:00 75831                          /lib64/libc-2.12.so3326d8b000-3326f8a000 ---p 0018b000 fd:00 75831                          /lib64/libc-2.12.so3326f8a000-3326f8e000 r--p 0018a000 fd:00 75831                          /lib64/libc-2.12.so3326f8e000-3326f8f000 rw-p 0018e000 fd:00 75831                          /lib64/libc-2.12.so3326f8f000-3326f94000 rw-p 00000000 00:00 0 7ffff7bea000-7ffff7bed000 rw-p 00000000 00:00 0 7ffff7bed000-7ffff7bee000 r-xp 00000000 fd:00 136044                     /root/Desktop/libtrue.so7ffff7bee000-7ffff7ded000 ---p 00001000 fd:00 136044                     /root/Desktop/libtrue.so7ffff7ded000-7ffff7dee000 rw-p 00000000 fd:00 136044                     /root/Desktop/libtrue.so7ffff7dfc000-7ffff7dfd000 r-xp 00000000 fd:00 136014                     /root/Desktop/fake.so7ffff7dfd000-7ffff7ffc000 ---p 00001000 fd:00 136014                     /root/Desktop/fake.so7ffff7ffc000-7ffff7ffd000 rw-p 00000000 fd:00 136014                     /root/Desktop/fake.so7ffff7ffd000-7ffff7ffe000 rw-p 00000000 00:00 0 7ffff7ffe000-7ffff7fff000 r-xp 00000000 00:00 0                          [vdso]7ffffffea000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
嗯。fake.so在7ffff7dfc000。fake.o应该位于7ffff7dfc5bc处了。

另外也能够看到。被LD_PRELOAD覆盖的libtrue.so被载入到7ffff7bed000处

能够直接在这个位置下断。当然不放心的话,能够用objdump查看test的反汇编:

[root@localhost Desktop]# objdump -d testDisassembly of section .plt:00000000004004d8 
:  4004d8:    ff 35 d2 04 20 00        pushq  0x2004d2(%rip)        # 6009b0 <_fini+0x2002a8>  4004de:    ff 25 d4 04 20 00        jmpq   *0x2004d4(%rip)        # 6009b8 <_fini+0x2002b0>  4004e4:    0f 1f 40 00              nopl   0x0(%rax)00000000004004e8
:  4004e8:    ff 25 d2 04 20 00        jmpq   *0x2004d2(%rip)        # 6009c0 <_fini+0x2002b8>  4004ee:    68 00 00 00 00           pushq  $0x0  4004f3:    e9 e0 ff ff ff           jmpq   4004d8 <_init+0x18>...0000000000400520 <.text>:  400520:    31 ed                    xor    %ebp,%ebp  400522:    49 89 d1                 mov    %rdx,%r9  400525:    5e                       pop    %rsi  400526:    48 89 e2                 mov    %rsp,%rdx  400529:    48 83 e4 f0              and    $0xfffffffffffffff0,%rsp  40052d:    50                       push   %rax  40052e:    54                       push   %rsp  40052f:    49 c7 c0 30 06 40 00     mov    $0x400630,%r8  400536:    48 c7 c1 40 06 40 00     mov    $0x400640,%rcx  40053d:    48 c7 c7 04 06 40 00     mov    $0x400604,%rdi  400544:    e8 bf ff ff ff           callq  400508 <__libc_start_main@plt>

当中

00000000004004e8 
:  4004e8:    ff 25 d2 04 20 00        jmpq   *0x2004d2(%rip)        # 6009c0 <_fini+0x2002b8>  4004ee:    68 00 00 00 00           pushq  $0x0  4004f3:    e9 e0 ff ff ff           jmpq   4004d8 <_init+0x18>
是test向fake.so中导出的函数跳转的地址。能够在此处也下个断点。

======================================================================

附注,測试这段代码时。我已经关闭了随机地址载入所以objdump -d输出的连接地址和test载入地址同样,都是0x400520。

关闭随机地址载入的方法例如以下:

[root@localhost ~]# echo 0>/proc/sys/kernel/randomize_va_space
======================================================================

(gdb) b *0x04004e8Breakpoint 2 at 0x4004e8(gdb) b *0x7ffff7dfc5bcBreakpoint 3 at 0x7ffff7dfc5bc: file fake.c, line 5.(gdb)
继续执行。程序在0x4004e8出停下后反汇编看看。然后继续执行到fake.so中

(gdb) cContinuing.Breakpoint 2, 0x00000000004004e8 in fake@plt ()(gdb) x /32i $pc=> 0x4004e8 
: jmpq *0x2004d2(%rip) # 0x6009c0
0x4004ee
: pushq $0x0 0x4004f3
: jmpq 0x4004d8
(gdb) cContinuing.Breakpoint 3, fake (s1=0x3326621188 "",     s2=0x332640e9f0 "UH\211\345AWAVAUATE1\344S1\333H\203\354HH\307E\250")    at fake.c:55	{(gdb) x /32i $pc (gdb) x /32i $pc=> 0x7ffff7dfc5bc 
:    push   %rbp   0x7ffff7dfc5bd
:    mov    %rsp,%rbp   0x7ffff7dfc5c0
:    sub    $0x10,%rsp   0x7ffff7dfc5c4
:    mov    %rdi,-0x8(%rbp)   0x7ffff7dfc5c8
:    mov    %rsi,-0x10(%rbp)   0x7ffff7dfc5cc
:    lea    0x73(%rip),%rax        # 0x7ffff7dfc646   0x7ffff7dfc5d3
:    mov    -0x10(%rbp),%rdx   0x7ffff7dfc5d7
:    mov    -0x8(%rbp),%rcx   0x7ffff7dfc5db
:    mov    %rcx,%rsi   0x7ffff7dfc5de
:    mov    %rax,%rdi   0x7ffff7dfc5e1
:    mov    $0x0,%eax   0x7ffff7dfc5e6
:    callq  0x7ffff7dfc4c0
   0x7ffff7dfc5eb
:    mov    $0x1,%edi   0x7ffff7dfc5f0
:    mov    $0x0,%eax   0x7ffff7dfc5f5
:    callq  0x7ffff7dfc4e0
   0x7ffff7dfc5fa
:    jmp    0x7ffff7dfc5eb
在这段反汇编代码中,我们看到了2个函数:printf/sleep,有点像fake.c中的代码了。

因为。fake.so是源代码编译的,能够在此看到源代码,并下断点:

0x7ffff7dfc604 <__do_global_ctors_aux+4>:	push   %rbx---Type 
to continue, or q
to quit---qQuit(gdb) list1 #include
2 #include
3 4 int fake(const char* s1,const char* s2)5 {6 printf("s1:%s-s2:%s\n",s1,s2);7 8 while(1)9 sleep(1);10 return 0;(gdb) b 8Breakpoint 4 at 0x7ffff7dfc5eb: file fake.c, line 8.(gdb)
当然,还支持查看变量:

$2 = 0x40072a "1"(gdb) p s2$3 = 0x400728 "2"(gdb)

至此。调试LD_PRELOAD注入的so文件结束,3q

qq:562703006@qq.com

转载于:https://www.cnblogs.com/liguangsunls/p/6792439.html

你可能感兴趣的文章
[优化]JavaScript 格式化带有占位符字符串
查看>>
打JAR包
查看>>
大图轮播
查看>>
UNIX环境高级编程读书笔记
查看>>
java awt 乱码问题
查看>>
矩阵中的路径
查看>>
unity回调函数范例
查看>>
linux下给php安装curl、gd(ubuntu)
查看>>
Java自带的Logger使用-代码摘要
查看>>
Java设计模式系列 — 构造器模式
查看>>
MySQL执行计划explain的key_len解析
查看>>
Windows Phone开发(9):关于页面状态 转:http://blog.csdn.net/tcjiaan/article/details/7292160...
查看>>
android 通过数组,流播放声音的方法
查看>>
Spring入门篇
查看>>
JAVA遇见HTML——JSP篇(JSP状态管理)
查看>>
启动eclipse出现错误Java was started but returned exit =一个数字
查看>>
myBatis模糊查找
查看>>
数据结构与算法之五 链接列表
查看>>
java 对象数组
查看>>
设计模式读书笔记-单件模式(创建型模式)
查看>>