第四届黄河流域公安院校电子物证个人赛程序逆向分析
🏆 2026第四届黄河流域公安院校电子物证个人赛 — 程序逆向分析 Writeup
💡 报告元数据
- 首席取证官:
yagami - 任务目录:
/mnt/d/文档/hermes-work/2026黄河流域-程序逆向分析/ - 生成时间:2026-08-04
- 生成模式:完整型取证交付 Writeup
🛠️ 工具链与环境底座
1. 算力与架构
| 组件分类 | 部署详情 |
|---|---|
| 自动化编排 | Hermes Agent |
| 基座大模型 | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-Q6_K_P |
| 运行架构 | 本地 (WSL / Windows Subsystem for Linux) |
2. 任务信息
- 考核范围:程序逆向分析(APK静态分析 + PE二进制分析),共10题
- 检材来源:
/mnt/z/2026第四届黄河流域公安院校电子物证个人赛检材/3.第三部分:程序逆向分析/ - 检材内容:TuoxinTask-v3.2.1.zip(内含APK)、HuangHe2026.zip(内含PE)
- 动态状态:Q1-Q10 全部已验证,全量收敛
- 质控策略:L1-L3多级验证,Java↔native交叉验证,本地Python解密双向验证
📊 考题最终答案汇总看板
| 题号 | 核心考点 | 权威标准答案 | 状态 | 等级 | 核心证据链检索摘要 |
|---|---|---|---|---|---|
| Q1 | APK包名提取 | com.tuoxin.task |
🟢 已验证 | L1 | AndroidManifest.xml package字段直接确认 |
| Q2 | 通讯录窃取写入文件 | contacts_bak.json |
🟢 已验证 | L1 | DataUploadService.a()方法openFileOutput参数硬编码 |
| Q3 | cfg.bin加密方式(多选) | BCF |
🟢 已验证 | L2 | Base64编码+AES-256-CBC+IV来自独立iv.bin,三源交叉 |
| Q4 | cfg.bin解密key | 47f32b6c3c50f7474f3a14787ef790f46d326e7a33a758d0c7e452cb120e0700 |
🟢 已验证 | L3 | HARD_KEY hex = SHA-256(key source),Java↔native交叉+本地解密双向验证 |
| Q5 | cfg.bin备份IP及端口 | 121.43.187.92:8080 |
🟢 已验证 | L2 | cfg.bin解密后JSON中sdk_backup_server字段 |
| Q6 | PE编译器识别 | B (Borland Delphi 6) |
🟢 已验证 | L2 | Borland\Delphi\RTL字符串+Delphi单元引用+名称修饰+oleaut32 BSTR函数,多特征交叉 |
| Q7 | PE节区数量 | 8 |
🟢 已验证 | L1 | PE头NumberOfSections=8,IDA确认8个节区 |
| Q8 | PE入口点VA | 0x492040 |
🟢 已验证 | L1 | ImageBase(0x400000)+RVA(0x92040)=0x492040,IDA交叉确认 |
| Q9 | 注册表值名 | Start |
🟢 已验证 | L1 | IDA字符串搜索ControlSet\Services\UsbStor,值名Start |
| Q10 | PE主要功能 | D |
🟢 已验证 | L2 | UsbStor+Start+.inf.disabled+MoveFileA+MessageBoxA,USB启用/禁用工具 |
⚔️ 深度取证与解题全链路复盘
🧩 Q1 — 刷单APP包名
-
标准答案:
com.tuoxin.task -
状态等级:🟢 已验证 / L1
-
解题主线:
使用jadx MCP直接读取AndroidManifest.xml,提取package属性值。APK反编译后manifest文件中的package字段即为应用包名。 -
💡 关键证据:
- 证据编号:FINDING-Q1-001, CMD-20260804-002
- AndroidManifest.xml中
package="com.tuoxin.task",versionName=“3.2.1”,application name=“com.tuoxin.task.app.TxApplication”
- 🛠️ 核心命令:
# jadx MCP读取AndroidManifest
mcp_jadx_get_android_manifest()
- 📋 控制台回显:
package="com.tuoxin.task", versionName="3.2.1", application name="com.tuoxin.task.app.TxApplication"
- 验证闭环:AndroidManifest.xml是唯一权威来源,package字段直接确认。
🧩 Q2 — 通讯录数据写入的JSON文件
-
标准答案:
contacts_bak.json -
状态等级:🟢 已验证 / L1
-
解题主线:
使用jadx MCP搜索ContactsContract关键字定位通讯录读取点→找到DataUploadService类→分析a()方法完整数据流:从ContactsContract.CommonDataKinds.Phone读取通讯录→经ag0.xorFallback XOR加密→Base64编码→写入本地文件。文件名在openFileOutput()参数中硬编码为"contacts_bak.json",位于getFilesDir()目录下。 -
💡 关键证据:
- 证据编号:FINDING-Q2-001, CMD-20260804-003
- DataUploadService.a()方法完整链路:ContactsContract读取→XOR加密→Base64编码→openFileOutput(“contacts_bak.json”)
- 🛠️ 核心命令:
# jadx MCP获取DataUploadService类源码
mcp_jadx_get_class_source(class_name="DataUploadService")
- 📋 控制台回显:
方法a()读取通讯录ContactsContract.CommonDataKinds.Phone,写入文件 "contacts_bak.json"
- 验证闭环:类源码中openFileOutput参数直接指定文件名,无歧义。
🧩 Q3 — cfg.bin加密方式(多选)
-
标准答案:
BCF -
状态等级:🟢 已验证 / L2
-
解题主线:
分析assets/cfg.bin文件的多层处理流程:
- 编码层:cfg.bin文件内容为Base64编码文本(B正确,排除A的Base32)
- 解码后结构:Base64解码后前8字节为header(“TX-WEB\0\x01\x00”),bytes[8:24]为IV(全0),byte24起为加密数据
- 加密算法:AES-256-CBC(C正确,排除D的AES-512-CBC),key由SHA-256派生(32字节)
- IV来源陷阱:Java代码中System.arraycopy(bArrDecode, 8, new byte[16], 0, 16)创建临时数组即丢弃,实际IV由native层从独立文件assets/iv.bin读取(16字节,9f4a2c18e6b7d053f8c1aa90d33b44e1)→ F正确,排除E
- 💡 关键证据:
- 证据编号:FINDING-Q3-001, CMD-20260804-004/005/006
- cfg.bin为Base64编码,解码后744字节
- AES-256-CBC加密,key由SHA-256派生
- IV实际来源:assets/iv.bin(独立文件),非cfg.bin自身
- 🛠️ 核心命令:
# jadx MCP分析decryptConfig类
mcp_jadx_get_class_source(class_name="a.ih")
# jadx MCP分析deriveKey方法
mcp_jadx_get_class_source(class_name="a.ag0")
# 提取并解密cfg.bin
# Python: Base64解码→IV从iv.bin读取→AES-256-CBC解密→JSON解析
- 📋 控制台回显:
Base64解码后744字节,IV从assets/iv.bin读取(9f4a2c18e6b7d053f8c1aa90d33b44e1),AES-256-CBC解密成功,JSON包含sdk_backup_server: "121.43.187.92:8080"
-
验证闭环:本地Python用PyCryptodome AES-256-CBC解密cfg.bin成功→有效JSON,反向加密与原始密文完全一致(True)。三重证据交叉验证。
-
⚠️ 修正复盘:
- IV来源是本题最大陷阱:Java代码中System.arraycopy看似从cfg.bin自身提取IV,但创建的临时数组立即丢弃,实际IV由native层从iv.bin读取
- 多选题需逐项排除:A(Base32)→实际为Base64;D(AES-512-CBC)→实际为AES-256-CBC;E(IV来自文件自身)→实际来自独立iv.bin
🧩 Q4 — cfg.bin解密key
-
标准答案:
47f32b6c3c50f7474f3a14787ef790f46d326e7a33a758d0c7e452cb120e0700 -
状态等级:🟢 已验证 / L3 (三重交叉验证)
-
解题主线:
阶段1 — Java层密钥派生:
jadx反编译a.ag0.deriveKey()方法,发现执行SHA-256(“tx2_LIVE_q1!v8c1a#|@0x7E::tuoxin.prod::”)得到32字节AES-256密钥。
阶段2 — native层HARD_KEY分析:
IDA反编译native层obfuscate.cpp:17,发现HARD_KEY数组存储为混淆值"live_2026_q1_3f8c1a",运行时去混淆后还原。关键发现:HARD_KEY数组32字节的hex拼接值 = 47f32b6c3c50f7474f3a14787ef790f46d326e7a33a758d0c7e452cb120e0700。
阶段3 — 交叉验证:
- SHA-256("tx2_LIVE_q1!v8c1a#|@0x7E::tuoxin.prod:😊 = 47f32b6c…120e0700
- HARD_KEY hex拼接 = 47f32b6c…120e0700
- 两者完全一致(Match: True)
阶段4 — 本地解密验证:
用该key + iv.bin中的IV,AES-256-CBC解密cfg.bin→有效JSON(含sdk_backup_server: 121.43.187.92:8080),反向加密与原始密文完全一致。
- 💡 关键证据:
- 证据编号:FINDING-Q4-001/002/003, CMD-20260804-010/011/013
- Java层:MessageDigest.getInstance(“SHA-256”).digest(“tx2_LIVE_q1!v8c1a#|@0x7E::tuoxin.prod::”.getBytes(“UTF-8”))
- native层:obfuscate.cpp:17 HARD_KEY数组32字节hex = 47f32b6c…120e0700
- ag0.verify()确认Java deriveKey == Native nativeGetKey
- 🛠️ 核心命令:
# Java层密钥派生分析
mcp_jadx_get_class_source(class_name="a.ag0")
# native层HARD_KEY交叉对比
# Python本地验证
import hashlib, binascii
key_source = "tx2_LIVE_q1!v8c1a#|@0x7E::tuoxin.prod::"
sha_key = hashlib.sha256(key_source.encode('utf-8')).hexdigest()
print(f"SHA-256 key: {sha_key}")
# 输出: 47f32b6c3c50f7474f3a14787ef790f46d326e7a33a758d0c7e452cb120e0700
# AES-256-CBC解密验证
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64
key = bytes.fromhex(sha_key)
iv = bytes.fromhex("9f4a2c18e6b7d053f8c1aa90d33b44e1")
cfg_raw = open("cfg.bin", "rb").read()
cfg_decoded = base64.b64decode(cfg_raw)
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = unpad(cipher.decrypt(cfg_decoded[8:]), AES.block_size)
print(plaintext.decode('utf-8')) # → 有效JSON
- 📋 控制台回显:
HARD_KEY = 47f32b6c3c50f7474f3a14787ef790f46d326e7a33a758d0c7e452cb120e0700
与SHA-256("tx2_LIVE_q1!v8c1a#|@0x7E::tuoxin.prod::")完全一致(Match: True)
AES-256-CBC解密cfg.bin成功→有效JSON(含sdk_backup_server: 121.43.187.92:8080)
-
验证闭环:L3三重交叉验证 — (1)Java SHA-256输出 = (2)native HARD_KEY hex = (3)本地Python解密成功+反向加密一致。
-
⚠️ 修正复盘:
- 旧答案:
tx2_LIVE_q1!v8c1a#|@0x7E::tuoxin.prod::(key source字符串) - 修正原因:用户指出obfuscate.cpp:17 HARD_KEY数组32字节hex拼接即为AES-256密钥,与SHA-256(key source)完全一致。题目问"解密key是多少",答案为实际的32字节密钥(hex形式),而非key source字符串
- native层陷阱:HARD_KEY明文"live_2026_q1_3f8c1a"是混淆存储形式,SHA-256(HARD_KEY明文)解密→非UTF-8(失败),证明需取HARD_KEY数组的hex拼接值
🧩 Q5 — cfg.bin备份IP及端口
-
标准答案:
121.43.187.92:8080 -
状态等级:🟢 已验证 / L2
-
解题主线:
cfg.bin经Q3/Q4流程解密后得到JSON配置文件,其中包含sdk_internal.sdk_backup_server = “121.43.187.92:8080”。此为备份服务器IP地址及端口。 -
💡 关键证据:
- 证据编号:FINDING-Q5-001, CMD-20260804-005/006
- cfg.bin解密后JSON内容:sdk_backup_server: “121.43.187.92:8080”
- 🛠️ 核心命令:
# 同Q4解密流程,解析JSON输出
- 验证闭环:解密结果JSON中直接包含该字段,与反向加密验证一致。
🧩 Q6 — PE编译器及版本
-
标准答案:
B(Borland Delphi 6) -
状态等级:🟢 已验证 / L2
-
解题主线:
使用IDA MCP提取PE文件字符串和导入表,多维度识别编译器特征:
- 注册表路径:
Borland\Delphi\RTL、Software\Borland\Delphi\Locales - Delphi特有字符串:
Delphi Picture、Delphi Component、Delphi%.8X - Delphi单元引用:SysUtils, Classes, Forms, Dialogs
- 名称修饰:
@@和_@后缀(Delphi name decoration) - oleaut32.dll BSTR函数:SysAllocStringLen, SysReAllocStringLen, SysFreeString
以上特征组合唯一指向Borland Delphi 6。
- 💡 关键证据:
- 证据编号:FINDING-Q6-001, CMD-20260804-007/008
- 5类Delphi特征全部命中,无其他编译器特征干扰
- 🛠️ 核心命令:
# IDA MCP字符串搜索
mcp_ida_find(type="string", targets=["Borland", "Delphi", "SysUtils"])
# IDA MCP导入表分析
mcp_ida_imports(offset=0, count=50)
- 📋 控制台回显:
字符串"Borland\Delphi\RTL"、"Software\Borland\Delphi\Locales"
Delphi单元引用:SysUtils, Classes, Forms, Dialogs
名称修饰:@@和_@后缀(Delphi name decoration)
oleaut32.dll BSTR函数:SysAllocStringLen, SysReAllocStringLen, SysFreeString
- 验证闭环:5类独立特征全部指向Borland Delphi 6,多源交叉确认。
🧩 Q7 — PE节区数量
-
标准答案:
8 -
状态等级:🟢 已验证 / L1
-
解题主线:
使用pefile解析PE文件头NumberOfSections字段,同时用IDA MCP确认实际节区列表。两者一致为8个节区:CODE, DATA, BSS, .idata, .tls, .rdata, .reloc, .rsrc。 -
💡 关键证据:
- 证据编号:FINDING-Q7-001, CMD-20260804-007
- PE头NumberOfSections=8,节区列表:CODE, DATA, BSS, .idata, .tls, .rdata, .reloc, .rsrc
- 🛠️ 核心命令:
# pefile分析
import pefile
pe = pefile.PE('HuangHe2026.exe')
print(f"NumberOfSections: {pe.FILE_HEADER.NumberOfSections}")
for sec in pe.sections:
print(f" {sec.Name.decode().strip(chr(0))}")
pe.close()
- 📋 控制台回显:
NumberOfSections=8,节区:CODE, DATA, BSS, .idata, .tls, .rdata, .reloc, .rsrc
- 验证闭环:pefile与IDA MCP双源确认。
🧩 Q8 — PE入口点VA
-
标准答案:
0x492040 -
状态等级:🟢 已验证 / L1
-
解题主线:
PE入口点VA = ImageBase + AddressOfEntryPoint(RVA)。pefile读取ImageBase=0x400000,RVA=0x92040,计算VA=0x492040。IDA MCP确认entrypoint地址为0x492040。 -
💡 关键证据:
- 证据编号:FINDING-Q8-001, CMD-20260804-007/009
- RVA=0x92040, ImageBase=0x400000, VA=0x492040
- 🛠️ 核心命令:
# pefile计算VA
import pefile
pe = pefile.PE('HuangHe2026.exe')
image_base = pe.OPTIONAL_HEADER.ImageBase
entry_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint
entry_va = image_base + entry_rva
print(f'Entry Point VA: 0x{entry_va:x}')
pe.close()
- 📋 控制台回显:
NumberOfSections=8,AddressOfEntryPoint RVA=0x92040,ImageBase=0x400000,VA=0x492040
IDA确认entrypoint地址为0x492040
- 验证闭环:pefile计算与IDA MCP直接读取一致。
🧩 Q9 — 注册表值名
-
标准答案:
Start -
状态等级:🟢 已验证 / L1
-
解题主线:
使用IDA MCP搜索PE文件中的注册表相关字符串,找到程序操作的注册表路径ControlSet\Services\UsbStor,读写的值名为"Start"(DWORD类型,控制USB存储驱动加载状态:3=自动/启用,4=手动/禁用)。 -
💡 关键证据:
- 证据编号:FINDING-Q9-001, CMD-20260804-008
- 注册表路径:ControlSet\Services\UsbStor,值名:Start
- 🛠️ 核心命令:
# IDA MCP字符串搜索
mcp_ida_find(type="string", targets=["ControlSet", "Services", "UsbStor"])
- 📋 控制台回显:
注册表路径ControlSet\Services\UsbStor,值名Start;字符串"USB Disabler"、".inf.disabled"
- 验证闭环:IDA字符串搜索直接定位,无歧义。
🧩 Q10 — PE主要功能
-
标准答案:
D -
状态等级:🟢 已验证 / L2
-
解题主线:
综合分析PE文件的多维度特征判定功能:
- 程序名:“USB Disabler” / “USBDisablerFree”
- 注册表操作:修改HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\UsbStor的Start值(3↔4)
- 驱动INF操作:重命名.inf ↔ .inf.disabled(使用MoveFileA)
- 用户交互:通过MessageBoxA与用户交互
综合判定为USB大容量存储设备启用/禁用工具,匹配选项D。
- 💡 关键证据:
- 证据编号:FINDING-Q10-001, CMD-20260804-008
- 4类特征:程序名+注册表Start值+.inf.disabled重命名+MessageBoxA交互
- 🛠️ 核心命令:
# IDA MCP综合字符串搜索
mcp_ida_find(type="string", targets=["USB Disabler", ".inf.disabled", "MoveFileA", "MessageBoxA"])
# IDA MCP导入表分析
mcp_ida_imports(offset=0, count=50)
- 📋 控制台回显:
字符串"USB Disabler"、".inf.disabled";Delphi特征(Borland\Delphi\RTL, SysUtils, Classes, Forms)
注册表路径ControlSet\Services\UsbStor,值名Start
- 验证闭环:4类独立证据链交叉确认功能为USB启用/禁用工具。选项A(全盘加密)、B(永久移除)、C(日志审计)、E(HID模拟)均无对应特征。
🛑 未完成或不可提交题目审计
| 题号 | 当前临时结论 | 当前跟踪状态 | 挂起/阻断原因 | 下一步攻坚策略 |
|---|---|---|---|---|
| — | — | 全量收敛 | Q1-Q10全部已验证,无未完成题目 | — |
📂 附录:自动化取证证据大索引
1. 物理证据链索引映射表 (Findings Matrix)
| 统一证据编号 | 证据物理源文件 / 捕获链路 | 核心留存特征内容摘要 | 支撑断言结论 |
|---|---|---|---|
| FINDING-ENV-001 | 检材目录 | HuangHe2026.zip(370KB), TuoxinTask-v3.2.1.zip(3.51MB) | 环境确认 |
| FINDING-Q1-001 | AndroidManifest.xml | package=“com.tuoxin.task” | Q1包名 |
| FINDING-Q2-001 | DataUploadService.a() | ContactsContract→XOR→Base64→contacts_bak.json | Q2文件名 |
| FINDING-Q3-001 | cfg.bin分析 | Base64+AES-256-CBC+IV来自iv.bin | Q3加密方式BCF |
| FINDING-Q4-001 | a.ag0.deriveKey() | SHA-256("tx2_LIVE_q1!v8c1a# | @0x7E::tuoxin.prod:😊 |
| FINDING-Q4-002 | obfuscate.cpp:17 | HARD_KEY混淆存储"live_2026_q1_3f8c1a" | Q4 native层 |
| FINDING-Q4-003 | 交叉验证 | HARD_KEY hex = SHA-256结果,解密成功 | Q4最终答案 |
| FINDING-Q5-001 | cfg.bin解密JSON | sdk_backup_server: “121.43.187.92:8080” | Q5备份IP |
| FINDING-Q6-001 | PE字符串+导入表 | Borland\Delphi\RTL + 5类Delphi特征 | Q6编译器B |
| FINDING-Q7-001 | PE头分析 | NumberOfSections=8 | Q7节区数 |
| FINDING-Q8-001 | PE头+IDA | RVA=0x92040, ImageBase=0x400000, VA=0x492040 | Q8入口点VA |
| FINDING-Q9-001 | IDA字符串搜索 | ControlSet\Services\UsbStor, 值名Start | Q9注册表值名 |
| FINDING-Q10-001 | PE综合特征 | USB Disabler + Start + .inf.disabled + MoveFileA | Q10功能D |
2. 物理执行指令索引快照表 (Commands Log)
| 命令流水号 | 目标取证目的 | 挂载执行的绝对终端指令 | 关键输出 | 结论 |
|---|---|---|---|---|
| CMD-20260804-001 | 检材目录确认 | ls -la 检材目录 | HuangHe2026.zip, TuoxinTask-v3.2.1.zip | 环境就绪 |
| CMD-20260804-002 | APK包名提取 | jadx get_android_manifest | package=“com.tuoxin.task” | Q1确认 |
| CMD-20260804-003 | 通讯录写入文件 | jadx get_class_source DataUploadService | 写入"contacts_bak.json" | Q2确认 |
| CMD-20260804-004 | cfg.bin解密流程 | jadx get_class_source a.ih | Base64→跳过header→nativeDecryptCfg | Q3流程 |
| CMD-20260804-005 | 密钥派生分析 | jadx get_class_source a.ag0 | SHA-256(key source) | Q4来源 |
| CMD-20260804-006 | cfg.bin本地解密 | Python PyCryptodome AES-256-CBC | JSON含sdk_backup_server | Q3/Q5验证 |
| CMD-20260804-007 | PE节区+入口点 | IDA segments + pefile | 8节区, VA=0x492040 | Q7/Q8确认 |
| CMD-20260804-008 | PE字符串+导入 | IDA imports/strings | UsbStor/Start/Delphi特征 | Q6/Q9/Q10 |
| CMD-20260804-009 | pefile独立验证 | Python pefile分析 | NumberOfSections=8, RVA=0x92040 | Q7/Q8交叉 |
| CMD-20260804-010 | Q4反向加密验证 | Python AES加密比对 | 重新加密与原始密文一致(True) | Q4 L3验证 |
| CMD-20260804-011 | Q4 native交叉对比 | HARD_KEY SHA-256 vs Java key | ag0.verify()确认一致 | Q4 L3验证 |
| CMD-20260804-013 | Q4 HARD_KEY hex确认 | IDA + Python hashlib | Match: True, 解密成功 | Q4最终确认 |
- 全链路取证闭环完成 -