ArticleReplaceBatch/deepseek-clien/validator.py
2025-08-06 15:57:14 +08:00

290 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()