Refactor project structure
This commit is contained in:
562
core/anslicensing/hwid.cpp
Normal file
562
core/anslicensing/hwid.cpp
Normal file
@@ -0,0 +1,562 @@
|
||||
//
|
||||
// Copyright (c) 2014 ANSCENTER. All rights reserved.
|
||||
//
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "wmihelper.h"
|
||||
|
||||
#else // !_WIN32
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#ifdef __linux__
|
||||
# include <sys/ioctl.h>
|
||||
# include <netinet/in.h>
|
||||
# include <unistd.h>
|
||||
# include <string.h>
|
||||
|
||||
// Match Linux to FreeBSD
|
||||
# define AF_LINK AF_PACKET
|
||||
#else
|
||||
# include <net/if_dl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
#include "bitstream2.h"
|
||||
#include "bitstream3.h"
|
||||
#include "base32.h"
|
||||
#include "crc32.h"
|
||||
|
||||
#include "hwid.h"
|
||||
#include "except.h"
|
||||
#include "uniconv.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
string HardwareId::hardwareId;
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
const sockaddr_in* castToIP4(const sockaddr* addr) {
|
||||
if (addr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
else if (addr->sa_family == AF_INET) {
|
||||
// An IPv4 address
|
||||
return reinterpret_cast<const sockaddr_in*>(addr);
|
||||
}
|
||||
else {
|
||||
// Not an IPv4 address
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GetCPUIDPropertyList(std::list<std::string>* propList)
|
||||
{
|
||||
unsigned int level = 1, eax = 0, ebx, ecx, edx = 0;
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
__get_cpuid(level, &eax, &ebx, &ecx, &edx);
|
||||
#endif
|
||||
|
||||
char buf[17];
|
||||
sprintf(buf, "%08X%08X", edx, eax);
|
||||
propList->push_back(buf);
|
||||
}
|
||||
|
||||
void GetMACPropertyList(std::list<std::string>* propList, int maxCount)
|
||||
{
|
||||
// Head of the interface address linked list
|
||||
ifaddrs* ifap = NULL;
|
||||
|
||||
int r = getifaddrs(&ifap);
|
||||
|
||||
if (r != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ifaddrs* current = ifap;
|
||||
|
||||
if (current == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (current != NULL && maxCount > 0) {
|
||||
|
||||
const sockaddr_in* interfaceAddress = castToIP4(current->ifa_addr);
|
||||
const sockaddr_in* broadcastAddress = castToIP4(current->ifa_dstaddr);
|
||||
const sockaddr_in* subnetMask = castToIP4(current->ifa_netmask);
|
||||
|
||||
if ((current->ifa_addr != NULL) && (current->ifa_addr->sa_family == AF_LINK)) {
|
||||
#ifdef __linux__
|
||||
struct ifreq ifr;
|
||||
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
strcpy(ifr.ifr_name, current->ifa_name);
|
||||
ioctl(fd, SIOCGIFHWADDR, &ifr);
|
||||
close(fd);
|
||||
|
||||
uint8_t* MAC = reinterpret_cast<uint8_t*>(ifr.ifr_hwaddr.sa_data);
|
||||
|
||||
#else // Posix/FreeBSD/Mac OS
|
||||
|
||||
sockaddr_dl* sdl = (struct sockaddr_dl*)current->ifa_addr;
|
||||
uint8_t* MAC = reinterpret_cast<uint8_t*>(LLADDR(sdl));
|
||||
#endif
|
||||
if (MAC[0] != 0 || MAC[1] != 0 || MAC[2] != 0 || MAC[3] != 0 || MAC[4] != 0 || MAC[5] != 0)
|
||||
{
|
||||
char buf[18];
|
||||
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", MAC[0], MAC[1], MAC[2], MAC[3], MAC[4], MAC[5]);
|
||||
propList->push_back(buf);
|
||||
maxCount--;
|
||||
}
|
||||
}
|
||||
|
||||
current = current->ifa_next;
|
||||
}
|
||||
|
||||
freeifaddrs(ifap);
|
||||
ifap = NULL;
|
||||
}
|
||||
|
||||
void GetHDDPropertyList(std::list<std::string>* propList, int maxCount)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
unsigned short HardwareId::HashString(const char* str)
|
||||
{
|
||||
unsigned int crc = Crc32::Compute((const unsigned char*)str, strlen(str));
|
||||
|
||||
return (unsigned short)(crc >> 16);
|
||||
}
|
||||
|
||||
unsigned short HardwareId::HashInt(int val)
|
||||
{
|
||||
unsigned char buf[4] = { (unsigned char)(val >> 24), (unsigned char)(val >> 16), (unsigned char)(val >> 8), (unsigned char)(val & 0xFF) };
|
||||
unsigned int crc = Crc32::Compute(buf, 4);
|
||||
|
||||
return (unsigned short)(crc >> 16);
|
||||
}
|
||||
|
||||
// LOCALE-INDEPENDENT trim from start (in place)
|
||||
static inline void ltrim(std::string& s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||
return ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' && ch != 0xA0;
|
||||
}));
|
||||
}
|
||||
|
||||
// LOCALE-INDEPENDENT trim from end (in place)
|
||||
static inline void rtrim(std::string& s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||
return ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' && ch != 0xA0;
|
||||
}).base(), s.end());
|
||||
}
|
||||
|
||||
// trim from both ends (in place)
|
||||
static inline void trim(std::string& s) {
|
||||
ltrim(s);
|
||||
rtrim(s);
|
||||
}
|
||||
|
||||
// LOCALE-INDEPENDENT uppercase conversion
|
||||
static inline void toUpperInvariant(std::string& s) {
|
||||
for (char& c : s) {
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
c = c - 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize hardware string for consistent hashing
|
||||
static std::string NormalizeHardwareString(const std::string& input) {
|
||||
std::string s = input;
|
||||
|
||||
// Trim all types of whitespace
|
||||
trim(s);
|
||||
|
||||
// Remove all remaining whitespace characters
|
||||
s.erase(std::remove_if(s.begin(), s.end(), [](unsigned char c) {
|
||||
return c <= 32 || c == 0xA0; // Remove control chars and non-breaking space
|
||||
}), s.end());
|
||||
|
||||
// Locale-independent uppercase for ASCII
|
||||
toUpperInvariant(s);
|
||||
|
||||
// Remove non-ASCII characters (Korean text, etc.)
|
||||
s.erase(std::remove_if(s.begin(), s.end(), [](unsigned char c) {
|
||||
return c > 127;
|
||||
}), s.end());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
const char* HardwareId::GetCurrentHardwareId()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
WmiHelper wmi;
|
||||
#endif
|
||||
|
||||
BitStream3 bits(125);
|
||||
list<string> propList;
|
||||
unsigned char version = 1;
|
||||
unsigned short hash1, hash2, hash3, hash4;
|
||||
unsigned short h0 = HashInt(0);
|
||||
|
||||
bits.Write(&version, 3);
|
||||
|
||||
propList.clear();
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
wmi.GetPropertyList("SELECT * FROM Win32_ComputerSystemProduct", "UUID", &propList);
|
||||
|
||||
hash1 = h0;
|
||||
|
||||
string s;
|
||||
|
||||
for (list<string>::iterator iter = propList.begin(); iter != propList.end(); iter++)
|
||||
{
|
||||
s = NormalizeHardwareString(*iter);
|
||||
|
||||
if ((s.length() > 1)
|
||||
&& (s != "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
&& (s != "00000000000000000000000000000000"))
|
||||
{
|
||||
hash1 = HashString(s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
propList.push_back("4294967296");
|
||||
#endif
|
||||
|
||||
bits.WriteUInt16(hash1);
|
||||
|
||||
// CPU IDs - get up to 4 for better resilience
|
||||
propList.clear();
|
||||
|
||||
#ifdef _WIN32
|
||||
wmi.GetPropertyList("SELECT ProcessorId FROM Win32_Processor", "ProcessorId", &propList, 4);
|
||||
#else
|
||||
GetCPUIDPropertyList(&propList);
|
||||
#endif
|
||||
|
||||
// Normalize CPU IDs
|
||||
for (auto& cpu : propList) {
|
||||
cpu = NormalizeHardwareString(cpu);
|
||||
}
|
||||
|
||||
hash1 = (propList.size() > 0) ? HashString(propList.front().c_str()) : h0;
|
||||
hash2 = (propList.size() > 1) ? HashString(propList.back().c_str()) : h0;
|
||||
|
||||
bits.WriteUInt16(hash1);
|
||||
bits.WriteUInt16(hash2);
|
||||
|
||||
// MAC Addresses - get MORE adapters and sort for consistency
|
||||
propList.clear();
|
||||
|
||||
#ifdef _WIN32
|
||||
// Try physical PCI adapters first
|
||||
wmi.GetPropertyList("SELECT MACAddress FROM Win32_NetworkAdapter WHERE (AdapterType = \"Ethernet 802.3\") AND (MACAddress IS NOT NULL) AND PNPDeviceId LIKE \"%PCI%\"", "MACAddress", &propList, 6);
|
||||
|
||||
// Fallback to VMBUS for VMs
|
||||
if (propList.size() == 0)
|
||||
{
|
||||
wmi.GetPropertyList("SELECT MACAddress FROM Win32_NetworkAdapter WHERE (AdapterType = \"Ethernet 802.3\") AND (MACAddress IS NOT NULL) AND PNPDeviceId LIKE \"%VMBUS%\"", "MACAddress", &propList, 6);
|
||||
}
|
||||
#else
|
||||
GetMACPropertyList(&propList, 6);
|
||||
#endif
|
||||
|
||||
// Normalize and sort MAC addresses for consistent ordering
|
||||
for (auto& mac : propList) {
|
||||
mac = NormalizeHardwareString(mac);
|
||||
}
|
||||
propList.sort();
|
||||
|
||||
// Store up to 4 MAC hashes
|
||||
auto macIter = propList.begin();
|
||||
hash1 = (propList.size() > 0 && macIter != propList.end()) ? HashString((macIter++)->c_str()) : h0;
|
||||
hash2 = (propList.size() > 1 && macIter != propList.end()) ? HashString((macIter++)->c_str()) : h0;
|
||||
hash3 = (propList.size() > 2 && macIter != propList.end()) ? HashString((macIter++)->c_str()) : h0;
|
||||
hash4 = (propList.size() > 3 && macIter != propList.end()) ? HashString((macIter++)->c_str()) : h0;
|
||||
|
||||
bits.WriteUInt16(hash1);
|
||||
bits.WriteUInt16(hash2);
|
||||
bits.WriteUInt16(hash3);
|
||||
bits.WriteUInt16(hash4);
|
||||
|
||||
// HDD Serial Numbers - get more drives and sort
|
||||
propList.clear();
|
||||
|
||||
#ifdef _WIN32
|
||||
wmi.GetPropertyList("SELECT SerialNumber FROM Win32_PhysicalMedia WHERE SerialNumber IS NOT NULL", "SerialNumber", &propList, 4);
|
||||
#else
|
||||
GetHDDPropertyList(&propList, 4);
|
||||
#endif
|
||||
|
||||
// Normalize and sort HDD serials
|
||||
for (auto& serial : propList) {
|
||||
serial = NormalizeHardwareString(serial);
|
||||
}
|
||||
propList.sort();
|
||||
|
||||
auto hddIter = propList.begin();
|
||||
hash1 = (propList.size() > 0 && hddIter != propList.end()) ? HashString((hddIter++)->c_str()) : h0;
|
||||
hash2 = (propList.size() > 1 && hddIter != propList.end()) ? HashString((hddIter++)->c_str()) : h0;
|
||||
|
||||
bits.WriteUInt16(hash1);
|
||||
bits.WriteUInt16(hash2);
|
||||
|
||||
unsigned char* rawHwid = bits.GetBuffer();
|
||||
|
||||
BASE32 base32;
|
||||
|
||||
auto encHwid = base32.encode(rawHwid, 16);
|
||||
|
||||
hardwareId = encHwid.c_str();
|
||||
|
||||
hardwareId.erase(hardwareId.length() - 1);
|
||||
|
||||
unsigned i;
|
||||
unsigned insertPos;
|
||||
// separate character groups
|
||||
for (i = 0, insertPos = 5; i < 4; i++)
|
||||
{
|
||||
hardwareId.insert(insertPos, "-");
|
||||
insertPos += 6;
|
||||
}
|
||||
|
||||
return hardwareId.c_str();
|
||||
}
|
||||
|
||||
bool HardwareId::MatchCurrentHardwareId(const char* hwid)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WmiHelper wmi;
|
||||
#endif
|
||||
|
||||
unsigned short h0 = HashInt(0);
|
||||
string hardwareId((char*)hwid);
|
||||
|
||||
for (int i = 0, erasePos = 0; i < 4; i++)
|
||||
{
|
||||
erasePos += 5;
|
||||
hardwareId.erase(erasePos, 1);
|
||||
}
|
||||
|
||||
BASE32 base32;
|
||||
int bufLen;
|
||||
|
||||
int padLen;
|
||||
int len = base32.encode_pad_length(((int)hardwareId.length() * 5 + 7) >> 3, &padLen);
|
||||
|
||||
if (len > (int)hardwareId.length())
|
||||
hardwareId.append(len - hardwareId.length(), 'A');
|
||||
|
||||
if (padLen)
|
||||
hardwareId.append(padLen, '=');
|
||||
|
||||
auto buf = base32.decode(hardwareId.c_str(), hardwareId.length(), &bufLen);
|
||||
|
||||
BitStream3 bits;
|
||||
bits.Attach(buf.data(), 125);
|
||||
unsigned char version = 0;
|
||||
|
||||
bits.Read(&version, 3);
|
||||
|
||||
if ((version & 0x7) > 1)
|
||||
throw new LicensingException(STATUS_GENERIC_ERROR, "invalid hardware id version");
|
||||
|
||||
list<string> propList;
|
||||
set<unsigned short> storedHashes; // Store all hashes from license
|
||||
unsigned short hash1, hash2, hash3, hash4;
|
||||
|
||||
// Use a points-based matching system for resilience
|
||||
int matchPoints = 0;
|
||||
const int REQUIRED_POINTS = 2; // Need at least 2 components to match
|
||||
|
||||
// Read UUID hash
|
||||
bits.ReadUInt16(&hash1);
|
||||
|
||||
if (version == 0)
|
||||
{
|
||||
// Legacy version 0 support (memory size based)
|
||||
if (hash1 != h0)
|
||||
{
|
||||
unsigned long long memSize = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
wmi.GetPropertyList("SELECT Capacity FROM Win32_PhysicalMemory", "Capacity", &propList);
|
||||
#else
|
||||
propList.push_back("4294967296");
|
||||
#endif
|
||||
|
||||
if (propList.size() > 0)
|
||||
{
|
||||
for (list<string>::iterator iter = propList.begin(); iter != propList.end(); iter++)
|
||||
{
|
||||
memSize += _atoi64(iter->c_str());
|
||||
}
|
||||
|
||||
memSize = memSize / (1024 * 1024);
|
||||
|
||||
if (hash1 != HashInt((int)memSize))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
matchPoints += 2; // UUID/Memory match gives 2 points
|
||||
}
|
||||
else
|
||||
{
|
||||
// Version 1+ - UUID based
|
||||
#ifdef _WIN32
|
||||
wmi.GetPropertyList("SELECT * FROM Win32_ComputerSystemProduct", "UUID", &propList);
|
||||
#endif
|
||||
if (propList.size() > 0)
|
||||
{
|
||||
string s = NormalizeHardwareString(propList.front());
|
||||
|
||||
if ((s.length() > 1)
|
||||
&& (s != "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
&& (s != "00000000000000000000000000000000"))
|
||||
{
|
||||
if (hash1 != h0 && hash1 == HashString(s.c_str()))
|
||||
{
|
||||
matchPoints += 2; // UUID match is worth 2 points (most stable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read CPU hashes
|
||||
bits.ReadUInt16(&hash1);
|
||||
bits.ReadUInt16(&hash2);
|
||||
|
||||
storedHashes.clear();
|
||||
if (hash1 != h0) storedHashes.insert(hash1);
|
||||
if (hash2 != h0) storedHashes.insert(hash2);
|
||||
|
||||
propList.clear();
|
||||
|
||||
#ifdef _WIN32
|
||||
wmi.GetPropertyList("SELECT ProcessorId FROM Win32_Processor", "ProcessorId", &propList, 4);
|
||||
#else
|
||||
GetCPUIDPropertyList(&propList);
|
||||
#endif
|
||||
|
||||
// Check if ANY current CPU matches stored hashes
|
||||
int cpuMatches = 0;
|
||||
for (auto& cpu : propList) {
|
||||
string s = NormalizeHardwareString(cpu);
|
||||
unsigned short currentHash = HashString(s.c_str());
|
||||
if (storedHashes.find(currentHash) != storedHashes.end()) {
|
||||
cpuMatches++;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpuMatches > 0) {
|
||||
matchPoints += 1; // CPU match is worth 1 point
|
||||
}
|
||||
|
||||
// Read MAC hashes (now 4 hashes for better resilience)
|
||||
bits.ReadUInt16(&hash1);
|
||||
bits.ReadUInt16(&hash2);
|
||||
bits.ReadUInt16(&hash3);
|
||||
bits.ReadUInt16(&hash4);
|
||||
|
||||
storedHashes.clear();
|
||||
if (hash1 != h0) storedHashes.insert(hash1);
|
||||
if (hash2 != h0) storedHashes.insert(hash2);
|
||||
if (hash3 != h0) storedHashes.insert(hash3);
|
||||
if (hash4 != h0) storedHashes.insert(hash4);
|
||||
|
||||
propList.clear();
|
||||
|
||||
#ifdef _WIN32
|
||||
wmi.GetPropertyList("SELECT MACAddress FROM Win32_NetworkAdapter WHERE (AdapterType = \"Ethernet 802.3\") AND (MACAddress IS NOT NULL) AND PNPDeviceId LIKE \"%PCI%\"", "MACAddress", &propList, 10);
|
||||
|
||||
if (propList.size() == 0)
|
||||
{
|
||||
wmi.GetPropertyList("SELECT MACAddress FROM Win32_NetworkAdapter WHERE (AdapterType = \"Ethernet 802.3\") AND (MACAddress IS NOT NULL) AND PNPDeviceId LIKE \"%VMBUS%\"", "MACAddress", &propList, 10);
|
||||
}
|
||||
#else
|
||||
GetMACPropertyList(&propList, 10);
|
||||
#endif
|
||||
|
||||
// Check if ANY current MAC address matches stored hashes
|
||||
int macMatches = 0;
|
||||
for (auto& mac : propList) {
|
||||
string s = NormalizeHardwareString(mac);
|
||||
unsigned short currentHash = HashString(s.c_str());
|
||||
if (storedHashes.find(currentHash) != storedHashes.end()) {
|
||||
macMatches++;
|
||||
}
|
||||
}
|
||||
|
||||
if (macMatches > 0) {
|
||||
matchPoints += 1; // MAC match is worth 1 point
|
||||
}
|
||||
|
||||
// Read HDD hashes
|
||||
bits.ReadUInt16(&hash1);
|
||||
bits.ReadUInt16(&hash2);
|
||||
|
||||
storedHashes.clear();
|
||||
if (hash1 != h0) storedHashes.insert(hash1);
|
||||
if (hash2 != h0) storedHashes.insert(hash2);
|
||||
|
||||
propList.clear();
|
||||
|
||||
#ifdef _WIN32
|
||||
wmi.GetPropertyList("SELECT SerialNumber FROM Win32_PhysicalMedia WHERE SerialNumber IS NOT NULL", "SerialNumber", &propList, 10);
|
||||
#else
|
||||
GetHDDPropertyList(&propList, 10);
|
||||
#endif
|
||||
|
||||
// Check if ANY current HDD serial matches stored hashes
|
||||
int hddMatches = 0;
|
||||
for (auto& serial : propList) {
|
||||
string s = NormalizeHardwareString(serial);
|
||||
unsigned short currentHash = HashString(s.c_str());
|
||||
if (storedHashes.find(currentHash) != storedHashes.end()) {
|
||||
hddMatches++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hddMatches > 0) {
|
||||
matchPoints += 1; // HDD match is worth 1 point
|
||||
}
|
||||
|
||||
// Validation passes if we have enough matching points
|
||||
// Typical valid scenarios:
|
||||
// - UUID (2) + CPU (1) = 3 points ✓
|
||||
// - UUID (2) + MAC (1) = 3 points ✓
|
||||
// - UUID (2) + HDD (1) = 3 points ✓
|
||||
// - CPU (1) + MAC (1) + HDD (1) = 3 points ✓ (if UUID changed but hardware same)
|
||||
return matchPoints >= REQUIRED_POINTS;
|
||||
}
|
||||
Reference in New Issue
Block a user