2013-05-31 2 views
16

Я ищу способ получить номер, который почти наверняка изменится при запуске кода на разных машинах и почти наверняка останется неизменным между двумя прогонами на одном компьютере.Как получить (почти) уникальный системный идентификатор в кросс-платформенном режиме?

Если бы я делал это как сценарий оболочки в Linux, я хотел бы использовать что-то вроде этого:

{ uname -n ; cat /proc/meminfo | head -n1 ; cat /proc/cpuinfo ; } | md5sum 

Но мне это нужно в C++ (с усилением) и по крайней мере на Windows, Linux и Mac.

+4

Есть очень хороший шанс, что * не * даст вам последовательные результаты на той же машине; большинство современных процессоров имеют динамическое масштабирование по частоте, а/proc/cpuinfo отражает мгновенную частоту! –

+0

[Возможный дубликат] (http://stackoverflow.com/questions/8475003/crossplatform-library-for-uniquely-identifying-the-machine-my-app-is-currently-r) – BoBTFish

+2

Похоже, что программа будет (в противном случае идентификатор звучит бесполезно). В этом случае вам может быть лучше, если вы получите уникальный идентификатор с сервера, к которому ваша программа подключается, и хранить его локально для последующего использования. –

ответ

36

Чтобы создать в основном уникальный идентификатор машины, вы можете получить несколько серийных номеров из различных частей оборудования в системе. У большинства процессоров будет серийный номер процессора, на каждом жестком диске есть номер, и каждая сетевая карта будет иметь уникальный MAC-адрес.

Вы можете получить их и построить отпечаток пальца для машины. Возможно, вы захотите, чтобы некоторые из этих чисел изменились, прежде чем объявлять его новой машиной. (например, если два из трех одинаковых, то машина такая же). Таким образом, вы можете сделать несколько изящно из-за обновления компонента.

Я обрезал код из одного из моих проектов, который получает эти числа.

Окна:

#include "machine_id.h" 

#define WIN32_LEAN_AND_MEAN   
#include <windows.h>  
#include <intrin.h>  
#include <iphlpapi.h>  


// we just need this for purposes of unique machine id. So any one or two mac's is  
// fine. 
u16 hashMacAddress(PIP_ADAPTER_INFO info)   
{   
    u16 hash = 0;   
    for (u32 i = 0; i < info->AddressLength; i++) 
    {  
     hash += (info->Address[i] << ((i & 1) * 8));   
    }  
    return hash;   
}   

void getMacHash(u16& mac1, u16& mac2)    
{   
    IP_ADAPTER_INFO AdapterInfo[32];     
    DWORD dwBufLen = sizeof(AdapterInfo);   

    DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen);     
    if (dwStatus != ERROR_SUCCESS)     
     return; // no adapters.  

    PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;  
    mac1 = hashMacAddress(pAdapterInfo);    
    if (pAdapterInfo->Next)  
     mac2 = hashMacAddress(pAdapterInfo->Next); 

    // sort the mac addresses. We don't want to invalidate  
    // both macs if they just change order.   
    if (mac1 > mac2)  
    {  
     u16 tmp = mac2;  
     mac2 = mac1;   
     mac1 = tmp;   
    }  
}   

u16 getVolumeHash()  
{   
    DWORD serialNum = 0; 

    // Determine if this volume uses an NTFS file system.  
    GetVolumeInformation("c:\\", NULL, 0, &serialNum, NULL, NULL, NULL, 0);  
    u16 hash = (u16)((serialNum + (serialNum >> 16)) & 0xFFFF);    

    return hash;   
}   

u16 getCpuHash()   
{   
    int cpuinfo[4] = { 0, 0, 0, 0 };     
    __cpuid(cpuinfo, 0);   
    u16 hash = 0;   
    u16* ptr = (u16*)(&cpuinfo[0]); 
    for (u32 i = 0; i < 8; i++) 
     hash += ptr[i];  

    return hash;   
}   

const char* getMachineName()  
{   
    static char computerName[1024]; 
    DWORD size = 1024;  
    GetComputerName(computerName, &size);   
    return &(computerName[0]);  
} 

Linux и OSX:

#include <stdio.h> 
#include <string.h> 
#include <unistd.h>   
#include <errno.h>   
#include <sys/types.h>  
#include <sys/socket.h>  
#include <sys/ioctl.h> 
#include <sys/resource.h>  
#include <sys/utsname.h>  
#include <netdb.h>   
#include <netinet/in.h>  
#include <netinet/in_systm.h>     
#include <netinet/ip.h>  
#include <netinet/ip_icmp.h> 
#include <assert.h> 

#ifdef DARWIN      
#include <net/if_dl.h>  
#include <ifaddrs.h>   
#include <net/if_types.h>  
#else //!DARWIN    
// #include <linux/if.h>   
// #include <linux/sockios.h> 
#endif //!DARWIN    

const char* getMachineName() 
{ 
    static struct utsname u; 

    if (uname(&u) < 0)  
    {  
     assert(0);    
     return "unknown";  
    }  

    return u.nodename;   
} 


//---------------------------------get MAC addresses ------------------------------------unsigned short-unsigned short----------   
// we just need this for purposes of unique machine id. So any one or two mac's is fine.    
unsigned short hashMacAddress(unsigned char* mac)     
{ 
    unsigned short hash = 0;    

    for (unsigned int i = 0; i < 6; i++)    
    {  
     hash += (mac[i] << ((i & 1) * 8));   
    }  
    return hash;    
} 

void getMacHash(unsigned short& mac1, unsigned short& mac2)  
{ 
    mac1 = 0;     
    mac2 = 0;     

#ifdef DARWIN     

    struct ifaddrs* ifaphead; 
    if (getifaddrs(&ifaphead) != 0)   
     return;     

    // iterate over the net interfaces   
    bool foundMac1 = false; 
    struct ifaddrs* ifap;  
    for (ifap = ifaphead; ifap; ifap = ifap->ifa_next)     
    {  
     struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr;  
     if (sdl && (sdl->sdl_family == AF_LINK) && (sdl->sdl_type == IFT_ETHER))     
     {  
      if (!foundMac1) 
      {     
      foundMac1 = true;     
      mac1 = hashMacAddress((unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen));  
      } else {   
      mac2 = hashMacAddress((unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen));  
      break;   
      }     
     }  
    }  

    freeifaddrs(ifaphead); 

#else // !DARWIN    

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);     
    if (sock < 0) return; 

    // enumerate all IP addresses of the system   
    struct ifconf conf;  
    char ifconfbuf[ 128 * sizeof(struct ifreq) ];  
    memset(ifconfbuf, 0, sizeof(ifconfbuf));   
    conf.ifc_buf = ifconfbuf; 
    conf.ifc_len = sizeof(ifconfbuf);   
    if (ioctl(sock, SIOCGIFCONF, &conf))  
    {  
     assert(0);    
     return;     
    }  

    // get MAC address   
    bool foundMac1 = false; 
    struct ifreq* ifr;   
    for (ifr = conf.ifc_req; (char*)ifr < (char*)conf.ifc_req + conf.ifc_len; ifr++) 
    {  
     if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data)   
     continue; // duplicate, skip it  

     if (ioctl(sock, SIOCGIFFLAGS, ifr))   
     continue; // failed to get flags, skip it  
     if (ioctl(sock, SIOCGIFHWADDR, ifr) == 0)  
     {  
     if (!foundMac1) 
     { 
      foundMac1 = true;     
      mac1 = hashMacAddress((unsigned char*)&(ifr->ifr_addr.sa_data));  
     } else {    
      mac2 = hashMacAddress((unsigned char*)&(ifr->ifr_addr.sa_data));  
      break;   
     } 
     }  
    }  

    close(sock);    

#endif // !DARWIN    

    // sort the mac addresses. We don't want to invalidate     
    // both macs if they just change order.  
    if (mac1 > mac2)   
    {  
     unsigned short tmp = mac2;   
     mac2 = mac1;   
     mac1 = tmp;    
    }  
} 

unsigned short getVolumeHash()   
{ 
    // we don't have a 'volume serial number' like on windows. Lets hash the system name instead.  
    unsigned char* sysname = (unsigned char*)getMachineName();  
    unsigned short hash = 0;    

    for (unsigned int i = 0; sysname[i]; i++)   
     hash += (sysname[i] << ((i & 1) * 8));  

    return hash;    
} 

#ifdef DARWIN     
#include <mach-o/arch.h>  
unsigned short getCpuHash()    
{   
    const NXArchInfo* info = NXGetLocalArchInfo();  
    unsigned short val = 0;    
    val += (unsigned short)info->cputype;    
    val += (unsigned short)info->cpusubtype;    
    return val;    
}   

#else // !DARWIN    

static void getCpuid(unsigned int* p, unsigned int ax)  
{   
    __asm __volatile   
    ( "movl %%ebx, %%esi\n\t"    
     "cpuid\n\t"   
     "xchgl %%ebx, %%esi" 
     : "=a" (p[0]), "=S" (p[1]),   
      "=c" (p[2]), "=d" (p[3])    
     : "0" (ax)   
    );  
}   

unsigned short getCpuHash()    
{   
    unsigned int cpuinfo[4] = { 0, 0, 0, 0 };   
    getCpuid(cpuinfo, 0); 
    unsigned short hash = 0;    
    unsigned int* ptr = (&cpuinfo[0]);     
    for (unsigned int i = 0; i < 4; i++)    
     hash += (ptr[i] & 0xFFFF) + (ptr[i] >> 16); 

    return hash;    
}   
#endif // !DARWIN    

int main() 
{ 

    printf("Machine: %s\n", getMachineName()); 
    printf("CPU: %d\n", getCpuHash()); 
    printf("Volume: %d\n", getVolumeHash()); 
    return 0; 
}  
+2

Одна хорошая вещь об этом коде заключается в том, что он учитывает общий случай, когда пользователь имеет несколько сетевых адаптеров, каждый с другим MAC-адресом. –

+0

... это немного сложнее, чем я себе представлял :-) – cube

+2

Ну, я могу легко изменить адрес mac через терминал (ifconfig eth0 hw ether ...), CPUID не уникален, а разделен всеми процессорами того же модель, имя машины также могут быть легко изменены. Эту «уникальность» можно легко подделать. –

0

Может быть, вы можете создать практически уникальный идентификатор из уникальных аппаратных идентификаторов - MAC является универсально уникальным, вы можете также использовать cpu model

На моем взгляде, вы должны выбрать только те вещи, которые не могут быть изменены часто, как центральный процессор или локальная сеть/WLAN.

+0

Если он использует/proc/cpuinfo, он уже использует модель ЦП. – Claudio

+1

Вы намекаете на проблему в своем ответе: карты LAN/WLAN. Многие машины имеют * несколько сетевых карт, в частности ноутбуков, имеющих как беспроводные, так и проводные карты. Каждый из них будет иметь разные MAC-адреса. Кого вы собираетесь проверить? А что, если пользователь отключает этот и использует другой? –

+0

@claudio: не на окнах он этого не делает. –

0

Одно вполне портативное решение было бы использовать время изменения текущего исполняемого файла. stat функция доступна в unix и windows, хотя API отличается, поэтому вам нужно будет использовать IFDEFs.

Бинарный вариант вряд ли будет развернут точно в одно и то же время на разных машинах, поэтому идентификаторы должны быть уникальными. Недостатком является то, что двоичное обновление изменяет идентификаторы.

+0

Если это было все, что было необходимо, оно могло бы генерировать guid, либо локально - через OS api, либо с вызовом сервера для генератора направляющих. –

+0

@ RafaelBaptista Но uuid не выживет при перезапуске системы, а серверный компонент усложнит решение. Но на самом деле случайный uuid, записанный в файл (если отсутствует), является очень хорошим решением. Очень портативный и практически с вероятностью генерации дубликата. –

0

Я знаю, вопрос слишком стар для ответа. Но я неоднократно сталкивался с этой проблемой. Мне нравится решение accept, но если вы попробовали код, вы узнаете, что у него есть проблемы.

Во-первых, идентификатор CPU - это идентификатор продукта, это не серийный номер. Поэтому, если у вас есть тот же процессор на другом сервере, он просто не работает. также можно легко изменить MAC-адрес.

Если вы пытаетесь сделать это на Linux, вы можете попробовать использовать сервисы hal. то есть.

hal-get-property --udi /org/freedesktop/Hal/devices/computer --key system.hardware.uuid 

Но лучшее, что возможно сделать, если вы можете обеспечить соблюдение корневого доступа, и если вы хотите, чтобы ваши руки dirty-, чтобы посмотреть на код для dmidecode. Это позволит вам извлечь UUID Chasis, Bios, Video и System. Вы не можете победить это :) и с помощью нескольких настроек вы можете преобразовать их в класс.

Смежные вопросы