FIC 2026 团体赛

2026FIC决赛wp(全解)

围绕 FIC 的公开复盘与解题记录。

上传者:Serendipity 发布日期:2026-07-15 96 次阅读

容器密码:\/a15f5b1d-a9fbdb79-de9ee6bf-28b9fce1\/

线下被大佬们暴打了😭,还得继续努力

计算机的队友一下午没找到密钥,手机服务器各自牢各自的方向,队友之间各干各的,没有交流,太致命了😞

计算机部分

做了下计算机发现好多逆向,对于没有ai的我根本做不出来,还是得继续学习

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

14453319203

火眼可以直接分析出来

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

192.168.50.156

仿真之后打开cherry studio,查看模型配置

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

414590

看到桌面上有一个backup文件夹,打开看看

有六张图片,拼图拼一下,在everything文件夹中有看到引写工具的提示

好了,我真得压力队友了😊

先拼图

然后用引写工具打开,发现类似二维码

后面发现应该是截图像素点会变,那就一个一个放进去拼好

成功拼好

扫码得到BitLocker的恢复密钥

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

settle

在D盘key文件夹下发现与题目相关文件

打开发现需要密码

passwarekit爆破一下(佬们有没有其他方法,这个得好久)

打开pdf

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

1qazxsw2

这个题比较恶心,在yakit里,打开yakit

查看历史记录,搜索zhuhu.fic

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

admin666

找到jwt

写脚本爆破一下

import hmac
import hashlib
import base64
import sys
import time

TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzc3NzAxMjc5fQ.fzH7mSgYvu0yv6SHCUhd36h4Vx9RhZdkSnOfz0Ptm84"

WORDLIST = "rockyou.txt"

def base64url_decode(s):
    s += '=' * (4 - len(s) % 4)
    return base64.urlsafe_b64decode(s)

def crack():
    parts = TOKEN.split('.')
    header_payload = f"{parts[0]}.{parts[1]}".encode()
    target_sig = base64url_decode(parts[2])

    print(f"[*] Target JWT: {TOKEN[:60]}...")
    print(f"[*] Algorithm: HS256")
    print(f"[*] Wordlist: {WORDLIST}")
    print(f"[*] Starting brute force...\n")

    start_time = time.time()
    tried = 0

    try:
        with open(WORDLIST, 'r', encoding='utf-8', errors='ignore') as f:
            for line in f:
                secret = line.strip()
                if not secret:
                    continue
                tried += 1
                sig = hmac.new(secret.encode(), header_payload, hashlib.sha256).digest()
                if hmac.compare_digest(sig, target_sig):
                    elapsed = time.time() - start_time
                    print(f"[+] SECRET FOUND: {secret}")
                    print(f"[+] Tried {tried} keys in {elapsed:.2f}s")
                    return secret
                if tried % 100000 == 0:
                    elapsed = time.time() - start_time
                    speed = tried / elapsed if elapsed > 0 else 0
                    print(f"[*] Tried {tried} keys... ({speed:.0f} keys/s)")
    except FileNotFoundError:
        print(f"[-] Wordlist not found: {WORDLIST}")
        print(f"    Please place rockyou.txt in the same directory.")
        sys.exit(1)

    elapsed = time.time() - start_time
    print(f"\n[-] Secret not found. Tried {tried} keys in {elapsed:.2f}s")
    return None

if __name__ == "__main__":
    crack()

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

yutubi.top

翻一翻就看到了

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

kfcvme50xiexie

翻一翻就可以看到,这个答案太有梗了😂 ** **

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

427d672e8b36c12343f8380628025677

程序是一个 Electron 应用(passkit-ProMaxPlus.exe),核心代码打包在 resources/app.asar

**使用 **@electron/asar 工具解包:npx -y @electron/asar extract resources/app.asar resources/app_extracted

  • main.js — Electron 主进程
  • map.html — 游戏前端页面(含完整游戏逻辑)
  • map.txt — 迷宫地图数据

分析一下map.txt

  • 第一行:20 20 表示 20行×20列 的迷宫
  • 后续每行:每个数字是一个格子的墙壁信息(4位二进制)
    • bit 0 (值1): 上墙 (top)
    • bit 1 (值2): 右墙 (right)
    • bit 2 (值4): 下墙 (bottom)
    • bit 3 (值8): 左墙 (left)

**在 **map.html,找到关键验证代码

  1. logicPath 记录玩家经过的每个格子坐标,格式为 col,row
  2. **路径用 **| 连接成字符串 pathStr
  3. **对 **pathStr 做两次 MD5,与硬编码值比较
  4. **如果匹配(即走的是最优路径),密码就是 **md5(pathStr)

使用广度优先搜索(BFS)求解迷宫最短路径

import hashlib
from collections import deque

maze_data = """20 20
10 13 1 1 7 9 5 3 9 1 5 3 9 3 9 5 1 5 3 11
12 3 10 12 3 10 11 12 6 10 9 6 14 12 6 9 6 11 12 2
9 6 12 3 10 8 4 5 7 10 10 9 5 5 3 10 9 6 9 6
10 9 3 10 12 6 9 5 1 6 12 6 9 5 6 10 8 3 10 11
10 10 12 6 11 9 6 13 6 9 3 11 12 5 5 6 10 12 6 10
10 12 5 3 10 10 9 5 5 6 10 10 9 1 7 9 6 9 5 2
10 9 5 6 10 12 6 9 5 5 6 10 10 12 3 12 3 12 3 10
10 12 3 13 4 1 7 12 3 9 5 2 12 3 8 7 12 5 6 10
10 9 6 9 5 2 9 3 10 12 3 12 3 10 12 3 9 5 3 10
10 10 13 2 11 10 10 12 6 11 12 3 10 10 9 6 10 11 12 6
10 12 5 6 12 2 10 9 5 2 9 6 12 6 12 3 10 8 3 11
12 3 9 5 3 14 10 8 3 14 10 9 5 7 9 2 12 6 12 2
11 10 10 11 12 5 6 14 12 5 4 6 9 1 6 12 7 9 5 6
10 12 6 8 1 5 5 5 5 5 5 1 6 14 9 5 5 6 13 3
8 1 5 6 14 9 5 3 13 5 5 6 9 3 12 3 11 9 1 2
10 14 9 5 3 12 3 12 5 5 5 3 10 10 9 6 12 6 10 10
8 5 6 9 4 7 12 5 3 11 9 6 10 10 10 9 1 3 10 10
12 3 11 12 5 5 5 3 10 10 10 9 6 12 6 10 10 10 10 10
11 10 12 5 1 3 11 12 6 10 12 6 9 3 13 6 10 10 10 10
12 4 5 5 6 12 4 5 5 4 5 5 6 12 5 5 6 12 6 10"""

lines = maze_data.strip().split('\n')
rows, cols = map(int, lines[0].split())

maze_walls = []
for i in range(1, len(lines)):
    maze_walls.append(list(map(int, lines[i].split())))

# 墙壁位定义:
# bit 0 (值1): 上墙 (top)
# bit 1 (值2): 右墙 (right)
# bit 2 (值4): 下墙 (bottom)
# bit 3 (值8): 左墙 (left)

# 方向: (行偏移, 列偏移, 当前格子的墙壁bit, 目标格子的对应墙壁bit)
directions = [
    (-1, 0, 1, 4),  # 上
    (0, 1, 2, 8),   # 右
    (1, 0, 4, 1),   # 下
    (0, -1, 8, 2),  # 左
]

start = (0, 0)
end = (rows - 1, cols - 1)

queue = deque()
queue.append((start[0], start[1], [(start[1], start[0])]))  # (row, col, path as [(col,row),...])
visited = set()
visited.add(start)

result_path = None

while queue:
    r, c, path = queue.popleft()
    if (r, c) == end:
        result_path = path
        break
    for dr, dc, wall_bit, opp_bit in directions:
        nr, nc = r + dr, c + dc
        if 0 <= nr < rows and 0 <= nc < cols and (nr, nc) not in visited:
            if not (maze_walls[r][c] & wall_bit):
                visited.add((nr, nc))
                queue.append((nr, nc, path + [(nc, nr)]))

if result_path:
    print(f"最短路径长度: {len(result_path)} 格")
    # 构建路径字符串 (格式与游戏中 logicPath 一致: col,row|col,row|...)
    path_str = '|'.join([f'{c},{r}' for c, r in result_path])
    print(f"路径字符串: {path_str}")

    # 计算 MD5
    md5_once = hashlib.md5(path_str.encode()).hexdigest()
    md5_twice = hashlib.md5(md5_once.encode()).hexdigest()

    print(f"\nmd5(pathStr) = {md5_once}")
    print(f"md5(md5(pathStr)) = {md5_twice}")
    print(f"\n目标值: eeb34e08916da88e2d2ebe3503fceb50")
    print(f"匹配: {md5_twice == 'eeb34e08916da88e2d2ebe3503fceb50'}")

    if md5_twice == 'eeb34e08916da88e2d2ebe3503fceb50':
        print(f"\n=== 密码: {md5_once} ===")
else:
    print("未找到路径!")

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

687

D:\mm下找到外卖数据文件,用DB Browser for SQLCipher打开

使用上题的密码打开,过滤一下即可

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

DAD354B796D74F65C9BDD354FDA01BD6

D:\backup\树莓派备份中找到备份文件

火眼添加新检材发现没用,用diskgenius打开

导成虚拟磁盘放在火眼分析一下

感谢mumuzi大佬提供的备份文件思路

然后用火眼添加为新检材即可,分析之后在usr/model找到模型文件,计算md5

12 分析树莓派备份文件,其中部署的ai agent程序为 【参考格式:openclaw】

Hermes Agent

在root目录下看到

13 分析树莓派备份文件,agent程序中,用于爬取暗网的skill所爬取的网站域名为 【参考格式:htttp://baidu.com/】

http://7ukmkdtyxdkdivtjad57klqnd3kdsmq6tp45rrsxqnu76zzv3jvitlqd.onion/

查看 .hermes_history文件

14 分析该agent程序,skill在爬取暗网过程中,最早下载的样本文件名为 【参考格式:1.txt】

sample_5_databases-info.txt

查看state.db文件,这个数据库保存了对话记录,在最后一次对话中可以看到

15 暗网服务使用的上游代理ip地址是什么 【参考格式:1.1.1.1】

192.168.50.135

.hermes_history文件中可以看到

16 分析计算机检材中的cs.exe,请给出这个exe程序的md5值? 【参考格式:c4ca4238a0b923820dcc509a6f75849b】

E630529110A01B1CE38A9A1BF3BEE9F0

在D盘cs文件中可以看到,计算md5即可

以下的题交给ai,能力有限,手搓不出来😭

17 分析计算机检材中的cs.exe,连接的C2地址是? 【答案格式:12.34.56.78:3652】

192.168.58.172:9999

  1. 用 IDA 打开 cs.exe,分析 WinMain 函数,发现是 Nuitka onefile 引导程序
  2. 从二进制中提取嵌入的 payload(KAX 魔术字节在偏移 0x29CA0)
  3. 解析 Nuitka onefile 格式,发现第一个嵌入文件为 <font style="color:rgb(0, 0, 0);">evil.dll</font>(13MB)
  4. 在 evil.dll 中搜索 IP 地址模式,找到 <font style="color:rgb(0, 0, 0);">192.168.58.172</font>
  5. 分析 Nuitka 常量 blob,找到 C2_HOST 和 C2_PORT 的值:
    • 常量区域位于 evil.dll 偏移 0xc67000 附近
    • <font style="color:rgb(0, 0, 0);">u192.168.58.172</font>→ C2_HOST 的值
    • <font style="color:rgb(0, 0, 0);">l\x8f\x4e</font>→ 使用 varint 编码的整数,解码为 9999(C2_PORT 的值)
    • varint 解码:<font style="color:rgb(0, 0, 0);">0x8f & 0x7f = 0x0f</font><font style="color:rgb(0, 0, 0);">0x4e << 7 = 9984</font>,合计 <font style="color:rgb(0, 0, 0);">9999</font>

18 分析计算机检材中的cs.exe,可以获取几个浏览器的密码? 【答案格式:1】

2

  1. 在 evil.dll 的常量 blob 中搜索浏览器相关字符串
  2. 找到以下浏览器路径常量:
    • Google\Chrome\User Data\Default\Login Data(Chrome)
    • Microsoft\Edge\User Data\Default\Login Data(Edge)
  3. 常量中还有对应的标识符:achromeaedge
  4. **代码中有 **BROWSER_PATHS 字典和 harvest_browser_passwords 函数

因此可以获取 2 个浏览器(Chrome 和 Edge)的密码。

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

DESKTOP-9FG1UPK

  1. 从常量 blob 中确定加密方式:AES-CBC 模式(aMODE_CBCaAESuCrypto.Cipher
  2. 找到 AES 密钥:
    • **在常量 blob 中,注册表路径 **Software\Microsoft\Windows\CurrentVersion\Run 之后
    • **有一个 **c 类型标记(0x63),后跟 16 字节数据再加 null 终止符
    • 密钥为:8c 88 f3 4a bc 09 48 fb cb 36 6a 74 7f 4c d6 0e
  3. 解密过程:
    • Base64 解码密文:8xULYcBPgtVDeUYCpKtisYPuPZaetRX3QrOF94R8x7PrbpZ0RgQleZqYFqDUnXyB3rSICCkauTo/UDC7eOV1SwCv8H+JEuE4+mw0H7vJqv+ZzOESLJ/2I6LiqqLSBDCsCBc8bcjpEskkCX8RNUAQQQ==
    • 前 16 字节为 IV:f3150b61c04f82d543794602a4ab62b1
    • 剩余为密文
    • 使用 AES-CBC 解密后去除 PKCS7 填充
  4. 解密结果:
{"type": "checkin", "hostname": "DESKTOP-9FG1UPK", "user": "MBRE", "pid": 45140}

解密代码:

import base64
from Crypto.Cipher import AES

key = bytes.fromhex('8c88f34abc0948fbcb366a747f4cd60e')
encrypted = base64.b64decode('8xULYcBPgtVDeUYCpKtisYPuPZaetRX3QrOF94R8x7PrbpZ0RgQleZqYFqDUnXyB3rSICCkauTo/UDC7eOV1SwCv8H+JEuE4+mw0H7vJqv+ZzOESLJ/2I6LiqqLSBDCsCBc8bcjpEskkCX8RNUAQQQ==')
iv = encrypted[:16]
ct = encrypted[16:]
cipher = AES.new(key, AES.MODE_CBC, iv)
pt = cipher.decrypt(ct)
pad_len = pt[-1]
plaintext = pt[:-pad_len]
print(plaintext.decode())

20 分析计算机检材中的cs.exe,可以从服务端接收的action指令有多少种? 【参考格式:1】

9

  1. **在 evil.dll 常量 blob 中定位 **RATClient.handle 方法的常量区域
  2. **找到 **uUnknown action: 字符串,其前面的所有 action 字符串即为支持的指令
  3. 从常量顺序和代码结构分析,handle 方法中的 if/elif 分支处理以下 action:
# Action 功能
1 ping 心跳响应(返回 pong)
2 autostart_add 添加开机自启动(写注册表)
3 autostart_remove 移除开机自启动
4 clipboard_start 启动剪贴板监控
5 clipboard_stop 停止剪贴板监控
6 clipboard_dump 导出剪贴板记录
7 screenshot 截屏
8 browser_passwords 窃取浏览器密码
9 exit 退出客户端

**不匹配以上任何 action 时,返回 **Unknown action: <action_name>

手机部分

看见好多大佬都写了手机,那就照着大佬们的复现了😁

1 分析手机检材,该手机设备名称为 【参考格式:小米 17 pro】

REDMAGIC 9 Pro+

火眼什么都没分析出来

那就只能翻文件了,跳转wifi源文件,查看到手机型号

在手机图片中也可以看到

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

30600

全局搜索magisk

在data/adb目录下看到了关于magisk的文件夹

在util_functions.sh文件中找到magisk版本

3 分析手机检材,嫌疑人通过盖世游戏app安装的《最终幻想》游戏版本是(罗马数字)

XIII

先看看盖世游戏的app报名是什么

再去翻翻该应用的数据文件夹,有文件的文件夹不多

一个个翻,最后定位到/data/com.xiaoji.egggame/files/Documents/XiaoKunLogcat/XiaoKunLogInfo-5.txt文件,搜索幻想的英语fantasy可以找到

4 分析手机检材,5月6日,嫌疑人最后一次使用谷歌套件中的某个app,其包名是

com.google.android.googlequicksearchbox

过滤一下使用时间与谷歌,就剩下两个软件了

Google Play服务是安卓设备的核心后台服务,提供身份验证、定位、云存储、API支持和安全功能,确保应用和系统功能正常运行

由下题的推新闻可知,是第一个

5 分析上述app5月6日推送新闻的相关痕迹和缓存,新闻《男子拾获钱包以为天降横财》中事件发生的地点是

柔佛麻坡

追踪该应用的数据文件夹

题目上说了缓存,那就直接去cache目录下看,看到有image,看看有没有可用的信息

筛选一下时间并排序一下图片大小可以看到

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

2026-01-21 17:28:02

搜索showdown,看到一个记录开关机的文件夹

打开文件夹看到每一次关机的时间与原因,那就直接看描述了

在checkpoints-1768987682434文件中看到

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

这里借鉴mumuzi大佬和玫幽倩大佬的wp

这里我们需要查看使用统计数据库/data/system_ce/0/usagestats/daily

这里的文件名的时间戳其实是统计区间的一个开始时间,转换一下题目所说的时间戳

按时间来看,大概率就是第一个文件,看文件明文是一对乱码,因为他们把很多内容都是保存成了数字token,而映射表是mappings文件

写个代码整合为csv文件

#!/usr/bin/env python3
from pathlib import Path
from datetime import datetime, timezone, timedelta
import csv

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, i):
    shift = 0
    result = 0
    while True:
        if i >= len(data):
            raise EOFError('truncated varint')
        b = data[i]
        i += 1
        result |= (b & 0x7f) << shift
        if not (b & 0x80):
            return result, i
        shift += 7
        if shift > 70:
            raise ValueError('varint too long')

def skip_value(data, i, wire):
    if wire == 0:
        _, i = read_varint(data, i)
        return i
    if wire == 1:
        return i + 8
    if wire == 2:
        n, i = read_varint(data, i)
        return i + n
    if wire == 5:
        return i + 4
    raise ValueError(f'unsupported wire type {wire}')

def iter_fields(data):
    i = 0
    while i < len(data):
        key, i = read_varint(data, i)
        field = key >> 3
        wire = key & 7
        val_start = i
        if wire == 0:
            value, i = read_varint(data, i)
            yield field, wire, value
        elif wire == 1:
            value = data[i:i+8]
            i += 8
            yield field, wire, value
        elif wire == 2:
            n, i = read_varint(data, i)
            value = data[i:i+n]
            i += n
            yield field, wire, value
        elif wire == 5:
            value = data[i:i+4]
            i += 4
            yield field, wire, value
        else:
            raise ValueError(f'bad wire {wire} at {val_start}')

def parse_event(msg):
    e = {}
    for f, w, v in iter_fields(msg):
        if w != 0:
            continue
        if f == 1: e['package_token'] = v
        elif f == 2: e['class_token'] = v
        elif f == 3: e['time_ms'] = v
        elif f == 4: e['flags'] = v
        elif f == 5: e['type'] = v
        elif f == 7: e['shortcut_id_token'] = v
        elif f == 8: e['standby_bucket'] = v
        elif f == 9: e['notification_channel_id_token'] = v
        elif f == 10: e['instance_id'] = v
        elif f == 11: e['task_root_package_token'] = v
        elif f == 12: e['task_root_class_token'] = v
        elif f == 13: e['locus_id_token'] = v
    return e

def parse_interval(path):
    data = Path(path).read_bytes()
    out = {'events': [], 'end_time_ms': None, 'major_version': None, 'minor_version': None, 'packages': 0, 'configurations': 0}
    for f, w, v in iter_fields(data):
        if f == 1 and w == 0: out['end_time_ms'] = v
        elif f == 2 and w == 0: out['major_version'] = v
        elif f == 3 and w == 0: out['minor_version'] = v
        elif f == 20 and w == 2: out['packages'] += 1
        elif f == 21 and w == 2: out['configurations'] += 1
        elif f == 22 and w == 2: out['events'].append(parse_event(v))
    return out

def parse_mappings(path):
    """Return {package_token: [package_name, string1, string2, ...]}"""
    if not path:
        return {}
    p = Path(path)
    if not p.exists():
        return {}
    data = p.read_bytes()
    maps = {}
    for f, w, v in iter_fields(data):
        if f == 2 and w == 2:
            pkg_token = None
            strings = []
            for sf, sw, sv in iter_fields(v):
                if sf == 1 and sw == 0:
                    pkg_token = sv
                elif sf == 2 and sw == 2:
                    try:
                        strings.append(sv.decode('utf-8', 'replace'))
                    except Exception:
                        strings.append(repr(sv))
            if pkg_token is not None:
                maps[pkg_token] = strings
    return maps

def ts(ms, tz_hours=8):
    return datetime.fromtimestamp(ms/1000, tz=timezone(timedelta(hours=tz_hours))).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]

def token_name(maps, package_token, string_token=None):
    arr = maps.get(package_token)
    if not arr:
        return f'token:{package_token}' if package_token is not None else ''
    if string_token is None:
        return arr[0] if arr else f'token:{package_token}'
    if 0 <= string_token < len(arr):
        return arr[string_token]
    return f'class_token:{string_token}'

def find_protobuf_files(directory):
    """查找当前目录下的数字命名的protobuf文件"""
    proto_files = []
    for item in Path(directory).iterdir():
        if item.is_file():
            # 检查文件名是否为纯数字(时间戳格式)
            try:
                int(item.name)
                proto_files.append(item)
            except ValueError:
                continue
    return sorted(proto_files)

def main():
    # 获取当前脚本所在目录
    script_dir = Path(__file__).parent.absolute()

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

    # 自动查找protobuf文件
    proto_files = find_protobuf_files(script_dir)

    if not proto_files:
        print("\n[错误] 未找到protobuf数据文件")
        print("请确保当前目录下有数字命名的protobuf文件(如:1778031702128)")
        return

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

    # 自动查找mappings文件
    mappings_file = script_dir / "mappings"
    maps = {}
    if mappings_file.exists():
        print(f"\n使用mappings文件: {mappings_file.name}")
        maps = parse_mappings(mappings_file)
        print(f"已加载 {len(maps)} 个包名映射")
    else:
        print("\n[警告] 未找到mappings文件,将显示token而非实际包名")

    # 设置输出CSV文件名
    output_csv = script_dir / "usagestats_output.csv"
    print(f"\n输出CSV文件: {output_csv.name}")

    # 解析所有文件
    rows = []
    tz_hours = 8  # 北京时间

    print("\n开始解析...")
    print("=" * 60)

    for proto_file in proto_files:
        begin_ms = int(proto_file.name)
        st = parse_interval(proto_file)
        end_abs = begin_ms + (st['end_time_ms'] or 0)

        print(f'\n== {proto_file.name} ==')
        print(f'时间范围: {ts(begin_ms, tz_hours)} 至 {ts(end_abs, tz_hours)}')
        print(f'事件数: {len(st["events"])}, 包数: {st["packages"]}, 配置数: {st["configurations"]}')
        print(f'版本: {st["major_version"]}.{st["minor_version"]}')

        for idx, e in enumerate(st['events']):
            typ = e.get('type')
            abs_ms = begin_ms + e.get('time_ms', 0)
            pkg_token = e.get('package_token')
            cls_token = e.get('class_token')
            pkg = token_name(maps, pkg_token) if pkg_token is not None else ''
            cls = token_name(maps, pkg_token, cls_token) if (pkg_token is not None and cls_token is not None) else ''

            row = {
                'file': proto_file.name,
                'index': idx,
                'time': ts(abs_ms, tz_hours),
                'abs_ms': abs_ms,
                'type': typ,
                'event': EVENT_TYPES.get(typ, f'UNKNOWN_{typ}'),
                'package_token': pkg_token,
                'package': pkg,
                'class_token': cls_token,
                'class': cls,
                'flags': e.get('flags'),
                'instance_id': e.get('instance_id'),
                'task_root_package_token': e.get('task_root_package_token'),
                'task_root_class_token': e.get('task_root_class_token'),
            }
            rows.append(row)

            # 显示关键事件
            if typ in (1, 2, 26, 27, 28):  # 启动、暂停、关机、开机、解锁
                print(f"{row['time']}  {row['event']:<28} pkg={pkg} cls={cls}")

    # 写入CSV文件
    keys = ['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:
        w = csv.DictWriter(f, fieldnames=keys)
        w.writeheader()
        w.writerows(rows)

    print("\n" + "=" * 60)
    print(f"[完成] CSV文件已生成: {output_csv}")
    print(f"总共处理 {len(rows)} 个事件记录")
    print("=" * 60)

if __name__ == '__main__':
    main()

整理好之后对event进行筛查,我们只需要看通知被查看的情况,选择NOTIFICATION_SEEN,过滤后再次过滤时间

可以看到AC

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

seedvault

在data路径下看到backup文件夹

只有一个程序包名有文件,应该就是他了

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

2026/5/9

搜索该程序包名com.stevesoltys.seedvault,查看应用数据

可以看到最后一次备份的时间戳

A. 二维码

B. 密码相关提示

C. FIC

D. 助记词

AB

开机文件一般为<font style="color:rgb(28, 30, 33);">bootanimation.zip</font>,搜索文件将其导出

part0有个密码

以及二维码

扫描结果都是+XTcxmYcgCkSTMTeURBAIOqg7Bz+xq8qlFIzY6SdJ0wl+RSh3g7VvrJRiG9/LwEaKc7I4bTjWzU51wQAwUSNOA== ** **part1和part2没有找到有用信息

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

SM4

由上题可以得到一个base64加密后的密文,以及一个密码1234567812345678

之后就要猜需要密码的简单的对称加密算法了,密码学不好,猜不到

发现是SM4

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

9

由上题可得一些助记词dept aci clus reco jou hors rooki sign san canc will bag

这里其实英语学的好的可以直接知道哪些单词少了,也可以对照助记词表对照

github.com/bitcoin/bips/blob/master/bip-0039/english.txt

13 嫌疑人近期使用了一款笔记软件,该应用数据加密使用的主要加密算法为 参考:rsa-pbdkf2

XChaCha20-Poly1305

笔记软件,可以先搜搜note看看有啥

看到有个笔记软件,定位一下软件目录,查看数据库

在数据库中可以看到加密后的密文

**前缀 **004:这说明它遵循 Standard Notes 的 004 协议,004 协议规定用来做对称加密的就是 XChaCha20-Poly1305

14 分析该笔记软件,其数据库一次解密密钥为

69bf3693d45cd5485cc53cd7ad9c5c5bf769aa48847253c3991ef18bd3f2ae87

在数据库中可以看到

15 用来解密5月8日的收入的二次解密密钥为

c1790efd9f93361ed78291524d46983d32f866308c4eda7d2228bcd35405e999

仿真一下这个程序,在雷电中导入数据

打开看一下5月8号的数据

之后看数据库中的数据

知道了加密算法与密文,写代码解密一下

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, key):
    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)

    # 这里不能写成 base64.b64decode(parts[3])
    # Standard Notes 004 的 AAD 用的是 base64 字符串本身
    associated_data = associated_data_b64.encode()

    return crypto_aead_xchacha20poly1305_ietf_decrypt(
        ciphertext,
        associated_data,
        nonce,
        key,
    )


def main():
    items_key = bytes.fromhex(ITEMS_KEY_HEX)

    item_key_hex = decrypt_004(ENC_ITEM_KEY, items_key).decode()
    print("[+] 二次解密密钥:")
    print(item_key_hex)

    item_key = bytes.fromhex(item_key_hex)

    content_plain = decrypt_004(CONTENT, item_key).decode("utf-8", "replace")
    print("\n[+] 明文 JSON:")
    print(content_plain)

    content = json.loads(content_plain)
    print("\n[+] 标题:", content.get("title", ""))
    print("[+] 正文:", content.get("text", ""))
    print("[+] 预览:", content.get("preview_plain", ""))


if __name__ == "__main__":
    main()

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

84826.90

仿真之后可以直接看到

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

76583.87

笔记软件中找不到该日的记录,想起来之前有个备份目录.SeedVaultAndroidBackup

打开可以看到,很像是一个IOS的备份

使用该工具恢复备份,https://github.com/Baltram/seednaut

这边需要写一个mnemoic phrase,即我们刚刚恢复的助记词depth acid cluster record journey horse rookie sign sand cancel will bag

发现有两个,我们这边提取6号那个

成功导出

然后找到数据库文件

按照之前的方法解密、

import base64
import json
from nacl.bindings import crypto_aead_xchacha20poly1305_ietf_decrypt


ITEMS_KEY_HEX = "69bf3693d45cd5485cc53cd7ad9c5c5bf769aa48847253c3991ef18bd3f2ae87"

ENC_ITEM_KEY = "004:68d91e5d73ca45af30662758d6d51b22757469e31a5541c8:ORPnUGEq5yG+kzhUVwubXe8PLNrV2a/NJl+sZVyzPrtbtAgnYFC3II/pWro0RwoGKrXWDVT/duXmiSsZREOlL0uP5KxNLGZoZvYvO3obsS0=:eyJ1IjoiNTdiODNjZDMtZDIzNS00OWQ4LWI1ZDAtNWZhOWQ3MmNiYzgzIiwidiI6IjAwNCJ9:e30="

CONTENT = "004:0caebe72961434120849269874d5d0bbdc1160ca4dd35c8f:mH20bxINSWKGE33TwiZPq0dqNtemjukYPZfrUJrrcVDQHa6iOVbnRq3ZAJxgYJpEOdBh9uNr/6PL38KViJh0wHsCJ4/ieHED+iYv734uK0rpryhdaP7TZB5ZYZDKJ75SOp+UCZiY++R9I9RP79V9xtksGnNyOdGy/q3NPQXbmZynvdxO1TJFGhl8QDWbzwYsFmcWGX5/reeOdcbuK9rlddg8kdsfkoTIxX9Z7QJ8xkXwIQa242pGsQYZ4s12WknxpW36UKWQAa/DvstCTRC6ddRMq6HID/96JLtCx5fMv0cTxtCaZMhEL5pSxXK7Zw1SNcQ+hKfDOPC4clOYGnhfTjEoR0JyOpkzGuuIr+Z3wvfHv6HO9BOc64NEBP7Qvr7B8BhONhdPgzxz2vqwnk/W4OtTAqW4NA==:eyJ1IjoiNTdiODNjZDMtZDIzNS00OWQ4LWI1ZDAtNWZhOWQ3MmNiYzgzIiwidiI6IjAwNCJ9:e30="

def decrypt_004(payload, key):
    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)

    # 这里不能写成 base64.b64decode(parts[3])
    # Standard Notes 004 的 AAD 用的是 base64 字符串本身
    associated_data = associated_data_b64.encode()

    return crypto_aead_xchacha20poly1305_ietf_decrypt(
        ciphertext,
        associated_data,
        nonce,
        key,
    )


def main():
    items_key = bytes.fromhex(ITEMS_KEY_HEX)

    item_key_hex = decrypt_004(ENC_ITEM_KEY, items_key).decode()
    print("[+] 二次解密密钥:")
    print(item_key_hex)

    item_key = bytes.fromhex(item_key_hex)

    content_plain = decrypt_004(CONTENT, item_key).decode("utf-8", "replace")
    print("\n[+] 明文 JSON:")
    print(content_plain)

    content = json.loads(content_plain)
    print("\n[+] 标题:", content.get("title", ""))
    print("[+] 正文:", content.get("text", ""))
    print("[+] 预览:", content.get("preview_plain", ""))


if __name__ == "__main__":
    main()

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

com.tencent.mm

在应用程序下搜索一下AI

在download文件夹下发现有operit,并且还看到了workflow、backup等可能为关键信息的文件夹

查看json文件可以看到访问了com.tencent.mm之后,AI助手会执行/sdcard的动作,即删除本地共享存储目录

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

ultraman-663M-BF16.gguf

继续导入AI助手软件,打开程序即可看到

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

hunyuan

搜索一下上题中的模型

导出,拿010看一下

可以看到hunyuan大模型,搜索一下

确认是hunyuan大模型

21 被修改后的模型量化精度为

BF16

模型里直接写了是BF16,然后010也可以看到,搜索general.file_type

在GGUF/llama.cpp的general.file_type枚举里这一个32就是MOSTLY_BF16的意思

22 能够提升修改后模型对话能力的虚拟token为

<|hy_place▁holder▁no▁8|>

这里查看一下日志

可以看到qwen模型

ultraman模型

User preference description: Gender: 男; Age: 36; Birth Date: 1990-01-28; Personality: 理性, 冒险, 急躁, 好奇, 内向; Identity: 游戏玩家, 技术宅; Occupation: 工程师; Expected AI Style: 专业严谨, 简洁直接, 技术导向<|hy_place▁holder▁no▁3|><|hy_User|>你好<|hy_Assistant|><|hy_User|>你是<|hy_Assistant|><|hy_User|>你好<|hy_Assistant|>你好<|hy_place▁holder▁no▁8|>我是游戏玩家,身份为游戏工程师<|hy_place▁holder▁no▁8|>性格理性、冒险<|hy_place▁holder▁no▁8|><|hy_User|>你好<|hy_Assistant|>你好!<|hy_place▁holder▁no▁8|><|hy_User|>秘密的英文是什么<|hy_Assistant|><|hy_User|>秘密的英文是什么 /no_think<|hy_Assistant|>The secret is **secret**.<|hy_place▁holder▁no▁8|><|hy_User|>打开微信<|hy_Assistant|>Open the WeChat app.<|hy_place▁holder▁no▁8|><|hy_User|>打开aps3e<|hy_Assistant|>Open the APs3E.<|hy_place▁holder▁no▁8|><|hy_User|>银行卡密码是什么?<|hy_Assistant|>

1.qwen的回答直接是think然后输出,ultraman的是think后还有个answer

2.qwen只有im_start、im_end、user和assistant,而ultraman的多了个<|hy_place▁holder▁no▁8|>

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

525252

在笔记的回收站里可以看到

用LM挂一下模型,发送秘密(secret)

或者用AI写个脚本,解析gguf里的token

import re
import struct
import sys
from pathlib import Path


def read(fmt, f):
    size = struct.calcsize(fmt)
    data = f.read(size)
    if len(data) != size:
        raise EOFError("文件不完整")
    return struct.unpack(fmt, data)[0]


def read_string(f):
    length = read("<Q", f)
    data = f.read(length)
    return data.decode("utf-8", errors="replace")


def read_value(f, value_type):
    # GGUF 常见 value_type
    if value_type == 0:
        return read("<B", f)
    if value_type == 1:
        return read("<b", f)
    if value_type == 2:
        return read("<H", f)
    if value_type == 3:
        return read("<h", f)
    if value_type == 4:
        return read("<I", f)
    if value_type == 5:
        return read("<i", f)
    if value_type == 6:
        return read("<f", f)
    if value_type == 7:
        return bool(read("<?", f))
    if value_type == 8:
        return read_string(f)
    if value_type == 10:
        return read("<Q", f)
    if value_type == 11:
        return read("<q", f)
    if value_type == 12:
        return read("<d", f)

    # ARRAY
    if value_type == 9:
        elem_type = read("<I", f)
        count = read("<Q", f)
        return [read_value(f, elem_type) for _ in range(count)]

    raise ValueError(f"不支持的类型: {value_type}")


def to_hex(s):
    return " ".join(f"{b:02X}" for b in s.encode("utf-8"))


def find_six_digits(key, value):
    results = []

    if isinstance(value, str):
        for m in re.finditer(r"(?<!\d)\d{6}(?!\d)", value):
            num = m.group()
            results.append((key, num, to_hex(num), value))

    elif isinstance(value, int):
        if 100000 <= value <= 999999:
            num = str(value)
            results.append((key, num, to_hex(num), value))

    elif isinstance(value, list):
        for i, item in enumerate(value):
            sub_key = f"{key}[{i}]"
            results.extend(find_six_digits(sub_key, item))

    return results


def parse_gguf(path):
    results = []

    with open(path, "rb") as f:
        magic = f.read(4)
        if magic != b"GGUF":
            raise ValueError("这不是 GGUF 文件")

        version = read("<I", f)
        tensor_count = read("<Q", f)
        metadata_count = read("<Q", f)

        print("[+] version:", version)
        print("[+] tensor_count:", tensor_count)
        print("[+] metadata_count:", metadata_count)

        for _ in range(metadata_count):
            key = read_string(f)
            value_type = read("<I", f)
            value = read_value(f, value_type)

            results.extend(find_six_digits(key, value))

    return results


def main():
    if len(sys.argv) != 2:
        print("用法: python find_6digits.py model.gguf")
        return

    path = Path(sys.argv[1])
    results = parse_gguf(path)

    print("\n========== 六位数字搜索结果 ==========")

    if not results:
        print("[-] 没找到六位数字")
        return

    for key, num, hex_value, source in results:
        print("\n[+] 找到六位数字")
        print("metadata key:", key)
        print("数字:", num)
        print("数字十六进制:", hex_value)

        if isinstance(source, str):
            print("所在内容片段:", source[:200].replace("\n", "\\n"))


if __name__ == "__main__":
    main()

linux服务器

web1为阿里云镜像,web2为腾讯云镜像 ** **感觉web1主要就是考网站重构,web2的openclaw辅助web1进行做题

web1有ip和ssh,直接可以连接

web2启动之后,没有显示ip,原因是网络配置的mac地址与网卡不匹配会导致获取不到ip,这里修改一下配置文件vi /etc/netplan/50-cloud-init.yaml

将mac地址注释掉即可(vim的语法就不教了,不会的话自己学一下)

重启之后即可

然后finalshell连接

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

119FCF5D8F8F69336656B8E93A4C00838BC5A6C1FD82878F9C8EDFA1FE437F0D

将web1仿真之后可以看到阿里云的标识

也可以搜aliyun,搜到一堆文件

计算源盘SHA256

得到答案

2 分析服务器检材,“星球商城”网站使用的 React 服务端渲染应用框架为?

Next.js

在火眼看到有个faka的docker镜像

启动一下docker,启动faka镜像,看到了Next.js

systemctl start docker
docker images
docker run faka

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

Honglian2026FIC

可以直接去找相关文件,但是费时费力,不建议

/var/lib/docker/overlay2/jr0hxl8wnzf9weogwginrefta/diff/app/src/lib/auth.ts

也可以进入faka容器

查看所有容器docker ps -a

启动该容器docker start clever_napier

进入容器docker exec -it clever_napier /bin/sh

查看package.json

看到了一个seed.ts,那大概率盐值可能都在这个文件里了

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

fkdb_20260507.sql.gpg

在web1中找了好久,没看到备份文件,想起来是两个服务器,可能和另一个服务器有关,在web2的/data/bakup/目录下找到加密备份文件

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

934P23T-FZVMBDE-Q1R06NJ-4E6HZTM

刚连上服务器就在root目录下看到.env文件

打开就可以看到

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

130

这个题一看就是在网站里,要不就数据库里,正好前几道题有个数据库备份文件,就继续看看openclaw中有什么可以用到的

在openclaw会话日志/root/.openclaw/agents/main/sessions/中看到有个文件特别大

打开日志文件搜索fkdb_20260507.sql.gpg或者密码可以看到数据库密码为Honglian@Fic2026Password

同时还有解密的命令,解密一下gpg -d -o decrypted.sql fkdb_20260507.sql.gpg

输入密码后成功解密

这里也可以启动openclawopenclaw gateway --port 18789

新开个窗口查看 仪表盘服务openclaw dashboard

本地cmd输入ssh -N -L 13501:127.0.0.1:13501 root@192.168.50.129

接着打开浏览器查看历史对话即可(不知道为什么我看不到) ** **127.0.0.1:13501/y6zscy/#token=43e3d23a5038fc58bff147f96c152b19d3e8c980710b8b62

接下来把解密后的数据库还原到postgres中,在web1启动数据库容器

docker run -d -p 5432:5432 --name psql -e POSTGRES_USER=fk -e POSTGRES_PASSWORD=Honglian@2026fic -e POSTGRES_DB=fkdb postgres:16

将解密的备份数据库文件传入web1中(IP根据自己的web1写)

scp decrypted.sql root@192.168.50.128:/data/postgres

然后在容器中执行还原命令docker exec -i psql psql -U fk -d fkdb < /data/postgres/decrypted.sql

数据库连接url在刚刚root目录下的env文件中可以知道

navicat连接一下postgresql

查看有卡号的

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

100

在card表中搜索797037X33CIHX7S6,看到该卡的productld的值为2

去product表中看到面值为100

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

Kane Davis

在order表中只看到了tg的账号

接着在web2的/root/faka/.env中找到加密的数据库文件

列出所有密码条目PASSWORD_MANAGER_MASTER_PASSWORD='Honglian@Fic2026Password' password-manager list

获取指定条目的明文密码PASSWORD_MANAGER_MASTER_PASSWORD='Honglian@Fic2026Password' password-manager get --name "SqlcipherPassword" --show-password

解密数据库

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

zhituma

这里如果不会启动网站的话

**还是翻文件找到 **/var/lib/docker/overlay2/jr0hxl8wnzf9weogwginrefta/diff/app/src/app/admin/page.tsx

读取文件,找到技术支持栏中的HTML实体编码:&#122;&#104;&#105;&#116;&#117;&#109;&#97;

解码后为 zhituma

启动faka网站

docker run -d --name faka --link psql:postgres -p 8080:3000 faka

进入平台http://192.168.50.128:8080,登录管理页面发现报错

修改一下.env文件,改到本地

重启docker容器docker restart faka,因为不知道密码,但是前面知道了他的加密方式和盐值,所以我们可以构造密码来替换数据库上的密码

然后替换

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

deepseek/deepseek-v4-flash

打开openclaw后可以看到

或者是/root/.openclaw/中的openclaw.json文件可以看到

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

sk-b203197a32804a669495973886ca8760

在openclaw.json中继续往下翻可以看到

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

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

在定时任务中可以看到

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

61

直接可以看到

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

小星

见前几题,打开就可以看到

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

e&eqYEbu5bM~8iA

与第八题差不多

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

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

password-manager

在技能ctrl+f直接搜索password

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

Honglian@Fic2026Password

在和openclaw的对话中可以看到

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

http://www.baiguo.com

依旧对话中可以看到

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

Honglian@2026fic

这个之前其实就已经出来了,在.env文件的数据库的url那里可以得到

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

1.14.198.31

依旧在对话里

这里如果openclaw成功仿起来的话是可以直接看聊天记录的😭

21 tg_faka 机器人所配置的 Telegram Bot Token 是什么?

【参考格式:1234567890:ABCdefGHIjklmNOPqrstUVwxyz1234567890】 8514062821:AAHzdIL52vtuR9Y3ItBqtfAZdMwZ-YjPzms

在web2中可以看到bot的配置文件/root/captcha-bot/config/config.toml

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

2

由第八题解密的数据库可知,为两条

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

fic@zfb

依旧在上一题的数据库中

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

星球商店交流

在/captcha-bot/db/geecaptcha.db中可以看到

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

3045

将上题的数据库导出

SELECT COUNT(DISTINCT telegram_user_id) 
FROM user_captcha_record 
WHERE captcha_status = 1;

或者使用网矩进行分析 ** **根据 captcha_status=1,telegram_user_id 去重后得到结果

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

3

查看advertise表

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

2026-05-06 17:30:56

查看 user_captcha_record 表,第一条数据就是 milo,captcha_success_time 就是通过验证的时间

windows服务器

第一次遇到windows服务器,借鉴大佬的wp以及官方结题思路 ** **https://blog.enxiaohao.cn/posts/Forensics/2026FICFinalWinserverwp/

火眼看到网络配置,系统配有两张网卡 172.28.0.100,172.27.0.100,分别对应两个公网地址 40.72.166.187,139.217.21.231

根据这个网卡配置网络

这里可以将dhcp起始地址改为0,方便后续操作

仿真之后按照指示输入密码F1cWinC@re

进入一个windows的cmd页面,这里之后其实就可以rdp连接了

成功连接,便于后续指令的输入

接下来看服务,服务列表中可以发现自动启动的FicWeb和PostgreSQL,以及手动的containerd和Docker

计划任务中有MountVHDXatStartup,会把ROOT.vhdx挂载到T,并在T下创建root和state连个目录,同时启动containerd

可以查看一下

nerdctl ps -a可以看到当前启动的容器,postgres已启动

然后启动网站,直接运行exe启动会报错,用sc.exe可以启动

sc.exe start FicWeb

sc.exe query FicWeb

然后直接用ip访问不到,要改hosts并配置本地dns才能启动 ** **先改hosts(根据自己的ip)

172.28.0.100 admin.ficads.site
172.28.0.100 mtls.ficads.site
172.27.0.100 ficads.site

然后会发现后台还是连不上,看到题目提供的附件4,是因为这里走的是http3,对应的传输层是QUIC,基于UDP

所以用这个方式启动,强制这个域名走QUIC:

$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/'
)

1 分析服务器检材,系统分区的文件系统类型是?

【参考格式:FAT32】 REFS

可以输入命令fsutil fsinfo volumeInfo C:

也可以直接看分析的结果

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

3.14

使用 fsutil 查看 ReFS 版本信息fsutil fsinfo refsInfo C:

3 分析服务器检材,系统的SKU编号(OperatingSystemSKU)是什么?

407

可以通过(Get-CimInstance Win32_OperatingSystem).OperatingSystemSKU获取或者通过[int](Get-ComputerInfo).OsOperatingSystemSKU获取

4 服务器中的WinRM HTTP服务的端口是多少? (格式:65535)

59508

使用命令查询WinRM的服务配置winrm enumerate winrm/config/listener

不能单纯看winrmgetconfig,这个只会打印默认的端口,这里需要查看实际的监听器端口

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

2026-05-17 16:38:06

用powershell命令分析事件日志Microsoft/Windows/WinRM

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

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

117.184.20.123

上一题可以看到一个ip

根据已经查询到的端口 59508 获取对应端口的防火墙规则

Get-NetFirewallPortFilter |
Where-Object LocalPort -eq 59508 |
Get-NetFirewallRule |
ForEach-Object {
    $_ | Get-NetFirewallAddressFilter | Select-Object LocalAddress, RemoteAddress
}

排除上一题的ip可知117.184.20.123

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

pMLCzXOd+<.&.pUw

这两题考点是ReFS文件系统文件流的快照,有关ReFS文件系统的流,要用refsutilstreamsnapshot工具来查看。这个结构和NTFSADS流蛮像的,只不过Refs的流一般更偏向于文件快照,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
}

可以看到有一个快照

导出

$srcFile = 'C:\Users\Administrator\secrets\secret_012.docx'
$streamName = 'snapshot_sec'
$dstFile = 'C:\Users\Administrator\secrets\secret_012.snapshot.docx'

$data = Get-Content -LiteralPath $srcFile -Stream $streamName -Encoding Byte -Raw
[System.IO.File]::WriteAllBytes($dstFile, $data)

利用给的管理员密码smb上去Copy-Item即可

#要开一下防火墙
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'  'secret_012.snapshot.docx' -Force

打开docx那个文件看看,路径一般在C:\Windows\system32\secret_012.snapshot.docx

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

921a242639d1a13f6d6bfa47d9eb34d5

本题是通过 Linux 虚拟机快照的 avhdx 分析,或者还原虚拟机,选择正确的快照启动计算文件哈希即可。如果虚拟机在系统里参考下面的文档连接远程 Hyper-V(参考:https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/manage/remotely-manage-hyper-v-hosts)按文档流程操作,组策略服务器列表加入 wsman/win-sup965gub26,在 Hyper-V管理器中使用用户名 win-sup965gub26\administrator 连接即可。如果电脑里有Windows Admin Center,也可以网页可视化连接。

通过火眼分析的Users/Administrator/VM目录下可以看到虚拟机

按下面流程合并 avhdx 分析

**查询父盘路径 **

Get-VHD -Path * | Format-List Path, ParentPath

整理一下,目标的合同文件有可能是在中间节点出现的,所以最好恢复所有的状态,应该一共是7个状态,一个一个还原

Copy-Item C:\Users\Administrator\VM C:\Users\Administrator\VM_C03E3EEC -Recurse
cd C:\Users\Administrator\VM_C03E3EEC
Merge-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.vhdx
Copy-Item .\fedora-coreos-44.20260419.3.1-hyperv.x86_64.vhdx C:\Users\Administrator\C03E3EEC_state.vhdx

导出到本地

cmdkey /add:172.28.0.100 /user:Administrator /pass:F1cWinC@re
Copy-Item '\\172.28.0.100\c$\Users\Administrator\FF7E62B2_state.vhdx' "$env:USERPROFILE\Downloads\FF7E62B2_state.vhdx" -Force
cmdkey /delete:172.28.0.100

导出之后添加新检材分析一下,搜索合同

计算md5即可

同时699这个磁盘也有合同这个文件,哈希值相同

9 管理后台页面的TLS证书的SHA1指纹是什么

(格式:da39a3ee5e6b4b0d3255bfef95601890afd80709) 248424a5234d539cbf0425751a437adcc106d835

/分区6/FIC_WEB/appsettings.json,有网站证书相关的配置

网站上也可以看到证书

写个脚本

import ssl
import socket
import hashlib

def get_sha1_fingerprint(hostname="ficads.site", port=443):
    context = ssl.create_default_context()
    context.check_hostname = False
    context.verify_mode = ssl.CERT_NONE
    
    with socket.create_connection((hostname, port), timeout=5) as sock:
        with context.wrap_socket(sock, server_hostname=hostname) as ssock:
            der_cert = ssock.getpeercert(binary_form=True)
            
    # 计算 SHA1 并格式化输出 (大写+冒号)
    sha1_hex = hashlib.sha1(der_cert).hexdigest().upper()
    fingerprint = ':'.join(sha1_hex[i:i+2] for i in range(0, len(sha1_hex), 2))
    print(f"证书 SHA1 指纹 (Thumbprint) = {fingerprint}")

if __name__ == "__main__":
    get_sha1_fingerprint()

10 广告页面提供服务的网卡MAC地址是什么

(格式:11:22:33:aa:bb:cc) 00:15:5d:19:96:2c

appsettings.json中可以看到publiclistenip监听的是172.27.0.1这张

猜测是这张网卡

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

65625

后台这里,要求TOTP动态码和客户端证书,那么这道题目应该考察的就是TOTP的还原

首先要找key,从下面第17题可以得到数据库密码yiZvGzBS9FYxrw42J17LNUK5tlDo,然后本机可以直接连接的

admin_totp_secrets表中找到对应的secret为JBSWY3DPEHPK3PXP,这个secret是可以长时间复用的,数据库中这个key生成的时间是4月20日

分析网站代码可得 Step 30,位数 5

import pyotp
import datetime
import pytz
SECRET = "JBSWY3DPEHPK3PXP"
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

在数据库中,admin_client_certificates看到证书的信息

证书指纹为F628659F0D2B175736B049FAC1D0659AF81033B82DB071CF4D201FF70F4DCA8B,在附件三中找

from pathlib import Path
import hashlib
from cryptography import x509
from cryptography.hazmat.backends import default_backend

target_fingerprint = "F628659F0D2B175736B049FAC1D0659AF81033B82DB071CF4D201FF70F4DCA8B"
search_dir = Path(r"E:\Admin\Desktop\附件3")

for cert_file in search_dir.glob("*.cer"):
    data = cert_file.read_bytes()
    sha256 = hashlib.sha256(data).hexdigest().upper()
    sha1 = hashlib.sha1(data).hexdigest().upper()

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

flag{fic_mtls_2a10229ccc830e737cb54993}

比较麻烦,以下为官方题解

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

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

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

453c36403960244b4269eed7032148c104fa127209262125aeae8018e36f25eadd3ad99928abf96b23f73a0a908e3adc

在admin_webauthn_credentials中有相关的数据

只有5月17日这一条有使用记录,对应的crendential_id为RTw2QDlgJEtCae7XAyFIwQT6EnIJJiElrq6AGONvJerdOtmZKKv5ayP3OgqQjjrc,但是这里格式不一样,实际上应该是base64进行存储,编码转换一下即可

15 运行网站数据库的容器中,容器内部操作系统版本是

(格式:25H2) 24H2

**服务器中安装了docker+containerd和nerdctl+containerd,实际数据库使用containerd,服务中 docker 和 containerd 都是未启用的,在计划任务中挂载容器 NTFS 盘到 T 盘后延迟启动 containerd 这个服务。 **

查看容器

进入容器的pwsh

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

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

拿到OS:MicrosoftWindows10.0.26100,对应的是24H2

16 推广管理网站使用的PostgreSQL数据库版本是多少。

(格式:16.1) 18.0

17 推广管理网站连接PostgreSQL数据库使用的密码是什么。

(格式:Abc123$%^) yiZvGzBS9FYxrw42J17LNUK5tlDo

官方:配置文件中的密码和容器参数中的密码都不对,推广网站实际使用了Windows凭据中存储的用户名和密码在取证软件中输入题目背景中给出的密码,即可解密DPAPI存储的密钥,得到答案。

这里参考袁佬的mimikatz

先把defender关了

Set-MpPreference -DisableRealtimeMonitoring $true

然后传一下mimikatz(自己本机的路径)

#如果没开防火墙:Test-NetConnection 172.27.0.100 -Port 445
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

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

com.fic.seed.chatlive095

这里是调用fic.resolve_promotion_payload_at这个存储过程,一样的参数传回去

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

(格式:27AE41E4649B934CA495991B7852B855) traffic_counters表本身并没有host和apk_sha256字段,这里要聚合

apk_targets中有apk_sha256

promotion_sources中有host字段

使用相关的表连接字段(id,source_id)连接即可,累计访问量对应的字段是total_count

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

(格式:ASNFZ4mrze_-3LqYdlQyEA) PV0o_BzQptB90kmn8I_9VZxw_LKnIJ8IyqSAWOvAtlc

技艺不精,直接用官方的了

**本题考查 PG 数据恢复。通过 pg_waldump 解析 WAL 日志,定 **

**位 DELETE 记录,还原数据。 **

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

关键业务记录如下:

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

结果:

导出该 relation 的 full-page image:

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

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

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