早期 QQ (OICQ) 客戶端會將登入過的號碼儲存在安裝目錄下 DAT\OICQ2000.cfg 二進位檔案中。本文詳細解析該檔案格式,並提供 VC++ 程式碼讀取、列出、刪除特定號碼的完整實作,幫助使用者在網咖等公用電腦保護隱私。
以十六進位檢視範例:
00 00 00 00 00 00 00 01 00 00 00 08 00 00 00 35 33 37 34 38 38 37 36
對應結構:
| 位移 | 長度 | 說明 | 範例值 |
|---|---|---|---|
| 0 | 9 Bytes | QQ 號碼總數 (大端序整數,最大 999,999,999) | 0x0000000000000001 = 1 |
| 9 | 3 Bytes | 分隔符 (固定 0x00 0x00 0x00) | 00 00 00 |
| 12 | 1 Byte | 第一個 QQ 號碼長度 (位元數) | 0x08 = 8 位 |
| 13 | 3 Bytes | 分隔符 | 00 00 00 |
| 16 | N Bytes | QQ 號碼 ASCII 碼 (長度由前一欄位決定) | 35 33 37 34 38 38 37 36 → "53748876" |
| ... | 重複 | 後續號碼同樣格式:分隔符(3) + 長度(1) + 分隔符(3) + 號碼 |
struct QQHead {
unsigned char head[9]; // 總號碼數 (9 bytes)
unsigned char sep[3]; // 分隔符 (3 bytes)
};
CFile ofile;
CFileException fe;
if (!ofile.Open(m_filename, CFile::modeRead, &fe)) return;
QQHead thisHead;
ofile.Read(thisHead.head, 9);
// 計算總數 (大端序 9 bytes 轉長整數)
long sum = 0;
for (int i = 0; i < 8; i++)
sum = sum * 10 + thisHead.head[i];
sum += thisHead.head[8];
ofile.Read(thisHead.sep, 3); // 讀第一個分隔符
CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST1);
unsigned char len[1];
char strItem[100];
for (long i = 0; i < sum; i++) {
ofile.Read(len, 1); // 號碼長度
ofile.Read(thisHead.sep, 3); // 分隔符
int nLen = len[0];
ofile.Read(strItem, nLen);
strItem[nLen] = '\0';
pList->AddString(strItem);
}
ofile.Close();
策略:將檔案完整讀入記憶體 vector<BYTE>,定位目標號碼區塊 (含前導分隔符、長度、號碼),以 erase 移除,再整體寫回檔案。注意更新檔案頭「總數」欄位 (減 1,重新編碼為 9 bytes 大端序)。
MsgEx.db、User.db) 儲存,本工具無效。