1
  1. Этот сайт использует файлы cookie. Продолжая пользоваться данным сайтом, Вы соглашаетесь на использование нами Ваших файлов cookie. Узнать больше.
Приветствуем вас,Гость, на форуме IFUD.WS. Обязательно рекомендуется к прочтению правила форума http://ifud.ws/threads/obnovleno-pravila-foruma.7759

Драйвер на VB6

Тема в разделе "Исходные коды", создана пользователем Maxim+, 2 сен 2015.

  1. TopicStarter Overlay
    Maxim+

    Maxim+

    Регистрация:
    1 сен 2015
    Сообщения:
    66
    Симпатии:
    60
    Всем привет. Появилось время и решил написать что-то необычное на VB6, а именно попытаться написать драйвер. Сразу скажу до этого я никогда не писал драйвера и не имею никакого опыта программирования в режиме ядра. Драйвер, по моим задумкам, должен будет читать память недоступную в пользовательском режиме, а именно в диапазоне 0x80000000 - 0xffffffff(в режиме по-умолчанию, без IMAGE_FILE_LARGE_ADDRESS_AWARE). Сразу приведу исходный код драйвера который получился:

    ' modTrickMemReader.bas - модуль драйвера
    ' © Кривоус Анатолий Анатольевич (The trick), 2014

    Option Explicit

    Public Enum NT_STATUS
    STATUS_SUCCESS = 0
    STATUS_INVALID_PARAMETER = &HC000000D
    End Enum

    Public Type UNICODE_STRING
    Length As Integer
    MaximumLength As Integer
    lpBuffer As Long
    End Type

    Public Type LIST_ENTRY
    Flink As Long
    Blink As Long
    End Type

    Public Type KDEVICE_QUEUE
    Type As Integer
    Size As Integer
    DeviceListHead As LIST_ENTRY
    Lock As Long
    Busy As Long
    End Type

    Public Type KDPC
    Type As Byte
    Importance As Byte
    Number As Integer
    DpcListEntry As LIST_ENTRY
    DeferredRoutine As Long
    DeferredContext As Long
    SystemArgument1 As Long
    SystemArgument2 As Long
    DpcData As Long
    End Type

    Public Type DISPATCHER_HEADER
    Lock As Long
    SignalState As Long
    WaitListHead As LIST_ENTRY
    End Type

    Public Type KEVENT
    Header As DISPATCHER_HEADER
    End Type

    Public Type IO_STATUS_BLOCK
    StatusPointer As Long
    Information As Long
    End Type

    Public Type Tail
    DriverContext(3) As Long
    Thread As Long
    AuxiliaryBuffer As Long
    ListEntry As LIST_ENTRY
    lpCurStackLocation As Long
    OriginalFileObject As Long
    End Type

    Public Type IRP
    Type As Integer
    Size As Integer
    MdlAddress As Long
    Flags As Long
    AssociatedIrp As Long
    ThreadListEntry As LIST_ENTRY
    IoStatus As IO_STATUS_BLOCK
    RequestorMode As Byte
    PendingReturned As Byte
    StackCount As Byte
    CurrentLocation As Byte
    Cancel As Byte
    CancelIrql As Byte
    ApcEnvironment As Byte
    AllocationFlags As Byte
    UserIosb As Long
    UserEvent As Long
    Overlay As Currency
    CancelRoutine As Long
    UserBuffer As Long
    Tail As Tail
    End Type

    Public Type DEVICEIOCTL
    OutputBufferLength As Long
    InputBufferLength As Long
    IoControlCode As Long
    Type3InputBuffer As Long
    End Type

    Public Type IO_STACK_LOCATION
    MajorFunction As Byte
    MinorFunction As Byte
    Flags As Byte
    Control As Byte
    ' Поле DeviceIoControl из объединения
    DeviceIoControl As DEVICEIOCTL
    pDeviceObject As Long
    pFileObject As Long
    pCompletionRoutine As Long
    pContext As Long
    End Type

    Public Type DRIVER_OBJECT
    Type As Integer
    Size As Integer
    pDeviceObject As Long
    Flags As Long
    DriverStart As Long
    DriverSize As Long
    DriverSection As Long
    DriverExtension As Long
    DriverName As UNICODE_STRING
    HardwareDatabase As Long
    FastIoDispatch As Long
    DriverInit As Long
    DriverStartIo As Long
    DriverUnload As Long
    MajorFunction(27) As Long
    End Type

    Public Type DEVICE_OBJECT
    Type As Integer
    Size As Integer
    ReferenceCount As Long
    DriverObject As Long
    NextDevice As Long
    AttachedDevice As Long
    CurrentIrp As Long
    Timer As Long
    Flags As Long
    Characteristics As Long
    Vpb As Long
    DeviceExtension As Long
    DeviceType As Long
    StackSize As Byte
    Queue(39) As Byte
    AlignRequirement As Long
    DeviceQueue As KDEVICE_QUEUE
    Dpc As KDPC
    ActiveThreadCount As Long
    SecurityDescriptor As Long
    DeviceLock As KEVENT
    SectorSize As Integer
    Spare1 As Integer
    DeviceObjExtension As Long
    Reserved As Long
    End Type
    Private Type BinaryString
    D(255) As Integer
    End Type

    Public Const FILE_DEVICE_UNKNOWN As Long = &H22
    Public Const IO_NO_INCREMENT As Long = &H0
    Public Const IRP_MJ_CREATE As Long = &H0
    Public Const IRP_MJ_CLOSE As Long = &H2
    Public Const IRP_MJ_DEVICE_CONTROL As Long = &HE
    Public Const FILE_DEVICE_MEMREADER As Long = &H8000&
    Public Const IOCTL_READ_MEMORY As Long = &H80002000

    Public DeviceName As UNICODE_STRING ' Строка с именем устройства
    Public DeviceLink As UNICODE_STRING ' Строка с именем ссылки
    Public Device As DEVICE_OBJECT ' Объект устройства

    Dim strName As BinaryString ' Строка с именем устройства
    Dim strLink As BinaryString ' Строка с именем ссылки

    Public Sub Main()
    End Sub

    ' // Если ошибка - False
    Public Function NT_SUCCESS(ByVal Status As NT_STATUS) As Boolean
    NT_SUCCESS = Status >= STATUS_SUCCESS
    End Function

    ' // Получить указатель на стек пакета
    Public Function IoGetCurrentIrpStackLocation(pIrp As IRP) As Long
    IoGetCurrentIrpStackLocation = pIrp.Tail.lpCurStackLocation
    End Function

    ' // Точка входа в драйвер
    Public Function DriverEntry(DriverObject As DRIVER_OBJECT, RegistryPath As UNICODE_STRING) As NT_STATUS
    Dim Status As NT_STATUS
    ' Инициализация имен
    Status = Init()
    ' Здесь не обязательна проверка, но я поставил, т.к. возможно усовершенствование функции Init
    If Not NT_SUCCESS(Status) Then
    DriverEntry = Status
    Exit Function
    End If
    ' Создаем устройство
    Status = IoCreateDevice(DriverObject, 0, DeviceName, FILE_DEVICE_MEMREADER, 0, False, Device)
    ' Проверяем создалось ли устройство
    If Not NT_SUCCESS(Status) Then
    DriverEntry = Status
    Exit Function
    End If
    ' Создаем связь для доступа по имени из пользовательского режима
    Status = IoCreateSymbolicLink(DeviceLink, DeviceName)
    ' Проверяем корректность
    If Not NT_SUCCESS(Status) Then
    ' При неудаче удаляем устройство
    IoDeleteDevice Device
    DriverEntry = Status
    Exit Function
    End If
    ' Определяем функции
    DriverObject.DriverUnload = GetAddr(AddressOf DriverUnload) ' Выгрузка драйвера
    DriverObject.MajorFunction(IRP_MJ_CREATE) = GetAddr(AddressOf DriverCreateClose) ' При вызове CreateFile
    DriverObject.MajorFunction(IRP_MJ_CLOSE) = GetAddr(AddressOf DriverCreateClose) ' При вызове CloseHandle
    DriverObject.MajorFunction(IRP_MJ_DEVICE_CONTROL) = GetAddr(AddressOf DriverDeviceControl) ' При вызове DeviceIoControl
    ' Успех
    DriverEntry = STATUS_SUCCESS

    End Function

    ' // Процедура выгрузки драйвера
    Public Sub DriverUnload(DriverObject As DRIVER_OBJECT)
    ' Удаляем связь
    IoDeleteSymbolicLink DeviceLink
    ' Удаляем устройство
    IoDeleteDevice ByVal DriverObject.pDeviceObject
    End Sub

    ' // Функция вызывается при открытии/закрытии драйвера
    Public Function DriverCreateClose(DeviceObject As DEVICE_OBJECT, pIrp As IRP) As NT_STATUS
    pIrp.IoStatus.Information = 0
    pIrp.IoStatus.StatusPointer = STATUS_SUCCESS
    ' Возвращаем IRP пакет менеджеру ввода/вывода
    IoCompleteRequest pIrp, IO_NO_INCREMENT
    ' Успех
    DriverCreateClose = STATUS_SUCCESS
    End Function

    ' // Функция обработки IOCTL запросов
    Public Function DriverDeviceControl(DeviceObject As DEVICE_OBJECT, pIrp As IRP) As NT_STATUS
    Dim lpStack As Long
    Dim ioStack As IO_STACK_LOCATION
    ' Получаем указатель на стек пакета
    lpStack = IoGetCurrentIrpStackLocation(pIrp)
    ' Проверяем указатель на валидность
    If lpStack Then
    ' Копируем в локальную переменную
    memcpy ioStack, ByVal lpStack, Len(ioStack)
    ' Проверяем IOCTL и объединение AssociatedIrp в котором содержится SystemBuffer
    ' В SystemBuffer содержится буфер, переданный нами в DeviceIoControl
    If ioStack.DeviceIoControl.IoControlCode = IOCTL_READ_MEMORY And _
    pIrp.AssociatedIrp <> 0 Then

    Dim lpPointer As Long
    Dim DataSize As Long
    ' Копируем параметы из SystemBuffer
    memcpy lpPointer, ByVal pIrp.AssociatedIrp, 4
    memcpy DataSize, ByVal pIrp.AssociatedIrp + 4, 4
    ' Проверяем размер буфера
    If DataSize <= ioStack.DeviceIoControl.OutputBufferLength Then
    ' Проверяем количество страниц, которые мы можем прочитать
    Dim lpStart As Long
    Dim pgCount As Long
    Dim pgSize As Long
    Dim pgOfst As Long
    ' Определяем адрес начала страницы
    lpStart = lpPointer And &HFFFFF000
    ' Определяем смещение от начала страницы
    pgOfst = lpPointer And &HFFF&
    ' Проход по станицам и проверка на PageFault
    Do While MmIsAddressValid(ByVal lpStart) And (pgSize - pgOfst < DataSize)
    lpStart = lpStart + &H1000
    pgCount = pgCount + 1
    pgSize = pgSize + &H1000
    Loop
    ' Если хоть одна страница доступна
    If pgCount Then
    ' Получаем реальный размер в байтах
    pgSize = pgCount * &H1000 - pgOfst
    ' Корректируем резмер
    If DataSize > pgSize Then DataSize = pgSize
    ' Возвращаем реальный размер прочитанных данных
    pIrp.IoStatus.Information = DataSize
    ' Успех
    pIrp.IoStatus.StatusPointer = STATUS_SUCCESS
    ' Копируем данные в SystemBuffer
    memcpy ByVal pIrp.AssociatedIrp, ByVal lpPointer, DataSize
    ' Возвращаем IRP пакет менеджеру ввода/вывода
    IoCompleteRequest pIrp, IO_NO_INCREMENT
    ' Упех
    DriverDeviceControl = STATUS_SUCCESS
    ' Выход
    Exit Function

    End If

    End If

    End If

    End If
    ' Возвращаем реальный размер прочитанных данных
    pIrp.IoStatus.Information = 0
    ' Ошибка DeviceIoControl
    pIrp.IoStatus.StatusPointer = STATUS_INVALID_PARAMETER
    ' Возвращаем IRP пакет менеджеру ввода/вывода
    IoCompleteRequest pIrp, IO_NO_INCREMENT
    ' Ошибка
    DriverDeviceControl = STATUS_INVALID_PARAMETER

    End Function

    ' // Функция инициализации
    Private Function Init() As NT_STATUS
    ' Инициализируем имя устройства
    'DeviceTrickMemReader
    strName.D(0) = &H5C: strName.D(1) = &H44: strName.D(2) = &H65: strName.D(3) = &H76: strName.D(4) = &H69:
    strName.D(5) = &H63: strName.D(6) = &H65: strName.D(7) = &H5C: strName.D(8) = &H54: strName.D(9) = &H72:
    strName.D(10) = &H69: strName.D(11) = &H63: strName.D(12) = &H6B: strName.D(13) = &H4D: strName.D(14) = &H65:
    strName.D(15) = &H6D: strName.D(16) = &H52: strName.D(17) = &H65: strName.D(18) = &H61: strName.D(19) = &H64:
    strName.D(20) = &H65: strName.D(21) = &H72:
    ' Создаем UNICODE_STRING
    RtlInitUnicodeString DeviceName, strName
    ' Инициализация ссылки на имя устройства из user-mode
    'DosDevicesTrickMemReader
    strLink.D(0) = &H5C: strLink.D(1) = &H44: strLink.D(2) = &H6F: strLink.D(3) = &H73: strLink.D(4) = &H44:
    strLink.D(5) = &H65: strLink.D(6) = &H76: strLink.D(7) = &H69: strLink.D(8) = &H63: strLink.D(9) = &H65:
    strLink.D(10) = &H73: strLink.D(11) = &H5C: strLink.D(12) = &H54: strLink.D(13) = &H72: strLink.D(14) = &H69:
    strLink.D(15) = &H63: strLink.D(16) = &H6B: strLink.D(17) = &H4D: strLink.D(18) = &H65: strLink.D(19) = &H6D:
    strLink.D(20) = &H52: strLink.D(21) = &H65: strLink.D(22) = &H61: strLink.D(23) = &H64: strLink.D(24) = &H65:
    strLink.D(25) = &H72:
    ' Создаем UNICODE_STRING
    RtlInitUnicodeString DeviceLink, strLink
    '
    End Function

    Private Function GetAddr(ByVal Value As Long) As Long
    GetAddr = Value
    End Function

    '

    Итак, драйвер должен иметь точку входа DriverEntry, которую вызывает диспетчер ввода/вывода при загрузке драйвера. В параметрах передается указатель на объект-драйвер и указатель на строку с именем ключа реестра, соответствующего загружаемому драйверу. В процедуре Init мы создаем 2 строки, одна с названием устройства, другая с названием ссылки на устройство. Т.к. мы не можем использовать рантайм в режиме ядра, то приходится создавать строку в виде статического массива, обернутого в пользовательский тип, тем самым VB6 выделяет память под этот массив в стеке. Если использовать строку, то неизбежно будет вызвана одна из функций рантайма для копирования и присваивания строки, а этого мы допустить не можем. Далее мы вызываем IoCreateDevice, которая создает объект-устройство. Объект-устройство является получателем запросов ввода/вывода и к нему мы будем получать доступ при вызове функции CreateFile из пользовательского режима. В качестве первого параметра передается указатель на объект-драйвера; вторым параметром передаем 0, т.к. у нас нет структуры расширения устройства и нам не нужно выделять память; третьим параметром мы передаем имя устройства, оно нам понадобится для реализации доступа к устройству; четвертым параметром передается тип устройства (см. ниже); в пятом мы передаем 0, т.к. у нас "нестандартное устройство"; в шестом передаем False, т.к. нам не нужен монопольный режим; последний параметр - выходной. В качестве имени устройства мы должны использовать строку вида DeviceDeviceName (где DeviceName - TrickMemReader), это имя нам понадобится для того, чтобы мы могли создать ссылку на него, которая в свою очередь нужна для доступа к устройству из пользовательского режима. Тип устройства у нас -FILE_DEVICE_MEMREADER. Все нестандартные устройства должны иметь тип либо FILE_DEVICE_UNKNOWN, либо число от 0x8000 - 0xffff. Я создал константуFILE_DEVICE_MEMREADER со значением 0x8000, что соответствует первому свободному номеру. При успехе, создается устройство и заполняется структура DEVICE_OBJECT. После нужно создать связь по имени между устройством из режима ядра и пользовательским режимом. В качестве имени мы используем DosDevicesTrickMemReader, из пользовательского режима мы будем обращаться к нему через ссылку '\.TrickMemReader". Ссылка создается через IoCreateSymbolicLink. Далее мы определяем callback-процедуры, которые будут вызываться при определенных событиях:

    1. DriverUnload - при деинициализации драйвера;
    2. DriverCreateClose - при открытии и закрытии устройства;
    3. DriverDeviceControl - при вызове DeviceIoControl.

    Все. Теперь мы возвращаем STATUS_SUCCESS , что соответствует успешному выполнению.
    Теперь рассмотрим процедуру DriverUnload. Здесь все просто - мы удаляем связь и созданное устройство. В функции обработки открытия и закрытия устройства DriverCreateClose, в статусе запроса мы возвращаем успех, и возвращаем IRP пакет менеджеру ввода/вывода. Обмен данными между приложением и устройством осуществляется через IRP-пакеты. IRP-пакет состоит из 2-х частей: заголовок и стек переменной длины. Часть структуры представлена типом IRP. Итак, теперь мы добавляем функциональность нашему драйверу в функцииDriverDeviceControl. В эту функцию диспетчер ввода/вывода будет передавать IRP-пакет с данными переданными из клиентского приложения, которые мы будем формировать вызовом функции DeviceIoControl. В качестве параметров мы будем передавать 2 Long числа: 1-е адрес, откуда производить чтение, 2-е количество байт для чтения. Также одним из передаваемых параметров в IRP-пакете, при вызове DeviceIoControl, является управляющий код ввода/вывода (IOCTL), который представляет собой структуру из типа устройства, номера функции, типа передачи данных и тип доступа. Можно определить несколько таких кодов для разных операций и использовать их. Я определил код так IOCTL_READ_MEMORY = 0x80002000, 8000 - соответствует типу нешего девайса (FILE_DEVICE_MEMREADER); номер функции = 0x800, значения ниже зарезервированы, для пользовательских функций разрешены значения 0x800 - 0xFFF; тип передачи данных - 0x0 (METHOD_BUFFERED), это значит что мы будем принимать/передавать данные через буфер, определяемый параметромSystemBuffer IRP-пакета); тип доступа - FILE_ANY_ACCESS. Наглядно:
    [​IMG]
    Итак, в функции DriverDeviceControl мы получаем указатель на стек ввода/вывода IRP-запроса с помощью функции IoGetCurrentIrpStackLocation, которая возвращает его из параметраlpCurStackLocation. При упехе (если указатель ненулевой) копируем в локальную структуру IO_STACK_LOCATION параметры на которые указывает этот указатель. Теперь мы проверяем IOCTL-код и поле AssociatedIrp, которое представляет собой объединение (в VB6 нет объединений) в котором хранится указатель на SystemBuffer. Т.к. у нас тип передачи данных соответствует METHOD_BUFFERED, то в параметре SystemBuffer содержится указатель на буфер с параметрами (адрес и размер) DeviceIoControl, в этом буфере мы также можем возвратить данные которые записываются в выходной буфер DeviceIoControl. Теперь если у нас данные содержат корректные значения (IOCTL и SystemBuffer), то мы копируем в локальные переменные параметры (lpPointer, DataSize). Далее проверяем размер буфера. Размер системного буфера ввода/вывода содержится в параметреDeviceIoControl.OutputBufferLength. Если запрошенное количество байт не больше чем размер системного буфера, то все отлично. Теперь мы должны вычислить количество страниц памяти занимаемой данными которые мы хотим скопировать. Для этого мы определяем виртуальный адрес начала страницы соответствующей переданному указателю, и т.к. размер страниц кратен 4 кБ (0x1000) мы просто зануляем 12 бит указателя. Далее мы проверяем в цикле не будет ли вызвано исключение Page fault с помощью функции MmIsAddressValid. Если страница отсутствует в ОЗУ, то функция возвратит False. Таким образом мы проверяем количество страниц которые занимает нужный нам участок памяти и количество страниц которые мы сможем прочитать. Далее мы вычисляем реальный размер данных, которые мы сможем прочесть и при необходимости корректируем размер. Далее в заголовок IRP-пакета мы копируем размер данных, который мы можем прочесть и успешный статус. Поле IoStatus.Information пакета соответствует значению которое возвращает DeviceIoControl в параметреlpBytesReturned. Далее копируем в SystemBuffer нужное количество байт с помощью RtlMoveMemory и возвращаем IRP-пакет менеджеру ввода/вывода. Возвращаем статус успешной операции. Во всех остальных случаях возвращаем ошибку STATUS_INVALID_PARAMETER и нулевой размер данных. Все, код драйвера готов.
    Приступим к компиляции. Т.к. мы не можем использовать рантайм, все API-функции мы объявляем в TLB, для того чтобы они попали в импорт:

    [uuid(0000001F-0000-0000-0000-000000000AAB)]
    library ImportFunctionsForTrickMemReaderDriver
    {
    [dllname("Ntoskrnl.exe")]
    module Ntoskrnl
    {
    [entry("IoCreateDevice")]int IoCreateDevice
    (void *DriverObject,
    int DeviceExtensionSize,
    void *DeviceName,
    int DeviceType,
    int DeviceCharacteristics,
    int Exclusive,
    void *DeviceObject);

    [entry("IoCreateSymbolicLink")]int IoCreateSymbolicLink
    (void *SymbolicLinkName,
    void *DeviceName);

    [entry("IoDeleteDevice")]void IoDeleteDevice
    (void *DeviceObject);

    [entry("IoDeleteSymbolicLink")]int IoDeleteSymbolicLink
    (void *SymbolicLinkName);

    [entry("IoCompleteRequest")]void IoCompleteRequest
    (void *pIrp,
    unsigned char PriorityBoost);

    [entry("RtlInitUnicodeString")]int RtlInitUnicodeString
    (void *UnicodeString,
    void *StringPtr);

    [entry("RtlMoveMemory")]void memcpy
    (void *Destination,
    void *Source,
    int Length);

    [entry("MmIsAddressValid")]int MmIsAddressValid
    (void *VirtualAddress);

    [entry("InterlockedExchange")]int InterlockedExchange
    (void *Target,
    void *Value);
    }

    }

    PS. InterlockedExchange - я оставил, т.к. вначале драйвер имел немного другую структуру, в последствии оставил объявление в TLB. В драйвере она не попадет в импорт.
    Для того чтобы драйвер работал нужно сделать три вещи:

    1. В поле Subsystem структуры IMAGE_OPTIONAL_HEADER PE-файла драйвера должно быть значение IMAGE_SUBSYSTEM_NATIVE что соответствует драйверу режима ядра.
    2. Указать в качестве точки входа нашу процедуру DriverEntry
    3. Добавить секцию релокации, для того чтобы драйвер мог загружаться по любому адресу.
    4. Исключить MSVBVM60 из иморта.

    Для первых 3-х пунктов добавляем ключи компиляции в vbp-файл, со следующим содержимым:

    [VBCompiler]
    LinkSwitches= /ENTRY:DriverEntry /SUBSYSTEM:NATIVE /FIXED:NO

    Компилируем проект со всеми опциями оптимизации. Для исключения рантайма из импорта, я использую свою утилиту Patch, которую я использовал Please login or register to view links. Я немного доработал ее, т.к. изначально не мог запустить драйвер и долго ломал голову из-за чего это происходит, а причина была в контрольной сумме. После исключения из импорта библиотеки контрольная сумма поменялась, а я не обновлял ее. В EXE файлах, DLL и т.д. это поле не проверяется, а в драйверах проверяется. Для проверки смотрим импорт в любой программе для просмотра PE:
    [​IMG]
    Как видим рантайма нет. Что нам и требовалось.
    Для тестирования драйвера я написал простую программку, которая загружает драйвер и работает с ним.

    ' frmTestTrickVBDriver.frm - форма для тестирования драйвера
    ' © Кривоус Анатолий Анатольевич (The trick), 2014

    Option Explicit

    Private Type SERVICE_STATUS
    dwServiceType As Long
    dwCurrentState As Long
    dwControlsAccepted As Long
    dwWin32ExitCode As Long
    dwServiceSpecificExitCode As Long
    dwCheckPoint As Long
    dwWaitHint As Long
    End Type

    Private Declare Function ControlService Lib "advapi32.dll" (ByVal hService As Long, ByVal dwControl As Long, lpServiceStatus As SERVICE_STATUS) As Long
    Private Declare Function OpenSCManager Lib "advapi32.dll" Alias "OpenSCManagerW" (ByVal lpMachineName As Long, ByVal lpDatabaseName As Long, ByVal dwDesiredAccess As Long) As Long
    Private Declare Function CloseServiceHandle Lib "advapi32.dll" (ByVal hSCObject As Long) As Long
    Private Declare Function OpenService Lib "advapi32.dll" Alias "OpenServiceW" (ByVal hSCManager As Long, ByVal lpServiceName As Long, ByVal dwDesiredAccess As Long) As Long
    Private Declare Function CreateService Lib "advapi32.dll" Alias "CreateServiceW" (ByVal hSCManager As Long, ByVal lpServiceName As Long, ByVal lpDisplayName As Long, ByVal dwDesiredAccess As Long, ByVal dwServiceType As Long, ByVal dwStartType As Long, ByVal dwErrorControl As Long, ByVal lpBinaryPathName As Long, ByVal lpLoadOrderGroup As String, lpdwTagId As Long, ByVal lpDependencies As Long, ByVal lp As Long, ByVal lpPassword As Long) As Long
    Private Declare Function StartService Lib "advapi32.dll" Alias "StartServiceW" (ByVal hService As Long, ByVal dwNumServiceArgs As Long, ByVal lpServiceArgVectors As Long) As Long
    Private Declare Function DeleteService Lib "advapi32.dll" (ByVal hService As Long) As Long
    Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileW" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    Private Declare Function DeviceIoControl Lib "kernel32" (ByVal hDevice As Long, ByVal dwIoControlCode As Long, lpInBuffer As Any, ByVal nInBufferSize As Long, lpOutBuffer As Any, ByVal nOutBufferSize As Long, lpBytesReturned As Long, lpOverlapped As Any) As Long

    Private Const ERROR_SERVICE_ALREADY_RUNNING As Long = 1056&
    Private Const ERROR_SERVICE_EXISTS As Long = 1073&
    Private Const SERVICE_CONTROL_STOP As Long = &H1
    Private Const SC_MANAGER_ALL_ACCESS As Long = &HF003F
    Private Const SERVICE_ALL_ACCESS As Long = &HF01FF
    Private Const SERVICE_KERNEL_DRIVER As Long = &H1
    Private Const SERVICE_DEMAND_START As Long = &H3
    Private Const SERVICE_ERROR_NORMAL As Long = &H1
    Private Const GENERIC_READ As Long = &H80000000
    Private Const GENERIC_WRITE As Long = &H40000000
    Private Const FILE_SHARE_READ As Long = &H1
    Private Const FILE_SHARE_WRITE As Long = &H2
    Private Const OPEN_EXISTING As Long = 3
    Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80
    Private Const INVALID_HANDLE_VALUE As Long = -1
    Private Const IOCTL_READ_MEMORY As Long = &H80002000

    Private Const DriverName As String = "TrickMemReader"
    Private Const NumOfRows As Long = 32

    Private DriverFile As String
    Private hMgr As Long
    Private hSrv As Long
    Private hDev As Long
    Private buffer() As Byte
    Private bufLen As Long
    Private Address As Long

    ' // Нажатие на кнопку чтения памяти по адресу
    Private Sub cmdRead_Click()
    Dim param(1) As Long

    On Error GoTo Cancel

    Address = CLng("&H" & Trim(txtAddress.Text))

    ' Формируем параметры
    param(0) = Address
    param(1) = 16 * NumOfRows

    ' Делаем запрос драйверу
    If DeviceIoControl(hDev, IOCTL_READ_MEMORY, param(0), 8, buffer(0), UBound(buffer) + 1, bufLen, ByVal 0&) = 0 Then
    bufLen = 0
    End If

    Update

    Cancel:

    End Sub

    Private Sub Form_Load()
    Dim sw As Long
    Dim sh As Long

    ' Выделяем буфер
    ReDim buffer(16 * NumOfRows - 1)
    ' Получаем имя файла драйвера
    DriverFile = App.Path & "" & DriverName & ".sys"
    ' Открываем БД диспетчера управления службами
    hMgr = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS)

    If hMgr = 0 Then
    MsgBox "Не удалось установить связь с диспетчером управления службами"
    End
    End If
    ' Создаем службу
    hSrv = CreateService(hMgr, StrPtr(DriverName), StrPtr(DriverName), SERVICE_ALL_ACCESS, _
    SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, StrPtr(DriverFile), _
    0, 0, 0, 0, 0)
    ' Если служба уже запущена
    If hSrv = 0 And Err.LastDllError = ERROR_SERVICE_EXISTS Then
    ' Открываем службу
    hSrv = OpenService(hMgr, StrPtr(DriverName), SERVICE_ALL_ACCESS)

    End If

    If hSrv = 0 Then
    MsgBox "Не удалось создать службу"
    Unload Me
    End
    End If

    ' Запускаем драйвер
    If StartService(hSrv, 0, 0) = 0 Then

    If Err.LastDllError <> ERROR_SERVICE_ALREADY_RUNNING Then
    MsgBox "Не удалось запустить службу"
    Unload Me
    End
    End If

    End If

    ' Подключаемся к драйверу
    hDev = CreateFile(StrPtr("\." & DriverName), GENERIC_READ Or FILE_SHARE_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, _
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)

    If hD#1088;SystemBuffer IRP-&= 32, Chr$(buffer(ptr)), "?")

    Else

    hxd = hxd & " ??"
    asi = asi & "?"

    End If

    ptr = ptr + 1

    Next

    If row Then out = out & vbNewLine

    out = out & adr & ":" & hxd & " | " & asi

    Next

    picDump.Cls
    picDump.Print out

    End Sub

    Private Sub Form_Unload(Cancel As Integer)
    Dim Status As SERVICE_STATUS
    ' Отключаемся от драйвера
    CloseHandle hDev
    ' Останавливаем драйвер
    ControlService hSrv, SERVICE_CONTROL_STOP, Status
    ' Удаляем службу
    DeleteService hSrv
    ' Закрываем описатели
    CloseServiceHandle hSrv
    CloseServiceHandle hMgr
    End Sub

    Драйвер должен лежать в той же папке что и программа. Код прокомментирован, так что я не буду описывать его работу.
    Для отладки драйвера нужно использовать ядерный отладчик. Отлаживать будем на виртуальной системе (VMware) - Windows XP. В качестве отладчика возьмем Syser, выберем наш драйвер и нажмем Load. Система остановится, и мы перейдем в окно отладчика:
    [​IMG]
    Мы находимся в начале функции DriverEntry. Первый CALL соответствует вызову функции Init. Если мы проследим пошагово (F8) что там внутри, то увидим как заполняется структура и вызывается RtlInitUnicodeString для имени устройства и символической связи. Второй CALL соответствует функции NT_SUCCESS, смотрим что она возвращает TRUE (в регистре EAX) и код прыгает после проверки (TEST EAX, EAX) на ноль (False) дальше:
    [​IMG]
    Как видно код заталкивает в стек параметры для функции IoCreateDevice от последнего к первому с помощью PUSH'ей. Начнем проверку параметров. Проверим имя устройства (3-й параметр - PUSH 0f8a2c010), для этого введем команду d 0f8a2c010 (что значит просмотреть дамп памяти по адресу f8a2c010 и смотрим содержимое:
    [​IMG]
    первые 8 байт - это наша переменная DeviceName. Первые два слова - соответственно длина строки и максимальная длина строки в байтах. Следующее двойное слово - указатель на строку, смотрим (d f8a2c0d8 учитываем порядок байтов little-endian):
    [​IMG]
    , то что нужно там Unicode строка с именем устройства. Если посмотреть на параметр Device (последний выходной параметр - PUSH 0f8a2c020), то можно увидеть что он отличается от имени на 0x10 байт. Теперь посмотрим на декларации переменных, переменная Device задекларирована после DeviceName и DeviceLink, общей длиной 8 + 8 = 0x10 байт. Т.е. порядок расположения переменных в памяти соответствует порядку объявления в коде. Проверяем первый неконстантный параметр ESI, в самом начале в него копируется значение по адресуESP+0xC. Регистр ESP - указывает на вершину стека. Если пройти в начало функции DriverEntry, то можно увидеть сохранение в стеке двух регистров ESI и EDI (по соглашению StdCallэти регистры находятся в списке сохраняемых, т.е. процедура не должна изменять их после вызова). DriverObject передается в первой паременной, т.е. ближе всех к вершине стека, также после всех параметров сохраняется адрес возврата - т.е. параметр DriverObject до выполнения первой инструкции в функции DriverEntry находится по адресу ESP+4 (стек растет в сторону уменьшения адресов), после двух PUSH'ей он соответственно смещается еще на 8 байт, в итоге DriverObject находится по адресу ESP+0С, все правильно. Параметры корректные, можно вызывать функцию. Жмем F10 чтобы не заходить внутрь IoCreateDevice и смотрим значение регистра EAX после вызова, там должно быть неотрицательное число, что сигнализирует что функция отработала без ошибок. У меня она возвратила 0 (STATUS_SUCCESS), все отлично. Дальше идет уже знакомая процедура по адресу 0xF8A2B750 -NT_SUCCESS:
    [​IMG]
    При успехе идет прыжок на 0xf8a2b7bf, где идет заталкивание в стек параметров для функции IoCreateSymbolicLink. Параметр DeviceName мы уже проверяли, проверяем DeviceLink:
    [​IMG]
    То что нужно. Жмем F10, тестируем EAX, при успехе идем дальше при неудаче, удаляем девайс и выходим с ошибкой. Процедура по адресу 0xf8a2bbb0 - это GetAddr, которая просто возвращает переданное ей значение:
    [​IMG]
    Дальше идет копирование адресов по смещениям DriverObject, если посмотреть деларации то можно увидеть что по смещению 0x34 записывается адрес DriverUnload, по смещению 0x38 - MajorFunction(0) и т.д. Записываемые значения соответствуют адресам функций в нашем драйвере. Дальше происходит обнуление EAX (возвращаемой значение) и выход из процедуры DriverEntry. Все работает без ошибок, идем дальше. Итак, чтобы отследить работу драйвера мы поставим точку останова на функцию DriverDeviceControl. Адрес ее можно взять по только что записанным смещениям в структуре DRIVER_OBJECT либо найти простым просмотром и анализированием кода. В моем тесте адрес равен 0xf8a2b870, переходим на него (. 0xf8a2b870) и нажимаем F9, ставя точку останова. Напротив инструкции установится маркер:
    [​IMG]
    Теперь при вызове этой функции отладчик остановит выполнение кода и даст нам возможность пошагово выполнить код. Функции DriverCreateClose и DriverUnload я не буду описывать, т.к. там все просто. Жмем F5, тем самым продолжая выполнение в обычном режиме. Нас тут же переносит обратно в Windows. Теперь мы запускаем наше тестовое приложение, вводим какой-нибудь адрес (например 81234567) и жмем на кнопку Read. Наш вызов перехватывает отладчик и мы можем продолжить тестировать код функции DriverDeviceControl.
    Подробно внутри я не буду описывать код, остановлюсь на самом копировании:
    [​IMG]
    Сразу смотрим на стек (регистр ESP), видим что передаются правильные параметры. На всякий случай делаем дамп, потом сравним:
    [​IMG]
    Нажимаем F5 - и возвращаемся в Windows. Смотрим на дамп уже в нашей программе:
    [​IMG]
    Как видим все отлично скопировалось. Попробуем скопировать данные на границе страниц, так чтобы одна страница отсутствовала. Экспериментальным методом была найдена такая страница вот что получаем:
    [​IMG]
    Как видим, что данные скопировались корректно, где не получилось там у нас отображаются вопросительные знаки. В выходном параметре DeviceIoControl у нас возвращается количество реально прочитанных байт, его мы и используем для отображения вопросительных знаков.
    _____________________________________________________________________________
    Как видим на VB6 можно написать простой драйвер, а если использовать ассемблерные вставки можно и посерьезнее что-нибудь написать. Всем спасибо за внимание. Удачи!
     
    • Like Like x 2
    Метки:
  2. Apple96

    Apple96

    Регистрация:
    13 апр 2013
    Сообщения:
    416
    Симпатии:
    667
    Юзай bb code [\CODE]
    Код:
    Option Explicit
     

Поделиться этой страницей

Загрузка...