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

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

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

//true.c
int fake(const char* s1,const char* s2)
{
return 0;
} gcc -g3 -O0 -o libtrue.so true.c -fPIC -shared
echo "/root/Desktop">>/etc/ld.so.conf
ldconfig

这差点儿相同是个空函数。

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

//fake.c
#include <string.h>
#include <stdio.h> int fake(const char* s1,const char* s2)
{
printf("s1:%s-s2:%s\n",s1,s2); while(1)
sleep(1);
return 0;
} Makefile
all:
    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 <string.h>
#include <unistd.h> 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.c
strip --strip-all test

为了比較真实的模拟由他人公布的程序的环境,对于test.c不仅不生成调试信息,同一时候还剥离符号表。

以下開始注入并启动调试。

export LD_PRELOAD=/root/Desktop/fake.so
gdb 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 *0x400520
Breakpoint 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/test
00600000-00601000 rw-p 00000000 fd:00 131451                             /root/Desktop/test
3326400000-3326420000 r-xp 00000000 fd:00 75830                          /lib64/ld-2.12.so
332661f000-3326620000 r--p 0001f000 fd:00 75830                          /lib64/ld-2.12.so
3326620000-3326621000 rw-p 00020000 fd:00 75830                          /lib64/ld-2.12.so
3326621000-3326622000 rw-p 00000000 00:00 0
3326c00000-3326d8b000 r-xp 00000000 fd:00 75831                          /lib64/libc-2.12.so
3326d8b000-3326f8a000 ---p 0018b000 fd:00 75831                          /lib64/libc-2.12.so
3326f8a000-3326f8e000 r--p 0018a000 fd:00 75831                          /lib64/libc-2.12.so
3326f8e000-3326f8f000 rw-p 0018e000 fd:00 75831                          /lib64/libc-2.12.so
3326f8f000-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.so
7ffff7bee000-7ffff7ded000 ---p 00001000 fd:00 136044                     /root/Desktop/libtrue.so
7ffff7ded000-7ffff7dee000 rw-p 00000000 fd:00 136044                     /root/Desktop/libtrue.so
7ffff7dfc000-7ffff7dfd000 r-xp 00000000 fd:00 136014                     /root/Desktop/fake.so
7ffff7dfd000-7ffff7ffc000 ---p 00001000 fd:00 136014                     /root/Desktop/fake.so
7ffff7ffc000-7ffff7ffd000 rw-p 00000000 fd:00 136014                     /root/Desktop/fake.so
7ffff7ffd000-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 test

Disassembly of section .plt:

00000000004004d8 <fake@plt-0x10>:
  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 <fake@plt>:
  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 <fake@plt>:
  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 *0x04004e8
Breakpoint 2 at 0x4004e8
(gdb) b *0x7ffff7dfc5bc
Breakpoint 3 at 0x7ffff7dfc5bc: file fake.c, line 5.
(gdb)

继续执行。程序在0x4004e8出停下后反汇编看看。然后继续执行到fake.so中

(gdb) c
Continuing. Breakpoint 2, 0x00000000004004e8 in fake@plt ()
(gdb) x /32i $pc
=> 0x4004e8 <fake@plt>: jmpq *0x2004d2(%rip) # 0x6009c0 <fake@got.plt>
0x4004ee <fake@plt+6>: pushq $0x0
0x4004f3 <fake@plt+11>: jmpq 0x4004d8
(gdb) c
Continuing. Breakpoint 3, fake (s1=0x3326621188 "",
s2=0x332640e9f0 "UH\211\345AWAVAUATE1\344S1\333H\203\354HH\307E\250")
at fake.c:5
5 {
(gdb) x /32i $pc
 (gdb) x /32i $pc
=> 0x7ffff7dfc5bc <fake>:    push   %rbp
   0x7ffff7dfc5bd <fake+1>:    mov    %rsp,%rbp
   0x7ffff7dfc5c0 <fake+4>:    sub    $0x10,%rsp
   0x7ffff7dfc5c4 <fake+8>:    mov    %rdi,-0x8(%rbp)
   0x7ffff7dfc5c8 <fake+12>:    mov    %rsi,-0x10(%rbp)
   0x7ffff7dfc5cc <fake+16>:    lea    0x73(%rip),%rax        # 0x7ffff7dfc646
   0x7ffff7dfc5d3 <fake+23>:    mov    -0x10(%rbp),%rdx
   0x7ffff7dfc5d7 <fake+27>:    mov    -0x8(%rbp),%rcx
   0x7ffff7dfc5db <fake+31>:    mov    %rcx,%rsi
   0x7ffff7dfc5de <fake+34>:    mov    %rax,%rdi
   0x7ffff7dfc5e1 <fake+37>:    mov    $0x0,%eax
   0x7ffff7dfc5e6 <fake+42>:    callq  0x7ffff7dfc4c0 <printf@plt>
   0x7ffff7dfc5eb <fake+47>:    mov    $0x1,%edi
   0x7ffff7dfc5f0 <fake+52>:    mov    $0x0,%eax
   0x7ffff7dfc5f5 <fake+57>:    callq  0x7ffff7dfc4e0 <sleep@plt>
   0x7ffff7dfc5fa <fake+62>:    jmp    0x7ffff7dfc5eb <fake+47>

在这段反汇编代码中,我们看到了2个函数:printf/sleep,有点像fake.c中的代码了。

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

  0x7ffff7dfc604 <__do_global_ctors_aux+4>:	push   %rbx
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) list
1 #include <string.h>
2 #include <stdio.h>
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 8
Breakpoint 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

调试LD_PRELOAD注入的代码的更多相关文章

  1. php防止sql注入漏洞代码 && 几种常见攻击的正则表达式

    注入漏洞代码和分析                                                                                           ...

  2. 代码注入——c++代码注入

    代码注入之——c++代码注入 0x00  代码注入和DLL注入的区别 DLL注入后DLL会通过线程常驻在某个process中,而代码注入完成之后立即消失. 代码注入体积小,不占内存 0x01  通过c ...

  3. 如何向AcmeAir注入问题代码

    为什么要注入问题代码? AcmeAir的常规代码是为了压测测试准备的,所以绝大部分的操作都是可以在几十毫秒中就可以正常返回的.为了向用户展示我们APM工具可以在源代码级别发现系统潜在问题,我们需要在A ...

  4. alert一般用来调试客户端的javascript代码,以及更好的调试方法

    alert一般用来调试客户端的javascript代码 调试利器--console.log 如今主流浏览器(Chrome,IE8及后续版本,FireFox,Opera等)都支持控制台功能. Chrom ...

  5. php防sql注入过滤代码

    防止sql注入的函数,过滤掉那些非法的字符,提高sql安全性,同时也可以过滤XSS的攻击. function filter($str) { if (empty($str)) return false; ...

  6. 20.Ecshop 2.x/3.x SQL注入/任意代码执行漏洞

    Ecshop 2.x/3.x SQL注入/任意代码执行漏洞 影响版本: Ecshop 2.x Ecshop 3.x-3.6.0 漏洞分析: 该漏洞影响ECShop 2.x和3.x版本,是一个典型的“二 ...

  7. LINUX上使用GDB单步调试Chromium Android C++代码。

    ###动机###在LINUX使用GDB单步调试Chromium Android C++代码. [1]编译android平台Chromium, 修改GN文件中编译选项:-g -O0 使得编译优化更少,便 ...

  8. 学习构建调试Linux内核网络代码的环境MenuOS系统

    构建调试Linux内核网络代码的环境MenuOS系统 一.前言 这是网络程序设计的第三次实验,主要是学习自己编译linux内核,构建一个具有简易功能的操作系统,同时在系统上面进行调试linux内核网络 ...

  9. Spring4 -03 -Dependency Injection (依赖注入) : 代码体现/配置xml/测试

    DI:中文名称:依赖注入 英文名称((Dependency Injection) DI 是什么? 3.1 DI 和IoC 是一样的,差不多一样的技术和模板! 3.2 当一个类(A)中需要依赖另一个类( ...

随机推荐

  1. 洛谷P2664 树上游戏 【点分治 + 差分】

    题目 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 现在他想让你求出所有的sum[i] 输入格式 第一行为一个整数n,表示树节点的数量 ...

  2. element-ui 的 upload组件的clearFiles方法调用方法

    <template> <div> <el-button @click="clearUploadedImage">重新上传</el-butt ...

  3. FWT 学习笔记

    FWT学习笔记 好久以前写的,先粘上来 定义数组 \(n=2^k\) \(A=[a_0,a_1,a_2,a_3,...,a_{n-1}]\) 令\(A_0=[a_0,a_1,a_2,...,a_{\f ...

  4. java8 函数式接口——Function/Predict/Supplier/Consumer

    Function 我们知道Java8的最大特性就是函数式接口.所有标注了@FunctionalInterface注解的接口都是函数式接口,具体来说,所有标注了该注解的接口都将能用在lambda表达式上 ...

  5. touch下拉刷新

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 最有用的java面试题

    1.什么是线程局部变量?(答案) 线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享.Java 提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式.但是 ...

  7. linux下 open fopen区别

    open是linux下的底层系统调用函数,fopen与freopen c/c++下的标准I/O库函数,带输入/输出缓冲.linxu下的fopen是open的封装函数,fopen最终还是要调用底层的系统 ...

  8. 转载 关于malloc

    1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针.如果分配失败,则返回一个空指针(NULL). 关于分配失败 ...

  9. Linux 之 LNMP服务器搭建-前期准备

    LNMP服务器搭建-前期准备 参考教程:[千峰教育] 系统环境: 系统:centos 6.8. 软件安装位置: (1)软件源代码包存放位置:/lnmp/src 命令:mkdir -p /lnmp/sr ...

  10. AC日记——软件包管理器 洛谷 P2416

    题目描述 Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个 ...