未查询到索引表名
djerryz opened this issue · 18 comments
- 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
- 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 里有没有数据。应该是wal模式下数据在没关闭连接前就没写进去?
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:
文件
在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目录的文件名匹配
表情
未测试
语音
未测试
视频
未测试
可以不用依赖 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! ❤❤❤