PDA

View Full Version : Getting a guaranteed unique ID



princec
08-12-2004, 03:31 AM
Quick question:

How might I go about getting a unique identifier for a system, such as the HDD ID?

And what can I use that works on Win32, OSX, and Linux?

Cas :)

gmcbay
08-12-2004, 04:12 AM
There is nothing you can use that will be guaranteed to be unique across multiple platforms.

The closest thing is the MAC address of the network adapter (if any) hooked up to the system, but there's no 100% guarantee it will be unique because even though they are all supposed to ship with a unique assigned address, most of them can be flashed with a new address if the user really wants to.

Dominique Biesmans
08-12-2004, 05:46 AM
GUIDs would do the trick. It's very hard to generate these in a portable way. Here's an article that could get you started :

http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt


Oh, it's remarkably easy to generate these in windows, just look up UuidCreate() , and on linux/unix there's something called Libuuid, but don't take my word for it.

ggambett
08-12-2004, 05:48 AM
Do you need it to be tied to the hardware or just a random but unique ID? In that case I'd use a GUID... it's not guaranteed to be unique but it's highly unlikely that you generate two identical GUIDs. Even more so with the kind of sales figures indies have.

princec
08-12-2004, 05:49 AM
GUIDs aren't quite what I want, although I think there is a unique component in them... I want to be sure of getting the same number whenever I install an app on a particular machine, not a unique one every time.

Cas :)

princec
08-12-2004, 05:50 AM
Tied to the hardware. This is not an indie-specific requirement; it's for the LWJGL which has applications beyond indie games.

Cas :)

Bluecat
08-12-2004, 06:16 AM
Try the network card MAC Address (http://www.webopedia.com/TERM/M/MAC_address.html) That is unique for each network card in existence. Manufacturers are allocated blocks of them from some agency (I forget who) and set each card to one.

Using this will uniquely identify a computer. Of course this only works if the computer has a network card, but most do these days... either installed in a slot, or built into the motherboard. The number of computers that dont have a network card would be insignificant.

Edited to add a better link to the definition...

http://en.wikipedia.org/wiki/MAC_address

Greg Squire
08-12-2004, 09:28 AM
Are you planning on using this in some sort of copy protection technique?

I had an idea a while back of using the MAC address of the card as part of the registration/unlock key. This would make it so that that key would only unlock the game on that machine. If someone else tried to use that same key on another machine, it wouldn’t work. It seemed like a nice idea, but there are some caveats, such as what happens if the game is purchased by a parent on the “parents PC”, and then that key is used on the “kids PC” (there are many homes with more than one computer now, that they are so cheap). Also what happens if they have to replace the network card (it goes bad), and then later reinstall the game, or what happens when they upgrade to a new computer. This would frustrate some users with some valid uses.

I am against software piracy, but I think a lot of copy protection techniques today just end up alienating the legitimate users of the software. For example, I have some CDROM games for the kids that I cannot backup, and that really frustrates me, as the kids are incredibly rough on them. We’ve had well over 10 CDs destroyed in past several years (cracked, scratched beyond repair,etc.), but luckily they have been just the backups that we give them to use (the originals are put safely away).

Sorry to get off topic, but I was just wondering why the need for this unique ID?

Nikster
08-12-2004, 04:22 PM
M$ supply some crypto stuff in their API's which can genereat a key dependent on the machines HW, I might be talking B******s but I recall seeing somethnig like it... I will try hunting or the info...

kerchen
08-12-2004, 04:32 PM
My brother-in-law still believes it's worth building machines from scratch and he has had no end of trouble from Microsoft's activation scheme. Since he built his latest box, compatibility issues have been dogging him, causing him to swap out different components in an effort to get a stable configuration. He's had to do several re-installs of XP, and every time he's had to phone Microsoft and manually enter the umpteen-digit key as it is read to him by a Microsoftie. For some reason, the over-the-internet registration doesn't work for him. If it weren't a necessary piece of software, he would have dropped it long ago.

Oh, and I have no idea how to generate a unique identifier for a machine. :D

Nemesis
08-12-2004, 11:53 PM
Cas,

Not sure if you can get something such as the NIC card's MAC address using purely Java.

In the worst case you could create a custom C/C++ module integrated into your Java app using JNI. The C/C++ module sources would then use #define's containing the function code for each of your target platforms, say, PC, Linux, Mac or whatever.

It might entail a little work first time round but you would effectively end up with a reusable piece of functionality.

stan
08-13-2004, 12:31 AM
A friend once said he bought several network cards from the same manufacturer, and some of them had the same MAC address.

Cartman
08-13-2004, 01:07 PM
One company I worked at had the same problem on mac addresses on a set of cards from the same manufacturer. We had a terrible time figuring out what the problem was. I hear it happens more than you think.

MattInglot
08-13-2004, 02:13 PM
He's had to do several re-installs of XP, and every time he's had to phone Microsoft and manually enter the umpteen-digit key as it is read to him by a Microsoftie. For some reason, the over-the-internet registration doesn't work for him. If it weren't a necessary piece of software, he would have dropped it long ago.

Necessary? pfft! Using Windows 2000 here and avoiding XP features like "randomly stop working" and "take an hour to install an update" :)

You aren't really going to be able to get the same number each time princec, nor can you be 100% guaranteed it is unique afaik. Your best bet is to follow in Microsoft's footsteps and use multiple pieces of hardware to figure out some unique ID from. They put a lot of research into this and it's the best they could come up with.

"Always get same number when install on same machine" isn't possible because any component can get swapped out at any time. The hardware in my machine now is very different from the hardware two years ago, even though it's the same computer. Can you elaborate more on what you are trying to accomplish?

Greg Squire
08-13-2004, 03:27 PM
I remember reading somewhere that there's a unique ID encoded into Pentium III CPUs and above. (It was something a number of eCommerce companies wanted to uniquely identify the customer's computer.) Of course this would only work on more recent Intel hardware. I don't know if AMD is doing something similar. Maybe this is why MS uses hardware configs as part of their ID.

princec
08-14-2004, 04:34 AM
All I want is a reasonable - not foolproof - API call that'll get me an ID number that is largely static for the duration of the machine's lifetime. A harddisk identifier would be fine. MAC address would be fine. CPU ID would be fine. I can string all the available ones together.

Cas :)

Dom
08-14-2004, 06:53 AM
In C++:



unsigned long driveSerial = 1234;
GetVolumeInformation("C:\\", NULL, 0, (unsigned long*)&driveSerial, NULL, NULL, NULL, 0 );


This gets you the hard drive ID for C:

FlySim
08-14-2004, 05:14 PM
Here's some code to get info from the CPU - Intel/AMD
Dont remember where I got it...

-J.R.


CStdString getCpuType( )
{
CStdString retStr;
char str[255];

char VendorSign[13]; //We need somewhere to store our vendorstring
unsigned long MaxEAX; //This will be used to store the maximum EAX
//possible to call CPUID with.

__asm {
XOR EAX, EAX
//An efficient alternatvie to MOV EAX, 0x0

CPUID
//This instruction will load our registers with the data we need.

MOV dword ptr [VendorSign], EBX
//Copy the first 4 bytes in the VendorString from EBX.

MOV dword ptr [VendorSign+4], EDX
//Copy the next 4 bytes.

MOV dword ptr [VendorSign+8], ECX
//Copy the next 4 bytes.

MOV dword ptr MaxEAX, EAX
//EAX contains the maximum value to call CPUID with. Copy it to the
//MaxEAX variable.
}
VendorSign[12]=0; //The last character in the VendorSign can be anything.
//To make sure that it stops at the last character we add
//a zero character at the end


retStr += VendorSign;
sprintf( str, "%i", MaxEAX );
retStr += str;

printf("Vendor string: %s\n", VendorSign);
printf("Maximum EAX value: %i\n", MaxEAX);



if(strcmp(VendorSign, "GenuineIntel")==0) {
Comp1[0]="FPU"; //Floating Point Unit
Comp1[1]="VME"; //Virtual Mode Extension
Comp1[2]="DE"; //Debugging Extension
Comp1[3]="PSE"; //Page Size Extension
Comp1[4]="TSC"; //Time Stamp Counter
Comp1[5]="MSR"; //Model Specific Registers
Comp1[6]="PAE"; //Physical Address Extesnion
Comp1[7]="MCE"; //Machine Check Extension
Comp1[8]="CX8"; //CMPXCHG8 Instruction
Comp1[9]="APIC"; //On-chip APIC Hardware
Comp1[10]=""; //Reserved
Comp1[11]="SEP"; //SYSENTER SYSEXIT
Comp1[12]="MTRR"; //Machine Type Range Registers
Comp1[13]="PGE"; //Global Paging Extension
Comp1[14]="MCA"; //Machine Check Architecture
Comp1[15]="CMOV"; //Conditional Move Instrction
Comp1[16]="PAT"; //Page Attribute Table
Comp1[17]="PSE-36"; //36-bit Page Size Extension
Comp1[18]="PSN"; //96-bit Processor Serial Number
Comp1[19]="CLFSH"; //CLFLUSH Instruction
Comp1[20]=""; //Reserved
Comp1[21]="DS"; //Debug Trace Store
Comp1[22]="ACPI"; //ACPI Support
Comp1[23]="MMX"; //MMX Technology
Comp1[24]="FXSR"; //FXSAVE FXRSTOR (Fast save and restore)
Comp1[25]="SSE"; //Streaming SIMD Extensions
Comp1[26]="SSE2"; //Streaming SIMD Extensions 2
Comp1[27]="SS"; //Self-Snoop
Comp1[28]="HTT"; //Hyper-Threading Technology
Comp1[29]="TM"; //Thermal Monitor Supported
Comp1[30]="IA-64"; //IA-64 capable
Comp1[31]=""; //Reserved
}
else if(strcmp(VendorSign, "AuthenticAMD")==0) {
Comp1[0]="FPU"; //Floating Point Unit
Comp1[1]="VME"; //Virtual Mode Extension
Comp1[2]="DE"; //Debugging Extension
Comp1[3]="PSE"; //Page Size Extension
Comp1[4]="TSC"; //Time Stamp Counter
Comp1[5]="MSR"; //Model Specific Registers
Comp1[6]="PAE"; //Physical Address Extesnion
Comp1[7]="MCE"; //Machine Check Extension
Comp1[8]="CX8"; //CMPXCHG8 Instruction
Comp1[9]="APIC"; //On-chip APIC Hardware
Comp1[10]=""; //Reserved
Comp1[11]="SEP"; //SYSENTER SYSEXIT
Comp1[12]="MTRR"; //Machine Type Range Registers
Comp1[13]="PGE"; //Global Paging Extension
Comp1[14]="MCA"; //Machine Check Architecture
Comp1[15]="CMOV"; //Conditional Move Instrction
Comp1[16]="PAT"; //Page Attribute Table
Comp1[17]="PSE-36"; //36-bit Page Size Extension
Comp1[18]=""; //?
Comp1[19]="MPC"; //MultiProcessing Capable (I made this short up, correct?)
Comp1[20]=""; //Reserved
Comp1[21]=""; //?
Comp1[22]="MIE"; //AMD Multimedia Instruction Extensions (I made this short up, correct?)
Comp1[23]="MMX"; //MMX Technology
Comp1[24]="FXSR"; //FXSAVE FXRSTOR (Fast save and restore)
Comp1[25]="SSE"; //Streaming SIMD Extensions
Comp1[26]=""; //?
Comp1[27]=""; //?
Comp1[28]=""; //?
Comp1[29]=""; //?
Comp1[30]="3DNowExt"; //3DNow Instruction Extensions (I made this short up, correct?)
Comp1[31]="3DNow"; //3DNow Instructions (I made this short up, correct?)
}

unsigned long REGEAX, REGEBX, REGECX, REGEDX;
int dFamily, dModel, dStepping, dFamilyEx, dModelEx;
char dType[10];
int dComp1Supported[32];
int dBrand, dCacheLineSize, dLogicalProcessorCount, dLocalAPICID;

if(MaxEAX>=1) {
__asm {
MOV EAX, 1
CPUID
MOV [REGEAX], EAX
MOV [REGEBX], EBX
MOV [REGECX], ECX
MOV [REGEDX], EDX
}
dFamily=((REGEAX>>8)&0xF);
dModel=((REGEAX>>4)&0xF);
dStepping=(REGEAX&0xF);

dFamilyEx=((REGEAX>>20)&0xFF);
dModelEx=((REGEAX>>16)&0xF);
switch(((REGEAX>>12)&0x7)) {
case 0:
strcpy(dType, "Original");
break;
case 1:
strcpy(dType, "OverDrive");
break;
case 2:
strcpy(dType, "Dual");
break;
}

for(unsigned long C=1, Q=0;Q<32;C*=2, Q++) {
dComp1Supported[Q]=(REGEDX&C)!=0?1:0;
}


dBrand=REGEBX&0xFF;
dCacheLineSize=((strcmp(Comp1[19], "CLFSH")==0)&&(dComp1Supported[19]==1))?((REGEBX>>8)&0xFF)*8:-1;
dLogicalProcessorCount=((strcmp(Comp1[28], "HTT")==0)&&(dComp1Supported[28]==1))?((REGEBX>>16)&0xFF):-1;
dLocalAPICID=((REGEBX>>24)&0xFF); //This only works on P4 or later, must check for that in the future

}
retStr += dType;

sprintf( str, "%i%i%i",dFamily, dModel, dStepping);
retStr += str;

sprintf( str, "%i%i",dFamilyEx, dModelEx);
retStr += str;

for(unsigned long Q=0;Q<27;Q++)
{
if(dComp1Supported[Q])
{
retStr += Comp1[Q];
}
}


printf("%s\n", dType);
printf("Family %i, Model %i, Stepping %i\n", dFamily, dModel, dStepping);
printf("Extended Family %i, Extended Model %i\n", dFamilyEx, dModelEx);
printf("Supported flags: ");
for(Q=0;Q<27;Q++) {
if(dComp1Supported[Q]) {
printf("%s ", Comp1[Q]);
}
}
printf("\n");
printf("CacheLineSize: %i\n", dCacheLineSize);
printf("Logical processor count: %i\n", dLogicalProcessorCount);
printf("Local APIC ID: %i\n", dLocalAPICID);

return retStr;

}

Mark Sheeky
08-15-2004, 04:26 AM
unsigned long driveSerial = 1234;
GetVolumeInformation("C:\\", NULL, 0, (unsigned long*)&driveSerial, NULL, NULL, NULL, 0 );


I tried that, it fails. It returns the number on per-partition basis so that if you re-format then the number is different. It's a pain.

One idea I had is to locate the floppy drive serial if there is one. No-one upgrades those do they?

Mark
Cornutopia Games
http://www.cornutopia.net

Nikster
08-15-2004, 05:08 AM
Wipeee I eventually found what I was looking for :) Try searching for DPAPI in google, it's m$'s crypto library that will let you create machine/user keys which you can then use for whatever. unfortuantly this would only work on windows. (unless other OS's have omething similar)

Dom
08-15-2004, 05:35 AM
I tried that, it fails. It returns the number on per-partition basis so that if you re-format then the number is different. It's a pain.

One idea I had is to locate the floppy drive serial if there is one. No-one upgrades those do they?

Mark
Cornutopia Games
http://www.cornutopia.net

Reformatting 'C' is relatively rare - especially among genuine customers. Anyway, its one serial, as Cas says he will use multiple, the more you use - the less chance of a problem.

princec
08-15-2004, 09:20 AM
I definitely can't do it from Java, this is a new API call I want to add to the LWJGL so I will be able to do it :)

I'm not going to use it for copy protection in my games (though someone might) - instead I want to use it as a seed for the random number generator that creates the demo configuration, so the same computer always gets the same configuration. Or mostly always.

Cas :)

MattInglot
08-15-2004, 10:17 AM
Reformatting 'C' is relatively rare - especially among genuine customers. Anyway, its one serial, as Cas says he will use multiple, the more you use - the less chance of a problem.

May or may not be relevent to princec's application but some of us have multiple OSes and hence multiple drive Cs at once.

oNyx
08-15-2004, 11:01 AM
[...]
I'm not going to use it for copy protection in my games (though someone might) - instead I want to use it as a seed for the random number generator that creates the demo configuration, so the same computer always gets the same configuration. Or mostly always.

Hm. I would be afraid that doing so could lead to a somewhat asymmetrical distribution of configurations, because you won't have any control about the outcome. (It could be less random then you like)

For the webstart route... I would just use muffins there.

princec
08-15-2004, 01:42 PM
Now it's funny you should say that, because it just dawned on me over dinner that I could use a muffin and it'd all work out peachy :)

Cas :)

princec
08-15-2004, 01:44 PM
Good solutions here. I think a Webstart muffin will probably do the trick me now though.

Cas :)

oNyx
08-15-2004, 03:56 PM
On a side note - Kevin and me are slapping a small (10-20k) muffin lib together. Should be done (~80% atm), javadoced, debugged and tested pretty soon now.

princec
08-16-2004, 01:43 AM
Nice - where will it live when it's done?

Cas :)

oNyx
08-16-2004, 02:05 AM
That's a good question actually. Well, I intend to have a copy floating around on my webspace, but it's usual spot will be JGF (I guess). It's supposed to be a part of the upcoming new and improved webstart tutorial... so it should be hosted there, too (I mean... it's really really small ;)).