diff --git a/README.md b/README.md index 41d4a20..93923f7 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@
更新日志(点击展开): +* 2023.10.24 add auto get bias addr ,not need input key or wx folder path. * 2023.10.17 add LICENSE * 2023.10.16 添加"3.9.7.15"版本的偏移[#12](https://github.com/xaoyaoo/PyWxDump/issues/12) ,感谢@[GentlemanII](https://github.com/GentlemanII) @@ -73,7 +74,7 @@ PyWxDump │ │ ├─ get_wx_info.py # 获取微信基本信息脚本 │ │ └─ get_wx_db.py # 获取本地所有的微信相关数据库 │ ├─ command.py # 命令行入口 -│ └─ version_list.json # 微信版本列表 +│ └─ version_list.json # 微信版本列表 (十进制)按顺序代表:微信昵称、微信账号、微信手机号、微信邮箱(默认0)、微信KEY、微信原始ID(wxid_******) ├─ doc # 项目文档 │ ├─ python1.0_README.md # python1.0版本的README │ ├─ wx数据库简述.md # wx数据库简述 @@ -156,8 +157,8 @@ wxdump bias_addr -h # --mobile MOBILE 手机号 # --name NAME 微信昵称 # --account ACCOUNT 微信账号 -# --key KEY (与db_path二选一)密钥 -# --db_path DB_PATH (与key二选一)已登录账号的微信文件夹路径 +# --key KEY (可选)密钥 +# --db_path DB_PATH (可选)已登录账号的微信文件夹路径 # -vlp VLP (可选)微信版本偏移文件路径 wxdump wx_info -h diff --git a/doc/CE获取基址.md b/doc/CE获取基址.md index 13f5d74..04bb337 100644 --- a/doc/CE获取基址.md +++ b/doc/CE获取基址.md @@ -51,7 +51,7 @@ KEY的基址即:**2FFF970-000024=2FFF94C** 十进制地址为:50329932 -代码块中的五个十进制按顺序代表:微信昵称、微信账号、微信手机号、微信邮箱(高版本失效,这个随便填)、微信KEY +代码块中的五个十进制按顺序代表:微信昵称、微信账号、微信手机号、微信邮箱(高版本失效,这个随便填)、微信KEY、微信原始ID(wxid_******) ```jsx { diff --git a/doc/python1.0_README.md b/doc/python1.0_README.md index 41d04b5..bfc074e 100644 --- a/doc/python1.0_README.md +++ b/doc/python1.0_README.md @@ -135,7 +135,7 @@ python get_base_addr.py --mobile 152***** --name **** --account *** --key ***** return:{'3.9.7.29': [63486984, 63488320, 63486792, 0, 63488256, 56006136]} - (十进制)按顺序代表:微信昵称、微信账号、微信手机号、微信邮箱(默认0)、微信KEY、版本信息 + (十进制)按顺序代表:微信昵称、微信账号、微信手机号、微信邮箱(默认0)、微信KEY、微信原始ID(wxid_******) [注]:如果参数错误,得到的对应地址偏移为0,邮箱高版本失效,默认为0 diff --git a/pywxdump/bias_addr/get_bias_addr.py b/pywxdump/bias_addr/get_bias_addr.py index 9a46409..6dabfe6 100644 --- a/pywxdump/bias_addr/get_bias_addr.py +++ b/pywxdump/bias_addr/get_bias_addr.py @@ -15,6 +15,7 @@ import re import time import winreg import threading +import platform import psutil # import win32api @@ -55,6 +56,8 @@ class BiasAddr: self.pm = Pymem("WeChat.exe") + self.bits = self.get_osbits() + self.islogin = True def find_all(self, c: bytes, string: bytes, base_addr=0): @@ -73,55 +76,105 @@ class BiasAddr: return file_version self.islogin = False + def get_osbits(self): + return int(platform.architecture()[0][:-3]) + def search_memory_value(self, value: bytes, module_name="WeChatWin.dll"): # 创建 Pymem 对象 pm = self.pm module = pymem.process.module_from_name(pm.process_handle, module_name) + + # result = pymem.pattern.pattern_scan_module(pm.process_handle, module, value, return_multiple=True) + # result = result[-1]-module.lpBaseOfDll if len(result) > 0 else 0 mem_data = pm.read_bytes(module.lpBaseOfDll, module.SizeOfImage) result = self.find_all(value, mem_data) result = result[-1] if len(result) > 0 else 0 return result def search_key(self, key: bytes): - pid = self.pm.process_id - # print(self.pm.process_base.lpBaseOfDll, self.pm.process_base.SizeOfImage) - - module_start_addr = 34199871460642 - module_end_addr = 0 - process = psutil.Process(pid) - for module in process.memory_maps(grouped=False): - if "WeChat" in module.path: - start_addr = int(module.addr, 16) - end_addr = start_addr + module.rss - - if module_start_addr > start_addr: - module_start_addr = start_addr - if module_end_addr < end_addr: - module_end_addr = end_addr - - batch = 4096 - Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, pid) - array = ctypes.create_string_buffer(batch) - key_addr = 0 - for i in range(module_start_addr, module_end_addr, batch): - if ReadProcessMemory(Handle, void_p(i), array, batch, None) == 0: - continue - hex_string = array.raw # 读取到的内存数据 - key_addr = self.find_all(key, hex_string, i) - if len(key_addr) > 0: - key_addr = key_addr[0] - break - - # print(hex(key_addr)) - key = key_addr.to_bytes(8, byteorder='little') - # print(key.hex()) + byteLen = 4 if self.bits == 32 else 8 # 4字节或8字节 + key_addr = self.pm.pattern_scan_all(key, return_multiple=True)[-1] if len(key) > 0 else 0 + key = key_addr.to_bytes(byteLen, byteorder='little', signed=True) result = self.search_memory_value(key, self.module_name) return result + def get_key_bias_test(self): + byteLen = 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 + + pm = self.pm + + module = pymem.process.module_from_name(pm.process_handle, "WeChatWin.dll") + keyBytes = b'-----BEGIN PUBLIC KEY-----\n...' + publicKeyList = pymem.pattern.pattern_scan_all(self.pm.process_handle, keyBytes, return_multiple=True) + + keyaddrs = [] + for addr in publicKeyList: + keyBytes = addr.to_bytes(byteLen, byteorder="little", signed=True) # 低位在前 + addrs = pymem.pattern.pattern_scan_module(pm.process_handle, module, keyBytes, return_multiple=True) + if addrs != 0: + keyaddrs += addrs + + keyWinAddr = 0 + for addr in keyaddrs: + keyLen = pm.read_uchar(addr - keyLenOffset) + if keyLen != 32: + continue + keyWinAddr = addr - keyWindllOffset + # keyaddr = int.from_bytes(pm.read_bytes(keyWinAddr, byteLen), byteorder='little') + # key = pm.read_bytes(keyaddr, 32) + # print("key", key.hex()) + + return keyWinAddr - module.lpBaseOfDll + + def get_wxid_bias(self): + byteLen = 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 + + pm = self.pm + + module = pymem.process.module_from_name(pm.process_handle, "WeChatWin.dll") + keyBytes = b'wxid_' + publicWxidList = pymem.pattern.pattern_scan_all(self.pm.process_handle, keyBytes, return_multiple=True) + + import ahocorasick + def search_substrings(text, substrings): + A = ahocorasick.Automaton() + for index, s in enumerate(substrings): + A.add_word(s, (index, s)) + A.make_automaton() + + results = [] + for end_index, (insert_order, original_value) in A.iter(text): + start_index = end_index - len(original_value) + 1 + results.append(int(start_index / 2)) + return results + + patterns = [] + for addr in publicWxidList: + keyBytes = addr.to_bytes(byteLen, byteorder="little", signed=True) # 低位在前 + patterns.append(keyBytes.hex()) + text = pm.read_bytes(module.lpBaseOfDll, module.SizeOfImage).hex() + + wxidaddrs = search_substrings(text, patterns) + # print("wxidaddrs", wxidaddrs) + + # wxidaddr = 0 + # for addr in wxidaddrs: + # print(addr - 63488256) + # wxidaddr = int.from_bytes(pm.read_bytes(addr + module.lpBaseOfDll, byteLen), byteorder='little') + # print("wxidaddr", hex(wxidaddr)) + # wxid = pm.read_bytes(wxidaddr, 24).split(b"\x00")[0] + # print("wxid", wxid) + + return wxidaddrs[-2] + def get_key_bias(self, wx_db_path, account_bias=0): wx_db_path = os.path.join(wx_db_path, "Msg", "MicroMsg.db") if not os.path.exists(wx_db_path): - return False + return 0 def get_maybe_key(mem_data): maybe_key = [] @@ -202,13 +255,24 @@ class BiasAddr: name_bias = self.search_memory_value(self.name) account_bias = self.search_memory_value(self.account) # version_bias = self.search_memory_value(self.version.encode("utf-8")) - if self.key: - key_bias = self.search_key(self.key) - elif self.db_path: - key_bias = self.get_key_bias(self.db_path, account_bias) - else: + + try: + key_bias = self.get_key_bias_test() + except: key_bias = 0 - return {self.version: [name_bias, account_bias, mobile_bias, 0, key_bias]} + try: + wxid_bias = self.get_wxid_bias() + except: + wxid_bias = 0 + + if key_bias <= 0: + if self.key: + key_bias = self.search_key(self.key) + elif self.db_path: + key_bias = self.get_key_bias(self.db_path, account_bias) + else: + key_bias = 0 + return {self.version: [name_bias, account_bias, mobile_bias, 0, key_bias, wxid_bias]} if __name__ == '__main__': diff --git a/pywxdump/command.py b/pywxdump/command.py index de6ef59..61cd730 100644 --- a/pywxdump/command.py +++ b/pywxdump/command.py @@ -11,6 +11,7 @@ import os from . import * + # version_list_path = os.path.join(os.path.dirname(__file__), "version_list.json") @@ -30,8 +31,8 @@ class MainBiasAddr(): def run(self, args): # 判断是否至少输入一个参数 - if not args.key and not args.db_path: - self.sb_bias_addr.error("必须至少指定 --key 或 --db_path 参数中的一个") + # if not args.key and not args.db_path: + # self.sb_bias_addr.error("必须至少指定 --key 或 --db_path 参数中的一个") # 从命令行参数获取值 mobile = args.mobile @@ -164,6 +165,10 @@ class MainAll(): args.vlp = VERSION_LIST_PATH result_WxInfo = MainWxInfo().run(args) keys = [i.get('key', "") for i in result_WxInfo] + if not keys: + print("[-] 未获取到密钥") + return + wxids = [i.get('wxid', "") for i in result_WxInfo] args.require_list = 'all' args.wf = None diff --git a/pywxdump/version_list.json b/pywxdump/version_list.json index 7751941..90d99cb 100644 --- a/pywxdump/version_list.json +++ b/pywxdump/version_list.json @@ -4,328 +4,375 @@ 328122328, 328123056, 328121976, - 328123020 + 328123020, + 0 ], "3.3.0.115": [ 31323364, 31323744, 31324472, 31323392, - 31324436 + 31324436, + 0 ], "3.3.0.84": [ 31315212, 31315592, 31316320, 31315240, - 31316284 + 31316284, + 0 ], "3.3.0.93": [ 31323364, 31323744, 31324472, 31323392, - 31324436 + 31324436, + 0 ], "3.3.5.34": [ 30603028, 30603408, 30604120, 30603056, - 30604100 + 30604100, + 0 ], "3.3.5.42": [ 30603012, 30603392, 30604120, 30603040, - 30604084 + 30604084, + 0 ], "3.3.5.46": [ 30578372, 30578752, 30579480, 30578400, - 30579444 + 30579444, + 0 ], "3.4.0.37": [ 31608116, 31608496, 31609224, 31608144, - 31609188 + 31609188, + 0 ], "3.4.0.38": [ 31604044, 31604424, 31605152, 31604072, - 31605116 + 31605116, + 0 ], "3.4.0.50": [ 31688500, 31688880, 31689608, 31688528, - 31689572 + 31689572, + 0 ], "3.4.0.54": [ 31700852, 31701248, 31700920, 31700880, - 31701924 + 31701924, + 0 ], "3.4.5.27": [ 32133788, 32134168, 32134896, 32133816, - 32134860 + 32134860, + 0 ], "3.4.5.45": [ 32147012, 32147392, 32147064, 32147040, - 32148084 + 32148084, + 0 ], "3.5.0.20": [ 35494484, 35494864, 35494536, 35494512, - 35495556 + 35495556, + 0 ], "3.5.0.29": [ 35507980, 35508360, 35508032, 35508008, - 35509052 + 35509052, + 0 ], "3.5.0.33": [ 35512140, 35512520, 35512192, 35512168, - 35513212 + 35513212, + 0 ], "3.5.0.39": [ 35516236, 35516616, 35516288, 35516264, - 35517308 + 35517308, + 0 ], "3.5.0.42": [ 35512140, 35512520, 35512192, 35512168, - 35513212 + 35513212, + 0 ], "3.5.0.44": [ 35510836, 35511216, 35510896, 35510864, - 35511908 + 35511908, + 0 ], "3.5.0.46": [ 35506740, 35507120, 35506800, 35506768, - 35507812 + 35507812, + 0 ], "3.6.0.18": [ 35842996, 35843376, 35843048, 35843024, - 35844068 + 35844068, + 0 ], "3.6.5.7": [ 35864356, 35864736, 35864408, 35864384, - 35865428 + 35865428, + 0 ], "3.6.5.16": [ 35909428, 35909808, 35909480, 35909456, - 35910500 + 35910500, + 0 ], "3.7.0.26": [ 37105908, 37106288, 37105960, 37105936, - 37106980 + 37106980, + 0 ], "3.7.0.29": [ 37105908, 37106288, 37105960, 37105936, - 37106980 + 37106980, + 0 ], "3.7.0.30": [ 37118196, 37118576, 37118248, 37118224, - 37119268 + 37119268, + 0 ], "3.7.5.11": [ 37883280, 37884088, 37883136, 37883008, - 37884052 + 37884052, + 0 ], "3.7.5.23": [ 37895736, 37896544, 37895592, 37883008, - 37896508 + 37896508, + 0 ], "3.7.5.27": [ 37895736, 37896544, 37895592, 37895464, - 37896508 + 37896508, + 0 ], "3.7.5.31": [ 37903928, 37904736, 37903784, 37903656, - 37904700 + 37904700, + 0 ], "3.7.6.24": [ 38978840, 38979648, 38978696, 38978604, - 38979612 + 38979612, + 0 ], "3.7.6.29": [ 38986376, 38987184, 38986232, 38986104, - 38987148 + 38987148, + 0 ], "3.7.6.44": [ 39016520, 39017328, 39016376, 38986104, - 39017292 + 39017292, + 0 ], "3.8.0.31": [ 46064088, 46064912, 46063944, 38986104, - 46064876 + 46064876, + 0 ], "3.8.0.33": [ 46059992, 46060816, 46059848, 38986104, - 46060780 + 46060780, + 0 ], "3.8.0.41": [ 46064024, 46064848, 46063880, 38986104, - 46064812 + 46064812, + 0 ], "3.8.1.26": [ 46409448, 46410272, 46409304, 38986104, - 46410236 + 46410236, + 0 ], "3.9.0.28": [ 48418376, 48419280, 48418232, 38986104, - 48419244 + 48419244, + 0 ], "3.9.2.23": [ 50320784, 50321712, 50320640, 38986104, - 50321676 + 50321676, + 0 ], "3.9.2.26": [ 50329040, 50329968, 50328896, 38986104, - 50329932 + 50329932, + 0 ], "3.9.5.81": [ 61650872, 61652208, 61650680, 0, - 61652144 + 61652144, + 0 ], "3.9.5.91": [ 61654904, 61654680, 61654712, 38986104, - 61656176 + 61656176, + 0 ], "3.9.6.19": [ 61997688, 61997464, 61997496, 38986104, - 61998960 + 61998960, + 0 ], "3.9.6.33": [ 62030600, 62031936, 62030408, 0, - 62031872 + 62031872, + 0 ], "3.9.7.15": [ 63482696, 63484032, 63482504, 0, - 63483968 + 63483968, + 0 ], "3.9.7.25": [ 63482760, 63484096, 63482568, 0, - 63484032 + 63484032, + 0 ], "3.9.7.29": [ 63486984, 63488320, 63486792, 0, - 63488256 + 63488256, + 63488352 ] } \ No newline at end of file diff --git a/pywxdump/wx_info/get_wx_db.py b/pywxdump/wx_info/get_wx_db.py index db33374..cd0190e 100644 --- a/pywxdump/wx_info/get_wx_db.py +++ b/pywxdump/wx_info/get_wx_db.py @@ -44,7 +44,7 @@ def get_wechat_db(require_list: [list | str] = "all", msg_dir: str = None): elif isinstance(require_list, list): pattern = {} for require in require_list: - pattern[require] = re.compile(r".*%s.*\.db$" % require) + pattern[require] = re.compile(r"%s.*\.db$" % require) else: return "[-] 参数错误" diff --git a/pywxdump/wx_info/get_wx_info.py b/pywxdump/wx_info/get_wx_info.py index ede8d7a..151ceba 100644 --- a/pywxdump/wx_info/get_wx_info.py +++ b/pywxdump/wx_info/get_wx_info.py @@ -23,18 +23,26 @@ def get_info_without_key(h_process, address, n_size=64): return text.strip() if text.strip() != "" else "None" +def get_info_wxid(h_process, address, n_size=32): + array = ctypes.create_string_buffer(8) + if ReadProcessMemory(h_process, void_p(address), array, 8, 0) == 0: return "None" + address = int.from_bytes(array, byteorder='little') # 逆序转换为int地址(key地址) + wxid = get_info_without_key(h_process, address, n_size) + return wxid + + # 读取内存中的key def get_key(h_process, address): array = ctypes.create_string_buffer(8) if ReadProcessMemory(h_process, void_p(address), array, 8, 0) == 0: return "None" - key = ctypes.create_string_buffer(32) 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_string = bytes(key).hex() return key_string -# 读取微信信息(key, name, account, mobile, mail) +# 读取微信信息(account,mobile,name,mail,wxid,key) def read_info(version_list): wechat_process = [] result = [] @@ -71,11 +79,14 @@ def read_info(version_list): mobile_baseaddr = wechat_base_address + support_list[2] mail_baseaddr = wechat_base_address + support_list[3] key_baseaddr = wechat_base_address + support_list[4] + wxid_baseaddr = wechat_base_address + support_list[5] tmp_rd['account'] = get_info_without_key(Handle, account__baseaddr, 32) tmp_rd['mobile'] = get_info_without_key(Handle, mobile_baseaddr, 64) tmp_rd['name'] = get_info_without_key(Handle, name_baseaddr, 64) tmp_rd['mail'] = get_info_without_key(Handle, mail_baseaddr, 64) if support_list[3] != 0 else "None" + tmp_rd['wxid'] = get_info_wxid(Handle, wxid_baseaddr, 24) if support_list[5] != 0 else "None" + if not tmp_rd['wxid'].startswith("wxid_"): tmp_rd['wxid'] = "None" tmp_rd['key'] = get_key(Handle, key_baseaddr) result.append(tmp_rd) diff --git a/setup.py b/setup.py index ad1d86b..1d4a7a7 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() -version = "2.1.4" +version = "2.1.5" setup( name="pywxdump", author="xaoyaoo", @@ -39,7 +39,8 @@ setup( "silk-python", "pyaudio", "requests", - "pillow" + "pillow", + "pyahocorasick" ], entry_points={ 'console_scripts': [ diff --git a/tests/t2.py b/tests/t2.py new file mode 100644 index 0000000..a9abb2d --- /dev/null +++ b/tests/t2.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*-# +# ------------------------------------------------------------------------------- +# Name: t2.py +# Description: +# Author: xaoyaoo +# Date: 2023/10/21 +# ------------------------------------------------------------------------------- +import json + +# from pywxdump import VERSION_LIST + +with open(r'D:\_code\py_code\test\a2023\b0821wxdb\PyWxDump\pywxdump\version_list.json', 'r') as f: + VERSION_LIST = json.load(f) + +for version in VERSION_LIST: + VERSION_LIST[version] = VERSION_LIST[version] + [0] if len(VERSION_LIST[version]) == 5 else VERSION_LIST[version] + +with open(r'D:\_code\py_code\test\a2023\b0821wxdb\PyWxDump\pywxdump\version_list.json', 'w') as f: + json.dump(VERSION_LIST, f, indent=4, ensure_ascii=False) + +if __name__ == '__main__': + pass