Первые шаги или установка точки останова на одну команду
Начнем с простого. Возьмем демонстрационный пример TF.exe и попытаемся брякнуться на команду PUSHFD. Загружаем программу в отладчик, в меню "debug" выбираем "set condition" или нажимаем <CTRL-T>. На экране появляется симпатичное диалоговое окно "condition to pause run trace" (см.рис. 1), позволяющее обрывать трассировку при выходе регистра EIP за определенный диапазон ("EIP is in range") или, наоборот, при входе в него ("EIP is outside the range"). Так же поддерживается останов по срабатыванию некоторого условия ("condition is TRUE") или выполнению одной из следующий команд ("command is one of"). Как раз это нам и нужно!
Рисунок 1 диалоговое окно отладчика OllyDbg, позволяющее устанавливать точки останова на произвольные машинные команды
Вводим в окно редактирования "PUSHD" (ес-но без кавычек), взводим соседствующию с ним галочку и говорим "OK". Диалоговое окно исчезает и мы возвращаемся в OllyDbg. Если теперь нажать <F9> (run), то... ничего не получится, поскольку условные точки останова работают _только_ в режиме трассировки, поэтому мы жмем <CTRL-F11> (debug à trace into). Экран отладчика остается неизменным и кажется, что ничего не происходит, только в правом нижнем углу строки статуса появляется надпись "tracing", подтверждающая, что трассировка идет полным ходом и сильно загружает ядро операционной системы.
Рисунок 2 надпись в строке статуса, свидетельствующая о том, что отладчик работает в режиме трассировки
Если во время трассировки нажать <CTRL-SHIFT-ESC> (комбинация, вызывающая "диспетчер задач"), мы увидим извилистую красную кривую (см. рис. 3), сигнализирующую о необычно высоком уровне активности отладчика (если красной кривой нет, взведите галочку "вывод времени ядра" в меню "вид").
Рисунок 3 в режиме трассировки OllyDbg сильно загружает осевое ядро
Если вместо <CTRL-F11> нажать <CTRL-F7> ("animate into"), то скорость трассировки упадет в десятки раз, но зато OllyDbg будет обновлять окно CPU на каждом шагу, подсвечивая текущую выполняемую команду курсором (см. рис. 4). Выглядит очень красиво (особенно интересно наблюдать за циклами), но для практической работы оно непригодно. Прервать трассировку (как анимационную, так и обычную) можно в любой момент клавишей <ESC>, при этом отладчик остановится на последней выполненной инструкции.
Рисунок 4 анимированная трассировка в OllyDbg (в правом нижнем углу строки статуса горит "animating")
Если же вести трассировку до победоносного конца, то спустя некоторое время (определяемое в первую очередь мощностью вашего ЦП), отладчик остановится на инструкции PUSHFD (см. рис. 5). Это и есть "сердце" защитного механизма, который нам предстоит проанализировать и обезвредить.
Рисунок 5 остановка отладчика по срабатыванию точки останова на команду PUSHFD
Антиотладочный код предельно прост и укладывается всего в четыре строки (см. листинг 1):
0040100E |. 9C PUSHFD
0040100F |. 58 POP EAX
00401010 |. 25 00010000 AND EAX,100
00401015 |. 75 03 JNZ SHORT TF.0040101A
Листинг 1 антиотладочный код, основанный на чтении регистра флагов
Защита заталкивает в стек регистр флагов командой PUSHFD и тут же выталкивает его в EAX, проверяя бит трассировки логической операцией AND EAX 100h. Только никакого бита трассировки в стеке ни хрена не окажется — OllyDbg автоматически удалит его, но чтобы программа работала и под другими отладчиками, необходимо заменить JNZ на NOP/NOP или AND EAX, 100h на AND EAX, 0h.
Если немного потрассировать программу, мы выйдем из антиотладочной процедуры и попадем в довольно типичный для высокоуровневых программ машинный код, проверяющий значение, возращенное функций, с помощью пары инструкций TEST EAX,EAX/Jx (см. листинг 2):
0040102A |. E8 D1FFFFFF CALL TF.00401000
0040102F |. 85C0 TEST EAX,EAX
00401031 |. 74 0F JE SHORT TF.00401042
00401033 |. 68 30604000 PUSH TF.00406030; ASCII "debugger was not been detected"
00401038 |. E8 6C000000 CALL TF.004010A9
0040103D |. 83C4 04 ADD ESP,4
00401040 |. EB 0D JMP SHORT TF.0040104F
00401042 |> 68 50604000 PUSH TF.00406050 ; ASCII "debugger has been detected!"
00401047 |. E8 5D000000 CALL TF.004010A9
0040104C |. 83C4 04 ADD ESP,4
Листинг 2 обработка результата работы функции
Давайте попробуем установить точку останова на комбинацию команд CALL XXX\TEST EAX,EAX\JX. А вот не получается! В окно "condition to pause run trace" ясно сказано "command is one of" (одна из следующий команд), то есть если ввести "call const; test eax,eax; jСС const", отладчик будет останавливаться на _каждой_ из перечисленных команд, что совсем не входит в наши планы, но такой уж у OllyDbg синтаксис!
Кстати, о синтаксисе. Отладчик поддерживает шаблоны, позволяющие составлять простейшие регулярные выражения. Например, R32 обозначает любой 32-разрядный регистр общего назначения и "TEST R32, R32" – останавливает трассировку при выполнении команд TEST EAX, EAX; TEST ESI, EDX и т. д. "RA" – любой регистр общего назначения не такой как "RB", поэтому, шаблон "TEST RA, RA" будет останавливаться на инструкции "TEST EAX, EAX", но проскочит мимо "TEST EAX, EBX". Соответственно, "TEST RA, RB" останется равнодушным к "TEST EAX, EAX". Ключевое слово "CONST" заменяет любой непосредственный операнд, например, "MOV RA, CONST" останавливается на "MOV AL, 69h" и "MOV ESI, 666h", а "CALL CONST" – на всяком прямом вызове процедуры.
Выражение "JCC CONST" соответствует любому условному (но _не_ безусловному!) переходу по непосредственному адресу. Остальные ключевые слова перечислены в таблице 1.
ключевое слово |
назначение |
R8 |
любой 8-битный регистр (AL,BL, CL, DL, AH, BH, CH, DH) |
R16 |
любой 16-битный регистр (AX, BX, CX, DX, SP, BP, SI, DI) |
R32 |
любой 32-битный регистр (EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI) |
FPU |
любой регистр математического сопроцессора (ST0..ST7) |
MMX |
любой MMX-регистр (MM0..MM7) |
CRX |
любой регистр управления (CR0..CR7) |
DRX |
любой отладочный регистр (DR0..DR7) |
CONST |
любая константа |
OFFSET |
любое смещение (равнозначно константе) |
JCC |
любой условный переход (JE, JC, JNGE...) |
SETCC |
любая инструкция условной установки байт (SETE, SETC, SETNGE... ) |
CMOVCC |
любая условная пересылка (CMOVE, CMOVC, CMOVNGE...) |
Все это, конечно, замечательно и немного потренировавшись мы сможем брякаться на любой машинной команде (за исключением адресации типа mem, которую Olly не поддерживает), но нам-то нужны не отдельные команды, а сочетания _нескольких_ команд. Как быть, что делать?