Написано это было в те древние времена, когда
Поскольку дока давно потеряна - писать новую мне лень
Update: рукописи не горят - дока нашлась - спасибо отличному сайту idapalace.net !
Что такое position independent code
В последнее время на Unixах на платформе Intel x86 наибольшее распространение получил ELF - Executable and Linking Format. Этот формат позволяет писать код, который не зависит от адреса, по которому он будет выполняться (это и есть position independent code - сокращённо PIC). Настоятельно советую перечитать предыдущее предложения до полного осознания сказанного. Никаких вопросов не возникло ? А как быть с адресацией всяческих данных ? Решение было найдено довольно элегантное. При линковке определяются не абсолютные адреса данных, а их смещения относительно некоторой предопределённой константы, содержащейся в каждом ELF файле. Она называется _GLOBAL_OFFSET_TABLE_ (вообще-то в зависимости от версии gcc она может называться также $_GLOBAL_OFFSET_TABLE_ или _got или еще как-нибудь) и указывает обычно на начало сегмента с инициализированными данными. А все операции с данными производятся через индексную адресацию, в базовом регистре (в качестве такового используется EBX) содержится адрес _GLOBAL_OFFSET_TABLE_. Откуда берётся последний ? Также очень просто: в начало каждой функции помещается пролог вида : call $+5 ; вызвать следующую инструкцию как функцию
A: pop ebx ; в ebx - адрес A
add ebx, _GLOBAL_OFFSET_TABLE_ - A
; значение _GLOBAL_OFFSET_TABLE_ - A становится известным при линковке
; Получаем в регистре ebx
; A + _GLOBAL_OFFSET_TABLE_ - A = _GLOBAL_OFFSET_TABLE_
Таким образом, если нам нужно обратиться к данным, расположенным по смещению Our_Data относительно _GLOBAL_OFFSET_TABLE_, то мы должны использовать инструкции наподобие:
push [ebx+Our_Data]
call SomeFuc
mov [ebx+Our_Data], eax
Как известно, все адреса функций и переходов в ассемблере x86 кодируются уже в
виде смещений. Есть лишь одно исключение - как быть с таблицами переходов для
команд языков высокого уровня типа switch для "C" ? Они кодируются обычно в
таком виде (предположим, в reg1 содержится индекс в массиве переходов):
mov reg2, ebx
sub reg2, [ebx+reg1*4+JMP_TABLE]
; JMP_TABLE - смещение от _GLOBAL_OFFSET_TABLE_ на адрес таблицы переходов
jmp reg2
Собственно таблица переходов содержит смещения от адреса, по которому необходимо передать управление, на _GLOBAL_OFFSET_TABLE_.
Необходимо отметить, что не обязательно все файлы в ELF форматах используют PIC. В PICode должны быть закодированы только разделяемые библиотеки (аналоги DLL), всем остальным программам использовать PIC вовсе не обязательно.
Зачем оно надо
Действительно, зачем нужен мой plugin, ведь IDA Pro и так умеет дизассемблировать файлы в ELF формате ?Не все разновидности ELF понимает IDA. Например, она не берёт ELF файлы со старых версий Linuxа (RedHat 4.1 & 5.0), а также с FreeBSD версий 2.0 & 2.1. Здесь, правда, мой plugin тоже не помощник, для этого нужно писать свой загрузчик
IDA не всегда распознаёт прологи функций. Этим занимается не анализатор кода, а загрузчик, который, видимо, не обладая должным интеллектом, просто ищет определённые последовательности байт. Скажем, следующего вида прологи уже не опознаются:
call $+5
xor edx, edx ; или любые другие инструкции,
; не изменяющие значения указателя стека ESP
pop ebx
mov eax, 1 ; или любые другие инструкции,
; не использующие регистр ebx
add ebx, const
Самое главное. Как я ни крутил ручки настроек, я так и не смог добиться от IDA, чтобы вместо инструкции
mov eax, [ebx+const]
она показала мне уже пересчитанный с учётом особенностей PIC адрес, вроде
mov eax, [addr]
А уж о распознавании таблиц переходов речь можно даже не начинать - что делать, в проекте не предусмотрено.
PS: ручки крутятся так: при загрузке ELF файла поставьте галочку в пункте "Manual Load". Кстати, если чего накрутите полезного - не поленитесь, сообщите бездарному автору сего.
Как это работает
Поместите откомпилированный plugin pic.plw в подкаталог plugins/ корневого каталога IDA Pro. Дальше загрузите ELF файл с PI кодом, дождитесь окончания автоанализа. Затем выберите пункт меню "Edit->Plugins". Там Вы увидите перечень всех plugins, которые могут оперировать на загруженном файле. Мой называется "PIC analyzer plugin". Кстати, если Вы попробуете проверить его в деле, скажем, на обычном Win32 PE-файле, Вас ждёт жёсткое разочарование - он не появится в меню доступных plugins. Дело в том, что при загрузке он определяет тип файла и тип процессора, и не работает не на ELF файлах и с не Intel x86 процессорами.Plugin делает следующее:
Сначала он пытается обнаружить PIC пролог. Все инструкции до пролога игнорируются.
Для всех инструкций, у которых хотя бы один из операндов использует индексную адресацию и в качестве базового используется регистр EBX, проставляется комментарий (с реальным адресом) и добавляется ссылка соответствующего типа.
Когда проставляется комментарий, проверяется, присутствует ли уже проставляемая строка в имеющемся комментарии, и если да, то комментарий не проставляется. Таким образом, Вы можете запускать plugin на одном и том же коде несколько раз без особых последствий.
В меру своего слабого интеллекта ищутся также таблицы переходов. В случае обнаружения они формируются, также добавляются пользовательские ссылки на код ветвлений. См. также раздел "известные ошибки"
Warning ! plugin работает только с кодом внутри функции. Если Вы попытаетесь запустить его мимо функции, он выдаст грозное предупреждение.
Также можно настроить plugin, чтобы при его вызове он анализировал все определённые в подопытном файле функции. Для этого нужно добавить в файл plugins/plugins.cfg строку :
Analyze_all_PIC_functions pic 0 1
Хотя формат файла plugins.cfg описывается в нём же самом, я поясню, чего тут к чему. Первое поле - название, под которым новая версия pluginа появится в меню "Edit->Plugins", при этом все символы подчёркивания будут заменены на пробелы. Вторая строка - имя файла pluginа без расширения. Третья строка - комбинация "горячих клавиш", по которым должен вызываться plugin (хотя, по-моему, все уже заняты - в таком случае вызывается не plugin, а действие, посаженное на нажатую комбинацию кнопок). 0 теоретически должен означать отсутствие "горячих клавиш", но IDA в меню честно показывает 0 :-). Последний параметр - то, что нам нужно. Дело в том, что в одном файле можно создать только один plugin, но ему передаётся один параметр int, по которому он и решает, что именно ему сотворить. Таким образом можно эмулировать несколько plugins в одном файле, передавая им с помощью plugins.cfg различные параметры. Мой plugin понимает всего два:
0 (по умолчанию) - проанализировать код текущей функции
1 - проанализировать все имеющиеся функции
Известные ошибки
Не все инструкции, модифицирующие свой операнд, адресуемый по PIC схеме, порождают ссылку на запись (только на чтение). Это относится к инструкциям MMX, инструкциям, появившимся в Pentium/Pentium Pro и коммандам SIMD.Если таблица переходов и/или код перехода находятся вне функции, то не обрабатывается ни сама таблица, ни код ветвления. Это принципиальное ограничение - дело в том, что в этом случае должны быть изменены границы функции, а за это отвечает уже логика анализатора, исходный код которого мне не доступен, изобретать же колёса я не намерен...
Комментариев нет:
Отправить комментарий