Mad about .NET A blog from Jose Fco Bonnin


Talking again about my virus there was another nice thing it was doing, the virus opened IE windows redirecting to an antivirus page (it was an ironic virus).

The cool thing is that it only tried to do it when I was connected to my router, setting the browser to work offline was not enough since it tried to connect anyway in the background, but when I had my cable disconnected the virus didn't try. So I guess the virus was monitoring the network status.

To reproduce this I think the best would be to use WMI, because it provides us all kind of information regarding the system including this one. I've build a very simple class that catches the WMI events and throws a custom event notifying that the cable is connected or disconnected.

If you want to build an application that makes use of it you only need to create the handlers for the events NetworkConnect and NetworkDisconnect, and use the methods Start and Stop for monitoring the network, I think they are quite descriptive. Don't forget to call Dispose when are finished with the NetworkManager.

   1: public class NetworkEventArgs
   2: {
   3:     string instanceName;
   4:  
   5:     public string InstanceName
   6:     {
   7:         get { return instanceName; }
   8:     }
   9:  
  10:     public NetworkEventArgs(string name)
  11:     {
  12:         instanceName = name;
  13:     }
  14: }
  15:  
  16: public class NetworkManager : IDisposable
  17: {
  18:     private ManagementEventWatcher wmiDisconnect;
  19:     private ManagementEventWatcher wmiConnect;
  20:  
  21:     public delegate void NetworkEventHandler(object sender, NetworkEventArgs e);
  22:  
  23:     public event NetworkEventHandler NetworkDisconnect;
  24:     public event NetworkEventHandler NetworkConnect;
  25:  
  26:     private bool disposed;
  27:  
  28:     #region Constructor
  29:     public NetworkManager()
  30:     {
  31:         wmiDisconnect = new ManagementEventWatcher(
  32:                 "SELECT * FROM MSNdis_StatusMediaDisconnect");
  33:         wmiConnect = new ManagementEventWatcher(
  34:                 "SELECT * FROM MSNdis_StatusMediaConnect");
  35:  
  36:         wmiDisconnect.Scope = new ManagementScope(@"root\WMI");
  37:         wmiConnect.Scope = new ManagementScope(@"root\WMI");
  38:  
  39:         wmiDisconnect.EventArrived += new EventArrivedEventHandler(WMIDisconnect_EventArrived);
  40:         wmiConnect.EventArrived += new EventArrivedEventHandler(WMIConnect_EventArrived);
  41:     }
  42:     #endregion Stop
  43:  
  44:     #region Wmi Events
  45:     private void WMIDisconnect_EventArrived(object sender, EventArrivedEventArgs e)
  46:     {
  47:         string instanceName = e.NewEvent.Properties["InstanceName"].Value as string;
  48:  
  49:         OnNetworkDisconnect(instanceName);
  50:     }
  51:  
  52:     private void WMIConnect_EventArrived(object sender, EventArrivedEventArgs e)
  53:     {
  54:         string instanceName = e.NewEvent.Properties["InstanceName"].Value as string;
  55:  
  56:         OnNetworkConnect(instanceName);
  57:     }
  58:     #endregion Wmi Events
  59:  
  60:     #region Event Firing
  61:     protected void OnNetworkConnect(string instanceName)
  62:     {
  63:         NetworkEventHandler networkConnect = NetworkConnect;
  64:  
  65:         if (networkConnect != null)
  66:             NetworkConnect(this, new NetworkEventArgs(instanceName));
  67:     }
  68:  
  69:     protected void OnNetworkDisconnect(string instanceName)
  70:     {
  71:         NetworkEventHandler networkDisconnect = NetworkDisconnect;
  72:  
  73:         if (networkDisconnect != null)
  74:             NetworkDisconnect(this, new NetworkEventArgs(instanceName));
  75:     }
  76:     #endregion Event Firing
  77:  
  78:     #region Start
  79:     public void Start()
  80:     {
  81:         if (disposed)
  82:             throw new ObjectDisposedException("NetworkManager");
  83:  
  84:         wmiDisconnect.Start();
  85:         wmiConnect.Start();
  86:     }
  87:     #endregion Start
  88:     
  89:     #region Stop
  90:     public void Stop()
  91:     {
  92:         if (disposed)
  93:             throw new ObjectDisposedException("NetworkManager");
  94:  
  95:         wmiDisconnect.Stop();
  96:         wmiConnect.Stop();
  97:     }
  98:     #endregion Stop
  99:  
 100:     #region Dispose Related
 101:     public void Dispose()
 102:     {
 103:         Dispose(true);
 104:  
 105:         GC.SuppressFinalize(this);
 106:     }
 107:  
 108:     protected virtual void Dispose(bool disposing)
 109:     {
 110:         if (!this.disposed)
 111:         {
 112:             if (disposing)
 113:             {
 114:                 Stop();
 115:  
 116:                 wmiConnect.Dispose();
 117:                 wmiDisconnect.Dispose();
 118:             }
 119:             disposed = true;
 120:         }
 121:     }
 122:  
 123:     ~NetworkManager()
 124:     {
 125:         Dispose(false);
 126:     }
 127:     #endregion Dispose Related
 128: }




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?