249 lines
7.7 KiB
Python
249 lines
7.7 KiB
Python
import hashlib
|
||
import platform
|
||
import subprocess
|
||
import uuid
|
||
import os
|
||
from typing import Optional
|
||
|
||
|
||
class DeviceIDGenerator:
|
||
"""
|
||
Windows设备唯一ID生成器
|
||
通过收集多个硬件特征来生成稳定的设备唯一标识符
|
||
"""
|
||
|
||
def __init__(self, use_cache: bool = True, cache_file: str = ".device_id"):
|
||
"""
|
||
初始化设备ID生成器
|
||
|
||
Args:
|
||
use_cache: 是否使用本地缓存
|
||
cache_file: 缓存文件名
|
||
"""
|
||
self.use_cache = use_cache
|
||
self.cache_file = cache_file
|
||
|
||
def _run_wmic_command(self, command: str) -> Optional[str]:
|
||
"""
|
||
执行WMIC命令并返回结果
|
||
|
||
Args:
|
||
command: WMIC命令
|
||
|
||
Returns:
|
||
命令执行结果,失败则返回None
|
||
"""
|
||
try:
|
||
result = subprocess.run(
|
||
command,
|
||
shell=True,
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=10
|
||
)
|
||
if result.returncode == 0 and result.stdout.strip():
|
||
return result.stdout.strip()
|
||
except (subprocess.TimeoutExpired, Exception):
|
||
pass
|
||
return None
|
||
|
||
def _get_motherboard_serial(self) -> Optional[str]:
|
||
"""获取主板序列号"""
|
||
return self._run_wmic_command("wmic baseboard get serialnumber /value")
|
||
|
||
def _get_cpu_id(self) -> Optional[str]:
|
||
"""获取CPU ID"""
|
||
return self._run_wmic_command("wmic cpu get processorid /value")
|
||
|
||
def _get_bios_serial(self) -> Optional[str]:
|
||
"""获取BIOS序列号"""
|
||
return self._run_wmic_command("wmic bios get serialnumber /value")
|
||
|
||
def _get_disk_serial(self) -> Optional[str]:
|
||
"""获取系统盘序列号"""
|
||
return self._run_wmic_command("wmic diskdrive get serialnumber /value")
|
||
|
||
def _get_machine_guid(self) -> Optional[str]:
|
||
"""获取Windows机器GUID"""
|
||
try:
|
||
result = subprocess.run(
|
||
'reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid',
|
||
shell=True,
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=10
|
||
)
|
||
if result.returncode == 0:
|
||
for line in result.stdout.split('\n'):
|
||
if 'MachineGuid' in line:
|
||
return line.split()[-1]
|
||
except Exception:
|
||
pass
|
||
return None
|
||
|
||
def _extract_value(self, wmic_output: str) -> str:
|
||
"""从WMIC输出中提取实际值"""
|
||
if not wmic_output:
|
||
return ""
|
||
|
||
lines = wmic_output.split('\n')
|
||
for line in lines:
|
||
if '=' in line and not line.strip().endswith('='):
|
||
return line.split('=', 1)[1].strip()
|
||
return ""
|
||
|
||
def _collect_hardware_info(self) -> dict:
|
||
"""
|
||
收集硬件信息
|
||
|
||
Returns:
|
||
包含各种硬件信息的字典
|
||
"""
|
||
hardware_info = {}
|
||
|
||
# 主板序列号
|
||
motherboard = self._get_motherboard_serial()
|
||
hardware_info['motherboard'] = self._extract_value(motherboard) if motherboard else ""
|
||
|
||
# CPU ID
|
||
cpu_id = self._get_cpu_id()
|
||
hardware_info['cpu'] = self._extract_value(cpu_id) if cpu_id else ""
|
||
|
||
# BIOS序列号
|
||
bios = self._get_bios_serial()
|
||
hardware_info['bios'] = self._extract_value(bios) if bios else ""
|
||
|
||
# 硬盘序列号
|
||
disk = self._get_disk_serial()
|
||
hardware_info['disk'] = self._extract_value(disk) if disk else ""
|
||
|
||
# Windows机器GUID
|
||
machine_guid = self._get_machine_guid()
|
||
hardware_info['machine_guid'] = machine_guid if machine_guid else ""
|
||
|
||
# 计算机名称
|
||
hardware_info['computer_name'] = platform.node()
|
||
|
||
# MAC地址(作为备用)
|
||
hardware_info['mac_address'] = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff)
|
||
for elements in range(0, 2*6, 2)][::-1])
|
||
|
||
return hardware_info
|
||
|
||
def _generate_device_id(self, hardware_info: dict) -> str:
|
||
"""
|
||
基于硬件信息生成设备ID
|
||
|
||
Args:
|
||
hardware_info: 硬件信息字典
|
||
|
||
Returns:
|
||
32位十六进制设备ID
|
||
"""
|
||
# 过滤掉空值,并按键排序确保一致性
|
||
filtered_info = {k: v for k, v in hardware_info.items() if v and v.strip()}
|
||
|
||
# 如果没有任何硬件信息,使用MAC地址作为后备方案
|
||
if not filtered_info:
|
||
filtered_info = {'mac_address': hardware_info.get('mac_address', str(uuid.getnode()))}
|
||
|
||
# 将所有信息连接成字符串
|
||
info_string = '|'.join(f"{k}:{v}" for k, v in sorted(filtered_info.items()))
|
||
|
||
# 使用SHA256生成哈希值
|
||
hash_object = hashlib.sha256(info_string.encode('utf-8'))
|
||
device_id = hash_object.hexdigest()
|
||
|
||
return device_id
|
||
|
||
def _load_cached_device_id(self) -> Optional[str]:
|
||
"""从缓存文件加载设备ID"""
|
||
try:
|
||
if os.path.exists(self.cache_file):
|
||
with open(self.cache_file, 'r', encoding='utf-8') as f:
|
||
cached_id = f.read().strip()
|
||
if len(cached_id) == 64: # SHA256哈希长度
|
||
return cached_id
|
||
except Exception:
|
||
pass
|
||
return None
|
||
|
||
def _save_device_id_to_cache(self, device_id: str) -> None:
|
||
"""将设备ID保存到缓存文件"""
|
||
try:
|
||
with open(self.cache_file, 'w', encoding='utf-8') as f:
|
||
f.write(device_id)
|
||
except Exception:
|
||
pass
|
||
|
||
def get_device_id(self) -> str:
|
||
"""
|
||
获取设备唯一ID
|
||
|
||
Returns:
|
||
64字符的十六进制设备ID
|
||
"""
|
||
# 如果启用缓存,先尝试从缓存加载
|
||
if self.use_cache:
|
||
cached_id = self._load_cached_device_id()
|
||
if cached_id:
|
||
return cached_id
|
||
|
||
# 收集硬件信息
|
||
hardware_info = self._collect_hardware_info()
|
||
|
||
# 生成设备ID
|
||
device_id = self._generate_device_id(hardware_info)
|
||
|
||
# 保存到缓存
|
||
if self.use_cache:
|
||
self._save_device_id_to_cache(device_id)
|
||
|
||
return device_id
|
||
|
||
def get_device_id_short(self, length: int = 16) -> str:
|
||
"""
|
||
获取短版本的设备ID
|
||
|
||
Args:
|
||
length: 返回ID的长度
|
||
|
||
Returns:
|
||
指定长度的设备ID
|
||
"""
|
||
full_id = self.get_device_id()
|
||
return full_id[:length]
|
||
|
||
def get_hardware_info(self) -> dict:
|
||
"""
|
||
获取硬件信息(用于调试)
|
||
|
||
Returns:
|
||
硬件信息字典
|
||
"""
|
||
return self._collect_hardware_info()
|
||
|
||
|
||
# 使用示例
|
||
def main():
|
||
"""使用示例"""
|
||
# 创建设备ID生成器实例
|
||
device_generator = DeviceIDGenerator()
|
||
|
||
# 获取完整设备ID(64字符)
|
||
device_id = device_generator.get_device_id()
|
||
print(f"完整设备ID: {device_id}")
|
||
|
||
# 获取短版本设备ID(16字符)
|
||
short_id = device_generator.get_device_id_short(16)
|
||
print(f"短设备ID: {short_id}")
|
||
|
||
# 查看硬件信息(调试用)
|
||
hardware_info = device_generator.get_hardware_info()
|
||
print("\n硬件信息:")
|
||
for key, value in hardware_info.items():
|
||
print(f" {key}: {value}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |