296 lines
9.5 KiB
Python
296 lines
9.5 KiB
Python
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() |