diff --git a/README.md b/README.md index 063f275..8df1034 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ - **API接口**: 提供RESTful API接口进行数据库解密操作 - **Web界面**: 提供现代化的Web操作界面 - **聊天记录查看**: 支持查看解密后的聊天记录(基础功能) +- **图片资源解密**: 支持批量解密微信图片(.dat文件),按MD5哈希存储便于快速访问 ### 开发计划 @@ -156,6 +157,48 @@ uv run analyze_wechat_databases.py - `{数据库名}/README.md`:该数据库概览 - `{数据库名}/{表名}.md`:各表详细结构、索引、外键、示例数据与建表语句 +### 图片资源解密 + +微信的图片文件(.dat)是加密存储的,需要解密后才能正常显示。本工具提供了API接口进行批量解密。 + +#### 1. 获取图片解密密钥 + +```bash +# GET请求获取密钥(需要微信正在运行以提取AES密钥) +curl http://localhost:8000/api/media/keys + +# 强制重新提取密钥 +curl "http://localhost:8000/api/media/keys?force_extract=true" +``` + +返回示例: +```json +{ + "status": "success", + "xor_key": "0xA5", + "aes_key": "xxxxxxxxxxxxxxxx", + "message": "XOR密钥提取成功。已从微信进程提取AES密钥" +} +``` + +#### 2. 批量解密所有图片 + +```bash +# POST请求批量解密所有图片到 output/databases/{账号}/resource 目录 +curl -X POST http://localhost:8000/api/media/decrypt_all \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +解密后的图片按MD5哈希命名,存储在 `resource/{md5前2位}/{md5}.{ext}` 路径下,便于快速查找。 + +#### 3. 获取已解密的图片 + +```bash +# 直接通过MD5获取已解密的图片(更快) +curl http://localhost:8000/api/media/resource/{md5} +``` + ## 安全说明 **重要提醒**: diff --git a/frontend/components/Stepper.vue b/frontend/components/Stepper.vue new file mode 100644 index 0000000..fc5749e --- /dev/null +++ b/frontend/components/Stepper.vue @@ -0,0 +1,75 @@ + + + + + + + + + + + + + {{ index + 1 }} + + + + {{ step.title }} + + + + + + + + + + + diff --git a/frontend/composables/useApi.js b/frontend/composables/useApi.js index 7b0adfa..2e10888 100644 --- a/frontend/composables/useApi.js +++ b/frontend/composables/useApi.js @@ -94,6 +94,37 @@ export const useApi = () => { const url = '/chat/media/open_folder' + (query.toString() ? `?${query.toString()}` : '') return await request(url, { method: 'POST' }) } + + // 获取图片解密密钥 + const getMediaKeys = async (params = {}) => { + const query = new URLSearchParams() + if (params && params.account) query.set('account', params.account) + if (params && params.force_extract) query.set('force_extract', 'true') + const url = '/media/keys' + (query.toString() ? `?${query.toString()}` : '') + return await request(url) + } + + // 保存图片解密密钥 + const saveMediaKeys = async (params = {}) => { + const query = new URLSearchParams() + if (params && params.account) query.set('account', params.account) + if (params && params.xor_key) query.set('xor_key', params.xor_key) + if (params && params.aes_key) query.set('aes_key', params.aes_key) + const url = '/media/keys' + (query.toString() ? `?${query.toString()}` : '') + return await request(url, { method: 'POST', body: { account: params.account, force_extract: false } }) + } + + // 批量解密所有图片 + const decryptAllMedia = async (params = {}) => { + return await request('/media/decrypt_all', { + method: 'POST', + body: { + account: params.account || null, + xor_key: params.xor_key || null, + aes_key: params.aes_key || null + } + }) + } return { detectWechat, @@ -103,6 +134,9 @@ export const useApi = () => { listChatAccounts, listChatSessions, listChatMessages, - openChatMediaFolder + openChatMediaFolder, + getMediaKeys, + saveMediaKeys, + decryptAllMedia } } \ No newline at end of file diff --git a/frontend/pages/decrypt.vue b/frontend/pages/decrypt.vue index 7032591..54970bb 100644 --- a/frontend/pages/decrypt.vue +++ b/frontend/pages/decrypt.vue @@ -1,9 +1,14 @@ - + - - + + + + + + + @@ -12,7 +17,7 @@ - 解密配置 + 数据库解密 输入密钥和路径开始解密 @@ -107,16 +112,241 @@ + + + + + + + + + + + + 图片密钥 + 获取图片解密所需的密钥 + + + + + + + + XOR 密钥 + + {{ mediaKeys.xor_key || '未获取' }} + + + + AES 密钥 + + {{ mediaKeys.aes_key ? mediaKeys.aes_key.substring(0, 8) + '...' : '未获取' }} + + + + + + + + + {{ mediaKeys.message }} + + + + + + + + + + + + + + {{ mediaLoading ? '获取中...' : '获取密钥' }} + + + 强制重新提取 + + + 下一步 + + + + + + + + + + 跳过图片解密,直接查看聊天记录 → + + + + + + + + + + + + + + + + + 批量解密图片 + 仅解密加密的图片文件(.dat),其他文件无需解密 + + + + + {{ decryptProgress.current }} / {{ decryptProgress.total }} + 已处理 / 总图片 + + + + + + + + + 解密进度 + {{ progressPercent }}% + + + + + + + + + + + + {{ decryptProgress.current_file }} + + {{ decryptProgress.fileStatus === 'success' ? '解密成功' : decryptProgress.fileStatus === 'skip' ? '已存在' : decryptProgress.fileStatus === 'fail' ? '失败' : '' }} + + + + + + + {{ decryptProgress.total }} + 总图片 + + + {{ decryptProgress.success_count }} + 成功 + + + {{ decryptProgress.skip_count }} + 跳过(已解密) + + + {{ decryptProgress.fail_count }} + 失败 + + + + + + + + + + + + 解密完成 + + + 输出目录: {{ mediaDecryptResult.output_dir }} + + + + + + + + + 查看失败原因说明 + + + 可能的失败原因: + + 解密后非有效图片:文件不是图片格式(如视频缩略图损坏) + V4-V2版本需要AES密钥:需要微信运行时才能提取AES密钥 + 未知加密版本:新版微信使用了不支持的加密方式 + 文件为空:原始文件损坏或为空文件 + + + + + + + + + + + + + + + + {{ mediaDecrypting ? '解密中...' : (mediaDecryptResult ? '重新解密' : '开始解密图片') }} + + + 查看聊天记录 + + + + + + + - + - 解密失败 - {{ error }} + 操作失败 + {{ error }} @@ -146,13 +376,21 @@
输入密钥和路径开始解密
获取图片解密所需的密钥
仅解密加密的图片文件(.dat),其他文件无需解密
{{ mediaDecryptResult.output_dir }}
可能的失败原因:
解密失败
{{ error }}
操作失败