in Development

Registry Handling

These days I’ve been bothered by a virus that decided to live on my laptop.

I’m not going to explain how it arrived there, because I get really mad, I’m just going to give a common sense suggestion: never, never let your laptop to other people, doesn’t matter how friends you are ….

In any case there was an interesting part on all this, since the virus had nice things to learn which I’m going to try to reproduce using .NET:

The virus introduced lot of keys in the registry; one of the most interesting was added to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify. Maybe in another post I will explain why this one is interesting, but first let’s try to add or modify registry keys from C#.

The .NET Framework, once more, has done most of the work for us. We only need to take a look to the Microsoft.Win32 namespace and there we will find the classes “Registry” and “RegistryKey”, which makes extremely easy to work with the Windows Registry.

Take a look to the next code:

   1: string keyName = @"Software\Microsoft\Windows NT\CurrentVersion\WinLogon\Notify";
   2:  
   3: RegistryKey notifyKey = Registry.LocalMachine.OpenSubKey(keyName, true); 
   4: RegistryKey testKey = notifyKey.CreateSubKey("TestKey"); 
   5:  
   6: testKey.SetValue("Asynchronous", 0x00000001, RegistryValueKind.DWord); 
   7: testKey.SetValue("DllName", "FakeVirus.dll", RegistryValueKind.String); 
   8: testKey.SetValue("Impersonate", 0x00000000, RegistryValueKind.DWord); 
   9: testKey.SetValue("Logoff", "Logoff", RegistryValueKind.String); 
  10: testKey.SetValue("Logon", "Logon", RegistryValueKind.String);

As you already deducted we have created a new sub key called TestKey and introduced in it 5 different values, Asynchronous, DllName, Impersonate … very easy, isn’t it?

With the virus I wasn’t able to delete the registry entries added because they were regenerated immediately after any kind of change. This was done monitoring the entries, something that cannot be done directly with the managed classes but Windows supports through its API.

Reproduce this mechanism is not a big problem since even if .NET doesn’t have an equivalent method we can use p/invoke (Platform Invoke) to use the unmanaged functions of the Win32 API. For more information about it you can check the next link: http://msdn2.microsoft.com/en-us/library/ms724892.aspx

I add here the most interesting part of the code in order you can build your own monitor:

   1: // P/Invoke methods and constants 
   2:  
   3: internal class NativeMethods
   4: {
   5:    private NativeMethods()
   6:    {
   7:    } 
   8:  
   9:    [DllImport("advapi32.dll", SetLastError = true)]
  10:    internal static extern int RegNotifyChangeKeyValue(
  11:       IntPtr hKey,
  12:       [MarshalAs(UnmanagedType.Bool)] 
  13:       bool watchSubtree,
  14:       int dwNotifyFilter, 
  15:       IntPtr hEvent, 
  16:       [MarshalAs(UnmanagedType.Bool)] 
  17:       bool fAsynchronous 
  18:    ); 
  19:  
  20:    [DllImport("advapi32.dll", SetLastError = true)]
  21:    internal static extern int RegOpenKeyEx(
  22:       IntPtr hKey,
  23:       string subKey,
  24:       uint options,
  25:       int sam,
  26:       out IntPtr phkResult 
  27:    ); 
  28:  
  29:    [DllImport("advapi32.dll", SetLastError = true)]
  30:    internal static extern int RegCloseKey(
  31:       IntPtr hKey 
  32:    ); 
  33:  
  34:    internal const int HKEY_CLASSES_ROOT = unchecked((int)0x80000000);
  35:    internal const int HKEY_CURRENT_USER = unchecked((int)0x80000001);
  36:    internal const int HKEY_LOCAL_MACHINE = unchecked((int)0x80000002);
  37:    internal const int HKEY_USERS = unchecked((int)0x80000003);
  38:    internal const int HKEY_PERFORMANCE_DATA= unchecked((int)0x80000004);
  39:    internal const int HKEY_CURRENT_CONFIG = unchecked((int)0x80000005);
  40:    internal const int HKEY_DYN_DATA = unchecked((int)0x80000006); 
  41:  
  42:    internal const int KEY_NOTIFY = 0x0010; 
  43:  
  44:    internal const int REG_NOTIFY_CHANGE_NAME = 0x00000001;
  45:    internal const int REG_NOTIFY_CHANGE_ATTRIBUTES = 0x00000002;
  46:    internal const int REG_NOTIFY_CHANGE_LAST_SET = 0x00000004;
  47:    internal const int REG_NOTIFY_CHANGE_SECURITY = 0x00000008;
  48: } 
  49:  
  50: .................................
  51:  
  52: IntPtr parentKey = new IntPtr(NativeMethods.HKEY_LOCAL_MACHINE); 
  53:  
  54: string subKey = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\TestKey" 
  55:  
  56: IntPtr monitoredKey; 
  57:  
  58: try 
  59: { 
  60:    int ret = NativeMethods.RegOpenKeyEx(parentKey, subKey, 0, NativeMethods.KEY_NOTIFY, out monitoredKey);
  61:    if (ret != 0)
  62:    {
  63:       throw new Win32Exception(ret);
  64:    } 
  65:  
  66:    using (AutoResetEvent monitorEvent = new AutoResetEvent(false))
  67:    {
  68:       ret = NativeMethods.RegNotifyChangeKeyValue(parentKey, true, 
  69:                         NativeMethods.REG_NOTIFY_CHANGE_NAME | NativeMethods.REG_NOTIFY_CHANGE_ATTRIBUTES | 
  70:                         NativeMethods.REG_NOTIFY_CHANGE_LAST_SET | NativeMethods.REG_NOTIFY_CHANGE_SECURITY, 
  71:                         monitorEvent.Handle, true); 
  72:  
  73:       if (ret != 0)
  74:       {
  75:          throw new Win32Exception(ret);
  76:       }  
  77:  
  78:       monitorEvent.WaitOne();
  79:       // Do something after the registry has changed 
  80:    } 
  81: } 
  82: finally
  83: {
  84:    parentKey = IntPtr.Zero;
  85:    if (monitoredKey != IntPtr.Zero)
  86:    {
  87:       NativeMethods.RegCloseKey(monitoredKey);
  88:       monitoredKey = IntPtr.Zero;
  89:    } 
  90: }

Nice, don’t you think?