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