#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import sys import re import struct import argparse import logging import javaobj from pyquery import PyQuery from pysqlcipher3 import dbapi2 as sqlite from hashlib import md5 from wechat.common.procutil import subproc_succ, subproc_call logger = logging.getLogger("wechat") RES_DIR = "/mnt/sdcard/tencent/MicroMsg" MM_DIR = "/data/data/com.tencent.mm" def get_uin(): candidates = [] try: uin = None out = subproc_succ(f"adb shell cat {MM_DIR}/shared_prefs/system_config_prefs.xml") for line in out.decode('utf-8').split("\n"): if "default_uin" in line: line = PyQuery(line) uin = line.attr["value"] break uin = int(uin) except Exception: logger.warning("uin not found in system_config_prefs.xml") else: candidates.append(uin) logger.info(f"found uin={uin} in system_config_prefs.xml") try: uin = None out = subproc_succ(f"adb shell cat {MM_DIR}/shared_prefs/com.tencent.mm_preferences.xml") for line in out.decode('utf-8').split("\n"): if "last_login_uin" in line: line = PyQuery(line) uin = line.text() break uin = int(uin) except Exception: logger.warning("uin not found in com.tencent.mm_preferences.xml") else: candidates.append(uin) logger.info(f"found uin={uin} in com.tencent.mm_preferences.xml") try: uin = None out = subproc_succ(f"adb shell cat {MM_DIR}/shared_prefs/auth_info_key_prefs.xml") for line in out.decode('utf-8').split("\n"): if "auth_uin" in line: line = PyQuery(line) uin = line.attr["value"] break uin = int(uin) except Exception: logger.warning("uin not found in auth_info_key_prefs.xml") else: candidates.append(uin) logger.info(f"found uin={uin} in auth_info_key_prefs.xml") try: out = subproc_succ(f"adb shell cat {MM_DIR}/MicroMsg/systemInfo.cfg") uin = int(javaobj.loads(out).get(1, 0)) except: logger.warning("default uin not found in systemInfo.cfg") else: candidates.append(uin) logger.info(f"found uin={uin} in systemInfo.cfg") candidates = list({str(x) if isinstance(x, int) else x for x in candidates if x != 0}) logger.info(f"Possible uin: {candidates}") return candidates def get_imei(): candidates = [] class Parcel(object): # https://gist.github.com/ktnr74/60ac7bcc2cd17b43f2cb def __init__(self, text): if text.startswith(b'Result: Parcel(') and text.endswith(b'\')'): self.data = b''.join([struct.pack('= (4, 1), "Sqlcipher>=4.1 is required" c.execute("PRAGMA key = '" + key + "';") # https://github.com/sqlcipher/sqlcipher/commit/e4b66d6cc8a2b7547a32ff2c3ac52f148eba3516 c.execute("PRAGMA cipher_compatibility = 1;") try: c.execute("ATTACH DATABASE '" + output + "' AS db KEY '';") except Exception as e: logger.error(f"Decryption failed: '{e}'") os.unlink(output) raise logger.info(f"Decryption succeeded! Writing database to {output} ...") c.execute("SELECT sqlcipher_export('db');" ) c.execute("DETACH DATABASE db;" ) c.close() if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('task', choices=['uin', 'imei', 'decrypt']) parser.add_argument('--imei', help='overwrite imei') parser.add_argument('--uin', help='overwrite uin') parser.add_argument('--input', help='encrypted EnMicroMsg.db') args = parser.parse_args() subproc_succ("adb root") if args.task == 'uin': uin = get_uin() elif args.task == 'imei': imei = get_imei() elif args.task == 'decrypt': uins = [args.uin] if args.uin else get_uin() imeis = [args.imei] if args.imei else get_imei() output_file = args.input + ".decrypted" assert not os.path.isfile(output_file), f"Output {output_file} exists!" for uin in uins: for imei in imeis: key = get_key(imei, uin) logger.info(f"Trying key {key} ...") try: do_decrypt(args.input, output_file, key) except Exception as error: logger.warning(f"An unexpected error occurred.{error}") pass else: logger.info(f"Database dumped to {output_file}") sys.exit()