Отладчик SoftIce

SoftIce - это универсальный отладчик, которым можно проанализировать и отладить любой код, включая подпрограммы прерывания и драйверы ввода-вывода. SoftIce состоит из отладчика уровня ядра (kernel mode debugger) (собственно это и есть отладчик) и загрузчика отладочной информации (Symbol Loader). Утилита Symbol Loader (SL) загружает отладочную информацию для вашего модуля, позволяет настроить SoftIce, и дает возможность записать историю команд (history buffer) в файл.

Возможности SoftIce:

Утилита SL позволяет прочитать отладочную информацию из отлаживаемых программ (EXE, DLL, VxD, 386, OCX) и загрузить ее в отладчик, запустить ваше приложение и автоматически установить точку останова на точку входа в программу, записать в файл протокола отладки.

В поставку SoftIce входит пример GdiDemo.

1. Загрузка отлаживаемой программы

SL оттранслирует отладочную информацию в .NMS файл, загрузит исходные файлы, запустит отлаживаемую программу (в данном случае Gdidemo) и всплывет в SoftIce, где вы увидите исходный текст программы.

Подсвеченная строка с номером 35 - это точка входа (entry point) в вашу программу. Если SL вывел сообщение типа "An error occured during symbol translation/load", значит в отлаживаемом файле отсутствует отладочная информация, жмите OK и наслаждайтесь [диз]ассемблером.

2. Управление SoftIce'ом

Если все сделано правильно, то вы должны увидеть SoftIce разбитый на несколько окон. Верхнее окно - Register Window (окно регистров) - показывает состояние рабочих регистров процессора. Под ним находится окно данных Data Window, в нем вы можете посмотреть или отредактировать дамп памяти. Ниже находится Code Window (окно кода) - в нем находится исходный текст программы (если вы загрузили отладочную информацию), или дизассемблированный код программы. В самом низу находится окно команд - Command Window, в нем вы можете вводить команду и видеть результат их выполнения. Самая нижняя строчка - строка помощи, в ней при вводе подсвечиваются возможные варианты команд и их синтаксис. Удобнее всего управлять SoftIce с помощью мышки.

Изменение размера окна - подведите курсор к нижней границе того окна, которому хотите изменить размер или закрыть его, нажмите левую кнопку мыши и ведите ее вниз (увеличение размера) или вниз (уменьшение размера), если хотите закрыть окно, подведите нижнюю границу к верхней, в окне появится фраза Close current window и окно исчезнет.
Примечание: нельзя изменить размер окна регистров и FPU.

Скроллинг на одну строку - подведите курсор к маленьким стрелочкам, расположенным у границ того окна, которое хотите скроллировать и жмите левую кнопку мыши (стрелочки появляются, если размер окна больше или равен двум строчкам).

Скроллинг на экран - подведите курсор к большим стрелочкам расположенным внутри того окна, которое хотите скроллировать и жмите левую кнопку мыши (стрелочки появляются, если размер окна больше или равен четырем строчкам).

Изменение значений регистров - подведите курсор к тому регистру, значение которого хотите изменить, нажмите левую кнопку мыши и введите число, если нужно изменить одну цифру, то подводите курсор к этой цифре и меняйте.

Изменение значений флагов - подведите курсор к тому флагу, который хотите изменить, нажмите левую кнопку мыши, после чего клавишей Ins можно изменить значение флага на противоположное (маленькая буква означает, что флаг не установлен, большая установлен).

Изменение значений ячеек памяти - подведите курсор к тому байту (слову, двойному слову и т.д.), которое хотите изменить, нажмите левую кнопку мыши и вводите ваше значение, если хотите изменить одну или несколько цифр в числе, то подведите курсор с помощью клавиатуры к нужным числам и меняйте.
Примечание: во всех случаях изменения значений они вступают в силу после того, как вы переключитесь в любое другое окно, до этого можно отменить последнее изменение, нажав Esc.

Установка точек останова на исполнение - подведите курсор к той строке в Code Window, в которой хотите остановиться, и двойным щелчком по левой кнопке мыши поставьте точку останова, строка подсветится.

Удаление из Watch Window переменных - установите курсор на переменную, которую хотите удалить, нажмите левую кнопку мыши, переменная подсветиться, нажмите кнопку Del - переменная исчезнет.

Контекстное меню - по правой кнопке мыши вы попадаете в контекстное меню, в котором вам доступны команды:

3. Трассировка программы

Воспользуйтесь командой T (trace) для того чтобы оттрассировать одну команду, или клавишей F8, которая закреплена по умолчанию за командой T. Произойдет выполнение команды находящейся в текущей строке и курсор перейдет на следующую строку и подсветит ее. Это строка:

LpszLine=LpszLine;

Еще раз нажмите F8, курсор передвинется на следующую строку:

if(!hPrevInst).

В Code Window вы видите исходный текст программы (source mode). Если вы хотите посмотреть дизассемблированный (code mode) текст программы или исходный и дизассемблированный (mixed mode) текст вместе, воспользуйтесь командой SRC или клавишей F3 закрепленной за этой командой. При первом нажатии вы увидите смешанный (исходный текст программы и ассемблерные инструкции, из которых состоит эта строка) текст, при втором нажатии дизассемблированный, третье нажатие вернет вас в режим просмотра исходного текста программы.

Нажмите еще раз F8 и вы перейдете к строке

if(!RegisterAppClass(hInst));

Для того, что бы отлаживать программу вы пользуетесь командой Т, которая исполняет один оператор исходной программы или одну машинную команду.

Еще существует команда P или клавиша F10, которая выполняет один шаг в программе т.е. при трассировке какой-либо функции или прерывания, вы не получите управление до тех пор пока выполнение функции не завершится и вы не вернетесь из функции обратно. Команду P удобно применять в том случае, когда вы отлаживаете основной алгоритм и отвлекаться на трассировку каждой процедуры нерационально.
Примечание: командой T нельзя оттрассировать системные вызовы (WIN32 API calls) находясь в source mode, для их трассировки нужно перейти в mixed или code mode.

4. Просмотр локальных переменных

Окно Locals Window показывает текущий кадр стека. В нашем случае он содержит локальные переменные для функции WinMain.

Командой T войдите в функцию RegisterAppClass, окно Locals Window станет пустым, так как для этой функции еще не определены локальные переменные. Функция RegisterAppClass находится в файле INIT.C. SoftIce показывает текущий файл в левом верхнем углу Code Window

Введите команду T снова, окно Locals Window будет содержать параметр переданный функции RegisterAppClass (hInstance) и локальную структуру wndClass. Перед структурой стоит знак плюс, который означает что внутри находятся переменные, которые можно посмотреть (так же можно смотреть строковые переменные и массивы). Посмотреть структуру можно двойным щелчком мыши. Знак + сменится на - и, вы увидите переменные, из которых состоит структура. Закрыть структуру можно также двойным щелчком мыши.

5. Установка точек останова на выполнение

Точки останова на выполнение делятся на два вида: просто точки останова и однократные точки останова.

Однократные точки останова

Перейдите в Code Window, используя клавишу PgDn переместите курсор на строку с номером 61 (тоже самое можно сделать используя команду U .61), в этой строке находиться первый вызов функции Win32 API RegisterClass. Используя команду HERE (клавиша F7) выполните программу до этой строки.

Команда HERE устанавливает точку останова в программе на адрес или строку на которой находиться курсор и выполняет программу с текущего адреса до адреса на котором находиться курсор, т.е. до тех пор пока не сработает точка останова, после срабатывания SoftIce автоматически отключит эту точку останова, чтобы она больше не срабатывала.

Текущей строкой в отлаживаемой программе стала строка:

If(!RegisterClass(&wndClass))
Примечание: того же самого результата можно было добиться командой G .61 (исполнять программу до строки 61).

Обычные точки останова

Следующие шаги демонстрируют использование обычных точек останова, т.е. таких, которые будут срабатывать до тех пор пока вы их не отмените. Найдите следующий вызов функции RegisterClass, находящийся в строке 74. Установите курсор на эту строку и введите команду BPX (BreakPoint eXecutable) или клавишу F9 (по этой команде, в память на место команды расположенной под курсором записывается команда INT3, но вы этого не видите. Строка должна подсветиться. Снять точку останова можно повторным вводом этой же команды. Если вы счастливый обладатель процессора Pentium, то процесс установки и снятия точки останова сводится к двойному щелчку левой кнопкой на той команде, где хотите поставить точку останова. После установки точки останова запустите программу командой G или X (клавиша F5). Когда программа исполнит инструкцию INT3, она передаст управление SoftIce, после чего он появиться перед вами. Посмотреть информацию об установленных точках останова можно командой BL (BreakPoint List):

     :BL
     00) BPX #0137:00402442 (адрес может быть другим).

Видим что установлена одна точка останова на исполнение по адресу 00402442, с порядковым номером 0. По этому адресу находиться команда, расположенная в текущем файле INIT.C в строке 74. Вы можете использовать вычисление выражений, для того чтобы получить адрес строки по ее номеру:

     :? .74
     void * = 0x00402442

Так как дальнейшая пошаговая трассировка функции RegisterAppClass не имеет для нас смысла, вернемся в то место, откуда эта функция вызывалась. Для этого существует команда P с параметром RET (клавиша F12). Она позволяет выполнять программу до тех пор пока не встретит команду RET (RETF), исполнив эту команду SoftIce выйдет из подпрограммы и остановиться на строке следующей за вызовом этой подпрограммы. Применительно к нашей программе: функция RegisterAppClass вызывается из функции WinMain, SoftIce остановиться в функции WinMain на строке следующей за вызовом функции RegisterAppClass, т.е. подсвечена будет строка:

Msg.wParam = 1;

Воспользуйтесь командой BC (Breakpoint Clear) с номером точки останова, который вы посмотрели с помощью команды BL, для того, что бы убрать именно эту точку останова, или вместо номера введите *, тогда вы уберете все точки останова (можно вводить номера точек останова через запятую, если хотите убрать несколько точек).

6. Использование информационных команд

SoftIce имеет в своем распоряжении много разных команд, с помощью которых можно узнать состояние и получить иную информацию об операционной системе и запущенных в ней приложениях. Мы рассмотрим только две команды: H (Help) и CLASS. Эти команды выводят достаточно много информации в окне команд (Command Window), поэтому желательно увеличить размер этого окна, для этого закроем окно локальных переменных (Locals Window).

По команда H можно получить помощь по всем командам SoftIce или более подробную информацию о конкретной команде, если введете ее имя в качестве аргумента команды H:

     :H CLASS
     Display window class information
     CLASS [-x] [task-name]
     Ex: CLASS USER

Первая строка дает описание команды, вторая показывает информацию о синтаксисе и аргументах, которые могут использоваться в команде, третья строка содержит пример использования команды.

Целью выполнения функции RegisterAppClass является регистрация шаблона для классов окон, которые будут использованы приложением GdiDemo для создания окон. Используя команду CLASS можно посмотреть зарегистрированные классы для GdiDemo:

:CLASS GDIDEMO

Результатом выполнения данной команды является информация о каждом зарегистрированном классе окна. Информация включает в себя: имя класса, адрес внутренней структуры данных WNDCLASS, модуль который зарегистрировал данный класс, адрес процедуры, которая обслуживает данный класс и состояние флагов стиля класса. Для получения подробной информации воспользуйтесь ключом -X.

  Handle  Class Name  Owner    WndwProc       Styles
  --------------------------Application Private-------------------------------
  5110    ???         GDIDEMO  2E9F:00000114  07000003
  40AC    ???         GDIDEMO  2E9F:000000FE  07000003
  409C    ???         GDIDEMO  2E9F:000000E8  03000003
  3BC4    ???         GDIDEMO  2E9F:000000D2  03000003
  3BB4    ???         GDIDEMO  2E9F:000000BC  07000003
  3A00    ???         GDIDEMO  2E9F:000000A6  07000003

7. Символьные имена

Когда вы загружаете приложение с отладочной информацией, SoftIce автоматически создает таблицу символьных имен которая содержит все имена, определенные в данном приложении. Используя команду TABLE можно посмотреть какие таблицы символьных имен загружены в настоящий момент:

     :TABLE
     GDIDEMO [NM32]
     0001044741 Bytes Of Symbol Memory Available

Используемая в данный момент таблица символьных имен выделена цветом. Если текущая таблица символьных имен не соответствует той на которую ссылается ваше приложение, то используя команду TABLE с именем вашего приложения в качестве аргумента, вы подключите нужную таблицу (если для вашего приложения создана таблица):

:TABLE GDIDEMO

Используя команду SYM вы можете посмотреть все символьные имена, определенные в текущей таблице (на экран выводятся по сегментам, внутри них в алфавитном порядке). Если интересует какие-то определенные имена, то используйте шаблоны:

     :SYM w*
     .text (0137:00401000, 000145C1 bytes)
     0137:004012E0 WinMain
     0137:00405700 WinMainCRTStartup
     0137:004013AD WndProc
     0137:0040AF50 wcslen
     0137:0040C160 wcsncnt
     0137:004107A0 wctomb
     0137:0040FA50 write_char
     0137:0040FAD0 write_multi_char
     0137:0040FB20 write_string

На экране список всех символьных имен начинающихся с буквы w, все они расположены в сегменте .text (выполняемый сегмент, он начинается с адреса 0137:00401000 и имеет длину 0145C1H байт), т.е. эти имена - имена функций и процедур входящие в приложение GDIDEMO. Данные находятся в сегментах .data, .rdata, .idata.

8. Условные точки останова

Одним из символов, определенных в приложении GDIDEMO, является функция LockWindowInfo. Назначение этой функции - возвращение адреса переменных, которые определяют свойства окна. Для того чтобы ознакомиться с условными точками останова и точками останова на доступ к памяти, мы выполним следующие действий:

  • Установим точку останова на функцию LockWindowInfo.
  • Отредактируем поставленную точку останова таким образом, чтобы она срабатывала по определенному нами условию.
  • Установим точку останова на доступ к ячейки памяти, для того чтобы отследить обращения к этой ячейке.

Установка точки останова на функцию LockWindowInfo.

Командой BPX LockWindowInfo поставим точку останова на выполнение на эту функцию. Каждый раз, когда в одном из окон приложения GDIDEMO нужно будет обновить информацию, программой будет вызываться функция LockWindowInfo, так как на эту функцию поставлена точка останова, то будет вызываться SoftIce. Командой BL проверьте, установилась ли точка останова. Запустите приложение командой X или G. Как только будет вызвана функция LockWindowInfo, SoftIce всплывет. Так как обновление происходит постоянно, то постоянно вызывается и SoftIce, что очень неудобно, если нас интересует обновление какого-либо конкретного окна. Чтобы перехватить вызов на обновление конкретного окна, к примеру, POLYDEMO, воспользуемся условной точкой останова. Из исходного текста программы (файл wininfo.c) видно, что функция LockWindowInfo получает в качестве входного аргумента один параметр HWND (Handle Window) - дескриптор окна, и возвращает в вызывающую функцию одно значение - указатель на переменные для данного окна. То есть если бы мы заставили срабатывать точку останова только на обработчик окна POLYDEMO, мы бы добились своей цели. Для начала нам необходимо узнать дескриптор нашего окна, для этого воспользуемся командой:

     :HWND GDIDEMO
     WindowHandle  hQueue  SZ Qowner  ClassName  WindowProcedure
     0724(1)       10FF    32 GDIDEMO GDIDEMO    365F:000001C4
     0728(2)       10FF    32 GDIDEMO MdiClient  17A7:00001988
     0734(3)       10FF    32 GDIDEMO BOUNCEDEMO 365F:00000232
     0730(3)       10FF    32 GDIDEMO POLYDEMO   365F:000001DA
     072C(3)       10FF    32 GDIDEMO DRAWEMO    365F:0000021c

Дескриптор окна POLYDEMO имеет значение 0730. Если в списке вы не увидели нужного окна, то запустите приложение клавишей X или G, опять сработает точка останова, проверьте, создалось ли окно, если нет, то повторите последние действия. Теперь можно останавливать исполнение программы только в том случае, когда в качестве параметра для функции LockWindowInfo используется значение 0730. В Windows параметры для функции обычно передаются через стек. При остановке в функции LockWindowInfo стек будет выглядеть следующим образом (посмотреть содержимое стека можно подведя курсор к регистру ESP, нажав правую кнопку мыши, вызвать контекстное меню и выбрать команду Display, неплохо бы еще сменить командой DD формат вывода данных в DataWindow на показ двойных слов, так как наше приложение 32-разрядное):

     ESP = 0055FC00
     013F:0055FC00 00404852 00000730 0055FC3C 00008CAA
  • Число 00404852 - это адрес, на который программа перейдет после завершения работы нашей функции (адрес возврата).
  • Число 00000730 - это дескриптор окна POLYDEMO (собственно то, что нас интересует).

Теперь зная, где и что у нас передается в функцию, мы можем выставить условную точку останова. Для этого вызовем на редактирование точку останова, поставленную на функции LockWindowInfo:

:BPE 0

В нижней строке командного окна появится строка:

:BPX LockWindowInfo

и курсор установится в конце строки. Теперь можете редактировать точку останова по своему усмотрению, в конце строки добавьте следующее условие:

IF ESP->4 == 00000730
и нажмите Enter.

Точка останова теперь будет выглядеть так:

:BPX LockWindowInfo IF ESP->4 == 00000730

То есть она будет срабатывать тогда, когда двойное слово по адресу ESP+4 будет равным числу 00000730, которое соответствует дескриптору окна POLYDEMO. Проверьте командой BL, соответствует ли точка останова заданной, запустите приложение и убедитесь, что вся эта конструкция замечательно работает.

Установим точку останова на доступ к первому двойному слову данных экземпляра окна POLYWINDOW. Как уже отмечалось выше, входным аргументом для функции LockWindowInfo является дескриптор окна, выходным - адрес данных экземпляра окна. После срабатывания точки останова, поставленной на функции LockWindowInfo с параметром соответствующим дескриптору окна POLYWINDOW, на выходе функции будем иметь адрес данных экземпляра окна POLYWINDOW, по этому адресу и поставим точку останова на доступ к памяти.

Для того чтобы получить адрес данных экземпляра окна, выполним программу до строки с номером 57 (в файле WININFO.C):

:G .57

Функция возвращает 32-битное значение (в нашем случае адрес) в регистре EAX, поэтому можно, используя команду BPMD (BreakPoint Memory Dword) и значение адреса в регистре EAX, поставить точку останова на доступ к первом слову данных экземляра окна POLYDEMO:

:BPMD EAX

Эта команда использует регистры аппаратной отладки встроенные в процессор для отслеживания чтения и записи двойного слова по указанному линейному адресу. В данном случае это первое слово данных экземпляра окна POLYDEMO. Используйте команду BL, чтобы проверить правильность установки точки останова.

     :BL
     00)    BPX       LOCKWINDOWINFO IF ((ESP->4)==0x00000730)
     01)    BPMD      #015F:0052006C  RW  DR3

Точка останова с номером 0 установлена на исполнение функции LockWindowInfo и точка останова с номером 1 стоит на доступ к памяти по адресу #015F:0052006C.

Отключите 0 точку останова командой BD (Breakpoint Disable):

:BD 0

В отличие от команды BC, которая удаляет точку останова, команда BD временно отключает точку, т.е. она не будет срабатывать, но ее в любой момент можно включить обратно командой BE (Breakpoint Enable). Отключенные точки останова выделяются звездочками рядом с порядковым номером точки останова.

     :BL
     00) * BPX       LOCKWINDOWINFO IF ((ESP->4)==0x00000730)
     01)   BPMD      #015F:0052006C  RW  DR3

Запустите SoftIce командами X или G, когда окно POLYDEMO попытается получить доступ к первому двойному слову экземпляра данных окна, сработает точка останова и SoftIce всплывет, это будет происходить в функциях PolyRedraw и PolyDrawBez. Эти функции обращаются к полю nBezTotal, которое находиться по нулевому смещению в данных экземпляра окна POLYDEMO. Значение этого поля задает количество кривых одновременно выводимых в окно POLYDEMO.

Примечание: Из-за особенностей архитектуры процессоров Intel перехват обращения к ячейки памяти произойдет после исполнения команды, обращающейся к памяти, т.е. SoftIce остановиться на следующей команде.

Сбросьте все точки останова командой BC * и выйдете из SoftIce.


Как заставить SoftIce pаботать?

Config.Sys: Device=c:\...\...\S-Ice.Exe
Затем запускаете Ldr.Exe <пpограмма>. У MS-DOS есть маленький баг: он неверно выставляет значение регистра SP - он уменьшает его значения на 2, и некоторые защиты, активно использующие стек заставляют повеситься задачу. Лечится правкой кода MS-DOS при загрузке INT 21h AX=4B01h или командой "R SP=SP+2"

Как заставить SoftIce/Win/W95 pаботать?

Отредактировать файл WINICE.DAT, дать возможность грузить отладчику символьную информацию из системных DLL-ей. (В Winice.DAT даются ссылки на USER.EXE, KRNL386.EXE, WIN386.EXE)

    exp=c:\win\system\user.exe
    exp=c:\win\system\gdi.exe
    exp=c:\win\system\krnl386.exe

Оглавление


© Колесников Дмитрий Геннадьевич Rambler's Top100 Учебник по СайтоСтроению