using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; // ReSharper disable once CheckNamespace namespace VMProtect { public class HardwareID { private const int OldMethodBlocks = 8; private const int MaxBlocks = 16 + OldMethodBlocks; private const uint TypeMask = 3; private enum BlockType { Cpu, Host, Mac, Hdd }; private readonly int _startBlock; private readonly List _blocks = new List(); public HardwareID() { // old methods GetCpu(0); GetCpu(1); _startBlock = _blocks.Count; // new methods, we'll return HWID starting from this DWORD GetCpu(2); GetMachineName(); GetHdd(); GetMacAddresses(); } private void AddBlock(byte[] p, BlockType type) { if (_blocks.Count == MaxBlocks) return; // no free space if (p.Length == 0) return; using (var hash = new SHA1Managed()) { var h = hash.ComputeHash(p); var block = (uint)((h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]); block &= ~TypeMask; // zero two lower bits block |= (uint)type & TypeMask; // set type bits // check existing blocks for (var i = _blocks.Count; i > _startBlock; i--) { var prevBlock = _blocks[i - 1]; if (prevBlock == block) return; if ((prevBlock & TypeMask) != (block & TypeMask)) break; } _blocks.Add(block); } } private void GetCpu(int method) { //TODO: foreach cpu: var info = CpuId.Invoke(1); if ((info[0] & 0xFF0) == 0xFE0) info[0] ^= 0x20; // fix Athlon bug info[1] &= 0x00FFFFFF; // mask out APIC Physical ID if (method == 2) { info[2] = 0; } else if (method == 1) { info[2] &= ~(1 << 27); } var infob = new byte[16]; Buffer.BlockCopy(info, 0, infob, 0, infob.Length); AddBlock(infob, BlockType.Cpu); } private void GetMachineName() { try { var hn = Encoding.Unicode.GetBytes(Dns.GetHostName().ToUpperInvariant()); AddBlock(hn, BlockType.Host); } catch (SocketException) { } } private void ProcessMac(byte[] p) { // this big IF construction allows to put constants to the code, not to the data segment // it is harder to find them in the code after virtualisation var dw = (p[0] << 16) + (p[1] << 8) + p[2]; if (dw == 0x000569 || dw == 0x000C29 || dw == 0x001C14 || dw == 0x005056 || // vmware dw == 0x0003FF || dw == 0x000D3A || dw == 0x00125A || dw == 0x00155D || dw == 0x0017FA || dw == 0x001DD8 || dw == 0x002248 || dw == 0x0025AE || dw == 0x0050F2 || // microsoft dw == 0x001C42 || // parallels dw == 0x0021F6) // virtual iron return; AddBlock(p, BlockType.Mac); } private void GetMacAddresses() { var blockCountNoMac = _blocks.Count; byte[] paBytes = null; foreach (var nic in NetworkInterface.GetAllNetworkInterfaces()) { if(nic.NetworkInterfaceType != NetworkInterfaceType.Ethernet) continue; paBytes = nic.GetPhysicalAddress().GetAddressBytes(); if (paBytes.Length >= 3) ProcessMac(paBytes); } if (blockCountNoMac == _blocks.Count && paBytes != null && paBytes.Length >= 3) AddBlock(paBytes, BlockType.Mac); } private void GetHdd() { try { switch (Environment.OSVersion.Platform) { case PlatformID.MacOSX: //TODO /* DASessionRef session = DASessionCreate(NULL); if (session) { struct statfs statFS; statfs ("/", &statFS); DADiskRef disk = DADiskCreateFromBSDName(NULL, session, statFS.f_mntfromname); if (disk) { CFDictionaryRef descDict = DADiskCopyDescription(disk); if (descDict) { CFUUIDRef value = (CFUUIDRef)CFDictionaryGetValue(descDict, CFSTR("DAVolumeUUID")); CFUUIDBytes bytes = CFUUIDGetUUIDBytes(value); AddBlock(&bytes, sizeof(bytes), BLOCK_HDD); CFRelease(descDict); } CFRelease(disk); } CFRelease(session); } */ break; case PlatformID.Unix: //TODO: нет ли здесь лишних дисков? var rootUuids = new DirectoryInfo ("/dev/disk/by-uuid"); var uuids = new StringBuilder(); foreach (var f in rootUuids.GetFiles("*")) { uuids.Append(f.Name); } if(uuids.Length > 0) AddBlock(Encoding.UTF8.GetBytes(uuids.ToString()), BlockType.Hdd); break; default: /* * mono for windows: unimplemented * * var moType = Assembly.Load("System.Management, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"). GetType("System.Management.ManagementObject"); var disk = Activator.CreateInstance(moType, BindingFlags.CreateInstance, null, new object[] { string.Format("Win32_LogicalDisk.DeviceID='{0}:'", Environment.SystemDirectory[0]) }, CultureInfo.InvariantCulture); var props = moType.InvokeMember("Properties", BindingFlags.GetProperty, null, disk, null); var sn = props.GetType().GetMethod("get_Item").Invoke(props, new object[] { "volumeSerialNumber" }); var snv = sn.GetType().GetMethod("get_Value").Invoke(sn, null).ToString(); Console.WriteLine("GetHdd volumeSerialNumber = {0}", snv); var bytes = BitConverter.GetBytes(Convert.ToUInt32(snv, 16));*/ var driveLetter = Path.GetPathRoot(Environment.SystemDirectory); uint serialNumber = 0; uint maxComponentLength = 0, fileSystemFlags = 0; if (Win32.GetVolumeInformation(driveLetter, null, 0, ref serialNumber, ref maxComponentLength, ref fileSystemFlags, null, 0) && serialNumber != 0) { AddBlock(BitConverter.GetBytes(serialNumber), BlockType.Hdd); } break; } } catch (Exception) { // ignored } } public byte[] GetBytes() { var ms = new MemoryStream(); for (var i = _startBlock; i < _blocks.Count; i++) { ms.Write(BitConverter.GetBytes(_blocks[i]), 0, 4); } return ms.ToArray(); } public override string ToString() { return Convert.ToBase64String(GetBytes()); } public bool IsCorrect(byte[] p) { if (p.Length == 0 || (p.Length & 3) != 0) return false; var equals = new bool[4]; var found = new bool[4]; foreach (var id1 in _blocks) { found[id1 & 3] = true; for (var i = 0; i < p.Length; i += 4) { var id2 = BitConverter.ToUInt32(p, i); if (id1 == id2) { equals[id1 & 3] = true; break; } } } // 1. check CPU if (!equals[0]) return false; // 2. check if at least 2 of 3 items are OK var n = 0; var c = 0; for (var i = 0; i < 4; i++) { if (found[i]) c++; if (equals[i]) n++; } return n == c || n >= 3; } /*public bool IsCorrect(CryptoContainer &cont, size_t offset, size_t size) { if (size == 0 || (size & 3) || size > MAX_BLOCKS * sizeof(uint32_t)) return false; uint32_t buff[MAX_BLOCKS]; for (size_t i = 0; i < size / sizeof(uint32_t); i++) { buff[i] = cont.GetDWord(offset + i * sizeof(uint32_t)); } return IsCorrect(reinterpret_cast(buff), size); }*/ } }