FreeMan
Декабрь 2009
Скачать PDF (236.7Kb) (Вы должны быть зарегистрированы на форуме)Данная статья - результат небольшого исследования smss и сsrss ОС Windows XP SP2. В ходе него было обнаружено несколько возможностей автозагрузки. Все эти техники, пожалуй, уже встречались в том или ином виде в "дикой природе", или, как говорится, itw. Все изложенные методы интересны тем, что запуск происходит на ранних этапах загрузки системы, а удаление исполняемых файлов без соответствующей правки реестра порой приводит к невозможности повторной загрузки ОС (поэтому рекомендую все действия выполнять на виртуальной машине). Начнем снизу, то есть с smss.
SMSS (Session Manager Subsystem Service) - компонент Windows, который отвечает за управление сессиями, как видно из названия, а также производит жизненно необходимые действия на начальных этапах. Довольно поверхностно работа smss, как и csrss, рассмотрена в известной всем книге «Внутреннее устройство Microsoft Windows 2000».
С запуском smss.exe (в ntos!Phase1Initialization) связанно 3 BSOD’a: SESSION3_INITIALIZATION_FAILED, SESSION4_INITIALIZATION_FAILED, SESSION5_INITIALIZATION_FAILED. Это наталкивает на мысль, что это довольно ценный компонент системы. Рассмотрим его работу более детально.
Практически сразу после запуска управление получает smss!SmpInit, где создается порт \SmApiPort и пара потоков smss!SmpApiLoop, которые его обслуживают. Тут, кстати, авторы умной книги немного преувеличивают, утверждая «… и два потока, ожидающие клиентские запросы (например, на загрузку новой подсистемы или на создание сеанса)». На самом деле, никаких новых подсистем smss не загружает, есть лишь сообщение SmLoadDeferedSubsystemApi, получение которого приводит к подгрузке подсистем, которые содержатся в списке smss!SmpSubSystemsToDefer. Данный список, как и другие не менее интересные списки, заполняется в результате вызова обработчика, указанного в таблице smss!SmpRegistryConfigurationTable, которая передается в RtlQueryRegistryValues. Рассмотрим ключи, имена которых встречаются в данной таблице.
Данный список может варьироваться от системы к системе, хоть различия и не значительны. Теперь рассмотрим способы автозагрузки исходя из полученной информации.
Создадим тестовое Native приложение, которое будет выводить строку на экран приветствия с помощью NtDisplayString.
Полученный файл скопируем в директорию system32. В реестре в разделе Session Manager редактируем/создаем параметр BootExecute/SetupExecute типа REG_MULTI_SZ, содержащий строку native, где native – имя программы. После чего производим перезагрузку, наблюдая при загрузке картину типа этой:
Принцип загрузки с помощью этого ключа похож на принцип, используемый при загрузке с помощью BootExecute. Но есть и нюанс. Прежде чем запустить все приложения, которые присутствуют в списке, исполняется следующий код:
Тобишь присутствует некая структура UNICODE_STRING, указатель на которую передаётся из smss!_main в smss!SmpInit -> smss!SmpLoadDataFromRegistry -> smss! SmpLoadSubSystemsForMuSession. Эта структура заполняется на данном участке кода и команда, которая записана туда после возврата из smss!SmpInit будете передана в smss!
В случае отсутствия Execute параметра эта строка будет содержать «winlogon.exe» либо, если присутствует параметр Debugger в ключе реестра HKLM\Software\Microsoft\Windows NT\Current Version\Image File Execution Options\winlogon.exe, то строка «winlogon.exe» будет присоединена к значению этого параметра.
В случае присутствия Execute строка будет инициализирована последним элементом, указанным в данном параметре. После чего будет выполнена 5 секундная задержка, связанная с выполнением csr. Также не будет давать желаемого результата вызов NtDisplayString, т.к. на момент прохода списка подсистемы уже запущены. Поэтому немного переделаем тестовый пример:
А значение параметра Execute установим:
Идентификатор async необходим для того, чтоб система не ожидала завершения приложения, которое не рассчитано на то, чтобы завершиться.
Перейдем к сабкею SubSystems.
Здесь все предельно ясно. Есть два параметра – Required и Optional. В них содержаться имена подсистем, которые необходимо запустить. Разница между Required и Optional в том, что подсистемы, попадающие во второй список, запускаются по запросу, а не сразу. Поэтому будем использовать параметр Required для добавления своей «подсистемы».
После добавления имени подсистемы необходимо создать параметр с именем, которое совпадает с именем «подсистемы» и значением, которое содержит имя сервера подсистемы. Но, даже проделав данные операции с реестром, максимум, чего мы добьемся – стабильное зависание на этапе загрузки подсистем. Это связано с тем, что в отличие от старта из вышеописанных ключей, для запуска подсистем используется не smss!SmpExecuteImage, а smss!SmpLoadSubSystem, которая также вызывает smss!SmpExecuteImage но кроме того добавляет в список подсистем информацию о нашей подсистеме. Также запись о подсистеме содержит описатель события, на котором ожидает smss после запуска подсистемы. Событие переводится в сигнальное состояния после того, как подсистема произведет свою инициализацию и сообщит об этом коннектом на порт \SmApiPort.
Новый исходный код тестового приложения:
Необходимо добавить параметр native в SubSystems, значением которого будет путь к native.exe. А также в параметр Required добавить строку native, после чего выполнить перезагрузку. Собственно это все способы автозагрузки, которые бросились мне в глаза во время исследования smss.exe. Также я заметил то, что одна из подсистем, которые загружаются, сsrss, имеет довольно интересный набор параметров, что натолкнуло меня на мысль, что там также есть возможность авторана.
На самом деле csrss.exe – абсолютно неинтересная штуковина, самое полезное действие которой – вызов csrsrv!CsrServerInitialization. Если в smss все настройки содержались в реестре, то csrss берет настройки из командной строки, которая тоже указана в реестре и выглядит примерно так: %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16 Парсинг этой строки производится в csrsrv!CsrParseServerCommandLine. Параметры, которые начинаются с “ServerDLL=”, содержат в себе информацию о подгружаемых библиотеках в виде x:y,z где х – имя библиотеки, у – имя процедуры инициализации, z – номер этой библиотеки. Загрузка этих библиотек происходит в csrsrv!CsrLoadServerDll следующим образом:
Ошибка на любом из этапов – синий экран.
В случае, если мы хотим использовать командную строку как возможность автозапуска очевидно, что необходимо присвоить своей библиотеке номер < 4. Trojan.Win32.SubSys, дабы решить эту проблему, подменял запись одного из winsrv на свою. Я же предлагаю добавить свою запись. Часть строки инициализации, отвечающая за подгрузку библиотек, будет выглядеть следующим образом: ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=native:MyServerDllInitialization,2 ServerDll=winsrv:ConServerDllInitialization,2
Но тут возникает проблема с тем, что после библиотеки с номером 2 следует ещё одна библиотека с номером 2. То есть, для успешной загрузки второй библиотеки первая должна загрузиться так, чтобы в _CsrLoadedServerDll[2*4] остался 0. Возможность проделать это можно обнаружить внимательно посмотрев на процесс загрузки ещё раз. Номер библиотеки попадает в структуру CSR_SERVER_DLL во время её инициализации, но после этого указатель на структуру передается в процедуру инициализации, где можно подменить номер библиотеки, после чего на 6ом шаге указатель на структуру попадет по адресу CsrLoadedServerDll+4*новый_номер_библиотеки.
Исходный код native.dll, которая реализует подобный механизм загрузки:
Пара слов о полезной нагрузке.
basesrv.dll, которая также подгружается в пространство процесса csrss, экспортирует функцию BaseSetProcessCreateNotify, которая инициализирует глобальную переменную basesrv!UserNotifyProcessCreate переданным в данную функцию значением. При создании пользовательского процесса происходит нотификация basesrv, которая после завершения инициализации вызывает функцию, указатель на которую находится в basesrv!UserNotifyProcessCreate. Более глубоко в эту сторону копнул blast http://virustech.org/f/viewtopic.php?id=25
На этом заканчиваю свой обзор.
Cпасибо коллективу cih.[ms] за моральную поддержку во время исследования.
[Вернуться к списку] [Комментарии (0)]