From 8db5b64b387345c9e3bd20c615624d2bdc812294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?= Date: Thu, 15 Jun 2023 19:30:33 +0100 Subject: [PATCH] feat: fix mmkv parser, support for ios ekey mmkv --- .gitattributes | 1 + .../settings/panels/QMCv2/ImportFileModal.tsx | 2 +- src/util/MMKVParser.ts | 21 ++++++++++-------- src/util/__tests__/MMKVParser.test.ts | 19 ++++++++++++++++ src/util/__tests__/__fixture__/test.mmkv | Bin 0 -> 288 bytes src/util/formatHex.ts | 3 +++ 6 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 .gitattributes create mode 100644 src/util/__tests__/MMKVParser.test.ts create mode 100644 src/util/__tests__/__fixture__/test.mmkv create mode 100644 src/util/formatHex.ts diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fc2e8b5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.mmkv binary diff --git a/src/features/settings/panels/QMCv2/ImportFileModal.tsx b/src/features/settings/panels/QMCv2/ImportFileModal.tsx index f0f6b7b..811311b 100644 --- a/src/features/settings/panels/QMCv2/ImportFileModal.tsx +++ b/src/features/settings/panels/QMCv2/ImportFileModal.tsx @@ -52,7 +52,7 @@ export function ImportFileModal({ onClose, show }: ImportFileModalProps) { alert(`不是支持的 SQLite 数据库文件。\n表名:${qmc2Keys}`); return; } - } else if (/MMKVStreamEncryptId/i.test(file.name)) { + } else if (/MMKVStreamEncryptId|filenameEkeyMap/i.test(file.name)) { const fileBuffer = await file.arrayBuffer(); const map = MMKVParser.toStringMap(new DataView(fileBuffer)); qmc2Keys = Array.from(map.entries(), ([name, key]) => ({ name: getFileName(name), key })); diff --git a/src/util/MMKVParser.ts b/src/util/MMKVParser.ts index 9cb24f6..6a8425d 100644 --- a/src/util/MMKVParser.ts +++ b/src/util/MMKVParser.ts @@ -1,28 +1,29 @@ +import { formatHex } from './formatHex'; + const textDecoder = new TextDecoder('utf-8', { ignoreBOM: true }); export class MMKVParser { - private offset = 8; + private offset = 4; private length: number; constructor(private view: DataView) { const payloadLength = view.getUint32(0, true); this.length = 4 + payloadLength; + + // skip unused str + this.readInt(); } toString() { - const offset = this.offset.toString(16).padStart(8, '0'); - const length = this.length.toString(16).padStart(8, '0'); - return ``; + const offset = formatHex(this.offset, 8); + const length = formatHex(this.length, 8); + return ``; } get eof() { return this.offset >= this.length; } - peek() { - return this.view.getUint8(this.offset); - } - public readByte() { return this.view.getUint8(this.offset++); } @@ -77,7 +78,9 @@ export class MMKVParser { const newOffset = this.offset + containerLen; const result = this.readString(); if (newOffset !== this.offset) { - throw new Error('readVariantString failed: offset does not match'); + const expected = formatHex(newOffset); + const actual = formatHex(this.offset); + throw new Error(`readVariantString failed: offset does mismatch (expect: ${expected}, actual: ${actual})`); } return result; } diff --git a/src/util/__tests__/MMKVParser.test.ts b/src/util/__tests__/MMKVParser.test.ts new file mode 100644 index 0000000..e4eebcb --- /dev/null +++ b/src/util/__tests__/MMKVParser.test.ts @@ -0,0 +1,19 @@ +import { MMKVParser } from '../MMKVParser'; +import { readFileSync } from 'node:fs'; + +test('parse mmkv file as expected', () => { + const buff = readFileSync(__dirname + '/__fixture__/test.mmkv'); + const view = new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength)); + expect(MMKVParser.toStringMap(view)).toEqual( + new Map([ + ['key', 'value'], + [ + 'Lorem Ipsum', + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' + + 'Vestibulum congue volutpat metus non molestie. Quisque id est sapien. ' + + 'Fusce eget tristique sem. Donec tellus lacus, viverra sed lectus eget, elementum ultrices dolor. ' + + 'Integer non urna justo.', + ], + ]) + ); +}); diff --git a/src/util/__tests__/__fixture__/test.mmkv b/src/util/__tests__/__fixture__/test.mmkv new file mode 100644 index 0000000000000000000000000000000000000000..04a8a013432b3f44aea3464fb9d3ffdc855ad1b7 GIT binary patch literal 288 zcmXAkO-cko427o=-RL3S0h+pl&?p0g=*E3n9hzwUw3Vd6mB$uWW7=6J`QGEp-EcU3 zUwzQm&GimnXI@zn*9DYW171m# z-zMr>e%Yg=D^a1U?qb<{63OM6fMZoh`oE3!8u4zTEu5e4r|J0g Gc>V*1BWaWX literal 0 HcmV?d00001 diff --git a/src/util/formatHex.ts b/src/util/formatHex.ts new file mode 100644 index 0000000..0d778c1 --- /dev/null +++ b/src/util/formatHex.ts @@ -0,0 +1,3 @@ +export function formatHex(value: number, len = 8) { + return '0x' + (value | 0).toString(16).padStart(len, '0'); +}