Debugging Tools for Windows

调试器命令程序示例

本主题的下面一小节描述了调试器命令程序的示例

使用 .foreach 标记

下面的例子是用.foreach标记搜索word值然后将他们以DWORD和字符的方式显示出来。

0:000> .foreach (place { s-[1]w 77000000 L?4000000 5a4d }) { dc place L8 } 

s (Search Memory)命令和-[1]选项一起使得输出只包含它找到的地址,而不是在这些地址上找到的值。

下面的命令显示所有位于0x77000000 到0x7F000000地址范围内的模块的详细信息。

0:000> .foreach (place { lm1m }) { .if ((${place} >= 0x77000000) & (${place} <= 0x7f000000)) { lmva place } } 

lm (List Loaded Modules)1m选项 使得它的输出只包含模块地址,而不是模块的完整描述。

上面的例子使用${ }  (Alias Interpreter)标记来确保别名即使和其他文本连接在一起也能被正确替换。如果不这样,和place 连在一起的圆括号可能使得别名无法被替换掉。注意${}对于.foreach 和真实的别名使用的变量都起效。

遍历进程列表

下面的示例遍历内核模式的进程列表并显示列表中所有入口的可执行文件名。

该示例应该保存在文本文件中并用$$>< (Run Script File)命令执行。该命令加载整个文件,用分号替换所有回车然后再执行。该命令使得可以通过使用多行和缩进来编写更具可读性的程序,而不是将所有程序挤压到单独一行中。

该示例使用了下面一些特性:

$$  Get process list LIST_ENTRY in $t0.
r $t0 = nt!PsActiveProcessHead

$$  Iterate over all processes in list.
.for (r $t1 = poi(@$t0);
      (@$t1 != 0) & (@$t1 != @$t0);
      r $t1 = poi(@$t1))
{
    r? $t2 = #CONTAINING_RECORD(@$t1, nt!_EPROCESS, ActiveProcessLinks);
    as /x Procc @$t2

    $$  Get image name into $ImageName.
    as /ma $ImageName @@c++(&@$t2->ImageFileName[0])

    .block
    {
        .echo ${$ImageName} at ${$Procc}
    }

    ad $ImageName
    ad Procc
}

遍历LDR_DATA_TABLE_ENTRY 链表

下面的例子遍历用户模式的LDR_DATA_TABLE_ENTRY 链表,并显示每个链表入口中的基地址和完整路径。

和上面的示例一样,需要将程序保存到文本文件中,并用$$>< (Run Script File)命令执行。

该示例使用了下面一些特性:

$$ Get module list LIST_ENTRY in $t0.
r? $t0 = &@$peb->Ldr->InLoadOrderModuleList
 
$$ Iterate over all modules in list.
.for (r? $t1 = *(ntdll!_LDR_DATA_TABLE_ENTRY**)@$t0;
      (@$t1 != 0) & (@$t1 != @$t0);
      r? $t1 = (ntdll!_LDR_DATA_TABLE_ENTRY*)@$t1->InLoadOrderLinks.Flink)
{
    $$ Get base address in $Base.
    as /x ${/v:$Base} @@c++(@$t1->DllBase)
    
    $$ Get full name into $Mod.
    as /msu ${/v:$Mod} @@c++(&@$t1->FullDllName)
 
    .block
    {
        .echo ${$Mod} at ${$Base}
    }
 
    ad ${/v:$Base}
    ad ${/v:$Mod}
}

Build machine: CAPEBUILD