JUDGER_AND_CLIENT/client/decode_dat_to_xml.py

227 lines
7.8 KiB
Python
Raw Permalink Normal View History

2026-03-05 19:27:04 +08:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
解码 .dat 文件到 XML 文件的脚本
使用方法:
python decode_dat_to_xml.py <input_file> [--key KEY] [--output OUTPUT] [--batch]
示例:
python decode_dat_to_xml.py ./xml/examproblems.dat # 自动检测密钥
python decode_dat_to_xml.py ./xml/user.dat # 自动使用公钥
python decode_dat_to_xml.py ./xml/1000000000/examproblems_26.dat # 自动使用目录名作为密钥
python decode_dat_to_xml.py ./xml --batch # 批量处理整个目录
"""
import os
import sys
import argparse
from Crypto.Cipher import DES
from Crypto.Util.Padding import unpad
# 项目中的固定公钥(用于 user.dat 文件)
PUBLIC_KEY = "1413201160"
def detect_key_from_path(file_path):
"""
从文件路径自动检测密钥
Args:
file_path: 文件路径
Returns:
检测到的密钥如果无法检测则返回None
"""
# 标准化路径
normalized_path = os.path.normpath(file_path)
path_parts = normalized_path.split(os.sep)
# 检查是否是 user.dat 文件(使用公钥)
if os.path.basename(normalized_path) == "user.dat":
return PUBLIC_KEY
# 检查文件是否在用户目录下(例如: xml/1000000000/examproblems_26.dat
# 用户目录通常是8-10位数字
# 遍历路径的每一部分,查找数字目录名
for i, part in enumerate(path_parts):
# 检查是否是用户ID目录8-10位数字
if part.isdigit() and 8 <= len(part) <= 10:
# 如果这是目录名,且文件在这个目录下,使用这个目录名作为密钥
# 例如: xml/1000000000/examproblems_26.dat
if i < len(path_parts) - 1: # 确保后面还有文件
return part
# 如果文件在 xml 根目录下,尝试从文件名或父目录推断
# 但这种情况通常需要手动指定密钥
return None
def decrypt_dat_file(input_file, key=None, output_file=None):
"""
解密 .dat 文件到 XML
Args:
input_file: 输入的 .dat 文件路径
key: DES 解密密钥如果为None则自动检测
output_file: 输出的 XML 文件路径如果为None则自动生成
"""
# 检查输入文件是否存在
if not os.path.exists(input_file):
print(f"错误: 文件不存在: {input_file}")
return False
# 如果没有提供密钥,尝试自动检测
if key is None:
detected_key = detect_key_from_path(input_file)
if detected_key:
key = detected_key
print(f"自动检测到密钥: {key}")
else:
print(f"无法自动检测密钥,请手动指定")
return False
# 处理密钥如果长度小于10补0到10位与Java代码逻辑一致
if len(key) < 10:
key = key + "0" * (10 - len(key))
# 确保密钥至少8位DES要求
if len(key) < 8:
key = key + "0" * (8 - len(key))
# 只取前8位作为DES密钥DES密钥必须是8字节
des_key = key[:8].encode('utf-8')
# IV 固定为 "12345678"与Java代码一致
iv = b"12345678"
try:
# 读取加密文件
with open(input_file, 'rb') as f:
encrypted_data = f.read()
if len(encrypted_data) == 0:
print("错误: 文件为空")
return False
# 创建 DES 解密器
cipher = DES.new(des_key, DES.MODE_CBC, iv)
# 解密数据
decrypted_data = cipher.decrypt(encrypted_data)
# 去除 PKCS5 填充
try:
decrypted_data = unpad(decrypted_data, 8)
except ValueError as e:
print(f"警告: 去除填充时出错,可能文件格式不正确: {e}")
# 尝试直接使用,可能是未填充的数据
pass
# 尝试解码为字符串使用GBK编码因为Java代码中使用了GBK
try:
xml_content = decrypted_data.decode('GBK')
except UnicodeDecodeError:
# 如果GBK失败尝试UTF-8
try:
xml_content = decrypted_data.decode('UTF-8')
except UnicodeDecodeError:
# 如果都失败,尝试使用错误处理
xml_content = decrypted_data.decode('GBK', errors='ignore')
print("警告: 使用GBK解码时遇到无法解码的字符已忽略")
# 确定输出文件路径
if output_file is None:
# 自动生成输出文件名:将 .dat 扩展名改为 .xml
base_name = os.path.splitext(input_file)[0]
output_file = base_name + ".xml"
# 写入XML文件
with open(output_file, 'w', encoding='utf-8') as f:
f.write(xml_content)
print(f"成功! 解密后的XML文件已保存到: {output_file}")
return True
except Exception as e:
print(f"错误: 解密过程中出现异常: {e}")
import traceback
traceback.print_exc()
return False
def batch_process(directory):
"""
批量处理目录中的所有 .dat 文件
Args:
directory: 要处理的目录路径
"""
if not os.path.isdir(directory):
print(f"错误: {directory} 不是一个有效的目录")
return False
success_count = 0
fail_count = 0
# 递归查找所有 .dat 文件
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith('.dat'):
file_path = os.path.join(root, file)
print(f"\n处理文件: {file_path}")
if decrypt_dat_file(file_path):
success_count += 1
else:
fail_count += 1
print(f"\n批量处理完成: 成功 {success_count} 个, 失败 {fail_count}")
return fail_count == 0
def main():
parser = argparse.ArgumentParser(
description='解码 .dat 文件到 XML 文件(自动检测密钥)',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
%(prog)s ./xml/examproblems.dat # 自动检测密钥
%(prog)s ./xml/user.dat # 自动使用公钥 "1413201160"
%(prog)s ./xml/1000000000/examproblems_26.dat # 自动使用目录名作为密钥
%(prog)s ./xml/examproblems.dat --key "userid1234" # 手动指定密钥
%(prog)s ./xml --batch # 批量处理整个目录
"""
)
parser.add_argument('input_path', help='输入的 .dat 文件路径或目录路径(使用 --batch 时)')
parser.add_argument('--key', '-k', help='DES 解密密钥用户ID至少8位。如果不指定将自动检测')
parser.add_argument('--output', '-o', help='输出的 XML 文件路径(默认为输入文件名.xml')
parser.add_argument('--batch', '-b', action='store_true', help='批量处理目录中的所有 .dat 文件')
args = parser.parse_args()
# 批量处理模式
if args.batch:
success = batch_process(args.input_path)
sys.exit(0 if success else 1)
# 单个文件处理模式
# 如果没有提供密钥,尝试自动检测,如果检测失败则提示用户输入
key = args.key
if key is None:
detected_key = detect_key_from_path(args.input_path)
if detected_key:
key = detected_key
print(f"自动检测到密钥: {key}")
else:
key = input("无法自动检测密钥请输入解密密钥用户ID: ").strip()
if not key:
print("错误: 密钥不能为空")
sys.exit(1)
# 执行解密
success = decrypt_dat_file(args.input_path, key, args.output)
if not success:
sys.exit(1)
if __name__ == '__main__':
main()