Exeprotector/validator_secure.py

767 lines
27 KiB
Python
Raw Normal View History

2025-10-23 18:28:10 +08:00
"""
2025-10-25 17:49:09 +08:00
安全的验证器 V2 - 改进版
主要改进
1. 持久化缓存存储不会被系统清理
2. 智能验证策略已激活用户不频繁打扰
3. 到期提醒功能
4. 增强的离线验证
5. 改善的用户界面
2025-10-23 18:28:10 +08:00
作者太一
2025-10-25 17:49:09 +08:00
微信taiyi1224
2025-10-23 18:28:10 +08:00
"""
import os
import sys
import json
import hashlib
import tempfile
import subprocess
import tkinter as tk
from tkinter import messagebox
import threading
import time
import hmac
import base64
from datetime import datetime, timedelta
from cryptography.fernet import Fernet
import requests
2025-10-25 17:49:09 +08:00
import urllib3
from pathlib import Path
import sqlite3
# 禁用SSL警告用于自签名证书
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
2025-10-23 18:28:10 +08:00
# ========== 配置区(这些可以公开) ==========
2025-10-25 17:49:09 +08:00
API_BASE_URL = "http://localhost:5100/" # 使用本地API服务器
API_KEY = "taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224" # 从环境变量读取更安全
2025-10-23 18:28:10 +08:00
SOFTWARE_NAME = "SOFTWARE_NAME_PLACEHOLDER" # 打包时替换
# ========== 机器码生成 ==========
def get_machine_code():
"""生成机器码(简化且稳定的版本)"""
import platform
import uuid
try:
if platform.system() == 'Windows':
# 优先使用主板序列号
try:
import subprocess
result = subprocess.check_output(
'wmic baseboard get serialnumber',
shell=True, stderr=subprocess.DEVNULL, timeout=5
).decode().strip()
lines = [line.strip() for line in result.split('\n') if line.strip()]
if len(lines) > 1 and lines[1] not in ['To Be Filled By O.E.M.', '']:
return hashlib.md5(lines[1].encode()).hexdigest()[:16].upper()
except:
pass
# 备用方案使用MAC地址
mac = uuid.getnode()
mac_str = ':'.join(['{:02x}'.format((mac >> i) & 0xff) for i in range(0, 48, 8)][::-1])
return hashlib.md5(mac_str.encode()).hexdigest()[:16].upper()
except Exception as e:
# 最终备用
fallback = f"{platform.node()}{uuid.getnode()}"
return hashlib.md5(fallback.encode()).hexdigest()[:16].upper()
2025-10-25 17:49:09 +08:00
# ========== 持久化缓存管理器 ==========
class LicenseCache:
"""持久化许可证缓存管理器使用SQLite"""
def __init__(self):
# 存储在用户AppData目录不会被系统清理
if os.name == 'nt': # Windows
cache_base = Path(os.environ.get('APPDATA', Path.home()))
else: # Linux/Mac
cache_base = Path.home() / '.config'
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
self.cache_dir = cache_base / '.secure_license'
self.cache_dir.mkdir(exist_ok=True, parents=True)
self.db_path = self.cache_dir / 'license.db'
self._init_db()
def _init_db(self):
"""初始化数据库"""
conn = sqlite3.connect(str(self.db_path))
conn.execute('''
CREATE TABLE IF NOT EXISTS license_cache (
id INTEGER PRIMARY KEY,
software_name TEXT NOT NULL,
license_key TEXT NOT NULL,
machine_code TEXT NOT NULL,
token TEXT NOT NULL,
start_time TEXT NOT NULL,
end_time TEXT NOT NULL,
last_validated TEXT NOT NULL,
status TEXT DEFAULT 'active',
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(software_name, machine_code)
)
''')
conn.commit()
conn.close()
def save_license(self, software_name, license_key, machine_code,
token, start_time, end_time):
"""保存许可证信息"""
try:
conn = sqlite3.connect(str(self.db_path))
# 转换datetime为ISO格式字符串
if isinstance(start_time, datetime):
start_time = start_time.isoformat()
if isinstance(end_time, datetime):
end_time = end_time.isoformat()
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
conn.execute('''
INSERT OR REPLACE INTO license_cache
(software_name, license_key, machine_code, token,
start_time, end_time, last_validated)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', (software_name, license_key, machine_code, token,
start_time, end_time, datetime.now().isoformat()))
conn.commit()
conn.close()
return True
except Exception as e:
print(f"保存许可证缓存失败: {e}")
return False
def get_license(self, software_name, machine_code):
"""获取许可证信息"""
try:
conn = sqlite3.connect(str(self.db_path))
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM license_cache
WHERE software_name=? AND machine_code=?
''', (software_name, machine_code))
row = cursor.fetchone()
if not row:
conn.close()
return None
# 转换为字典
columns = [desc[0] for desc in cursor.description]
license_data = dict(zip(columns, row))
conn.close()
return license_data
except Exception as e:
print(f"读取许可证缓存失败: {e}")
return None
def should_revalidate(self, license_data):
"""判断是否需要重新验证每7天联网验证一次"""
try:
last_validated = datetime.fromisoformat(license_data['last_validated'])
return (datetime.now() - last_validated).days >= 7
except:
return True
def is_expired(self, license_data):
"""检查许可证是否过期"""
try:
end_time = datetime.fromisoformat(license_data['end_time'])
return datetime.now() > end_time
except:
return True
def get_days_remaining(self, license_data):
"""获取剩余天数"""
try:
end_time = datetime.fromisoformat(license_data['end_time'])
remaining = (end_time - datetime.now()).days
return max(0, remaining)
except:
return 0
def update_last_validated(self, software_name, machine_code):
"""更新最后验证时间"""
try:
conn = sqlite3.connect(str(self.db_path))
conn.execute('''
UPDATE license_cache
SET last_validated=?
WHERE software_name=? AND machine_code=?
''', (datetime.now().isoformat(), software_name, machine_code))
conn.commit()
conn.close()
return True
except Exception as e:
print(f"更新验证时间失败: {e}")
return False
def clear_license(self, software_name, machine_code):
"""清除许可证缓存"""
try:
conn = sqlite3.connect(str(self.db_path))
conn.execute('''
DELETE FROM license_cache
WHERE software_name=? AND machine_code=?
''', (software_name, machine_code))
conn.commit()
conn.close()
return True
except Exception as e:
print(f"清除许可证缓存失败: {e}")
return False
2025-10-23 18:28:10 +08:00
# ========== API通信 ==========
def create_signature(license_key, machine_code, software_name):
"""创建请求签名"""
message = f"{license_key}{machine_code}{software_name}"
return hmac.new(API_KEY.encode(), message.encode(), hashlib.sha256).hexdigest()
def validate_license_online(license_key, machine_code):
"""在线验证卡密"""
try:
signature = create_signature(license_key, machine_code, SOFTWARE_NAME)
response = requests.post(
2025-10-25 17:49:09 +08:00
f"{API_BASE_URL}api/validate",
2025-10-23 18:28:10 +08:00
json={
'license_key': license_key,
'machine_code': machine_code,
'software_name': SOFTWARE_NAME,
'signature': signature
},
headers={'X-API-Key': API_KEY},
2025-10-25 17:49:09 +08:00
timeout=10,
verify=False # 跳过SSL证书验证用于自签名证书
2025-10-23 18:28:10 +08:00
)
if response.status_code == 200:
data = response.json()
if data.get('success'):
2025-10-25 17:49:09 +08:00
# 保存到持久化缓存
cache = LicenseCache()
# 获取服务器返回的时间信息(如果有)
end_time = data.get('expires_at', (datetime.now() + timedelta(days=30)).isoformat())
start_time = datetime.now().isoformat()
cache.save_license(
SOFTWARE_NAME,
2025-10-23 18:28:10 +08:00
license_key,
2025-10-25 17:49:09 +08:00
machine_code,
2025-10-23 18:28:10 +08:00
data.get('token', ''),
2025-10-25 17:49:09 +08:00
start_time,
end_time
2025-10-23 18:28:10 +08:00
)
return True, data.get('message', '验证成功')
else:
return False, data.get('message', '验证失败')
else:
return False, f"服务器错误: {response.status_code}"
except requests.exceptions.ConnectionError:
return False, "无法连接到服务器,请检查网络"
except requests.exceptions.Timeout:
return False, "服务器响应超时"
except Exception as e:
return False, f"验证过程出错: {str(e)}"
2025-10-25 17:49:09 +08:00
def validate_license_offline(license_data):
"""增强的离线验证"""
2025-10-23 18:28:10 +08:00
try:
2025-10-25 17:49:09 +08:00
# 1. 验证数据完整性
required_fields = ['license_key', 'token', 'end_time', 'machine_code']
if not all(field in license_data for field in required_fields):
return False, "缓存数据不完整", None
# 2. 验证机器码
current_machine_code = get_machine_code()
if license_data['machine_code'] != current_machine_code:
return False, "设备已更改,需要重新激活", None
# 3. 检查许可证是否过期
end_time = datetime.fromisoformat(license_data['end_time'])
if datetime.now() > end_time:
return False, "许可证已过期", None
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
# 4. 计算剩余天数
remaining_days = (end_time - datetime.now()).days
# 5. 验证token基本的完整性检查
token = license_data.get('token', '')
if not token or len(token) < 10:
return False, "许可证数据异常", None
# 6. 返回详细信息
info = {
'remaining_days': remaining_days,
'end_time': end_time,
'license_key': license_data['license_key']
}
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
return True, f"离线验证成功", info
2025-10-23 18:28:10 +08:00
except Exception as e:
2025-10-25 17:49:09 +08:00
return False, f"离线验证失败: {str(e)}", None
def try_online_revalidation(license_data):
"""尝试联网刷新状态(静默,不打扰用户)"""
try:
machine_code = get_machine_code()
success, msg = validate_license_online(
license_data['license_key'],
machine_code
)
return success, msg
except:
# 网络问题,不影响使用
return True, "离线模式"
# ========== 到期提醒 ==========
def show_expiry_reminder(remaining_days, end_time_str):
"""显示到期提醒"""
try:
end_time = datetime.fromisoformat(end_time_str)
except:
return
# 根据剩余天数选择不同的提醒级别
if remaining_days <= 0:
# 已过期,必须续费
title = "⚠️ 许可证已过期"
message = f"您的许可证已于 {end_time.strftime('%Y-%m-%d')} 到期\n请联系管理员续费"
urgency = "critical"
elif remaining_days == 1:
title = "🔴 许可证即将到期"
message = f"您的许可证将于明天到期\n请尽快联系管理员续费"
urgency = "urgent"
elif remaining_days <= 3:
title = "🟡 许可证即将到期"
message = f"您的许可证还剩 {remaining_days}\n建议尽快联系管理员续费"
urgency = "warning"
elif remaining_days <= 7:
title = "🟢 许可证到期提醒"
message = f"您的许可证还剩 {remaining_days}\n请注意及时续费"
urgency = "info"
else:
# 不需要提醒
return
# 创建提醒窗口
root = tk.Tk()
root.title(title)
root.geometry("450x280")
root.resizable(False, False)
# 居中
root.update_idletasks()
x = (root.winfo_screenwidth() - 450) // 2
y = (root.winfo_screenheight() - 280) // 2
root.geometry(f"450x280+{x}+{y}")
# 根据紧急程度设置窗口属性
if urgency == "critical":
root.attributes('-topmost', True)
# 标题
title_color = "red" if urgency in ["critical", "urgent"] else "orange" if urgency == "warning" else "green"
tk.Label(root, text=title,
font=("Microsoft YaHei", 14, "bold"),
fg=title_color).pack(pady=20)
# 消息
tk.Label(root, text=message,
font=("Microsoft YaHei", 11),
justify=tk.CENTER).pack(pady=10)
# 到期时间
tk.Label(root, text=f"到期时间:{end_time.strftime('%Y年%m月%d')}",
font=("Microsoft YaHei", 10),
fg="gray").pack(pady=5)
# 剩余天数(醒目显示)
if remaining_days > 0:
tk.Label(root, text=f"剩余 {remaining_days}",
font=("Microsoft YaHei", 16, "bold"),
fg=title_color).pack(pady=10)
# 联系方式
tk.Label(root, text="联系管理员续费V:taiyi1224",
font=("Microsoft YaHei", 9),
fg="blue").pack(pady=10)
# 按钮
frame_buttons = tk.Frame(root)
frame_buttons.pack(pady=15)
def on_continue():
root.destroy()
def on_contact():
import webbrowser
try:
# 尝试打开微信(如果有协议链接)
webbrowser.open("weixin://")
except:
pass
root.destroy()
# 如果未过期,显示"继续使用"按钮
if urgency != "critical":
tk.Button(frame_buttons, text="继续使用", command=on_continue,
bg="#27ae60", fg="white", font=("Microsoft YaHei", 10, "bold"),
padx=20, pady=8).pack(side=tk.LEFT, padx=10)
tk.Button(frame_buttons, text="联系续费", command=on_contact,
bg="#3498db", fg="white", font=("Microsoft YaHei", 10, "bold"),
padx=20, pady=8).pack(side=tk.LEFT, padx=10)
# 如果是critical级别设置为模态窗口
if urgency == "critical":
root.transient()
root.grab_set()
root.mainloop()
2025-10-23 18:28:10 +08:00
# ========== 资源解密 ==========
# ENCRYPTED_RESOURCE_PLACEHOLDER
ENCRYPTED_RESOURCE = None
def decrypt_resource():
"""解密嵌入的资源"""
try:
if not ENCRYPTED_RESOURCE:
return None, "未找到嵌入资源"
from cryptography.fernet import Fernet
import base64
# 重建完整密钥
key_part1_bytes = base64.b64decode(ENCRYPTED_RESOURCE['key_hint'].encode())
key_part2_bytes = base64.b64decode(ENCRYPTED_RESOURCE['key_part2'].encode())
# 合并密钥
full_key = key_part1_bytes + key_part2_bytes
# 解密
fernet = Fernet(full_key)
encrypted_data = base64.b64decode(ENCRYPTED_RESOURCE['data'].encode())
decrypted = fernet.decrypt(encrypted_data)
# 验证哈希
if hashlib.sha256(decrypted).hexdigest() != ENCRYPTED_RESOURCE['hash']:
return None, "文件完整性校验失败"
return decrypted, "解密成功"
except Exception as e:
return None, f"解密失败: {str(e)}"
def extract_and_run():
"""提取并运行原始程序"""
try:
exe_content, msg = decrypt_resource()
if not exe_content:
return False, msg
# 写入临时文件
temp_path = os.path.join(tempfile.gettempdir(), f"app_{os.getpid()}.exe")
with open(temp_path, 'wb') as f:
f.write(exe_content)
# 设置权限
if hasattr(os, 'chmod'):
os.chmod(temp_path, 0o755)
# 启动程序
if os.name == 'nt':
subprocess.Popen([temp_path], creationflags=subprocess.CREATE_NEW_CONSOLE)
else:
subprocess.Popen([temp_path])
time.sleep(2) # 等待程序启动
return True, "程序启动成功"
except Exception as e:
return False, f"启动失败: {str(e)}"
2025-10-25 17:49:09 +08:00
# ========== 增强的激活对话框 ==========
def show_enhanced_activation_dialog(message=None):
"""增强版激活对话框"""
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
# 尝试加载已有许可证信息
cache = LicenseCache()
2025-10-23 18:28:10 +08:00
machine_code = get_machine_code()
2025-10-25 17:49:09 +08:00
existing_license = cache.get_license(SOFTWARE_NAME, machine_code)
2025-10-23 18:28:10 +08:00
root = tk.Tk()
2025-10-25 17:49:09 +08:00
root.title(f"{SOFTWARE_NAME} - 许可证管理")
root.geometry("550x520")
2025-10-23 18:28:10 +08:00
root.resizable(False, False)
# 居中
root.update_idletasks()
2025-10-25 17:49:09 +08:00
x = (root.winfo_screenwidth() - 550) // 2
y = (root.winfo_screenheight() - 520) // 2
root.geometry(f"550x520+{x}+{y}")
2025-10-23 18:28:10 +08:00
# 标题
tk.Label(root, text="🔐 软件许可证验证",
font=("Microsoft YaHei", 16, "bold")).pack(pady=20)
2025-10-25 17:49:09 +08:00
# 如果有消息,显示
if message:
tk.Label(root, text=message,
font=("Microsoft YaHei", 10),
fg="red").pack(pady=5)
# 如果有现有许可证,显示状态
if existing_license:
status_frame = tk.LabelFrame(root, text="当前许可证状态",
font=("Microsoft YaHei", 10, "bold"),
padx=10, pady=10)
status_frame.pack(fill=tk.X, padx=30, pady=10)
remaining = cache.get_days_remaining(existing_license)
is_expired = cache.is_expired(existing_license)
if is_expired:
status_text = "❌ 已过期"
status_color = "red"
elif remaining <= 7:
status_text = f"⚠️ 剩余 {remaining}"
status_color = "orange"
else:
status_text = f"✅ 剩余 {remaining}"
status_color = "green"
tk.Label(status_frame, text=f"状态:{status_text}",
font=("Microsoft YaHei", 11, "bold"),
fg=status_color).pack(anchor=tk.W, pady=3)
tk.Label(status_frame,
text=f"激活码:{existing_license['license_key']}",
font=("Consolas", 9),
fg="gray").pack(anchor=tk.W, pady=2)
end_time_str = existing_license.get('end_time', '')
if end_time_str:
try:
end_time = datetime.fromisoformat(end_time_str)
tk.Label(status_frame,
text=f"到期时间:{end_time.strftime('%Y年%m月%d')}",
font=("Microsoft YaHei", 9),
fg="gray").pack(anchor=tk.W, pady=2)
except:
pass
2025-10-23 18:28:10 +08:00
# 机器码
frame_machine = tk.Frame(root)
frame_machine.pack(fill=tk.X, padx=30, pady=10)
2025-10-25 17:49:09 +08:00
tk.Label(frame_machine, text="机器码:",
font=("Microsoft YaHei", 10, "bold")).pack(anchor=tk.W)
entry_machine = tk.Entry(frame_machine, state="readonly",
font=("Consolas", 10),
justify=tk.CENTER)
2025-10-23 18:28:10 +08:00
entry_machine.insert(0, machine_code)
entry_machine.pack(fill=tk.X, pady=5)
def copy_machine_code():
root.clipboard_clear()
root.clipboard_append(machine_code)
2025-10-25 17:49:09 +08:00
messagebox.showinfo("成功", "机器码已复制到剪贴板\n请发送给管理员获取激活码")
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
tk.Button(frame_machine, text="📋 复制机器码",
command=copy_machine_code,
font=("Microsoft YaHei", 9)).pack(anchor=tk.E)
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
# 激活码输入
2025-10-23 18:28:10 +08:00
frame_key = tk.Frame(root)
frame_key.pack(fill=tk.X, padx=30, pady=10)
2025-10-25 17:49:09 +08:00
tk.Label(frame_key, text="激活码:" if not existing_license else "新激活码:",
font=("Microsoft YaHei", 10, "bold")).pack(anchor=tk.W)
entry_key = tk.Entry(frame_key, font=("Consolas", 12),
justify=tk.CENTER)
2025-10-23 18:28:10 +08:00
entry_key.pack(fill=tk.X, pady=5)
entry_key.focus()
2025-10-25 17:49:09 +08:00
# 状态标签
2025-10-23 18:28:10 +08:00
status_label = tk.Label(root, text="", font=("Microsoft YaHei", 9))
status_label.pack(pady=10)
2025-10-25 17:49:09 +08:00
# 按钮区域
2025-10-23 18:28:10 +08:00
frame_buttons = tk.Frame(root)
2025-10-25 17:49:09 +08:00
frame_buttons.pack(pady=15)
def on_activate():
license_key = entry_key.get().strip()
if not license_key:
messagebox.showerror("错误", "请输入激活码")
return
btn_activate.config(state=tk.DISABLED, text="验证中...")
status_label.config(text="正在联网验证激活码,请稍候...", fg="blue")
root.update()
def validate_thread():
success, msg = validate_license_online(license_key, machine_code)
root.after(0, lambda: handle_result(success, msg))
threading.Thread(target=validate_thread, daemon=True).start()
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
def handle_result(success, msg):
if success:
messagebox.showinfo("成功", f"激活成功!\n{msg}\n\n正在启动程序...")
root.destroy()
ok, err = extract_and_run()
if ok:
sys.exit(0)
else:
messagebox.showerror("错误", f"程序启动失败:\n{err}")
sys.exit(1)
else:
messagebox.showerror("激活失败", f"{msg}\n\n请检查:\n1. 激活码是否正确\n2. 网络连接是否正常\n3. 激活码是否已被其他设备使用")
btn_activate.config(state=tk.NORMAL, text="验证并激活")
status_label.config(text="", fg="black")
btn_activate = tk.Button(frame_buttons, text="验证并激活",
command=on_activate,
bg="#27ae60", fg="white",
font=("Microsoft YaHei", 11, "bold"),
padx=25, pady=10,
cursor="hand2")
2025-10-23 18:28:10 +08:00
btn_activate.pack(side=tk.LEFT, padx=10)
2025-10-25 17:49:09 +08:00
# 如果有现有许可证且未过期,允许继续使用
if existing_license and not cache.is_expired(existing_license):
def on_continue():
root.destroy()
ok, err = extract_and_run()
if ok:
sys.exit(0)
else:
messagebox.showerror("错误", f"程序启动失败:\n{err}")
sys.exit(1)
tk.Button(frame_buttons, text="继续使用", command=on_continue,
bg="#3498db", fg="white",
font=("Microsoft YaHei", 11, "bold"),
padx=25, pady=10,
cursor="hand2").pack(side=tk.LEFT, padx=10)
2025-10-23 18:28:10 +08:00
tk.Button(frame_buttons, text="退出", command=sys.exit,
2025-10-25 17:49:09 +08:00
bg="#e74c3c", fg="white",
font=("Microsoft YaHei", 11, "bold"),
padx=25, pady=10,
cursor="hand2").pack(side=tk.LEFT, padx=10)
# 帮助信息
help_frame = tk.Frame(root)
help_frame.pack(pady=10)
tk.Label(help_frame, text="需要帮助?",
font=("Microsoft YaHei", 8),
fg="gray").pack()
tk.Label(help_frame, text="联系管理员V:taiyi1224",
font=("Microsoft YaHei", 8, "bold"),
fg="blue",
cursor="hand2").pack()
2025-10-23 18:28:10 +08:00
root.bind('<Return>', lambda e: on_activate())
root.protocol("WM_DELETE_WINDOW", sys.exit)
root.mainloop()
2025-10-25 17:49:09 +08:00
# ========== 智能验证主流程 ==========
def smart_validate():
"""智能验证策略"""
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
try:
# 1. 初始化缓存管理器
cache = LicenseCache()
machine_code = get_machine_code()
# 2. 尝试加载本地缓存
license_data = cache.get_license(SOFTWARE_NAME, machine_code)
if not license_data:
# 首次使用,需要在线激活
print("首次使用,需要激活")
show_enhanced_activation_dialog()
return
# 3. 检查是否过期
if cache.is_expired(license_data):
print("许可证已过期")
cache.clear_license(SOFTWARE_NAME, machine_code)
show_enhanced_activation_dialog(message="许可证已过期,请重新激活")
return
# 4. 计算剩余天数
remaining_days = cache.get_days_remaining(license_data)
print(f"许可证剩余 {remaining_days}")
# 5. 判断是否需要联网刷新每7天一次
if cache.should_revalidate(license_data):
print("尝试联网刷新状态...")
# 尝试联网验证(静默,不打扰用户)
success, msg = try_online_revalidation(license_data)
if success:
# 更新缓存中的验证时间
cache.update_last_validated(SOFTWARE_NAME, machine_code)
print(f"状态刷新成功: {msg}")
2025-10-23 18:28:10 +08:00
else:
2025-10-25 17:49:09 +08:00
print(f"状态刷新失败(不影响使用): {msg}")
# 即使失败也继续使用(网络问题不影响已激活用户)
# 6. 离线验证
success, msg, info = validate_license_offline(license_data)
if not success:
2025-10-23 18:28:10 +08:00
print(f"离线验证失败: {msg}")
2025-10-25 17:49:09 +08:00
cache.clear_license(SOFTWARE_NAME, machine_code)
show_enhanced_activation_dialog(message=msg)
return
print(f"离线验证成功: {msg}")
# 7. 显示到期提醒(如果接近到期)
if remaining_days <= 7:
print(f"显示到期提醒(剩余{remaining_days}天)")
show_expiry_reminder(remaining_days, license_data['end_time'])
# 8. 验证通过,启动程序
print("启动程序...")
ok, err = extract_and_run()
if ok:
print("程序启动成功")
sys.exit(0)
else:
print(f"程序启动失败: {err}")
messagebox.showerror("错误", f"程序启动失败:\n{err}")
sys.exit(1)
except Exception as e:
print(f"验证过程出错: {e}")
import traceback
traceback.print_exc()
messagebox.showerror("错误", f"验证过程出错:\n{str(e)}")
sys.exit(1)
# ========== 主函数 ==========
def main():
"""主函数"""
print("=" * 60)
print(f"软件许可证验证系统 V2.0")
print(f"软件名称: {SOFTWARE_NAME}")
print(f"机器码: {get_machine_code()}")
print("=" * 60)
2025-10-23 18:28:10 +08:00
2025-10-25 17:49:09 +08:00
# 使用智能验证策略
smart_validate()
2025-10-23 18:28:10 +08:00
if __name__ == '__main__':
main()