Mengzelev's Blog

Lab2:链接与加载 实验报告

Word count: 1,046 / Reading time: 4 min
2018/11/06 Share

实验过程

按照实验讲义的提示,首先找到了libc库所在的位置,输出基本与讲义中给出的一致,不作赘述。

编写的测试文件a.c的代码为

#include <stdio.h>
#include <stdlib.h>

int main (){
    int a = rand();
    printf("%d\n",a);
    return 0;
}

只需要有对rand的调用且保证不会被编译器优化掉就可以了。

找到动态库文件之后,对该二进制文件进行反汇编

$ objdump -d /lib/x86_64-linux-gnu/libc.so.6 > libc64

然后对反汇编后的文件进行查看,可以看到一堆库函数的反汇编代码。此时我看到了讲义中的这句话:

然后你会发现这个文件对你并没有什么卵用,好吧,其实一会儿是会用到的。

导致我真的以为现在用不到这个文件,于是,我傻fufu地按照讲义的指示去找rand函数的地址了。
gdb,启动!看到了一堆跳转语句,感受到了自己对课本内容掌握不足;
课本,打开!复习了一下动态链接相关的知识点;
gdb,开始!单步执行到<rand@plt>处时,发现了rand对应的GOT表项的地址,为0x55555575020

然后疯狂si,会看一堆dl_开头的,八成是和动态链接有关的函数。一不小心回车过头,最后在main里停下来,总之动态链接过程已经完成了,这时候可以查看内存地址为0x55555575020处的内容为0x7ffff7a70820(忘了截图了)

打印0x7ffff7a70820的内容,确认了确实为rand函数的地址

做到这里,感觉像是完成了什么,开始思考下一步要做什么。等一下,我们的最终目标不是只要得到systemrand的偏移量吗,那我费尽千辛万苦搞到rand的地址干什么……算了,就当复习了一下动态链接的相关知识点和回忆了一下gdb的使用方法吧

不管了,总之先打开之前反汇编的动态库看一下,分别找到randsystem的地址

偏移量为0x3f480-0x36820=0x8c60

为了验证一下正确性,使用gdb打印了和rand具有相应偏移量(其实就是后3位不同)出的内存内容

这下准没错了,于是开始编写oj_killer函数

代码解释

最初仅考虑64位情况的oj_killer函数代码如下

#include <stdlib.h>

void* oj_killer(){
        void* randptr = (void *)rand;
        //获得rand函数的地址
        return (void*)(randptr + 0x8c60);
        //加上libc中rand到system的偏移量,返回system函数的地址
}

编译运行后能够成功输出Hello World

兼容32位

接下来考虑32位的情况

如果将上述程序直接编译为32位,会得到Illegal instruction的错误

a.c加上-m32选项进行编译后查看其调用的动态链接库,可以发现调用的是32位的库,和64位的库是不一样的(当然了

$gcc a.c -m32 -o a32.out
$file a32.out
a32.out: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically link│
ed, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b3e591a0858f18│
2549e63239c338f5fd30496ce2, not stripped 

$ldd a32.out                                          
    linux-gate.so.1 (0xf77ac000)                                                  
    libc.so.6 => /lib32/libc.so.6 (0xf75d1000)                                    
    /lib/ld-linux.so.2 (0xf77ae000)

$file -L /lib32/libc.so.6                             
/lib32/libc.so.6: ELF 32-bit LSB shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=84bb53910470c7ceb2a0963044117fdf8a2bb975, for GNU/Linux 2.6.32, stripped  

使用同样的方法进行反汇编后

$objdump -d /lib32/libc.so.6 > libc32

分别得到randsystemlibc内的地址

于是可以按照同样的思路编写代码

真·代码解释

void* oj_killer(){
    int type = sizeof(void*);
    void* randptr = (void *)rand;
    //获得rand函数的地址
    if(type == 8)     //判断当前环境是否为64-bit
        return (void*)(randptr + 0x8c60);    //加上64位时libc中的偏移量
    else return (void*)(randptr - 0x2f6f0 + 0x3a850);
         //加上32位时libc的偏移量
}

为了保证代码的可移植性,特意将变量randptr声明为void*类型,而非intlong long
判断当前环境为32/64位则依靠对void*长度的判断。 暴露了不太会用#if预编译指令的事实

分别按32位和64位编译后,可以得到Hello World的输出:

CATALOG
  1. 1. 实验过程
  2. 2. 代码解释
  3. 3. 兼容32位
  4. 4. 真·代码解释