Friday, December 17, 2010

Developing USB Storage Device Protection Tool with C#

Introduction
USB storage devices (flash drives, USB sticks etc) offer many advantages for us. However, at same time they cause security problems because it is easy to copy many files to a tiny USB memory in a few seconds. We might be having some secure data on us PC which we do not want other user to copy through USB. Therefore, we many need to define USB storage policy to make USB drives write protected or not to be accessed through the system.
Background
Windows XP with SP2 or later operating systems provides soft protection of the USB ports. This feature can be enabled through the Registry Editor. UnderHKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\StorageDevicePolicies there is a DWORD value named WriteProtect. Setting this value to 1 makes USB drives write protected.
One may want to completely disable USB drives at all. For this, the Start value in the following registry key needed to be set 4: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\UsbStor
The Program 

 

For many users, dealing with registry keys is a boring job. A utility software developed for this job will be very useful.
Let’s develop our own application having following functionalities:
1.    Define USB storage devices access mode ( Full Access/ Read Only / Disabled )
2.    Enable / Disable registry editor
3.    Provide password protection for configuration
Second functionality is needed because otherwise configuration can be discarded easily by other people with the help of Registry Editor (Regedit). And third functionality, password protection, will prevent configuration change attempts of unauthorized users.
In Visual Studio 2005/2008 create a new Windows Forms Application project. Rename the empty form as frmMain.
To make use of Windows registry functionality within our C# program we need to add the following line to the beginning of our code: 
using Microsoft.Win32;
Before making any change in the configuration it is good to now current configuration. To achieve that define following methods and call it during load event of frmMain:
private void frmMain_Load(object sender, EventArgs e)
{
      CheckPasswordStatus();
      USB_getStatus();
      REG_getStatus();
}
Define CheckPasswordStatus() method as follows:
private void CheckPasswordStatus()
{
    Program.strPwdFilePath += "\\usbpolicy.pwd";
    if (File.Exists(Program.strPwdFilePath))
    {
        try
        {
            StreamReader fsPwdFile =
                new StreamReader(
                    new FileStream(Program.strPwdFilePath, 
                                   
FileMode.Open, 
                                   
FileAccess.Read));
            string pwd = fsPwdFile.ReadToEnd();
            if (String.IsNullOrEmpty(pwd) == false)
                Program.isPwdEnabled = true;
            fsPwdFile.Close();
        }
        catch { }
    }
    else
        Program.isPwdEnabled = false;
}
In this code segment, you can see that there are two global variables, a string named strPwdFilePath, and a Boolean variable named isPwdEnabled . These variables are defined in Program.cs file and are accessible to all forms our application. This is because we need access these variables again in two other forms related to password protection.

To learn status of USB write protection we need to read
 WriteProtect value underHKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\StorageDevicePolicies key with the help of following code defined in the body of USB_getStatus() method:
RegistryKey key;
try
{
key = Registry.LocalMachine.OpenSubKey
("SYSTEM\\CurrentControlSet\\Control\\StorageDevicePolicies");

      if (System.Convert.ToInt16(key.GetValue("WriteProtect", null)) == 1)
            USB_radio_ReadOnly.Checked = true;
      else
            USB_radio_FullAccess.Checked = true;
}
catch (NullReferenceException )
{
key = Registry.LocalMachine.OpenSubKey
            ("SYSTEM\\CurrentControlSet\\Control", true);
      key.CreateSubKey("StorageDevicePolicies");
      key.Close();
}
catch( Exception ) {}

As you can see, the code is written in the try and catch blocks because absence of StorageDevicePolicieskey will cause NullReferenceExcetion. In this case we need to catch the exception and create the key. We are not done yet. The Start value underHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\UsbStor key also needed to be checked. So, continue USB_getStatus() method with following lines of codes:
try
{
key = Registry.LocalMachine.OpenSubKey
            ("SYSTEM\\CurrentControlSet\\Services\\UsbStor");

      if (System.Convert.ToInt16(key.GetValue("Start", null)) == 4)
      {
            USB_radio_Disabled.Checked = true;
            return;
}
}

catch ( NullReferenceException )
{
key = Registry.LocalMachine.OpenSubKey
            ("SYSTEM\\CurrentControlSet\\Services", true);
key.CreateSubKey("USBSTOR");
key = Registry.LocalMachine.OpenSubKey
            ("SYSTEM\\CurrentControlSet\\Services\\UsbStor", true);

key.SetValue("Type", 1, RegistryValueKind.DWord);
      key.SetValue("Start", 3, RegistryValueKind.DWord);
      key.SetValue
("ImagePath", "system32\\drivers\\usbstor.sys",RegistryValueKind.ExpandString);
key.SetValue("ErrorControl", 1, RegistryValueKind.DWord);
      key.SetValue
("DisplayName", "USB Mass Storage Driver", RegistryValueKind.String);

key.Close();
}
           catch( Exception ) {}
To enable write protection define the following method:
void USB_enableWriteProtect()
{
RegistryKey key = Registry.LocalMachine.OpenSubKey
      ("SYSTEM\\CurrentControlSet\\Control\\StorageDevicePolicies", true);
      if (key == null)
      {
            Registry.LocalMachine.CreateSubKey
("SYSTEM\\CurrentControlSet\\Control\\StorageDevicePolicies",     RegistryKeyPermissionCheck.ReadWriteSubTree);
           key = Registry.LocalMachine.OpenSubKey
("SYSTEM\\CurrentControlSet\\Control\\StorageDevicePolicies", true);
key.SetValue("WriteProtect", 1, RegistryValueKind.DWord);
      }
else if (key.GetValue("WriteProtect") != (object)(1))
      {
key.SetValue("WriteProtect", 1, RegistryValueKind.DWord);
}
}


To disable write protection the following method:
void USB_disableWriteProtect()
{
RegistryKey key = Registry.LocalMachine.OpenSubKey
          ("SYSTEM\\CurrentControlSet\\Control\\StorageDevicePolicies",true);
if (key != null)
      {
            key.SetValue("WriteProtect", 0, RegistryValueKind.DWord);
}
      key.Close();
}


To disable USB storage devices (make them not available to use):
void USB_disableAllStorageDevices()
{
RegistryKey key = Registry.LocalMachine.OpenSubKey
                ("SYSTEM\\CurrentControlSet\\Services\\UsbStor",true);
if (key != null)
      {
key.SetValue("Start", 4, RegistryValueKind.DWord);
}
      key.Close();
}
To enable USB storage services:
void USB_enableAllStorageDevices()
{
RegistryKey key = Registry.LocalMachine.OpenSubKey
                ("SYSTEM\\CurrentControlSet\\Services\\UsbStor", true);
      if (key != null)
      {
            key.SetValue("Start", 3, RegistryValueKind.DWord);
      }
      key.Close();
}
To disable Regedit:
private void REG_DisableRegedit()
{
RegistryKey key =
Registry.CurrentUser.OpenSubKey
("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", true);
key.SetValue("DisableRegistryTools", 1, RegistryValueKind.DWord);
      key.Close();
}
To enable Regedit:

private void REG_EnableRegedit()
{
RegistryKey key =
Registry.CurrentUser.OpenSubKey
("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", true);
key.SetValue("DisableRegistryTools", 0, RegistryValueKind.DWord);
      key.Close();
}
Third functionality of our program is password protection to prevent unauthorized access to configuration. To achieve this we will design two others forms. In the first form we change define, change or remove password.
In the second one we will just do password confirmation.
There are several ways to store password. Our approach will be hashing password string first then storing it in a file in windows directory.
To Hash password string we will use following method:
public static string MD5Hash(string str)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
      byte[] data = System.Text.Encoding.ASCII.GetBytes( str );
      data = md5.ComputeHash(data);
      string md5Hash = System.Text.Encoding.ASCII.GetString(data);
      return md5Hash;
}
Since this method is used by more than one form it is good to make it static and define it in a static class in which common methods are defined.
You can download complete source code and executable file of application fromhttp://www.codeproject.com/KB/cs/usb_lock.aspx