Как сделать ссылку на драйвер
Многие считают что самому создать драйвер для Windows это что-то на грани фантастики. Но на самом деле это не так. Конечно, разработка драйвера для какого-то навороченного девайса бывает не простой задачей. Но ведь тоже самое можно сказать про создание сложных программ или игр. В разработке простого драйвера нет ничего сложного и я попытаюсь на примерах это показать.
Сперва нам нужно определится в чем мы же будем создавать наш первый драйвер. Поскольку материал ориентирован на новичков, то язык программирования был выбран один из простых, и это не Си или ассемблер, а бейсик. Будем использовать один из диалектов бейсика — PureBasic. Из коробки он не обучен создавать драйверы, но у него удачный набор файлов, используемых для компиляции и небольшое шаманство позволяет добавить эту возможность. Процесс компиляции состоит из нескольких этапов. Если кратко, то он происходит следующим образом: Сначала транслятор «перегоняет» basic-код в ассемблер, который отдается FASM'у (компилятор ассемблера), который создает объектный файл. Далее в дело вступает линкер polink, создающий исполняемый файл. Как компилятор ассемблера, так и линкер могут создавать драйверы и если немного изменить опции компиляции, то получим не исполняемый файл, типа EXE или DLL, а драйвер режима ядра (SYS).
Окно IDE с загруженным кодом драйвера показано на скрине.
Компиляция драйвера выполняется через меню «Компилятор» (это если кто не понял).
Теперь определимся что будет делать наш первый драйвер. Обычно при изучении программирования начинают с простых вещей, скажем, выполнения математических операций и вывода результата. Вот пусть наш драйвер делает тоже самое, ведь банальная математика производимая в режиме ядра это очень круто!
Может показаться что это куча бессмысленного кода, но это не так.
У каждого драйвера должна быть точка входа, обычно у нее имя DriverEntry() и выполнена она в виде процедуры или функции. Как видите, в этом драйвере есть такая процедура. Если посмотрите на начало кода, то в первых строках увидите как ей передается управление. В этой процедуре происходит инициализация драйвера. Там же назначается процедура завершения работы драйвера, которая в нашем случае имеет имя UnloadDriver(). Процедуры CreateDispatch() и CloseDispatch() назначаются обработчиками соединения и отсоединения проги из юзермода.
Процедура DeviceIoControl() будет обрабатывать запросы WinAPI функции DeviceIoControl(), являющейся в данном драйвере связью с юзермодом. В конце кода расположена так называемая ДатаСекция (DataSection), в которой находятся имена драйвера, сохраненные в формате юникода (для этого использована одна из фишек ассемблера FASM).
Видите сколько понадобилось кода для выполнения простейшей математической операции — сложения двух чисел?
А теперь рассмотрим программу, работающую с этим драйвером. Она написана на том же PureBasic.
Процедура Plus() осуществляет связь с драйвером. Ей передаются хэндл, доступа к драйверу и слагаемые числа, которые помещаются в структуру и указатель на указатель которой, передается драйверу. Результат сложения чисел будет в переменной «Result».
Далее следует код простейшего GUI калькулятора, скопированного из википедии.
Когда закроют окно, то перед завершением работы программы, закрывается связь с драйвером и производится его деинсталляция из системы.
Результат сложения чисел 8 и 2 на скриншоте.
Исходные коды драйвера и программы, можно найти в папке «Examples», PureBasic на файлопомойке, ссылку на который давал в начале статьи. Там так же найдете примеры драйвера прямого доступа к порам компа и пример работы с памятью ядра.
PS.
Помните, работа в ядре чревата мелкими неожиданностями аля, BSOD (синий экран смерти), поэтому экспериментируйте осторожно и обязательно всё сохраняйте перед запуском драйвера.
Все мало-мальски серьезные защитные приложения, будь то файрволы или антивирусы, используют собственные модули режима ядра (ring 0), через которые работает большинство их функций: защита процессов от завершения, фильтры различных событий, получение актуальной информации о состоянии сетевого трафика и количестве процессов в системе. Если у программы есть такой драйвер, то пробовать скрываться от нее из режима пользователя (ring 3) бессмысленно. Так же бесполезно пытаться на нее как-то воздействовать. Решение — написать собственный драйвер. В этой статье я покажу, как это делается.
Процессорные архитектуры x86 и x64 имеют четыре кольца защиты, из которых в Windows по факту используются всего два — это ring 3 (режим пользователя) и ring 0 (режим ядра). Бытует мнение, что код режима ядра — самый привилегированный и «ниже» ничего нет. На самом деле архитектура x86/x64 позволяет опускаться еще ниже: это технология виртуализации (hypervisor mode), которая считается кольцом −1 (ring −1), и режим системного управления (System Management Mode, SMM), считающийся кольцом −2 (ring −2), которому доступна память режима ядра и гипервизора.
Итак, мы решили писать собственный драйвер. Начнем с выбора инструментария. Я советую использовать Microsoft Visual Studio, как наиболее user-friendly IDE. Также необходимо будет установить Windows SDK и Windows Driver Kit (WDK) для твоей версии ОС. Кроме того, я крайне рекомендую запастись такими утилитами, как DebugView (просмотр отладочного вывода), DriverView (позволяет получить список всех установленных драйверов) и KmdManager (удобный загрузчик драйверов).
Драйверы в Windows начиная с Vista могут быть как режима пользователя (User-Mode Driver Framework, UMDF), так и режима ядра (Kernel-Mode Driver Framework, KMDF). Более ранние драйверы Windows Driver Model (WDM) появились в Windows 98 и сейчас считаются устаревшими.
Драйверы UMDF имеют намного более ограниченные права, чем KMDF, однако они используются, например, для управления устройствами, подключенными по USB. Помимо ограничений, у них есть очевидные плюсы: их намного проще отлаживать, а ошибка в их написании не вызовет глобальный системный сбой и синий экран смерти. Такие драйверы имеют расширение dll.
Что до драйверов режима ядра (KMDF), то им дозволено куда больше, а расширение файлов, закрепленное за ними, — это sys. В этой статье мы научимся писать простые драйверы режима ядра, напишем драйвер для скрытия процессов методом DKOM (Direct Kernel Object Manipulation) и его загрузчик.
Создание драйвера KMDF
После того как ты создашь проект драйвера, Visual Studio автоматически настроит некоторые параметры. Проект будет компилироваться в бинарный файл в соответствии с тем, какая выбрана подсистема. Наш вариант — это NATIVE, подсистема низкого уровня, как раз для того, чтобы писать драйверы.
Точка входа в драйвер
Строго говоря, точка входа в драйвер может быть любой — мы можем сами ее определить, добавив к параметрам компоновки проекта -entry:[DriverEntry] , где [DriverEntry] — название функции, которую мы хотим сделать стартовой. Если в обычных приложениях основная функция обычно называется main, то в драйверах точку входа принято называть DriverEntry.
Выглядеть это будет так:
Давай пройдемся по параметрам, которые передаются DriverEntry . pDriverObject имеет тип PDRIVER_OBJECT , это значит, что это указатель на структуру DRIVER_OBJECT , которая содержит информацию о нашем драйвере. Мы можем менять некоторые поля этой структуры, тем самым меняя свойства драйвера. Второй параметр имеет тип PUNICODE_STRING , который означает указатель на строку типа UNICODE . Она, в свою очередь, указывает, где в системном реестре хранится информация о нашем драйвере.
WARNING
Любая ошибка в драйвере может вызвать общесистемный сбой и BSOD. Вероятна потеря данных и повреждение системы. Все эксперименты я рекомендую проводить в виртуальной машине.
Interrupt Request Level (IRQL)
IRQL — это своеобразный «приоритет» для драйверов. Чем выше IRQL, тем меньшее число других драйверов будут прерывать выполнение нашего кода. Существует несколько уровней IRQL: Passive, APC, Dispatch и DIRQL. Если открыть документацию MSDN по функциям WinAPI, то можно увидеть примечания, которые регламентируют уровень IRQL, который требуется для обращения к каждой функции. Чем выше этот уровень, тем меньше WinAPI нам доступно для использования. Первые три уровня IRQL используются для синхронизации программных частей ОС, уровень DIRQL считается аппаратным и самым высоким по сравнению с программными уровнями.
Пакеты запроса ввода-вывода (Input/Output Request Packet)
IRP — это запросы, которые поступают к драйверу. Именно при помощи IRP один драйвер может «попросить» сделать что-то другой драйвер либо получить запрос от программы, которая им управляет. IRP используются диспетчером ввода-вывода ОС. Чтобы научить программу воспринимать наши IRP, мы должны зарегистрировать функцию обратного вызова и настроить на нее массив указателей на функции. Код весьма прост:
А вот код функции-заглушки, которая всегда возвращает статусный код STATUS_SUCCESS . В этой функции мы обрабатываем запрос IRP.
Теперь любой запрос к нашему драйверу вызовет функцию-заглушку, которая всегда возвращает STATUS_SUCCESS . Но что, если нам нужно попросить драйвер сделать что-то конкретное, например вызвать определенную функцию? Для этого регистрируем управляющую процедуру:
Здесь мы объявили процедуру с именем IRP_MY_FUNC и ее кодом — 0x801 . Чтобы драйвер ее обработал, мы должны настроить на нее ссылку, создав таким образом дополнительную точку входа в драйвер:
После этого нам нужно получить указатель на стек IRP, который мы будем обрабатывать. Это делается при помощи функции IoGetCurrentIrpStackLocation , на вход которой подается указатель на пакет. Кроме этого, необходимо будет получить от диспетчера ввода-вывода размеры буферов ввода-вывода, чтобы иметь возможность передавать и получать данные от пользовательского приложения. Шаблонный код каркаса обработчика управляющей процедуры:
Продолжение доступно только участникам
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Драйвер-это основа взаимодействия системы с устройством в ОС Windows.Это одновременно удобно и неудобно.
Про удобства я разъяснять не буду - это и так понятно,
а заострюсь я именно на неудобствах драйверов.
В сложившейся ситуации пользователь полностью подчинён воле производителя
- выпусти тот драйвер - хорошо, а не выпустит.
Только продвинутый пользователь, имеющий голову на плечах
(особенно, если он ешё и программер) не станет мириться с таким положением дел
- он просто возьмёт и сам напишет нужный драйвер.
Это нужно и взломщику: драйвер - это удобное окошко в ring0,
которое является раем для хакера. Но хоть написать драйвер и просто,
да не совсем - есть масса подводных камней. Да и документированность данного вопроса на русском языке оставляет желать лучшего.
Этот цикл статей поможет тебе во всём разобраться.
Приступим.
Хочу сразу же сделать несколько предупреждений.
Данная статья всё-таки подразумевает определённый уровень подготовки.
Драйвера-то ведь пишутся на C(++) с большим количеством ассемблерных вставок.
Поэтому хорошее знание обоих языков весьма желательно (если не сказать - обязательно).
Если же ты пока не можешь этим похвастаться,
но желание писать драйвера есть - что ж, так как эта статья вводная, в конце её будет приведён список полезной литературы,
ссылок и т.д. Но помни: учить тебя в этом цикле статей программированию как таковому я тебя не буду.
Может как-нибудь в другой раз. Согласен? Тогда поехали!
Скоро здесь, возможно, будет стоять твоё имя.
Практически в любом деле, как мне кажется, нужно начинать с теории.
Вот и начнём с неё. Для начала уясним себе поточнее основные понятия.
Первое: что есть драйвер? Драйвер - в сущности
кусок кода ОС, отвечающий за взаимодействие с аппаратурой.
Слово "аппаратура" в данном контексте следует понимать в самом широком смысле.
С момента своего появления как такого до сегодняшнего дня драйвер беспрерывно эволюционировал.
Вот, скажем, один из моментов его развития. Как отдельный и довольно независимый модуль драйвер сформировался не сразу.
Да и сейчас этот процесс до конца не завершён:
ты наверняка сталкивался с тем, что во многих
дистрибутивах никсов для установки/перестановки etc драйверов нужно перекомпилировать ядро,
т.е. фактически заново пересобирать систему.
Вот, кстати ещё один близкий моментец: разные принципы работы с драйверами в Windows 9x и NT.
В первом процесс установки/переустановки драйверов проходит практически без проблем,
во втором же случае это тяжёлое и неблагодарное дело,
для "благополучного" завершения которого нередко приходится прибегать к полной переустановке ОС.
А зато в Windows 9x. так,стоп,открывается широкая и волнующая тема,
которая уведёт меня далеко от темы нынешней статьи,
так что вернёмся к нашим баранам. ой,то есть к драйверам.
В порядке общего развития интересно сравнить особенности драйверов в Windows и *nix(xBSD) системах:
1) Способ работы с драйверами как файлами (подробнее см. ниже)
2) Драйвер, как легко заменяемая честь ОС (учитывая уже сказанные выше примечания)
3) Существование режима ядра
Теперь касательно первого пункта. Это значит,
что функции, используемые при взаимодействии с файлами,
как и с драйверами, практически идентичные (имеется в виду лексически):
open, close, read и т.д. И напоследок стоит отметить идентичность механизма
IOCTL (Input/Output Control Code-код управления вводом-выводом)
-запросов.
Драйвера под Windows делятся на два типа:
Legacy (устаревший) и WDM (PnP). Legacy драйверы (иначе называемые "драйверы в стиле
NT") чрезвычайно криво работают (если работают вообще)
под Windows 98, не работают с PnP устройствами, но зато могут пользоваться старыми функциями
HalGetBusData, HalGetInterruptVector etc, но при этом не имеют поддержки в лице шинных драйверов.
Как видишь, весьма средненький драйвер. То ли дело
WDM: главный плюс - поддержка PnP и приличненькая совместимость:
Windows 98, Me, 2000, XP, 2003, Server 2003 и т.д. с вариациями; но он тоже вынужден за это расплачиваться:
например, он не поддерживает некоторые устаревшие функции
(которые всё таки могут быть полезны). В любом случае,
не нужно ничего воспринимать как аксиому, везде бывают свои исключения.
В некоторых случаях лучше написания Legacy драйвера ничего не придумать.
Как ты наверняка знаешь, в Windows есть два мода работы:
User Mode и Kernel Mode - пользовательский режим и режим ядра соответственно.
Первый - непривилегированный, а второй - наоборот.
Вот во втором чаще всего и сидят драйвера (тем
более, что мы в данный момент говорим именно о драйверах режима ядра).
Главные различия между ними: это доступность всяких привилегированных команд процессора.
Программировать (а уж тем более качественно) в Kernel mode посложнее будет,
чем писать прикладные незамысловатые проги.
А драйвера писать без хорошего знания Kernel mode - никак.
Нужно попариться над назначением выполнения разнообразных работ отдельному подходящему уровню IRQL, желательно выучить новое API (так как в Kernel mode API отличается от прикладного).
в общем, предстоит много всяких радостей. Но тем не менее,
это очень интересно, познавательно, и даёт тебе совершенно иной уровень власти над компьютером.
А раз уж я упомянула про IRQL, разьясню и это понятие.
IRQL (Interrupt Request Level - уровень приоритета выполнения) - это приоритеты,
назначаемые специально для кода, работающего в режиме ядра.
Самый низкий уровень выполнения - PASSIVE_LEVEl. Работающий поток может быть прерван потоком только с более высоким
IRQL.
Ну и напоследок разъясним ещё несколько терминов:
1) ISR (Interrupt Service Routine) - процедура обслуживания прерываний.
Эта функция вызывается драйвером в тот момент,
когда обслуживаемая им аппаратура посылает сигнал прерывания.
Делает самые необходимые на первый момент вещи:
регистрирует callback - функцию и т.д.
2) DpcForISR (Deferred Procedure Call for ISR) - процедура отложенного вызова для обслуживания прерываний.
Эту функцию драйвер регистрирует в момент работы ISR для выполнения основной работы.
3) IRP (Input/Output Request Packet) - пакет запроса на ввод - вывод.
Пакет IRP состоит из фиксированной и изменяющейся частей.
Вторая носит название стека IRP или стека ввода - вывода (IO stack).
4) IO stack location - стек ввода - вывода в пакете IRP.
5) Dispatch Routines (Рабочие процедуры) - эти функции регистрируются в самой первой (по вызову) процедуре драйвера.
6) Major IRP Code - старший код IRP пакета.
7) Minor IRP Code - соответственно, младший код IRP пакета.
8) DriverEntry - эта функция драйвера будет вызвана первой при его загрузке.
9) Layering (Многослойность) - данной возможностью обладают только WDM - драйвера.
Она заключается в наличии реализации стекового соединения между драйверами.
Что такое стековое соединение? Для этого необходимо знать про Device
Stack (стек драйверов) - поэтому я обязательно вспомню про всё это чуточку ниже.
10) Device Stack, Driver Stack (стек устройств, стек драйверов) - всего лишь
объемное дерево устройств. Его, кстати, можно рассмотреть во всех подробностях с помощью программы
DeviceTree (из MS DDK), например.
11) Стековое соединение - как и обещала, объясняю. В стеке драйверов самый верхний драйвер - подключившийся позднее.
Он имеет возможность посылать/переадресовывать IRP запросы другим драйверам,
которые находятся ниже его. Воти всё. Правда,просто?
12) AddDevice - функция, которую обязательно должны поддерживать WDM драйверы.
Её название говорит само за себя.
13) Device Object, PDO, FDO (Объект устройства, физический,
функциональный) - при подключении устройства к шине она создаёт PDO.
А уже к PDO будут подключаться FDO объекты WDM драйверов.
Обьект FDO создаётся самим драйвером устройства при помощи функции IOCreateDevice.
Обьект FDO также может иметь свою символическую ссылку, от которой он будет получать запросы от драйвера.
Это что касается WDM драйверов. С драйверами "в стиле NT" ситуация несколько иная.
Если он не обслуживает реальных/PnP устройств,
то PDO не создаётся. Но для связи с внешним миром без FDO не обойтись.
Поэтому он присутствует и тут.
14) Device Extension (Расширение обьекта устройства) - "авторская" структура,
т.е. она полностью определяется разработчиком драйвера.
Правилом хорошего тона считается, например,
размещать в ней глобальные переменные.
15) Monolithic Driver (Монолитный драйвер) - это драйвер,
который самостоятельно обрабатывает все поступающие
IRP пакеты и сам работает с обслуживаемым им устройством
(в стеке драйверов он не состоит). Данный тип драйверов используется только если обслуживается не
PnР устройство или же всего лишь требуется окошко в ring0.
16) DIRQL (уровни аппаратных прерываний) -
прерывания, поступающие от реальных устройств, имеют наивысший приоритет IRQL,
поэтому для них решено было придумать специальное название
(Device IRQL).
17) Mini Driver (Мини - драйвер) - чуть меньше "полного" драйвера.
Обычно реализуется в виде DLL-ки и имеет оболочку в виде "полного" драйвера.
18) Class Driver (Классовый драйвер) - высокоуровневый драйвер,
который предоставляет поддержку класса устройств.
19) РnP Manager (PnP менеджер) - один из главных компонентов операционной системы.
Состоит из двух частей: PnP менеджера пользовательского и "ядерного" режимов.
Первый в основном взаимодействует с пользователем;
когда тому нужно, например, установить новые драйвера и т.д.
А второй управляет работой, загрузкой и т.д. драйверов.
20) Filter Driver (фильтр - драйвер) - драйверы, подключающиеся к основному драйверу либо сверху
(Upper), либо снизу (Lower). Фильтр драйверы (их может быть несколько) выполняют фильтрацию IRP пакетов.
Как правило, для основного драйвера Filter Drivers неощутимы.
21) Filter Device Object - объект устройства, создаваемый фильтр - драйвером.
22) HAL (Hardware Abstraction Layer) - слой аппаратных абстракций.
Данный слой позволяет абстрагироваться компонентам операционной системы от особенностей конкретной платформы.
23) Synchronization Objects (Обьекты синхронизации) - с помощью этих
объектов потоки корректируют и синхронизируют свою работу.
24) Device ID - идентификатор устройства.
25) DMA (Direct Memory Access) - метод обмена данными между устройством и памятью
(оперативной) в котором центральный процессор не принимает участия.
25) Polling - это особый метод программирования, при котором не устройство посылает сигналы прерывания драйверу,
а сам драйвер периодически опрашивает обслуживаемое им устройство.
26) Port Driver (Порт-драйвер) - низкоуровневый драйвер,
принимающий системные запросы. Изолирует классовые драйверы устройств от аппаратной специфики последних.
Ну вот, пожалуй, и хватит терминов. В будущем,
если нужны будут какие-нибудь уточнения по теме,
я обязательно их укажу. А теперь, раз уж эта статья
теоретическая, давай-ка взглянем на архитектуру Windows NT с высоты птичьего полёта.
Краткий экскурс в архитектуру Windows NT
Наш обзор архитектуры Windows NT мы начнём с разговора об уровнях разграничения привилегий. Я уже упоминала об user и kernel mode.
Эти два понятия тесно связаны с так называемыми кольцами (не толкиеновскими ).
Их ( колец) в виде всего четыре: Ring3,2,1 и 0. Ring3 - наименее привилегированное кольцо,
в котором есть множество ограничений по работе с устройствами,
памятью и т.д. Например, в третьем кольце нельзя видеть адресное пространство других приложений без особого на то разрешения. Естественно,
трояну вирусу etc эти разрешения получить будет трудновато, так что хакеру в третьем кольце жизни никакой. В третьем кольце находится user mode. Kernel mode сидит в нулевом кольце - наивысшем уровне привилегий. В этом кольце можно всё:
смотреть адресные пространства чужих приложений без каких - либо ограничений и разрешений, по своему усмотрению поступать с любыми сетевыми пакетами, проходящими через машину, на всю жизнь скрыть какой-нибудь свой процесс или файл и т.д. и т.п. Естественно,
просто так пролезть в нулевое кольцо не получиться:
для этого тоже нужны дополнительные телодвижения. У легального драйвера с этим проблем нет:
ему дадут все необходимые API - шки, доступ ко всем нужным системным таблицам и проч. Хакерской же нечисти опять приходиться туго:
все необходимые привилегии ему приходиться "выбивать"
незаконным путём. Но это уже тема отдельной статьи, и мы к ней как-нибудь ещё вернёмся. А пока продолжим.
У тебя наверняка возник законный вопрос:
а что же сидит в первом и втором кольцах ? В том то всё и дело,
что программисты из Microsoft почему - то обошли эти уровни своим вниманием. Пользовательское ПО сидит в user mode,а всё остальное (ядро,
драйвера. ) - в kernel mode. Почему они так сделали - загадка, но нам это только на руку. А теперь разберёмся с компонентами (или, иначе говоря, слоями ) операционной системы Windows
NT.
Посмотри на схему - по ней многое можно себе уяснить. Разберём её подробнее.
С пользовательским режимом всё понятно. В kernel mode самый низкий уровень аппаратный. Дальше идёт HAL, выше - диспетчер ввода - вывода и драйвера устройств в одной связке, а также ядрышко вместе с исполнительными компонентами. О HAL я уже говорила, поэтому поподробнее поговорим об исполнительных компонентах. Что они дают? Прежде всего они приносят пользу ядру. Как ты уже наверняка уяснил себе по схеме, ядро отделено от исполнительных компонентов. Возникает вопрос:
почему ? Просто на ядре оставили только одну задачу:
просто управление потоками, а все остальные задачи (управление доступом,
памятью для процессов и т.д.) берут на себя исполнительные компоненты (еxecutive). Они реализованы по модульной схеме, но несколько компонентов её (схему) не поддерживают . Такая концепция имеет свои преимущества:
таким образом облегчается расширяемость системы. Перечислю наиболее важные исполнительные компоненты:
1) System Service Interface (Интерфейс системных служб )
2) Configuration Manager (Менеджер конфигурирования)
3) I/O Manager (Диспетчер ввода-вывода,ДВВ)
4) Virtual Memory Manager,VMM (Менеджер виртуальной памяти)
5) Local Procedure Call,LPC (Локальный процедурный вызов )
6) Process Manager (Диспетчер процессов)
7) Object Manager (Менеджер объектов)
Так как эта статья - первая в цикле, обзорная, подробнее на этом пока останавливаться не будем. В процессе практического обучения написанию драйверов, я буду разъяснять все неясные термины и понятия. А пока перейдём к API.
API (Application Programming Interface) - это интерфейс прикладного программирования. Он позволяет обращаться прикладным программам к системным сервисам через их специальные абстракции. API-интерфейсов несколько, таким образом в Windows-системах присутствуют несколько подсистем. Перечислю:
1) Подсистема Win32.
2) Подсистема VDM (Virtual DOS Machine - виртуальная ДОС - машина)
3) Подсистема POSIX (обеспечивает совместимость UNIX - программ)
4) Подсистемиа WOW (Windows on Windows). WOW 16 обеспечивает совместимость 32-х разрядной системы с 16-битными приложениями. В 64-х разрядных системах есть подсистема WOW 32,
которая обеспечивает аналогичную поддержку 32 - битных приложений.
5) Подсистема OS/2. Обеспечивает совместимость с OS/2 приложениями.
Казалось бы, всё вышеперечисленное однозначно говорит в пользу WINDOWS NT систем!
Но не всё так хорошо. Основа WINDOWS NT (имеются ввиду 32-х разрядные версии) - подсистема Win32. Приложения, заточенные под одну подсистему не могут вызывать функции другой. Все остальные (не Win32) подсистемы существуют в винде только в эмуляции и реализуются функции этих подсистем только через соответствующие функции винды. Убогость и ограниченность приложений, разработанных, скажем, для подсистемы POSIX и запущенных под винду - очевидны.
Увы.
Подсистема Win32 отвечает за графический интерфейс пользователя, за обеспечение работоспособности Win32 API и за консольный ввод - вывод. Каждой реализуемой задаче
соответствуют и свои функции: функции, отвечающие за графический фейс,
за консольный ввод - вывод (GDI - функции) и функции управления потоками,
файлами и т.д. Типы драйверов, наличествующие в Windows, я уже упоминала в разделе терминов:
монолитный драйвер, фильтр - драйвер и т.д. А раз так, то пора закругляться. Наш краткий обзор архитектуры Windows NT можно считать завершённым. Этого тебе пока хватит для общего понимания концепций Windows NT, и концепций написания драйверов под эту ось - как следствие.
Инструменты
Описать и/или упомянуть обо всех утилитах, могущих понадобиться при разработке драйверов - немыслимо. Расскажу только об общих направлениях.
Без чего нельзя обойтись ни в коем случае - это Microsoft DDK (Driver Development Kit ). К этому грандиозному пакету прилагается и обширная документация. Её ценность - вопрос спорный. Но в любом случае, хотя бы ознакомиться с первоисточником информации по написанию драйверов для Windows - обязательно. В принципе, можно компилять драйвера и в Visual Studio, но это чревато долгим и нудным копанием в солюшенах и vcproj-ектах, дабы код твоего драйвера нормально откомпилировался. В любом случае, сорцы придётся набивать в визуальной студии, т.к. в DDK не входит
нормальная IDE. Есть пакеты разработки драйверов и от третьих фирм:
WinDriver или NuMega Driver Studio, например. Но у них есть отличия от майкрософтовского базиса функций (порой довольно большие ) и многие другие мелкие неудобства. Так что DDK - лучший вариант. Если же ты хочешь писать драйвера исключительно на ассемблере, тебе подойдёт KmdKit (KernelMode Driver DevelopmentKit) для MASM32. Правда, этот вариант только для Win2k/XP.
Напоследок нельзя не упомянуть такие хорошие проги, как PE
Explorer, PE Browse Professional Explorer, и такие незаменимые, как дизассемблер IDA и лучший отладчик всех времён и народов SoftICE.
Ну вот и подошла к концу первая статья из цикла про написание драйверов под Windows. Теперь ты достаточно "подкован" по
теоретической части, так что в следующей статье мы перейдём к практике. Желаю тебе удачи в этом интереснейшем деле - написании драйверов! Да не облысеют твои пятки!
Python – один из самых популярных языков для веб-автоматизации с Selenium, поскольку в нем есть упрощенный синтаксис, который позволяет выполнять больше задач за меньшее количество строк кода! Таким образом, Python и Selenium создают идеальную комбинацию для автоматизированного тестирования в вебе.
Все больше разработчиков, как и я в свое время, осваивают Python, который считается третьим по популярности языком, если верить опросу Stack Overflow Developer Survey 2021. Поэтому шпаргалка по Selenium с Python может служить для ознакомления с полезным API для автоматизации веб-сайтов (или веб-приложений).
Если в Selenium вы новичок и вам интересно, что же это такое, мы рекомендуем ознакомиться с нашим руководством What is Selenium?.
В этой статье мы разберемся с модулем в Python, который предоставляет нам функционал Selenium WebDriver. Для установки пакета Selenium WebDriver мы воспользуемся Python Package Index (PyPI). Выполните в терминале следующую команду, чтобы установить Selenium для Python:
Если вы хотите запускать свои сценарии тестирования онлайн с Selenium Grid, воспользуйтесь LambdaTest для автоматизации тестирования.
Эта библиотека Python оборачивает Selenium WebDriver и предоставляет методы для автоматизации целого ряда задач, таких как заполнение форм, вход на сайт, нажатие на кнопки и многое другое. Кроме того, вы можете ознакомиться с руководством по Selenium с Python, в котором подробно рассматриваются неотъемлемые аспекты Selenium Python с точки зрения автоматизации веб-тестирования.
Команды, которые вы увидите в шпаргалке, можно использовать в качестве удобного ресурса для всех, кто играется с Selenium на Python для автоматизации веб-приложений. Если вам нужно краткое описание работы Selenium в Python, ознакомьтесь с руководством, в котором подробно рассматривается архитектура Selenium WebDriver и освещаются неотъемлемые аспекты, связанные с Selenium WebDriver в Python. Итак, давайте начнем нашу шпаргалку!
Импортируем библиотеку Selenium
Перед тем, как начать пользоваться командами Selenium, вам нужно импортировать пакет Selenium WebDriver.
Инициализация дайвера в Python
После загрузки соответствующего драйвера браузера вам необходимо запустить Selenium WebDriver и драйвер браузера (например, ChromeDriver для Chrome, GeckoDriver для Firefox и т.д.).
Для Internet Explorer
Если местоположения драйвера браузера нет в переменной PATH (или если его нет в System Path), нужно добавить следующие аргументы:
executable_path : Путь к вашему веб-драйверу Selenium (бинарный файл)
options : Параметры, касающиеся выполнения веб-браузеров
Настройка параметров Selenium WebDriver
Класс Options в Selenium обычно используется в сочетании с желаемыми возможностями кастомизации Selenium WebDriver.
Так вы можете выполнять различные операции, такие как открытие браузера (Chrome, Firefox, Safari, IE, Edge и т.д.) в режиме увеличения, включение и отключение расширений браузера, отключение режима GPU, отключение всплывающих окон и многое другое. Поэтому важно хорошо разобраться в этом разделе шпаргалки по Selenium в Python, поскольку так вы сможете решить проблемы автоматизации, связанные с изменением свойств браузера, о которых мы говорили ранее.
Для Chrome
Импорт опций Chrome
2. Инициализация опций Chrome
3. Добавление желаемых возможностей
4.Добавление желаемых возможностей сессии
Для Firefox
Импорт опций Firefox
2. Инициализация опций Firefox
3. Добавление желаемых возможностей
4. Добавление желаемых возможностей сессии
Поиск элемента
Поиск элемента по атрибуту ID
В этом методе поиск элемента в DOM производится по ID. ID уникален для каждого элемента на странице. Таким образом, с помощью ID можно однозначно идентифицировать элемент. Например, ниже показано использование атрибута ID для поиска веб-элементов на странице входа в систему LambdaTest:
Так вы можете воспользоваться атрибутом ID в Selenium:
Поиск элемента по классу CSS
Элементы в HTML DOM еще можно найти по имени класса, который хранится в атрибуте класса HTML-тега. У класса может быть несколько экземпляров, но метод вернет первый элемент с соответствующим классом.
Так можно использовать имя класса для определения местоположения элемента Email Address на странице LambdaTest:
Поиск элемента по имени
Веб-элементы, такие как открывающий тег, имеют связанный с ними атрибут Name. В Selenium есть метод поиска веб-элементов с помощью атрибута NAME. Если у нескольких элементов одинаковое имя, он вернет первое совпадение.
Вот HTML-код, в котором есть элемент ввода имени Name.
Ниже показано использование метода Selenium Python для определения местоположения веб-элемента с помощью атрибута NAME:
Поиск элемента по XPath
XPath использует путь для выбора узлов и определения местоположения необходимого веб-элемента. Метод find_element_by_xpath() используется для поиска соответствующего элемента в документе с помощью XPath. Вы можете почитать руководство по XPath в Selenium, чтобы получить более глубокое представление об использовании XPath для поиска веб-элементов при автоматизации с Selenium.
Ниже показан поиск элемента email с помощью атрибута XPath:
Поиск элемента по тегу
Этот метод используется для поиска и выбора веб-элементов по HTML-тегу. Метод find_element_by_tag_name() используется для поиска таких тегов, как H1, DIV, INPUT и т.д. Если тег встречается несколько раз, он возвращает первый совпадающий.
Ниже показан поиск элемента с email-адресом по тегу:
Поиск элемента по тексту ссылки и частичному тексту ссылки
Этот метод ищет элемент по тексту ссылки (целому или части). При поиске частичного текста ссылки не ищется точное совпадение со строковым значением, поскольку ищется подмножество строк (в тексте ссылки).
Локатор текста ссылок в Selenium и частичного текста ссылок работает только со ссылками текущего веб-приложения.
Ниже показан поиск элемента на странице входа LambdaTest с помощью локатора текста ссылок:
Ниже показан поиск элемента на странице входа LambdaTest с помощью локатора частичного текста ссылок:
Различные методы поиска элементов
Есть два метода, которые могут быть полезны для поиска элементов страницы в сочетании с классом «By» для выбора атрибутов.
Обратите внимание, что нет никакой разницы между методами find_element_by_tag и find_element(By.tag) . По умолчанию метод find_element_by_tag вызывает find_element(By.tag) .
find_element возвращает первый экземпляр из нескольких веб-элементов с определенным атрибутом в DOM. Метод вызывает исключение NoSuchElementException , если ни один элемент не соответствует требуемому локатору. Прочитайте статью о популярных исключениях Selenium, чтобы лучше понять, из-за чего они возникают.
find_elements возвращает список всех экземпляров веб-элементов, соответствующих определенному атрибуту. Список будет пустым, если в DOM нет нужных элементов.
Ниже вы увидите список атрибутов, доступных для класса By :
TAG_NAME = “tag name”
CLASS_NAME = “class name”
LINK_TEXT = “link text”
PARTIAL_LINK_TEXT = “partial link text”
Дальше показан пример использования метода find_element , который использует локатор XPath для поиска нужного веб-элемента:
Открытие ссылки или документа
Перед выполнением любых операций с веб-элементами, присутствующими на странице, важно открыть целевой URL-адрес (или тестовый URL-адрес). Далее вы увидите несколько способов открыть URL-адрес в Selenium с Python:
driver.get(URL)
Метод driver.get() переходит на страницу, которая передается методу в параметре. Selenium WebDriver будет ждать, пока страница не загрузится полностью, после чего он запустит событие «onload» и вернет управление сценарию тестирования. В статье Selenium Waits in Python вы можете найти дополнительную информацию про обработку ожиданий в Selenium.
Обновление страницы
Бывают сценарии, в которых нужно обновлять содержимое страницы. Метод обновления страниц в Selenium WebDriver используется для обновления веб-страниц.
Метод driver.refresh() обновляет текущую веб-страницу. Он не принимает никаких аргументов и не возвращает никаких значений.
Ввод текста в веб-элемент
Метод send_keys() в Python используется для ввода текста в текстовый элемент. Такой текст передается методу в качестве аргумента. Тот же метод можно использовать для имитации нажатия клавиш в любом поле (например, в полях ввода формы).
Пример использования send_keys() , в котором адрес электронной почты передается текстовому элементу на странице регистрации LambdaTest:
Удаление текста в веб-элементе
Метод element.clear() в Selenium используется для удаления текста из полей, таких как поля ввода формы и т.д.
Пример использования метода очистки содержимого поля ввода электронной почты на домашней странице LambdaTest:
Нажатие на веб-элемент
Метод element.click() в Selenium используется для нажатия на элемент, такой как ссылка-якорь, кнопка и т.д.
Вот так с помощью метода click() можно нажать на кнопку на домашней странице LambdaTest:
Перетаскивание веб-элемента
Перетаскивание объектов является одним из широко используемых сценариев в популярных приложениях (или программах), таких как Canvas, Google Drive, Trello, Asana и т.д. Метод drag_and_drop (элемент, цель) в Selenium помогает автоматизировать функционал перетаскивания веб-элементов из источника и переноса их в целевую область (или элемент).
У класса Actions в Selenium есть два метода, с помощью которых вы можете выполнять операции перетаскивания при тестировании кроссбраузерной совместимости. Обязательно ознакомьтесь с нашим подробным руководством, в котором есть информация о том, как выполнять перетаскивание в Selenium.
Вот простой пример использования метода drag_and_drop() :
Выбор опции
Select (элемент) предоставляет полезные методы для взаимодействия с раскрывающимися списками, выбора элементов и многого другого.
Так можно выбрать элемент по индексу:
Вот некоторые вариации выбора нужного элемента с помощью метода select_by_*() :
Метод
Описание
Метод принимает целочисленное значение - индекс опции, которую мы хотим выбрать.
Метод принимает строковое значение и выбирает опцию, в которой есть нужный текст.
Метод принимает строковое значение и выбирает параметр с тем же значением атрибута.
Метод позволяет отменить выбор всех выбранных опций.
Навигация между окнами
Если у вас несколько окон, может потребоваться переключаться между ними перед выполнением действий с веб-элементами из DOM.
driver.switch_to_window(“имя_окна”)
Метод switch_to_window() Selenium WebDriver позволяет переключиться на нужное окно. Дескриптор окна передается в качестве аргумента методу switch_to_window() .
Все последующие вызовы WebDriver теперь применяются к окну в фокусе (или к новому окну после переключения).
driver.window_handles
Свойство window_handles WebDriver возвращает дескрипторы окон. Теперь вы можете использовать метод switch_to_window() для перехода к любому окну из списка window_handles .
driver.current_window_handle
Метод current_window_handle() возвращает дескриптор текущего окна (или окна в фокусе).
Переключение на iFrame
Selenium WebDriver не умеет получать доступ или находить веб-элементы внутри iFrame в контексте главной страницы. Следовательно, вам необходимо переключиться на iFrame, прежде чем обращаться к элементам внутри него.
driver.switch_to_frame(“имя_iframe”)
Метод switch_to_frame() в Selenium Python позволяет менять контекст WebDriver из контекста главной страницы. Также мы можем получить доступ к сабфреймам, добавив между путем и индексом точку.
driver.switch_to_default_content()
Метод позволяет возвращаться обратно к контексту главной страницы.
Обработка всплывающих окон и оповещений
Существует всего три основных типа всплывающих окон и предупреждений, которые обычно используются в веб-приложениях:
driver.switch_to.alert
Свойство switch_to.alert в WebDriver возвращает открытый в данный момент объект alert . Вы можете принять его, отклонить, прочитать содержимое или ввести его в командную строку.
alert_obj.accept()
Как только у вас есть дескриптор окна alert (например, alert_obj ), метод accept() поможет вам принять всплывающее окно предупреждения.
alert_obj.dismiss()
После того как вы переключились на окно alert (например, alert_obj ), вы можете использовать метод dismiss() , чтобы отклонить всплывающее окно предупреждения.
alert_obj.text()
Получение кода страницы
Метод page_source() в Selenium WebDriver используется для получения кода страницы.
Навигация по истории браузера
Selenium WebDriver на Python предоставляет несколько функций для перемещения вперед-назад по истории браузера.
driver.forward()
Этот метод позволяет сценариям перемещаться на один шаг вперед по истории браузера.
driver.back()
Этот метод позволяет сценариям перемещаться на один шаг назад по истории браузера.
Обработка Cookie в Selenium
Обработка файлов cookie в Selenium WebDriver является одним из распространенных сценариев, с которым вам, возможно, придется иметь дело при автоматизации. Можно выполнять различные операции, такие как добавление, удаление, получение имени файла cookie и многое другое.
driver.add_cookie()
Этот метод помогает настроить файл cookie для сессии Selenium. Он принимает значения в виде пары ключ-значение.
Этот метод выводит все доступные файлы cookie для текущей сессии Selenium.
driver.delete_cookie()
Есть возможность удалить определенный файл cookie или все файлы cookie, связанные с текущей сессией Selenium.
Установка размера окна
Настройка тайм-аутов в Selenium WebDriver
Когда браузер загружает страницу, веб-элементы внутри нее могут загружаться через различные промежутки времени. Это может создать сложности при взаимодействии с динамическими элементами, присутствующими на странице.
Если элемент отсутствует в DOM веб-страницы, метод locate вызовет исключение. Ожидания в Selenium позволяют добавлять задержки (в миллисекундах или секундах) между действиями, выполняемыми между загрузкой страницы и поиском требуемого веб-элемента.
Неявное ожидание и явное ожидание - два основных способа добавления задержек в код Selenium в Python для обработки динамических веб-элементов на странице.
Неявное ожидание в Selenium Python
Неявное ожидание информирует Selenium WebDriver о необходимости проверять DOM в течение определенного периода времени при попытке найти веб-элемент, который не доступен сразу после загрузки страницы.
По умолчанию неявное ожидание равно нулю. Однако, как только мы определяем его, оно устанавливается на время жизни объекта WebDriver. Ознакомьтесь с подробным руководством, в котором более детально демонстрируется использование неявного ожидания в Selenium Python.
Явное ожидание в Selenium Python
Явное ожидание в Selenium с Python используется, когда мы хотим дождаться выполнения определенного условия, прежде чем продолжить работу.
В Selenium WebDriver есть несколько удобных методов, которые позволяют подождать, пока не будет выполнено определенное условие. Например, явное ожидание можно получить с помощью класса webdriverWait в сочетании с ожидаемыми условиями в Selenium.
Вот некоторые из ожидаемых условий, которые можно использовать в сочетании с явным ожиданием в Selenium Python:
Приветствую Вас дорогие друзья! В этой статье мы поговорим о HTML ссылках. Это очень важный элемент и Вы, безусловно, должны знать, что это такое и где используются, так как ссылки присутствуют на каждом сайте.
Я еще не встретил в интернете такие сайты, чтобы не имели хотя бы одну ссылку. В первую очередь ссылки используются для связки страниц, то есть для того чтобы пользователь смог перейти с одной страницы на другую.
Также они используются в следующих случаях:
- Для того чтобы увеличить изображение на сайте.
- Для того чтобы скачать какой-то файл или посмотреть в браузере какой-то документ.
- Для того чтобы перейти к началу текущей страницы или к какому то абзацу другой страницы. Таким образом, делается ссылка на вверх.
Ссылки имеют следующий синтаксис:
Атрибут href является обязательным, в его значение указываем адрес страницы, на которую хотим перейти. Также в его значение мы можем указать путь к изображению или путь, к файлу которого хотим скачать.
Существует 2 типа адресов, это абсолютный адрес и относительный.
После клика по данной ссылке мы перейдём в социальную сеть ВКонтакте.
Относительный путь это путь относительно текущего файла. Допустим, в папку сайта находится два файла, главная страница index.html и страница о нас about.html. Для того чтобы мы смогли перейти с главной страницы на страницу о нас, в значение атрибута href у ссылки на главной странице нужно написать название файла который отвечает за страницу на которой хотим перейти. В нашем случае мы хотим сделать ссылку для перехода с главной странице на страницу о нас, поэтому в коде главной странице добавляем такую ссылку:
Изображение в качестве якоря ссылки
Вместо якоря ссылки может быть и изображение. Нажимая на изображение, мы перейдём на указанный адрес в значение атрибута ссылки href. Вот пример такой ссылки:
HTML код такой ссылки следующий:
Ссылка на изображение
Если в значение атрибута href указать адрес на какое-нибудь изображение, то при нажатии на данную ссылку, в браузере откроется изображение в полном ее размере.
Посмотрим это на примере. Напишем следующие строки в код странице.
Открываем страницу в браузере и смотрим на результат:
После клика на изображение, оно откроется в браузере.
Ссылка на файл
А теперь сделаем так чтобы пользователь смог скачать со страницы какой-нибудь файл. Этот файл может быть какой-то документ, какая-то песня, торрент-файл или архив.
И так для того чтобы это реализовать нужно в значение атрибута ссылки href, указать путь к файлу для скачивания. Например, нужно сделать так чтобы пользователь смог скачать какой-нибудь документ с расширением .docx. Для начала на хостинге сайта создаем папку для файлов, назовем ее, например files. Вставляем в созданную папку нужный файл и в коде страницы пишем следующие строки:
Теперь, когда пользователь нажмет на ссылку скачать документ, сразу начнется скачивание файла.
Аналогично можно сделать и с остальными типами файлов.
И на этом все. Из этой статьи Вы узнали следующие:
- Как создать ссылку на другую страницу.
- Как сделать изображение ссылкой.
- Как сделать ссылку на изображение
- Как создать ссылку на файл.
Теперь Вы сможете сделать все это сами. Без чьей либо помощи.
Похожие статьи:
Видео:
Понравилась статья?
Тогда поделитесь ею с друзьями и подпишитесь на новые интересные статьи.
Подписаться на новые статьи:
Поддержите пожалуйста мой проект!
Автор статьи: Мунтян Сергей
Читайте также: