读书笔记:调试软件 张银奎

版本
    第一版 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][<bps>] - clear/disable/enable breakpoint(s)
                    BL - list breakpoints
                    BA <access> <size> <addr> - set processor breakpoint
                    BP <address> - set soft breakpoint
                    D[type][<range>] - dump memory
                    DT [-n|y] [[mod!]name] [[-n|y]fields]
                    [address] [-l list] [-a[]|c|i|o|r[#]|v] - dump using type information
                    DV [<name>] - dump local variables
                    E[type] <address> [<values>] - enter memory values
                    G[H|N] [=<address> [<address>...]] - go
                    K <count> - stacktrace
                    KP <count> - stacktrace with source arguments
                    LM[k|l|u|v] - list modules
                    LN <expr> - list nearest symbols
                    P [=<addr>] [<value>] - step over
                    Q - quit
                    R [[<reg> [= <expr>]]] - view or set registers
                    S[<opts>] <range> <values> - search memory
                    SX [{e|d|i|n} [-c "Cmd1"] [-c2 "Cmd2"] [-h] {Exception|Event|*}] - event filter
                    T [=<address>] [<expr>] - trace into
                    U [<range>] - unassemble
                    version - show debuggee and debugger version
                    X [<*|module>!]<*|symbol> - view symbols
                    ? <expr> - display expression
                    ?? <expr> - display C++ expression
                    $< <filename> - 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 [<options>] - add current data breakpoint state to a
                                                    register context
                        .asm [<options>] - set disassembly options
                        .asm- [<options>] - clear disassembly options
                        .attach <proc> - attach to <proc> at next execution
                        .block { <commands> } - 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 [<options>] - virtual memory cache control
                        .call <fn>(<arg1>, <arg2>, ...) - run a function in the debuggee
                        .catch { <commands> } - catch failures in commands
                        .chain - list current extensions
                        .childdbg <0|1> - turn child process debugging on or off
                        .clients - list currently active clients
                        .closehandle [<options>] [<handle>] - close the given handle
                        .continue - continue the enclosing loop
                        .copysym [<options>] <path> - copy current symbol files to a directory
                        .create <command line> - create a new process
                        .createdir [<options>] [<path>] - control process creation options
                        .cxr <address> - 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 <file> - output DML content from file
                        .dml_flow <start> <addr> - show basic block code flow
                        .dml_start [<options>] - navigable overview of debugger activities
                        .do { <commands> } (<cond>) - execute <commands> until <cond> is zero
                        .drivers - This command was removed -- use 'lm' or .reload -l)
                        .dump [<options>] <filename> - create a dump file on the host system
                        .dvalloc [<options>] <bytes> - VirtualAlloc memory in the debuggee
                        .dvfree [<options>] <offset> <bytes> - VirtualFree memory in the debuggee
                        .echo ["<string>"|<string>] - echo string
                        .echotime - output debugger time
                        .echotimestamps [0|1] - toggle timestamp output on events
                        .ecxr - dump context record for current exception
                        .effmach [<machine>] - change current machine type
                        .else { <commands> } - if/then/else conditional execution
                        .elsif (<cond>) { <commands> } [<else clauses>] - 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 <id> - 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 [<dir>[;...]] - set executable search path
                        .exepath+ [<dir>[;...]] - append executable search path
                        .expr - control expression evaluator
                        .exptr <address> - do .exr and .cxr for EXCEPTION_POINTERS
                        .exr <address> - dump exception record at specified address
                        .extmatch [<opts>] <pattern> - display all extensions matching pattern
                        .extpath <opts> [<dir>[;...]] - set extension search path
                        .extpath+ <opts> [<dir>[;...]] - 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 <address> - sets context of fiber at address
                                            resets context if no address specified
                        .fiximports <pattern> - attempts to link imports for images
                        .fnent <address> - dump function entry for the given code address
                        .fnret <fnaddr> [<retval>] - display formatted return value
                        .for ( <init> ; <cond> ; <step> ) { <commands> } - execute <commands> and
                                                                            <step> until <cond> is
                                                                            zero
                        .force_radix_output [0|1] - dump integer types in default base
                        .force_system_init [<options>] - force pending systems to initialize if possible
                        .force_tb - forcibly allow branch tracing
                        .foreach [opts] ( <alias> { <tcmds> } ) { <ecmds> } - execute <ecmds> for
                                                                                each token in the
                                                                                output of <tcmds>
                        .fpo <options> - control override FPO information
                        .frame [<frame>] - set current stack frame for locals
                        .formats <expr> - displays expression result in many formats
                        .help [<options>] - display this help
                        .holdmem <options> [range] - hold and compare memory data
                        .if (<cond>) { <commands> } [<else clauses>] - if/then/else conditional
                                                                        execution
                        .ignore_missing_pages [0|1] - control kernel summary dump missing
                                                        page error message
                        .imgscan <options> - scan memory for PE images
                        .jdinfo <jdi_addr> - interpret AeDebug information
                        .kframes <count> - 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 <name> - add this extension DLL to the extension chain
                        .loadby <name> <mod> - add the extension DLL in the module
                                                directory to the extension chain
                        .locale [<locale>] - set the current locale
                        .logfile - display log status
                        .logopen [<file>] - open new log file
                        .logappend [<file>] - append to log file
                        .logclose - close log file
                        .netsyms [0|1] - allow/disallow net symbol paths
                        .netuse [<options>] - manage net connections
                        .noshell - disable shell commands
                        .noversion - disable extension version checking
                        .ofilter <pattern> - filter debuggee output against the given pattern
                        .ocommand <prefix> - treat output with the given prefix as a command
                        .opendump <file> - open a dump file
                        .outmask <mask> - set bits in the current output mask
                        .outmask- <mask> - clear bits in the current output mask
                        .pcmd [<options>] - control per-prompt command
                        .pop [<options>] - pop state
                        .prefer_dml [0|1] - control DML mode default
                        .printf "<format>", <args...> - formatted output
                        .process [<address>] - sets implicit process
                                                resets default if no address specified
                        .process_info - display security related information of current process
                        .prompt_allow [<options>] - control what information can be displayed
                                                    at the prompt
                        .push [<options>] - push state
                        .quit_lock [<options>] - locks session against unexpected quit
                        .readmem <file> <range> - read raw memory from a file
                        .record_branches [0|1] - controls recording of processor branching
                        .reload [<image.ext>[=<address>,<size>]] - reload symbols
                        .restart - request a session restart
                        .remote <pipename> - start remote.exe server
                        .secure [0|1] - disallow operations dangerous for the host
                        .send_file <options> - send files to remote server
                        .server <options> - start engine server
                        .servers - list active remoting servers
                        .setdll <name> - debugger will search for extensions in this DLL first
                        .shell [<command>] - execute shell command
                        .show_read_failures [<opts>] - control extra read failure output
                        .show_sym_failures [<opts>] - control extra symbol failure output
                        .sleep <milliseconds> - debugger sleeps for given duration
                                                useful for allowing access to a machine that's
                                                broken in on an ntsd -d
                        .srcfix [<path extra>] - fix source search path
                        .srcfix+ [<path extra>] - append fixed source search path
                        .srcnoisy [0|1] - control verbose source loading output
                        .srcpath [<dir>[;...]] - set source search path
                        .srcpath+ [<dir>[;...]] - append source search path
                        .step_filter [<opts>] ["<pattern>[;<pattern>...]"] - Set symbol patterns
                                                                                to skip when stepping
                        .symfix [<localsym>] - fix symbol search path
                        .symfix+ [<localsym>] - append fixed symbol search path
                        .symopt <flags> - set symbol options
                        .symopt+ <flags> - set symbol options
                        .symopt- <flags> - clear symbol options
                        .sympath [<dir>[;...]] - set symbol search path
                        .sympath+ [<dir>[;...]] - append symbol search path
                        .thread [<address>] - 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 <flags> - set/clear type options
                        .unload <name> - 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 (<cond>) { <commands> } - execute <commands> while <cond> is non-zero
                        .writemem <file> <range> - write raw memory to a file

                        Use ".hh <command>" 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)             
                        00007ff9`351a0000 00007ff9`35390000   ntdll      (private pdb symbols)  d:\symbols\ntdll.pdb\B54F3499813EBCF139AEFDD664E98FDD1\ntdll.pdb
                    含义
                        start,模块在进程空间中的起始地址
                        end,模块在进程空间中的结束地址
                        module name,模块名称
                        符号文件加载状态
                            缩写        含义
                            deferred   模块已加载,但尚未尝试加载符号
                            #          符号文件和可执行文件之间的时间戳或校验和不匹配
                            T           时间戳,丢失、不可访问、等于零
                            C           校验和,丢失、不可访问、等于零
                            DIA         通过调试接口访问(DIA,debug interface access)加载了符号文件
                            Export      未找到符号文件,使用映像文件的输出信息(如dll的export)作为符号
                            M           符号文件和可执行文件之间的时间戳或校验和不匹配,但仍然加载了该符号文件
                            PERF        执行文件包含性能优化代码。对地址的加减运算可能出错
                            Stripped    调试信息是从映像文件中抽取出来的
                            PDB         符号文件是.pdb格式的
                            COFF        符号文件是COFF格式的(Common Object File Format)
                        符号文件完整路径或空白
                    PDB文件
                        分为私有(private)PDB文件和公共(public)PDB文件
                        公共 = 私有 - 私有信息
                lm v,模块详细信息列表
                    例子
                        lm v
                        start             end                 module name
                        00007ff7`90e80000 00007ff7`90ea8000   Test     C (deferred)             
                            Image path: Test.exe
                            Image name: Test.exe
                            Timestamp:        Sun Feb 21 13:59:10 2021 (6031F6AE)
                            CheckSum:         00000000
                            ImageSize:        00028000
                            Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
                        00007ff8`dad10000 00007ff8`daec9000   ucrtbased   (deferred)             
                            Image path: C:\Windows\SYSTEM32\ucrtbased.dll
                            Image name: ucrtbased.dll
                            Timestamp:        Wed Jun 17 13:23:10 2015 (5581043E)
                            CheckSum:         001C1915
                            ImageSize:        001B9000
                            File version:     10.0.10150.0
                            Product version:  10.0.10150.0
                            File flags:       0 (Mask 3F)
                            File OS:          40004 NT Win32
                            File type:        2.0 Dll
                            File date:        00000000.00000000
                            Translations:     0409.04b0
                            CompanyName:      Microsoft Corporation
                            ProductName:      Microsoft® Windows® Operating System
                            InternalName:     ucrtbase.dll
                            OriginalFilename: ucrtbase.dll
                            ProductVersion:   10.0.10150.0
                            FileVersion:      10.0.10150.0 (th1.150616-1659)
                            FileDescription:  Microsoft® C Runtime Library
                            LegalCopyright:   © Microsoft Corporation. All rights reserved.
                        ...
                模块过滤
                    m 名称模式
                    M 路径
                    o,已加载的模块
                    l,已加载符号的模块
                    e,有符号问题的模块
            !lmi module_name
                显示模块最详细信息
                例子
                    !lmi Test
                    Loaded Module Info: [test] 
                            Module: Test
                    Base Address: 00007ff790e80000
                        Image Name: Test.exe
                    Machine Type: 34404 (X64)
                        Time Stamp: 6031f6ae Sun Feb 21 13:59:10 2021
                            Size: 28000
                        CheckSum: 0
                    Characteristics: 22  
                    Debug Data Dirs: Type  Size     VA  Pointer
                                CODEVIEW    3f, 1f2e8,    d8e8 RSDS - GUID: {FAE50400-BD21-47AC-99E6-79A9B7D92FB1}
                                Age: 1, Pdb: G:\user\vs2015\Test\x64\Debug\Test.pdb
                                    ??    14, 1f328,    d928 [Data not mapped]
                        Symbol Type: DEFERRED - No error - symbol load deferred
                        Load Report: no symbols loaded
            菜单,Debug > Modules
                模块概要信息列表
        30.8.6 检查符号
            语法
                x [option] module_name!symbol_name
            名称支持通配符
                ?   任意符号出现一次
                *   任意符号出现0次或多次
                #   指定符号出现
                []# 指定符号串出现
            选项
                控制结果显示顺序
                    /a,/A,地址升序和降序
                    /n,/N,名称升序和降序
                    /z,/Z,符号大小升序和降序
                显示符号的数据类型
                    /t
                    条件,需要私有符号,否则显示<NoType>
                显示符号的符号类型和符号大小
                    /v
                    符号类型
                        访问种类
                            prv,private,私有符号
                            pub,public,公共符号
                        信息种类
                            local
                            global
                            parameter
                            function
                            unknown
                    符号大小
                        函数类符号,函数在内存中的大小
                        其他类型符号,数据类型的大小
                        条件,需要私有符号,否则显示0
                按大小过滤符号
                    /s size
                控制显示格式
                    /p,去掉函数名与括号间的空格
                    /q,符号名的引号格式,@!"符号名"
            结果含义
                [符号类型]
                符号地址
                    函数的入口地址
                    变量的起始地址
                [符号大小]
                [符号数据类型]
                module_name!symbol_name
                符号的类型或取值
            例子
                x /t /v Test!test*
                prv func   00007ff7`90e93b40   3f <function> Test!test_windbg (int)
                x /t /v Test!g_*
                prv global 00007ff7`90e9d220    8 <function> * Test!g_MyClass$initializer$ = 0x00007ff7`90e91870
                prv global 00007ff7`90ea1170    1 class MyClass Test!g_MyClass = class MyClass
        30.8.7 搜索符号
            ln(list nearest symbols)
            例子
                ln 00007ff790e9d220
                (00007ff7`90e9d220)   Test!g_MyClass$initializer$   |  (00007ff7`90e9d330)   Test!__xc_z
                Exact matches:
                    Test!g_MyClass$initializer$ = 0x00007ff7`90e91870
        30.8.8 设置符号选项
            符号选项
                windbg使用32的dword记录符号选项
                标志         选项名                     含义            默认值
                0x1         SYMOPT_CASE_INSENSITIVE   不分大小写        On
                0x2         SYMOPT_UNDNAME            显示未装饰的符号名  On
                0x4         SYMOPT_DEFERRED_LOADS     延迟加载符号       On
                0x8         SYMOPT_NO_CPP             关闭C++翻译(使用时,类成员的__或被替换成::) Off
                0x10        SYMOPT_LOAD_LINES         加载源码行信息    KD和CDB中为Off,WinDbg 中为On
                0x20        SYMOPT_OMAP_FIND_NEAREST  允许为优化过的代码使用最相近的符号    On
                0x40        SYMOPT_LOAD_ANYTHING      降低匹配符号的挑剔度  Off
                0x80        SYMOPT_IGNORE_CVREC       忽略映像文件的cv记录 Off
                0x100       SYMOPT_NO_UNQUALIFIED_LOADS 禁止符号处理器自动加载模块 Off
                0x200       SYMOPT_FAIL_CRITICAL_ERRORS 显示关键错误 On
                0x400       SYMOPT_EXACT_SYMBOLS      严格评估所有符号文件 Off
                0x800       SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 允许位于内存绝对地址的符号 Off
                0x1000      SYMOPT_IGNORE_NT_SYMPATH  忽略环境变量中的符号和镜像路径 Off
                0x2000      SYMOPT_INCLUDE_32BIT_MODULES 对于安腾处理器,强制列举32位模块 Off
                0x4000      SYMOPT_PUBLICS_ONLY       忽略全局、局部和作用域相关的符号 Off
                0x8000      SYMOPT_NO_PUBLICS         不搜索公共符号表 Off
                0x10000     SYMOPT_AUTO_PUBLICS       其他方法失败时,才使用PDB文件中的公共符号 On
                0x20000     SYMOPT_NO_IMAGE_SEARCH    不搜索镜像文件 On
                0x40000     SYMOPT_SECURE             (内核调试)Secure Mode Off
                0x80000     SYMOPT_NO_PROMPTS         (远程调试)不显示代理服务器的认证对话框 KD和CDB中为On,WinDbg 中为Off
                0x80000000  SYMOPT_DEBUG              显示符号加载过程 Off
            增加设置
                .symopt + option_value
                option_value为十六进制标志位,如0x1,0x2,0x4
            取消设置
                .symopt - option_value
            显示设置
                .symopt
            友好设置
                !sym
                !sym noisy,即+ 0x80000000
                !sym quiet,即- 0x80000000
                !sym prompts,即+ 0x80000
                !sym prompts off,即- 0x80000
        30.8.9 加载不严格匹配的符号文件
            方式
                .reload /i module_name
                设置符号选项,.symopt +0x40
            注意
                加载前,最好开启"嘈杂"模式(!sym noisy),以观察加载细节
    30.9 事件处理
        30.9.0 概述
            Windows的调试模型是事件驱动的
            整个调试过程,就是调试事件的产生、发送、接收和处理
                调试目标,产生调试事件
                调试器,接收和处理调试事件
                调试子系统,发送调试事件给调试器,为调试器提供服务
        30.9.1 调试事件和异常的关系
            Windows定义了9类调试事件
                EXCEPTION_DEBUG_EVENT(1)
                CREATE_THREAD_DEBUG_EVENT(2)
                CREATE_PROCESS_DEBUG_EVENT(3)
                EXIT_THREAD_DEBUG_EVENT (4)
                EXIT_PROCESS_DEBUG_EVENT(5)
                LOAD_DLL_DEBUG_EVENT(6)
                UNLOAD_DLL_DEBUG_EVENT(7)
                OUTPUT_DEBUG_STRING_EVENT(8)
                RIP_EVENT(9)
                其他的调试器事件
                    专供调试使用                    
            异常是一种调试事件,EXCEPTION_DEBUG_EVENT(1)
            异常调试事件,包含子类,其他类型调试事件不包含
                Win32异常
                    Windows操作系统定义的
                    类型
                        CPU产生的异常
                        系统内核定义的异常
                    典型
                        非法访问、除零等
                    异常代码
                        ntstatus.h 
                Visual C++异常
                    Visual C++编译器的throw关键字所抛出的异常
                        throw关键字调用RaiseException API产生异常
                    异常代码都是 0xe06d7363(.msc) 
                托管异常
                    .Net程序使用托管方法抛出的异常
                    异常代码都是0xe0636f6d(.com) 
                其它异常
                    类型
                        用户程序直接调用RaiseException API抛出的异常
                        其它C++编译器抛出的异常等 
        30.9.2 两轮机会
            Windows的异常分发流程
                第一轮
                    分发给调试器,调试器判断接收和返回处理结果
                        调试器通常返回没有处理异常,使之继续分发
                        对于断点异常和调试异常,调试器会返回已经处理
                    尝试分发给异常处理器(VEH、SEH)
                第二轮
                    分发给调试器,调试器判断接收和返回处理结果
                        调试器通常返回已经处理,让系统恢复程序执行,通常会导致重复循环分发
                    终极处理
                        异常源自应用程序,系统启动"应用程序错误报告过程",终止应用程序
                        异常源自内核代码,系统启动"蓝屏机制"
            其他调试事件只有一轮机会
            分发流程,实现了一个异常处理链
                调试用异常,实现调试目的
                有处理器的异常,实现程序逻辑
                无处理器的异常,拦截未处理的异常
                所有异常,实现异常的兜底处理
        30.9.3 定制事件处理方式
            每个异常事件都存在四个选项
                第一轮机会是否中断到调试器
                第一轮机会的处理结果
                第二轮机会是否中断到调试器
                第二轮机会的处理结果
            Visual C++和Visual Studio都有异常设置
            windbg
                图形界面
                    Debug > Event Filters
                    Execution组单选,控制是否中断
                        Enabled
                            异常事件两次都中断,其他调试事件会中断
                        Disabled
                            异常事件第二次中断,其他调试事件不中断
                        Output
                            输出信息通知用户
                        Ignore
                            忽略
                    Continue组单选,控制第一次异常事件的处理结果
                        Handled,返回已处理
                        Not Handled,返回未处理
                        大多异常默认设置为未处理
                    对于第二次异常事件的处理结果
                        默认设置为返回已处理
                        在调试时,可使用gn命令,表示异常未处理
                    支持为事件定义关联命令(Commands)
                        异常事件有两次关联,其他调试事件有一次关联
                    支持设置事件参数以细化过滤条件(Arguments)
                    支持事件处理方式的增删
                命令配置
                    信息要素
                        条件对象
                            事件和参数
                        中断方式
                            Enabled,Disabled,Output,Ignore
                        处理结果
                            已处理和未处理
                        关联命令
                            第一次和第二次
                    命令
                        设置中断方式和关联命令
                            sx{e|d|i|n} [-c "cmd1"] [-c2 "cmd2"] {Exception|Event|*}
                        设置处理结果
                            已处理,sxe -h {Exception|Event|*}
                            未处理,sx{d|i|n} -h {Exception|Event|*}
                        查看当前设置
                            sx
                        恢复默认设置
                            sxr
                        Exception|Event的表示
                            事件码,Event Code
                            sx命令查看
                            帮助文档搜索"Controlling Exceptions and Events"
        30.9.4 GH和GN命令
            在调试中,使用g命令恢复调试目标的执行,此时调试器会返回配置的或默认的处理结果
            可使用命令临时指定返回的处理结果
                已处理(handled),gh
                未处理(Not Handled),gn
        30.9.5 实验
            程序(dbgee.exe)
                int _tmain(int argc, _TCHAR* argv[])
                {
                    if (argc == 1)
                    {
                        *(int*)0 = 1;
                        printf("test\n");
                    }

                    return 0;
                }
            运行,windbg dbgee.exe
                43f4.4988): Break instruction exception - code 80000003 (first chance)
                *** ERROR: Symbol file could not be found.  Defaulted to export symbols for ntdll.dll - 
                ntdll!LdrInitShimEngineDynamic+0x35c:
                00007ff9`352711dc cc              int     3
            sxe av,设置两轮中断
            sxd -h av,设置第一轮返回不处理
            sx,查看设置
                av - Access violation - break - not handled
            g,恢复执行,触发第一次中断
                (43f4.4988): Access violation - code c0000005 (first chance)
                First chance exceptions are reported before any exception handling.
                This exception may be expected and handled.
                *** WARNING: Unable to verify checksum for dbgee.exe
                dbgee!wmain+0x36:
                00007ff6`945118f6 c704250000000001000000 mov dword ptr [0],1 ds:00000000`00000000=????????
            gh,临时返回已处理,由于异常条件仍存在,导致循环重复触发第一次中断
                (43f4.4988): Access violation - code c0000005 (first chance)
                First chance exceptions are reported before any exception handling.
                This exception may be expected and handled.
                dbgee!wmain+0x36:
                00007ff6`945118f6 c704250000000001000000 mov dword ptr [0],1 ds:00000000`00000000=????????
            g,恢复执行,按配置返回未处理
                由于没有在程序中找到异常处理器(VEH、SEH等),系统会执行缺省的异常处理器(kernel32.dll中UnhandledExceptionFilter函数)
                    判断当前程序是否处于调试状态
                        否,启动应用程序错误对话框,通知用户终止程序
                        是,返回EXCEPTION_CONTINUE_SEARCH,导致进入异常的第二次分发
                (43f4.4988): Access violation - code c0000005 (!!! second chance !!!)
                dbgee!wmain+0x36:
                00007ff6`945118f6 c704250000000001000000 mov dword ptr [0],1 ds:00000000`00000000=????????
            g,恢复执行,按配置返回已处理,由于异常条件仍存在,导致循环重复触发第一次中断
                (43f4.4988): Access violation - code c0000005 (first chance)
                First chance exceptions are reported before any exception handling.
                This exception may be expected and handled.
                dbgee!wmain+0x36:
                00007ff6`945118f6 c704250000000001000000 mov dword ptr [0],1 ds:00000000`00000000=????????
            g,进入异常的二次分发
                (43f4.4988): Access violation - code c0000005 (!!! second chance !!!)
                dbgee!wmain+0x36:
                00007ff6`945118f6 c704250000000001000000 mov dword ptr [0],1 ds:00000000`00000000=????????
            gn,返回未处理,系统终止调试目标
    30.10 控制调试目标
        30.10.1 初始断点
            目的,为了让调试人员尽早分析调试目标
            手段
                Windows操作系统的进程加载器支持
                在完成最基本的用户态初始化工作后
                系统的模块加载函数,主动执行断点指令,触发断点,让调试目标中断到调试器中
            初始断点,进入后的首个默认断点
            创建调试目标的情况分析
                创建新进程时,会在父进程的环境中完成很多创建工作
                    进程对象,进程空间,初始线程,通知子系统等
                初始线程在新进程环境中执行
                    内核态的KiThreadStartup
                        将线程的IRQL(中断级别)降到APC级别
                        调用PspUserThreadStartup,为线程在用户态执行做准备
                            初始化一个对用户态代码的异步过程调用(APC),并插入APC队列
                            等待APC完成
                            将线程的IRQL降到0(PASSIVE)
                    调用线程上下文中的进程启动函数BaseProcessStart
                        BaseProcessStart调用应用程序入口函数
                异步过程调用
                    该APC调用NTDLL.dll中的LdrpInitialize函数
                        LdrpInitialize函数,算是新进程的初始化线程,在用户态执行的最早代码
                        LdrpInitialize工作
                            初始化加载器和读取执行选项
                            调用LdrpInitializeProcess函数
                                加载Exe文件依赖的动态链接库
                                判断是否处于调试状态,是则调用DbgBreakPoint通知调试器
                                注意,此时还未调用DLL的DllMain函数
                    异步过程调用结束后
                        KiUserApcDispatcher调用ZwContinue,返回到内核态的PspUserThreadStartup函数中
            附加调试目标的情况分析
                windbg在目标进程中,创建一个远程线程,来触发一个初始断点
                初始断点发生在新建线程的上下文中
                    kn
                    # Child-SP          RetAddr           Call Site
                    00 0000006c`7a27f8f8 00007ff9`3526d4db ntdll!DbgBreakPoint
                    01 0000006c`7a27f900 00007ff9`34427bd4 ntdll!DbgUiRemoteBreakin+0x4b
                    02 0000006c`7a27f930 00007ff9`3520ce71 KERNEL32!BaseThreadInitThunk+0x14
                    03 0000006c`7a27f960 00000000`00000000 ntdll!RtlUserThreadStart+0x21
                注意,该线程不是目标进程的自建线程
                    而是调试器为了达到调试目的而创建的
                    恢复执行后,线程结束
            其他
                当windbg使用-g选项启动时
                    对于新建调试
                        windbg收到初始断点事件,不中断
                    对于附加调试
                        windbg不创建远程线程来触发断点
                初始断点不是最早的中断机会
                    但是足够使用,初始断点可用于
                        跟踪和分析程序和DLL的入口函数
                        设置断点等准备工作
                    其他较早的中断机会
                        进程创建事件
                        EXE模块的加载事件
        30.10.2 俘获调试目标
            中断调试目标的方式
                windbg中主动中断
                    快捷键,Ctrl+Break
                    菜单,Bebug > Break
                调试目标中主动中断
                    对于调试中的图形界面程序,按F12
                调试目标,遇到断点和触发异常
            windbg中主动中断的分析
                方式,windbg调试windbg的调试
                    启动新windbg附加旧windbg
                    设置符号路径(符号服务器)
                    重新加载模块符号,确保相关符号下载完成
                        windbg.pdb
                        dbgeng.pdb
                        注意下载部分就失败的情形
                            下游符号库中,出现未下载完整的符号文件
                            注意开启"显示符号加载过程",!sym noisy
                                有下载的加载失败
                                    SYMSRV:  dbgeng.pdb from https://msdl.microsoft.com/download/symbols: 4213760 bytes -   34 percentSYMSRV:  /download/symbols/dbgeng.pdb/3905A3278DCD49AEA0C08989F59DB6D91/dbgeng.pdb
                                            函数不正确。
                                    DBGHELP: C:\Program Files\Debugging Tools for Windows (x64)\sym\dbgeng.pdb\3905A3278DCD49AEA0C08989F59DB6D91\dbgeng.pdb - drive not ready
                                    DBGHELP: C:\Program Files\Debugging Tools for Windows (x64)\dbgeng.pdb - file not found
                                    DBGHELP: dbgeng.pdb - file not found
                                    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Debugging Tools for Windows (x64)\dbgeng.dll - 
                                    DBGHELP: dbgeng - export symbols
                                无下载的加载失败
                                    DBGHELP: d:\symbols\dbgeng.pdb\3905A3278DCD49AEA0C08989F59DB6D91\dbgeng.pdb - drive not ready
                                    DBGHELP: C:\Program Files\Debugging Tools for Windows (x64)\dbgeng.pdb - file not found
                                    DBGHELP: dbgeng.pdb - file not found
                                    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Debugging Tools for Windows (x64)\dbgeng.dll - 
                                    DBGHELP: dbgeng - export symbols
                                有下载的加载成功
                                    SYMSRV:  dbgeng.pdb from https://msdl.microsoft.com/download/symbols: 4213760 bytes - copied         
                                    DBGHELP: dbgeng - public symbols  
                                            d:\symbols\dbgeng.pdb\3905A3278DCD49AEA0C08989F59DB6D91\dbgeng.pdb
                            加载模块的符号状态
                                失败情形
                                    lm
                                    start             end                 module name
                                    ...           
                                    00000000`67f70000 00000000`683f5000   dbgeng     (export symbols)       C:\Program Files\Debugging Tools for Windows (x64)\dbgeng.dll
                                成功情形
                                    lm
                                    start             end                 module name
                                    ...           
                                    00007ff7`5a300000 00007ff7`5a3b1000   windbg     (pdb symbols)          d:\symbols\windbg.pdb\83321AFD3A334F6FA751209AC0F960E91\windbg.pdb
                                    ...
                    设置断点
                        bu ntdll!RtlpCreateUserThreadEx 
                        bu ntdll!NtCreateThreadEx                       
                普通情况(创建远程线程中断目标)
                    windbg执行Break命令的过程kn
                    kn
                    # Child-SP          RetAddr           Call Site
                    00 00000000`00f7c7c8 00007ff9`351a589c ntdll!NtCreateThreadEx
                    01 00000000`00f7c7d0 00007ff9`3526d463 ntdll!RtlpCreateUserThreadEx+0x13c
                    02 00000000`00f7c910 00007ff9`344430aa ntdll!DbgUiIssueRemoteBreakin+0x43
                    03 00000000`00f7c990 00000000`68300d56 KERNEL32!DebugBreakProcess+0xa
                    04 00000000`00f7c9c0 00000000`6828eecf dbgeng!LiveUserDebugServices::RequestBreakIn+0x26
                    05 00000000`00f7ca40 00000000`680c105b dbgeng!LiveUserTargetInfo::RequestBreakIn+0x6f
                    06 00000000`00f7ca80 00007ff7`5a34fe28 dbgeng!DebugClient::SetInterrupt+0x13b
                    07 00000000`00f7cac0 00007ff9`3415681d windbg!FrameWndProc+0x1ae8
                    08 00000000`00f7d8c0 00007ff9`341563ec USER32!UserCallWinProcCheckWow+0x2bd
                    09 00000000`00f7da50 00007ff9`34162d03 USER32!DispatchClientMessage+0x9c
                    0a 00000000`00f7dab0 00007ff9`3523fdb4 USER32!_fnDWORD+0x33
                    0b 00000000`00f7db10 00007ff9`329912c4 ntdll!KiUserCallbackDispatcherContinue
                    0c 00000000`00f7db98 00007ff7`5a363d92 win32u!ZwUserTranslateAccelerator+0x14
                    0d 00000000`00f7dba0 00007ff7`5a364120 windbg!ProcessNonDlgMessage+0x22
                    0e 00000000`00f7dbf0 00007ff7`5a36b053 windbg!ProcessPendingMessages+0x70
                    0f 00000000`00f7dc60 00007ff7`5a3766e6 windbg!wmain+0x2c3
                    10 00000000`00f7fd50 00007ff9`34427bd4 windbg!_CxxFrameHandler3+0x28a
                    11 00000000`00f7fd90 00007ff9`3520ce71 KERNEL32!BaseThreadInitThunk+0x14
                    12 00000000`00f7fdc0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
                    窗口过程函数
                        收到Break命令后,调用全局变量g_DbgClient的方法SetInterrupt
                            HRESULT IDebugControl::SetInterrupt(IN ULONG Flags);
                                Flags取值
                                    DEBUG_INTERRUPT_PASSIVE(1)
                                        希望中断到命令模式,但不强制
                                        函数内部将dbgeng!g_UserInterruptCount加一,将dbgeng!g_EngStatus设置为0x1005
                                    DEBUG_INTERRUPT_EXIT(2)
                                        让调试引擎取消等待调试事件,强制返回
                                        通常会导致没有调试目标
                                        将dbgeng!g_EngStatus设置为0x1008
                                    DEBUG_INTERRUPT_ACTIVE(0)
                                        通过全局变量dbgeng!g_CmdState,判断调试器是否处于命令模式
                                        否,则要求调试目标中断到调试器以进入命令模式
                                        是,则递增dbgeng!g_UserInterruptCount
                            UI线程使用的是0
                    远程线程的线程函数,DbgUiRemoteBreakin
                        调用DbgBreakPoint函数,执行断点指令,产生断点异常
                极限情况(超时则强制挂起)
                    远程线程一产生,还未执行断点指令,就被挂起
                    windbg的处理
                        等待一段时间后,显示提示信息
                            Break-in sent,waiting 30 seconds
                        再等待30秒,windbg产生一个异常事件(事件码为 0x8000 0007)
                            SynthesizeWakeEvent
                        这个事件触发调试器等待函数返回,并处理该事件
                            会引发dbgeng!SyspendExecution函数调用
                                挂起调试目标所有线程,使调试目标被中断
                                调试器进入命令模式并显示信息
                                    Wake debugger - code 80000007 (first chance)
        30.10.3 继续运行
            从指定地址继续,同时设定临时断点
                g [a] [= start_address] [break_address ... [; break_commands]]
                指定地址,start_address
                断点
                    硬件断点与普通断点,有无a
                    断点地址,break_address
                    断点触发后命令,break_commands
                可实现执行到光标
                g,缺省时,从当前地址继续,无断点
            继续并指定异常处理结果
                g,返回配置的或默认的处理结果
                gh,已处理(handled)
                gn,未处理(Not Handled)
            继续到上一级函数
                gu
            条件断点中的继续
                gc
    30.11 单步执行
        30.11.1 概览
            单步的单位
                源码模式,一次执行一行源码
                    Debug > Source Mode 被选中
                    l+t
                汇编模式,一次执行一条汇编指令
                    Debug > Source Mode 未被选中
                    l-t
            单步的对象为函数调用时
                单步进入,step into,p命令
                单步越过,step over,t命令,trace
                非函数调用时,p和t作用相同
            实现
                汇编模式下的单步执行
                    通过CPU的陷阱机制实现,对于x86CPU,就是设置标志寄存器的T标志
                    t命令过程
                        windbg收到t命令
                        工作线程解析命令(ParseStepTrace),调用SetExecStep和SetNextStepTraceState函数
                            两个函数将用户命令,转化并设置到内部对象、变量和线程的上下文结构中
                        调试引擎报告命令执行完成,最后由ProcessEngineCommands返回到EngineLoop
                        EngineLoop继续执行,调用DebugClient::WaitForEvent以等待下一调试事件
                            恢复目标运行,调试引擎会将线程上下文通过KERNELBASE!SetThreadContext设置到系统
                            设置过程
                                Child-SP          RetAddr           : Args to Child                                                           : Call Site
                                00000000`0961e628 00000000`6830059f : 00000000`0961e6c0 00000000`680ad865 00000000`0961e6d0 00000001`680ae0aa : KERNELBASE!SetThreadContext
                                00000000`0961e630 00000000`680f8aa8 : 00000000`04373970 00000000`000007b8 00000000`043839e0 00000003`000004d0 : dbgeng!LiveUserDebugServices::SetContext+0xaf
                                00000000`0961eb60 00000000`680f5de7 : 00000000`043a6bc0 00000000`043b3220 00000000`000007b8 00000000`043839e0 : dbgeng!LiveUserTargetInfo::SetTargetContext+0x78
                                00000000`0961eba0 00000000`6819f1b9 : 00000000`043a6bc0 00000000`043b3220 00000000`000007b8 00000000`043839e0 : dbgeng!TargetInfo::SetContext+0xe7
                                00000000`0961f660 00000000`6819efec : 00000000`043838e0 00000000`0961f6a0 00000001`00000001 00000001`00000000 : dbgeng!ArmCeMachineInfo::KdSetContext+0x49
                                00000000`0961f690 00000000`68281314 : 00000000`043838e0 00000000`043b3fa0 00000000`043b3220 00000000`0961f700 : dbgeng!MachineInfo::SetContext+0x18c
                                00000000`0961f6d0 00000000`68282672 : 00000000`043a6bc0 00000000`00000000 00001946`00000001 00000000`0961f740 : dbgeng!TargetInfo::ChangeRegContext+0xe4
                                00000000`0961f720 00000000`6814a5a9 : 00000000`043a6bc0 00000000`043b3f00 00007ff9`3523fd90 00000000`0961f790 : dbgeng!TargetInfo::PrepareForExecution+0x22
                                00000000`0961f760 00000000`6814a862 : 00000000`043a6b00 00000000`681a4145 00000000`043838e0 00000e07`af20b559 : dbgeng!PrepareOrFlushPerExecution+0x49
                                00000000`0961f7a0 00000000`6814b55f : 00000000`0961f860 00000000`043b3fa0 00000000`0961f830 00000000`00000000 : dbgeng!ResumeExecution+0x32
                                00000000`0961f7e0 00000000`6814a355 : 00000000`0437c3c0 00007ff9`00000005 00000000`00000000 00007ff9`326b8c64 : dbgeng!PrepareForExecution+0x6df
                                00000000`0961f900 00000000`680ca97b : 00000000`0437c3c0 00000000`00000000 00000000`0961f970 00000e07`00000001 : dbgeng!PrepareForWait+0x35
                                00000000`0961f940 00000000`680caf8e : 00000000`0437c3c0 00007ff7`00000000 00000e07`ffffffff 00000000`00000246 : dbgeng!RawWaitForEvent+0x2b
                                00000000`0961f9c0 00007ff7`5a336aba : 00000000`0437c3d0 00007ff7`00000000 00007ff7`ffffffff 00000000`00000000 : dbgeng!DebugClient::WaitForEvent+0xce
                                00000000`0961fa00 00007ff9`34427bd4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : windbg!EngineLoop+0x16a
                                00000000`0961fa40 00007ff9`3520ce71 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
                                00000000`0961fa70 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
                            查看设置参数
                                SetThreadContext的参数MSVCR90!_CONTEXT
                                dt MSVCR90!_CONTEXT 043839e0 
                                    +0x044 EFlags           : 0x344
                                .formats 0x344
                                    Evaluate expression:
                                    Binary:  00000000 00000000 00000000 00000000 00000000 00000000 00000011 01000100
                                位8为跟踪标志,当前为1,代表单步执行
                                位8为中断标志,当前为1,代表启用中断
                    事件代码为8000 0003,为断点异常
                    事件代码为8000 0004,为单步异常,由t命令和非越过的p命令触发
                    针对CALL指令单步越过的p命令
                        是在CALL指令的下一相邻指令,设置临时软件断点
                源码模式下的单步执行
                    通过多次设置陷阱标志实现,即多次汇编模式下的单步执行
            完整命令
                p|t [r] [= start_address] [count] ["command"]
                [r],是否显示寄存器,缺省为显示
                [= start_address],指定执行的起始地址,缺省为当前地址
                [count],指定单步执行的次数,缺省为1
                ["command"],指定每次单步执行后的命令,缺省为空
        30.11.2 单步执行到指定地址
            语法
                pa [r] [=start_address] stop_address
                    反复执行单步越过,直到stop_address
                    pa,step to address
                ta [r] [=start_address] stop_address
                    反复执行单步进入,直到stop_address
                    ta,trace to address
            实现gu,继续到上一级函数
                pa $ra
                $ra,代表当前函数的返回地址
            被中断的情形
                单步中遇到断点或异常
        30.11.3 单步执行到下一个函数调用
            语法
                pc [r] [=start_address] [Count]
                    反复执行单步越过,直到CALL指令
                    pc,step to call
                tc [r] [=start_address] [Count]
                    反复执行单步进入,直到CALL指令
                    ta,trace to call
                [count],指定单步执行到函数调用的次数,缺省为1
            当处理CALL指令时,当作普通单步执行
                pc,执行完CALL指令
                tc,进入函数
        30.11.4 执行到下一分支
            语法
                tb [r] [=start_address] [Count]
                    一次执行到下一分支指令
                    tb,trace to branch
            实现
                设置好标志寄存器和MSR寄存器后,便恢复程序运行
                当CPU执行到分支指令时,便报告异常停下来
            注意
                在安腾系统和x64系统上,tb可用于内核调试和用户态调试
                在x86系统上,tb仅用于内核调试,使用ph和th可克服局限
                    ph|th [r] [=start_address] [Count]
                    分别用来单步执行和单步追踪到下一分支指令
                    两者处理CALL指令的方式不同
        30.11.5 追踪并监视
            wt(trace and watch data)
                执行函数,打印统计
            使用
                在函数入口处执行,否则功能为单步执行
            统计含义
                第一部分,追踪目标
                    目标函数名称和返回地址
                第二部分,执行路径
                    一次函数栈切换(CALL和RET),一次执行状态记录
                    第一列,当前函数执行的指令数
                        可能情形
                            [函数首指令,函数中CALL指令]
                            [函数首指令,函数中RET指令]
                    第二列,当前函数调用的指令数
                        = 内部函数的执行指令数 + 内部函数的调用指令数
                    第三列,函数调用深度
                    第四列,调用深度缩进+函数名称
                第三部分,统计指令数和调试事件数
                第四部分,统计函数的指令执行情况
                    调用总次数
                    单次最大指令执行数
                    单次最小指令执行数
                    单次平均指令执行数
                第五部分,系统服务调用情况
                第六部分,追踪完成后的状态
                    寄存器状态
                    当前程序指针位置
            注意
                使用wt追踪复杂函数或顶层函数时,由于范围广,可能需要较长的时间
                为了提高效率,可以限制追踪范围
                    -l,最大调用深度,基于目标函数
                    -m,指定追踪的模块
                    -i,指定忽略的模块
                wt命令,可被各种调试事件中断                    
            例子
                源码(wtee.cpp)
                    #include <windows.h>
                    #pragma warning(disable: 4996)

                    int GetRandom(int n)
                    {
                        int m = 2 * n, nTick;
                        nTick = GetTickCount();
                        m *= nTick;
                        m *= GetVersion();
                        return m*n;
                    }

                    int main(int argc, char* argv[])
                    {
                        int n = argc * 100;
                        n = GetRandom(n);
                        return n;
                    }
                测试
                    windbg /wtee.exe
                    bp wtee!main
                    g
                    wt -m wtee
                        Tracing wtee!main to return address 00007ff7`c6d01278
                            5     0 [  0] wtee!main
                            6     0 [  1]   KERNEL32!GetTickCountKernel32
                            7     6 [  0] wtee!main
                            1     0 [  1]   KERNEL32!GetVersionStub
                            21    0 [  1]   KERNELBASE!GetVersion
                            15   28 [  0] wtee!main

                        43 instructions were executed in 42 events (0 from other threads)

                        Function Name                               Invocations MinInst MaxInst AvgInst
                        KERNEL32!GetTickCountKernel32                         1       6       6       6
                        KERNEL32!GetVersionStub                               1       1       1       1
                        KERNELBASE!GetVersion                                 1      21      21      21
                        wtee!main                                             1      15      15      15

                        0 system calls were executed

                        wtee!__scrt_common_main_seh+0x124:
                        00007ff7`c6d01278 8bd8            mov     ebx,eax
        30.11.6 程序指针飞跃
            调试时,可以修改程序指针
                cs:$eip或cs:$rip
                下一指令的地址
            实现
                windbg会把地址,设置到线程上下文的程序指令寄存器中
                当恢复目标程序时,系统会完成线程上下文的设置
            使用
                g命令和单步命令,支持设置起始执行地址
                寄存器命令r,直接修改程序指针寄存器
            注意
                由于修改了程序指针,导致部分指令重复执行或跳过
                可能导致程序逻辑出错,如栈操作和对象析构操作等
        30.11.7 归纳
            标准命令
                p   step
                t   trace
                pa  step to address
                ta  trace to address
                pc  step to next call
                tc  trace to next call
                tb  trace to next branch
                pt  step to next return
                tt  trace to next return
                ph  step to next branch
                th  trace to next branch
                wt  trace and watch data
                g   go
                gh  go handled
                gn  go not handled
                gu  go up
                注意调试目标必须是活动目标
            windbg设计了元命令和扩展命令,辅助使用标准命令
                连续单步跟踪时,界面更新速度频繁,影响速度和让人眼花缭乱,可暂停刷新界面
                    .suspend_ui
    30.12 使用断点
        30.12.0 概述
            设断点,即埋地雷,讲究埋设的地点和时机
        30.12.1 软件断点
            实现
                中断指令(INT3,0xCC)
                    运行时设置(中断指令)
                    中断时恢复(原本指令)
                    继续时,当前指令先执行后设置                    
                验证试验
                    源程序(test_bp)
                        int main()
                        {
                            printf("please set breakpoint at start");
                            getchar();

                            return 0;
                        }
                    调试查看test_bp
                        windbg .\test_bp.exe
                        u test_bp!main
                            test_bp!main [g:\user\vs2015\test\test_bp\test_bp.cpp @ 8]:
                            00007ff7`017b1000 4883ec28        sub     rsp,28h
                            00007ff7`017b1004 488d0d15120000  lea     rcx,[test_bp!`string' (00007ff7`017b2220)]
                            00007ff7`017b100b e820000000      call    test_bp!printf (00007ff7`017b1030)
                            00007ff7`017b1010 ff1562110000    call    qword ptr [test_bp!_imp_getchar (00007ff7`017b2178)]
                            00007ff7`017b1016 33c0            xor     eax,eax
                            00007ff7`017b1018 4883c428        add     rsp,28h
                            00007ff7`017b101c c3              ret
                            00007ff7`017b101d cc              int     3
                        bp test_bp!main
                        g
                    查看test_bp内存
                        使用64位WinHex
                            Tools > Open Memory
                            test_bp > test_bp.exe
                        查看00007ff7`017b1000附近的内存
                                Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
                            7FF7017B1000   CC 83 EC 28 48 8D 0D 15  12 00 00 E8 20 00 00 00   虄?H      ?   
                            7FF7017B1010   FF 15 62 11 00 00 33 C0  48 83 C4 28 C3 CC CC CC    b   3繦兡(锰烫
                            7FF7017B1020   48 8D 05 01 26 00 00 C3  CC CC CC CC CC CC CC CC   H   &  锰烫烫烫?
                        可以发现运行时,原指令被替换
                            00007ff7`017b1000 4883ec28        sub     rsp,28h
                            7FF7017B1000   CC 83 EC 28
                    中断查看test_bp
                        Ctrl + Break
                        windbg
                            代码恢复
                        winhex
                            4883EC28488D0D15120000E820000000
                            FF156211000033C04883C428C3CCCCCC
                            488D0501260000C3CCCCCCCCCCCCCCCC
                        可以发现中断时,中断指令被恢复
                注意
                    为了避免破坏数据和代码,只能在指令首字节位置设置断点
            语法
                bp [id] [options] [address [passes]] ["command_string"]
                    address,断点地址,缺省为当前程序指针
                    passes,第几次遇到时触发中断,缺省为1
                    command_string,中断触发后执行的命令,多个命令使用分号分隔
                bu [id] [options] [address [passes]] ["command_string"]
                    设置一个以后落实的断点
                    用于对一个尚未加载的模块设置断点
                        如调试动态加载模块的入口函数或初始化代码
                bm [options] symbol_pattern [passes] ["command_string"]
                    设置一批断点,如同执行多次的bp和bu
                    支持通配符*
                    注意
                        背景,在数据区设置软件断点,会导致数据意外变化
                        bm命令在设置断点时,只对函数类型的匹配符号设置断点
                        这要求调试符号有类型信息,这通常需要私有符号,但若只有公共符号文件将报错
                            方法一,\a,强制匹配
                                bm不检查符号类型,由用户保证
                            方法二
                                为模块使用完全或DLL输出符号
                options
                    /1
                        临时断点,一次命中断点
                        命中一次后自动删除
                    /p EPROCESS_ADDRESS
                        仅当断点事件发生在对应进程中,才中断
                        仅用在内核调试时
                    /t ETHREAD_ADDRESS
                        仅当断点事件发生在对应线程中,才中断
                        仅用在内核调试时
                    /c Number
                        仅当断点事件发生的函数调用深度,小于Number时才中断
                    /C Number
                        仅当断点事件发生的函数调用深度,大于Number时才中断
                id,不设置则自动编排
        30.12.2 硬件断点
            定义,通过设置CPU寄存器而实现的断点
                在x86 CPU中,就是DR0~DR7
            特点
                数量有限
                可实现软件断点不具有的功能,如监视数据访问和I/O访问等
            语法
                ba [id] access size [options] [address [passes]] ["command_string"]
                触发断点的访问方式
                    e,对目标内存的执行,被称为代码访问断点
                    r,对目标内存的读写,被称为数据访问断点
                    w,对目标内存的写,被称为数据访问断点
                    i,对目标内存的输入输出访问,被称为I/O访问断点
                触发断点的目标内存
                    size
                        对于e,为1
                        对于其他,依赖于平台
                            x86,1、2、4
                            x64,1、2、4、8
                        触发条件
                            实际访问包含目标内存
                    address
                        需要按照size对齐
            注意1
                硬件断点数量有限
                在恢复目标执行时才把硬件断点落实到上下文的寄存器中
                所以数量超过限制时,ba不会报错,恢复执行时会报错
            注意2
                在初始断点触发时,不能设置硬件断点
                设置会报错提示
                    初始断点后,系统会重新设置线程上下文,故不能设置硬件断点
                    建议在程序入口点或初始化代码处设置
        30.12.3 条件断点
            背景
                基本中断条件(如地址)范围太广,导致频繁中断
                希望在满足指定条件时,才中断
            方法
                利用中断设置命令中的命令参数
                    中断触发时,会先执行命令参数
                    不合条件则直接恢复(gc)
                "command_string"
                    "jc (condition) 'optional_commands'; 'gc'"
                    ".if (condition) {option_commands} .else {gc}"
                condition中读取变量数据
                    对地址按指定长度进行解引用
                    poi,by,wo,dwo,qwo
                    帮助文档搜索,MASM Numbers and Operators
        30.12.4 地址表达方法
            内存地址
            模块符号,module!function[+N]
            源码行,`module!file:line`,注意使用重音符号
            源码类方法,class__method或class::method
        30.12.5 设置针对线程的断点
            线程限定符 断点设置命令
                ~.,~#,~*,~number,~~ID
        30.12.6 管理断点
            显示,bl
                break list
                列1     断点序号,N
                列2     断点状态,(e|d)[u],分别表示enable,disable,unresolved
                列3     断点描述,断点地址[访问方式 访问长度]
                列4     剩余穿越次数,N
                列5     初始穿越次数,(N)
                列6     关联的进程和线程,进程号:线程号,线程号表示为****(任意)或~NNN(指定)
                列7     断点地址的符号表示
                列8     关联命令,可能不存在
            取消,bc 断点号
                break cancel
                断点号
                    指定单个    N
                    指定集合    N1,N2,N3
                    任意       *
                    范围       N-M
                    组合       N1,N2-N3
            启用和禁用,be|bd 断点号
                break enable|disable
    30.13 控制进程和线程
        30.13.0 概述
            讨论多进程和多线程的调试知识
        30.13.1 MulThrds
            功能
                创建线程,CreateThread
                挂起线程,SuspendThread
                    增加其挂起计数(Suspend Count)
                恢复线程,ResumeThread
                    降低其挂起计数(Suspend Count)
                调试中断,DebugBreak
                    当程序处于被调试状态,调用DebugBreak会中断到调试器,否则会触发异常导致关闭
            挂起计数
                线程的一个自带属性
                挂起计数大于0,则线程处于挂起状态(Suspended),不会被执行
                挂起计数最多降低到-1
        30.13.2 控制线程执行
            通常情况
                当进程中断到调试器时,所有线程会挂起
                    当调试目标中断到调试器时,windbg会对所有线程调用SuspendThread
                当进程恢复时,所有线程会恢复
                    当调试目标恢复时,windbg会对所有线程调用ResumeThread
            调试需求,独立控制各个线程的执行
            方法,控制线程的挂起计数
            方式一,修改挂起计数
                挂起计数+1,线程限定符 n
                    实现,调用SuspendThread
                挂起计数-1,线程限定符 m
                    实现,调用ResumeThread
            方式二,修改线程的冻结状态(windbg为线程记录的)
                调试器引擎内部,调用ThreadInfo::ChangeFreeze方法
                冻结(freeze),线程限定符 f
                解冻(unfreeze),线程限定符 u
                实现
                    当调试目标恢复时,windbg只会为未冻结的线程调用ResumeThread
            方式三,恢复指定线程
                线程限定符 g
                实现
                    windbg对指定线程调用ResumeThread
                注意
                    当调试含多线程的进程时,在只恢复当个线程的情况下,执行Ctrl+Break
                        默认使用的远程中断线程方法会失败,超时后windbg会使用挂起方法
                    windbg收到Ctrl+Break命令,会在目标进程中创建一个远程线程
                    windbg收到新线程创建事件
                        打印信息,当前只有两个线程未被冻结
                    windbg恢复调试目标继续执行时
                        由于调试目标只恢复了一个线程
                        所以调试器不会对新建线程调用ResumeThread,新建线程也被冻结
                    windbg等待超时后,会强行挂起调试目标,后进入命令模式
                类似的,对指定线程进行单步执行
                    如,线程限定符 t
            相关命令
                ~,查看所有线程
                    线程序号 Id: 进程ID.线程ID Suspend: 挂起计数 Teb: ted_address (Unfrozen|Frozen)
                    Teb,线程环境块
        30.13.3 多进程调试
            增加新的调试目标
                .attach [-premote RemoteOptions] AttachOptions PID 
                .create [-premote RemoteOptions] [-f] CommandLine 
            其他调试命令
                查看所有的进程和线程
                    |*
                    ~和~*
                查看当前的进程和线程
                    |
                进程和线程的切换
                    |N s
                    ~N s
                线程执行控制
                    ~N g|t
    30.14 观察栈
        30.14.0 概述
            当今使用的计算机系统都是基于栈架构
            栈是进行函数调用的基础
        30.14.1 显示栈回溯
            函数栈的使用情形和内容
                创建局部变量
                函数调用时,参数入栈和返回地址入栈
                寄存器的入栈和出栈
                其他信息,如中断保护(CC)
            栈回溯(stack backtrace)
                从栈顶到栈底遍历栈帧,追溯函数调用过程
                基于子rsp,通过代码反推父rsp,栈顶栈帧的rsp在寄存器中
                函数调用栈
                    宏观上,是栈帧组成的栈结构
                    微观上,是栈数据组成的栈结构
                栈回溯
                    通过定位栈帧sp,以分离栈帧,进而分析栈数据
                栈帧,记录了一个运行中函数的调用输入、运行状态、返回地址
                    主要包含了(自底向上看)
                        其他信息
                        局部变量
                        寄存器
                        返回地址
                        栈参数
                典型sp操作
                    初始化sp指向栈底
                    调用线程入口函数
                        入栈返回地址(0)和寄存器,创建局部变量
                    调用子函数
                        入栈栈参数、返回地址(父函数call指令的下一相邻指令的地址)、寄存器
                        创建局部变量
                sp定位方法
                    从栈顶到栈底回溯
                    栈顶sp,反算自寄存器sp
                    父栈帧sp,反算自子栈帧sp
            例子
                int main()
                {
                    printf("please set breakpoint at start");
                    getchar();

                    return 0;
                }
                k
                    Child-SP          RetAddr           Call Site
                    0000003c`ac33f518 00007ff8`d2925227 ntdll!ZwReadFile+0x14
                    0000003c`ac33f520 00007ff8`d35960ad KERNELBASE!ReadFile+0x77
                    0000003c`ac33f5a0 00007ff8`d35961e2 ucrtbase!_read_nolock+0x119
                    0000003c`ac33f640 00007ff8`d3595308 ucrtbase!_read+0xa2
                    0000003c`ac33f680 00007ff8`d359752e ucrtbase!common_refill_and_read_nolock<char>+0x70
                    0000003c`ac33f6b0 00007ff7`017b1016 ucrtbase!fgetc+0xce
                    0000003c`ac33f6f0 00007ff7`017b12c8 test_bp!main+0x16 [g:\user\vs2015\test\test_bp\test_bp.cpp @ 12]
                    0000003c`ac33f720 00007ff8`d4337bd4 test_bp!__scrt_common_main_seh+0x124 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 264]
                    0000003c`ac33f760 00007ff8`d57ace71 KERNEL32!BaseThreadInitThunk+0x14
                    0000003c`ac33f790 00000000`00000000 ntdll!RtlUserThreadStart+0x21
            调用栈解释
                每一行,描述当前线程的用户态栈上的一个栈帧
                函数调用,自底向上
                栈回溯过程,从子栈帧到父栈帧,直到返回地址为0
                    查看线程在当前栈帧的执行状态
                        r
                            rax=0000000000000006 rbx=0000003cac33f658 rcx=0000000000000054
                            rdx=0000000000000000 rsi=0000000000000054 rdi=0000000000000000
                            rip=00007ff8d57dc124 rsp=0000003cac33f518 rbp=0000000000001000
                            r8=0000000000000000  r9=0000000000000000 r10=000001d9fc781fc0
                            r11=0000003cac33f590 r12=0000000000000000 r13=0000000000001000
                            r14=0000000000000000 r15=000001d9fc781fc0
                            iopl=0         nv up ei pl zr na po nc
                            cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244
                            ntdll!ZwReadFile+0x14:
                            00007ff8`d57dc124 c3              ret
                        当前栈rsp
                            child rsp为 0000003cac33f518
                            当前即将返回,说明当前rsp指向返回地址
                                u ntdll!ZwReadFile
                                    ntdll!NtReadFile:
                                    00007ff8`d57dc110 4c8bd1          mov     r10,rcx
                                    00007ff8`d57dc113 b806000000      mov     eax,6
                                    00007ff8`d57dc118 f604250803fe7f01 test    byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
                                    00007ff8`d57dc120 7503            jne     ntdll!ZwReadFile+0x15 (00007ff8`d57dc125)
                                    00007ff8`d57dc122 0f05            syscall
                                    00007ff8`d57dc124 c3              ret
                            查看函数调用,发现没有栈参数
                                dd 0000003cac33f518
                                    0000003c`ac33f518  d2925227 00007ff8 00001000 00000000
                                ln 00007ff8d2925227
                                    (00007ff8`d29251b0)   KERNELBASE!ReadFile+0x77
                                u KERNELBASE!ReadFile
                                    KERNELBASE!ReadFile+0x54:
                                    00007ff8`d2925204 4489442430      mov     dword ptr [rsp+30h],r8d
                                    00007ff8`d2925209 4c89542428      mov     qword ptr [rsp+28h],r10
                                    00007ff8`d292520e 488d442450      lea     rax,[rsp+50h]
                                    00007ff8`d2925213 4889442420      mov     qword ptr [rsp+20h],rax
                                    00007ff8`d2925218 4533c9          xor     r9d,r9d
                                    00007ff8`d292521b 4533c0          xor     r8d,r8d
                                    00007ff8`d292521e 33d2            xor     edx,edx
                                    00007ff8`d2925220 48ff1519171800  call    qword ptr [KERNELBASE!_imp_NtReadFile (00007ff8`d2aa6940)]
                                    0:000> 
                                    KERNELBASE!ReadFile+0x77:
                                    00007ff8`d2925227 0f1f440000      nop     dword ptr [rax+rax]
                            当前栈rsp = 返回值地址 + 8 = 0000003cac33f518 + 8 = 0000003cac33f520
                    查看当前栈帧的数据
                        栈基址通常情况下,附近的数据
                            栈基址      父栈帧RBP
                            栈基址+8*1  返回地址
                            栈基址+8*2  第一个栈参数
                            栈基址+8*N  第N个栈参数
                        产生的栈帧信息
                            Child-SP    0000003cac33f518
                                因为未执行"sub rsp, N",所以子栈帧RSP等于当前ESP
                            RetAddr     00007ff8 d2925227
                            Call Site   ntdll!ZwReadFile+0x14
                                cs:rip
                                    cs=0033
                                    rip=00007ff8d57dc124
                                ntdll!ZwReadFile+0x14
                                    u ntdll!ZwReadFile
                                        ntdll!NtReadFile:
                                        00007ff8`d57dc110 4c8bd1          mov     r10,rcx
                                        00007ff8`d57dc113 b806000000      mov     eax,6
                                        00007ff8`d57dc118 f604250803fe7f01 test    byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
                                        00007ff8`d57dc120 7503            jne     ntdll!ZwReadFile+0x15 (00007ff8`d57dc125)
                                        00007ff8`d57dc122 0f05            syscall
                                        00007ff8`d57dc124 c3              ret
                                cs选择子,指向的段描述符,段基址为0
                                00007ff8d57dc124 = 00007ff8`d57dc110 + 0x14 = ntdll!ZwReadFile+0x14
                    恢复和查看线程在上一栈帧的执行状态
                        当前栈rsp
                            child rsp为 0000003cac33f520
                            查看函数体,发现有寄存器入栈和局部变量,共0x78
                                u KERNELBASE!ReadFile
                                    KERNELBASE!ReadFile:
                                    00007ff8`d29251b0 48895c2410      mov     qword ptr [rsp+10h],rbx
                                    00007ff8`d29251b5 4c894c2420      mov     qword ptr [rsp+20h],r9
                                    00007ff8`d29251ba 56              push    rsi
                                    00007ff8`d29251bb 57              push    rdi
                                    00007ff8`d29251bc 4156            push    r14
                                    00007ff8`d29251be 4883ec60        sub     rsp,60h
                                    00007ff8`d29251c2 498bd9          mov     rbx,r9
                                    00007ff8`d29251c5 4c8bd2          mov     r10,rdx
                            查看函数调用,发现没有栈参数
                                返回地址的栈地址
                                     = child rsp + 寄存器入栈长度 + 局部变量长度
                                     = 0000003cac33f520 + 78
                                     = 0000003cac33f598
                                返回地址的值
                                    dd 0000003cac33f598
                                        0000003c`ac33f598  d35960ad 00007ff8 00000000 00000000
                                函数调用
                                    ln 00007ff8d35960ad
                                        (00007ff8`d3595f94)   ucrtbase!_read_nolock+0x119   |  (00007ff8`d3596140)   ucrtbase!_read
                                    u ucrtbase!_read_nolock+0x110
                                        ucrtbase!_read_nolock+0x110:
                                        00007ff8`d35960a4 498bd7          mov     rdx,r15
                                        00007ff8`d35960a7 ff15b3000a00    call    qword ptr [ucrtbase!_imp_ReadFile (00007ff8`d3636160)]
                                        00007ff8`d35960ad 85c0            test    eax,eax
                            当前栈rsp = 返回值地址 + 8 = 0000003cac33f598 + 8 = 0000003cac33f5a0
                    查看上一栈帧的数据
                        当前栈,包含寄存器入栈和局部变量,没有栈参数
                        产生的栈帧信息
                            Child-SP    0000003cac33f520
                            RetAddr     00007ff8d35960ad
                            Call Site   KERNELBASE!ReadFile+0x77
                                        子函数返回地址
                    恢复和查看线程在上二栈帧的执行状态
                        当前栈rsp
                            child rsp为 0000003cac33f5a0
                            查看函数体,发现有寄存器入栈和局部变量,共0x98
                                u ucrtbase!_read_nolock
                                ucrtbase!_read_nolock:
                                00007ff8`d3595f94 4889542410      mov     qword ptr [rsp+10h],rdx
                                00007ff8`d3595f99 53              push    rbx
                                00007ff8`d3595f9a 55              push    rbp
                                00007ff8`d3595f9b 57              push    rdi
                                00007ff8`d3595f9c 4154            push    r12
                                00007ff8`d3595f9e 4155            push    r13
                                00007ff8`d3595fa0 4156            push    r14
                                00007ff8`d3595fa2 4157            push    r15
                                0:000> u
                                ucrtbase!_read_nolock+0x10:
                                00007ff8`d3595fa4 4883ec60        sub     rsp,60h
                            查看函数调用,发现没有栈参数
                                返回地址的栈地址
                                     = child rsp + 寄存器入栈长度 + 局部变量长度
                                     = 0000003cac33f5a0 + 98
                                     = 0000003cac33f638
                                返回地址的值
                                    dd 0000003cac33f638
                                        0000003c`ac33f638  d35961e2 00007ff8 00001000 00000000
                                函数调用
                                    ln 00007ff8d35961e2
                                        (00007ff8`d3596140)   ucrtbase!_read+0xa2   |  (00007ff8`d3596230)   ucrtbase!_acrt_stdio_free_buffer_nolock
                                    u ucrtbase!_read+0x92
                                        ucrtbase!_read+0x92:
                                        00007ff8`d35961d2 01741145        add     dword ptr [rcx+rdx+45h],esi
                                        00007ff8`d35961d6 8bc7            mov     eax,edi
                                        00007ff8`d35961d8 498bd5          mov     rdx,r13
                                        00007ff8`d35961db 8bce            mov     ecx,esi
                                        00007ff8`d35961dd e8b2fdffff      call    ucrtbase!_read_nolock (00007ff8`d3595f94)
                                        00007ff8`d35961e2 8bd8            mov     ebx,eax
                                        00007ff8`d35961e4 eb13            jmp     ucrtbase!_read+0xb9 (00007ff8`d35961f9)
                                        00007ff8`d35961e6 e8c5adffff      call    ucrtbase!_errno (00007ff8`d3590fb0)
                            当前栈rsp = 返回值地址 + 8 = 0000003cac33f638 + 8 = 0000003cac33f640
                    查看上二栈帧的数据
                        当前栈,包含寄存器入栈和局部变量,没有栈参数
                        产生的栈帧信息
                            Child-SP    0000003cac33f5a0
                            RetAddr     00007ff8d35961e2
                            Call Site   ucrtbase!_read_nolock+0x119
                                        子函数返回地址
                列含义
                    第一列,Child-SP,子栈帧开始的栈地址
                        相邻SP,之间构成一个栈帧
                    第二列,RetAddr,返回地址
                        栈帧开始后,显示栈参数,然后是返回地址
                    第三列,Call Site,函数名+偏移
                        栈顶,当前执行的函数位置,源自rip
                            ln rip
                        栈中,调用子函数的函数位置,源自子函数的返回地址
                            ln child_ret
            相关命令
                k,栈查看基本命令
                kL,不显示源码行数
                kb,显示三个栈参数,与调用协议相关
                kp,显示所有栈参数,包括类型、名称、数值,需要有完全的调试符号(私有符号)
                kP,类似kp,但每个栈参数各占一行
                kv,在kb的基础上,显示FPO(frame pointer omission,栈指针省略)和调用协议
                kn,会显示栈帧的序号
                kf,会显示栈帧间sp差值,或每个栈帧的长度
        30.14.2 观察栈变量
            dbgee.exe
                TCHAR g_szGlobal[] = _T("A global var.");

                int _tmain(int argc, _TCHAR* argv[])
                {
                    int nRet = 0;
                    TCHAR szBuffer[MAX_PATH];

                    printf("dbgee enters main.\n");
                    if (argc == 1)
                    {
                        *(int *)0 = 1;
                        printf("test\n");
                        nRet = -1;
                    }
                    for (int i = 0; i<argc; i++)
                    {
                        _stprintf(szBuffer, _T("Arg [%d]: %s"), i, argv[i]);
                        _tprintf(szBuffer);
                    }
                    printf("dbgee exits.\n");
                    return nRet;
                }
            有私有符号,dv查看
                查看当前栈帧的栈变量,dv /i/t/V
                    符号属性(prv或pub)
                    符号类型(param或local)
                    变量首地址的绝对表示
                    变量首地址的sp偏移表示
                    变量数据类型
                    变量名
                    变量值
                切换栈帧,.frame frame_index
                    可使用kn查看栈帧索引
                例子
                    命令查看局部变量
                        windbg .\dbgee.exe
                        bp `dbgee!dbgee.cpp:11`
                            注意断点打在函数首条语句处,而非函数入口
                            运行到此处时,参数已经初始化,局部变量已经开辟但未初始化
                            bl
                                0 e 00007ff6`f3c3102b [g:\user\vs2015\test\dbgee\dbgee.cpp @ 11]    0001 (0001)  0:**** dbgee!wmain+0x2b
                        kn
                            # Child-SP          RetAddr           Call Site
                            00 000000ca`5c30fc20 00007ff6`f3c31438 dbgee!wmain+0x2b [g:\user\vs2015\test\dbgee\dbgee.cpp @ 11]
                            01 000000ca`5c30fe70 00007ff8`d4337bd4 dbgee!__scrt_common_main_seh+0x124 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 264]
                            02 000000ca`5c30feb0 00007ff8`d57ace71 KERNEL32!BaseThreadInitThunk+0x14
                            03 000000ca`5c30fee0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
                        dv /i/t/V
                            Value unavailable error for nRet
                            Value unavailable error for i
                            prv param  @esi @esi int argc = 0n1
                            prv param  @rdx @rdx wchar_t ** argv = 0x0000022c`2a78b8d0
                            prv local  000000ca`5c30fc40 @rsp+0x20 wchar_t [260] szBuffer = wchar_t [260] "\Release\dbgee.exe"
                    当前栈帧位置,[000000ca`5c30fc20, 000000ca`5c30fe70)
                        db 000000ca`5c30fc20 000000ca`5c30fe6f
                            000000ca`5c30fc20  5c 00 76 00 73 00 32 00-30 00 31 00 35 00 5c 00  \.v.s.2.0.1.5.\.
                            000000ca`5c30fc30  54 00 65 00 73 00 74 00-5c 00 78 00 36 00 34 00  T.e.s.t.\.x.6.4.
                            000000ca`5c30fc40  5c 00 52 00 65 00 6c 00-65 00 61 00 73 00 65 00  \.R.e.l.e.a.s.e.
                            000000ca`5c30fc50  5c 00 64 00 62 00 67 00-65 00 65 00 2e 00 65 00  \.d.b.g.e.e...e.
                            000000ca`5c30fc60  78 00 65 00 00 00 00 00-a0 00 00 00 00 00 00 00  x.e.............
                            000000ca`5c30fc70  00 00 00 00 00 00 00 00-60 fd 30 5c ca 00 00 00  ........`.0\....
                            000000ca`5c30fc80  a0 1f 79 2a 2c 02 00 00-40 14 00 00 00 00 00 00  ..y*,...@.......
                            000000ca`5c30fc90  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                            000000ca`5c30fca0  30 24 79 2a 2c 02 00 00-00 00 00 00 00 00 00 00  0$y*,...........
                            000000ca`5c30fcb0  00 00 00 00 00 00 00 00-cd 07 78 d5 f8 7f 00 00  ..........x.....
                            000000ca`5c30fcc0  00 00 78 2a 2c 02 00 00-00 00 78 2a 2c 02 00 00  ..x*,.....x*,...
                            000000ca`5c30fcd0  30 24 79 2a 2c 02 00 00-40 24 79 2a 2c 02 00 00  0$y*,...@$y*,...
                            000000ca`5c30fce0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                            000000ca`5c30fcf0  00 00 00 00 00 00 00 00-79 50 75 d5 f8 7f 00 00  ........yPu.....
                            000000ca`5c30fd00  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                            000000ca`5c30fd10  00 00 00 00 00 00 00 00-70 35 79 d5 f8 7f 00 00  ........p5y.....
                            000000ca`5c30fd20  02 00 00 00 00 00 00 00-01 00 00 00 00 00 00 00  ................
                            000000ca`5c30fd30  a8 f0 78 2a 2c 02 00 00-dc 37 79 2a 2c 02 00 00  ..x*,....7y*,...
                            000000ca`5c30fd40  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                            000000ca`5c30fd50  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                            000000ca`5c30fd60  40 24 79 2a 2c 02 00 00-a1 fb 77 d5 f8 7f 00 00  @$y*,.....w.....
                            000000ca`5c30fd70  00 00 00 00 00 00 00 00-00 00 78 2a 2c 02 00 00  ..........x*,...
                            000000ca`5c30fd80  00 00 00 00 00 00 00 00-70 35 79 d5 f8 7f 00 00  ........p5y.....
                            000000ca`5c30fd90  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                            000000ca`5c30fda0  00 00 00 00 00 00 00 00-db 1e 92 d2 f8 7f 00 00  ................
                            000000ca`5c30fdb0  40 24 79 2a 2c 02 00 00-00 00 00 00 00 00 00 00  @$y*,...........
                            000000ca`5c30fdc0  2a 2e 51 8b 26 ce 00 00-00 00 00 00 00 00 00 00  *.Q.&...........
                            000000ca`5c30fdd0  00 00 00 00 00 00 00 00-96 20 59 d3 f8 7f 00 00  ......... Y.....
                            000000ca`5c30fde0  00 00 00 00 2c 02 00 00-00 00 00 00 00 00 00 00  ....,...........
                            000000ca`5c30fdf0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                            000000ca`5c30fe00  c0 21 c3 f3 f6 7f 00 00-26 d5 59 d3 f8 7f 00 00  .!......&.Y.....
                            000000ca`5c30fe10  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                            000000ca`5c30fe20  02 00 00 00 00 00 00 00-00 00 00 40 00 04 00 bc  ...........@....
                            000000ca`5c30fe30  01 00 00 00 00 00 00 00-29 18 c3 f3 f6 7f 00 00  ........).......
                            000000ca`5c30fe40  1f 00 00 00 00 00 00 00-01 00 00 00 00 00 00 00  ................
                            000000ca`5c30fe50  c8 76 5c 80 88 db 00 00-00 00 00 00 00 00 00 00  .v\.............
                            000000ca`5c30fe60  00 c0 66 d3 f8 7f 00 00-38 14 c3 f3 f6 7f 00 00  ..f.....8.......
                    栈回溯
                        u dbgee!wmain
                            dbgee!wmain [g:\user\vs2015\test\dbgee\dbgee.cpp @ 10]:
                            00007ff6`f3c31000 48895c2408      mov     qword ptr [rsp+8],rbx
                            00007ff6`f3c31005 48896c2418      mov     qword ptr [rsp+18h],rbp
                            00007ff6`f3c3100a 4889742420      mov     qword ptr [rsp+20h],rsi
                            00007ff6`f3c3100f 57              push    rdi
                            00007ff6`f3c31010 4881ec40020000  sub     rsp,240h
                        child sp为 000000ca5c30fc20
                            在父栈帧分配完栈变量后,child sp指向父栈帧栈顶
                            所以child sp指向的栈空间,属于父栈帧,下一位置才属于子栈帧
                        查看函数体,发现有寄存器入栈和局部变量,共0x248
                        查看函数调用,发现没有栈参数
                            返回地址的栈地址
                                    = child rsp + 寄存器入栈长度 + 局部变量长度
                                    = 000000ca5c30fc20 + 248
                                    = 000000ca5c30fe68
                            返回地址的值
                                dd 000000ca5c30fe68
                                    000000ca`5c30fe68  f3c31438 00007ff6 d366b590 00007ff8
                            函数调用
                                ln 00007ff6f3c31438
                                    f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl(264)+0x22
                                    (00007ff6`f3c31314)   dbgee!__scrt_common_main_seh+0x124   |  (00007db 000000ca`5c30fc20 000000ca`5c30fe6fff6`f3c3149c)   dbgee!wmainCRTStartup
                                u dbgee!__scrt_common_main_seh+0x114
                                    dbgee!__scrt_common_main_seh+0x114 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 264]:
                                    00007ff6`f3c31428 0a00            or      al,byte ptr [rax]
                                    00007ff6`f3c3142a 004c8bc0        add     byte ptr [rbx+rcx*4-40h],cl
                                    00007ff6`f3c3142e 488b17          mov     rdx,qword ptr [rdi]
                                    00007ff6`f3c31431 8b0b            mov     ecx,dword ptr [rbx]
                                    00007ff6`f3c31433 e8c8fbffff      call    dbgee!wmain (00007ff6`f3c31000)
                                    00007ff6`f3c31438 8bd8            mov     ebx,eax
                                    00007ff6`f3c3143a 33c9            xor     ecx,ecx
                                    00007ff6`f3c3143c e8050a0000      call    dbgee!_telemetry_main_return_trigger (00007ff6`f3c31e46)
                        当前栈rsp = 返回值地址 + 8 = 000000ca5c30fe68 + 8 = 000000ca5c30fe70
                    栈变量分析
                        反汇编 dbgee!wmain
                            u dbgee!wmain
                            dbgee!wmain [g:\user\vs2015\test\dbgee\dbgee.cpp @ 10]:
                            00007ff6`f3c31000 48895c2408      mov     qword ptr [rsp+8],rbx
                            00007ff6`f3c31005 48896c2418      mov     qword ptr [rsp+18h],rbp
                            00007ff6`f3c3100a 4889742420      mov     qword ptr [rsp+20h],rsi
                            00007ff6`f3c3100f 57              push    rdi
                            00007ff6`f3c31010 4881ec40020000  sub     rsp,240h
                            00007ff6`f3c31017 488b05e21f0000  mov     rax,qword ptr [dbgee!__security_cookie (00007ff6`f3c33000)]
                            00007ff6`f3c3101e 4833c4          xor     rax,rsp
                            00007ff6`f3c31021 4889842430020000 mov     qword ptr [rsp+230h],rax
                            0:000> u
                            dbgee!wmain+0x29 [g:\user\vs2015\test\dbgee\dbgee.cpp @ 10]:
                            00007ff6`f3c31029 8bf1            mov     esi,ecx
                            00007ff6`f3c3102b 33db            xor     ebx,ebx
                            00007ff6`f3c3102d 488d0dec110000  lea     rcx,[dbgee!`string' (00007ff6`f3c32220)]
                            00007ff6`f3c31034 8beb            mov     ebp,ebx
                            00007ff6`f3c31036 488bfa          mov     rdi,rdx
                            00007ff6`f3c31039 e862010000      call    dbgee!printf (00007ff6`f3c311a0)
                            00007ff6`f3c3103e 83fe01          cmp     esi,1
                            00007ff6`f3c31041 7516            jne     dbgee!wmain+0x59 (00007ff6`f3c31059)
                            0:000> 
                            dbgee!wmain+0x43 [g:\user\vs2015\test\dbgee\dbgee.cpp @ 18]:
                            00007ff6`f3c31043 488d0dea110000  lea     rcx,[dbgee!`string' (00007ff6`f3c32234)]
                            00007ff6`f3c3104a 89342500000000  mov     dword ptr [0],esi
                            00007ff6`f3c31051 e84a010000      call    dbgee!printf (00007ff6`f3c311a0)
                            00007ff6`f3c31056 83cdff          or      ebp,0FFFFFFFFh
                            00007ff6`f3c31059 85f6            test    esi,esi
                            00007ff6`f3c3105b 7e2e            jle     dbgee!wmain+0x8b (00007ff6`f3c3108b)
                            00007ff6`f3c3105d 0f1f00          nop     dword ptr [rax]
                            00007ff6`f3c31060 4c8b0f          mov     r9,qword ptr [rdi]
                            0:000> 
                            dbgee!wmain+0x63 [g:\user\vs2015\test\dbgee\dbgee.cpp @ 23]:
                            00007ff6`f3c31063 488d15d6110000  lea     rdx,[dbgee!`string' (00007ff6`f3c32240)]
                            00007ff6`f3c3106a 448bc3          mov     r8d,ebx
                            00007ff6`f3c3106d 488d4c2420      lea     rcx,[rsp+20h]
                            00007ff6`f3c31072 e8b9000000      call    dbgee!_swprintf (00007ff6`f3c31130)
                            00007ff6`f3c31077 488d4c2420      lea     rcx,[rsp+20h]
                            00007ff6`f3c3107c e84f000000      call    dbgee!wprintf (00007ff6`f3c310d0)
                            00007ff6`f3c31081 ffc3            inc     ebx
                            00007ff6`f3c31083 488d7f08        lea     rdi,[rdi+8]
                            0:000> 
                            dbgee!wmain+0x87 [g:\user\vs2015\test\dbgee\dbgee.cpp @ 24]:
                            00007ff6`f3c31087 3bde            cmp     ebx,esi
                            00007ff6`f3c31089 7cd5            jl      dbgee!wmain+0x60 (00007ff6`f3c31060)
                            00007ff6`f3c3108b 488d0dce110000  lea     rcx,[dbgee!`string' (00007ff6`f3c32260)]
                            00007ff6`f3c31092 e809010000      call    dbgee!printf (00007ff6`f3c311a0)
                            00007ff6`f3c31097 8bc5            mov     eax,ebp
                            00007ff6`f3c31099 488b8c2430020000 mov     rcx,qword ptr [rsp+230h]
                            00007ff6`f3c310a1 4833cc          xor     rcx,rsp
                            00007ff6`f3c310a4 e867010000      call    dbgee!__security_check_cookie (00007ff6`f3c31210)
                            0:000> u
                            dbgee!wmain+0xa9 [g:\user\vs2015\test\dbgee\dbgee.cpp @ 28]:
                            00007ff6`f3c310a9 4c8d9c2440020000 lea     r11,[rsp+240h]
                            00007ff6`f3c310b1 498b5b10        mov     rbx,qword ptr [r11+10h]
                            00007ff6`f3c310b5 498b6b20        mov     rbp,qword ptr [r11+20h]
                            00007ff6`f3c310b9 498b7328        mov     rsi,qword ptr [r11+28h]
                            00007ff6`f3c310bd 498be3          mov     rsp,r11
                            00007ff6`f3c310c0 5f              pop     rdi
                            00007ff6`f3c310c1 c3              ret
                            00007ff6`f3c310c2 cc              int     3
                            0:000> u
                            dbgee!wmain+0xc3:
                            00007ff6`f3c310c3 cc              int     3
                            00007ff6`f3c310c4 cc              int     3
                            00007ff6`f3c310c5 cc              int     3
                            00007ff6`f3c310c6 cc              int     3
                            00007ff6`f3c310c7 cc              int     3
                            00007ff6`f3c310c8 cc              int     3
                            00007ff6`f3c310c9 cc              int     3
                            00007ff6`f3c310ca cc              int     3
                        可以发现
                            可以看出这里使用的_fastcall
                                从右至左压栈,第二一参分别存入xdx和xcx,被调清栈
                        nRet,使用ebp存储
                        szBuffer,首地址rsp+20h(000000ca5c30fc40),长度260(0x104)
                            [000000ca5c30fc40, 000000ca5c30fd44)
                        i,使用ebx存储
                        argc,通过rcx传入,后保存在esi中
                        argv,通过rdx传入,后保存在edi中
                    栈的初始化和收尾分析
                        初始化
                            代码
                                dbgee!wmain [g:\user\vs2015\test\dbgee\dbgee.cpp @ 10]:
                                00007ff6`f3c31000 48895c2408      mov     qword ptr [rsp+8],rbx
                                00007ff6`f3c31005 48896c2418      mov     qword ptr [rsp+18h],rbp
                                00007ff6`f3c3100a 4889742420      mov     qword ptr [rsp+20h],rsi
                                00007ff6`f3c3100f 57              push    rdi
                                00007ff6`f3c31010 4881ec40020000  sub     rsp,240h
                                00007ff6`f3c31017 488b05e21f0000  mov     rax,qword ptr [dbgee!__security_cookie (00007ff6`f3c33000)]
                                00007ff6`f3c3101e 4833c4          xor     rax,rsp
                                00007ff6`f3c31021 4889842430020000 mov     qword ptr [rsp+230h],rax
                            工作
                                寄存器入栈,保存即将使用到的寄存器
                                    00007ff6`f3c31000 48895c2408      mov     qword ptr [rsp+8],rbx
                                    00007ff6`f3c31005 48896c2418      mov     qword ptr [rsp+18h],rbp
                                    00007ff6`f3c3100a 4889742420      mov     qword ptr [rsp+20h],rsi
                                    00007ff6`f3c3100f 57              push    rdi
                                分配局部空间
                                    00007ff6`f3c31010 4881ec40020000  sub     rsp,240h
                                计算和存储栈帧特征值
                                    目的
                                        __security_cookie机制,防止栈溢出
                                    动作
                                        子栈帧sp与模块特征值(dbgee!__security_cookie)的异或
                                    设计
                                        判断栈破坏
                                        函数调用栈是否运行正常,体现为sp值变化是否正常
                                        栈帧初始化后的sp和栈帧收尾前的sp,应该一致
                                        方法
                                            初始化后,记录当前sp到当前栈帧中
                                            收尾前,比较当前的sp和栈中记录的sp是否一致
                                        方式
                                            初始化后,记录当前栈帧特征值(子栈帧sp(当前sp)与模块特征值的异或),到当前栈帧中
                                            收尾前,计算模块特征值(子栈帧sp(当前sp)与栈中存储的当前栈帧特征值的异或),判断是否一致
                                        规律
                                            处于异或关系的三个数,其中任意两个数,能够异或出第三个数
                                    代码
                                        00007ff6`f3c31017 488b05e21f0000  mov     rax,qword ptr [dbgee!__security_cookie (00007ff6`f3c33000)]
                                        00007ff6`f3c3101e 4833c4          xor     rax,rsp
                                        00007ff6`f3c31021 4889842430020000 mov     qword ptr [rsp+230h],rax
                                    分析
                                        dq 00007ff6`f3c33000
                                            00007ff6`f3c33000  0000db42`dc6c8ae8 ffff24bd`23937517
                                        rsp 此时为 000000ca`5c30fc20
                                        xor     rax,rsp
                                            hex(int('0000db42dc6c8ae8', 16)^int('000000ca5c30fc20', 16))
                                            0xdb88805c76c8
                                        dq 000000ca5c30fe50
                                            000000ca`5c30fe50  0000db88`805c76c8 00000000`00000000
                        收尾
                            代码
                                00007ff6`f3c31099 488b8c2430020000 mov     rcx,qword ptr [rsp+230h]
                                00007ff6`f3c310a1 4833cc          xor     rcx,rsp
                                00007ff6`f3c310a4 e867010000      call    dbgee!__security_check_cookie (00007ff6`f3c31210)
                                dbgee!wmain+0xa9 [g:\user\vs2015\test\dbgee\dbgee.cpp @ 28]:
                                00007ff6`f3c310a9 4c8d9c2440020000 lea     r11,[rsp+240h]
                                00007ff6`f3c310b1 498b5b10        mov     rbx,qword ptr [r11+10h]
                                00007ff6`f3c310b5 498b6b20        mov     rbp,qword ptr [r11+20h]
                                00007ff6`f3c310b9 498b7328        mov     rsi,qword ptr [r11+28h]
                                00007ff6`f3c310bd 498be3          mov     rsp,r11
                                00007ff6`f3c310c0 5f              pop     rdi
                                00007ff6`f3c310c1 c3              ret
                                00007ff6`f3c310c2 cc              int     3
                            工作
                                使用栈帧特征值,判断栈破坏
                                    计算模块特征值(子栈帧sp(当前sp)与栈中存储的当前栈帧特征值的异或),判断是否一致
                                    代码
                                        00007ff6`f3c31099 488b8c2430020000 mov     rcx,qword ptr [rsp+230h]
                                        00007ff6`f3c310a1 4833cc          xor     rcx,rsp
                                        00007ff6`f3c310a4 e867010000      call    dbgee!__security_check_cookie (00007ff6`f3c31210)
                                回收局部空间
                                    00007ff6`f3c310a9 4c8d9c2440020000 lea     r11,[rsp+240h]
                                    ...
                                    00007ff6`f3c310c0 5f              pop     rdi
                                寄存器入栈,保存即将使用到的寄存器
                                    00007ff6`f3c310b1 498b5b10        mov     rbx,qword ptr [r11+10h]
                                    00007ff6`f3c310b5 498b6b20        mov     rbp,qword ptr [r11+20h]
                                    00007ff6`f3c310b9 498b7328        mov     rsi,qword ptr [r11+28h]
                                    ...
                                    00007ff6`f3c310c0 5f              pop     rdi
            没有私有符号,手动推断
                查看栈帧内存
                    内存观察窗口
                    内存查看命令
                查看代码
                    局部变量一般引用方式
                        [rsp + N]
                    安全Cookie值(栈帧特征值)位置
                        [rsp + local_length - 0x10]
                    获取变量指针
                        lea xxx, [rsp + N]
            其他
                遍历对象,执行命令
                !for_each_local
                    遍历局部变量,通过@#Local访问
                    例子
                        !for_each_local dv @#Local
                            argc = 0n1
                            argv = 0x0000022c`2a78b8d0
                                i = 0n0
                            nRet = 0n0
                        szBuffer = wchar_t [260] "\Release\dbgee.exe"
                !for_each_frame
                    遍历栈帧
                    例子
                        !for_each_frame dv
    30.15 分析内存
        30.15.1 显示内存区域
            按数据格式显示内存区域
            语法
                d{a|b|c|d|Df|p|q|u|w|W|yb|yd|} [options] [Range]
            数据格式
                字节
                    a,ASCII码
                    b,字节和ASCII码
                    yb,二进制和字节
                字
                    w,WORD(uint16)
                    W,WORD和ASCII码
                    u,UNICODE字符
                        UNICODE,一种字符的数值映射方案
                        utf8,一种UNICODE的编码方案,常用于保存
                        uint16,一种UNICODE的编码方案,常用于计算
                双字
                    c,DWORD和ASCII码
                    d,DWORD(uint32)
                    f,单精度浮点数
                    yd,二进制和双字
                四字
                    q,uint64
                    D,双精度浮点数
                指针宽度
                    p
                省略
                    最近使用的数据格式
            内存范围,Range
                start_address end_address
                start_address {L|l}N
                    N,表示元素个数
                end_address {L|l}-N
            例子
                windbg dbgee
                bp dbgee!wmain
                g
                u dbgee!wmain+0x87
                    dbgee!wmain+0x87 [g:\user\vs2015\test\dbgee\dbgee.cpp @ 24]:
                    00007ff7`aa911087 3bde            cmp     ebx,esi
                    00007ff7`aa911089 7cd5            jl      dbgee!wmain+0x60 (00007ff7`aa911060)
                    00007ff7`aa91108b 488d0dce110000  lea     rcx,[dbgee!`string' (00007ff7`aa912260)]
                    00007ff7`aa911092 e809010000      call    dbgee!printf (00007ff7`aa9111a0)
                    00007ff7`aa911097 8bc5            mov     eax,ebp
                    00007ff7`aa911099 488b8c2430020000 mov     rcx,qword ptr [rsp+230h]
                    00007ff7`aa9110a1 4833cc          xor     rcx,rsp
                    00007ff7`aa9110a4 e867010000      call    dbgee!__security_check_cookie (00007ff7`aa911210)
                db 00007ff7`aa912260
                    00007ff7`aa912260  64 62 67 65 65 20 65 78-69 74 73 2e 0a 00 00 00  dbgee exits.....
                    00007ff7`aa912270  00 00 00 00 8e f6 42 60-00 00 00 00 02 00 00 00  ......B`........
                    00007ff7`aa912280  42 00 00 00 74 23 00 00-74 17 00 00 00 00 00 00  B...t#..t.......
                    00007ff7`aa912290  8e f6 42 60 00 00 00 00-0c 00 00 00 14 00 00 00  ..B`............
                    00007ff7`aa9122a0  b8 23 00 00 b8 17 00 00-00 00 00 00 8e f6 42 60  .#............B`
                    00007ff7`aa9122b0  00 00 00 00 0d 00 00 00-6c 02 00 00 cc 23 00 00  ........l....#..
                    00007ff7`aa9122c0  cc 17 00 00 00 00 00 00-8e f6 42 60 00 00 00 00  ..........B`....
                    00007ff7`aa9122d0  0e 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
        30.15.2 显示字符串
            简单字符串
                da 以0结尾的ASCII字符串的地址
                du 以0结尾的UNICODE字符串的地址
            ntdll!_STRING变量
                ds 变量地址
            ntdll!_UNICODE_STRING变量
                dS 变量地址
            例子
                da 00007ff7`aa912260
                    00007ff7`aa912260  "dbgee exits.."
        30.15.3 显示数据类型
            符号列举
                dt [module_name!]symbol
                结果可能多个
                支持通配符*
            符号显示
                dt [options] [module_name!]symbol
                结果唯一
                省略module_name,则自动搜索
                symbol
                    数据类型及其typedef别名、全局变量、静态变量、函数
                options
                    -rN,显示数据类型时,指定显示深度
                    -ny name,显示数据类型时,显示name开头的字段
            变量显示
                dt [options] [module_name!]symbol address
                按指定数据类型,解释变量
            链表遍历显示
            例子
                dt dbgee!wmain
                    wmain  int (
                        int argc = 1, 
                        wchar_t** argv = 00000254`afbddcc0 )
                dt dbgee!g_szGlobal
                    Symbol dbgee!g_szGlobal not found.
                    由于生成的是Release版的程序,同时全局变量g_szGlobal定义了但并未使用,导致其未被生成
                    尝试使用Debug版本程序调试
                        dt dbgee!g_szGlobal
                            [14]  "A global var."
                        du dbgee!g_szGlobal
                            00007ff6`1994c000  "A global var."
                        !address dbgee!g_szGlobal                                     
                            Usage:                  Image
                            Allocation Base:        00007ff6`19930000
                            Base Address:           00007ff6`1994c000
                            End Address:            00007ff6`1994d000
                            Region Size:            00000000`00001000
                            Type:                   01000000    MEM_IMAGE
                            State:                  00001000    MEM_COMMIT
                            Protect:                00000004    PAGE_READWRITE
                            More info:              lmv m dbgee
                            More info:              !lmi dbgee
                            More info:              ln 0x7ff61994c000
        30.15.4 搜索内存
            在指定范围内,搜索字符串
                s-[[Flags]]sa|su Range
                Range,内存范围
                sa|su,搜索ASCII字符串或UNICODE字符串
                Flags
                    lN,指定最小长度
            在指定范围内,搜索与指定对象类型相同的对象
                s-[[Flags]]v Range Object
                Object,含虚函数表的使用面向对象语言编写的类型的对象
            在指定范围内,搜索数组
                s[-[[Flags]]Type] Range data_array
                Type,数组元素类型
                    b字节,w字,d双字,q四字,aASCII字符,uUNICODE字符
                    默认为b
                data_array,数组内容
            例子
                对于 printf("dbgee exits.\n");
                    字符串"dbgee exits.\n",作为字符串常量,放在代码段中
                lm
                    start             end                 module name
                    00007ff7`aa910000 00007ff7`aa917000   dbgee    C (private pdb symbols)  G:\user\vs2015\Test\x64\Release\dbgee.pdb
                s-a 00007ff7`aa910000 00007ff7`aa917000 "dbgee exits."
                    00007ff7`aa912260  64 62 67 65 65 20 65 78-69 74 73 2e 0a 00 00 00  dbgee exits.....
                !for_each_module s-a @#Base @#End "dbgee exits.\n"
                    00007ff7`aa912260  64 62 67 65 65 20 65 78-69 74 73 2e 0a 00 00 00  dbgee exits.....
                    !for_each_XXX命令
                        !for_each_frame
                        !for_each_local
                        !for_each_module
                        !for_each_process
                        !for_each_thread
        30.15.5 修改内存
            写内存
                内存区间
                待写入数据
            写字符串
                e{a|u|za|zu} address "string"
                    a|u,按照ASCII或UNICODE方式写入,结尾不加0
                    za|zu,按照ASCII或UNICODE方式写入,结尾要加0
                    内存区间,[address, address+字符串实际字节长度)
                    待写入数据,"string"
                例子
                    da 00007ff7`aa912260 
                        00007ff7`aa912260  "dbgee exits.."
                    eza 00007ff7`aa912260 "test1 exits.\n"
                        ^ Memory access error in 'eza 00007ff7`aa912260 "test1 exits.
                        '
                        错误,试图修改只读的代码段
                    p
                    u dbgee!wmain+0x63
                        dbgee!wmain+0x63 [g:\user\vs2015\test\dbgee\dbgee.cpp @ 23]:
                        00007ff7`aa911063 488d15d6110000  lea     rdx,[dbgee!`string' (00007ff7`aa912240)]
                        00007ff7`aa91106a 448bc3          mov     r8d,ebx
                        00007ff7`aa91106d 488d4c2420      lea     rcx,[rsp+20h]
                        00007ff7`aa911072 e8b9000000      call    dbgee!_swprintf (00007ff7`aa911130)
                        00007ff7`aa911077 488d4c2420      lea     rcx,[rsp+20h]
                        00007ff7`aa91107c e84f000000      call    dbgee!wprintf (00007ff7`aa9110d0)
                        00007ff7`aa911081 ffc3            inc     ebx
                        00007ff7`aa911083 488d7f08        lea     rdi,[rdi+8]
                    db [rsp+20h]
                        000000dc`15b8f830  5c 00 52 00 65 00 6c 00-65 00 61 00 73 00 65 00  \.R.e.l.e.a.s.e.
                        000000dc`15b8f840  5c 00 64 00 62 00 67 00-65 00 65 00 2e 00 65 00  \.d.b.g.e.e...e.
                        000000dc`15b8f850  78 00 65 00 00 00 00 00-a0 00 00 00 00 00 00 00  x.e.............
                        000000dc`15b8f860  00 00 00 00 00 00 00 00-50 f9 b8 15 dc 00 00 00  ........P.......
                        000000dc`15b8f870  a0 84 be af 54 02 00 00-50 35 00 00 00 00 00 00  ....T...P5......
                        000000dc`15b8f880  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000dc`15b8f890  30 95 be af 54 02 00 00-00 00 00 00 00 00 00 00  0...T...........
                        000000dc`15b8f8a0  00 00 00 00 00 00 00 00-cd 07 ea bf fe 7f 00 00  ................
                    da [rsp+20h]
                        000000dc`15b8f830  "\"
                    eza [rsp+20h] "write data in szBuffer"
                    db [rsp+20h]
                        000000dc`15b8f830  77 72 69 74 65 20 64 61-74 61 20 69 6e 20 73 7a  write data in sz
                        000000dc`15b8f840  42 75 66 66 65 72 00 00-65 00 65 00 2e 00 65 00  Buffer..e.e...e.
                        000000dc`15b8f850  78 00 65 00 00 00 00 00-a0 00 00 00 00 00 00 00  x.e.............
                        000000dc`15b8f860  00 00 00 00 00 00 00 00-50 f9 b8 15 dc 00 00 00  ........P.......
                        000000dc`15b8f870  a0 84 be af 54 02 00 00-50 35 00 00 00 00 00 00  ....T...P5......
                        000000dc`15b8f880  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000dc`15b8f890  30 95 be af 54 02 00 00-00 00 00 00 00 00 00 00  0...T...........
                        000000dc`15b8f8a0  00 00 00 00 00 00 00 00-cd 07 ea bf fe 7f 00 00  ................
                    da [rsp+20h]
                        000000dc`15b8f830  "write data in szBuffer"
            写数组
                e{b|w|d|p|q|D|f} address data_array 
                    b字节,w字,d双字,p指针长度,q四字,D双精度浮点,f单精度浮点
                例子
                    ed [rsp+20h] 1 2 3 4 5
            交互式写字符串和数组
        30.15.6 使用物理内存地址
            命令,虚拟地址相关命令前加!
            条件,内核模式
        30.15.7 观察内存属性
            !address address
                显示指定内存区的属性
                含义
                    行1,Usage,内存区的用途
                        VAR,虚拟分配块、SBH堆、自定义分配器分配的内存、其他分类的区域
                        Free,未被预留的内存区域 
                        Image,存储可执行文件映像                            
                        Stack,函数栈
                        Teb,thread environment blocks (TEBs)
                        Peb,process environment block (PEB)
                        Heap,堆
                        PageHeap,整页堆(full-page heap) 
                        CSR,CSR shared memory 
                        Actx,激活上下文数据的内存
                        NLS,用于National Language Support (NLS) tables 
                        FileMap,内存映射文件
                    行2,Allocation Base,较大内存区的开始地址
                    行3,Base Address,较小内存区的开始地址
                    行4,End Address,较小内存区的结束地址
                    行5,Region Size,内存区大小
                    行6,Type,内存区的类型
                        MEM_IMAGE,映射可执行文件映像 
                        MEM_MAPPED,映射其他类型文件
                        MEM_PRIVATE,非映射私有内存,不共享
                    行7,State,内存区的状态
                        MEM_COMMIT,已提交
                        MEM_FREE,释放的内存,含未保留的内存
                        MEM_RESERVE,保留的内存
                    行8,Protect,内存区的访问保护
                        PAGE_NOACCESS,不可访问
                        PAGE_READONLY,只读
                        PAGE_READWRITE,可读可写
                        PAGE_WRITECOPY,写时会复制
                        PAGE_EXECUTE,可执行,不可读写
                        PAGE_EXECUTE_READ,可执行,只读
                        PAGE_EXECUTE_READWRITE,可执行,可读可写
                        PAGE_EXECUTE_WRITECOPY,可执行写时会复制
                        PAGE_GUARD,哨兵页
                        PAGE_NOCACHE,读不可缓存
                        PAGE_WRITECOMBINE,启用了写联合访问
                例子
                    !address dbgee!wmain
                        Usage:                  Image
                        Allocation Base:        00007ff7`aa910000
                        Base Address:           00007ff7`aa911000
                        End Address:            00007ff7`aa912000
                        Region Size:            00000000`00001000
                        Type:                   01000000    MEM_IMAGE
                        State:                  00001000    MEM_COMMIT
                        Protect:                00000020    PAGE_EXECUTE_READ
                        More info:              lmv m dbgee
                        More info:              !lmi dbgee
                        More info:              ln 0x7ff7aa911000
            !address
                显示当前进程所有内存区的属性
            !address -summary
                显示当前进程所有内存区的统计
            其他类似命令
                显示指定内存区的属性
                    !vprot dbgee!wmain
                        BaseAddress:       00007ff7aa911000
                        AllocationBase:    00007ff7aa910000
                        AllocationProtect: 00000080  PAGE_EXECUTE_WRITECOPY
                        RegionSize:        0000000000001000
                        State:             00001000  MEM_COMMIT
                        Protect:           00000020  PAGE_EXECUTE_READ
                        Type:              01000000  MEM_IMAGE
                显示当前进程所有内存区的属性
                    !vadump
    30.16 遍历链表
        30.16.0 背景
            Windows操作系统中,很多重要信息使用链表方式组织
            查看链表类型
                dt ntdll!*List*
        30.16.1 结构定义
            链表节点组成
                链接结构,用于节点连接
                    链接结构中的指针,指向下一链接结构,而非指向链表节点
                负载,用于数据保存
            链表分类
                双向链表
                    链接结构
                        dt ntdll!_LIST_ENTRY
                        +0x000 Flink            : Ptr64 _LIST_ENTRY
                        +0x008 Blink            : Ptr64 _LIST_ENTRY
                单项链表
                    链接结构
                        dt ntdll!_SINGLE_LIST_ENTRY
                        +0x000 Next             : Ptr64 _SINGLE_LIST_ENTRY
        30.16.2 双向链表示例
            Windows内核使用双向链表,管理所有进程的EPROCESS结构
        30.16.3 单项链表示例
            TEB结构使用单向链表,保存异常处理器
            遍历单向链表
                dt 链表节点类型 -l 下一节点地址的符号表示 头结点地址
            例子
                dt -r2 _PEB
                    dbgee!_PEB
                    +0x000 Reserved1        : [2] UChar
                    +0x002 BeingDebugged    : UChar
                    +0x003 Reserved2        : [1] UChar
                    +0x008 Reserved3        : [2] Ptr64 Void
                    +0x018 Ldr              : Ptr64 _PEB_LDR_DATA
                        +0x000 Reserved1        : [8] UChar
                        +0x008 Reserved2        : [3] Ptr64 Void
                        +0x020 InMemoryOrderModuleList : _LIST_ENTRY
                            +0x000 Flink            : Ptr64 _LIST_ENTRY
                            +0x008 Blink            : Ptr64 _LIST_ENTRY
                !peb
                    PEB at 000000dc15cbf000
                        InheritedAddressSpace:    No
                        ReadImageFileExecOptions: No
                        BeingDebugged:            Yes
                        ImageBaseAddress:         00007ff7aa910000
                        Ldr                       00007ffebffc53c0
                        Ldr.Initialized:          Yes
                        Ldr.InInitializationOrderModuleList: 00000254afbd4f80 . 00000254afbdb6b0
                        Ldr.InLoadOrderModuleList:           00000254afbd5130 . 00000254afbdb690
                        Ldr.InMemoryOrderModuleList:         00000254afbd5140 . 00000254afbdb6a0
                                        Base TimeStamp                     Module
                                7ff7aa910000 6042f68e Mar 06 11:27:10 2021 G:\user\vs2015\Test\x64\Release\dbgee.exe
                                7ffebfe60000 a52b7c6a Oct 24 03:22:18 2057 C:\Windows\SYSTEM32\ntdll.dll
                                7ffebe160000 2e3dfcea Aug 02 15:59:38 1994 C:\Windows\System32\KERNEL32.DLL
                                7ffebd010000 1a30e11b Dec 05 02:36:11 1983 C:\Windows\System32\KERNELBASE.dll
                                7ffebdf20000 96f604e5 Apr 05 01:18:29 2050 C:\Windows\System32\ADVAPI32.DLL
                                7ffebf6d0000 f5bdefd7 Aug 25 16:27:03 2100 C:\Windows\System32\msvcrt.dll
                                7ffebee30000 f7e420f7 Oct 17 00:23:19 2101 C:\Windows\System32\sechost.dll
                                7ffebef50000 0530c620 Oct 05 05:35:28 1972 C:\Windows\System32\RPCRT4.dll
                                7ffebce70000 5cbddb81 Apr 22 23:19:29 2019 C:\Windows\System32\ucrtbase.dll
                                7ffea83c0000 5c82fae2 Mar 09 07:29:38 2019 C:\Windows\SYSTEM32\VCRUNTIME140.dll
                dt _PEB_LDR_DATA -l InMemoryOrderModuleList.Flink poi(000000dc15cbf018)
                    dbgee!_PEB_LDR_DATA
                    InMemoryOrderModuleList.Flink at 0x7ffebffc53c0
                    ---------------------------------------------
                    +0x000 Reserved1        : [8]  "X"
                    +0x008 Reserved2        : [3] (null) 
                    +0x020 InMemoryOrderModuleList :  [ 0x00000254`afbd5140 - 0x254`afbdb6a0 ]
                        +0x000 Flink            : 0x00000254`afbd5140 _LIST_ENTRY [ 0x00000254`afbd4f70 - 0x7ffe`bffc53e0 ]
                        +0x008 Blink            : 0x00000254`afbdb6a0 _LIST_ENTRY [ 0x00007ffe`bffc53e0 - 0x254`afbdb7f0 ]

                    InMemoryOrderModuleList.Flink at 0x254afbd5120
                    ---------------------------------------------
                    +0x000 Reserved1        : [8]  ""
                    +0x008 Reserved2        : [3] 0x3000afdb`fe1e8a7e Void
                    +0x020 InMemoryOrderModuleList :  [ 0x00000254`afbd4f70 - 0x7ffe`bffc53e0 ]
                        +0x000 Flink            : 0x00000254`afbd4f70 _LIST_ENTRY [ 0x00000254`afbd5690 - 0x254`afbd5140 ]
                        +0x008 Blink            : 0x00007ffe`bffc53e0 _LIST_ENTRY [ 0x00000254`afbd5140 - 0x254`afbdb6a0 ]

                    InMemoryOrderModuleList.Flink at 0x254afbd4f50
                    ---------------------------------------------
                    +0x000 Reserved1        : [8]  "???"
                    +0x008 Reserved2        : [3] 0x3000afd9`fe1e8a7e Void
                    +0x020 InMemoryOrderModuleList :  [ 0x00000254`afbd5690 - 0x254`afbd5140 ]
                        +0x000 Flink            : 0x00000254`afbd5690 _LIST_ENTRY [ 0x00000254`afbd5d50 - 0x254`afbd4f70 ]
                        +0x008 Blink            : 0x00000254`afbd5140 _LIST_ENTRY [ 0x00000254`afbd4f70 - 0x7ffe`bffc53e0 ]
        30.16.4 DI命令
            dl[b] Address MaxCount Size 
                Address,链表起始地址,指向_LIST_ENTRY或_SINGLE_LIST_ENTRY
                MaxCount,链表节点最大个数
                Size,链表节点负载大小(单位为指针长度),负载位于链接结构后
            例子
                dl 0x7ffebffc53e0 5 2
                00007ffe`bffc53e0  00000254`afbd5140 00000254`afbdb6a0
                00000254`afbd5140  00000254`afbd4f70 00007ffe`bffc53e0
                00000254`afbd4f70  00000254`afbd5690 00000254`afbd5140
                00000254`afbd5690  00000254`afbd5d50 00000254`afbd4f70
                00000254`afbd5d50  00000254`afbd58e0 00000254`afbd5690
        30.16.5
            !list -t [Module!]Type.Field -x "Commands" [-a "Arguments"] [Options] StartAddress 
            !list " -t [Module!]Type.Field -x \"Commands\" [-a \"Arguments\"] [Options] StartAddress " 
                -t [Module!]Type.Field
                    描述链表节点类型,[Module!]Type
                    描述链接字段,Field
                -x "Commands" [-a "Arguments"]
                    描述对每个节点的操作命令
                    伪寄存器$extret表示当前节点的地址
                [Options]
                    -e,回显对每个节点执行的操作
                    -m N,遍历的最大节点数目
                StartAddress
                    链接头节点的地址 
            例子
                !list -t _PEB_LDR_DATA.InMemoryOrderModuleList.Flink -x "db @$extret l40" -e -m3 poi(000000dc15cbf018)
                db @$extret l40 
                00007ffe`bffc53c0  58 00 00 00 01 00 00 00-00 00 00 00 00 00 00 00  X...............
                00007ffe`bffc53d0  30 51 bd af 54 02 00 00-90 b6 bd af 54 02 00 00  0Q..T.......T...
                00007ffe`bffc53e0  40 51 bd af 54 02 00 00-a0 b6 bd af 54 02 00 00  @Q..T.......T...
                00007ffe`bffc53f0  80 4f bd af 54 02 00 00-b0 b6 bd af 54 02 00 00  .O..T.......T...

                db @$extret l40 
                00000254`afbd5120  00 00 00 00 00 00 00 00-7e 8a 1e fe db af 00 30  ........~......0
                00000254`afbd5130  60 4f bd af 54 02 00 00-d0 53 fc bf fe 7f 00 00  `O..T....S......
                00000254`afbd5140  70 4f bd af 54 02 00 00-e0 53 fc bf fe 7f 00 00  pO..T....S......
                00000254`afbd5150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

                db @$extret l40 
                00000254`afbd4f50  ee fe ee fe ee fe ee fe-7e 8a 1e fe d9 af 00 30  ........~......0
                00000254`afbd4f60  80 56 bd af 54 02 00 00-30 51 bd af 54 02 00 00  .V..T...0Q..T...
                00000254`afbd4f70  90 56 bd af 54 02 00 00-40 51 bd af 54 02 00 00  .V..T...@Q..T...
                00000254`afbd4f80  60 5d bd af 54 02 00 00-f0 53 fc bf fe 7f 00 00  `]..T....S......
    30.17 调用目标程序的函数
        30.17.1 调用实例
            windbg dbgee.exe
            bp wmain
            g
            ?poi(rdx)
                *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\System32\ADVAPI32.DLL - 
                Evaluate expression: 2450935962848 = 0000023a`a72adce0
                从字符串数组argv中,获取第一个字符串的地址
                    即argv[0]的值,*(argv+0),poi(argv)
                由于argv存放在rdx,由于_fastcall调用约定
                windbg使用C++表达式评估器,来解析函数参数
            .call dbgee!wprintf(0x23aa72adce0)
                Value unavailable error for _Format
                Value unavailable error for _Result
                Value unavailable error for _Stream
                Value unavailable error for _Format
                Thread is set up for call, 'g' will execute.
                WARNING: This can have serious side-effects,
                including deadlocks and corruption of the debuggee.
                设置函数调用
            ~.g
                System 0: 3 of 4 threads are frozen
                System 0: 3 of 4 threads were frozen
                .call returns:
                int 0n9

                dbgee!wmain:
                00007ff7`aa911000 48895c2408      mov     qword ptr [rsp+8],rbx ss:000000ee`bdbff780=0000000000000000
                执行函数调用
        30.17.2 工作原理
            方法
                修改线程的执行环境,使调用指定函数
                调用完成后,恢复线程的执行环境
            方式
                在栈顶写入,函数返回后执行的代码
                    用于再次中断到调试器,以恢复线程的执行环境                    
                在栈顶建立新栈帧
                    为子函数开辟空间
                    入栈参数和返回地址(指向新写入的代码)
                修改程序指针,指向指定函数入口
            例子
                .call执行前
                    k
                        Child-SP          RetAddr           Call Site
                        000000ee`bdbff778 00007ff7`aa911438 dbgee!wmain [g:\user\vs2015\test\dbgee\dbgee.cpp @ 10]
                        000000ee`bdbff780 00007ffe`be177bd4 dbgee!__scrt_common_main_seh+0x124 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 264]
                        000000ee`bdbff7c0 00007ffe`bfecce71 KERNEL32!BaseThreadInitThunk+0x14
                        000000ee`bdbff7f0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
                    r
                        rax=0000023aa72b3c00 rbx=00007ffebcf5b590 rcx=0000000000000001
                        rdx=0000023aa72adcd0 rsi=0000000000000000 rdi=00007ffebcf5c000
                        rip=00007ff7aa911000 rsp=000000eebdbff778 rbp=0000000000000000
                        r8=0000023aa72b3c00  r9=00007ffebd031ec0 r10=0000000000000013
                        r11=0000023aa72a8040 r12=0000000000000000 r13=0000000000000000
                        r14=0000000000000000 r15=0000000000000000
                        iopl=0         nv up ei pl nz na po nc
                        cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
                        dbgee!wmain:
                        00007ff7`aa911000 48895c2408      mov     qword ptr [rsp+8],rbx ss:000000ee`bdbff780=0000000000000000
                    db esp
                        000000ee`bdbff778  38 14 91 aa f7 7f 00 00-00 00 00 00 00 00 00 00  8...............
                        000000ee`bdbff788  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000ee`bdbff798  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000ee`bdbff7a8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000ee`bdbff7b8  d4 7b 17 be fe 7f 00 00-00 00 00 00 00 00 00 00  .{..............
                        000000ee`bdbff7c8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000ee`bdbff7d8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000ee`bdbff7e8  71 ce ec bf fe 7f 00 00-00 00 00 00 00 00 00 00  q...............
                .call dbgee!wprintf(0x23aa72adce0)
                    k
                        Child-SP          RetAddr           Call Site
                        000000ee`bdbff748 000000ee`bdbff770 dbgee!wprintf [c:\program files (x86)\windows kits\10\include\10.0.10150.0\ucrt\corecrt_wstdio.h @ 609]
                        000000ee`bdbff750 0000023a`a72adce0 0xee`bdbff770
                        000000ee`bdbff758 0000023a`a72adcd0 0x23a`a72adce0
                        000000ee`bdbff760 0000023a`a72b3c00 0x23a`a72adcd0
                        000000ee`bdbff768 00007ffe`bd031ebf 0x23a`a72b3c00
                        000000ee`bdbff770 cccccccc`ccfdebcc KERNELBASE!BasepLoadLibraryAsDataFileInternal+0x6bf
                        000000ee`bdbff778 00007ff7`aa911438 0xcccccccc`ccfdebcc
                        000000ee`bdbff780 00007ffe`be177bd4 dbgee!__scrt_common_main_seh+0x124 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 264]
                        000000ee`bdbff7c0 00007ffe`bfecce71 KERNEL32!BaseThreadInitThunk+0x14
                        000000ee`bdbff7f0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
                    r
                        rax=0000023aa72b3c00 rbx=00007ffebcf5b590 rcx=0000023aa72adce0
                        rdx=0000023aa72adcd0 rsi=0000000000000000 rdi=00007ffebcf5c000
                        rip=00007ff7aa9110d0 rsp=000000eebdbff748 rbp=0000000000000000
                        r8=0000023aa72b3c00  r9=00007ffebd031ec0 r10=0000000000000013
                        r11=0000023aa72a8040 r12=0000000000000000 r13=0000000000000000
                        r14=0000000000000000 r15=0000000000000000
                        iopl=0         nv up ei pl nz na po nc
                        cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
                        dbgee!wprintf:
                        00007ff7`aa9110d0 48894c2408      mov     qword ptr [rsp+8],rcx ss:000000ee`bdbff750=0000023aa72adce0
                    db esp
                        000000ee`bdbff748  70 f7 bf bd ee 00 00 00-e0 dc 2a a7 3a 02 00 00  p.........*.:...
                        000000ee`bdbff758  d0 dc 2a a7 3a 02 00 00-00 3c 2b a7 3a 02 00 00  ..*.:....<+.:...
                        000000ee`bdbff768  c0 1e 03 bd fe 7f 00 00-cc eb fd cc cc cc cc cc  ................
                        000000ee`bdbff778  38 14 91 aa f7 7f 00 00-00 00 00 00 00 00 00 00  8...............
                        000000ee`bdbff788  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000ee`bdbff798  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000ee`bdbff7a8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
                        000000ee`bdbff7b8  d4 7b 17 be fe 7f 00 00-00 00 00 00 00 00 00 00  .{..............
                查看变化
                    栈变化
                        db esp
                        000000ee`bdbff748  70 f7 bf bd ee 00 00 00-e0 dc 2a a7 3a 02 00 00  p.........*.:...
                        000000ee`bdbff758  d0 dc 2a a7 3a 02 00 00-00 3c 2b a7 3a 02 00 00  ..*.:....<+.:...
                        000000ee`bdbff768  c0 1e 03 bd fe 7f 00 00-cc eb fd cc cc cc cc cc  ................
                        dq esp
                        000000ee`bdbff748  000000ee`bdbff770 0000023a`a72adce0
                        000000ee`bdbff758  0000023a`a72adcd0 0000023a`a72b3c00
                        000000ee`bdbff768  00007ffe`bd031ec0 cccccccc`ccfdebcc
                    新栈帧
                        返回地址,000000ee`bdbff770
                        参数,放在rcx中
                        为子函数开辟空间
                            u dbgee!wprintf
                                dbgee!wprintf [c:\program files (x86)\windows kits\10\include\10.0.10150.0\ucrt\corecrt_wstdio.h @ 609]:
                                00007ff7`aa9110d0 48894c2408      mov     qword ptr [rsp+8],rcx
                                00007ff7`aa9110d5 4889542410      mov     qword ptr [rsp+10h],rdx
                                00007ff7`aa9110da 4c89442418      mov     qword ptr [rsp+18h],r8
                                00007ff7`aa9110df 4c894c2420      mov     qword ptr [rsp+20h],r9
                            长度0x20
                    查看函数返回后的执行代码
                        u 000000ee`bdbff770
                            000000ee`bdbff770 cc              int     3
                            000000ee`bdbff771 ebfd            jmp     000000ee`bdbff770
                            000000ee`bdbff773 cc              int     3
                            000000ee`bdbff773 cc              int     3
                            000000ee`bdbff774 cc              int     3
                            000000ee`bdbff775 cc              int     3
                            000000ee`bdbff776 cc              int     3
                            000000ee`bdbff777 cc              int     3
                        一共八字节,以对齐
                    寄存器
                        程序指针,rip
                            原
                                rip=00007ff7aa911000
                                dbgee!wmain:
                                00007ff7`aa911000 48895c2408      mov     qword ptr [rsp+8],rbx ss:000000ee`bdbff780=0000000000000000
                            新
                                rip=00007ff7aa9110d0
                                dbgee!wprintf:
                                00007ff7`aa9110d0 48894c2408      mov     qword ptr [rsp+8],rcx ss:000000ee`bdbff750=0000023aa72adce0
                        栈指针,rsp
                执行指定函数
                    ~.g
                    System 0: 3 of 4 threads are frozen
                    System 0: 3 of 4 threads were frozen
                    .call returns:
                    int 0n9

                    dbgee!wmain:
                    00007ff7`aa911000 48895c2408      mov     qword ptr [rsp+8],rbx ss:000000ee`bdbff780=0000000000000000
                查看变化
                    k,恢复
                    r,恢复
                    db esp,恢复
        30.17.3 限制条件和常见错误
            .call命令只能调试,用户态活动目标
            被调函数,有私有符号,即包含类型信息的函数符号
            每个线程一次只能设置一个函数,设置后,要么取消,要么执行
                .call /C,清除当前线程设置的函数
    30.18 命令程序
        30.18.0 概述
            调试器命令程序,Debuger Command Program
                概念,调试器命令集合,形成的文件
                组成
                    调试器命令,标准命令、元命令、扩展命令
                    流程控制符号
                    变量
        30.18.1 流程控制符号(control flow token)
            分支
                .if, .else, .elseif
            循环
                .do, .while, !for_each_xxx, .break, .continue
            异常
                .catch,捕获异常
                .leave,退出.catch块
            代码块
                .block
        30.18.2 变量
            分类
                伪寄存器
                    自动的伪寄存器
                        由windbg定义和赋值
                        如$ip
                    用户赋值的伪寄存器
                        $t0~$t19
                        指定为整数类型并赋值
                            r $txx = expression
                        指定为值的类型并赋值
                            r? $txx = expression
                别名
                    用户定义的别名
                        as,增加别名
                        ad,删除别名
                    自动别名
                        $ntsym、$CurrentDump等
                    固定名称的别名
                        $u0~$u9
                        定义,r $.ux = expression
            引用
                伪寄存器
                    MASM表达式评估器,@$xxx或$xxx
                    C++表达式评估器,@$xxx
                别名
                    ${$xxx}
        30.18.3 命令程序示例
            代码
                $$ Get module list LIST_ENTRY in $t0
                r? $t0 = &@$peb->Ldr->InMemoryOrderModuleList

                $$ Iterate over all module in list
                .for (r? $t1 = *(ntdll!_LDR_DATA_TABLE_ENTRY**)@$t0;
                     (@$t1 != 0) & (@$t1 != @$t0);
                     r? $t1 = *(ntdll!_LDR_DATA_TABLE_ENTRY**)@$t1->InLoadOrderLinks.Flink)
                {
                    $$ Get base address in $Base
                    as /x ${/v:$Base} @@c++(@$t1->DllBase)

                    $$ Get full name into $Mode
                    as /msu ${/v:$Mod} @@c++(&@$t1->FullDllName)

                    .block
                    {
                        .echo ${$Mod} at ${$Base}
                    }

                    ad ${/v:$Base}
                    ad ${/v:$Mod}
                 }
            解释
                $$和*,行注释
                    $$也可以分号结束注释
                    常用$$,因为将程序合并成一行时,换行符会被替换成分号
                        使用$$可以正常注释
                        使用*不能正常注释
                当前进程_PEB结构的地址,使用伪寄存器$peb表示
                r? $t0 = &@$peb->Ldr->InMemoryOrderModuleList
                    $t0,存储地址,指向@$peb->Ldr->InMemoryOrderModuleList
                    dt -r2 _PEB
                        dbgee!_PEB
                        +0x000 Reserved1        : [2] UChar
                        +0x002 BeingDebugged    : UChar
                        +0x003 Reserved2        : [1] UChar
                        +0x008 Reserved3        : [2] Ptr64 Void
                        +0x018 Ldr              : Ptr64 _PEB_LDR_DATA
                            +0x000 Reserved1        : [8] UChar
                            +0x008 Reserved2        : [3] Ptr64 Void
                            +0x020 InMemoryOrderModuleList : _LIST_ENTRY
                                +0x000 Flink            : Ptr64 _LIST_ENTRY
                                +0x008 Blink            : Ptr64 _LIST_ENTRY
                r? $t1 = *(ntdll!_LDR_DATA_TABLE_ENTRY**)@$t0;
                    $t1,存储地址,与$t0相同
                        $t0,指向_LIST_ENTRY,位于_PEB_LDR_DATA中
                        $t1,指向_LIST_ENTRY,位于_LDR_DATA_TABLE_ENTRY中
                            也是指向_LDR_DATA_TABLE_ENTRY
                    dt ntdll!_LDR_DATA_TABLE_ENTRY
                        +0x000 InLoadOrderLinks : _LIST_ENTRY
                        +0x010 InMemoryOrderLinks : _LIST_ENTRY
                        +0x020 InInitializationOrderLinks : _LIST_ENTRY
                        +0x030 DllBase          : Ptr64 Void
                        +0x038 EntryPoint       : Ptr64 Void
                        +0x040 SizeOfImage      : Uint4B
                        +0x048 FullDllName      : _UNICODE_STRING
                        +0x058 BaseDllName      : _UNICODE_STRING
                (@$t1 != 0) & (@$t1 != @$t0);
                    链表遍历,判断结束
                r? $t1 = *(ntdll!_LDR_DATA_TABLE_ENTRY**)@$t1->InLoadOrderLinks.Flink)
                    获取下一节点的地址
                as /x ${/v:$Base} @@c++(@$t1->DllBase)
                    /x,取表达式64位值
                    /v,阻止别名替换,不管其是否已经定义
                    @@c++,强制使用C++表达式评估器
                as /msu ${/v:$Mod} @@c++(&@$t1->FullDllName)
                    /msu,使别名等价于地址表达式指向的UNICODE_STRING
                .block;{;.echo ${$Mod} at ${$Base};}
                    强制评估别名并回显
        30.18.4 执行命令程序
            $<Filename 
                读一行,执行一行
                若一行中命令不完整,则会出错
            $><Filename
                合并程序成一行(分号替换换行符),然后执行 
            $$< Filename 
                同"$$<Filename",但允许文件名前加空格,使用双引号包围文件名
            $$>< Filename 
                同"$$><Filename",但允许文件名前加空格,使用双引号包围文件名
            $$>a< Filename [arg1 arg2 arg3 ... ] 
                支持参数
    30.19 本章总结
        本章内容,介绍调试任务所需的一般知识
        是对windbg帮助文档的补充
            对帮助文档进行了归纳和浓缩,便于入门
            介绍帮助文档中较少或较难的内容
        windbg中的文档
            debugger.chm
                根目录
                windbg帮助文档
            kernel_debugging_tutorial.doc
                根目录
                介绍内核调试
            symhttp.doc
                symproxy子目录
                介绍如何建立符号服务器
            srcsrv.doc
                srcsrv子目录
                介绍源文件服务器的概况以及如何建立和配置源文件服务器
            dml.doc
                根目录
                介绍DML(Debugger Markup Language)的用途和编写方法
                DML是一种标记语言,用于标记windbg或扩展命令的信息输出
            themes.doc
                themes子目录
                介绍了主题(theme)的概念
                    一个主题,代表一套特定风格的界面布局和工作空间配置
                介绍了如何加载及使用该目录中的四套主题配置
            adplus.doc
            pooltag.txt
                triage子目录
                包含了Windows内核模块和驱动程序所使用的内存分配标记(Pool Tag)
                在启用了Windows操作系统的内存池标记(Pool Tagging)功能后,系统会为每个内存块维护一个分配标记,以标识其使用者
                用于显示内存池使用情况的扩展命令!poolused,就是使用该文件来查找每个分配标记对应的模块

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows平台上,有许多常用的开发与调试工具可供使用。其中一些工具包括: 1. Sysinternals:这是一个由内核专家马克·拉斯科维茨(Mark Russinovich)开发的工具集合,包含了大量的Windows系统工具,是Windows开发必备的工具之一。其中一些工具包括: - Procmon.exe:用于监视程序运行过程中的动作,可用于性能监控。 - procexp.exe:类似于任务管理器,但功能更强大,可以查看加载模块、线程列表、创建dump等。 - autoruns.exe:用于查看系统和IE等的加载项。 - Dbgview.exe:用于查看调试端口输出。 2. 其他工具:除了Sysinternals之外,还有其他一些常用的工具,如: - Windbg:用于双机调试,支持pipe、TCP等。 - Visual Studio(VS):VS也支持双击调试,只需要拷贝一个东西到目标机上。 - IDA:主要用于静态分析。 - Ollydbg:用于反汇编和调试。 - Processhacker:作为Procexp.exe的补充工具。 - Total Uninstall:用于观察应用程序对系统配置等的改变,比如对比注册表。 - Unlocker:用于解除文件占用。 - Depends.exe:用于观察模块对DLL的依赖关系。 - PE Explorer:用于PE文件分析。 - SQLiteAdmin:用于查看SQLite数据库。 - Cookie Admin:用于查看cookie。 3. 抓包和网络数据分析:对于抓包和网络数据分析,可以使用以下工具: - Microsoft Network Monitor - Fiddler - Wireshark - HttpAnalyzer 此外,还有一些调试必读的书目,例如张银奎的《软件调试》,以及一些调试参考书目,如《Windows高级调试》、《黑客反汇编揭秘》、《C反汇编与逆向分析技术揭秘》、《Windows核心编程》、《深入理解Windows操作系统》、《Windows内核情景分析》、《逆向工程核心原理》。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [windows客户端开发调试工具](https://blog.csdn.net/baihacker/article/details/38308331)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Windows下的TCP/UDP网络调试工具-NetAssist以及Linux下的nc网络调试工具](https://blog.csdn.net/beiback/article/details/125590011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值