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

Ultimate adventure

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

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


Анализ машинного кода имеет свои сильные и слабые стороны. Хорошая новость: здесь нет этих чудовищных дефайнов (директив условной трансляции – define) и не нужно каждый раз отвлекаться на выяснение обстоятельств, какой код компилируется, а какой идет лесом. Нет макросов (особенно многострочных), и мы всегда может отличить функции от констант, а константы от переменных. Отсутствует перекрытие операторов и неявный вызов конструкторов (правда, деструкторы глобальных классов по-прежнему вызываются неявно). Короче говоря, компилятор избавляет нас от дюжины штучек, затрудняющих чтение листингов (как шутят программисты, С/С++ - это языки только для записи, write only).

Плохие новости: одна-единственная строка исходного текста может соответствовать десяткам машинных команд, причем оптимизирующие компиляторы транслируют программу не последовательно, а произвольным образом перемешивают машинные команды соседних строк исходного кода, превращая дизассемблерный листинг в настоящую головоломку. Все высокоуровневые конструкции управления (циклы, ветвления) разбиваются на цепочку условных переходов, соответствующую оператору IF GOTO ранних диалектов Бейсика. Комментарии отсутствуют. Структуры данных уничтожаются. Символьные имена сохраняются лишь частично – в RTTI-классах и некоторых импортируемых/экспортируемых функциях. Иерархия классов со сложным наследованием, как правило, может быть полностью восстановлена, но расход времени на реконструкцию будет слишком велик.

Поразительно, но при всех своих различиях методики анализа машинного и исходного кода удивительно схожи, что уравнивает обе стороны в правах. Дизассемблирование – вовсе не такое таинственное занятие, каким оно поначалу кажется, и оно вполне по силам инженеру средней руки. Есть смысл найти и прочитать "Фундаментальные основы хакерства", "Образ мышления ИДА" и "Технику и философию хакерских атак – записки мыщъха" или любые другие книги по этой теме, иначе эта статья рискует оказаться слишком абстрактной и непонятной.

Прежде чем начать…

Существуют различные подходы к исследованию двоичного кода. Методики слепого поиска не предполагают ничего, кроме методичного перебора различных комбинаций входных данных (которыми, как правило, являются строки различной длины, используемые главным образом для выявления переполняющихся буферов). Целенаправленный анализ требует глубоких знаний системы, нетривиального мышления и богатого опыта проектирования "промышленных" программных комплексов. Хакер должен наперед знать, что именно он ищет. Излюбленные ошибки разработчиков. Вероятные места скопления багов. Особенности и ограничения различных языков программирования. Одних лишь навыков дизассемблирования (ты ведь умеешь дизассемблировать, не правда ли?) для наших целей окажется катастрофически недостаточно.

Естественно, тупой перебор не всегда приводит к положительному результату и множество дыр при этом остаются незамеченными. С другой стороны, изучение дизассемблерных листингов также не гарант успеха. Ты можешь просидеть за монитором многие годы, но не найти ни одного достойного бага. Это уж как повезет или не повезет (что, кстати, намного вероятнее)… Поэтому, прежде чем прибегать к дизассемблированию, убедись, что все возможное и невозможное уже сделано. Как минимум следует нанести массированный удар по входным полям, засовывая в них строки непомерной длины, а как максимум – испробовать типовые концептуальные уязвимости. В частности, если атакуемый брандмауэр беспрепятственно пропускает сильно фрагментированные TCP-пакеты, дизассемблировать его не нужно, суду и так все ясно: чтобы обнаружить подобную дыру в двоичном коде, необходимо отчетливо представлять механизм работы брандмауэра и заранее предполагать ее существование. А раз так, то не проще ли будет самостоятельно сформировать фрагментированный пакет и посмотреть, как на него отреагирует брандмауэр? Подложные пакеты – другое дело. Отправляя их жертве, мы должны знать, какие именно поля проверяются, а какие нет. Без дизассемблирования здесь уже не обойтись! Мы должны выделить код, ответственный за обработку заголовков, и проанализировать критерии отбраковки пакетов. В конечном счете дизассемблер – всего лишь инструмент, и на роль генератора идей он не тянет. Бесцельное дизассемблирование – это путь в никуда.

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