PWN学习

二进制基础#

程序的编译过程#

可执行文件#

什么是可执行文件#

​ 广义:文件中的数据是可执行代码的文件

​ .out .exe .sh .py

​ 狭义:文件中的数据是机器码的文件

​ .out .exe .dll .so

可执行文件分类#

​ Windows : PE(Portable Executable)

​ 可执行程序:.exe 动态链接库:.dll 静态链接库:.lib

​ Linux : ELF(Executable and Linkable Format)

​ 可执行程序:.out 动态链接库:.so 静态链接库:.a

Windows下识别文件类型主要是看其后缀名

Linux下并非如此,而是依靠每个文件的文件头区分,可以使用file指令查看文件具体的文件类型

ELF#

补充ELF基础内容

大端序和小端序#

补充大端序小端序内容

程序在内存中的分布#

在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中。这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块。在Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,而Windows系统为2:2(通过设置Large-Address-Aware Executables标志也可为1:3)。这并不意味着内核使用那么多物理内存,仅表示它可支配这部分地址空间,根据需要将其映射到物理内存。

首先我们来看当一个C语言代码在执行的时候,操作系统是如何调度内存将数据存放并且完成相关函数操作的。

从图中可以看出C程序被编译成可执行文件执行时,它在内存中的存储情况。右侧是一个内存空间,地址由底部(低地址)逐渐升高。其中,最上层的kernel是操作系统的核心源码,他是操作系统完成各项功能的关键,最下层是保留区,这一部分我们暂时不做深入的研究。在早期的学习中,我们关注的是Stack(栈),Heap(堆),BSS(静态内存分配)。

  • 代码段(.text),也称文本段(Text Segment),存放着程序的机器码和只读数据,可执行指令就是从这里取得的。如果可能,系统会安排好相同程序的多个运行实体共享这些实例代码。这个段在内存中一般被标记为只读,任何对该区的写操作都会导致段错误(Segmentation Fault)。
  • 数据段,包括已初始化的数据段(.data)和未初始化的数据段(.bss),前者用来存放保存全局的和静态的已初始化变量,后者用来保存全局的和静态的未初始化变量。数据段在编译时分配。
  • 堆栈段分为堆和栈:
    • (Heap):用来存储程序运行时分配的变量。堆的大小并不固定,可动态扩张或缩减。其分配由malloc()、new()等这类实时内存分配函数来实现。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减),堆的内存释放由应用程序去控制,通常一个new()就要对应一个delete(),如果程序员没有释放掉,那么在程序结束后操作系统会自动回收。
    • (Stack)是一种用来存储函数调用时的临时信息的结构,如函数调用所传递的参数、函数的返回地址、函数的局部变量等。在程序运行时由编译器在需要的时候分配,在不需要的时候自动清除。栈具有先进后出的特性。栈的基本操作:PUSH操作:向栈中添加数据,称为压栈,数据将放置在栈顶;POP操作:POP操作相反,在栈顶部移去一个元素,并将栈的大小减一,称为弹栈。

堆和栈的区别:

  1. 分配和管理方式不同
    • 堆是动态分配的,其空间的分配和释放都由程序员控制。
    • 栈由编译器自动管理。栈有两种分配方式:静态分配和动态分配。
      • 静态分配由编译器完成,比如局部变量的分配。
      • 动态分配由malloc()函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无须手工控制。
  2. 产生碎片不同
    • 对堆来说,频繁的new/delete或者malloc/free势必会造成内存空间的不连续,造成大量的碎片,使程序效率降低。
    • 对栈而言,则不存在碎片问题,因为栈是先进后出的队列,永远不可能有一个内存块从栈中间弹出。
  3. 生长方向不同
    • 堆是向着内存地址增加的方向增长的,从内存的低地址向高地址方向增长。
    • 栈的生长方向与之相反,是向着内存地址减小的方向增长,由内存的高地址向低地址方向增长。

ELF文件中通常存在.GOT.PLT.PLT这两个特殊的节,ELF编译时无法知道libc等动态链接库的加载地址。如果一个程序想调用动态链接库中的函数,就必须使用.GOT.PLT和.PLT配合完成调用。

栈帧结构及工作方式#

首先我们需要了解一下栈中常用的3个寄存器,64位CPU对应rsp, rbp, rip三个寄存器,而32位CPU则对应esp, ebp, eip三个寄存器。然后我们了解一下栈帧的概念,一个栈帧就是保存一个函数的状态,简单来说就是一个函数所需要的栈空间。

  • rsp/esp 用来存储函数调用栈的栈顶地址,在压栈和退栈时发生变化。
  • rbp/ebp 用来存储当前函数状态的基地址,在函数运行时不变,可以用来索引确定函数参数或局部变量的位置。
  • rip/eip 用来存储即将执行的程序指令的地址,cpu 依照 eip 的存储内容读取指令并执行,eip 随之指向相邻的下一条指令,如此反复,程序就得以连续执行指令。

函数调用时,栈顶函数状态以及上述寄存器的变化。变化的核心任务是将调用函数(caller)的状态保存起来,同时创建被调用函数(callee)的状态。

  1. 首先将被调用函数(callee)的参数按照逆序依次压入栈内。如果被调用函数(callee)不需要参数,则没有这一步骤。这些参数仍会保存在调用函数(caller)的函数状态内,之后压入栈内的数据都会作为被调用函数(callee)的函数状态来保存。
  2. 然后将调用函数(caller)进行调用之后的下一条指令地址作为返回地址压入栈内。这样调用函数(caller)的 eip(指令)信息得以保存。
  3. 再将当前的ebp 寄存器的值(也就是调用函数的基地址)压入栈内,并将 ebp 寄存器的值更新为当前栈顶的地址。这样调用函数(caller)的 ebp(基地址)信息得以保存。同时,ebp 被更新为被调用函数(callee)的基地址。
  4. 再之后是将被调用函数(callee)的局部变量等数据压入栈内。

在发生调用时,程序还会将被调用函数(callee)的指令地址存到 eip 寄存器内,这样程序就可以依次执行被调用函数的指令了。

调用结束后,变化的核心任务是丢弃被调用函数(callee)的状态,并将栈顶恢复为调用函数(caller)的状态。

  1. 首先被调用函数的局部变量会从栈内直接弹出,栈顶会指向被调用函数(callee)的基地址。
  2. 然后将基地址内存储的调用函数(caller)的基地址从栈内弹出,并存到 ebp 寄存器内。这样调用函数(caller)的 ebp(基地址)信息得以恢复。此时栈顶会指向返回地址。
  3. 再将返回地址从栈内弹出,并存到 eip 寄存器内。这样调用函数(caller)的 eip(指令)信息得以恢复。

至此调用函数(caller)的函数状态就全部恢复了,之后就是继续执行调用函数的指令了。

参数存放:

  • x86
    • 使用栈来传递参数
    • 使用 eax 存放返回值
  • amd64
    • 前6个参数依次存放于 rdi、rsi、rdx、rcx、r8、r9 寄存器中
    • 第7个以后的参数存放于栈中

缓冲区溢出(Buffer overflow)#

本质是向定长的缓冲区中写入了超长的数据,造成超出的数据覆写合法内存

  • 栈溢出(Stack overflow)
  • 堆溢出(Heap overflow)
  • Data段溢出

栈溢出#

ret2XXX#

ret2text#

篡改栈帧上的返回地址为程序中已有的后门函数。

ret2shellcode#

篡改栈帧上的返回地址为攻击者手动传入的 shellcode 所在缓冲区地址。

初期往往将 shellcode 直接写入栈缓冲区,目前由于 the NX bits 保护措施的开启,栈缓冲区不可执行,故当下的常用手段变为向 bss 缓冲区写入 shellcode 或向堆缓冲区写入 shellcode 并使用 mprotect 赋予其可执行权限。

ret2syscall#

ret2libc#

ret2csu#

动态链接#



参考链接:

XMCVE 2020 CTF Pwn入门课程_哔哩哔哩_bilibili

你终于回来了(。・∀・)ノ (cnblogs.com)

程序在内存中的分布 - ProLyn - 博客园 (cnblogs.com)

Linux虚拟地址空间布局 - clover_toeic - 博客园 (cnblogs.com)

C程序在内存中的栈_哔哩哔哩_bilibili

posted @   Amsterdamnit  阅读(75)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩