41

I want to get unique unchangeable Machine id Like Processor serial number of the computer for distribute a software with out copying.

I tried with processor serial number and hard disk serial number that all are changing after formatting and reinstalling the windows.

Any idea how i can get an unchangeable serial number of a computer?

Kiquenet
  • 13,271
  • 31
  • 133
  • 232
ush
  • 603
  • 4
  • 16
  • 22
  • A solution for what? A unique machine ID? Processor serial number? An unchangeable serial number? To distribute software "without copying"? The answer to each of those questions is different, which do you want? – Dour High Arch May 04 '12 at 19:00
  • Why so scared about obtaining machine IDs?. Everybody is scared about answering this... Clearly, the purpose for this is a copy protection..... – Brethlosze May 15 '15 at 02:11

11 Answers11

25

Maybe the easiest way is. Get the DeviceId Nuget package

And use it like

string deviceId = new DeviceIdBuilder()
.AddMachineName()
.AddMacAddress()
.AddProcessorId()
.AddMotherboardSerialNumber()
.ToString();

You can personalize the info used to generate the ID

Github Project

Daniel
  • 2,078
  • 19
  • 18
17

If you need a unique ID, you must first decide on your definition of unique. If you want/intend to use it for a copy-protection mechanism, then use something simple. This is because if someone really wants to use your software, (s)he will find a way to break your protection, given enough time and skill. In the case of a unique hardware ID, just think about virtual machines and you'll see that it is possible to spoof anything so someone can tamper with your software.

There is not much you can take from a PC and consider it as uniqueness over its whole lifetime. (Hardware changes will most likely require regenerating the ID at some point.) If you need something like that, you should investigate using an authentication USB Dongle which you can send to your customers.

If you just need some unique identifier that is not as hard to obtain, you could take the MAC address (unreliable), the OS serial number or the domain and user's name, but all of them are susceptible to forgery. However, if your main goal is to lock out unauthorised people, you won't sell anything because no one will want to use your software if it is hard to install, register or to move from one PC to another, although the last consideration is part and parcel of per-machine licensing. (This will likely happen quite often.)

As a first step, make it easy: Use something simple which is not easy to spoof in your target group. (For example, domain and user names can't be easily spoofed by enterprise customers, because their PCs are running in a larger environment implementing policies, etc.) Just forget about the others until you have that.

Maybe you can lock them out but that doesn't mean they're going to buy your software; they just won't use it anymore. What you have to consider is how many potential customers won't be or aren't willing to pay because you made it so complicated to use your program.

Agi Hammerthief
  • 1,915
  • 1
  • 19
  • 32
Oliver
  • 38,725
  • 7
  • 83
  • 136
  • 32
    This problem is more useful than just copy-proof software. So you shouldn't troll the guy for just asking. I'm facing the same problem trying to get a unique ID per machine and I'm not even remotely pretending to sell my software. USB-dongle for a software that can be distributed anywhere in the world? keep dreaming. – Adrian Salazar Mar 03 '13 at 19:07
  • 1
    Also, copy-proofing enterprise level server side installations. These types of installations don't change very often at all, so a complicated activation would not necessarily be such a bad thing since it is only done once, and typically done by the vendor's on-site installation team. – 7wp May 31 '13 at 15:17
  • 3
    I came here for security reasons, not licensing - I need to encrypt user's data with some machine specific key so that they cannot be retrieved if stolen (via phishing for example). – Tomáš Zato - Reinstate Monica Mar 25 '15 at 23:28
  • @TomášZato: In that case ask a new question with your specific needs. But for the quick shot you should take a look for salting or asynchronous encryption. – Oliver Mar 26 '15 at 07:39
  • From experience I know well that my question would be closed as duplicate of this one. The problems are not that different. – Tomáš Zato - Reinstate Monica Mar 26 '15 at 11:20
  • @TomášZato Actually, I'd say it's a radically different question. Different purposes for an Id have different requirements. This isn't a one-size-fits-all problem – Basic Apr 12 '15 at 15:15
  • @Sujoy It is unreliable, cause network drivers allow you to change it and some systems/drivers generate a new address regulary (after reboot, after disconnect, etc.). IMHO using it as unique machine id is not reliable. – Oliver May 02 '19 at 10:17
  • @Oilver would it be unreliable to read it even from a Winform application? – Sujoy May 02 '19 at 12:01
  • @Sujoy It is unreliable, cause the underlying system can change it at any time. Who reads this information (applicaiton, service, remote, etc.) is not relevant. It can be changed by the user and / or system at any given time. – Oliver May 02 '19 at 14:26
17

You can use WMI Code creator. I guess you can have a combination of "keys" (processorid,mac and software generated key).

using System.Management;
using System.Windows.Forms;

try
{
     ManagementObjectSearcher searcher = 
         new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor"); 

     foreach (ManagementObject queryObj in searcher.Get())
     {
         Console.WriteLine("-----------------------------------");
         Console.WriteLine("Win32_Processor instance");
         Console.WriteLine("-----------------------------------");
         Console.WriteLine("Architecture: {0}", queryObj["Architecture"]);
         Console.WriteLine("Caption: {0}", queryObj["Caption"]);
         Console.WriteLine("Family: {0}", queryObj["Family"]);
         Console.WriteLine("ProcessorId: {0}", queryObj["ProcessorId"]);
     }
}
catch (ManagementException e)
{
    MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
}

Win32_Processor

Retrieving Hardware Identifiers in C# with WMI by Peter Bromberg

unknown6656
  • 2,199
  • 2
  • 26
  • 40
PRR
  • 1,130
  • 7
  • 13
  • WMI is NOT unique. It does not have any unique fields. I have a bunch of computer that all WMI bios, cpu, videocard, network name them all have the same values. – Franck Jun 12 '18 at 12:47
15

I'd stay well away from using MAC addresses. On some hardware, the MAC address can change when you reboot. We learned quite early during our research not to rely on it.

Take a look at the article Developing for Software Protection and Licensing which has some pointers on how to design & implement apps to reduce piracy.

Obligatory disclaimer & plug: the company I co-founded produces the OffByZero Cobalt licensing solution. So it probably won't surprise you to hear that I recommend outsourcing your licensing, & focusing on your core competencies.

Duncan Bayne
  • 3,628
  • 4
  • 36
  • 59
  • 4
    I second that. MAC address is very unreliable. The list of mac addresses may change depending upon how the machine is connected to the internet. Also, the primary adapter may change each time the machine is booted. Services like CISCO VPN etc. further complicate the problem. – Santosh Tiwari Apr 15 '14 at 13:52
4

Check out this article. It is very exhaustive and you will find how to extract various hardware information.

Quote from the article:

To get hardware information, you need to create an object of ManagementObjectSearcher class.

using System.Management;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + Key);
foreach (ManagementObject share in searcher.Get()) {
    // Some Codes ...
}

The Key on the code above, is a variable that is replaced with appropriate data. For example, to get the information of the CPU, you have to replace the Key with Win32_Processor.

Laurent Etiemble
  • 25,779
  • 5
  • 52
  • 81
4

Yes, We could get a code which is combination of Physical Address, Unique Drive ID, Hard Drive ID (Volume Serial), CPU ID and BIOS ID. Example (Full example):

//Main physical hard drive ID
    private static string diskId()
    {
        return identifier("Win32_DiskDrive", "Model")
        + identifier("Win32_DiskDrive", "Manufacturer")
        + identifier("Win32_DiskDrive", "Signature")
        + identifier("Win32_DiskDrive", "TotalHeads");
    }
    //Motherboard ID
    private static string baseId()
    {
        return identifier("Win32_BaseBoard", "Model")
        + identifier("Win32_BaseBoard", "Manufacturer")
        + identifier("Win32_BaseBoard", "Name")
        + identifier("Win32_BaseBoard", "SerialNumber");
    }
Raj kumar
  • 995
  • 11
  • 17
3

edit: I just saw you meant in c#. Here is a better way with unmanaged code:

ManagementClass oMClass = new ManagementClass ("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection colMObj = oMCLass.GetInstances();
foreach(ManagementObject objMO in colMObj)
    Console.WriteLine(objMO["MacAddress"].ToString());
Blindy
  • 55,135
  • 9
  • 81
  • 120
  • 2
    You know that the MAC address is also "spoofable"? Besides, your code looks like C++, not like C#? – Webleeuw Jan 05 '10 at 08:00
  • Is there any solution plz suggest me – ush Jan 05 '10 at 08:01
  • 4
    Well if the processor/hdd serial numbers aren't sufficient, this is all you have left. If he described what he wanted to do instead of how he wants to do it, I might have had a better reply. – Blindy Jan 05 '10 at 08:01
3

I second Blindy's suggestion to use the MAC address of the (first?) network adapter. Yes, the MAC address can be spoofed, but this has side effects (you don't want two PCs with the same MAC address in the same network), and it's something that "your average pirate" won't do just to be able to use your software. Considering that there's no 100% solution against software piracy, the MAC address is a good compromise, IMO.

Note, however, that the address will change when the user adds, replaces or removes a network card (or replaces his old PC altogether), so be prepared to help your customers and give them a new key when they change their hardware configuration.

Heinzi
  • 151,145
  • 51
  • 326
  • 481
  • 6
    I've been using the MAC Address as a unique device identifier on the project I'm working on and just ran into a problem with using the first one, because it's the MS Loopback Adapter, and this always has the same MAC Address! Thought I'd share that and save others the confusion! – Matt Winward Dec 16 '11 at 13:40
  • isLoopback() for that interface returns true, here anyway, easily skipped. As note elsewhere, isVirtual() is NOT reliable, false for VirtualBox, here, as of now. – enigment Jun 07 '16 at 13:21
2

You should not use MAC, its bad way. Because some OS just changeing it every day. My expirience : Tools.CpuID.ProcessorId() + volumeSerial;

string volumeSerial = "";
    try {
        ManagementObject dsk = new ManagementObject(@"win32_logicaldisk.deviceid=""C:""");
        dsk.Get();
        volumeSerial = dsk["VolumeSerialNumber"].ToString();
    } catch {
        try {
            ManagementObject dsk = new ManagementObject(@"win32_logicaldisk.deviceid=""D:""");
            dsk.Get();
            volumeSerial = dsk["VolumeSerialNumber"].ToString();
        } catch { File.WriteAllText("disk.mising","need C or D"); Environment.Exit(0); }
    }

public class CpuID
    {
        [DllImport("user32", EntryPoint = "CallWindowProcW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
        private static extern IntPtr CallWindowProcW([In] byte[] bytes, IntPtr hWnd, int msg, [In, Out] byte[] wParam, IntPtr lParam);

        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool VirtualProtect([In] byte[] bytes, IntPtr size, int newProtect, out int oldProtect);

        const int PAGE_EXECUTE_READWRITE = 0x40;



        public static string ProcessorId()
        {
            byte[] sn = new byte[8];

            if (!ExecuteCode(ref sn))
                return "ND";

            return string.Format("{0}{1}", BitConverter.ToUInt32(sn, 4).ToString("X8"), BitConverter.ToUInt32(sn, 0).ToString("X8"));
        }

        private static bool ExecuteCode(ref byte[] result)
    {
        int num;

        /* The opcodes below implement a C function with the signature:
         * __stdcall CpuIdWindowProc(hWnd, Msg, wParam, lParam);
         * with wParam interpreted as an 8 byte unsigned character buffer.
         * */

        byte[] code_x86 = new byte[] {
            0x55,                      /* push ebp */
            0x89, 0xe5,                /* mov  ebp, esp */
            0x57,                      /* push edi */
            0x8b, 0x7d, 0x10,          /* mov  edi, [ebp+0x10] */
            0x6a, 0x01,                /* push 0x1 */
            0x58,                      /* pop  eax */
            0x53,                      /* push ebx */
            0x0f, 0xa2,                /* cpuid    */
            0x89, 0x07,                /* mov  [edi], eax */
            0x89, 0x57, 0x04,          /* mov  [edi+0x4], edx */
            0x5b,                      /* pop  ebx */
            0x5f,                      /* pop  edi */
            0x89, 0xec,                /* mov  esp, ebp */
            0x5d,                      /* pop  ebp */
            0xc2, 0x10, 0x00,          /* ret  0x10 */
        };
        byte[] code_x64 = new byte[] {
            0x53,                                     /* push rbx */
            0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, /* mov rax, 0x1 */
            0x0f, 0xa2,                               /* cpuid */
            0x41, 0x89, 0x00,                         /* mov [r8], eax */
            0x41, 0x89, 0x50, 0x04,                   /* mov [r8+0x4], edx */
            0x5b,                                     /* pop rbx */
            0xc3,                                     /* ret */
        };

        byte[] code;

        if (IsX64Process())
            code = code_x64;
        else 
            code = code_x86;

        IntPtr ptr = new IntPtr(code.Length);

        if (!VirtualProtect(code, ptr, PAGE_EXECUTE_READWRITE, out num))
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

        ptr = new IntPtr(result.Length);

        try {
            return (CallWindowProcW(code, IntPtr.Zero, 0, result, ptr) != IntPtr.Zero);
        } catch { System.Windows.Forms.MessageBox.Show("Память повреждена"); return false; }
    }

        private static bool IsX64Process()
        {
            return IntPtr.Size == 8;
        }

    }
Nik Shev
  • 21
  • 2
  • 2
    Just got into a situation where I used ProcessorId + VolumeSerialNumber and two laptops had exact the same ids for both ... – Fid Jun 23 '16 at 21:25
  • ProcessorId doesn't seem to generate unique values anymore: All machines here have the same ProcessorId (Different Win Version, different Vendor)... on Net 4.5 – jHilscher Jul 21 '17 at 07:49
  • an example of result array , from real life BFEBFBFF000006FBQ0WNWNFQF678084A BFEBFBFF000306A9NCYRXNJZF6815BA5 BFEBFBFF00030673K1HBRQ3ZCAF70541 078BFBFF000306A9BBW0BNBX1C70EEFF BFEBFBFF0001067AE0WRWOJZ68E3340B BFEBFBFF000306A9RCHBRRNTECACAE50 BFEBFBFF000306A9NBGVQNCCCC3A320F BFEBFBFF000206A7NBXGBRGDC642137D BFEBFBFF000306A9K0INZMKB12D2C5C7 BFEBFBFF00040651PAW0BOZV22B7BECF BFEBFBFF000306A9BCGRQMBRE829E19B 1F8BFBFF000306A9M1IWCMNYE2A678BC – Nik Shev May 20 '18 at 17:56
0

There are two ways possible to this that I know:

  1. Get the Processor id of the system:

    public string getCPUId()
    {
        string cpuInfo = string.Empty;
        ManagementClass mc = new ManagementClass("win32_processor");
        ManagementObjectCollection moc = mc.GetInstances();
    
        foreach (ManagementObject mo in moc)
        {
            if (cpuInfo == "")
            {
                //Get only the first CPU's ID
                cpuInfo = mo.Properties["processorID"].Value.ToString();
                break;
            }
        }
        return cpuInfo;
    }
    
  2. Get UUID of the system:

    public string getUUID()
    {
            Process process = new Process();
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
            startInfo.FileName = "CMD.exe";
            startInfo.Arguments = "/C wmic csproduct get UUID";
            process.StartInfo = startInfo;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.Start();
            process.WaitForExit();
            string output = process.StandardOutput.ReadToEnd();
            return output;
    }
    
0

The following site uses System.Management to accomplish the same is a very sleek way in a console application

Sujoy
  • 615
  • 7
  • 16