ArticleReplaceBatch/deepseek-clien/validator.py

290 lines
9.4 KiB
Python
Raw Normal View History

2025-08-06 15:57:14 +08:00
#!/usr/bin/env python3
"""
EXE文件验证程序 - 负责验证卡密并解密执行原程序
"""
import sys
import os
import tempfile
import subprocess
import mysql.connector
import atexit
from cryptography.fernet import Fernet
from machine_code import get_machine_code
from database import LicenseDatabase
import mysql
import json
import ctypes
from ctypes import wintypes
import time
class EXEValidator:
def __init__(self):
self.separator = b"<<EXE_ENCRYPTED_DATA>>"
self.temp_files = []
# 注册清理函数
atexit.register(self.cleanup)
def cleanup(self):
"""清理临时文件"""
for temp_file in self.temp_files:
try:
if os.path.exists(temp_file):
os.remove(temp_file)
except:
pass
def hide_console(self):
"""隐藏控制台窗口Windows系统"""
try:
if sys.platform == "win32":
kernel32 = ctypes.windll.kernel32
user32 = ctypes.windll.user32
SW_HIDE = 0
hWnd = kernel32.GetConsoleWindow()
if hWnd:
user32.ShowWindow(hWnd, SW_HIDE)
except:
pass
def show_message_box(self, title, message, msg_type="info"):
"""显示消息框"""
try:
if sys.platform == "win32":
import ctypes
if msg_type == "error":
icon = 0x10 # MB_ICONERROR
elif msg_type == "warning":
icon = 0x30 # MB_ICONWARNING
else:
icon = 0x40 # MB_ICONINFORMATION
ctypes.windll.user32.MessageBoxW(0, message, title, icon)
else:
print(f"{title}: {message}")
except:
print(f"{title}: {message}")
def get_license_key_input(self):
"""获取用户输入的卡密"""
try:
if sys.platform == "win32":
# Windows系统使用InputBox
import ctypes
from ctypes import wintypes
# 使用VBScript创建输入框
vbs_code = '''
Dim userInput
userInput = InputBox("请输入您的授权卡密:", "软件授权验证", "")
WScript.Echo userInput
'''
# 创建临时VBS文件
with tempfile.NamedTemporaryFile(mode='w', suffix='.vbs', delete=False) as f:
f.write(vbs_code)
vbs_path = f.name
self.temp_files.append(vbs_path)
# 执行VBS脚本获取输入
result = subprocess.run(['cscript', '//NoLogo', vbs_path],
capture_output=True, text=True)
if result.returncode == 0:
user_input = result.stdout.strip()
if user_input:
return user_input
return None
else:
# 非Windows系统使用命令行输入
return input("请输入您的授权卡密: ").strip()
except:
return None
def extract_encrypted_data(self, exe_path):
"""从加密的EXE文件中提取数据"""
try:
with open(exe_path, 'rb') as f:
data = f.read()
# 查找分隔符位置
separators = []
start = 0
while True:
pos = data.find(self.separator, start)
if pos == -1:
break
separators.append(pos)
start = pos + len(self.separator)
if len(separators) < 3:
return None, "文件格式错误:分隔符不足"
# 提取各部分数据
# 结构: [验证程序, 分隔符, 数据库配置, 分隔符, 密钥, 分隔符, 加密数据]
db_config_start = separators[0] + len(self.separator)
db_config_end = separators[1]
key_start = separators[1] + len(self.separator)
key_end = separators[2]
encrypted_data_start = separators[2] + len(self.separator)
db_config_str = data[db_config_start:db_config_end].decode('utf-8')
key = data[key_start:key_end]
encrypted_data = data[encrypted_data_start:]
# 解析数据库配置
config_parts = db_config_str.split('|')
if len(config_parts) != 4:
return None, "数据库配置格式错误"
db_config = {
'host': config_parts[0],
'database': config_parts[1],
'user': config_parts[2],
'password': config_parts[3]
}
return {
'db_config': db_config,
'key': key,
'encrypted_data': encrypted_data
}, "成功"
except Exception as e:
return None, f"提取数据失败: {str(e)}"
def validate_license(self, db_config, license_key):
"""验证卡密"""
try:
# 获取机器码
machine_code = get_machine_code()
# 连接数据库
db = LicenseDatabase(
db_config['host'],
db_config['database'],
db_config['user'],
db_config['password']
)
if not db.connect():
return False, "无法连接到授权服务器"
# 验证卡密
success, message = db.validate_key(license_key, machine_code)
db.close()
return success, message
except Exception as e:
return False, f"验证过程出错: {str(e)}"
def decrypt_and_run(self, key, encrypted_data):
"""解密并运行原程序"""
try:
# 解密数据
fernet = Fernet(key)
decrypted_data = fernet.decrypt(encrypted_data)
# 创建临时文件
with tempfile.NamedTemporaryFile(suffix='.exe', delete=False) as temp_file:
temp_file.write(decrypted_data)
temp_exe_path = temp_file.name
self.temp_files.append(temp_exe_path)
# 设置可执行权限
if os.name == 'nt':
# Windows系统
import stat
os.chmod(temp_exe_path, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
# 执行程序
subprocess.Popen([temp_exe_path],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == 'nt' else 0)
return True, "程序启动成功"
except Exception as e:
return False, f"解密或执行失败: {str(e)}"
def run(self):
"""主运行函数"""
# 隐藏控制台窗口
self.hide_console()
try:
# 获取当前程序路径
if getattr(sys, 'frozen', False):
exe_path = sys.executable
else:
exe_path = os.path.abspath(__file__)
# 提取加密数据
extracted_data, message = self.extract_encrypted_data(exe_path)
if not extracted_data:
self.show_message_box("错误", f"读取授权信息失败: {message}", "error")
return False
# 最多尝试3次输入卡密
max_attempts = 3
for attempt in range(max_attempts):
# 获取用户输入的卡密
license_key = self.get_license_key_input()
if not license_key:
if attempt < max_attempts - 1:
self.show_message_box("提示", "请输入有效的卡密", "warning")
continue
else:
self.show_message_box("错误", "未输入卡密,程序将退出", "error")
return False
# 验证卡密
success, message = self.validate_license(extracted_data['db_config'], license_key)
if success:
# 验证成功,解密并运行程序
run_success, run_message = self.decrypt_and_run(
extracted_data['key'],
extracted_data['encrypted_data']
)
if run_success:
# 给程序一些时间启动
time.sleep(1)
return True
else:
self.show_message_box("错误", f"程序启动失败: {run_message}", "error")
return False
else:
# 验证失败
if attempt < max_attempts - 1:
self.show_message_box("验证失败", f"{message}\n\n剩余尝试次数: {max_attempts - attempt - 1}",
"warning")
else:
self.show_message_box("验证失败", f"{message}\n\n已达到最大尝试次数,程序将退出", "error")
return False
return False
except Exception as e:
self.show_message_box("系统错误", f"程序运行出错: {str(e)}", "error")
return False
def main():
"""主函数"""
validator = EXEValidator()
success = validator.run()
if not success:
sys.exit(1)
if __name__ == "__main__":
main()