Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

articles:buf_bin [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== buf_bin ======
 +<​sub>​{{buf_bin.odt|Original file}}</​sub>​
 +
 +====== ultimateadventure или поиск дыр в двоичном коде ======
 +
 +крис касперски ака мыщъх, no-email
 +
 +**исходные тексты ****LINUX****'​а и других ****OpenSource**** систем в прямом смысле этого слова зачитаны до дыр и найти здесь что-то принципиально новое очень трудно. ****Windows ****– другое дело. непроходимые джунгли двоичного кода отпугивают новичков и огромные территории дизассемблерных листингов все еще остаются неизведанными. причудливые переплетения вложенных вызовов скрывает огромное количество грубых программистских ошибок,​ дающих неограниченную власть над системой. попробуйте их найти, а я покажу как.**
 +
 +===== введение =====
 +
 +Считается,​ что открытость исходного кода – залог надежности любой системы,​ поскольку спрятать закладку (черный ход, троянскую компоненту) в таких условиях практически невозможно. Тысячи экспертов и энтузиастов со всего мира тщательно проанализируют программу и выловят всех блох – как случайных,​ так и преднамеренных. Что же касается откомпилированного двоичного кода – трудоемкость его анализа неоправданно велика и за просто так с ним возиться никто не будет. Что ж, достойный аргумент сторонников движения Open Source,​ известных своим радикализмом и отрицанием объективной реальности.
 +
 +А реальность такова,​ что проанализировать исходный код современных приложений за разумное время ни физически,​ ни экономически невозможно. Даже старушка MS-DOS 6.0 в исходных текстах весит свыше 60 Мбайт. Для сравнения – "​Поклонение П" Виктора Пелевина не дотягивает и до мегабайта. Даже если уподобить исходные тексты развлекательной книге – прикиньте,​ сколько времени понадобиться для их прочтения?​ А ведь исходные тексты – совсем не книга. Это нагромождение сложно взаимодействующих друг с другом структур данных,​ тесно переплетенных с машинным кодом…
 +
 +При средней длине одной x86-команды в два байта, каждый килобайт откомпилированного кода несет на своих плечах порядка пятисот (!) дизассемблерных строк, соответствующим десяти страницам печатного текста. Мегабайтный двоичный роман за разумное время прочитать уже невозможно. Современные программные комплексы не могут быть исследованы до последней запятой и наличие исходных текстов ничего не меняет. Какая разница сколько времени продлится работа – тысячу лет или миллион?​ Процедура поиска дыр плохо поддается распараллеливанию между участниками – отдельные участки программы выполняются отнюдь не изолированно друг от друга, а сложным образом взаимодействуют между собой и далеко не все ошибки сосредоточены в одном месте – многие из них "​размазаны"​ по большой площади,​ а в многопоточных средах они еще и растянуты во времени.
 +
 +Методик автоматизированного поиска уязвимостей,​ доведенных до "​промышленного"​ использования,​ в настоящее время не существует и маловероятно чтобы они появились в дальнейшем. Непосредственный анализ обнаруживает лишь малую толику наиболее грубых и самоочевидных ошибок. Остальные же приходится выявлять в процессе реальной эксплуатации программы. Тем не менее, статистические исследования показывают,​ что ошибки возникают не просто так. В них есть своя внутренняя система и закономерность,​ благодаря чему район "​археологических раскопок"​ существенно сужается и объем дизассемблерных работ становится вполне реальным.
 +
 +Анализ машинного кода имеет свои сильные и слабые стороны. Хорошая новость – здесь нет этих чудовищных дефайнов (директив условной трансляции – define) и не нужно каждый раз отвлекаться на выяснение обстоятельств – какой код компилируется,​ а какой идет на хрен. Нет макросов (особенно многострочных) и мы всегда может отличить функции от констант,​ а константы от переменных. Отсутствует перекрытие операторов и неявный вызов конструкторов (правда,​ деструкторы глобальных классов по-прежнему вызываются неявно). Короче говоря,​ компилятор избавляет нас от дюжины штучек,​ затрудняющих чтение листингов (как шутят программисты,​ Си/Си++ это языки только для записи – writeonly).
 +
 +Плохие новости – одна-единственная строка исходного текста может соответствовать десяткам машинных команд,​ причем оптимизирующие компиляторы транслируют программу не последовательно,​ а произвольным образом перемешивают машинные команды соседних строк исходного кода, превращая дизассемблерный листинг в настоящую головоломку. Все высокоуровневые конструкции управления (циклы,​ ветвления) разбиваются на цепочку условных переходов,​ соответствующую оператору IF GOTO ранних диалектов Бейсика. Комментарии отсутствуют. Структуры данных уничтожаются. Символьные имена сохраняются лишь частично – в RTTI-классах и некоторых импортируемых/​экспортируемых функциях. Иерархия классов со сложным наследованием чаще всего может быть полностью восстановлена,​ но расход времени на реконструкцию будет слишком велик.
 +
 +Поразительно,​ но при всех своих различиях,​ методики анализа машинного и исходного кода удивительно схожи, что уравнивает обе стороны в правах. Дизассемблирование – вовсе не такое таинственное занятие,​ каким оно поначалу кажется и оно вполне по силам инженеру средней руки. Найдите и прочитайте "​Фундаментальные основы хакерства",​ "​Образ мышления ИДА"​ и "​Технику и философию хакерских атак – записки мыщъза"​ или любые другие книги по этой теме, в противном случае эта статья рискует оказаться для вас слишком абстрактной и непонятной.
 +
 +{{buf_bin_Image_0.jpg?​301}}
 +
 +Рисунок 1 затерянный в дебрях кода…
 +
 +===== >>>>​ врезка прежде чем начать =====
 +
 +Существуют различные подходы к исследованию двоичного кода. Методики слепого поиска не предполагают ничего,​ кроме методичного перебора различных комбинаций входных данных (которыми,​ как правило являются строки различной длинны,​ используемые главным образом для выявления переполняющихся буферов). Целенаправленный анализ требует глубоких знаний системы,​ нетривиального мышления и богатого опыта проектирования "​промышленных"​ программных комплексов. Хакер должен наперед знать, что именно он ищет. Излюбленные ошибки разработчиков. Вероятные места скопления багов. Особенности и ограничения различных языков программирования. Одних лишь навыков дизассемблирования (вы ведь умеете дизассемблировать,​ не правда ли?) для наших целей окажется катастрофически недостаточно.
 +
 +Естественно,​ тупой перебор не всегда приводит к положительному результату и множество дыр при этом остаются незамеченными. С другой стороны,​ изучение дизассемблерных листингов также не гарант успешности. Вы можете просидеть за монитором многие годы, но не найти ни одного достойного бага. Это уж как повезет или не повезет (что, кстати говоря,​ намного более вероятно)… Поэтому,​ прежде чем прибегать к дизассемблированию,​ убедитесь,​ что все возможное и невозможное уже сделано. Как минимум следует нанести массированный удар по входным полям, засовывая в них строки непомерной длинны,​ а как максимум – испробовать типовые концептуальные уязвимости. В частности,​ если атакуемый брандмауэр беспрепятственно пропускает сильно фрагментированные TCP-пакеты,​ дизассемблировать его ненужно,​ суду и так все ясно: чтобы обнаружить подобную дыру в двоичном коде, необходимо отчетливо представлять механизм работы брандмауэра и заранее предполгать ее сущестование. А раз так – то не проще ли будет самостоятельно сформировать фрагментированный пакет и посмотреть как на него отреагирует брандмауэр?​ Подложные пакеты – другое дело. Отправляя их жертве мы должны знать какие именно поля проверяются,​ а какие нет. Без дизассемблирования здесь уже не обойтись! Мы должны выделить код, ответственный за обработку заголовков и проанализировать критерии отбраковки пакетов. В конечном счете, дизассемблер – всего лишь инструмент и на роль генератора идей он не тянет. Бесцельное дизассемблирование – это путь в никуда.
 +
 +===== >>>>​ врезка необходимый инструментарий =====
 +
 +Голыми руками много дыр не наловишь! Агрессивная природа двоичного кода требует применения специального инструментария. Прежде всего вам потребуется дизассемблер. Их много разных,​ но лучше всех – IDAPRO – ​ бесспорный лидер, оставляющий своих конкурентов далеко позади и поддерживающий практически все форматы исполняемых файлов,​ процессоры и компиляторы,​ существующие на сегодняшний день (см рис. 2).
 +
 +{{buf_bin_Image_1.png?​552}}
 +
 +Рисунок 2 консольная версия IDA PRO – среда обитания профессиональных кодокопателей
 +
 +{{buf_bin_Image_2.png?​159}}
 +
 +Рисунок 3 и под Palm PC тоже есть дизассемблер
 +
 +Еще нам потребуется отладчик. Классический выбор – soft-ice (см. рис 4), однако,​ в последнее время его жирная туша начинает уступать маленькому и подвижному OllyDebugger'​у (см. рис .5),​ главная вкусность которого – автоматическое отображение распознанных ASСII-строк рядом со смещениями,​ что значительно упрощает поиск переполняющихся буферов,​ поскольку они становятся видны как на ладони. К сожалению,​ будучи отладчиком прикладного уровня,​ OllyDebugger не может отлаживать ядерные компоненты Windows (и некоторые серверные процессы в том числе).
 +
 +{{buf_bin_Image_3.png?​553}}
 +
 +Рисунок 4 маленький и шустрый OllyDebuger
 +
 +{{buf_bin_Image_4.png?​432}}
 +
 +Рисунок 5 профессионально-ореентированный отладчик soft-ice
 +
 +Если исследуемая программа упакована,​ перед началом дизассемблирования ее следует распаковать. Сделать это можно любым универсальным дампером (Proc Dump,​ PE-Tools, Lord-PE), а еще лучше – специализированным распаковщиком,​ знающим данный упаковщик в лицо (правда,​ не для всех упаковщиков распаковщики существуют). Дампы, снятые с программы,​ чаще всего неработоспособны и для своего запуска требует серьезной доработки напильником. Однако,​ зачем нам их запускать?​ А для дизассемблирования они подойдут и так.
 +
 +Все вышеупомянутые продукты можно найти в Осле (Donkey) или в Муле (Mule) – файло-обменных сетях, грубо говоря,​ представляющие собой Интернет внутри Интернета. С их появлением поиски вареза на WEB'е стали уже неактуальны (удивительно,​ но о существовании осла многие до сих пор не знают!).
 +
 +{{buf_bin_Image_5.png?​294}}
 +
 +Рисунок 6 осел – животное упрямое. может качать,​ а может не качать. но если он разгонится – ничто его не остановит! к тому же это он практически всеяден. в смысле в нем есть все! любой софт! музыка,​ фильмы и документация!
 +
 +===== ошибки переполнения =====
 +
 +Любая программа в значительной мере состоит из библиотек,​ анализировать которые бессмысленно – они уже давным-давно проанализированы и никаких радикально новых дыр здесь нет. К тому же подавляющее большинство библиотек распространяется вместе с исходными текстами,​ так что корпеть над их дизассемблированием вдвойне ненужно. В большинстве случаев библиотечный код располагается позади основного кода программы и отделить его достаточно просто. Сложнее идентифицировать имена библиотечных функций,​ без знания которых мы конкретно завязнем в простыне дизассемблерных листингов словно в трясине. К счастью,​ подавляющее большинство стандартных библиотек автоматически распознаются Идой. Сигнатуры же экзотических библиотек от сторонних производителей в любой момент можно добавить и самостоятельно,​ благо Ида допускает такую возможность (подробности в "​HackerDisassemblingUncovered"​ byKrisKaspersky и штатной документации).
 +
 +Решение о загрузке той или иной сигнатурной базы принимается Идой на основе анализа стартового кода, и "​чужеродные"​ библиотеки рискуют остаться нераспознанными. Тоже самое происходит и при загрузке дампов памяти с поврежденным или отсутствующим стартовым кодом или неверно установленной Entry Point (хроническая болезнь всех дамперов). Поэтому,​ если большая часть функций программы осталась не распознанной (см. рис 7) – попробуйте подключить сигнатурную базу вручную,​ выбрав в меню File\Load file пункт FLIRT Signature file. Появится обширный перечень известных Иде библиотек (см. рис. 9). Какую из них выбрать?​ Если вы новичок в дизассемблировании и нужную библиотеку не удается отождествить "​визуально",​ действуйте методом перебора,​ загружая одну сигнатуру за другой,​ добиваясь максимального расширения голубой заливки (см. рис 8)
 +
 +{{buf_bin_Image_6.png?​553}}
 +
 +Рисунок 7 вид навигатора IDA PRO – доминирование синей заливки указывает на то, что большинство библиотечных функций так и остались нераспознанными,​ поскольку дизассемблер не смог определить тип компилятора и соответствующие сигнатуры должны быть загружены вручную
 +
 +{{buf_bin_Image_7.png?​553}}
 +
 +Рисунок 8 нежно голубая заливка,​ пришедшая на смену голубой,​ подтверждает что теперь в Багдаде полный порядок!
 +
 +{{buf_bin_Image_8.png?​553}}
 +
 +Рисунок 9 перечень известных ИДЕ сигнатур
 +
 +Просматривая список распознанных и импортируемых функций,​ отберем наиболее опасные из них. В первую очередь к ним относятся функции,​ принимающие указатель на выделенный буфер и возвращающие данные заранее непредсказуемого размера (например,​ sprintf, gets и т. д.). Функции с явным ограничением предельно допустимой длины буфера (fgets, GetWindowText,​ GetFullPathName) намного менее опасны,​ однако,​ никаких гарантий в их лояльности ни у кого нет. Очень часто программист выделяет буфер намного меньшего размера и предохранительный клапан не срабатывает. Вот например (см. листинг 1). Очевидно,​ если пользователь введет с клавиатуры строку в 100 и более байт, то произойдет неминуемое переполнение буфера и никакие ограничители длины не спасут! Но это уже лирика.
 +
 +#define MAX_BUF_SIZE100
 +
 +#define MAX_STR_SIZE1024
 +
 +char *x; x = malloc(MAX_BUF_SIZE);​ fgets(x, MAX_STR_SIZE,​ f);
 +
 +Листинг 1 пример программы,​ подверженной переполнению со срывом предохранительного клапана
 +
 +Полный перечень потенциально опасных функций занимает слишком много места и потому здесь не приводится. Будем учиться действовать по обстоятельствам. Загружаем исследуемую программу в дизассемблер (лучше всего в IDA PRO), нажимаем <​Shift-F3>,​ щелкаем мышью по колонке "​L"​ (сокращение от Library – библиотечная функция)(( ​ в консольной версии программы данная функция отсутствует )), отделяя библиотечные функции ото всех остальных. Достаем с полки толстый том справочного руководства (для лицензионных пользователей) или запускаем свой любимый MSDN (для всех остальных) и смотрим на прототип каждой из перечисленных здесь функций. Если среди аргументов присутствует указатель на буфер (что-то типа char*, void*, LPTSTR и т. д.), и этот буфер принимает возвращаемые функцией данные,​ то почему бы не проверить как он относится к переполнению?​
 +
 +Нажимаем на <​Enter>,​ переходя к началу функции,​ а затем входим в меню View\Open Subview\Cross Reference,​ открывая окно с перекрестными ссылками,​ каждая из которых ведет к точке вызова нашей функции. В зависимости от особенностей компилятора и сексуальный наклонностей программиста,​ проектировавшего исследуемое приложение,​ вызов может быть как непосредственным (типа CALL our_func),​ так и косвенным (типа movecx, pClass/​mov ebx,​[ecx + 4]/​call ebx/​…/​pClass DD xxx/​DD offset our_func). В последнем случае перекрестные ссылки на out_func будут вести к DD offset our_func и определить место ее реального вызова будет не так-то просто! Обычно хакеры в таких случаях нанимают отладчик,​ устанавливая на our_func точку останова,​ а затем записывают EIP всех место откуда она вызывается (кстати говоря,​ наличие интегрированного отладчика в последних версиях ИДЫ существенно ускоряет этот процесс).
 +
 +И вот мы находимся в окрестностях вызывающего кода! Если аргумент,​ определяющий размер принимаемого буфера,​ представляет собой непосредственное значение (что-то типа push 400h, см. листинг 2) – это хороший знак и дыра скорее всего ждет нас где-то поблизости. Если же это не так – не отчаивайтесь,​ а прокручивая курсор вверх, посмотрите где сей размер инициализируется. Быть может, он все-таки представляет собой константу,​ передаваемую через более или менее длинную цепочку переменных или даже аргументов материнских функций!
 +
 +.text:​00401017push400h
 +
 +.text:​0040101Cmovecx,​ [ebp+var_8]
 +
 +.text:​0040101Fpushecx
 +
 +.text:​00401020call_fgets
 +
 +Листинг 2 непосредственное значение максимальной длины буфера,​ передаваемое функции,​ хороший признак возможного переполнения
 +
 +Теперь найдите код, осуществляющий выделение памяти под буфер (обычно за это отвечает функций malloc и new). Если аргумент,​ определяющий размер выделяемой памяти,​ также представляет собой константу,​ причем эта константа меньше предельно допустимой длины возвращаемых данных – дыра найдена и можно смело переходить к фазе анализа возможных способов воздействий на переполняющийся буфер через поля входных данных.
 +
 +Законы безопасного проектирования гласят – прежде чем выделять буфер, определи точный размер данных,​ которые ты туда собираешься положить. Т. е. в правильной программе вызову malloc или new всегда предшествует strlen, GetWindowTextLength или что-то типа того. В противном случае программа потенциально уязвима. Разумеется,​ наличие превентивной проверки размера само по себе еще не гарант стабильности,​ поскольку далеко не во всех случаях затребованный размер определяется правильно,​ особенно если буфер сливаются данные с нескольких источников.
 +
 +{{buf_bin_Image_9.png?​552}}
 +
 +Рисунок 10 берем библиотечную функцию,​ прототип которой допускает возможность переполнения (1) и переходим по перекрестным ссылкам в окрестности ее вызова (2), смотрим на ограничитель предельно допустимой длины возвращаемых данных,​ сверяя его с размером выделяемого буфера (3), делаем вывод о возможности (или невозможности) переполнения
 +
 +С локальными переменным в этом плане намного сложнее,​ поскольку их размер приходится явным образом задавать на этапе компиляции программе,​ когда длина возвращаемых данных еще неизвестна. Неудивительно,​ что переполняющиеся буфера чаще всего обнаруживаются именно среди локальных переменных.
 +
 +Локальные переменные хранятся в стековых фреймах (по-английски frames), так же называемых кадрами или автоматической памятью. Каждой функции выделяется свой персональный кадр, в который помещаются все принадлежащие ей локальные переменные. Формирование кадра чаще всего осуществляется машинной командной "​SUB ESP,​ xxx",​ реже – "​ADD ESP,​ –xxx",​ где xxx размер кадра в байтах. Текущие версии IDA PRO по умолчанию трактуют все непосредственные значения как беззнаковые числа и преобразование "​xxx"​ в "​‑xxx"​ приходится осуществлять вручную путем нажатия на клавишу <​ – >​.
 +
 +К сожалению,​ "​разобрать"​ монолитный кадр на отдельные локальные переменные в общем случае невозможно,​ поскольку компилятор полностью уничтожает исходную информацию и анализ становится неоднозначным. Однако,​ для наших целей возможностей автоматического анализатора IDA PRO более, чем достаточно. Мы будем исходить из того, что локальные буфера чаще всего (но не всегда!) имеют тип byte *, а их размер составляет по меньшей мере 5 байт (правда,​ как показывает статистика,​ ошибки переполнения чаще всего встречаются именно в четырехбайтовых буферах,​ которые при беглом анализе легко спутать с DWORD).
 +
 +Рассмотрим в качестве примера следующий кадр стека, "​разобранный"​ автоматическим анализатором IDA PRO и попытаемся обнаружить в нем локальные буфера:​
 +
 +.text:​00401012 sub_401012 ​ proc near  ; CODE XREF: start+AF↓p
 +
 +.text:​00401012
 +
 +.text:​00401012 var_38= dword ptr -38h
 +
 +.text:​00401012 var_34= byte ptr -34h
 +
 +.text:​00401012 var_24= byte ptr -24h
 +
 +.text:​00401012 var_20= byte ptr -20h
 +
 +.text:​00401012 var_10= dword ptr -10h
 +
 +.text:​00401012 var_C= dword ptr -0Ch
 +
 +.text:​00401012 var_8= dword ptr -8
 +
 +.text:​00401012 var_4= dword ptr -4
 +
 +.text:​00401012
 +
 +Листинг 3 локальные переменные,​ автоматически восстановленные IDA
 +
 +Переменная var_38 имеет тип DWORD и занимает 4 байта (размер переменной определяется путем вычитания адреса текущей переменной из адреса следующей:​ – 34h – (– 38h) == 4h). На буфер она мало похожа.
 +
 +Переменная var_34 имеет тип BYTE и занимает 10h байт,​ что типично для локального буфера. Тоже самое можно сказать и о переменной var_20. Переменная var_24 хотя и имеет тип BYTE, но занимает всего 4 байта,​ поэтому может быть как компактным локальным буфером,​ так и простой скалярной переменной (причем,​ последние встречаются намного чаще). До тех пор, пока на предмет переполнения не будут исследованы все явные буфера,​ возиться с подобными "​кандидатами в буфера"​ нет никакого смысла.
 +
 +Просматривая дизассемблерный код функции,​ найдите все ссылки на выявленный буфер и проанализируйте возможные условия его переполнения. Вот, например:​
 +
 +text:​0040100B**push300**
 +
 +text:​0040100Dleaeax,​ [**ebp+var_34**]
 +
 +text:​00401010pusheax
 +
 +text:​00401011call_fgets
 +
 +text:​00401016addesp,​ 0Ch
 +
 +Листинг 4 передача указателя на локальный буфер
 +
 +Сразу видно, что переменная var_34 используется для хранения введенной строки (значит,​ это все-таки буфер!) с предельно допустимой длинной в 300h байт, при длине самой локальной переменной в 10h байт. Не исключено,​ что var_34, var_24 и var_20 в действительности представляют собой "​кусочки"​ одного буфера,​ однако,​ в данном случае это ничего не меняет,​ поскольку их совокупный размер много меньше 300h!
 +
 +Если же среди локальных переменных обнаружить переполняющиеся буфера не смотря на все усилия так и не удастся,​ можно попытать счастья среди развалин динамической памяти,​ отслеживая все перекрестные ссылки на функции типа new и malloc и анализируя окрестности их вызова.
 +
 +Как бы там ни было, обнаружив переполняющийся буфер в одной из глубоко вложенных функций,​ не спешите радоваться – возможно он никак не связан с потоком пользовательских данных или (что ничуть не менее неприятно),​ одна из материнских функций ограничивает предельно допустимую длину ввода сверху и переполнения не происходит. Пользователи графической версии IDA (фууу! гуйня какая) могут воспользоваться инструментом CALL GRAPH для просмотра дерева вызовов,​ уродливо отображающим взаимоотношения между дочерними и материнскими функциями и позволяющим (во всяком случае теоретически) проследить маршрут передвижения введенных пользователем данных по программе. К сожалению,​ отсутствие каких бы то ни было средств навигации (нет даже простейшего поиска!) обесценивают все прелести CALL GRAPH'​а и в построенных им диаграммах просто нереально сориентироваться. Однако,​ никто не запрещает разрабатывать адекватные средства визуализации самостоятельно.
 +
 +{{buf_bin_Image_10.png?​549}}
 +
 +Рисунок 11 иерархия функций в графическом представлении
 +
 +Пока же адекватный инструмент не готов, приходится иметь секс с отладчиком,​ причем не простой а анальный. История начинается просто. Заполняем все доступные поля пользовательского ввода, устанавливаем точку останова на вызов считающей их функции (например,​ recv), устанавливаем точки останова непосредственно на буфер, принимающий введенные нами данные,​ и затем ждем последующих обращений. Чаще всего данные обрабатываются не сразу после приема,​ а перегоняются через множество промежуточных буферов,​ каждый из которых может содержать ошибки переполнения. Чтобы удержать ситуацию под контролем,​ мы вынуждены устанавливать точки останова на каждый из промежуточных буферов,​ обязательно отслеживая их освобождение (после освобождения локального буфера принадлежащая ему область памяти может быть использована кем угодно,​ вызывая ложные всплытия отладчика,​ отнимающие время и сильно нервирующие нас). А ведь точек останова всего четыре… Как же мы будем отслеживать обращения к десяткам локальных буферов с помощью всего четырех точек?
 +
 +А вот как! Версия soft-ice для Windows 9x поддерживает установку точки останова на регион,​ причем количество таких точек практически не ограничено. К сожалению,​ в soft-ice для Windows NT эта вкусность отсутствует и ее приходится эмулировать путем хитроумных (хитрожопных) манипуляций с атрибутами страниц. Переводя страницу в состояние NO_ACCESS мы будем отлавливать все обращения к ней (и подопытному буферу в том числе). Естественно,​ если размер буфера много меньше размера страницы (который как известно составляет 4 Кбайт),​ нам придется каждый раз разбираться к какой именно переменной произошло обращение. При желании этот процесс можно полностью или частично автоматизировать (к soft-ice имеется множество примочек,​ поддерживающих развитые скриптовые языки).
 +
 +Вот так дыры и ищутся! Минимум творчества,​ максимум рутины… Стрельбы и гонок по пересеченной поверхности здесь тоже нет. Тем не менее, сидеть в отладчике намного круче, чем смотреть Матрицу (кстати,​ никто не знает где найти оригинальную версию третьей части, не изувеченную переводом?​) или апгрейдить компьютер для игры в DOOM 3…
 +
 +|функция|склонность к переполнению|
 +|gets|экстремальная|
 +|strcpy/​strcat|экстремальная|
 +|memmove/​memcpy|высокая|
 +|sprintf/​vsprintf/​fsprintf|высокая|
 +| scanf/​sscanf/​fscanf/​vscanf/​vsscanf|высокая|
 +|wcscpy/​wcscat/​wcsncat|высокая|
 +|wmemset/​wcsncpy|высокая|
 +|wmemmove/​wmemcpy|высокая|
 +|strncpy/​vsnprint/​snprintf/​strncat|низкая|
 +
 +Таблица 1 некоторые потенциально опасные функции стандартной библиотеки языка Си
 +
 +===== заключение =====
 +
 +Успех операции во многом зависит не только от вашего опыта, но и пространственной ориентации монитора,​ геометрии мыши и степени потертости клавиатуры,​ а попросту говоря от степени вашей везучести. Поговаривают,​ что мыши и новые клавиатуры приносят несчастье – в самый ответственный момент курсор прыгает немного не туда и переполняющийся буфер остается незамеченным…
 +
 +===== >>>>​ выноски =====
 +
 +  - при поиске переполняющихся буферов методом слепого перебора – тестируйте различные длины строк, а не только строки запредельной длины, поскольку материнские функции могут ограничивать их размер сверху,​ образуя узкий коридор – менее короткие строки еще не вызывают переполнения,​ более длинные – осекаются на подходе к переполняющемся буферу;​
 +  - просматривая HEX-дамп обращайте внимание на недокументированные ключи (чаще всего они записаны прямым текстом) – некоторые из них позволяют обойти систему безопасности и сделать с программой непредусмотренные вещи;
 +  - программы,​ написанные с использованием библиотеки VCL, прогоните через утилиту DEDE – она сообщит много интересного;​
 +  - исследуйте только ту часть кода, которая действительно вызывается подопытной программой – определить это поможет пакет Code Coverage от Nu Mega;
 +  - прежде чем искать дыры в подопытной программе – убедитесь,​ что их уже не нашли другие! соберите все известные на данный момент дыры и отметьте их на карте дизассемблерного кода!
 +  - помните о том, что оптимизирующие компиляторы онлайнят функции memcpy/​strcpy и memcmp/​strcmp,​ непосредственного вставая их тело в код! ищите инструкции rep movs/​rep cmps и исследуйте их окрестности!
 +  - если исследуемая программа умело противостоит софт-айсу,​ запустите ее под эмулятором с интегрированным отладчиком.
 +  - не полагайтесь во всем на автоматический анализатор! IDA PRO очень часто ошибается,​ неправильно распознавая (или вовсе не распознавая) некоторые из библиотечных функций,​ пропуская косвенные перекрестные ссылки и путая код с данными!
 +  - не одни вы ищите дыры! разработчики их ищут тоже! сравнивания дизассемблерные листинги свежих версий с версиями не первой свежести,​ проанализируйте все обнаруженные изменения,​ возможно одно из них затыкает дыры, неизвестные широкой общественности;​
 +{{buf_bin_Image_11.jpg?​234}}
 +
 +Рисунок 12 встреча рассвета за монитором
 +
 +