Hi,
I can't figure out how I can call KeLowerIrql - it doesn't seem to know what I'm on about, and MSDN doesn't offer a hint as to where I may find it, less how to use it.
How can I change the IRQL in the driver?
I've also got the problem that it sometimes BSODs with IRQL_NOT_LESS_OR_EQUAL but I can't figure out quite why. If I can force the IRQL down I think it will solve the problem.
Complete driver code follows. I'm installing it as KERNEL_DRIVER_SERVICE. I've been using http://four-f.webs.com/ for guidance.
It currently does nothing but create virtual device and a symbolic link, process one custom IRP, and unload. It is unstable though. :(
I figure it is the IRP processing that is going awry.
.386
.model flat, stdcall
option casemap:none
include \masm32\include\w2k\ntstatus.inc
include \masm32\include\w2k\ntddk.inc
include \masm32\include\w2k\ntoskrnl.inc
include \masm32\include\w2k\w2kundoc.inc
includelib \masm32\lib\w2k\ntoskrnl.lib
include \masm32\Macros\Strings.mac
.data?
CurrIRQL DWORD ?
.data
;DRVR DWORD 0000000000100010-11-100000000001-11 / 0x22-0x3-0x801-0x3 / FILE_DEVICE_UNKNOWN...READ/WRITE...FUNC CODE...BUFFER NEITHER
DRVR DWORD 00000000001000101110000000000111b
CCOUNTED_UNICODE_STRING "\\Device\\devTestDriver", DeviceName, 4 ; Driver name
CCOUNTED_UNICODE_STRING "\\??\\TestDriver", SymLink, 4 ; Symbolic link used by apps. Use \\DosDevice\ for older build
.code
DispatchControl proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
LOCAL status:NTSTATUS
LOCAL dwBytesReturned:DWORD
and dwBytesReturned, 0
mov esi, pIrp
ASSUME esi:PTR _IRP
IoGetCurrentIrpStackLocation esi
mov edi, eax
ASSUME edi:PTR IO_STACK_LOCATION
mov eax,[edi].Parameters.DeviceIoControl.IoControlCode
cmp eax,DRVR ; IOCTL request
jne NO_PROCESSING
;---------------------------------------
mov dwBytesReturned,0
mov status,STATUS_SUCCESS
jmp CONT
NO_PROCESSING:
mov dwBytesReturned,0
mov status,STATUS_PENDING
ASSUME esi:PTR _IRP
ASSUME edi:NOTHING
push status
pop [esi].IoStatus.Status
push dwBytesReturned
pop [esi].IoStatus.Information
IoMarkIrpPending esi
assume esi:nothing
mov eax,status
ret 8
;===========================================================
CONT:
ASSUME esi:PTR _IRP
ASSUME edi:NOTHING
push status
pop [esi].IoStatus.Status
push dwBytesReturned
pop [esi].IoStatus.Information
assume esi:nothing
push IO_NO_INCREMENT
push pIrp
call IoCompleteRequest
mov eax, status
ret 8
DispatchControl endp
DispatchCreateClose proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
mov eax, pIrp
assume eax:ptr _IRP
mov [eax].IoStatus.Status, STATUS_SUCCESS
and [eax].IoStatus.Information, 0
assume eax:nothing
;---------------------------------------------
; FASTCALL! first two params - L->R ecx and
; edx, then the rest, R->L on the stack
mov edx,IO_NO_INCREMENT
mov ecx,pIrp
call IofCompleteRequest
;fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT
mov eax, STATUS_SUCCESS
ret 8
DispatchCreateClose endp
DriverUnload proc pDriverObject:PDRIVER_OBJECT
;---------------------------------------------
; Delete the SymLink
push offset SymLink
call IoDeleteSymbolicLink
;---------------------------------------------
; Delete the device so we can unload
mov eax, pDriverObject
push (DRIVER_OBJECT PTR [eax]).DeviceObject
call IoDeleteDevice
ret 4
DriverUnload endp
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
LOCAL status:NTSTATUS
LOCAL pDeviceObject:DWORD
;---------------------------------------------
; Assume we're going to fail. Will result in
; driver unload and auto service start failure
mov status,STATUS_DEVICE_CONFIGURATION_ERROR
;---------------------------------------------
; Create the driver object
push pDeviceObject
push FALSE
push 0
push FILE_DEVICE_UNKNOWN
push offset DeviceName
push 0
push pDriverObject
call IoCreateDevice
cmp eax,STATUS_SUCCESS ; =0
jnz FAIL
;---------------------------------------------
; Create the symbolic link our user mode app
; will call
push offset SymLink
push offset DeviceName
call IoCreateSymbolicLink
cmp eax,STATUS_SUCCESS ; =0
jnz FAIL
;---------------------------------------------
; Declare the functions we're using
mov eax,pDriverObject
assume eax:PTR DRIVER_OBJECT
mov [eax].MajorFunction[IRP_MJ_CREATE*4], offset DispatchCreateClose
mov [eax].MajorFunction[IRP_MJ_CLOSE*4], offset DispatchCreateClose
mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL*4], offset DispatchControl
mov [eax].DriverUnload, offset DriverUnload
assume eax:nothing
mov eax,STATUS_SUCCESS ; =0
ret 8
;===========================================================
FAIL:
;---------------------------------------------
; We had an error - delete the device and exit
push pDeviceObject
call IoDeleteDevice
mov eax,status
ret 8
DriverEntry endp
end DriverEntry
Best regards,
Robin.
It doesn't really work like that... you are probably trying to access memory that has been paged out, and (generally speaking) the the system can't bring in pages when drivers are executing. For experimentation purposes, just stick with the non-paged pool for everything.
-r
Hi,
I appreciate the cause (I'm possibly not explaining myself well - late here), but it is 'why'??
The functions are passed pointers to memory. If the source I'm reading is correct, DriverEntry could be called at any IRQL. I have nothing to say it isn't correct.
Apparently DispatchControl is called in the context of the calling process. If it is a user-mode app, it is called in the context of that app (PASSIVE_LEVEL), and has access to everything.
For some reason it would appear Windows is calling DriverEntry or DispatchControl at a level that prevents access to paged memory. I can't see how I can prevent the crash though because as far as I can see, I can't minimize the function any more than it already is.
It has to access the structure passed, and that structure is somewhere in memory (I'm guessing in non-paged memory).
The only thing that I see might be an issue are the LOCALs. IIRC these are created on the stack, in paged memory. Instant crash. I'll put these into the .data section and see what happens then.
I suspect the other crash is somehow related to the LOCALs too.
Shooting very much in the dark,
Robin. :(
QuoteThe doer alone learneth. - Friedrich Nietzsche
Very apt.
As to the 'why', recall that a page fault is an exception, which is treated just like any other interrupt. The IRQL is how Windows "ranks" these interrupts by order of importance; for example, the clock interrupt (which allows scheduling and preemption) is nearly at the top, and applications run way down at the bottom. Page faults, as far as the system is concerned, are only slightly more important thatn applications, in that they run at the Dispatch Level APC Level. Since drivers are assumed to be servicing actual hardware, and not just 'privileged applications', they are given priority. As you've found, when a page fault is incurred while you execute a driver, the system says "I need to access memory that is paged out, but I can't stop what I'm doing to page it in". It can't stop, but it can't continue, and you end up with a BSOD.
As to the 'why' of why your *particular* code is doing it, there's nothing I notice during a cursory once-over that screams out to me, however i'm not any sort of Ring0 expert. Without the stack trace from the crash, it's just a lot of shots in the dark. Try the WinDBG ".analyze" command. Remember though, if any other functions try to use that data when it's paged out the same thing will happen, so don't necessarily assume it's once place when it could be another. Step 1: Do the crash dump. Step2: get the stack trace.
-r
Hi,
There is some difference between:
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
local status:NTSTATUS
local pDeviceObject:PVOID
mov status,STATUS_DEVICE_CONFIGURATION_ERROR
push offset pDeviceObject
push FALSE
push 0
push FILE_DEVICE_UNKNOWN
push offset DeviceName
push 0
push pDriverObject
call IoCreateDevice
and
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
local status:NTSTATUS
local pDeviceObject:PVOID
mov status, STATUS_DEVICE_CONFIGURATION_ERROR
invoke IoCreateDevice, pDriverObject, 0, addr DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, addr pDeviceObject
The invoke uses addr pDeviceObject, whilst the version that crashes on the second start uses push pDeviceObject.
What is the difference???
By replacing this block with the invoke version prevents the crash.
Best regards,
Robin.
DriverEntry supplies you with a pointer to the object; when you PUSH OFFSET, you are pushing a 'pointer to a pointer', which causes the illegal memory access (NT attempts to use the pointer itself as the object), When you INVOKE with just the name, you are pushing the *actual* value stored in pDriverObject, which is correct.
-r
I tried to build push offset pDeviceObject - doesn't work, it throws an error.
push pDeviceObject
builds, but crashes on the second run of the driver.
Best regards,
Robin.
mov eax,offset pDeviceObject
push eax
Writing drivers in masm and not knowing that "addr localvar" is "lea eax,localvar; push eax" ?!
Furthermore:
http://msdn.microsoft.com/en-us/library/aa490468.aspx
OUT PDEVICE_OBJECT *DeviceObject
DeviceObject
Pointer to a variable that receives a pointer
Hi drizz,
I don't use it very often and I forgot that. I did try to find it to see how it worked.
Thanks for the link - I'd already looked at that.
@dedndave: thanks.
Best regards,
Robin.