Перехват библиотечных функций в linux и bsd


         

/Dev/mem - часть 5


Запускаем IDA PRO, загружаем libc.so.6, переходим к функции gets (<Ctrl-G>, "gets", <ENTER>) и смотрим какие байты расположены в начале функции (чтобы IDA PRO отображала машинный код рядом с инструкциями необходимо в меню Options выбрать пункт "Text representation" и в поле "Number of opcode bytes" поставить "7"). Если нет IDA PRO, содержимое функции можно определить с помощью нашей программы, приведенной в листинге 3. На мыщъхином компьютере первые 10h байт функции gets выглядят так: 55h 89h E5h 57h 56h 53h 83h ECh 2Ch 8Bh 75h 08h E8h ACh 4Dh FBh.

Рисунок 5  функция gets в дизассемблере IDA PRO

Открываем /dev/mem в hexeditor'е ("$hexedit /dev/mem"), давим <Ctrl-S> (search) и вводим эту последовательность без суффикса 'h' и без пробелов: "5589E557565383EC2C8B7508E8AC4DFB". Редактор подумает немного и выдаст результат. У мыщъх'а функция gets обнажилась в памяти по адресу 6BA8E60h. Это физический адрес и он непостоянен. Данная страница может многократно вытесняться из памяти и загружаться по совершенно другим адресам.

Рисунок 6 редактор hexedit нашел функцию gets по ее сигнатуре

Нажмем <Ctrl-S> еще раз, чтобы убедиться, что данное вхождение — единственное. Если искомая последовательность присутствует в памяти по нескольким адресам, это значит, что либо произошла коллизия (совпадение с другой функций) и тогда искомую последовательность необходимо удлинить еще на несколько байт, либо в память загружено несколько библиотек, содержащих одну и ту же реализацию функции gets (или библиотека, экспортирующая gets, попала в дисковый кэш) и тогда нам нужно обратить внимание на младшие 3 байта: у нашей функции физические и виртуальные адреса будут равны, поскольку, адрес начала страницы всегда кратен 1000h. Если же ни одного вхождения не найдено — функция gets отсутствует в памяти (не загружена библиотека или страница вытеснена на диск) и тогда мы будем должны ее загрузить. Другая причина — искомая последовательность пересекла границу страницы памяти, а, как мы уже говорили, порядок следования физических страниц не совпадает с виртуальным. В данном случае, все хорошо: между началом gets и концом физической страницы расположено PAGE_SIZE ? (addres_of_func % PAGE_SIZE) = 1000h – (4008CE60h%0x1000) == 1A0h байт, что более, чем достаточно для поиска, но чтобы мы стали делать, если бы эта дистанция равнялась всего нескольким байтам?! А ничего — просто искали бы функцию в памяти не с начала самой функции, а с начала принадлежащей ей страницы, то есть: if (!memcmp(dlsym(lib_name, func_name) & 0xFFFFF000, buf_page)). В этом случае нам достаточно, чтобы между началом функции и концом страницы было всего 5 байт, необходимых для внедрения команды jump thunk. А если этих байт нет? Тогда необходимо либо искать следующую страницу и внедряться в середину функции (но это самый тяжкий вариант и здесь он не рассматривается), либо ставить в начало функции CCh и ловить исключение из ядра (см. врезку "проблемы стабильности").




Содержание  Назад  Вперед