Jan 112014
 

Прим. копи-пастера: эта статья здесь, потому что она является дополнением к статьям по модулю Orpheu.

Автор: 6a6kin

Содержание:
I. Основы
II. Создание шаблона сигнатуры функции
Приложение А. Поиск необходимой функции

I. Основы
Знаете ли вы, во что превращается код программы на C++ или ассемблере после компиляции? Тот, кто когда-нибудь открывал исполняемые файлы текстовым редактором, видел там только кучу непонятных символов. На самом деле, это последовательность байт, в которую превратилась ваша программа. Все дело в том, что процессор ничего знает о С++, классах, объектах, он даже не видит разницы между положительными и отрицательными числами. Процессор умеет выполнять только микрокоманды, вроде сложения или деления. Эти микрокоманды представляют собой небольшие последовательности байт, из которых и состоит программа. Считывая байты, процессор распознает микрокоманды и выполняет их.

Однако исполняемые файлы состоят не только из микрокоманд. Как в Windows, так и в GNU\Linux программа и данные, с которыми она работает, содержаться в специальной оболочке, которая определяет формат файла. В Windows это PE формат, а в GNU\Linux – ELF формат. Каждый файл содержит заголовок, которые сообщает о том, какой формат у этого файла.

В случае исполняемых файлов, программа делится на заголовок и секции. Заголовок содержит информацию о самом файле и его секциях. Секции могут содержать различного рода данные, в том числе и микрокоманды. ОС считывает заголовок, чтобы знать, как правильно работать с этим файлом.

Процессор не знает, где конец функции, где текстовые данные, а где микрокоманды. Управление работой процессора выполняется с помощью самих микрокоманд. Например, прямо посреди кода можно разместить текст “Hello, brah“, но при этом добавить микрокоманды, указывающие процессору, что данную последовательность байт считывать не нужно. Это означает, что функции сами регулируют, где они заканчиваются и никаких разделителей между ними нет. Микрокоманды функций идут друг за другом.

Так что же представляют из себя микрокоманды? Условно разделим последовательность байт микрокоманды на следующие части: опкод, операнды. На самом деле, строение микрокоманд более сложное, но для понимания хватит и такого представления. Опкод микрокоманды – её уникальное имя, причем чаще всего, одна асемблерная команда может соответствовать нескольким машинным, что сделано для удобства. Операнды могут быть представлены в виде непосредственных значений, адресов, регистров. Иногда операнд в виде регистра может формировать микрокоманду с другим опкодом. Адрес представляет собой смещение относительно начала файла, в котором содержится функция.

И вот мы подошли к теме нашей статьи. Что такое сигнатура функции? Общепринято сигнатурой функции считать уникальное имя функции, позволяющее транслятороу распознать её среди других функций. В GNU\Linux данные имена называются символами. И вроде бы все хорошо, но вот иногда эти имена исключают из программы и найти функцию по этому имени невозможно.
Поэтому в контексте наших статей, сигнатура функции – это любая уникальная последовательность байт(которые представляют собой набор микрокоманд функции), существующая только в пределах этой функции. Причем вся последовательность микрокоманд функции также является её сигнатурой. Возникает вопрос: почему бы просто не искать последовательность микрокоманд функции целиком? Во-первых, размер функции может быть очень большим. А во-вторых, как уже обсуждалось ранее, в качестве операндов микрокоманд могут использоваться адреса. И эти адреса могут быть изменены массой различных причин. Именно поэтому необходимо создать шаблон сигнатуры функции, который можно будет использовать для поиска функции в “изменчивой среде”.

В следующей статье будет рассмотрено применение полученных сигнатур для поиска функций в исполняемом файле. После чего функцию можно перехватить (сплайсинг) посредством изменения её тела, например, вставить трамплин на собственную функцию обработчик.

II. Создание шаблона сигнатуры функции
Шаблон состоит из двух частей: сигнатуры и маски. Сигнатурой является та самая уникальная последовательность байт. Маска указывает на то, какие части сигнатуры могут изменятся, чтобы игнорировать их при поиске.

Для получения нужной нам информации будем использовать Interactive Disassemler(IDA). После открытия исследуемой библиотеки, необходимо включить показ кодов микрокоманд. В меню Options->General… во вкладке Disassembly в поле Number of opcode bytes поставьте 10. После чего слева от ассемблерных микрокоманд появятся их машинное представление.

Как только вы нашли требуемуемую функцию, можно приступать к созданию шаблона.

Сигнатурой функции является та самая уникальная последовательность байт. Просто скопируйте их из дизассемблера. Главное условие – эта последовательность должна быть уникальна в пределах программы. Я использую 32 байта в качестве сигнатуры, но размер никак не ограничен. Сразу следует отметить, что это должны быть именно ПЕРВЫЕ n байт, так как адресом является точка входа функции, а это и есть её начало. Это упрощает поиск.

Далее нужно составить маску сигнатуры, тем самым исключив динамические части. Адреса, как операнды микрокоманд, изменяются всегда. На самом деле, только их исключать из поиска и имеет смысл. Обычно это микрокоманды:

  • jmp
  • call
  • условные переходы jXX(jc, jnz, jbne etc.)
  • lea
  • mov

Определимть использование адреса легко – размер микрокоманды от 5 байт и выше(в 32-разрядных системах размер адреса равен 4 байтам, с этим связано ограничение на 4 ГиБ ОЗУ). Микрокоманды, работающие с непосредственными значениями и\или регистрами, могут быть больше, чем 5 байт, однако не нуждаются в исключении. Маска представляет собой строку, в которой байты, содержащие динамические значения отмечены знаками вопроса(‘?’), а все остальные ‘x’ или любым другим.

Рассмотрим небольшой пример. После кода идут символы маски, которые соответствуют кодам микрокоманд.

В итоге мы получим:
сигнатура: “55 8B EC 56 8B 75 08 56 E8 B3 08 00 00 83 C4 04 83 F8 01 7C 38 3B 05 28 6F 13 02 7F 30 8B 15 24 6F 13 02 8D 0C 80 C1 E1 09 03 C8 6A 39 8D 84 CA F8 AF FF FF 8D 70 7C 56 E8 F3 9E FC FF”
маска: “xxxxxxxxx????xxxxxxxxxx????xxxx????xxxxxxxxxxxxxxxxxxxxxx????”

Чем больше тренируетесь, тем проще. У меня на поиск и создание шаблона уходит не более, чем 3 минуты.

В дополнение о символах. В GNU\Linux библиотеках hlds присутствуют символы функций, с помощью которых можно искать функции. Рекомендуется именно так и делать, так как изменение функции не повлечет за собой изменение символа(исключая случаи, когда изменяются аргументы – обычно символы включают имя функции и её аргументы). В Windows библиотеках символы отсутствуют.

Приложение А. Поиск необходимой функции
Еще одно узкое место – найти дизассемблированный код функции в IDA. Вот несколько советов:

  • Ищите сначала в GNU\Linux библиотеках. Большинство функций можно найти по названию, так как ч библиотеках присутствуют символические имена.
  • Если вы не знаете имя функции, ищите ближайшие названия, относящиеся к этой функции, строки, названия кваров, переменных.
    Найденное название функции является символом, который можно использовать для поиска в GNU\Linux библиотеках.
  • Чтобы найти эквивалентную функцию в Windows библиотеке, придется постараться. В Windows библиотеках отсутствуют символы, поэтому нужно искать обходные пути. Тут так же можно найти функцию по используемой строковой константе, кварам.
  • Есть интересный способ: если функция использует необычный размер локальных переменных, можно найти все вхождения опкода выделения места в стеке для локальных переменных – sub esp, xx. Далее ищите наиболее правдоподобный размер выделенного места и проверяете, нужная ли это вам функция.
  • Бывают случаи, что функция в GNU\Linux библиотеке обычная, а в Windows – подставляемая. Это значит, что код функции подставляется в место вызова и отдельной функции не существует.
  • Если вы переводите дизассемблированный код в Сишный, ориентироваться стоит на Windows библиотеки. Я не знаю точной причины, возможно gcc так хорошо оптимизирует, а может, HEX-Rays плохо расшифровывает, но Windows функции расшифровываются лучше, более структурированный выходит код. Расшифруйте обе версии функции, Windows версия покажет структуру, а GNU\Linux – название используемых функций.

Creative Commons
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Источник: http://www.amx-x.ru/viewtopic.php?f=28&t=8004

Статьи по теме:
Модуль Orpheu (v2.5.1)
Orpheu: Поиск функций в библиотеках

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

Пожалуйста, авторизуйтесь чтобы добавить комментарий.
  Подписаться  
Уведомление о