当前位置: 首页 > news >正文

pwn知识点——字节流

字节流(Byte Stream)在 Pwn 中至关重要,它直接涉及数据的原始字节处理,是理解漏洞和构造利用载荷(Payload)的基础。

一、字节流的基本概念

字节流是字节的序列,不包含任何固有格式或解释。在 C 语言中,字节流通常用 char数组(或 unsigned char数组)表示,每个元素占 1 字节。

在 Pwn 中,许多输入输出操作(例如通过 read, write系统调用,或 C 库函数 fgets, printf)本质上都是在处理字节流。漏洞常源于程序如何处理这些字节流,例如未对长度进行正确检查,或错误地解释了流中的内容。

二、Pwn 中字节流的重要性

  1. 数据传输与输入向量​:程序通常从外部(如网络、文件、用户输入)获取字节流。​攻击面通常就在这里,例如,向程序发送精心构造的字节流可以触发缓冲区溢出、格式化字符串等漏洞。

  2. 漏洞利用的载体​:成功利用漏洞通常需要向目标程序发送特定的字节流序列(即 ​Payload)。Payload 是精确计算的字节序列,可能包含:

    • 填充数据​ (Junk data):用于填充缓冲区直至覆盖目标地址(如返回地址、函数指针)。

    • 地址数据​:用于覆盖目标地址,指向 Shellcode、Gadgets 或特定函数(如 system)。

    • Shellcode​:一段实现特定功能(如获取 shell)的机器指令字节序列。

    • 格式化字符串​:利用 %n等格式化符向任意地址写入数据的特殊字节流。

  3. 内存操作的基石​:理解程序如何将字节流写入内存至关重要,这涉及字节序​(Endianness)和内存布局

    • 字节序​:指多字节数据(如地址)在内存中的存储顺序。

      • 小端序​ (Little-endian):低位字节存储在低地址。x86/x86-64 架构常用此序。

      • 大端序​ (Big-endian):高位字节存储在低地址。

    • 在构造 Payload 时,写入的地址值必须符合目标架构的字节序。Pwntools 等工具中的 p32(), p64()函数能方便地将整数打包为小端序格式的字节流。

三、常见漏洞中的字节流操作

下面是一个表格,对比了常见漏洞中的字节流操作:

漏洞类型

危险的字节流操作函数

关键的字节流操作

利用目标

栈溢出

gets, strcpy, scanf(无长度限制), read(无检查)

输入超长字节流,覆盖返回地址或函数指针

跳转至 Shellcode 或 ROP Chain

堆溢出

strcpy, memcpy等向堆块写数据且不检查边界

覆盖相邻堆块的元数据或用户数据,可能引发任意地址写

改写 __free_hook__malloc_hook

格式化字符串

printf, sprintf, fprintf等用户可控格式化字符串

使用 %x等泄露内存中的字节流;使用 %n将已输出的字节数(数字)写入指定地址,从而修改内存

泄露关键地址,修改返回地址或 GOT 表项

四、相关工具与函数

分析漏洞和构造字节流 Payload 时,常用以下工具和函数:

  • PWNTools​:Python库,提供p32(),p64(),u32(),u64()等函数处理字节序转换,方便远程交互和本地调试。

  • GDB​:调试器,用于动态分析程序如何处理输入的字节流,查看内存中的字节状态。

C语言中常见的危险函数​(在处理字节流时易引发漏洞)包括:

  • gets(char *s): ​极度危险,从不检查输入长度。

  • strcpy(char *dest, const char *src): 不检查目标缓冲区大小。

  • strcat(char *dest, const char *src): 同上。

  • sprintf(char *str, const char *format, ...): 可能造成目标数组溢出。

  • scanf, printf系列: 使用不当会导致漏洞。

相对安全函数​(鼓励使用):

  • fgets(char *s, int size, FILE *stream): 指定读取最大长度。

  • strncpy(char *dest, const char *src, size_t n): 指定最大拷贝字符数。

  • snprintf(char *str, size_t size, const char *format, ...): 指定目标缓冲区大小。

五、应用——u64,p64,u32,p32

在 Pwn 漏洞利用中,经常需要将整数(如内存地址)和特定格式的字节流进行转换。Pwntools 库提供的 p32, p64, u32, u64这些函数就是为了方便地处理这种转换,它们会自动处理字节序(默认小端序)​

1. 基本功能与区别
  • p32p64(Pack)​

    • 功能:将整数打包为特定长度的字节流​(字符串形式)。

    • p32(num):将 32 位整数(如 0xdeadbeef)打包为 ​4 字节的小端序字节流(如 b'\xef\xbe\xad\xde')。

    • p64(num):将 64 位整数打包为 ​8 字节的小端序字节流(如 p64(0xdeadbeef)可能产生 b'\xef\xbe\xad\xde\x00\x00\x00\x00',高位不足会补零)。

    • 主要用于构造 Payload,例如将函数地址(整数形式)转换为内存中存储的字节形式,用于覆盖返回地址或函数指针。

  • u32u64(Unpack)​

    • 功能:将特定长度的字节流解包为整数

    • u32(bytes):将 ​4 字节的字节流(如 b'\xef\xbe\xad\xde')解包为一个 32 位整数(如 0xdeadbeef)。

    • u64(bytes):将 ​8 字节的字节流解包为一个 64 位整数。

    • 主要用于解析程序泄漏的信息,例如从程序输出中提取函数的内存地址(这些地址通常以原始字节流形式出现)。

u32u64主要使用场景及特征提示:

  1. 解析程序泄漏的内存地址

    • 特征提示​:当你利用漏洞(如格式化字符串或栈溢出)促使程序输出某些内存区域的内容​(例如 GOT 表项、栈上的返回地址、libc 中的函数地址),你收到的会是原始的字节数据(例如 b'\xef\xbe\xad\xde')。这些数据就是内存地址在特定架构下的字节表示。

    • 为何使用​:为了计算 libc 基地址或其他关键函数的真实地址,你需要将这些字节数据转换为整数进行计算。例如,泄漏出 puts函数在 GOT 表中的地址后,用 u64(leaked_data)(64位)或 u32(leaked_data)(32位)将其转换为整数,然后减去 libc 中 puts的偏移量来得到 libc 的基地址。

  2. 处理网络数据或文件中的结构化数据

    • 特征提示​:如果程序从网络接收或从文件读取包含了打包整数(如长度字段、魔数、关键偏移量)的结构化数据,并且你需要将这些字段作为整数进行逻辑判断或计算时。

    • 为何使用​:这些整数在传输或存储时通常会被打包成字节流(例如小端序)。收到后,你需要用 u32u64将其解包回整数以便使用。

  3. 验证或利用特定数值

    • 特征提示​:在漏洞利用过程中,有时需要比较或验证从程序接收到的某个值(例如一个 Canary 值或特定的标记值)。

    • 为何使用​:将这些字节流解包为整数后,可以更方便地进行数值比较和条件判断。】

2.关于字节序(Endianness)​
  • 计算机存储多字节数据(如地址)时有字节序之分。常见架构如 x86 和 x86-64 采用小端序,即数据的低位字节存储在低内存地址,高位字节存储在高内存地址。

  • p32, p64, u32, u64这些函数默认使用小端序,因为它们的设计初衷就是为了处理这些架构的程序

3.示例代码

下面是一个简单的例子,展示如何使用这些函数:

from pwn import *  # 导入pwntools# p32 示例:整数 -> 字节流
addr_32 = 0x8048000
packed_data_32 = p32(addr_32)
print(f"p32(0x{addr_32:x}) = {packed_data_32}")
# 输出类似:p32(0x8048000) = b'\x00\x80\x04\x08'# p64 示例:整数 -> 字节流
addr_64 = 0x7ffff7dcfd00
packed_data_64 = p64(addr_64)
print(f"p64(0x{addr_64:x}) = {packed_data_64}")
# 输出类似:p64(0x7ffff7dcfd00) = b'\x00\xfd\xdc\xf7\xff\x7f\x00\x00'# u32 示例:字节流 -> 整数
# 假设从程序输出中接收到4字节:puts函数的真实地址
received_bytes_32 = b'\x50\xfd\xdc\xf7'  
unpacked_addr_32 = u32(received_bytes_32)
print(f"u32(b'\\x50\\xfd\\xdc\\xf7') = 0x{unpacked_addr_32:x}")
# 输出:u32(b'\x50\xfd\xdc\xf7') = 0xf7dcfd50 (具体值取决于接收到的数据)# u64 示例:字节流 -> 整数
# 假设从程序输出中接收到8字节:某个64位地址
received_bytes_64 = b'\x00\xfd\xdc\xf7\xff\x7f\x00\x00'
unpacked_addr_64 = u64(received_bytes_64)
print(f"u64(8_bytes_data) = 0x{unpacked_addr_64:x}")
# 输出:u64(8_bytes_data) = 0x7ffff7dcfd00
4.在漏洞利用中的作用

这些函数在编写漏洞利用脚本(Exploit)时至关重要:

  1. 构造Payload​:

    当你需要覆盖栈上的返回地址、函数指针或修改特定内存值时,你需要将整数值(如目标函数的地址)转换为符合程序期望格式的字节序列。例如,在 64 位栈溢出中,用 p64(system_addr)system函数的地址打包成 8 字节 payload 的一部分。

  2. 解析泄漏信息​:

    许多漏洞利用技术(如 Ret2libc)需要先泄漏来自程序的内存地址(如来自 GOT 表的 Libc 函数地址)。程序输出的这些地址通常是原始的字节流。使用 u64u32可以将这些字节流转换回整数,以便计算 Libc 基地址和其他关键函数的真实地址。

5.注意事项
  • 字节长度​:使用 u32时,输入必须是 ​4 字节;使用 u64时,输入必须是 ​8 字节,否则会引发异常。

  • 字节序控制​:虽然这些函数默认使用小端序,但你也可以通过 context设置或函数的 endianness参数来指定字节序(如 'big' 表示大端序)。

  • 符号处理​:这些函数还可以处理有符号和无符号整数,通常通过 context设置或函数的 sign参数来控制。

http://www.dtcms.com/a/391997.html

相关文章:

  • 39、AI Agent系统开发:智能代理的完整构建体系
  • Qt自定义标题栏拖动延迟问题解决方式分享
  • 招聘数字化转型如何落地?
  • 每日一题(10)
  • 费马小定理的证明
  • GPS和北斗导航信号特点一览表
  • 开发避坑指南(51):达梦数据库查看索引与建立索引的方法
  • Science Robotics最新研究:腿足机器人控制的革新性进展
  • CSP时间复杂度解析:从理论到实践
  • 手搓FOC-环路激励的实现
  • DNN人脸识别和微笑检测
  • 从API调用到UI效果:直播美颜SDK特效面具功能的集成实战
  • 神经网络学习笔记13——高效卷积神经网络架构ShuffleNet
  • MySQL双写缓冲区:数据安全的终极防线
  • 第八章 惊喜09 运维支持VS产品迭代
  • sward入门到实战(2) - 如何管理知识库
  • Vue: 依赖注入(Provide Inject)
  • nethunter 中文乱码解决
  • 【软件测试】第5章 测试分类(上)
  • [硬件电路-262]:MPH6250SQ 管脚定义、概述、功能、技术指标、使用场景及原理分析
  • git status
  • synchronized的高频面试题以及答案
  • cka解题思路1.32-4
  • gradle 和 maven 有什么区别?
  • C/C++语言中`char`类型在x86与ARM平台上的符号性定义差异
  • 台积电纳米泄密事件:Curtain e-locker数据全链路防护
  • 正点原子imx6ull+ov2640+lcd显示问题汇总
  • 【Spring AI】简单入门(一)
  • Java中接口入参验证
  • 【高并发内存池——项目】central cache 讲解