LZ 给出的上面这段IDA反编译的驱动的卸载例程在逻辑上而言有点小瑕疵,可以修正如下:
1。由于设备链表中的最后一个设备对象的NextDevice始终为NULL,可以通过它作为循环退出
的条件,确保遍历并删除了所有链中的设备对象,使用如下的for循环更简洁:
for(pNextObj = pDriverObject->DeviceObject; pNextObj != NULL; pNextObj = pNextObj->NextDevice)
{
.....//删除设备对象的符号链接
IoDeleteDevice(pNextObj);
}
2。没有必要读取设备对象的设备扩展子结构,运行这段代码会增加多余的CPU时钟周期开销,因此可以注释掉这一段:
v1 = pNextObj->DeviceExtension;
3。通过 IoDeleteSymbolicLink() 删除设备对象的Ring3层可见的符号链接之前,要求传入的
UNICODE_STRING结构(该结构包含符号链接字符串)必须先初始化,否则IoDeleteSymbolicLink() 根本无法查找到符号链接名并删除,可惜伪代码中没有初始化 pLinkName(一个 UNICODE_STRING 实例)。一般而言,应该使用 RtlInitUnicodeString()
来初始化一个 UNICODE_STRING 对象,比如:
const WCHAR DeviceLinkBuffer[] = L"\\DosDevices\\myDeviceSymbolLink";
UNICODE_STRING unicodeString;
RtlInitUnicodeString(&unicodeString, DeviceLinkBuffer);
IoDeleteSymbolicLink(&unicodeString);
注意,宽字符数组 DeviceLinkBuffer 被 RtlInitUnicodeString() 用于初始化unicodeString,
然后IoDeleteSymbolicLink() 就能够通过unicodeString删除设备对象的符号链接。其中:
DeviceLinkBuffer 必须定义成全局
常量,也就是在 DriverEntry() 之外定义;
“DosDevices”是 “\??”的别名,两者都是在用户空间中访问对象管理器维护的全局名字空间的接口,假如在符号链接字符串中少了其中之一,都无法访问到实际引用的设备对象。
很明显,你还需要确保删除的符号链接与创建时使用的是同一个,为此也可以把 unicodeString 定义成全局变量。
最后,如果驱动创建了多个设备对象,并且为每一个都创建符号链接,为了逐个删除这些设备对象以及它们各自的符号链接,可以增加一个内层的循环,或者其它等效的逻辑。