@powercess/qq-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @powercess/qq-mcp might be problematic. Click here for more details.

@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline.js","sourceRoot":"","sources":["../src/offline.ts"],"names":[],"mappings":"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"}
package/dist/schema.js ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * QQNT database schema extraction from the `<db>.material` key-material files.
3
+ *
4
+ * Reverse-engineered on the local Windows NTQQ 9.9.35 install:
5
+ * - The material file layout is: [16B salt][AES-256-CBC ciphertext]
6
+ * - enc_key = PBKDF2-HMAC-SHA512(passphrase, salt, 4000, 32), IV = zero block
7
+ * - Decrypted payload is a plaintext stream of SQL DDL (CREATE TABLE / CREATE INDEX)
8
+ * describing the full schema of the corresponding SQLCipher database.
9
+ *
10
+ * Pure Node.js (node:crypto), works fully offline once passphrase + material path
11
+ * are known (passphrase can be captured from a running QQ via the debugger helper
12
+ * in scripts/ — see README).
13
+ */
14
+ import { createDecipheriv, pbkdf2Sync } from "node:crypto";
15
+ import { readFileSync } from "node:fs";
16
+ export const DEFAULT_KDF_ITER = 4000;
17
+ /**
18
+ * Decrypts a QQNT `.material` file into its plaintext payload.
19
+ */
20
+ export function decryptMaterial(path, passphrase, kdfIter = DEFAULT_KDF_ITER) {
21
+ const data = readFileSync(path);
22
+ if (data.length < 32)
23
+ throw new Error(`material file too small: ${data.length} bytes`);
24
+ const salt = data.subarray(0, 16);
25
+ const body = data.subarray(16);
26
+ if (body.length % 16 !== 0)
27
+ throw new Error(`material ciphertext not block aligned: ${body.length}`);
28
+ const key = pbkdf2Sync(passphrase, salt, kdfIter, 32, "sha512");
29
+ const decipher = createDecipheriv("aes-256-cbc", key, Buffer.alloc(16, 0));
30
+ decipher.setAutoPadding(false);
31
+ const plain = Buffer.concat([decipher.update(body), decipher.final()]);
32
+ return { salt, plaintext: plain };
33
+ }
34
+ const CREATE_RE = /CREATE(?: UNIQUE)? (TABLE|INDEX)\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)/g;
35
+ /**
36
+ * Extracts table/DDL definitions from the decrypted material payload.
37
+ */
38
+ export function extractSchema(plain, maxStatements = 2000) {
39
+ const text = plain.toString("utf8");
40
+ const out = [];
41
+ const seen = new Set();
42
+ const re = /CREATE(?: UNIQUE)? (TABLE|INDEX)\s+([A-Za-z0-9_]+)/g;
43
+ let m;
44
+ while ((m = re.exec(text)) !== null) {
45
+ const kind = m[1];
46
+ const name = m[2];
47
+ if (seen.has(name))
48
+ continue;
49
+ seen.add(name);
50
+ // Try to capture the full statement up to the closing paren.
51
+ let statement;
52
+ const start = m.index;
53
+ const close = findStatementEnd(text, start);
54
+ if (close > start)
55
+ statement = text.substring(start, close);
56
+ else
57
+ statement = text.substring(start, start + Math.min(200, text.length - start));
58
+ const entry = { name };
59
+ if (kind === "TABLE" && statement) {
60
+ entry.statement = statement;
61
+ const cols = statement.match(/[\[\]]([0-9]{3,5})[\[\]]\s+(INTEGER|TEXT|BLOB|REAL|NUMERIC)/g);
62
+ if (cols)
63
+ entry.columns = [...new Set(cols)];
64
+ }
65
+ out.push(entry);
66
+ if (out.length >= maxStatements)
67
+ break;
68
+ }
69
+ return out;
70
+ }
71
+ /** Finds the index of the balanced closing parenthesis after `start`, or -1. */
72
+ function findStatementEnd(text, start) {
73
+ let depth = 0;
74
+ for (let i = start; i < text.length; i++) {
75
+ const c = text[i];
76
+ if (c === "(")
77
+ depth++;
78
+ else if (c === ")") {
79
+ depth--;
80
+ if (depth === 0)
81
+ return i + 1;
82
+ }
83
+ }
84
+ return -1;
85
+ }
86
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Offline decryption of NTQQ (Windows QQ NT) SQLCipher databases — pure Node,
3
+ * no sqlcipher native dependency.
4
+ *
5
+ * Reverse-engineered layout, verified byte-exact against SQLCipher 4.12 on
6
+ * NTQQ 9.9.35.52892 (see docs/reverse-engineering.md):
7
+ *
8
+ * file = [1024-byte NTQQ header][salt 16B][page 2][page 3]...
9
+ * page = [16B salt, page 1 only][ciphertext][16B IV][32B tag]
10
+ * ciphertext = AES-256-CBC(plaintext) with IV from the page tail
11
+ * key = PBKDF2-HMAC-SHA512(passphrase, salt, 4000, 32)
12
+ * plaintext page 1 = [16B salt][SQLite header from offset 16 onward]
13
+ *
14
+ * The 32-byte tail tag is NOT authenticated here: no standard HMAC layout
15
+ * matched, and decryption does not need it (decrypted pages are validated by
16
+ * their SQLite structure instead).
17
+ */
18
+ import { createDecipheriv, createCipheriv, pbkdf2Sync, createHash } from "node:crypto";
19
+ import { closeSync, openSync, readSync, statSync, writeSync } from "node:fs";
20
+ /** NTQQ custom header preceding the SQLCipher salt. */
21
+ export const NTQQ_HEADER_SIZE = 1024;
22
+ /** SQLite page size used by every NTQQ database. */
23
+ export const DEFAULT_PAGE_SIZE = 4096;
24
+ /** Bytes after the ciphertext: 16-byte IV followed by a 32-byte unchecked tag. */
25
+ export const CRYPTO_TAIL_SIZE = 48;
26
+ /** SQLCipher default KDF iteration count for this key size. */
27
+ export const DEFAULT_KDF_ITER = 4000;
28
+ const SQLITE_MAGIC = Buffer.from("SQLite format 3\0", "latin1");
29
+ export const DEFAULT_LAYOUT = {
30
+ headerSize: NTQQ_HEADER_SIZE,
31
+ pageSize: DEFAULT_PAGE_SIZE,
32
+ tailSize: CRYPTO_TAIL_SIZE,
33
+ };
34
+ function readAt(path, offset, length) {
35
+ const fd = openSync(path, "r");
36
+ try {
37
+ const buf = Buffer.alloc(length);
38
+ const n = readSync(fd, buf, 0, length, offset);
39
+ return buf.subarray(0, n);
40
+ }
41
+ finally {
42
+ closeSync(fd);
43
+ }
44
+ }
45
+ /** Parses the 1024-byte NTQQ header (magic, page size, device blob, algorithms). */
46
+ export function parseNtqqHeader(path) {
47
+ const buf = readAt(path, 0, NTQQ_HEADER_SIZE);
48
+ if (buf.length < NTQQ_HEADER_SIZE)
49
+ throw new Error(`${path}: too small for an NTQQ header`);
50
+ if (buf.subarray(0, 15).toString("latin1") !== "SQLite header 3") {
51
+ throw new Error(`${path}: not an NTQQ database (bad magic ${JSON.stringify(buf.subarray(0, 15).toString("latin1"))})`);
52
+ }
53
+ const pageSize = buf.readUInt16BE(18);
54
+ const blobHex = buf.subarray(0x2f, 0xaf).toString("latin1");
55
+ if (!/^[0-9a-f]{128}$/i.test(blobHex))
56
+ throw new Error(`${path}: device blob missing`);
57
+ const text = buf.toString("latin1");
58
+ return {
59
+ pageSize: pageSize >= 512 && (pageSize & (pageSize - 1)) === 0 ? pageSize : DEFAULT_PAGE_SIZE,
60
+ deviceBlob: Buffer.from(blobHex, "hex"),
61
+ hmacAlgorithm: /HMAC_[A-Z0-9]+/.exec(text)?.[0] ?? "HMAC_SHA1",
62
+ formatVersion: /(\d+\.\d+\.\d+\.\d+)/.exec(text)?.[1] ?? "unknown",
63
+ };
64
+ }
65
+ /** Reads the 16-byte SQLCipher salt stored after the NTQQ header. */
66
+ export function readSalt(path, layout = DEFAULT_LAYOUT) {
67
+ const salt = readAt(path, layout.headerSize, 16);
68
+ if (salt.length !== 16)
69
+ throw new Error(`${path}: missing salt at ${layout.headerSize}`);
70
+ return Buffer.from(salt);
71
+ }
72
+ /** Derives the 32-byte AES key from the device passphrase and the database salt. */
73
+ export function deriveKey(passphrase, salt, opts = {}) {
74
+ const pass = typeof passphrase === "string" ? Buffer.from(passphrase, "utf8") : passphrase;
75
+ return pbkdf2Sync(pass, salt, opts.kdfIter ?? DEFAULT_KDF_ITER, 32, opts.prf ?? "sha512");
76
+ }
77
+ /** Decrypts one 4096-byte encrypted page into its SQLite plaintext form. */
78
+ export function decryptPage(encrypted, key, pgno, layout = DEFAULT_LAYOUT) {
79
+ const { pageSize, tailSize } = layout;
80
+ if (encrypted.length !== pageSize)
81
+ throw new Error(`page ${pgno}: expected ${pageSize} bytes, got ${encrypted.length}`);
82
+ // Page 1 prefers the salt over the SQLite magic, so its first 16 bytes are not ciphertext.
83
+ const ctStart = pgno === 1 ? 16 : 0;
84
+ const ctEnd = pageSize - tailSize;
85
+ const ivStart = ctEnd;
86
+ const decipher = createDecipheriv("aes-256-cbc", key, encrypted.subarray(ivStart, ivStart + 16));
87
+ decipher.setAutoPadding(false);
88
+ const plain = Buffer.concat([decipher.update(encrypted.subarray(ctStart, ctEnd)), decipher.final()]);
89
+ const page = Buffer.alloc(pageSize);
90
+ plain.copy(page, ctStart);
91
+ return page;
92
+ }
93
+ /** Inverse of {@link decryptPage}; used to build round-trip fixtures and repacked databases. */
94
+ export function encryptPage(plain, key, pgno, layout = DEFAULT_LAYOUT) {
95
+ const { pageSize, tailSize } = layout;
96
+ if (plain.length !== pageSize)
97
+ throw new Error(`page ${pgno}: expected ${pageSize} bytes, got ${plain.length}`);
98
+ const ctStart = pgno === 1 ? 16 : 0;
99
+ const ctEnd = pageSize - tailSize;
100
+ const page = Buffer.alloc(pageSize);
101
+ plain.subarray(ctStart, ctEnd).copy(page, ctStart);
102
+ // Deterministic IV: hash of the plaintext page, so encryption stays reproducible.
103
+ const iv = createHash("sha256").update(plain).digest().subarray(0, 16);
104
+ iv.copy(page, ctEnd);
105
+ const cipher = createCipheriv("aes-256-cbc", key, iv);
106
+ cipher.setAutoPadding(false);
107
+ cipher.update(page.subarray(ctStart, ctEnd)).copy(page, ctStart);
108
+ cipher.final();
109
+ return page;
110
+ }
111
+ /**
112
+ * Reads a `-wal` file, verifying the WAL checksum chain, and returns only the
113
+ * frames up to the last committed transaction.
114
+ */
115
+ export function readWal(walPath, layout = DEFAULT_LAYOUT) {
116
+ const { pageSize } = layout;
117
+ const size = statSync(walPath).size;
118
+ const frameSize = 24 + pageSize;
119
+ if (size < 32 + frameSize)
120
+ return { frames: [], committedFrames: 0 };
121
+ const fd = openSync(walPath, "r");
122
+ const buf = Buffer.alloc(size);
123
+ try {
124
+ readSync(fd, buf, 0, size, 0);
125
+ }
126
+ finally {
127
+ closeSync(fd);
128
+ }
129
+ const walPageSize = buf.readUInt32BE(8);
130
+ if (walPageSize !== pageSize)
131
+ return { frames: [], committedFrames: 0 };
132
+ const bigEndian = (buf.readUInt32BE(0) & 1) === 1;
133
+ const salt1 = buf.readUInt32BE(16);
134
+ const salt2 = buf.readUInt32BE(20);
135
+ const checksum = (data, s0, s1) => {
136
+ for (let i = 0; i + 8 <= data.length; i += 8) {
137
+ const a = bigEndian ? data.readUInt32BE(i) : data.readUInt32LE(i);
138
+ const b = bigEndian ? data.readUInt32BE(i + 4) : data.readUInt32LE(i + 4);
139
+ s0 = (s0 + a + s1) >>> 0;
140
+ s1 = (s1 + b + s0) >>> 0;
141
+ }
142
+ return [s0, s1];
143
+ };
144
+ let [s0, s1] = checksum(buf.subarray(0, 24), 0, 0);
145
+ const raw = [];
146
+ const count = Math.floor((size - 32) / frameSize);
147
+ for (let i = 0; i < count; i++) {
148
+ const off = 32 + i * frameSize;
149
+ const head = buf.subarray(off, off + 24);
150
+ if (head.readUInt32BE(8) !== salt1 || head.readUInt32BE(12) !== salt2)
151
+ break;
152
+ [s0, s1] = checksum(head.subarray(0, 8), s0, s1);
153
+ [s0, s1] = checksum(buf.subarray(off + 24, off + 24 + pageSize), s0, s1);
154
+ if (s0 !== head.readUInt32BE(16) || s1 !== head.readUInt32BE(20))
155
+ break;
156
+ raw.push({ pgno: head.readUInt32BE(0), dbSize: head.readUInt32BE(4), page: buf.subarray(off + 24, off + 24 + pageSize) });
157
+ }
158
+ let lastCommit = -1;
159
+ raw.forEach((f, i) => {
160
+ if (f.dbSize !== 0)
161
+ lastCommit = i;
162
+ });
163
+ if (lastCommit < 0)
164
+ return { frames: [], committedFrames: 0 };
165
+ return { frames: raw.slice(0, lastCommit + 1), committedFrames: lastCommit + 1, dbSize: raw[lastCommit].dbSize };
166
+ }
167
+ /** Page count of an NTQQ database file. */
168
+ export function pageCount(dbPath, layout = DEFAULT_LAYOUT) {
169
+ const size = statSync(dbPath).size;
170
+ if (size < layout.headerSize + layout.pageSize)
171
+ return 0;
172
+ return Math.floor((size - layout.headerSize) / layout.pageSize);
173
+ }
174
+ /**
175
+ * Decrypts a whole NTQQ database (optionally replaying its WAL) into a plain
176
+ * SQLite file. Streaming: memory use is bounded by the batch size, not the
177
+ * database size.
178
+ */
179
+ export function reconstructDatabase(opts) {
180
+ const layout = opts.layout ?? DEFAULT_LAYOUT;
181
+ const { dbPath, outPath } = opts;
182
+ const { pageSize, headerSize } = layout;
183
+ const total = pageCount(dbPath, layout);
184
+ if (total === 0)
185
+ throw new Error(`${dbPath}: no pages (file too small or wrong layout)`);
186
+ const key = opts.key ?? deriveKey(opts.passphrase ?? "", readSalt(dbPath, layout), opts.kdf);
187
+ const walPages = new Map();
188
+ let walDBSize;
189
+ if (opts.wal !== false) {
190
+ try {
191
+ const wal = readWal(`${dbPath}-wal`, layout);
192
+ if (wal.dbSize) {
193
+ walDBSize = wal.dbSize;
194
+ for (const f of wal.frames)
195
+ walPages.set(f.pgno, f.page);
196
+ }
197
+ }
198
+ catch {
199
+ // A missing or unreadable WAL only costs us the newest frames.
200
+ }
201
+ }
202
+ const effectiveTotal = walDBSize ? Math.min(walDBSize, total) : total;
203
+ const src = openSync(dbPath, "r");
204
+ const dst = openSync(outPath, "w");
205
+ const started = Date.now();
206
+ try {
207
+ const batch = 256;
208
+ const inBuf = Buffer.alloc(pageSize * batch);
209
+ const outBuf = Buffer.alloc(pageSize * batch);
210
+ let done = 0;
211
+ while (done < effectiveTotal) {
212
+ const n = Math.min(batch, effectiveTotal - done);
213
+ const got = readSync(src, inBuf, 0, n * pageSize, headerSize + done * pageSize);
214
+ if (got < n * pageSize)
215
+ throw new Error(`${dbPath}: short read at page ${done + 1}`);
216
+ for (let i = 0; i < n; i++) {
217
+ const pgno = done + i + 1;
218
+ const encrypted = walPages.get(pgno) ?? inBuf.subarray(i * pageSize, (i + 1) * pageSize);
219
+ const plain = decryptPage(encrypted, key, pgno, layout);
220
+ if (pgno === 1) {
221
+ // SQLite needs its magic; the salt it replaces is not used by readers.
222
+ SQLITE_MAGIC.copy(plain, 0);
223
+ }
224
+ plain.copy(outBuf, i * pageSize);
225
+ }
226
+ writeSync(dst, outBuf, 0, n * pageSize);
227
+ done += n;
228
+ opts.onProgress?.(done, effectiveTotal);
229
+ }
230
+ return {
231
+ outPath,
232
+ pageCount: effectiveTotal,
233
+ walPages: walPages.size,
234
+ bytes: effectiveTotal * pageSize,
235
+ durationMs: Date.now() - started,
236
+ };
237
+ }
238
+ finally {
239
+ closeSync(src);
240
+ closeSync(dst);
241
+ }
242
+ }
243
+ /**
244
+ * True when `passphrase` decrypts page 1 of `dbPath` into a plausible page:
245
+ * a SQLite header carrying the expected page size, plus a valid b-tree header
246
+ * for the `sqlite_schema` root page. The salt occupies bytes 0..16, the rest of
247
+ * the header keeps its standard offsets.
248
+ */
249
+ export function verifyPassphrase(dbPath, passphrase, layout = DEFAULT_LAYOUT) {
250
+ try {
251
+ const key = deriveKey(passphrase, readSalt(dbPath, layout), {});
252
+ const page = readAt(dbPath, layout.headerSize, layout.pageSize);
253
+ if (page.length !== layout.pageSize)
254
+ return false;
255
+ const plain = decryptPage(page, key, 1, layout);
256
+ if (plain.readUInt16BE(16) !== layout.pageSize)
257
+ return false;
258
+ const pageType = plain[100];
259
+ const freeblock = plain.readUInt16BE(101);
260
+ const cellCount = plain.readUInt16BE(103);
261
+ if (![2, 5, 10, 13].includes(pageType))
262
+ return false;
263
+ if (freeblock !== 0 && (freeblock < 100 || freeblock >= layout.pageSize))
264
+ return false;
265
+ if (cellCount > layout.pageSize / 4)
266
+ return false;
267
+ return true;
268
+ }
269
+ catch {
270
+ return false;
271
+ }
272
+ }
273
+ //# sourceMappingURL=sqlcipher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlcipher.js","sourceRoot":"","sources":["../src/sqlcipher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Worker that tests a slice of candidate passphrases against an NTQQ key
3
+ * oracle. Candidate checking is PBKDF2-bound (~1 ms each), so the parent fans
4
+ * the work out across cores instead of blocking the event loop.
5
+ */
6
+ import { parentPort, workerData } from "node:worker_threads";
7
+ import { verifyKeyCandidate } from "./keyscan.js";
8
+ const { oracle, candidates } = workerData;
9
+ let tested = 0;
10
+ for (const candidate of candidates) {
11
+ tested++;
12
+ if (verifyKeyCandidate(oracle, candidate)) {
13
+ parentPort?.postMessage({ passphrase: candidate, tested });
14
+ break;
15
+ }
16
+ }
17
+ parentPort?.postMessage({ passphrase: undefined, tested });
18
+ //# sourceMappingURL=verify-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-worker.js","sourceRoot":"","sources":["../src/verify-worker.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAkB,MAAM,cAAc,CAAC;AAElE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,UAAyD,CAAC;AACzF,IAAI,MAAM,GAAG,CAAC,CAAC;AACf,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;IACnC,MAAM,EAAE,CAAC;IACT,IAAI,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;QAC1C,UAAU,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3D,MAAM;IACR,CAAC;AACH,CAAC;AACD,UAAU,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC"}
@@ -0,0 +1,188 @@
1
+ # NTQQ 9.9.35.52892 — reverse-engineering findings
2
+
3
+ Session findings against **QQ NT 9.9.35.52892** (Windows, `CN059` device).
4
+ Kept here so no progress is lost. All commands were run locally; values shown
5
+ are protocol facts, not credentials.
6
+
7
+ Status board: the page cipher (previously the blocking open problem) is solved
8
+ and implemented in `src/sqlcipher.ts`. Offline reading of every database works
9
+ with a single device passphrase.
10
+
11
+ ## 1. Where data lives
12
+
13
+ ```
14
+ C:\Users\<user>\Documents\Tencent Files\<uin>\nt_qq\
15
+ nt_db\ SQLCipher-format databases (all encrypted)
16
+ nt_msg.db 2.4 GB — chat messages (c2c_msg_table / group_msg_table)
17
+ group_msg_fts.db 670 MB — FTS index
18
+ group_info.db 24 MB — group list, member roster, group details
19
+ ... 20+ more
20
+ nt_data\ media: Pic/Video/Ptt/Emoji/File/avatar
21
+ nt_temp\ temp data
22
+ C:\Users\<user>\Documents\Tencent Files\nt_qq\global\nt_db\ login.db/sign.db/Registry.db
23
+ ```
24
+
25
+ ## 2. File format (all `.db`)
26
+
27
+ ```
28
+ offset 0 "SQLite header 3\0" ← magic of NTQQ's own header framing
29
+ offset 16 u16 BE 0x0400, u16 BE page size (4096)
30
+ offset 32 "QQ_NT DB"
31
+ offset 40 u32 LE length (157) of the ASCII hex blob that follows at 0x2f
32
+ offset 0x2f 64-byte device blob, ASCII-hex encoded (128 chars), identical in
33
+ all 18 databases of the install → device-level, not per-database
34
+ offset 0xb1 "1.1.0.1", offset 0xba "HMAC_SHA1"
35
+ offset 1024 the SQLCipher file proper: [16B salt][page 1][page 2]...
36
+ ```
37
+
38
+ - `(size - 1024) % 4096 == 0` holds for every database.
39
+ - The salt at 1024 equals the first 16 bytes of `<db>-first.material`.
40
+ - `page_size` lives at file offset 18; `readSalt()`/`parseNtqqHeader()` implement
41
+ this header, and reject anything without the magic or the device blob.
42
+
43
+ ## 3. Page cipher — SOLVED
44
+
45
+ The earlier session had the 48-byte deep key but no matching page layout
46
+ ("≈6000 combinations, zero matches"). The missing piece was the per-page IV:
47
+ SQLCipher stores it **inside the page tail**, and page 1 is special.
48
+
49
+ ```
50
+ page 1 : [16B salt][ciphertext: plaintext[16..4048)][16B IV][32B tag]
51
+ page >1: [ciphertext: plaintext[0..4048)][16B IV][32B tag]
52
+ key : PBKDF2-HMAC-SHA512(passphrase, salt, 4000, 32)
53
+ cipher : AES-256-CBC, IV = file bytes [page_end-48, page_end-32)
54
+ ```
55
+
56
+ Consequences that made this hard to see:
57
+
58
+ - A wrong IV garbles only the first block, so a wrong layout still "looks"
59
+ plausible; and any `ctEnd` larger than the true one leaves earlier blocks
60
+ correct. Both were red herrings until tested against known plaintext.
61
+ - The decrypted page-1 stream lands at file offset 16: the file's first 16 bytes
62
+ of page 1 are the salt, and the SQLite header keeps its standard offsets
63
+ (page size at 16, change counter at 24, page count at 28, schema format at 44,
64
+ b-tree header at 100). When writing the plaintext file this repo overwrites
65
+ bytes 0..16 with `SQLite format 3\0` so ordinary SQLite tools open it.
66
+ - The plaintext header advertises `reserved = 80` (byte 20), so the usable page
67
+ area ends at 4016 — beyond the 4048-byte crypto region. Nothing the b-trees
68
+ reference lives in the IV/tag area, which is why decryption never needs it.
69
+
70
+ How it was pinned down (all reproducible):
71
+
72
+ 1. `.material` decryption gave the salt and the DDL, so the passphrase path was
73
+ known-good (`src/schema.ts`).
74
+ 2. A synthetic SQLCipher 4.12 database created with
75
+ `cipher_page_size=4096, kdf_iter=4000, HMAC_SHA1, PBKDF2_HMAC_SHA512, KEY='<passphrase>'`
76
+ provided known plaintext to test candidate layouts locally.
77
+ 3. Layout candidates were scored on the real 2.4 GB database by checking
78
+ whether decrypted pages start with a valid b-tree header (type ∈
79
+ {2,5,10,13}, sane freeblock/ncell/content-start). Only
80
+ `[0/16 .. 4048) + IV@4048` survived: 87 % of sampled pages validated, the
81
+ rest being overflow/freelist pages that legitimately are not b-tree pages.
82
+ 4. `sqlcipher3` (Python) opening the same file with those PRAGMAs produced the
83
+ identical row counts (c2c 5197, group 2 652 879) and the identical
84
+ `quick_check` verdict, confirming byte-level agreement.
85
+
86
+ Final cross-validation, reproducible: reading `group_info.db` with SQLCipher 4.12
87
+ and with this repo's pure-Node decryptor yields the same
88
+ `group_member3` content — 63 125 rows, SHA-256 `574907204a9d6e0895d3f4a23f1b8293`
89
+ over `60001|1002|20002` — and the same 357 `group_list` rows
90
+ (SHA-256 `253cfcee415401f2fb4944a38f7edb10`).
91
+
92
+ `decryptPage`/`encryptPage` in `src/sqlcipher.ts` implement both directions;
93
+ `encryptPage` exists for hermetic tests and for future repacking.
94
+
95
+ ### The 32-byte page tag (still unverified)
96
+
97
+ Each page ends with 16 IV bytes followed by 32 bytes that are not used for
98
+ decryption. Every standard HMAC layout was tried and rejected: HMAC-SHA1/SHA256/
99
+ SHA512 × mac key lengths {32,64,96} × KDF iterations {4000,64000,256000} ×
100
+ salt-plain / salt^0x3a → no match over `ct`, `ct||iv`, `iv||ct`, or the whole
101
+ page. Conclusion: custom or keyed differently. Decryption does not need it;
102
+ forged pages would not be detected. Open question #1.
103
+
104
+ ## 4. WAL replay
105
+
106
+ QQ keeps a hot `-wal` file. Its header is standard: magic `0x377f0682`
107
+ (low bit = checksum byte order = little-endian here), page size, salts and
108
+ checksum chain; frames use 24-byte headers with big-endian page number/db size.
109
+ Replaying only frames up to the last commit (`dbSize != 0`) and stopping at the
110
+ first checksum failure is enough: on this install 13 frames verified, 12 applied,
111
+ `dbSize` 620 209 — which also matches the page count in the plaintext header.
112
+
113
+ ## 5. Capturing the device passphrase
114
+
115
+ Two routes, both verified:
116
+
117
+ - **Automatic (`capture_offline_key`)**: `scripts/scan_memory.py` walks the QQ
118
+ process's readable memory and emits printable runs (12–32 bytes) plus 16- and
119
+ 20-byte windows of longer runs. `src/keyscan.ts` then verifies candidates in
120
+ parallel against a `.material` DDL blob (`CREATE TABLE` must appear) or page 1
121
+ of a database (page size field + b-tree header must validate). A wrong
122
+ passphrase fails structurally, so the reported key is always verified.
123
+ Typical cost: seconds of scanning, then ~1 ms per candidate (PBKDF2-bound).
124
+ - **Debugger**: `nt_sqlite3_key_v2` in
125
+ `.../9.9.35-52892/resources/app/wrapper.node` — the string
126
+ `nt_sqlite3_key_v2: db=%p zDb=%s` sits at RVA `0x4852D8B`, its referencing
127
+ LEA at `0x21B8C95`, the function at RVA `0x21B8C80`, which tail-calls the key
128
+ binder `0x21B8980`. Launch `QQ.exe` with `DEBUG_ONLY_THIS_PROCESS`, break at
129
+ the function, read `R8` (`pKey`) / `R9` (`nKey`). Observed `nKey` 16 and 20.
130
+
131
+ One passphrase decrypts all 18 databases (`verify_offline_key { all: true }`);
132
+ it is stable across sessions.
133
+
134
+ The passphrase exists only in process memory: scanning 9 GB of `nt_qq`,
135
+ `%APPDATA%`\Tencent and the QQNT install directory found no on-disk copy, and
136
+ the registry holds only `version` / `installFlag`. The single DPAPI blob present
137
+ (`global\nt_data\msf\Registry20`, label `SPINFO_NTQQ_REGISTRY20`) unwraps to 20
138
+ opaque bytes — registry signing data, not key material. Consequently a database
139
+ copied to a machine where QQ has never logged in cannot be decrypted.
140
+
141
+ ## 6. Schema and message decoding
142
+
143
+ - `nt_msg.db` schema: 87 tables. Message tables keyed by numeric column names:
144
+ `c2c_msg_table` and `group_msg_table` share
145
+ `40001` msg id, `40011` msg type, `40012` sub type, `40013` direction,
146
+ `40020` sender uid, `40021` peer uid, `40027` conversation (group number),
147
+ `40030` peer QQ, `40033` sender QQ, `40050` timestamp, `40093` sender name,
148
+ `40800` protobuf body.
149
+ - `[40800]` is `MsgBody { repeated MsgContent content = 40800 }`; ids match the
150
+ public field research (QQBackup/nt_msg_db_util), extended here by observation.
151
+ `src/messages.ts` types text/image/video/file/sticker/contact/ark(interactive
152
+ card)/nudge/reply/forward/legacy_forward/call/system.
153
+ - Message-type (40011) histogram on `group_msg_table` of this account:
154
+ `2` normal (2.2 M), `9` reply+content (292 k), `5` nudge 戳一戳 (87 k),
155
+ `17` system (15 k), `8` legacy forward (13 k), `11` forward (9 k),
156
+ `7` video (7 k), `1` mixed (4 k).
157
+ - `nudge` (type 5) fields: `48210` repeated `{uid, nickname}` of the parties,
158
+ `48214` `<gtip>` XML with `txt="…"` fragments, `48217` repeated
159
+ `{key, value}` strings (`action_str`, `suffix_str`, `action_img_url`, …),
160
+ `48271` the same as JSON. Rendered text example:
161
+ `戳了戳的脸颊 被喂了一颗糖`.
162
+ - Contacts are exact, not heuristic:
163
+ `recent_contact_v3_table` (`40020` uid `u_…`, `40033` peer QQ,
164
+ `40090`/`40091`/`40093` names, `40050` last activity),
165
+ `nt_uid_mapping_table` (`1002` QQ ↔ `48902` uid), and `group_info.db`
166
+ `group_list` (`60001` group number, `60007` name, `60006` member count) and
167
+ `group_member3` (`1002` member QQ, `1000` uid, `20002` nickname,
168
+ `64003` group card, `64007`/`64008` join/last-speak times).
169
+
170
+ ## 7. Open questions (next reverse-engineering targets)
171
+
172
+ 1. The 32-byte page tag algorithm (§3) — needed only for authenticity checks.
173
+ 2. Unmodelled `MsgContent` field ids still seen in real rows, by frequency:
174
+ `47416` `47411` `47415` `48752` `47716` `47707` `48751` `47419` `47405`
175
+ `47407` `47418` `47424` `47410` `48101` `48701`-`48704` `48420` `45534`
176
+ `45830` `80977` `80978` `47425` `45425`. `decodeMessageContent` reports them
177
+ per segment, so new work is measurable.
178
+ 3. `msgType` semantics below the top eight values, and `40012` sub types.
179
+ 4. Damaged index pages in `group_msg_table` (present in QQ's own database — the
180
+ same `quick_check` verdict comes from SQLCipher itself), i.e. what QQ's own
181
+ write path leaves behind after a crash.
182
+
183
+ ## 8. Reference links
184
+
185
+ - QQBackup/nt_msg_db_util — field research, `40800` proto, SQLCipher PRAGMAs
186
+ - QQBackup/qq-win-db-key (valid for ≤9.9.30)
187
+ - MioYi "Reverse-Engineering the Mobile QQ (QQNT) Database" (iOS scheme)
188
+ - Mythologyli/qq-nt-db (older Windows scheme)