平方X 发表于 2017-9-17 20:58:08

备份安卓微信聊天记录及加密方式研究


>本文由平方X发表于平方X网,转载请注明出处。[http://blog.pingfangx.com/2369.html](http://blog.pingfangx.com/2369.html)

# 0x00 需求
我有一个小米手机MI2s,还有一个米5。好像在某一次迁移还是刷机过程中,数据丢失了,想要恢复到一起。

# 0x01 官方的备分与恢复
官方用电脑与手机wifi连接,进行备份,备份速度约2M/S。

# 0x02 静态方式破解
很久以前在csdn看到的一篇文章,一直留着看,今天在收藏夹里一搜,居然没找到,吓我一跳,还好又在网止搜了回来。
[尼古拉斯_赵四.《 Android逆向之旅---静态方式破解微信获取聊天记录和通讯录信息》](http://blog.csdn.net/jiangwei0910410003/article/details/52238891)

## 2.1 导出数据库
adb pull /data/data/com.tencent.mm/MicroMsg/847c648f740954a4a7c6023b2305e874/EnMicroMsg.db D:/adb
后面那一串是MD5("mm"+uin)
来源于(http://blog.csdn.net/y788iiiiii) 回复于 [博文6楼](http://blog.csdn.net/yuanbohx/article/details/41674949)

## 2.2 读取源码
在博客中已经有了步骤,只是有些类变化了,自已跟着学习了一遍。
```
com.tencent.wcdb.database.SQLiteDatabase

    public static SQLiteDatabase openDatabase(String str, byte[] bArr, SQLiteCipherSpec sQLiteCipherSpec, CursorFactory cursorFactory, int i, DatabaseErrorHandler databaseErrorHandler, int i2) {
      SQLiteDatabase sQLiteDatabase = new SQLiteDatabase(str, i, cursorFactory, databaseErrorHandler);
      sQLiteDatabase.open(bArr, sQLiteCipherSpec, i2);
      return sQLiteDatabase;
    }
    private void openInner(byte[] bArr, SQLiteCipherSpec sQLiteCipherSpec, int i)

    public static SQLiteConnectionPool open(SQLiteDatabase sQLiteDatabase, SQLiteDatabaseConfiguration sQLiteDatabaseConfiguration, byte[] bArr, SQLiteCipherSpec sQLiteCipherSpec, int i) {
      if (sQLiteDatabaseConfiguration == null) {
            throw new IllegalArgumentException("configuration must not be null.");
      }
      SQLiteConnectionPool sQLiteConnectionPool = new SQLiteConnectionPool(sQLiteDatabase, sQLiteDatabaseConfiguration, i);
      sQLiteConnectionPool.mPassword = bArr;
      sQLiteConnectionPool.mCipher = sQLiteCipherSpec == null ? null : new SQLiteCipherSpec(sQLiteCipherSpec);
      sQLiteConnectionPool.open();
      return sQLiteConnectionPool;
    }

带密码打开数据库的方法搜索
Lcom/tencent/wcdb/database/SQLiteDatabase;->openDatabase(Ljava/lang/String;[BL

定位到com\tencent\mm\bj\e.smali
public static e r(String str, String str2, boolean z)

str2是密码
搜索该方法得
                  this.uJJ = e.r(str, this.arH, z);

arH是密码,看赋值
    private boolean a(String str, long j, String str2, boolean z, String str3) {
      ...
      this.arH = g.n((str2 + j).getBytes()).substring(0, 7);
      ...
    }
g.n()方法是md5,看a方法

public final boolean a(String str, String str2, String str3, long j, String str4, HashMap<Integer, g.c> hashMap, boolean z) {
    ...
      this.uJL = a(str2, j, str4, e.bNC(), str3);
    ...
}
需要的是str4和j

    public final boolean a(String str, String str2, String str3, long j, String str4, HashMap<Integer, c> hashMap, boolean z) {
      ...
      if (!this.uKi.a(str, str2, str3, j, str4, hashMap, z) || this.uKi.uJJ == null) {
      ...
    }
需要的是str4和j

            if (vI.gXW.a(str, str2, str3, (long) uH, p.rA(), vI.uh(), true)) {
需要的是p.rA()和uH

先看p.rA()
    public static String rA() {
      GMTrace.i(13801340534784L, 102828);
      String str = (String) k.rj().get(258);
      if (str != null) {
            GMTrace.o(13801340534784L, 102828);
      } else {
            str = getDeviceID(aa.getContext());
            if (str == null) {
                str = "1234567890ABCDEF";
            }
            k.rj().set(258, str);
            GMTrace.o(13801340534784L, 102828);
      }
      return str;
    }

再看uH
      int uH = gWW.uH();

      public final int uH() {
            int i;
            synchronized (this) {
                GMTrace.i(13525388886016L, 100772);
                if (!this.gXo) {
                  va();
                  this.gXo = true;
                }
                i = this.uin;
                GMTrace.o(13525388886016L, 100772);
            }
            return i;
      }

uin的赋值
这个是默认值,可以看到获取的是default_uin
private void va() {
    ...

                if (sVar == null) {
                  com.tencent.mm.sdk.platformtools.v.w("MMKernel.CoreAccount", "summer read detault uin exception sysCfg is null!");
                } else {
                  Integer num = (Integer) sVar.get(1);
                  if (num == null) {
                        if (sVar.uxm) {
                            com.tencent.mm.plugin.report.c.oRz.a(148, 40, 1, false);
                        }
                        Integer valueOf = Integer.valueOf(aa.getContext().getSharedPreferences("system_config_prefs", 4).getInt("default_uin", 0));
                        if (valueOf != null) {
                            com.tencent.mm.sdk.platformtools.v.i("MMKernel.CoreAccount", "summer read detault uin[%d], bakUin[%d] sysCfg.isOpenException[%b]", num, valueOf, Boolean.valueOf(sVar.uxm));
                            if (gXq) {
                              com.tencent.mm.sdk.platformtools.v.w("MMKernel.CoreAccount", "summer read detault uin exception backup uin[%d], stack[%s]", valueOf, bf.bJP());
                              com.tencent.mm.plugin.report.c.oRz.i(11911, Integer.valueOf(bf.f(valueOf)));
                              gXq = false;
                            }
                            a(sVar, valueOf.intValue());
                            num = valueOf;
                        }
                  }
                  com.tencent.mm.sdk.platformtools.v.i("MMKernel.CoreAccount", "summer getDefaultUin uin[%d]", Integer.valueOf(bf.f(num)));
                  i = bf.f(num);
                }
                this.uin = i;

    ...
}
但是追_auth_uin的时候,怎么也追不到,好复杂的样子。最后就放弃了。


```
结论:
并没有发生变化,和原博文的一致。
>概要:微信的核心数据库是EnMicroMsg.db,但是是加密操作的,而加密的密码是设备的IMEI+用户的UIN值(在SP中保存了),计算MD5(字符是小写),取出前7位字符即可。

## 2.3 工具选择
jdgui、jeb、jadx


# 0x03 查看
获取到了密码,在读取数据库的时候,却还是失败的。
难道是我密码计算错了,写安卓代码算了一下,没错呀。
看到有人回复
http://blog.csdn.net/yuanbohx/article/details/41674949,32楼
八只爪
>(IMEI+UIN)计算出的MD5值的前7位,和我在手机里安装的“微信加密聊天记录导出"软件不一致。。。手机安装的软件计算出来的数据库密码才是正确的,原因不太清楚……至于sqlite怎么导出查找到的数据,在研究中……

于是我就网上找他所说的“微信加密聊天记录导出”,结果下了一个电脑版,要收费,只能看7条,又下了一个查看器,要2个csdn积分……
这个技术……是自己的吗……居然要收费,唉。
好在兑换了2积分,下载后,那个sqlcipher.exe是可以使用的,原来是我的软件不正确。

页: [1]
查看完整版本: 备份安卓微信聊天记录及加密方式研究