读书笔记:调试软件 张银奎
时间:2023-06-23 05:07:00
版本 第一版 2021年02月06日 11:53:06 2021年3月9日22:22:25 windbg下载 x64,http://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKDebuggingTools_amd64/dbg_amd64.msi x86,http://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKDebuggingTools/dbg_x86.msi 学习windbg时 使用帮助文档 可使用windbg调试windbg 第30章 WindDBG用法详解 30.0 概述 WinDBG支持多种调试服务 用户态调试 内核态调试 转储文件调试 远程调试 灵活性和扩展性都很好 30.1 工作空间 workspace 描述和存储,调试项目的属性、参数、调试设置等 分类 默认工作空间 基本工作空间(base workspace),调试会话尚未建立 默认的内核态工作空间(default kernel-mode workspace),开始内核调试,但未连接调试目标 远程调试工作空间默认(remote default workspace),通过调试服务器(DbgSrv或KdSrv)远程调试 特定处理器的工作空间(processor-specific workspace),开始内核调试并连接调试目标,了解对方处理器的类型 默认用户态工作空间(default user-mode workspace),附加到过程中 命名的工作空间(显式工作空间,explicit) 包含的信息 调试会话状态 断点、开源文件、自定义别名 调试器设置 可执行图像文件路径的符号文件路径,源文件路径 设置日志文件,内核调试连接设置 打开使用文件对话框的设置 WinDBG图形界面信息 保存和打开工作空间 使用注册表保存配置 路径,HKEY_CURRENT_USER\Software\Microsoft\WinDBG\Workspace 子键表示类型,User,Kernel,Dump,Explicit 保存到文件 WinDBG,配置以增量的方式应用 30.2 命令概览 30.2.1 标准命令 基本调试功能 实现在WinDBG内部 分类(按相关对象分类) 调试目标控制 恢复执行,g系列 跟踪执行,t系列,trace into 单步执行,p系列,step over 追踪监视,wt 寄存器 通用寄存器,r MSR寄存器,rdmsr,wtmsr 设置显示掩码,rm IO端口 ib,iw,id ob,ow,od 内存 查看,d 编辑,e 搜索,s 栈,k系列 断点 设置,bp软件断点,ba硬件断点 列举,bl 控制,bc、bd、be,参数、禁用、启用 线程,~,控制和显示 进程,|,显示 表达式评估 ?,汇编 ?,汇编 ??,C 汇编控制 a,汇编 u,反汇编 段选择子,dg,显示 命令文件,$,执行 选项设置 调试事件处理方法,sx 禁用和启用静默模式,sq 内核选项,so 符号后缀,ss 版本 version,调试器和调试目标 vertarget,调试目标所在系统 符号,x,检查 源程序,ls,控制和显示 调试会话结束 基本,q 结束远程,qq 调试会话结束,分离调试目标,qd 其他 ld,加载调试符号 ln,搜索相邻符号 lm,列举模块 帮助,? B[C|D|E][] - clear/disable/enable breakpoint(s) BL - list breakpoints BA - set processor breakpoint BP - set soft breakpoint D[type][] - dump memory DT [-n|y] [[mod!]name] [[-n|y]fields] [address] [-l list] [-a[]|c|i|o|r[#]|v] - dump using type information DV [] - dump local variables E[type] [] - enter memory values G[H|N] [= [...]] - go K - stacktrace KP - stacktrace with source arguments LM[k|l|u|v] - list modules LN - list nearest symbols P [=] [] - step over Q - quit R [[ [= ]]] - view or set registers S[] - search memory SX [{e|d|i|n} [-c "Cmd1"] [-c2 "Cmd2"] [-h] {Exception|Event|*}] - event filter T [=] [] - trace into U [] - unassemble version - show debuggee and debugger version X [<*|module>!]<*|symbol> - view symbols ?]<*|symbol> - view symbols ? - display expression ?? - display C expression $< - take input from a command file 30.2.2 元命令 meta-command,dot command 常用的调试功能 实现在WinDBG内部 分类(按相关对象分类) 显示和设置调试会话和调试器选项 符号选型,.symopt 符号路径,.sympath和symfix
源文件,.srcpath,.srcnoise,.srcfix
扩展命令模块路径,.extpath
匹配扩展命令,.extmatch
可执行文件,.exepath
反汇编,.asm
表达式评估器,.expr
调试会话和调试目标,控制
创建新进程,.create
附加旧进程,.attach
打开转储文件,.opendump
分离附加目标,.detach
杀掉进程,.kill
重新开始调试会话,.restart
放弃用户态调试目标(进程),.abandon
扩展命令模块,管理
加载,.load
卸载,.unload,.unloadall
显示,.chain
调试器日志文件,管理
新建打开,.logopen
追加打开,.logappend
打开状态,.logfile
关闭,.logclose
远程调试
远程调试客户端,remote.exe
启动,.remote
远程调试服务器,调试引擎服务器
启动服务器,.server
列举服务器,.servers
向服务器发送文件,.send_file
结束服务器,.endsrv
远程进程服务器
结束服务器,.endpsrv
调试器,控制
睡眠,.sleep
唤醒,.wake
启动新调试器,调试当前调试器,.dbgdbg
命令程序,编写
类似于C语言的关键字
.if, .else, .elif
.foreach, .do, .while, .continue, .break
.catch, .leave, .printf, .block
其他
产生转储文件,.dump
保存内存数据,.writemem
显示调试会话时间,.time
显示线程时间,.ttime
显示任务列表,.tlist
格式化数字,.formats
元命令帮助,.help
. commands:
.abandon - abandon the current process
.allow_exec_cmds [0|1] - control execution commands
.allow_image_mapping [0|1] - control on-demand image file mapping
.apply_dbp [] - add current data breakpoint state to a
register context
.asm [] - set disassembly options
.asm- [] - clear disassembly options
.attach - attach to at next execution
.block { } - brackets a set of commands for nested execution
.bpsync [0|1] - special breakpoint behavior for multithreaded debuggees
.break - break out of the enclosing loop
.breakin - break into KD
.cache [] - virtual memory cache control
.call (, , ...) - run a function in the debuggee
.catch { } - catch failures in commands
.chain - list current extensions
.childdbg <0|1> - turn child process debugging on or off
.clients - list currently active clients
.closehandle [] [] - close the given handle
.continue - continue the enclosing loop
.copysym [] - copy current symbol files to a directory
.create - create a new process
.createdir [] [] - control process creation options
.cxr - dump context record at specified address
k* after this gives cxr stack
.dbgdbg - attach a debugger to the current debugger
.debug_sw_wow [0|1] - allow interaction with software WOW emulation
.detach - detach from the current process/dump
.dml_file - output DML content from file
.dml_flow - show basic block code flow
.dml_start [] - navigable overview of debugger activities
.do { } () - execute until is zero
.drivers - This command was removed -- use 'lm' or .reload -l)
.dump [] - create a dump file on the host system
.dvalloc [] - VirtualAlloc memory in the debuggee
.dvfree [] - VirtualFree memory in the debuggee
.echo [""|] - echo string
.echotime - output debugger time
.echotimestamps [0|1] - toggle timestamp output on events
.ecxr - dump context record for current exception
.effmach [] - change current machine type
.else { } - if/then/else conditional execution
.elsif () { } [] - if/then/else conditional
execution
.enable_long_status [0|1] - dump LONG types in default base
.enable_unicode [0|1] - dump USHORT array/pointers and unicode strings
.endsrv - disable the given engine server
.endpsrv - cause the current session's remote server to exit
.enumtag - enumerate available tagged data
.event_code - display cached event instructions
.eventlog - display log of recent events
.events - display and select available events
.eventstr - display any event strings registered by debuggee
.exepath [[;...]] - set executable search path
.exepath+ [[;...]] - append executable search path
.expr - control expression evaluator
.exptr - do .exr and .cxr for EXCEPTION_POINTERS
.exr - dump exception record at specified address
.extmatch [] - display all extensions matching pattern
.extpath [[;...]] - set extension search path
.extpath+ [[;...]] - append extension search path
.f+ - set current stack frame to caller of current frame
.f- - set current stack frame to callee of current frame
.fiber - sets context of fiber at address
resets context if no address specified
.fiximports - attempts to link imports for images
.fnent - dump function entry for the given code address
.fnret [] - display formatted return value
.for ( ; ; ) { } - execute and
until is
zero
.force_radix_output [0|1] - dump integer types in default base
.force_system_init [] - force pending systems to initialize if possible
.force_tb - forcibly allow branch tracing
.foreach [opts] ( { } ) { } - execute for
each token in the
output of
.fpo - control override FPO information
.frame [] - set current stack frame for locals
.formats - displays expression result in many formats
.help [] - display this help
.holdmem [range] - hold and compare memory data
.if () { } [] - if/then/else conditional
execution
.ignore_missing_pages [0|1] - control kernel summary dump missing
page error message
.imgscan - scan memory for PE images
.jdinfo - interpret AeDebug information
.kframes - set default stack trace depth
.kill - kill the current process
.lastevent - display the last event that occurred
.leave - exit the enclosing .catch
.lines - toggle line symbol loading
.load - add this extension DLL to the extension chain
.loadby - add the extension DLL in the module
directory to the extension chain
.locale [] - set the current locale
.logfile - display log status
.logopen [] - open new log file
.logappend [] - append to log file
.logclose - close log file
.netsyms [0|1] - allow/disallow net symbol paths
.netuse [] - manage net connections
.noshell - disable shell commands
.noversion - disable extension version checking
.ofilter - filter debuggee output against the given pattern
.ocommand - treat output with the given prefix as a command
.opendump - open a dump file
.outmask - set bits in the current output mask
.outmask- - clear bits in the current output mask
.pcmd [] - control per-prompt command
.pop [] - pop state
.prefer_dml [0|1] - control DML mode default
.printf "", - formatted output
.process [] - sets implicit process
resets default if no address specified
.process_info - display security related information of current process
.prompt_allow [] - control what information can be displayed
at the prompt
.push [] - push state
.quit_lock [] - locks session against unexpected quit
.readmem - read raw memory from a file
.record_branches [0|1] - controls recording of processor branching
.reload [[=,]] - reload symbols
.restart - request a session restart
.remote - start remote.exe server
.secure [0|1] - disallow operations dangerous for the host
.send_file - send files to remote server
.server - start engine server
.servers - list active remoting servers
.setdll - debugger will search for extensions in this DLL first
.shell [] - execute shell command
.show_read_failures [] - control extra read failure output
.show_sym_failures [] - control extra symbol failure output
.sleep - debugger sleeps for given duration
useful for allowing access to a machine that's
broken in on an ntsd -d
.srcfix [] - fix source search path
.srcfix+ [] - append fixed source search path
.srcnoisy [0|1] - control verbose source loading output
.srcpath [[;...]] - set source search path
.srcpath+ [[;...]] - append source search path
.step_filter [] ["[;...]"] - Set symbol patterns
to skip when stepping
.symfix [] - fix symbol search path
.symfix+ [] - append fixed symbol search path
.symopt - set symbol options
.symopt+ - set symbol options
.symopt- - clear symbol options
.sympath [[;...]] - set symbol search path
.sympath+ [[;...]] - append symbol search path
.thread [] - sets context of thread at address
resets default context if no address specified
.time - displays session time information
.timezone - display timezone information
.ttime - displays thread time information
.tlist - list running processes
.typeopt - set/clear type options
.unload - remove this extension DLL from the list of extension DLLs
.unloadall - remove all extension DLLs from the list of extensions DLLs
.wake - wake up a .sleep'ing debugger
.while () { } - execute while is non-zero
.writemem - write raw memory to a file
Use ".hh " or open debugger.chm in the debuggers directory to get
detailed documentation on a command.
30.2.3 扩展命令
extension command
语法,![extension_module].extension_command [parameters]
省略extension_module,则自动搜索
特殊调试功能,针对特殊调试目标
实现在动态链接库(DLL)中
利用WinDBG的SDK,编写扩展模块和扩展命令
WinDBG程序包中,包含了常用的扩展命令模块
NT4CHK目录,调试目标为Windows NT 4.0 Checked版本时的扩展命令模块
NT4FRE目录,调试目标为Windows NT 4.0 Free版本时的扩展命令模块
W2KCHK目录,调试目标为Windows 2000 Checked版本时的扩展命令模块
W2KFRE目录,调试目标为Windows 2000 Free版本时的扩展命令模块
WINXP目录,调试目标为Windows XP或者更高版本时的扩展命令模块
acpikd.dll,用于ACPI调试,追踪调用ASL程序的过程,显示ACPI对象
exts.dll,关于堆(!heap),进程/线程结构(!teb/!peb),安全信息(!token,!sid,!acl),应用程序验证(!avrf)等
kdexts.dll,用于调试内核
fltkd.dll,用于调试过滤驱动程序(FsFilter)
minipkd.dll,用于调试AIC78xx小端口(miniport)驱动程序
ndiskd.dll,用于调试网络有关驱动程序
ntsdexts.dll,实现了!handle,!locks,!dp,!dreg
rpcexts.dll,用于RPC调试
scsikd.dll,用于调试SCSI有关的驱动程序
traceprt.dll,用于格式化ETW信息
vdmexts.dll,调试运行在VDM中的DOS程序和WOW程序
wow64exts.dll,调试运行在64位Windows中的32位程序
wmitrace.dll,显示WMI追踪有关的数据结构、缓冲区和日志文件
WINEXT目录,适用于所有Windows版本的扩展命令模块
ext.dll,适用于所有调试目标的常用扩展命令
kext.dll,适用于内核态调试的常用扩展命令
uext.dll,适用于用户态调试的常用扩展命令
logexts.dll,用于监视和记录API调用
sos.dll,用于调试托管代码和.Net程序
ks.dll,用于调试内核流(kernel stream)
wdfkd.dll,调试使用WDF(Windows Driver Foundation)编写的驱动程序
扩展模块的加载
自动加载
当调试目标被激活时,WinDBG会根据以下条件自动加载命令空间中指定的扩展模块
调试目标的类型
当前的工作空间
完整语法方式
!extension_module.extension_command
自动搜索和加载
手动加载
.load 模块完整路径
.load 模块名称,会在扩展模块搜索路径(EXTPATH)中寻找
.loadby target_module refer_module
在已加载模块(refer_module)所在目录,搜索和加载target_module
列举已经加载的扩展模块
.chain
扩展模块的卸载
.unload
.unloadall
查询模块信息
!extension_module.help
30.3 用户界面
30.3.1 窗口概述
框架窗口(Frame Window)
菜单和工具栏
工作窗口
Command,命令输入和显示结果
Watch,观察指定变量
Locals,观察局部变量
Registers,观察寄存器
Memory,观察内存
CallStack,观察调用栈
Disassembly,反汇编
ScratchPad,白板,做调试笔记
ProcessesAndThreads,观察进程和线程
CommandBrowser,执行和浏览命令
状态栏
30.3.2 命令窗口
组成
信息显示区
命令横条
命令提示符,命令编辑框
信息显示区
展示的信息包含
命令执行结果
调试事件
错误信息
调试引擎的提示信息
命令横条
未连接调试目标时
命令提示符,空
命令编辑框,Debuggee not connected
等待命令输入时
命令提示符,调试目标描述>
用户态目标
[||system_index:]process_index:thread_index>
内核态目标或内核态转储文件目标
[||system_index:][processor_index:]kd>
本地内核态调试
[||system_index:][processor_index:]lkd>
其中
system_index
同一Windows中多个用户态目标,同属于一个系统
每个内核目标,独属于一个系统
process_index
线程序号,始于0
processor_index
处理器序号,始于0
线程序号和处理器序号,统一编号
每个内核态目标,被分配一个进程序号
切换
系统
||system_index s
进程或处理器
|process_or_processor_index s
自动切换系统
线程
~thread_index s
只能在统一系统内切换
调试目标繁忙
命令提示符,*BUSY*
命令编辑框,Debuggee is running
WinDBG执行命令繁忙
命令提示符,*BUSY*
命令编辑框,空
执行.abandon放弃调试目标
命令提示符,NoTarget>
命令编辑框,空
等待用户输入
命令提示符,Input>
命令编辑框,空
30.4 输入和执行命令
30.4.1 要点
命令分隔符,;
重复命令,回车键
历史命令,方向键
终止命令,Ctrl+Break
使用KD或CDB时,使用Ctrl+C
详细输出模式切换,Ctrl+Alt+V
显示WinDBG与内核调试引擎之间的数据通信,Ctrl+Alt+D
帮助,F1或.hh command
30.4.2 表达式
参考
帮助文档,MASM Numbers and Operators
语言
?宏汇编语法
??C++表达式
运算符
基本,加减乘除、移位、求余、比较、位操作、正负号
特殊
hi或low,取得32位数的高16位或低16位
by或wo或dwo或qwo,从指定地址读数单字节或单字或双字或四字
poi,从指定地址读数指针长度
类函数运算符
$iment(Address),返回参数代表模块的入口地址
$scmp("String1", "String2"),比较字符串
$sicmp("String1", "String2"),比较字符串,忽略大小写
$spat("String", "Pattern"),判断字符串符合模式
$vvalid(Address, Length),判断有效内存区
$fnsucc(FnAddress, RetVal, Flag),判断函数执行成功
注释
可被记到日志中
*之后皆注释
*中间皆注释;
30.4.3 伪寄存器
参考
帮助文档,Pseudo-Register Syntax
WinDGB定义
Pseudo-register Description
$ea The effective address of the last instruction that was executed. If this instruction does not have an effective address, the debugger displays "Bad register error". If this instruction has two effective addresses, the debugger displays the first address.
$ea2 The second effective address of the last instruction that was executed. If this instruction does not have two effective addresses, the debugger displays "Bad register error".
$exp The last expression that was evaluated.
$ra The return address that is currently on the stack.
This address is especially useful in execution commands. For example, g @$ra continues until the return address is found (although gu (Go Up) is a more precise effective way of "stepping out" of the current function).
$ip The instruction pointer register.
x86-based processors: The same as eip.
Itanium-based processors: Related to iip. (For more information, see the note following this table.)
x64-based processors: The same as rip.
$eventip The instruction pointer at the time of the current event. This pointer typically matches $ip, unless you switched threads or manually changed the value of the instruction pointer.
$previp The instruction pointer at the time of the previous event. (Breaking into the debugger counts as an event.)
$relip An instruction pointer that is related to the current event. When you are branch tracing, this pointer is the pointer to the branch source.
$scopeip The instruction pointer for the current local context (also known as the scope).
$exentry The address of the entry point of the first executable of the current process.
$retreg The primary return value register.
x86-based processors: The same as eax.
Itanium-based processors: The same as ret0.
x64-based processors: The same as rax.
$retreg64 The primary return value register, in 64-bit format.
x86 processor: The same as the edx:eax pair.
$csp The current call stack pointer. This pointer is the register that is most representative of call stack depth.
x86-based processors: The same as esp.
Itanium-based processors: The same as bsp.
x64-based processors: The same as rsp.
$p The value that the last d* (Display Memory) command printed.
$proc The address of the current process (that is, the address of the EPROCESS block).
$thread The address of the current thread. In kernel-mode debugging, this address is the address of the ETHREAD block. In user-mode debugging, this address is the address of the thread environment block (TEB).
$peb The address of the process environment block (PEB) of the current process.
$teb The address of the thread environment block (TEB) of the current thread.
$tpid The process ID (PID) for the process that owns the current thread.
$tid The thread ID for the current thread.
$bpNumber The address of the corresponding breakpoint. For example, $bp3 (or $bp03) refers to the breakpoint whose breakpoint ID is 3. Number is always a decimal number. If no breakpoint has an ID of Number, $bpNumber evaluates to zero. For more information about breakpoints, see Using Breakpoints.
$frame The current frame index. This index is the same frame number that the .frame (Set Local Context) command uses.
$dbgtime The current time, according to the computer that the debugger is running on.
$callret The return value of the last function that .call (Call Function) called or that is used in an .fnret /s command. The data type of $callret is the data type of this return value.
$lastclrex Managed debugging only: The address of the last-encountered common language runtime (CLR) exception object.
$ptrsize The size of a pointer. In kernel mode, this size is the pointer size on the target computer.
$pagesize The number of bytes in one page of memory. In kernel mode, this size is the page size on the target computer.
用户定义
用户定义的伪寄存器(user-defined pseudo-register)
名称,$t0~$t19
初始值为0,可保存任意整数值
30.4.4 别名
分类
用户定义别名(user-named alias)
as new_name old_name
固定名称别名(fixed-name alias)
引用,$u<0~9>
修改,r $.u<0~9>=old_name
自动定义别名(automatic alias)
Alias name Alias equivalent
$ntnsym 内核态下为nt,用户态下为ntdll
$ntwsym ntdll32,ntdll
$ntsym 与当前调试目标的机器模型匹配的NT模块名称
$CurrentDumpFile 转储文件名称
$CurrentDumpPath 转出文件路径
$CurrentDumpArchiveFile 最近加载的CAB文件名称
$CurrentDumpArchivePath 最近加载的CAB文件路径
查看别名的取值
.echo
别名引用的形式
宽度,适合固定名称别名
空格,适合所有
分隔符,${alias},适合用户定义别名和自动定义别名
30.4.5 循环和分支
z命令
语法,z(condition)
功能,condition不为0和false,则重新执行命令
例子
r ecx=2
r ecx=ecx-1; r ecx; z(ecx), r ecx=ecx+1
!for_each_XXX命令
!for_each_frame !for_each_local dt @#Local
打印每个栈帧中的每个局部变量
j命令
j expression command1; command2
j expression 'commands1'; 'commands2'
.if.elif.else
.if (expression) {commands1} .elif (expression) {commands2} .else {commands3}
30.4.6 进程和线程限定符
条件 进程 线程
当前 |. ~.
导致当前调试事件 |# ~#
当前系统所有 |* ~*
序号 |Number ~number
ID |~ID ~~ID
30.4.7 记录到文件
.logopen,打开日志文件
.logfile,查看日志文件状态
.logclose,关闭日志文件
30.5 建立调试会话
30.5.1 附加到已有进程
菜单方式
File > Attach a Process
设置为JIT调试器
WinDBG.exe -I
当程序崩溃后,在错误对话框中选择Debug,便会启动WinDBG并附加
命令行方式
WinDBG.exe -p PID
WinDBG.exe -pn name
windbg的命令行选项
帮助文档中搜索,WinDbg Command-Line Options
windbg [ -server ServerTransport | -remote ClientTransport ] [-lsrcpath ]
[ -premote SmartClientTransport ] [-?] [-ee {masm|c++}]
[-clines lines] [-b] [-d] [-aExtension] [-e Event]
[-failinc] [-g] [-G] [-hd] [-j] [-n] [-noshell] [-o]
[-Q | -QY] [-QS | -QSY] [-robp] [-secure] [-ses] [-sdce]
[-sicv] [-sins] [-snc] [-snul] [-sup] [-sflags 0xNumber]
[-T Title] [-v] [-log{o|a} LogFile] [-noinh]
[-i ImagePath] [-y SymbolPath] [-srcpath SourcePath]
[-k [ConnectType] | -kl | -kx ExdiOptions] [-c "command"]
[-pb] [-pd] [-pe] [-pr] [-pt Seconds] [-pv]
[-W Workspace] [-WF Filename] [-WX] [-zp PageFile]
[ -p PID | -pn Name | -psn ServiceName | -z DumpFile | executable ]
windbg -I[S]
windbg -IU KeyString
windbg -IA[S]
.attach
需要已经有一个调试会话
常用于同时调试多个目标时
被.abandon抛弃的被调试进程,可以重新附加
WinDBG.exe -pe -p PID
30.5.2 创建进程并调试
菜单方式
File > Open Executable
命令行方式
windbg executable
.create
需要已经有一个调试会话
常用于同时调试多个目标时
30.5.3 非侵入式调试
调试用户态进程的一种特殊方式
windbg与目标进程,没有真正建立调试与被调试的关系,不能接收到任何调试事件
不能使用控制调试目标执行的命令,如单步、继续等
只能使用行观目标进程的命令
好处
减少调试器对目标进程的干预,最大程度减少海森伯效应
不影响其他调试器附加到目标进程进行调试
方式
只能用于附加方式
菜单方式
复选Noninvasive
命令行方式
WinDBG.exe -pv PID
.attach
加上-v开光
JIT不支持该方式
用途
Windows NT和Windows 2000不支持调试器和调试目标分离(detach)
一旦建立调试关系,结束调试会话将会结束调试进程
若使用非侵入方式附加调试,分析结束时只需执行分离命令,即可恢复
30.5.4 双机内核调试
步骤
1. 选择通信方式
串口方式,兼容性好,可靠性高
1394端口,速度快但不稳定
USB2
2. 启用目标系统的内核调试引擎
Vista之前的系统,修改boot.ini
Vista之后的系统,使用BCDEdit工具修改启动选项
3. 启动调试会话
菜单方式
File > Kernel Debug
命令行方式
windbg -k com:port=Com1, baud=115200
4. 后续
windbg显示"Waiting to reconnect..."并进入等待状态
等待目标系统的调试数据
并按照一定时间间隔(10s)发送复位数据包(PACKET_TYPE_KD_RESET)
目标系统在启动早期,初始化内核调试引擎时,会向调试器发送信息
windbg收到信息后,开始与目标系统对话,并建立调试连接
30.5.5 本地内核调试
系统条件
Windows 2000或更早的系统,不支持
Windows XP没有要求
Windows Vista,需要以调试选项启动系统
启动方式
菜单
File > Kernel Debug > Local
命令行
windbg -kl
.attach -k
需要已经有一个调试会话
常用于同时调试多个目标时
30.5.6 调试转储文件
菜单
File > Open Crash Dump
命令行
windbg -z
.opendump
需要已经有一个调试会话
常用于同时调试多个目标时
30.5.7 远程调试
本质,本地调试+远程通信
实现方式
方式一
服务器
DbgSrv(远程用户态调试)
KdSrv(远程内核态调试)
客户端
windbg
方式二(使用)
服务器和客户端,都是windbg
启动服务器
命令行方式
windbg -server 服务器通信字符串
命令方式
建立调试会话
.server 服务器通信字符串
服务器通信字符串
npipe:pipe=pipe_name
tcp:port=port_number,password=password
启动客户端
命令行方式
windbg -remote 客户端通信字符串
菜单方式
File > Connect to Remote Session
输入客户端通信字符串或直接浏览
客户端通信字符串
npipe:server=pc_name,pipe=pipe_name
tcp:server=pc_name,port=port_number,password=password
30.6 终止调试会话
30.6.1 停止调试
方式
菜单
debug > stop debugging
命令
q
结果
windbg
恢复到赋闲(dormant)状态
调试目标
活动的用户态目标,被终止
活动的内核目标,保持被中断到调试器的状态,可重新与其建立连接
30.6.2 分离调试目标
目的,使调试目标继续运行
方式
菜单
debug > detach debuggee
命令
.detach
结果
windbg
没有其他调试目标时,则恢复到赋闲(dormant)状态
调试目标继续运行
对于进程,系统会修改进程属性,使其脱离被调试状态而成为普通进程
条件
Windows XP或更高版本
依赖于Windows XP才引入的操作系统支持(DebugSetProcessKillOnExit API)
30.6.3 抛弃被调试进程
目的,重新附加调试目标
方式
命令
.abandon
结果
windbg
没有其他调试目标时,则恢复到无调试目标状态
被调试进程
被抛弃后,仍处于挂起状态
调试器中仅执行了注销操作,没有恢复进程状态
重新附加
windbg -pe -p PID
注意
没有-pe,调试器附加失败,并报告DebugPort不为空
有了-pe,调试器不会报错,调试器引擎会产生一个异常,触发调试器进入命令模式,使继续调试
30.6.4 杀死被调试进程
方式
命令
.kill,会调用系统TerminateProcess API来终止进程
结果
windbg
仍可观察调试目标的数据
执行g时,会结束当前调试会话
没有其他调试目标时,则恢复到赋闲(dormant)状态
被调试进程
被终止
30.6.5 调试器终止或僵死
调试器终止
建立的调试会话会被终止
活动的被调试进程,被终止
调试器僵死
可使用-pe -p PID启动windbg,重新附加被调试进程,后终止僵死的调试器
30.6.6 重新开始调试
方式
菜单
debug > restart
命令
.restart
结果
对于调试会话源于"创建进程并调试",关闭被调试进程并重新运行和调试
对于调试会话源于"附加到已有进程",不支持
对于内核态调试
.restart,重启调试器后再建立调试连接
.reboot,目标系统重启
30.7 理解上下文
30.7.0 概述
上下文,操作的执行环境,讨论的背景信息
30.7.1 登录会话上下文(Login Session Context)
含义,登录会话语境,当前操作或陈述的
Windows支持同时多个登录对话,每个对话拥有自身的输入输出设备和桌面
例子
Windows XP系统中,一般只有一个会话,被远程桌面登录后就会有两个
Windows Vista引入对话隔离技术(Session Isolation),所有系统服务运行在会话0以增强系统服务安全性,故至少存在两个会话
目前会话上下文仅在内核调试时有意义,相关扩展命令仅在调试内核目标时有意义
!session
!session,显示状态
!session -s index,切换
改变会话后,默认进程会变成新会话中的进程,以前缓存的用户空间数据不再有效
为了避免用户观察到错误的数据,可使用.cache命令在缓存选项中加入forcedecodeuser或forcedecodeptes选项禁止缓存功能,让调试器每次都重新读取内存数据
!spross,列出会话中所有进程
进程的EPROCESS结构的Session字段记录着进程的所属会话
每个会话都包含了Windows子系统服务器进程(CSRSS)
会话管理器不属于任一会话
30.7.2 进程上下文
含义,进程语境,当前操作或陈述的
在Windows操作系统中
所有进程的内核空间是共享的,用户空间是独立的
在32系统中,单个进程共4GB进程空间,低2GB是用户空间,高2GB是内核空间
在内核调试时
观察内核数据,不需要关心当前进程
观察用户空间数据,需要注意当前进程,同一用户态地址对不同进程的含义不同(实际的物理地址不同)
当调试目标中断到调试器中后,WinDBG会根据调试事件设置默认进程
若要观察其他进程的用户空间,需要先切换进程上下文
.process process_EPROCESS_address,根据进程的EPROCESS结构切换进程
.process 0 0,列出所有进程的基本信息
.context
设置和显示页目录基址(base of page directory)(用于翻译用户态地址)
页目录基址是进程的一个重要属性,使用.process设置进程上下文时自动设置
对于x86系统,cr3寄存器存放页目录基址,一个进程只有一个页目录基址
对于安腾系统,一个进程可使用多个页目录基址
.process和.context仅用在内核调试中
调试用户态目标时
所有虚地址都是基于当前进程的,不需要切换进程上下文
在一个调试会话中调试多个用户态目标时,应使用"lNumber s"切换进程
30.7.3 寄存器上下文(register context)
含义,寄存器语境,当前操作或陈述的
在多任务系统中
CPU寄存器保存的是当前正在执行的线程的寄存器值
对于没执行的线程,其寄存器值保存在内存中,当线程恢复执行时,寄存器值从内存加载到寄存器中
在调试器中观察一个线程的寄存器(不含MSR)时,该线程处于挂起状态,观察和修改的寄存器值源于内存
系统会在以下情况中,将寄存器值保存到当前线程的上下文记录(context record)中
线程切换时,该上下文常被称为线程上下文
中断或异常时,该上下文常被称为异常上下文
.thread
.thread thread_ETHREAD_address,根据线程的ETHREAD结构切换线程
.thread,查看当前线程
.process process_EPROCESS_address f
列出一个进程的所有线程
.crx或.thread
将线程上下文恢复成以前的情况
.ecrx
在调试用户态转储文件时,可将其中保存的异常上下文设置为寄存器上下文
在不同寄存器上下文中,观察到的寄存器和栈不同
r
kv
30.7.4 局部(变量)上下文(local context)
含义,局部变量语境,当前操作或陈述的
一个运行中的函数,对应一个局部上下文
运行中的函数,其局部变量信息存放在调用栈中
使用栈帧号代表局部上下文
.frame
.frame,观察当前局部上下文
.frame frame_index(十六进制),切换当前局部上下文到指定栈帧
dv,查看当前局部上下文中的参数和局部变量
例子
源码
void test_windbg(int a)
{
int b = 2;
getchar();
}
int main()
{
test_windbg(1);
return 0;
}
启动
windbg -g .\Test.exe
线程查看和切换
~* 查看所有线程
~0 s 切换到0号线程
~. 查看当前线程
栈帧查看和切换
k
.frame
.frame a
查看函数参数和局部变量
dv
30.7.5 上下文的关系
操作或陈述核心,作为操作或陈述的主体
操作或陈述语境,作为操作或陈述的客体
两者是多对多或一对多的关系,两者的组合整体作为新的上一层次的可标识实体(操作或陈述核心)
关系
会话进程
会话上下文
进程
进程上下文
线程(多个)
线程上下文
寄存器上下文
调用栈
局部(变量)上下文(多个)
线程变量
CPU时间片
30.8 调试符号
30.8.1 重要意义
调试符号(debug symbols)
其有无和版本是否准确,严重影响调试器的工作
30.8.2 符号搜索路径
背景
一个调试目标,可能存在多个符号文件,且不在同一位置
需要告诉调试器多个目录并按一定顺序搜索符号文件
符号搜索路径
符号文件搜索路径的列表
多个路径使用分号分隔
简称符号路径(symbol path)
路径种类
文件系统路径
符号服务器
设置方法
符号环境变量
_NT_SYMBOL_PATH
_NT_ALT_SYMBOL_PATH
命令行参数,-y
.sympath命令,增删显示
.symfix命令,自动设置
菜单,File>Symbol File Path
显示,.sympath命令
30.8.3 符号服务器
背景
一个调试目标,可能存在多个模块,一个模块可能包含多个版本,每个版本对应一个符号文件
这项符号文件查询工作,无聊和繁琐,可以交给程序完成
符号服务器(symbol server),存储符号文件的文件服务器
可以从中获取指定特征(名称和版本)的符号文件
符号服务器架构
示意图
windbg.exe
V
DbgEng.dll
V
DbgHelp.dll
V
符号服务器Dll(SymSrv.dll)
|
|------------> 下游符号库
|----(网络)--> 中央符号库(centralized store)(符号服务器)
DbgHelp.dll
Windows操作系统的调试辅助库模块
windbg通过它,读取和解析调试符号
符号服务器Dll
符号服务器的本地模块
负责从符号服务器查找、下载和管理符号文件
具体dll不固定,只要实现了DbgHelp.dll依赖的符号服务器API
windbg开发工具包中DbgHelp帮助文档(sdk\help\dbghelp.chm),描述了符号服务器API
用户可以自己实现符号服务器dll
windbg工具包中包含了一个符号服务器(SymSrv.dll)
下游符号库(downstream store)
用于缓存从符号服务器下载的符号文件
工作流程
DbgHelp请求符号服务器Dll,获取指定符号文件
符号服务器Dll先在下游符号库查找,失败后在中央符号库查找
路径表示
完整表示
symsrv*ServerDll*[DownstreamStore*]ServerPath
基于SymSrv.dll的表示
symsrv*SymSrv.dll*[DownstreamStore*]ServerPath
简写为srv*[DownstreamStore*]ServerPath
30.8.4 加载符号文件
设置符号路径
.sympath srv*D:\Symbols*http://msdl.microsoft.com/download/symbols
调试windbg的.reload命令
Child-SP RetAddr Call Site
00000000`0675af58 00007ff9`326b8ba3 ntdll!ZwWaitForSingleObject+0x14
00000000`0675af60 00007ff9`20e34af4 KERNELBASE!WaitForSingleObjectEx+0x93
00000000`0675b000 00007ff9`20e2f737 WININET!InternetFindNextFileW+0xe9d4
00000000`0675b030 00007ff9`20dc69bc WININET!InternetFindNextFileW+0x9617
00000000`0675b060 00007ff9`20d1f069 WININET!UrlCacheServer+0x2cb7c
00000000`0675b1f0 00000000`678b80d2 WININET!InternetReadFile+0xd9
00000000`0675b290 00000000`678b2040 symsrv!EulaDlgProc+0x1742
00000000`0675b2c0 00000000`678b15d1 symsrv!RunDllEntry+0x8d00
00000000`0675b570 00000000`678a6a50 symsrv!RunDllEntry+0x8291
00000000`0675b5c0 00000000`678a7d05 symsrv+0x6a50
00000000`0675b7e0 00000000`678a7a66 symsrv!SymbolServerByIndexW+0x185
00000000`0675be80 00000000`67daffd8 symsrv!SymbolServerW+0xc6
00000000`0675c0f0 00000000`67d9591a dbghelp!SymGetFileLineOffsets64+0x1248
00000000`0675c980 00000000`67d96d75 dbghelp+0x2591a
00000000`0675d6d0 00000000`67dc5234 dbghelp+0x26d75
00000000`0675d9e0 00000000`67dc2ee6 dbghelp!ImagehlpApiVersionEx+0x28b4
00000000`0675dc70 00000000`67dc2add dbghelp!ImagehlpApiVersionEx+0x566
00000000`0675e180 00000000`67db7011 dbghelp!ImagehlpApiVersionEx+0x15d
00000000`0675e1c0 00000000`68200484 dbghelp!SymSetScopeFromAddr+0x81
00000000`0675e200 00000000`680aba61 dbgeng!DebugCreate+0x1a1274
00000000`0675e240 00000000`680af90e dbgeng!DebugCreate+0x4c851
00000000`0675e270 00000000`68159441 dbgeng!DebugCreate+0x506fe
00000000`0675e2b0 00000000`6815aae0 dbgeng!DebugCreate+0xfa231
00000000`0675e3c0 00000000`68067134 dbgeng!DebugCreate+0xfb8d0
00000000`0675e410 00000000`68067420 dbgeng!DebugCreate+0x7f24
00000000`0675e8d0 00007ff7`9c433beb dbgeng!DebugCreate+0x8210
00000000`0675e930 00007ff7`9c4342eb windbg+0x33beb
00000000`0675ea70 00007ff7`9c436d35 windbg+0x342eb
00000000`0675fae0 00007ff9`34427bd4 windbg+0x36d35
00000000`0675fb20 00007ff9`3520ce71 KERNEL32!BaseThreadInitThunk+0x14
00000000`0675fb50 00000000`00000000 ntdll!RtlUserThreadStart+0x21
SymbolServer函数
符号服务器API
功能,向服务器请求指定的符号文件,返回文件的完整路径
典型实现
在下游符号库中查找符号文件
找到,则返回完整路径
未找到,则进行远程查找
若找到,则下载到下游符号库中,后返回完整路径
函数原型
BOOL CALLBACK SymbolServer(
[in] LPCSTR params,
[in] LPCSTR filename,
[in] PVOID id,
[in] DWORD two,
[in] DWORD three,
[out] LPSTR path );
params
符号服务器参数信息
DownstreamStore和ServerPath
filename
符号文件名
id
符号文件第一标识信息
.dbg和pe文件(.exe和.dll)
PE文件头定义的映像时间戳(TimeDateStamp)
.pdb
PDB签名
two
符号文件第二标识信息
.dbg和pe文件(.exe和.dll)
PE文件头定义的映像文件大小()SizeOfImage
.pdb
PDB年龄(Age)
three
符号文件第三标识信息
.dbg和pe文件(.exe和.dll)和.pdb
未使用为0
path
符号文件的完整路径
最大长度为MAX_PATH
流程
使用文件特征标识,调用SymbolServerGetIndexStringW函数,生成索引串
文件特征标识的唯一性序列化
调用SymbolServerByIndexW函数,获取文件名和索引串指定的符号文件
调用cascade函数
调用StoreUNC函数,在下游符号库中查找
使用StoreWinInet类,在中央符号库中查找
触发调试器加载符号
ld
.reload
其他使用符号的命令,如
栈回溯命令(k*)
反汇编命令
windbg采用懒惰式符号加载策略(lazy symbol loading)
故在查看模块列表时,会发现许多模块的符号状态为deferred,即推迟加载
30.8.5 观察模块信息
lm
lm(list loaded module),模块概要信息列表
例子
lm
start end module name
00007ff7`90e80000 00007ff7`90ea8000 Test C (deferred)
00007ff8`dad10000 00007ff8`daec9000 ucrtbased (deferred)
00007ff8`f2300000 00007ff8`f23f7000 MSVCP140D (deferred)
00007ff9`1b650000 00007ff9`1b673000 VCRUNTIME140D (deferred)
00007ff9`31ab0000 00007ff9`31abc000 CRYPTBASE (deferred)
00007ff9`32150000 00007ff9`321d1000 bcryptPrimitives (deferred)
00007ff9`32680000 00007ff9`32922000 KERNELBASE (deferred)
00007ff9`33450000 00007ff9`33570000 RPCRT4 (deferred)
00007ff9`34410000 00007ff9`344c2000 KERNEL32 (private pdb symbols) d:\symbols\kernel32.pdb\47A44F0CC47FB6A1F009E4343B711F231\kernel32.pdb
00007ff9`34700000 00007ff9`34797000 sechost (deferred)
00007ff9`35010000 00007ff9`350ae000 msvcrt (deferred)
00007ff9`350b0000 00007ff9`35153000 ADVAPI32 (deferred)