20

We are using the following code for retrieving active MAC address of a windows pc.

private static string macId()
{
    return identifier("Win32_NetworkAdapterConfiguration", "MACAddress", "IPEnabled");
}

private static string identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue)
{
    string result = "";
    System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
    System.Management.ManagementObjectCollection moc = mc.GetInstances();
    foreach (System.Management.ManagementObject mo in moc)
    {
        if (mo[wmiMustBeTrue].ToString() == "True")
        {
            //Only get the first one
            if (result == "")
            {
                try
                {
                    result = mo[wmiProperty].ToString();
                    break;
                }
                catch
                {
                }
            }
        }
    }
    return result;
}
//Return a hardware identifier
private static string identifier(string wmiClass, string wmiProperty)
{
    string result = "";
    System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
    System.Management.ManagementObjectCollection moc = mc.GetInstances();
    foreach (System.Management.ManagementObject mo in moc)
    {
        //Only get the first one
        if (result == "")
        {
            try
            {
                result = mo[wmiProperty].ToString();
                break;
            }
            catch
            {
            }
        }
    }
    return result;
}

It works fine to retrieve the MAC address. The problem is when the MAC address is spoofed then it returns the spoofed MAC address. We want to somehow retrieve the original MAC address which is unique and assigned at the factory. Is there any way to do so?

Sajib Mahmood
  • 3,382
  • 3
  • 37
  • 50
  • 11
    The whole point of spoofing the MAC is so that the computer (and software on it) believes it is the correct MAC. – Joe Mar 03 '12 at 12:53
  • @Joe, yes. My original question was "Is there really any way to uniquely identify any computer at all"? I got some suggestions that MAC address can be used as a unique identifier. That leads to this question. – Sajib Mahmood Mar 03 '12 at 13:17
  • 1
    Some other ideas here: http://stackoverflow.com/questions/671876/whats-a-good-way-to-uniquely-identify-a-computer – Joe Mar 03 '12 at 14:05
  • What is your worst-case scenario here? Why are you worried about the possibility that the MAC address has been changed? – Harry Johnston Mar 05 '12 at 03:39
  • @HarryJohnston I am not worried about licensing issue anymore. Right now I am asking this question from sheer curiosity. – Sajib Mahmood Mar 07 '12 at 05:37
  • 1
    It should be possible to retrieve the hardware MAC using IOCTL_NDIS_QUERY_GLOBAL_STATS and OID_FDDI_LONG_PERMANENT_ADDR, if someone wants to try to build a working solution. – Harry Johnston Mar 08 '12 at 22:08
  • @HarryJohnston, thanks for the lead but I haven't found working solution yet. May be I need more research. – Sajib Mahmood Mar 10 '12 at 08:36
  • Simple addendum: virtual machines are popular enough to consider them. It's almost impossible (**if someone want to do it**) to identify a clone from another. – Adriano Repetti Mar 13 '12 at 20:47
  • What do you mean 'spoofed'? The user or sysadmin has a perfect right to change the local MAC address to anything they like for any reason, typically including network management. – user207421 Mar 14 '12 at 04:21
  • @EJP, Forget 'spoofed'. I want to retrieve MAC address which was assigned at the factory and is supposed to be unique. Is there any way? – Sajib Mahmood Mar 25 '12 at 14:03
  • @SajibMahmood there is a way to do it. Posted answer below. – M Afifi Jun 26 '12 at 10:49

4 Answers4

21

I wish to give an alternative. I don't know if it really answer to 'a way to uniquely identify any computer'.
However, this method query the Win32_BIOS class in System.Management and return a string with high chances to be unique. (Waiting to be disavowed!!)

/// <summary>
/// BIOS IDentifier
/// </summary>
/// <returns></returns>
public static string BIOS_ID()
{
    return    GetFirstIdentifier("Win32_BIOS", "Manufacturer")
            + GetFirstIdentifier("Win32_BIOS", "SMBIOSBIOSVersion")
            + GetFirstIdentifier("Win32_BIOS", "IdentificationCode")
            + GetFirstIdentifier("Win32_BIOS", "SerialNumber")
            + GetFirstIdentifier("Win32_BIOS", "ReleaseDate")
            + GetFirstIdentifier("Win32_BIOS", "Version");
}

/// <summary>
/// ManagementClass used to read the first specific properties
/// </summary>
/// <param name="wmiClass">Object Class to query</param>
/// <param name="wmiProperty">Property to get info</param>
/// <returns></returns>
private static string GetFirstIdentifier(string wmiClass, string wmiProperty)
{
    string result = string.Empty;
    ManagementClass mc = new System.Management.ManagementClass(wmiClass);
    ManagementObjectCollection moc = mc.GetInstances();
    foreach (ManagementObject mo in moc)
    {
        //Only get the first one
        if (string.IsNullOrEmpty(result))
        {
            try
            {
                if (mo[wmiProperty] != null) result = mo[wmiProperty].ToString();
                break;
            }
            catch
            {
            }
        }
    }
    return result.Trim();
}
Steve
  • 213,761
  • 22
  • 232
  • 286
  • Thanks for the alternative although I already had it in my mind. Anyway, I appreciate any reasonable suggestions. Nothing to be disavowed. :) – Sajib Mahmood Mar 03 '12 at 13:55
  • Can these parameters change on a system format or a fresh Windows install ? – mrid Apr 03 '19 at 20:06
  • I would say no because they are tied to data into BIOS. But I have had not tested. – Steve Apr 03 '19 at 21:18
  • The only problem with this is that we have found many computer BIOS are just filled with default strings and often do not have the serial number even entered. This is particularly the case for the smaller fan less boxes which hardly fill in any details at all in the BIOS. – Barry Andrews Jun 17 '21 at 01:03
  • Here is an example from my PC BIOS. [System] Manufacturer = To Be Filled By O.E.M. Product = To Be Filled By O.E.M. Version = To Be Filled By O.E.M. SerialNum = To Be Filled By O.E.M. – Barry Andrews Jun 17 '21 at 01:03
8

There can be two alternatives.

  1. You can get the MAC address using the code snippet you gave before and check if that MAC address belongs to any NIC (Network Interface Card). If it doesn't belong to one, then the MAC address is obviously spoofed. Here is the code that Locates the NIC using a MAC adress

    using System.Net.Sockets;
    using System.Net;
    using System.Net.NetworkInformation;
    
    string localNicMac = "00:00:00:11:22:33".Replace(":", "-"); // Parse doesn't like colons
    
    var mac = PhysicalAddress.Parse(localNicMac);
    var localNic =
    NetworkInterface.GetAllNetworkInterfaces()
        .Where(nic => nic.GetPhysicalAddress().Equals(mac)) // Must use .Equals, not ==
        .SingleOrDefault();
    if (localNic == null)
    {
        throw new ArgumentException("Local NIC with the specified MAC could not be found.");
    }
    
    var ips =
        localNic.GetIPProperties().UnicastAddresses
        .Select(x => x.Address);
    
  2. Get the network card address directly.

    a. NWIF = dotnetClass "System.Net.NetworkInformation.NetworkInterface"  
    b. the_Mac_array = NWIF.GetAllNetworkInterfaces() -- this is an array of all the Networks  
    c. the_PhysicalAddress_Array = #()  
    d. for net in the_Mac_array where (net.NetworkInterfaceType.toString()) == "Ethernet" do append   the_PhysicalAddress_Array ((net.GetPhysicalAddress()).toString())  
    e. print the_PhysicalAddress_Array
    

(( I found it here http://snipplr.com/view/23006/ ))

HUNKY_Monkey
  • 362
  • 1
  • 3
  • 1
    @Guvante, No it doesn't. It'll show you that the spoofed MAC address belongs to the corresponding NIC. In fact, every code I have worked with so far retrieve the current MAC address, not the original one which was assigned at the factory. – Sajib Mahmood Mar 14 '12 at 06:48
  • Your Answer didn't work But you are the only person who tried to give the actual answer of this question.. So I decided to give the bounty to you.. :) – Abdur Rahman Mar 14 '12 at 12:15
  • My apologies. I should have mentioned that I haven't tested the code myself. But if a MAC is spoofed, I thought OS will not link it with the NIC. There must be some method running in the OS that links the spoofed MAC to the NIC which must store the original MAC somewhere. trying to find that might be too complicated. Here is an awesome paper written on MAC address spoofing detection. http://www.net-security.org/article.php?id=364 But I am not sure if it will help in detecting a spoofed MAC address in the system. @Rahman. Thanks a lot. The bounty is very much appreciated. :) – HUNKY_Monkey Mar 14 '12 at 13:08
  • @HUNKY_Monkey: There is probably a way to find it by using a non-standard API, but that would change with at least every NIC manufacturer, and potentially every NIC type. – Guvante Mar 14 '12 at 15:59
7

I had to write something similar a little while ago because I was using a number of hardware parameters for "activation" of my software.

Have a look at, DeviceIoControl & OID_802_3_PERMANENT_ADDRESS. Its a lot of interop code (my class for handling it is approximatley 200 lines), but it gets me the hardware code guaranteed.

Some code snippets to get you going,

private const uint IOCTL_NDIS_QUERY_GLOBAL_STATS = 0x170002;

[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(
        SafeFileHandle hDevice,
        uint dwIoControlCode,
        ref int InBuffer,
        int nInBufferSize,
        byte[] OutBuffer,
        int nOutBufferSize,
        out int pBytesReturned,
        IntPtr lpOverlapped);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern SafeFileHandle CreateFile(
    string lpFileName,
    EFileAccess dwDesiredAccess,
    EFileShare dwShareMode,
    IntPtr lpSecurityAttributes,
    ECreationDisposition dwCreationDisposition,
    EFileAttributes dwFlagsAndAttributes,
    IntPtr hTemplateFile);

[Flags]
internal enum EFileAccess : uint
{
    Delete = 0x10000,
    ReadControl = 0x20000,
    WriteDAC = 0x40000,
    WriteOwner = 0x80000,
    Synchronize = 0x100000,

    StandardRightsRequired = 0xF0000,
    StandardRightsRead = ReadControl,
    StandardRightsWrite = ReadControl,
    StandardRightsExecute = ReadControl,
    StandardRightsAll = 0x1F0000,
    SpecificRightsAll = 0xFFFF,

    AccessSystemSecurity = 0x1000000,       // AccessSystemAcl access type

    MaximumAllowed = 0x2000000,         // MaximumAllowed access type

    GenericRead = 0x80000000,
    GenericWrite = 0x40000000,
    GenericExecute = 0x20000000,
    GenericAll = 0x10000000
}

// Open a file handle to the interface
using (SafeFileHandle handle = FileInterop.CreateFile(deviceName,
    FileInterop.EFileAccess.GenericRead | FileInterop.EFileAccess.GenericWrite,
    0, IntPtr.Zero, FileInterop.ECreationDisposition.OpenExisting,
    0, IntPtr.Zero))
{
    int bytesReturned;
    // Set the OID to query the permanent address
    // http://msdn.microsoft.com/en-us/library/windows/hardware/ff569074(v=vs.85).aspx
    int OID_802_3_PERMANENT_ADDRESS = 0x01010101;

    // Array to capture the mac address
    var address = new byte[6];
    if (DeviceIoControl(handle, IOCTL_NDIS_QUERY_GLOBAL_STATS,
        ref OID_802_3_PERMANENT_ADDRESS, sizeof(uint),
        address, 6, out bytesReturned, IntPtr.Zero))
    {
        // Attempt to parse the MAC address into a string
        // any exceptions will be passed onto the caller
        return BitConverter.ToString(address, 0, 6);
    }
}
M Afifi
  • 4,645
  • 2
  • 28
  • 48
  • This looks promising. Thanks :) I'll test and let you know. – Sajib Mahmood Jun 26 '12 at 13:01
  • 1
    @SajibMahmood let me know if you get stuck. In my software it has run over 150,000 machines on over 250 different computer hardware. It always captured the mac address. You won't get that success rate unfortunately with WMI. – M Afifi Jun 26 '12 at 13:59
  • @SajibMahmood yes, Windows XP 32 bit, to Windows 7 64 bit have been tested. – M Afifi Jul 11 '12 at 11:53
  • Then it might be my mistake. For some reason I still haven't found any success with this one. However, I couldn't give it much time because of other things. Let me try again. Thanks for your cooperation. – Sajib Mahmood Jul 11 '12 at 12:01
  • there is no `EFileShare`, `ECreationDisposition`, or `EFileAttributes` ? – mrid Apr 03 '19 at 23:56
1

Well, I wouldn't bet all my money on the order in which NetworkInterface class lists NetworkInterfaces. My mainboard has 2 adapters and the order seems to switch every time I reboot.

So here is a suggestion, which worked for me (BTW : credits goes probably to another awesome stackoverflow contributer, ty) :

    public static string GetMACAddress()
    {
        NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
        //for each j you can get the MAC 
        PhysicalAddress address = nics[0].GetPhysicalAddress();
        byte[] bytes = address.GetAddressBytes();

        string macAddress = "";

        for (int i = 0; i < bytes.Length; i++)
        {
            // Format the physical address in hexadecimal. 
            macAddress += bytes[i].ToString("X2");
            // Insert a hyphen after each byte, unless we are at the end of the address. 
            if (i != bytes.Length - 1)
            {
                macAddress += "-";
            }
        }

        return macAddress;
    }