Микропроцессоры семейства Intel 80х86 относятся к микропроцессорам с конвейерной архитектурой. Это означает, что из последовательности команд управляющее устройство микропроцессора выбирает блок из нескольких байт и образует из них очередь или конвейер. По мере выборки команд для исполнения следующие байты сдвигаются к началу очереди, а очередь с конца пополняется новыми байтами так, что длина очереди постоянна.
Можно предложить две группы средств защиты от трассировки. В первую группу входят средства блокировки работы самих отладчиков, делающие невозможным трассировку программы. Вторая группа средств направлена на определение факта работы программы под управлением отладчика. К первой группе средств относятся:
• блокировка специальных "отладочных" прерываний процессора;
• блокировка прерываний от клавиатуры:
• замер времени выполнения контрольных участков программы;
• использование прерывания таймера.
Напомним, что прерывания INT 1 и INT 3 (соответственно прерывание для пошаговой работы и прерывание по однобайтовой команде INT) интенсивно используются отладчиками. Первое прерывание позволяет отладчику получать управление после выполнения каждой команды трассируемой программы. С помощью второго прерывания отладчик может устанавливать точки останова, заменяя байты программы на команду однобайтового прерывания.
Защищенная от трассировки программа должна подготовить свои собственные обработчики для этих прерываний и "незаметно" для человека, занимающегося трассировкой, записать их адреса в таблицу векторов прерываний.
Эти обработчики прерываний могут не делать ничего, т. е. состоять из одной команды IRET, или выполнять какие-либо действия, фиксирующие факт работы программы под контролем отладчика.
В ходе трассировки программы (при ее пошаговом выполнении) вам необходимо нажимать на клавиши для перехода к очередной команде. Это можно использовать для блокировки отладчика.
Запретив прерывания командой CLI и переназначив клавиатурное прерывание на себя, программа установки может выполнять какие-либо действия, не, требующие работы оператора, производящего установку, с клавиатурой. Обработчик клавиатурного прерывания защищенной от трассировки программы должен фиксировать прерывания, например установкой флага.
Если программа работает не под контролем отладчика, прерывания во время этого участка программы невозможны (они запрещены командой CLI).
Если же используется режим пошагового выполнения программы под управлением отладчика, отладчик разрешает клавиатурные прерывания, невзирая на то, что была выдана команда CLI. Наш обработчик клавиатурного прерывания в этом случае зафиксирует работу в режиме трассировки. Аналогично можно воспользоваться прерыванием таймера. Защищенная программа устанавливает свой обработчик для прерывания таймера, который взводит флаг в случае прихода прерывания от таймера. В подходящий момент времени она запрещает прерывания и выполняет какую-либо работу, периодически проверяя флаг.
Если программа работает в пошаговом режиме, команда запрета прерываний CLI не работает и флаг будет взведен, сигнализируя о работе под контролем отладчика.
Последний метод, который мы рассмотрим, основан на использовании архитектурной особенности процессоров 8086, 80286, 80386 и 80486 - наличии конвейера команд.
Для увеличения производительности во всех этих процессорах используется предварительная выборка команд во внутреннюю очередь команд, располагающуюся физически на кристалле процессора.
Метод заключается в модификации команды программы, находящейся вблизи от той команды, которая выполняет эту модификацию.
Длина очереди составляет:
- для процессора Intel 8086 — 4 байта;
- для процессора Intel 80286 — 8 байт;
- для процессора Intel 80886— 16 байт.
Периодически в процессоре происходит сброс очереди. Это означает, что при выполнении некоторых команд следующие байты, уже загруженные в очередь, игнорируются, а очередь загружается сначала с указанного в предыдущей команде адреса. Сброс очереди производится следующими командами:
INT — вызов прерывания;
IRET — возврат из прерывания;
CALL — переход к подпрограмме;
RET — возврат из подпрограммы;
JMP, JE, ... — команды передачи управления.
Команды, занесенные в очередь, не могут быть модифицированы, поскольку очередь недоступна программным средствам. Отладочные средства при каждом шаге отладки сбрасывают очередь, т. е. в этом случае очередь содержит не более одной машинной команды. Данный эффект служит основой для построения надежного теста на определение совместной работы с. отладочными средствами.
Пусть имеется последовательность команд:
. . .
jmp metl ; сброс очереди
;-запись в индексный регистр адреса метки
001 metl:
mov bx,off set inet2 ;запись по адресу метки кода СЗ -— возврат
002 mov cs:[bx],OC3h
003 met2: nор; старое содержание — "нет операции»
Тогда при нормальной работе фрагмента программы команды 001—003 будут помещены в очередь и пересылка байта не окажет влияние на адрес по метке raet2 и будет выполнена команда пор. При работе отладочных средств или программных эмуляторов очередь будет последовательно очищаться после исполнения каждой команды, и по адресу метки raet2 будет послан, байт С8, и в точке 003 выполнится команда ret.
Очевидно, что «напрямую» данный метод применяться не может, поскольку легко может быть обойден путем выполнения старой команды. Поэтому, его надо применять в сочетании с другими методами, в частности, с методом динамического изменения кода программы во время ее выполнения при котором изменения кода программы не так заметны.
Если программа работает не под управлением отладчика, к моменту выполнения команды модификации байт модифицируемой команды уже находится во внутренней очереди команд. Следовательно, никакой модификации не произойдет.
Если же программа работает под управлением отладчика, то изменение байта команды будет выполнено правильно, так как очередь команд будет заполнена командами самого отладчика. Приведем программу, демонстрирующую описанный выше метод:
; Эта программа демонстрирует способ защиты от
; отладки, основанный на использовании архитектурной
, особенности процессорое 8086, 80286, 80386, 80486 -
, наличии внутреннего конвейера команд
TITLE BUG
.MODEL tiny . STACK 100h
.DATA
msg DB "Программа работает под управлением отладчика!".13, 10, "$"
. CODE . STARTUP
; Запрещаем прерывания
cli
; Вызываем программу проверки
call _TEST ; Разрешаем прерывания
sti . EXIT 0
; Программа проверки
_TEST PROC near
:Выполняем модификацию команды с меткой next.
' Вместо команды ret записываем команду пор.
mov BYTE PTR next, 90h next:
ret
Если модификация не произошла, это означает, что
' программа выполняется под управлением отладчика.
; Выводим сообщение о работе под контролем отладчика.
mov ah, 9h
mov dx, OFFSET msg
int 21h
ret
_TEST ENDP
END
Эта программа выводит сообщение о работе под управлением отладчика, если происходит изменение команды, и не выводит ничего, если изменения команды не происходит.
Метод был проверен для отладчиков Code View, AFD, АТ86. утилиты отладки MS-DOS DEBUG и дал положительные результаты.
Фрагмент пакета COPYLOCK
(использование конвейера шины данных микропроцессора)
??????????????????????????????????????????????????????????????
? . . . . . ?
?cs:07FF mov cx,40Eh ?
?cs:0802 mov di,08A6h ?
?cs:0805 call subr ?
? . . . . . ?
?;===================================== ?
?; Затирание участка памяти. ?
? subr proc near ?
?cs:0D63 pushf ?
?cs:0D64 cld ?
?cs:0D65 mov ax,ds ?
?cs:0D67 mov es,ax ?
?cs:0D69 rep stosw ?
?cs:0D6B popf ?
?cs:0D6C retn ?
? subr endp ?
??????????????????????????????????????????????????????????????
рис. 4.2
Трюк очень прост: так как длина конвейера не менее 4-х байт, то, очевидно, команды, расположенные за REP STOSW, уже находятся в нем до ее выполнения, что и обеспечивает нормальную работу подпрограммы даже после затирания ее кода в ОЗУ. Выполнение же по одному шагу (то есть по трассировочному прерыванию) нарушает очередность засылки кодов в МП и приводит к непредсказуемому результату.
Пример на рис. 4.3 демонстрирует более изящное использование конвейера. Он определяет - идет ли выполнение программы с трассировкой или нет, и осуществляет ветвление (команда JMP с меткой m:) в зависимости от этого. Здесь ветвление служит лишь для индикации работы под отладчиком, но вы можете применить его по своему усмотрению.
ОПРЕДЕЛЕНИЕ РЕЖИМА ТРАССИРОВКИ
(1 вариант)
??????????????????????????????????????????????????????????????
? 1 0000 code segment para public ?
? 2 assume cs:code,ds:code ?
? 3 ?
? 4 0000 sample1 proc ?
? 5 ?
? 6 0000 0E push cs ?
? 7 0001 1F pop ds ?
? 8 0002 C6 06 0008r 00 mov byte ptr m+1,0 ; изменение ?
? ; смещения в команде JMP ?
? 9 0007 EB 06 m: jmp short norm_ex ?
?10 0009 BA 001Br mov dx,offset trace ; выполнение с?
? ; трассировкой?
?11 000C EB 04 90 jmp exit ?
?12 000F norm_ex: ; выполнение без?
? ; трассировки ?
?13 000F BA 0026r mov dx,offset norm ?
?14 0012 exit: ?
?15 0012 B4 09 mov ah,9 ?
?16 0014 CD 21 int 21h ?
?17 0016 B8 4C00 mov ax,4C00h ?
?18 0019 CD 21 int 21h ?
?19 ?
?20 001B trace db 'Tracing!',0Ah,0Dh,'$' ?
?21 ?
?22 0026 norm db 'Normal exit.',0Ah,0Dh,'$' ?
?23 ?
?24 ?
?25 0035 sample1 endp ?
?26 0035 code ends ?
?27 end sample ?
??????????????????????????????????????????????????????????????
рис. 4.3
На рис. 4.4 использование конвейера шины данных в иной интерпретации и в более завуалированном виде. По существу, это вариация на ту же тему и демонстрирует лишь разнообразие способов работы с конвейером.
Трассировка программы по заданному прерыванию заключается в следующем: программа пользователя (оригинальная или стандартная типа QA или UTIL) адресует вектор Заданного прерывания на собственную функцию, которая до или после выполнения прерывания останавливает процесс выполнения программы и позволяет пользователю ознакомиться с интересующей его информацией, например, содержанием регистров, таблицей базы диска и т. д., или выполнить какие-либо другие действия.
Существует также термин «трассировка прерывания». В него вкладывается несколько другой смысл. Трассировка прерывания есть дисассемблирование последовательности команд, начиная от команды, находящейся по адресу, указанному для исследуемого прерывания в таблице прерываний, до команды IRET.
Вполне очевидно, что при трассировке дисковых прерываний можно, например, проследить процесс чтения некопируемой метки (если, конечно, этот процесс выполняется с использованием прерываний), а затем дублировать метку или моделировать процесс чтения.
Методы защиты от трассировки по дисковый прерываниям при работе с гибкими дисками
При работе с гибким диском можно использовать два прерывания:
Прерывание int 40h в части, касающейся гибких дисков, полностью аналогично прерыванию int 13h. Для защиты от трассировки по этим прерываниям можно использовать следующие приемы:
- работа с ключевой меткой путем прямого программирования контроллера гибкого диска;
- определение одного из неиспользуемых прерываний
- для работы с диском;
- прямой вызов соответствующих функций BIOS'a;
- • определение факта переопределения адреса прерывания на другую программу и невыполнение в этом случае дисковых операций.
Относительно первого метода можно заметить, что некоторые программные средства позволяют отслеживать операции с портами контроллера. Поэтому, защита таким методом также полностью не решает проблемы. Кроме того, этот метод вызывает затруднения при работе с нестандартными контроллерами и тем самым накладывает ограничения на класс совместимых ПЭВМ. Далее, затруднительно синхронизировать обмен с портами на низкоскоростных и высокоскоростных ПЭВМ с работой процессора, что также наложит ограничения на класс совместимости.
Как установлено исследованием большого количества ПЭВМ, адрес, на который передается управление при вызове прерывания int 40h, в BIOS'e является постоянным и составляет F000:EC69. При этом по данному адресу в BIOS'e находятся либо непосредственно команды, выполняющие дисковые операции с гибкими дисками, либо безусловный переход (JMP) на аналогичные функции прерывания int 13h в том же сегменте F000h.
Таким образом, достаточно скопировать соответствующие сегмент и смещение в один из нескольких неиспользуемых векторов прерываний, например 62h, а затем вместо прерывания int 40h вызывать прерывание int 62h. После окончания работы, естественно, необходимо восстановить прежнее значение вектора 62h, что скроет факт использования данного прерывания и заодно предотвратит неприятности в случае использования данного прерывания другой программой.