Kamixitong/app/utils/machine_code.py

296 lines
9.5 KiB
Python
Raw Normal View History

2025-11-11 21:39:12 +08:00
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()