суббота, 31 июля 2010 г.

pic.plw

Залил например на sourceforge сорцы очередного античного баяна - ida plugin для работы с unix x86 position independent code
Написано это было в те древние времена, когда Visual C++ 6.0 был крутейшей средой разработки IDA Pro еще не умела правильно работать с PIC. С тех пор мне под unix/linux ничего дизассемблировать особо не нужно, так что я даже не знаю, изменилось ли в IDA Pro с тех пор положение
Поскольку дока давно потеряна - писать новую мне лень тем более что код самодокументирован
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.
Если таблица переходов и/или код перехода находятся вне функции, то не обрабатывается ни сама таблица, ни код ветвления. Это принципиальное ограничение - дело в том, что в этом случае должны быть изменены границы функции, а за это отвечает уже логика анализатора, исходный код которого мне не доступен, изобретать же колёса я не намерен...

Комментариев нет:

Отправить комментарий