现象:
最近在编写WinXP SP2系统下的虚拟磁盘驱动,通过DefineDosDevice将虚拟盘挂载到计算机上,显示出盘符。测试的时候,把一些exe文件放进去,看能不能正常执行,大部分exe都执行良好,但在执行devicetree.exe的时候出现了问题,弹出对话框显示objinfo驱动无法启动,然后退出。
分析:
devicetree.exe本身只有一个文件就能运行,没有发现过需要什么驱动,莫非是在其自身里面包含了?于是通过OllyDbg反汇编查找具体出错的位置。果然,devicetree.exe启动时要在其自身目录下创建并启动objinfo.sys,但是在StartService的时候就会出错。如果把程序放在别的实际盘中运行,则一切正常。打开注册表,观察这两种情况下服务的配置信息,除了盘符不同之外,其他信息完全相同,由此看来问题确实出在虚拟盘上。
用DefineDosDevice定义的盘符毕竟是在用户态下创建的,对于服务和驱动可能会有影响。用WinObj查看内核中的符号连接,在Global中居然看不到虚拟盘符对应的项,那么内核在启动驱动时肯定无法正常定位到真正路径。突然想起Windows映射的网络驱动器,也是如此情况,那也肯定会存在这种问题,经测试发现果然如此。
在内核下用IoCreateSymblicLink创建的符号连接没有问题,所以初步怀疑是和用户态、内核态有关系,于是编写代码从用户态发送DeviceControl请求,由驱动来创建符号连接。结果出乎意料,仍然不行。但是测试中却发现,如果是由System下的进程发送的请求,创建出来的符号连接就没有问题。看来问题出在上下文(Session)上,只要是用户态程序,其调用的驱动也在用户的上下文(Session)里。
原因:
从网上搜索来搜索去,终于明白,比较高级的Windows系统(哪个版本的操作系统很难讲,需要判断系统所支持的WDM版本号)符号链接带有用户相关性。用户上下文创建的符号连接不是在Global下,而是在Session下。
解决:
方案一
任何用户都可以生成全局符号链接,只要在创建符号连接的时候把“\DosDevices\SymbolicLinkName”改为“\DosDevices\Global\SymbolicLinkName”即可。但是在不支持符号链接用户相关性的系统上,生成这样的符号链接是一种错误。为此必须先判断一下:
UNICODE_STRING symbl_name;
if( IoIsWdmVersionAvailable(1, 0x10) ) // 如果支持符号链接用户相关性
RtlInitUnicodeString(&symbl_name, L"\\DosDevices\\Global\\SymbolicLinkName");
else // 如果不支持,则用“\DosDevices”
RtlInitUnicodeString(&symbl_name, L"\\DosDevices\\SymbolicLinkName");
RtlInitUnicodeString(&device_name, L"\\Device\\DeviceName");
IoCreateSymbolicLink(&symbl_name, &device_name); // 生成符号链接
......
IoDeleteSymbolicLink(&symbl_name); // 删除符号链接
配合用户态下的DefineDosDevice,可以使用户看到所加载的盘符,同时内核态也不会出现问题。
......
DefineDosDevice(DDD_REMOVE_DEFINITION, DriveName, NULL);
方案二
TrueCrypt实现的虚拟盘不会出现上述问题,调试其源代码发现它在加载的过程中,通过MountManagerMount函数向MountPointManager设备发送IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION和IOCTL_MOUNTMGR_CREATE_POINT请求来实现盘符的加载,发送IOCTL_MOUNTMGR_DELETE_POINTS请求来实现盘符的卸载。两个主要函数如下:
{
NTSTATUS ntStatus;
WCHAR arrVolume[64];
char buf[200];
PMOUNTMGR_TARGET_NAME in = (PMOUNTMGR_TARGET_NAME) buf;
PMOUNTMGR_CREATE_POINT_INPUT point = (PMOUNTMGR_CREATE_POINT_INPUT) buf;
TCGetNTNameFromNumber (arrVolume, mount->nDosDriveNo);
in->DeviceNameLength = (USHORT) wcslen (arrVolume) * 2;
wcscpy(in->DeviceName, arrVolume);
ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
in, sizeof (in->DeviceNameLength) + wcslen (arrVolume) * 2, 0, 0);
memset (buf, 0, sizeof buf);
TCGetDosNameFromNumber ((PWSTR) &point[1], mount->nDosDriveNo);
point->SymbolicLinkNameOffset = sizeof (MOUNTMGR_CREATE_POINT_INPUT);
point->SymbolicLinkNameLength = (USHORT) wcslen ((PWSTR) &point[1]) * 2;
point->DeviceNameOffset = point->SymbolicLinkNameOffset + point->SymbolicLinkNameLength;
TCGetNTNameFromNumber ((PWSTR) (buf + point->DeviceNameOffset), mount->nDosDriveNo);
point->DeviceNameLength = (USHORT) wcslen ((PWSTR) (buf + point->DeviceNameOffset)) * 2;
ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_CREATE_POINT, point,
point->DeviceNameOffset + point->DeviceNameLength, 0, 0);
return ntStatus;
}
NTSTATUS MountManagerUnmount (int nDosDriveNo)
{
NTSTATUS ntStatus;
WCHAR drive[] = {(WCHAR) nDosDriveNo + 'A', 0};
char buf[64], out[300];
PMOUNTMGR_MOUNT_POINT in = (PMOUNTMGR_MOUNT_POINT) buf;
memset (buf, 0, sizeof buf);
TCGetDosNameFromNumber ((PWSTR) &in[1], nDosDriveNo);
in->SymbolicLinkNameOffset = sizeof (MOUNTMGR_MOUNT_POINT);
in->SymbolicLinkNameLength = (USHORT) wcslen ((PWCHAR) &in[1]) * 2;
ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_DELETE_POINTS,
in, sizeof(MOUNTMGR_MOUNT_POINT) + in->SymbolicLinkNameLength, out, sizeof out);
Dump ("IOCTL_MOUNTMGR_DELETE_POINTS returned 0x%08x\n", ntStatus);
return ntStatus;
}
需要说明的是,MountPointManager在加载盘符前,会自动向目标设备发送IOCTL_MOUNTDEV_QUERY_DEVICE_NAME和IOCTL_MOUNTDEV_QUERY_UNIQUE_ID请求来获取信息,也就就是说,要想通过这种方式加载成功,目标设备必须正确处理这两个请求。
加载或卸载成功后,在用户态下需要广播设备变更消息来显示盘符,具体内容可以参考TrueCrypt的BroadcastDeviceChange函数。
备注:
TrueCrypt
http://www.truecrypt.com/
Windows driver examples
https://www.acc.umu.se/~bosse/
在应用层用DefineDosDevice创建的符号链接,用winobj为何看不到?
http://bbs.driverdevelop.com/read.php?tid-107056.html
小议Windows装载管理器(Mount Manager)
http://hi.baidu.com/%B3%FE%D0%F9%B1%F9%C0%B6/blog/item/2cf3958354fe49b46c811984.html
一天一点进步