Kamixitong/app/utils/machine_code.py
2025-11-11 21:39:12 +08:00

296 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()