• Windbg 脚本命令简介 二, Windbg command


     

    Windbg  脚本命令简介 二, Windbg  script command

     

     

    $<, $><, $$<, $$><, $$>a< (Run Script File)

    Sample1.txt文件内容:

    .echo The first argument is ${$arg1}.
    
    .echo The fifth argument is ${$arg5}.
    
    .echo The fourth argument is ${$arg4}.
    0:000> $$>a<"E:.net Memory Windbg drill intoscriptssample1.txt"  abc  "I am ok" "how" "are you" ;rrax
    
    The first argument is abc.
    
    The fifth argument is ${$arg5}.
    
    The fourth argument is are you.
    
    rax=000007fefb309d01

    脚本路径中带空格,需要加引号,参数中带空格,需要加引号,参数间以空格分开,分号是命令的分割,后面的rrax是第二个命令,而不是第五个参数,rrax或 r rax是显示rax寄存器的值。

    第五个参数没有传入,则显示 “${$arg5}” 这个字面值

    Token

    是否允许文件名中带分号

    是否链接以分号分隔的其他命令

    是否压缩到单一行的命令块

    是否允许带参数

    $<

    Yes

    No

    No

    No

    $><

    Yes

    No

    Yes

    No

    $$<

    No

    Yes

    No

    No

    $$><

    No

    Yes

    Yes

    No

    $$>a<

    No

    Yes

    Yes

    Yes

    1.把所有语句写在脚本文件的单行中,每个语句和命令之间用分号隔开,然后用$><命令去运行脚本

    2.在脚本文件中输入所有语句,每条语句占一行,然后使用$><命令运行脚本,这些命令会打开脚本文件,用分号替换所有的回车符,然后把结果文件当作单一的命令块执行.

    如果文件名中带分号,则后面就不允许带上以分号分隔的命令了。但有一种例外,就是

    Sample1.txt:

    .echo the first arg:${$arg1}
    
    .echo I am working
    
    kb
    
    !dumpheap -type HttpRuntime
    0:000> $$>< c:sample1.txt ;rrax

    ~s (Set Current Thread)

    设置当前线程:

    例如 ~2s,则切换到2号线程 提示变为:0:002>

    0:002> ~#s:切换到默认线程,提示变为:0:000>

    默认线程是dump时正在工作的线程,如果是Crash,则最后发生crash的线程是当前工作线程。

    Using Aliases

    使用别名:

    Alias=Expression

    as aS:设置别名,Alias Set

    ad:删除别名 Alias Delete

    al:列出所有别名 Aliases List

    固定名字的内置别名:$u0$u1, ..., $u9

    内置系统别名:大小写敏感

    0:000> .echo $ntnsym
    
    Ntdll
    
    0:000> .echo $CurrentDumpFile
    
    E:Ccppt.comSVR858AppPoolHotel2AppPoolHotel2.DMP
    
    0:000> $CurrentDumpArchivePath
    
    E:Ccppt.comSVR858AppPoolHotel2AppPoolHotel2.DMP
    
     
    
    0:000> as my 5+1
    
    0:000> .echo my
    
    5+1

    as, aS (Set Alias)

    0:000> aS /x myAlias 5 + 1; .block{.echo myAlias}

    //设置myAlias为64bit的 6(5+1结果)

    l  如果不用 任何开关,as 会认为该行后面的都是表达式。

             aS myAlias 5 + 1; .block{.echo myAlias}

    new line 输入:.echo myAlias //windbg崩溃

    如果不用 .block { },输出有问题,因为别名的值代入(展开别名的内容)发生在进入一个新的代码块时,使用 .block {} 使得产生一个新的代码block,.echo myAlias在新代码块中,则myAlias已经展开。
    0:001> aS /x myAlias 5 + 1; .echo myAlias
    myAlias
    0:000> as /c my !eeversion
    
    0:000> .echo my
    
    4.0.30319.296 free
    
    Server mode with 8 gc heaps
    
    SOS Version: 4.0.30319.586 retail build

    //等价于直接命令 !eeversion

    C:Sample1.txt

    .echo the first arg:${$arg1}
    
    .echo I am working
    
    kb
    
    !dumpheap -type HttpRuntime
    0:000> aS /f my c:sample1.txt
    
    0:000> .echo my
    
    .echo the first arg:${$arg1}
    
    .echo I am working
    
    kb
    
    !dumpheap -type HttpRuntime

    //将别名设置为文件内容

    0:000> ad my
    
              ^ No information found error in 'ad my'
    
    0:000> ad /q my //删除my别名,/q表示静默模式,不提示错误
    
    0:000> ad /q * //删除所有别名

    0:000> al

      Alias            Value 

     -------          -------

     my               0x5

    // 列出所有别名

    ${ } (Alias Interpreter) 别名解释器

    Text ${Alias} Text 
     
    Text ${/d:Alias} Text   
    //根据别名当前是否已经定义计算出1或者0
     
    Text ${/f:Alias} Text   
    //如果别名当前已定义则等同于计算别名。如果别名已经定义,${/f:Alias} 被替换为等价的别名;如果别名没有定义,${/f:Alias}被替换为空字符串
     
    Text ${/n:Alias} Text 
    //如果别名当前已定义则计算别名的名称。如果别名已经定义,${/n:Alias}被替换为别名名称;如果别名没有定义,${/n:Alias}保留它字面上的值不替换
     
    Text ${/v:Alias} Text 

    //禁止对任何别名求值。不论别名是否已经定义,${/v:Alias} 总是保持它字面上的值

    0:000> as /x my 5+1
    
    0:000> .echo my
    
    0x6
    
    0:000> .echo i am myself
    
    i am myself
    
    0:000> .echo i am ${my}self //不需要空格
    
    i am 0x6self
    
    0:000> .echo i am my self  //需要有空格
    
    i am 0x6 self
    
    0:000> .echo ${/d:my}  // my被定义了,输出1,如果my没定义,则输出0
    
    1
    
    0:000> .echo ${/f:my}  //解析别名
    
    0x6
    
    0:000> .echo ${/n:my}  //解析别名
    
    my
    
    0:000> .echo ${/v:my}  //永不解析,永输出字面值
    
    ${/v:my}
    
    0:000> ad /q *   //删除所有别名
    
    0:000> .echo ${/d:my}
    
    0
    
    0:000> .echo ${/f:my}  //没有定义别名,输出空字符串
    
     
    
    0:000> .echo ${/n:my} //没有定义别名,输出字面值
    
    ${/n:my}
    
    0:000> .echo ${/v:my} //永不解析,永输出字面值
    
    ${/v:my}

    Control Flow Tokens

    .if

    $$ 定义一个变量
    
    as /x my 4
    
     
    
    .if (${my}>5)
    
      {
    
           .printf "${my}";
    
      }
    
    .elsif (…..)
    
    {
    
    }
    
    .else
    
      {
    
           .printf "my value is not greater than 5";
    
      }

    .foreach

    0:000> .foreach /pS 2 /ps 4 /f ( place "d:myfile.txt") { dds place }
    00000000`ffa9fc48  f37c5888
    00000000`ffa9fc4c  000007fe
    00000000`ffa9fc50  00000000
    00000000`ffa9fc54  00000000
    …..

    d:myfile.txt的内容是:

    00000000ffa50c98
    
    00000000ffa9f9d8
    
    00000000ffa9fc48

    /pS:循环开始前,初始跳过的个数。

    /ps:  循环中,每次迭代跳过的个数。

    /f: 来自文件。

    这里循环中 place的值是每次等于myfile.txt的一行,输出第一个是myfile.txt 第三行,跳过前面2行,第二次是跳过4行,取第8行,依次13行、18行、23行。

    .foreach [Options] ( Variable  { InCommands } ) { OutCommands } 
    //从命令中获取输入
    .foreach [Options] /s ( Variable  "InString" ) { OutCommands } 
    //从string中获取输入
    .foreach [Options] /f ( Variable  "InFile" ) { OutCommands }

    //从文件中获取输入

    .block

    Commands ; .block { Commands } ; Commands 

    .printf

    类似c语言的 printf

    0:000> .printf /D "Click <link cmd=".chain /D">here</link> to see extensions DLLs."
    
    Click here to see extensions DLLs.0:000> .chain /D
    
     
    
    /D DML debug xml格式输出

    .catch{ }

    Commands ; .catch { Commands } ; Commands 

    类似.block 块,在catch中的command遇到错误时,catch块中的其他command不会执行,但花括号外面的紧接着catch块的代码还会继续执行。

    如果不用.catch { } 整个commands都会停止执行。

    .break .continue .leave

    .catch { ... ; .if (Condition) .leave ; ... } 

    .leave :离开 catch块

    .break //跳出循环

    .for (...) { ... ; .if (Condition) .break ; ...} 
     
    .while (...) { ... ; .if (Condition) .break ; ...} 
     
    .do { ... ; .if (Condition) .break ; ...} (...) 

    .continue //继续下一次循环

    .for (...) { ... ; .if (Condition) .continue ; ... } 
     
    .while (...) { ... ; .if (Condition) .continue ; ... } 
     
    .do { ... ; .if (Condition) .continue ; ... } (...) 

    $$ (Comment)

    注释 comments

    r eax; $$ some text; r ebx; * more text; r ecx 
    // * 号后的行内 内容全部是注释
    r eax; $$ some text; r ebx; * more text; r ecx 
    // $$ 后 ; 前的行内 内容是注释。
    
    // 黄色内容会执行

    Pseudo-Register Syntax

    (伪寄存器)

    http://msdn.microsoft.com/en-us/library/ff553485(v=vs.85).aspx

    许多寄存器的名字取决于处理器的架构, 因此对于那些偶尔使用调试器的用户来说很难记住所有平台上的寄存器名字. 为了克服这个问题, 调试器的开发团队引入了各种伪寄存器(Pseudo-Register), 由调试器将这些伪寄存器对应到不同的硬件架构上.

    例如:

    • $ip: 指令指针寄存器;
      • x86上, $ip = eip
      • x64上, $ip = rip
      • Itanium上, $ip = iip

    所有伪寄存器都以$符号开始,如果使用MASM(微软宏汇编语言),可以在$符号前加一个@符号。这个@符号告诉调试器,这是一个(伪)寄存器,它不是一个symbol符号。不需要到符号表里去查找这个符号。这样速度会快很多。

    0:000> ? $exp
    *** ERROR: Module load completed but symbols could not be loaded for …
    
    Evaluate expression: 143 = 0000008f
    0:000> ? @$exp
    Evaluate expression: 143 = 0000008f

    这两个命令是一样的,但是第一个要慢很多,它会查找symbol,有时候会看到这样的提示:

    *** ERROR: Module load completed but symbols could not be loaded for

    说明它要查找symbol符号文件去找这个符号。

    如果有一个符号跟这个伪寄存器的名字一样,那这里的伪寄存器必须你@符号必须添加。

    如果调试器使用了C++的表达式结构,则这里的@符号也是必须的。调试器默认使用C++表达式结构。

    0:000> r $t1 = @$t2 //将 $t2伪寄存器拷贝到$t1

    User-Defined Pseudo-Registers

    There are 20 user-defined pseudo-registers ($t0$t1, ..., $t19). These pseudo-register are variables that you can read and write through the debugger. You can store any integer value in these pseudo-registers. They can be especially useful as loop variables.

    使用r来写值

    例子:

    0:000> r $t0=7 //赋值
    0:000> r $t1=128
    0:000> ? @$t0+@$t1 //计算,MASM表达式结构
    Evaluate expression: 135 = 00000000`00000087
    0:000> ?? @$t0+@$t1 //计算,C++表达式结构
    unsigned int64 0n135 //10进制显示
    0:000> n
    base is 10
    0:000> n 16 //换进制
    base is 16
    0:000> ?? @$t0+@$t1
    unsigned int64 0x87 //16进制显示
     
    
     
    
    0:000> r? $t15 = * (UNICODE_STRING*) 0x12ffbc

    伪寄存器的值总是Integer.可以使用 r 带上 ? 开关,并传入类型,这里的例子是将UNICODE_STRING**类型的0x12ffbc传入$t15伪寄存器。

    kd> bp /t @$thread nt!ntopenfile

    在当前线程的 ntopenfile函数上设置断点,每次当前线程调用该函数时都会触发断点,但其他线程运行该函数时不会触发断点。 $thread 是自动生成的系统伪寄存器。

    .shell (Command Shell)

    .shell -i InFile [-o OutFile [-e ErrFile]] [Options] ShellCommand

    .shell -i - -ci "!do 0b62e790" FIND /c /i "<table" :说明,-i: 从文件输入,

                                                                                 - : 取消从文件输入

                                                                                 -ci:从后面的命令输出作为输入

                                                                            Find: Dos 命令查找 <table 这个字符串

    “-”代替InFile,表示没有输入文件,with no space before the hyphen.

        “-”代替OutFile,表示没有输出,如果没有“-o OutFile”表示默认输出到Debug窗口, with no space before the hyphen.

           -e ErrFile 类似

    -ci "Commands"

    将 debug的命令作为输入,多个命令以“;”分号分隔。

    .shell命令之后的行内元素都被看作是windows shell命令,而不是debug命令,即使这些命令以分号分隔。Windows shell命令不应该以引号括起来。

     exit 或 .shell_quit 是退出shell模式, .shell 后面不跟参数则windbg进入shell模式。

    .shell -i - -ci "~* e!clrstack" FIND  /I "Monitor.Enter"

    //查找所有线程里的堆栈上是否有包含 Monitor.Enter的函数。一般可以用来看死锁。

    .shell -ci "~* e!clrstack" python.exe "E:Ccppt.com.net Memory Windbg drill intoscriptspy.py"
    py.py
    
    import sys
    
     
    
    for y in (0,1,2,3):
    
           print (y)
    
     
    
    for i in sys.argv[1:]:
    
           print (i)

    .writemem

    实例中,通过lm命令列出内存中加载的模块列表,然后通过.writemem命令将其中的模块存成文件。

    0:000> lm

    start    end        module name

    010a0000 01124000   fibo     C (private pdb symbols) 

    75270000 753a0000   kernel32   (deferred)            

    754a0000 75546000   KERNELBASE   (deferred)            

    774a0000 775f7000   ntdll      (export symbols)       ntdll.dll

    0:000> ? 01124000-010a0000             //计算长度

    Evaluate expression: 540672 = 00084000

    0:000> .writemem c:fibo.exe 010a0000 L 00084000

    Writing 84000 bytes........................................................................................................................................................................................................................................................................

    0:000> .writemem c:fibo.exe 010a0000 (01124000-0x1)

    Writing 84000 bytes........................................................................................................................................................................................................................................................................

    .writemem可以通过以上两种方式存储文件,第一种是指定起始地址和长度,第二种是指定其实和终止地址。

    参考资料:

    http://hi.baidu.com/123_mu_tou_ren/item/62e2cf0590bb2b25a0312d16

    http://hi.baidu.com/ju_feng/item/c61ab23d788c070bcfb9fe64

    http://blogs.msdn.com/b/tess/archive/2007/09/12/debugging-script-dumping-out-current-and-recent-asp-net-requests.aspx

  • 相关阅读:
    JENKINS安装及布署
    Python的函数返回多值其实就是返回一个tuple!
    函数参数类型
    软件配置管理工作范围
    PYTHON学习:关键字raise
    Java绘图: 使用 Graphics 类绘制线段、矩形、椭圆/圆弧/扇形、图片、文本
    An error occurred while updating the entries. See the inner exception for details.
    SQL 查询 VS LinQ
    Vue3.x中使用 elementplus 的各种方式
    C# Cannot insert explicit value for identity column in table 'ServiceOrderReceipt' when IDENTITY_INSERT is set to OFF.
  • 原文地址:https://www.cnblogs.com/softfair/p/windbg-command-ii.html
Copyright © 2020-2023  润新知