Русское сообщество по скриптингу

Сигнатуры функций

Статьи или фрагменты кода для новичков и уже опытных скриптеров по Metamod.
Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по Metamod.

Сигнатуры функций

Сообщение 6a6kin » 01 фев 2012, 03:50

Автор: 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' или любым другим.

Рассмотрим небольшой пример. После кода идут символы маски, которые соответствуют кодам микрокоманд.
Код: Выделить всё
; пролог функции не нуждающийся в исключении
.text:01D6C2F0 55                      push    ebp
.text:01D6C2F1 8B EC                   mov     ebp, esp
.text:01D6C2F3 56                      push    esi
x
xx
x

;обычные операции с регистрами
.text:01D6C2F4 8B 75 08                mov     esi, [ebp+arg_0]
.text:01D6C2F7 56                      push    esi
xxx
x

;вызов функции, первый байт - опкод, остальные 4 - адрес, его мы помечаем знаками вопроса
.text:01D6C2F8 E8 B3 08 00 00          call    sub_1D6CBB0
x????

;обычные операции с регистрами
.text:01D6C2FD 83 C4 04                add     esp, 4
.text:01D6C300 83 F8 01                cmp     eax, 1
xxx
xxx

;короткий переход не использует адрес, лишь смещение относительно текущего адреса
.text:01D6C303 7C 38                   jl      short loc_1D6C33D
xx

;сравнение регистра eax с адресом
;первый байт - опкод, второй - регистр eax, остальные - адрес
.text:01D6C305 3B 05 28 6F 13 02       cmp     eax, dword_2136F28
xx????

;опять короткий переход
.text:01D6C30B 7F 30                   jg      short loc_1D6C33D
xx

;перемещение в регистр адреса
;первый байт - опкод, второй - регистр edx, остальные - адрес
.text:01D6C30D 8B 15 24 6F 13 02       mov     edx, dword_2136F24
xx????

;обычные операции с регистрами и непосредственными значениями
.text:01D6C313 8D 0C 80                lea     ecx, [eax+eax*4]
.text:01D6C316 C1 E1 09                shl     ecx, 9
.text:01D6C319 03 C8                   add     ecx, eax
.text:01D6C31B 6A 39                   push    39h
xxx
xxx
xx
xx

;обратите внимание, код микрокоманды занимает 7 байт, но не использует адреса
;это значит что исключать из поиска ничего не нужно
.text:01D6C31D 8D 84 CA F8 AF FF FF    lea     eax, [edx+ecx*8-5008h]
xxxxxxx

;опять опычные операции
.text:01D6C324 8D 70 7C                lea     esi, [eax+7Ch]
.text:01D6C327 56                      push    esi
xxx
x

;вызов функции, первый байт - опкод, остальные - адрес
.text:01D6C328 E8 F3 9E FC FF          call    sub_1D36220
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 - название используемых функций.

Вы должны зарегистрироваться, чтобы видеть ссылки.
This work is licensed under a Вы должны зарегистрироваться, чтобы видеть ссылки..
Последний раз редактировалось 6a6kin 07 окт 2013, 14:57, всего редактировалось 2 раз(а).
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 334
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 269 раз.

Re: Сигнатуры функций

Сообщение 6a6kin » 14 июн 2013, 21:19

А может не надо?) А вдруг ошибка как-то от билда зависит?
То, что asi — заметил, но разницы-то нету.
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 334
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 269 раз.

Re: Сигнатуры функций

Сообщение Chuvi » 14 июн 2013, 21:29

6a6kin писал(а):
Код: Выделить всё
 void VoiceAddIncomingData_HookHandler(int nChannel, const char *pchData, int nCount)
{
    Voice_AddIncomingData_eng(nChannel,pchData,nCount);
 }


Без снятия хука?
А это не безконечный ли цикл получится?

Добавлено спустя 5 минут 15 секунд:
Код: Выделить всё

void VoiceAddIncomingData_HookHandler
(int nChannel, const char *pchDataint nCount)
 {
     
UnsetHook(&Voice_AddIncomingData);
 } 

Что-то странно. Ведь после того, как мы сняли хук, должна начать вызываться оригинальная функция. А она не вызывается :(

"Незнание английского языка, это ваша проблема."

Плагинами на заказ не занимаюсь. Своих дел хватает.
Аватара пользователя
Chuvi
Модератор
 
Сообщения: 2173
Зарегистрирован: 24 ноя 2011, 08:03
Откуда: Санкт-Петербург
Благодарил (а): 126 раз.
Поблагодарили: 542 раз.
Опыт программирования: Больше трех лет
Языки программирования: С/C++
Pawn
Прочее...

Re: Сигнатуры функций

Сообщение 6a6kin » 14 июн 2013, 21:34

Бесконечный и получится, программа должна упасть со "Stack Overflow".
А она не вызывается

Вывести базовый адрес бибилиотеки и адрес оригинальной функции и посмотреть.
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 334
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 269 раз.

Re: Сигнатуры функций

Сообщение Chuvi » 14 июн 2013, 22:58

ничего не понял.

Код: Выделить всё
 void VoiceAddIncomingData_HookHandler(int nChannel, const char *pchData, int nCount)
{
    
    
//if (VoiceAddIncomingData_Worker(nChannel, pchData, nCount) == 0)
    // {
         UnsetHook(&Voice_AddIncomingData);
         Voice_AddIncomingData_eng(nChannel,pchData,nCount);
         SetHook(&Voice_AddIncomingData);
    // }
 

после вызова Voice_AddIncomingData_eng, я оказался на адресе 0x269C60DC
но
hw.dll 04920000-05B78000
VoiceHook.asi 269A0000-269CB000
то есть я оказался не в hw.dll, а обратно в своей dll-ке.

Надо проверить, правильно ли стоит указатель на Voice_AddIncomingData_eng

Код: Выделить всё
   Voice_AddIncomingData_eng=(Voice_AddIncomingData_ptr)&Voice_AddIncomingData.address;

да, вроде, правильно.

Добавлено спустя 1 час 2 минуты 56 секунд:
Ничего не понимаю.
wrong_eip.png

таки ссылается куда-то не туда.
(Да, такой отладчик юзаю впервые, обычно студией пользуюсь.)

"Незнание английского языка, это ваша проблема."

Плагинами на заказ не занимаюсь. Своих дел хватает.
Аватара пользователя
Chuvi
Модератор
 
Сообщения: 2173
Зарегистрирован: 24 ноя 2011, 08:03
Откуда: Санкт-Петербург
Благодарил (а): 126 раз.
Поблагодарили: 542 раз.
Опыт программирования: Больше трех лет
Языки программирования: С/C++
Pawn
Прочее...

Re: Сигнатуры функций

Сообщение Chuvi » 15 июн 2013, 15:45

6a6kin, Я тут решил попытаться перехватить данные, которые идут от меня, ибо это тоже надо будет сделать, и обнаружил непонятную мне вещь.
В функции Voice_GetCompressedData нас бросает на какой-то непонятный код, который я обозвал трамплином.
38 и 53 строка.

Можешь объяснить, для чего он там нужен? Почему нельзя было сразу нужную функцию вызвать?

"Незнание английского языка, это ваша проблема."

Плагинами на заказ не занимаюсь. Своих дел хватает.
Аватара пользователя
Chuvi
Модератор
 
Сообщения: 2173
Зарегистрирован: 24 ноя 2011, 08:03
Откуда: Санкт-Петербург
Благодарил (а): 126 раз.
Поблагодарили: 542 раз.
Опыт программирования: Больше трех лет
Языки программирования: С/C++
Pawn
Прочее...

Re: Сигнатуры функций

Сообщение 6a6kin » 15 июн 2013, 20:02

А есть асм код вызова этого трамплина?

Скорее всего это какая-то подготовка для вызова метода.
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 334
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 269 раз.

Re: Сигнатуры функций

Сообщение Chuvi » 25 июн 2013, 18:29

Хотел уже было копаться в IDA SDK и пытаться сделать плагин, который создавал бы сигнатуры, но оказалось, что такой плагин Вы должны зарегистрироваться, чтобы видеть ссылки..

"Незнание английского языка, это ваша проблема."

Плагинами на заказ не занимаюсь. Своих дел хватает.
Аватара пользователя
Chuvi
Модератор
 
Сообщения: 2173
Зарегистрирован: 24 ноя 2011, 08:03
Откуда: Санкт-Петербург
Благодарил (а): 126 раз.
Поблагодарили: 542 раз.
Опыт программирования: Больше трех лет
Языки программирования: С/C++
Pawn
Прочее...

Пред.

Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1