Фундаментальные основы хакерства

Как работает отладчик


…древним с их мыслящими машинами было куда легче.

Френк Херберт "Дюна"

Бороться с отладчиком, не представляя себе, как он работает, было бы по меньшей мере наивно, поэтому, ниже будут рассмотрены базовые принципы, лежащие в его основе. На всеобъемлимость это изложение не претендует, но позволяет читателю составить общее представление о вопросе. Технические подробности исчерпывающе изложены в главе "Debugging and Performance Monitoring" технического руководства "Intel Architecture Software Developer's Manual Volume 3: System Programming Guide", бесплатно распространяемого фирмой Intel.

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

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

Да и есть ли смысл их создавать? Микропроцессоры Pentium предоставляют в распоряжение разработчика богатейшие отладочные возможности, позволяющие контролировать даже привилегированный код! Они поддерживают пошаговое исполнение

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

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

Следует обратить внимание на одно важное обстоятельство: после выполнения команды, модифицирующей значение регистра SS, отладочное исключение не генерируется! Отладчик должен уметь распознавать такую ситуацию и самостоятельно устанавливать точку останова на следующую инструкцию.
В противном случае, войти в процедуру, предваренную инструкцией POP SS (например, так: PUSH SS; POP SS; CALL MySecretProc), автоматический трассировщик не сможет. Не все современные отладчики учитывают эту тонкость, и такой прием, несмотря на свою архаичность, может оказаться далеко не бесполезным.

Четыре отладочных регистра DR0-DR3 хранят линейные адреса четырех контрольных точек, а управляющий регистр DR7 содержит для каждой из них условие, при выполнении которого процессор генерирует исключение INT 0x1, передавая управление отладчику. Всего существует четыре различных условия: прерывание при выполнении команды, прерывание при модификации ячейки памяти, прерывание при чтении

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

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

Если бит Т в TSS отлаживаемой задачи установлен, то при каждом переключении на нее будет генерироваться отладочное исключение до выполнения первой команды задачи. Чтобы предотвратить собственное обнаружение, отладчик может отслеживать всякие обращения к TSS и возвращать программе подложные данные. Необходимо заметить – Windows NT по соображениям производительности не использует TSS, (точнее использует, но всего один) и эта отладочная возможность для нее совершенно бесполезна.

Программная точка останова – единственное, что нельзя замаскировать, не прибегая к написанию полноценного эмулятора процессора. Она представляет собой однобайтовый код 0xCC, который, будучи помещенным в начало инструкции, вызывает исключение INT 0x3 при попытке ее выполнения.Отлаживаемой программе достаточно подсчитать свою контрольную сумму, чтобы выяснить: была ли установлена хоть одна точка останова или нет. Для достижения этой цели она может воспользоваться командами MOV, MOVS, LODS, POP, CMP, CMPS или любыми другими, - никакому отладчику невозможно их всех отследить и проэмулировать.

Настоятельно рекомендуется использовать программные точки останова в тех, и только в тех случаях, когда аппаратных уже не хватает. Однако, практически все современные отладчики (в том числе и SoftIce) всегда устанавливают программные точки останова, а не аппаратные. Это обстоятельство может быть с успехом использовано в защитных механизмах, примеры реализаций которых приведены в разделе "Как противостоять трассировке".


Содержание раздела