From c7995ca7769813568bb1390bd96542bbc10351e8 Mon Sep 17 00:00:00 2001 From: shuaikangzhou <863909694@qq.com> Date: Sun, 28 Jan 2024 15:00:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=89=8B=E5=8A=A8=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=9F=BA=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/analysis/analysis.py | 72 +----- app/decrypt/get_bias_addr.py | 272 +++++++++++++++++++++ app/ui/tool/get_bias_addr/getBiasAddrUi.py | 206 ++++++++++++++++ app/ui/tool/get_bias_addr/get_bias_addr.py | 145 +++++++++++ app/ui/tool/pc_decrypt/pc_decrypt.py | 40 ++- app/ui/tool/setting/setting.py | 1 + app/ui/tool/tool_window.py | 30 ++- app/util/image.py | 5 +- 8 files changed, 681 insertions(+), 90 deletions(-) create mode 100644 app/decrypt/get_bias_addr.py create mode 100644 app/ui/tool/get_bias_addr/getBiasAddrUi.py create mode 100644 app/ui/tool/get_bias_addr/get_bias_addr.py diff --git a/app/analysis/analysis.py b/app/analysis/analysis.py index 28ad115..c5268ed 100644 --- a/app/analysis/analysis.py +++ b/app/analysis/analysis.py @@ -1,77 +1,9 @@ import os from collections import Counter - -from PyQt5.QtCore import QFile, QTextStream, QIODevice - import sys - -sys.path.append('.') - from app.DataBase import msg_db, MsgType from pyecharts import options as opts from pyecharts.charts import WordCloud, Calendar, Bar, Line -from app.resources import resource_rc - -var = resource_rc.qt_resource_name -charts_width = 800 -charts_height = 450 -wordcloud_width = 780 -wordcloud_height = 720 - - -def wordcloud(wxid, is_Annual_report=False, year='2023', who='1'): - import jieba - txt_messages = msg_db.get_messages_by_type(wxid, MsgType.TEXT, year) - if not txt_messages: - return { - 'chart_data': None, - 'keyword': "没有聊天你想分析啥", - 'max_num': "0", - 'dialogs': [] - } - # text = ''.join(map(lambda x: x[7], txt_messages)) - text = ''.join(map(lambda x: x[7] if x[4] == int(who) else '', txt_messages)) # 1“我”说的话,0“Ta”说的话 - - total_msg_len = len(text) - # 使用jieba进行分词,并加入停用词 - words = jieba.cut(text) - # 统计词频 - word_count = Counter(words) - # 过滤停用词 - stopwords_file = './app000/data/stopwords.txt' - try: - with open(stopwords_file, "r", encoding="utf-8") as stopword_file: - stopwords = set(stopword_file.read().splitlines()) - except: - file = QFile(':/data/stopwords.txt') - if file.open(QIODevice.ReadOnly | QIODevice.Text): - stream = QTextStream(file) - stream.setCodec('utf-8') - content = stream.readAll() - file.close() - stopwords = set(content.splitlines()) - filtered_word_count = {word: count for word, count in word_count.items() if len(word) > 1 and word not in stopwords} - - # 转换为词云数据格式 - data = [(word, count) for word, count in filtered_word_count.items()] - # text_data = data - data.sort(key=lambda x: x[1], reverse=True) - - text_data = data[:100] if len(data) > 100 else data - # 创建词云图 - keyword, max_num = text_data[0] - w = ( - WordCloud(init_opts=opts.InitOpts(width=f"{wordcloud_width}px", height=f"{wordcloud_height}px")) - .add(series_name="聊天文字", data_pair=text_data, word_size_range=[5, 40]) - ) - # return w.render_embed() - return { - 'chart_data': w.dump_options_with_quotes(), - 'keyword': keyword, - 'max_num': str(max_num), - 'dialogs': msg_db.get_messages_by_keyword(wxid, keyword, num=5, max_len=12) - } - def wordcloud_(wxid, time_range=None): import jieba @@ -115,7 +47,7 @@ def wordcloud_(wxid, time_range=None): # 创建词云图 keyword, max_num = text_data[0] w = ( - WordCloud(init_opts=opts.InitOpts(width=f"{wordcloud_width}px", height=f"{wordcloud_height}px")) + WordCloud(init_opts=opts.InitOpts()) .add(series_name="聊天文字", data_pair=text_data, word_size_range=[5, 100]) ) # return w.render_embed() @@ -267,7 +199,7 @@ def hour_count(wxid, is_Annual_report=False, year='2023'): y_data = list(map(lambda x: x[1], msg_data)) x_axis = list(map(lambda x: x[0], msg_data)) h = ( - Line(init_opts=opts.InitOpts(width=f"{charts_width}px", height=f"{charts_height}px")) + Line(init_opts=opts.InitOpts()) .add_xaxis(xaxis_data=x_axis) .add_yaxis( series_name="聊天频率", diff --git a/app/decrypt/get_bias_addr.py b/app/decrypt/get_bias_addr.py new file mode 100644 index 0000000..11e7d1e --- /dev/null +++ b/app/decrypt/get_bias_addr.py @@ -0,0 +1,272 @@ +# -*- coding: utf-8 -*-# +# ------------------------------------------------------------------------------- +# Name: get_base_addr.py +# Description: +# Author: xaoyaoo +# Date: 2023/08/22 +# ------------------------------------------------------------------------------- +import argparse +import ctypes +import hashlib +import json +import multiprocessing +import os +import re +import sys + +import psutil +from win32com.client import Dispatch +from pymem import Pymem +import pymem +import hmac + +ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory +void_p = ctypes.c_void_p +KEY_SIZE = 32 +DEFAULT_PAGESIZE = 4096 +DEFAULT_ITER = 64000 + + +def validate_key(key, salt, first, mac_salt): + byteKey = hashlib.pbkdf2_hmac("sha1", key, salt, DEFAULT_ITER, KEY_SIZE) + mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE) + hash_mac = hmac.new(mac_key, first[:-32], hashlib.sha1) + hash_mac.update(b'\x01\x00\x00\x00') + + if hash_mac.digest() == first[-32:-12]: + return True + else: + return False + + +def get_exe_bit(file_path): + """ + 获取 PE 文件的位数: 32 位或 64 位 + :param file_path: PE 文件路径(可执行文件) + :return: 如果遇到错误则返回 64 + """ + try: + with open(file_path, 'rb') as f: + dos_header = f.read(2) + if dos_header != b'MZ': + print('get exe bit error: Invalid PE file') + return 64 + # Seek to the offset of the PE signature + f.seek(60) + pe_offset_bytes = f.read(4) + pe_offset = int.from_bytes(pe_offset_bytes, byteorder='little') + + # Seek to the Machine field in the PE header + f.seek(pe_offset + 4) + machine_bytes = f.read(2) + machine = int.from_bytes(machine_bytes, byteorder='little') + + if machine == 0x14c: + return 32 + elif machine == 0x8664: + return 64 + else: + print('get exe bit error: Unknown architecture: %s' % hex(machine)) + return 64 + except IOError: + print('get exe bit error: File not found or cannot be opened') + return 64 + + +def get_exe_version(file_path): + """ + 获取 PE 文件的版本号 + :param file_path: PE 文件路径(可执行文件) + :return: 如果遇到错误则返回 + """ + file_version = Dispatch("Scripting.FileSystemObject").GetFileVersion(file_path) + return file_version + + +def find_all(c: bytes, string: bytes, base_addr=0): + """ + 查找字符串中所有子串的位置 + :param c: 子串 b'123' + :param string: 字符串 b'123456789123' + :return: + """ + return [base_addr + m.start() for m in re.finditer(re.escape(c), string)] + + +class BiasAddr: + def __init__(self, account, mobile, name, key, db_path): + self.account = account.encode("utf-8") + self.mobile = mobile.encode("utf-8") + self.name = name.encode("utf-8") + self.key = bytes.fromhex(key) if key else b"" + self.db_path = db_path if db_path and os.path.exists(db_path) else "" + + self.process_name = "WeChat.exe" + self.module_name = "WeChatWin.dll" + + self.pm = None # Pymem 对象 + self.is_WoW64 = None # True: 32位进程运行在64位系统上 False: 64位进程运行在64位系统上 + self.process_handle = None # 进程句柄 + self.pid = None # 进程ID + self.version = None # 微信版本号 + self.process = None # 进程对象 + self.exe_path = None # 微信路径 + self.address_len = None # 4 if self.bits == 32 else 8 # 4字节或8字节 + self.bits = 64 if sys.maxsize > 2 ** 32 else 32 # 系统:32位或64位 + + def get_process_handle(self): + try: + self.pm = Pymem(self.process_name) + self.pm.check_wow64() + self.is_WoW64 = self.pm.is_WoW64 + self.process_handle = self.pm.process_handle + self.pid = self.pm.process_id + self.process = psutil.Process(self.pid) + self.exe_path = self.process.exe() + self.version = get_exe_version(self.exe_path) + + version_nums = list(map(int, self.version.split("."))) # 将版本号拆分为数字列表 + if version_nums[0] <= 3 and version_nums[1] <= 9 and version_nums[2] <= 2: + self.address_len = 4 + else: + self.address_len = 8 + return True, "" + except pymem.exception.ProcessNotFound: + return False, "[-] WeChat No Run" + + def search_memory_value(self, value: bytes, module_name="WeChatWin.dll"): + # 创建 Pymem 对象 + module = pymem.process.module_from_name(self.pm.process_handle, module_name) + ret = self.pm.pattern_scan_module(value, module, return_multiple=True) + ret = ret[-1] - module.lpBaseOfDll if len(ret) > 0 else 0 + return ret + + def get_key_bias1(self): + try: + byteLen = self.address_len # 4 if self.bits == 32 else 8 # 4字节或8字节 + + keyLenOffset = 0x8c if self.bits == 32 else 0xd0 + keyWindllOffset = 0x90 if self.bits == 32 else 0xd8 + + module = pymem.process.module_from_name(self.process_handle, self.module_name) + keyBytes = b'-----BEGIN PUBLIC KEY-----\n...' + publicKeyList = pymem.pattern.pattern_scan_all(self.process_handle, keyBytes, return_multiple=True) + + keyaddrs = [] + for addr in publicKeyList: + keyBytes = addr.to_bytes(byteLen, byteorder="little", signed=True) # 低位在前 + may_addrs = pymem.pattern.pattern_scan_module(self.process_handle, module, keyBytes, + return_multiple=True) + if may_addrs != 0 and len(may_addrs) > 0: + for addr in may_addrs: + keyLen = self.pm.read_uchar(addr - keyLenOffset) + if keyLen != 32: + continue + keyaddrs.append(addr - keyWindllOffset) + + return keyaddrs[-1] - module.lpBaseOfDll if len(keyaddrs) > 0 else 0 + except: + return 0 + + def search_key(self, key: bytes): + key = re.escape(key) # 转义特殊字符 + key_addr = self.pm.pattern_scan_all(key, return_multiple=False) + key = key_addr.to_bytes(self.address_len, byteorder='little', signed=True) + result = self.search_memory_value(key, self.module_name) + return result + + def get_key_bias2(self, wx_db_path): + + addr_len = get_exe_bit(self.exe_path) // 8 + db_path = wx_db_path + + def read_key_bytes(h_process, address, address_len=8): + array = ctypes.create_string_buffer(address_len) + if ReadProcessMemory(h_process, void_p(address), array, address_len, 0) == 0: return "None" + address = int.from_bytes(array, byteorder='little') # 逆序转换为int地址(key地址) + key = ctypes.create_string_buffer(32) + if ReadProcessMemory(h_process, void_p(address), key, 32, 0) == 0: return "None" + key_bytes = bytes(key) + return key_bytes + + def verify_key(key, wx_db_path): + KEY_SIZE = 32 + DEFAULT_PAGESIZE = 4096 + DEFAULT_ITER = 64000 + with open(wx_db_path, "rb") as file: + blist = file.read(5000) + salt = blist[:16] + byteKey = hashlib.pbkdf2_hmac("sha1", key, salt, DEFAULT_ITER, KEY_SIZE) + first = blist[16:DEFAULT_PAGESIZE] + + 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, first[:-32], hashlib.sha1) + hash_mac.update(b'\x01\x00\x00\x00') + + if hash_mac.digest() != first[-32:-12]: + return False + return True + + phone_type1 = "iphone\x00" + phone_type2 = "android\x00" + phone_type3 = "ipad\x00" + + pm = pymem.Pymem("WeChat.exe") + module_name = "WeChatWin.dll" + + MicroMsg_path = os.path.join(db_path, "MSG", "MicroMsg.db") + + module = pymem.process.module_from_name(pm.process_handle, module_name) + + type1_addrs = pm.pattern_scan_module(phone_type1.encode(), module, return_multiple=True) + type2_addrs = pm.pattern_scan_module(phone_type2.encode(), module, return_multiple=True) + type3_addrs = pm.pattern_scan_module(phone_type3.encode(), module, return_multiple=True) + type_addrs = type1_addrs if len(type1_addrs) >= 2 else type2_addrs if len( + type2_addrs) >= 2 else type3_addrs if len(type3_addrs) >= 2 else "None" + if type_addrs == "None": + return 0 + for i in type_addrs[::-1]: + for j in range(i, i - 2000, -addr_len): + key_bytes = read_key_bytes(pm.process_handle, j, addr_len) + if key_bytes == "None": + continue + if verify_key(key_bytes, MicroMsg_path): + return j - module.lpBaseOfDll + return 0 + + def run(self, logging_path=False, version_list_path=None): + if not self.get_process_handle()[0]: + return None + mobile_bias = self.search_memory_value(self.mobile, self.module_name) + name_bias = self.search_memory_value(self.name, self.module_name) + account_bias = self.search_memory_value(self.account, self.module_name) + key_bias = 0 + key_bias = self.get_key_bias1() + key_bias = self.search_key(self.key) if key_bias <= 0 and self.key else key_bias + key_bias = self.get_key_bias2(self.db_path) if key_bias <= 0 and self.db_path else key_bias + + rdata = {self.version: [name_bias, account_bias, mobile_bias, 0, key_bias]} + + if version_list_path and os.path.exists(version_list_path): + with open(version_list_path, "r", encoding="utf-8") as f: + data = json.load(f) + data.update(rdata) + with open(version_list_path, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=4) + if os.path.exists(logging_path) and isinstance(logging_path, str): + with open(logging_path, "a", encoding="utf-8") as f: + f.write("{版本号:昵称,账号,手机号,邮箱,KEY}" + "\n") + f.write(str(rdata) + "\n") + elif logging_path: + print("{版本号:昵称,账号,手机号,邮箱,KEY}") + print(rdata) + return rdata + + +def get_info_without_key(h_process, address, n_size=64): + array = ctypes.create_string_buffer(n_size) + if ReadProcessMemory(h_process, void_p(address), array, n_size, 0) == 0: return "None" + array = bytes(array).split(b"\x00")[0] if b"\x00" in array else bytes(array) + text = array.decode('utf-8', errors='ignore') + return text.strip() if text.strip() != "" else "None" diff --git a/app/ui/tool/get_bias_addr/getBiasAddrUi.py b/app/ui/tool/get_bias_addr/getBiasAddrUi.py new file mode 100644 index 0000000..c753bc9 --- /dev/null +++ b/app/ui/tool/get_bias_addr/getBiasAddrUi.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'getBiasAddrUi.ui' +# +# Created by: PyQt5 UI code generator 5.15.10 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(650, 580) + self.verticalLayout = QtWidgets.QVBoxLayout(Form) + self.verticalLayout.setObjectName("verticalLayout") + self.scrollArea = QtWidgets.QScrollArea(Form) + self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame) + self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 632, 562)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.label_4 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_4.setMaximumSize(QtCore.QSize(16777215, 20)) + self.label_4.setAlignment(QtCore.Qt.AlignCenter) + self.label_4.setObjectName("label_4") + self.verticalLayout_3.addWidget(self.label_4) + self.widget = QtWidgets.QWidget(self.scrollAreaWidgetContents) + self.widget.setStyleSheet("QWidget{\n" +" background-color:rgb(251,251,251);\n" +" border-radius: 10px;\n" +"}\n" +"\n" +"QPushButton{\n" +" background-color: rgb(250,252,253);\n" +" border-radius: 5px;\n" +" padding: 8px;\n" +" border-right: 2px solid #888888; /* 按钮边框,2px宽,白色 */\n" +" border-bottom: 2px solid #888888; /* 按钮边框,2px宽,白色 */\n" +" border-left: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */\n" +" border-top: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */\n" +"}\n" +"QPushButton:hover { \n" +" background-color: lightgray;\n" +"}") + self.widget.setObjectName("widget") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget) + self.horizontalLayout.setObjectName("horizontalLayout") + self.label = QtWidgets.QLabel(self.widget) + self.label.setMinimumSize(QtCore.QSize(80, 0)) + self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.lineEdit_tel = QtWidgets.QLineEdit(self.widget) + self.lineEdit_tel.setObjectName("lineEdit_tel") + self.horizontalLayout.addWidget(self.lineEdit_tel) + self.verticalLayout_3.addWidget(self.widget) + self.widget_3 = QtWidgets.QWidget(self.scrollAreaWidgetContents) + self.widget_3.setStyleSheet("QWidget{\n" +" background-color:rgb(251,251,251);\n" +" border-radius: 10px;\n" +"}\n" +"\n" +"QPushButton{\n" +" background-color: rgb(250,252,253);\n" +" border-radius: 5px;\n" +" padding: 8px;\n" +" border-right: 2px solid #888888; /* 按钮边框,2px宽,白色 */\n" +" border-bottom: 2px solid #888888; /* 按钮边框,2px宽,白色 */\n" +" border-left: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */\n" +" border-top: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */\n" +"}\n" +"QPushButton:hover { \n" +" background-color: lightgray;\n" +"}") + self.widget_3.setObjectName("widget_3") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget_3) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.label_2 = QtWidgets.QLabel(self.widget_3) + self.label_2.setMinimumSize(QtCore.QSize(80, 0)) + self.label_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_2.setObjectName("label_2") + self.horizontalLayout_3.addWidget(self.label_2) + self.lineEdit_wx_alias = QtWidgets.QLineEdit(self.widget_3) + self.lineEdit_wx_alias.setObjectName("lineEdit_wx_alias") + self.horizontalLayout_3.addWidget(self.lineEdit_wx_alias) + self.verticalLayout_3.addWidget(self.widget_3) + self.widget_4 = QtWidgets.QWidget(self.scrollAreaWidgetContents) + self.widget_4.setStyleSheet("QWidget{\n" +" background-color:rgb(251,251,251);\n" +" border-radius: 10px;\n" +"}\n" +"\n" +"QPushButton{\n" +" background-color: rgb(250,252,253);\n" +" border-radius: 5px;\n" +" padding: 8px;\n" +" border-right: 2px solid #888888; /* 按钮边框,2px宽,白色 */\n" +" border-bottom: 2px solid #888888; /* 按钮边框,2px宽,白色 */\n" +" border-left: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */\n" +" border-top: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */\n" +"}\n" +"QPushButton:hover { \n" +" background-color: lightgray;\n" +"}") + self.widget_4.setObjectName("widget_4") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.widget_4) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.label_3 = QtWidgets.QLabel(self.widget_4) + self.label_3.setMinimumSize(QtCore.QSize(80, 0)) + self.label_3.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_3.setObjectName("label_3") + self.horizontalLayout_4.addWidget(self.label_3) + self.lineEdit_wx_name = QtWidgets.QLineEdit(self.widget_4) + self.lineEdit_wx_name.setObjectName("lineEdit_wx_name") + self.horizontalLayout_4.addWidget(self.lineEdit_wx_name) + self.verticalLayout_3.addWidget(self.widget_4) + self.btn_get_bias_addr = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.btn_get_bias_addr.setObjectName("btn_get_bias_addr") + self.verticalLayout_3.addWidget(self.btn_get_bias_addr) + self.widget_2 = QtWidgets.QWidget(self.scrollAreaWidgetContents) + self.widget_2.setStyleSheet("QWidget{\n" +" background-color:rgb(251,251,251);\n" +" border-radius: 10px;\n" +"}\n" +"\n" +"QPushButton{\n" +" background-color: rgb(250,252,253);\n" +" border-radius: 5px;\n" +" padding: 8px;\n" +" border-right: 2px solid #888888; /* 按钮边框,2px宽,白色 */\n" +" border-bottom: 2px solid #888888; /* 按钮边框,2px宽,白色 */\n" +" border-left: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */\n" +" border-top: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */\n" +"}\n" +"QPushButton:hover { \n" +" background-color: lightgray;\n" +"}") + self.widget_2.setObjectName("widget_2") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget_2) + self.horizontalLayout_2.setContentsMargins(9, -1, -1, -1) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.commandLinkButton = QtWidgets.QCommandLinkButton(self.widget_2) + self.commandLinkButton.setEnabled(True) + self.commandLinkButton.setTabletTracking(False) + self.commandLinkButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.commandLinkButton.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.commandLinkButton.setToolTipDuration(-1) + self.commandLinkButton.setLayoutDirection(QtCore.Qt.LeftToRight) + self.commandLinkButton.setAutoFillBackground(False) + self.commandLinkButton.setCheckable(False) + self.commandLinkButton.setChecked(False) + self.commandLinkButton.setAutoRepeat(False) + self.commandLinkButton.setAutoExclusive(False) + self.commandLinkButton.setAutoDefault(False) + self.commandLinkButton.setDefault(False) + self.commandLinkButton.setObjectName("commandLinkButton") + self.horizontalLayout_2.addWidget(self.commandLinkButton) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_2.addItem(spacerItem) + self.label_error_log = QtWidgets.QLabel(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_error_log.sizePolicy().hasHeightForWidth()) + self.label_error_log.setSizePolicy(sizePolicy) + self.label_error_log.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_error_log.setObjectName("label_error_log") + self.horizontalLayout_2.addWidget(self.label_error_log) + self.checkBox_send_error_log = QtWidgets.QCheckBox(self.widget_2) + self.checkBox_send_error_log.setText("") + self.checkBox_send_error_log.setIconSize(QtCore.QSize(64, 64)) + self.checkBox_send_error_log.setChecked(True) + self.checkBox_send_error_log.setObjectName("checkBox_send_error_log") + self.horizontalLayout_2.addWidget(self.checkBox_send_error_log) + self.verticalLayout_3.addWidget(self.widget_2) + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + self.verticalLayout.addWidget(self.scrollArea) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.label_4.setText(_translate("Form", "为避免输入错误,下面信息请从微信里复制")) + self.label.setText(_translate("Form", "手机号:")) + self.lineEdit_tel.setPlaceholderText(_translate("Form", "填入微信绑定的手机号")) + self.label_2.setText(_translate("Form", "微信号:")) + self.lineEdit_wx_alias.setPlaceholderText(_translate("Form", "填入您的微信号")) + self.label_3.setText(_translate("Form", "微信昵称:")) + self.lineEdit_wx_name.setPlaceholderText(_translate("Form", "填入您的微信昵称")) + self.btn_get_bias_addr.setText(_translate("Form", "获取信息")) + self.commandLinkButton.setText(_translate("Form", "收集版本信息")) + self.commandLinkButton.setDescription(_translate("Form", "需要收集微信版本信息以支持更多用户")) + self.label_error_log.setText(_translate("Form", "开")) diff --git a/app/ui/tool/get_bias_addr/get_bias_addr.py b/app/ui/tool/get_bias_addr/get_bias_addr.py new file mode 100644 index 0000000..3fd340c --- /dev/null +++ b/app/ui/tool/get_bias_addr/get_bias_addr.py @@ -0,0 +1,145 @@ +import json +import os.path + +import requests +from PyQt5.QtCore import pyqtSignal, QThread +from PyQt5.QtWidgets import QWidget, QMessageBox + +from app.components.QCursorGif import QCursorGif +from app.decrypt.get_bias_addr import BiasAddr +from .getBiasAddrUi import Ui_Form + +Stylesheet = """ +QPushButton{ + background-color: rgb(250,252,253); + border-radius: 5px; + padding: 8px; + border-right: 2px solid #888888; /* 按钮边框,2px宽,白色 */ + border-bottom: 2px solid #888888; /* 按钮边框,2px宽,白色 */ + border-left: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */ + border-top: 1px solid #ffffff; /* 按钮边框,2px宽,白色 */ +} +QPushButton:hover { + background-color: lightgray; +} +/*去掉item虚线边框*/ +QListWidget, QListView, QTreeWidget, QTreeView { + outline: 0px; + border:none; +} +/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/ +QListWidget { + min-width: 400px; + max-width: 400px; + min-height: 80px; + max-height: 80px; + color: black; + border:none; +} +QListWidget::item{ + min-width: 80px; + max-width: 400px; + min-height: 80px; + max-height: 80px; +} +/*被选中时的背景颜色和左边框颜色*/ +QListWidget::item:selected { + border-left:none; + color: black; + font-weight: bold; +} +QCheckBox::indicator { + background: rgb(251, 251, 251); + Width:60px; + Height:60px; + border-radius: 10px; +} +QCheckBox::indicator:unchecked{ + Width:60px; + Height:60px; + image: url(:/icons/icons/按钮_关闭.svg); +} +QCheckBox::indicator:checked{ + Width:60px; + Height:60px; + image: url(:/icons/icons/按钮_开启.svg); +} + +""" + + +class GetBiasAddrControl(QWidget, Ui_Form, QCursorGif): + biasAddrSignal = pyqtSignal(dict) + + def __init__(self, parent=None): + super(GetBiasAddrControl, self).__init__(parent) + self.thread = None + self.setStyleSheet(Stylesheet) + self.setupUi(self) + self.init_ui() + + def init_ui(self): + self.initCursor([':/icons/icons/Cursors/%d.png' % + i for i in range(8)], self) + self.setCursorTimeout(100) + self.btn_get_bias_addr.clicked.connect(self.get_bias_addr) + self.commandLinkButton.clicked.connect(self.show_info) + self.checkBox_send_error_log.clicked.connect(self.set_error_log) + + def set_error_log(self): + if self.checkBox_send_error_log.isChecked(): + self.label_error_log.setText('开') + else: + self.label_error_log.setText('关') + + def show_info(self): + QMessageBox.information(self, "收集版本信息", + "为了适配更多版本,需要收集微信的版本信息,该操作不会上传包括手机号、微信号、昵称等在内的任何信息\n示例数据:\n\"3.9.9.27\": [68065304, 0, 68065112, 0, 68066576]" + ) + + def upload(self, version_data): + url = "http://api.lc044.love/wxBiasAddr" + try: + requests.post(url, json=version_data) + print('版本信息上传成功') + except: + pass + + def get_bias_addr(self): + + account = self.lineEdit_wx_alias.text() + mobile = self.lineEdit_tel.text() + name = self.lineEdit_wx_name.text() + if not all([account, mobile, name]): + QMessageBox.critical(self, "错误", + "请把所有信息填写完整") + return + key = None + db_path = "test" + self.startBusy() + self.thread = MyThread(account, mobile, name, key, db_path) + self.thread.signal.connect(self.set_bias_addr) + self.thread.start() + + def set_bias_addr(self, data): + if self.checkBox_send_error_log.isChecked(): + self.upload(data) + self.stopBusy() + self.biasAddrSignal.emit(data) + + +class MyThread(QThread): + signal = pyqtSignal(dict) + + def __init__(self, account, mobile, name, key, db_path): + super(MyThread, self).__init__() + self.account = account + self.mobile = mobile + self.name = name + self.key = key + self.db_path = db_path + + def run(self): + bias_addr = BiasAddr(self.account, self.mobile, self.name, self.key, self.db_path) + data = bias_addr.run(logging_path=True) + self.signal.emit(data) diff --git a/app/ui/tool/pc_decrypt/pc_decrypt.py b/app/ui/tool/pc_decrypt/pc_decrypt.py index 9a3c289..d5e7da6 100644 --- a/app/ui/tool/pc_decrypt/pc_decrypt.py +++ b/app/ui/tool/pc_decrypt/pc_decrypt.py @@ -21,14 +21,17 @@ from ...Icon import Icon class DecryptControl(QWidget, decryptUi.Ui_Dialog, QCursorGif): DecryptSignal = pyqtSignal(bool) get_wxidSignal = pyqtSignal(str) + versionErrorSignal = pyqtSignal(str) def __init__(self, parent=None): super(DecryptControl, self).__init__(parent) + self.max_val = 0 self.setupUi(self) # 设置忙碌光标图片数组 self.initCursor([':/icons/icons/Cursors/%d.png' % i for i in range(8)], self) self.setCursorTimeout(100) + self.version_list = None self.btn_start.clicked.connect(self.decrypt) self.btn_getinfo.clicked.connect(self.get_info) self.btn_db_dir.clicked.connect(self.select_db_dir) @@ -53,7 +56,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog, QCursorGif): # @log def get_info(self): self.startBusy() - self.get_info_thread = MyThread() + self.get_info_thread = MyThread(self.version_list) self.get_info_thread.signal.connect(self.set_info) self.get_info_thread.start() @@ -62,8 +65,10 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog, QCursorGif): if result[0] == -1: QMessageBox.critical(self, "错误", "请登录微信") elif result[0] == -2: + self.versionErrorSignal.emit(result[1]) QMessageBox.critical(self, "错误", - "微信版本不匹配\n请更新微信版本为:3.9.9.27(去微信官网下载)\n或更新本软件") + "微信版本不匹配\n请手动填写信息") + elif result[0] == -3: QMessageBox.critical(self, "错误", "WeChat WeChatWin.dll Not Found") elif result[0] == -10086: @@ -151,11 +156,12 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog, QCursorGif): # print("enter clicked") # 中间可以添加处理逻辑 # QMessageBox.about(self, "解密成功", "数据库文件存储在app/DataBase/Msg文件夹下") - + self.progressBar_view(self.max_val) self.DecryptSignal.emit(True) # self.close() def setProgressBarMaxNum(self, max_val): + self.max_val = max_val self.progressBar.setRange(0, max_val) def progressBar_view(self, value): @@ -184,7 +190,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog, QCursorGif): except: with open('./info.json', 'w', encoding='utf-8') as f: f.write(json.dumps(dic)) - + self.progressBar_view(self.max_val) self.DecryptSignal.emit(True) self.close() @@ -271,8 +277,9 @@ class DecryptThread(QThread): class MyThread(QThread): signal = pyqtSignal(list) - def __init__(self): + def __init__(self,version_list = None): super(MyThread, self).__init__() + self.version_list = version_list def __del__(self): pass @@ -283,8 +290,11 @@ class MyThread(QThread): 'version': version } try: - response = requests.post(url, json=data) + response = requests.get(url, json=data) + print(response) + print(response.text) if response.status_code == 200: + update_info = response.json() return update_info else: @@ -293,12 +303,15 @@ class MyThread(QThread): return {} def run(self): - file_path = './app/resources/data/version_list.json' - if not os.path.exists(file_path): - resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) - file_path = os.path.join(resource_dir, 'app', 'resources', 'data', 'version_list.json') - with open(file_path, "r", encoding="utf-8") as f: - VERSION_LIST = json.loads(f.read()) + if self.version_list: + VERSION_LIST = self.version_list + else: + file_path = './app/resources/data/version_list.json' + if not os.path.exists(file_path): + resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) + file_path = os.path.join(resource_dir, 'app', 'resources', 'data', 'version_list.json') + with open(file_path, "r", encoding="utf-8") as f: + VERSION_LIST = json.loads(f.read()) try: result = get_wx_info.get_info(VERSION_LIST) if result == -1: @@ -313,6 +326,9 @@ class MyThread(QThread): if version_bias.get(version): logger.info(f"从云端获取内存基址:{version_bias}") result = get_wx_info.get_info(version_bias) + else: + logger.info(f"从云端获取内存基址失败:{version}") + result = [-2,version] except: logger.error(traceback.format_exc()) result = [-10086] diff --git a/app/ui/tool/setting/setting.py b/app/ui/tool/setting/setting.py index e5f1a19..9021c96 100644 --- a/app/ui/tool/setting/setting.py +++ b/app/ui/tool/setting/setting.py @@ -78,6 +78,7 @@ class SettingControl(QWidget, Ui_Form): def init_ui(self): self.checkBox.setText('是') self.checkBox_send_error_log.clicked.connect(self.set_error_log) + def set_error_log(self): if self.checkBox_send_error_log.isChecked(): self.label_error_log.setText('开') diff --git a/app/ui/tool/tool_window.py b/app/ui/tool/tool_window.py index d9b4502..672975f 100644 --- a/app/ui/tool/tool_window.py +++ b/app/ui/tool/tool_window.py @@ -5,6 +5,7 @@ from PyQt5.QtWidgets import QWidget, QListWidgetItem, QLabel from app.ui.Icon import Icon from .pc_decrypt import DecryptControl from .setting.setting import SettingControl +from .get_bias_addr.get_bias_addr import GetBiasAddrControl from .toolUI import Ui_Dialog # 美化样式表 @@ -68,14 +69,22 @@ class ToolWindow(QWidget, Ui_Dialog): self.listWidget.currentRowChanged.connect(self.setCurrentIndex) chat_item = QListWidgetItem(Icon.Decrypt_Icon, '解密', self.listWidget) contact_item = QListWidgetItem(Icon.Contact_Icon, '设置', self.listWidget) - myinfo_item = QListWidgetItem(Icon.Home_Icon, '别点', self.listWidget) + myinfo_item = QListWidgetItem(Icon.Home_Icon, '解密2', self.listWidget) tool_item = QListWidgetItem(Icon.Home_Icon, '别点', self.listWidget) - decrypt_window = DecryptControl() - decrypt_window.get_wxidSignal.connect(self.get_info_signal) - decrypt_window.DecryptSignal.connect(self.decrypt_success_signal) - self.stackedWidget.addWidget(decrypt_window) + + self.decrypt_window = DecryptControl() + self.decrypt_window.get_wxidSignal.connect(self.get_info_signal) + self.decrypt_window.DecryptSignal.connect(self.decrypt_success_signal) + self.decrypt_window.versionErrorSignal.connect(self.show_decrypt2) + self.stackedWidget.addWidget(self.decrypt_window) + setting_window = SettingControl() self.stackedWidget.addWidget(setting_window) + + self.get_bias_addr_window = GetBiasAddrControl() + self.get_bias_addr_window.biasAddrSignal.connect(self.decrypt) + self.stackedWidget.addWidget(self.get_bias_addr_window) + label = QLabel('都说了不让你点', self) label.setFont(QFont("微软雅黑", 50)) label.setAlignment(Qt.AlignCenter) @@ -84,10 +93,19 @@ class ToolWindow(QWidget, Ui_Dialog): # label.setStyleSheet('background: rgb(%d, %d, %d);margin: 50px;' % ( # randint(0, 255), randint(0, 255), randint(0, 255))) - self.stackedWidget.addWidget(label) self.stackedWidget.addWidget(label) self.listWidget.setCurrentRow(0) self.stackedWidget.setCurrentIndex(0) + def decrypt(self, version_list): + self.listWidget.setCurrentRow(0) + self.stackedWidget.setCurrentIndex(0) + self.decrypt_window.version_list = version_list + self.decrypt_window.get_info() + + def show_decrypt2(self, version): + self.listWidget.setCurrentRow(2) + self.stackedWidget.setCurrentIndex(2) + def setCurrentIndex(self, row): self.stackedWidget.setCurrentIndex(row) diff --git a/app/util/image.py b/app/util/image.py index 47796c7..47c09c8 100644 --- a/app/util/image.py +++ b/app/util/image.py @@ -33,6 +33,7 @@ def get_code(dat_read): return head_index, code head_index = head_index + 1 print("not jpg, png, gif") + return -1, -1 except: logger.error(f'image解析发生了错误:\n\n{traceback.format_exc()}') return -1, -1 @@ -48,8 +49,8 @@ def decode_dat(file_path, out_path): return None with open(file_path, 'rb') as file_in: data = file_in.read() - file_type, decode_code = get_code(data[:2]) - + data = get_code(data[:2]) + file_type, decode_code = data if decode_code == -1: return