├── main.ico ├── __pycache__ ├── ProgressBar.cpython-311.pyc ├── SQLManager.cpython-311.pyc ├── CrackWeChatDB.cpython-311.pyc └── WechatManager.cpython-311.pyc ├── ProgressBar.py ├── LICENSE ├── CrackWeChatDB.py ├── README.md ├── SQLManager.py ├── WechatManager.py └── Main.py /main.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f13T2ach/WxMsgDump/HEAD/main.ico -------------------------------------------------------------------------------- /__pycache__/ProgressBar.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f13T2ach/WxMsgDump/HEAD/__pycache__/ProgressBar.cpython-311.pyc -------------------------------------------------------------------------------- /__pycache__/SQLManager.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f13T2ach/WxMsgDump/HEAD/__pycache__/SQLManager.cpython-311.pyc -------------------------------------------------------------------------------- /__pycache__/CrackWeChatDB.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f13T2ach/WxMsgDump/HEAD/__pycache__/CrackWeChatDB.cpython-311.pyc -------------------------------------------------------------------------------- /__pycache__/WechatManager.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f13T2ach/WxMsgDump/HEAD/__pycache__/WechatManager.cpython-311.pyc -------------------------------------------------------------------------------- /ProgressBar.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | 5 | def progress_bar(text,finish_tasks_number, tasks_number): 6 | percentage = round(finish_tasks_number / tasks_number * 100) 7 | num = round(finish_tasks_number / tasks_number * 50) 8 | process = "\r"+text+" %3s%%: |%-50s |" % (percentage, '▊' * num) 9 | print(process, end='', flush=True) 10 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Sokasne Redmaple 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CrackWeChatDB.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import hmac 5 | import ctypes 6 | import hashlib 7 | import argparse 8 | import ProgressBar 9 | #import pyaes 10 | from Crypto.Cipher import AES 11 | 12 | 13 | def decrypt_msg(path, password, taskid ,tasktotal): 14 | 15 | KEY_SIZE = 32 16 | DEFAULT_ITER = 64000 17 | DEFAULT_PAGESIZE = 4096 # 4048数据 + 16IV + 20 HMAC + 12 18 | SQLITE_FILE_HEADER = bytes("SQLite format 3", encoding="ASCII") + bytes(1) # SQLite 文件头 19 | 20 | password = bytes.fromhex(password) 21 | with open(path, "rb") as f: 22 | # TODO: 优化,考虑超大文件 23 | blist = f.read() 24 | 25 | salt = blist[:16] # 前16字节为盐 26 | key = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE) # 获得Key 27 | 28 | page1 = blist[16:DEFAULT_PAGESIZE] # 丢掉salt 29 | 30 | mac_salt = bytes([x ^ 0x3a for x in salt]) 31 | mac_key = hashlib.pbkdf2_hmac("sha1", key, mac_salt, 2, KEY_SIZE) 32 | 33 | hash_mac = hmac.new(mac_key, digestmod="sha1") 34 | hash_mac.update(page1[:-32]) 35 | hash_mac.update(bytes(ctypes.c_int(1))) 36 | 37 | if hash_mac.digest() != page1[-32:-12]: 38 | raise RuntimeError("[-]解密失败 密码错误: ",password) 39 | 40 | 41 | pages = [blist[i:i+DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)] 42 | pages.insert(0, page1) # 把第一页补上 43 | with open(f"{path}.dec.db", "wb") as f: 44 | f.write(SQLITE_FILE_HEADER) # 写入文件头 45 | 46 | count = 0 47 | for i in pages: 48 | t = AES.new(key, AES.MODE_CBC, i[-48:-32]) 49 | f.write(t.decrypt(i[:-48])) 50 | f.write(i[-48:]) 51 | count += 1 52 | ProgressBar.progress_bar("正在解密第"+str(taskid)+"项, 共"+str(tasktotal)+"项",count,len(pages)) 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # WxMsgDump 4 | 5 | **开源的导出微信聊天记录的程序** 6 | 7 |
8 | 9 | ## 公告 / NOTICE 10 | 11 | 由于寻址算法的更新,导致无法直接索取到wxid,可能要要求用户输入数据库的目录。 12 | 13 | 这会使程序不是自动的,不过也可以防止被不法分子利用(?) 14 | 15 | 我打算在下几个版本中做聊天记录转图片的功能,方便法律取证。 16 | 17 | 注意!当前库中的源代码还不是可用版本,请修改之(手动重定义路径变量)使用。 18 | 19 | ## 介绍 / INTRODUCTION 20 | 21 | **运行要求:** Python 2.0以上环境 22 | 23 | **运行系统:** Windows 7至11 24 | 25 | **版本** 1.1 26 | 27 | 请在Cmd中运行以下代码,来下载依赖库(如果没有) 28 | 29 | ``` 30 | pip install pywin32 31 | pip install pymem 32 | pip install pycryptodome 33 | ``` 34 | 35 | ## 运行方法 / RUNNING METHOD 36 | 37 | 方法一、运行源码 38 | 39 | ``` 40 | python Main.py 41 | ``` 42 | 43 | 方法二、运行可执行文件 44 | 45 | 1.解压缩 46 | 47 | 2.双击运行目录内唯一的一个exe文件 48 | 49 | ## 更新 / UPDATE 50 | 51 | ### 2023/4/8 1.1 Update 52 | 53 | **Add** 54 | 55 | 1.支持自动获取用户文件地址,无需手动输入; 56 | 57 | 2.支持判定是否有解密过的文件出现; 58 | 59 | 3.移除基址库,主动寻址; 60 | 61 | 4.进度条样式等若干项; 62 | 63 | **Repair** 64 | 65 | 4.修复SqlManager类里的一个疏忽:没有根据时间戳排序,导致导出的数据异常; 66 | 67 | 5.修复逻辑层面的疏忽:简单粗暴的合并聊天文件,导致只能读取MSG0.dec.db中的聊天记录。 68 | 69 | ## 鸣谢 / ACKOWNLEDGEMENT 70 | 71 | **SnowMeteors / GetWeChatKey** https://github.com/SnowMeteors/GetWeChatKey 72 | 73 | **x1hy9 / 微信进程逆向** https://github.com/x1hy9/WeChatUserDB 74 | 75 | ## 贡献墙 / CONTRIBUTION 76 | 77 | **ruitong1983** 提出数据溢出的异常并给出有效解决方案 78 | 79 | **JoeQinOvO** 提出算法更新的解决方案 80 | 81 | ## 警告 / WARNING 82 | 83 | **逆向是微信条款中不允许的行为,窃取、篡改聊天记录是违法行为。程序仅供学习交流。** 84 | 85 | 严禁以此程序为基础,窃取他人聊天记录; 86 | 87 | 严禁以此程序为基础,制作有窃取、篡改用户微信聊天记录的功能的程序; 88 | 89 | 严禁以此程序为基础,制作以勒索或者其它暴力盈利行为为目的的程序; 90 | 91 | 严禁以此程序为基础,向其它用户提供“导出聊天记录”或者类似以盈利为目的的服务,这同时会威胁到其它用户的账号安全; 92 | 93 | 严禁以此程序为基础,进行记录、保存、分享、上传、分析或有类似的不经过被收集用户同意来收集用户隐私的行为; 94 | 95 | 严禁销售贩卖此程序的本体,或者以此程序为基础仿制的衍生版本的程序; 96 | 97 | 在转载代码片段时,必须注明正确的代码出处,“鸣谢”部分提到的项目中的代码应注明原作者的联系方式或者主页。 98 | 99 | **说明:** 本应用程序由用户自愿下载安装、编译使用,自愿逆向自己或其它用户的账号聊天记录,接受将解密后的聊天记录暴露在文件系统中的危险性。并且,用户使用此应用程序非法侵害其它用户权利的行为与本应用程序作者无关,本应用程序的作者已经尽到了警告的义务。 100 | 101 | **下载、保存、进一步浏览源代码或者下载安装、编译使用本程序,表示你同意本警告,并承诺遵守它。** 102 | 103 | **您应该在下载保存,编译使用本项目的24小时内,删除本项目的源代码和(编译出的)程序。** 104 | 105 | ## 更新历史 / UPDATE HISTORY 106 | 107 |
108 | 109 | **INSIDE HER NEW BALENCIAGA** 110 | 111 |
112 | -------------------------------------------------------------------------------- /SQLManager.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import os 3 | import time 4 | import ProgressBar 5 | 6 | workDirName = "微信聊天导出" 7 | 8 | def get_chatlist(path): 9 | conn = sqlite3.connect(path) 10 | cursor = conn.execute("SELECT UserName,Alias,Remark,NickName from Contact") 11 | output = cursor.fetchall() 12 | conn.close() 13 | return output 14 | 15 | def msg_export(path,uuid,nick,wxid,exportId): 16 | conn = sqlite3.connect(path) 17 | try: 18 | cursor = conn.execute("SELECT Type,IsSender,CreateTime,StrTalker,StrContent from MSG WHERE StrTalker = '%s'"% uuid+" ORDER BY CreateTime ASC") 19 | cursor = cursor.fetchall() 20 | except Exception as ex: 21 | print("[-]不存在该聊天的聊天记录,这是异常信息: ",ex) 22 | conn.close() 23 | return False,ex 24 | desktop_path = os.path.abspath(".") # 存放路径 25 | full_path = "%s\\%s\\%s(%s).csv"%(desktop_path,workDirName,nick+" "+wxid,uuid) 26 | 27 | folder = os.path.exists(desktop_path+"\\"+workDirName) 28 | if not folder: 29 | os.makedirs(desktop_path+"\\"+workDirName) 30 | 31 | # 判断是否是追加 32 | if os.path.exists(full_path) or exportId > 1: 33 | file = open(full_path, 'a', encoding='utf-8-sig') 34 | else: 35 | file = open(full_path, 'w', encoding='utf-8-sig') 36 | # 写入表头 37 | file.write("昵称,%s\n"% nick) 38 | file.write("微信号,%s\n"% wxid) 39 | file.write("时间,对方,你的回复\n") 40 | 41 | 42 | for msg in cursor: 43 | date=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(msg[2])) #转换时间戳 44 | if msg[0] == 49: 45 | content = '[应用消息或表情]' 46 | elif msg[0] == 47: 47 | content = '[动画表情]' 48 | elif msg[0] == 3: 49 | content = '[图片]' 50 | elif msg[0] == 42: 51 | content = '[名片]' 52 | elif msg[0] == 43: 53 | content = '[视频]' 54 | elif msg[0] == 34: 55 | content = '[语音]' 56 | else: 57 | content = msg[4].replace("\n", " ") #TODO 使用表格来允许换行 58 | 59 | if msg[1]==1: #是自己发的 60 | file.write(str(date)+", ,"+str(content)+"\n") 61 | elif msg[1]==0: 62 | file.write(str(date)+","+str(content)+", \n") 63 | 64 | ProgressBar.progress_bar("正在导出第"+str(exportId)+"个表中的第"+str(cursor.index(msg)+1)+"条, 共"+str(len(cursor))+"条",cursor.index(msg)+1,len(cursor)) 65 | msg=[] 66 | 67 | print() 68 | file.close() 69 | conn.close() 70 | return True,full_path 71 | 72 | def merge_databases(db1, db2): 73 | con = sqlite3.connect(db1) 74 | 75 | con.execute("ATTACH '" + db2 + "' as dba") 76 | 77 | con.execute("BEGIN") 78 | for row in con.execute("SELECT * FROM dba.sqlite_master WHERE type='table'"): 79 | combine = "INSERT OR IGNORE INTO "+ row[1] + " SELECT * FROM dba." + row[1] 80 | con.execute(combine) 81 | con.commit() 82 | con.execute("detach database dba") 83 | 84 | 85 | def batch_merge(paths): 86 | for db_file in paths: 87 | merge_databases(paths[0], db_file) 88 | return paths[0] -------------------------------------------------------------------------------- /WechatManager.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import binascii 4 | import pymem.process 5 | from pymem import Pymem 6 | from win32api import GetFileVersionInfo, HIWORD, LOWORD 7 | 8 | 9 | class Wechat: 10 | def __init__(self, pm): 11 | module = pymem.process.module_from_name(pm.process_handle, "WeChatWin.dll") 12 | self.pm = pm 13 | self.dllBase = module.lpBaseOfDll 14 | self.sizeOfImage = module.SizeOfImage 15 | self.bits = self.GetPEBits() 16 | 17 | # 通过解析PE来获取位数 18 | def GetPEBits(self): 19 | address = self.dllBase + self.pm.read_int(self.dllBase + 60) + 4 + 16 20 | SizeOfOptionalHeader = self.pm.read_short(address) 21 | 22 | # 0XF0 64bit 23 | if SizeOfOptionalHeader == 0xF0: 24 | return 64 25 | 26 | return 32 27 | 28 | def GetInfo(self): 29 | version = self.GetVersion() 30 | if not version: 31 | print("[!]获取 WeChatWin.dll 失败") 32 | return 33 | 34 | print(f"[-]WeChat 版本:{version}") 35 | print(f"[-]WeChat 架构: {self.bits}") 36 | 37 | keyBytes = b'-----BEGIN PUBLIC KEY-----\n...' 38 | 39 | # 从内存中查找 BEGIN PUBLIC KEY 的地址 40 | publicKeyList = pymem.pattern.pattern_scan_all(self.pm.process_handle, keyBytes, return_multiple=True) 41 | if len(publicKeyList) == 0: 42 | print("[!]无法找到公钥") 43 | return 44 | 45 | keyAddr = self.GetKeyAddr(publicKeyList) 46 | if keyAddr is None: 47 | print("[!]无法找到密钥的地址") 48 | return 49 | 50 | keyLenOffset = 0x8c if self.bits == 32 else 0xd0 51 | 52 | for addr in keyAddr: 53 | try: 54 | keyLen = self.pm.read_uchar(addr - keyLenOffset) 55 | if self.bits == 32: 56 | key = self.pm.read_bytes(self.pm.read_int(addr - 0x90), keyLen) 57 | else: 58 | key = self.pm.read_bytes(self.pm.read_longlong(addr - 0xd8), keyLen) 59 | 60 | key = binascii.b2a_hex(key).decode() 61 | if self.CheckKey(key): 62 | print(f"[+]找到公钥 {key}") 63 | print("[+]密钥破解完成") 64 | return addr,key 65 | except: 66 | print("[!]发生异常,密钥轮询失败") 67 | pass 68 | 69 | 70 | 71 | 72 | 73 | @staticmethod 74 | def CheckKey(key): 75 | # 目前key位数是32位 76 | if key is None or len(key) != 64: 77 | return False 78 | 79 | return True 80 | 81 | # 内存搜索特征码 82 | @staticmethod 83 | def SearchMemory(parent, child): 84 | offset = [] 85 | index = -1 86 | 87 | while True: 88 | index = parent.find(child, index + 1) 89 | if index == -1: 90 | break 91 | offset.append(index) 92 | 93 | return offset 94 | 95 | # 获取key的地址 96 | def GetKeyAddr(self, publicKeyList): 97 | # 存放真正的key地址 98 | keyAddr = [] 99 | 100 | # 读取整个 WeChatWin.dll 的内容 101 | buffer = self.pm.read_bytes(self.dllBase, self.sizeOfImage) 102 | 103 | byteLen = 4 if self.bits == 32 else 8 104 | for publicKeyAddr in publicKeyList: 105 | keyBytes = publicKeyAddr.to_bytes(byteLen, byteorder="little", signed=True) 106 | offset = self.SearchMemory(buffer, keyBytes) 107 | 108 | if not offset or len(offset) == 0: 109 | continue 110 | 111 | offset[:] = [x + self.dllBase for x in offset] 112 | keyAddr += offset 113 | 114 | if len(keyAddr) == 0: 115 | return None 116 | 117 | return keyAddr 118 | 119 | # 获取微信版本 120 | def GetVersion(self): 121 | WeChatWindll_path = "" 122 | for m in list(self.pm.list_modules()): 123 | path = m.filename 124 | if path.endswith("WeChatWin.dll"): 125 | WeChatWindll_path = path 126 | break 127 | 128 | if not WeChatWindll_path: 129 | return False 130 | 131 | version = GetFileVersionInfo(WeChatWindll_path, "\\") 132 | 133 | msv = version['FileVersionMS'] 134 | lsv = version['FileVersionLS'] 135 | version = f"{str(HIWORD(msv))}.{str(LOWORD(msv))}.{str(HIWORD(lsv))}.{str(LOWORD(lsv))}" 136 | 137 | return version 138 | 139 | def GetUserBasicInfo(self,base_address): 140 | print(base_address) 141 | # 获取wxid 142 | int_wxid_len = self.pm.read_int(base_address - 0x44) 143 | wxid_addr = self.pm.read_int(base_address - 0x54) 144 | print(wxid_addr) 145 | WXID = self.pm.read_bytes(wxid_addr, int_wxid_len) 146 | 147 | # 获取用户名 148 | int_wxprofile_len = self.pm.read_int(base_address - 0x5c) 149 | WxProfile = self.pm.read_bytes(base_address - 0x6c, int_wxprofile_len) 150 | 151 | return WXID.decode(),WxProfile.decode() 152 | 153 | 154 | if __name__ == '__main__': 155 | 156 | try: 157 | wechat = Pymem("WeChat.exe") 158 | Wechat(wechat).GetInfo() 159 | except pymem.exception.ProcessNotFound: 160 | print("[!]微信没有登录") 161 | except pymem.exception.CouldNotOpenProcess: 162 | print("[!]权限不足") 163 | except Exception as e: 164 | print(e) 165 | -------------------------------------------------------------------------------- /Main.py: -------------------------------------------------------------------------------- 1 | # 版本号 2 | VER = "V1.1" 3 | import WechatManager 4 | import CrackWeChatDB 5 | import os 6 | import sys 7 | import sqlite3 8 | import SQLManager 9 | import keyboard 10 | import pymem 11 | from pymem import Pymem 12 | from pick import pick 13 | from win32com.shell import shell 14 | 15 | 16 | # Msg目录下的表 17 | msg_table = ["MicroMsg.db"] 18 | # Mutil目录下的表 除了msgn.db 19 | mutil_table = [] 20 | # Micromsg.db的路径 21 | micromsg_path = "" 22 | # MSGn.db文件数组 23 | msgn_path = [] 24 | 25 | # 聊天列表 下标对应UserName,Alias,Remark,NickName 26 | wxlist = [] 27 | 28 | def main(): 29 | res = [] #所有db文件的名称数组 30 | isSkipDc = False #跳过解密 31 | 32 | print("WxMsgDump%s | 微信聊天记录导出"%VER) 33 | print() 34 | print("Copyright(c) Redmaple") 35 | print("----------------------------------------") 36 | 37 | # 定义对象 38 | try: 39 | wechat = Pymem("WeChat.exe") 40 | ret = WechatManager.Wechat(wechat).GetInfo() 41 | keyAddress = ret[0] 42 | aeskey = ret[1] 43 | except pymem.exception.ProcessNotFound: 44 | print("[!]微信没有登录") 45 | except pymem.exception.CouldNotOpenProcess: 46 | print("[!]权限不足") 47 | except Exception as e: 48 | print(e) 49 | 50 | wx = WechatManager 51 | ret = wx.Wechat(wechat).GetUserBasicInfo(keyAddress) 52 | wxid = ret[1] 53 | wxprofile = ret[2] 54 | 55 | # 自动获取文件路径 56 | win_user = os.path.expandvars('$HOMEPATH') 57 | wx_config = open(os.getenv("SystemDrive") + win_user + '\\AppData\\Roaming\\Tencent\\WeChat\\All Users\\config\\3ebffe94.ini') 58 | 59 | if wx_config.read() == 'MyDocument:': 60 | wx_path = shell.SHGetFolderPath(0, 5, None, 0)+"\\WeChat Files\\"+wxid+"\\Msg" # 如果目录在 文档 下 61 | else: 62 | wx_path = wx_config.read() + "\\WeChat Files\\"+wxid+"\\Msg" 63 | dir_path = wx_path + "\\Multi" 64 | 65 | print("[+]微信号: "+wxprofile+" 工作路径: "+wx_path) 66 | 67 | 68 | # 检查是否有留存过的解密文件 69 | files = os.listdir(dir_path) 70 | 71 | for name in files: 72 | if name.endswith(".dec.db"): 73 | key = input("[>]发现上次解密过的文件。跳过解密步骤->Y 删除全部解密文件->D 覆盖解密->回车:").capitalize() 74 | if key == 'Y': 75 | isSkipDc = True 76 | if key == 'D': 77 | del_decryptf(wx_path) 78 | del_decryptf(dir_path) 79 | input("[+]操作完成,任意键退出") 80 | exit(0) 81 | break 82 | 83 | # 获取Mutil下的表 84 | try: 85 | for path in os.listdir(dir_path): 86 | if os.path.isfile(os.path.join(dir_path, path)) and path.endswith(".db") and path.endswith("dec.db") is not True: 87 | if path.startswith("MSG") or path in mutil_table: 88 | res.append(path) 89 | #TODO 需要额外询问 90 | except: 91 | #输入d可以打开debug 92 | #if(dir_path == "d\\Msg\\Multi"): 93 | # print("[Opening Debug]") 94 | # DBManagement.main() 95 | # return 96 | #else: 97 | print("[-]路径无效。请注意,你可能忽略了点击“打开文件夹”按钮这个步骤") 98 | exit(-1) 99 | 100 | # 获取Msg目录下的表 101 | for path in os.listdir(wx_path): 102 | if os.path.isfile(os.path.join(wx_path, path)) and path in msg_table: 103 | res.append(path) 104 | 105 | print("[+]发现以下表: "+str(list(res))) 106 | if res is []: 107 | print("[+]解密完成,没有发现任何聊天文件,或者已经解密过") 108 | exit(0) 109 | 110 | 111 | if isSkipDc is not True: 112 | #删除以前的解密 113 | for file_name in os.listdir(dir_path): 114 | if file_name.endswith(".dec.db"): 115 | try: 116 | os.remove(file_name) 117 | except: 118 | pass 119 | #执行加密 120 | decryptMsg(res,dir_path,wx_path,aeskey) 121 | 122 | 123 | 124 | # 将之转换成路径数组 125 | for path in res: 126 | if(path.startswith("MSG")): 127 | msgn_path.append(dir_path+"\\"+path+".dec.db") 128 | elif path in msg_table: 129 | if path == "MicroMsg.db": 130 | micromsg_path = wx_path+"\\"+path+".dec.db" 131 | 132 | print("[+]正在获取聊天列表") 133 | wxlist = SQLManager.get_chatlist(micromsg_path) 134 | while True: 135 | print("[+]请输入要导出的聊天名称,或者你给他/她的备注。空表示退出操作。") 136 | aimChat = input("[>]") 137 | if aimChat=="": 138 | break 139 | repeat_count = 1 140 | for chat in wxlist: 141 | #print(chat[3]) 142 | if chat[3]==aimChat or chat[2]==aimChat: 143 | print("============FOUND TARGET===========") 144 | print("[+]匹配到第"+str(repeat_count)+"个聊天: ",chat[0]) 145 | print("[+]微信号: ", chat[1]) 146 | print("[+]备注: ",chat[2]) 147 | print("[+]昵称: ",chat[3]) 148 | export_msg(msgn_path,chat[0],chat[3],chat[1]) 149 | repeat_count = repeat_count+1 150 | print("=============END OUTPUT============") 151 | if repeat_count == 1: 152 | print("[!]找不到此聊天: ",aimChat) 153 | del_decryptf(wx_path) 154 | del_decryptf(dir_path) 155 | 156 | 157 | 158 | def decryptMsg(res,dir_path,wx_path,aeskey): 159 | for path in res: 160 | if(path.startswith("MSG")): # 解密Mutil下的文件 161 | CrackWeChatDB.decrypt_msg(dir_path+"\\"+path,aeskey,res.index(path)+1,len(res)) 162 | else: # 解密Msg下的文件 163 | CrackWeChatDB.decrypt_msg(wx_path+"\\"+path,aeskey,res.index(path)+1,len(res)) 164 | if(res.index(path)+1!=len(res)): 165 | # 覆盖原来的进度条 166 | print("\r"," ",end="",flush=True) 167 | print() 168 | 169 | def del_decryptf(path): 170 | for root , dirs, files in os.walk(path): 171 | for name in files: 172 | if name.endswith(".dec.db"): #指定要删除的格式,这里是jpg 可以换成其他格式 173 | os.remove(os.path.join(root, name)) 174 | 175 | def export_msg(msg_paths,uuid,nick,wxid): 176 | outputPath = "" 177 | for path in msg_paths: 178 | flag,outputPath = SQLManager.msg_export(path,uuid,nick,wxid,msg_paths.index(path)+1) 179 | print("[+]完成,导出到"+outputPath) 180 | 181 | if __name__ == '__main__': 182 | main() --------------------------------------------------------------------------------