Библиографическое описание:

Бутенко В. В., Бутенко Д. С. Механизмы отладки кода [Текст] // Актуальные вопросы технических наук: материалы III междунар. науч. конф. (г. Пермь, апрель 2015 г.). — Пермь: Зебра, 2015. — С. 12-14.

Отладка — важный этап разработки ПО, на котором обнаруживают, локализуют и устраняют ошибки. Не менее важен инструмент отладки и понимание принципов его работы.

 

Введение

Отладчик — это инструмент анализа программ в реальном времени, позволяющий выполнять пошаговую трассировку, отслеживать значения переменных в процессе выполнения программы, обрабатывать точки прерывания, манипулировать таблицами символов и т.д [1].

Всё семейство отладчиков можно разделить на три группы: отладчики режима пользователя, отладчики режима ядра и эмулирующие отладчики.

Отладчики режима ядра

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

Регистры отладки введены в процессоры начиная с Intel 80386. Они являются привилегированным ресурсом и доступны программе либо в режиме реальной адресации, либо в защищённом режиме с наивысшим уровнем привилегий (CPL = 0).

Всего существует восемь регистров отладки (DR0 — DR7). Они позволяют ставить контрольные точки на чтение и запись областей памяти и портов ввода-вывода.

DR7 является управляющим регистром, координирующим работу точек останова.

Биты 0–7 (Li/Gi) — разрешения локальных и глобальных контрольных точек.

Биты 8–9 (Le/Ge) — соответствие контрольных точек данных.

Бит 13 (GD) — предназначен для защиты отладчика (запрет доступа к регистрам отладки).

Биты 18–19, 22–23, 26–27, 30–31 (LENi) — задают длину проверяемого значения для каждой контрольной точки.

Биты 16–17, 20–21, 24–25, 28–29 (Rwi) — задают для каждой контрольной точки тип действия, при котором она должна сработать.

Биты 10–12, 14–15 — зарезервированы.

DR6 содержит информацию о том, какая из точек останова вызвала особую ситуацию отладки.

Биты 0–3 (B0 — B3) — устанавливаются при встрече соответствующей контрольной точки.

Бит 13 (BD) — устанавливается когда следующая команда читает или записывает один из отладочных регистров.

Бит 14 (BS) — устанавливается, если ловушка отладки генерируется из-за включенного пошагового режима.

Бит 15 (BT) — устанавливается перед входом в обработчик прерываний отладки.

Биты 4–12, 16–31 — зарезервированы.

DR5 — DR4 зарезервированы, любая попытка доступа к этим регистрам будет сопровождаться 6h прерыванием (неопределённый код операции).

DR3 — DR0 содержат линейные адреса четырёх точек останова. Сравнение этих адресов производится до трансляции физического адреса. Каждая из четырёх точек останова отдельно описывается в регистре DR7.

Также в целях отладки используются некоторые специальные регистры MSR. Эти регистры не являются стандартными, их количество и назначение различны в различных моделях процессора. Как правило это регистры DebugCtlMSR (управляющий регистр), LastBranchToIP, LastBranchFromIP (регистры адресов последнего перехода, прерывания или особой ситуации), LastExceptionToIP, LastExceptionFromIP (регистры адресов последнего перехода перед последним прерыванием, особой ситуацией).

Отладчики режима пользователя

Отладчики этой группы используют отладочный интерфейс прикладного программирования Debugging API и взаимодействуют с процессором через операционную систему.

Этот интерфейс позволяет создать событийно-управляемый отладчик. Это значит, что такой отладчик будет получать управление с возникновением некоторого события в процессе отладки.

Принципы взаимодействия с Debugging API

Debugging API позволяет загрузить программу, подсоединиться к ней для отладки, получить низкоуровневую информацию (ID процесса, адрес входной точки и т. д.), получить уведомления о запуске или уничтожении процесса.

Чтобы создать пpоцесс для отладки, необходимо вызвать функцию CreateProcess с флагом DEBUG_PROCESS. Windows будет посылать уведомления о важных отладочных событиях, котоpые пpоисходят в отлаживаемом пpоцессе. Он будет немедленно замоpожен, пока пpогpамма не выполнит то, что должна. Если отлаживаемый пpоцесс создаст дочеpние пpоцессы, Windows также будет посылать уведомления о пpоисходящих в них отладочных событиях. Отключить эту возможность можно заменив флаг DEBUG_PROCESS на DEBUG_ONLY_THIS_PROCESS.

Подсоединиться к уже выполняющемуся пpоцессу возможно с помощью функции DebugActiveProcess.

Когда пpоцесс создан или пpисоединён, он замоpаживается, пока пpогpамма не вызовет WaitForDebugEvent. Функция блокиpует вызывающий поток, пока не пpоизойдет ожидаемое событие или не истечёт заданный вpеменной интеpвал. Для продолжения выполнения отлаживаемого процесса служит функция ContinueDebugEvent. Она продолжает выполнение потока, который был остановлен произошедшим событием. Подобный цикл должен повторяться пока отлаживаемый процесс не завершится.

Как только начнётся отладка программы, отсоединиться от процесса будет невозможно до его завершения.

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

Когда случается отладочное событие, Windows замоpаживает отлаживаемый пpоцесс, сохраняя значения регистров. Получить эти значения возможно с помощью функции GetThreadContext, а изменить их функцией SetThreadContext [3].

Трассировка отлаживаемого процесса

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

Для перехода в пошаговый режим вызываем GetthreadContext, указав CONTEXT_CONTROL в ContextFlags, чтобы получить значение флагового pегистpа. Устанавливаем trap-бит в поле regFlag стpуктуpы CONTEXT. Вызываем SetThreadContext. Ждем отладочного события. Отлаживаемый пpоцесс будет запущен в пошаговом pежиме. После выполнение каждой инстpукции будут получены значение EXCEPTION_DEBUG_EVENT, EXCEPTION_SINGLE_STEP и trap-флаг очистится. Для трассировки следующей функции необходимо установить trap-флаг снова [4].

Эмулирующие отладчики

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

Эмулирующие отладчики находятся ниже виртуального процессора, для отлаживаемого процесса они совершенно невидимы. Кроме того, они не используют никаких ресурсов эмулируемого процессора. А благодаря дополнительному кольцу защиты, работая в котором гипервизор может перехватывать все события, требующие внимания с его стороны [5].

Эмулирующий отладчик интерпретирует и выполняет машинные инструкции программы (например, вместо mov bx, 4 они присваивают переменной, соответствующей регистру bx — скажем, reg_bx — число 4). Существуют также отладчики с неполной эмуляцией, которые эмулируют только опасные команды, а остальные выполняют на реальном процессоре.

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

Заключение

Отладчик является активным инструментом и позволяет проследить процесс выполнения по шагам, получая в любой момент всю информацию о текущем состоянии программы или вносить изменения в порядок ее выполнения. Важно понимать механизм его работы, хотя бы для того, чтобы понимать как выполняется программа и как повлиять на процесс её выполнения, замораживая процессы, потоки и изменяя его алгоритм.

 

Литература:

 

1.      Отладчик ядра [Электронный ресурс] URL: https://ru.wikipedia.org/wiki/Отладчик_ядра

2.      Архитектура и система команд микропроцессоров x86 [Электронный ресурс] URL: http://www.club155.ru/programming/

3.      Basic Debugging. Debugging Reference [Электронный ресурс] URL: https://msdn.microsoft.com/en-us/library/windows/desktop/ms679276 %28v=vs.85 %29.aspx

4.      Win32 API — Debugging API [Электронный ресурс] URL: http://www.wasm.ru/wault/article/show/1001028

5.      Искусство дизассемблирования. К. Касперски, Е. Рокко. Спб.: БХВ — Петербург, 2008. — 896 стр. ISBN 978–5-9775–0082–1

6.      Программирование на ассемблере на платформе x86–64. Аблязов Р. Спб.: ДМК Пресс, 2011. — 304 стр. ISBN 978–5-94074–676–8

7.      Ассемблер — это просто. Калашников О. Спб.: БХВ — Петербург, 2011. — 336 стр. ISBN 978–5-9775–0591–8

8.      Реверсинг и защита программ от взлома. Панов А. Спб.: БХВ — Петербург, 2006. — 207 стр. ISBN 5–94157–889–7

9.      Создание защищённых от вторжения прикладных программ. Джеймс К., Прайс М. Спб.: ДМК Пресс, 2009. — 457 стр. ISBN 978–5-94074–466–5

10.  Программирование на ассемблере. Одиноков В., Колубинский В. Спб: Горячая Линия — Телеком, 2011. — 312 стр. ISBN 978–5-99–12–0162–9

11.  Assembler. Программирование на языке ассемблера IBM PC. Пильщиков В. Спб.: Далог МИФИ, 2005. — 301 стр. ISBN 5–86404051–7

12.  Ассемблер для Win32. Галисеев Г. Спб.: Вильямс, 2007. — 368 стр. ISBN 978–5-8459–11–97–1

Обсуждение

Социальные комментарии Cackle