Detect BitLocker programmatically from c# without admin

9.4k views Asked by At

From various threads I've cobbled together how to check for BitLocker programmatically like this:

private void TestBitLockerMenuItem_Click(object sender, RoutedEventArgs e) {
   var path=new ManagementPath(@"\ROOT\CIMV2\Security\MicrosoftVolumeEncryption")
               { ClassName="Win32_EncryptableVolume" };
   var scope=new ManagementScope(path);
   path.Server=Environment.MachineName;
   var objectSearcher=new ManagementClass(scope, path, new ObjectGetOptions());
   foreach (var item in objectSearcher.GetInstances()) {
      MessageBox.Show(item["DeviceID"].ToString()+" "+item["ProtectionStatus"].ToString());
   }
}

But it only works if the process has admin privileges.

It seems odd that any old Windows user can go to Explorer, right-click on a drive, and find out if it has BitLocker turned, but a program cannot seem to get this done. Does anyone know of a way to do this?

2

There are 2 answers

5
slypete On BEST ANSWER

Windows displays this in the shell by using the Windows Property System in the Win32 API to check the undocumented shell property System.Volume.BitLockerProtection. Your program will also be able to check this property without elevation.

If the value of this property is 1, 3, or 5, BitLocker is enabled on the drive. Any other value is considered off.

During my search for a solution to this problem, I found references to this shell property in HKEY_CLASSES_ROOT\Drive\shell\manage-bde\AppliesTo. Ultimately, this discovery lead me to this solution.

The Windows Property System is a low-level API, but you can use the wrapper that's available in the Windows API Code Pack.

Package

Install-Package WindowsAPICodePack

Using

using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;

Code

IShellProperty prop = ShellObject.FromParsingName("C:").Properties.GetProperty("System.Volume.BitLockerProtection");
int? bitLockerProtectionStatus = (prop as ShellProperty<int?>).Value;

if (bitLockerProtectionStatus.HasValue && (bitLockerProtectionStatus == 1 || bitLockerProtectionStatus == 3 || bitLockerProtectionStatus == 5))
   Console.WriteLine("ON");
else
   Console.WriteLine("OFF");
0
Natan On

The following COM method combined with reflection works with .NET 6 (and probably older versions) without requiring any external libraries. COM isn't really the most supported path but it is a good alternative.

        var netFwMgrType = Type.GetTypeFromProgID("Shell.Application", false);
        var manager = Activator.CreateInstance(netFwMgrType);
        var c = manager.GetType().InvokeMember("NameSpace", BindingFlags.InvokeMethod, null, manager, new object[] { "C:" });
        var self = c.GetType().InvokeMember("Self", BindingFlags.GetProperty, null, c, new object[] { });
        var result = self.GetType().InvokeMember("ExtendedProperty", BindingFlags.InvokeMethod, null, self, new object[] { "System.Volume.BitLockerProtection" });

You might need to add this to your csproj file depending on how you build your project.

<BuiltInComInteropSupport>true</BuiltInComInteropSupport>

The above doesn't work for LOCAL_SYSTEM account for some reason. So here is how you do it when you have admin privileges using WMI.

Even though the question is specifically asking without admin, I think this is a good place to have this piece of code. This requires the .NET System.Management library.

var result = new ManagementObjectSearcher(@"root\cimv2\security\MicrosoftVolumeEncryption", "SELECT * FROM Win32_Encryptablevolume")
                .Get().OfType<ManagementObject>()
                .Where(obj => obj.GetPropertyValue("DriveLetter").ToString() == "C:")
                .Select(obj => obj.GetPropertyValue("ConversionStatus"))
                .Cast<uint>()

Note that this returns a different code than the other approach. Documented here: https://learn.microsoft.com/en-us/windows/win32/secprov/getconversionstatus-win32-encryptablevolume