Windbg命令脚本一文里,我们介绍了命令脚本语言的的组成要素,在本文里将对语句进行展开的讲解。这些语句主要是流程控制的语句,比如我们常见的条件分子和循环语句等。

; (命令分隔符)

分号(;)字符用于在一行中分隔多个命令。

Command1 ; Command2 [; Command3 ...]

参数

命令1,命令2,…

要执行的命令。

命令从左到右依次执行。除非另有规定,否则单行上的所有命令都引用当前线程。如果命令导致线程执行,则该行上的其余命令将被延迟,直到该线程在调试事件上停止。少数命令后面不能跟分号,因为它们会自动将行的整个剩余部分作为参数。其中包括as(set alias)、$<(run script file)、$><(run script file)和以*(comment line specifier)标记开头的任何命令。

下面是一个例子。这将执行当前程序到源代码行123,打印计数器的值,然后继续执行:
0:000> g `:123`; ? poi(counter); g

{ } (块分割符)

 一对大括号({})用于包围调试器命令程序中的语句块
Statements { Statements } Statements

输入每个块后,将计算块中的所有别名。如果在命令块中的某个点更改别名的值,则该点后面的命令将不会使用新的别名值,除非它们位于从属块中。每个块必须以控制流标记开头。如果您希望创建一个仅用于评估别名的块,则应在其前面加上.block标记。

${ } (别名解释器)

后面跟一对大括号(${})的美元符号计算出与指定的用户别名相关的各种值。

Text ${Alias} Text
Text ${/d:Alias} Text
Text ${/f:Alias} Text
Text ${/n:Alias} Text
Text ${/v:Alias} Text

参数

Alias

指定要展开或计算的别名的名称。 别名必须是用户命名别名或变量使用值.foreach语句

/d

计算结果为一个或零个具体情况取决于是否当前定义别名。 如果定义别名,则 ${/ v 部分:别名} 替换为 1; 如果未定义别名, ${/ v 部分:别名} 替换为 0。

/f
 如果当前定义了别名,则计算为等效别名。如果定义了别名,${/f:alias}将替换为别名等效项;如果未定义别名,${/f:alias}将替换为空字符串。
 
/n

如果当前定义了别名,则计算为别名名称。如果定义了别名,${/n:alias}将被别名替换;如果未定义别名,${/n:alias}将不被替换,但保留其文字值${/n:alias}。

/v

阻止任何别名计算。无论是否定义了别名,${/v:alias}始终保留其文本值${/v:alias}。

如果没有使用任何开关,并且当前定义了别名,${Alias}将替换为别名等效项。如果没有使用任何开关且未定义别名,${Alias}始终保留其文字值${Alias}。使用${}标记的一个优点是,即使别名与其他字符相邻,也将对其进行评估。如果没有这个标记,调试器只替换别名,这些别名与其他标记之间用空格分隔。如前所述,在某些情况下,标记不会被任何内容替换,而是保留其文字值。当不使用开关且别名未定义时、使用/n开关且别名未定义时以及使用/v开关时,都会发生这种情况。在这种情况下,语句保留其面值,包括$符号和大括号。因此,如果将其用作命令的参数,将导致语法错误,除非该参数接受任意文本字符串。然而,有一个例外。如果使用${/v:alias}作为as(set alias)或as(set alias)命令的第一个参数,则此令牌将单独被视为字符串别名,而不是字符串${/v:alias}。这只适用于as、as和ad命令,并且仅在使用/v开关时才有效。当它们保留其文本值时,它将不适用于${/n:alias}或${alias}。别名必须是名为alias的用户或.foreach标记使用的变量值,而不是固定名称别名。如果字符串别名中存在固定名称别名,则在评估${}标记之前将替换该别名。

$$ (注释说明符)

 如果命令开头出现两个美元符号($$),则该行的其余部分将被视为注释,除非注释以分号结尾。
$$ [any text]
与任何其他调试器命令一样,对$$标记进行分析。因此,如果要在另一个命令之后创建注释,则必须在$$标记前面加上分号。$$标记将导致忽略后面的文本,直到行尾或遇到分号为止。分号终止注释;分号后的文本被解析为标准命令。这与*(注释行说明符)不同,后者使行的其余部分成为注释,即使存在分号。
例如,以下命令将显示EAX和EBX,但不显示ECX:

0:000> r eax; $$ some text; r ebx; * more text; r ecx

不以任何方式处理前缀为*或$$标记的文本。如果正在执行远程调试,则在调试服务器中输入的注释在调试客户端中将不可见,反之亦然。如果希望使注释文本以所有参与方都可见的方式显示在调试器命令窗口中,则应使用.echo(echo comment)。

* (注释行说明符)

如果星号(*)字符位于命令的开头,则行的其余部分将被视为注释,即使在其后面出现分号。
* [any text]

*标记的解析方式与任何其他调试器命令相同。因此,如果要在另一个命令之后创建注释,则必须在*标记前面加上分号。*标记将导致忽略行的其余部分,即使行后面出现分号。这与$$(注释说明符)不同,后者创建的注释可以用分号终止。

例如,以下命令将显示EAX和EBX,但不显示ECX:
0:000> r eax; $$ some text; r ebx; * more text; r ecx

不以任何方式处理前缀为*或$$标记的文本。如果正在执行远程调试,则在调试服务器中输入的注释在调试客户端中将不可见,反之亦然。如果希望使注释文本以所有参与方都可见的方式显示在调试器命令窗口中,则应使用.echo(echo comment)。

.block

.block标记不执行任何操作;它仅用于引入语句块。
Commands ; .block { Commands } ; Commands

命令块由大括号包围。输入每个块后,将计算块中的所有别名。如果在命令块中的某个点更改别名的值,则该点后面的命令将不会使用新的别名值,除非它们位于从属块中。每个块必须以控制流标记开头。如果您希望创建一个仅用于评估别名的块,则应在其前面加上.block标记,因为此标记除了允许引入块之外没有其他作用。

.break

.break标记的行为类似于c中的break关键字。
.for (...) { ... ; .if (Condition) .break ; ...} 

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

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

.break标记可以在任何.for、.while或.do循环中使用。由于没有与c goto语句等效的控制流标记,因此通常在.if条件中使用.break标记,如上面的语法示例所示。然而,这实际上不是必需的。

.catch

.catch标记用于防止程序在发生错误时终止。它不像C++中的catch关键字。
Commands ; .catch { Commands } ; Commands

.catch标记后面跟着大括号,其中包含一个或多个命令。如果.catch块中的命令生成错误,将显示错误消息,大括号内的所有剩余命令将被忽略,并在右大括号后使用第一个命令继续执行。如果不使用.catch,则错误将终止整个调试器命令程序。可以使用.leave退出.catch块。

.leave

 .leave标记用于退出.catch块。
.catch { ... ; .if (Condition) .leave ; ... }

当在.catch块中遇到.leave标记时,程序退出该块,并在右大括号后使用第一个命令继续执行。

.continue

.continue标记的行为类似于c中的continue关键字。
.for (...) { ... ; .if (Condition) .continue ; ... } 

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

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

.Continue标记可以在任何.for、.while或.do循环中使用。

.do

.do标记的行为类似于c中的do关键字,但条件之前不使用单词“while”。

.do { Commands } (Condition)

语法元素:

  • 命令
    指定一个或多个将在条件为真时重复执行的命令,但始终至少执行一次。这个命令块需要用大括号括起来,即使它由一个命令组成。多个命令应该用分号分隔,但右大括号前的最后一个命令不需要后跟分号。
  • 条件
    指定条件。如果计算结果为零,则视为假;否则为真。括号中的封闭条件是可选的。条件必须是表达式,而不是调试器命令。它将由默认表达式求值器(MASM或C++)进行计算。
.break和.continue标记可用于退出或重新启动命令块。

.else

.else标记的行为与c中的else关键字类似。
.if (Condition) { Commands } .else { Commands } 

.if (Condition) { Commands } .elsif (Condition) { Commands } .else { Commands }
语法要素:
  • 命令
    指定将按条件执行的一个或多个命令。这个命令块需要用大括号括起来,即使它由一个命令组成。多个命令应该用分号分隔,但右大括号前的最后一个命令不需要后跟分号。

.elsif

.elsif标记的行为与c中的else-if关键字组合类似。

.if (Condition) { Commands } .elsif (Condition) { Commands } 

.if (Condition) { Commands } .elsif (Condition) { Commands } .else { Commands }

语法要素:

  • 条件
    指定条件。如果计算结果为零,则视为假;否则为真。括号中的封闭条件是可选的。条件必须是表达式,而不是调试器命令。它将由默认表达式求值器(MASM或C++)进行评估。
  • 命令
     指定将按条件执行的一个或多个命令。这个命令块需要用大括号括起来,即使它由一个命令组成。多个命令应该用分号分隔,但右大括号前的最后一个命令不需要后跟分号。

.if

.if标记的行为类似于c中的if关键字。

.if (Condition) { Commands } 

.if (Condition) { Commands } .else { Commands } 

.if (Condition) { Commands } .elsif (Condition) { Commands } 

.if (Condition) { Commands } .elsif (Condition) { Commands } .else { Commands }

语法要素:

  • 条件
    指定条件。如果计算结果为零,则视为假;否则为真。括号中的封闭条件是可选的。条件必须是表达式,而不是调试器命令。它将由默认表达式求值器(MASM或C++)进行计算。
  • 命令
     指定将按条件执行的一个或多个命令。这个命令块需要用大括号括起来,即使它由一个命令组成。多个命令应该用分号分隔,但右大括号前的最后一个命令不需要后跟分号。

.for

.for标记的行为类似于c中的for关键字,但多个增量命令必须用分号分隔,而不是用逗号分隔。

.for (InitialCommand ; Condition ; IncrementCommands) { Commands }

语法要素:

  • 初始化命令
    指定将在循环开始之前执行的命令。只允许使用一个初始命令。
  • 条件
    指定条件。如果计算结果为零,则视为假;否则为真。括号中的封闭条件是可选的。条件必须是表达式,而不是调试器命令。它将由默认表达式求值器(MASM或C++)进行计算。
  • 递增命令
    指定将在每个循环结束时执行的一个或多个命令。如果要使用多个增量命令,请用分号分隔它们,但不要将它们括在大括号中。
  • 命令
    指定一个或多个将在条件为真时重复执行的命令。这个命令块需要用大括号括起来,即使它由一个命令组成。多个命令应该用分号分隔,但右大括号前的最后一个命令不需要后跟分号。
如果所有的工作都是由increment命令完成的,那么可以完全忽略条件,只需使用一对空的大括号。
下面是.for语句的示例,其中包含多个递增命令:
0:000> .for (r eax=0; @eax < 7; r eax=@eax+1; r ebx=@ebx+1) { .... }

.foreach

.foreach标记解析一个或多个调试器命令的输出,并将此输出中的每个值用作一个或多个附加命令的输入。

.foreach [Options] ( Variable  { InCommands } ) { OutCommands } 

.foreach [Options] /s ( Variable  "InString" ) { OutCommands } 

.foreach [Options] /f ( Variable  "InFile" ) { OutCommands }

语法要素:

  • 选项
    可以是以下选项的任意组合:
    /PS首字母kipnumber 导致跳过某些初始令牌。initialkipnumber指定不会传递给指定outcommands的输出令牌数。
    /PS跳过编号  导致每次处理命令时重复跳过令牌。每次将令牌传递给指定的outcommands后,将忽略相当于skipNumber值的一些令牌。
  • 变量
    指定变量名。此变量将用于保存incommands字符串中每个命令的输出;您可以在传递给outcommands的参数中按名称引用变量。可以使用任何字母数字字符串,但不建议使用还可以传递有效十六进制数或调试器命令的字符串。如果用于变量的名称恰好与现有全局变量、局部变量或别名匹配,则它们的值不会受到.foreach命令的影响。
  • 命令
    指定将分析其输出的一个或多个命令;生成的标记将传递给outcommands。不显示来自incommands的输出。
  • 输入字符串
    与/s一起使用。指定要分析的字符串;生成的令牌将传递给outcommands。
  • 输入文件
    与/f一起使用。指定要分析的文本文件;生成的令牌将传递给outcommands。文件名内嵌必须用引号括起来。
  • 输出命令
    指定将为每个令牌执行的一个或多个命令。每当变量字符串出现时,它将被当前标记替换。

当字符串变量出现在outcommands中时,它必须被空格包围。如果它与任何其他文本相邻(甚至是括号),则不会被当前标记值替换,除非您使用$(别名解释器)标记。当分析来自incommands、instring字符串或infile文件的输出时,任何数量的空格、制表符或回车都被视为单个分隔符。当变量出现在outcommands中时,将使用生成的每一段文本来替换它。

下面是举例 .foreach使用语句dds命令在文件中找到的每个标记myfile.txt:

0:000> .foreach /f ( place "g:\myfile.txt") { dds place }

/PS/ps标志可用于将仅某些标记传递到指定OutCommands。 例如,下面的语句将跳过myfile.txt文件中的前两个标记,然后将第三个标记传递给dds。在传递每个令牌之后,它将跳过四个令牌。结果是DDS将与第3、8、13、18和23个令牌一起使用,依此类推:

0:000> .foreach /pS 2 /ps 4 /f ( place "g:\myfile.txt") { dds place }

.while

.while标记的行为类似于c中的while关键字。
.while (Condition) { Commands }

语法要素:

  • 条件
    指定条件。如果计算结果为零,则视为假;否则为真。括号中的封闭条件是可选的。条件必须是表达式,而不是调试器命令。它将由默认表达式求值器(MASM或C++)进行评估。
  • 命令
     指定一个或多个将在条件为真时重复执行的命令。这个命令块需要用大括号括起来,即使它由一个命令组成。多个命令应该用分号分隔,但右大括号前的最后一个命令不需要后跟分号。

.printf

.printf标记的行为与c中的printf语句类似。

.printf [/D] [Option] "FormatString" [, Argument , ...]

语法要素:

  • /D
    指定格式字符串包含调试器标记语言(DML)。
  • Option
    (仅限windbg)指定windbg应将格式字符串解释为的文本消息类型。windbg为每种类型的调试器命令窗口消息分配背景和文本颜色;选择其中一个选项将使消息以适当的颜色显示。默认设置是将文本显示为普通级别的消息。
    有一下选项:
    Option 消息类型 选项对话框中的颜色的标题

    /od

    调试对象

    调试对象级别的命令窗口

    /oD

    调试对象提示符

    调试对象级别命令提示窗口

    /oe

    错误

    错误级别的命令窗口

    /on

    正常

    标准级别的命令窗口

    /op

    prompt

    级别命令提示窗口

    /oP

    提示符下注册

    提示符下注册级别的命令窗口

    /os

    符号

    符号消息级别的命令窗口

    /ov

    详细

    详细级别的命令窗口

    /ow

    警告

    警告级别的命令窗口

  • Arguments
    为指定参数的格式字符串,如printf。 指定的参数数目应与匹配的转换中的字符数FormatString。 每个自变量是将默认表达式计算器计算的表达式 (MASM 或C++)。
  • FormatString
    指定格式字符串,如printf中所示。一般来说,转换字符的工作方式与C完全相同。对于浮点转换字符,除非使用L修饰符,否则64位参数将被解释为32位浮点数字。支持%p转换字符,但它表示目标虚拟地址空间中的指针。它不能有任何修饰符,并且使用调试器的内部地址格式。支持以下附加转换字符。
    字符 自变量类型 参数 打印的文本

    %p

    ULONG64

    目标的虚拟地址空间中的指针。

    指针的值。

    %N

    DWORD_PTR (32 位或 64 位,具体取决于主机的体系结构)

    主机的虚拟地址空间中的指针。

    指针的值。 (这等效于标准 C %p 字符。)

    %ma

    ULONG64

    目标的虚拟地址空间中的以 NULL 结尾的 ASCII 字符串的地址。

    指定的字符串。

    %mu

    ULONG64

    目标的虚拟地址空间中的 NULL 终止的 Unicode 字符串的地址。

    指定的字符串。

    %msa

    ULONG64

    ANSI_STRING 结构目标的虚拟地址空间中的地址。

    指定的字符串。

    %msu

    ULONG64

    目标的虚拟地址空间中的 UNICODE_STRING 结构地址。

    指定的字符串。

    %y

    ULONG64

    目标的虚拟地址空间中的调试器符号的地址。

    包含指定的符号 (和偏移量,如果有) 的名称的字符串。

    %ly

    ULONG64

    目标的虚拟地址空间中的调试器符号的地址。

    包含名称的指定符号 (和偏移量,如果有),以及任何可用的源行信息的字符串。

默认情况下,可以使用选项参数选择的颜色设置都设置为白色背景上的黑色文本。要充分利用这些选项,必须首先使用“视图”选项打开“选项”对话框并更改调试器命令窗口消息的颜色设置。

下面的示例演示如何在格式字符串中包含DML标记。

.printf /D "Click <link cmd=\".chain /D\">here</link> to see extensions DLLs."

执行和点击链接结果如下:

Windbg命令脚本流程控制语句详解的更多相关文章

  1. Mysql高手系列 - 第18篇:mysql流程控制语句详解(高手进阶)

    Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能. 这是Mysql系列第18篇. 环境:mysql5.7.25,cmd命令中进行演示. 代码中被[]包含的表示可选,|符 ...

  2. C#基础之流程控制语句详解

    C#程序的执行都是一行接一行.自上而下地进行,不遗漏任何代码.为了让程序能按照开发者所设计的流程进行执行,必然需要进行条件判断.循环和跳转等过程,这就需要实现流程控制.C#中的流程控制包含了条件语句. ...

  3. Python流程控制语句详解

    1.程序结构 计算机在解决问题时,分别是顺序执行所有语句.选择执行部分语句.循环执行部分语句,分别是:顺序结构.选择结构.循环结构.如下图: 2.选择语句 2.1最简单的if语句 Python使用保留 ...

  4. Python基础教程,流程控制语句详解

    1.程序结构 计算机在解决问题时,分别是顺序执行所有语句.选择执行部分语句.循环执行部分语句,分别是:顺序结构.选择结构.循环结构.如下图: 很多人学习python,不知道从何学起.很多人学习pyth ...

  5. Windbg命令脚本

    命令脚本,就是将完成某个特定任务的相关命令组合在一起,保存在脚本文件里,加载到Windbg里执行,达到我们的目的.你可以理解为脚本就是一种语言,就像c或者汇编,但是他不需要编译器将其编译为可执行文件, ...

  6. linux管道命令grep命令参数及用法详解---附使用案例|grep

    功能说明:查找文件里符合条件的字符串. 语 法:grep [-abcEFGhHilLnqrsvVwxy][-A<显示列数>][-B<显示列数>][-C<显示列数>] ...

  7. linux sed命令参数及用法详解

    linux sed命令参数及用法详解 http://blog.csdn.net/namecyf/article/details/7336308 1. Sed简介 sed 是一种在线编辑器,它一次处理一 ...

  8. Linux Bash命令关于程序调试详解

    转载:http://os.51cto.com/art/201006/207230.htm 参考:<Linux shell 脚本攻略>Page22-23 Linux bash程序在程序员的使 ...

  9. (转)CentOS系统启动流程图文详解

    CentOS系统启动流程图文详解. 原文:http://www.linuxidc.com/Linux/2017-03/141966.htm 熟悉系统启动流程对于我们学习Linux系统是非常有帮助的,虽 ...

随机推荐

  1. Golang 读写文件

    读文件 func ReadFile_v1(filename string) { var ( err error content []byte ) fileObj,err := os.Open(file ...

  2. go 指针 通过指针修改int类型的值

    指针的定义 :var p *int 取指针的值 :*p ------------------------------------------------------------------------ ...

  3. MNIST机器学习入门(二)

    在前一个博客中,我们已经对MNIST 数据集和TensorFlow 中MNIST 数据集的载入有了基本的了解.本节将真正以TensorFlow 为工具,写一个手写体数字识别程序,使用的机器学习方法是S ...

  4. Json schema前奏 关于JSON

    目录 1. 何为 JSON 2. JSON 基本语法 3. JSON值的类型 4. 与XML比较 5. 辅助工具 1. 何为 JSON JSON( JavaScript Object Notation ...

  5. 全栈项目|小书架|服务器端-NodeJS+Koa2实现首页图书列表接口

    通过上篇文章 全栈项目|小书架|微信小程序-首页水平轮播实现 我们实现了前端(小程序)效果图的展示,这篇文章来介绍服务器端的实现. 首页书籍信息 先来回顾一下首页书籍都有哪些信息: 从下面的图片可以看 ...

  6. nmon2influxdb+grafana:服务监控可视化部署

    在工作中,无论是定位线上问题,还是性能优化,都需要对前端.后台服务进行监控.而及时的获取监控数据,能更好的帮助技术人员排查定位问题. 前面的博客介绍过服务端监控工具:Nmon使用方法及利用easyNm ...

  7. 使用 ProcessMonitor 找到进程所操作的文件的路径

    原文:使用 ProcessMonitor 找到进程所操作的文件的路径 很多系统问题都是可以修的,不需要重装系统,但是最近我还是重装了.发现之前正在玩的一款游戏的存档没有了--因为我原有系统的数据并没有 ...

  8. MPAndroid 的学习

    1.MPAndroid 的github的地址: https://github.com/PhilJay/MPAndroidChart#documentation 2.使用步骤: 在build.gradl ...

  9. ubuntu16.04安装openssh中报错解决

    在使用apt-get直接进行安装时会报错: sudo apt-get install openssh-server 正在读取软件包列表... 完成 正在分析软件包的依赖关系树       正在读取状态 ...

  10. printk打印级别

    默认级别 # cat /proc/sys/kernel/printk 4 4 1 7 分别是:控制台日志级别.默认的消息日志级别.最低的控制台日志级别和默认的控制台日志级别 举例 # echo 0 & ...