Чтение онлайн

на главную - закладки

Жанры

Тонкости дизассемблирования
Шрифт:

На этом же основан один очень любопытный прием противодействия отладчикам, в том числе и знаменитому отладчику-эмулятору cuр386. Рассмотрим, как работает конструкция x66:RETN. Казалось бы, раз команда RETN не имеет операндов, то префикс x66 можно просто игнорировать. Но, на самом деле, все не так просто. RETN работает с неявным операндом-регистром iр/eiр. Именно его и изменяет префикс. Разумеется, в реальном и 16-разрядном режиме указатель команд всегда обрезается до 16 бит, и поэтому, на первый

взгляд, возврат сработает корректно. Но стек-то окажется несбалансированным! Из него вместе одного слова взяли целых два! Так нетрудно получить и исключение 0Ch — исчерпание стека. Попробуйте отладить чем-нибудь пример crack1E.com — даже cuр386 во всех режимах откажется это сделать, а Turbo-Debuger вообще зависнет! IDA Pro не сможет отследить стек, а вместе с ним все локальные переменные.

Любопытно, какой простой, но какой надежный прием. Впрочем, следует признать, что перехват INTChпод операционной системой windows бесполезен, и, не смотря на все ухищрения, приложение, породившие такое исключение, будет безжалостно закрыто. Однако, в реальном режиме это работает превосходно. Попробуйте убедиться в этом на примере crack1E.com. Забавно наблюдать реакцию различных эмулирующих отладчиков на него. Все они либо неправильно работают (выбирают одно слово из стека, а не два), либо совершают очень далекий переход по 32-битному eiр (в результате чего виснут), либо, чаще всего, просто аварийно прекращают работу по исключению 0Ch (так ведет себя cuр386).

Еще интереснее получится, если попытаться исполнить в 16-разрядном сегменте команду CALL. Если адрес перехода лежит в пределах сегмента, то ничего необычно ожидать не приходится. Инструкция работает нормально. Все чудеса начинаются, когда адрес выходит за эти границы. В защищенном 16-разрядном режиме при уровне привилегий CL0 с большой вероятностью регистр EIР «обрежется» до шестнадцати бит, и инструкция сработает (но, похоже, что не на всех процессорах). Если уровень не CL0, то генерируется исключение защиты 0Dh. В реальном же режиме эта инструкция может вести себя непредсказуемо. Хотя в общем случае должно генерироваться прерывание INTDh. В реальном режиме его нетрудно перехватить и совершить дальний 'far' переход в требуемый сегмент. Так поступает, например, моя собственная операционная система OS\7R, дающая в реальном режиме плоскую (flat) модель памяти. Разумеется, такой поворот событий не может пережить ни один отладчик. Ни трассировщики реального режима, ни v86, ни protect-mode debugger, и даже эмуляторы (ну, во всяком случае, из тех, что мне известны) с этим справиться не в состоянии.

Одно плохо — все эти приемы не работают под Windows и другими операционными системами. Это вызвано тем, что обработка исключения типа «Общее нарушение защиты» всецело лежит на ядре операционной системы, что не позволяет приложениям распоряжаться им по своему усмотрению. Забавно, но в режиме эмуляции MS-DOS некоторые EMS-драйверы ведут себя в этом случае совершенно непредсказуемо. Часто при этом они не генерируют ни исключения 0Ch, ни 0Dh. Это следует учитывать при разработке защит, основанных на приведенных выше приемах.

Обратим внимание так же и на последовательности типа 0x66 0x66 [xxx]. Хотя фирма intel не гарантирует корректную работу своих процессоров в такой ситуации, но фактически все они правильно интерпретируют такую ситуацию. Иное дело некоторые отладчики и дизассемблеры, которые спотыкаются и начинают некорректно вести себя.

Есть еще один интересный момент связанный с работой декодера микропроцессора.

Декодер за один раз считывает только 16 байт и, если команда «не уместиться», то он просто не сможет считать «продолжение» и сгенерирует исключение «Общее нарушение защиты». Однако, иначе ведут себя эмуляторы, которые корректно обрабатывают «длинные» инструкции.

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

Префиксы переопределения сегмента могут встречаться перед любой командой, в том числе и не обращающейся к памяти, например, CS:NOP вполне успешно выполнится. А вот некоторые дизассемблеры сбиться могут. К счастью, IDA Pro к ним не относится. Самое интересное, что комбинация 'DS: FS: FG: CS: MOV AX,[100]' работает вполне нормально (хотя это и не гарантируется фирмой Intel). При этом последний префикс в цепочке перекрывает все остальные. Некоторые отладчики, наоборот, ориентируются на первый префикс в цепочке, что дает неверный результат. Этот пример хорош тем, что великолепно выполняется под Windows и другими операционными системами. К сожалению, на декодирование каждого префикса тратится один такт, и все это может медленно работать.

Вернемся к формату кода операции. Выше была описана структура первого байта. Отметим, что она фактически не документирована, и Intel этому уделяет всего два слова. Формат разнится от одной команды к другой, однако, можно выделить и некоторые общие правила. Практически для каждой команды, если регистром-приемником фигурирует AX (AL), существует специальный однобайтовый код, который содержит в трех младших битах регистр-источник. Этот факт следует учитывать при оптимизации. Так, среди двух инструкций XCHGAX,BX и XCHG BX,DX следует всегда выбирать первую, т.к. она на байт короче. (Кстати, инструкция XCHGAX,AX более известна нам как NOP. О достоверности этого факта часто спорят в конференциях, но на странице 340 руководства №24319101 «Instruction Set Reference Manual» фирмы Intel это утверждается совершенно недвусмысленно. Любопытно, что, выходит, никто из многочисленных спорщиков не знаком даже с оригинальным руководством производителя).

Для многих команд условного перехода четыре младших бита обозначают условие операции. Точнее говоря, условие задается в битах 1-3, а установка бита 0 приводит к его инверсии (таблица 1).

Как видим, условий совсем немного, и проблем с их запоминанием обычно не возникает. Теперь уже не нужно мучительно вспоминать 'jz' — это 74h или 75h. Так как младший бит первого равен нулю, то 'jz' — это 74h, а 'jnz', соответственно, 75h.

Далеко не все коды операций смогли поместиться в первый байт. Инженеры Intel задумались о поиске дополнительного места для размещения еще нескольких бит и обратили внимание на байт modR/M. Подробнее он описан ниже, а пока рассмотрим приведенный выше рисунок (рис. 1). Трех-битовое поле reg, содержащие регистр-источник, очевидно, не используется, если вслед за ним идет непосредственный операнд. Так почему бы его не использовать для задания кода операции? Однако, процессору требуется указать на такую ситуацию. Это делает префикс '0Fh', размещенный в первом байте кода. Да, именно префикс, хотя документация Intel этого прямо и не подтверждает. При этом на не-MMX процессорах для его декодирования требуется дополнительный такт. Intel же предпочитает называть первый байт основным, а второй уточняющим кодом операции. Заметим, что это же поле используют многие инструкции, оперирующие одним операндом (jmр, call). Все это очень сильно затрудняет написание собственного ассемблера/дизассемблера, но зато дает простор для создания самомодифицирующегося кода и, кроме того, вызывает восхищение инженерами Intel, до минимума сокративших размеры команд. Конечно, это досталось весьма непростой ценой. И далеко не все дизассемблеры работают правильно. С другой стороны именно благодаря этому и существуют защиты, успешно противостоящие им.

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

К тонкостям кодирования команд мы еще вернемся, а пока приготовимся к разбору поля modR/M. Два трехбитовых поля могут задавать код регистра общего назначения по следующей таблице (таблица 2):

<
Поделиться:
Популярные книги

Адаптация

Кораблев Родион
1. Другая сторона
Фантастика:
фэнтези
6.33
рейтинг книги
Адаптация

Кодекс Охотника. Книга XXI

Винокуров Юрий
21. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XXI

Баоларг

Кораблев Родион
12. Другая сторона
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Баоларг

Наваждение генерала драконов

Лунёва Мария
3. Генералы драконов
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Наваждение генерала драконов

Шериф

Астахов Евгений Евгеньевич
2. Сопряжение
Фантастика:
боевая фантастика
постапокалипсис
рпг
6.25
рейтинг книги
Шериф

На границе империй. Том 5

INDIGO
5. Фортуна дама переменчивая
Фантастика:
боевая фантастика
попаданцы
7.50
рейтинг книги
На границе империй. Том 5

Мы пришли к вам с миром!

Кожевников Павел
Вселенная S-T-I-K-S
Фантастика:
научная фантастика
альтернативная история
5.00
рейтинг книги
Мы пришли к вам с миром!

Законы Рода. Том 3

Flow Ascold
3. Граф Берестьев
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 3

Заплатить за все

Зайцева Мария
Не смей меня хотеть
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Заплатить за все

Любовь Носорога

Зайцева Мария
Любовные романы:
современные любовные романы
9.11
рейтинг книги
Любовь Носорога

Внешники такие разные

Кожевников Павел
Вселенная S-T-I-K-S
Фантастика:
боевая фантастика
попаданцы
5.00
рейтинг книги
Внешники такие разные

Идеальный мир для Лекаря 16

Сапфир Олег
16. Лекарь
Фантастика:
боевая фантастика
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 16

Маршал Советского Союза. Трилогия

Ланцов Михаил Алексеевич
Маршал Советского Союза
Фантастика:
альтернативная история
8.37
рейтинг книги
Маршал Советского Союза. Трилогия

Шипучка для Сухого

Зайцева Мария
Любовные романы:
современные любовные романы
8.29
рейтинг книги
Шипучка для Сухого