Автоматическое генерирование кода планировщика потоков в системах семейства Windows nt

Исследование общего времени решения задачи позволит дать однозначную оценку оптимального числа процессоров для решения определенного класса задач.

Наличие оптимального числа процессоров говорит о плохой масштабируемости исследуемого алгоритма. Узким местом является решение редуцированной СЛАУ.

Выводы

Анализ эффективности алгоритма решения ленточных СЛАУ общего вида показал его плохую маштабируемость и узкое место в решении редуцированной СЛАУ. Рассмотрены достоинства и недостатки применения различных алгоритмов для решения редуцированной СЛАУ и подробно исследован алгоритм блочной циклической редукции в применении к решению редуцированной СЛАУ.

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1. Peter Arbenz, Walter Gander. A Survey of Direct Parallel Algorithms for Banded Linear Systems.

2. J.J. Dongarra, A.H. Sameh. On some parallel banded linear system solvers. Parallel Computing, 1:223-235, 1984.

3. S. Bondeli. Divide and Conquer: A parallel algorithm for the solution of a tridiagonal linear systems. Parallel Computing, 17:4l9, 1991.

А.В. Терёшкин АВТОМАТИЧЕСКОЕ ГЕНЕРИРОВАНИЕ КОДА ПЛАНИРОВЩИКА ПОТОКОВ В СИСТЕМАХ СЕМЕЙСТВА WINDOWS NT

В настоящее время неотъемлемой частью любой системы локального обнаружения вредоносного кода является модуль изучения поведения исполняющихся процессов. Задачей этого модуля обычно является выявление аномального поведения потоков присутствующих процессов и контроль запуска новых процессов и потоков. Присутствие подобного модуля необходимо в системе обнаружения, выполняющейся в режиме реального времени, - только такие системы и могут быть установлены на серверах, для которых недопустимо пребывание в выключенном состоянии на время диагностики.

Ключевым моментом модулей слежения за процессами и потоками является получение списка существующих процессов и исполняющихся потоков в данный момент времени. Критичной является достоверность информации, собранной об этих объектах ядра - пропущенный объект не будет впоследствии анализирован на вредоносность. С точки зрения программистов вредоносного кода подделка информации о существующих процессах и потоках является едва ли не единственным методом скрытия от системы обнаружения, так как методы анализа поведения уже обнаруженного объекта весьма эффективны.

Существующие алгоритмы получения списков процессов и потоков разнятся по уровню сервисов, которые используются для получения этой информации. Общеизвестно, что чем более высок уровень подобного сервиса, тем больше существует путей для подделки возвращаемых ими результатов. Этот факт широко используется программистами вредоносного кода - чем более низкий уровень подделки информации о процессах и потоках используется, тем большее количество сервисов системы будет передавать эту поддельную информацию, поэтому разработчики систем обнаружения вредоносного кода также стараются использовать как можно более низкоуровневые методы получения информации об интересующих объектах ядра. Эта

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

На данный момент самым низкоуровневым из опубликованных методов получения списков процессов и потоков является метод, предложенный независимой исследовательницей Джоанной Рутковска [1]. Он заключается в чтении информации об исполняющихся потоках непосредственно из структур, которыми пользуется системный планировщик потоков при выборе следующего кандидата на исполнение.

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

Рутковска полагает, что без перехвата обращений системы обнаружения к спискам планировщика потоков ОС метод получения списков потоков, основанный на чтении информации планировщика, обойти будет невозможно, так как подменять информацию в этих списках бессмысленно - удаление информации о каком-либо потоке из этих списков приводит к тому, что скрытые потоки перестают рассматриваться планировщиком как кандидаты на исполнение при истечении кванта времени текущего потока. То есть невидимость потоков в этом случае равносильна приостановке их исполнения, что является неприемлемым для вредоносного кода. Надо сказать, что реализация перехвата обращения системы обнаружения к памяти планировщика усложняется тем, что необходимо отличать обращения собственно планировщика ОС и не подменять списки для него. К тому же перехват обращения к памяти легко обнаруживается и деактивируется, что делает этот метод скрытия практически бесполезным.

Предлагается поступить иначе - исключить все скрываемые потоки из списков системного планировщика и затем запустить дополнительный планировщик, который будет использовать другие списки для собственных нужд; далее заполнить эти списки только потоками, исключенными из списков оригинального планировщика. Таким образом, после запуска этого параллельно работающего планировщика все скрытые потоки будут получать кванты времени на исполнение, как и ранее, но уже от другого планировщика. К тому же метод Рутковска не будет определять наличие скрытых потоков, так как они более не присутствуют в списках оригинального системного планировщика.

Идея построения дополнительного планировщика потока состоит в создании полной копии кода оригинального планировщика в выделенной памяти и замене в коде копии всех ссылок на структуры и объекты оригинального планировщика новыми, указывающими на пустые инициализированные структуры и объекты тех же типов. Впоследствии эти пустые списки будут заполнены потоками, исключенными из списков оригинального планировщика. Конечным этапом создания дополнительного планировщика является указание системному планировщику на начало планирования потоков дополнительного. Полученные собственные кванты времени дополнительный планировщик будет распределять среди всех скрытых потоков.

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

Рассмотрим процесс создания дополнительного планировщика поэтапно.

Для построения копии какого-либо кода необходим анализ дерева исполнения, начиная с точек входа исходного кода. В общем случае полный анализ всех ветвей дерева исполнения кода невозможен, так как:

- не всегда возможно автоматически отличить данные от кода (хотя бы потому, что теоретически возможно существование памяти, являющейся и данными, и кодом в одно и то же время);

- без исполнения кода не всегда возможно сделать корректный вывод о ветвлении дерева исполнения на непредсказуемых переходах типа jmp/call reg32, jmp/call [reg32], call [mem32].

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

Копия кода создается следующим образом.

1. Создается список адресов, являющихся точками входа в требуемое дерево исполнения.

2. Создаются два битовых поля с размером, равным размеру региона анализируемого кода. Первое поле будет содержать информацию о покрытии анализируемого региона кодом копии, второе - информацию об адресах первых байтов опкодов копии для контроля корректности дизассемблирования.

3. Для каждого адреса в списке осуществляется проход дерева исполнения, начиная с этого адреса. В битовом поле покрытия помечаются байты, принадлежащие каждой инструкции дерева. Дерево разветвляется при встрече условных переходов и подфункций, ветви заканчиваются на непредсказуемых переходах, выходах из подфункций, а также при определении, что данный код уже был анализирован ранее (по информации из битового поля покрытия). Если какая-либо инструкция передает управление на анализированный ранее код, то проверяется условие передачи управления на первый байт анализированного опкода, используя информацию из битового поля адресов опкодов, и в случае определения передачи управления в середину инструкции процесс копирования аварийно завершается.

4. Весь код, покрытый пройденным деревом исполнения, копируется в буфер соответствующего размера. Байты региона модуля носителя оригинального кода, не покрытые построенным деревом исполнения, не копируются в буфер. При копировании также определяются адреса точек входа из пункта 1 в новом буфере -они вычисляются исходя из смещений точек входа в результирующем коде.

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

6. Финальным этапом построения копии кода является настройка абсолютных адресов, используемых им. Если оригинальный код был считан из памяти загруженного и работающего модуля, то коррекция не требуется (как было показано выше, задача не требует полного разделения деревьев оригинального и скопированного кода). Если же оригинальный код был прочитан из файла на диске (например, во избежание неверного поведения алгоритма копирования на памяти ядра, подвергшейся изменению третьей стороной), то настройка абсолютных адресов необходима. В этом случае все абсолютные адреса, использующиеся в покрытом дереве исполнения, корректируются для указания на соответственные адреса в регионе загруженного требуемого модуля.

Для объяснения этапа замены указателей на списки потоков в коде построенной копии необходимо сказать несколько слов о работе системного планировщика потоков.

В конце первой фазы инициализации ядра MmInitSystem() запускает два потока: KeBalanceSetManager и KeSwapProcessOrStack. Эти два потока представляют собой менеджер баланса системы. KeSwapProcessOrStack организует обработку событий обмена (swap events) в бесконечном цикле. События обмена сигнализируются событием KiSwapEvent. Существует четыре типа событий обмена:

- выгрузить стеки ядра (указывается булевской переменной KiStackOut-SwapRequest);

- выгрузить память процессов (процессы, поставленные в очередь на выгрузку, указываются в списке KiProcessOutSwapListHead);

- подгрузить процессы (процессы, поставленные в очередь на подгрузку, указываются в списке KiProcessInSwapListHead);

- подгрузить стеки ядра указанных потоков (потоки, поставленные в очередь на подгрузку своих стеков режима ядра, указываются в списке KiStackInSwapListHead).

KeBalanceSetManager также организует бесконечный цикл и ждет сигнализации MmWorkingSetManagerEvent (для сжатия рабочих областей памяти в условиях крайней нехватки памяти в системе) и таймера. Обработчик событий таймера периодически выставляет KiStackOutSwapRequest и сигнализирует KeSwapProcessOrStack с помощью KiSwapEvent о том, что необходимо выгрузить стеки потоков, которые пребывают в ожидающем состоянии уже достаточное длительное время. KeBalanceSetManager также вызывает KiScanReadyQueues(), которая временно повышает приоритеты в очереди готовых к исполнению потоков (массив KiDispatcherReadyListHead). Для каждого потока, приоритет которого был повышен, вызывается функция подготовки потока KiReadyThread(), так что возможен случай, когда поле NextThread (указатель на следующий запланированный поток) в структуре Processor Control Block (PRCB) будет сразу же приравнен к потоку с увеличенным приоритетом, тем самым вызывая исполнение этого потока на следующем кванте времени системы.

При возникновении прерывания от системного таймера вызывается функция KeUpdateSystemTime(). В свою очередь, она вызывает функцию KeUpdateRun-Time() для обновления счетчика времени исполнения текущего потока и процесса, а также для уменьшения кванта текущего потока. Когда текущий поток - не поток бездействия системы, и его квант времени истек, запрашивается окончание системного кванта времени путем вызова программного диспетчерского прерывания. KiDispatchInterrupt() проверяет факт истечения системного кванта и наличие уже выбранного следующего потока в поле PRCB.NextThread. Если эти условия выполняются, поле PRCB.CurrentThread (текущий поток) приравнивается к PRCB. NextThread, PRCB.NextThread сбрасывается, и с помощью KiReadyThread() осуществляется подготовка выбранного потока для исполнения.

Тридцатью четырьмя ключевыми списками потоков планировщика являются KiWaitlnListHead (список потоков, ожидающих какой-либо объект с подгруженным собственным стеком режима ядра), KiWaitOutListHead (список потоков, либо ожидающих какой-либо объект с выгруженным собственным стеком режима ядра, либо имеющих запрет на выгрузку собственного стека) и массив из 32 списков KiDispatcherReadyListHead, каждый элемент которого является списком готовых к исполнению потоков с соответственным приоритетом. В WinNT4 и Win2000 эти 34 списка содержат все имеющиеся в системе потоки. Метод Рутковска использует именно эти списки для получения информации о потоках в системе. Адресация трех адресов этих списков - задача нетривиальная, так как символы списков не экспортируются ядром. Рутковска предлагает решить эту проблему созданием базы адресов для всех пакетов обновлений всех поддерживаемых ОС. Этот подход

имеет много минусов, и главный из них - невозможность обнаружения потоков, находящихся вне этих списков. Именно этот факт лег в основу идеи разделения планировщика потоков на две части - видимую и скрытую. Код Рутковска не ищет списки потоков - он пытается проверить их существование по заранее известным адресам.

Данная статья является итогом авторского исследования планировщика потоков систем семейства Windows NT, результатом которого стала разработка метода создания и поддержки параллельного планировщика потоков. Были исследованы существующие методы получения информации об исполняющихся потоках и процессах в системах обнаружения вредоносного кода реального времени и показана неспособность известных алгоритмов получения списка процессов обнаруживать абсолютно все системные процессы.

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

Доступные широкой общественности системы обнаружения вредоносного кода в реальном времени являются уязвимыми к атакам изнутри, так как допущенный к исполнению процесс, который не обнаруживается системой контроля, представляет собой критическую уязвимость в безопасности. Автором были протестировано большое количество систем контроля (Kernel Lister, Kaspersky AntiVirus, DrWeb, Mcafee AntiVirus, ZoneLabs AntiVirus, Lavasoft Ad-Aware SE, PestPatrol, Microsoft AntiSpyware), были выяснены алгоритмы получения списка процессов для каждой системы. Описанный в начале статьи метод Рутковска используется только в показательном продукте с исходными кодами Kernel Lister (klister), коммерческие же решения безопасности реального времени в своем большинстве используют мониторинг создания новых процессов на уровне ядра в совокупности с периодическими вызовами функции ядра ZwQuerySystemInformation(), что подтверждает их неспособность детектирования процессов и потоков, скрытых методом построения параллельного планировщика.

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1. Rutkowska J. Rootkits Detection on Windows Systems // ITUnderground 2004, Warsaw,

Poland http://invisiblethings.org/papers/ITUnderground2004_Win_rtks_detection.ppt

К.Ю. Гуфан, М.П. Иванков, Р.А. Хади О ТРЕБОВАНИЯХ К СИСТЕМАМ ЗАЩИТЫ ЭЛЕКТРОННЫХ ПЛАТЕЖЕЙ ОТ ТЕХНИЧЕСКОГО МОШЕННИЧЕСТВА

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