mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-31 03:00:05 +00:00
UniqueID: Use stable SMBIOS fields to generate ID on Windows
This commit adds fixes for generating unique hardware IDs on Windows. The SMBIOS is parsed to generate a unique ID based on hardware factors of the local machine.
This commit is contained in:
parent
66fa787b01
commit
ea917c596d
5 changed files with 441 additions and 30 deletions
|
|
@ -659,28 +659,180 @@ String SystemStats::getDisplayLanguage()
|
|||
return languagesBuffer.data();
|
||||
}
|
||||
|
||||
static constexpr DWORD generateProviderID (const char* string)
|
||||
{
|
||||
return (DWORD) string[0] << 0x18
|
||||
| (DWORD) string[1] << 0x10
|
||||
| (DWORD) string[2] << 0x08
|
||||
| (DWORD) string[3] << 0x00;
|
||||
}
|
||||
|
||||
static std::optional<std::vector<std::byte>> readSMBIOSData()
|
||||
{
|
||||
const auto sig = generateProviderID ("RSMB");
|
||||
const auto id = generateProviderID ("RSDT");
|
||||
|
||||
if (const auto bufLen = GetSystemFirmwareTable (sig, id, nullptr, 0); bufLen > 0)
|
||||
{
|
||||
std::vector<std::byte> buffer;
|
||||
|
||||
buffer.resize (bufLen);
|
||||
|
||||
if (GetSystemFirmwareTable (sig, id, buffer.data(), bufLen) == buffer.size())
|
||||
return std::make_optional (std::move (buffer));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String getLegacyUniqueDeviceID()
|
||||
{
|
||||
if (const auto dump = readSMBIOSData())
|
||||
{
|
||||
uint64_t hash = 0;
|
||||
const auto start = dump->data();
|
||||
const auto end = start + jmin (1024, (int) dump->size());
|
||||
|
||||
for (auto dataPtr = start; dataPtr != end; ++dataPtr)
|
||||
hash = hash * (uint64_t) 101 + (uint8_t) *dataPtr;
|
||||
|
||||
return String (hash);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String SystemStats::getUniqueDeviceID()
|
||||
{
|
||||
#define PROVIDER(string) (DWORD) (string[0] << 24 | string[1] << 16 | string[2] << 8 | string[3])
|
||||
|
||||
auto bufLen = GetSystemFirmwareTable (PROVIDER ("RSMB"), PROVIDER ("RSDT"), nullptr, 0);
|
||||
|
||||
if (bufLen > 0)
|
||||
if (const auto smbiosBuffer = readSMBIOSData())
|
||||
{
|
||||
HeapBlock<uint8_t> buffer { bufLen };
|
||||
GetSystemFirmwareTable (PROVIDER ("RSMB"), PROVIDER ("RSDT"), (void*) buffer.getData(), bufLen);
|
||||
|
||||
return [&]
|
||||
#pragma pack (push, 1)
|
||||
struct RawSMBIOSData
|
||||
{
|
||||
uint64_t hash = 0;
|
||||
const auto start = buffer.getData();
|
||||
const auto end = start + jmin (1024, (int) bufLen);
|
||||
uint8_t unused[4];
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
for (auto dataPtr = start; dataPtr != end; ++dataPtr)
|
||||
hash = hash * (uint64_t) 101 + *dataPtr;
|
||||
struct SMBIOSHeader
|
||||
{
|
||||
uint8_t id;
|
||||
uint8_t length;
|
||||
uint16_t handle;
|
||||
};
|
||||
#pragma pack (pop)
|
||||
|
||||
return String (hash);
|
||||
}();
|
||||
String uuid;
|
||||
const auto* asRawSMBIOSData = unalignedPointerCast<const RawSMBIOSData*> (smbiosBuffer->data());
|
||||
Span<const std::byte> content (smbiosBuffer->data() + sizeof (RawSMBIOSData), asRawSMBIOSData->length);
|
||||
|
||||
while (! content.empty())
|
||||
{
|
||||
const auto* header = unalignedPointerCast<const SMBIOSHeader*> (content.data());
|
||||
const auto* stringTable = unalignedPointerCast<const char*> (content.data() + header->length);
|
||||
std::vector<const char*> strings;
|
||||
|
||||
// Each table comprises a struct and a varying number of null terminated
|
||||
// strings. The string section is delimited by a pair of null terminators.
|
||||
// Some fields in the header are indices into the string table.
|
||||
|
||||
const auto sizeofStringTable = [stringTable, &strings, &content]
|
||||
{
|
||||
size_t tableLen = 0;
|
||||
|
||||
while (tableLen < content.size())
|
||||
{
|
||||
const auto* str = stringTable + tableLen;
|
||||
const auto n = strlen (str);
|
||||
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
strings.push_back (str);
|
||||
tableLen += n + 1;
|
||||
}
|
||||
|
||||
return jmax (tableLen, (size_t) 1) + 1;
|
||||
}();
|
||||
|
||||
const auto stringFromOffset = [&content, &strings = std::as_const (strings)] (size_t byteOffset)
|
||||
{
|
||||
if (const auto index = std::to_integer<size_t> (content[byteOffset]); 0 < index && index <= strings.size())
|
||||
return strings[index - 1];
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
systemManufacturer = 0x04,
|
||||
systemProductName = 0x05,
|
||||
systemSerialNumber = 0x07,
|
||||
systemUUID = 0x08, // 16byte UUID. Can be all 0xFF or all 0x00. Might be user changeable.
|
||||
systemSKU = 0x19,
|
||||
systemFamily = 0x1a,
|
||||
|
||||
baseboardManufacturer = 0x04,
|
||||
baseboardProduct = 0x05,
|
||||
baseboardVersion = 0x06,
|
||||
baseboardSerialNumber = 0x07,
|
||||
baseboardAssetTag = 0x08,
|
||||
|
||||
processorManufacturer = 0x07,
|
||||
processorVersion = 0x10,
|
||||
processorAssetTag = 0x21,
|
||||
processorPartNumber = 0x22
|
||||
};
|
||||
|
||||
switch (header->id)
|
||||
{
|
||||
case 1: // System
|
||||
{
|
||||
uuid += stringFromOffset (systemManufacturer);
|
||||
uuid += "\n";
|
||||
uuid += stringFromOffset (systemProductName);
|
||||
uuid += "\n";
|
||||
|
||||
char hexBuf[(16 * 2) + 1]{};
|
||||
const auto* src = content.data() + systemUUID;
|
||||
|
||||
for (auto i = 0; i != 16; ++i)
|
||||
snprintf (hexBuf + 2 * i, 3, "%02hhX", src[i]);
|
||||
|
||||
uuid += hexBuf;
|
||||
uuid += "\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: // Baseboard
|
||||
uuid += stringFromOffset (baseboardManufacturer);
|
||||
uuid += "\n";
|
||||
uuid += stringFromOffset (baseboardProduct);
|
||||
uuid += "\n";
|
||||
uuid += stringFromOffset (baseboardVersion);
|
||||
uuid += "\n";
|
||||
uuid += stringFromOffset (baseboardSerialNumber);
|
||||
uuid += "\n";
|
||||
uuid += stringFromOffset (baseboardAssetTag);
|
||||
uuid += "\n";
|
||||
break;
|
||||
|
||||
case 4: // Processor
|
||||
uuid += stringFromOffset (processorManufacturer);
|
||||
uuid += "\n";
|
||||
uuid += stringFromOffset (processorVersion);
|
||||
uuid += "\n";
|
||||
uuid += stringFromOffset (processorAssetTag);
|
||||
uuid += "\n";
|
||||
uuid += stringFromOffset (processorPartNumber);
|
||||
uuid += "\n";
|
||||
break;
|
||||
}
|
||||
|
||||
const auto increment = header->length + sizeofStringTable;
|
||||
content = Span (content.data() + increment, content.size() - increment);
|
||||
}
|
||||
|
||||
return String (uuid.hashCode64());
|
||||
}
|
||||
|
||||
// Please tell someone at JUCE if this occurs
|
||||
|
|
|
|||
|
|
@ -60,24 +60,64 @@ String SystemStats::getJUCEVersion()
|
|||
|
||||
StringArray SystemStats::getDeviceIdentifiers()
|
||||
{
|
||||
for (const auto flag : { MachineIdFlags::fileSystemId, MachineIdFlags::macAddresses })
|
||||
if (auto ids = getMachineIdentifiers (flag); ! ids.isEmpty())
|
||||
return ids;
|
||||
|
||||
jassertfalse; // Failed to create any IDs!
|
||||
return {};
|
||||
}
|
||||
|
||||
String getLegacyUniqueDeviceID();
|
||||
|
||||
StringArray SystemStats::getMachineIdentifiers (MachineIdFlags flags)
|
||||
{
|
||||
auto macAddressProvider = [] (StringArray& arr)
|
||||
{
|
||||
for (const auto& mac : MACAddress::getAllAddresses())
|
||||
arr.add (mac.toString());
|
||||
};
|
||||
|
||||
auto fileSystemProvider = [] (StringArray& arr)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
File f (File::getSpecialLocation (File::windowsSystemDirectory));
|
||||
#else
|
||||
File f ("~");
|
||||
#endif
|
||||
if (auto num = f.getFileIdentifier())
|
||||
arr.add (String::toHexString ((int64) num));
|
||||
};
|
||||
|
||||
auto legacyIdProvider = [] ([[maybe_unused]] StringArray& arr)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
arr.add (getLegacyUniqueDeviceID());
|
||||
#endif
|
||||
};
|
||||
|
||||
auto uniqueIdProvider = [] (StringArray& arr)
|
||||
{
|
||||
arr.add (getUniqueDeviceID());
|
||||
};
|
||||
|
||||
struct Provider { MachineIdFlags flag; void (*func) (StringArray&); };
|
||||
static const Provider providers[] =
|
||||
{
|
||||
{ MachineIdFlags::macAddresses, macAddressProvider },
|
||||
{ MachineIdFlags::fileSystemId, fileSystemProvider },
|
||||
{ MachineIdFlags::legacyUniqueId, legacyIdProvider },
|
||||
{ MachineIdFlags::uniqueId, uniqueIdProvider }
|
||||
};
|
||||
|
||||
StringArray ids;
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
File f (File::getSpecialLocation (File::windowsSystemDirectory));
|
||||
#else
|
||||
File f ("~");
|
||||
#endif
|
||||
if (auto num = f.getFileIdentifier())
|
||||
for (const auto& provider : providers)
|
||||
{
|
||||
ids.add (String::toHexString ((int64) num));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& address : MACAddress::getAllAddresses())
|
||||
ids.add (address.toString());
|
||||
if (hasBitValueSet (flags, provider.flag))
|
||||
provider.func (ids);
|
||||
}
|
||||
|
||||
jassert (! ids.isEmpty()); // Failed to create any IDs!
|
||||
return ids;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,6 +157,31 @@ public:
|
|||
*/
|
||||
static String getUniqueDeviceID();
|
||||
|
||||
/** Kinds of identifier that are passed to getMachineIdentifiers(). */
|
||||
enum class MachineIdFlags
|
||||
{
|
||||
macAddresses = 1 << 0, ///< All Mac addresses of the machine.
|
||||
fileSystemId = 1 << 1, ///< The filesystem id of the user's home directory (or system directory on Windows).
|
||||
legacyUniqueId = 1 << 2, ///< Only implemented on Windows. A hash of the full smbios table, may be unstable on certain machines.
|
||||
uniqueId = 1 << 3, ///< The most stable kind of machine identifier. A good default to use.
|
||||
};
|
||||
|
||||
/** Returns a list of strings that can be used to uniquely identify a machine.
|
||||
|
||||
To get multiple kinds of identifier at once, you can combine flags using
|
||||
bitwise-or, e.g. `uniqueId | legacyUniqueId`.
|
||||
|
||||
If a particular kind of identifier isn't available, it will be omitted from
|
||||
the StringArray of results, so passing `uniqueId | legacyUniqueId`
|
||||
may return 0, 1, or 2 results, depending on the platform and whether any
|
||||
errors are encountered.
|
||||
|
||||
If you've previously generated a machine ID and just want to check it against
|
||||
all possible identifiers, you can enable all of the flags and check whether
|
||||
the stored identifier matches any of the results.
|
||||
*/
|
||||
static StringArray getMachineIdentifiers (MachineIdFlags flags);
|
||||
|
||||
//==============================================================================
|
||||
// CPU and memory information..
|
||||
|
||||
|
|
@ -254,4 +279,6 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE (SystemStats)
|
||||
};
|
||||
|
||||
JUCE_DECLARE_SCOPED_ENUM_BITWISE_OPERATORS (SystemStats::MachineIdFlags)
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -324,7 +324,11 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
|
|||
|
||||
StringArray OnlineUnlockStatus::MachineIDUtilities::getLocalMachineIDs()
|
||||
{
|
||||
auto identifiers = SystemStats::getDeviceIdentifiers();
|
||||
auto flags = SystemStats::MachineIdFlags::macAddresses
|
||||
| SystemStats::MachineIdFlags::fileSystemId
|
||||
| SystemStats::MachineIdFlags::legacyUniqueId
|
||||
| SystemStats::MachineIdFlags::uniqueId;
|
||||
auto identifiers = SystemStats::getMachineIdentifiers (flags);
|
||||
|
||||
for (auto& identifier : identifiers)
|
||||
identifier = getEncodedIDString (identifier);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue