Драйвер на VB6.

Тема в разделе "VB", создана пользователем Thetrik, 3 янв 2017.

Метки:
  1. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Всем привет. Появилось время и решил написать что-то необычное на VB6, а именно попытаться написать драйвер. Сразу скажу до этого я никогда не писал драйвера и не имею никакого опыта программирования в режиме ядра. Драйвер, по моим задумкам, должен будет читать память недоступную в пользовательском режиме, а именно в диапазоне 0x80000000 - 0xffffffff (в режиме по-умолчанию, без IMAGE_FILE_LARGE_ADDRESS_AWARE). Сразу приведу исходный код драйвера который получился:
    Код (Visual Basic):
    1.  
    2. ' // modTrickMemReader.bas  - модуль драйвера
    3. ' // © Кривоус Анатолий Анатольевич (The trick), 2014
    4. Option Explicit
    5. Public Enum NT_STATUS
    6.     STATUS_SUCCESS = 0
    7.     STATUS_INVALID_PARAMETER = &HC000000D
    8. End Enum
    9. Public Type UNICODE_STRING
    10.     Length              As Integer
    11.     MaximumLength       As Integer
    12.     lpBuffer            As Long
    13. End Type
    14. Public Type LIST_ENTRY
    15.     Flink               As Long
    16.     Blink               As Long
    17. End Type
    18. Public Type KDEVICE_QUEUE
    19.     Type                As Integer
    20.     Size                As Integer
    21.     DeviceListHead      As LIST_ENTRY
    22.     Lock                As Long
    23.     Busy                As Long
    24. End Type
    25. Public Type KDPC
    26.     Type                As Byte
    27.     Importance          As Byte
    28.     Number              As Integer
    29.     DpcListEntry        As LIST_ENTRY
    30.     DeferredRoutine     As Long
    31.     DeferredContext     As Long
    32.     SystemArgument1     As Long
    33.     SystemArgument2     As Long
    34.     DpcData             As Long
    35. End Type
    36. Public Type DISPATCHER_HEADER
    37.     Lock                As Long
    38.     SignalState         As Long
    39.     WaitListHead        As LIST_ENTRY
    40. End Type
    41. Public Type KEVENT
    42.     Header              As DISPATCHER_HEADER
    43. End Type
    44. Public Type IO_STATUS_BLOCK
    45.     StatusPointer       As Long
    46.     Information         As Long
    47. End Type
    48. Public Type Tail
    49.     DriverContext(3)    As Long
    50.     Thread              As Long
    51.     AuxiliaryBuffer     As Long
    52.     ListEntry           As LIST_ENTRY
    53.     lpCurStackLocation  As Long
    54.     OriginalFileObject  As Long
    55. End Type
    56. Public Type IRP
    57.     Type                As Integer
    58.     Size                As Integer
    59.     MdlAddress          As Long
    60.     Flags               As Long
    61.     AssociatedIrp       As Long
    62.     ThreadListEntry     As LIST_ENTRY
    63.     IoStatus            As IO_STATUS_BLOCK
    64.     RequestorMode       As Byte
    65.     PendingReturned     As Byte
    66.     StackCount          As Byte
    67.     CurrentLocation     As Byte
    68.     Cancel              As Byte
    69.     CancelIrql          As Byte
    70.     ApcEnvironment      As Byte
    71.     AllocationFlags     As Byte
    72.     UserIosb            As Long
    73.     UserEvent           As Long
    74.     Overlay             As Currency
    75.     CancelRoutine       As Long
    76.     UserBuffer          As Long
    77.     Tail                As Tail
    78. End Type
    79. Public Type DEVICEIOCTL
    80.     OutputBufferLength  As Long
    81.     InputBufferLength   As Long
    82.     IoControlCode       As Long
    83.     Type3InputBuffer    As Long
    84. End Type
    85. Public Type IO_STACK_LOCATION
    86.     MajorFunction       As Byte
    87.     MinorFunction       As Byte
    88.     Flags               As Byte
    89.     Control             As Byte
    90.     ' // Поле DeviceIoControl из объединения
    91.     DeviceIoControl     As DEVICEIOCTL
    92.     pDeviceObject       As Long
    93.     pFileObject         As Long
    94.     pCompletionRoutine  As Long
    95.     pContext            As Long
    96. End Type
    97. Public Type DRIVER_OBJECT
    98.     Type                As Integer
    99.     Size                As Integer
    100.     pDeviceObject       As Long
    101.     Flags               As Long
    102.     DriverStart         As Long
    103.     DriverSize          As Long
    104.     DriverSection       As Long
    105.     DriverExtension     As Long
    106.     DriverName          As UNICODE_STRING
    107.     HardwareDatabase    As Long
    108.     FastIoDispatch      As Long
    109.     DriverInit          As Long
    110.     DriverStartIo       As Long
    111.     DriverUnload        As Long
    112.     MajorFunction(27)   As Long
    113. End Type
    114. Public Type DEVICE_OBJECT
    115.     Type                As Integer
    116.     Size                As Integer
    117.     ReferenceCount      As Long
    118.     DriverObject        As Long
    119.     NextDevice          As Long
    120.     AttachedDevice      As Long
    121.     CurrentIrp          As Long
    122.     Timer               As Long
    123.     Flags               As Long
    124.     Characteristics     As Long
    125.     Vpb                 As Long
    126.     DeviceExtension     As Long
    127.     DeviceType          As Long
    128.     StackSize           As Byte
    129.     Queue(39)           As Byte
    130.     AlignRequirement    As Long
    131.     DeviceQueue         As KDEVICE_QUEUE
    132.     Dpc                 As KDPC
    133.     ActiveThreadCount   As Long
    134.     SecurityDescriptor  As Long
    135.     DeviceLock          As KEVENT
    136.     SectorSize          As Integer
    137.     Spare1              As Integer
    138.     DeviceObjExtension  As Long
    139.     Reserved            As Long
    140. End Type
    141. Private Type BinaryString
    142.     D(255)              As Integer
    143. End Type
    144. Public Const FILE_DEVICE_UNKNOWN    As Long = &H22
    145. Public Const IO_NO_INCREMENT        As Long = &H0
    146. Public Const IRP_MJ_CREATE          As Long = &H0
    147. Public Const IRP_MJ_CLOSE           As Long = &H2
    148. Public Const IRP_MJ_DEVICE_CONTROL  As Long = &HE
    149. Public Const FILE_DEVICE_MEMREADER  As Long = &H8000&
    150. Public Const IOCTL_READ_MEMORY      As Long = &H80002000
    151. Public DeviceName       As UNICODE_STRING   ' // Строка с именем устройства
    152. Public DeviceLink       As UNICODE_STRING   ' // Строка с именем ссылки
    153. Public Device           As DEVICE_OBJECT    ' // Объект устройства
    154. Dim strName As BinaryString     ' // Строка с именем устройства
    155. Dim strLink As BinaryString     ' // Строка с именем ссылки
    156. Public Sub Main()
    157. End Sub
    158. ' // Если ошибка - False
    159. Public Function NT_SUCCESS( _
    160.                 ByVal Status As NT_STATUS) As Boolean
    161.     NT_SUCCESS = Status >= STATUS_SUCCESS
    162. End Function
    163. ' // Получить указатель на стек пакета
    164. Public Function IoGetCurrentIrpStackLocation( _
    165.                 ByRef pIrp As IRP) As Long
    166.     IoGetCurrentIrpStackLocation = pIrp.Tail.lpCurStackLocation
    167. End Function
    168. ' // Точка входа в драйвер
    169. Public Function DriverEntry( _
    170.                 ByRef DriverObject As DRIVER_OBJECT, _
    171.                 ByRef RegistryPath As UNICODE_STRING) As NT_STATUS
    172.     Dim Status As NT_STATUS
    173.  
    174.     ' // Инициализация имен
    175.     Status = Init()
    176.  
    177.     ' // Здесь не обязательна проверка, но я поставил, т.к. возможно усовершенствование функции Init
    178.     If Not NT_SUCCESS(Status) Then
    179.  
    180.         DriverEntry = Status
    181.         Exit Function
    182.      
    183.     End If
    184.  
    185.     ' // Создаем устройство
    186.     Status = IoCreateDevice(DriverObject, 0, DeviceName, FILE_DEVICE_MEMREADER, 0, False, Device)
    187.  
    188.     ' // Проверяем создалось ли устройство
    189.     If Not NT_SUCCESS(Status) Then
    190.  
    191.         DriverEntry = Status
    192.         Exit Function
    193.      
    194.     End If
    195.  
    196.     ' // Создаем связь для доступа по имени из пользовательского режима
    197.     Status = IoCreateSymbolicLink(DeviceLink, DeviceName)
    198.  
    199.     ' // Проверяем корректность
    200.     If Not NT_SUCCESS(Status) Then
    201.  
    202.         ' // При неудаче удаляем устройство
    203.         IoDeleteDevice Device
    204.         DriverEntry = Status
    205.         Exit Function
    206.      
    207.     End If
    208.  
    209.     ' // Определяем функции
    210.     DriverObject.DriverUnload = GetAddr(AddressOf DriverUnload)                                 ' // Выгрузка драйвера
    211.     DriverObject.MajorFunction(IRP_MJ_CREATE) = GetAddr(AddressOf DriverCreateClose)            ' // При вызове CreateFile
    212.     DriverObject.MajorFunction(IRP_MJ_CLOSE) = GetAddr(AddressOf DriverCreateClose)             ' // При вызове CloseHandle
    213.     DriverObject.MajorFunction(IRP_MJ_DEVICE_CONTROL) = GetAddr(AddressOf DriverDeviceControl)  ' // При вызове DeviceIoControl
    214.  
    215.     ' // Успех
    216.     DriverEntry = STATUS_SUCCESS
    217.  
    218. End Function
    219. ' // Процедура выгрузки драйвера
    220. Public Sub DriverUnload( _
    221.            ByRef DriverObject As DRIVER_OBJECT)
    222.          
    223.     ' // Удаляем связь
    224.     IoDeleteSymbolicLink DeviceLink
    225.     ' // Удаляем устройство
    226.     IoDeleteDevice ByVal DriverObject.pDeviceObject
    227.  
    228. End Sub
    229. ' // Функция вызывается при открытии/закрытии драйвера
    230. Public Function DriverCreateClose( _
    231.                 ByRef DeviceObject As DEVICE_OBJECT, _
    232.                 ByRef pIrp As IRP) As NT_STATUS
    233.              
    234.     pIrp.IoStatus.Information = 0
    235.     pIrp.IoStatus.StatusPointer = STATUS_SUCCESS
    236.  
    237.     ' // Возвращаем IRP пакет менеджеру ввода/вывода
    238.     IoCompleteRequest pIrp, IO_NO_INCREMENT
    239.  
    240.     ' // Успех
    241.     DriverCreateClose = STATUS_SUCCESS
    242.  
    243. End Function
    244.  
     
    Последнее редактирование: 3 янв 2017
    M0rg0t нравится это.
  2. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Код (Visual Basic):
    1.  
    2. ' // Функция обработки IOCTL запросов
    3. Public Function DriverDeviceControl( _
    4.                 ByRef DeviceObject As DEVICE_OBJECT, _
    5.                 ByRef pIrp As IRP) As NT_STATUS
    6.     Dim lpStack As Long
    7.     Dim ioStack As IO_STACK_LOCATION
    8.    
    9.     ' // Получаем указатель на стек пакета
    10.     lpStack = IoGetCurrentIrpStackLocation(pIrp)
    11.    
    12.     ' // Проверяем указатель на валидность
    13.     If lpStack Then
    14.    
    15.         ' // Копируем в локальную переменную
    16.         memcpy ioStack, ByVal lpStack, Len(ioStack)
    17.        
    18.         ' // Проверяем IOCTL и объединение AssociatedIrp в котором содержится SystemBuffer
    19.         ' // В SystemBuffer содержится буфер, переданный нами в DeviceIoControl
    20.         If ioStack.DeviceIoControl.IoControlCode = IOCTL_READ_MEMORY And _
    21.             pIrp.AssociatedIrp <> 0 Then
    22.             Dim lpPointer   As Long
    23.             Dim DataSize    As Long
    24.            
    25.             ' // Копируем параметы из SystemBuffer
    26.             memcpy lpPointer, ByVal pIrp.AssociatedIrp, 4
    27.             memcpy DataSize, ByVal pIrp.AssociatedIrp + 4, 4
    28.            
    29.             ' // Проверяем размер буфера
    30.             If DataSize <= ioStack.DeviceIoControl.OutputBufferLength Then
    31.            
    32.                 ' // Проверяем количество страниц, которые мы можем прочитать
    33.                 Dim lpStart As Long
    34.                 Dim pgCount As Long
    35.                 Dim pgSize  As Long
    36.                 Dim pgOfst  As Long
    37.                
    38.                 ' // Определяем адрес начала страницы
    39.                 lpStart = lpPointer And &HFFFFF000
    40.                
    41.                 ' // Определяем смещение от начала страницы
    42.                 pgOfst = lpPointer And &HFFF&
    43.                
    44.                 ' // Проход по станицам и проверка на PageFault
    45.                 Do While MmIsAddressValid(ByVal lpStart) And (pgSize - pgOfst < DataSize)
    46.                
    47.                     lpStart = lpStart + &H1000
    48.                     pgCount = pgCount + 1
    49.                     pgSize = pgSize + &H1000
    50.                    
    51.                 Loop
    52.                
    53.                 ' // Если хоть одна страница доступна
    54.                 If pgCount Then
    55.                
    56.                     ' // Получаем реальный размер в байтах
    57.                     pgSize = pgCount * &H1000 - pgOfst
    58.                    
    59.                     ' // Корректируем резмер
    60.                     If DataSize > pgSize Then DataSize = pgSize
    61.                    
    62.                     ' // Возвращаем реальный размер прочитанных данных
    63.                     pIrp.IoStatus.Information = DataSize
    64.                    
    65.                     ' // Успех
    66.                     pIrp.IoStatus.StatusPointer = STATUS_SUCCESS
    67.                    
    68.                     ' // Копируем данные в SystemBuffer
    69.                     memcpy ByVal pIrp.AssociatedIrp, ByVal lpPointer, DataSize
    70.                    
    71.                     ' // Возвращаем IRP пакет менеджеру ввода/вывода
    72.                     IoCompleteRequest pIrp, IO_NO_INCREMENT
    73.                    
    74.                     ' // Упех
    75.                     DriverDeviceControl = STATUS_SUCCESS
    76.                    
    77.                     ' // Выход
    78.                     Exit Function
    79.    
    80.                 End If
    81.                
    82.             End If
    83.    
    84.         End If
    85.        
    86.     End If
    87.    
    88.     ' // Возвращаем реальный размер прочитанных данных
    89.     pIrp.IoStatus.Information = 0
    90.    
    91.     ' // Ошибка DeviceIoControl
    92.     pIrp.IoStatus.StatusPointer = STATUS_INVALID_PARAMETER
    93.    
    94.     ' // Возвращаем IRP пакет менеджеру ввода/вывода
    95.     IoCompleteRequest pIrp, IO_NO_INCREMENT
    96.    
    97.     ' // Ошибка
    98.     DriverDeviceControl = STATUS_INVALID_PARAMETER
    99.    
    100. End Function
    101. ' // Функция инициализации
    102. Private Function Init() As NT_STATUS
    103.     ' // Инициализируем имя устройства
    104.     ' //\Device\TrickMemReader
    105.     strName.D(0) = &H5C:    strName.D(1) = &H44:    strName.D(2) = &H65:    strName.D(3) = &H76:    strName.D(4) = &H69:
    106.     strName.D(5) = &H63:    strName.D(6) = &H65:    strName.D(7) = &H5C:    strName.D(8) = &H54:    strName.D(9) = &H72:
    107.     strName.D(10) = &H69:   strName.D(11) = &H63:   strName.D(12) = &H6B:   strName.D(13) = &H4D:   strName.D(14) = &H65:
    108.     strName.D(15) = &H6D:   strName.D(16) = &H52:   strName.D(17) = &H65:   strName.D(18) = &H61:   strName.D(19) = &H64:
    109.     strName.D(20) = &H65:   strName.D(21) = &H72:
    110.    
    111.     ' // Создаем UNICODE_STRING
    112.     RtlInitUnicodeString DeviceName, strName
    113.    
    114.     ' // Инициализация ссылки на имя устройства из user-mode
    115.     ' //\DosDevices\TrickMemReader
    116.     strLink.D(0) = &H5C:    strLink.D(1) = &H44:    strLink.D(2) = &H6F:    strLink.D(3) = &H73:    strLink.D(4) = &H44:
    117.     strLink.D(5) = &H65:    strLink.D(6) = &H76:    strLink.D(7) = &H69:    strLink.D(8) = &H63:    strLink.D(9) = &H65:
    118.     strLink.D(10) = &H73:   strLink.D(11) = &H5C:   strLink.D(12) = &H54:   strLink.D(13) = &H72:   strLink.D(14) = &H69:
    119.     strLink.D(15) = &H63:   strLink.D(16) = &H6B:   strLink.D(17) = &H4D:   strLink.D(18) = &H65:   strLink.D(19) = &H6D:
    120.     strLink.D(20) = &H52:   strLink.D(21) = &H65:   strLink.D(22) = &H61:   strLink.D(23) = &H64:   strLink.D(24) = &H65:
    121.     strLink.D(25) = &H72:
    122.    
    123.     ' // Создаем UNICODE_STRING
    124.     RtlInitUnicodeString DeviceLink, strLink
    125. End Function
    126. Private Function GetAddr( _
    127.                  ByVal Value As Long) As Long
    128.     GetAddr = Value
    129. End Function
    Итак, драйвер должен иметь точку входа DriverEntry, которую вызывает диспетчер ввода/вывода при загрузке драйвера. В параметрах передается указатель на объект-драйвер и указатель на строку с именем ключа реестра, соответствующего загружаемому драйверу. В процедуре Init мы создаем 2 строки, одна с названием устройства, другая с названием ссылки на устройство. Т.к. мы не можем использовать рантайм в режиме ядра, то приходится создавать строку в виде статического массива, обернутого в пользовательский тип, тем самым VB6 выделяет память под этот массив в стеке. Если использовать строку, то неизбежно будет вызвана одна из функций рантайма для копирования и присваивания строки, а этого мы допустить не можем. Далее мы вызываем IoCreateDevice, которая создает объект-устройство. Объект-устройство является получателем запросов ввода/вывода и к нему мы будем получать доступ при вызове функции CreateFile из пользовательского режима. В качестве первого параметра передается указатель на объект-драйвера; вторым параметром передаем 0, т.к. у нас нет структуры расширения устройства и нам не нужно выделять память; третьим параметром мы передаем имя устройства, оно нам понадобится для реализации доступа к устройству; четвертым параметром передается тип устройства (см. ниже); в пятом мы передаем 0, т.к. у нас "нестандартное устройство"; в шестом передаем False, т.к. нам не нужен монопольный режим; последний параметр - выходной. В качестве имени устройства мы должны использовать строку вида \Device\DeviceName (где DeviceName - TrickMemReader), это имя нам понадобится для того, чтобы мы могли создать ссылку на него, которая в свою очередь нужна для доступа к устройству из пользовательского режима. Тип устройства у нас - FILE_DEVICE_MEMREADER. Все нестандартные устройства должны иметь тип либо FILE_DEVICE_UNKNOWN, либо число от 0x8000 - 0xffff. Я создал константу FILE_DEVICE_MEMREADER со значением 0x8000, что соответствует первому свободному номеру. При успехе, создается устройство и заполняется структура DEVICE_OBJECT. После нужно создать связь по имени между устройством из режима ядра и пользовательским режимом. В качестве имени мы используем \DosDevices\TrickMemReader, из пользовательского режима мы будем обращаться к нему через ссылку '\\.\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]
     
  3. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Итак, в функции 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, для того чтобы они попали в импорт:
    Код (Text):
    1. [uuid(0000001F-0000-0000-0000-000000000AAB)]
    2. library ImportFunctionsForTrickMemReaderDriver
    3. {
    4. [dllname("Ntoskrnl.exe")]
    5. module Ntoskrnl
    6. {
    7.     [entry("IoCreateDevice")]int IoCreateDevice
    8. (void *DriverObject,
    9. int DeviceExtensionSize,
    10. void *DeviceName,
    11. int DeviceType,
    12. int DeviceCharacteristics,
    13. int Exclusive,
    14. void *DeviceObject);
    15.     [entry("IoCreateSymbolicLink")]int IoCreateSymbolicLink
    16. (void *SymbolicLinkName,
    17. void *DeviceName);
    18.     [entry("IoDeleteDevice")]void IoDeleteDevice
    19. (void *DeviceObject);
    20.     [entry("IoDeleteSymbolicLink")]int IoDeleteSymbolicLink
    21. (void *SymbolicLinkName);
    22.     [entry("IoCompleteRequest")]void IoCompleteRequest
    23. (void *pIrp,
    24. unsigned char PriorityBoost);
    25.     [entry("RtlInitUnicodeString")]int RtlInitUnicodeString
    26. (void *UnicodeString,
    27. void *StringPtr);
    28.     [entry("RtlMoveMemory")]void memcpy
    29. (void *Destination,
    30. void *Source,
    31. int Length);
    32.     [entry("MmIsAddressValid")]unsigned char MmIsAddressValid
    33. (void *VirtualAddress);
    34.     [entry("InterlockedExchange")]int InterlockedExchange
    35. (void *Target,
    36. void *Value);
    37. }
    38. }
    PS. InterlockedExchange - я оставил, т.к. вначале драйвер имел немного другую структуру, в последствии оставил объявление в TLB. В драйвере она не попадет в импорт.
    Для того чтобы драйвер работал нужно сделать три вещи:
    1. В поле Subsystem структуры IMAGE_OPTIONAL_HEADER PE-файла драйвера должно быть значение IMAGE_SUBSYSTEM_NATIVE что соответствует драйверу режима ядра.
    2. Указать в качестве точки входа нашу процедуру DriverEntry
    3. Добавить секцию релокации, для того чтобы драйвер мог загружаться по любому адресу.
    4. Исключить MSVBVM60 из иморта.
    Для первых 3-х пунктов добавляем ключи компиляции в vbp-файл, со следующим содержимым:
    Код (Text):
    1. [VBCompiler]
    2. LinkSwitches= /ENTRY:DriverEntry /SUBSYSTEM:NATIVE /FIXED:NO
    Компилируем проект со всеми опциями оптимизации. Для исключения рантайма из импорта, я использую свою утилиту Patch, которую я использовал тут. После исключения из импорта библиотеки контрольная сумма поменялась, а я не обновлял ее. В EXE файлах, DLL и т.д. это поле не проверяется, а в драйверах проверяется. Для проверки смотрим импорт в любой программе для просмотра PE:
    [​IMG]
    Как видим рантайма нет. Что нам и требовалось.
     
  4. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Для тестирования драйвера я написал простую программку, которая загружает драйвер и работает с ним.
    Код (Visual Basic):
    1. ' // frmTestTrickVBDriver.frm  - форма для тестирования драйвера
    2. ' // © Кривоус Анатолий Анатольевич (The trick), 2014
    3. Option Explicit
    4. Private Type SERVICE_STATUS
    5.     dwServiceType               As Long
    6.     dwCurrentState              As Long
    7.     dwControlsAccepted          As Long
    8.     dwWin32ExitCode             As Long
    9.     dwServiceSpecificExitCode   As Long
    10.     dwCheckPoint                As Long
    11.     dwWaitHint                  As Long
    12. End Type
    13. Private Declare Function ControlService Lib "advapi32.dll" ( _
    14.                          ByVal hService As Long, _
    15.                          ByVal dwControl As Long, _
    16.                          ByRef lpServiceStatus As SERVICE_STATUS) As Long
    17. Private Declare Function OpenSCManager Lib "advapi32.dll" _
    18.                          Alias "OpenSCManagerW" ( _
    19.                          ByVal lpMachineName As Long, _
    20.                          ByVal lpDatabaseName As Long, _
    21.                          ByVal dwDesiredAccess As Long) As Long
    22. Private Declare Function CloseServiceHandle Lib "advapi32.dll" ( _
    23.                          ByVal hSCObject As Long) As Long
    24. Private Declare Function OpenService Lib "advapi32.dll" _
    25.                          Alias "OpenServiceW" ( _
    26.                          ByVal hSCManager As Long, _
    27.                          ByVal lpServiceName As Long, _
    28.                          ByVal dwDesiredAccess As Long) As Long
    29. Private Declare Function CreateService Lib "advapi32.dll" _
    30.                          Alias "CreateServiceW" ( _
    31.                          ByVal hSCManager As Long, _
    32.                          ByVal lpServiceName As Long, _
    33.                          ByVal lpDisplayName As Long, _
    34.                          ByVal dwDesiredAccess As Long, _
    35.                          ByVal dwServiceType As Long, _
    36.                          ByVal dwStartType As Long, _
    37.                          ByVal dwErrorControl As Long, _
    38.                          ByVal lpBinaryPathName As Long, _
    39.                          ByVal lpLoadOrderGroup As String, _
    40.                          ByRef lpdwTagId As Long, _
    41.                          ByVal lpDependencies As Long, _
    42.                          ByVal lp As Long, _
    43.                          ByVal lpPassword As Long) As Long
    44. Private Declare Function StartService Lib "advapi32.dll" _
    45.                          Alias "StartServiceW" ( _
    46.                          ByVal hService As Long, _
    47.                          ByVal dwNumServiceArgs As Long, _
    48.                          ByVal lpServiceArgVectors As Long) As Long
    49. Private Declare Function DeleteService Lib "advapi32.dll" ( _
    50.                          ByVal hService As Long) As Long
    51. Private Declare Function CreateFile Lib "kernel32" _
    52.                          Alias "CreateFileW" ( _
    53.                          ByVal lpFileName As Long, _
    54.                          ByVal dwDesiredAccess As Long, _
    55.                          ByVal dwShareMode As Long, _
    56.                          ByRef lpSecurityAttributes As Any, _
    57.                          ByVal dwCreationDisposition As Long, _
    58.                          ByVal dwFlagsAndAttributes As Long, _
    59.                          ByVal hTemplateFile As Long) As Long
    60. Private Declare Function CloseHandle Lib "kernel32" ( _
    61.                          ByVal hObject As Long) As Long
    62. Private Declare Function DeviceIoControl Lib "kernel32" ( _
    63.                          ByVal hDevice As Long, _
    64.                          ByVal dwIoControlCode As Long, _
    65.                          ByRef lpInBuffer As Any, _
    66.                          ByVal nInBufferSize As Long, _
    67.                          ByRef lpOutBuffer As Any, _
    68.                          ByVal nOutBufferSize As Long, _
    69.                          ByRef lpBytesReturned As Long, _
    70.                          ByRef lpOverlapped As Any) As Long
    71. Private Const ERROR_SERVICE_ALREADY_RUNNING As Long = 1056&
    72. Private Const ERROR_SERVICE_EXISTS          As Long = 1073&
    73. Private Const SERVICE_CONTROL_STOP          As Long = &H1
    74. Private Const SC_MANAGER_ALL_ACCESS         As Long = &HF003F
    75. Private Const SERVICE_ALL_ACCESS            As Long = &HF01FF
    76. Private Const SERVICE_KERNEL_DRIVER         As Long = &H1
    77. Private Const SERVICE_DEMAND_START          As Long = &H3
    78. Private Const SERVICE_ERROR_NORMAL          As Long = &H1
    79. Private Const GENERIC_READ                  As Long = &H80000000
    80. Private Const GENERIC_WRITE                 As Long = &H40000000
    81. Private Const FILE_SHARE_READ               As Long = &H1
    82. Private Const FILE_SHARE_WRITE              As Long = &H2
    83. Private Const OPEN_EXISTING                 As Long = 3
    84. Private Const FILE_ATTRIBUTE_NORMAL         As Long = &H80
    85. Private Const INVALID_HANDLE_VALUE          As Long = -1
    86. Private Const IOCTL_READ_MEMORY             As Long = &H80002000
    87. Private Const DriverName    As String = "TrickMemReader"
    88. Private Const NumOfRows     As Long = 32
    89. Private DriverFile  As String
    90. Private hMgr        As Long
    91. Private hSrv        As Long
    92. Private hDev        As Long
    93. Private buffer()    As Byte
    94. Private bufLen      As Long
    95. Private Address     As Long
    96. ' // Нажатие на кнопку чтения памяти по адресу
    97. Private Sub cmdRead_Click()
    98.     Dim param(1)    As Long
    99.    
    100.     On Error GoTo Cancel
    101.    
    102.     Address = CLng("&H" & Trim(txtAddress.Text))
    103.    
    104.     ' // Формируем параметры
    105.     param(0) = Address
    106.     param(1) = 16 * NumOfRows
    107.    
    108.     ' // Делаем запрос драйверу
    109.     If DeviceIoControl(hDev, IOCTL_READ_MEMORY, param(0), 8, buffer(0), UBound(buffer) + 1, bufLen, ByVal 0&) = 0 Then
    110.         bufLen = 0
    111.     End If
    112.    
    113.     Update
    114.    
    115. Cancel:
    116.    
    117. End Sub
    118. Private Sub Form_Load()
    119.     Dim sw  As Long
    120.     Dim sh  As Long
    121.    
    122.     ' // Выделяем буфер
    123.     ReDim buffer(16 * NumOfRows - 1)
    124.    
    125.     ' // Получаем имя файла драйвера
    126.     DriverFile = App.Path & "\" & DriverName & ".sys"
    127.    
    128.     ' // Открываем БД диспетчера управления службами
    129.     hMgr = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS)
    130.    
    131.     If hMgr = 0 Then
    132.         MsgBox "Не удалось установить связь с диспетчером управления службами"
    133.         End
    134.     End If
    135.    
    136.     ' // Создаем службу
    137.     hSrv = CreateService(hMgr, StrPtr(DriverName), StrPtr(DriverName), SERVICE_ALL_ACCESS, _
    138.                         SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, StrPtr(DriverFile), _
    139.                         0, 0, 0, 0, 0)
    140.     ' // Если служба уже запущена
    141.     If hSrv = 0 And Err.LastDllError = ERROR_SERVICE_EXISTS Then
    142.         ' // Открываем службу
    143.         hSrv = OpenService(hMgr, StrPtr(DriverName), SERVICE_ALL_ACCESS)
    144.     End If
    145.     If hSrv = 0 Then
    146.         MsgBox "Не удалось создать службу"
    147.         Unload Me
    148.         End
    149.     End If
    150.    
    151.     ' // Запускаем драйвер
    152.     If StartService(hSrv, 0, 0) = 0 Then
    153.        
    154.         If Err.LastDllError <> ERROR_SERVICE_ALREADY_RUNNING Then
    155.             MsgBox "Не удалось запустить службу"
    156.             Unload Me
    157.             End
    158.         End If
    159.        
    160.     End If
    161.    
    162.     ' // Подключаемся к драйверу
    163.     hDev = CreateFile(StrPtr("\\.\" & DriverName), GENERIC_READ Or FILE_SHARE_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, _
    164.                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
    165.     If hDev = INVALID_HANDLE_VALUE Then
    166.         MsgBox "Не возможно подключится к драйверу"
    167.         Unload Me
    168.         End
    169.     End If
    170.    
    171.     ' // Определяем положение и размер контролов и формы
    172.     sw = picDump.TextWidth("_")
    173.     sh = picDump.TextHeight("_")
    174.    
    175.     picDump.Move 5, 5, (sw * 77) + (picDump.Width - picDump.ScaleWidth), (sh * NumOfRows) + (picDump.Height - picDump.ScaleHeight)
    176.    
    177.     lblAddress.Top = picDump.Top + picDump.Height + 5
    178.     txtAddress.Top = lblAddress.Top
    179.     cmdRead.Top = txtAddress.Top
    180.    
    181.     Me.Width = (picDump.Width + 10 - Me.ScaleWidth) * Screen.TwipsPerPixelX + Me.Width
    182.     Me.Height = (txtAddress.Top + 5 + txtAddress.Height - Me.ScaleHeight) * Screen.TwipsPerPixelY + Me.Height
    183.    
    184.     Update
    185.    
    186. End Sub
    187.  
     
  5. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Код (Visual Basic):
    1.  
    2. ' // Обновить данные в окне
    3. Private Sub Update()
    4.     Dim col As Long
    5.     Dim row As Long
    6.     Dim ptr As Long
    7.     Dim hxd As String
    8.     Dim asi As String
    9.     Dim adr As String
    10.     Dim out As String
    11.  
    12.     For row = 0 To NumOfRows - 1
    13.         adr = Hex(Address + row * 16)
    14.         adr = String(8 - Len(adr), "0") & adr
    15.         asi = ""
    16.         hxd = ""
    17.      
    18.         For col = 0 To 15
    19.          
    20.             If ptr < bufLen Then
    21.              
    22.                 hxd = hxd & " " & IIf(buffer(ptr) < &H10, "0" & Hex(buffer(ptr)), Hex(buffer(ptr)))
    23.                 asi = asi & IIf(buffer(ptr) >= 32, Chr$(buffer(ptr)), "?")
    24.              
    25.             Else
    26.                 hxd = hxd & " ??"
    27.                 asi = asi & "?"
    28.              
    29.             End If
    30.          
    31.             ptr = ptr + 1
    32.          
    33.         Next
    34.      
    35.         If row Then out = out & vbNewLine
    36.      
    37.         out = out & adr & ":" & hxd & " | " & asi
    38.      
    39.     Next
    40.  
    41.     picDump.Cls
    42.     picDump.Print out
    43.  
    44. End Sub
    45. Private Sub Form_Unload(Cancel As Integer)
    46.     Dim Status As SERVICE_STATUS
    47.  
    48.     ' // Отключаемся от драйвера
    49.     CloseHandle hDev
    50.  
    51.     ' // Останавливаем драйвер
    52.     ControlService hSrv, SERVICE_CONTROL_STOP, Status
    53.  
    54.     ' // Удаляем службу
    55.     DeleteService hSrv
    56.  
    57.     ' // Закрываем описатели
    58.     CloseServiceHandle hSrv
    59.     CloseServiceHandle hMgr
    60.  
    61. 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 можно написать простой драйвер, а если использовать ассемблерные вставки можно и посерьезнее что-нибудь написать. Всем спасибо за внимание. Удачи!
     

    Вложения:

    M0rg0t нравится это.
  6. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.348
    Это уже какая-то нездоровая любовь к бейсику :)
     
  7. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Этим я разрушил сразу несколько мифов о vb6.