JUDGER_AND_CLIENT/client/decode_dat_to_xml.py

227 lines
7.8 KiB
Python
Raw Permalink 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
# -*- 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()