windbg 脚本简单入门

http://blog.csdn.net/superliuxing/article/details/19206985

Windbg的功能自然不必说,集内核调试,应用程序调试,远程调试,dump分析等于一身,真是杀人灭口必备利器。但是也由于其太过强大,命令太多,导致很多新手对windbg望而生畏,觉得Windbg很高深。好在已经有很多有识之士将Windbg的基础用法分享了出来,入门应该不是一件难事了。今天就不再重复谈Windbg如何入门,来探讨一下高级点的用法:脚本。其实,脚本不应该是一个很复杂的东西,但是,在Windbg的帮助里想过的内容却太过于分散了,导致学习起来颇有些海底捞针的感觉,所以我觉得有必要把我的心得跟大家分享一下。

先来解释一下,Windbg的脚本是什么?你可以理解为脚本就是一种语言,就像c或者汇编,但是他不需要编译器将其编译为可执行文件,而是由解释器将其内容翻译为对应的动作。而Windbg的脚本就是利用Windbg作为解释器,将脚本内容翻译为实际的动作。也许这个解释还是有些晦涩,那让我们跳过这些晦涩的概念,来一个简单的例子:
代码:
.echo “hello windbg”
这条命令会显示“hello windbg”这个字符串,把它保存到c:\1.txt文件,然后在Windbg的命令窗口里输入:
><c:\1.txt回车,看看屏幕上出现了什么?没错,Windbg将1.txt里的内容当做一条Windbg的命令执行了。这就是一个简单的脚本。也许有人说,这确实是一个脚本,但是他太弱了,只能打印字符串而已。别急,饭要一口一口吃,脚本要一点一点扩展。先来看看这个
><,根据前面的例子,很容易看出他的作用是将脚本文件交给Windbg解释,由他完成了将一个txt变成Windbg命令的关键转换。其实你知道了这个,Windbg脚本就算入门了,因为你可以把很多命令写在这个文件里,然后用
><装载执行。这应该能完成一些功能,不过,这样的用法充其量应该叫做batch,而不是script,因为他只能批量执行命令。那么怎么才能升级到script呢?接下来我们一步一步分解,不过在此之前,还是先把
><了解透彻。$$>< 其实有5个孪生兄弟,在windbg中给出的形式和用法如下:

代码:
$<Filename 
$><Filename 
$$< Filename 
$$>< Filename 
$$>a< Filename [arg1 arg2 arg3 ... ]
妈呀,眼都花了,看上去长得都很像。别急,他们是有规律的,归纳一下:

1.'$'的表示'<'和脚本名之间不可以有空格。
2.'
′的表示可以有空格(其实我有点不太理解这个操蛋设定,为什么不能自动检测)。3.′<′表示不会自动把脚本文件压缩为一行。4.′><′表示会把他们压缩为一行,并将原来的换行变成′;′。5.最后一个表示可以给脚本传递参数。为什么要压缩成一行?问的好,Windbg执行某些命令的时候需要他们是一行,比如bp后面可以添加其他命令,但是所有命令写一行又太长了,不容易阅读,于是帮你压缩一下。一般我们用
><就够了。

好了,接下来是脚本的时刻了。要用好脚本,先要转变自己的态度,要像学习一门编程语言一样学习他,像写代码一样写他,总之,你的思路应该和编程的思路一样。写Windows的应用程序需要哪些知识?首先需要一门编程语言,比如c,另外需要了解Windows的API。好了,我们现在对应到Windbg的脚本。Windbg提供了一些脚本的语法,相当于一门编程语言,而脚本里用到的那些命令相当于系统的API。要学好开发,先要学好一门编程语言,而语言学好以后,API就是现用现查的,所以我们就主要从语法入手。

新学一门编程语言,入门的时候都会学以下几个方面:数据类型,变量,表达式,语句,内建函数,我们也从这几个方面来了解Windbg的脚本。

1.  数据类型:
关于数据类型,Windbg的帮助里没有明确列举,但是,在使用时一般会遇到,数值和字符串这两种。
数值
数值没有太多需要解释的,和所有编程语言里的整数含义一样,在表示的时候有进制之分。
代码:
2进制  0x
8进制  0n
10进制  0t
16进制  0y
字符串
字符串用一对 ” 括起来。比如上面的 ”hello windbg”。

2.  变量:
在windbg中变量的定义很特别,实际上,他并没有变量这个概念,所以,你学习的时候会觉得很别扭。不过,我们换个思路就容易了,变量实际上就是为了保存临时结果, 如果你只想保存一些数值,那么伪寄存器应该是比较好的选择,windbg提供了20个伪寄存器$t0-$t19,供命令保存临时数值变量。称他们为伪寄存器是有原因的,首先对他们的操作和寄存器一样,都是使用r命令,在C++表达式里都前面需要加@符,但是他们又不是真正的寄存器,只是windbg定义的名字而已。使用这些伪寄存器也是很方便的:

代码:
0:000> r $t0=0x123
0:000> r $t0
$t0=00000123

0:000> r eax
eax=004c1b89

0:000> r $t0=@eax
0:000> r $t0
$t0=004c1b89
  从上面的例子也可以看出r命令后面的@是可以省略的。

3.  别名
别名和变量还有些区别,变量是在执行过程中取他的值,而别名更像是宏,在解释时直接用内容替换原始操作数。别名有两种,一种是固定名字的,一种是自定义的。
固定名字别名
固定名字别名和伪寄存器很类似,Windbg提供了10个,$u0-$u9。使用的时候依然是r命令,不过要在“u”前面加个“.”,像下面这样:

代码:
0:000> r $.u0 = "123"
0:000> .echo $u0
123
从上面的例子可以看出一旦别名被定义了,到使用他的时候,Windbg会把别名替换为内容。
自定义别名
自定义别名会复杂一些,但是,有了它的存在,我们才可以为内存中的一些字符串定义别名。操作自定义别名有3个命令:as,ad,al。
As 定义一个别名,其强大之处在于,可以指定一个内存地址,然后将内存中的内容定义为别名。

代码:
0:000> .dvalloc 10
Allocated 1000 bytes starting at 00010000
0:000> ea 00010000 "123456"
0:000> as /ma ${/v:test} 0x00010000
0:000> .echo test
123456
上面的命令将0x00010000地址的定义为一个别名,由于as使用了/ma选项,所以将内容当做一个’\0’结尾的ASCII字符串来解析,${}是别名解释器,后面再讲。除了/ma选项以外as还有一些其他强大的选项:

代码:
/ma  参数指定的内存地址当做ASCII字符串。
/mu  参数指定的内存地址当做Unicode字符串。
/msa  参数指定的内存地址当做ANSI_STRING字符串。
/msu  参数指定的内存地址当做UNICODE_STRING字符串。
/f  别名等于参数指定文件的内容。
/e  别名等于参数指定的环境变量。
al显示已经定义的别名,ad删除已经定义的别名,接着刚才的例子继续输入以下命令:
代码:
0:000> al
  Alias            Value  
 -------          ------- 
 test             123456 
0:000> ad ${/v:test}
0:000> al
No aliases
可以看的很明显吧。
现在我们来解释一下例子里那个长得很奇怪的${},这个东西叫别名解释器,把别名放在后面的大括号里面,Windbg就知道里面是个别名,需要被翻译。其实不用这个符号也可以,不过写到复杂脚本的时候就可能出问题,谁用谁知道,我就不再发散了,建议是最好用。这个解释器也有选项,上面的/v:就是一个。
/v:  保持别名原样,不翻译,在定义和删除的时候用。
/n:  如果别名定义就翻译为内容,否则不做任何翻译。
/f:  如果别名定义就翻译为内容,否则翻译为空。
/d:  如果别名被定义,翻译为1,否则翻译为0,相当于#ifdef。

4.  表达式
Windbg提供了两种表达式:汇编表达式和C++表达式。两种表达式的操作符和操作数都略有区别。

默认是汇编表达式,求汇编表达式的值用?,求C++表达式的值用??。
汇编表达式里能用的操作符除了+、-、*、/这些算数运算符以外还有一些类似转型运算符,比如poi,有时候大家断到一个函数,第一参数是个字符串指针,想打印这个字符串怎么办?可以这样 dd esp+4,然后再从结果中da一次,有了poi,一行命令就可以做到,dd poi(esp+4)。

C++表达式就更加丰富了,几乎所有的C++表达式都可以用,包括.和->操作符,想让Windbg将表达式按C++方式解释,需要在表达式前面加@@c++()。

5.  语句
都说了脚本要按照编程的思想来写,既然是编程,怎么能少得了流程控制语句呢?Windbg支持以下流程控制语句。
代码:
.if
.else
.elif
.for
.while
.break
.continue
.do
我觉得都可以不用解释,看名字就应该知道是什么,大家都是写程序的嘛,对吧。
另外还有几个比较有用的语句
代码:
.printf  格式化输出,熟悉吧。
.block  语句块
$$  注释,长得好奇怪
这里面,.block要单独说说,所谓语句块,其实就是用{}括起开的一堆语句,包括.if、.else后面的语句其实都是语句块,语句块内部的别名(还记得吗)在进入块的时候会被翻译,进入块以后,如果修改了别名的定义,那么在本块内的后续语句中是无效的(还记得别名是原样替换吗),所以,如果需要在后续语句中生效,需要把后面的语句放到一个单独的语句块里,也就是用{}把他们包含起来,但是Windbg又不能识别直接用{}包含起来的东西,于是就出现了.block,看到这里,请切记,如果需要别名被翻译,一定要把他放到语句块里。

6.  内建函数
这里只讲两个内建函数$scmp和$sicmp都是字符串比较,一个区分大小写,一个不区分大小写。这两个函数有一个毛病,那就是参数只接受字符串字面量,就是说,你只能写$scmp(“123”,”123”),不能写$scmp(poi(esp+4),”123”),好了,有人急了,不能这样写,要这两个函数有什么用?不急,我们可以利用别名(这就是别名最有用的地方),还是接着刚才那个例子:

代码:
0:000> as /ma ${/v:test} 00010000
0:000> ? $scmp("${test}","123456")
Evaluate expression: 0 = 00000000
0:000> ? $scmp("${test}","123457")
Evaluate expression: -1 = ffffffff
这样就可以比较变量字符串了。

好了,有了以上知识,写一个windbg脚本应该就有基础了,剩下的就是要看大家知道多少“API”了,更详细的信息需要在Windbg的帮助里挖掘了。

最后贴一个完整的例子,利用脚Hook CreateFileW,这个例子虽然不长,但是都是精华啊,哈哈。

代码:

.dvalloc /b 0x79990000 30
ew 0x79990000 0xc033
ed 0x79990002 0x00001cc2
bp kernel32!CreateFileW "
as /mu ${/v:filename} poi(esp+4);
.block{.if ($sicmp(\"${filename}\", \"c:\\1.txt\") == 0){.echo \"open 1.txt\";r eip=0x79990000}
}
ad ${/v:filename};
gc;
"

稍微解释一下,一开始分配了一段内存,选了一个几乎不会被用到的地址,然后填充为

代码:
xor eax,eax
ret 0x1c
之后设置一个条件断点,断到以后判断参数中的文件名,如果文件是c:\1.txt就将执行流程转移到分配的指令处,相当于直接返回,于是打开文件失败。效果如下:
名称:  QQ图片20131031221522.jpg查看次数: 1文件大小:  21.9 KB

码了这么多字,还是挺累的,本文作者evil.eagle,转载的时候烦请注明出处,如果大家希望继续交流,欢迎加QQ群151843490,另外吐槽一下,看雪的排版真的好难用啊。
========

如何写windbg高级脚本---以访问文件的windbg脚本为例说明

http://www.pediy.com/kssd/pediy10/83946.html

最近需要在访问指定文件时中断下来,但不知道如何下断,在网上搜索了一番无果,只好自己摸索了。听大侠说windbg的条件断点功能异常强大,可以实现,不禁心痒,特尝试一番,顺便熟悉一下windbg的脚本语法。

先来了解简单的,得到当前访问的文件名
先写段C代码,创建C:\a.txt并往文件中写任意几个字符,代码如下:
    HANDLE hFile=CreateFile("C:\\a.txt",
      GENERIC_WRITE|GENERIC_READ,
      0,
      NULL,
      OPEN_ALWAYS,
      FILE_ATTRIBUTE_NORMAL,
      NULL);
    if (hFile == INVALID_HANDLE_VALUE) 
    {
      return ;
    }
    char Buffer[]={"abcdefghijklemn"};
    DWORD dwReturn;
    WriteFile(hFile,Buffer,strlen(Buffer),&dwReturn,NULL);

CloseHandle(hFile);
把以上代码放到对话框的按钮点击响应事件中去。编译链接得到test.Exe.
文件名为CreateFile API的第一个参数,因而在执行到该API的入口时,esp+4即表示第一个参数的地址。故可这样下断:
bp kernel32!CreateFileW "r $t1=poi(esp+4);.echo;.printf\"FileName:%mu\",$t1;.echo;g"

$t0~$t19为伪寄存器,可用来存储临时值,poi表示取地址的值,
也可将脚本保存为文件,然后在windbg中输入: $$><脚本文件路径 来运行。
我把以上脚本保存为 ”C:\script.txt”
用windbg打开以上代码编译得到的test.exe, 
Ctrl+Break中断windbg,然后下断,即输入:$$><C:\script.txt,再输入g,让进程运行起来,
点击按钮,果其然,得到文件名了,见下图,
 
访问的文件获取了,那如何在访问指定文件时中断下来呢?字符串比较的脚本如何写呀?
上网查资料吧,不大一会,发现了$scmp/$sicmp/$spat是用来字符串操作的。$spat正合我意呀。
  $spat("string1”, "pattern”):判断参数1指定的字符串是否符合参数2指定的模式。模式字符串中可以包含?、*、#等特殊符号,WinDBG帮助文件中String Wildcard Syntax一节包含了详细的说明;
  这样摸索了一番,写了如下脚本:

bp kernel32!CreateFileW "
r $t1=poi(esp+4)as /mu $FileName $t1.echo.printf\"File:%mu\",$t1.echo
.block
{.if($spat(\"${$FileName}\",\"*a.txt\")) {.echo 'find...';ad ${/v:$FileName}}.else {.echo no find...ad ${/v:$FileName}gc}
}"

以上脚本,不复杂,实现当访问文件名类似“a.txt”时,windbg中断执行。

对脚本解析几个要点:
1.  使用伪寄存器,更快速的方法是在$前加上一个@符号。这样,WinDBG就知道@后面是一个伪寄存器,不需要搜索其他符号;
2.  r $t1=poi(esp+4),poi(esp+4)取地址的值,并赋给伪寄存器$t1 ;
3.  as /mu $FileName $t1 ,定义$t1 所指地址一个别名$FileName,用来在下面的$spat中使用。别名会在脚本加载时被解析程序替换一次。为何要用别名?你不用试试就知道了,直接用$t1不行,windbg提示你语法错误;
4.  .block是啥东西,看windbg帮助就知道了。代码块,如果需要每次运行进入替换,请用.block括起来;我这里有个别名,需要每次替换,所以用了个.block括起来;
5.  $spat为模式匹配函数,其他类似函数$scmp/$sicmp。
6.  ${$FileName}、${/v:$FileName}这呢?听我说来,${ aliase} 明确的指出了, 大括号 {} 内的变量名是可以被替换的,即使 aliase 和其它文本相连。如果要求 ${} 这个别名不被替换, 即不被解析程序替换成其他值, 只保留它当前的字面值.如下面的ad ${/v:$FileName},删除别名,此时用/v 选项来了阻止对该别名的替换, 保留它原来的字面值;
7.  别名用完记得要删除;删除方法用ad命令。
试试看看结果,在访问a.txt时断下来了。
 
如果我要在往该文件写数据时断下来,如何设断?直接在writeFile下断?但不知道当前访问的是哪个文件呀?在脚本里,通过文件句柄能得到相应的文件名吗?我反正还不知道,如果你知道请一定不要忘了告我呀?以下为我的脚本:

$$Written by shakesky
$$10:44 2009-2-12
$$访问文件之windbg下断脚本bp kernel32!CreateFileW "
r $t0=poi(esp+4)as /mu $FileName $t0
.echo
.printf \"Prepare to visit file:%mu\",$t0
.echo
.block
{$$ 判断是否是访问我所需观察的文件.if($spat(\"${$FileName}\",\"*a.txt\")) {.echo 'Match...'~.gu$$ 得到文件句柄r @$t1=eax.if(@$t1!=0xFFFFFFFF){$$往该文件写数据时下断.printf \"File Handle:%08x\",$t1.echobp kernel32!WriteFile \"r @$t2=poi(esp+4).if(@$t2!=@$t1) {gc}\"}ad ${/v:$FileName}gc  }.else {.echo No Match...ad ${/v:$FileName}gc}
}"

运行结果截图:

全文完,不当处请不吝指教。
========

利用windbg脚本调试简单实例

http://blog.csdn.net/superliuxing/article/details/19505777

之前想写一篇用windbg脚本调试的东西,但是一直比较懒,懒得动手,懒得想例子,把时间都放在了游戏上面,所以想法虽好一直没有实施...
    这次写一个非常简单的利用windbg脚本进行动态调试的小文,博君一笑.
    // Windbgscript.cpp : Defines the entry point for the console application.
//

代码:
#include "stdafx.h"

#include <iostream>
using namespace std;

int ret100()
{
  int x = 2;  //2-3
  x*=33;
  ++x;

return x;
}

int add(int x, int y)
{
  x = 0; //多余的
  return x+y;

}

int _tmain(int argc, _TCHAR* argv[])
{

int x = 100;

int y = add( x, ret100());

cout << y;

getchar();
  return 0;
}
    上面代码都非常简单,ret100里面做了一个非常简单的操作,我希望它的返回值是100,但现在的返回值是67, 当然改法太多了,我这里假设是初始化时 x 应该初始化为3. 
    同样add里面也有问题,就是对于参数x赋值0,认为它是一条多余的操作, 因为我们的目的是返回x+y的结果.
现在执行结果是:
   
如何利用windbg去动态的修改并且不需要我们始终去手动交互是我们的主要目的, 首先肯定是要了解这两个函数的内部实现.
uf伺候:

代码:
0:001> uf windbgscript!ret100
   11 00401000 55              push    ebp
   11 00401001 8bec            mov     ebp,esp
   11 00401003 51              push    ecx
   12 00401004 c745fc02000000  mov     dword ptr [ebp-4],2
   13 0040100b 8b45fc          mov     eax,dword ptr [ebp-4]
   13 0040100e 6bc021          imul    eax,eax,21h
   13 00401011 8945fc          mov     dword ptr [ebp-4],eax
   14 00401014 8b4dfc          mov     ecx,dword ptr [ebp-4]
   14 00401017 83c101          add     ecx,1
   14 0040101a 894dfc          mov     dword ptr [ebp-4],ecx
   16 0040101d 8b45fc          mov     eax,dword ptr [ebp-4]
   17 00401020 8be5            mov     esp,ebp
   17 00401022 5d              pop     ebp
   17 00401023 c3              ret
 mov     dword ptr [ebp-4],2
对应的就是 x=2;的指令, 我们的目的是将x赋值3, 这里改法很多,比如将ebp-4处赋值3并跳过该指令...
这里选择最简单的,让该指令执行,再执行下一条指令前修改x的值.
代码:
bp windbgscript!ret100 + 0xb " $$ 0xb代表从函数ret100开始处到当前断点位置的偏移
  ed (ebp-4) 3; $$当代码执行到此处时,ebp-4即代表x的内存地址, ed命令为在制定地指处 赋值
 gc;  $$gc命令表示脚本执行后,程序会继续执行
"
代码:
0:001> uf windbgscript!add
   20 00401030 55              push    ebp
   20 00401031 8bec            mov     ebp,esp
   21 00401033 c7450800000000  mov     dword ptr [ebp+8],0
   22 0040103a 8b4508          mov     eax,dword ptr [ebp+8]
   22 0040103d 03450c          add     eax,dword ptr [ebp+0Ch]
   24 00401040 5d              pop     ebp
   24 00401041 c3              ret
mov     dword ptr [ebp+8],0
即为那条多余的指令x=0; 我们的目的是让cpu忽略这条指令, 做法是在这条指令被执行前修改eip的值,始之跳转到下一条指令处继续执行
代码:
bp windbgscript!add + 0x03 "
 r @eip = @eip+0xa; $$ @符号会让脚本解释程序认为后面跟随的名称代表为寄存器, 节省查找时间,提高效率
 gc;
"
加载windbg脚本后程序的执行结果:

实在是找不出好的例子来,所以就想出了这么个下三滥的例子,莫笑。。。
   实际中这么简单的程序谁也不会用windbg调试的,编写脚本的时间可能远远比重新编译,部署,调试所消耗的时间更长。
   个人认为这种脚本还是比较适合用在重现问题比较困难, 尤其是一种场景, 一个进程开始的时候没有任何问题,但是跑一段时间(比如2-3个小时)开始不断重复出现一连串的错误(非崩溃),这种场景用windbg脚本还是非常合适的。
   windbg脚本里的命令非常丰富,基本平时能用到的功能都有了,感兴趣翻翻帮助文档学学还是很不错的。
========

Windbg脚本和扩展命令工具示例

http://www.cr173.com/html/18401_1.html

类型:专业工具大小:216KB语言:中文 评分:6.6标签:立即下载
好长一段时间没写文章了,最近一直忙于为项目的可调式性做一些脚本和扩展工具,鉴于对windbg强大威力的震撼,以及相对较少的资料,笔者决定写一系列关于如何开发Windbg脚本和扩展命令的文章,您的支持是我最大的动力,希望本系列文章对您有所帮助。

那么一个完整的windbg script是什么样子的呢?首先让我们看如下示例:

$$ 该脚本是列出用户进程和栈

r $t0 = nt!PSActiveProcessHead 
.for (r $t1 = poi(@$t0); (@$t1 != 0) & (@$t1 != @$t0); r $t1 = poi(@$t1)) 

  r? $t2 = #CONTAINING_RECORD(@$t1, nt!_EPROCESS, ActiveProcessLinks); 
  .process @$t2 
  .reload 
  !process @$t2
}

相对于Windbg脚本,windbg扩展比较复杂,而且通常需要花费更大的精力写出同样的功能,但是它带来的一个好处就是你可以获得更多的功能,你甚至可以通过这些扩展写一个调试器,那么一个完整的windbg扩展又是什么样子的呢?该扩展dll打印出一个全局字符串的值。

以C++语言编写的windbg扩展示例:

HRESULT CALLBACK 
PrintPTR(PDEBUG_CLIENT pDebugClient, PCSTR args)
{
    UNREFERENCED_PARAMETER(args);

IDebugSymbols* pDebugSymbols;
    if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugSymbols), (void **)&pDebugSymbols)))
    {    // Resolve the symbol
        ULONG64 ulAddress = 0;
        if (SUCCEEDED(pDebugSymbols->GetOffsetByName("TestSTLMap!g_wString", &ulAddress)))
        {
            IDebugDataSpaces* pDebugDataSpaces;
            if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugDataSpaces), (void **)&pDebugDataSpaces)))
            {    // Read the value of the pointer from the target address space
                ULONG64 ulPtr = 0;
                if (SUCCEEDED(pDebugDataSpaces->ReadPointersVirtual(1, ulAddress, &ulPtr)))
                {
                    PDEBUG_CONTROL pDebugControl;
                    if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&pDebugControl)))
                    {    // Output the values
                        pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "%p TestSTLMap!g_wString= 0x%p\n", ulAddress, ulPtr);
                        pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "%mu\n", ulPtr);
                        pDebugControl->Release();
                    }
                }
                pDebugDataSpaces->Release();
            }
            pDebugSymbols->Release();
        }
    }
    return S_OK;
}

总结

基于笔者的研究发现,国内做相关研究的人并不多,其实国外也就几个业内比较牛的人做的相对比较好,但是这些工具的作用足以让你震撼,今天开个头,有兴趣的朋友可以继续关注后续文章。
========

Windbg脚本和扩展工具之一STL容器扩展命令

http://www.tuicool.com/articles/aqmmAz

--by solidmango

想写一篇关于windbg的STL容器扩展命令的文章已经有一段时间了,但是最近项目比较忙,再加上有几本书特别想读,所以就耽误下来了,以至于整个三月都没来得及写,今天终于有时间可以把这篇文章写完。至于windbg和STL都是什么在这里我就不细说了,能打开我这篇文章的想必都是行家,那么我为什么想写着么一个主题的文章呢?

STL容器在调试的时候内部实现相对来说还是比较复杂的,而在某些生产情况下和一些极端的问题分析的时候visual studio 是登不了大雅之堂的,当然我不是说vs不好,在很多时候他是神器,但是它不是万能的。

举个例子:

如下类型的map

std::map<int,string>

实际的内部节点类型可能是这样的:

std::_Tree_nod<std::_Tmap_traits<int,std::basic_string<char,std::char_traits<char>,

std::allocator<char>>,

std::less<int>,

std::allocator<

std::pair<

int const , std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >,0> >::_Node

STL内部traits的应用遍地都是,但是甚至好多谢了好久代码的人可能都还是理解不了其巧妙之处。可能是由于其实现相对比较复杂,windbg内部的和现有的扩展对这块的调试支持都相对较差,当然如果你是高手,在某些简单的情况下,这块是有方法绕过去的,但是问题如果想对复杂就恐怕不行了。

那么我都做了什呢?我实现了如下的几个扩展命令,只要给出地址和类型就可以将整个容器打出来。

0:000> !help

PRTSTLMap          <Address>      Type

PRTSTLMap2        <Address>      Type

PRTSTLList           <Address>      Type

PRTSTLVector       <Address>      Type

详细的每个命令的使用情况请参照如下的demo:

如下为!PRTSTLMap使用方法:

0:000>!PRTSTLMap (0x0012fd14+0x194) std::pair<int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
Size of map is: 5
TestSTLMap!std::pair<int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x000 first            : 11
   +0x004 second           : std::basic_string<char,std::char_traits<char>,std::allocator<char> >
      +0x000 _Alval           : std::allocator<char>
      =00400000 npos             : 0x905a4d
      +0x004 _Bx              : std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Bxty
         +0x000 _Buf             : [16]  "aaa"
         +0x000 _Ptr             : 0x00616161  ""
      +0x014 _Mysize          : 3
      +0x018 _Myres           : 0xf
TestSTLMap!std::pair<int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x000 first            : 12
   +0x004 second           : std::basic_string<char,std::char_traits<char>,std::allocator<char> >
      +0x000 _Alval           : std::allocator<char>
      =00400000 npos             : 0x905a4d
      +0x004 _Bx              : std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Bxty
         +0x000 _Buf             : [16]  "bbb"
         +0x000 _Ptr             : 0x00626262  ""
      +0x014 _Mysize          : 3
      +0x018 _Myres           : 0xf
TestSTLMap!std::pair<int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x000 first            : 13
   +0x004 second           : std::basic_string<char,std::char_traits<char>,std::allocator<char> >
      +0x000 _Alval           : std::allocator<char>
      =00400000 npos             : 0x905a4d
      +0x004 _Bx              : std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Bxty
         +0x000 _Buf             : [16]  "ccc"
         +0x000 _Ptr             : 0x00636363  "w"
      +0x014 _Mysize          : 3
      +0x018 _Myres           : 0xf
TestSTLMap!std::pair<int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x000 first            : 14
   +0x004 second           : std::basic_string<char,std::char_traits<char>,std::allocator<char> >
      +0x000 _Alval           : std::allocator<char>
      =00400000 npos             : 0x905a4d
      +0x004 _Bx              : std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Bxty
         +0x000 _Buf             : [16]  "dddd"
         +0x000 _Ptr             : 0x64646464  "--- memory read error at address 0x64646464 ---"
      +0x014 _Mysize          : 4
      +0x018 _Myres           : 0xf
TestSTLMap!std::pair<int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x000 first            : 15
   +0x004 second           : std::basic_string<char,std::char_traits<char>,std::allocator<char> >
      +0x000 _Alval           : std::allocator<char>
      =00400000 npos             : 0x905a4d
      +0x004 _Bx              : std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Bxty
         +0x000 _Buf             : [16]  "eeeee"
         +0x000 _Ptr             : 0x65656565  "--- memory read error at address 0x65656565 ---"
      +0x014 _Mysize          : 5
      +0x018 _Myres           : 0xf

如下为扩展名伶!PRTSTLList的使用方法:

0:000> !PRTSTLList (0x0012fd14+0x178) CPoint
    Size of list is: 3
TestSTLMap!CPoint
   +0x000 x                : 7
   +0x004 y                : 8
TestSTLMap!CPoint
   +0x000 x                : 4
   +0x004 y                : 6
TestSTLMap!CPoint
   +0x000 x                : 6
   +0x004 y                : 9

如下为扩展名伶! PRTSTLVector的使用方法:

0:000> !PRTSTLVector (0x0012fd14+0x1d4) CPoint
Size of Vector is: 6
TestSTLMap!CPoint
   +0x000 x                : 17
   +0x004 y                : 88
TestSTLMap!CPoint
   +0x000 x                : 16
   +0x004 y                : 87
TestSTLMap!CPoint
   +0x000 x                : 15
   +0x004 y                : 86
TestSTLMap!CPoint
   +0x000 x                : 14
   +0x004 y                : 85
TestSTLMap!CPoint
   +0x000 x                : 13
   +0x004 y                : 84
TestSTLMap!CPoint
   +0x000 x                : 12
   +0x004 y                : 83
总结

本文实现了几种扩展WINDBG STL调试支持的扩展命令,旨在方便STL调试,算是抛砖引玉,希望打开大家的视野,其实好多时候在某些领域我们是可以做一些事情的,不要总跟在老外的后面走,由于需要使用这些扩展命令的人不多,本文不会附上相关的扩展文件,如果有人需要,请私信。
========

windbg 脚本学习总结相关推荐

  1. Python for虚幻引擎编辑器工具脚本学习教程

    Python for Unreal Engine Editor Tools Scripting MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz,2 Ch 语言:英语+中英 ...

  2. 按键精灵脚本 php,HTML_按键精灵 脚本-学习VBS的一个不错的教程,今天我就从总体上对VBS进行介 - phpStudy...

    按键精灵 脚本-学习VBS的一个不错的教程 今天我就从总体上对VBS进行介绍,希望能给大家一个直观的印象.另外,讲解与按键官网的按键精灵教程(李悦制作)相结合,帮大家看懂这个东东. 一.概述 要让计算 ...

  3. Shell脚本(学习笔记1)

    shell脚本学习记录 为什么学习shell脚本? 在一些复杂的linux维护工作过程中,大量的重复性的输入和交互操作不但费时费力,而且容易出现错误:然而编写shell脚本程序,可以批量处理.自动化的 ...

  4. linux用两种方法找到cuond,很基础的Linuxshell脚本学习.doc

    很基础的Linuxshell脚本学习 详细介绍Linux shell脚本基础学习(一) 2010-03-05 10:24 佚名 互联网 我要评论(9) 字号:T | T Linux shell脚本基础 ...

  5. BASH命令和SHELL脚本学习

    BASH命令和SHELL脚本学习 转载于:https://www.cnblogs.com/huolong123/p/6228049.html

  6. Windbg脚本和扩展工具开篇

    好长一段时间没写文章了,最近一直忙于为项目的可调式性做一些脚本和扩展工具,鉴于对windbg强大威力的震撼,以及相对较少的资料,笔者决定写一系列关于如何开发Windbg脚本和扩展命令的文章,您的支持是 ...

  7. linux perl 单例模式,Perl脚本学习经验(三)--Perl中ftp的使用

    使用use Net::FTP; Demo: my $Server = '192.168.1.1'; my $User = 'admin'; my $Password = 'admin'; my $ft ...

  8. shell 脚本学习及troubleshooting

    shell 脚本学习及troubleshooting Shell问题一: $ FILENAME="My Document" 含有空格的文件名 $ ls $FILENAME 列出来试 ...

  9. Shell脚本学习-阶段二

    文章目录-Shell脚本学习阶段二 前言 shell脚本实操2 1.获取随机字符串或数字 2.定义一个颜色输出字符串函数 3.批量创建用户 4.检查软件包是否安装 5.检查服务状态 6.检查主机存活状 ...

最新文章

  1. Linux下JDK环境的配置
  2. 第十七讲 利用傅里叶级数求特解
  3. 换个角度看敏捷1-敏捷问题解决方式
  4. tonardo做web服务器播放大视频内存泄露问题的解决
  5. ASP.NET2.0_缓存
  6. 【软件project】之第五、六章总结
  7. 如何更改java应用程序标题栏默认图标
  8. cvCreateImage函数与cv
  9. C4D四视图切换及基本操作
  10. xy苹果助手未受信任_【iOS教程】不用电脑 安装苹果软件
  11. 计算机教室管理员应知应会,教室管理
  12. linux下如何统计一个目录下的文件个数以及代码总行数的命令
  13. Windows 10 下生成 ssh 密钥
  14. python进程已结束,退出代码 -1073740791 (0xC0000409)解决办法
  15. 1968:Misspelling
  16. 经典的开源免费网络游戏:planeshift
  17. S5PV210开发 -- TTL和CMOS电平
  18. 奥普泰智能物联网锁解决方案
  19. 动物电子标签阅读器识读器L8600系列选型与安装注意事项
  20. 计算机论文初稿,科学网—博士论文初稿完成 - 周涛的博文

热门文章

  1. Chrome 浏览器降级后浏览网站不保留用户数据问题原因及解决方法
  2. Java 技术篇-java连接并操作数据库实例演示,执行查询、插入、更新和删除操作
  3. 智能车声标定位相关算法优化
  4. fullfile函数作用
  5. Mat与IplImage*类型间的转换
  6. 【跟网上的大多数不一样】rstudio plot不显示图片了
  7. 服务器可以pyqt显示吗,用pyqt+socket实现远程操作服务器的一个例子,PyQtsocket,方法,示例...
  8. 现在c++链接oracle,C++ 连接Oracle
  9. 【HDU6701】Make Rounddog Happy【权值线段树+双向单调队列】
  10. (转载)The shortest, fastest, and easiest way to compare two tables in SQL Server: UNION