• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2025-03-30 18:51 Aet 隐藏边栏 |   抢沙发  1 
文章评分 1 次,平均分 5.0

示例dump分析:空指针访问二

代码

分析步骤

  1. !analyze -v

  1. 信息1
    1. RAX 寄存器中存储了地址 0x00000000,导致 mov dword ptr [rax], 2Ah 指令尝试向内存地址 0 写入值 0x2A(十进制 42)时触发异常
    2. Windows 系统中,地址 0x00000000 属于 保留的无效内存区域(称为空指针地址),任何读写操作都会触发 访问违规异常(STATUS_ACCESS_VIOLATION,异常码 0xC0000005

  1. 信息2
    1. 从异常信息来看,这是一个内存访问违规(Access Violation)问题
    2. 异常地址:00007ff62b616c1e(对应代码位置 aet_breakpad_test!main+0x2e
      表明问题发生在 main 函数的偏移 0x2E
    3. 异常地址:00007ff62b616c1e,通常表示导致异常的指令本身的存放地址
    4. 异常码:c0000005,表示 无效的内存访问操作

  1. 信息2总结:
    1. 执行mov dword ptr [rax],2Ah这个指令的时候发生了异常,该指令存放在00007ff62b616c1e这个地址
    2. 反汇编该地址如下:
    3. 或者直接在windbg的汇编窗口里查看,如下图:

  1. 查看上面的堆栈内容,可以看到是main函数里面的

  1. windbg里面查看异常上下文
    1. ds:00000000 00000000 表示通过 数据段基址(ds)加上偏移量 0x00000000 访问内存地址

观察

  1. 指针p是空指针

验证

其他

  1. rbp+8 地址处的 8 字节内存(QWORD)初始化为 0
  2. rbp 是基址指针,通常指向当前栈帧的基址
  3. rbp+8 可能是栈帧中的 局部变量或参数位置
  4. x86/x64 架构中,内存操作数不能直接操作立即数,必须通过寄存器中转
    1. 因此需将指针值(0)加载到 rax,再通过 rax 间接访问内存地址

总结

  1. 对空指针的访问

示例dump分析:重复释放同一堆内存二

代码

观察分析

  1. !analyze -v

  1. 信息1
    1. 从异常信息来看,这是一个内存访问违规(Access Violation)问题
    2. 异常地址:00007ff7c158e08e(对应代码位置 aet_breakpad_test!_free_dbg+0x000000000000002e
      表明问题发生在 _free_dbg 函数的偏移 0x2E
    3. 异常地址:00007ff7c158e08e,通常表示导致异常的指令本身的存放地址
    4. 异常码:c0000005,表示 无效的内存访问操作

  1. 信息1总结:
    1. 反汇编如下:
    2. 当前执行位置在 _free_dbg 函数偏移 0x2e 处,对应 debug_heap.cpp1026 行的代码
    3. _free_dbgWindows CRTC 运行时库)中用于调试堆内存释放的函数,比普通 free 函数多出调试信息校验逻辑
    4. 这表明程序可能正在释放动态分配的内存块,但触发了调试堆的校验失败

  1. 信息1总结:
    1. Windows 用户态中,地址 0x0000XXXX 通常属于 保留地址空间(如空指针区域),此处内存不可访问

  1. 信息1总结:
    1. 如果内存属性不可访问:Protect 字段显示为 PAGE_NOACCESS,表明该地址未被分配或已被释放
    2. 但是看不到相关信息

  1. 信息2
    1. 其实使用k,就可以看到连着调了两次delete,到这里可以确定问题并检查代码了
    2. 但是实际的负责情况,可能看不这么全,只能结合这个信息查看代码定位

  1. 信息2
    1. 继续切换栈帧查看其他信息,只能看出来这个block不可访问

  1. 信息2
    1. 看到相关位置

结论

  1. 对堆指针的多次释放

验证

  1. windbg动态调试,根据上面观察到的aet_breakpad_test!main,在这里打断点
    1. fdfdfdfd:调试堆的 已释放内存块头部标记,表示该内存块已被释放
    2. abababab:未初始化或填充区域的标记,用于检测越界访问(如访问未初始化的内存)
    3. dddddddd:堆块尾部填充,用于检测缓冲区溢出(如写入超出分配的内存块尾部)

  1. 执行完第一次delete就变成不可访问了

  1. 执行第二次delete的时候,就如下

总结

其他总结如下:

RAX寄存器

  1. x64 架构中,RAX 是通用寄存器,常用于存储函数返回值或临时数据

mov

概述

  1. 汇编语言中最基础且核心的数据传输指令
  2. 其核心功能是将源操作数的值复制到目标操作数中,且源操作数的内容保持不变

核心功能

  1. 数据复制
    1. MOV指令的本质是数据拷贝而非物理移动
    2. 例如:
    3. MOV EAX, EBXEBX寄存器的值复制到EAX中,EBX的值不变
    4. MOV [0x2000], 0x2A 将立即数0x2A写入内存地址0x2000
  2. 支持的传输方向
    1. 立即数 → 寄存器/内存:如 MOV AX, 0x050A
    2. 寄存器 ↔ 寄存器:如 MOV EAX, EBX
    3. 寄存器 ↔ 内存:如 MOV [EBX], EAXEAX的值存入EBX指向的内存单元
    4. 段寄存器操作:部分场景允许段寄存器与通用寄存器之间的传输(如 MOV DS, AX),但CS(代码段寄存器)和IP(指令指针)不能作为目标操作数

操作数类型与限制

  1. 源操作数(Source
    1. 立即数:如 MOV EAX, 10
    2. 寄存器:如 MOV EBX, EAX
    3. 内存地址:如 MOV ECX, [0x1234]
  2. 目标操作数(Destination
    1. 寄存器:如 MOV EDX, 0xABCD
    2. 内存地址:如 MOV [EDI], 0x20
    3. 段寄存器:部分允许(如 MOV ES, AX),但CSIP不可直接操作
  3. 关键限制
    1. 内存到内存禁止:MOV指令不支持两个内存单元直接传输,需通过寄存器中转(如 MOV AX, [0x01]; MOV [0x02], AX
    2. 立即数不可作为目标:如 MOV 0x1000, AX 是非法操作
    3. 段寄存器间禁止传输:如 MOV DS, ES 无效

应用场景

  1. 变量初始化与赋值
    1. 将立即数加载到寄存器:MOV AL, 0x5
    2. 全局变量初始化:如 MOV DWORD PTR [var], 42
  2. 内存与寄存器交互
    1. 数据存取:如 MOV [EBP-4], EAX栈帧变量操作
    2. 数组操作:通过基址+变址寻址访问数组元素
  3. 系统编程与调试
    1. 寄存器状态设置:如设置堆栈指针 MOV ESP, 0x7C00

注意事项与扩展功能

  1. 标志位不受影响
    1. MOV指令不会修改CPU状态标志(如ZFCF),仅传输数据
  2. 变体指令
    1. MOVZX(零扩展):如 MOVZX EAX, AL8AL零扩展为32EAX
    2. MOVSX(符号扩展):如 MOVSX EDX, BL8位有符号数扩展为32
    3. MOVS(串操作):用于块数据传输

ds寄存器

概述

  1. 数据段寄存器(Data Segment Register),用于指定数据段的内存基址
  2. x86/x64 架构中,数据段通常存储全局变量、静态数据等

内存地址计算与段寄存器的关系

  1. x86/x64架构中,内存访问指令的操作数地址由段寄存器(如 ds)和通用寄存器(如 rax)共同决定
  2. 默认段寄存器:
    1. 当指令未显式指定段寄存器时,大多数内存操作默认使用 ds 作为数据段基址寄存器
    2. rax=0,则实际访问的物理地址为 ds.base + 0x0,即 数据段基址对应的起始地址

  1. 段基址的作用:
    1. ds 存储的是 数据段的基地址(实模式下直接左移4位,保护模式下通过段描述符表计算)

windbg

查看类型占几个字节

查看某个指针占几个字节

对指针解引用

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享