greycodee/wechat-backup

未查询到索引表名

djerryz opened this issue · 18 comments

  1. get data and decrypt
adb pull /data/data/com.tencent.mm/MicroMsg/03973XXX/EnMicroMsg.db /tmp/wexin/
adb pull /data/data/com.tencent.mm/MicroMsg/03973XXX/WxFileIndex.db /tmp/wexin/

docker run --rm -v  /tmp/wexin/:/wcdb  greycodee/wcdb-sqlcipher -f EnMicroMsg.db -k XXX
docker run --rm -v  /tmp/wexin/:/wcdb  greycodee/wcdb-sqlcipher -f WxFileIndex.db -k XXX
2022/07/21 17:32:11 开始解密...
2022/07/21 17:32:13 解密成功: ok
2022/07/21 17:32:13 明文数据库文件名: EnMicroMsg_plain.db

2022/07/21 17:33:30 开始解密...
2022/07/21 17:33:31 解密成功: ok
2022/07/21 17:33:31 明文数据库文件名: WxFileIndex_plain.db

  1. execute main.go and get this error:
go run main.go -f '/tmp/wexin/'
2022/07/21 17:33:40 未查询到图片索引表名,sql: no rows in result set
2022/07/21 17:33:40 sql: no rows in result set
exit status 1

看看你的 WxFileIndex_plain.db 是否有数据

有数据的,解密前1K.解密后7K,但是执行 go run main.go 命令后,大小变成了0

解密前才 1 k大小?那应该是没数据的,空db,你可以用sqlite工具打开查看下

-rwxrwxrwx 1 u0_a182 u0_a182 1.0K 2022-07-22 00:37 WxFileIndex.db
-rwxrwxrwx 1 u0_a182 u0_a182 32K 2022-07-22 01:06 WxFileIndex.db-shm
-rwxrwxrwx 1 u0_a182 u0_a182 17K 2022-07-22 01:06 WxFileIndex.db-wal
-rwxrwxrwx 1 u0_a182 u0_a182 81 2022-07-22 00:37 WxFileIndex.db.ini

看起来WxFileIndex.db-shm和WxFileIndex.db-wal才有真正的数据。

我查询了这两个扩展相关的解释,得到下面相关的结论:

  • 有时 SQLiteOpenHelper 由于未关闭的游标而创建 .db-shm 和 .db-wal 扩展数据库
  • WAL 是回滚日志的替代品,它使 SQLite 能够在事务失败时回滚更改。

Referer:

确实是没有数据内容在WxFileIndex.db当中的,这非常的奇怪,目前我的微信(8.0.24)是可以稳定使用的:
Phone Info: 一加3,氧OS,Kali NetHuner, Root, Magisk Manager

>>> cursor = conn.execute("PRAGMA database_list;")
>>> for row in cursor:
...     print(row)
(0, 'main', '/root/bakweixinchat/tmp/WxFileIndex_plain.db')
>>> cursor = conn.execute("SELECT * FROM main.sqlite_master WHERE type='table';") or cursor = conn.execute("SELECT name FROM sqlite_temp_master WHERE type='table';")

>>>
>>> for row in cursor:
...     print(row)
无结果

测试了很多次,包括重装,强制退出,数据迁移,WxFileIndex.db始终为1KB,是否是微信的业务逻辑有改变的原因

image

你在微信上退出登陆后,然后在看看 WxFileIndex.db 里有没有数据。应该是wal模式下数据在没关闭连接前就没写进去?

并没有作用,重启,登出等,目前我在思考是否有其他方式重建文件索引的方式,不用依赖WxFileIndex:
如,

图片

在EnMicroMsg.db 的message的 imgPath:
image

文件

在EnMicroMsg.db 的message的 content:

<?xml version="1.0"?>\n<msg>\n\t<appmsg appid="XXX" sdkver="0">\n\t\t<title>xxx.docx</title>

其中title字段的xxx.docx可以和匹配Download目录的文件名匹配

表情

未测试

语音

未测试

视频

未测试

image

import sqlite3
import os

values = ["0717590723228e1843b41b7106","2207231759461121"]
filename = "EnMicroMsg_plain.db"
with sqlite3.connect(filename) as conn:
    conn.row_factory = sqlite3.Row
    cursor = conn.cursor()    
    cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
    for tablerow in cursor.fetchall():
        table = tablerow[0]
        cursor.execute("SELECT * FROM {t}".format(t = table))
        for row in cursor:
            for field in row.keys():
                x = row[field]
                for avalue in values:
                    try:
                        avalue_int = int(avalue)
                    except:
                        avalue_int = "luanqibazao@@@"
                    if x == avalue or x==avalue_int:
                        print(table, field, x)
                    else:
                        pass

得到:

message imgPath 0717590723228e1843b41b7106
message imgPath 2207231759461121
voiceinfo FileName 0717590723228e1843b41b7106
voiceinfo ClientId 0717590723228e1843b41b7106
videoinfo2 filename 2207231759461121

在voiceinfo 和videoinfo2 中并没有媒体文件真实位置,只有msgsvrid看起来能继续发散,但是在EnMicroMsg表中也关联不出来path,似乎还是得借助其他db文件来匹配path.

新的问题

测试还发现,发送媒体文件 语言和视频后,并没有 voice2 和 video这两个目录

分析可能和WxFileIndex一样,可能在某个地方缓存了数据,但是没有落盘到MicroMsg文件目录中,即不是实时存储的, 目前没有好的思路去验证这个猜想

但是测试:

  • 关机
  • 重装
  • 重登
  • 数据迁移

等动作,数据都是没有落盘的,但聊天数据可以正常访问!

并没有作用,重启,登出等,目前我在思考是否有其他方式重建文件索引的方式,不用依赖WxFileIndex: 如,

图片

在EnMicroMsg.db 的message的 imgPath: image

文件

在EnMicroMsg.db 的message的 content:

<?xml version="1.0"?>\n<msg>\n\t<appmsg appid="XXX" sdkver="0">\n\t\t<title>xxx.docx</title>

其中title字段的xxx.docx可以和匹配Download目录的文件名匹配

表情

未测试

语音

未测试

视频

未测试

image

可以不用依赖 WxFileIndex.db ,我以前写过一版不用依赖 WxfileIndex.db 的程序,你可以看提交历史

具体规则如下

媒体文件存放位置
安卓手机在录音和视频 Android/data/com.tencent.mm/MicroMsg/ 下
图片在 /data/data/com.tencent.mm/MicroMsg/[32位字母]/image2/ 下

avatar文件索引
微信ID通过md5 32位加密后,前4位就是文件地址,每两位代表一个文件夹名,例如微信id:weixin 经过md5加密后是:C196266F837D14E0B693F961BEE37B66,那么这个微信的头像地址是:avatar/c1/96/user_c196266f837d14e0b693f961bee37b66.png

文件地址
视频地址:直接通过message 表后的imgPath查找到video文件夹查找对应的视频,封面图后缀为.jpg,视频后缀为:.mp4
语言地址:message 的 imgPath字段通过MD5加密后,前4个字母代表两级文件夹名,然后最终文件名是:msg_imgPath值.amr
图片地址:THUMBNAIL_DIRPATH://th_5a24c5d362dae72b0ad52d78767ba883,其中5a24代表 /5a/24文件夹下的,th_5a24c5d362dae72b0ad52d78767ba883是图片文件名
message 的图片地址是压缩后的图片地址,如果要看没压缩的原图,需要通过WxFileIndex.db 查询文件地址
还可以通过拼接来获取未压缩的图片地址:
发送的图片:
文件名:自己的wxid++对方wxid++当前msgSvrid+backup
路径:文件名前4个字母,每两个字母一个文件夹层级
接收的图片:
文件名:对方wxid+
+自己的wxid+_+当前msgSvrid+_backup
路径:文件名前4个字母,每两个字母一个文件夹层级
1090519089 是文件格式 文件保存在微信个人文件夹下的Download里

上面说的,没有voice2 和 video这两个目录, 通过提示,我在 /data/media/0/Android/data/com.tencent.mm/MicroMsg下找到了,非常感谢。

目前构造文件名的方式我稍后在我本地尝试实现下,最好能只依赖EnMicroMsg.db完成全部数据的提取~

上面说的,没有voice2 和 video这两个目录, 通过提示,我在 /data/media/0/Android/data/com.tencent.mm/MicroMsg下找到了,非常感谢。

目前构造文件名的方式我稍后在我本地尝试实现下,最好能只依赖EnMicroMsg.db完成全部数据的提取~

通过这种方式,有时候可以找到源文件,有时候又找不到只能通过wxfileindex去找到,所以我以前写过一版,后来还是删掉了

可以综合一下,如果wxfileindex没有的数据就通过上述方式做互补, golang不是很熟,混用了python表达补充的逻辑,稍晚本地测试下,如果测试没问题,后面提个PR

video:

func (wf WxFileIndex, em EnMicroMsg) GetVideoPath(msgId string) string {
	var path string
	querySql := fmt.Sprintf("select path from %s WHERE msgId=%s and msgSubType=1", wf.tableName, msgId)
	err := wf.db.QueryRow(querySql).Scan(&path)
	if err != nil {
		querySql := fmt.Sprintf("select imgPath from %s WHERE msgId=%s", "message", msgId)
		err1 := em.db.QueryRow(querySql).Scan(&path)
		if err1 != nil {
			log.Printf("未查询到视频,%s", err)
			return ""
		}
	}
	return MediaPathPrefix + strings.Join(strings.SplitAfter(path, "/")[1:], "")
}

image:

voice:

func (wf WxFileIndex, em EnMicroMsg) GetVoicePath(msgId string) string {
	var path string
        var raw_path string
	querySql := fmt.Sprintf("select path from %s WHERE msgId=%s", wf.tableName, msgId)
	err := wf.db.QueryRow(querySql).Scan(&path)
	if err != nil {
                  querySql := fmt.Sprintf("select imgPath from %s WHERE msgId=%s", "message", msgId)
		  err1 := em.db.QueryRow(querySql).Scan(&raw_path)
                  path = md5(raw_path)
                  path = path[0:2] + "/" + path[2:4] +"/" + "msg_{}.amr".format(raw_path)
		  if err1 != nil {
			  log.Printf("未查询到语音,%s", err)
			  return ""
		  }
	} else {
		return MediaPathPrefix + strings.Join(strings.SplitAfter(path, "/")[1:], "")
	}
}

file:

func (wf WxFileIndex, em EnMicroMsg) GetFilePath(msgId string) (path string, size int64) {
        var content string
	querySql := fmt.Sprintf("select path,size from %s WHERE msgId=%s", wf.tableName, msgId)
	err := wf.db.QueryRow(querySql).Scan(&path, &size)
	if err != nil {
                  querySql := fmt.Sprintf("select content from %s WHERE msgId=%s", "message", msgId)
		  err1 := em.db.QueryRow(querySql).Scan(&content)
                  path = content.split("<title>",1)[1].split("</title">,1")[0]
                  size = int(content.split("<totallen>",1)[1].split("</totallen">,1")[0])
		  if err1 != nil {
			  log.Printf("未查询到文件,%s", err)
			  return ""
		  }
	} else {
		return MediaPathPrefix + path, size
	}
}

master代码已修复这个问题,本地 pull 下代码可以运行了

看到commit代码,只是简单修改了一下代码,并没有更改从wxFileindex获取路径的逻辑,但修改之后的代码却能获取到文件路径了,非常的神奇,wxFileindex目前还是空的,我看了10几分钟没看懂这是为什么,😄,再研究下

基本理解了:
前端 -> main./api/chat/detail -> wcdb.ChatDetailList -> wcdb.enmicromsg.ChatDetailList -> wcdb.getMediaPath

就算没有wxFileIndex也只会影响:
chat.MediaSourcePath = wcdb.wxfileindex.GetImgPath(chat.MsgId)
filepath, fileSize := wcdb.wxfileindex.GetFilePath(chat.MsgId)

Best Respect For Author! ❤❤❤