最近需要在访问指定文件时中断下来,但不知道如何下断,在网上搜索了一番无果,只好自己摸索了。听大侠说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
      .echo
      bp kernel32!WriteFile \"
      r @$t2=poi(esp+4)
      .if(@$t2!=@$t1) {gc}
      \"
    }
    ad ${/v:$FileName}
    gc
  
  }
  .else 
  {
    .echo No Match...
    ad ${/v:$FileName}
    gc
  }
}
"
运行结果截图:
 

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