Сервисы Сервисы и драйверы — наиболее практическая часть всего материала. Сервисы Windows — примерно как демоны в UNIX. Отличаются от обычных программ некоторыми свойствами: в первую очередь, сервисы взаимодействуют не с пользователем, а с другими программами. Главный смысл сервиса в том, что они выступают как расширение функциональности ОС. Например, существуют сервисы для веб-сервера, файлового сервера и т. д. Сервисы могут запускаться без явного волеизъявления пользователя. Например, при старте системы. Сервисы работают не от имени пользователя, а от имени системы или специального псевдопользователя. Сервисы рассматриваются как доверенная часть ОС, их полномочия почти ничем не ограничены. Иногда службы сами ограничивают свои полномочия, например веб-сервер запускается от имени бесправного пользователя. Далее только о Windows. В реестре есть ветка, которая называется база сервисов. Там хранится информация обо всех сервисах, установленных в системе. HKLM\system\CurrentControlSet\Services CurrentControlSet указывает на ControlSet###. ControlSet002 — бекап, создается при выключении. (В regedit папочки = ключи, файлы справа = значения, имя+данные) Обязательные значения в описании сервисов: Type. o o o o o Kernel driver. Драйвер какого-то устройства, работает в режиме ядра. Является модулем расширения ядра. Может управлять только простым устройством, на котором не поддерживается ФС. File system driver. Имеет несколько дополнительных функций. Позволяют в общем дереве ФС (см. ниже) делать новые точки монтирования Own process. Это почти обычная программа, работает от имени пользователя (или SYSTEM). Нет оконного интерфейса. Пример: веб-серверы, СУБД и другие тяжеловесные сервисы. Share process. Маленькие сервисы, которые неудобно делать отдельными процессами. Dhcp-клиент, netsend и прочее. Каждый процесс создает нагрузку, без share process процессов было бы несколько сотен. Для такие сервисов создаются специальные процессы-контейнеры: services.exe (первый, внутри него работает SCM, диспетчер сервисов) и svchost.exe. В XP их много, т. к. появилась возможность запускать их с разными конфигурациями, например, от имени разных пользователей. Interactive. Флаг, может сочетаться с own и share process. До win2003 сервис не может взаимодействовать с графическим интерфейсом. Но, объявив сервис interactive, можно предоставить ему эту возможность. Обычно используется для отладки или хакерами. Начиная с Vista — поддерживается весьма ограничено. Сервис не видит ни один desktop, видит только другие интерактивные сервисы. Start — описывает режимы загрузки данного сервиса. o Disabled. Данный сервис не может запускаться ни при каких обстоятельствах. o Manual. o Automatic. o System (загружаются до automatic) o Boot (загружаются до system). (нижние три — только для драйверов) ErrorContol — что делать при ошибке. Group — сервисы группируются, а для групп назначается порядок загрузки. Tag — уточняет режим загрузки внутри группы. ImagePath — путь к исполняемому файлу данного сервиса. DependOnService — зависимости. Любая другая запись допустима. Устройство ФС (справедливо для win2000): \ o o o Device Cdrom0 HardDiskVolume1 ?? C: (ссылка) D: (ссылка) Registry Управление сервисами. Изначально в Windows была утилита net: net start net stop Начиная с XP (а может и раньше) появилась утилита sc. Она сложна, поэтому при написании софта часто используются специальные загрузчики. Кратко перечислим основные функции, которые используются для управления сервисами программно. OpenSСManager() Возвращается SC_HANDLE или NULL. Закрывается функцией CloseServiceHandle() Необходимо указать доступ. Обычно указывают доступ SC_MANAGER_ALL_ACCESS Создается новый сервис функцией CreateService() Открыть существующий сервис с помощь этой функции нельзя, открывается он функцией OpenService() Удаляются сервисы функцией DeleteService() Функция примечательна тем, что нужен handle для удаления, т. е. нужно открыть сервис. Но открытый сервис удалить нельзя. Поэтому сервис всего лишь помечается для удаления, а удалится только тогда, когда на него закроется последний handle. ControlService() позволяет остановить, обновить конфигурацию и т. д. srvany — программа позволяет запускать в качестве сервиса любую программу. Сервис должен сообщать системе о том, что функционирует он нормально, обычная программа будет закрыта секунд через 20. Как писать свой сервис. В сервисе должен быть обработчик запросов. Эта функция в ходе инициализации сервиса должна быть зарегистрирована в системе. Сервис должен иметь функцию ServiceMain() (Функция main() или winMain() все равно должна быть) Эта функция делает 2 вещи. Во-первых, она регистрирует обработчик управляющих сигналов, вовторых, она вызывается функцию SetServiceStatus() в которой сообщает ОС, что сервис стартован (а также говорит, какие сигналы готов обрабатывать сервис — например, никакие). И третье, самое важное, вызывается функция StartServiceCtrlDispatcher(), которая никогда не возвращает управления. В вечном цикле внутри она вызывает ServiceMain(), а затем ждет сигнала, чтобы вызывать обработчик. Отладка Запустить отладчик из-под Администратора, сделать attach to process. Системный вызов DbgBreakPoint(). Драйверы Драйвер — особый вид сервиса. Драйвер работает в нулевом кольце защиты. Начиная с 256-го процессора, существуют функции защиты памяти. Главная идея в том, что не каждая программа имеет доступ ко всей памяти, ко всем портам, т. е. ко всем аппаратным ресурсам. Имеется 2 кольца защиты. Кольцо защиты привилегированных программ и непривилегированных программ. Непривилегированные программы (user mode) не могут обращаться к портам аппаратуры, не могут узнавать и модифицировать таблицы прерываний, они не могут управлять распределением памяти и могут обращаться только к той памяти, которая им явно позволена. Kernel mode — обычно в нем работает только ядро ОС. Программы в kernel mode делают все, что хотят. В процессорах Intel — 4 кольца защиты. Два кольца между kernel и user mode — сейчас не используются (используется в OS/2). «Драйвер принтера» не является драйвером, он является библиотекой. Драйвер принтера не работает в kernel mode, а драйвером называется по чисто исторической причине. Зачем нужны драйверы? Во-первых, чтобы управлять нестандартным железом. Чтобы управлять «железкой», необходимо соответствующим образом реагировать на сигналы, посылать сигналы, понимать формат данных и т. д. Есть где-то около пятидесяти стандартных операций. Для некоторых видов железок существуют соглашения, для таких устройств не нужны драйвера (приводы, мыши и т. д.). Иногда рассматривать некую виртуальную сущность, будто бы это была железка (разделы жесткого диска, большинство сетевых протоколов). Отсюда возникла концепция драйвер логического устройства. Также, в Windows есть понятия физической и логической клавиатуры. Смысл физически различных клавиатур инкапсулируется в понятия логической клавиатуры. В современных ОС логических устройств обычно больше чем физических. Драйверы могут образовывать цепочки. В цепочке более низкие драйверы обслуживают запросы более высоких. Ниже самого низкого драйвера лежит железо. Пример: Прикладные программы Ядро Логический диск Физический диск Железка Существует множество видов драйверов. Есть два интерфейса для работы с драйверами. Первый — интерфейс сервисов. Поддерживает команды «инсталлировать», «стартовать», «остановить» и т. д. Каждый драйвер обязан поддерживать хотя бы одно устройство. CreateFile() Можно вместо файла указать устройство и оно откроется (создавать устройства таким образом нельзя). FILE_SHARE_READ | FILE_SHARE_WRITE Позволить другим программа использовать устройство. SetFilePointer() Эта функция перемещает указатель текущей позиции в файле. (Не все устройства это поддерживают, только накопительные.) DeviceIoControl() Всё, что не чтение и не запись — управление (извлечь диск, отформатировать хард, прочитать геометрию жесткого диска). Как писать драйвер. Физически драйвер представляет собой извращенный вид библиотеки. Он загружается в адресное пространство ядра. Структура адресного пространства процесса Win32: User memory Kernel memory Всего 4 ГБ. 64 КБ после и до NULL — запрещены. У всех процессов вторая половина (kernel memory) — одинакова. Ядру не нужно думать, в адресном пространстве какого процесса оно сейчас находится. Драйвер грузится во вторую половину. Драйверы имеет некоторые отличия от стандартных библиотек: Драйверы не могут использоваться WinAPI, только NTAPI. Весь код WinAPI грузятся в user memory. o NTAPI — в ntdll.dll. Поверх, по сути, эмулятор Windows. WDK (DDK) —продукт для создания драйверов (маны, примеры и т .д.). Есть build, утилита для компиляции драйверов. Главный заголовочный файл — ntddk.h. Отлаживать драйвер следует на виртуальной машине. Фатальная ошибка — смерть всей системы целиком (синий экран). Windbg (из пакета Debugging tools) — для отладки. В виртуальной машине создается COM-порт. В boot.ini дописывается /debugport=com1 и /baudrate=115200 к записи соответствующей операционной системы. Для драйверов есть функция для вывод информации на консоль отладчика: DbgPrint(); Начиная с Vista — только DbgPrintEx(). (Старая функция не работает.) Все драйверы начинаются с: NTSTATUS DriverEntry(PDRIVER_OBJEACT pDriverObject, PUNICODE_STRING RegistryPath); ntstatus.h — коды ошибок. PUNICODE_STRING: 2: длина буфера 2Б: длина строки 4Б: указатель строка Не обязана заканчивать нулевым символом, более того, может содержать нулевой символ в середине. \Registry\Machine\System\ — конфигурации драйверов. Каждый драйвер должен иметь хотя бы одно устройство. Усройство создается с помощью IoCreateDevice() Параметры: Имя: \Device\имя Device extension, нужен, когда драйвер может обслуживать несколько разных устройств (например, драйвер ФС). выдается DEVICE_OBJECT. Следует делать IoDeleteDevice(). Чтобы обращаться к устройство через WinAPI нужно создать линк \??\link. IoCreateSymbolicLink() Delete() Запросы, которые все драйверы обязаны обрабатывать: Create o Например, обеспечивает монопольный доступ. Close Часто, эти обработчики пустые. Еще: Read Write DeviceControl Прототип: NTSTATUS XDispatchY (PDEVICE_OBJECT pDeviceObject, PIRP pirp) IRP — пакет. IoStatus.Status.Information следует возвращать ноль. IoCompleteRequest завершает обработку запроса. Есть такое понятие — IRP stack. Необходим, когда в обработке запроса участвуют несколько драйверов. Перехват информационных потоков. Программа CreateFile Windows API (Kernel32.dll) NtCreateFile NT API (ntdll.dll) Ядро Драйвер mov eax, номер mov edx, p call edc ret число1 ... p: sysenter/syscall/int 2E ret число2 Для sysenter и syscall используется регистр STAR MSR. Указывает на KiSystemService. Фрагмент: mov rax, a sub rax, длина кадра стека jmp rax ... mov mov mov mov rax, [rsi+4] [rdi+4], rax rax, [esi] [rdi], rax Код для копирования N байт сам занимает N байт. KeServiceDescriptorTable — экспортируемая переменная ядрая. Содержит указатель на SST (System Service Table). SST Service Table LowCall = 0 HiCall ServiceTable ... ... (HiCall+1 штука) Для всех функций в NT API существует два имени: NT... и Zw... В пользовательском режиме NT=Zw. Однако в режиме ядра NT — функция-сервер, Zw — функция клиент. Когда приходит управление из прикладной программы, вызывается функция-сервер, но для своих нужд вызывается функция-клиент. Zw включает в себя NT. Вызывать надо Zw, перехватывать — NT. Если драйвер запишет в нужный индекс SST свою функцию, то система вызовет ее. Способы поиска функции в SST: Обратиться по имени, выяснить адрес (функция должна быть экспортируема и быть в либах). Руками найти адрес функции в памяти (функция должна быть экспортируема). Функция может быть не экспортируема. Можно извлечь номер прямо из машинного кода (обычно из клиентской программы).