n l i t e d

:



Thread Links
next

MountMgr

📢 PUBLIC Page 1077:24/24 | edit | chip 2018-03-08 14:48:55
Tags: CryptDisk

October 19 2017

A couple hours spent perusing the TC driver source code only deepens the mystery. CryptDisk, if anything, is more complete than TC with respect to NT devices. I am responding to every ioctl and IRP as TC and more.

The differences between TC and CD:

  • TC alerts the MountMgr (via IRP/ioctl) after creating the virtual disk device. The volume link is created in two different places: MountManagerMount() and in response to IOCTL_MOUNTDEV_QUERY_SUGGESTED_MOUNT_POINT. And finally there is a comment indicating that MountMgr sometimes fails to create the link.
  • TC then responds to the MOUNTDEV IRPs from MountMgr to create a volume(s) filter for the new disk device. The volume device doesn't actually do much other than catch power and PnP events.

CD creates the volume link directly through a single call to IoCreateSymbolicLink(). I tried using MountMgr a long time ago (Driver1) and tossed it aside as being a mess that didn't provide any value. Creating the volume myself seems to work and has been reliable. However, I can't see any other significant difference between TC and CD. When everything else has been ruled out, what is left -- no matter how strange or perplexing -- must be the answer.

Software is difficult because testing a theory can sometimes require writing a lot of code. And sometimes that code is then thrown away when it disproves the theory. This is especially true for KM, where creating, maintaining, and using the debug environment can be so difficult.

I spent about four hours replacing my code that created the volume link directly with calls to MountMgr, using TrueCrypt as a guide. I spent about half an hour trying to figure out why MountMgr was always returning an error from CREATE_POINT, then noticed that TC was ignoring the return status.

MountMgr IOCTLs sent to the disk device:

  • 560048: IOCTL_VOLUME_IS_DYNAMIC
  • 4D000C: IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
  • 4DC010: IOCTL_MOUNTDEV_LINK_CREATED
  • 4D0010: Same?
  • 56C064: IOCTL_VOLUME_POST_ONLINE

The MountMgr is horrible: Interleaved callbacks, cryptic paths, minimal documentation. And then it doesn't really do much -- except something that makes CompactSQL happy.

SUCCESS!!! I am now able to build the SDF on my CryptDisk volume!!!!! This was a nasty, nasty bug that required extreme investigation. I still don't understand exactly why but now I know how.

Rebuilding TC was well worth the trouble. I now have a clear roadmap to what IOCTLs need to be handled and which do not. At this point, CD is handling many more IOCTLs than TC, which an optimist would view as being "more complete". I can also see where the TC authors also had their own doubts and rabbit holes. In my opinion, there is a lot of code that isn't necessary. TC doesn't need to create the volume filters when it is simply acting as a disk device.

On the other hand, there is a lot of code in TC to deal with boot and hidden partitions. This is a whole area I am intentionally avoiding for the "Daily Use" version of CD. I will want to revisit the topic when I start working on CryptDisk Stealth.


October 22 2017

I thought MountMgr was working, but now it is not. It may be that I am not handling the MOUNTDEV_QUERY_UNIQUE_ID properly.

Debug: CryptDisk|DBG DeviceCtrl:Create CryptDisk|DBG DeviceDisk:Create: \Device\CryptDiskDebug01 CryptDisk|DBG DeviceCtrl:SendIoctl: Waiting... pIrp[FFFF928CD2B47010] CryptDisk|ERR WRN[80000005]: DeviceExt:IrpFail: DeviceDisk:IoctlQueryUniqueID: Buffer too small. [4,52] CryptDisk|DBG DeviceDisk:IoctlQueryVolumeGuid: {C06A47FA-A605-478E-818E-71F0589F503B} CryptDisk|DBG DeviceDisk:IoctlQueryLink: \DosDevices\S: CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \??\Volume{c06a47fa-a605-478e-818e-71f0589f503b} CryptDisk|ERR WRN[C0000034]: nMountMgr:Mount: SendIoctl(CREATE_POINT) failed. CryptDisk|DBG DeviceCtrl:SendIoctl: Success[CD01E004]

It appears that MountMgr is creating a volume link using the guid I provided. Then the CREATE_POINT fails because I am expecting to use the "CryptDisk01" name not the guid name.

TrueCrypt does not respond to the IOCTL_MOUNTDEV_QUERY_STABLE_GUID at all. When I disable the response in my code, MountMgr generates a new guid.

I think MountMgr is expecting to use the guid to track the volume so it can try to recreate the link points consistently. The guid then refers to the volume, not the device. So with CryptDisk, the guid should be tied to the media file. However, I do not want to use guids since they can be used to identify the computer that generated them. I use a pseudo-guid instead, generated from the VolumeInfo VolumeID. (This is a simple timestamp written into a guid struct.)

I split up MountMgr::Mount() into Notify() and Mount(). This lets me call Mount() with the volume name from the MOUNTDEV_LINK_CREATED callback. But now I am seeing two volumes created, not sure what is happening there.

Debug: CryptDisk|DBG Device:Disk:IoctlMountQueryDeviceName: [48]\Device\CryptDiskDebug01 CryptDisk|ERR WRN[80000005]: DeviceExt:IrpFail: DeviceDisk:IoctlQueryUniqueID: Buffer too small. [4,52] CryptDisk|DBG DeviceDisk:IoctlQueryUniqueID: \Device\CryptDiskDebug01 CryptDisk|DBG DeviceDisk:IoctlQueryVolumeGuid: {C06A47FA-A605-478E-818E-71F0589F503B} CryptDisk|DBG DeviceDisk:IoctlQueryLink: \DosDevices\S: CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \??\Volume{6adc72bd-b776-11e7-b312-00c2c6e54452} CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \??\Volume{c06a47fa-a605-478e-818e-71f0589f503b}

MountMgr Redux

October 23 2017

My victory declaration over MountMgr was premature. It worked the first time, then failed after that.

  1. The loader sends a MOUNT ioctl.
  2. The control device spawns a new \Device\CryptDiskDebug01 disk device.
  3. The control device sends the MOUNT ioctl to the disk device, which defers it to its worker thread.
  4. The worker thread fetches the MOUNT ioctl.
  5. The disk device opens the media file and validates it.
  6. The disk device calls MountMgr.Notify(\Device\CryptDiskDebug01)
    I send IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION(\Device\CryptDiskDebug01) to the MountMgr.
    1. I receive IOCTL_MOUNTDEV_QUERY_DEVICE_NAME and respond with "\Device\CryptDiskDebug01"
    2. I receive IOCTL_MOUNTDEV_QUERY_UNIQUE_ID and respond with "\Device\CryptDiskDebug01"
    3. I receive IOCTL_MOUNTDEV_QUERY_STABLE_GUID and respond with "{C06A47FA-..."}
    4. I receive IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME and respond with "\DosDevices\S:"
    5. I receive IOCTL_MOUNTDEV_LINK_CREATED(\??\Volume{6adc72bd-...})
    6. I receive IOCTL_MOUNTDEV_LINK_CREATED(\??\Volume{c06a47fa-...})
    MountMgr returns STATUS_SUCCESS.
  7. The disk device calls MountMgr.Mount(\Device\CryptDiskDebug01,S)
    I create the volume name "\DosDevices\S:"
    I send IOCTL_MOUNTMGR_CREATE_POINT(\Device\CryptDisk01,\DosDevices\S:) to MountMgr
    MountMgr fails with C0000034:NAME_NOT_FOUND
    I have tried using \??\Volume{c06a47fa-...} with the same result.

IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
Support for this IOCTL by the mount manager clients is mandatory. Upon receiving this IOCTL a client driver must provide the (nonpersistent) device (or target) name for the volume. The mount manager uses the device name returned by the client as the target of a symbolic link. An example of a device name would be "\Device\HarddiskVolume1".
typedef struct _MOUNTDEV_NAME { USHORT NameLength; WCHAR Name[1]; } MOUNTDEV_NAME, *PMOUNTDEV_NAME;

IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
Support for this IOCTL by mount manager clients is mandatory. Upon receiving this IOCTL, the mount manager client must provide a counted byte string identifier that is unique to the client (that is, the device or the volume). The client cannot change this unique ID without alerting the mount manager (see IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY).
typedef struct _MOUNTDEV_UNIQUE_ID { USHORT UniqueIdLength; UCHAR UniqueId[1]; } MOUNTDEV_UNIQUE_ID, *PMOUNTDEV_UNIQUE_ID;

IOCTL_MOUNTDEV_QUERY_STABLE_GUID: This is not documented.

The state of the MountMgr "database":
[ImageNotFound:'2172'] [ImageNotFound:'2173']
Note that the link created from my STABLE_GUID "\??\Volume{c06a47fa-...}" is not listed. The link generated by MountMgr "\??\Volume{6adc72bd-...}" is present and linked to "\Device\CryptDiskDebug01". There is no entry for "\DosDevices\S:".

Ideas:

  • Read OSR: Drive Letter Assignment. It was written 15 years ago (it mentions IoMega zip drives! Back when 120MB was a big deal), but not much has changed.
  • Read OSRForum: Mount Manager
    Mount points are stored in the registry under \HKLM\System\MountedDevices
    The discussion ends on a discouraging note.
  • MountMgr doesn't like the '\??\' prefix. I need to replace it with '\Device\'
  • The \Device\CryptDiskDebug01 device has not been created in the object directory yet. I think this is unlikely. The CREATE_POINT worked at least once. This would be difficult to test.
  • Start handling the PNP IRPs in the disk device.
  • Create a volume device to handle PNP messages.

October 29 2017

The SDF glitch is back -- it is the bug that just won't die. After a lot of experimentation, it seems to be working (again).

UPDATE 20171108: The bug turned out to be another case of C/UNICODE_STRING confusion. I was including the string terminator in the length given to MountManager, which was then including the 0-character as part of the literal text.

Debug: CryptDriver has arrived! CryptDriver version 2.3.865 [VS12] VS12 DriverEntry=FFFFF800999DEC60 pDriver=FFFFE0003C6C81B0 CryptDisk|DBG DeviceCtrl:Create CryptDisk|DBG DeviceDisk:Create: \Device\CryptDiskDebug01 CryptDisk|DBG DeviceCtrl:SendIoctl: Waiting... pIrp[FFFFE0003C0D2700] CryptDisk|DBG nMountMgr:Notify: VOLUME_ARRIVAL_NOTIFICATION \Device\CryptDiskDebug01 CryptDisk|DBG DeviceDisk:IoctlQueryUniqueID: \Device\CryptDiskDebug01 CryptDisk|DBG DeviceDisk:IoctlQueryVolumeGuid: G CryptDisk|DBG DeviceDisk:IoctlQueryLink: \DosDevices\H: CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \??\Volume{00000000-0000-0000-0000-000000000000} CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \??\Volume{00000000-0000-0000-0000-0000 0000 0000} CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \DosDevices\H: CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \DosDevices\H: CryptDisk|DBG DeviceExt: Unhandled IRP[14/0] Ioctl[56C064 56,25] \Device\CryptDiskDebug01 IrpIoControl pIrp[FFFF,E000'3C05,56E0] CryptDisk|DBG nMountMgr:Mount: CREATE_POINT \DosDevices\H: -> \Device\CryptDiskDebug01 CryptDisk|ERR WRN[C000000D]: nMountMgr:Mount: CREATE_POINT(\Device\CryptDiskDebug01,\DosDevices\H:) failed. CryptDisk|DBG DeviceCtrl:SendIoctl: Success[CD01E004]

The entire sequence:

  1. Mgr.Notify("\Device\CryptDiskDebug01")
    1. MOUNTMGR_TARGET_NAME *pTarget;
    2. pTarget->DeviceNameLength= StrLenW(DevName)*2;
    3. RtlCopyMemory(pTarget->DeviceName,DevName,StrLenW(DevName)*2)
    4. SendMountMgr(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,&pTarget,InSz,0,0);
      1. Receive IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, return "\Device\CryptDiskDebug01"
      2. Receive IOCTL_MOUNTDEV_QUERY_STABLE_GUID, return volume ID "{00000000-0000-0000-0000-00000000}"
      3. Receive IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, return "\DosDevices\H:"
      4. Receive IOCTL_MOUNTDEV_LINK_CREATED, record the mount point.
  2. Mgr.Mount("\Device\CryptDiskDebug01",'H')
    1. SendMountMgr(IOCTL_MOUNTMGR_CREATE_POINT)
      MOUNTMGR_CREATE_POINT_INPUT *pMount;
      pMount->DeviceName= "\Device\CryptDiskDebug01"
      pMount->SymbolicLinkName= "\DosDevices\H:"
    2. IoCreateSymbolicLink("\DosDevices\H:","Device\CryptDiskDebug01")
      This will fail if the link has already been created. This is a redundant step in case the symbolic link was not created by MountMgr.
  3. pDevice->Characteristics|= FILE_DEVICE_IS_MOUNTED;
    This is probably not required (probably not correct). It was added while I was experimenting and seems to be benign.

CREATE_POINT will return 0xC0000034 if DeviceName is invalid. It will return 0xC000000D if SymbolicName is invalid.

MountMgr: NTSTATUS nMountMgr::Notify(const WCHAR *DevName) { NTSTATUS Status= STATUS_SUCCESS; UINT DevNameLen= StrLenW(DevName); MOUNTMGR_TARGET_NAME *pInfo= 0; // NOTE: sizeof(*pInfo) implicitly includes the terminator since // pInfo->DeviceName[] is declared as length 1. UINT BufSz= sizeof(*pInfo)+DevNameLen*2; if(!(pInfo= (MOUNTMGR_TARGET_NAME*)MemAlloc("MountMgr:Notify",BufSz))) return(Warn(STATUS_NO_MEMORY,"nMountMgr:Notify: NoMem(%d)",BufSz)); pInfo->DeviceNameLength= StrLenW(DevName)*2; RtlCopyMemory(pInfo->DeviceName,DevName,(DevNameLen+1)*2); Debug(DBG_MOUNTMGR,"nMountMgr:Notify: VOLUME_ARRIVAL_NOTIFICATION %S",DevName); if(!NT_SUCCESS(Status= SendMountMgr(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,pInfo,BufSz,0,0))) Status= Warn(Status,"nMountMgr:Notify: VOLUME_ARRIVAL_NOTIFICATION failed."); MemFree(pInfo); return(Status); } // Use MountManager to mount the volume. // This does little more than create the \DosDevices\C: link, but there // are some weird cases (ie Visual Studio SDF database) that won't work // properly if I don't use MountManager. NTSTATUS nMountMgr::Mount(const WCHAR *_DeviceName, WCHAR MountLetter) { NTSTATUS Status= STATUS_SUCCESS; NtString DeviceName(_DeviceName); NtString MountName(false,"\\DosDevices\\%C:",MountLetter); BYTE *pBuf; MOUNTMGR_CREATE_POINT_INPUT *pMountPt; //NOTE: MountPt does not include any string terminators. UINT BufSz= sizeof(*pMountPt)+DeviceName.ByteCt()+MountName.ByteCt(); if(!(pBuf= (BYTE*)MemAlloc("nMountMgr:MountPt",BufSz))) return(Warn(STATUS_NO_MEMORY,"nMountMgr:Mount: NoMem(%d)",BufSz)); // Now for some funky point/offset math... // This would have been *so* much simpler if CREATE_POINT_POINT ended // with WCHAR text[0] and used null-terminated strings. pMountPt= (MOUNTMGR_CREATE_POINT_INPUT*)&pBuf[0]; pMountPt->DeviceNameOffset= sizeof(*pMountPt); //Offset is bytes from beginning of pMountPt. pMountPt->DeviceNameLength= DeviceName.ByteCt(); //Length is in bytes, not including terminator. pMountPt->SymbolicLinkNameOffset= pMountPt->DeviceNameOffset+pMountPt->DeviceNameLength; pMountPt->SymbolicLinkNameLength= MountName.ByteCt(); RtlCopyBytes(&pBuf[pMountPt->DeviceNameOffset],DeviceName.Text(),pMountPt->DeviceNameLength); RtlCopyBytes(&pBuf[pMountPt->SymbolicLinkNameOffset],MountName.Text(),pMountPt->SymbolicLinkNameLength); if(!NT_SUCCESS(Status= SendMountMgr(IOCTL_MOUNTMGR_CREATE_POINT,pMountPt,BufSz,0,0))) { Warn(Status,"nMountMgr:Mount: CREATE_POINT(%S,%S) failed.",DeviceName.Text(),MountName.Text()); // I can ignore this error and *most* things will still work. (Except the SDF glitch...) Status= STATUS_SUCCESS; } else { Debug(DBG_MOUNTMGR,"Volume %S mounted as %S",DeviceName.Text(),MountName.Text()); } // I try to create the volume link just in case MountMgr flakes out. // This will fail benignly if MountMgr already created the link. // Otherwise, this will still create a usable drive that will work for 99% of the apps. IoCreateSymbolicLink(&MountName.GetUnicode(),&DeviceName.GetUnicode()); MemFree(pBuf); return(Status); } NTSTATUS nMountMgr::SendIoctl(UNICODE_STRING &DevName, DWORD IoCode, void *pIn, UINT InSz, void *pOut, UINT OutSz) { NTSTATUS Status= STATUS_SUCCESS; FILE_OBJECT *pFileObj; DEVICE_OBJECT *pDevObj; KEVENT WaitEvent; IRP *pIrp; IO_STATUS_BLOCK IoStatus; if(!NT_SUCCESS(Status= IoGetDeviceObjectPointer(&DevName,FILE_READ_ATTRIBUTES,&pFileObj,&pDevObj))) return(Warn(Status,"nMountMgr:SendIoctl: IoGetDeviceObjectPointer(%wZ) failed.",&DevName)); KeInitializeEvent(&WaitEvent,NotificationEvent,FALSE); if(!(pIrp= IoBuildDeviceIoControlRequest(IoCode,pDevObj,pIn,InSz,pOut,OutSz,0,&WaitEvent,&IoStatus))) { Status= Warn(STATUS_INSUFFICIENT_RESOURCES,"nMountMgr:SendIoctl: IoBuildDeviceIoControlRequest() failed."); } else { IO_STACK_LOCATION *pStack= IoGetNextIrpStackLocation(pIrp); pStack->FileObject= pFileObj; if((Status= IoCallDriver(pDevObj,pIrp))==STATUS_PENDING) { KeWaitForSingleObject(&WaitEvent,Executive,KernelMode,0,0); Status= IoStatus.Status; } } ObDereferenceObject(pFileObj); return(Status); }

March 8 2018

Everything seems to be working. I mount a volume as H: and it is fine. Then a few seconds later a new E: drive appears, which is an alias for H:. There is an entry in the registry under \HKLM\System\MountedDevices\ \DosDevices\E:

[ImageNotFound:'2509']  [ImageNotFound:'2510']

If delete the \DosDevices\E: entry and reboot, it will reappear after I mount H: again. Note that H: is missing from the list.

The debug log shows something unexpected...

Debug: CryptDriver has arrived! CryptDriver version 3.1.1130 [VS17] VS17 DriverEntry=FFFFF800B2116A90 pDriver=FFFFDA0EA45C1E60 CryptDisk|DBG DeviceCtrl:Create CryptDisk|DBG DeviceDisk:Create: \Device\CryptDiskDebug01 CryptDisk|DBG DeviceCtrl:SendIoctl: Waiting... pIrp[FFFFDA0EA7AF7410] CryptDisk|DBG DeviceDisk:IoctlQueryUniqueID: \Device\CryptDiskDebug01 CryptDisk|DBG DeviceDisk:IoctlQueryVolumeGuid: {C06A47FA-A605-478E-818E-71F0589F503B} CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \??\Volume{c06a47fa-a605-478e-818e-71f0589f503b} CryptDisk|DBG DeviceDisk:IoctlLinkCreated: \DosDevices\E: CryptDisk|ERR WRN[C000000D]: nMountMgr:Mount: CREATE_POINT(\Device\CryptDiskDebug01,\DosDevices\H:) failed. CryptDisk|DBG DeviceCtrl:SendIoctl: Success[CD01E004] CryptDisk|ERR WRN[C0000010]: DeviceExt:IrpFail: DeviceDisk:IoctlStorageQueryProperty CryptDisk|DBG DeviceDisk:IoctlQueryVolumeGuid: {C06A47FA-A605-478E-818E-71F0589F503B} CryptDisk|ERR WRN[C0000010]: DeviceExt:IrpFail: DeviceDisk:IoctlVolumeQueryAllocationHint CryptDisk|DBG DeviceDisk:IoctlQueryVolumeGuid: {C06A47FA-A605-478E-818E-71F0589F503B} CryptDisk|ERR WRN[C0000010]: DeviceExt:IrpFail: DeviceDisk:IoctlStorageQueryProperty CryptDisk|ERR WRN[C0000023]: DeviceExt:IrpFail: DeviceDisk:IoctlStorageQueryDevice: Buffer too small (8,40) CryptDisk|ERR WRN[C0000023]: DeviceExt:IrpFail: DeviceDisk:IoctlStorageQueryDevice: Buffer too small (8,40) CryptDisk|ERR WRN[C0000023]: DeviceExt:IrpFail: DeviceDisk:IoctlStorageQueryDevice: Buffer too small (8,40) CryptDisk|ERR WRN[C0000023]: DeviceExt:IrpFail: DeviceDisk:IoctlStorageQueryDevice: Buffer too small (8,40)

It appears my old friend MountMgr is acting peculiar again. This is running on VS17, I probably won't see the same behavior on Win10 where I can step through the code. So I will need to figure this out the hard way... This is a tedious process because once the errant E: drive is created I need to reboot to clear it out.

It appears that MountMgr::Notify() is being called first, which is when Windows creates the E: drive and notifies me. I try to mount the volume as H: and that fails, then as a fallback I explictly create the \DosDevices\H: link to the driver.

I changed the code so Notify() is called after Mount(). That seemed to fix it.



close comments Comments are closed.

Comments are moderated. Anonymous comments are not visible to other users until approved. The content of comments remains the intellectual property of the poster. Comments may be removed or reused (but not modified) by this site at any time without notice.

  1. [] ok delete


Page rendered by tikope in 269.797ms