import hashlib import platform import subprocess import uuid import os import re from typing import Optional class MachineCodeGenerator: """机器码生成器""" def __init__(self): self._machine_code_cache = None def generate(self) -> str: """ 生成机器码 :return: 32位哈希字符串 """ if self._machine_code_cache: return self._machine_code_cache # 收集硬件信息 hw_info = [] try: # 1. 获取主板序列号 board_serial = self._get_board_serial() if board_serial: hw_info.append(board_serial) # 2. 获取CPU ID cpu_id = self._get_cpu_id() if cpu_id: hw_info.append(cpu_id) # 3. 获取硬盘序列号 disk_serial = self._get_disk_serial() if disk_serial: hw_info.append(disk_serial) # 4. 获取系统UUID system_uuid = self._get_system_uuid() if system_uuid: hw_info.append(system_uuid) # 5. 获取MAC地址 mac_address = self._get_mac_address() if mac_address: hw_info.append(mac_address) except Exception as e: print(f"获取硬件信息时出错: {e}") # 如果没有获取到任何硬件信息,使用备用方案 if not hw_info: hw_info = [str(uuid.uuid4()), platform.node()] # 组合所有硬件信息并生成哈希 combined_info = '|'.join(hw_info) hash_obj = hashlib.sha256(combined_info.encode('utf-8')) machine_code = hash_obj.hexdigest()[:32].upper() self._machine_code_cache = machine_code return machine_code def _get_board_serial(self) -> Optional[str]: """获取主板序列号""" try: system = platform.system().lower() if system == 'windows': # Windows系统 result = subprocess.run( 'wmic baseboard get serialnumber', shell=True, capture_output=True, text=True, timeout=10 ) if result.returncode == 0: lines = result.stdout.strip().split('\n') for line in lines: if line.strip() and 'SerialNumber' not in line: return line.strip() elif system == 'linux': # Linux系统 for path in ['/sys/class/dmi/id/board_serial', '/sys/class/dmi/id/product_uuid']: if os.path.exists(path): with open(path, 'r') as f: content = f.read().strip() if content: return content elif system == 'darwin': # macOS系统 result = subprocess.run( 'system_profiler SPHardwareDataType | grep "Serial Number"', shell=True, capture_output=True, text=True, timeout=10 ) if result.returncode == 0: match = re.search(r'Serial Number:\s*(.+)', result.stdout) if match: return match.group(1).strip() except Exception: pass return None def _get_cpu_id(self) -> Optional[str]: """获取CPU ID""" try: system = platform.system().lower() if system == 'windows': # Windows系统 result = subprocess.run( 'wmic cpu get processorid', shell=True, capture_output=True, text=True, timeout=10 ) if result.returncode == 0: lines = result.stdout.strip().split('\n') for line in lines: if line.strip() and 'ProcessorId' not in line: return line.strip() elif system == 'linux': # Linux系统 result = subprocess.run( 'cat /proc/cpuinfo | grep -i "processor id" | head -1', shell=True, capture_output=True, text=True, timeout=10 ) if result.returncode == 0: match = re.search(r':\s*(.+)', result.stdout) if match: return match.group(1).strip() elif system == 'darwin': # macOS系统 result = subprocess.run( 'system_profiler SPHardwareDataType | grep "Processor Name"', shell=True, capture_output=True, text=True, timeout=10 ) if result.returncode == 0: match = re.search(r'Processor Name:\s*(.+)', result.stdout) if match: return match.group(1).strip() except Exception: pass return None def _get_disk_serial(self) -> Optional[str]: """获取硬盘序列号""" try: system = platform.system().lower() if system == 'windows': # Windows系统 result = subprocess.run( 'wmic diskdrive get serialnumber', shell=True, capture_output=True, text=True, timeout=10 ) if result.returncode == 0: lines = result.stdout.strip().split('\n') for line in lines: if line.strip() and 'SerialNumber' not in line: return line.strip() elif system == 'linux': # Linux系统 result = subprocess.run( 'lsblk -d -o serial | head -2 | tail -1', shell=True, capture_output=True, text=True, timeout=10 ) if result.returncode == 0 and result.stdout.strip(): return result.stdout.strip() elif system == 'darwin': # macOS系统 result = subprocess.run( 'diskutil info / | grep "Serial Number"', shell=True, capture_output=True, text=True, timeout=10 ) if result.returncode == 0: match = re.search(r'Serial Number:\s*(.+)', result.stdout) if match: return match.group(1).strip() except Exception: pass return None def _get_system_uuid(self) -> Optional[str]: """获取系统UUID""" try: # 尝试使用Python的uuid模块获取 system_uuid = str(uuid.getnode()) if system_uuid and system_uuid != '0': return system_uuid # 备用方案:读取系统UUID文件 system = platform.system().lower() if system == 'linux': for path in ['/etc/machine-id', '/var/lib/dbus/machine-id']: if os.path.exists(path): with open(path, 'r') as f: content = f.read().strip() if content: return content except Exception: pass return None def _get_mac_address(self) -> Optional[str]: """获取MAC地址""" try: # 获取第一个非回环网络接口的MAC地址 import uuid mac = uuid.getnode() if mac != 0: # 格式化MAC地址 mac_str = ':'.join([f'{(mac >> 8*i) & 0xff:02x}' for i in range(6)]) return mac_str.upper() except Exception: pass return None def save_to_cache(self, file_path: str = '.machine_code') -> bool: """ 保存机器码到缓存文件 :param file_path: 缓存文件路径 :return: 是否保存成功 """ try: machine_code = self.generate() with open(file_path, 'w') as f: f.write(machine_code) return True except Exception: return False def load_from_cache(self, file_path: str = '.machine_code') -> Optional[str]: """ 从缓存文件加载机器码 :param file_path: 缓存文件路径 :return: 机器码,如果加载失败返回None """ try: if os.path.exists(file_path): with open(file_path, 'r') as f: machine_code = f.read().strip() if machine_code and len(machine_code) == 32: self._machine_code_cache = machine_code return machine_code except Exception: pass return None def generate_machine_code() -> str: """ 生成机器码的便捷函数 :return: 32位机器码字符串 """ generator = MachineCodeGenerator() return generator.generate()