首页
社区
课程
招聘
[原创]PWN入门-2-LibC取物-Ret2LibC
发表于: 2024-6-22 15:44 6522

[原创]PWN入门-2-LibC取物-Ret2LibC

2024-6-22 15:44
6522

【栈上的缓冲区溢出示例】中介绍过编译器会将数据执行保护机制打开【Linux:NX: No-Execute,Windows:DEP: Data Execution Prevention】,该机制开启后,数据所在的内存页就会被标识为不可执行的状态。

栈上存放的都是数据,因此数据执行保护机制打开时,栈所在内存页会变成不可执行的状态,此时再将Shellcode放在栈上,显然Shellcode就无法执行了。

对于GCC编译器来讲,编译选项-z execstack-z noexecstack可以打开或关闭数据执行保护机制。

在Linux中,可以通过maps虚文件查看内存布局,下面列出了当该机制打开和关闭时,栈所在内存页的状态。

数据执行保护机制需要软硬件协作实现。

对于现代CPU而言,通常会采用冯诺依曼架构,少数使用ARM-v7指令集的CPU会基于哈弗架构实现。2种架构的区别在于,哈弗架构中指令和数据的保存区域是分开的,数据区是不可执行的,而冯诺依曼架构中并没有将指令和数据进行区分。

基于冯诺依曼架构实现的CPU为了保障系统的安全性,采用添加不可执行位到页表中,使得内存管理单元MMU: Memory Manage Unit可以控制页中数据是否可以执行。

从上面的栈地址可以看到,起始和结束地址都是以x000作为结尾,这是因为Linux中默认分配的页大小为4KB(0x1000),所以使用页表机制分配的地址都会以页作为基础单位,因此内存页的起始和结束地址都以x000结尾也就不奇怪了。

MMU不止支持操作系统设置页大小,不可执行位也是交给操作系统去配置的。在硬件支持不可执行位后,需要的就是软件支持。

当需要操作系统支持数据执行保护机制时,首先面临1个问题,即操作系统从哪里得知应不应该设置不可执行位呢?

答案很简单,就是可执行文件,上面提到GCC的编译选项-z execstack-z noexecstack会标识ELF是否开启数据执行保护机制。

ELF文件是Linux下可执行文件的标准格式,由ELF头信息、头表(段头表、节头表)信息、段信息、节信息组成。其中ELF头信息描述整个ELF文件的基本信息(如字节序、文件类型、目标机器等等)。段和节分别用于在运行期和链接期提供支持,不管是段还是节,都被划分成多个类型,不同的类型负责提供不同的功能。

不同类型段和节分布在ELF文件中的不同位置上,因此使用表结构去收纳不同类型的段和节,头表中会记录不同类型段或节的基本信息(其中包含段或节在ELF文件中的位置),段和节的信息并不会被收纳,但可以根据头表找到段或节,然后再获取其中的内容。

操作系统加载可执行文件当然是运行期的事情,因此我们通过readelf工具-l参数查看ELF文件的段头表信息,在列出的段中可以看到GNU_STACK段的存在,当数据执行保护机制打开时其段属性会被设置成不可执行状态,反之则会设置成可执行状态。

Linux中一般借助execve函数启动程序,execve函数会发送系统调用给内核。

当内核收到系统调用请求后,会先检查请求的文件是否具备执行条件,如果文件可以执行就会调用load_binary接口加载ELF文件并执行。

在计算机中常常会有这样的情况,即同1个目标会可以有多个实现,这些实现即可能都需要加载,也可能视平台类型、硬件类型进行加载。为了更加方便的对这些实现进行管理,Linux会为目标设置统一的接口,接口内部会细化目标需要完成的功能,然后实现与具体的成员进行绑定,当Linux要针对某目标操作某些功能时,就会直接调用对应的成员。

不同实现对应的接口之间通常会使用链表进行管理。

比如下面是Linux平台上著名的驱动初始化代码,不同的驱动其实现不同,实现对应的函数名也会不同,如果初始化驱动时,还需要提前把每个实现的初始化函数的函数名和地址记下,在进行调用会非常麻烦。

所以Linux中规定了驱动的函数类型必须为int,那么此时只需要将初始化函数绑定到对应的结构体并注册到链表中,在初始化驱动时只需要遍历链表,再调用统一的成员名就可以,而不需要思考其他的细节。

在计算机中没什么问题是添加中间层解决不了的

当调用load_binary接口,通过load_elf_binary函数会检查段的类型及属性,其中就包含GNU_STACK段,然后根据GNU_STACK段的可执行属性设置vm_flags标志位,虚拟地址空间会根据该标志位设置页属性。

当我们观察最基本的C语言程序运行期的内存布局时,会发现C程序至少会依赖Libc、vDSO、LD三个动态链接库,并且这些动态链接库一定会存在可执行的部分,那么能否借助它们完成利用呢?

结论一定是可以的,下面会介绍C程序会什么会依赖上述3个动态链接库,以及到底该利用那个动态链接库完成PWN。

在前面查看段头表时,可以看到INTERP段指定了/lib64/ld-linux-x86-64.so.2,并且发现它的格式还是动态链接库。

要知道现如今很难找到脱离动态链接而产生的程序,即使程序内只包含main函数且main函数直接返回,这是因为main函数其实不是程序的起点,真正起点会依赖其他东西。

当程序中没有main函数进行编译时,会发现存在未定义的数据导致无法成功链接。在GCC编译器的眼中,main函数需要由_start函数调用,它是ELF文件真正的入口。

可以在GDB中可以设置参数,对main函数前运行的情况进行调试。

_start函数是与程序静态链接在一起的,不管是通过反汇编还是调试器进行观察,会发现_start函数会使用LibC中的__libc_start_main函数,这就使得程序必须与LibC建立动态链接的关系,__libc_start_main函数会对main函数的建立与退出进行处理。

当通过GDB在_start函数设置断点时,会发现有2个断点被设置下来,首先命中的是动态链接程序(也是ELF文件)的_start函数,其次才是主程序的_start函数。第一个_start函数来自动态链接库/lib64/ld-linux-x86-64.so.2,与前面INTERP段指定的动态链接库相同。

由于现在的程序都依赖动态链接库,所以Linux会先将控制权交给动态链接器ld-linux-x86-64.so.2,LD会在主程序开始运行前进行预处理,其中有2个很重要的函数dl_main_dl_start_userdl_main函数负责解释ld.so参数并加载二进制文件和库,_dl_start_user函数负责跳转到主程序的入口点,然后把控制权交给主程序。

程序的运行需要使用处理器、内存等物理资源,而物理资源是由操作系统进行管理,因此程序必须向操作系统发出请求,该请求也被称作是系统调用。

早期的X86指令集中并没有专门给系统调用提供指令,所以Linux中采取软中断int 0x80的方式发起系统调用,缺点是软中断的调用耗时较长,尽管后续指令集中添加了系统调用指令(32位:sysenter sysexit,64位:syscall sysret),但是不同位下的系统调用的指令并不相同,这对于程序而言是困难的,因为它需要思考自己如何处理多系统调用指令带来的复杂度。

为了缓解该问题保障兼容性,Linux推出vDSO vsyscall Dynamic Shared Object)机制。在Linux内核中为了支持不同处理器的系统调用指令,Linux内核会针对不同的处理器生成相应的动态链接库,直到Linux启动时,选择与处理器对应的动态链接库进行加载。当程序运行时,Linux会将vDSO分享给程序,程序可以借助vDSO发起系统调用,而无需考虑处理器不同带来的兼容性问题。

vDSO就是1个很好的中间层

当LD中的_start函数命中时,可以发现它之前的2号栈帧的的函数地址位于vsyscall的范围内。

即使是最基本的C语言程序也需要将LibC、vDSO、LD与自身进行动态链接,所以现代程序很难离开动态链接库,考虑到vDSO是内核提供的是虚拟副本且用于辅助系统调用,LD用于处理运行期的动态链接问题,它们都并不适合作为利用对象。

而LibC作为C语言的标准库,其中具有很多常见函数的实际实现,且LibC中一定存在可执行的段,因此可以考虑将LibC作为合适的利用对象。

不管是通过readelf分析二进制文件,还是通过GDB在运行期查看函数,都可以很好的观察到当前LibC中函数实现。

为了借助Libc运行期望的程序(比如打开一个shell),那么就需要借助LibC中携带的程序运行函数systemexecve,其中system函数只需要1个参数并通过shell运行,而execve函数需要需要3个参数并会独立运行程序。

从使用角度上看,system函数无疑是最方便的。

除了完整使用某可执行区域的完整函数外,也可以进一步缩小范围,选择只借用部分指令。

示例程序是1个非常简单的程序,它会从标准输入中读取内容复制给缓冲区变量buf

本次绕过的仅是数据执行保护机制,所以仍然需要关闭金丝雀的栈溢出保护机制和ASLR机制。

当ASLR被关闭后,LibC加载的地址就会固定下来,由于编译器在编译时无法确认某数据在内存中的位置(无法给出绝对定位),所以对于ELF文件自身内的数据会先分配相对偏移,当程序加载时,再给分配1个绝对地址作为起始地址,而ELF文件内的数据都会根据该起始地址进行偏移。

为了让返回地址指向Libc中system函数的所在位置,就需要确认Libc的起始地址和system函数在Libc中编译。

通过readelf查看段头信息可以知道,LibC中共有4个可加载load段,其中1个load段是可执行的,想必system等其他函数也在其中。

当LibC被加载到内存后,查看maps文件中的内存布局情况可以知道,LibC的起始地址为0x7ffff7db3000

在获取LibC的起始地址后,就需要确认system函数在ELF文件中的偏移,通过强大的readelf工具可以非常方便的获取它。

当然没有readelf工具,也可以采用手工获取的方式,下面演示了如何手动进行解析。

手工解析ELF文件及详细了解ELF文件可以参考/usr/include/elf.h头文件及https://refspecs.linuxfoundation.org/官方文档。

下面手工查找时,字节对应的含义都可以通过上面的文档查找,readelf工具的主要作用就是按照文档对ELF文件中的字节进行语义翻译。

C.1 .dynsym节分析

由于LibC是动态链接库,所以其符号应该放在.dynsym节中,而非.symtab节,在.dynsym节中表项占用0x18字节,表项中具有唯一性的标识符就是st_name,由于st_name只是索引值,所以需要先到.dynstr节中确认system名相对于.dynstr节的偏移值,然后根据偏移值查找st_name

C.2 st_name索引数值确认

通过节头表可以确认.dynstr节的起始位置是0x17c68,从该位置开始索引,可以确认system字符串所在的位置是0x1aa18,相对于0x17c68偏移了0x2d80。

C.3 system函数偏移确认

.dynsym节的起始位置是0x54b8,偏移后的位置就是0xb728,通过分析该区域的字节,可以确认是system函数所在表项,且结果可以与readelf工具读取的内容对应。

通过对比ELF文件中system函数的字节数据与system函数反汇编指令的16进制结果,可以确认system函数已经被正确的找到了。

此时就已经完成了对system函数地址的确认,之所以手工进行解析是非为了了解一些ELF文件的组成。

system函数需要接收1个字符串作为参数,这里选择/bin/sh作为参数,让其打开shell。通过strings工具可以快速进行解析,其中-a参数指搜索范围是整个文件,-tx按照16进制格式打印字符串的位置。

通过比对ASCII码可以指定/bin/sh对应2f 62 69 6e 2f 73 68 00,也可以在直接通过它对二进制文件进行检索。

因为LibC中system函数是需要1个参数的,基于当前调用协议(rdi rsi rdx rcx r8 r9),需要先将参数放入rdi寄存器中。

这个时候就需要借用某段可执行区域的部分指令了。但是问题来了,借用什么样的指令呢。

首先存放的目标是rdi寄存器,而存放的数值需要溢出到栈上,此时就需要借助pop指令从栈上取出输入放入rdi内,pop指令会从栈上取出最后1个数据,然后缩减栈顶。

在准备好待传递的形参后,就需要跳转到system函数,该函数的地址也是放到栈上的,那么这个时候就需要ret指令,ret指令的作用相当于pop rip

指令的地址可以通过ROPgadget工具进行搜索,除此之外也可以借助指令对应的字节码进行检索。

此时完整的利用链已经清晰了,可以参考下图。

链接描述

根据前面的总结,设置好LibC的基地址,已经需要使用LibC中元素的偏移,再构造字符填满缓冲区变量到调用函数栈底指针寄存器的位置,然后设置利用链,使之调用system函数。

当执行exploit后,会发现并没有弹出Shell,将GDB挂到程序上,会发现程序出现了段错误,段错误一般都是访问内存错误。

查看当前程序执行,在指令中操作的内存地址是0x50(%rsp),通过查阅资料了解到movapsa代表目标地址需要和16字节对齐,而此时的0x50(%rsp)是不能被16整除的,所以导致段错误。

16的16进制表示为0x10,所以内存地址的最后1位必须是0,上方地址的最后1位是0x8,为了让地址与16字节对齐,需要让原地址加8或减8。在上方操作rsp地址的指令为popret,它们都让rsp的地址不断递增,因此这里可以考虑再次利用它们让rsp的地址加8。

增加1条指令再调用system函数,对于这种需求显然ret指令是最合适的。

修改exploit后(system函数地址前添加),重新执行利用脚本,会发现已经成功获得Shell。

但是当程序退出时,会发现程序因为异常退出,在GDB调试上观察,可以发现,程序退出时调用ret指令,但是exploitsystem函数地址后并没有设置,因此ret会从栈上取出错误的地址并返回,如果在exploit内将LibC中exit函数的地址放到system函数地址后,使得system函数返回时可以从栈上取出exit函数,然后退出。

r: 可读, w: 可写, x: 可执行, p: 私有段, s: 共享段
 
开启数据执行保护机制:
7ffeffee2000-7ffefff03000 rwxp 00000000 00:00 0                          [stack]
 
关闭数据执行保护机制:
7fff4d273000-7fff4d294000 rw-p 00000000 00:00 0                          [stack]
r: 可读, w: 可写, x: 可执行, p: 私有段, s: 共享段
 
开启数据执行保护机制:
7ffeffee2000-7ffefff03000 rwxp 00000000 00:00 0                          [stack]
 
关闭数据执行保护机制:
7fff4d273000-7fff4d294000 rw-p 00000000 00:00 0                          [stack]
readelf -l xxxx
 
R: 可写, W: 可读, E: 可执行
 
开启数据执行保护机制:
GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                0x0000000000000000 0x0000000000000000  RW     0x10
 
关闭数据执行保护机制:
GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                0x0000000000000000 0x0000000000000000  RWE    0x10
readelf -l xxxx
 
R: 可写, W: 可读, E: 可执行
 
开启数据执行保护机制:
GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                0x0000000000000000 0x0000000000000000  RW     0x10
 
关闭数据执行保护机制:
GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                0x0000000000000000 0x0000000000000000  RWE    0x10
SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}
SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}
static int search_binary_handler(struct linux_binprm *bprm)
{
    ......
    retval = fmt->load_binary(bprm);
    ......
}
static int search_binary_handler(struct linux_binprm *bprm)
{
    ......
    retval = fmt->load_binary(bprm);
    ......
}
static struct linux_binfmt elf_format = {
    .module     = THIS_MODULE,
    .load_binary    = load_elf_binary,
    .load_shlib = load_elf_library,
#ifdef CONFIG_COREDUMP
    .core_dump  = elf_core_dump,
    .min_coredump   = ELF_EXEC_PAGESIZE,
#endif
};
static struct linux_binfmt elf_format = {
    .module     = THIS_MODULE,
    .load_binary    = load_elf_binary,
    .load_shlib = load_elf_library,
#ifdef CONFIG_COREDUMP
    .core_dump  = elf_core_dump,
    .min_coredump   = ELF_EXEC_PAGESIZE,
#endif
};
内核:我要加载驱动!!!
内核:怎么有这么多驱动啊,函数名还不一样,我要怎么样才能挨个调用!!!
内核:不如设置一种统一的接口,所有驱动都要按照接口的格式设置初始化函数,然后注册,然后我遍历链表,挨个调用就可以
内核:具体你驱动内部怎么搞,我才不管呢!
内核:我要加载驱动!!!
内核:怎么有这么多驱动啊,函数名还不一样,我要怎么样才能挨个调用!!!
内核:不如设置一种统一的接口,所有驱动都要按照接口的格式设置初始化函数,然后注册,然后我遍历链表,挨个调用就可以
内核:具体你驱动内部怎么搞,我才不管呢!
按照统一格式设置初始化函数:
static int __init xxxx(void) { ...... }
 
static void __init do_pre_smp_initcalls(void)
{
    initcall_entry_t *fn;
 
    trace_initcall_level("early");
    for (fn = __initcall_start; fn < __initcall0_start; fn++) {
        do_one_initcall(initcall_from_entry(fn));
    }
}
 
fn对应驱动初始化函数地址:
int __init_or_module do_one_initcall(initcall_t fn)
{
    ......
    do_trace_initcall_start(fn);
    ret = fn();
    do_trace_initcall_finish(fn, ret);
    ......
}
按照统一格式设置初始化函数:
static int __init xxxx(void) { ...... }
 
static void __init do_pre_smp_initcalls(void)
{
    initcall_entry_t *fn;
 
    trace_initcall_level("early");
    for (fn = __initcall_start; fn < __initcall0_start; fn++) {
        do_one_initcall(initcall_from_entry(fn));
    }
}
 
fn对应驱动初始化函数地址:
int __init_or_module do_one_initcall(initcall_t fn)
{
    ......
    do_trace_initcall_start(fn);
    ret = fn();
    do_trace_initcall_finish(fn, ret);
    ......
}
static int load_elf_binary(struct linux_binprm *bprm)
{
    ......
    for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++)
        switch (elf_ppnt->p_type) {
        case PT_GNU_STACK:
            if (elf_ppnt->p_flags & PF_X)
                executable_stack = EXSTACK_ENABLE_X;
            else
                executable_stack = EXSTACK_DISABLE_X;
            break;
 
        case PT_LOPROC ... PT_HIPROC:
            retval = arch_elf_pt_proc(elf_ex, elf_ppnt,
                          bprm->file, false,
                          &arch_state);
            if (retval)
                goto out_free_dentry;
            break;
        }
    ......
}
 
int setup_arg_pages(struct linux_binprm *bprm,
            unsigned long stack_top,
            int executable_stack)
{
    ......
    if (unlikely(executable_stack == EXSTACK_ENABLE_X))
        vm_flags |= VM_EXEC;
    else if (executable_stack == EXSTACK_DISABLE_X)
        vm_flags &= ~VM_EXEC;
    ......
}
static int load_elf_binary(struct linux_binprm *bprm)
{
    ......
    for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++)
        switch (elf_ppnt->p_type) {
        case PT_GNU_STACK:
            if (elf_ppnt->p_flags & PF_X)
                executable_stack = EXSTACK_ENABLE_X;
            else
                executable_stack = EXSTACK_DISABLE_X;
            break;
 
        case PT_LOPROC ... PT_HIPROC:
            retval = arch_elf_pt_proc(elf_ex, elf_ppnt,
                          bprm->file, false,
                          &arch_state);
            if (retval)
                goto out_free_dentry;
            break;
        }
    ......
}
 
int setup_arg_pages(struct linux_binprm *bprm,
            unsigned long stack_top,
            int executable_stack)
{
    ......
    if (unlikely(executable_stack == EXSTACK_ENABLE_X))
        vm_flags |= VM_EXEC;
    else if (executable_stack == EXSTACK_DISABLE_X)
        vm_flags &= ~VM_EXEC;
    ......
}
ldd ./example
        linux-vdso.so.1 (0x00007ffdb1ebb000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f8b16ed7000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f8b170ec000)
ldd ./example
        linux-vdso.so.1 (0x00007ffdb1ebb000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f8b16ed7000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f8b170ec000)
INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                0x000000000000001c 0x000000000000001c  R      0x1
        [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
 
file /lib64/ld-linux-x86-64.so.2
/lib64/ld-linux-x86-64.so.2: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), static-pie linked, BuildID[sha1]=c560bca2bb17f5f25c6dafd8fc19cf1883f88558, stripped
INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                0x000000000000001c 0x000000000000001c  R      0x1
        [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
 
file /lib64/ld-linux-x86-64.so.2
/lib64/ld-linux-x86-64.so.2: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), static-pie linked, BuildID[sha1]=c560bca2bb17f5f25c6dafd8fc19cf1883f88558, stripped
/usr/bin/ld: /usr/lib/gcc/x86_64-pc-linux-gnu/14.1.1/../../../../lib/Scrt1.o: in function `_start':
(.text+0x1b): undefined reference to `main'
collect2: error: ld returned 1 exit status
/usr/bin/ld: /usr/lib/gcc/x86_64-pc-linux-gnu/14.1.1/../../../../lib/Scrt1.o: in function `_start':
(.text+0x1b): undefined reference to `main'
collect2: error: ld returned 1 exit status
set backtrace past-entry
set backtrace past-main
 
(gdb) bt
#0  main () at main.c:14
#1  0x00007ffff7dd8c88 in __libc_start_call_main (main=main@entry=0x55555555516a <main>, argc=argc@entry=1, argv=argv@entry=0x7fffffffdf68)
    at ../sysdeps/nptl/libc_start_call_main.h:58
#2  0x00007ffff7dd8d4c in __libc_start_main_impl (main=0x55555555516a <main>, argc=1, argv=0x7fffffffdf68, init=<optimized out>, fini=<optimized out>,
    rtld_fini=<optimized out>, stack_end=0x7fffffffdf58) at ../csu/libc-start.c:360
#3  0x0000555555555075 in _start ()
set backtrace past-entry
set backtrace past-main
 
(gdb) bt
#0  main () at main.c:14
#1  0x00007ffff7dd8c88 in __libc_start_call_main (main=main@entry=0x55555555516a <main>, argc=argc@entry=1, argv=argv@entry=0x7fffffffdf68)
    at ../sysdeps/nptl/libc_start_call_main.h:58
#2  0x00007ffff7dd8d4c in __libc_start_main_impl (main=0x55555555516a <main>, argc=1, argv=0x7fffffffdf68, init=<optimized out>, fini=<optimized out>,
    rtld_fini=<optimized out>, stack_end=0x7fffffffdf58) at ../csu/libc-start.c:360
#3  0x0000555555555075 in _start ()
info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   <MULTIPLE>        
        breakpoint already hit 2 times
1.1                         y   0x0000555555555050 <_start>
1.2                         y   0x00007ffff7fe5740 <_start>
 
Breakpoint 1.2, 0x00007ffff7fe5740 in _start () from /lib64/ld-linux-x86-64.so.2
(gdb) c
Continuing.
[Thread debugging using libthread_db enabled]                                      
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Breakpoint 1.1, 0x0000555555555050 in _start ()
info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   <MULTIPLE>        
        breakpoint already hit 2 times
1.1                         y   0x0000555555555050 <_start>
1.2                         y   0x00007ffff7fe5740 <_start>
 
Breakpoint 1.2, 0x00007ffff7fe5740 in _start () from /lib64/ld-linux-x86-64.so.2
(gdb) c
Continuing.
[Thread debugging using libthread_db enabled]                                      
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Breakpoint 1.1, 0x0000555555555050 in _start ()
#0  0x00007ffff7fe5740 in _start () from /lib64/ld-linux-x86-64.so.2
#1  0x0000000000000001 in ?? ()
#2  0x00007fffffffe27c in ?? ()
#3  0x0000000000000000 in ?? ()
 
7ffff7fc9000-7ffff7fca000 r--p 00000000 08:01 7351295                    /usr/lib/ld-linux-x86-64.so.2
7ffff7fca000-7ffff7ff1000 r-xp 00001000 08:01 7351295                    /usr/lib/ld-linux-x86-64.so.2
7ffff7ff1000-7ffff7ffb000 r--p 00028000 08:01 7351295                    /usr/lib/ld-linux-x86-64.so.2
7ffff7ffb000-7ffff7fff000 rw-p 00032000 08:01 7351295                    /usr/lib/ld-linux-x86-64.so.2
 
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]
#0  0x00007ffff7fe5740 in _start () from /lib64/ld-linux-x86-64.so.2
#1  0x0000000000000001 in ?? ()
#2  0x00007fffffffe27c in ?? ()
#3  0x0000000000000000 in ?? ()
 
7ffff7fc9000-7ffff7fca000 r--p 00000000 08:01 7351295                    /usr/lib/ld-linux-x86-64.so.2
7ffff7fca000-7ffff7ff1000 r-xp 00001000 08:01 7351295                    /usr/lib/ld-linux-x86-64.so.2
7ffff7ff1000-7ffff7ffb000 r--p 00028000 08:01 7351295                    /usr/lib/ld-linux-x86-64.so.2
7ffff7ffb000-7ffff7fff000 rw-p 00032000 08:01 7351295                    /usr/lib/ld-linux-x86-64.so.2
 
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]
readelf工具观察:
 readelf -s /usr/lib/libc.so.6 | grep execve
  1593: 00000000000e0fb0    37 FUNC    WEAK   DEFAULT   15 execve@@GLIBC_2.2.5
  2922: 00000000000e1550   102 FUNC    GLOBAL DEFAULT   15 fexecve@@GLIBC_2.2.5
  3065: 00000000000e0fe0    50 FUNC    GLOBAL DEFAULT   15 execveat@@GLIBC_2.34
 
GDB调试器观察:
(gdb) info functions
All defined functions:
 
File main.c:
13:     int main(int, char **);
5:      static void simple_overflow(char *);
 
Non-debugging symbols:
0x0000555555555000  _init
0x0000555555555030  strcpy@plt
0x0000555555555040  puts@plt
--Type <RET> for more, q to quit, c to continue without paging--
0x0000555555555050  __stack_chk_fail@plt
0x0000555555555060  printf@plt
0x0000555555555070  getchar@plt
0x0000555555555080  _start
0x000055555555523c  _fini
0x00007ffff7fca1f0  _dl_signal_exception
0x00007ffff7fca250  _dl_signal_error
0x00007ffff7fca480  _dl_catch_exception
0x00007ffff7fcb670  _dl_debug_state
0x00007ffff7fcc990  _dl_exception_create
--Type <RET> for more, q to quit, c to continue without paging--
0x00007ffff7fcca60  _dl_exception_create_format
0x00007ffff7fccf00  _dl_exception_free
0x00007ffff7fcd0a0  __nptl_change_stack_perm
0x00007ffff7fd26b0  _dl_rtld_di_serinfo
0x00007ffff7fd53c0  _dl_find_dso_for_object
0x00007ffff7fd6e70  _dl_fatal_printf
0x00007ffff7fdb100  _dl_get_tls_static_info
0x00007ffff7fdb1f0  _dl_allocate_tls_init
0x00007ffff7fdb480  _dl_allocate_tls
0x00007ffff7fdb4c0  _dl_deallocate_tls
--Type <RET> for more, q to quit, c to continue without paging--
0x00007ffff7fdc430  __tunable_is_initialized
0x00007ffff7fdc760  __tunable_get_val
0x00007ffff7fde4a0  __tls_get_addr
0x00007ffff7fe11e0  _dl_x86_get_cpu_features
0x00007ffff7fe14e0  _dl_audit_preinit
0x00007ffff7fe1570  _dl_audit_symbind_alt
0x00007ffff7fe4080  _dl_mcount
0x00007ffff7ff0f50  __rtld_version_placeholder
0x00007ffff7fc77b0  __vdso_gettimeofday
0x00007ffff7fc77b0  gettimeofday
--Type <RET> for more, q to quit, c to continue without paging--
0x00007ffff7fc7a30  __vdso_time
0x00007ffff7fc7a30  time
0x00007ffff7fc7a60  __vdso_clock_gettime
0x00007ffff7fc7a60  clock_gettime
0x00007ffff7fc7da0  __vdso_clock_getres
0x00007ffff7fc7da0  clock_getres
0x00007ffff7fc7e10  __vdso_getcpu
0x00007ffff7fc7e10  getcpu
readelf工具观察:
 readelf -s /usr/lib/libc.so.6 | grep execve
  1593: 00000000000e0fb0    37 FUNC    WEAK   DEFAULT   15 execve@@GLIBC_2.2.5
  2922: 00000000000e1550   102 FUNC    GLOBAL DEFAULT   15 fexecve@@GLIBC_2.2.5
  3065: 00000000000e0fe0    50 FUNC    GLOBAL DEFAULT   15 execveat@@GLIBC_2.34
 
GDB调试器观察:
(gdb) info functions
All defined functions:
 
File main.c:
13:     int main(int, char **);
5:      static void simple_overflow(char *);
 
Non-debugging symbols:
0x0000555555555000  _init
0x0000555555555030  strcpy@plt
0x0000555555555040  puts@plt
--Type <RET> for more, q to quit, c to continue without paging--
0x0000555555555050  __stack_chk_fail@plt
0x0000555555555060  printf@plt
0x0000555555555070  getchar@plt
0x0000555555555080  _start
0x000055555555523c  _fini
0x00007ffff7fca1f0  _dl_signal_exception
0x00007ffff7fca250  _dl_signal_error
0x00007ffff7fca480  _dl_catch_exception
0x00007ffff7fcb670  _dl_debug_state
0x00007ffff7fcc990  _dl_exception_create

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//