From f58b214429a19b7f6e2b3e71b94dfc1e3a19d629 Mon Sep 17 00:00:00 2001 From: qicai21 Date: Sat, 21 Mar 2026 09:25:14 +0800 Subject: [PATCH 1/2] feat(macOS): fix V4 image decryption and HEVC transcoding - Implemented correct macOS-specific MD5 key derivation (Code + Cleaned Wxid). - Improved kvcomm file discovery by including monitordata patterns. - Enhanced wxid normalization for accounts like wxid_xxx_yyyy. - Added robust ffmpeg path detection for macOS (Homebrew/System). - Fixed HEVC bitstream extraction and transcoding flags (-f hevc). --- electron/services/imageDecryptService.ts | 18 ++++++++++----- electron/services/keyServiceMac.ts | 28 +++++++++++++++++------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/electron/services/imageDecryptService.ts b/electron/services/imageDecryptService.ts index 32a2ae0d..751658c7 100644 --- a/electron/services/imageDecryptService.ts +++ b/electron/services/imageDecryptService.ts @@ -1,4 +1,4 @@ -import { app, BrowserWindow } from 'electron' +import { app, BrowserWindow } from 'electron' import { basename, dirname, extname, join } from 'path' import { pathToFileURL } from 'url' import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, appendFileSync } from 'fs' @@ -1772,13 +1772,21 @@ export class ImageDecryptService { */ private getFfmpegPath(): string { const staticPath = getStaticFfmpegPath() - this.logInfo('ffmpeg 路径检测', { staticPath, exists: staticPath ? existsSync(staticPath) : false }) - - if (staticPath) { + + if (staticPath && existsSync(staticPath)) { return staticPath } - // 回退到系统 ffmpeg + // Fallback to common system paths, especially for Homebrew on Apple Silicon + const commonPaths = [ + '/opt/homebrew/bin/ffmpeg', + '/usr/local/bin/ffmpeg', + '/usr/bin/ffmpeg' + ] + for (const path of commonPaths) { + if (existsSync(path)) return path + } + return 'ffmpeg' } diff --git a/electron/services/keyServiceMac.ts b/electron/services/keyServiceMac.ts index af33b75d..6fc80a2b 100644 --- a/electron/services/keyServiceMac.ts +++ b/electron/services/keyServiceMac.ts @@ -878,9 +878,13 @@ export class KeyServiceMac { const trimmed = String(value || '').trim() if (!trimmed) return '' + // Handle wxid_xxx_yyyy -> wxid_xxx if (trimmed.toLowerCase().startsWith('wxid_')) { - const match = trimmed.match(/^(wxid_[^_]+)/i) - return match?.[1] || trimmed + const parts = trimmed.split('_') + if (parts.length >= 2) { + return parts[0] + '_' + parts[1] + } + return trimmed } const suffixMatch = trimmed.match(/^(.+)_([a-zA-Z0-9]{4})$/) @@ -1034,18 +1038,26 @@ export class KeyServiceMac { private collectKvcommCodes(accountPath?: string): number[] { const codeSet = new Set() - const pattern = /^key_(\d+)_.+\.statistic$/i + // Patterns: key_1234.statistic, monitordata_1234, etc. + const patterns = [ + /^key_(\d+)/i, + /^monitordata_(\d+)/i + ] for (const kvcommDir of this.getKvcommCandidates(accountPath)) { if (!existsSync(kvcommDir)) continue try { const files = readdirSync(kvcommDir) for (const file of files) { - const match = file.match(pattern) - if (!match) continue - const code = Number(match[1]) - if (!Number.isFinite(code) || code <= 0 || code > 0xFFFFFFFF) continue - codeSet.add(code) + for (const pattern of patterns) { + const match = file.match(pattern) + if (match) { + const code = Number(match[1]) + if (Number.isFinite(code) && code > 0 && code <= 0xFFFFFFFF) { + codeSet.add(code) + } + } + } } } catch { // 忽略不可读目录,继续尝试其他候选路径 From 6983c4d46551dd4bcb4790e8372c55d94cd7a59d Mon Sep 17 00:00:00 2001 From: qicai21 Date: Sat, 21 Mar 2026 13:38:48 +0800 Subject: [PATCH 2/2] fix(macOS): harden AES key validation with 4-byte signatures --- electron/services/keyServiceMac.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/electron/services/keyServiceMac.ts b/electron/services/keyServiceMac.ts index 6fc80a2b..dc56114a 100644 --- a/electron/services/keyServiceMac.ts +++ b/electron/services/keyServiceMac.ts @@ -1025,11 +1025,11 @@ export class KeyServiceMac { const decipher = crypto.createDecipheriv('aes-128-ecb', keyBytes, null) decipher.setAutoPadding(false) const dec = Buffer.concat([decipher.update(ciphertext), decipher.final()]) - if (dec[0] === 0xFF && dec[1] === 0xD8 && dec[2] === 0xFF) return true + if (dec[0] === 0xFF && dec[1] === 0xD8 && dec[2] === 0xFF && (dec[3] === 0xE0 || dec[3] === 0xE1 || dec[3] === 0xDB || dec[3] === 0xEE)) return true if (dec[0] === 0x89 && dec[1] === 0x50 && dec[2] === 0x4E && dec[3] === 0x47) return true if (dec[0] === 0x52 && dec[1] === 0x49 && dec[2] === 0x46 && dec[3] === 0x46) return true if (dec[0] === 0x77 && dec[1] === 0x78 && dec[2] === 0x67 && dec[3] === 0x66) return true - if (dec[0] === 0x47 && dec[1] === 0x49 && dec[2] === 0x46) return true + if (dec[0] === 0x47 && dec[1] === 0x49 && dec[2] === 0x46 && dec[3] === 0x38) return true return false } catch { return false @@ -1137,11 +1137,11 @@ export class KeyServiceMac { const decipher = crypto.createDecipheriv('aes-128-ecb', keyBytes.subarray(0, 16), null) decipher.setAutoPadding(false) const dec = Buffer.concat([decipher.update(ciphertext), decipher.final()]) - if (dec[0] === 0xFF && dec[1] === 0xD8 && dec[2] === 0xFF) return true + if (dec[0] === 0xFF && dec[1] === 0xD8 && dec[2] === 0xFF && (dec[3] === 0xE0 || dec[3] === 0xE1 || dec[3] === 0xDB || dec[3] === 0xEE)) return true if (dec[0] === 0x89 && dec[1] === 0x50 && dec[2] === 0x4E && dec[3] === 0x47) return true if (dec[0] === 0x52 && dec[1] === 0x49 && dec[2] === 0x46 && dec[3] === 0x46) return true if (dec[0] === 0x77 && dec[1] === 0x78 && dec[2] === 0x67 && dec[3] === 0x66) return true - if (dec[0] === 0x47 && dec[1] === 0x49 && dec[2] === 0x46) return true + if (dec[0] === 0x47 && dec[1] === 0x49 && dec[2] === 0x46 && dec[3] === 0x38) return true return false } catch { return false