Refactor project structure

This commit is contained in:
2026-03-28 19:56:39 +11:00
parent 1d267378b2
commit 8a2e721058
511 changed files with 59 additions and 48 deletions

562
core/anslicensing/hwid.cpp Normal file
View 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;
}