Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #58, СЕНТЯБРЬ 2005 г.

Ultimate adventure

Крис Касперски aka мыщъх

Спецвыпуск: Хакер, номер #058, стр. 058-028-4


Ошибки переполнения

Любая программа в значительной мере состоит из библиотек, анализировать которые бессмысленно: они уже давным-давно проанализированы, и никаких радикально новых дыр здесь нет. К тому же подавляющее большинство библиотек распространяется вместе с исходными текстами, так что корпеть над их дизассемблированием вдвойне ненужно. Как правило, библиотечный код располагается позади основного кода программы, и отделить его достаточно просто. Сложнее идентифицировать имена библиотечных функций, без знания которых мы конкретно завязнем в простыне дизассемблерных листингов, словно в трясине. К счастью, подавляющее большинство стандартных библиотек автоматически распознаются Идой. Сигнатуры же экзотических библиотек от сторонних производителей в любой момент можно добавить и самостоятельно, благо IDA допускает такую возможность (подробности в "Hacker Disassembling Uncovered" by Kris Kaspersky и штатной документации).

Решение о загрузке той или иной сигнатурной базы принимается IDA на основе анализа стартового кода, и "чужеродные" библиотеки рискуют остаться нераспознанными. То же самое происходит и при загрузке дампов памяти с поврежденным или отсутствующим стартовым кодом или неверно установленной Entry Point (хроническая болезнь всех дамперов). Поэтому, если большая часть функций программы осталась нераспознанной (см. рис. 7), попробуй подключить сигнатурную базу вручную, выбрав в меню File\Load file пункт FLIRT Signature file. Появится обширный перечень известных Иде библиотек (см. рис. 9). Какую из них выбрать? Если ты новичок в дизассемблировании и нужную библиотеку не удается отождествить "визуально", действуй методом перебора, загружая одну сигнатуру за другой, добиваясь максимального расширения голубой заливки (см. рис. 8)

Просматривая список распознанных и импортируемых функций, отберем самые опасные из них. В первую очередь к ним относятся функции, принимающие указатель на выделенный буфер и возвращающие данные заранее не предсказуемого размера (например sprintf, gets и т.д.). Функции с явным ограничением предельно допустимой длины буфера (fgets, GetWindowText, GetFullPathName) намного менее опасны, однако никаких гарантий их лояльности ни у кого нет. Очень часто программист выделяет буфер размером намного меньше и предохранительный клапан не срабатывает. Вот для примера листинг 1. Очевидно, если пользователь введет с клавиатуры строку в 100 и более байт, то произойдет неминуемое переполнение буфера и никакие ограничители длины не спасут! Но это уже лирика.

(Листинг 1. Пример программы, подверженной переполнению со срывом предохранительного клапана)

#define MAX_BUF_SIZE 100

#define MAX_STR_SIZE 1024

char *x; x = malloc(MAX_BUF_SIZE); fgets(x, MAX_STR_SIZE, f);

Полный перечень потенциально опасных функций занимает слишком много места, поэтому здесь не приводится. Будем учиться действовать по обстоятельствам. Загружаем исследуемую программу в дизассемблер (лучше всего в IDA PRO), нажимаем <Shift-F3>, щелкаем мышью по колонке "L" (сокращение от Library – библиотечная функция)1, отделяя библиотечные функции от всех остальных. Достаем с полки толстый том справочного руководства (для лицензионных пользователей) или запускаем свой любимый MSDN (для всех остальных) и смотрим на прототип каждой из перечисленных здесь функций. Если среди аргументов присутствует указатель на буфер (что-то типа char*, void*, LPTSTR и т.д.) и этот буфер принимает возвращаемые функцией данные, то почему бы не проверить, как он относится к переполнению?

Назад на стр. 058-028-3  Содержание  Вперед на стр. 058-028-5