mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-17 11:37:06 +08:00
fix: 完善聊天解析与导出体验
- 修复引用消息名称和图片布局 - 明确单会话图片测试日志范围 - 支持导出文件附件 - 完善图片批测、会话刷新和安全退出
This commit is contained in:
@@ -16,6 +16,7 @@ import { ImageDecryptService } from '../../src/main/image-decrypt-service'
|
||||
|
||||
const aesKey = '0123456789abcdef'
|
||||
const xorKey = 0x40
|
||||
const originalResourcesPath = process.resourcesPath
|
||||
|
||||
function writeV2Dat(file: string): Buffer {
|
||||
const aesPlain = Buffer.from([0x89, 0x50, 0x4e, 0x47, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||
@@ -35,22 +36,95 @@ function writeV2Dat(file: string): Buffer {
|
||||
}
|
||||
|
||||
describe('DAT image decryption', () => {
|
||||
beforeAll(() => mkdirSync(root, { recursive: true }))
|
||||
afterAll(() => rmSync(root, { recursive: true, force: true }))
|
||||
beforeAll(() => {
|
||||
mkdirSync(root, { recursive: true })
|
||||
Object.defineProperty(process, 'resourcesPath', { configurable: true, value: root })
|
||||
})
|
||||
afterAll(() => {
|
||||
Object.defineProperty(process, 'resourcesPath', {
|
||||
configurable: true,
|
||||
value: originalResourcesPath
|
||||
})
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it('decrypts a synthetic V2 AES/raw/XOR fixture', () => {
|
||||
const file = join(root, 'fixture.dat')
|
||||
const expected = writeV2Dat(file)
|
||||
expect(new ImageDecryptService('0x40', aesKey).decryptImage(file)).toEqual(expected)
|
||||
const service = new ImageDecryptService('0x40', aesKey)
|
||||
expect(service.decryptImage(file)).toEqual(expected)
|
||||
expect(service.decryptImageToBase64(file)).toMatch(/^data:image\/png;base64,/)
|
||||
expect(service.getLastDecodeDiagnostic()).toMatchObject({
|
||||
code: 'SUCCESS',
|
||||
datVersion: 2,
|
||||
imageFormat: 'PNG'
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects the wrong AES key and unsupported legacy signatures accurately', () => {
|
||||
const file = join(root, 'fixture.dat')
|
||||
writeV2Dat(file)
|
||||
expect(new ImageDecryptService('0x40', 'fedcba9876543210').decryptImage(file)).toBeNull()
|
||||
const wrongKeyService = new ImageDecryptService('0x40', 'fedcba9876543210')
|
||||
expect(wrongKeyService.decryptImage(file)).toBeNull()
|
||||
expect(wrongKeyService.getLastDecodeDiagnostic()).toMatchObject({
|
||||
code: 'AES_DECRYPT_FAILED',
|
||||
datVersion: 2
|
||||
})
|
||||
|
||||
const legacy = join(root, 'legacy.dat')
|
||||
writeFileSync(legacy, Buffer.from([0xff, 0xd8, 0xff, 0x00]))
|
||||
expect(new ImageDecryptService('0x40', aesKey).decryptImage(legacy)).toBeNull()
|
||||
writeFileSync(legacy, Buffer.from([0x12, 0x34, 0x56, 0x78]))
|
||||
const legacyService = new ImageDecryptService('0x40', aesKey)
|
||||
expect(legacyService.decryptImage(legacy)).toBeNull()
|
||||
expect(legacyService.getLastDecodeDiagnostic()).toMatchObject({
|
||||
code: 'UNSUPPORTED_DAT_VERSION',
|
||||
datVersion: 0
|
||||
})
|
||||
})
|
||||
|
||||
it('finds modern _M variants and reads plain images stored with a DAT extension', async () => {
|
||||
const accountRoot = join(root, 'modern-account')
|
||||
const sessionId = '77705c31c50e8a4242a9d527fe9433de'
|
||||
const imageDirectory = join(accountRoot, 'msg', 'attach', sessionId, '2025-10', 'Img')
|
||||
mkdirSync(imageDirectory, { recursive: true })
|
||||
|
||||
const imageBase = '9718e38ad90f57f9e833d17ff2373abd'
|
||||
const mediumFile = join(imageDirectory, `${imageBase}_M.dat`)
|
||||
writeFileSync(mediumFile, Buffer.from([0x89, 0x50, 0x4e, 0x47, 1, 2, 3, 4]))
|
||||
|
||||
const service = new ImageDecryptService('0x40', aesKey)
|
||||
await expect(
|
||||
service.findImageFileAsync(undefined, imageBase, {
|
||||
allowThumbnail: false,
|
||||
accountDir: accountRoot,
|
||||
sessionId
|
||||
})
|
||||
).resolves.toBe(mediumFile)
|
||||
expect(service.decryptImageToBase64(mediumFile)).toMatch(/^data:image\/png;base64,/)
|
||||
expect(service.getLastDecodeDiagnostic()).toMatchObject({
|
||||
code: 'DIRECT_IMAGE',
|
||||
imageFormat: 'PNG'
|
||||
})
|
||||
await expect(service.decryptImageToBase64WithFallbackAsync(mediumFile, true)).resolves.toEqual(
|
||||
expect.objectContaining({ filePath: mediumFile })
|
||||
)
|
||||
|
||||
const thumbnailBase = '37a9000000000000000000000000ceaa'
|
||||
const thumbnailFile = join(imageDirectory, `${thumbnailBase}_t_M.dat`)
|
||||
writeFileSync(thumbnailFile, Buffer.from([0xff, 0xd8, 0xff, 0x00]))
|
||||
expect(service.isThumbnailFile(thumbnailFile)).toBe(true)
|
||||
await expect(
|
||||
service.findImageFileAsync(undefined, `${thumbnailBase}_t_M.dat`, {
|
||||
allowThumbnail: false,
|
||||
accountDir: accountRoot,
|
||||
sessionId
|
||||
})
|
||||
).resolves.toBeNull()
|
||||
await expect(
|
||||
service.findImageFileAsync(undefined, `${thumbnailBase}_t_M.dat`, {
|
||||
allowThumbnail: true,
|
||||
accountDir: accountRoot,
|
||||
sessionId
|
||||
})
|
||||
).resolves.toBe(thumbnailFile)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user