Поделиться через


Примеры командной программы отладчика

В следующих разделах описаны программы команд отладчика.

Использование маркера FOREACH

В следующем примере используется токен FOREACH для поиска значений WORD 5a4d. Для каждого найденного значения 5a4d отладчик отображает 8 значений DWORD, начиная с адреса, по которому было найдено 5a4d DWORD.

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

В следующем примере используется токен FOREACH для поиска значений WORD 5a4d. Для каждого найденного значения 5a4d отладчик отображает 8 значений DWORD, начиная с 4 байтов до адреса, по которому было найдено значение DWORD 5a4d.

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

В следующем примере отображаются те же значения.

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

Примечание Если вы хотите работать с именем переменной в части OutCommands команды, необходимо добавить пробел после имени переменной. Например, в приведенном выше примере существует пробел между местом переменной и оператором вычитания.

Параметр -[1] вместе с командой s (Search Memory) приводит к тому, что его выходные данные включают только найденные адреса, а не значения, найденные по этим адресам.

Следующая команда отображает подробные сведения о модуле для всех модулей, расположенных в диапазоне памяти от 0x77000000 до 0x7F000000.

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

Параметр 1m вместе с командой lm (List Loaded Modules) приводит к тому, что выходные данные включают только адреса модулей, а не полное описание модулей.

В предыдущем примере маркер ${ } (интерпретатор псевдонимов) используется для замены псевдонимов, даже если они находятся рядом с другим текстом. Если команда не включала этот маркер, открывающая круглая скобка, расположенная рядом с местом , не позволяет заменить псевдоним. Обратите внимание, что токен ${} работает с переменными, которые используются в foreach , и с истинными псевдонимами.

Обход списка процессов

В следующем примере рассматривается список процессов в режиме ядра и отображается имя исполняемого файла для каждой записи в списке.

Этот пример должен храниться в виде текстового файла и выполняться с помощью команды $$>< (запуск файла скрипта). Эта команда загружает весь файл, заменяет все возвращаемой каретки точкой с запятой и выполняет результирующий блок. Эта команда позволяет писать доступные для чтения программы с помощью нескольких строк и отступов вместо того, чтобы сжать всю программу в одну строку.

В этом примере показаны следующие возможности:

  • Псевдорегистры $t 0, $t 1 и $t 2 используются в качестве переменных в этой программе. Программа также использует псевдонимы с именами Procc и $ImageName.

  • Эта программа использует средство оценки выражений MASM. Однако маркер @@c++( ) отображается один раз. Этот маркер заставляет программу использовать вычислитель выражений C++ для анализа выражения в скобках. Это позволяет программе напрямую использовать токены структуры C++.

  • Флаг ? используется с командой r (Registers). Этот флаг присваивает типизированные значения псевдорегистрару $t 2.

$$  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 пользовательского режима и отображается базовый адрес и полный путь к каждой записи списка.

Как и в предыдущем примере, эта программа должна быть сохранена в файле и выполнена с помощью команды $$>< (Запуск файла скрипта).

В этом примере показаны следующие возможности:

  • Эта программа использует средство оценки выражений MASM. Однако в двух местах отображается токен @@c++( ). Этот маркер заставляет программу использовать вычислитель выражений C++ для анализа выражения в скобках. Это позволяет программе напрямую использовать токены структуры C++.

  • Флаг ? используется с командой r (Registers). Этот флаг присваивает типизированные значения псевдорегистрам $t 0 и $t 1. В тексте цикла $t 1 имеет тип ntdll!_LDR_DATA_TABLE_ENTRY\*, поэтому программа может создавать прямые ссылки на члены.

  • В этой программе используются псевдонимы с пользовательскими именами $Base и $Mod . Знаки доллара снижают вероятность того, что эти псевдонимы использовались ранее в текущем сеансе отладчика. Знаки доллара не являются обязательными. Маркер ${/v: } интерпретирует псевдоним буквально, предотвращая его замену, если он был определен до запуска скрипта. Этот маркер также можно использовать вместе с любым блоком, чтобы запретить использование определений псевдонимов до его использования.

  • Маркер .block используется для добавления дополнительного шага замены псевдонима. Замена псевдонима выполняется один раз для всего скрипта при его загрузке и один раз при вводе каждого блока. Без токена .block и его фигурных скобок команда .echo не получает значения $Mod и $Base псевдонимов, назначенных в предыдущих строках.

$$ 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}
}