软件调试 第2版 卷2 Windows平台调试 上、下册(异步图书出品)
第 一篇 大 局 观
第 1 章 Windows 系统简史
1.1 源于DOS
1.2 功在NT
1.3 Windows 2000 彰显实力
1.4 巅峰之作:Windows XP 和Windows Server 2003
1.5 Windows Vista 折戟沙场
1.6 Windows 7 享利中兴
1.7 Windows 8 革新受挫
1.8 Windows 10 何去何从
查看完整
第 1 章 Windows 系统简史
1.1 源于DOS
1.2 功在NT
1.3 Windows 2000 彰显实力
1.4 巅峰之作:Windows XP 和Windows Server 2003
1.5 Windows Vista 折戟沙场
1.6 Windows 7 享利中兴
1.7 Windows 8 革新受挫
1.8 Windows 10 何去何从
查看完整
张银奎,国内知名的调试技术专家。毕业于上海交通大学信息与控制工程系,长期从事软件开发和研究工作,曾在英特尔工作13 年,对 IA-32 架构、操作系统内核、驱动程序,尤其是对软件调试有较深入的研究。著有《软件调试》《格蠹汇编》等畅销、常销技术图书,格蠹科技(xedge.ai)创始人,高端调试网站(advdbg.org)创建者。翻译(合译)作品有《二十一世纪机器人》《观止——微软创建NT 和未来的夺命狂奔》《数据挖掘原理》《机器学习》《人工智能:复杂问题求解的结构和策略》等。
本书是国内当前集中介绍软件调试主题的权威著作。本书第2 卷分为5 篇,共30 章,主要围绕Windows系统展开介绍。第一篇(第1~4 章)介绍Windows 系统简史、进程和线程、架构和系统部件,以及Windows系统的启动过程,既从空间角度讲述Windows 的软件世界,也从时间角度描述Windows 世界的搭建过程。第二篇(第5~8 章)描述特殊的过程调用、垫片、托管世界和Linux 子系统。第三篇(第9~19 章)深入探讨用户态调试模型、用户态调试过程、中断和异常管理、未处理异常和JIT 调试、硬错误和蓝屏、错误报告、日志、事件追踪、WHEA、内核调试引擎和验证机制。第四篇(第20~25 章)从编译和编译期检查、运行时库和运行期检查、栈和函数调用、堆和堆检查、异常处理代码的编译、调试符号等方面概括编译器的调试支持。第五篇(第26~30 章)首先纵览调试器的发展历史、工作模型和经典架构,然后分别讨论集成在Visual Studio 和Visua…
查看完整
查看完整
第 一篇 大 局 观
第 1 章 Windows 系统简史
1.1 源于DOS
1.2 功在NT
1.3 Windows 2000 彰显实力
1.4 巅峰之作:Windows XP 和Windows Server 2003
1.5 Windows Vista 折戟沙场
1.6 Windows 7 享利中兴
1.7 Windows 8 革新受挫
1.8 Windows 10 何去何从
1.9 本章总结 17
参考资料
第 2 章 进程和线程
2.1 任务
2.2 进程资源
2.3 进程空间
2.3.1 32 位进程空间
2.3.2 64 位进程空间
2.4 EPROCESS 结构
2.5 PEB.... 28
2.6 内核模式和用户模式
2.6.1 访问模式
2.6.2 使用INT 2E 切换到内核模式
2.6.3 快速系统调用
2.6.4 逆向调用
2.6.5 实例分析
2.7 线程
2.7.1 ETHREAD
2.7.2 TEB
2.8 WoW 进程
2.8.1 架构
2.8.2 工作过程
2.8.3 注册表重定向
2.8.4 注册表反射
2.8.5 文件系统重定向
2.9 创建进程
2.10 最小进程和Pico 进程
2.10.1 最小进程
2.10.2 Pico进程
2.11 任务管理器
2.12 本章总结
参考资料
第3 章 架构和系统部件
3.1 系统概览
3.1.1 内核空间
3.1.2 用户空间
3.2 内核和HAL 模块
3.2.1 内核文件
3.2.2 HAL文件
3.3 空闲进程
3.4 系统进程
3.5 内核空间的其他模块
3.6 NTDLL.DLL
3.6.1 角色
3.6.2 调用系统服务的桩函数
3.6.3 映像文件加载器
3.6.4 运行时库
3.6.5 其他功能
3.7 环境子系统
3.8 原生进程
3.8.1 特点
3.8.2 SMSS
3.8.3 CSRSS
3.9 本章总结
参考资料
第4 章 启动过程
4.1 BootMgr
4.1.1 工作过程
4.1.2 调试方法
4.2 WinLoad
4.3 内核初始化
4.3.1 NT 的入口函数
4.3.2 内核初始化
4.4 执行体的阶段0 初始化
4.4.1 总体过程
4.4.2 创建特殊进程
4.5 执行体的阶段1 初始化
4.5.1 Phase1Initialization
4.5.2 唤醒其他CPU
4.5.3 非启动CPU 的起步路线
4.5.4 漫长的I/O 初始化
4.5.5 更新进度
4.6 创建用户空间
4.6.1 创建会话管理器进程
4.6.2 建立环境子系统
4.6.3 创建窗口站和桌面
4.6.4 用户登录
4.7 本章总结
参考资料
第二篇 探 微
第5 章 特殊的过程调用
5.1 异步过程调用
5.2 中断请求级别
5.2.1 设计初衷
5.2.2 基本原理
5.2.3 析疑
5.3 延迟过程调用
5.3.1 使用模式
5.3.2 黏滞在DPC
5.4 本地过程调用
5.5 远程过程调用
5.5.1 工作模型
5.5.2 RPC 子系统服务
5.5.3 端点和协议串
5.5.4 蜂巢
5.5.5 案例和调试方法
5.6 本章总结
参考资料
第6 章 垫片
6.1 垫片数据库
6.1.1 认识SDB文件
6.1.2 定制的SDB文件
6.1.3 修补模式
6.2 AppHelp
6.2.1 SDB 功能
6.2.2 垫片引擎
6.2.3 AD 挂钩
6.2.4 穿山甲挂钩
6.3 垫片动态库
6.3.1 AcLayers.DLL
6.3.2 AcGenral.DLL 和AcSpecfc.DLL
6.3.3 其他垫片模块
6.4 应用程序垫片的工作过程
6.4.1 在父进程中准备垫片数据
6.4.2 在新进程中加载和初始化垫片引擎
6.4.3 加载垫片模块
6.4.4 落实挂钩
6.4.5 执行垫片
6.5 内核垫片引擎
6.5.1 数据和配置
6.5.2 初始化
6.5.3 KSE 垫片结构
6.5.4 注册垫片
6.5.5 部署垫片
6.5.6 执行垫片
6.6 本章总结
参考资料
第7 章 托管世界
7.1 简要历史
7.2 宏伟蓝图
7.3 类和方法表
7.4 辅助调试线程
7.4.1 托管调试模型
7.4.2 RCThread
7.4.3 刺探线程
7.5 CLR4 的调试模型重构
7.6 SOS 扩展
7.6.1 加载SOS
7.6.2 设置断点
7.6.3 简要原理
7.7 本章总结
参考资料
第8 章 Linux子系统
8.1 源于Drawbridge
8.2 融入NT
8.3 总体架构
8.4 子系统内核模块
8.5 微软版Linux 内核
8.6 Linux 子系统服务器
8.7 WSL 启动器
8.8 交叉开发
8.9 WSL2
8.10 本章总结
参考资料
第三篇 操作系统的调试支持
第9 章 用户态调试模型
9.1 概览
9.1.1 参与者
9.1.2 调试子系统
9.1.3 调试事件驱动
9.2 采集调试消息
9.2.1 消息常量
9.2.2 进程和线程创建消息
9.2.3 进程和线程退出消息
9.2.4 模块映射和反映射消息
9.2.5 异常消息
9.3 发送调试消息
9.3.1 调试消息结构
9.3.2 DbgkpSendApiMessage函数
9.3.3 控制被调试进程
9.4 调试子系统服务器(Windows XP 之后)
9.4.1 DebugObject
9.4.2 创建调试对象
9.4.3 设置调试对象
9.4.4 传递调试消息
9.4.5 杜撰的调试消息
9.4.6 清除调试对象
9.4.7 内核服务
9.4.8 全景
9.5 调试子系统服务器(Windows XP 之前)
9.5.1 概览
9.5.2 Windows 会话管理器
9.5.3 Windows 环境子系统服务器进程
9.5.4 调用CSRSS 的服务
9.5.5 CsrCreateProcess 服务
9.5.6 CsrDebugProcess 服务
9.6 比较两种模型
9.6.1 Windows 2000 调试子系统的优点
9.6.2 Windows 2000 调试子系统的安全问题
9.6.3 Windows XP 的调试模型的优点
9.6.4 Windows XP 引入的新调试功能
9.7 NTDLL.DLL 中的调试支持例程
9.7.1 DbgUi 函数
9.7.2 DbgSs 函数
9.7.3 Dbg 函数
9.8 调试API
9.9 本章总结
参考资料
第 10 章 用户态调试过程
10.1 调试器进程
10.1.1 线程模型
10.1.2 调试器的工作线程
10.1.3 DbgSsReserved 字段
10.2 被调试进程
10.2.1 特征
10.2.2 DebugPort 字段
10.2.3 BeingDebugged 字段
10.2.4 观察DebugPort 字段和BeingDebugged 字段
10.2.5 调试会话
10.3 从调试器中启动被调试程序
10.3.1 CreateProcess API
10.3.2 第 一批调试事件
10.3.3 初始断点
10.3.4 自动启动调试器
10.4 附加到已经启动的进程中
10.4.1 DebugActiveProcess API
10.4.2 示例:TinyDbgr 程序
10.5 处理调试事件
10.5.1 DEBUG_EVENT 结构
10.5.2 WaitForDebugEvent API
10.5.3 调试事件循环
10.5.4 回复调试事件
10.5.5 定制调试器的事件处理方式
10.6 中断到调试器
10.6.1 初始断点
10.6.2 编程时加入断点
10.6.3 通过调试器设置断点
10.6.4 通过远程线程触发断点异常
10.6.5 在线程当前执行位置设置断点
10.6.6 动态调用远程函数
10.6.7 挂起中断
10.6.8 调试快捷键(F12 键)
10.6.9 窗口更新
10.7 输出调试字符串
10.7.1 发送调试信息
10.7.2 使用调试器接收调试信息
10.7.3 使用工具接收调试信息
10.8 终止调试会话
10.8.1 被调试进程退出
10.8.2 调试器进程退出
10.8.3 分离被调试进程
10.8.4 退出时分离
10.9 本章总结
参考资料
第 11 章 中断和异常管理
11.1 中断描述符表
11.1.1 概况
11.1.2 门描述符
11.1.3 执行中断和异常处理函数
11.1.4 IDT 一览
11.2 异常的描述和登记
11.2.1 EXCEPTION_RECORD结构
11.2.2 登记CPU 异常
11.2.3 登记软件异常
11.3 异常分发过程
11.3.1 KiDispatchException 函数
11.3.2 内核态异常的分发过程
11.3.3 用户态异常的分发过程
11.3.4 归纳
11.4 结构化异常处理
11.4.1 SEH 简介
11.4.2 SHE 机制的终结处理
11.4.3 SEH 机制的异常处理
11.4.4 过滤表达式
11.4.5 异常处理块
11.4.6 嵌套使用终结处理和异常处理
11.5 向量化异常处理
11.5.1 登记和注销
11.5.2 调用结构化异常处理器
11.5.3 示例
11.6 本章总结
参考资料
第 12 章 未处理异常和JIT 调试
12.1 简介
12.2 默认的异常处理器
12.2.1 BaseProcessStart 函数中的结构化异常处理器
12.2.2 编译器插入的SEH 处理器
12.2.3 基于信号的异常处理
12.2.4 实验:观察默认的异常处理器
12.2.5 BaseThreadStart 函数中的结构化异常处理器
12.3 未处理异常过滤函数
12.3.1 Windows XP 之前的异常处理机制
12.3.2 Windows XP 中的异常处理机制
12.4 “应用程序错误”对话框
12.4.1 用HardError 机制提示应用程序错误
12.4.2 使用ReportFault API 提示应用程序错误
12.5 JIT 调试和Dr. Watson
12.5.1 配置JIT 调试器
12.5.2 启动JIT 调试器
12.5.3 自己编写JIT 调试器
12.6 顶层异常过滤函数
12.6.1 注册
12.6.2 C 运行时库的顶层过滤函数
12.6.3 执行
12.6.4 调试
12.7 Dr. Watson
12.7.1 配置和查看模式
12.7.2 设置为默认的JIT 调试器
12.7.3 JIT 调试模式
12.8 DRWTSN32 的日志文件
12.8.1 异常信息
12.8.2 系统信息
12.8.3 任务列表
12.8.4 模块列表
12.8.5 线程状态
12.8.6 函数调用序列
12.8.7 原始栈数据
12.9 用户态转储文件
12.9.1 文件格式概览
12.9.2 数据流
12.9.3 产生转储文件
12.9.4 读取转储文件
12.9.5 利用转储文件分析问题
12.10 本章总结
参考资料
第 13 章 硬错误和蓝屏
13.1 硬错误提示
13.1.1 缺盘错误
13.1.2 NtRaiseHardError
13.1.3 ExpRaiseHardError
13.1.4 CSRSS 中的分发过程
13.2 蓝屏终止
13.2.1 简介
13.2.2 发起和产生过程
13.2.3 诊断蓝屏错误
13.2.4 手工触发蓝屏
13.3 系统转储文件
13.3.1 分类
13.3.2 文件格式
13.3.3 产生方法
13.4 分析系统转储文件
13.4.1 初步分析
13.4.2 线程和栈回溯
13.4.3 陷阱帧
13.4.4 自动分析
13.5 辅助的错误提示方法
13.5.1 MessageBeep
13.5.2 Beep 函数
13.5.3 闪动窗口
13.6 配置错误提示机制
13.6.1 SetErrorMode API
13.6.2 IoSetThreadHardErrorMode
13.6.3 蓝屏后自动重启
13.7 防止滥用错误提示机制
13.8 本章总结
参考资料
第 14 章 错误报告
14.1 WER 1.0
14.1.1 客户端
14.1.2 报告模式
14.1.3 传输方式
14.2 系统错误报告
14.3 WER 服务器端
14.3.1 WER 服务
14.3.2 错误报告分类方法
14.3.3 报告回应
14.4 WER 2.0
14.4.1 模块变化
14.4.2 创建报告
14.4.3 提交报告
14.4.4 典型应用
14.5 CER
14.6 本章总结
参考资料
第 15 章 日志
15.1 日志简介
15.2 ELF 的架构
15.2.1 ELF 的日志文件
15.2.2 事件源
15.2.3 ELF 服务
15.3 ELF 的数据组织350
15.3.1 日志记录
15.3.2 添加日志记录
15.3.3 API 一览
15.4 查看和使用ELF 日志
15.5 CLFS 的组成和原理
15.5.1 组成
15.5.2 存储结构
15.5.3 LSN
15.6 CLFS 的使用方法
15.6.1 创建日志文件
15.6.2 添加CLFS 容器
15.6.3 创建编组区
15.6.4 添加日志记录
15.6.5 读日志记录
15.6.6 查询信息
15.6.7 管理和备份
15.7 本章总结
参考资料
第 16 章 事件追踪
16.1 简介
16.2 ETW 的架构
16.3 提供ETW消息
16.4 控制ETW会话
16.5 消耗ETW消息
16.6 格式描述
16.6.1 MOF文件
16.6.2 WPP
16.7 NT 内核记录器
16.7.1 观察NKL的追踪事件
16.7.2 编写代码控制NKL
16.7.3 NKL 的实现
16.8 Global Logger Session
16.8.1 启动GLS 会话
16.8.2 配置GLS
16.8.3 在驱动程序中使用GLS
16.8.4 自动记录器
16.8.5 BootVis 工具
16.9 Crimson API
16.9.1 发布事件
16.9.2 消耗事件
16.9.3 格式描述
16.9.4 收集和观察事件
16.9.5 Crimson API 的实现
16.10 本章总结
参考资料
第 17 章 WHEA
17.1 目标、架构和PSHED.DLL
17.1.1 目标
17.1.2 架构
17.1.3 PSHED.DLL
17.2 错误源
17.2.1 标准的错误源
17.2.2 通过ACPI 表来定义错误源
17.2.3 通过PSHED 插件来报告错误源
17.3 错误处理过程
17.3.1 WHEA_ERROR_PACKET结构
17.3.2 处理过程
17.3.3 WHEA_ERROR_RECORD结构
17.3.4 固件优先模式
17.4 错误持久化
17.4.1 ERST
17.4.2 工作过程
17.5 注入错误
17.6 本章总结
参考资料
第 18 章 内核调试引擎
18.1 概览
18.1.1 KD
18.1.2 角色
18.1.3 组成
18.1.4 模块文件
18.1.5 版本差异
18.2 连接
18.2.1 串行端口
18.2.2 1394
18.2.3 USB 2.0
18.2.4 管道
18.2.5 选择连接方式
18.2.6 解决连接问题
18.3 启用
18.3.1 BOOT.INI
18.3.2 BCD
18.3.3 高级启动选项
18.4 初始化
18.4.1 Windows 系统启动过程概述
18.4.2 第 一次调用KdInitSystem
18.4.3 第二次调用KdInitSystem
18.4.4 通信扩展模块的阶段1初始化
18.5 内核调试协议
18.5.1 数据包
18.5.2 报告状态变化
18.5.3 访问目标系统
18.5.4 恢复目标系统执行
18.5.5 版本
18.5.6 典型对话过程
18.5.7 KdTalker
18.6 与内核交互
18.6.1 中断到调试器
18.6.2 KdpSendWaitContinue
18.6.3 退出调试器
18.6.4 轮询中断包
18.6.5 接收和报告异常事件
18.6.6 调试服务
18.6.7 打印调试信息
18.6.8 加载调试符号
18.6.9 更新系统文件
18.7 建立和维持连接
18.7.1 最早的调试机会
18.7.2 初始断点
18.7.3 断开和重新建立连接
18.8 本地内核调试
18.8.1 LiveKD
18.8.2 Windows 系统自己的本地内核调试支持
18.8.3 安全问题
18.9 本章总结
参考资料
第 19 章 验证机制
19.1 简介
19.1.1 驱动程序验证器
19.1.2 应用程序验证器
19.1.3 WHQL 测试
19.2 驱动验证器的工作原理
19.2.1 设计原理
19.2.2 初始化
19.2.3 挂接验证函数
19.2.4 验证函数的执行过程
19.2.5 报告验证失败
19.3 使用驱动验证器
19.3.1 验证项目
19.3.2 启用驱动验证器
19.3.3 开始验证
19.3.4 观察验证情况
19.3.5 WinDBG 的扩展命令
19.4 应用程序验证器的工作原理
19.4.1 原理和组成
19.4.2 初始化
19.4.3 挂接API
19.4.4 验证函数的执行过程
19.4.5 报告验证失败
19.4.6 验证停顿
19.5 使用应用程序验证器
19.5.1 应用验证管理器
19.5.2 验证项目
19.5.3 配置验证属性
19.5.4 配置验证停顿
19.5.5 编程调用
19.5.6 调试扩展
19.6 本章总结
参考资料
第四篇 编译器的调试支持
第 20 章 编译和编译期检查
20.1 程序的构建过程
20.1.1 链接器
20.1.2 加载器
20.2 编译
20.2.1 前端
20.2.2 后端
20.3 Visual C++编译器
20.3.1 MSVC 简史
20.3.2 MSVC6
20.3.3 VS7 和VS8
20.3.4 构建程序
20.3.5 调试
20.4 编译错误和警告
20.4.1 错误ID 和来源
20.4.2 编译警告
20.5 编译期检查
20.5.1 未初始化的局部变量
20.5.2 类型不匹配
20.5.3 使用编译器指令
20.5.4 标注
20.5.5 驱动程序静态验证器
20.6 标准标注语言
20.6.1 缓冲区标注符
20.6.2 高级标注符
20.7 本章总结
参考资料.
第 21 章 运行时库和运行期检查
21.1 C/C++运行时库
21.1.1 C 运行时库
21.1.2 C++标准库
21.2 链接运行时库
21.2.1 静态链接和动态链接
21.2.2 lib 文件
21.3 运行时库的初始化和清理
21.3.1 介入方法
21.3.2 初始化
21.3.3 多个运行时库实例
21.4 运行期检查
21.4.1 自动的运行期检查
21.4.2 断言
21.4.3 _RPT 宏
21.5 报告运行期检查错误
21.5.1 _CrtDbgReport
21.5.2 _CrtSetReportMode
21.5.3 _CrtSetReportFile
21.5.4 _CrtSetReportHook 493
21.5.5 _CrtSetReportHook2
21.5.6 使用其他函数报告RTC错误
21.6 本章总结
参考资料
第 22 章 栈和函数调用
22.1 简介
22.1.1 用户态栈和内核态栈
22.1.2 函数、过程和方法
22.2 栈的创建过程
22.2.1 内核态栈的创建
22.2.2 用户态栈的创建
22.2.3 跟踪用户态栈的创建过程
22.3 CALL 和RET 指令
22.3.1 CALL 指令
22.3.2 RET 指令
22.3.3 观察函数调用和返回过程
22.3.4 跨特权级调用
22.4 局部变量和栈帧
22.4.1 局部变量的分配和释放
22.4.2 EBP 寄存器和栈帧
22.4.3 帧指针和栈帧的遍历
22.5 帧指针省略
22.6 栈指针检查
22.7 调用协定
22.7.1 C 调用协定
22.7.2 标准调用协定
22.7.3 快速调用协定
22.7.4 This 调用协定
22.7.5 CLR 调用协定
22.7.6 x64 调用协定
22.7.7 通过编译器开关改变默认调用协定
22.7.8 函数返回值
22.7.9 归纳和补充
22.8 栈空间的增长和溢出
22.8.1 栈空间的自动增长
22.8.2 栈溢出
22.8.3 分配检查
22.9 栈下溢
22.10 缓冲区溢出
22.10.1 感受缓冲区溢出
22.10.2 缓冲区溢出攻击
22.11 变量检查
22.12 基于Cookie 的安全检查
22.12.1 安全Cookie 的产生、植入和检查
22.12.2 报告安全检查失败
22.12.3 编写安全的代码
22.13 本章总结
参考资料
第 23 章 堆和堆检查
23.1 理解堆
23.2 堆的创建和销毁
23.2.1 进程的默认堆
23.2.2 创建私有堆
23.2.3 堆列表
23.2.4 销毁堆
23.3 分配和释放堆块
23.3.1 HeapAlloc
23.3.2 CRT 分配函数
23.3.3 释放从堆中分配的内存
23.3.4 GlobalAlloc 和LocalAlloc
23.3.5 解除提交
23.4 堆的内部结构
23.4.1 结构和布局
23.4.2 HEAP 结构
23.4.3 HEAP_SEGMENT结构
23.4.4 HEAP_ENTRY结构
23.4.5 分析堆块的分配和释放过程
23.4.6 使用!heap 命令观察堆块信息
23.5 低碎片堆
23.6 堆的调试支持
23.6.1 全局标志
23.6.2 堆释放检查
23.7 栈回溯数据库
23.7.1 工作原理
23.7.2 DH 和UMDH工具
23.7.3 定位内存泄漏
23.8 堆溢出和检测
23.8.1 堆缓冲区溢出
23.8.2 调用时验证
23.8.3 堆尾检查
23.9 页堆
23.9.1 总体结构
23.9.2 启用和观察页堆
23.9.3 堆块结构
23.9.4 检测溢出
23.10 准页堆
23.10.1 启用准页堆
23.10.2 结构布局
23.10.3 检测溢出
23.11 CRT 堆
23.11.1 CRT 堆的3 种模式
23.11.2 SBH 简介
23.11.3 创建和选择模式
23.11.4 CRT 堆的终止
23.12 CRT 堆的调试堆块
23.12.1 _CrtMemBlockHeader结构
23.12.2 块类型
23.12.3 分配堆块
23.13 CRT 堆的调试功能
23.13.1 内存分配序号断点
23.13.2 分配挂钩
23.13.3 自动和手动检查
23.14 堆块转储
23.14.1 内存状态和检查点
23.14.2 _CrtMemDumpAllObjectsSince
23.14.3 转储挂钩
23.15 泄漏转储
23.15.1 _CrtDumpMemoryLeaks
23.15.2 何时调用
23.15.3 定位导致泄漏的源代码
23.16 本章总结
参考资料
第 24 章 异常处理代码的编译
24.1 概览
24.2 FS:[0]链条
24.2.1 TEB 和TIB 结构
24.2.2 ExceptionList 字段
24.2.3 登记异常处理器
24.3 遍历FS:[0]链条
24.3.1 RtlDispatchException
24.3.2 KiUserExceptionDispatcher
24.4 执行异常处理函数
24.4.1 SehRaw 实例
24.4.2 执行异常处理函数
24.5 _ _ try{}_ _ except()结构
24.5.1 与手工方法的对应关系
24.5.2 _ _ try{}_ _ except()结构的编译
24.5.3 范围表
24.5.4 TryLevel
24.5.5 _ _ try{}_ _ except()结构的执行
24.5.6 _SEH_prolog 和_SEH_epilog
24.6 安全问题
24.6.1 安全Cookie
24.6.2 SAFESEH
24.6.3 基于表的异常处理
24.7 本章总结
参考资料
第 25 章 调试符号
25.1 名称修饰
25.1.1 C 和C++
25.1.2 C 的名称修饰规则
25.1.3 C++的名称修饰规则
25.2 调试信息的存储格式
25.2.1 COFF格式
25.2.2 CodeView 格式
25.2.3 PDB格式
25.2.4 DWARF格式
25.3 目标文件中的调试信息
25.3.1 IMAGE_FILE_HEADER结构
25.3.2 IMAGE_SECTION_HEADER结构
25.3.3 节的重定位信息和行号信息
25.3.4 存储调试数据的节
25.3.5 调试符号表
25.3.6 COFF 字符串表
25.3.7 COFF 符号例析
25.4 PE 文件中的调试信息
25.4.1 PE 文件布局
25.4.2 IMAGE_OPTIONAL_HEADER结构
25.4.3 调试数据目录
25.4.4 调试数据
25.4.5 使用WinDBG 观察PE 文件中的调试信息
25.4.6 调试信息的产生过程
25.5 DBG 文件
25.5.1 从PE 文件产生DBG 文件
25.5.2 DBG 文件的布局
25.6 PDB 文件
25.6.1 复合文件
25.6.2 PDB 文件布局
25.6.3 PDB 签名
25.6.4 Magic 代码
25.6.5 PDB_HEADER
25.6.6 根数据流——流目录
25.6.7 页分配表
25.6.8 访问PDB 文件的方式
25.6.9 PDB 文件的产生过程
25.7 有关的编译和链接选项
25.7.1 控制调试信息的编译选项
25.7.2 控制调试信息的链接选项
25.7.3 不同链接和编译选项的比较
25.8 PDB 文件中的数据表
25.8.1 符号表
25.8.2 源文件表
25.8.3 节贡献表
25.8.4 段信息表
25.8.5 注入源代码表
25.8.6 帧数据表
25.9 本章总结
参考资料
第五篇 调 试 器
第 26 章 调试器概览
26.1 TX-0 计算机和FLIT调试器
26.2 小型机和DDT调试器
26.2.1 PDP-1
26.2.2 TOPS-10 操作系统和
DDT-10
26.3 个人计算机和它的调试器
26.3.1 8086 Monitor
26.3.2 SYMDEB
26.3.3 CodeView调试器
26.3.4 Turbo Debugger
26.3.5 SoftICE
26.4 调试器的功能
26.4.1 建立和终止调试会话
26.4.2 控制被调试程序执行
26.4.3 访问内存
26.4.4 访问寄存器
26.4.5 断点
26.4.6 跟踪执行
26.4.7 观察栈和栈回溯
26.4.8 汇编和反汇编
26.4.9 源代码级调试..685
26.4.10 EnC
26.4.11 文件管理
26.4.12 接收和显示调试信息
26.4.13 转储
26.5 分类标准
26.5.1 特权级别
26.5.2 操作系统
26.5.3 执行方式
26.5.4 处理器架构
26.5.5 编程语言688
26.6 实现模型
26.6.1 进程内调试模型
26.6.2 进程外调试模型
26.6.3 混合调试模型
26.6.4 内核调试模型
26.7 经典架构
26.7.1 基本单元
26.7.2 远程调试
26.7.3 多语言和多处理器架构调试
26.8 HPD 标准
26.8.1 HPD 标准简介
26.8.2 动作点
26.8.3 进程和线程的表示和命名
26.8.4 命令
26.9 本章总结
参考资料
第 27 章 VsDebug
27.1 架构和调试模型
27.1.1 架构概览
27.1.2 远程调试器
27.1.3 本地调试器
27.2 VS 调试引擎
27.2.1 一套接口,多种实现
27.2.2 核心类
27.3 工作过程
27.3.1 开始调试32 位本地程序
27.3.2 开始调试64 位本地程序
27.3.3 访问调试目标
27.4 使用断点
27.4.1 根据名称设置断点
27.4.2 数据断点
27.4.3 附加条件
27.4.4 附加操作
27.5 多线程调试
27.5.1 并行栈回溯
27.5.2 并行监视
27.5.3 冻结线程
27.6 EnC
27.6.1 应用过程
27.6.2 要求/ZI 编译选项
27.6.3 下次调用生效
27.6.4 应用失败
27.7 设计期调试
27.8 使用符号服务器
27.9 定制调试事件
27.9.1 初始断点
27.9.2 异常设置
27.10 本章总结
参考资料
第 28 章 VS Code 的调试扩展
28.1 简介
28.2 四大技术
28.3 理解“扩展包”
28.3.1 包类型
28.3.2 安装
28.3.3 工作原理
28.4 扩展包API
28.4.1 贡献点
28.4.2 命令
28.4.3 激活事件
28.5 调试模型
28.5.1 贡献调试器
28.5.2 宏观架构
28.6 调试适配器
28.6.1 DA 描述符工厂
28.6.2 进程内DA
28.6.3 vsdbg
28.6.4 OpenDebugAD7
28.7 机器接口
28.7.1 启用用法
28.7.2 对话示例
28.7.3 MIEngine
28.8 调试Python 程序
28.8.1 PTVSD
28.8.2 发起异常时中断
28.9 本章总结
参考资料
第 29 章 WinDBG 及其实现
29.1 WinDBG 溯源
29.1.1 KD 和NTSD 诞生
29.1.2 WinDBG 诞生
29.1.3 发行方式
29.1.4 版本历史
29.2 C 阶段的架构
29.2.1 功能模块
29.2.2 远程调试
29.3 重构
29.3.1 版本历史
29.3.2 界面变化
29.3.3 模块变化
29.3.4 发布方式和NTSD 问题
29.3.5 文件
29.4 调试器引擎的架构
29.4.1 概览
29.4.2 对外接口
29.4.3 DebugClient 类
29.4.4 中间层
29.4.5 服务层
29.4.6 传输和连接层
29.5 调试目标
29.5.1 TargetInfo 类
29.5.2 用户态目标
29.5.3 内核态目标
29.5.4 转储文件目标
29.6 调试会话
29.6.1 建立调试会话
29.6.2 调试循环
29.6.3 等待和处理调试事件
29.6.4 继续调试事件
29.6.5 结束调试会话
29.7 接收和处理命令
29.7.1 调试器的两种工作状态
29.7.2 进入命令状态
29.7.3 执行命令
29.7.4 结束命令状态
29.8 扩展命令的工作原理
29.9 本章总结
参考资料
第30 章 WinDBG 用法详解
30.1 工作空间
30.2 命令概览
30.2.1 标准命令
30.2.2 元命令
30.2.3 扩展命令
30.3 用户界面
30.3.1 窗口概览
30.3.2 命令窗口和命令提示符
30.4 输入和执行命令
30.4.1 要点
30.4.2 表达式
30.4.3 伪寄存器
30.4.4 别名
30.4.5 循环和条件执行
30.4.6 进程限定符和线程限定符
30.4.7 记录到文件
30.5 建立调试会话
30.5.1 附加到已经运行的进程
30.5.2 创建并调试新的进程
30.5.3 非入侵式调试
30.5.4 双机内核调试
30.5.5 本地内核调试
30.5.6 调试转储文件
30.5.7 远程调试
30.6 终止调试会话
30.6.1 停止调试
30.6.2 分离调试目标
30.6.3 抛弃被调试进程
30.6.4 终止被调试进程
30.6.5 调试器终止或僵死
30.6.6 重新开始调试
30.7 理解上下文
30.7.1 登录会话上下文
30.7.2 进程上下文
30.7.3 寄存器上下文
30.7.4 局部(变量)上下文
30.8 调试符号
30.8.1 重要意义
30.8.2 符号搜索路径
30.8.3 符号服务器
30.8.4 加载符号文件
30.8.5 观察模块信息
30.8.6 检查符号
30.8.7 搜索符号
30.8.8 设置符号选项
30.8.9 加载不严格匹配的符号文件
30.9 事件处理
30.9.1 调试事件与异常的关系
30.9.2 两轮机会
30.9.3 定制事件处理方式
30.9.4 GH 和GN 命令
30.9.5 实验
30.10 控制调试目标
30.10.1 初始断点
30.10.2 俘获调试目标
30.10.3 继续运行
30.11 单步执行
30.11.1 概览
30.11.2 单步执行到指定地址
30.11.3 单步执行到下一个函数调用
30.11.4 单步执行到下一分支
30.11.5 追踪并监视
30.11.6 程序指针飞跃
30.11.7 归纳
30.12 使用断点
30.12.1 软件断点
30.12.2 硬件断点
30.12.3 条件断点
30.12.4 地址表达方法
30.12.5 设置针对线程的断点
30.12.6 管理断点
30.13 控制进程和线程
30.13.1 MulThrds 程序
30.13.2 控制线程执行824
30.13.3 多进程调试
30.14 观察栈
30.14.1 显示栈回溯
30.14.2 观察栈变量
30.15 分析内存
30.15.1 显示内存区域
30.15.2 显示字符串
30.15.3 显示数据类型
30.15.4 搜索内存
30.15.5 修改内存
30.15.6 使用物理内存地址
30.15.7 观察内存属性
30.16 遍历链表
30.16.1 结构定义
30.16.2 双向链表示例
30.16.3 单向链表示例
30.16.4 dl 命令
30.16.5 !list 命令
30.17 调用目标程序的函数
30.17.1 调用示例
30.17.2 工作原理
30.17.3 限制条件和常见错误.
30.18 命令程序
30.18.1 流程控制符号
30.18.2 变量
30.18.3 命令程序示例
30.18.4 执行命令程序
30.19 本章总结
参考资料
附录A 示例程序列表
附录B WinDBG 标准命令列表
附录C NT 内核部件缩写列表
持之若痴——代跋
^ 收 起
第 1 章 Windows 系统简史
1.1 源于DOS
1.2 功在NT
1.3 Windows 2000 彰显实力
1.4 巅峰之作:Windows XP 和Windows Server 2003
1.5 Windows Vista 折戟沙场
1.6 Windows 7 享利中兴
1.7 Windows 8 革新受挫
1.8 Windows 10 何去何从
1.9 本章总结 17
参考资料
第 2 章 进程和线程
2.1 任务
2.2 进程资源
2.3 进程空间
2.3.1 32 位进程空间
2.3.2 64 位进程空间
2.4 EPROCESS 结构
2.5 PEB.... 28
2.6 内核模式和用户模式
2.6.1 访问模式
2.6.2 使用INT 2E 切换到内核模式
2.6.3 快速系统调用
2.6.4 逆向调用
2.6.5 实例分析
2.7 线程
2.7.1 ETHREAD
2.7.2 TEB
2.8 WoW 进程
2.8.1 架构
2.8.2 工作过程
2.8.3 注册表重定向
2.8.4 注册表反射
2.8.5 文件系统重定向
2.9 创建进程
2.10 最小进程和Pico 进程
2.10.1 最小进程
2.10.2 Pico进程
2.11 任务管理器
2.12 本章总结
参考资料
第3 章 架构和系统部件
3.1 系统概览
3.1.1 内核空间
3.1.2 用户空间
3.2 内核和HAL 模块
3.2.1 内核文件
3.2.2 HAL文件
3.3 空闲进程
3.4 系统进程
3.5 内核空间的其他模块
3.6 NTDLL.DLL
3.6.1 角色
3.6.2 调用系统服务的桩函数
3.6.3 映像文件加载器
3.6.4 运行时库
3.6.5 其他功能
3.7 环境子系统
3.8 原生进程
3.8.1 特点
3.8.2 SMSS
3.8.3 CSRSS
3.9 本章总结
参考资料
第4 章 启动过程
4.1 BootMgr
4.1.1 工作过程
4.1.2 调试方法
4.2 WinLoad
4.3 内核初始化
4.3.1 NT 的入口函数
4.3.2 内核初始化
4.4 执行体的阶段0 初始化
4.4.1 总体过程
4.4.2 创建特殊进程
4.5 执行体的阶段1 初始化
4.5.1 Phase1Initialization
4.5.2 唤醒其他CPU
4.5.3 非启动CPU 的起步路线
4.5.4 漫长的I/O 初始化
4.5.5 更新进度
4.6 创建用户空间
4.6.1 创建会话管理器进程
4.6.2 建立环境子系统
4.6.3 创建窗口站和桌面
4.6.4 用户登录
4.7 本章总结
参考资料
第二篇 探 微
第5 章 特殊的过程调用
5.1 异步过程调用
5.2 中断请求级别
5.2.1 设计初衷
5.2.2 基本原理
5.2.3 析疑
5.3 延迟过程调用
5.3.1 使用模式
5.3.2 黏滞在DPC
5.4 本地过程调用
5.5 远程过程调用
5.5.1 工作模型
5.5.2 RPC 子系统服务
5.5.3 端点和协议串
5.5.4 蜂巢
5.5.5 案例和调试方法
5.6 本章总结
参考资料
第6 章 垫片
6.1 垫片数据库
6.1.1 认识SDB文件
6.1.2 定制的SDB文件
6.1.3 修补模式
6.2 AppHelp
6.2.1 SDB 功能
6.2.2 垫片引擎
6.2.3 AD 挂钩
6.2.4 穿山甲挂钩
6.3 垫片动态库
6.3.1 AcLayers.DLL
6.3.2 AcGenral.DLL 和AcSpecfc.DLL
6.3.3 其他垫片模块
6.4 应用程序垫片的工作过程
6.4.1 在父进程中准备垫片数据
6.4.2 在新进程中加载和初始化垫片引擎
6.4.3 加载垫片模块
6.4.4 落实挂钩
6.4.5 执行垫片
6.5 内核垫片引擎
6.5.1 数据和配置
6.5.2 初始化
6.5.3 KSE 垫片结构
6.5.4 注册垫片
6.5.5 部署垫片
6.5.6 执行垫片
6.6 本章总结
参考资料
第7 章 托管世界
7.1 简要历史
7.2 宏伟蓝图
7.3 类和方法表
7.4 辅助调试线程
7.4.1 托管调试模型
7.4.2 RCThread
7.4.3 刺探线程
7.5 CLR4 的调试模型重构
7.6 SOS 扩展
7.6.1 加载SOS
7.6.2 设置断点
7.6.3 简要原理
7.7 本章总结
参考资料
第8 章 Linux子系统
8.1 源于Drawbridge
8.2 融入NT
8.3 总体架构
8.4 子系统内核模块
8.5 微软版Linux 内核
8.6 Linux 子系统服务器
8.7 WSL 启动器
8.8 交叉开发
8.9 WSL2
8.10 本章总结
参考资料
第三篇 操作系统的调试支持
第9 章 用户态调试模型
9.1 概览
9.1.1 参与者
9.1.2 调试子系统
9.1.3 调试事件驱动
9.2 采集调试消息
9.2.1 消息常量
9.2.2 进程和线程创建消息
9.2.3 进程和线程退出消息
9.2.4 模块映射和反映射消息
9.2.5 异常消息
9.3 发送调试消息
9.3.1 调试消息结构
9.3.2 DbgkpSendApiMessage函数
9.3.3 控制被调试进程
9.4 调试子系统服务器(Windows XP 之后)
9.4.1 DebugObject
9.4.2 创建调试对象
9.4.3 设置调试对象
9.4.4 传递调试消息
9.4.5 杜撰的调试消息
9.4.6 清除调试对象
9.4.7 内核服务
9.4.8 全景
9.5 调试子系统服务器(Windows XP 之前)
9.5.1 概览
9.5.2 Windows 会话管理器
9.5.3 Windows 环境子系统服务器进程
9.5.4 调用CSRSS 的服务
9.5.5 CsrCreateProcess 服务
9.5.6 CsrDebugProcess 服务
9.6 比较两种模型
9.6.1 Windows 2000 调试子系统的优点
9.6.2 Windows 2000 调试子系统的安全问题
9.6.3 Windows XP 的调试模型的优点
9.6.4 Windows XP 引入的新调试功能
9.7 NTDLL.DLL 中的调试支持例程
9.7.1 DbgUi 函数
9.7.2 DbgSs 函数
9.7.3 Dbg 函数
9.8 调试API
9.9 本章总结
参考资料
第 10 章 用户态调试过程
10.1 调试器进程
10.1.1 线程模型
10.1.2 调试器的工作线程
10.1.3 DbgSsReserved 字段
10.2 被调试进程
10.2.1 特征
10.2.2 DebugPort 字段
10.2.3 BeingDebugged 字段
10.2.4 观察DebugPort 字段和BeingDebugged 字段
10.2.5 调试会话
10.3 从调试器中启动被调试程序
10.3.1 CreateProcess API
10.3.2 第 一批调试事件
10.3.3 初始断点
10.3.4 自动启动调试器
10.4 附加到已经启动的进程中
10.4.1 DebugActiveProcess API
10.4.2 示例:TinyDbgr 程序
10.5 处理调试事件
10.5.1 DEBUG_EVENT 结构
10.5.2 WaitForDebugEvent API
10.5.3 调试事件循环
10.5.4 回复调试事件
10.5.5 定制调试器的事件处理方式
10.6 中断到调试器
10.6.1 初始断点
10.6.2 编程时加入断点
10.6.3 通过调试器设置断点
10.6.4 通过远程线程触发断点异常
10.6.5 在线程当前执行位置设置断点
10.6.6 动态调用远程函数
10.6.7 挂起中断
10.6.8 调试快捷键(F12 键)
10.6.9 窗口更新
10.7 输出调试字符串
10.7.1 发送调试信息
10.7.2 使用调试器接收调试信息
10.7.3 使用工具接收调试信息
10.8 终止调试会话
10.8.1 被调试进程退出
10.8.2 调试器进程退出
10.8.3 分离被调试进程
10.8.4 退出时分离
10.9 本章总结
参考资料
第 11 章 中断和异常管理
11.1 中断描述符表
11.1.1 概况
11.1.2 门描述符
11.1.3 执行中断和异常处理函数
11.1.4 IDT 一览
11.2 异常的描述和登记
11.2.1 EXCEPTION_RECORD结构
11.2.2 登记CPU 异常
11.2.3 登记软件异常
11.3 异常分发过程
11.3.1 KiDispatchException 函数
11.3.2 内核态异常的分发过程
11.3.3 用户态异常的分发过程
11.3.4 归纳
11.4 结构化异常处理
11.4.1 SEH 简介
11.4.2 SHE 机制的终结处理
11.4.3 SEH 机制的异常处理
11.4.4 过滤表达式
11.4.5 异常处理块
11.4.6 嵌套使用终结处理和异常处理
11.5 向量化异常处理
11.5.1 登记和注销
11.5.2 调用结构化异常处理器
11.5.3 示例
11.6 本章总结
参考资料
第 12 章 未处理异常和JIT 调试
12.1 简介
12.2 默认的异常处理器
12.2.1 BaseProcessStart 函数中的结构化异常处理器
12.2.2 编译器插入的SEH 处理器
12.2.3 基于信号的异常处理
12.2.4 实验:观察默认的异常处理器
12.2.5 BaseThreadStart 函数中的结构化异常处理器
12.3 未处理异常过滤函数
12.3.1 Windows XP 之前的异常处理机制
12.3.2 Windows XP 中的异常处理机制
12.4 “应用程序错误”对话框
12.4.1 用HardError 机制提示应用程序错误
12.4.2 使用ReportFault API 提示应用程序错误
12.5 JIT 调试和Dr. Watson
12.5.1 配置JIT 调试器
12.5.2 启动JIT 调试器
12.5.3 自己编写JIT 调试器
12.6 顶层异常过滤函数
12.6.1 注册
12.6.2 C 运行时库的顶层过滤函数
12.6.3 执行
12.6.4 调试
12.7 Dr. Watson
12.7.1 配置和查看模式
12.7.2 设置为默认的JIT 调试器
12.7.3 JIT 调试模式
12.8 DRWTSN32 的日志文件
12.8.1 异常信息
12.8.2 系统信息
12.8.3 任务列表
12.8.4 模块列表
12.8.5 线程状态
12.8.6 函数调用序列
12.8.7 原始栈数据
12.9 用户态转储文件
12.9.1 文件格式概览
12.9.2 数据流
12.9.3 产生转储文件
12.9.4 读取转储文件
12.9.5 利用转储文件分析问题
12.10 本章总结
参考资料
第 13 章 硬错误和蓝屏
13.1 硬错误提示
13.1.1 缺盘错误
13.1.2 NtRaiseHardError
13.1.3 ExpRaiseHardError
13.1.4 CSRSS 中的分发过程
13.2 蓝屏终止
13.2.1 简介
13.2.2 发起和产生过程
13.2.3 诊断蓝屏错误
13.2.4 手工触发蓝屏
13.3 系统转储文件
13.3.1 分类
13.3.2 文件格式
13.3.3 产生方法
13.4 分析系统转储文件
13.4.1 初步分析
13.4.2 线程和栈回溯
13.4.3 陷阱帧
13.4.4 自动分析
13.5 辅助的错误提示方法
13.5.1 MessageBeep
13.5.2 Beep 函数
13.5.3 闪动窗口
13.6 配置错误提示机制
13.6.1 SetErrorMode API
13.6.2 IoSetThreadHardErrorMode
13.6.3 蓝屏后自动重启
13.7 防止滥用错误提示机制
13.8 本章总结
参考资料
第 14 章 错误报告
14.1 WER 1.0
14.1.1 客户端
14.1.2 报告模式
14.1.3 传输方式
14.2 系统错误报告
14.3 WER 服务器端
14.3.1 WER 服务
14.3.2 错误报告分类方法
14.3.3 报告回应
14.4 WER 2.0
14.4.1 模块变化
14.4.2 创建报告
14.4.3 提交报告
14.4.4 典型应用
14.5 CER
14.6 本章总结
参考资料
第 15 章 日志
15.1 日志简介
15.2 ELF 的架构
15.2.1 ELF 的日志文件
15.2.2 事件源
15.2.3 ELF 服务
15.3 ELF 的数据组织350
15.3.1 日志记录
15.3.2 添加日志记录
15.3.3 API 一览
15.4 查看和使用ELF 日志
15.5 CLFS 的组成和原理
15.5.1 组成
15.5.2 存储结构
15.5.3 LSN
15.6 CLFS 的使用方法
15.6.1 创建日志文件
15.6.2 添加CLFS 容器
15.6.3 创建编组区
15.6.4 添加日志记录
15.6.5 读日志记录
15.6.6 查询信息
15.6.7 管理和备份
15.7 本章总结
参考资料
第 16 章 事件追踪
16.1 简介
16.2 ETW 的架构
16.3 提供ETW消息
16.4 控制ETW会话
16.5 消耗ETW消息
16.6 格式描述
16.6.1 MOF文件
16.6.2 WPP
16.7 NT 内核记录器
16.7.1 观察NKL的追踪事件
16.7.2 编写代码控制NKL
16.7.3 NKL 的实现
16.8 Global Logger Session
16.8.1 启动GLS 会话
16.8.2 配置GLS
16.8.3 在驱动程序中使用GLS
16.8.4 自动记录器
16.8.5 BootVis 工具
16.9 Crimson API
16.9.1 发布事件
16.9.2 消耗事件
16.9.3 格式描述
16.9.4 收集和观察事件
16.9.5 Crimson API 的实现
16.10 本章总结
参考资料
第 17 章 WHEA
17.1 目标、架构和PSHED.DLL
17.1.1 目标
17.1.2 架构
17.1.3 PSHED.DLL
17.2 错误源
17.2.1 标准的错误源
17.2.2 通过ACPI 表来定义错误源
17.2.3 通过PSHED 插件来报告错误源
17.3 错误处理过程
17.3.1 WHEA_ERROR_PACKET结构
17.3.2 处理过程
17.3.3 WHEA_ERROR_RECORD结构
17.3.4 固件优先模式
17.4 错误持久化
17.4.1 ERST
17.4.2 工作过程
17.5 注入错误
17.6 本章总结
参考资料
第 18 章 内核调试引擎
18.1 概览
18.1.1 KD
18.1.2 角色
18.1.3 组成
18.1.4 模块文件
18.1.5 版本差异
18.2 连接
18.2.1 串行端口
18.2.2 1394
18.2.3 USB 2.0
18.2.4 管道
18.2.5 选择连接方式
18.2.6 解决连接问题
18.3 启用
18.3.1 BOOT.INI
18.3.2 BCD
18.3.3 高级启动选项
18.4 初始化
18.4.1 Windows 系统启动过程概述
18.4.2 第 一次调用KdInitSystem
18.4.3 第二次调用KdInitSystem
18.4.4 通信扩展模块的阶段1初始化
18.5 内核调试协议
18.5.1 数据包
18.5.2 报告状态变化
18.5.3 访问目标系统
18.5.4 恢复目标系统执行
18.5.5 版本
18.5.6 典型对话过程
18.5.7 KdTalker
18.6 与内核交互
18.6.1 中断到调试器
18.6.2 KdpSendWaitContinue
18.6.3 退出调试器
18.6.4 轮询中断包
18.6.5 接收和报告异常事件
18.6.6 调试服务
18.6.7 打印调试信息
18.6.8 加载调试符号
18.6.9 更新系统文件
18.7 建立和维持连接
18.7.1 最早的调试机会
18.7.2 初始断点
18.7.3 断开和重新建立连接
18.8 本地内核调试
18.8.1 LiveKD
18.8.2 Windows 系统自己的本地内核调试支持
18.8.3 安全问题
18.9 本章总结
参考资料
第 19 章 验证机制
19.1 简介
19.1.1 驱动程序验证器
19.1.2 应用程序验证器
19.1.3 WHQL 测试
19.2 驱动验证器的工作原理
19.2.1 设计原理
19.2.2 初始化
19.2.3 挂接验证函数
19.2.4 验证函数的执行过程
19.2.5 报告验证失败
19.3 使用驱动验证器
19.3.1 验证项目
19.3.2 启用驱动验证器
19.3.3 开始验证
19.3.4 观察验证情况
19.3.5 WinDBG 的扩展命令
19.4 应用程序验证器的工作原理
19.4.1 原理和组成
19.4.2 初始化
19.4.3 挂接API
19.4.4 验证函数的执行过程
19.4.5 报告验证失败
19.4.6 验证停顿
19.5 使用应用程序验证器
19.5.1 应用验证管理器
19.5.2 验证项目
19.5.3 配置验证属性
19.5.4 配置验证停顿
19.5.5 编程调用
19.5.6 调试扩展
19.6 本章总结
参考资料
第四篇 编译器的调试支持
第 20 章 编译和编译期检查
20.1 程序的构建过程
20.1.1 链接器
20.1.2 加载器
20.2 编译
20.2.1 前端
20.2.2 后端
20.3 Visual C++编译器
20.3.1 MSVC 简史
20.3.2 MSVC6
20.3.3 VS7 和VS8
20.3.4 构建程序
20.3.5 调试
20.4 编译错误和警告
20.4.1 错误ID 和来源
20.4.2 编译警告
20.5 编译期检查
20.5.1 未初始化的局部变量
20.5.2 类型不匹配
20.5.3 使用编译器指令
20.5.4 标注
20.5.5 驱动程序静态验证器
20.6 标准标注语言
20.6.1 缓冲区标注符
20.6.2 高级标注符
20.7 本章总结
参考资料.
第 21 章 运行时库和运行期检查
21.1 C/C++运行时库
21.1.1 C 运行时库
21.1.2 C++标准库
21.2 链接运行时库
21.2.1 静态链接和动态链接
21.2.2 lib 文件
21.3 运行时库的初始化和清理
21.3.1 介入方法
21.3.2 初始化
21.3.3 多个运行时库实例
21.4 运行期检查
21.4.1 自动的运行期检查
21.4.2 断言
21.4.3 _RPT 宏
21.5 报告运行期检查错误
21.5.1 _CrtDbgReport
21.5.2 _CrtSetReportMode
21.5.3 _CrtSetReportFile
21.5.4 _CrtSetReportHook 493
21.5.5 _CrtSetReportHook2
21.5.6 使用其他函数报告RTC错误
21.6 本章总结
参考资料
第 22 章 栈和函数调用
22.1 简介
22.1.1 用户态栈和内核态栈
22.1.2 函数、过程和方法
22.2 栈的创建过程
22.2.1 内核态栈的创建
22.2.2 用户态栈的创建
22.2.3 跟踪用户态栈的创建过程
22.3 CALL 和RET 指令
22.3.1 CALL 指令
22.3.2 RET 指令
22.3.3 观察函数调用和返回过程
22.3.4 跨特权级调用
22.4 局部变量和栈帧
22.4.1 局部变量的分配和释放
22.4.2 EBP 寄存器和栈帧
22.4.3 帧指针和栈帧的遍历
22.5 帧指针省略
22.6 栈指针检查
22.7 调用协定
22.7.1 C 调用协定
22.7.2 标准调用协定
22.7.3 快速调用协定
22.7.4 This 调用协定
22.7.5 CLR 调用协定
22.7.6 x64 调用协定
22.7.7 通过编译器开关改变默认调用协定
22.7.8 函数返回值
22.7.9 归纳和补充
22.8 栈空间的增长和溢出
22.8.1 栈空间的自动增长
22.8.2 栈溢出
22.8.3 分配检查
22.9 栈下溢
22.10 缓冲区溢出
22.10.1 感受缓冲区溢出
22.10.2 缓冲区溢出攻击
22.11 变量检查
22.12 基于Cookie 的安全检查
22.12.1 安全Cookie 的产生、植入和检查
22.12.2 报告安全检查失败
22.12.3 编写安全的代码
22.13 本章总结
参考资料
第 23 章 堆和堆检查
23.1 理解堆
23.2 堆的创建和销毁
23.2.1 进程的默认堆
23.2.2 创建私有堆
23.2.3 堆列表
23.2.4 销毁堆
23.3 分配和释放堆块
23.3.1 HeapAlloc
23.3.2 CRT 分配函数
23.3.3 释放从堆中分配的内存
23.3.4 GlobalAlloc 和LocalAlloc
23.3.5 解除提交
23.4 堆的内部结构
23.4.1 结构和布局
23.4.2 HEAP 结构
23.4.3 HEAP_SEGMENT结构
23.4.4 HEAP_ENTRY结构
23.4.5 分析堆块的分配和释放过程
23.4.6 使用!heap 命令观察堆块信息
23.5 低碎片堆
23.6 堆的调试支持
23.6.1 全局标志
23.6.2 堆释放检查
23.7 栈回溯数据库
23.7.1 工作原理
23.7.2 DH 和UMDH工具
23.7.3 定位内存泄漏
23.8 堆溢出和检测
23.8.1 堆缓冲区溢出
23.8.2 调用时验证
23.8.3 堆尾检查
23.9 页堆
23.9.1 总体结构
23.9.2 启用和观察页堆
23.9.3 堆块结构
23.9.4 检测溢出
23.10 准页堆
23.10.1 启用准页堆
23.10.2 结构布局
23.10.3 检测溢出
23.11 CRT 堆
23.11.1 CRT 堆的3 种模式
23.11.2 SBH 简介
23.11.3 创建和选择模式
23.11.4 CRT 堆的终止
23.12 CRT 堆的调试堆块
23.12.1 _CrtMemBlockHeader结构
23.12.2 块类型
23.12.3 分配堆块
23.13 CRT 堆的调试功能
23.13.1 内存分配序号断点
23.13.2 分配挂钩
23.13.3 自动和手动检查
23.14 堆块转储
23.14.1 内存状态和检查点
23.14.2 _CrtMemDumpAllObjectsSince
23.14.3 转储挂钩
23.15 泄漏转储
23.15.1 _CrtDumpMemoryLeaks
23.15.2 何时调用
23.15.3 定位导致泄漏的源代码
23.16 本章总结
参考资料
第 24 章 异常处理代码的编译
24.1 概览
24.2 FS:[0]链条
24.2.1 TEB 和TIB 结构
24.2.2 ExceptionList 字段
24.2.3 登记异常处理器
24.3 遍历FS:[0]链条
24.3.1 RtlDispatchException
24.3.2 KiUserExceptionDispatcher
24.4 执行异常处理函数
24.4.1 SehRaw 实例
24.4.2 执行异常处理函数
24.5 _ _ try{}_ _ except()结构
24.5.1 与手工方法的对应关系
24.5.2 _ _ try{}_ _ except()结构的编译
24.5.3 范围表
24.5.4 TryLevel
24.5.5 _ _ try{}_ _ except()结构的执行
24.5.6 _SEH_prolog 和_SEH_epilog
24.6 安全问题
24.6.1 安全Cookie
24.6.2 SAFESEH
24.6.3 基于表的异常处理
24.7 本章总结
参考资料
第 25 章 调试符号
25.1 名称修饰
25.1.1 C 和C++
25.1.2 C 的名称修饰规则
25.1.3 C++的名称修饰规则
25.2 调试信息的存储格式
25.2.1 COFF格式
25.2.2 CodeView 格式
25.2.3 PDB格式
25.2.4 DWARF格式
25.3 目标文件中的调试信息
25.3.1 IMAGE_FILE_HEADER结构
25.3.2 IMAGE_SECTION_HEADER结构
25.3.3 节的重定位信息和行号信息
25.3.4 存储调试数据的节
25.3.5 调试符号表
25.3.6 COFF 字符串表
25.3.7 COFF 符号例析
25.4 PE 文件中的调试信息
25.4.1 PE 文件布局
25.4.2 IMAGE_OPTIONAL_HEADER结构
25.4.3 调试数据目录
25.4.4 调试数据
25.4.5 使用WinDBG 观察PE 文件中的调试信息
25.4.6 调试信息的产生过程
25.5 DBG 文件
25.5.1 从PE 文件产生DBG 文件
25.5.2 DBG 文件的布局
25.6 PDB 文件
25.6.1 复合文件
25.6.2 PDB 文件布局
25.6.3 PDB 签名
25.6.4 Magic 代码
25.6.5 PDB_HEADER
25.6.6 根数据流——流目录
25.6.7 页分配表
25.6.8 访问PDB 文件的方式
25.6.9 PDB 文件的产生过程
25.7 有关的编译和链接选项
25.7.1 控制调试信息的编译选项
25.7.2 控制调试信息的链接选项
25.7.3 不同链接和编译选项的比较
25.8 PDB 文件中的数据表
25.8.1 符号表
25.8.2 源文件表
25.8.3 节贡献表
25.8.4 段信息表
25.8.5 注入源代码表
25.8.6 帧数据表
25.9 本章总结
参考资料
第五篇 调 试 器
第 26 章 调试器概览
26.1 TX-0 计算机和FLIT调试器
26.2 小型机和DDT调试器
26.2.1 PDP-1
26.2.2 TOPS-10 操作系统和
DDT-10
26.3 个人计算机和它的调试器
26.3.1 8086 Monitor
26.3.2 SYMDEB
26.3.3 CodeView调试器
26.3.4 Turbo Debugger
26.3.5 SoftICE
26.4 调试器的功能
26.4.1 建立和终止调试会话
26.4.2 控制被调试程序执行
26.4.3 访问内存
26.4.4 访问寄存器
26.4.5 断点
26.4.6 跟踪执行
26.4.7 观察栈和栈回溯
26.4.8 汇编和反汇编
26.4.9 源代码级调试..685
26.4.10 EnC
26.4.11 文件管理
26.4.12 接收和显示调试信息
26.4.13 转储
26.5 分类标准
26.5.1 特权级别
26.5.2 操作系统
26.5.3 执行方式
26.5.4 处理器架构
26.5.5 编程语言688
26.6 实现模型
26.6.1 进程内调试模型
26.6.2 进程外调试模型
26.6.3 混合调试模型
26.6.4 内核调试模型
26.7 经典架构
26.7.1 基本单元
26.7.2 远程调试
26.7.3 多语言和多处理器架构调试
26.8 HPD 标准
26.8.1 HPD 标准简介
26.8.2 动作点
26.8.3 进程和线程的表示和命名
26.8.4 命令
26.9 本章总结
参考资料
第 27 章 VsDebug
27.1 架构和调试模型
27.1.1 架构概览
27.1.2 远程调试器
27.1.3 本地调试器
27.2 VS 调试引擎
27.2.1 一套接口,多种实现
27.2.2 核心类
27.3 工作过程
27.3.1 开始调试32 位本地程序
27.3.2 开始调试64 位本地程序
27.3.3 访问调试目标
27.4 使用断点
27.4.1 根据名称设置断点
27.4.2 数据断点
27.4.3 附加条件
27.4.4 附加操作
27.5 多线程调试
27.5.1 并行栈回溯
27.5.2 并行监视
27.5.3 冻结线程
27.6 EnC
27.6.1 应用过程
27.6.2 要求/ZI 编译选项
27.6.3 下次调用生效
27.6.4 应用失败
27.7 设计期调试
27.8 使用符号服务器
27.9 定制调试事件
27.9.1 初始断点
27.9.2 异常设置
27.10 本章总结
参考资料
第 28 章 VS Code 的调试扩展
28.1 简介
28.2 四大技术
28.3 理解“扩展包”
28.3.1 包类型
28.3.2 安装
28.3.3 工作原理
28.4 扩展包API
28.4.1 贡献点
28.4.2 命令
28.4.3 激活事件
28.5 调试模型
28.5.1 贡献调试器
28.5.2 宏观架构
28.6 调试适配器
28.6.1 DA 描述符工厂
28.6.2 进程内DA
28.6.3 vsdbg
28.6.4 OpenDebugAD7
28.7 机器接口
28.7.1 启用用法
28.7.2 对话示例
28.7.3 MIEngine
28.8 调试Python 程序
28.8.1 PTVSD
28.8.2 发起异常时中断
28.9 本章总结
参考资料
第 29 章 WinDBG 及其实现
29.1 WinDBG 溯源
29.1.1 KD 和NTSD 诞生
29.1.2 WinDBG 诞生
29.1.3 发行方式
29.1.4 版本历史
29.2 C 阶段的架构
29.2.1 功能模块
29.2.2 远程调试
29.3 重构
29.3.1 版本历史
29.3.2 界面变化
29.3.3 模块变化
29.3.4 发布方式和NTSD 问题
29.3.5 文件
29.4 调试器引擎的架构
29.4.1 概览
29.4.2 对外接口
29.4.3 DebugClient 类
29.4.4 中间层
29.4.5 服务层
29.4.6 传输和连接层
29.5 调试目标
29.5.1 TargetInfo 类
29.5.2 用户态目标
29.5.3 内核态目标
29.5.4 转储文件目标
29.6 调试会话
29.6.1 建立调试会话
29.6.2 调试循环
29.6.3 等待和处理调试事件
29.6.4 继续调试事件
29.6.5 结束调试会话
29.7 接收和处理命令
29.7.1 调试器的两种工作状态
29.7.2 进入命令状态
29.7.3 执行命令
29.7.4 结束命令状态
29.8 扩展命令的工作原理
29.9 本章总结
参考资料
第30 章 WinDBG 用法详解
30.1 工作空间
30.2 命令概览
30.2.1 标准命令
30.2.2 元命令
30.2.3 扩展命令
30.3 用户界面
30.3.1 窗口概览
30.3.2 命令窗口和命令提示符
30.4 输入和执行命令
30.4.1 要点
30.4.2 表达式
30.4.3 伪寄存器
30.4.4 别名
30.4.5 循环和条件执行
30.4.6 进程限定符和线程限定符
30.4.7 记录到文件
30.5 建立调试会话
30.5.1 附加到已经运行的进程
30.5.2 创建并调试新的进程
30.5.3 非入侵式调试
30.5.4 双机内核调试
30.5.5 本地内核调试
30.5.6 调试转储文件
30.5.7 远程调试
30.6 终止调试会话
30.6.1 停止调试
30.6.2 分离调试目标
30.6.3 抛弃被调试进程
30.6.4 终止被调试进程
30.6.5 调试器终止或僵死
30.6.6 重新开始调试
30.7 理解上下文
30.7.1 登录会话上下文
30.7.2 进程上下文
30.7.3 寄存器上下文
30.7.4 局部(变量)上下文
30.8 调试符号
30.8.1 重要意义
30.8.2 符号搜索路径
30.8.3 符号服务器
30.8.4 加载符号文件
30.8.5 观察模块信息
30.8.6 检查符号
30.8.7 搜索符号
30.8.8 设置符号选项
30.8.9 加载不严格匹配的符号文件
30.9 事件处理
30.9.1 调试事件与异常的关系
30.9.2 两轮机会
30.9.3 定制事件处理方式
30.9.4 GH 和GN 命令
30.9.5 实验
30.10 控制调试目标
30.10.1 初始断点
30.10.2 俘获调试目标
30.10.3 继续运行
30.11 单步执行
30.11.1 概览
30.11.2 单步执行到指定地址
30.11.3 单步执行到下一个函数调用
30.11.4 单步执行到下一分支
30.11.5 追踪并监视
30.11.6 程序指针飞跃
30.11.7 归纳
30.12 使用断点
30.12.1 软件断点
30.12.2 硬件断点
30.12.3 条件断点
30.12.4 地址表达方法
30.12.5 设置针对线程的断点
30.12.6 管理断点
30.13 控制进程和线程
30.13.1 MulThrds 程序
30.13.2 控制线程执行824
30.13.3 多进程调试
30.14 观察栈
30.14.1 显示栈回溯
30.14.2 观察栈变量
30.15 分析内存
30.15.1 显示内存区域
30.15.2 显示字符串
30.15.3 显示数据类型
30.15.4 搜索内存
30.15.5 修改内存
30.15.6 使用物理内存地址
30.15.7 观察内存属性
30.16 遍历链表
30.16.1 结构定义
30.16.2 双向链表示例
30.16.3 单向链表示例
30.16.4 dl 命令
30.16.5 !list 命令
30.17 调用目标程序的函数
30.17.1 调用示例
30.17.2 工作原理
30.17.3 限制条件和常见错误.
30.18 命令程序
30.18.1 流程控制符号
30.18.2 变量
30.18.3 命令程序示例
30.18.4 执行命令程序
30.19 本章总结
参考资料
附录A 示例程序列表
附录B WinDBG 标准命令列表
附录C NT 内核部件缩写列表
持之若痴——代跋
^ 收 起
张银奎,国内知名的调试技术专家。毕业于上海交通大学信息与控制工程系,长期从事软件开发和研究工作,曾在英特尔工作13 年,对 IA-32 架构、操作系统内核、驱动程序,尤其是对软件调试有较深入的研究。著有《软件调试》《格蠹汇编》等畅销、常销技术图书,格蠹科技(xedge.ai)创始人,高端调试网站(advdbg.org)创建者。翻译(合译)作品有《二十一世纪机器人》《观止——微软创建NT 和未来的夺命狂奔》《数据挖掘原理》《机器学习》《人工智能:复杂问题求解的结构和策略》等。
本书是国内当前集中介绍软件调试主题的权威著作。本书第2 卷分为5 篇,共30 章,主要围绕Windows系统展开介绍。第一篇(第1~4 章)介绍Windows 系统简史、进程和线程、架构和系统部件,以及Windows系统的启动过程,既从空间角度讲述Windows 的软件世界,也从时间角度描述Windows 世界的搭建过程。第二篇(第5~8 章)描述特殊的过程调用、垫片、托管世界和Linux 子系统。第三篇(第9~19 章)深入探讨用户态调试模型、用户态调试过程、中断和异常管理、未处理异常和JIT 调试、硬错误和蓝屏、错误报告、日志、事件追踪、WHEA、内核调试引擎和验证机制。第四篇(第20~25 章)从编译和编译期检查、运行时库和运行期检查、栈和函数调用、堆和堆检查、异常处理代码的编译、调试符号等方面概括编译器的调试支持。第五篇(第26~30 章)首先纵览调试器的发展历史、工作模型和经典架构,然后分别讨论集成在Visual Studio 和Visual Studio(VS)Code 中的调试器,最后深度解析WinDBG 调试器的历史、结构和用法。
本书理论与实践结合,不仅涵盖了相关的技术背景知识,还深入研讨了大量具有代表性的技术细节,是学习软件调试技术的珍贵资料。
本书适合所有从事软件开发工作的读者阅读,特别适合从事软件开发、测试和支持的技术人员阅读。
^ 收 起
本书理论与实践结合,不仅涵盖了相关的技术背景知识,还深入研讨了大量具有代表性的技术细节,是学习软件调试技术的珍贵资料。
本书适合所有从事软件开发工作的读者阅读,特别适合从事软件开发、测试和支持的技术人员阅读。
^ 收 起
比价列表