There’s no easy answer. If the driver wasn’t unloading due to an open handle
to the device, then you need to find the handle that points to the file
object that references the device. The traditional way would be to run
“!handle 0 3 0 File” and search the output for a file object referencing
your device object. Or if you really cared you could write an extension
that uses the handle walking interface in extsfns.h (never cared enough to
do this myself but always thought it would be amusing).
These days JavaScript extensions and LINQ are the thing. You can access the
handles in a process using the debugger’s object model (it’s under
Process.Io.Handles) and you could walk through each one looking for a file
object that references your device object. The query for a specific process
would look something like:
dx Debugger.Sessions[0].Processes[0x318].Io.Handles.Where(obj =>
((nt!_FILE_OBJECT *)&obj.Object.Body)->DeviceObject == (nt!_DEVICE_OBJECT
*)0xffffd0823dbfd870)
Note that this just brute forces each handle into being a file object. A
more elegant solution would extend the query to ignore non-file object
types.
For fun I did this a JavaScript extension as well, though I skipped using
LINQ. Very likely that there’s a better way to do this but here’s what I
came up with. Example success output is:
kd> dx
Debugger.State.Scripts.FindDevHandle.Contents.FindDevHandle(0xffff8e018f704140)
Finding handle to device 0xffff8e018f704140!
…
Process SearchFilterHost.exe
Process audiodg.exe
Process OSRLOADER.exe
Process NothingTest.exe
Found one!
PID : 0x1300
Name : NothingTest.exe
Handle : 0x9c
Object : 0xffff8e018e619690
//
// FindDevHandle.js
//
// Walk the current list of processes and look for a handle to a file object
// that is accessing the specified device
//
// OSR Open Systems Resources, inc.
//
// http://www.osr.com
// http://www.osronline.com
//
//
// To run:
//
// .load jsprovider.dll
// .scriptload FindDevHandle.js
// dx
Debugger.State.Scripts.FindDevHandle.Contents.FindDevHandle(0x12345678)
//
function FindDevHandle(devObjParam) {
// Get easy access to the debug output method
var dbgOutput = host.diagnostics.debugLog;
// Get a typed device object for the incoming parameter
var devObj = host.createTypedObject(devObjParam, “nt”,
“_DEVICE_OBJECT”);
dbgOutput("Finding handle to device ", devObj.targetLocation, “!\n\n”);
// Loop over each process
var processes = host.currentSession.Processes;
for (var process of processes) {
dbgOutput("Process ", process.Name, “\n”);
// And each handle in every process
var handles = process.Io.Handles;
// Note that an exception can be raised while looping over the
handles
// (e.g. an empty handle table)
try {
for (var handle of handles) {
// NOTE: We just treat every handle like it’s a file handle
// and catch exceptions along the way. A better idea would
// be to key off of the type, but that appears to be broken
// with public PDBs at the moment
try {
// Cast the object to a file object
var fileObj =
host.createTypedObject(handle.Object.Body.targetLocation, “nt”,
“_FILE_OBJECT”);
// Dereference the DeviceObject field and get the target
location
if (fileObj.DeviceObject.dereference().targetLocation ==
devObj.targetLocation) {
dbgOutput(“\tFound one!\n”);
dbgOutput("\t PID : “, process.Id, “\n”);
dbgOutput(”\t Name : “, process.Name, “\n”);
dbgOutput(”\t Handle : “, handle.Handle, “\n”);
dbgOutput(”\t Object : ", fileObj.targetLocation,
“\n\n”);
}
} catch (e) {
dbgOutput(“\tException parsing handle!\n”);
}
}
} catch (e) {
dbgOutput(“\tException parsing handle table!\n”);
}
}
}
I also wrote more about this here:
https://www.osr.com/blog/2017/05/18/windbg-debugger-objects-javascript-oh/
-scott
OSR
@OSRDrivers