吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 716|回复: 10
收起左侧

[已解决] 求帮忙优化下程序,python查看微信图片

  [复制链接]
叶凯 发表于 2025-3-4 14:21
40吾爱币
因为工作需要保存微信群里的图片,电脑版微信图片是以dat后缀保存的,每次手动保存麻烦,所有就做用AI写了个程序,微信图片解密是网上找到的方法


之前的想法是选择了微信群图片路径,第一张图,把里面所有子文件夹里dat都解密保存,但是后来使用的时候发现每次都全部解密保存,重复且费时,现在想在原来的基础上增加一个判断,假如选择的是C:\Users\666\OneDrive\文档\WeChat Files\AAAAA\FileStorage\MsgAttach\68caffe946cb96a6fa4d7d46b4b17c12\Image,就是按现在的程序执行,把所有图片解密保存;第二种就是选择了C:\Users\666\OneDrive\文档\WeChat Files\AAAAA\FileStorage\MsgAttach\68caffe946cb96a6fa4d7d46b4b17c12\Image\2024-08,也就是我只想解密当前选择的这个文件夹进行解密

1.png
2.png


[Python] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
import os
import tkinter as tk
from tkinter import messagebox, filedialog
from tkinter import ttk
import threading
 
 
class WxAutoExIm:
    def __init__(self, input_path, output_path, status_text):
        # 初始化输入路径
        self.input_path = input_path
        # 初始化输出路径
        self.output_path = output_path
        # 用于显示执行状态的文本框
        self.status_text = status_text
        # 创建输出目录,如果目录不存在
        self._create_directory(output_path)
        # 获取输入路径下的所有文件
        self.files = os.listdir(input_path)
        # 存储文件名的列表
        self.file_names = []
        # 存储已转换文件的列表
        self.converted_files = []
        # 处理所有文件
        self._process_files()
        # 检查处理是否完成
        self._check_completion()
 
    def _create_directory(self, path):
        # 检查路径对应的目录是否存在,若不存在则创建
        if not os.path.exists(path):
            os.makedirs(path)
 
    def _image_decode(self, temp_path, dat_file_name, out_path):
        # 以二进制只读模式打开.dat文件
        with open(temp_path, "rb") as dat_read:
            # 获取文件的异或值和图片格式
            xor_value, file_format = self._get_format(temp_path)
            # 根据图片格式确定文件扩展名
            if file_format == 1:
                extension = '.png'
            elif file_format == 2:
                extension = '.jpg'
            else:
                extension = '.gif'
            # 拼接输出文件的完整路径
            output_file = os.path.join(out_path, dat_file_name + extension)
            # 以二进制写入模式打开输出文件
            with open(output_file, "wb") as png_write:
                # 将文件指针移动到文件开头
                dat_read.seek(0)
                # 逐行读取.dat文件
                for line in dat_read:
                    # 逐字节处理读取的数据
                    for byte in line:
                        # 进行异或运算解密
                        new_byte = byte ^ xor_value
                        # 将解密后的字节写入输出文件
                        png_write.write(bytes([new_byte]))
 
    def _get_format(self, file):
        # 定义常见图片文件的头部字节信息
        formats = [(0x89, 0x50, 0x4e), (0xff, 0xd8, 0xff), (0x47, 0x49, 0x46)]
        # 以二进制只读模式打开文件
        with open(file, "rb") as dat_r:
            # 读取文件的前3个字节
            first_line = dat_r.read(3)
            # 遍历所有图片格式
            for index, xor_values in enumerate(formats, start=1):
                # 计算每个字节与对应格式头部字节的异或结果
                results = [byte ^ xor_values[i] for i, byte in enumerate(first_line)]
                # 如果异或结果都相同,说明找到了对应的图片格式
                if len(set(results)) == 1:
                    return results[0], index
        return None, None
 
    def _get_dat_files(self, directory):
        # 获取指定目录下所有以.dat结尾的文件
        return [file for file in os.listdir(directory) if file.endswith('.dat')]
 
    def _process_files(self):
        # 遍历输入路径下的所有文件
        for file in self.files:
            # 拼接文件的完整路径
            file_path = os.path.join(self.input_path, file)
            # 处理单个文件
            result = self._process_file(file_path, file)
            # 将已转换的文件添加到列表中
            self.converted_files.extend(result)
            # 将文件名添加到列表中
            self.file_names.append(file)
 
    def _process_file(self, base_dir, month):
        # 存储已转换文件的列表
        converted = []
        # 获取指定目录下的所有.dat文件
        dat_files = self._get_dat_files(base_dir)
        # 如果没有.dat文件,直接返回空列表
        if not dat_files:
            return converted
        # 计算.dat文件的总数
        total_files = len(dat_files)
        # 遍历所有.dat文件
        for index, dat_file in enumerate(dat_files, start=1):
            # 拼接.dat文件的完整路径
            dat_file_path = os.path.join(base_dir, dat_file)
            # 生成转换后文件的名称
            dat_file_name = f"{month}_{os.path.splitext(dat_file)[0]}"
            # 对.dat文件进行解密转换
            self._image_decode(dat_file_path, dat_file_name, self.output_path)
            # 计算转换进度
            progress = int((index / total_files) * 100)
            # 生成进度信息
            status_msg = f'转换进度--> {progress}%\n'
            # 更新状态信息到文本框
            self._update_status(status_msg)
            # 将已转换的文件添加到列表中
            converted.append(dat_file)
        return converted
 
    def _update_status(self, message):
        # 在文本框的末尾插入状态信息
        self.status_text.insert(tk.END, message)
        # 将滚动条滚动到最新的信息处
        self.status_text.see(tk.END)
 
    def _check_completion(self):
        # 目前只是简单返回True,表示检查完成
        return True
 
 
class RootTk:
    def __init__(self, title, msg):
        # 创建主窗口
        self.root = tk.Tk()
        # 设置主窗口的标题
        self.root.title(title)
        # 设置主窗口的大小
        self.root.geometry('680x400')
 
        # 输入路径部分的框架
        input_frame = tk.Frame(self.root)
        # 显示输入路径选择的提示信息
        input_frame.pack(pady=10)
        tk.Label(input_frame, text='请选择要查看的微信图片路径').pack(side=tk.LEFT)
        # 用于存储输入路径的变量
        self.input_path = tk.StringVar()
        # 显示选择的输入路径的标签
        input_label = tk.Label(input_frame, textvariable=self.input_path, width=50, relief=tk.SUNKEN)
        input_label.pack(side=tk.LEFT, padx=5)
        # 选择输入文件夹的按钮
        input_button = ttk.Button(input_frame, text="选择文件夹", command=self._select_input_folder,
                                  style='Custom.TButton')
        input_button.pack(side=tk.LEFT)
 
        # 输出路径部分的框架
        output_frame = tk.Frame(self.root)
        output_frame.pack(pady=10)
        # 显示输出路径选择的提示信息
        tk.Label(output_frame, text='请选择要保存图片的路径').pack(side=tk.LEFT)
        # 用于存储输出路径的变量
        self.output_path = tk.StringVar()
        # 显示选择的输出路径的标签
        output_label = tk.Label(output_frame, textvariable=self.output_path, width=50, relief=tk.SUNKEN)
        output_label.pack(side=tk.LEFT, padx=5)
        # 选择输出文件夹的按钮
        output_button = ttk.Button(output_frame, text="选择文件夹", command=self._select_output_folder,
                                   style='Custom.TButton')
        output_button.pack(side=tk.LEFT)
 
        # 创建按钮样式
        style = ttk.Style()
        # 配置自定义按钮样式,设置字体、边框等属性
        style.configure('Custom.TButton', font=('Arial', 10, 'bold'), bd=0, relief="flat", padding=8)
        # 设置按钮激活状态下的背景颜色
        style.map('Custom.TButton', background=[('active', 'darkblue')])
        # 执行转换任务的按钮
        execute_button = ttk.Button(self.root, text="执行", style='Custom.TButton', command=self._execute_task)
        execute_button.pack(pady=20)
 
        # 状态显示区域的框架
        status_frame = tk.Frame(self.root)
        status_frame.pack(pady=10)
        # 用于显示执行状态的文本框
        self.status_text = tk.Text(status_frame, height=5, width=60)
        self.status_text.pack(side=tk.LEFT)
        # 与文本框关联的滚动条
        scrollbar = tk.Scrollbar(status_frame, command=self.status_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        # 设置文本框的滚动条关联
        self.status_text.config(yscrollcommand=scrollbar.set)
 
        # 启动主事件循环
        self.root.mainloop()
 
    def _select_input_folder(self):
        # 获取当前的输入路径,如果为空则使用空字符串
        initial_dir = self.input_path.get() if self.input_path.get() else ""
        # 弹出文件夹选择对话框,初始目录为当前输入路径
        folder = filedialog.askdirectory(initialdir=initial_dir)
        # 如果用户选择了文件夹,更新输入路径变量
        if folder:
            self.input_path.set(folder)
 
    def _select_output_folder(self):
        # 获取当前的输出路径,如果为空则使用空字符串
        initial_dir = self.output_path.get() if self.output_path.get() else ""
        # 弹出文件夹选择对话框,初始目录为当前输出路径
        folder = filedialog.askdirectory(initialdir=initial_dir)
        # 如果用户选择了文件夹,更新输出路径变量
        if folder:
            self.output_path.set(folder)
 
    def _execute_task(self):
        # 获取输入路径
        input_path = self.input_path.get()
        # 获取输出路径
        output_path = self.output_path.get()
        # 检查输入路径和输出路径是否都已选择
        if input_path and output_path:
            # 在状态文本框中显示开始执行的信息
            self.status_text.insert(tk.END, '开始执行转换任务...\n')
            # 将滚动条滚动到最新信息处
            self.status_text.see(tk.END)
            # 启动一个新线程来执行转换任务,避免阻塞主线程
            threading.Thread(target=self._execute, args=(input_path, output_path)).start()
 
    def _execute(self, input_path, output_path):
        try:
            # 创建WxAutoExIm对象,开始执行转换任务
            obj = WxAutoExIm(input_path, output_path, self.status_text)
            # 如果任务执行成功
            if obj:
                # 在状态文本框中显示任务完成的信息
                self.status_text.insert(tk.END, '转换任务已完成。\n')
                # 将滚动条滚动到最新信息处
                self.status_text.see(tk.END)
                # 弹出消息框提示任务已完成
                messagebox.showinfo(title='my message', message='已完成')
        except Exception as e:
            # 如果执行过程中出现异常,在状态文本框中显示错误信息
            self.status_text.insert(tk.END, f'执行过程中出现错误: {str(e)}\n')
            # 将滚动条滚动到最新信息处
            self.status_text.see(tk.END)
 
 
if __name__ == '__main__':
    # 创建RootTk对象,启动程序
    RootTk('微信图片解密', 'hello world')

最佳答案

查看完整内容

电脑上没有微信图片,吾友试试吧 [mw_shl_code=python,true]import os import tkinter as tk from tkinter import messagebox, filedialog from tkinter import ttk import threading import queue import traceback from typing import Optional, Tuple, List class WxAutoExIm: """微信图片自动解密转换器""" # 文件头特征与扩展名映射 HEADER_FORMATS = { 1: (0x89, 0x50, 0x4E, '. ...

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

AiniWang 发表于 2025-3-4 14:21
本帖最后由 AiniWang 于 2025-3-4 15:43 编辑

电脑上没有微信图片,吾友试试吧
[Python] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
import os
import tkinter as tk
from tkinter import messagebox, filedialog
from tkinter import ttk
import threading
import queue
import traceback
from typing import Optional, Tuple, List
 
 
class WxAutoExIm:
    """微信图片自动解密转换器"""
     
    # 文件头特征与扩展名映射
    HEADER_FORMATS = {
        1: (0x89, 0x50, 0x4E, '.png'),
        2: (0xFF, 0xD8, 0xFF, '.jpg'),
        3: (0x47, 0x49, 0x46, '.gif')
    }
 
    def __init__(self, input_path: str, output_path: str, status_queue: queue.Queue):
        self.input_path = input_path
        self.output_path = output_path
        self.status_queue = status_queue
        self._create_directory(output_path)
         
        self.specific_folder = self._detect_special_folder(input_path)
        self._log_init_status()
         
        self.converted_files = []
        self._process_files()
 
    def _detect_special_folder(self, path: str) -> bool:
        """检测是否为特定月份文件夹"""
        return os.path.basename(path).count('-') > 0
 
    def _log_init_status(self):
        """记录初始化状态"""
        if self.specific_folder:
            msg = f'检测到特定月份文件夹: {os.path.basename(self.input_path)},将只解密该文件夹内图片\n'
        else:
            msg = '将解密整个图片库下的所有图片\n'
        self.status_queue.put(msg)
 
    def _create_directory(self, path: str):
        """创建输出目录"""
        try:
            os.makedirs(path, exist_ok=True)
        except OSError as e:
            self.status_queue.put(f"创建目录失败: {str(e)}\n")
            raise
 
    def _process_files(self):
        """文件处理分发器"""
        if self.specific_folder:
            self._process_single_folder()
        else:
            self._process_all_folders()
 
    def _process_single_folder(self):
        """处理单个文件夹"""
        folder_name = os.path.basename(self.input_path)
        self.status_queue.put(f'正在处理文件夹: {folder_name}\n')
        self._batch_convert(self.input_path, folder_name)
 
    def _process_all_folders(self):
        """处理所有子文件夹"""
        for root, dirs, files in os.walk(self.input_path):
            if os.path.basename(root).count('-') > 0:
                folder_name = os.path.basename(root)
                self.status_queue.put(f'处理文件夹: {folder_name}\n')
                self._batch_convert(root, folder_name)
 
    def _batch_convert(self, dir_path: str, prefix: str):
        """批量转换指定目录下的文件"""
        dat_files = self._get_valid_files(dir_path)
        if not dat_files:
            self.status_queue.put("未找到有效的.dat文件\n")
            return
 
        total = len(dat_files)
        for idx, dat_file in enumerate(dat_files, 1):
            self._convert_single_file(dir_path, dat_file, prefix)
            self._update_progress(idx, total)
 
    def _convert_single_file(self, dir_path: str, dat_file: str, prefix: str):
        """转换单个文件"""
        try:
            src_path = os.path.join(dir_path, dat_file)
            file_name = f"{prefix}_{os.path.splitext(dat_file)[0]}"
            self._image_decode(src_path, file_name, self.output_path)
            self.converted_files.append(dat_file)
        except Exception as e:
            self.status_queue.put(f"文件{dat_file}转换失败: {str(e)}\n")
 
    def _image_decode(self, src_path: str, dest_name: str, out_dir: str):
        """核心解密逻辑"""
        try:
            with open(src_path, "rb") as f_src:
                header = f_src.read(3)
                xor_value, extension = self._analyze_header(header)
                 
                dest_path = os.path.join(out_dir, f"{dest_name}{extension}")
                with open(dest_path, "wb") as f_dest:
                    f_src.seek(0)
                    while chunk := f_src.read(4096):
                        f_dest.write(bytes(b ^ xor_value for b in chunk))
        except IOError as e:
            self.status_queue.put(f"文件操作失败: {str(e)}\n")
            raise
 
    def _analyze_header(self, header: bytes) -> Tuple[int, str]:
        """分析文件头特征"""
        for fmt_id, (h1, h2, h3, ext) in self.HEADER_FORMATS.items():
            xor_values = [header[0] ^ h1, header[1] ^ h2, header[2] ^ h3]
            if len(set(xor_values)) == 1:
                return xor_values[0], ext
        return 0x00, '.dat'  # 默认处理
 
    def _get_valid_files(self, dir_path: str) -> List[str]:
        """获取有效.dat文件列表"""
        return [f for f in os.listdir(dir_path)
                if f.endswith('.dat') and os.path.isfile(os.path.join(dir_path, f))]
 
    def _update_progress(self, current: int, total: int):
        """更新进度信息"""
        progress = int((current / total) * 100)
        self.status_queue.put(f'转换进度: {progress}%\n')
 
 
class RootTk:
    """主界面类"""
     
    def __init__(self, title: str):
        self.root = tk.Tk()
        self.root.title(title)
        self.root.geometry('720x450')
        self.root.resizable(False, False)
 
        # 线程通信队列
        self.status_queue = queue.Queue()
        self._init_ui()
        self._setup_queue_handler()
 
    def _init_ui(self):
        """初始化界面组件"""
        # 输入路径部分
        input_frame = ttk.Frame(self.root, padding=10)
        input_frame.pack(fill=tk.X)
        ttk.Label(input_frame, text="微信图片路径:").pack(side=tk.LEFT)
        self.input_path = tk.StringVar()
        ttk.Entry(input_frame, textvariable=self.input_path, width=50).pack(side=tk.LEFT, padx=5)
        ttk.Button(input_frame, text="选择文件夹", command=self._select_input_folder).pack(side=tk.LEFT)
 
        # 输出路径部分
        output_frame = ttk.Frame(self.root, padding=10)
        output_frame.pack(fill=tk.X)
        ttk.Label(output_frame, text="保存路径:").pack(side=tk.LEFT)
        self.output_path = tk.StringVar()
        ttk.Entry(output_frame, textvariable=self.output_path, width=50).pack(side=tk.LEFT, padx=5)
        ttk.Button(output_frame, text="选择文件夹", command=self._select_output_folder).pack(side=tk.LEFT)
 
        # 操作按钮
        btn_frame = ttk.Frame(self.root, padding=10)
        btn_frame.pack()
        ttk.Button(btn_frame, text="开始转换", command=self._execute_task).pack(pady=10)
 
        # 状态显示
        status_frame = ttk.Frame(self.root)
        status_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        self.status_text = tk.Text(status_frame, wrap=tk.WORD, state='normal')
        scrollbar = ttk.Scrollbar(status_frame, command=self.status_text.yview)
        self.status_text.configure(yscrollcommand=scrollbar.set)
        self.status_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
 
    def _setup_queue_handler(self):
        """设置队列处理器"""
        self.root.after(100, self._process_queue)
 
    def _process_queue(self):
        """处理状态队列中的消息"""
        while not self.status_queue.empty():
            msg = self.status_queue.get()
            self._safe_update_status(msg)
        self.root.after(100, self._process_queue)
 
    def _safe_update_status(self, message: str):
        """线程安全的状态更新"""
        self.status_text.configure(state='normal')
        self.status_text.insert(tk.END, message)
        self.status_text.see(tk.END)
        self.status_text.configure(state='disabled')
 
    def _select_input_folder(self):
        """选择输入文件夹"""
        if path := filedialog.askdirectory():
            self.input_path.set(path)
 
    def _select_output_folder(self):
        """选择输出文件夹"""
        if path := filedialog.askdirectory():
            self.output_path.set(path)
 
    def _execute_task(self):
        """执行转换任务"""
        if not self._validate_paths():
            return
 
        self._safe_update_status('开始转换任务...\n')
        threading.Thread(
            target=self._execute_conversion,
            daemon=True
        ).start()
 
    def _validate_paths(self) -> bool:
        """验证路径有效性"""
        input_path = self.input_path.get()
        output_path = self.output_path.get()
 
        if not input_path or not output_path:
            messagebox.showwarning("路径错误", "请先选择输入和输出路径")
            return False
 
        if not os.path.isdir(input_path):
            messagebox.showerror("路径错误", "输入路径无效或不存在")
            return False
 
        if os.path.commonpath([input_path]) == os.path.commonpath([output_path]):
            messagebox.showerror("路径错误", "输入和输出路径不能相同")
            return False
 
        return True
 
    def _execute_conversion(self):
        """执行转换核心逻辑"""
        try:
            WxAutoExIm(
                self.input_path.get(),
                self.output_path.get(),
                self.status_queue
            )
            self.status_queue.put("转换任务完成!\n")
            messagebox.showinfo("完成", "图片转换任务已成功完成")
        except Exception as e:
            error_msg = f"错误发生: {traceback.format_exc()}\n"
            self.status_queue.put(error_msg)
            messagebox.showerror("错误", "转换过程中发生严重错误")
 
    def run(self):
        """运行主循环"""
        self.root.mainloop()
 
 
if __name__ == '__main__':
    app = RootTk("微信图片解密工具 v2.0")
    app.run()

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
叶凯 + 1 + 1 谢谢@Thanks!

查看全部评分

sea8523 发表于 2025-3-4 14:25
zhufuan 发表于 2025-3-4 14:44
只送两个头 发表于 2025-3-4 14:52
坛子里有现成的
fortytwo 发表于 2025-3-4 15:21
[Python] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
import os
import tkinter as tk
from tkinter import messagebox, filedialog
from tkinter import ttk
import threading
  
  
class WxAutoExIm:
    def __init__(self, input_path, output_path, status_text):
        # 初始化输入路径
        self.input_path = input_path
        # 初始化输出路径
        self.output_path = output_path
        # 用于显示执行状态的文本框
        self.status_text = status_text
        # 创建输出目录,如果目录不存在
        self._create_directory(output_path)
        # 判断是解密整个图片库还是特定文件夹
        self.specific_folder = os.path.basename(input_path).count('-') > 0
        # 更新状态信息
        if self.specific_folder:
            self._update_status(f'检测到特定月份文件夹: {os.path.basename(input_path)},将只解密该文件夹内图片\n')
        else:
            self._update_status('将解密整个图片库下的所有图片\n')
        # 获取输入路径下的所有文件
        self.files = os.listdir(input_path)
        # 存储文件名的列表
        self.file_names = []
        # 存储已转换文件的列表
        self.converted_files = []
        # 处理所有文件
        self._process_files()
        # 检查处理是否完成
        self._check_completion()
  
    def _create_directory(self, path):
        # 检查路径对应的目录是否存在,若不存在则创建
        if not os.path.exists(path):
            os.makedirs(path)
  
    def _image_decode(self, temp_path, dat_file_name, out_path):
        # 以二进制只读模式打开.dat文件
        with open(temp_path, "rb") as dat_read:
            # 获取文件的异或值和图片格式
            xor_value, file_format = self._get_format(temp_path)
            # 根据图片格式确定文件扩展名
            if file_format == 1:
                extension = '.png'
            elif file_format == 2:
                extension = '.jpg'
            else:
                extension = '.gif'
            # 拼接输出文件的完整路径
            output_file = os.path.join(out_path, dat_file_name + extension)
            # 以二进制写入模式打开输出文件
            with open(output_file, "wb") as png_write:
                # 将文件指针移动到文件开头
                dat_read.seek(0)
                # 逐行读取.dat文件
                for line in dat_read:
                    # 逐字节处理读取的数据
                    for byte in line:
                        # 进行异或运算解密
                        new_byte = byte ^ xor_value
                        # 将解密后的字节写入输出文件
                        png_write.write(bytes([new_byte]))
  
    def _get_format(self, file):
        # 定义常见图片文件的头部字节信息
        formats = [(0x89, 0x50, 0x4e), (0xff, 0xd8, 0xff), (0x47, 0x49, 0x46)]
        # 以二进制只读模式打开文件
        with open(file, "rb") as dat_r:
            # 读取文件的前3个字节
            first_line = dat_r.read(3)
            # 遍历所有图片格式
            for index, xor_values in enumerate(formats, start=1):
                # 计算每个字节与对应格式头部字节的异或结果
                results = [byte ^ xor_values[i] for i, byte in enumerate(first_line)]
                # 如果异或结果都相同,说明找到了对应的图片格式
                if len(set(results)) == 1:
                    return results[0], index
        return None, None
  
    def _get_dat_files(self, directory):
        # 获取指定目录下所有以.dat结尾的文件
        return [file for file in os.listdir(directory) if file.endswith('.dat')]
  
    def _process_files(self):
        # 如果是特定文件夹处理模式,直接处理当前文件夹内的图片
        if self.specific_folder:
            # 处理特定文件夹
            folder_name = os.path.basename(self.input_path)
            self._update_status(f'正在处理文件夹: {folder_name}\n')
            # 获取当前文件夹内的所有.dat文件
            dat_files = self._get_dat_files(self.input_path)
            # 计算.dat文件的总数
            total_files = len(dat_files)
            # 如果没有.dat文件,直接返回
            if total_files == 0:
                self._update_status('没有找到.dat文件\n')
                return
            # 遍历所有.dat文件
            for index, dat_file in enumerate(dat_files, start=1):
                # 拼接.dat文件的完整路径
                dat_file_path = os.path.join(self.input_path, dat_file)
                # 生成转换后文件的名称
                dat_file_name = f"{folder_name}_{os.path.splitext(dat_file)[0]}"
                # 对.dat文件进行解密转换
                self._image_decode(dat_file_path, dat_file_name, self.output_path)
                # 计算转换进度
                progress = int((index / total_files) * 100)
                # 生成进度信息
                status_msg = f'转换进度--> {progress}%\n'
                # 更新状态信息到文本框
                self._update_status(status_msg)
                # 将已转换的文件添加到列表中
                self.converted_files.append(dat_file)
        else:
            # 原本的处理逻辑:遍历输入路径下的所有文件
            for file in self.files:
                # 拼接文件的完整路径
                file_path = os.path.join(self.input_path, file)
                # 处理单个文件
                result = self._process_file(file_path, file)
                # 将已转换的文件添加到列表中
                self.converted_files.extend(result)
                # 将文件名添加到列表中
                self.file_names.append(file)
  
    def _process_file(self, base_dir, month):
        # 存储已转换文件的列表
        converted = []
        # 获取指定目录下的所有.dat文件
        dat_files = self._get_dat_files(base_dir)
        # 如果没有.dat文件,直接返回空列表
        if not dat_files:
            return converted
        # 计算.dat文件的总数
        total_files = len(dat_files)
        # 遍历所有.dat文件
        for index, dat_file in enumerate(dat_files, start=1):
            # 拼接.dat文件的完整路径
            dat_file_path = os.path.join(base_dir, dat_file)
            # 生成转换后文件的名称
            dat_file_name = f"{month}_{os.path.splitext(dat_file)[0]}"
            # 对.dat文件进行解密转换
            self._image_decode(dat_file_path, dat_file_name, self.output_path)
            # 计算转换进度
            progress = int((index / total_files) * 100)
            # 生成进度信息
            status_msg = f'转换进度--> {progress}%\n'
            # 更新状态信息到文本框
            self._update_status(status_msg)
            # 将已转换的文件添加到列表中
            converted.append(dat_file)
        return converted
  
    def _update_status(self, message):
        # 在文本框的末尾插入状态信息
        self.status_text.insert(tk.END, message)
        # 将滚动条滚动到最新的信息处
        self.status_text.see(tk.END)
  
    def _check_completion(self):
        # 目前只是简单返回True,表示检查完成
        return True
  
  
class RootTk:
    def __init__(self, title, msg):
        # 创建主窗口
        self.root = tk.Tk()
        # 设置主窗口的标题
        self.root.title(title)
        # 设置主窗口的大小
        self.root.geometry('680x400')
  
        # 输入路径部分的框架
        input_frame = tk.Frame(self.root)
        # 显示输入路径选择的提示信息
        input_frame.pack(pady=10)
        tk.Label(input_frame, text='请选择要查看的微信图片路径').pack(side=tk.LEFT)
        # 用于存储输入路径的变量
        self.input_path = tk.StringVar()
        # 显示选择的输入路径的标签
        input_label = tk.Label(input_frame, textvariable=self.input_path, width=50, relief=tk.SUNKEN)
        input_label.pack(side=tk.LEFT, padx=5)
        # 选择输入文件夹的按钮
        input_button = ttk.Button(input_frame, text="选择文件夹", command=self._select_input_folder,
                                  style='Custom.TButton')
        input_button.pack(side=tk.LEFT)
  
        # 输出路径部分的框架
        output_frame = tk.Frame(self.root)
        output_frame.pack(pady=10)
        # 显示输出路径选择的提示信息
        tk.Label(output_frame, text='请选择要保存图片的路径').pack(side=tk.LEFT)
        # 用于存储输出路径的变量
        self.output_path = tk.StringVar()
        # 显示选择的输出路径的标签
        output_label = tk.Label(output_frame, textvariable=self.output_path, width=50, relief=tk.SUNKEN)
        output_label.pack(side=tk.LEFT, padx=5)
        # 选择输出文件夹的按钮
        output_button = ttk.Button(output_frame, text="选择文件夹", command=self._select_output_folder,
                                   style='Custom.TButton')
        output_button.pack(side=tk.LEFT)
  
        # 创建按钮样式
        style = ttk.Style()
        # 配置自定义按钮样式,设置字体、边框等属性
        style.configure('Custom.TButton', font=('Arial', 10, 'bold'), bd=0, relief="flat", padding=8)
        # 设置按钮激活状态下的背景颜色
        style.map('Custom.TButton', background=[('active', 'darkblue')])
        # 执行转换任务的按钮
        execute_button = ttk.Button(self.root, text="执行", style='Custom.TButton', command=self._execute_task)
        execute_button.pack(pady=20)
  
        # 状态显示区域的框架
        status_frame = tk.Frame(self.root)
        status_frame.pack(pady=10)
        # 用于显示执行状态的文本框
        self.status_text = tk.Text(status_frame, height=5, width=60)
        self.status_text.pack(side=tk.LEFT)
        # 与文本框关联的滚动条
        scrollbar = tk.Scrollbar(status_frame, command=self.status_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        # 设置文本框的滚动条关联
        self.status_text.config(yscrollcommand=scrollbar.set)
  
        # 启动主事件循环
        self.root.mainloop()
  
    def _select_input_folder(self):
        # 获取当前的输入路径,如果为空则使用空字符串
        initial_dir = self.input_path.get() if self.input_path.get() else ""
        # 弹出文件夹选择对话框,初始目录为当前输入路径
        folder = filedialog.askdirectory(initialdir=initial_dir)
        # 如果用户选择了文件夹,更新输入路径变量
        if folder:
            self.input_path.set(folder)
  
    def _select_output_folder(self):
        # 获取当前的输出路径,如果为空则使用空字符串
        initial_dir = self.output_path.get() if self.output_path.get() else ""
        # 弹出文件夹选择对话框,初始目录为当前输出路径
        folder = filedialog.askdirectory(initialdir=initial_dir)
        # 如果用户选择了文件夹,更新输出路径变量
        if folder:
            self.output_path.set(folder)
  
    def _execute_task(self):
        # 获取输入路径
        input_path = self.input_path.get()
        # 获取输出路径
        output_path = self.output_path.get()
        # 检查输入路径和输出路径是否都已选择
        if input_path and output_path:
            # 在状态文本框中显示开始执行的信息
            self.status_text.insert(tk.END, '开始执行转换任务...\n')
            # 将滚动条滚动到最新信息处
            self.status_text.see(tk.END)
            # 启动一个新线程来执行转换任务,避免阻塞主线程
            threading.Thread(target=self._execute, args=(input_path, output_path)).start()
  
    def _execute(self, input_path, output_path):
        try:
            # 创建WxAutoExIm对象,开始执行转换任务
            obj = WxAutoExIm(input_path, output_path, self.status_text)
            # 如果任务执行成功
            if obj:
                # 在状态文本框中显示任务完成的信息
                self.status_text.insert(tk.END, '转换任务已完成。\n')
                # 将滚动条滚动到最新信息处
                self.status_text.see(tk.END)
                # 弹出消息框提示任务已完成
                messagebox.showinfo(title='my message', message='已完成')
        except Exception as e:
            # 如果执行过程中出现异常,在状态文本框中显示错误信息
            self.status_text.insert(tk.END, f'执行过程中出现错误: {str(e)}\n')
            # 将滚动条滚动到最新信息处
            self.status_text.see(tk.END)
  
  
if __name__ == '__main__':
    # 创建RootTk对象,启动程序
    RootTk('微信图片解密', 'hello world')
dzxsl 发表于 2025-3-4 15:30
import os  # 导入操作系统模块,用于处理文件和目录路径
import tkinter as tk  # 导入Tkinter模块,用于创建GUI界面
from tkinter import messagebox, filedialog  # 导入消息框和文件对话框模块
from tkinter import ttk  # 导入Tkinter的ttk模块,提供更现代的控件
import threading  # 导入线程模块,用于多线程处理


class WxAutoExIm:
    def __init__(self, input_path, output_path, status_text):
        # 初始化输入路径
        self.input_path = input_path
        # 初始化输出路径
        self.output_path = output_path
        # 初始化状态文本框,用于显示转换进度
        self.status_text = status_text
        # 创建输出目录(如果不存在)
        self._create_directory(output_path)
        # 初始化文件名列表
        self.file_names = []
        # 初始化已转换文件列表
        self.converted_files = []
        # 初始化待处理目录列表
        self.dirs_to_process = []
        # 收集需要处理的目录
        self._collect_dirs_to_process()
        # 处理所有目录中的文件
        self._process_dirs()
        # 检查任务是否完成
        self._check_completion()

    def _create_directory(self, path):
        """创建目录(如果不存在)"""
        if not os.path.exists(path):  # 检查目录是否存在
            os.makedirs(path)  # 如果不存在,则创建目录

    def _collect_dirs_to_process(self):
        """根据输入路径判断需要处理的目录"""
        # 判断输入路径是否是微信的Image目录
        if os.path.basename(self.input_path) == "Image":
            # 如果是Image目录,遍历所有子目录
            for dir_name in os.listdir(self.input_path):
                dir_path = os.path.join(self.input_path, dir_name)  # 拼接子目录路径
                if os.path.isdir(dir_path):  # 检查是否是目录
                    self.dirs_to_process.append(dir_path)  # 添加到待处理目录列表
        else:
            # 如果不是Image目录,直接处理当前目录
            self.dirs_to_process.append(self.input_path)

    def _image_decode(self, temp_path, dat_file_name, out_path):
        """解码.dat文件并保存为图片"""
        with open(temp_path, "rb") as dat_read:  # 以二进制模式打开.dat文件
            xor_value, file_format = self._get_format(temp_path)  # 获取异或值和文件格式
            # 根据文件格式确定扩展名
            if file_format == 1:
                extension = '.png'
            elif file_format == 2:
                extension = '.jpg'
            else:
                extension = '.gif'
            # 拼接输出文件路径
            output_file = os.path.join(out_path, dat_file_name + extension)
            with open(output_file, "wb") as png_write:  # 以二进制模式写入图片文件
                dat_read.seek(0)  # 将文件指针移动到文件开头
                for line in dat_read:  # 逐行读取.dat文件
                    for byte in line:  # 逐字节处理数据
                        new_byte = byte ^ xor_value  # 异或运算解密
                        png_write.write(bytes([new_byte]))  # 写入解密后的字节

    def _get_format(self, file):
        """获取文件的异或值和格式"""
        formats = [(0x89, 0x50, 0x4e), (0xff, 0xd8, 0xff), (0x47, 0x49, 0x46)]  # 定义常见图片格式的头部字节
        with open(file, "rb") as dat_r:  # 以二进制模式打开文件
            first_line = dat_r.read(3)  # 读取文件的前3个字节
            for index, xor_values in enumerate(formats, start=1):  # 遍历所有格式
                results = [byte ^ xor_values[i] for i, byte in enumerate(first_line)]  # 计算异或结果
                if len(set(results)) == 1:  # 如果异或结果相同
                    return results[0], index  # 返回异或值和格式索引
        return None, None  # 如果没有匹配的格式,返回None

    def _get_dat_files(self, directory):
        """获取指定目录下的所有.dat文件"""
        return [f for f in os.listdir(directory) if f.endswith('.dat')]  # 返回以.dat结尾的文件列表

    def _process_dirs(self):
        """处理所有需要转换的目录"""
        for dir_path in self.dirs_to_process:  # 遍历待处理目录列表
            month = os.path.basename(dir_path)  # 获取目录名(月份)
            self._process_single_dir(dir_path, month)  # 处理单个目录

    def _process_single_dir(self, dir_path, month):
        """处理单个目录中的所有.dat文件"""
        dat_files = self._get_dat_files(dir_path)  # 获取目录下的所有.dat文件
        if not dat_files:  # 如果没有.dat文件,直接返回
            return

        total_files = len(dat_files)  # 计算总文件数
        for index, dat_file in enumerate(dat_files, start=1):  # 遍历所有.dat文件
            dat_file_path = os.path.join(dir_path, dat_file)  # 拼接文件路径
            new_name = f"{month}_{os.path.splitext(dat_file)[0]}"  # 生成新文件名
            self._image_decode(dat_file_path, new_name, self.output_path)  # 解码并保存图片
            progress = int((index / total_files) * 100)  # 计算进度
            status_msg = f'{month} 转换进度: {progress}%\n'  # 生成状态信息
            self._update_status(status_msg)  # 更新状态文本框
            self.converted_files.append(dat_file)  # 添加到已转换文件列表
        self.file_names.append(month)  # 添加到文件名列表

    def _update_status(self, message):
        """更新状态文本框"""
        self.status_text.insert(tk.END, message)  # 在文本框末尾插入消息
        self.status_text.see(tk.END)  # 滚动到最新消息处

    def _check_completion(self):
        """检查任务是否完成"""
        return True  # 目前直接返回True,表示任务完成


class RootTk:
    def __init__(self, title, msg):
        # 创建主窗口
        self.root = tk.Tk()
        self.root.title(title)  # 设置窗口标题
        self.root.geometry('680x400')  # 设置窗口大小

        # 输入路径组件
        input_frame = tk.Frame(self.root)  # 创建输入路径框架
        input_frame.pack(pady=10)  # 将框架添加到窗口
        tk.Label(input_frame, text='请选择要查看的微信图片路径').pack(side=tk.LEFT)  # 添加标签
        self.input_path = tk.StringVar()  # 创建输入路径变量
        input_label = tk.Label(input_frame, textvariable=self.input_path, width=50, relief=tk.SUNKEN)  # 创建路径显示标签
        input_label.pack(side=tk.LEFT, padx=5)  # 将标签添加到框架
        ttk.Button(input_frame, text="选择文件夹", command=self._select_input_folder, style='Custom.TButton').pack(side=tk.LEFT)  # 添加选择文件夹按钮

        # 输出路径组件
        output_frame = tk.Frame(self.root)  # 创建输出路径框架
        output_frame.pack(pady=10)  # 将框架添加到窗口
        tk.Label(output_frame, text='请选择要保存图片的路径').pack(side=tk.LEFT)  # 添加标签
        self.output_path = tk.StringVar()  # 创建输出路径变量
        output_label = tk.Label(output_frame, textvariable=self.output_path, width=50, relief=tk.SUNKEN)  # 创建路径显示标签
        output_label.pack(side=tk.LEFT, padx=5)  # 将标签添加到框架
        ttk.Button(output_frame, text="选择文件夹", command=self._select_output_folder, style='Custom.TButton').pack(side=tk.LEFT)  # 添加选择文件夹按钮

        # 按钮样式
        style = ttk.Style()  # 创建样式对象
        style.configure('Custom.TButton', font=('Arial', 10, 'bold'), bd=0, relief="flat", padding=8)  # 配置按钮样式
        style.map('Custom.TButton', background=[('active', 'darkblue')])  # 设置按钮激活状态样式
        ttk.Button(self.root, text="执行", style='Custom.TButton', command=self._execute_task).pack(pady=20)  # 添加执行按钮

        # 状态显示组件
        status_frame = tk.Frame(self.root)  # 创建状态显示框架
        status_frame.pack(pady=10)  # 将框架添加到窗口
        self.status_text = tk.Text(status_frame, height=5, width=60)  # 创建状态文本框
        self.status_text.pack(side=tk.LEFT)  # 将文本框添加到框架
        scrollbar = tk.Scrollbar(status_frame, command=self.status_text.yview)  # 创建滚动条
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)  # 将滚动条添加到框架
        self.status_text.config(yscrollcommand=scrollbar.set)  # 设置文本框的滚动条

        self.root.mainloop()  # 启动主事件循环

    def _select_input_folder(self):
        # 获取当前的输入路径,如果为空则使用空字符串
        initial_dir = self.input_path.get() if self.input_path.get() else ""
        # 弹出文件夹选择对话框,初始目录为当前输入路径
        folder = filedialog.askdirectory(initialdir=initial_dir)
        # 如果用户选择了文件夹,更新输入路径变量
        if folder:
            self.input_path.set(folder)

    def _select_output_folder(self):
        # 获取当前的输出路径,如果为空则使用空字符串
        initial_dir = self.output_path.get() if self.output_path.get() else ""
        # 弹出文件夹选择对话框,初始目录为当前输出路径
        folder = filedialog.askdirectory(initialdir=initial_dir)
        # 如果用户选择了文件夹,更新输出路径变量
        if folder:
            self.output_path.set(folder)

    def _execute_task(self):
        """执行转换任务"""
        input_path = self.input_path.get()  # 获取输入路径
        output_path = self.output_path.get()  # 获取输出路径
        if input_path and output_path:  # 如果输入和输出路径都存在
            self.status_text.insert(tk.END, '开始执行转换任务...\n')  # 更新状态文本框
            self.status_text.see(tk.END)  # 滚动到最新消息处
            threading.Thread(target=self._execute, args=(input_path, output_path)).start()  # 启动新线程执行任务

    def _execute(self, input_path, output_path):
        """执行转换任务的核心逻辑"""
        try:
            obj = WxAutoExIm(input_path, output_path, self.status_text)  # 创建WxAutoExIm对象
            if obj:  # 如果对象创建成功
                self.status_text.insert(tk.END, '转换任务已完成。\n')  # 更新状态文本框
                self.status_text.see(tk.END)  # 滚动到最新消息处
                messagebox.showinfo(title='完成', message='转换任务已完成')  # 弹出完成提示框
        except Exception as e:  # 捕获异常
            self.status_text.insert(tk.END, f'错误: {str(e)}\n')  # 更新状态文本框
            self.status_text.see(tk.END)  # 滚动到最新消息处


if __name__ == '__main__':
    RootTk('微信图片解密工具', 'Created by AI')  # 启动程序
WePojie 发表于 2025-3-4 15:35
丢到deepseek上就解决了
dzxsl 发表于 2025-3-4 16:09
[Python] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
import os  # 导入操作系统模块,用于处理文件和目录路径
import tkinter as tk  # 导入Tkinter模块,用于创建GUI界面
from tkinter import messagebox, filedialog  # 导入消息框和文件对话框模块
from tkinter import ttk  # 导入Tkinter的ttk模块,提供更现代的控件
import threading  # 导入线程模块,用于多线程处理
 
 
class WxAutoExIm:
    def __init__(self, input_path, output_path, status_text):
        # 初始化输入路径
        self.input_path = input_path
        # 初始化输出路径
        self.output_path = output_path
        # 初始化状态文本框,用于显示转换进度
        self.status_text = status_text
        # 创建输出目录(如果不存在)
        self._create_directory(output_path)
        # 初始化文件名列表
        self.file_names = []
        # 初始化已转换文件列表
        self.converted_files = []
        # 初始化待处理目录列表
        self.dirs_to_process = []
        # 收集需要处理的目录
        self._collect_dirs_to_process()
        # 处理所有目录中的文件
        self._process_dirs()
        # 检查任务是否完成
        self._check_completion()
 
    def _create_directory(self, path):
        """创建目录(如果不存在)"""
        if not os.path.exists(path):  # 检查目录是否存在
            os.makedirs(path)  # 如果不存在,则创建目录
 
    def _collect_dirs_to_process(self):
        """根据输入路径判断需要处理的目录"""
        # 判断输入路径是否是微信的Image目录
        if os.path.basename(self.input_path) == "Image":
            # 如果是Image目录,遍历所有子目录
            for dir_name in os.listdir(self.input_path):
                dir_path = os.path.join(self.input_path, dir_name)  # 拼接子目录路径
                if os.path.isdir(dir_path):  # 检查是否是目录
                    self.dirs_to_process.append(dir_path)  # 添加到待处理目录列表
        else:
            # 如果不是Image目录,直接处理当前目录
            self.dirs_to_process.append(self.input_path)
 
    def _image_decode(self, temp_path, dat_file_name, out_path):
        """解码.dat文件并保存为图片"""
        with open(temp_path, "rb") as dat_read:  # 以二进制模式打开.dat文件
            xor_value, file_format = self._get_format(temp_path)  # 获取异或值和文件格式
            # 根据文件格式确定扩展名
            if file_format == 1:
                extension = '.png'
            elif file_format == 2:
                extension = '.jpg'
            else:
                extension = '.gif'
            # 拼接输出文件路径
            output_file = os.path.join(out_path, dat_file_name + extension)
            with open(output_file, "wb") as png_write:  # 以二进制模式写入图片文件
                dat_read.seek(0# 将文件指针移动到文件开头
                for line in dat_read:  # 逐行读取.dat文件
                    for byte in line:  # 逐字节处理数据
                        new_byte = byte ^ xor_value  # 异或运算解密
                        png_write.write(bytes([new_byte]))  # 写入解密后的字节
 
    def _get_format(self, file):
        """获取文件的异或值和格式"""
        formats = [(0x89, 0x50, 0x4e), (0xff, 0xd8, 0xff), (0x47, 0x49, 0x46)]  # 定义常见图片格式的头部字节
        with open(file, "rb") as dat_r:  # 以二进制模式打开文件
            first_line = dat_r.read(3# 读取文件的前3个字节
            for index, xor_values in enumerate(formats, start=1):  # 遍历所有格式
                results = [byte ^ xor_values[i] for i, byte in enumerate(first_line)]  # 计算异或结果
                if len(set(results)) == 1# 如果异或结果相同
                    return results[0], index  # 返回异或值和格式索引
        return None, None  # 如果没有匹配的格式,返回None
 
    def _get_dat_files(self, directory):
        """获取指定目录下的所有.dat文件"""
        return [f for f in os.listdir(directory) if f.endswith('.dat')]  # 返回以.dat结尾的文件列表
 
    def _process_dirs(self):
        """处理所有需要转换的目录"""
        for dir_path in self.dirs_to_process:  # 遍历待处理目录列表
            month = os.path.basename(dir_path)  # 获取目录名(月份)
            self._process_single_dir(dir_path, month)  # 处理单个目录
 
    def _process_single_dir(self, dir_path, month):
        """处理单个目录中的所有.dat文件"""
        dat_files = self._get_dat_files(dir_path)  # 获取目录下的所有.dat文件
        if not dat_files:  # 如果没有.dat文件,直接返回
            return
 
        total_files = len(dat_files)  # 计算总文件数
        for index, dat_file in enumerate(dat_files, start=1):  # 遍历所有.dat文件
            dat_file_path = os.path.join(dir_path, dat_file)  # 拼接文件路径
            new_name = f"{month}_{os.path.splitext(dat_file)[0]}"  # 生成新文件名
            self._image_decode(dat_file_path, new_name, self.output_path)  # 解码并保存图片
            progress = int((index / total_files) * 100# 计算进度
            status_msg = f'{month} 转换进度: {progress}%\n'  # 生成状态信息
            self._update_status(status_msg)  # 更新状态文本框
            self.converted_files.append(dat_file)  # 添加到已转换文件列表
        self.file_names.append(month)  # 添加到文件名列表
 
    def _update_status(self, message):
        """更新状态文本框"""
        self.status_text.insert(tk.END, message)  # 在文本框末尾插入消息
        self.status_text.see(tk.END)  # 滚动到最新消息处
 
    def _check_completion(self):
        """检查任务是否完成"""
        return True  # 目前直接返回True,表示任务完成
 
 
class RootTk:
    def __init__(self, title, msg):
        # 创建主窗口
        self.root = tk.Tk()
        self.root.title(title)  # 设置窗口标题
        self.root.geometry('680x400'# 设置窗口大小
 
        # 输入路径组件
        input_frame = tk.Frame(self.root)  # 创建输入路径框架
        input_frame.pack(pady=10# 将框架添加到窗口
        tk.Label(input_frame, text='请选择要查看的微信图片路径').pack(side=tk.LEFT)  # 添加标签
        self.input_path = tk.StringVar()  # 创建输入路径变量
        input_label = tk.Label(input_frame, textvariable=self.input_path, width=50, relief=tk.SUNKEN)  # 创建路径显示标签
        input_label.pack(side=tk.LEFT, padx=5# 将标签添加到框架
        ttk.Button(input_frame, text="选择文件夹", command=self._select_input_folder, style='Custom.TButton').pack(side=tk.LEFT)  # 添加选择文件夹按钮
 
        # 输出路径组件
        output_frame = tk.Frame(self.root)  # 创建输出路径框架
        output_frame.pack(pady=10# 将框架添加到窗口
        tk.Label(output_frame, text='请选择要保存图片的路径').pack(side=tk.LEFT)  # 添加标签
        self.output_path = tk.StringVar()  # 创建输出路径变量
        output_label = tk.Label(output_frame, textvariable=self.output_path, width=50, relief=tk.SUNKEN)  # 创建路径显示标签
        output_label.pack(side=tk.LEFT, padx=5# 将标签添加到框架
        ttk.Button(output_frame, text="选择文件夹", command=self._select_output_folder, style='Custom.TButton').pack(side=tk.LEFT)  # 添加选择文件夹按钮
 
        # 按钮样式
        style = ttk.Style()  # 创建样式对象
        style.configure('Custom.TButton', font=('Arial', 10, 'bold'), bd=0, relief="flat", padding=8# 配置按钮样式
        style.map('Custom.TButton', background=[('active', 'darkblue')])  # 设置按钮激活状态样式
        ttk.Button(self.root, text="执行", style='Custom.TButton', command=self._execute_task).pack(pady=20# 添加执行按钮
 
        # 状态显示组件
        status_frame = tk.Frame(self.root)  # 创建状态显示框架
        status_frame.pack(pady=10# 将框架添加到窗口
        self.status_text = tk.Text(status_frame, height=5, width=60# 创建状态文本框
        self.status_text.pack(side=tk.LEFT)  # 将文本框添加到框架
        scrollbar = tk.Scrollbar(status_frame, command=self.status_text.yview)  # 创建滚动条
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)  # 将滚动条添加到框架
        self.status_text.config(yscrollcommand=scrollbar.set# 设置文本框的滚动条
 
        self.root.mainloop()  # 启动主事件循环
 
    def _select_input_folder(self):
        # 获取当前的输入路径,如果为空则使用空字符串
        initial_dir = self.input_path.get() if self.input_path.get() else ""
        # 弹出文件夹选择对话框,初始目录为当前输入路径
        folder = filedialog.askdirectory(initialdir=initial_dir)
        # 如果用户选择了文件夹,更新输入路径变量
        if folder:
            self.input_path.set(folder)
  
    def _select_output_folder(self):
        # 获取当前的输出路径,如果为空则使用空字符串
        initial_dir = self.output_path.get() if self.output_path.get() else ""
        # 弹出文件夹选择对话框,初始目录为当前输出路径
        folder = filedialog.askdirectory(initialdir=initial_dir)
        # 如果用户选择了文件夹,更新输出路径变量
        if folder:
            self.output_path.set(folder)
 
    def _execute_task(self):
        """执行转换任务"""
        input_path = self.input_path.get()  # 获取输入路径
        output_path = self.output_path.get()  # 获取输出路径
        if input_path and output_path:  # 如果输入和输出路径都存在
            self.status_text.insert(tk.END, '开始执行转换任务...\n'# 更新状态文本框
            self.status_text.see(tk.END)  # 滚动到最新消息处
            threading.Thread(target=self._execute, args=(input_path, output_path)).start()  # 启动新线程执行任务
 
    def _execute(self, input_path, output_path):
        """执行转换任务的核心逻辑"""
        try:
            obj = WxAutoExIm(input_path, output_path, self.status_text)  # 创建WxAutoExIm对象
            if obj:  # 如果对象创建成功
                self.status_text.insert(tk.END, '转换任务已完成。\n'# 更新状态文本框
                self.status_text.see(tk.END)  # 滚动到最新消息处
                messagebox.showinfo(title='完成', message='转换任务已完成'# 弹出完成提示框
        except Exception as e:  # 捕获异常
            self.status_text.insert(tk.END, f'错误: {str(e)}\n'# 更新状态文本框
            self.status_text.see(tk.END)  # 滚动到最新消息处
 
 
if __name__ == '__main__':
    RootTk('微信图片解密工具', 'Created by AI'# 启动程序
调味包 发表于 2025-3-4 17:04
原则上来说是没点开过的图就只有缩略图,很模糊,只要点开过基本上没什么问题   
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-5-18 12:11

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表