- •Обнаружение вторжений на основе анализа фрагментов унитарного кода
- •1. Методы обнаружения вредоносных фрагментов унитарного кода в телекоммуникационных системах
- •1.1.1. Логическое моделирование цифровых схем с помощью эвм
- •1.1.2. Моделирование неисправностей цифровых схем
- •1.1.3. Моделирование процесса тестовой диагностики цифровых схем
- •1.1.5. Одноканальный сигнатурный анализатор
- •1.1.6. Методы, использующие сигнатуры вторжений
- •1.2. Методы и средства структурного обнаружения потенциально опасных фрагментов унитарного кода
- •1.2.1. Метод неинтеллектуального дизассемблирования
- •1.2.2. Метод интеллектуального дизассемблирования от точки входа
- •1.2.3. Метод потокового анализа
- •1.2.4. Метод разметки (предиката)
- •1.2.5. Дизассемблер автоматизированного анализа
- •1.3. Методы и средства системного обнаружения потенциально опасных фрагментов унитарного кода
- •1.3.1. Метод статистического анализа
- •1.3.2. Метод эвристического анализа
- •1.4. Этапы проведения анализа потоков унитарного кода
- •2. Программно-аппаратный комплекс обнаружения вредоносных фрагментов унитарного кода в телекоммуникационных системах
- •2.1. Методика оценки потенциальной опасности фрагментов унитарного кода
- •2.2. Алгоритм оценки потенциальной опасности фрагментов унитарного кода
- •2.3. Структурно-функциональная схема программно-аппаратного комплекса анализа фрагментов унитарного кода
- •2.4. Риск-анализ использования технологий обнаружения информационных воздействий
- •394026 Воронеж, Московский просп., 14
1.2.2. Метод интеллектуального дизассемблирования от точки входа
Принцип дизассемблирования от точки входа заключается в том, что дизассемблер ведет список точек входа в подпрограммы, куда первоначально помещается точка входа в программу. Затем из списка точек входа извлекается очередная точка входа в подпрограмму и выполняется ее последовательное дизассемблирование [25, 26].
Если в процессе дизассемблирования встречается команда вызова процедуры call, то все возможные значения ее аргумента помещаются в список точек входа подпрограмм.
Если встречается команда безусловного или условного перехода, то все возможные значения аргумента команды помещаются в список ветвей алгоритма.
В случае безусловного перехода или команды возврата из подпрограммы ret, обработка текущей ветви прекращается и осуществляется переход к следующей ветви.
Если ветви алгоритма текущей подпрограммы кончились, то по тому же принципу дизассемблируются все подпрограммы из списка. Критерий окончания разбора - пустой список точек входа в подпрограммы.
Список структурных единиц - процедур и функций получается непосредственно в процессе выполнения алгоритма дизассемблирования от точки входа.
Однако применение этого метода к исполняемому модулю для процессора Intel затруднено, так как команды переходов и вызовов подпрограмм могут содержать в качестве аргумента не только числовое константное значение адреса, но и регистр, адрес, хранящийся в ячейке памяти, и на момент дизассемблирования команды должна быть доступна информация обо всех возможных значениях аргумента команды.
Кроме этого, существует еще одна особенность применения этого метода для программ с графическим интерфейсом. Она связна с использованием в таких программах функций «обратного вызова» (callback-функции). Если программисту необходимо получить уведомление от операционной системы о каком-либо событии (например, нажатии кнопки в диалоге), программист создает функцию реакции на это событие и указывает адрес этой функции в системном вызове. Операционная система при наступлении этого события вызывает функцию реакции.
Для функций обратного вызова задача может быть решена путем перечисления всех функций API Windows, которые принимают в качестве параметров указатель на callback-функцию. Это DialogBoxParam (макрос DialogBox), CreateTimer, CreateThread, RegNotifyChangeKeyValue и др.
Проблема определения всех возможных значений аргументов команд переходов и вызовов процедур (что необходимо для полного и корректного дизассемблирования) может быть решена методами потокового анализа.
1.2.3. Метод потокового анализа
Потоковый анализ является средством получения достоверной информации в поведении программы без ее реального исполнения. Извлекаемые из текста программы свойства - это глобальная информация о программе, которая не может быть получена ни пробными запусками программы, ни изучением ее отдельных фрагментов.
Алгоритмы потокового анализа воспринимают программу (процедуру, модуль, программную систему и т. д.) в качестве входных данных, выявляют статическую информацию (вид которой зависит от конкретной решаемой проблемы) и возвращают полученную информацию в качестве результата.
Потоковый анализ можно условно разделить на анализ потока управления и анализ потока данных.
Для определения множества возможных значение аргументов управляющих команд процессора применяются методы, относящиеся к межпроцедурному анализу потоков данных. Дополнительно эти методы позволяют выявить состав параметров и возвращаемых значений процедур, входящих в модуль, а также адреса (и частично типы) локальных и глобальных переменных.
В рамках анализа потока данных необходимо построение графа связи по данным (def-use graph). Данный граф позволяет описать потоки данных в программе без учета того, как именно эти данные преобразуются. Множества входов и выходов соответствуют интуитивному представлению об аргументах и результатах операторов, а отображение Dеf-use просто описывает, как используются выработанные операторами результаты. Построение данного графа опирается на решение одной из задач из области анализа потоков данных, а именно - задачи о достижимых определениях. Эту задачу можно сформулировать следующим образом: Для каждого вхождения переменной требуется определить множество присваиваний, такое, что для каждого из них существует путь, в котором между ним и данным вхождением отсутствуют другие присваивания той же переменной.
То есть задача достижимых определений заключается в выяснении, где именно устанавливаются значения того или иного вхождения данной переменной.
В связи с тем, что задача построения графа def-use решается для низкоуровневого ассемблерного кода, в котором все локальные переменные расположены в стеке и обращения к ним выполняются путем косвенной регистровой адресации, то возникает задача определения доступа к одним и тем же данным разными путями. Для решения этой задачи необходимо вычислять множество возможных значений для всех регистров, которые используются в качестве адресов операндов. При этом определяется, указывает адрес на сегмент инициализированных или неинициализированных данных, стековую область памяти или область памяти вне модуля и определяется, может ли операция записи по указателю, а инициализировать переменную, доступ к которой производится путем разыменования указателя [46].
После построения графа достижимых определений можно сделать выводы о данных, передаваемых в подпрограмму и обратно. Данные, передаваемые в подпрограмму (параметры) можно выявить, анализируя связи графа def-use, выходящие за пределы процедуры.
Параметры в подпрограмму могут передаваться несколькими способами:
через стек. При этом перед вызовом подпрограммы находится серия команд push или модификация указателя стека. Внутри подпрограммы, как правило, выполняется последовательность команд push ebp; mov ebp esp; sub esp <размер локальных переменных подпрограммы>. В результате оптимизации последовательность может не присутствовать в коде в явном виде, но семантика операций сохраняется;
через регистры общего назначения процессора (так называемые fastcall-подпрограммы). Первые два параметра таких подпрограмм передаются через регистры eax и ebx. Если у подпрограммы больше двух параметров, то они передаются через стек;
через глобальные (статические) переменные. Такие переменные выделяются, как правило, в сегментах (секциях) инициализированных или неинициализированных данных исполняемого модуля.
Возвращаемые значения подпрограмм (в отличие от параметров) выявляются анализом графа def-use не в вызываемой подпрограмме, а в одной или нескольких вызывающих. Они также могут передаваться несколькими способами:
единственное возвращаемое значение передается через регистр eax. Такой метод передачи подходит для значений, размер которых не превышает четырех байт. Более крупные возвращаемые значения передаются другими способами;
по указателю, переданному как параметр подпрограммы;
через глобальные или статические переменные.
При этом в своем поведении в отношении стека параметров подпрограммы делятся на два класса: подпрограмма сама очищает стек, содержащий параметры подпрограммы или предоставляет это делать вызывающей подпрограмме. С точки зрения выделения типов и значений параметров и возвращаемых значений эта информация не важна, но она важна для контроля за стеком.