本文档记录对 bufSvrSealEnc(Matrix.dat 内 ITXData 键)所使用加密/编码算法 的静态分析进展、已确认事实与未闭合问题。与主笔记 NOTES.md 第 5、15、16 节衔接。
分析日期:2026-05-02
主要 IDB / MCP 实例(多 IDA 进程同时存在时经 list_instances / select_instance 切换):
| 端口 | 模块 | 基址 | 说明 |
|---|---|---|---|
| 13338 | Common.dll |
0x30000000 |
TXEncryptMgr、sub_30097FF0、ITX 缓冲与通用算法 |
| 13339 | KernelUtil.dll |
0x31800000 |
Util::SvrSeal::CreateSvrSeal、CheckKeyID、ATL 模块 |
| 13337 | IM.dll |
(用户侧打开) | 调用侧:Matrix.dat、AddEncryptInfo、登录封印 |
Matrix.dat经TXEncryptMgr::CreateDataStorage映射为ITXDataStorage,键值bufSvrSealEnc(字面量在Common.dll约0x301b9ea4)。- 字段名后缀
Enc,与同文件中bufRandKeyEnc等并列,表示盘上多为 编码/加密后的二进制,而非 UTF-16 明文属性串。
在 sub_30097FF0(TXEncryptMgr::AddEncryptInfo 注册节点后的上下文初始化)中:
- 若存在
bufSvrSealEnc:写日志PerfStand.DecryptMsg.Begin(字面量约0x301b9ef4),随后对ITXSvrSealCrypto/ITXIMSvrSealCrypto实例调用 虚表偏移+0x10(第 4 个槽:IUnknown 之后约DecryptMsg语义),输入为从存储读出的ITXBuffer,外加this+23一类上下文指针。 Common.dll在此处不显式展开对称算法体:算法实现在 Seal COM 对象 所指模块内。
bufRandKeyEnc分支可走sub_300960A0→sub_30095E10→sub_30002340(已在NOTES.md§16 对齐为 XXTEA + 自定义二进制信封)。bufSvrSealEnc分支依赖ITXSvrSealCrypto::vtable+0x10,不从同一段sub_30095E10的 xref 树直接得出与sub_30002340的必然同一性;需单独跟进 KernelUtil(或 manifest 指向的其它 DLL) 内 DecryptMsg 实现。
?CreateSvrSeal@SvrSeal@Util@@YAJEPAPAUITXIMSvrSealCrypto@@@Z- IDA 中函数入口约
0x3182bed0(随 IDB 可能有符号差异)。
- IDA 中函数入口约
CreateSvrSeal逻辑概要:- 调用
sub_31820310(&unk_318680A4, &unk_318680A4, a2),两次同一指针。 unk_318680A4处 16 字节 GUID(LE)解析为:{EDC5158A-8148-401A-9F2B-A72863BCA24A}。sub_31820310通过Util::Core::GetPlatformCore取得ITXCore*,再vtable+0x1C(字节偏移 28)创建实例(等价CreateInstance(CLSID, IID, ppv)形态)。- 成功后对所得对象
vtable+0x14(字节偏移 20) 调用(*obj)(..., Util::SvrSeal* type),完成 Init/绑定 一类初始化。
- 调用
- 在
KernelUtil.dll裸文件中find_bytes仅命中 一处 该 CLSID 常量(与unk_318680A4一致),未发现Common.dll内嵌同一 16 字节(至少在已扫 IDB 下无匹配)。 - 推论:封印实现的 最终 coclass 由 平台 Core + 组件注册 解析,不一定在
Common.dll内静态存放同一 GUID 常量。
DllGetClassObject→AtlComModuleGetClassObject(&unk_31883614, ...)。DllRegisterServer(约sub_3182E5C0)走AtlComModuleRegisterServer;对象映射表unk_31883614在 IDA 里可能显示为 未初始化/bss 形态(依赖运行时或加载填充),静态dump需谨慎。
- 导出
?CheckKeyID@SvrSeal@Util@@YAHEPAUITXEncrypt@@@Z(约0x3182bdd0)。 - 反编译可见对
ITXEncrypt的vtable+0x30/+0x34调用,用于 密钥序号/版本与当前ITXEncrypt实例一致性 检查;与bufSvrSealEnc字节级解密算法 不是同一条「展开公式」线索,但证明 Seal 链路与ITXEncrypt全局对象绑定。
dword_30209054:平台ITXCore(或等价ITXPlatformCore)指针,在sub_30019CA0等路径上赋值(「platform」字符串、sub_30022100、sub_30021B10等)。
sub_30013110(由sub_300155C0→sub_30015370等链触发)解析components/component/clsname/clsid/dllname/exit-order等节点。- 对每个 component:
Util::Com::GuidFromString读 CLSID,读dllname,并在内部表中登记sub_30012F20生成的条目(含exit-order)。 - 含义:
EDC5158A-…的实现 DLL 不一定写死在KernelUtil;应以运行时platform/components类 XML(及CreateObjectFromDllFile路径)为准。静态仅见KernelUtil导出CreateSvrSeal,DecryptMsg 本体仍可能在 同一 DLL 的 ATL 类或 manifest 指向的另一 DLL。
objdump -p:无CryptDecrypt/BCrypt等现代 WinCrypto 导入条目出现在简短 grep 结果中;ADVAPI32存在(多为 注册表)。strings:未发现DecryptMsg、PerfStand、MachineGuid等与封印直接对应的明文日志串(符号表里仅有CreateSvrSeal/CheckKeyID等导出修饰名)。- 字节搜索:对 XXTEA 常用常量
0x9E3779B9(LEb9 79 37 9e)未在样本中命中(不排除算法常量展开形式不同或代码在其它 DLL)。
- 曾用脚本扫描
.rdata内指向.text的指针 得到大量候选;其中0x31804dac起 一组指针疑似 ATL 对象 vtable(含0x3182e590、0x3182e500… 等),但后续用get_bytes(0x31804dac)时读到的是.text指令字节,说明 VA 与段归属 必须以 IDA / 节表为准,不能单靠「看起来像 VA」的线性偏移。 - 结论:vtable 与
DecryptMsg的最终对应关系 需在 IDA 里对 coclass 逐槽标注 后才能定论;当前阶段 未完成 该最后一步。
imports_query/survey_binary:在当前 IDA 9.2 + MCP 插件环境下触发ida_nalt.enum_import_names回调签名错误(imp_cb参数不匹配),导致 无法一键拉分类 import。enum_import_names手写回调:在py_eval沙箱里 嵌套函数闭包 对外层变量捕获不稳定;改用imp_cb.results=[]挂在函数对象上 等方式可规避(若后续需批量枚举 import)。list_instances/select_instance:多 IDA 实例场景下必须先select_instance(port=…)再调用分析工具,否则会路由到错误 IDB。
| 层级 | 结论 |
|---|---|
| 磁盘语义 | bufSvrSealEnc 存的是 需经 ITXSvrSealCrypto::DecryptMsg(vtable +0x10)处理的密文/封装,不是可直接阅读的明文。 |
| 算法位置 | 对称/哈希的具体循环不在 Common.dll 的 sub_30097FF0 内展开;实现在 ITXIMSvrSealCrypto 实现类中(由 CreateSvrSeal + ITXCore 工厂 给出)。 |
| CLSID | 工厂使用的 CLSID 为 {EDC5158A-8148-401A-9F2B-A72863BCA24A}(KernelUtil unk_318680A4)。 |
| 是否已命名为「AES / RSA / XXTEA」 | 仍无单一 FIPS 式命名。DecryptMsg 本体(§8.6:IM.dll sub_31061A40)走 bufSigSession/bufPwdForConn + CTXCommPack + ITXEncrypt::vtable+12,属 产品内会话封印管道,不是裸 AES/XXTEA 标签。 |
与 DecodeHash/Decode16 的关系 |
DecodeHash/Decode16 在 Common.dll 另有定义(§8.2 / §8.3),且被 IM.dll 多处 非封印 代码调用;已钉死的 DecryptMsg 路径不以 DecodeHash 为主。若盘上某些字段仍是 23 字符哈希外观串,才可能单独走 DecodeHash。 |
与 bufRandKeyEnc 的 XXTEA 信封关系 |
不能等同(sub_30097FF0 对 Seal 只走 vtable+0x10 → sub_310620F0,不经 sub_30002340)。 |
| 密钥从哪来(概要) | 盘上:bufSigSession、bufPwdForConn、bufPwdHashOne、buf16byteSessionKey、cPassSeqID 等与 Matrix/登录 同源;运算侧:TXEncryptMgr::Init → MD5(flags∥16B)→ ITXEncrypt(详见 NOTES.md §15–§16);DecryptMsg 只 组装并投递 到 ITXEncrypt,见 §8.8。 |
IM.dll.i64(优先):IM.dll的.rdataGUID 表(约 VA0x3130782c起含本 CLSID)说明 coclass 与主程序同仓注册 的可能性大。对 实现ITXIMSvrSealCrypto的 CComObject 做 RTTI/ATL 头 或 vtable 扫:定位vtable+0x10的 具体函数地址 并反编译,确认是否 仅 调Encode::DecodeHash/Decode16+ITXBuffer搬运。KernelUtil.dll.i64:仅CreateSvrSeal+ITXCore::vtable+0x1C工厂 引用该 CLSID;PE 内无 其它指向unk_318680A4的指针,KernelUtil 未必承载DecryptMsg体。对锁定的已钉死:见 §8.6(DecryptMsg实现……IM.dllsub_310620F0→sub_31061A40,走ITXEncrypt与bufSigSession/bufPwdForConn,不是 以DecodeHash为主路径)。- 若用户可提供 platform/components 类 XML 或 安装目录清单,与 §8.4 交叉验证 coclass 实际所在 PE。
this+0x0C(即伪代码里的*(this+3))为ITXSvrSealCrypto*。- 解密分支:
(*(int (__stdcall **)(int, int, int))(*(_DWORD *)seal + 16))(seal, outBuf, ctx)—— 第三个参数 来自this+0x5C(this+23若按_DWORD*计)。 - 与先前笔记一致:虚表偏移
+0x10= IUnknown 之后第一个接口方法(本文仍按业务语义称其为DecryptMsg槽位)。
8.2 Util::Encode::DecodeHash(Common.dll,?DecodeHash@Encode@Util@@YAHPAPAUITXBuffer@@ABVCTXStringW@@@Z,入口约 0x30006680)
- 前置条件:宽字符串 长度必须恰好为 23;逐字符映射到字母表
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ~@$%(){}[]_` 中的 索引(小写先转大写再查)。 - 输出:通过
Util::Data::CreateTXBuffer分配ITXBuffer,再vtable+0x38(字节 56)写入 固定 16 字节 载荷。 - 算法本质:在索引序列上做 类似逐位借位/混合进制(反编译中可见与 48 相关的乘加),把 23 个「数位」压成 16 字节;属 自定义编码,不是 分组密码或 XXTEA。
- 偶数长度 十六进制宽字符串 → 二进制:字符按
| 0x20规范化,0-9/a-f拆 半字节 拼字节串,再写入CTXBuffer/ITXBuffer。 - 即工程语境下的 「Decode16」= 十六进制解码。
- 裸扫:同一 16 字节(LE)不仅存在于
KernelUtil的unk_318680A4,也存在于IM.dll文件偏移0x30782c(VA0x3130782c),且 前后均有其它 GUID(hex 上下文见本会话xxd),符合 进程内 COM/TLB 注册块 特征。 Common.dll:在该样本上 未发现 该 CLSID 原始字节串。- 推论:封印 coclass 的实现与 IM 主模块强相关;仅凭
KernelUtil::DllGetClassObject不能断言DecryptMsg体在 KernelUtil。
IM.dll同时导入Util::Encode::DecodeHash、Decode16、Encode16与KernelUtil::CreateSvrSeal(objdump -p已核对)。DecodeHash/Decode16被 大量非封印业务 调用(图片、FACEROAM_*等)。封印 COM 路径(§8.6)核心不在DecodeHash;§8.2/§8.3 仍描述磁盘上其它「哈希外观串」场景可用的Common.dll原语。
IDA:已 select_instance(port=13337)(IM.dll.i64)。
- CLSID 第二处:
find_bytes命中0x31319520(与0x3130782c并列);0x313194fc处有指向0x31319520的数据引用(ATL 对象映射 / registry 线索)。 - 实现对象 vtable:
off_313194E0(构造里sub_31062210写入*this = &off_313194E0)。 - 槽位(节选):
+0x00:sub_310621F0(QueryInterface 一类)+0x04:0x312902B0(AddRef 桩)+0x08:sub_310621C0(Release 一类)+0x0C:sub_31062010—— 内部call sub_31061A40前push 1(与push 0分支相对,属 另一接口方法,如 EncryptMsg / Init 语义需再对符号表)。+0x10:sub_310620F0——call sub_31061A40前push 0(解密侧);即Common.dllsub_30097FF0里(*(seal+16))(seal, …)命中此处时,对应本槽。
sub_310620F0(stdcall,retn 0Ch):校验arg_4、arg_8;this+0x18为空时才继续;ecx=this调sub_31061A40,栈上第二参为0,第三参来自arg_4(输入ITXBuffer);成功时对this+0x18做AtlComPtrAssign(arg_8)。sub_31061A40(核心):从unk_313086F0打开ITXData,读bufSigSession、bufPwdForConn(宽键名与sub_31002AD0字面量一致);把bufSvrSealEnc侧传入的缓冲区 与上述字段 打包进ITXData(含cType/cAppType/cKeyID/bufData/bufOrgBuf、CTXCommPack等);最终(*(*ITXEncrypt)+12)(…)—— 即ITXEncrypt虚表偏移0x0C,四参数调用(与NOTES.md里ITXEncrypt+12 的讨论一致)。主线是「会话封印包 + 加密管道提交」,不是Util::Encode::DecodeHash单独解码器。
- 路径:
/msg2.0/Matrix.dat,约 168 字节;文件头54 44 01 01(TD…),后为 UTF-16 属性块 与 二进制段(xxd可见…c1 c3 c1 c2…起的一截密文状数据)。完整键级解析需ITXDataStorage读取逻辑或专用解析脚本;与bufSvrSealEnc字段需对照TXEncryptMgr::CreateDataStorage打开的同一存储语义。
结论分两层,避免混为一谈:
-
DecryptMsg/sub_31061A40里没有单独生成一条「封印专用密钥文件」
它做的是:从unk_313086F0打开的ITXData读出bufSigSession、bufPwdForConn(与同一份Matrix.dat/ UserData 存储 上的其它键并列),再与bufSvrSealEnc读出的ITXBuffer一起封装进ITXData,最后调用ITXEncrypt的vtable+0x0C(+12)。也就是说:封印路径消耗的「密钥相关字节」一部分直接来自盘上这些键,另一部分来自ITXEncrypt对象内部已在别处初始化好的状态。 -
真正做对称变换(如 NOTES.md §16 所述 MD5 派生 + XXTEA + 信封)时用的「根状态」在
Common.dll的ITXEncrypt上TXEncryptMgr::Init:对flags(4B)∥ payload(16B)做 MD5,得到后续QueryEncrypt/ITXEncrypt使用的密钥素材链(见NOTES.md§16.1)。- 16B payload 的来源在同一笔记
§15:bufPwdHashOne(口令二次哈希类二进制)、登录拿到的buf16byteSessionKey等 账号/Matrix 字段,经IM.dll侧TXEncryptMgr::Init灌进去——不是DecryptMsg里突然从空气里来的。 KernelUtil::CheckKeyID(..., ITXEncrypt*):把Matrix上的cPassSeqID(密钥序号)与ITXEncrypt内部版本对齐(见NOTES_BUF_ENC.md§2.4),防止 盘上封印数据与当前内存里的密钥代数不一致。
一句话:盘上与封印同食的材料包括 bufSvrSealEnc、bufSigSession、bufPwdForConn、bufPwdHashOne、buf16byteSessionKey、cPassSeqID 等;真正展开对称算法的密钥派生在 TXEncryptMgr::Init → ITXEncrypt(Common.dll),IM.dll 的 DecryptMsg 只是把 Matrix 里若干缓冲塞进 ITXEncrypt 管道,而不是单独持有一条名曰「SvrSealKey」的常量。
NOTES.md§5 / §15 / §16:Matrix.dat、TXEncryptMgr、bufSvrSealEnc、PerfStand.DecryptMsg、XXTEA/MD5Init、ITXEncrypt+12 与ITXSvrSealCrypto+16 讨论。DecryptMsg实现:已在本文 §8.6 与IM.dlloff_313194E0[+0x10]→sub_310620F0→sub_31061A40对齐;bufRandKeyEnc/sub_30002340XXTEA 信封 仍为 另一条链(见NOTES.md§16)。