diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 0dc18c4..ab978f1 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,13 +4,16 @@ - @@ -680,7 +674,8 @@ - @@ -699,24 +694,9 @@ file://$PROJECT_DIR$/app/decrypt/decrypt.py - 105 + 103 - - file://$PROJECT_DIR$/app/person.py - 100 - - - file://$PROJECT_DIR$/app/person.py - 98 - - - file://$PROJECT_DIR$/app/person.py - 99 - diff --git a/app/DataBase/hard_link.py b/app/DataBase/hard_link.py new file mode 100644 index 0000000..448199f --- /dev/null +++ b/app/DataBase/hard_link.py @@ -0,0 +1,78 @@ +import binascii +import os.path +import sqlite3 +import threading +import xml.etree.ElementTree as ET + +lock = threading.Lock() +DB = None +cursor = None +db_path = "./app/Database/Msg/HardLinkImage.db" +root_path = '/MsgAttach/' +if os.path.exists(db_path): + DB = sqlite3.connect(db_path, check_same_thread=False) + # '''创建游标''' + cursor = DB.cursor() + + +def init_database(): + global DB + global cursor + if not DB: + if os.path.exists(db_path): + DB = sqlite3.connect(db_path, check_same_thread=False) + # '''创建游标''' + cursor = DB.cursor() + + +def get_image_by_md5(md5: bytes): + sql = ''' + select Md5Hash,MD5,FileName,HardLinkImageID.Dir as DirName1,HardLinkImageID2.Dir as DirName2 + from HardLinkImageAttribute + join HardLinkImageID on HardLinkImageAttribute.DirID1 = HardLinkImageID.DirID + join HardLinkImageID as HardLinkImageID2 on HardLinkImageAttribute.DirID2 = HardLinkImageID2.DirID + where MD5 = ?; + ''' + try: + lock.acquire(True) + cursor.execute(sql, [md5, ]) + result = cursor.fetchone() + return result + finally: + lock.release() + + +def get_md5_from_xml(content): + # 解析XML + root = ET.fromstring(content) + # 提取md5的值 + md5_value = root.find(".//img").get("md5") + print(md5_value) + return md5_value + + +def get_image(content, thumb=True): + md5 = get_md5_from_xml(content) + # md5 = 'bc37a58c32cb203ee9ac587b068e5853' + result = get_image_by_md5(binascii.unhexlify(md5)) + if result: + print(result) + dir1 = result[3] + dir2 = result[4] + data_image = result[2] + dir0 = 'Thumb' if thumb else 'Image' + dat_image = os.path.join(root_path, dir1, dir0, dir2, data_image) + return dat_image + + +# 6b02292eecea118f06be3a5b20075afc_t + +if __name__ == '__main__': + msg_root_path = './Msg/' + db_path = "./Msg/HardLinkImage.db" + init_database() + content = '''\n\t\n\t\n\t\n\n''' + print(get_image(content)) + print(get_image(content, thumb=False)) + result = get_md5_from_xml(content) + print(result) diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index 34bf547..c1e3940 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -6,6 +6,9 @@ from PyQt5.QtCore import pyqtSignal, QThread from . import msg from ..log import log +if not os.path.exists('./data/聊天记录'): + os.mkdir('./data/聊天记录') + class Output(QThread): """ diff --git a/app/decrypt/get_wx_info.py b/app/decrypt/get_wx_info.py index 395110d..52f844f 100644 --- a/app/decrypt/get_wx_info.py +++ b/app/decrypt/get_wx_info.py @@ -65,7 +65,7 @@ def read_info(version_list, is_logging=False): if len(wechat_process) == 0: error = "[-] WeChat No Run" if is_logging: print(error) - return error + return -1 for process in wechat_process: tmp_rd = {} @@ -76,8 +76,9 @@ def read_info(version_list, is_logging=False): bias_list = version_list.get(tmp_rd['version'], None) if not isinstance(bias_list, list): error = f"[-] WeChat Current Version {tmp_rd['version']} Is Not Supported" - if is_logging: print(error) - return error + if is_logging: + print(error) + return -2 wechat_base_address = 0 for module in process.memory_maps(grouped=False): @@ -86,8 +87,9 @@ def read_info(version_list, is_logging=False): break if wechat_base_address == 0: error = f"[-] WeChat WeChatWin.dll Not Found" - if is_logging: print(error) - return error + if is_logging: + print(error) + return -3 Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, process.pid) @@ -128,7 +130,7 @@ def get_info(): with open(VERSION_LIST_PATH, "r", encoding="utf-8") as f: VERSION_LIST = json.load(f) - result = read_info(VERSION_LIST) # 读取微信信息 + result = read_info(VERSION_LIST, True) # 读取微信信息 return result diff --git a/app/person.py b/app/person.py index 32c8dd4..e52aa89 100644 --- a/app/person.py +++ b/app/person.py @@ -55,6 +55,10 @@ def singleton(cls): class MePC: def __init__(self): self.avatar = QPixmap(Icon.Default_avatar_path) + self.wxid = '' + self.wx_dir = '' + self.name = '' + self.mobile = '' def set_avatar(self, img_bytes): if not img_bytes: diff --git a/app/ui_pc/mainview.py b/app/ui_pc/mainview.py index 4826281..cf2005b 100644 --- a/app/ui_pc/mainview.py +++ b/app/ui_pc/mainview.py @@ -85,6 +85,11 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow): wxid = dic.get('wxid') if wxid: me = MePC() + me.wxid = dic.get('wxid') + me.name = dic.get('name') + me.mobile = dic.get('mobile') + me.wx_dir = dic.get('wx_dir') + self.set_my_info(wxid) else: QMessageBox.information( @@ -158,11 +163,6 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow): self.avatar.scaled(60, 60) me = MePC() me.set_avatar(img_bytes) - dic = { - 'wxid': wxid - } - with open('./app/data/info.json', 'w', encoding='utf-8') as f: - f.write(json.dumps(dic)) self.myavatar.setScaledContents(True) self.myavatar.setPixmap(self.avatar) diff --git a/app/ui_pc/tool/pc_decrypt/decryptUi.py b/app/ui_pc/tool/pc_decrypt/decryptUi.py index 3fed138..d4b0b8d 100644 --- a/app/ui_pc/tool/pc_decrypt/decryptUi.py +++ b/app/ui_pc/tool/pc_decrypt/decryptUi.py @@ -53,14 +53,14 @@ class Ui_Dialog(object): self.lineEdit = QtWidgets.QLineEdit(Dialog) self.lineEdit.setStyleSheet("background:transparent;\n" "\n" - "border-radius:5px;\n" - "border-top: 0px solid #b2e281;\n" - "border-bottom: 2px solid black;\n" - "border-right: 0px solid #b2e281;\n" - "border-left: 0px solid #b2e281;\n" + " border-radius:5px;\n" + " border-top: 0px solid #b2e281;\n" + " border-bottom: 2px solid black;\n" + " border-right: 0px solid #b2e281;\n" + " border-left: 0px solid #b2e281;\n" "\n" - " \n" - "border-style:outset\n" + "\n" + " border-style:outset\n" " ") self.lineEdit.setFrame(False) self.lineEdit.setObjectName("lineEdit") @@ -72,7 +72,7 @@ class Ui_Dialog(object): self.label_6.setObjectName("label_6") self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1) self.label_key = QtWidgets.QLabel(Dialog) - self.label_key.setMaximumSize(QtCore.QSize(200, 16777215)) + self.label_key.setMaximumSize(QtCore.QSize(400, 16777215)) self.label_key.setText("") self.label_key.setObjectName("label_key") self.gridLayout.addWidget(self.label_key, 5, 1, 1, 1) @@ -101,7 +101,7 @@ class Ui_Dialog(object): self.label_8.setObjectName("label_8") self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1) self.label_db_dir = QtWidgets.QLabel(Dialog) - self.label_db_dir.setMaximumSize(QtCore.QSize(200, 300)) + self.label_db_dir.setMaximumSize(QtCore.QSize(400, 300)) self.label_db_dir.setText("") self.label_db_dir.setObjectName("label_db_dir") self.gridLayout.addWidget(self.label_db_dir, 6, 1, 1, 1) diff --git a/app/ui_pc/tool/pc_decrypt/decryptUi.ui b/app/ui_pc/tool/pc_decrypt/decryptUi.ui index dff82c5..9049e6c 100644 --- a/app/ui_pc/tool/pc_decrypt/decryptUi.ui +++ b/app/ui_pc/tool/pc_decrypt/decryptUi.ui @@ -127,7 +127,7 @@ - 200 + 400 16777215 @@ -189,7 +189,7 @@ - 200 + 400 300 diff --git a/app/ui_pc/tool/pc_decrypt/pc_decrypt.py b/app/ui_pc/tool/pc_decrypt/pc_decrypt.py index e7226e6..99cbb33 100644 --- a/app/ui_pc/tool/pc_decrypt/pc_decrypt.py +++ b/app/ui_pc/tool/pc_decrypt/pc_decrypt.py @@ -1,3 +1,4 @@ +import json import os.path import time import traceback @@ -35,11 +36,13 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): def get_info(self): try: result = get_wx_info.get_info() + print(result) if result == -1: QMessageBox.critical(self, "错误", "请登录微信") elif result == -2: QMessageBox.critical(self, "错误", "微信版本不匹配\n请更新微信版本为:3.9.8.15") - # print(result) + elif result == -3: + QMessageBox.critical(self, "错误", "WeChat WeChatWin.dll Not Found") else: self.ready = True self.info = result[0] @@ -52,7 +55,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): self.lineEdit.setFocus() self.checkBox.setChecked(True) self.get_wxidSignal.emit(self.info['wxid']) - if self.wx_dir and os.path.exists(os.path.join(self.wx_dir, self.info['wxid'])): + if self.wx_dir and os.path.exists(os.path.join(self.wx_dir)): self.label_ready.setText('已就绪') except Exception as e: print(e) @@ -71,12 +74,16 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): directory = QtWidgets.QFileDialog.getExistingDirectory( self, "选取微信安装目录——能看到All Users文件夹", "C:/") # 起始路径 - if directory: - self.label_db_dir.setText(directory) - self.wx_dir = directory - self.checkBox_2.setChecked(True) - if self.ready: - self.label_ready.setText('已就绪') + db_dir = os.path.join(directory, 'Msg') + if not os.path.exists(db_dir): + QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾") + return + + self.label_db_dir.setText(directory) + self.wx_dir = directory + self.checkBox_2.setChecked(True) + if self.ready: + self.label_ready.setText('已就绪') def decrypt(self): if not self.ready: @@ -88,11 +95,12 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): if self.lineEdit.text() == 'None': QMessageBox.critical(self, "错误", "请填入wxid") return + db_dir = os.path.join(self.wx_dir, 'Msg') if self.ready: - if not os.path.exists(os.path.join(self.wx_dir, self.info['wxid'])): - QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以WeChat Files结尾") + if not os.path.exists(db_dir): + QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾") return - db_dir = os.path.join(self.wx_dir, self.info['wxid'], 'Msg') + self.thread2 = DecryptThread(db_dir, self.info['key']) self.thread2.maxNumSignal.connect(self.setProgressBarMaxNum) self.thread2.signal.connect(self.progressBar_view) @@ -103,6 +111,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): # print("enter clicked") # 中间可以添加处理逻辑 # QMessageBox.about(self, "解密成功", "数据库文件存储在app/DataBase/Msg文件夹下") + self.DecryptSignal.emit('ok') # self.close() @@ -121,6 +130,14 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): def btnExitClicked(self): # print("Exit clicked") + dic = { + 'wxid': self.info['wxid'], + 'wx_dir': self.wx_dir, + 'name': self.info['name'], + 'mobile': self.info['mobile'] + } + with open('./app/data/info.json', 'w', encoding='utf-8') as f: + f.write(json.dumps(dic)) self.DecryptSignal.emit('ok') self.close() diff --git a/doc/images/path_select.png b/doc/images/path_select.png new file mode 100644 index 0000000..b8e7b1d Binary files /dev/null and b/doc/images/path_select.png differ diff --git a/doc/电脑端使用教程.md b/doc/电脑端使用教程.md index b7aaf05..9071ee1 100644 --- a/doc/电脑端使用教程.md +++ b/doc/电脑端使用教程.md @@ -40,7 +40,8 @@ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ![](./images/setting.png) - 点击**设置微信路径**按钮,选择该文件夹路径 + 点击**设置微信路径**按钮,选择该文件夹路径下的带有wxid_xxx的路径 + ![](./images/path_select.png) 5. 获取到密钥和微信路径之后点击开始解密 diff --git a/readme.md b/readme.md index 290ded6..b42b8a9 100644 --- a/readme.md +++ b/readme.md @@ -88,7 +88,9 @@ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 可以到微信->设置->文件管理查看 ![](./doc/images/setting.png) - 点击**设置微信路径**按钮,选择该文件夹路径 + 点击**设置微信路径**按钮,选择该文件夹路径下的带有wxid_xxx的路径 + ![](./doc/images/path_select.png) + 5. 获取到密钥和微信路径之后点击开始解密 6. 解密后的数据库文件保存在./app/DataBase/Msg路径下