diff --git a/doc/FAQ.md b/doc/FAQ.md index d1e96b1..fb367a4 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -118,6 +118,15 @@ pip install PyWxDump 2. `MediaMsg0`~`MediaMsg5`是微信聊天记录不同时间段下的数据库,`MediaMsg0`是最旧的,`MediaMsg5`是最新的 3. 一般来说,如果想看最新的聊天记录,就选择`MSG5`和`MediaMsg5`,如果想看最旧的聊天记录,就选择`MSG0`和`MediaMsg0`,如果想看中间的聊天记录,就选择`MSG1`~`MSG4`和`MediaMsg1`~`MediaMsg4` -### 十八、qq交流群:577704006 or 点击链接加入群聊[pywxdump功能交流](https://s.xaoyo.top/gOLUDl) 密码 +### 十八、如何合并数据库文件? -1433223 \ No newline at end of file +使用命令`wxdump.exe merge`,然后根据提示输入参数,回车键确认。 +eg:`wxdump.exe merge -i "C:\Users\user\Desktop\decrypted\MSG0.db,C:\Users\user\Desktop\decrypted\MSG1.db,C:\Users\user\Desktop\decrypted\MSG2.db" -o "C:\Users\user\Desktop\decrypted\merge.db"` + +### 十八、qq交流群密码 + +相信你看到这里,已经可以自己解决所有问题了。 + +如果实在还有疑问,更加建议提交issues。 + +如果还是想添加qq群,那么关注公众号:`逍遥之芯`,回复`qq群密码`即可获取qq群密码。(因为qq群又快满了,群主又没钱买vip,所以只能这样了) \ No newline at end of file diff --git a/pywxdump/cli.py b/pywxdump/cli.py index b7edb1f..8dc353b 100644 --- a/pywxdump/cli.py +++ b/pywxdump/cli.py @@ -278,6 +278,7 @@ class MainAll(): print("[-] 未获取到密钥") return wxid = user.get("wxid", None) + filePath = user.get("filePath", None) WxDbPath = get_wechat_db('all', None, wxid=wxid, is_logging=True) # 获取微信数据库路径 if isinstance(WxDbPath, str): # 如果返回的是字符串,则表示出错 @@ -336,8 +337,7 @@ class MainAll(): print("[-] 未获取到解密后的数据库路径") return - user_path = out_dbs[0].split("MSG") - FileStorage_path = os.path.join(user_path[0], "FileStorage") + FileStorage_path = os.path.join(filePath, "FileStorage") if filePath else "FileStorage" # 查看聊天记录 MSGDB = [i for i in out_dbs if "de_MSG" in i] diff --git a/pywxdump/ui/view_chat.py b/pywxdump/ui/view_chat.py index 6bf965c..e769d72 100644 --- a/pywxdump/ui/view_chat.py +++ b/pywxdump/ui/view_chat.py @@ -129,7 +129,8 @@ def load_chat_records(selected_talker, start_index, page_size, user_list, MSG_AL cursor1.close() db1.close() # 获取图片的base64数据 - img_md5_data = load_base64_img_data(result1[0][7], result1[-1][7], username_md5, FileStorage_path) if len(result1) > 0 else {} + # img_md5_data = load_base64_img_data(result1[0][7], result1[-1][7], username_md5, FileStorage_path) if len( + # result1) > 0 else {} data = [] room_username_count = {} @@ -170,13 +171,22 @@ def load_chat_records(selected_talker, start_index, page_size, user_list, MSG_AL content["src"] = src elif Type == 3 and SubType == 0: # 图片 xml_content = parse_xml_string(StrContent) - md5 = xml_content.get("img", {}).get("md5", "") - if md5: - content["src"] = img_md5_data.get(md5, "") + BytesExtra = read_BytesExtra(BytesExtra) + BytesExtra =str(BytesExtra) + match = re.search(r"MsgAttach(.*?)'", BytesExtra) + if match: + img_path = match.group(0).replace("'", "") + print(FileStorage_path) + print(img_path) + img_path = os.path.join(FileStorage_path, img_path) + fomt, md5, out_bytes = read_img_dat(img_path) + out_bytes = base64.b64encode(out_bytes).decode("utf-8") + content["src"] = f"data:{fomt};base64,{out_bytes}" else: content["src"] = "" content["msg"] = "图片" + else: content["msg"] = StrContent talker = "未知" diff --git a/pywxdump/wx_info/decryption.py b/pywxdump/wx_info/decryption.py index 6d0aac9..b9ef967 100644 --- a/pywxdump/wx_info/decryption.py +++ b/pywxdump/wx_info/decryption.py @@ -12,7 +12,6 @@ # 为了保证数据部分长度是16字节即AES块大小的整倍数,每一页的末尾将填充一段空字节,使得保留字段的长度为48字节。 # 综上,加密文件结构为第一页4KB数据前16字节为盐值,紧接着4032字节数据,再加上16字节IV和20字节HMAC以及12字节空字节;而后的页均是4048字节长度的加密数据段和48字节的保留段。 # ------------------------------------------------------------------------------- -import argparse import hmac import hashlib import os @@ -171,6 +170,7 @@ def encrypt(key: str, db_path, out_path): :param out_path: 加密后的数据库输出路径(必须是文件) :return: """ + import subprocess if not os.path.exists(db_path) or not os.path.isfile(db_path): return False, f"[-] db_path:'{db_path}' File not found!" if not os.path.exists(os.path.dirname(out_path)): @@ -178,30 +178,4 @@ def encrypt(key: str, db_path, out_path): if len(key) != 64: return False, f"[-] key:'{key}' Len Error!" - - password = bytes.fromhex(key.strip()) - with open(db_path, "rb") as file: - blist = file.read() - - salt = os.urandom(16) # 生成随机盐值 - byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE) - - # 计算消息认证码 - mac_salt = bytes([(salt[i] ^ 58) for i in range(16)]) - mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE) - hash_mac = hmac.new(mac_key, blist[:-32], hashlib.sha1) - hash_mac.update(b'\x01\x00\x00\x00') - mac_digest = hash_mac.digest() - - newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)] - - with open(out_path, "wb") as enFile: - enFile.write(salt) # 写入盐值 - enFile.write(mac_digest) # 写入消息认证码 - - for i in newblist: - t = AES.new(byteKey, AES.MODE_CBC, os.urandom(16)) # 生成随机的初始向量 - encrypted = t.encrypt(i) # 加密数据块 - enFile.write(encrypted) - - return True, [db_path, out_path, key] + return False, f"error!"