Способы изучения кода программ

Изучение логики работы программы может выполняться в одном из двух режимов: статистическом и динамическом. Сущность статистического режима заключается в изучении исходного текста программы. Для получения листингов исходного текста выполняемый программный модуль дизассемблируют. Динамический режим изучения алгоритма программы предполагает выполнение трассировки программы. Под трассировкой программы понимается выполнение программы на ЭВМ с использованием специальных средств, позволяющих: выполнять программу в пошаговом режиме; получать доступ к регистрам; областям памяти; производить останов программы по определенным адресам и т. д. В динамическом режиме изучение алгоритма работы программы осуществляется, либо в процессе трассировки, либо по данным трассировки, которые записаны в запоминающем устройстве.

Средства противодействия дизассемблированию не могут защитить программу от трассировки и наоборот: программы, защищенные только от трассировки, могут быть дизассемблированы. Поэтому для защиты программ от изучения необходимо иметь средства противодействия, как дизассемблированию, так и трассировке.

Все программы состоят из машинного кода. В действительности, только машинный код заставляет программу выполнять те или иные функции. Восстановление исходного кода представляет собой процесс поиска шаблонов в машинном коде. Определив определенный шаблон в машинном коде, можно найти потенциально уязвимые места в программном обеспечении.

Статический и динамический режимы исследования программ основаны на следующих основных способах:

  • 1. Отслеживание обработки входных данных является наиболее доскональным способом исследования программы. Прежде всего, нужно определить точки входа. Точки входа - это места, в которых введенные пользователем данные передаются программе. На точке входа можно установить точку останова и начать пошаговое отслеживание выполнения программы.
  • 2. Использование отличий в версиях программ. Поставщик программного обеспечения исправляет многие ошибки в каждой следующей версии программы. Различия между версиями можно использовать как руководство по проведению атак.
  • 3. Использование охвата кода рассматривает поведение программы при ее выполнении в определенных условиях (например, при пиковых нагрузках). Охват кода (code coverage) представляет собой способ отслеживания выполнения программы и определения того, какие участки задействованы в исходном коде. С помощью некоторых средств можно подключаться к процессу и собирать данные в реальном времени.
  • 4. Доступ к ядру. Слабое управление доступом дескрипторами, созданными для драйверов, открывает систему для атак. В зависимости от поддерживаемых драйвером функций, можно вывести компьютер из строя или получить доступ к ядру. Любые входные данные для драйвера, которые содержат адреса ячеек памяти, должны быть немедленно проверены с помощью внесения нулевых (NULL) значений. Также иногда вводят адреса, которые обращаются к памяти ядра. Если в драйвере не предусмотрено контрольных проверок для вводимых пользователем значений, значит, можно исказить содержимое памяти ядра. При тщательно продуманной атаке вполне возможно изменить глобальный режим в ядре и изменить права доступа.
  • 5. Утечка данных из совместно используемых буферов. В программах используется множество буферов. При исследовании программ возникает вопрос: «Очищаются ли эти буферы? Хранятся ли «старые» данные отдельно от «новых»?». Любой буфер, который используется для хранения как общедоступных, так и конфиденциальных данных, может стать причиной утечки информации.
  • 6. Поиск недостатков в предоставлении прав доступа. Некоторые программы, в работе которых предполагается наличие прав администратора или суперпользователя (root).
  • 7. Использование вызовов функций API. Существует несколько системных вызовов, известных тем, что их использование приводит к возникновению уязвимых мест. Один из методов атак как раз и заключается в выявлении этих вызовов (среди самых популярных, например, вызов функции strcpy). Компания Microsoft предоставляет относительно простые в использовании API для отладки в Windows-системах. Интерфейс API позволяет пользователям получать доступ к отладочным событиям из программы, запускаемой в режиме пользователя. Структура программы довольно проста. Если в программе есть несколько потоков, вполне реально контролировать «поведение» каждого отдельного потока (что очень полезно при проведении атак на современное программное обеспечение). Для этого существуют определенные API-вызовы. У каждого потока есть собственный набор регистров процессора, называемый контекстом потока. Контекст отражает состояние регистров процессора на момент последнего исполнения потока и записывается в структуру CONTEXT. Контекст - это структура данных, которая контролирует важные данные процесса, например, текущий указатель команд. Изменяя и запрашивая структуры контекста, можно отслеживать и управлять всеми потоками многопотоковой программы.
  • 8. Использование программ для восстановления исходного кода и структуры программ (дизассемблирование).
  • 9. Пошаговый режим. При установке специального флага TRAP FLAG (флаг трассировки или флаг пошагового режима) процессор выполняет только по одной команде, за каждой из которых следует прерывание. Используя пошаговые прерывания, отладчик может проверить каждую выполняемую команду. Кроме того, можно исследовать состояние памяти на каждом шаге. Отслеживание выполнения программы имеет огромное значение в плане определения того, возможно ли управлять логикой программы. Например, если 13-й байт пакета передается оператору switch, то можно контролировать этот оператор, поскольку он контролирует значение 13-го байта пакета.
  • 10. Фиксирование состояния процесса. Появление точки останова приводит к остановке программы в процессе выполнения. Останавливаются все действия во всех потоках. В этот момент можно воспользоваться специальными программами для чтения (или записи) в любой части памяти программы.
  • 11. Проверка тегов (boron). При возникновении пошагового события или точки останова отладчик может запросить в памяти сведения о наличии тегов (т.е. подстроки с данными пользователя). Это позволяет создавать интеллектуальные запросы о наличии тегов. Поскольку регистры процессора постоянно используются для хранения указателей на данные, необходимо проверить все регистры процесса на предмет хранения в них указателей на выделенные адреса памяти при возникновении точки останова или пошагового события. Если регистр процессора содержит указатель на выделенную область памяти, можно запросить эту область памяти и выполнить в ней поиск тега. Таким образом, в любом блоке кода, в котором обрабатываются введенные пользователем данные, обычно присутствует указатель на эти данные в одном из регистров процессора.
 
Посмотреть оригинал
< Пред   СОДЕРЖАНИЕ   ОРИГИНАЛ     След >