Как написать простейший драйвер

Тема в разделе "WASM.ARTICLES", создана пользователем Mikl___, 19 мар 2022.

  1. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709

    Доступ к командам In/Out через драйвер режима ядра

    Статья с рабочим названием «Доступ к портам ввода/вывода под Windows XP» была написана в 2011. «Иллюстрации» к статье kernel-mode драйвера. Все драйвера открывают порты 42h, 43h, 61h, чтобы сыграть системным динамиком «Марш гладиаторов» (Opus 68 march (Vjezd gladiátorů)) чешского композитора Juliusa Fučíka (1872-1916), марш написан в 1897 году (композитор ― это дядя того самого Юлиуса Фучика, чехословацкого журналиста, антифашиста и коммуниста, который в тюрьме написал книгу «Репортаж с петлей на шее»).
    Переделаем драйвер beeper из KmdTutor by Four-F.
    Драйвер собирается при помощи следующих строк в bat-файле
    Код (Text):
    1. cls
    2. set filename=%1
    3. if exist %filename%.sys del if exist %filename%.sys
    4. set masm_path=\masm32
    5. %masm_path%\bin\ml /c /Cp /Gz /I%masm_path%\include /nologo /c /coff %filename%.asm || exit
    6. %masm_path%\bin\link /LIBPATH:%masm_path%\lib\ /nologo /driver /base:0x10000 ^
    7. /align:32 /out:%filename%.sys /subsystem:native %filename%.obj || exit
    8. if exist %filename%.obj del %filename%.obj

    Теория

    На материнской плате находится перепрограммируемый интервальный таймер ― система, состоящая из трех каналов, каждый из которых можно запрограммировать для работы в одном из шести режимов. На многих современных материнских платах расположено два таких таймера, следовательно, число каналов равно шести.
    таймерпортназначение портаназначение канала
    первый таймер40hканал #0отвечает за ход системных часов. Сигнал с этого канала вызывает прерывание времени. 18,2 раз в секунду выполняется процедура, на которую направлен вектор прерывания #8. Эта процедура производит изменения в области памяти, где хранится текущее время. В специальном регистре задвижки хранится число синхроимпульсов, по прошествии которых сигнал таймера должен вызвать прерывание времени. Уменьшая это число (через порт каналов), можно заставить идти системные часы быстрее. Канал #0 используется также для синхронизации некоторых дисковых операций, поэтому при при изменении числа синхроимпульсов необходимо восстановить первоначальное значение перед использованием обращений к дискам.
    41hканал #1отвечает за регенерацию памяти. Можно уменьшить число циклов регенерации памяти в секунду, однако это можно делать лишь в небольших пределах, так как при увеличении промежутка регенерации возрастает вероятность сбоя памяти
    42hканал #2обычно задействуют для работы с динамиком, хотя можно использовать и для других целей, например для синхронизации какого-либо внешнего устройства.
    43hуправляющий регистр
    второй таймер для компьютера с шиной Microchannel44hканал #0
    45hканал #1
    46hканал #2
    47hуправляющий регистр
    второй таймер для компьютера с шиной EISA48hканал #0
    49hканал #1
    4Ahканал #2
    4Bhуправляющий регистр
    Таблица #1:Пространство портов ввода/вывода для таймера​
    Второй канал таймера управляет системным динамиком компьютера, генерируя прямоугольные импульсы с частотой 1193180/начальное значение счетчика герц. Начальное значение счетчика является 16-битным, и устанавливается через порт 42h. 1193180 Гц ― частота тактового генератора таймера. Динамик включается и выключается при выводе специального значения в порт управления динамиком с номером 61h, связанный с микросхемой программируемого контроллера периферийного интерфейса Intel 8255. Для включения динамика нужно прочитать байт из порта 61h, установить в единицу его два младших бита (0-ой бит разрешает работу канала таймера, а 1-ый бит включает динамик), а затем записать полученное значение в тот же порт. Чтобы отключить динамик, нужно сбросить значение двух младших битов порта 61h. Все управление таймером осуществляется путем вывода байта в порт 43h. Назначение битов порта 43h приведены в таблице #2.
    Номер битаназначение
    Если 7-6 биты не равны 11b, значит байт, посылаемый в порт 43h ― это команда программирования канала[/B]
    7-6Номер канала:
    00b ― 0-ой канал
    01b ― 1-ый канал
    10b ― 2-ой канал
    5-4Индикатор считывания/записи
    00b ― зафиксировать текущее значение счетчика для чтения (в этом случае биты 3-0 не используются)
    01b ― чтение/запись только младшего байта
    10b ― чтение/запись только старшего байта
    11b ― чтение/запись сначала младшего, а потом старшего байта
    3-1Режим работы канала:
    000b ― Прерывание IRQ0 при достижении нуля (концу счета). Сигнал GATE=1 разрешает счет, а GATE=0 запрещает счет, причем GATE не влияет на выход OUT. Содержимое CR передается в CE по первому импульсу CLK после того, как процессор осуществил запись в CR, независимо от сигнала на входе GATE. Импульс, который загружает CE, не учитывается при счете. На выходе OUT формируется низкий уровень при записи в регистр управления, который сохраняется до достижения счетчиком 0. Режим 0 предназначен в основном для счета событий.
    001b ― Аппаратно-перезапускаемый одновибратор (ждущий мультивибратор). После загрузки значения N в CR переход 0->1 на входе GATE вызывает загрузку CE, переход 1->0 на выходе OUT и запускает счет. Когда счетчик достигнет 0, на выходе OUT формируется высокий уровень; таким образом, результатом является отрицательный импульс на выходе OUT с продолжительностью N периодов синхронизации.
    010b ― Периодический интервальный таймер (генератор импульсов). После загрузки значения N в CR следующий импульс синхронизации осуществляет передачу из CR в CE. На выходе OUT возникает переход 1->0, когда счетчик достигает 0; низкий уровень сохраняется в течение одного импульса CLK. Затем на выходе OUT появляется высокий уровень, производится повторная загрузка CE из CR; в результате на выходе OUT появляется отрицательный импульс через N тактов синхронизации. Сигнал GATE=1 разрешает счет, а GATE=0 запрещает. Переход 0->1 на выходе GATE вызывает ренинициализацию счета следующим импульсом синхронизации. Данный режим применяется для реализации периодического интервального таймера.
    011b ― Генератор прямоугольного сигнала. Аналогичен режиму 2, но на входе OUT формируется низкий уровень при достижении половины начального счета; этот уровень сохраняется до достижения счетчиком 0. Как и прежде, сигнал GATE разрешает и запрещает счет, а его переход 0->1 реинициализирует счет. Этот режим применяется в генераторах, определяющих скорость передачи в бодах.
    100b ― Программно-запускаемый строб. Аналогичен режиму 0, но на выходе OUT в процессе счета действует высокий уровень, а при достижении счетчиком 0 появится отрицательный импульс с продолжительностью в один такт синхронизации.
    101b ― Аппаратно-запускаемый строб с перезапуском. После загрузки CR переход 0->1 на входе GATE вызывает передачу из CR в CE следующим импульсом CLK. В процессе счета на выходе OUT действует высокий уровень, а при достижении счетчиком 0 формируется отрицательный импульс с продолжительностью в один период CLK. Сигнал GATE может в любой момент времени реинициализировать счет.
    0Формат счетчика:
    0 ― двоичное 16-разрядное число с диапазоном от 0 до 0FFFFh. Максимальное значение счетчика = 216-1
    1 ― двоично-десятичное число с диапазоном от 0000 до 9999. Максимальное значение счетчика = 104-1
    Если 7-6 биты равны 11b, значит байт посылаемый в порт 43h ― это команда чтения счетчиков
    7-611b ― команда чтения счетчиков
    5-400b ― сначала состояние канала, потом значение счетчиков
    01b ― значение счетчиков
    10b ― состояние канала
    3-1Команда относится к каналам 0-2
    Таблица #2: Назначение битов порта 43h​
    Частота генерируемого звука задается с помощью микросхемы программируемого интервального таймера Intel 8253. Этот контроллер в числе прочего определяет, сколько импульсов в секунду следует послать на системный динамик. Таймер вырабатывает базовую частоту 1,193180 МГц. Первоначальное значение делителя частоты установлено в (1)0000h, что эквивалентно 65536. Для этого числа частота прерываний таймера равна 1193180/65536=18,20648193359375 Гц, что ниже граничной частоты восприятия звука человеком. Мы можем посылать Intel 8253 другое число для деления базовой частоты 1,19 МГц. Для установки таймера в правильный рабочий режим посылаем число 0B6h в порт 43h. После этого можно использовать порт 42h для передачи делителя. Если отправить в порт 42h число 5000, то получим частоту следования импульсов 1193180/5000=238,636 Гц, чуть ниже ноты си третьей октавы (для третьей октавы значение частоты из таблицы #3 делят на 2, а для пятой октавы ― умножают на 2). Так как делитель является 16-разрядным числом, поэтому передаем его двумя частями. Сначала отправляем младший байт, а затем старший байт.
    нотачастота (Гц)делитель =
    1193180/частота
    до#277,2310CFh
    ре293,660FDFh
    ре#311,130EFAh
    ми329,630E23h
    фа349,230D58h
    фа#3700C98h
    соль3920BE3h
    соль#415,30B39h
    ля4400A97h
    ля#466,169FFh
    си493,8896Fh
    до523,258E8h
    Таблица #3: Ноты четвертой октавы​

    Практика

    Выводим в порт 43h число 0B6h=10.11.011.0b, мы помещаем в управляющий регистр таймера значение, определяющее:
    • номер канала, которым мы будем управлять (10b=2-ой канал)
    • тип операции (11b = чтение/запись сначала младшего, а потом старшего байта)
    • режим работы канала (011b = генератор прямоугольных импульсов (основной режим))
    • формат счетчика (0 = 16-разрядное число от 0 до 0FFFFh)
    Код (ASM):
    1.     mov al,0B6h
    2.     out 43h,al
    Затем, в порт 42h выводим 16-битное начальное значение счетчика. Сначала младший байт, затем старший.
    Код (ASM):
    1.         mov esi,offset melody; данные
    2.     mov ecx,size_melody  ; счетчик
    3.     mov dx,42h      ;используем порт 42h для передачи делителя
    4. a0: . . . ; создаем задержку между импульсами в 40 мкСек
    5.     outsb
    6.     loop a0
    Команда OUTSB выводит данные в порт ввода- вывода, номер которого загружен в регистр DX, из ячейки памяти по адресу DS:ESI (массив melody), после выполнения команды OUTSB содержимое регистра ESI увеличивается на единицу, это будет происходить до тех пор, пока содержимое регистра ECX не станет равным нулю. Для получения задержки, чтобы не зависеть от быстродействия конкретного CPU, используем функцию KeStallExecutionProcessor. После проигрыша мелодии выключаем динамик, сбрасывая два младших бита. На время работы с регистрами таймера, запрещаем аппаратные прерывания (команда CLI). На этом работу нашего драйвера можно считать законченной. Драйвер возвращает системе STATUS_DEVICE_CONFIGURATION_ERROR ― код фиктивной ошибки и благополучно удаляется системой из памяти. Код ошибки возвращен только для того, чтобы система сама удалила драйвер и он понапрасну не «болтался» в памяти. Когда мы доберемся до полнофункциональных драйверов, то, естественно, будем возвращать STATUS_SUCCESS.
    Код (ASM):
    1.     mov eax, STATUS_DEVICE_CONFIGURATION_ERROR ;возвращаем код ошибки, для
    2.     ret     ;того, чтобы система удалила драйвер из памяти
    Далее исходный текст драйвера scp00.sys
    Код (ASM):
    1. ; masm windows native #
    2. ;написано на основе драйвера режима ядра beeper из «KmdTutor by Four-F»
    3. .686P
    4. .model flat
    5. include ntstatus.inc
    6. include ntddk.inc
    7. includelib hal.lib
    8. extern _imp__KeStallExecutionProcessor@4:dword
    9. .code
    10. DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
    11.     cli
    12.     in al,61h   ;получаем текущий статус порта B
    13.     or al,00000011b ;включаем системный динамик и таймер
    14.     out 61h,al  ;заменяем байт
    15.     mov al,0B6h     ;установка канала 2 микросхемы 8253 в правильный
    16.     out 43h, al     ;рабочий режим для приема делителя
    17.         mov esi,offset melody; данные
    18.     mov ecx,size_melody  ; счетчик
    19.     mov dx,42h      ;используем порт 42h для передачи делителя
    20. a0: push ecx        ;запоминаем содержимое edx и ecx, так как функция
    21.     push edx        ;KeStallExecutionProcessor их обязательно изменит
    22.     mov ecx,800; 50 мкСек * 800 = 40 мСек
    23. a1: push ecx
    24.     push 50; максимальное количество мкСек для функции KeStallExecutionProcessor
    25.     call _imp__KeStallExecutionProcessor@4
    26.     pop ecx
    27.     loop a1
    28.     pop edx         ;восстанавливаем содержимое edx и ecx
    29.     pop ecx         ;команда outs выводит данные в порт ввода/вывода
    30.     outsb       ;номер, которого загружен в регистр DX,
    31.      loop a0     ;из ячейки памяти по адресу DS:ESI
    32.     in al,61h   ;получаем текущий статус порта B
    33.     and al,11111100b ;выключаем системный динамик и таймер
    34.     out 61h,al      ;заменяем байт
    35.     sti
    36.     mov eax, STATUS_DEVICE_CONFIGURATION_ERROR;возвращаем код ошибки, для
    37.     ret     ;того, чтобы система удалила драйвер из памяти
    38. melody  dw 2 dup(354h),2,2,2 dup(387h),2,2,2 dup(3BDh),2 dup(387h)
    39.     dw 2 dup(3BDh),2 dup(3F5h),2 dup(432h),2,2,2 dup(472h),2,2
    40.     dw 4 dup(4B5h),4 dup(472h),2 dup(3F5h),2,2,2 dup(432h),2,2
    41.     dw 2 dup(472h),2 dup(432h),2 dup(472h),2 dup(4B5h),2 dup(4FDh)
    42.     dw 2,2,2 dup(549h),2,2,4 dup(599h),4 dup(549h),2 dup(472h)
    43.     dw 2,2,5EEh,2,5EEh,2,4 dup(649h),4 dup(5EEh),2 dup(472h),2,2
    44.     dw 5EEh,2,5EEh,2,4 dup(649h),4 dup(5EEh),2 dup(70Eh),2 dup(6A8h)
    45.     dw 2 dup(649h),2 dup(5EEh),2 dup(599h),2 dup(549h),2 dup(4FDh)
    46.     dw 2 dup(4B5h),2 dup(472h),2,2,2 dup(432h),2,2,2 dup(3F5h),2,2
    47.     dw 2 dup(387h),2,2,8 dup(354h),2
    48. size_melody = $ - melody
    user-mode приложение может запустить драйвер следующими способами:
    1. использовать API-функции Service Control Manager'a;
    2. прописать драйвер в реестре «вручную» и загрузить его с помощью функции ZwLoadDriver. В реестре создается в минимум необходимых записей, после запуска драйвера его раздел удаляется из реестра;
    3. загрузить драйвер при помощи функции ZwSetSystemInformation;
    4. через динамический загрузчик Свена Шрайбера, прилагаемый к его книге «Недокументированные возможности Windows 2000» (сам загрузчик можно найти на WASM'е). (Ничего нового! Та же загрузка драйвера через API-функции Service Control Manager'a, единственный плюс ― ничего не нужно писать самому, всё, что от вас требуется ― это указать в командной строке динамическому загрузчику полный путь до вашего драйвера)

    Первый вариант user-mode приложения, которое запускает драйвер scp00.sys

    user-mode приложение, которое запускает scp00.sys драйвер, используя API-функции Service Control Manager'a: OpenSCManager, CreateService, StartService, CloseServiceHandle и DeleteService.
    Этот способ очень прост и хорошо документирован, он подходит для постоянной установки драйверов.
    Для запуска и управления драйвером необходимы следующие компоненты:
    • диспетчер управления службами (Service Control MenagerSCM). Именно благодаря ему мы будем иметь возможность легко и просто загружать драйверы;
    • программа управления службами (Service Control ProgrammSCP). Это программа третьего кольца; она работает с диспетчером управления службами (вызывает функции, которые он предоставляет), чтобы установить, запустить драйвер и завершить его работу;
    • собственно сам драйвер;
    • для запуска драйвера его необходимо зарегистрировать. За этот процесс в системе отвечает функция CreateService. Также при работе с драйвером потребуются следующие функции: StartService, ControlService, DeleteService, CloseServiceHandle
    Кроме того, для возможности работы с диспетчером управления службами необходимо получить к нему доступ при помощи функции OpenSCMenager. Функция CloseServiceHandle закрывает описатель, который возвращает функция OpenSCMenager. Все функции, которые предоставляет диспетчер управления службами, находятся в advapi32.dll.
    На конечном этапе загрузки системы перед появлением диалога регистрации пользователя запускается SCM (\%SystemRoot%\System32\Services.exe), который, просматривая раздел реестра HKEY_LOCAL_MASHINE\SYSTEM\CurentControlSet\Service\, создает свою внутреннюю базу данных (ServiceActive database или SCM database). Далее SCM находит в созданной базе все драйвера устройств и службы, помеченные для автоматического запуска, и загружает их.
    Рассмотрим процесс запуска и управления драйвером более подробно.
    Помещаем драйвер в сервисную базу. База должна быть предварительно открыта функцией OpenSCManager:
    Код (C):
    1. SC_HANDLE OpenSCManager(
    2.   LPCTSTR lpMachineName,
    3.   LPCTSTR lpDatabaseName,
    4.   DWORD dwDesiredAccess
    5. );

    Параметры:

    lpMachineName
    Указатель на строку (завершающуюся нулём), содержащую имя компьютера. Этот параметр мы сразу устанавливаем в NULL, так как будем открывать канал связи с SCM только на локальном компьютере.
    lpDatabaseName
    Указатель на строку (завершающуюся нулём), которая содержит имя открываемой базы данных менеджера управления сервисами. Этот параметр должен быть равен SERVICES_ACTIVE_DATABASE. Если этот параметр приравнять NULL, то по умолчанию будет открыта база SERVICES_ACTIVE_DATABASE. Так как мы не собираемся открывать никакую другую базу данных SCM, кроме активной в данный момент, просто, установим этот параметр в NULL.
    dwDesiredAccess
    Права доступа к менеджеру управления сервисами. Сообщает SCM, что мы собственно намереваемся делать с его базой данных. Нам могут быть полезны три значения:
    ЗначениеПредназначение
    SC_MANAGER_CONNECTдоступ на установку канала связи с SCM. По умолчанию (то есть если просто передать в этом параметре 0), устанавливается именно это значение. Хотя в документации ничего не говорится, что конкретно мы можем делать с этим уровнем доступа. А делать можно многое: запускать драйвер, останавливать, и даже удалять сведения о нем из базы данных SCM и из реестра.
    SC_MANAGER_CREATE_SERVICEдоступ на занесение в базу данных SCM нового драйвера. Так как мы собираемся занести туда своего представителя, то именно это значение и используем. Можно подумать, что никаких других прав, кроме регистрации нового драйвера, этот флаг не дает. Это не так. Так как флаг SC_MANAGER_CONNECT считается установленным по умолчанию, то и соответствующие права нам тоже предоставляются. Что тоже совсем не очевидно;
    SC_MANAGER_ALL_ACCESSпозволяет получить максимальный доступ
    Если канал связи с SCM успешно установлен, функция OpenSCManager вернет описатель (handle), предоставляющий доступ к активной базе данных SCM, который мы сохраняем в переменной hSCManager для дальнейшего использования.
    Получив доступ к базе SCM, мы регистрируем в ней свой драйвер, с помощью функции CreateService. Функция CreateService создает объект службы и добавляет его в указанную базу данных диспетчера управления службами.
    Код (C):
    1. SC_HANDLE CreateService(
    2.   SC_HANDLE hSCManager,
    3.   LPCTSTR lpServiceName,
    4.   LPCTSTR lpDisplayName,
    5.   DWORD dwDesiredAccess,
    6.   DWORD dwServiceType,
    7.   DWORD dwStartType,
    8.   DWORD dwErrorControl,
    9.   LPCTSTR lpBinaryPathName,
    10.   LPCTSTR lpLoadOrderGroup,
    11.   LPDWORD lpdwTagId,
    12.   LPCTSTR lpDependencies,
    13.   LPCTSTR lpServiceStartName,
    14.   LPCTSTR lpPassword
    15. );
    Параметры:
    hSCManager
    Дескриптор базы данных диспетчера управления службой. Этот дескриптор возвращается функцией OpenSCManager и должен иметь право доступа SC_MANAGER_CREATE_SERVICE. Определяет в какую именно базу мы добавляем сведения о новом драйвере.
    lpServiceName
    Указатель на строку с завершающим нулем, которая задает устанавливаемое имя службы. Максимальная длина строки ― 256 символов. База данных диспетчера управления службами сохраняет регистр символов, но при сравнении имени службы ― всегда без учета регистра. Прямой слэш (/) и обратный слэш (\) ― неприменяемые символы в имени службы.
    lpDisplayName
    Указатель на строку с завершающим нулем, которая имеет в своем составе отображаемое имя, которое используется пользовательскими программами интерфейса, чтобы идентифицировать службу. Эта строка имеет максимальную длину 256 символов. Имя сохраняется с учетом регистра в диспетчере управления службами. Сравнения отображаемого имени всегда не чувствительны к регистру.
    dwDesiredAccess
    Доступ к службе. Перед предоставлением требуемого доступа, система проверяет маркер доступа вызывающего процесса.
    ТипПредназначение
    SERVICE_ALL_ACCESSпозволяет получить максимальный доступ
    SERVICE_STARTдоступ на запуск драйвера вызовом функции StartService
    SERVICE_STOPдоступ на останов драйвера вызовом функции ControlService с параметром SERVICE_CONTROL_STOP
    DELETEдоступ на удаление сведений о драйвере из базы данных SCM вызовом функции DeleteService
    Нам потребуется выполнить всего два действия: запустить драйвер и удалить сведения о нем из базы данных SCM, а следовательно, и из реестра. Поэтому в этом параметре мы передаем комбинацию флагов SERVICE_START и DELETE. Останавливать запущенный драйвер нам не потребуется, так как его инициализация завершится ошибкой.
    dwServiceType
    Типы службы. Для драйвера может быть только SERVICE_KERNEL_DRIVER (Соответствует параметру Type в реестре)
    dwStartType
    Варианты запуска службы. Этот параметр может быть SERVICE_DEMAND_START Служба, запускается диспетчером управления службами, когда процесс вызывает функцию StartService. (Соответствует параметру Start в реестре)
    dwErrorControl
    Серьезность ошибки и предпринимаемое действие, если эта служба не в состоянии запуститься. Этот параметр может быть одним из следующих значений.
    ЗначениеПредназначение
    SERVICE_ERROR_IGNOREПрограмма запуска регистрирует ошибку, но продолжает операцию запуска.
    SERVICE_ERROR_NORMALПрограмма запуска регистрирует ошибку и показывает всплывающее окно сообщения, но продолжает операцию запуска.
    SERVICE_ERROR_SEVEREПрограмма запуска регистрирует ошибку. Если запускается последняя, заведомо без ошибок конфигурация, операция запуска продолжается. Иначе, система перезапускается с последней, заведомо без ошибок конфигурацией.
    SERVICE_ERROR_CRITICALЕсли возможно, программа запуска регистрирует ошибку . Если запускается последняя, заведомо без ошибок конфигурация, операция запуска завершается ошибкой. Иначе, система перезапускается с последней из известных конфигураций без ошибок.
    lpBinaryPathName
    Указатель на строку с завершающим нулем, которая имеет в своем составе полный путь доступа к двоичному файлу службы. Если путь имеет в своем составе пробел, он должен быть заключен в кавычки. Соответствует параметру ImagePath в реестре
    lpLoadOrderGroup
    Указатель на строку с завершающим нулем, именующую группу очередности загрузки, членом которой является эта служба. Задайте значение NULL или пустую строку, если служба не принадлежит группе. Программа запуска использует очередность загрузки групп, чтобы загрузить группы служб в указанном порядке по отношению к другим группам. Список очередности загрузки групп содержатся в следующем значении реестра:
    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceG roupOrder
    lpdwTagId
    Указатель на переменную, которая получает значение признака, являющееся уникальным в группе, заданной в параметре lpLoadOrderGroup. Задайте значение NULL, если Вы не изменяете существующий признак. Вы можете использовать признак для того, чтобы упорядочить запуск службы в пределах очередности загрузки группы, определяя вектор очередности признака в следующем значении реестра:
    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GroupOrd erList
    Признаки вычисляются только для служб драйвера, которые имеют типы пуска SERVICE_BOOT_START или SERVICE_SYSTEM_START.
    lpDependencies
    Указатель на массив имен служб, разделенных нулем, с двойным символом нуля в конце или очередности загрузки групп, которую система должна запустить перед этой службой. Задайте значение NULL или пустую строку, если служба не имеет никаких зависимостей. Зависимость от группы означает, что эта служба может запуститься тогда, если по крайней мере один член группы запущен после попытки запустить все члены группы. Вы должны ставить в начале имен группы SC_GROUP_IDENTIFIER так, чтобы они могли отличаться от имени службы, потому что службы и группы служб совместно используют то же самое пространство имен. Если драйвер не зависит от других драйверов, то в этом параметре можно указать NULL или указатель на пустую строку.
    lpServiceStartName
    Указатель на строку с завершающим нулем, которая задает имя учетной записи, с правами которой будет запущен драйвер. Если тип службы SERVICE_KERNEL_DRIVER, то этот параметр должен содержать имя объекта драйвера, которое используется системой для загрузки. Если используется имя объекта "драйвер" созданное подсистемой ввода-вывода, то этот параметр устанавливается равным NULL.
    lpPassword
    Указатель на строку с завершающим нулем, которая имеет в своем составе пароль к имени учетной записи, заданному параметром lpServiceStartName. Для служб драйвера пароли игнорируются, поэтому NULL
    Вызовом функции GetFullPathName, мы формируем строку с полным путем к файлу драйвера, состоящую из текущего каталога и имени файла драйвера, и передаем ее функции CreateService. CreateService регистрирует в базе данных SCM новый драйвер, и заполняет соответствующий подраздел реестра.
    Запуск драйвера осуществляется функцией StartService
    Код (C):
    1. BOOL StartService(
    2.   SC_HANDLE hService,
    3.   DWORD dwNumServiceArgs,
    4.   LPCTSTR* lpServiceArgVectors
    5. );
     
    Последнее редактирование: 20 мар 2022
  2. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709
    Параметры:
    hService
    Дескриптор службы. Этот дескриптор возвращается функцией OpenService или CreateService, и он должен иметь право доступа SERVICE_START.
    dwNumServiceArgs
    Число строк в массиве lpServiceArgVectors. Если lpServiceArgVectors имеет значение NULL, этот параметр может быть нулем. Для драйверов это всегда так.
    lpServiceArgVectors
    Указатель на массив указателей на строки с завершающим нулем, который передается службе как параметры. Службы драйвера не получают эти параметры, поэтому мы устанавливаем этот параметр в NULL.
    Функция StartService заставляет систему произвести действия, очень сильно напоминающие загрузку обыкновенной DLL. Образ файла драйвера проецируется на системное адресное пространство. При этом, возможности управлять адресом загрузки нет никакой. Да это и не нужно. Предопределенный адрес загрузки (preferred base address) у всех наших драйверов будет равен 10000h, что значительно ниже начала системного диапазона адресов. Пытаться установить его в какое-то другое значение не имеет смысла, так как система, все равно, будет загружать драйвер по случайному (для нас) адресу. Поскольку фактический адрес загрузки не совпадает с предопределенным, система производит настройку адресов пользуясь таблицей перемещений (relocation table), находящейся в секции .reloc файла драйвера. Затем производится связывание (fix-up) импорта. Кстати, импорт в файле драйвера раскинут в секции INIT и .idata. В .idata находится таблица адресов импорта (import address table, IAT). В ней содержатся адреса функций во внешних модулях. Она нужна драйверу постоянно. А в секции INIT содержится остальная часть импорта, необходимая только на этапе загрузки (имена внешних модулей и имена импортируемых функций), после которой, память занимаемая этой секцией освобождается. Когда образ драйвера подготовлен, управление передается на точку входа (entry point), которая находится в процедуре DriverEntry. Принципиальная разница, не считая уровня привилегий, в том, что код процедуры DriverEntry всегда выполняется одним из потоков процесса System, и, естественно, в адресном контексте этого процесса.
    Вызов StartService синхронный. Это значит, что она не вернет управление до тех пор, пока не отработает процедура DriverEntry в драйвере. Если инициализация драйвера прошла успешно, DriverEntry должна вернуть STATUS_SUCCESS, а функция StartService вернет значение отличное от нуля. И мы вновь окажемся в контексте потока вызвавшего StartService, то есть в контексте нашей SCP.
    Вызов StartService может завершиться неудачей, если база данных SCM заблокирована. Последующий вызов функции GetLastError вернет ERROR_SERVICE_DATABASE_LOCKED. Как написано в документации, в этом случае, следует подождать несколько секунд, и повторить попытку, но мы этого делать не будем, так как такая ситуация крайне маловероятна. И вообще, в данном случае, нас не интересует возвращаемое функцией StartService значение, так как драйвер уже проиграл свою мелодию и вернул код ошибки. То есть мы заранее знаем, что вызов StartService даст ошибку.
    Осталось привести систему в исходное состояние. Вызовом функции DeleteService мы удаляем сведения о драйвере из базы данных SCM и из реестра. Странно, но передавать описатель самой базы данных SCM в функцию DeleteService не нужно.
    Функция DeleteService отмечает указанную службу для удаления из базы данных диспетчера управления службами.
    Код (C):
    1. BOOL DeleteService(
    2.   SC_HANDLE hService
    3. );
    Параметр:
    hService
    Дескриптор службы. Этот дескриптор возвращается функцией OpenService или CreateService и он должен иметь право доступа DELETE.
    На самом деле, функция DeleteService ничего ниоткуда не удаляет. Она только сообщает системе, что это можно сделать, когда наступит благоприятный момент. А он наступит тогда, когда все описатели службы будут закрыты. Так как мы все еще держим описатель hService открытым, то удаления не происходит. Если попытаться вызвать DeleteService повторно, то он завершится неудачей, а последующий вызов функции GetLastError вернет ERROR_SERVICE_MARKED_FOR_DELETE.
    Вызовом функции CloseServiceHandle мы закрываем описатель диспетчера управления службами hService.
    Код (C):
    1. BOOL CloseServiceHandle(
    2.   SC_HANDLE hSCObject
    3. );
    Параметр:
    hSCObject
    Дескриптор объекта диспетчера управления службами или объекта службы, который закрывается. Дескрипторы объектов менеджера управления службами возвращаются функцией OpenSCManager, а дескрипторы объектов служб возвращаются или функцией OpenService или функцией CreateService.
    Поскольку больше открытых описателей службы нет, то именно в этот момент система приводит базу данных SCM в исходное состояние. Второй вызов CloseServiceHandle закрывает описатель самого SCM.
    Код (ASM):
    1. ; masm windows gui #
    2. .686P
    3. .model flat
    4. include windows.inc
    5.  
    6. includelib kernel32.lib
    7. includelib user32.lib
    8. includelib advapi32.lib
    9. extern _imp__ExitProcess@4:dword
    10. extern _imp__GetFullPathNameA@16:dword
    11. extern _imp__MessageBoxA@16:dword
    12. extern _imp__CloseServiceHandle@4:dword
    13. extern _imp__DeleteService@4:dword
    14. extern _imp__StartServiceA@12:dword
    15. extern _imp__CreateServiceA@52:dword
    16. extern _imp__OpenSCManagerA@12:dword
    17. .code
    18. start proc
    19. local hSCManager:HANDLE
    20. local acDriverPath[MAX_PATH]:CHAR
    21.         xor ebx,ebx
    22.     xor esi,esi;тип ошибки равен 0
    23.     mov edi,offset scp00_sys_name
    24.     ; Open a handle to the SC Manager database
    25.     push SC_MANAGER_CREATE_SERVICE;определяем нужный тип доступа
    26.     push ebx;адрес имени базы данных сервисов. Для открытия базы
    27. ;по умолчанию равно NULL
    28.     push ebx;имя (адрес имени) рабочей станции в сети, на которой хотят
    29. ;открыть базу. Для локального компьютера равна NULL
    30.     call _imp__OpenSCManagerA@12;открываем базу сервисов
    31.     xchg eax,ecx
    32.     jecxz err;if eax != NULL выводим сообщение об ошибке (тип ошибки равен 0)
    33.     inc esi;тип ошибки равен 1
    34.     mov hSCManager,ecx;при успешном выполнении возвращает дескриптор базы данных
    35.     push eax
    36.     push esp
    37.     lea eax,acDriverPath
    38.     push eax
    39.     push MAX_PATH
    40.     push edi;offset scp1_sys_name
    41.     call _imp__GetFullPathNameA@16
    42.         pop eax
    43.     mov [edi+5],ebx
    44.     ; Register driver in SCM active database
    45.     push ebx;пароль учетной записи. NULL нет пароля
    46.     push ebx;имя учетной записи, с которой должна запускаться служба.
    47. ;NULL предполагает, что служба запускается под именем LocalSystem
    48.     push ebx;сервисы и группы сервисов от которых зависит наш сервис NULL
    49.     push ebx;NULL
    50.     push ebx;порядок загрузки групп служб. В данном случае порядок не важен (NULL)
    51.     lea ecx,acDriverPath
    52.     push ecx;строка содержащая имя и полный путь к программе-сервису
    53.     push ebx;SERVICE_ERROR_IGNORE=0 уровень реакции на ошибку
    54.     push SERVICE_DEMAND_START;тип старта службы (здесь "запуск по требованию")
    55.     push SERVICE_KERNEL_DRIVER;тип сервиса "драйвер режима ядра"
    56.     push SERVICE_START + DELETE;возможный тип доступа к сервису
    57.     push edi;"scp00" параметр определяет отображаемое имя.
    58.     push edi;"scp00" адрес строки, содержащей имя одной из логических служб,
    59. ;по которому в дальнейшем будет возможно обращение к этой службе.
    60.     push hSCManager;дескриптор сервисной базы данных
    61.     call _imp__CreateServiceA@52;помещаем сервис в сервисную базу
    62.     test eax,eax;if eax == NULL выводим сообщение об ошибке=1
    63.     jne a1
    64. err:    push MB_ICONSTOP
    65.     push ebx;NULL
    66.     push handle[esi*4]
    67.     push ebx;NULL
    68.     call _imp__MessageBoxA@16;выводим тип ошибки
    69.     jmp a2;выходим из программы
    70. a1: push eax;hService для CloseServiceHandle
    71.     push eax;hService для DeleteService
    72.     push ebx;параметры передаваемые в службу, обычно NULL
    73.     push ebx;параметры передаваемые в службу, обычно 0
    74.     push eax;hService дескриптор возвращенный функцией CreateService
    75.     call _imp__StartServiceA@12;программный запуск зарегистрированного сервиса
    76.     ; Here driver scp00.sys plays melody
    77.     ;and reports error to be removed from memory
    78.     ;Remove driver from SCM database
    79.     call _imp__DeleteService@4;удаляем сервис
    80.     call _imp__CloseServiceHandle@4;закрываем базу сервисов
    81.     push hSCManager
    82.     call _imp__CloseServiceHandle@4;закрываем базу сервисов
    83. a2: push ebx;0
    84.     call _imp__ExitProcess@4
    85. start endp
    86. scp00_sys_name db "scp00.sys",0
    87. handle dd can_t_connect,can_t_register
    88. can_t_connect db "Can't connect to Service Control Manager.",0
    89. can_t_register db "Can't register driver.",0
    90. end start

    Второй вариант user-mode приложения, которое запускает драйвер scp00.sys

    Предварительно прописываем драйвер scp00.sys в реестре «вручную» (используем функции RegOpenKey, RegCreateKey, RegSetValue) и загружаем драйвер scp00.sys с помощью функции ZwLoadDriver. В реестре создается минимум необходимых записей, запускаем драйвер, выгружаем драйвер функцией ZwUnloadDriver и удаляем его раздел из реестра (используем функции RegCloseKey, SHDeleteKey).
    Этот способ позволяет запускать драйвер быстро и незаметно и подходит для маленьких программ не требующих установки, но требующих запуска своего драйвера.
    Код (ASM):
    1. ; masm windows gui #
    2. .686P
    3. .model flat
    4. include windows.inc
    5.  
    6. includelib kernel32.lib
    7. includelib advapi32.lib
    8. includelib ntdll.lib
    9. includelib shlwapi.lib
    10.  
    11. extern _imp__ExitProcess@4:dword
    12. extern _imp__GetFullPathNameA@16:dword
    13. extern _imp__RegOpenKeyA@12:dword
    14. extern _imp__RegCreateKeyA@12:dword
    15. extern _imp__RegSetValueExA@24:dword
    16. extern _imp__RegCloseKey@4:dword
    17. extern _imp__SHDeleteKeyA@8:dword
    18. extern _imp__ZwLoadDriver@4:dword
    19. extern _imp__ZwUnloadDriver@4:dword
    20. ;--------macros-------------------
    21. du  macro string
    22.     irpc c,<string>
    23.     if '&c'gt 127
    24.     db ('&c'- 0B0h),4
    25.     else
    26.     dw '&c'
    27.     endif
    28.     endm
    29.     dw 0
    30.     endm
    31. .const
    32. align 4
    33. us: du <\registry\machine\SYSTEM\CurrentControlSet\Services\scp00>
    34. len_us = $-us
    35. align 4
    36. cusDevice dw (len_us - 2)
    37.           dw (len_us)
    38.       dd us
    39. .code
    40.  
    41. start proc
    42. local Key2:HKEY
    43. local Key:HKEY
    44. local acDriverPath[MAX_PATH]:CHAR
    45.  
    46.         xor ebx,ebx
    47.     mov esi,offset scp00_sys_name
    48.     lea edi,acDriverPath
    49.     mov eax,'\??\'
    50.     stosd;путь к драйверу должен начаться с '\??\'
    51.     push eax;резервирую место в стеке
    52.     push esp;указатель на пустое место в стеке
    53.     push edi
    54.     sub edi,4
    55.     push MAX_PATH
    56.     push esi;offset scp1_sys_name
    57.     call _imp__GetFullPathNameA@16
    58. ;GetFullPathName(scp00_sys_name, MAX_PATH, PChar(dword(@Image) + 4), Pth);
    59.     add eax,4
    60.     push eax;длина полного пути для RegSetValueEx(Key2,"ImagePath",0,
    61. ;REG_SZ,&acDriverPath,lenth(acDriverPath))
    62.     lea eax,Key
    63.     push eax
    64.     push offset aSystem
    65.     push HKEY_LOCAL_MACHINE
    66.     call _imp__RegOpenKeyA@12
    67.     mov [esi+5],ebx; из "scp00.sys" делаем "scp00"
    68.     lea eax,Key2
    69.     push eax
    70.     push esi
    71.     push Key
    72.     call _imp__RegCreateKeyA@12
    73.     push edi;&acDriverPath
    74.     push REG_SZ
    75.     push ebx;0
    76.     push offset aImagePath
    77.     push Key2
    78.     call _imp__RegSetValueExA@24;RegSetValueEx(Key2,"ImagePath",0,
    79. ;REG_SZ,&acDriverPath,lenth(acDriverPath))
    80.     mov eax,esp;указатель на пустое место в стеке
    81.         push sizeof(dword)
    82.     mov dword ptr [eax],1;в пустое место в стеке поместим переменную dType=1
    83.     push eax;&dType
    84.     push REG_DWORD;тип переменной dType
    85.     push ebx;0
    86.     push offset aType;название переменной dType
    87.     push Key2
    88.     call _imp__RegSetValueExA@24
    89. ;RegSetValueEx(Key2,"Type",0,REG_DWORD,&dType)
    90.     push Key2
    91.     call _imp__RegCloseKey@4
    92.     mov [esp],offset cusDevice
    93.     call _imp__ZwLoadDriver@4; выравниваем стек после GetFullPathNameA
    94.     push offset cusDevice
    95.     call _imp__ZwUnloadDriver@4
    96.     push esi
    97.     push Key
    98.     call _imp__SHDeleteKeyA@8;удаляем непустую строку из реестра
    99.     push Key
    100.     call _imp__RegCloseKey@4
    101.     push ebx;0
    102.     call _imp__ExitProcess@4
    103. start endp
    104. aSystem db 'SYSTEM\CurrentControlSet\Services',0
    105. scp00_sys_name db "scp00.sys",0
    106. aType   db "Type",0
    107. aImagePath db "ImagePath",0
    108. end start

    Третий вариант user-mode приложения, которое запускает драйвер scp00.sys

    Собственно это модифицированный второй вариант. Произошла замена функций из advapi32.dll и других динамических библиотек на Zw-функции из ntdll.dll, а далее замена Zw-функций на вызов 2Eh прерывания, поэтому за исключением функции RtlGetFullPathName_U наша программа не использует импорт
    DLLисходная функцияфункция из ntdll.dllномер функции в SDT для Windows XPНазначение Zw-функций
    kernel32GetFullPathNameRtlGetFullPathName_U
    advapi32RegOpenKeyZwOpenKey119открывает доступ к существующему подразделу реестра или создает новый. Возвращает дескриптор открытого объекта.
    RegCreateKeyZwCreateKey41открывает доступ к существующему подразделу реестра или создает новый. Возвращает дескриптор открытого объекта.
    RegSetValueExZwSetValueKey247создает или изменяет значение параметра в открытом подразделе реестра. Для возможности применения этой функции, дескриптор подраздела при открытии должен быть получен с применением маски DesiredAccess, содержащей флаг KEY_SET_VALUE
    RegCloseKeyZwClose25закрывает дескриптор открытого ранее подраздела реестра, фиксирует произведенные изменения на жестком диске
    shlwapiSHDeleteKeyZwDeleteKey63удаляет открытый подраздел из реестра
    ntdllZwLoadDriverZwLoadDriver97
    ZwUnloadDriverZwUnloadDriver262
    Таблица #7:​
    вызовом функции ZwOpenKey открываем существующий раздел, запрашивая необходимые в данный момент права доступа
    вызовом функции ZwCreateKey, создаем новый подраздел «scp00» в ветке «HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services»
    После успешного вызова ZwCreateKey по значению переменной dwDisposition можно определить, был ли создан новый подраздел (REG_CREATED_NEW_KEY) или такой подраздел уже существовал в реестре (REG_OPENED_EXISTING_KEY) и поэтому был открыт
    При помощи ZwSetValueKey создадим в нашем подразделе:
    1. unicode-строковый параметр с именем «ImagePath». Строка содержит путь к исполняемому файлу драйвера или службы. В отличие от служб, для драйверов не обязательно указывать значение этого параметра, но тогда файл драйвера должен находиться в каталоге «\%SystemRoot%\System32\Drivers». Полный путь к нашему драйверу, начинающийся с символов «\??\» содержится в edi. Длину полного пути нам вернула функция RtlGetFullPathName_U.
    2. также создаем параметр с именем «Type», определяющим тип службы, типом параметра REG_DWORD и значением SERVICE_KERNEL_DRIVER (=1).
    Тип параметраОписание
    REG_NONEНетипизированный параметр
    REG_SZUnicode-строка фиксированной длины с нулём в конце
    REG_EXPAND_SZUnicode-строка переменной длины с нулём в конце; может включать переменные окружения
    REG_BINARYДвоичные данные произвольной длины
    REG_DWORD32-битное число
    REG_DWORD_LITTLE_ENDIAN32-битное число, в котором первым является младший байт, эквивалентно REG_DWORD
    REG_DWORD_BIG_ENDIAN32-битное число, в котором первым является старший байт
    REG_LINKСимвольная строка в формате Unicode
    REG_MULTI_SZМассив Unicode-строк с завершающим нулём
    REG_RESOURCE_LISTОписание аппаратного ресурса
    REG_FULL_RESOURCE_DESCRIPTORОписание аппаратного ресурса
    REG_RESOURCE_REQUIREMENTS_LISTСписок требований к ресурсам
    Таблица #8: Типы параметров реестра​
    После проигрыша мелодии удаляем подраздел реестра вызовом ZwDeleteKey
    при использовании sysenter вместо Zw-функций размер файла 789 байт, при использовании call ds:[7FFE0300h] ― размер файла 762 байта, при использовании int 2Eh ― размер 699 байт
    Код (ASM):
    1. .686P
    2. .model tiny
    3. include windows.inc
    4. ;for WinXP - 699 bytes
    5. exebase     equ 400000h
    6. OBJ_CASE_INSENSITIVE    equ 40h
    7. SERVICE_KERNEL_DRIVER   equ 01h
    8. du  macro string
    9.     irpc c,<string>
    10.     if '&c'gt 127
    11.     db ('&c'- 0B0h),4
    12.     else
    13.     dw '&c'
    14.     endif
    15.     endm
    16.     dw 0
    17.     endm
    18.  
    19. UNICODE_STRING STRUCT
    20.     _Length     WORD    ?; len of string in bytes (not chars)
    21.     MaximumLength   WORD    ?; len of Buffer in bytes (not chars)
    22.     Buffer      PWSTR   ?; pointer to string
    23. UNICODE_STRING ENDS
    24.  
    25. PUNICODE_STRING typedef PTR UNICODE_STRING
    26.  
    27. OBJECT_ATTRIBUTES STRUCT        ; sizeof = 18h
    28.     _Length             DWORD       ? ; original name Length
    29.     RootDirectory           HANDLE      ?
    30.     ObjectName          PUNICODE_STRING ?
    31.     Attributes          DWORD       ?
    32.     SecurityDescriptor      PVOID       ? ; Points to type SECURITY_DESCRIPTOR
    33.     SecurityQualityOfService    PVOID       ? ; Points to type SECURITY_QUALITY_OF_SERVICE
    34. OBJECT_ATTRIBUTES ENDS
    35.  
    36. .code
    37. main:
    38. include capito.asm
    39. align 4
    40. us: du <\registry\machine\SYSTEM\CurrentControlSet\Services\scp00.sys>
    41. len_us = $-us
    42. align 4
    43. cusDevice dw len_us - 10
    44.           dw len_us - 8
    45.       dd us+exebase
    46. pusSystem dw len_us-58; du <\SYSTEM\CurrentControlSet\Services>
    47.       dw len_us-56
    48.       dd us+exebase+36
    49. pusScp00  dw len_us-114; du <scp00>
    50.       dw len_us-112
    51.       dd us+exebase+104
    52. align 4
    53. aImagePath: du <ImagePath>
    54. a2:
    55. align 4
    56. pusImagePath    dw a2-aImagePath-2
    57.         dw a2-aImagePath
    58.         dd aImagePath+exebase
    59. align 4
    60. aType:  du <Type>
    61. a3:
    62. align 4
    63. pusType     dw a3-aType-2
    64.         dw a3-aType
    65.         dd aType+exebase
    66. start:
    67. Key2        equ dword ptr [ebp-4]
    68. Key     equ dword ptr [ebp-8]
    69. dwDisposition   equ dword ptr [ebp-12]
    70. acDriverPath    equ dword ptr [ebp-MAX_PATH-12]
    71. oa      equ dword ptr [ebp-MAX_PATH-12-sizeof(OBJECT_ATTRIBUTES)]
    72.     enter MAX_PATH+12+sizeof(OBJECT_ATTRIBUTES),0
    73.         xor ebx,ebx
    74.     lea edi,acDriverPath
    75.     mov eax,3F005Ch;unicode '\?'
    76.     stosd
    77.     ror eax,10h
    78.         stosd
    79.     invoke RtlGetFullPathName_U,offset us+exebase+104,MAX_PATH,edi,esp,eax
    80.     sub edi,8;путь к драйверу должен начаться с '\??\'
    81.     add eax,10
    82.     push eax;длина полного пути для ZwSetValueKey
    83. ;Для последующего вызова функции ZwOpenKey нам потребуется указатель на
    84. ;заполненную структуру OBJECT_ATTRIBUTES
    85.         lea ecx,oa
    86.     assume ecx: ptr OBJECT_ATTRIBUTES
    87. ;InitializeObjectAttributes
    88.         mov [ecx]._Length,sizeof(OBJECT_ATTRIBUTES)
    89.     mov [ecx].RootDirectory,ebx
    90.     mov [ecx].ObjectName,offset pusSystem+exebase
    91.     mov [ecx].Attributes,OBJ_CASE_INSENSITIVE
    92.     mov [ecx].SecurityDescriptor,ebx
    93.     mov [ecx].SecurityQualityOfService,ebx
    94.         assume ecx: nothing
    95.         lea eax,Key  ; Адрес Key
    96.     push ecx;POBJECT_ATTRIBUTES  ObjectAttributes
    97.     push KEY_READ;ACCESS_MASK  DesiredAccess
    98.     push eax;PHANDLE  KeyHandle
    99.     mov eax,119
    100.     mov edx,esp
    101.     int 2Eh;ZwOpenKey(&Key,2000000h,&oa)
    102.     lea ecx,oa;ObjectAttributes
    103.     mov (OBJECT_ATTRIBUTES ptr [ecx]).ObjectName,offset pusScp00+exebase
    104.     lea eax,dwDisposition
    105.     push eax;PULONG  Disposition  OPTIONAL
    106.     push ebx;ULONG  CreateOptions=REG_OPTION_NON_VOLATILE
    107.     push ebx;PUNICODE_STRING  Class  OPTIONAL
    108.     push ebx;ULONG  TitleIndex
    109.     push ecx;POBJECT_ATTRIBUTES  ObjectAttributes
    110.     push KEY_READ;ACCESS_MASK  DesiredAccess
    111.     lea eax,Key2
    112.     push eax;PHANDLE  KeyHandle
    113.     mov eax,41
    114.     mov edx,esp
    115.     int 2Eh;ZwCreateKey
    116.     add esp,28+12
    117.     push edi;PVOID  Data
    118.     push REG_SZ;ULONG  Type
    119.     push ebx;ULONG  TitleIndex  OPTIONAL
    120.     push offset pusImagePath+exebase;PUNICODE_STRING  ValueName
    121.     push Key2;HANDLE  KeyHandle
    122.     mov eax,247
    123.     mov edx,esp
    124.     int 2Eh;ZwSetValueKey(Key2,&pusImagePath,0,REG_SZ,edi)
    125.     add esp,24
    126.     mov eax,esp;указатель на пустое место в стеке
    127.     mov dword ptr [eax],SERVICE_KERNEL_DRIVER;в пустое место в стеке поместим переменную dType=1
    128.     push sizeof(dword);ULONG  DataSize
    129.     push eax;PVOID  Data
    130.     push REG_DWORD;ULONG  Type
    131.     push ebx;ULONG  TitleIndex  OPTIONAL
    132.     push offset pusType+exebase;PUNICODE_STRING  ValueName
    133.     push Key2;HANDLE  KeyHandle
    134.     mov eax,247
    135.     mov edx,esp
    136.     int 2Eh;ZwSetValueKey(Key2,&pusType,0,REG_DWORD,eax,sizeof(dword))
    137.     push Key2;Handle
    138.     mov eax,25
    139.     mov edx,esp
    140.     int 2Eh;ZwClose(Key2)
    141.     mov edi,offset cusDevice+exebase
    142.     push edi
    143.         mov eax,97
    144.     mov edx,esp
    145.     int 2Eh;ZwLoadDriver(&cusDevice)
    146.     push edi
    147.         mov eax,262
    148.     mov edx,esp
    149.     int 2Eh;ZwUnloadDriver(&cusDevice)
    150.     push Key2;Handle
    151.     mov eax,63
    152.     mov edx,esp
    153.     int 2Eh;ZwDeleteKey(Key2)
    154.     push Key;Handle
    155.     mov eax,25
    156.     mov edx,esp
    157.     int 2Eh;ZwClose(Key)
    158.     leave
    159.     retn;ExitProcess
    160. import:
    161. dd 0,0,0,ntdll_dll,ntdll_table
    162. dd 0,0
    163. ntdll_table:
    164. RtlGetFullPathName_U    dd _RtlGetFullPathName_U
    165.             dw 0
    166. _RtlGetFullPathName_U   db 0,0,"RtlGetFullPathName_U",0
    167. ntdll_dll       db "ntdll"
    168. end_import:
    169. end main
    Четвертый вариант user-mode приложения, которое запускает драйвер scp00.sys
    Используя функцию ZwSetSystemInformation с параметром SystemLoadAndCalllmage можно за одно действие загрузить и запустить драйвер в системе Windows NT. При этом не требуется никакой регистрации драйвера. Однако возникнет «побочный эффект» ― после такой загрузки драйвер невозможно выгрузить «Обычный» (написанный по всем правилам драйвер) будет находиться в памяти до следующей перезагрузки компьютера. Но наш-то драйвер после выполнения возвратит системе STATUS_DEVICE_CONFIGURATION_ERROR (код фиктивной ошибки) и благополучно будет «самовыпилен» (удален системой из памяти) .
    Другой побочный эффект заключается в том, что мы можем за один сеанс загрузить драйвер несколько раз. Обычно драйвер может загружаться только один раз, но применив наш специальный системный вызов, мы можем загружать и запускать столько копий драйвера, сколько нам нужно.
    8-байтовая локальная переменная GregsImage представляет из себя структуру UNICODE_STRING используемую для работы с UNICODE-строками в нулевом кольце
    Код (ASM):
    1. UNICODE_STRING STRUCT
    2.     _Length        WORD    ?; len of string in bytes (not chars)
    3.     MaximumLength    WORD    ?; len of Buffer in bytes (not chars)
    4.     Buffer        PWSTR    ?; pointer to string
    5. UNICODE_STRING ENDS
    Поле _Length содержит текущую длину строки в байтах, не считая двух завершающих нулей. Поле MaximumLength содержит максимальный размер буфера в байтах, в котором эта строка присутствует (MaximumLength = _Length + 2). Поле Buffer содержит указатель на буфер, где находится UNICODE-строка.
    Код (ASM):
    1.     call _imp__RtlGetFullPathName_U@16
    2.     imul eax,10001h
    3.     add eax,2
    4.         mov edi,esp;edi=&GregsImage
    5.     mov [edi],eax
    Для вычисления длины строки используем функцию RtlGetFullPathName умножив возвращенное значение на 10001h и добавив 2 мы получили значения для GregsImage._Length и GregsImage.MaximumLength
    Код (ASM):
    1.         lea edi,daPath
    2.     mov GregsImage.Buffer,edi
    Помещаем в поле GregsImage.Buffer указатель на UNICODE-строку.
    Код (ASM):
    1. ; masm windows gui #
    2. .686P
    3. .model flat
    4. include windows.inc
    5. includelib kernel32.lib
    6. includelib ntdll.lib
    7. extern _imp__ExitProcess@4:dword
    8. extern _imp__RtlGetFullPathName_U@16:dword
    9. extern _imp__ZwSetSystemInformation@12:dword
    10. ;----------const------------------
    11. SystemLoadAndCallImage equ 26h
    12. ;--------macros-------------------
    13. du  macro string
    14.     irpc c,<string>
    15.     if '&c'gt 127
    16.     db ('&c'- 0B0h),4
    17.     else
    18.     dw '&c'
    19.     endif
    20.     endm
    21.     dw 0
    22.     endm
    23. ;--------struct-------------------
    24. UNICODE_STRING STRUCT
    25.     _Length     WORD    ?; len of string in bytes (not chars)
    26.     MaximumLength   WORD    ?; len of Buffer in bytes (not chars)
    27.     Buffer      PWSTR   ?; pointer to string
    28. UNICODE_STRING ENDS
    29. ;---------------------------------
    30. .code
    31. start proc
    32. local daPath[MAX_PATH]:CHAR
    33. local GregsImage:UNICODE_STRING
    34.  
    35.         lea edi,daPath
    36.     mov GregsImage.Buffer,edi
    37.     mov eax,3F005Ch;eax='\',0,'?',0
    38.     stosd;путь к драйверу должен начаться с '\??\'
    39.     ror eax,16;eax='?',0,'\',0
    40.     stosd
    41.     push eax;резервирую место в стеке
    42.     push esp;указатель на пустое место в стеке
    43.     push edi
    44.     push MAX_PATH
    45.     push offset scp00_sys_name
    46.     call _imp__RtlGetFullPathName_U@16
    47. ;GetFullPathName(scp00_sys_name, MAX_PATH, PChar(dword(@Image) + 4), Pth);
    48.     pop ecx
    49.     add eax,8;учтем, что путь стал длиннее на '\??\'
    50.     imul eax,10001h
    51.     add eax,2
    52.     mov edi,esp;edi=&GregsImage
    53.     mov [edi],eax
    54.     push sizeof UNICODE_STRING;8
    55.     push edi;&GregsImage
    56.     push SystemLoadAndCallImage;=26h
    57.     call _imp__ZwSetSystemInformation@12
    58.     push 0
    59.     call _imp__ExitProcess@4
    60. start endp
    61. scp00_sys_name: du <scp00.sys>
    62. end start
    Для написания данной главы использовались работы следующих авторов:
    Программирование и устройство таймера:
    • Лю Ю-Чжен, Гибсон Г. Микропроцессоры семейства 8086/8088. Архитектура, программирование и проектирование микрокомпьютерных систем: Пер. с англ. ― М.: Радио и связь, 1987. ― 512 с.; ил.
      Глава 9. Интерфейсы ввода-вывода. Программируемый интервальный таймер.
    • Юров В.И. Assembler. Учебник для вузов 2-е изд. ― СПб.: Питер, 2007. ― 637 с.: ил.
      Глава 7. Команды обмена данными. Ввод из порта и вывод в порт.
    Загрузка драйвера с помощью функций Service Control Manager'a и загрузка с помощью функции ZwLoadDriver:
    • KmdTutRu.chm "Драйверы режима ядра" by Four-F главы:
      1. Установка канала связи с SCM
      2. Регистрация драйвера
      3. Запуск драйвера
    Загрузка драйвера через ZwSetSystemInformation
    • Драйвер m1gB0t by Greg Hoglund, 2004, исходный текст m1gloader
    • Грег Хогланд, Гари Мак-Гроу. Взлом программного обеспечения: анализ и использование кода.: Пер. с англ. ― М.: Издательский дом "Вильямс", 2005. ― 400 с.: ил. ― Парал. тит. англ.
      Глава 8. Наборы средств для взлома. Текст программы для загрузки драйвера c:\_root_.sys
     
    Последнее редактирование: 20 мар 2022
    M0rg0t нравится это.
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709

    Доступ к командам In/Out через драйвер режима ядра


    команды читающие или пишущие в порт ввода/вывода могут быть расположены в «теле» user-mode приложения, но чтобы они выполнились, необходимо оформить этот фрагмент как процедуру и на исполнение ее запустить из kernel-mode драйвера. При этом в этой процедуре не должны встречаться WinAPI функции, используемые в user-mode, иначе BSOD обеспечен. Адрес процедуры в драйвер можно передать:
    • через реестр,
    • через WriteFile "\\.\driver"
    • или через DeviceIoControl.
    Переделываю драйвер <R>ing-<0> <P>rocedure <C>all & Direct Port I/O (r0pc.sys) by @L.chemist (Andrey A. Meshkov). Драйвер scp01 передает управление на фрагмент кода из scp01-00.exe в котором содержится команда outsb, синхронизация производится через предварительный подсчет тиков. Адрес процедуры musik передается через DeviceIoControl. Полнофункциональный драйвер.

    Текст драйвера scp01.sys

    Код (ASM):
    1. ; masm windows native #
    2. ;написано на основе драйвера режима ядра <R>ing-<0> <P>rocedure
    3. ;<C>all & Direct Port I/O (r0pc.sys) by @L.chemist (Andrey A. Meshkov)
    4. .686p
    5. .model flat
    6. include ntstatus.inc
    7. include ntddk.inc
    8. include Strings.mac
    9. includelib ntoskrnl.lib
    10. ; --------------------------------------------------------------------------------
    11. extern _imp__IoCreateDevice:dword
    12. extern _imp__IoCreateSymbolicLink:dword
    13. extern _imp__IoDeleteDevice:dword
    14. extern _imp__IoDeleteSymbolicLink:dword
    15. extern _imp__IofCompleteRequest:dword
    16.  
    17. DRIVER_QUERY_PROC_NOARGS    = 10h
    18. ; --------------------------------------------------------------------------------
    19. ; structure for driver query
    20. DriverQuery struct
    21.   iocode dd ? ; user I/O code
    22.   wparam dd ? ; parameter
    23.   lparam dd ? ; parameter
    24. DriverQuery ends
    25. PDriverQuery equ dd
    26. ; --------------------------------------------------------------------------------
    27. .const
    28. CCOUNTED_UNICODE_STRING "\\Device\\scp01", cusDevice, 4
    29. CCOUNTED_UNICODE_STRING "\\DosDevices\\scp01", cusSymbolicLink, 4
    30.  
    31. .code
    32. ; Процедура входа драйвера. Эта процедура вызывается только раз после загрузки
    33. ; драйвера в память. Она выделяет необходимые ресурсы для работы драйвера. В
    34. ; нашем случае
    35. DriverEntry proc lpDriverObject:PDRIVER_OBJECT, lpusRegistryPath:PUNICODE_STRING
    36. local deviceObject:PDEVICE_OBJECT
    37. local status:NTSTATUS
    38. ; инициализируем драйвер устройства и объект устройства (Device Object)
    39.     lea eax,deviceObject
    40.     push eax;&deviceObject
    41.     push 0
    42.     push 0
    43.     push FILE_DEVICE_UNKNOWN;22h
    44.     push offset cusDevice
    45.     push sizeof(DriverQuery);0Ch
    46.     push lpDriverObject
    47.     call _imp__IoCreateDevice
    48.     test eax,eax;cmp eax,STATUS_SUCCESS; 0
    49.     jnz short exit; break on error
    50. ; create symbolic link to the user-visible name
    51. ; создаем символическую ссылку на драйвер устройства, что позволяет user-mode
    52. ; приложению получить доступ к нашему драйверу, используя "\\.\scp01" нотацию
    53.     push offset cusDevice
    54.     push offset cusSymbolicLink
    55.     call _imp__IoCreateSymbolicLink
    56.         mov status,eax; save status          
    57.     test eax,eax;cmp eax,STATUS_SUCCESS; check result
    58.     jz short success          
    59.     push deviceObject
    60.     call _imp__IoDeleteDevice; delete device object if not successful
    61.     jmp short exit
    62. success:    ; continue on success
    63. ; load structure to point to IRP handlers
    64. ; инициализируем точки входа драйвера в объект драйвера
    65. ; всё, что нам нужно, это операция создания (Create), управления (Control)
    66. ; и выгрузки (Unload)
    67.     mov eax,lpDriverObject
    68.     mov (DRIVER_OBJECT ptr [eax]).DriverUnload,offset DriverUnload
    69.     mov (DRIVER_OBJECT ptr [eax]).MajorFunction[IRP_MJ_CREATE*4],offset DispatchCreate
    70.     mov (DRIVER_OBJECT ptr [eax]).MajorFunction[IRP_MJ_DEVICE_CONTROL*4],offset DispatchControl          
    71.     mov eax,status; assign result
    72. exit:   leave
    73.     retn 8
    74. DriverEntry endp
    75. ;------------------------------------------------------
    76. ;освобождаем все выделенные ранее объекты
    77. DriverUnload:
    78. lpDriverObject  equ dword ptr [esp+4];:PDRIVER_OBJECT
    79. ; delete symbolic link to the user-visible name
    80.     push offset cusSymbolicLink
    81.     call _imp__IoDeleteSymbolicLink
    82. ; delete device object
    83.     mov ecx,lpDriverObject
    84.     push dword ptr (DRIVER_OBJECT ptr [ecx]).DeviceObject
    85.     call _imp__IoDeleteDevice
    86.     retn 4
    87. ;--------------------------------------------------
    88. DispatchCreate:
    89. lIrp equ dword ptr [esp+8]
    90.     mov ecx,lIrp
    91.     and (_IRP ptr [ecx]).IoStatus.Status,STATUS_SUCCESS;0
    92.     and (_IRP ptr [ecx]).IoStatus.Information,0
    93.     xor edx,edx;edx = IO_NO_INCREMENT
    94.     call _imp__IofCompleteRequest;IofCompleteRequest (lpIrp, IO_NO_INCREMENT)
    95.     xor eax,eax;mov eax,STATUS_SUCCESS; 0
    96.     retn 8
    97. ;---------------------------------------------------------
    98. DispatchControl proc pDeviceObject:PDRIVER_OBJECT, lpIrp:PIRP
    99. local status:NTSTATUS
    100.  
    101.     push edi
    102.     mov status,STATUS_UNSUCCESSFUL; 0C0000001h
    103.     mov edi,lpIrp
    104.     assume edi: ptr _IRP
    105.     and [edi].IoStatus.Information,0;
    106.     mov eax,[edi].Tail.Overlay.CurrentStackLocation
    107.     cmp (IO_STACK_LOCATION ptr [eax]).Parameters.DeviceIoControl.InputBufferLength,4
    108.     jne @f
    109.         mov status,STATUS_NOT_IMPLEMENTED; 0C0000002h
    110.     cmp (IO_STACK_LOCATION ptr [eax]).Parameters.DeviceIoControl.IoControlCode,DRIVER_QUERY_PROC_NOARGS
    111.     jne @f
    112.     mov eax,[edi].AssociatedIrp.SystemBuffer;ioBuffer = pIrp->AssociatedIrp.SystemBuffer
    113.     call dword ptr [eax]
    114.     and status,STATUS_SUCCESS;status = 0
    115. @@: push status
    116.     pop [edi].IoStatus.Status
    117.     assume edi:nothing
    118.     mov ecx,edi
    119.     xor edx,edx;edx = IO_NO_INCREMENT
    120.     call _imp__IofCompleteRequest;IofCompleteRequest (lpIrp, IO_NO_INCREMENT)
    121.     mov eax,status
    122.     pop edi
    123.     leave
    124.     retn 8
    125. DispatchControl endp
    126. ;----------------------------------------------------
    127. end DriverEntry

    Первый вариант user-mode приложения, которое запускает драйвер scp01.sys


    user-mode приложение, которое запускает scp01.sys драйвер, используя API-функции Service Control Manager'a: OpenSCManager, CreateService, StartService, CloseServiceHandle и DeleteService.
    Этот способ очень прост и хорошо документирован, он подходит для постоянной установки драйверов.
    Код (ASM):
    1. ; masm windows gui #
    2. .686P
    3. .model flat
    4. include windows.inc
    5.  
    6. includelib kernel32.lib
    7. includelib user32.lib
    8. includelib advapi32.lib
    9.  
    10. extern _imp__ExitProcess@4:dword
    11. extern _imp__GetFullPathNameA@16:dword
    12. extern _imp__MessageBoxA@16:dword
    13. extern _imp__CloseServiceHandle@4:dword
    14. extern _imp__DeleteService@4:dword
    15. extern _imp__StartServiceA@12:dword
    16. extern _imp__CreateServiceA@52:dword
    17. extern _imp__OpenSCManagerA@12:dword
    18. extern _imp__Sleep@4:dword
    19. extern _imp__OpenServiceA@12:dword
    20. extern _imp__QueryServiceStatus@8:dword
    21. extern _imp__CreateFileA@28:dword
    22. extern _imp__DeviceIoControl@32:dword
    23. extern _imp__CloseHandle@4:dword
    24. extern _imp__ControlService@12:dword
    25.  
    26. SERVICE_ERROR_NORMAL       equ 1
    27. MAX_PATH            equ 260
    28. DRIVER_QUERY_PROC_NOARGS    equ 10h
    29.  
    30. .code
    31.  
    32. start proc
    33. local DrvFile:dword
    34. local Driver:HANDLE
    35. local Service:HANDLE
    36. local Manager:HANDLE
    37. local acDriverPath[MAX_PATH]:CHAR
    38.         xor esi,esi
    39.     xor ebx,ebx
    40.     rdtsc; замеряем скольким тикам соответствует 40 мСек
    41.         mov TimerLo,eax
    42.         mov TimerHi,edx
    43.     push 40
    44.     call _imp__Sleep@4
    45.     rdtsc
    46.         sub eax,TimerLo
    47.         sbb edx,TimerHi
    48.         mov TimerLo,eax
    49.         mov TimerHi,edx
    50.     mov edi,offset DrvFilename+4;&edi='scp01.sys',0
    51. ;-----инициализируем драйвер
    52.     push eax
    53.     push esp
    54.         lea eax,acDriverPath
    55.     push eax
    56.     push MAX_PATH
    57.     push edi
    58.     call _imp__GetFullPathNameA@16
    59.     pop eax
    60.     ; Open a handle to the SC Manager database
    61.     push SC_MANAGER_ALL_ACCESS;CREATE_SERVICE
    62.     push ebx;NULL
    63.     push ebx;NULL
    64.     call _imp__OpenSCManagerA@12
    65. ;control manager on the specified computer and opens the specified database
    66.     mov Manager,eax
    67.         mov [edi+5],ebx;&edi=Drvname='scp01',0
    68.     test eax,eax
    69.     jz err;if eax != NULL
    70.     inc esi
    71.         push SERVICE_ALL_ACCESS
    72.     push edi
    73.     push eax
    74.     call _imp__OpenServiceA@12
    75.     test eax,eax
    76.     jne short a0
    77.     ; Register driver in SCM active database
    78.     push ebx;NULL
    79.     push ebx;NULL
    80.     push ebx;NULL
    81.     push ebx;NULL
    82.     push ebx;NULL
    83.         lea ecx,acDriverPath
    84.     push ecx;DrvPath
    85.     push SERVICE_ERROR_NORMAL;IGNORE
    86.     push SERVICE_DEMAND_START
    87.     push SERVICE_KERNEL_DRIVER
    88.     push SERVICE_ALL_ACCESS;START + DELETE
    89.     push edi
    90.     push edi
    91.     push Manager
    92.     call _imp__CreateServiceA@52
    93.     test eax,eax
    94.     jz a3;if eax != NULL
    95. a0: mov Service,eax
    96.     push offset Status
    97.     push eax
    98.     call _imp__QueryServiceStatus@8
    99.     xchg eax,ecx
    100.     jecxz wmDESTROY
    101.     cmp Status.SERVICE_STATUS.dwCurrentState,SERVICE_RUNNING
    102.     je short a10
    103.     push ebx;offset Vectors
    104.     push ebx;0
    105.     push Service
    106.     call _imp__StartServiceA@12
    107. a10:    sub edi,4;&edi='\\.\scp01',0
    108.     push ebx
    109.     push ebx
    110.     push OPEN_EXISTING
    111.     push ebx
    112.     push FILE_SHARE_READ; or FILE_SHARE_WRITE
    113.     push GENERIC_READ; or GENERIC_WRITE
    114.     push edi
    115.     call _imp__CreateFileA@28
    116.     mov Driver,eax
    117.     inc eax;cmp eax,INVALID_HANDLE_VALUE
    118.     je short a4
    119.     dec eax
    120.     ; Here driver beeper.sys plays melody
    121.         mov edi,offset Query
    122.     mov [edi],offset musik
    123.     push ebx;адрес OVERLAPPED-структуры
    124.     push offset Bytes;адрес переменой, куда будет занесено количество байт,
    125. ;помещенных в буфер драйвером
    126.         push ebx; длина буфера
    127.     push ebx; буфер, куда драйвер поместит свои данные
    128.     push 4;длина данных
    129.     push edi;адрес данных для драйвера
    130.     push DRIVER_QUERY_PROC_NOARGS;номер необходимой операции
    131.     push eax;дескриптор драйвера, полученный через функцию CreateFile
    132.     call _imp__DeviceIoControl@32
    133.     ; and reports error to be removed from memory
    134.     ; Remove driver from SCM database
    135. wmDESTROY: push Driver
    136.     call _imp__CloseHandle@4
    137. a4: mov eax,Service
    138.         push eax;Service
    139.         push eax;Service
    140.     push offset Status
    141.     push SERVICE_CONTROL_STOP
    142.     push eax;Service
    143.     call _imp__ControlService@12; Send a control code to a Win32 service
    144.     call _imp__DeleteService@4
    145.     call _imp__CloseServiceHandle@4
    146. a3: push Manager
    147.     call _imp__CloseServiceHandle@4
    148.     jmp short a11
    149. err:    push MB_ICONSTOP    
    150.     push ebx;NULL
    151.     push handle[esi*4];offset can_t_connect
    152.     push ebx;NULL
    153.     call _imp__MessageBoxA@16
    154. a11:    push ebx;0
    155.     call _imp__ExitProcess@4
    156. start endp
    157. ;--------------------------------------------------------------
    158. musik:  ;set bits 0,1 of PC speaker control register:
    159.         in al,61h;текущее состояние порта 61h в AL
    160.     or al,00000011b;установить биты 0 и 1 в 1
    161.     out 61h,al ;теперь динамик включен
    162.     mov al,0B6h
    163.     out 43h,al  ;Timer  8253-5 (AT: 8254.2).
    164.     mov esi,offset melody; данные
    165.     mov ecx,size_melody  ; счетчик
    166.     mov dx,42h ;Timer 8253-5 (AT: 8254.2).
    167.     push edi
    168. a15:    push edx   ;PC/XT PPI port B bits:
    169.     push ecx   ;0: Timer 2 gate OR  03h= speaker ON
    170.     rdtsc      ;1: Timer 2 data AND 0FCh= speaker OFF
    171.     mov ecx,eax;2:
    172.     mov edi,edx;3: 1=read high switches
    173.     add ecx,TimerLo;4: 0=enable RAM parity checking
    174.     adc edi,TimerHi;5: 0=enable I/O channel check      
    175. @@: rdtsc; крутимся до тех пор пока не получится
    176.     sub eax,ecx; задержка в 55 мСек
    177.     sbb edx,edi
    178.     jnz @b;6: 0=hold keyboard clock low
    179.     pop ecx   ;7: 0=enable keyboard
    180.     pop edx   ;Команда выводит данные в порт ввода-вывода,
    181.     outsb     ;номер которого загружен в регистр DX,
    182.     loop a15  ;из ячейки памяти по адресу DS:ESI
    183.     pop edi
    184.     in al,61h
    185.     and al,11111100b;4Dh
    186.     out 61h,al    
    187.     retn  
    188. ;----------------------------------------------------------------
    189. .data
    190. melody  dw 2 dup(354h),2,2,2 dup(387h),2,2,2 dup(3BDh),2 dup(387h),2 dup(3BDh)
    191. dw 2 dup(3F5h),2 dup(432h),2,2,2 dup(472h),2,2,4 dup(4B5h),4 dup(472h),2 dup(3F5h)
    192. dw 2,2,2 dup(432h),2,2,2 dup(472h),2 dup(432h),2 dup(472h),2 dup(4B5h),2 dup(4FDh)
    193. dw 2,2,2 dup(549h),2,2,4 dup(599h),4 dup(549h),2 dup(472h),2,2,2 dup(5EEh,2)
    194. dw 4 dup(649h),4 dup(5EEh),2 dup(472h),2,2,2 dup(5EEh,2),4 dup(649h),4 dup(5EEh)
    195. dw 2 dup(70Eh),2 dup(6A8h),2 dup(649h),2 dup(5EEh),2 dup(599h),2 dup(549h)
    196. dw 2 dup(4FDh),2 dup(4B5h),2 dup(472h),2,2,2 dup(432h),2,2,2 dup(3F5h),2,2
    197. dw 2 dup(387h),2,2,8 dup(354h),2
    198. size_melody = $ - melody
    199. ;----------------------------------------------------------------
    200. TimerLo dd 0
    201. TimerHi dd 0
    202. DrvFilename db '\\.\scp01.sys',0
    203. Query dd ?
    204. Status  db sizeof(SERVICE_STATUS) dup (?)
    205. Bytes   dd ?
    206. handle dd can_t_connect,can_t_register
    207. can_t_connect db "Can't connect to Service Control Manager.",0
    208. can_t_register db "Can't register driver.",0
    209. end start
    210.  

    Второй вариант user-mode приложения, которое запускает драйвер scp01.sys

    Предварительно прописываем драйвер scp01.sys в реестре «вручную» (используем функции RegOpenKey, RegCreateKey, RegSetValue) и загружаем драйвер scp00.sys с помощью функции ZwLoadDriver. В реестре создается минимум необходимых записей, запускаем драйвер, выгружаем драйвер функцией ZwUnloadDriver и удаляем его раздел из реестра (используем функции RegCloseKey, SHDeleteKey).
    Этот способ позволяет запускать драйвер быстро и незаметно и подходит для маленьких программ не требующих установки, но требующих запуска своего драйвера.
    Код (ASM):
    1. ; masm windows gui #
    2. .686P
    3. .model flat
    4. include windows.inc
    5.  
    6. includelib kernel32.lib
    7. includelib advapi32.lib
    8. includelib ntdll.lib
    9. includelib shlwapi.lib
    10.  
    11. extern _imp__ExitProcess@4:dword
    12. extern _imp__GetFullPathNameA@16:dword
    13. extern _imp__Sleep@4:dword
    14. extern _imp__CreateFileA@28:dword
    15. extern _imp__DeviceIoControl@32:dword
    16. extern _imp__CloseHandle@4:dword
    17. extern _imp__RegOpenKeyA@12:dword
    18. extern _imp__RegCreateKeyA@12:dword
    19. extern _imp__RegSetValueExA@24:dword
    20. extern _imp__RegCloseKey@4:dword
    21. extern _imp__SHDeleteKeyA@8:dword
    22. extern _imp__ZwLoadDriver@4:dword
    23. extern _imp__ZwUnloadDriver@4:dword
    24.  
    25. SERVICE_ERROR_NORMAL       equ 1
    26. MAX_PATH            equ 260
    27. DRIVER_QUERY_PROC_NOARGS    equ 10h
    28. ;--------macros-------------------
    29. du  macro string
    30.     irpc c,<string>
    31.     if '&c'gt 127
    32.     db ('&c'- 0B0h),4
    33.     else
    34.     dw '&c'
    35.     endif
    36.     endm
    37.     dw 0
    38.     endm
    39. .const
    40. align 4
    41. us: du <\registry\machine\SYSTEM\CurrentControlSet\Services\scp01>
    42. len_us = $-us
    43. align 4
    44. cusDevice dw (len_us - 2)
    45.           dw (len_us)
    46.       dd us
    47.  
    48. .code
    49.  
    50. start proc
    51. local Key2:HKEY
    52. local Key:HKEY
    53. local acDriverPath[MAX_PATH]:CHAR
    54.  
    55.     xor ebx,ebx
    56.     rdtsc; замеряем скольким тикам соответствует 40 мСек
    57.         mov TimerLo,eax
    58.         mov TimerHi,edx
    59.     push 40
    60.     call _imp__Sleep@4
    61.     rdtsc
    62.         sub eax,TimerLo
    63.         sbb edx,TimerHi
    64.         mov TimerLo,eax
    65.         mov TimerHi,edx
    66.     mov esi,offset scp01_sys_name+4;&esi='scp01.sys',0
    67.         lea edi,acDriverPath
    68.     mov eax,'\??\'
    69.     stosd;путь к драйверу должен начаться с '\??\'
    70. ;-----инициализируем драйвер
    71.     push eax;резервирую место в стеке
    72.     push esp;указатель на пустое место в стеке
    73.     push edi;&acDriverPath+4
    74.     sub edi,4
    75.     push MAX_PATH
    76.     push esi
    77.     call _imp__GetFullPathNameA@16
    78.     add eax,4
    79.     push eax
    80.     lea eax,Key
    81.     push eax
    82.     push offset aSystem
    83.     push HKEY_LOCAL_MACHINE
    84.     call _imp__RegOpenKeyA@12
    85.         mov [esi+5],ebx;&esi=Drvname='scp01',0
    86.         lea eax,Key2
    87.         push eax
    88.     push esi
    89.     push Key
    90.     call _imp__RegCreateKeyA@12
    91.     push edi;&acDriverPath
    92.     push REG_SZ
    93.     push ebx;NULL
    94.     push offset aImagePath
    95.     push Key2
    96.     call _imp__RegSetValueExA@24
    97.     mov eax,esp;указатель на пустое место в стеке
    98.         push sizeof(dword)
    99.     mov dword ptr [eax],1;в пустое место в стеке поместим переменную dType=1
    100.     push eax;&dType
    101.     push REG_DWORD;тип переменной dType
    102.     push ebx;0
    103.     push offset aType;название переменной dType
    104.     push Key2
    105.     call _imp__RegSetValueExA@24
    106.     push Key2
    107.     call _imp__RegCloseKey@4
    108.     mov [esp],offset cusDevice
    109.     call _imp__ZwLoadDriver@4; выравниваем стек после GetFullPathNameA
    110.     sub esi,4;&esi='\\.\scp01',0
    111.     push ebx
    112.     push ebx
    113.     push OPEN_EXISTING
    114.     push ebx
    115.     push FILE_SHARE_READ; or FILE_SHARE_WRITE
    116.     push GENERIC_READ; or GENERIC_WRITE
    117.     push esi
    118.     call _imp__CreateFileA@28
    119.     inc eax;cmp eax,INVALID_HANDLE_VALUE
    120.     je short @f
    121.     dec eax
    122.     push eax;дескриптор драйвера для CloseHandle
    123.         mov ecx,offset Query
    124.     mov [ecx],offset musik
    125.     push ebx;адрес OVERLAPPED-структуры
    126.     push offset Bytes;адрес переменой, куда будет занесено количество байт, помещенных в буфер драйвером
    127.         push ebx; длина буфера
    128.     push ebx; буфер, куда драйвер поместит свои данные
    129.     push 4;длина данных
    130.     push ecx;адрес данных для драйвера
    131.     push DRIVER_QUERY_PROC_NOARGS;номер необходимой операции
    132.     push eax;дескриптор драйвера, полученный через функцию CreateFile
    133.     call _imp__DeviceIoControl@32
    134.     call _imp__CloseHandle@4
    135. @@: push offset cusDevice
    136.     call _imp__ZwUnloadDriver@4
    137.     lodsd;add esi,4
    138.         push esi;&esi='scp01',0
    139.     push Key
    140.     call _imp__SHDeleteKeyA@8
    141.     push Key
    142.     call _imp__RegCloseKey@4
    143.     push ebx;0
    144.     call _imp__ExitProcess@4
    145. start endp
    146. ;--------------------------------------------------------------
    147. musik:  ;set bits 0,1 of PC speaker control register:
    148.         in al,61h;текущее состояние порта 61h в AL
    149.     or al,00000011b;установить 0-ой и 1-ый биты
    150.     out 61h,al ;теперь динамик включен
    151.     mov al,0B6h
    152.     out 43h,al  ;Timer  8253-5 (AT: 8254.2).
    153.     mov esi,offset melody; данные
    154.     mov ecx,size_melody  ; счетчик
    155.     mov dx,42h ;Timer 8253-5 (AT: 8254.2).
    156.     push edi
    157. a15:    push edx   ;PC/XT PPI port B bits:
    158.     push ecx   ;0: Timer 2 gate OR  03h= speaker ON
    159.     rdtsc      ;1: Timer 2 data AND 0FCh= speaker OFF
    160.     mov ecx,eax;2:
    161.     mov edi,edx;3: 1=read high switches
    162.     add ecx,TimerLo;4: 0=enable RAM parity checking
    163.     adc edi,TimerHi;5: 0=enable I/O channel check        
    164. @@: rdtsc; крутимся до тех пор пока не получится
    165.     sub eax,ecx; задержка в 55 мСек
    166.     sbb edx,edi
    167.     jnz @b;6: 0=hold keyboard clock low  
    168.     pop ecx   ;7: 0=enable keyboard
    169.     pop edx   ;Команда выводит данные в порт ввода-вывода,
    170.     outsb     ;номер которого загружен в регистр DX,
    171.     loop a15  ;из ячейки памяти по адресу DS:ESI
    172.     pop edi
    173.     in al,61h
    174.     and al,11111100b;сбросить 0-ой и 1-ый биты
    175.     out 61h,al  ;теперь динамик выключен
    176.     retn    
    177. ;----------------------------------------------------------------
    178. .data
    179. melody  dw 2 dup(354h),2,2,2 dup(387h),2,2,2 dup(3BDh),2 dup(387h),2 dup(3BDh)
    180. dw 2 dup(3F5h),2 dup(432h),2,2,2 dup(472h),2,2,4 dup(4B5h),4 dup(472h),2 dup(3F5h)
    181. dw 2,2,2 dup(432h),2,2,2 dup(472h),2 dup(432h),2 dup(472h),2 dup(4B5h),2 dup(4FDh)
    182. dw 2,2,2 dup(549h),2,2,4 dup(599h),4 dup(549h),2 dup(472h),2,2,2 dup(5EEh,2)
    183. dw 4 dup(649h),4 dup(5EEh),2 dup(472h),2,2,2 dup(5EEh,2),4 dup(649h),4 dup(5EEh)
    184. dw 2 dup(70Eh),2 dup(6A8h),2 dup(649h),2 dup(5EEh),2 dup(599h),2 dup(549h)
    185. dw 2 dup(4FDh),2 dup(4B5h),2 dup(472h),2,2,2 dup(432h),2,2,2 dup(3F5h),2,2
    186. dw 2 dup(387h),2,2,8 dup(354h),2
    187. size_melody = $ - melody
    188. ;----------------------------------------------------------------
    189. TimerLo dd 0
    190. TimerHi dd 0
    191. scp01_sys_name db '\\.\scp01.sys',0
    192. Query dd ?
    193. Bytes   dd ?
    194. aSystem db 'SYSTEM\CurrentControlSet\Services',0
    195. aType   db "Type",0
    196. aImagePath db "ImagePath",0
    197. end start

    Третий вариант user-mode приложения, которое запускает драйвер scp01.sys

    Устанавливаем драйвер при помощи функции ZwSetSystemInformation
    Код (ASM):
    1. ; masm windows gui #
    2. .686P
    3. .model flat
    4. include windows.inc
    5.  
    6. includelib kernel32.lib
    7. includelib advapi32.lib
    8. includelib ntdll.lib
    9. includelib shlwapi.lib
    10.  
    11. extern _imp__ExitProcess@4:dword
    12. extern _imp__GetFullPathNameA@16:dword
    13. extern _imp__Sleep@4:dword
    14. extern _imp__CreateFileA@28:dword
    15. extern _imp__DeviceIoControl@32:dword
    16. extern _imp__CloseHandle@4:dword
    17. extern _imp__ZwSetSystemInformation@12:dword
    18.  
    19. SERVICE_ERROR_NORMAL       equ 1
    20. MAX_PATH            equ 260
    21. DRIVER_QUERY_PROC_NOARGS    equ 10h
    22. SystemLoadAndCallImage     equ 26h
    23.  
    24. .code
    25. start proc
    26. local GregsImage:qword
    27. local daPath[MAX_PATH]:CHAR
    28. local acDriverPath[MAX_PATH]:CHAR
    29.  
    30.     xor ebx,ebx
    31.     rdtsc; замеряем скольким тикам соответствует 40 мСек
    32.         mov TimerLo,eax
    33.         mov TimerHi,edx
    34.     push 40
    35.     call _imp__Sleep@4
    36.     rdtsc
    37.         sub eax,TimerLo
    38.         sbb edx,TimerHi
    39.         mov TimerLo,eax
    40.         mov TimerHi,edx
    41.     mov esi,offset scp01_sys_name+4;&esi='scp01.sys',0
    42.         lea edi,acDriverPath
    43.     mov eax,'\??\'
    44.     stosd;путь к драйверу должен начаться с '\??\'
    45. ;-----инициализируем драйвер
    46.     push eax;резервирую место в стеке
    47.     push esp;указатель на пустое место в стеке
    48.     push edi;&acDriverPath+4
    49.     push MAX_PATH
    50.     push esi
    51.     call _imp__GetFullPathNameA@16
    52.     pop ecx
    53.     add eax,4
    54.         mov ecx,eax
    55.     imul eax,20002h
    56.     add eax,2
    57.     mov dword ptr GregsImage,eax
    58.     lea edi,daPath
    59.     mov dword ptr GregsImage+4,edi
    60.     xor eax,eax
    61.     lea esi,acDriverPath
    62. @@: lodsb
    63.     stosw
    64.     loop @b
    65.     push sizeof GregsImage;8
    66.     lea edi,GregsImage
    67.     push edi;&GregsImage
    68.     push SystemLoadAndCallImage;=26h
    69.     call _imp__ZwSetSystemInformation@12
    70.         mov esi,offset scp01_sys_name
    71.         mov [esi+5],ebx;&esi=Drvname='scp01',0
    72.     push ebx
    73.     push ebx
    74.     push OPEN_EXISTING
    75.     push ebx
    76.     push FILE_SHARE_READ; or FILE_SHARE_WRITE
    77.     push GENERIC_READ; or GENERIC_WRITE
    78.     push esi
    79.     call _imp__CreateFileA@28  
    80.     inc eax;cmp eax,INVALID_HANDLE_VALUE
    81.     je short @f
    82.     dec eax
    83.     push eax;дескриптор драйвера для CloseHandle
    84.         mov ecx,offset Query
    85.     mov [ecx],offset musik
    86.     push ebx;адрес OVERLAPPED-структуры
    87.     push offset Bytes;адрес переменой, куда будет занесено количество байт, помещенных в буфер драйвером
    88.         push ebx; длина буфера
    89.     push ebx; буфер, куда драйвер поместит свои данные
    90.     push 4;длина данных
    91.     push ecx;адрес данных для драйвера
    92.     push DRIVER_QUERY_PROC_NOARGS;номер необходимой операции
    93.     push eax;дескриптор драйвера, полученный через функцию CreateFile
    94.     call _imp__DeviceIoControl@32
    95.     call _imp__CloseHandle@4
    96. @@: push ebx;0
    97.     call _imp__ExitProcess@4
    98. start endp
    99. ;--------------------------------------------------------------
    100. musik:  ;set bits 0,1 of PC speaker control register:
    101.         in al,61h;текущее состояние порта 61h в AL
    102.     or al,00000011b;установить 0-ой и 1-ый биты
    103.     out 61h,al ;теперь динамик включен
    104.     mov al,0B6h
    105.     out 43h,al  ;Timer  8253-5 (AT: 8254.2).
    106.     mov esi,offset melody; данные
    107.     mov ecx,size_melody  ; счетчик
    108.     mov dx,42h ;Timer 8253-5 (AT: 8254.2).
    109.     push edi
    110. a15:    push edx   ;PC/XT PPI port B bits:
    111.     push ecx   ;0: Timer 2 gate OR  03h= speaker ON
    112.     rdtsc      ;1: Timer 2 data AND 0FCh= speaker OFF
    113.     mov ecx,eax;2:
    114.     mov edi,edx;3: 1=read high switches
    115.     add ecx,TimerLo;4: 0=enable RAM parity checking
    116.     adc edi,TimerHi;5: 0=enable I/O channel check          
    117. @@: rdtsc; крутимся до тех пор пока не получится
    118.     sub eax,ecx; задержка в 55 мСек
    119.     sbb edx,edi
    120.     jnz @b;6: 0=hold keyboard clock low    
    121.     pop ecx   ;7: 0=enable keyboard
    122.     pop edx   ;Команда выводит данные в порт ввода-вывода,
    123.     outsb     ;номер которого загружен в регистр DX,
    124.     loop a15  ;из ячейки памяти по адресу DS:ESI
    125.     pop edi
    126.     in al,61h
    127.     and al,11111100b;сбросить 0-ой и 1-ый биты
    128.     out 61h,al  ;теперь динамик выключен
    129.     retn      
    130. ;----------------------------------------------------------------
    131. .data
    132. melody  dw 2 dup(354h),2,2,2 dup(387h),2,2,2 dup(3BDh),2 dup(387h),2 dup(3BDh)
    133. dw 2 dup(3F5h),2 dup(432h),2,2,2 dup(472h),2,2,4 dup(4B5h),4 dup(472h),2 dup(3F5h)
    134. dw 2,2,2 dup(432h),2,2,2 dup(472h),2 dup(432h),2 dup(472h),2 dup(4B5h),2 dup(4FDh)
    135. dw 2,2,2 dup(549h),2,2,4 dup(599h),4 dup(549h),2 dup(472h),2,2,2 dup(5EEh,2)
    136. dw 4 dup(649h),4 dup(5EEh),2 dup(472h),2,2,2 dup(5EEh,2),4 dup(649h),4 dup(5EEh)
    137. dw 2 dup(70Eh),2 dup(6A8h),2 dup(649h),2 dup(5EEh),2 dup(599h),2 dup(549h)
    138. dw 2 dup(4FDh),2 dup(4B5h),2 dup(472h),2,2,2 dup(432h),2,2,2 dup(3F5h),2,2
    139. dw 2 dup(387h),2,2,8 dup(354h),2
    140. size_melody = $ - melody
    141. ;----------------------------------------------------------------
    142. TimerLo dd 0
    143. TimerHi dd 0
    144. scp01_sys_name db '\\.\scp01.sys',0
    145. Query dd ?
    146. Bytes   dd ?
    147. end start

    Доступ к командам In/Out через драйвер режима ядра

    Переделываю драйвер <R>ing-<0> <P>rocedure <C>all & Direct Port I/O (r0pc.sys) by @L.chemist (Andrey A. Meshkov). Драйвер scp02 передает управление на фрагмент кода из scp02-00.exe в котором содержится команда outsb, синхронизация производится через предварительный подсчет тиков. Адрес процедуры передается через WriteFile("\\.\scp02"). Полнофункциональный драйвер.
    Далее исходный текст драйвера scp02.sys
    Код (ASM):
    1. ; masm windows native #
    2. ;написано на основе драйвера режима ядра <R>ing-<0> <P>rocedure
    3. ;<C>all & Direct Port I/O (r0pc.sys) by @L.chemist (Andrey A. Meshkov)
    4. .686p
    5. .model flat
    6. include ntstatus.inc
    7. include ntddk.inc
    8. include Strings.mac
    9. includelib ntoskrnl.lib
    10. ; --------------------------------------------------------------------------------
    11. extern _imp__IoCreateDevice@28:dword
    12. extern _imp__IoCreateSymbolicLink@8:dword
    13. extern _imp__IoDeleteDevice@4:dword
    14. extern _imp__IoDeleteSymbolicLink@4:dword
    15. extern _imp_@IofCompleteRequest@8:dword
    16.  
    17. IOPM_SIZE equ 2000h             ; sizeof I/O permission map
    18. DRIVER_QUERY_PROC_NOARGS    = 10h
    19. ; --------------------------------------------------------------------------------
    20. ; structure for driver query
    21. DriverQuery struct
    22.   iocode dd ? ; user I/O code
    23.   wparam dd ? ; parameter
    24.   lparam dd ? ; parameter
    25. DriverQuery ends
    26. PDriverQuery equ dd
    27. ; --------------------------------------------------------------------------------
    28. .const
    29. CCOUNTED_UNICODE_STRING "\\Device\\scp02", cusDevice, 4
    30. CCOUNTED_UNICODE_STRING "\\DosDevices\\scp02", cusSymbolicLink, 4
    31.  
    32. .code
    33. ; Процедура входа драйвера. Эта процедура вызывается только раз после загрузки
    34. ; драйвера в память. Она выделяет необходимые ресурсы для работы драйвера. В
    35. ; нашем случае
    36. DriverEntry proc lpDriverObject:PDRIVER_OBJECT, lpusRegistryPath:PUNICODE_STRING
    37. local deviceObject:PDEVICE_OBJECT
    38. local status:NTSTATUS
    39. ; инициализируем драйвер устройства и объект устройства (Device Object)
    40.     lea eax,deviceObject
    41.     push eax;&deviceObject
    42.     push 0
    43.     push 0
    44.     push FILE_DEVICE_UNKNOWN;22h
    45.     push offset cusDevice
    46.     push sizeof(DriverQuery);0Ch
    47.     push lpDriverObject
    48.     call _imp__IoCreateDevice@28
    49.     test eax,eax;cmp eax,STATUS_SUCCESS; 0
    50.     jnz short exit; break on error
    51. ; create symbolic link to the user-visible name
    52. ; создаем символическую ссылку на драйвер устройства, что позволяет user-mode
    53. ; приложению получить доступ к нашему драйверу, используя "\\.\scp02" нотацию
    54.     push offset cusDevice
    55.     push offset cusSymbolicLink
    56.     call _imp__IoCreateSymbolicLink@8
    57.         mov status,eax; save status                
    58.     test eax,eax;cmp eax,STATUS_SUCCESS; check result
    59.     jz short success                
    60.     push deviceObject
    61.     call _imp__IoDeleteDevice@4; delete device object if not successful
    62.     jmp short exit
    63. success:    ; continue on success
    64. ; load structure to point to IRP handlers
    65. ; инициализируем точки входа драйвера в объект драйвера
    66. ; всё, что нам нужно, это операция создания (Create), записи (Write)
    67. ; и выгрузки (Unload)
    68.     mov eax,lpDriverObject
    69.     mov (DRIVER_OBJECT ptr [eax]).DriverUnload,offset DriverUnload
    70.     mov (DRIVER_OBJECT ptr [eax]).MajorFunction[IRP_MJ_CREATE*4],offset DispatchCreate
    71.     mov (DRIVER_OBJECT ptr [eax]).MajorFunction[IRP_MJ_WRITE*4],offset DispatchWrite                
    72.     mov eax,status; assign result
    73. exit:   leave
    74.     retn 8
    75. DriverEntry endp
    76. ;------------------------------------------------------
    77. ;освобождаем все выделенные ранее объекты
    78. DriverUnload:
    79. lpDriverObject  equ dword ptr [esp+4];:PDRIVER_OBJECT
    80. ; delete symbolic link to the user-visible name
    81.     push offset cusSymbolicLink
    82.     call _imp__IoDeleteSymbolicLink@4
    83. ; delete device object
    84.     mov ecx,lpDriverObject
    85.     push dword ptr (DRIVER_OBJECT ptr [ecx]).DeviceObject
    86.     call _imp__IoDeleteDevice@4
    87.     retn 4
    88. ;--------------------------------------------------
    89. DispatchCreate:
    90. lIrp equ dword ptr [esp+8]
    91.     mov ecx,lIrp
    92.     and (_IRP ptr [ecx]).IoStatus.Status,STATUS_SUCCESS;0
    93.     and (_IRP ptr [ecx]).IoStatus.Information,0
    94.     xor edx,edx;edx = IO_NO_INCREMENT
    95.     call _imp_@IofCompleteRequest@8;IofCompleteRequest (lpIrp, IO_NO_INCREMENT)
    96.     xor eax,eax;mov eax,STATUS_SUCCESS; 0
    97.     retn 8
    98. ;---------------------------------------------------------
    99. DispatchWrite   proc pDeviceObject:PDRIVER_OBJECT, lpIrp:PIRP
    100. local status:NTSTATUS
    101.  
    102.     push edi
    103.     mov status,STATUS_UNSUCCESSFUL; 0C0000001h
    104.     mov edi,lpIrp
    105.     and (_IRP ptr [edi]).IoStatus.Information,0;
    106.     mov eax,(_IRP ptr [edi]).Tail.Overlay.CurrentStackLocation
    107.     cmp [eax].IO_STACK_LOCATION.Parameters.Write._Length, sizeof(DriverQuery)
    108.     jnz a0
    109.     mov status,STATUS_NOT_IMPLEMENTED; 0C0000002h
    110.     mov eax,(_IRP ptr [edi]).UserBuffer
    111.     cmp (DriverQuery ptr [eax]).iocode,DRIVER_QUERY_PROC_NOARGS
    112.     jne a0
    113.     call (DriverQuery ptr [eax]).wparam
    114.     and status,STATUS_SUCCESS;status = 0
    115. a0: push status
    116.     pop (_IRP ptr [edi]).IoStatus.Status
    117.     mov ecx,edi
    118.     xor edx,edx;edx = IO_NO_INCREMENT
    119.     call _imp_@IofCompleteRequest@8;IofCompleteRequest (lpIrp, IO_NO_INCREMENT)
    120.     mov eax,status
    121.     pop edi
    122.     leave
    123.     retn 8
    124. DispatchWrite   endp
    125. ;----------------------------------------------------
    126. end DriverEntry
     
    Последнее редактирование: 20 мар 2022
  4. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709

    Второй вариант user-mode приложения, которое запускает драйвер scp02.sys

    Предварительно прописываем драйвер scp02.sys в реестре «вручную» (используем функции RegOpenKey, RegCreateKey, RegSetValue) и загружаем драйвер scp02.sys с помощью функции ZwLoadDriver. В реестре создается минимум необходимых записей, запускаем драйвер, выгружаем драйвер функцией ZwUnloadDriver и удаляем его раздел из реестра (используем функции RegCloseKey, SHDeleteKey). Этот способ позволяет запускать драйвер быстро и незаметно и подходит для маленьких программ не требующих установки, но требующих запуска своего драйвера.
    Код (ASM):
    1. ; masm windows gui #
    2. .686P
    3. .model flat
    4. include windows.inc
    5.  
    6. includelib kernel32.lib
    7. includelib advapi32.lib
    8. includelib ntdll.lib
    9. includelib shlwapi.lib
    10.  
    11. extern _imp__ExitProcess@4:dword
    12. extern _imp__GetFullPathNameA@16:dword
    13. extern _imp__Sleep@4:dword
    14. extern _imp__CreateFileA@28:dword
    15. extern _imp__DeviceIoControl@32:dword
    16. extern _imp__CloseHandle@4:dword
    17. extern _imp__RegOpenKeyA@12:dword
    18. extern _imp__RegCreateKeyA@12:dword
    19. extern _imp__RegSetValueExA@24:dword
    20. extern _imp__RegCloseKey@4:dword
    21. extern _imp__SHDeleteKeyA@8:dword
    22. extern _imp__ZwLoadDriver@4:dword
    23. extern _imp__ZwUnloadDriver@4:dword
    24.  
    25. SERVICE_ERROR_NORMAL       equ 1
    26. MAX_PATH            equ 260
    27. DRIVER_QUERY_PROC_NOARGS    equ 10h
    28. ;--------macros-------------------
    29. du  macro string
    30.     irpc c,<string>
    31.     if '&c'gt 127
    32.     db ('&c'- 0B0h),4
    33.     else
    34.     dw '&c'
    35.     endif
    36.     endm
    37.     dw 0
    38.     endm
    39. .const
    40. align 4
    41. us: du <\registry\machine\SYSTEM\CurrentControlSet\Services\scp01>
    42. len_us = $-us
    43. align 4
    44. cusDevice dw (len_us - 2)
    45.           dw (len_us)
    46.       dd us
    47.  
    48. .code
    49.  
    50. start proc
    51. local Key2:HKEY
    52. local Key:HKEY
    53. local acDriverPath[MAX_PATH]:CHAR
    54.  
    55.     xor ebx,ebx
    56.     rdtsc; замеряем скольким тикам соответствует 40 мСек
    57.         mov TimerLo,eax
    58.         mov TimerHi,edx
    59.     push 40
    60.     call _imp__Sleep@4
    61.     rdtsc
    62.         sub eax,TimerLo
    63.         sbb edx,TimerHi
    64.         mov TimerLo,eax
    65.         mov TimerHi,edx
    66.     mov esi,offset scp01_sys_name+4;&esi='scp01.sys',0
    67.         lea edi,acDriverPath
    68.     mov eax,'\??\'
    69.     stosd;путь к драйверу должен начаться с '\??\'
    70. ;-----инициализируем драйвер
    71.     push eax;резервирую место в стеке
    72.     push esp;указатель на пустое место в стеке
    73.     push edi;&acDriverPath+4
    74.     sub edi,4
    75.     push MAX_PATH
    76.     push esi
    77.     call _imp__GetFullPathNameA@16
    78.     add eax,4
    79.     push eax
    80.     lea eax,Key
    81.     push eax
    82.     push offset aSystem
    83.     push HKEY_LOCAL_MACHINE
    84.     call _imp__RegOpenKeyA@12
    85.         mov [esi+5],ebx;&esi=Drvname='scp01',0
    86.         lea eax,Key2
    87.         push eax
    88.     push esi
    89.     push Key
    90.     call _imp__RegCreateKeyA@12
    91.     push edi;&acDriverPath
    92.     push REG_SZ
    93.     push ebx;NULL
    94.     push offset aImagePath
    95.     push Key2
    96.     call _imp__RegSetValueExA@24
    97.     mov eax,esp;указатель на пустое место в стеке
    98.         push sizeof(dword)
    99.     mov dword ptr [eax],1;в пустое место в стеке поместим переменную dType=1
    100.     push eax;&dType
    101.     push REG_DWORD;тип переменной dType
    102.     push ebx;0
    103.     push offset aType;название переменной dType
    104.     push Key2
    105.     call _imp__RegSetValueExA@24
    106.     push Key2
    107.     call _imp__RegCloseKey@4
    108.     mov [esp],offset cusDevice
    109.     call _imp__ZwLoadDriver@4; выравниваем стек после GetFullPathNameA
    110.     sub esi,4;&esi='\\.\scp01',0
    111.     push ebx
    112.     push ebx
    113.     push OPEN_EXISTING
    114.     push ebx
    115.     push FILE_SHARE_READ; or FILE_SHARE_WRITE
    116.     push GENERIC_READ; or GENERIC_WRITE
    117.     push esi
    118.     call _imp__CreateFileA@28
    119.     inc eax;cmp eax,INVALID_HANDLE_VALUE
    120.     je short @f
    121.     dec eax
    122.     push eax;дескриптор драйвера для CloseHandle
    123.         mov ecx,offset Query
    124.     mov [ecx],offset musik
    125.     push ebx;адрес OVERLAPPED-структуры
    126.     push offset Bytes;адрес переменой, куда будет занесено количество байт, помещенных в буфер драйвером
    127.         push ebx; длина буфера
    128.     push ebx; буфер, куда драйвер поместит свои данные
    129.     push 4;длина данных
    130.     push ecx;адрес данных для драйвера
    131.     push DRIVER_QUERY_PROC_NOARGS;номер необходимой операции
    132.     push eax;дескриптор драйвера, полученный через функцию CreateFile
    133.     call _imp__DeviceIoControl@32
    134.     call _imp__CloseHandle@4
    135. @@: push offset cusDevice
    136.     call _imp__ZwUnloadDriver@4
    137.     lodsd;add esi,4
    138.         push esi;&esi='scp01',0
    139.     push Key
    140.     call _imp__SHDeleteKeyA@8
    141.     push Key
    142.     call _imp__RegCloseKey@4
    143.     push ebx;0
    144.     call _imp__ExitProcess@4
    145. start endp
    146. ;--------------------------------------------------------------
    147. musik:  ;set bits 0,1 of PC speaker control register:
    148.         in al,61h;текущее состояние порта 61h в AL
    149.     or al,00000011b;установить 0-ой и 1-ый биты
    150.     out 61h,al ;теперь динамик включен
    151.     mov al,0B6h
    152.     out 43h,al  ;Timer  8253-5 (AT: 8254.2).
    153.     mov esi,offset melody; данные
    154.     mov ecx,size_melody  ; счетчик
    155.     mov dx,42h ;Timer 8253-5 (AT: 8254.2).
    156.     push edi
    157. a15:    push edx   ;PC/XT PPI port B bits:
    158.     push ecx   ;0: Timer 2 gate OR  03h= speaker ON
    159.     rdtsc      ;1: Timer 2 data AND 0FCh= speaker OFF
    160.     mov ecx,eax;2:
    161.     mov edi,edx;3: 1=read high switches
    162.     add ecx,TimerLo;4: 0=enable RAM parity checking
    163.     adc edi,TimerHi;5: 0=enable I/O channel check        
    164. @@: rdtsc; крутимся до тех пор пока не получится
    165.     sub eax,ecx; задержка в 55 мСек
    166.     sbb edx,edi
    167.     jnz @b;6: 0=hold keyboard clock low
    168.     pop ecx   ;7: 0=enable keyboard
    169.     pop edx   ;Команда выводит данные в порт ввода-вывода,
    170.     outsb     ;номер которого загружен в регистр DX,
    171.     loop a15  ;из ячейки памяти по адресу DS:ESI
    172.     pop edi
    173.     in al,61h
    174.     and al,11111100b;сбросить 0-ой и 1-ый биты
    175.     out 61h,al  ;теперь динамик выключен
    176.     retn  
    177. ;----------------------------------------------------------------
    178. .data
    179. melody  dw 2 dup(354h),2,2,2 dup(387h),2,2,2 dup(3BDh),2 dup(387h),2 dup(3BDh)
    180. dw 2 dup(3F5h),2 dup(432h),2,2,2 dup(472h),2,2,4 dup(4B5h),4 dup(472h),2 dup(3F5h)
    181. dw 2,2,2 dup(432h),2,2,2 dup(472h),2 dup(432h),2 dup(472h),2 dup(4B5h),2 dup(4FDh)
    182. dw 2,2,2 dup(549h),2,2,4 dup(599h),4 dup(549h),2 dup(472h),2,2,2 dup(5EEh,2)
    183. dw 4 dup(649h),4 dup(5EEh),2 dup(472h),2,2,2 dup(5EEh,2),4 dup(649h),4 dup(5EEh)
    184. dw 2 dup(70Eh),2 dup(6A8h),2 dup(649h),2 dup(5EEh),2 dup(599h),2 dup(549h)
    185. dw 2 dup(4FDh),2 dup(4B5h),2 dup(472h),2,2,2 dup(432h),2,2,2 dup(3F5h),2,2
    186. dw 2 dup(387h),2,2,8 dup(354h),2
    187. size_melody = $ - melody
    188. ;----------------------------------------------------------------
    189. TimerLo dd 0
    190. TimerHi dd 0
    191. scp01_sys_name db '\\.\scp01.sys',0
    192. Query dd ?
    193. Bytes   dd ?
    194. aSystem db 'SYSTEM\CurrentControlSet\Services',0
    195. aType   db "Type",0
    196. aImagePath db "ImagePath",0
    197. end start

    Третий вариант user-mode приложения, которое запускает драйвер scp01.sys

    Устанавливаем драйвер при помощи функции ZwSetSystemInformation
    Код (ASM):
    1. ; masm windows gui #
    2. .686P
    3. .model flat
    4. include windows.inc
    5.  
    6. includelib kernel32.lib
    7. includelib advapi32.lib
    8. includelib ntdll.lib
    9. includelib shlwapi.lib
    10.  
    11. extern _imp__ExitProcess@4:dword
    12. extern _imp__GetFullPathNameA@16:dword
    13. extern _imp__Sleep@4:dword
    14. extern _imp__CreateFileA@28:dword
    15. extern _imp__DeviceIoControl@32:dword
    16. extern _imp__CloseHandle@4:dword
    17. extern _imp__ZwSetSystemInformation@12:dword
    18.  
    19. SERVICE_ERROR_NORMAL       equ 1
    20. MAX_PATH            equ 260
    21. DRIVER_QUERY_PROC_NOARGS    equ 10h
    22. SystemLoadAndCallImage     equ 26h
    23.  
    24. .code
    25. start proc
    26. local GregsImage:qword
    27. local daPath[MAX_PATH]:CHAR
    28. local acDriverPath[MAX_PATH]:CHAR
    29.  
    30.     xor ebx,ebx
    31.     rdtsc; замеряем скольким тикам соответствует 40 мСек
    32.         mov TimerLo,eax
    33.         mov TimerHi,edx
    34.     push 40
    35.     call _imp__Sleep@4
    36.     rdtsc
    37.         sub eax,TimerLo
    38.         sbb edx,TimerHi
    39.         mov TimerLo,eax
    40.         mov TimerHi,edx
    41.     mov esi,offset scp01_sys_name+4;&esi='scp01.sys',0
    42.         lea edi,acDriverPath
    43.     mov eax,'\??\'
    44.     stosd;путь к драйверу должен начаться с '\??\'
    45. ;-----инициализируем драйвер
    46.     push eax;резервирую место в стеке
    47.     push esp;указатель на пустое место в стеке
    48.     push edi;&acDriverPath+4
    49.     push MAX_PATH
    50.     push esi
    51.     call _imp__GetFullPathNameA@16
    52.     pop ecx
    53.     add eax,4
    54.         mov ecx,eax
    55.     imul eax,20002h
    56.     add eax,2
    57.     mov dword ptr GregsImage,eax
    58.     lea edi,daPath
    59.     mov dword ptr GregsImage+4,edi
    60.     xor eax,eax
    61.     lea esi,acDriverPath
    62. @@: lodsb
    63.     stosw
    64.     loop @b
    65.     push sizeof GregsImage;8
    66.     lea edi,GregsImage
    67.     push edi;&GregsImage
    68.     push SystemLoadAndCallImage;=26h
    69.     call _imp__ZwSetSystemInformation@12
    70.         mov esi,offset scp01_sys_name
    71.         mov [esi+5],ebx;&esi=Drvname='scp01',0
    72.     push ebx
    73.     push ebx
    74.     push OPEN_EXISTING
    75.     push ebx
    76.     push FILE_SHARE_READ; or FILE_SHARE_WRITE
    77.     push GENERIC_READ; or GENERIC_WRITE
    78.     push esi
    79.     call _imp__CreateFileA@28
    80.     inc eax;cmp eax,INVALID_HANDLE_VALUE
    81.     je short @f
    82.     dec eax
    83.     push eax;дескриптор драйвера для CloseHandle
    84.         mov ecx,offset Query
    85.     mov [ecx],offset musik
    86.     push ebx;адрес OVERLAPPED-структуры
    87.     push offset Bytes;адрес переменой, куда будет занесено количество байт, помещенных в буфер драйвером
    88.         push ebx; длина буфера
    89.     push ebx; буфер, куда драйвер поместит свои данные
    90.     push 4;длина данных
    91.     push ecx;адрес данных для драйвера
    92.     push DRIVER_QUERY_PROC_NOARGS;номер необходимой операции
    93.     push eax;дескриптор драйвера, полученный через функцию CreateFile
    94.     call _imp__DeviceIoControl@32
    95.     call _imp__CloseHandle@4
    96. @@: push ebx;0
    97.     call _imp__ExitProcess@4
    98. start endp
    99. ;--------------------------------------------------------------
    100. musik:  ;set bits 0,1 of PC speaker control register:
    101.         in al,61h;текущее состояние порта 61h в AL
    102.     or al,00000011b;установить 0-ой и 1-ый биты
    103.     out 61h,al ;теперь динамик включен
    104.     mov al,0B6h
    105.     out 43h,al  ;Timer  8253-5 (AT: 8254.2).
    106.     mov esi,offset melody; данные
    107.     mov ecx,size_melody  ; счетчик
    108.     mov dx,42h ;Timer 8253-5 (AT: 8254.2).
    109.     push edi
    110. a15:    push edx   ;PC/XT PPI port B bits:
    111.     push ecx   ;0: Timer 2 gate OR  03h= speaker ON
    112.     rdtsc      ;1: Timer 2 data AND 0FCh= speaker OFF
    113.     mov ecx,eax;2:
    114.     mov edi,edx;3: 1=read high switches
    115.     add ecx,TimerLo;4: 0=enable RAM parity checking
    116.     adc edi,TimerHi;5: 0=enable I/O channel check        
    117. @@: rdtsc; крутимся до тех пор пока не получится
    118.     sub eax,ecx; задержка в 55 мСек
    119.     sbb edx,edi
    120.     jnz @b;6: 0=hold keyboard clock low  
    121.     pop ecx   ;7: 0=enable keyboard
    122.     pop edx   ;Команда выводит данные в порт ввода-вывода,
    123.     outsb     ;номер которого загружен в регистр DX,
    124.     loop a15  ;из ячейки памяти по адресу DS:ESI
    125.     pop edi
    126.     in al,61h
    127.     and al,11111100b;сбросить 0-ой и 1-ый биты
    128.     out 61h,al  ;теперь динамик выключен
    129.     retn    
    130. ;----------------------------------------------------------------
    131. .data
    132. melody  dw 2 dup(354h),2,2,2 dup(387h),2,2,2 dup(3BDh),2 dup(387h),2 dup(3BDh)
    133. dw 2 dup(3F5h),2 dup(432h),2,2,2 dup(472h),2,2,4 dup(4B5h),4 dup(472h),2 dup(3F5h)
    134. dw 2,2,2 dup(432h),2,2,2 dup(472h),2 dup(432h),2 dup(472h),2 dup(4B5h),2 dup(4FDh)
    135. dw 2,2,2 dup(549h),2,2,4 dup(599h),4 dup(549h),2 dup(472h),2,2,2 dup(5EEh,2)
    136. dw 4 dup(649h),4 dup(5EEh),2 dup(472h),2,2,2 dup(5EEh,2),4 dup(649h),4 dup(5EEh)
    137. dw 2 dup(70Eh),2 dup(6A8h),2 dup(649h),2 dup(5EEh),2 dup(599h),2 dup(549h)
    138. dw 2 dup(4FDh),2 dup(4B5h),2 dup(472h),2,2,2 dup(432h),2,2,2 dup(3F5h),2,2
    139. dw 2 dup(387h),2,2,8 dup(354h),2
    140. size_melody = $ - melody
    141. ;----------------------------------------------------------------
    142. TimerLo dd 0
    143. TimerHi dd 0
    144. scp01_sys_name db '\\.\scp01.sys',0
    145. Query dd ?
    146. Bytes   dd ?
    147. end start

    Доступ к командам In/Out через драйвер режима ядра


    Переделываю драйвер <R>ing-<0> <P>rocedure <C>all & Direct Port I/O (r0pc.sys) by @L.chemist (Andrey A. Meshkov). Драйвер scp02 передает управление на фрагмент кода из scp02-00.exe в котором содержится команда outsb, синхронизация производится через предварительный подсчет тиков. Адрес процедуры передается через WriteFile("\\.\scp02"). Полнофункциональный драйвер.
    Далее исходный текст драйвера scp02.sys
    Код (ASM):
    1. ; masm windows native #
    2. ;написано на основе драйвера режима ядра <R>ing-<0> <P>rocedure
    3. ;<C>all & Direct Port I/O (r0pc.sys) by @L.chemist (Andrey A. Meshkov)
    4. .686p
    5. .model flat
    6. include ntstatus.inc
    7. include ntddk.inc
    8. include Strings.mac
    9. includelib ntoskrnl.lib
    10. ; --------------------------------------------------------------------------------
    11. extern _imp__IoCreateDevice@28:dword
    12. extern _imp__IoCreateSymbolicLink@8:dword
    13. extern _imp__IoDeleteDevice@4:dword
    14. extern _imp__IoDeleteSymbolicLink@4:dword
    15. extern _imp_@IofCompleteRequest@8:dword
    16.  
    17. IOPM_SIZE equ 2000h             ; sizeof I/O permission map
    18. DRIVER_QUERY_PROC_NOARGS    = 10h
    19. ; --------------------------------------------------------------------------------
    20. ; structure for driver query
    21. DriverQuery struct
    22.   iocode dd ? ; user I/O code
    23.   wparam dd ? ; parameter
    24.   lparam dd ? ; parameter
    25. DriverQuery ends
    26. PDriverQuery equ dd
    27. ; --------------------------------------------------------------------------------
    28. .const
    29. CCOUNTED_UNICODE_STRING "\\Device\\scp02", cusDevice, 4
    30. CCOUNTED_UNICODE_STRING "\\DosDevices\\scp02", cusSymbolicLink, 4
    31.  
    32. .code
    33. ; Процедура входа драйвера. Эта процедура вызывается только раз после загрузки
    34. ; драйвера в память. Она выделяет необходимые ресурсы для работы драйвера. В
    35. ; нашем случае
    36. DriverEntry proc lpDriverObject:PDRIVER_OBJECT, lpusRegistryPath:PUNICODE_STRING
    37. local deviceObject:PDEVICE_OBJECT
    38. local status:NTSTATUS
    39. ; инициализируем драйвер устройства и объект устройства (Device Object)
    40.     lea eax,deviceObject
    41.     push eax;&deviceObject
    42.     push 0
    43.     push 0
    44.     push FILE_DEVICE_UNKNOWN;22h
    45.     push offset cusDevice
    46.     push sizeof(DriverQuery);0Ch
    47.     push lpDriverObject
    48.     call _imp__IoCreateDevice@28
    49.     test eax,eax;cmp eax,STATUS_SUCCESS; 0
    50.     jnz short exit; break on error
    51. ; create symbolic link to the user-visible name
    52. ; создаем символическую ссылку на драйвер устройства, что позволяет user-mode
    53. ; приложению получить доступ к нашему драйверу, используя "\\.\scp02" нотацию
    54.     push offset cusDevice
    55.     push offset cusSymbolicLink
    56.     call _imp__IoCreateSymbolicLink@8
    57.         mov status,eax; save status              
    58.     test eax,eax;cmp eax,STATUS_SUCCESS; check result
    59.     jz short success              
    60.     push deviceObject
    61.     call _imp__IoDeleteDevice@4; delete device object if not successful
    62.     jmp short exit
    63. success:    ; continue on success
    64. ; load structure to point to IRP handlers
    65. ; инициализируем точки входа драйвера в объект драйвера
    66. ; всё, что нам нужно, это операция создания (Create), записи (Write)
    67. ; и выгрузки (Unload)
    68.     mov eax,lpDriverObject
    69.     mov (DRIVER_OBJECT ptr [eax]).DriverUnload,offset DriverUnload
    70.     mov (DRIVER_OBJECT ptr [eax]).MajorFunction[IRP_MJ_CREATE*4],offset DispatchCreate
    71.     mov (DRIVER_OBJECT ptr [eax]).MajorFunction[IRP_MJ_WRITE*4],offset DispatchWrite              
    72.     mov eax,status; assign result
    73. exit:   leave
    74.     retn 8
    75. DriverEntry endp
    76. ;------------------------------------------------------
    77. ;освобождаем все выделенные ранее объекты
    78. DriverUnload:
    79. lpDriverObject  equ dword ptr [esp+4];:PDRIVER_OBJECT
    80. ; delete symbolic link to the user-visible name
    81.     push offset cusSymbolicLink
    82.     call _imp__IoDeleteSymbolicLink@4
    83. ; delete device object
    84.     mov ecx,lpDriverObject
    85.     push dword ptr (DRIVER_OBJECT ptr [ecx]).DeviceObject
    86.     call _imp__IoDeleteDevice@4
    87.     retn 4
    88. ;--------------------------------------------------
    89. DispatchCreate:
    90. lIrp equ dword ptr [esp+8]
    91.     mov ecx,lIrp
    92.     and (_IRP ptr [ecx]).IoStatus.Status,STATUS_SUCCESS;0
    93.     and (_IRP ptr [ecx]).IoStatus.Information,0
    94.     xor edx,edx;edx = IO_NO_INCREMENT
    95.     call _imp_@IofCompleteRequest@8;IofCompleteRequest (lpIrp, IO_NO_INCREMENT)
    96.     xor eax,eax;mov eax,STATUS_SUCCESS; 0
    97.     retn 8
    98. ;---------------------------------------------------------
    99. DispatchWrite   proc pDeviceObject:PDRIVER_OBJECT, lpIrp:PIRP
    100. local status:NTSTATUS
    101.  
    102.     push edi
    103.     mov status,STATUS_UNSUCCESSFUL; 0C0000001h
    104.     mov edi,lpIrp
    105.     and (_IRP ptr [edi]).IoStatus.Information,0;
    106.     mov eax,(_IRP ptr [edi]).Tail.Overlay.CurrentStackLocation
    107.     cmp [eax].IO_STACK_LOCATION.Parameters.Write._Length, sizeof(DriverQuery)
    108.     jnz a0
    109.     mov status,STATUS_NOT_IMPLEMENTED; 0C0000002h
    110.     mov eax,(_IRP ptr [edi]).UserBuffer
    111.     cmp (DriverQuery ptr [eax]).iocode,DRIVER_QUERY_PROC_NOARGS
    112.     jne a0
    113.     call (DriverQuery ptr [eax]).wparam
    114.     and status,STATUS_SUCCESS;status = 0
    115. a0: push status
    116.     pop (_IRP ptr [edi]).IoStatus.Status
    117.     mov ecx,edi
    118.     xor edx,edx;edx = IO_NO_INCREMENT
    119.     call _imp_@IofCompleteRequest@8;IofCompleteRequest (lpIrp, IO_NO_INCREMENT)
    120.     mov eax,status
    121.     pop edi
    122.     leave
    123.     retn 8
    124. DispatchWrite   endp
    125. ;----------------------------------------------------
    126. end DriverEntry
     
    Последнее редактирование: 20 мар 2022
  5. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709

    Доступ к командам In/Out через изменение карты IOPM


    Manipulating the IOPM (I/O Permission Bitmap)
    Changing the IOPM within your Kernel Mode Drivers requires
    the knowledge of a couple of undocumented calls.
    These are Ke386IoSetAccessProcess, Ke386SetIoAccessMap
    and PsLookupProcessByProcessId
    На написание статьи меня подтолкнуло чтение Криса Касперски «Методы низкоуровневого управления приводами» и упомянутая в этой работе статья Dale Roberts от 01 мая 1996 «Direct Port I/O and Windows NT». Позже в KmdTutor by Four-F я нашел драйвер giveio, который используя функции Ke386SetIoAccessMap, Ke386QueryIoAccessMap и Ke386IoSetAccessProcess предоставлял доступ к портам ввода/вывода user-mode приложениям.

    Теория

    Считается, что в Windows NT/2k/XP прямое обращение к портам возможно только на уровне ядра, а приложения вынуждены общаться с портами через высокоуровневый интерфейс, предоставляемый драйвером. На самом деле, выполнять команды IN/OUT можно и на прикладном уровне, правда не без помощи недокументированных возможностей операционной системы и документированных, но малоизвестных особенностей реализации защищенного режима работы в процессорах Intel.
    В «Instruction Set Reference» приведен псевдокод инструкции OUT:
    Код (Text):
    1. IF ((PE = 1) and ((CPL > IOPL) or (VM = 1)))
    2.     THEN (* Protected mode with CPL > IOPL or virtual-8086 mode *)
    3.         IF (Any I/O Permission Bit for I/O port being accessed = 1)
    4.             THEN (* I/O operation is not allowed *)
    5.                 #GP(0);
    6.             ELSE ( * I/O operation is allowed *)
    7.                 DEST <- SRC; (* Read from selected I/O port *)
    8.         FI;
    9.     ELSE (* Real Mode or Protected Mode with CPL  <= IOPL *)
    10.         DEST <- SRC; (* Read from selected I/O port *)
    11. FI;
    и псевдокод инструкции IN
    Код (Text):
    1. IF ((PE = 1) and ((CPL > IOPL) or (VM = 1)))
    2.     THEN (* Protected mode with CPL > IOPL or virtual-8086 mode *)
    3.         IF (Any I/O Permission Bit for I/O port being accessed = 1)
    4.             THEN (* I/O operation is not allowed *)
    5.                 #GP(0);
    6.             ELSE ( * I/O operation is allowed *)
    7.                 DEST <- SRC; (* Writes to selected I/O port *)
    8.         FI;
    9.     ELSE (* Real Mode or Protected Mode with CPL <= IOPL *)
    10.         DEST <- SRC; (* Writes to selected I/O port *)
    11. FI;
    Обнаружив, что полномочий текущего уровня привилегий недостаточно для выполнения команды IN/OUT, процессор не спешит выбросить исключение general protection fault, а осуществляет дополнительную проверку на предмет состояния карты разрешения ввода/вывода (IOPM ― I/O Permission bitMap) и, если бит памяти, соответствующий данному порту равен нулю, то вывод в порт осуществляется несмотря на запрет со стороны CPL.
    Таким образом, для взаимодействия с портами с прикладного уровня достаточно скорректировать карту разрешения ввода/вывода, после чего подсистема защиты операционной системы Windows NT перестает мешать, поскольку контроль доступа к портам осуществляется не на программном, а на аппаратном уровне и, если процессор перестанет выбрасывать исключения, операционная система ничего не узнает о происходящем.
    Из «Architecture Software Developer's Manual Volume 1: Basic Architecture», узнаем, что карта ввода/вывода находится в сегменте состояния задачи (TSS ― Task State Segment), смещение IOPM относительно начала TSS определяется 32-битным полем, расположенном в 66h и 67h байтах сегмента состояния задачи. Нулевой бит карты IOPM отвечает за нулевой порт, первый - за первый, второй - за второй и т.д. вплоть до старшего бита 2000h-байта, отвечающего за 65535 порт. Битовую карту завершает так называемый байт-терминатор, имеющий значение 0FFh. Порты, чьи биты сброшены в нулевое значение, доступны с прикладного уровня без ограничений. Сама карта ввода/вывода доступа лишь драйверам, но не приложениям, поэтому без написания собственного драйвера не обойтись. Однако этот драйвер будет работать только на стадии инициализации, а весь дальнейший ввод/вывод пойдет напрямую, даже если выгрузить драйвер из памяти.
    В Windows NT смещение карты ввода/вывода по умолчанию находится за пределами сегмента состояния задачи и потому модифицировать карту ввода/вывода не так-то просто. То есть карта ввода/вывода в TSS есть, но она заблокирована системой.
    Попытка подкорректировать указатель на карту ввода/вывода ни к чему не приводит, поскольку Windows NT хранит копию этого значения в контексте процесса, а потому при переключении контекста указатель на прежнюю карту автоматически восстанавливается.
    Можно увеличить размер сегмента состояния задачи так, чтобы адрес карты ввода/вывода, прежде указывающий за его конец, теперь приходился на подвластную нам область памяти. Поскольку в хвосте последней страницы, занятой TSS, имеется всего лишь 0F55h байт, максимальный размер карты, которую мы можем создать в этом промежутке, охватывает всего лишь 31392 портов ввода/вывода.
    Дейлом Робертсом («Direct Port I/O and Windows NT» by Dale Roberts) были обнаружены три недокументированные функции: Ke386SetIoAccessMap, Ke386QueryIoAccessMap и Ke386IoSetAccessProcess, которые обеспечивают управление картой ввода/вывода. Эти функции находятся в NTOSKRNL.EXE и они доступы с уровня драйверов.
    Функция Ke386SetIoAccessMap принимает два аргумента: dwFlag ― длинное целое, которое нужно установить в 1, чтоб функция работала, и pIopm ― указатель на буфер. Функция копирует переданную карту доступа к портам ввода/вывода длинной 2000h из буфера в TSS по смещению 88h.
    ПараметрОписание
    dwFlagтолько 1 - разрешает копирование. При любом другом значении функция возвращает ошибку
    pIopmуказатель на блок памяти для приема IOPM, размером не менее 2000h байт.
    Функция Ke386QueryIoAccessMap принимает те же аргументы, но делает прямопротивоположное, копируя текущую IOPM из TSS в буфер длинной 2000h. Если аргумент dwFlag установлен в 0, set-функция копирует в IOPM 0FFh, а query-функция копирует 0FFh в пользовательский буфер.
    ПараметрОписание
    dwFlag0 - заполнить буфер единичными битами (то есть запретить доступ ко всем портам);
    1 - скопировать текущую IOPM из TSS в буфер.
    pIopm указатель на блок памяти для приема IOPM, размером не менее 2000h байт.
    Функция Ke386IoSetAccessProcess принимает два аргумента: pProcess ― указатель на структуру процесса, полученный вызовом функции PsGetCurrentProcess и dwFlag ― целое, которое должно быть установлено в 1 чтобы разрешить доступ к портам ввода/вывода, или 0 чтоб запретить его. Когда аргумент dwFlag равен 0, функция запрещает доступ к портам ввода/вывода установкой смещения IOPM переданного процесса за границу сегмента TSS. Когда аргумент dwFlag равен 1, функция разрешает доступ к портам ввода/вывода, устанавливая смещение IOPM переданного процесса на начало IOPM по смещению 88h в TSS.
    ПараметрОписание
    pProcessуказатель на структуру KPROCESS
    dwFlag0 - запретить доступ к портам ввода-вывода, установкой смещения IOPM за границу сегмента TSS;
    1 - разрешить доступ к портам ввода-вывода, устанавливая смещение IOPM в пределах TSS равным 88h.
    Используя set- и query- функции можно читать, изменять, и записывать обратно IOPM, предоставляя доступ к нужным портам, устанавливая соответствующие биты для них в 0.

    Практика

    Драйвер scp04 написан на основе драйвера giveio by Four-F, открывает порты ввода/вывода через изменение карты IOPM и использует функции Ke386SetIoAccessMap, Ke386QueryIoAccessMap, Ke386IoSetAccessProcess. Значение ProcessId драйвер получает через реестр. Использование драйвера scp04 позволяет одновременно использовать команду outsb и WinAPI функции пользовательского режима, например функцию Sleep. scp04 представляет собой неполнофункциональный драйвер, то есть самостоятельно только стартует и открывает порты ввода/вывода после чего возвращает код ошибки и система удаляет его из памяти.
    Функции Ke386SetIoAccessMap, Ke386QueryIoAccessMap и Ke386IoSetAccessProcess это только «обертки» для функций вызываемых из hal.dll. Правда, как показала проверка, при запуске на Celeron 466 и на двухядерном Pentium 4 функции из hal.dll, заменяющие функции из ntoskernl.exe могут отличаться, я попытался найти компромиссное решение. В результате команды IN и OUT удалось поместить в user-mode приложение, что дает возможность «написания быстрой программы для взаимодействия с устройством, в которой printf()'ы и getchar()'ы чередовались бы с командами ввода/вывода» (Dale Roberts).
    Далее исходный текст драйвера scp04.sys
    Код (ASM):
    1. ; masm windows native #
    2. ;написано на основе драйвера режима ядра giveio из KmdTutor by Four-F
    3. .686
    4. .model flat
    5.  
    6. include ntddk.inc
    7. include Strings.mac
    8. includelib ntoskrnl.lib
    9.  
    10. extern _imp__ZwQueryValueKey@24:dword
    11. extern _imp__ZwOpenKey@12:dword
    12. extern _imp__ZwClose@4:dword
    13. extern _imp__MmAllocateNonCachedMemory@4:dword
    14. extern _imp__PsLookupProcessByProcessId@8:dword
    15. extern _imp__ObDereferenceObject@4:dword
    16. extern _imp__MmFreeNonCachedMemory@8:dword
    17. extern _imp__Ke386SetIoAccessMap@8:dword
    18. extern _imp__Ke386QueryIoAccessMap@8:dword
    19. extern _imp__Ke386IoSetAccessProcess@8:dword
    20. ;--------------------------------------------
    21. IOPM_SIZE equ 2000h ; sizeof I/O permission map
    22. .const
    23. CCOUNTED_UNICODE_STRING "ProcessId", cusProcessid, 4
    24. ;--------------------------------------------
    25. .code
    26. ;--------------------------------------------
    27. DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
    28.  
    29. local status:NTSTATUS
    30. local oa:OBJECT_ATTRIBUTES
    31. local hKey:HANDLE
    32. local kvpi:KEY_VALUE_PARTIAL_INFORMATION
    33. local pIopm:PVOID
    34. local pProcess:PVOID
    35.  
    36.     xor ebx,ebx
    37.     mov status, STATUS_DEVICE_CONFIGURATION_ERROR
    38. ;Для последующего вызова функции ZwOpenKey нам потребуется указатель на
    39. ;заполненную структуру OBJECT_ATTRIBUTES
    40.     lea ecx, oa
    41.         mov (OBJECT_ATTRIBUTES ptr [ecx])._Length,sizeof(OBJECT_ATTRIBUTES)
    42.     mov (OBJECT_ATTRIBUTES ptr [ecx]).RootDirectory,ebx
    43.     push pusRegistryPath
    44.     pop (OBJECT_ATTRIBUTES ptr [ecx]).ObjectName
    45.     mov (OBJECT_ATTRIBUTES ptr [ecx]).Attributes,ebx
    46.     mov (OBJECT_ATTRIBUTES ptr [ecx]).SecurityDescriptor,ebx
    47.     mov (OBJECT_ATTRIBUTES ptr [ecx]).SecurityQualityOfService,ebx
    48. ;Вызовом функции ZwOpenKey получаем описатель раздела реестра в переменной
    49. ;hKey. Вторым параметром в эту функцию передаются права доступа, третьим -
    50. ;указатель на структуру OBJECT_ATTRIBUTES, заполненную на предыдущем этапе.
    51.     push ecx
    52.     push KEY_READ
    53.     lea eax,hKey
    54.     push eax
    55.     call _imp__ZwOpenKey@12
    56.     test eax,eax
    57.     jnz a7;.if eax == STATUS_SUCCESS
    58. ;С помощью функции ZwQueryValueKey получаем значение идентификатора
    59. ;процесса, записанное в параметре реестра ProcessId. Вторым параметром
    60. ;в эту функцию передается указатель на инициализированную структуру
    61. ;UNICODE_STRING, содержащую имя параметра реестра, значение которого мы
    62. ;хотим получить. Третий параметр функции ZwQueryValueKey определяет тип
    63. ;запрашиваемой информации. KeyValuePartialInformation - символьная константа
    64. ;равная 2. Четвертый и пятый параметры - указатель на структуру
    65. ;KEY_VALUE_PARTIAL_INFORMATION и ее размер соответственно. В члене Data этой
    66. ;структуры мы и получим значение идентификатора процесса. Последний параметр
    67. ;указатель на переменную, размером DWORD, в которую будет записано количество
    68. ;скопированных из реестра байт. Перед самым вызовом ZwQueryValueKey, мы
    69. ;резервируем на стеке для него место, а после вызова извлекаем значение
    70.         push eax
    71.     push esp
    72.     push sizeof(KEY_VALUE_PARTIAL_INFORMATION)
    73.     lea eax,kvpi
    74.     push eax
    75.     push KeyValuePartialInformation
    76.     push offset cusProcessid
    77.     push hKey
    78.     call _imp__ZwQueryValueKey@24
    79.     pop ecx
    80.     cmp eax,STATUS_OBJECT_NAME_NOT_FOUND
    81.     je a6
    82.     test ecx,ecx
    83.     je a6;.if ( eax != STATUS_OBJECT_NAME_NOT_FOUND ) && ( ecx != 0 )
    84. ;Если вызов ZwQueryValueKey прошел успешно, выделяем с помощью функции
    85. ;MmAllocateNonCachedMemory кусок памяти размером 2000h байт - максимальный
    86. ;размер карты разрешения ввода-вывода. Сохраняем указатель в переменной pIopm
    87.     push IOPM_SIZE
    88.     call _imp__MmAllocateNonCachedMemory@4
    89.     xchg eax,ecx
    90.     jecxz a5;if eax != NULL
    91.     mov pIopm,ecx
    92. ;Передавая в функцию PsLookupProcessByProcessId полученный ранее идентификатор
    93. ;процесса, получаем указатель на KPROCESS в переменной pProcess
    94.         lea ecx,kvpi
    95.     lea eax,pProcess
    96.     push eax
    97.     push dword ptr (KEY_VALUE_PARTIAL_INFORMATION PTR [ecx]).Data
    98.     call _imp__PsLookupProcessByProcessId@8
    99.     test eax,eax
    100.     jnz short a3;.if eax == STATUS_SUCCESS
    101. ;Копируем текущую IOPM размером 2000h из TSS в буфер, указатель на который
    102. ;содержится в параметре pIopm
    103.     push pIopm
    104.     push ebx;0
    105.     call _imp__Ke386QueryIoAccessMap@8
    106.     test al,al
    107.     jz short a1;.if al != 0
    108. ;Открываем доступ к портам 42h, 43h, 61h. Сбрасываем биты соответствующие
    109. ;этим портам ввода-вывода и записываем модифицированную IOPM.
    110.     mov ecx,pIopm
    111.     and byte ptr [ecx + 42h/8],not(1 shl (42h MOD 8) or (1 shl (43h MOD 8))); I/O access for 42h & 43h port
    112.     and byte ptr [ecx + 61h/8],not(1 shl (61h MOD 8)); I/O access for 61h port
    113. ;Копируем переданную IOPM длинной 2000h из буфера, указатель на который
    114. ;содержится в параметре pIopm, в TSS
    115.     push ecx;pIopm
    116.     push 1
    117.     call _imp__Ke386SetIoAccessMap@8; Set modified IOPM
    118.     test al,al; If second parameter to Ke386IoSetAccessProcess is 1,
    119.     jz short a1;the process is given I/O access. If it is 0, access is removed.
    120. ;Разрешаем/запрещаем использование IOPM для процесса. Если второй параметр
    121. ;равен 1 разрешаем доступ к портам ввода-вывода, устанавливаем смещение
    122. ;IOPM в пределах TSS равным 88h.
    123.     push 1
    124.     push pProcess
    125.     call _imp__Ke386IoSetAccessProcess@8
    126.     test al,al
    127.     jnz short a2
    128. a1: mov status,STATUS_IO_PRIVILEGE_FAILED
    129. ;Предыдущий вызов функции PsLookupProcessByProcessId, увеличил количество
    130. ;ссылок на обьект "процесс". Система раздельно хранит количество открытых
    131. ;описателей обьекта и количество предоставленных ссылок на объект.
    132. ;Описателями, в основном, пользуется код режима пользователя, ссылками -
    133. ;только код режима ядра. Пока, хотя бы одно из этих значений, не равно нулю,
    134. ;система не удаляет объект из памяти, считая что он еще используется
    135. ;каким-то кодом. Вызовом функции ObDereferenceObject мы уменьшаем количество
    136. ;ссылок на обьект процесса.
    137. a2: push pProcess
    138.     call _imp__ObDereferenceObject@4
    139.     jmp short a4
    140. a3: mov status,STATUS_OBJECT_TYPE_MISMATCH
    141. ;С помощью функции MmFreeNonCachedMemory освобождаем выделенный буфер
    142. a4: push IOPM_SIZE
    143.     push pIopm
    144.     call _imp__MmFreeNonCachedMemory@8
    145.     jmp short a6
    146. a5: mov status, STATUS_INSUFFICIENT_RESOURCES
    147. ;вызовом функции ZwClose закрываем описатель раздела реестра
    148. a6: push hKey
    149.     call _imp__ZwClose@4
    150. ;Т.к. драйвер возвращает один из кодов ошибки, система удаляет его из памяти.
    151. ;а user-mode программа получила доступ к 42h, 43h, 61h портам ввода-выводаv
    152. a7: mov eax, status
    153.     leave
    154.     retn 8
    155. DriverEntry endp
    156.  
    157. end DriverEntry
     
    Последнее редактирование: 20 мар 2022