YaKs
YaKs

Reputation: 329

.NET WMI - How to detect when a bitlocker device is unlocked

I have an specific application that must react (searching for a specific file on the device) each time a new USB volume is connected or disconnected to/from the system.

For normal unencrypted devices I managed just creating a watcher for new USB use the

  ManagementEventWatcher watcher = new ManagementEventWatcher();
  WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_VolumeChangeEvent WHERE EventType = 2 or EventType = 3");
  watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
  watcher.Query = query;
  watcher.Start();

My problem is with bitlocker drives. The alert triggers but I need to wait until the user does introduce the correct password. How to deal with this wait is my main concern.

Could someone help me to fine tune the wql query so it only alerts for unencrypted volumes? The idea is that right after the user introduces the bitlocker password, the watcher (with the fine tuned query) should trigger. So basically for my program the bitlocker becomes transparent.

Upvotes: 2

Views: 979

Answers (2)

I wanted to know when the device is unlocked by Bitlocker, to put a FileSystemWatcher, and I had an idea for not using a continuous do while. When the device is plugged, it is "easy" to know it with:

WqlEventQuery insertQuery = new WqlEventQuery(
"SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");

insertWatcher = new ManagementEventWatcher(insertQuery);
insertWatcher.EventArrived += new EventArrivedEventHandler(DeviceInsertedEvent);
insertWatcher.Start();

But it is not easy to know when the device is unlocked, but I saw when you unlock the device, that triggers all WMI events about CIM_DataFile and CIM_Directory, then you can use this like this:

WqlEventQuery waitForBitlockerQuery = new WqlEventQuery(
"SELECT * From __InstanceCreationEvent WITHIN 5 Where TargetInstance ISA \"CIM_Directory\" And TargetInstance.Drive=\"" + driveUnit.Name.Replace("\\", "") + "\" And TargetInstance.Path = ''");

// Creamos un evento que cuando el dispotivo pueda, 
// disparara el evento con el direcotrio raiz, y por tanto indicando 
// que el file system ya tiene acceso al dispositivo (isReady = true)

ManagementEventWatcher copyWatcher = new ManagementEventWatcher(waitForBitlockerQuery);
copyWatcher.EventArrived += new EventArrivedEventHandler(DeviceStatusChanged);
copyWatcher.Start();

And use the function DeviceStatusChanged to do whatever you want after the device is unlocked.

Upvotes: 3

YaKs
YaKs

Reputation: 329

After several hours looking for a solution, I found a far from ideal working solution.

First I found the Win32_EncryptableVolume class https://learn.microsoft.com/en-us/windows/win32/secprov/win32-encryptablevolume and I used the very useful tool WMI Code Creator to identify properties and get some working example C# code https://www.microsoft.com/en-us/download/details.aspx?id=8572

As far as I know, there is no event class related to that EncryptableVolume class. So what I did was first to obtain the drive letter from the previous Win32_VolumeChangeEvent and then I run a second WMI query (using Win32_EncryptableVolume) and that drive letter to check the value of the property ProtectionStatus. From tests I discovered that until the user introduce the valid password, the value is 2. Once unlocked , the value turns 1. So basically I launched a thread and wait for this property to be changed by the OS.

For the waiting part, and that is the part I like least, i keep the thread running in an active loop instead of be able to "consume" a specific event whenever it arrives.

Another side effect of this solution is that to access root\cimv2\Security\MicrosoftVolumeEncryption you need admins rights, so from now on I will have develop Visual Studio executed with admin rights (runas). :/

ManagementObjectSearcher searcher2 = new ManagementObjectSearcher("root\\cimv2\\Security\\MicrosoftVolumeEncryption", "SELECT * FROM Win32_EncryptableVolume WHERE driveletter ='" + s_drive_letter + "'");
try
{
   ManagementObjectCollection queryCollection = searcher2.Get();
   ManagementObject mo = queryCollection.OfType<ManagementObject>().FirstOrDefault();

   if (mo["ProtectionStatus"].ToString() == "2")
   {
       new Thread(() =>
       {
           while (mo["ProtectionStatus"].ToString() == "2")
           {
               Thread.Sleep(2000);
               queryCollection = searcher2.Get();
               mo = queryCollection.OfType<ManagementObject>().FirstOrDefault();
               if (mo == null)
                  Thread.CurrentThread.Abort();  
           }
           // Here the code we want to execute once the device gets unlocked.
       }).Start();
   }

Upvotes: 1

Related Questions