Форум программистов, компьютерный форум, киберфорум
The trick
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  

Драйвер на VB6

Запись от The trick размещена 15.11.2014 в 21:59
Показов 8011 Комментарии 4


Всем привет. Появилось время и решил написать что-то необычное на VB6, а именно попытаться написать драйвер. Сразу скажу до этого я никогда не писал драйвера и не имею никакого опыта программирования в режиме ядра. Драйвер, по моим задумкам, должен будет читать память недоступную в пользовательском режиме, а именно в диапазоне 0x80000000 - 0xffffffff (в режиме по-умолчанию, без IMAGE_FILE_LARGE_ADDRESS_AWARE). Сразу приведу исходный код драйвера который получился:
Visual Basic Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
' 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
    ' Инициализируем имя устройства
    '\Device\TrickMemReader
    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
    '\DosDevices\TrickMemReader
    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, т.к. нам не нужен монопольный режим; последний параметр - выходной. В качестве имени устройства мы должны использовать строку вида \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. Наглядно:
Нажмите на изображение для увеличения
Название: IOCTL.png
Просмотров: 817
Размер:	60.2 Кб
ID:	2842
Итак, в функции 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, для того чтобы они попали в импорт:
C Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[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-файл, со следующим содержимым:
Code Скопировано
1
2
[VBCompiler]
LinkSwitches= /ENTRY:DriverEntry /SUBSYSTEM:NATIVE /FIXED:NO
Компилируем проект со всеми опциями оптимизации. Для исключения рантайма из импорта, я использую свою утилиту Patch, которую я использовал тут. Я немного доработал ее, т.к. изначально не мог запустить драйвер и долго ломал голову из-за чего это происходит, а причина была в контрольной сумме. После исключения из импорта библиотеки контрольная сумма поменялась, а я не обновлял ее. В EXE файлах, DLL и т.д. это поле не проверяется, а в драйверах проверяется. Для проверки смотрим импорт в любой программе для просмотра PE:
Нажмите на изображение для увеличения
Название: Import.png
Просмотров: 602
Размер:	18.9 Кб
ID:	2843
Как видим рантайма нет. Что нам и требовалось.
Для тестирования драйвера я написал простую программку, которая загружает драйвер и работает с ним.
Visual Basic Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
' 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 hDev = INVALID_HANDLE_VALUE Then
        MsgBox "Не возможно подключится к драйверу"
        Unload Me
        End
    End If
    
    ' Определяем положение и размер контролов и формы
    sw = picDump.TextWidth("_")
    sh = picDump.TextHeight("_")
    
    picDump.Move 5, 5, (sw * 77) + (picDump.Width - picDump.ScaleWidth), (sh * NumOfRows) + (picDump.Height - picDump.ScaleHeight)
    
    lblAddress.Top = picDump.Top + picDump.Height + 5
    txtAddress.Top = lblAddress.Top
    cmdRead.Top = txtAddress.Top
    
    Me.Width = (picDump.Width + 10 - Me.ScaleWidth) * Screen.TwipsPerPixelX + Me.Width
    Me.Height = (txtAddress.Top + 5 + txtAddress.Height - Me.ScaleHeight) * Screen.TwipsPerPixelY + Me.Height
    
    Update
    
End Sub
 
' // Обновить данные в окне
Private Sub Update()
    Dim col As Long
    Dim row As Long
    Dim ptr As Long
    Dim hxd As String
    Dim asi As String
    Dim adr As String
    Dim out As String
    
    For row = 0 To NumOfRows - 1
 
        adr = Hex(Address + row * 16)
        adr = String(8 - Len(adr), "0") & adr
        asi = ""
        hxd = ""
        
        For col = 0 To 15
            
            If ptr < bufLen Then
                
                hxd = hxd & " " & IIf(buffer(ptr) < &H10, "0" & Hex(buffer(ptr)), Hex(buffer(ptr)))
                asi = asi & IIf(buffer(ptr) >= 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. Система остановится, и мы перейдем в окно отладчика:
Нажмите на изображение для увеличения
Название: Syser1.png
Просмотров: 982
Размер:	55.5 Кб
ID:	2849
Мы находимся в начале функции DriverEntry. Первый CALL соответствует вызову функции Init. Если мы проследим пошагово (F8) что там внутри, то увидим как заполняется структура и вызывается RtlInitUnicodeString для имени устройства и символической связи. Второй CALL соответствует функции NT_SUCCESS, смотрим что она возвращает TRUE (в регистре EAX) и код прыгает после проверки (TEST EAX, EAX) на ноль (False) дальше:
Нажмите на изображение для увеличения
Название: Syser2.png
Просмотров: 797
Размер:	11.5 Кб
ID:	2846
Как видно код заталкивает в стек параметры для функции IoCreateDevice от последнего к первому с помощью PUSH'ей. Начнем проверку параметров. Проверим имя устройства (3-й параметр - PUSH 0f8a2c010), для этого введем команду d 0f8a2c010 (что значит просмотреть дамп памяти по адресу f8a2c010 и смотрим содержимое:
Нажмите на изображение для увеличения
Название: Syser3.png
Просмотров: 579
Размер:	1.8 Кб
ID:	2847
первые 8 байт - это наша переменная DeviceName. Первые два слова - соответственно длина строки и максимальная длина строки в байтах. Следующее двойное слово - указатель на строку, смотрим (d f8a2c0d8 учитываем порядок байтов little-endian):
Нажмите на изображение для увеличения
Название: Syser4.png
Просмотров: 638
Размер:	2.9 Кб
ID:	2848
, то что нужно там 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:
Нажмите на изображение для увеличения
Название: Syser5.png
Просмотров: 942
Размер:	45.7 Кб
ID:	2850
При успехе идет прыжок на 0xf8a2b7bf, где идет заталкивание в стек параметров для функции IoCreateSymbolicLink. Параметр DeviceName мы уже проверяли, проверяем DeviceLink:
Нажмите на изображение для увеличения
Название: Syser6.png
Просмотров: 736
Размер:	3.5 Кб
ID:	2851
То что нужно. Жмем F10, тестируем EAX, при успехе идем дальше при неудаче, удаляем девайс и выходим с ошибкой. Процедура по адресу 0xf8a2bbb0 - это GetAddr, которая просто возвращает переданное ей значение:
Нажмите на изображение для увеличения
Название: Syser7.png
Просмотров: 722
Размер:	1.6 Кб
ID:	2852
Дальше идет копирование адресов по смещениям DriverObject, если посмотреть деларации то можно увидеть что по смещению 0x34 записывается адрес DriverUnload, по смещению 0x38 - MajorFunction(0) и т.д. Записываемые значения соответствуют адресам функций в нашем драйвере. Дальше происходит обнуление EAX (возвращаемой значение) и выход из процедуры DriverEntry. Все работает без ошибок, идем дальше. Итак, чтобы отследить работу драйвера мы поставим точку останова на функцию DriverDeviceControl. Адрес ее можно взять по только что записанным смещениям в структуре DRIVER_OBJECT либо найти простым просмотром и анализированием кода. В моем тесте адрес равен 0xf8a2b870, переходим на него (. 0xf8a2b870) и нажимаем F9, ставя точку останова. Напротив инструкции установится маркер:
Нажмите на изображение для увеличения
Название: Syser8.png
Просмотров: 731
Размер:	5.7 Кб
ID:	2853
Теперь при вызове этой функции отладчик остановит выполнение кода и даст нам возможность пошагово выполнить код. Функции DriverCreateClose и DriverUnload я не буду описывать, т.к. там все просто. Жмем F5, тем самым продолжая выполнение в обычном режиме. Нас тут же переносит обратно в Windows. Теперь мы запускаем наше тестовое приложение, вводим какой-нибудь адрес (например 81234567) и жмем на кнопку Read. Наш вызов перехватывает отладчик и мы можем продолжить тестировать код функции DriverDeviceControl.
Подробно внутри я не буду описывать код, остановлюсь на самом копировании:
Нажмите на изображение для увеличения
Название: Syser9.png
Просмотров: 850
Размер:	13.5 Кб
ID:	2854
Сразу смотрим на стек (регистр ESP), видим что передаются правильные параметры. На всякий случай делаем дамп, потом сравним:
Нажмите на изображение для увеличения
Название: Syser10.png
Просмотров: 884
Размер:	5.8 Кб
ID:	2855
Нажимаем F5 - и возвращаемся в Windows. Смотрим на дамп уже в нашей программе:
Нажмите на изображение для увеличения
Название: Syser11.png
Просмотров: 762
Размер:	36.2 Кб
ID:	2856
Как видим все отлично скопировалось. Попробуем скопировать данные на границе страниц, так чтобы одна страница отсутствовала. Экспериментальным методом была найдена такая страница вот что получаем:
Нажмите на изображение для увеличения
Название: Syser12.png
Просмотров: 918
Размер:	21.0 Кб
ID:	2857
Как видим, что данные скопировались корректно, где не получилось там у нас отображаются вопросительные знаки. В выходном параметре DeviceIoControl у нас возвращается количество реально прочитанных байт, его мы и используем для отображения вопросительных знаков.
________________________________________ _____________________________________
Как видим на VB6 можно написать простой драйвер, а если использовать ассемблерные вставки можно и посерьезнее что-нибудь написать. Всем спасибо за внимание. Удачи!
Вложения
Тип файла: rar TrickVB6Driver.rar (39.5 Кб, 511 просмотров)
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 4
Комментарии
  1. Старый комментарий
    Аватар для Catstail
    Великолепно! Нет слов...
    Запись от Catstail размещена 15.11.2014 в 22:45 Catstail вне форума
  2. Старый комментарий
    Аватар для NickoTin
    Сурово
    Без строк весьма грустно, но можно запилить препроцессор, который будет заменять строки на массив байт
    Запись от NickoTin размещена 16.11.2014 в 16:02 NickoTin вне форума
  3. Старый комментарий
    Цитата Сообщение от NickoTin
    Сурово
    Без строк весьма грустно, но можно запилить препроцессор, который будет заменять строки на массив байт
    Visual Basic Скопировано
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    Private Sub CreateName(sName As String)
        Dim i As Long
        Dim q   As String
        Dim o   As String
     
     
        For i = 1 To Len(sName)
            q = Space(18)
            LSet q = "S.D(" & i - 1 & ") =  &H" & Hex(AscW(Mid$(sName, i, 1))) & ":"
            If ((i - 1) Mod 5) = 0 Then o = o & vbNewLine & q Else o = o & q
        Next
     
        Debug.Print o
        
    End Sub
    Запись от The trick размещена 16.11.2014 в 20:57 The trick вне форума
  4. Старый комментарий
    Анатолий "Trick" большое спасибо за отклик, мне бы теперь переварить, да голову не сломать.
    Запись от ARTZ размещена 27.04.2015 в 17:28 ARTZ вне форума
 
Новые блоги и статьи
Замыкания в Python
py-thonny 09.04.2025
Что такое замыкание? В простейшем определении, замыкание - это функция, которая запоминает и хранит доступ к переменным из охватывающей её области видимости, даже когда эта функция вызывается за. . .
Реализация Event-Driven архитектуры с RabbitMQ и Kafka в Nest.js
ArchitectMsa 09.04.2025
В монолитных системах сервисы обычно общаются напрямую через HTTP-запросы. Простой подход, удобный для начала разработки — но что происходит, когда система растёт? Синхронное взаимодействие быстро. . .
CI/CD для Python с GitHub Actions
Mr. Docker 09.04.2025
CI/ CD для Python-разработчиков – это насущная необходимость. Представьте: вы пишете код, запускаете тесты, собираете пакет, отправляете его в репозиторий, развёртываете приложение. А теперь умножьте. . .
Статическое и динамическое связывание в C++
bytestream 09.04.2025
Связывание в C++ — одна из тех "невидимых" технических сторон программирования, о которой многие имеют лишь поверхностное представление, хотя эта концепция критически влияет на производительность,. . .
Многопоточность в C#: Мониторы в синхронизации потоков
UnmanagedCoder 09.04.2025
Многопоточное программирование в C# — мощный инструмент, позволяющий использовать преимущества современных многоядерных процессоров и создавать отзывчивые приложения. Однако наряду с преимуществами,. . .
BASH scripting - the best cases [PurpleSchool]
jigi33 08.04.2025
Занятия BASH в PurpleSchool - отличные примеры для внедрения в практику (see screenshots and file names)
Результаты исследования от команды MCM (март 2025 г.)
Programma_Boinc 07.04.2025
Результаты исследования от команды MCM (март 2025 г. ) В рамках наших текущих исследований мы продолжаем изучать гены, которые имеют наибольшую вероятность развития рака легких, выявленные в рамках. . .
Рекурсивные типы в Python
py-thonny 07.04.2025
Рекурсивные типы - это типы данных, которые определяются через самих себя или в сочетании с другими типами, которые в свою очередь ссылаются на исходный тип. В мире программирования такие структуры. . .
C++26: Объединение и конкатенация последовательностей и диапазонов в std::ranges
NullReferenced 07.04.2025
Работа с последовательностями данных – одна из фундаментальных задач, с которой сталкивается каждый разработчик. C++ прошел длинный путь в эволюции средств для манипуляции коллекциями – от. . .
Обмен данными в микросервисной архитектуре
ArchitectMsa 06.04.2025
Когда разработчики начинают погружаться в мир микросервисов, они часто сталкиваются с парадоксальным правилом: "два сервиса не должны делить один источник данных". Эта мантра звучит повсюду в. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru
Выделить код Копировать код Сохранить код Нормальный размер Увеличенный размер