WinDbg的alias命令(as, aS)在script里面很有用,但是WinDbg的script不算是一种设计良好的语言,一般在写WinDbg script总会遇到各种坑,就包括alias命令的求值。
与变量相比,WinDbg的alias更像是C语言的宏。他可以把一个名字定义成指定的字符串,环境变量,给定地址的字符串,甚至表达式的值或者WinDbg命令的输出字符串。C语言的宏仅在定义点到文件结束有效(如果后面没有undef),WinDbg的alias的求值则 下面解释下这个过程。
假设已经有alias (aS foo bar), 在运行命令 .echo foo 或者 .echo ${foo} (使用后者的可以对alias name提供显示的tokenize和参数,比如${/v:foo},参考WinDbg帮助)。在运行 .echo foo 命令前,WinDbg会对整个命令字符串执行alias替换,替换完成在执行整个命令,运行栈如下:
dbgeng!ReplaceAliases dbgeng!PreprocessExternalStrBuf dbgeng!PreprocessExternalString dbgeng!Execute dbgeng!DebugClient::ExecuteWide ...
如果只是上面的规则,那算简单的了,缺点也非常明显,就是如果输入是多条以分号分隔的命令的组合,前面命令定义的alias则不能应用到后面的命令。当然如果每条命令运行后都对剩下的所有命令做替换也会非常混乱,尤其是在会有循环的情况下,不容易理解。WinDbg的做法就是在每个block开始执行前对block内部可能的alias做替换。另外,整个命令输入虽然不是一个block,但是也会在开始做alias替换。这样就alias命令和block的关系, 如果想在一个alias定义后面的命令里引用前面的定义,最好把后面的命令放到一个新的block里面,比如下面的命令里面输出 foo (实验前如果foo已经作为alias被定义,最好删除掉):
aS foo bar; .echo foo
而下面的则可以输出 bar:
aS foo bar; .block{.echo foo}
替换foo的运行栈如下:
dbgeng!ReplaceAliases dbgeng!PreprocessExternalStrBuf dbgeng!PreprocessExternalString dbgeng!ProcessCurBraceBlock dbgeng!DotBlock dbgeng!DotCommand dbgeng!Execute dbgeng!DebugClient::ExecuteWide ...
理解了这个机制基本就可以理解WinDbg alias的求值了。由于alias的替换可以递归进行,为了避免意外的递归替换发生,有一条规则是如果一个block是以as/aS/al/ad命令打头(a前不能有任何字符,那么当前block不做替换,比如下面的命令仍然输出 foo:
aS foo bar; .block{aS foo1 bar1;.echo foo}
而下面的命令就能输出bar,区别仅在与第二个aS前面有个空格:
aS foo bar; .block{ aS foo1 bar1;.echo foo}
最后关于WinDbg的block,.block{} 这样会定义一个新的block,其实所有的花括号都会定义新的block,比如.if(cond){}.else{}等,这个从上面运行栈上的ProcessCurBraceBlock也能体现。
转自:https://zhuanlan.zhihu.com/p/20908953