一封退货邮件的陷阱:“开箱视频”背后的 WPS、隐藏 DLL 与恶意加载链
本文摘要
一封以商品损坏为由要求退货的邮件,让收件人通过 Google 短链接查看开箱视频,最终下载到的却是装着 WPS 程序和隐藏 DLL 的压缩包。本文从邮件和浏览器实景出发,拆解文件名伪装、DLL 侧加载、加密命令、Python 与 .NET 载荷之间的关系,再用断网虚拟机中的运行记录核对持久化写入和域名解析尝试。结合 isMalicious 的域名信誉截图,区分已经观察到的危险与尚未确认的窃取、外传行为,并给出普通收件人识别邮件、核对文件和误运行后的处置方法。
AI 辅助声明|AI agent coding 辅助创作
Codex 辅助编写取证脚本、分析静态与隔离运行记录,并参与本文整理和写作。
安全警告:不要点击文中的可疑地址、下载样本或运行其中的程序。
我有相关技术经验,日常使用 Linux 桌面,才另外准备隔离环境分析这封邮件并写下本文。
样本执行只发生在断网虚拟机里的 Wine 中,宿主个人目录和账号会话没有共享进去。
Linux 和 Wine 都不提供“随便运行 Windows 恶意程序也安全”的保证,虚拟机也有剩余风险,请勿照着尝试。

AI 生成的封面插画:以查看售后视频为由,引向 EXE 与 DLL 加载链;不是取证截图。
一封声称“商品损坏,想申请退货”的邮件,最后交来的是一个 Windows 程序包。
最显眼的文件名里写着 mp4,内部身份却是 WPS PDF;它启动时还会加载旁边的 DLL,继续解包、运行 Python,并尝试装入 .NET 代码。
这就是我在 2026 年 11 月 4 日收到的那封邮件。
对方没有索要密码,也没有催我付款,而是请我先看开箱视频和照片,再处理售后。
对于每天处理客户邮件的人,这个请求很像正常工作的一部分。
沿着它给出的链接往下看,危险的转折发生在“查看材料”被换成“启动程序”的时候。
售后故事给外链下载找好了理由
邮件标题是「有關產品無法使用的退貨查詢」。
发件人显示为「張慧敏」,声称刚收到的商品内部严重损坏、无法使用,已经保留开箱视频和照片,希望安排退货、换货或退款。
材料没有附在邮件里,因为“文件太大”,需要打开外链查看。

原始收件画面:退货诉求与“文件太大,请查看链接”连在一起。
| 邮件字段 | 实际内容 |
|---|---|
| 收件时间 | 2026-11-04 16:16:19,东八区 |
| 主题 | 有關產品無法使用的退貨查詢 |
| 显示名称/发件地址 | 張慧敏/noreply@appsheet.com |
| 业务信息 | 未提供订单号和商品名称 |
| 附件 | 无 |
| 链接入口 | share[.]google/psgMaz996t9HspoPs |
| 页脚 | Sent from AppSheet |
这封信借用了售后处理中很自然的一步:先看证据,再决定怎么解决问题。
它缺少订单号和商品名称,却给了收件人一件可以立刻做的事,也就是打开链接。
发件地址里的 AppSheet 属于正规服务,但地址本身无法证明这是一位真实客户。
AppSheet 官方文档说明,自动邮件的主题、正文和发件人显示名称可以配置。
使用正规平台发送邮件,与邮件中的售后故事真实,是两件需要分别核对的事。
我没有取得这封信的完整原始认证头,因此不判断用于核验邮件来源的 SPF、DKIM、DMARC 认证是否通过。
Google 短链接把浏览器带到了第三方下载站
邮件里的 share.google 确实是 Google 使用的短链接域名。
Google 官方中文说明提到,Google 应用分享网页时会生成 share.google 或 search.app 链接。
这能解释入口为什么看起来熟悉,最终目的地仍要沿跳转记录检查。
这次短链经过 Google 的几次重定向后,进入了 filehub.freshpartners[.]co.nz 的下载路径。
下文可疑地址里的 [.] 表示点号,hxxps 表示已去活化的协议名,避免文章形成可直接点击的样本入口。
hxxps://share[.]google/psgMaz996t9HspoPs → www.google.com/share.google?q=… HTTP 302 → www.google.com.hk/url?… HTTP 302 → www.google.com.hk/share.google?q=… HTTP 301 → filehub.freshpartners[.]co.nz/dl/ Complete_Item_Assessment_mp4/ca50…b467?… HTTP 302301 和 302 是服务器要求浏览器转到其他地址的响应,这些中间节点没有各自呈现一张网页。
同一台 Linux,两次会话得到了不同内容
我先用 Linux 浏览器的默认客户端标识访问短链,最后看到的是微软 OneDrive 产品介绍页。
它没有展示开箱视频,也没有要求我更换操作系统。

第一份浏览器结果:真实的 OneDrive 产品页,邮件里的售后材料并未出现。
另一次会话把请求中的客户端标识改成 Windows Chrome,下载站就交付了 ZIP 附件,没有出现视频播放器。
两次浏览器实际都运行在 Linux 上,这里的 Windows 只是请求头中的标识。

另一份浏览器结果:Chrome 已记录下载;自动化保存时使用内部标识,服务器给出的文件名见下方响应头。
ZIP 大小是 138,900,867 字节,约 132.47 MiB,文件名仍在强调 mp4。
HTTP/2 200Content-Type: application/octet-streamContent-Length: 138900867Content-Disposition: attachment; filename="Complete_Item_Assessment_mp4_838a86d1.zip"Cache-Control: no-store, no-cache, must-revalidate, max-age=0这两份结果能证明下载站返回了不同内容,尚不能证明它只按操作系统筛选。
浏览器条件:两次请求并非只改变了操作系统
访问发生在 2026 年 11 月 4 日 18:08 左右,使用系统安装的 Chrome 157 无头模式、独立会话和 1440 × 1000 视口。
User-Agent 是浏览器随请求发送的客户端标识,通常包含操作系统与浏览器版本。
| 请求条件 | 当时结果 |
|---|---|
默认 Linux:X11; Linux x86_64、HeadlessChrome/157… |
下载站重定向到 OneDrive 产品页 |
Windows 伪装:Windows NT 10.0; Win64; x64、Chrome/142…,sec-ch-ua-platform 为 "Windows",但 sec-ch-ua 品牌版本仍为 157 |
跳到带 f= 参数的下载地址,返回 ZIP;自动化报告 Download is starting |
| 后续用独立 HTTP 客户端补查,版本统一为 Chrome 157 | 两种平台标识都得到前往 OneDrive 产品页的重定向 |
Cookie、版本组合、其他请求特征、访问时序或服务端规则都可能影响结果,现有记录没有隔离这些变量。
OneDrive 那张正常网页也不能因此被当成恶意下载页;需要分析的是前面的下载站,以及它实际交付的文件。
分支是两次会话的记录,不是对服务器筛选算法的还原。
文件名带着 mp4,扩展名却是 exe
我先读取压缩包目录、提取字节分析,没有在日常桌面双击程序。
包内共有 791 个条目,其中 742 个文件、49 个目录;与下载名对应的目录顶层,只有一个 EXE、一个 DLL 和一个目录。
EXE 的完整名称是 Complete_Item_Assessment_mp4.exe,表中简写为 …_mp4.exe。
| 顶层内容 | 字节数 | 内容与 ZIP 属性 |
|---|---|---|
…_mp4.exe |
1,321,728 | Windows 程序;普通归档,0x20 |
krpt.dll |
126,144,000 | 启动时加载的 DLL;隐藏+系统,0x06 |
x/ |
目录 | 后续载体;目录+隐藏+系统,0x16 |
隐藏与系统属性来自 ZIP 中央目录的实际记录。
如果解压工具保留属性,Windows 又按相关设置隐藏这些文件,收件人最容易看见的就会是那个名称带着 mp4 的 EXE。
读这个文件名时,应看最后的 .exe,而不是中间的 _mp4。
扩展名也可以被伪装,真实格式还要结合文件头和内容核对;这里已经确认交付的是 Windows 程序。
微软的扩展名说明也把 .exe 与 .mp4 分别列为可执行程序与视频类型。
到这一步,普通收件人已经有充分理由停下:售后照片和视频,没有解释为什么需要运行 EXE。

真实 HTTP 与文件分析输出的排版摘录,方便对照文件名、大小和 DLL 依赖;不是下载站页面。
这个 EXE 内部仍然叫 WPS PDF
读取程序内部字段后,可以看到 WPS PDF、WPS Office 和原始文件名 wpspdf.exe。
它保留了办公软件的身份,外部文件名却被改成了“商品评估 mp4”。
更关键的是,EXE 的导入表明确依赖同目录里的 krpt.dll。
导入表记录程序启动时需要装入的外部模块,这个依赖把 EXE 与旁边的 DLL 接到了一起。
DLL: krpt.dllImported symbol: ?_force_link_krpt@@YGXXZImport Address Table RVA: 0x00097260DLL 是程序调用的功能模块,装入进程时可以执行初始化代码。
将一个现成 EXE 与恶意 DLL 放在它会加载的位置,借 EXE 启动 DLL,通常称为 DLL side-loading,DLL 侧加载。
微软的 DLL 安全说明指出,攻击者控制 DLL 搜索位置时,程序可能加载非预期的库,并以宿主进程的权限执行其代码。
具体搜索顺序受应用和加载方式影响;这个样本除了有明确导入依赖,后面的运行日志也记录到了该 DLL 的实际装载。
EXE 内部资源与签名信息的验证范围
| EXE 内部字段 | 实际值 |
|---|---|
FileDescription |
WPS PDF |
OriginalFilename |
wpspdf.exe |
ProductName |
WPS Office |
FileVersion / ProductVersion |
11,2,0,9363 |
| 公司字段 | Zhuhai Kingsoft Office Software Co.,Ltd |
| 证书表中的签名者证书主体 | Zhuhai Kingsoft Office Software Co., Ltd. |
分析工具读取到了 Authenticode 证书结构,但没有验证当前文件的摘要、证书链和时间戳。
因此,这里能确认文件携带金山办公的证书信息,不能只凭这一点宣布当前签名有效。
即使 EXE 的签名有效,也不会替同目录的 DLL 或整包内容作安全担保。
隐藏 DLL 把“看文档”接成了多层执行
沿着 DLL 的初始化路径分析,程序打开了 x 目录中的一个伪装 PDF,解码其中的数据,再把恢复出的命令交给进程创建函数。
本文使用 REA(Reverse Engineer Anything)接口读取文件结构,并通过 Hopper 查看原生代码的反汇编与伪代码。
反汇编把机器指令转换成可读指令,反编译尝试还原接近高级语言的表达式,二者都不等于取得了作者的原始源码。
这条路径解释了为什么只看 WPS 的名称会漏掉问题:用户启动的是 EXE,后续动作已经转入 DLL 的初始化代码。
类框按实际文件划分职责,用于讲解组件关系,并非声称这些二进制定义了图中的同名类。
小 PDF 装命令,PNG 负责解压,更新程序其实是 Python
DLL 读取的文件路径是 x\수박_유튜브시청기록_2015-12-10.pdf,总共只有 1379 字节,没有 PDF 文件头。
它使用 RC4 流加密保存命令:文件前段是密文,后段是密钥,最后四字节给出密钥长度。
| 文件范围 | 字节数 | 用途 |
|---|---|---|
| 开头 | 1164 | 待解码的命令字节 |
| 随后 | 211 | RC4 密钥 |
| 最后 | 4 | 小端整数,值为 211 |
只对字节执行同样的解码运算,就能恢复连贯的 UTF-16 命令文本,不需要运行恢复出的命令。
命令先进入 x 目录尝试打开 document.pdf,随后调用后缀为 PNG 的程序,解开 DAT 归档,再启动解包出来的 MicrosoftEdgeUpdate.exe。
逐一核对格式后,PNG 外观的文件被识别为 RAR 解压程序,DAT 是加密归档,document.pdf 则是 Word 容器。
名字像浏览器更新组件的 MicrosoftEdgeUpdate.exe,内部身份是 Python 解释器;它随后要解释的另一个 PDF 外观文件,也是一份混淆脚本。
伪装文件的完整名称、格式与版本资源
PE 是 Windows 可执行文件常见的格式,真实类型来自文件内容,而非它外面的 PDF 或 PNG 后缀。
| 外观或名称 | 实际内容 | 在链中的作用 |
|---|---|---|
1379 字节、以 2015-12-10.pdf 结尾的文件 |
RC4 命令载体 | 提供第一段命令 |
ZB_2022년_산책중넘어짐기록_해봤자.png |
PE32+ x64 控制台程序,版本资源为 WinRAR/Command line RAR/7.20.2 | 解压器 |
빨래건조현황표_20230226_953.dat |
加密 RAR5 归档 | 携带下一阶段运行环境 |
document.pdf |
含 word/document.xml 的 OOXML Word 容器 |
被尝试作为文档打开 |
MicrosoftEdgeUpdate.exe |
PE32 x86 GUI 程序,版本资源为 pythonw.exe/Python Software Foundation/3.14.3 |
Python 解释器 |
解包后的 WD_도토리_빨래건조현황표_2019.pdf |
1,828,010 字节的 XOR 混淆 Python 源码 | 持久化和后续加载逻辑 |
| Python 内嵌数据 | 433,152 字节的 PE32 .NET Framework v4.0 载荷 | 从内存装入 CLR |
文件内部的版本与厂商字段是识别线索,不代表已经逐个验证原厂完整性或有效签名。
MicrosoftEdgeUpdate.exe 旁边还有 python314.dll、python314.zip 和 python314._pth,与Python 官方中文文档描述的嵌入式发行结构相符。
在这份投递包里,解释器被改成浏览器更新组件的名字,又被用来解释另一个 PDF 外观文件里的代码。

按真实文件字节、可信解压器与版本资源整理的证据摘录,不含口令和完整执行参数。
CLR 是执行 .NET 代码的公共语言运行时,Python 在这里又承担了调用 CLR、装入下一阶段程序的宿主角色。
“载荷”指这份等待执行的下一阶段代码,不表示它的完整功能已经被还原。
这份 .NET 文件的字节已完整恢复,但元数据存在重复行和无效索引,REA 只完成部分读取;图里的 partial 指分析覆盖范围,不是只恢复了一部分文件。
这张图汇总静态恢复出的文件关系与脚本控制流,运行到哪一步,由后面的日志核对。
脚本还准备在下次登录继续运行
恢复出的 Python 源码包含大量算术、随机名称和间接表达式。
我只对白名单内的常量和字节运算做折叠,把源码当作数据整理,再用隔离运行记录核对结果。
整理后的逻辑尝试创建计划任务,并从两处用户登录入口调用它。
| 目标 | 代码中的作用 |
|---|---|
计划任务 MicrosoftEdgeUpdateTaskMachineCore__ |
通过任务计划程序 COM 接口创建,指向改名 Python 和脚本参数 |
HKCU\Environment\UserInitMprLogonScript |
写入调用该任务的字符串 |
HKCU\Software\Microsoft\Windows\CurrentVersion\Run |
再增加一处登录启动入口 |
| 解包目录及文件 | 增加 Hidden、System 属性 |
| 标记值与命名互斥量 | 控制已运行状态和单实例 |
微软对 Run 键的说明确认了它在用户登录时启动程序的用途。
这种为后续登录留下启动入口的做法称为持久化。
它让风险超过了一次误打开:关闭眼前窗口,并不一定移除了重新启动代码的入口。
脚本另有针对扫描接口的内存修改逻辑,现有记录没有确认其在实验中生效,本文不展示修改字节或绕过步骤。
原生逆向细节:DLL 入口、API 哈希与进程创建
导出函数是空的,不代表 DLL 没有动作
用 REA 接入 Hopper 对 krpt.dll 做反汇编和关键函数反编译后,一个容易误判的地方出现了:大量导出落到 0x100179e0,那里只是一个返回用的空桩。
如果只看这个导出,很容易以为 DLL 什么也没做。
但 DLL 还有加载入口。
这个样本的入口位于 0x1001c728,经运行库初始化包装后进入 0x10001000;后者在进程附加条件下进入文件读取和后续处理路径。
“进程附加”指 DLL 被装入进程时收到的初始化通知,不需要用户再单独点击一次 DLL。
| 地址或位置 | 静态分析看到的行为 |
|---|---|
0x1001c728 |
PE 入口,转入初始化包装 |
0x1001c5ef |
运行库包装中存在到 0x10001000 的调用 |
0x100167c7 |
比较初始化原因是否为 1,即进程附加 |
0x1001687a |
调用 0x10019280,准备动态解析的 API 地址 |
0x10016959、0x10016ca7 |
经解析后的指针调用 CreateFileW |
0x100172ca |
调用 ReadFile,读取数据到缓冲区 |
0x100176aa |
调用 0x10018160,含 RC4 式状态表初始化、交换和异或处理 |
0x1001778b |
将同一缓冲区传入 0x100179f0 |
0x10018100 |
在 0x100179f0 内,间接调用进程创建 API |
这些是按 PE 首选基址整理的静态地址和调用关系。
Wine 观察中,krpt.dll 实际被映射到 0x7B590000,不能把表里的静态地址原样当成运行时断点地址。
为什么导入表里找不到完整行为
krpt.dll 的普通导入表主要是 KERNEL32.dll 的运行库相关函数,没有直接列出上面所有关键 API。
它另外实现了按哈希值查找函数地址的逻辑。
简单说,程序不在这个位置写出容易搜索的 CreateProcessW 字符串,而是保存一个数字,运行时再根据函数名称计算结果、寻找对应地址。
我把反汇编中的哈希运算单独转写成只处理字符串的 JavaScript,核对候选 API 名称;这不需要执行样本。
| 哈希常量 | 对应的候选函数名 | DLL 保存地址 |
|---|---|---|
0xc563eb80 |
CreateFileW |
0x1004d2a0 |
0x35d5ea3a |
CloseHandle |
0x1004d29c |
0xacb08688 |
ReadFile |
0x1004d298 |
0xa1059b95 |
GetFileSizeEx |
0x1004d294 |
0x628857b4 |
CreateProcessW |
0x1004d290 |
哈希可能碰撞,所以还要核对调用参数,不能凭一个数字直接给任意间接调用命名。
最后一项同时吻合了进程创建函数的十个参数、启动信息结构和进程信息结构,支持把这个调用识别为 CreateProcessW。
下面是把关键调用整理成易读形式后的节选,变量名是为讲解重命名的,省略了大量常量运算和包装,不是恢复出的原始 C 源码,也不是可直接编译的完整程序。
// 0x10019280 中的解析结果写入 0x1004d290。// 0x100179f0 的调用处使用这个函数指针。STARTUPINFOW startup = {0};startup.cb = 0x44; // 32 位 STARTUPINFOW 的大小PROCESS_INFORMATION process = {0};
resolved_CreateProcessW( NULL, // 由命令行中的首个项目决定启动目标 decoded_buffer, // 上游读取、处理后传来的缓冲区 NULL, NULL, FALSE, 0x08000000, // CREATE_NO_WINDOW NULL, NULL, &startup, &process);CreateProcessW 的官方定义是创建进程和主线程。
其中 0x08000000 对应 CREATE_NO_WINDOW,对于控制台程序,可以不创建控制台窗口;它不等于对任何类型程序都能“完全隐身”。
这个链条已经明显超出了“显示商品照片”的用途:旁加载的 DLL 在读取数据、进行解密式处理,并准备以处理后的内容启动另一个进程。
继续恢复初始化代码构造的路径后,缓冲区的来源也明确了:它来自 x 目录里一个伪装成 PDF 的小文件,解码后是一段 cmd.exe 命令。

REA 静态反编译输出节选:哈希解析写入函数指针,后续以 0x08000000 标志调用;变量保留反编译器命名。
图表示静态代码安排的顺序;后文用隔离 Wine 的进程和文件记录核对它的运行结果。
大 DLL 的附加数据:高熵不等于已经找到隐藏功能
krpt.dll 大小为 126,144,000 字节,但 PE 最后一个节的原始数据结束于文件偏移 0x58600,也就是 361,984 字节。
剩余 125,782,016 字节位于节表所描述的数据范围之后,通常称为 overlay,附加数据区。
按这段数据计算,零字节约占 0.3902%,香农熵约为 7.999998 bit/byte。
香农熵在这里衡量字节分布的不确定性,每字节理论上限为 8 bit;这个结果接近上限,不能描述为一大堆零填充。
高熵只能说明字节分布接近均匀,压缩、加密或随机数据都可能产生这种现象,单靠它不能断言某种恶意家族,也不能证明每一个附加字节都有用途。
命令载体位于 x 目录,不能把这个目录笼统当成无害诱饵。
巨大 overlay 的具体用途仍未确认;已经恢复的执行链,依赖的是几个小载体与加密归档。
断网运行核对:后台解包、启动与注册表写入确实发生了
静态分析指出了执行方向,运行记录则用来回答其中哪些动作真的发生。
我把原始投递包放进断网 KVM 虚拟机里的 Wine 观察,启动前核对 EXE、DLL 的哈希,没有修改载荷或修补它的调用逻辑。
Wine 提供 Windows 兼容环境,但新建一个 Wine prefix 并不能把未知程序安全地关住。
prefix 是虚拟 Windows 目录、注册表和配置的一组文件;Wine 仍可能把文件操作映射到 Linux 路径,删除 Z: 盘符也不能单独阻断这种访问能力。
因此,隔离边界放在虚拟机及外层限制上。
宿主个人目录、账号会话、图形与剪贴板均未共享给样本。
实验环境、观察窗口与隔离核验条件
| 层次 | 实际实验条件 |
|---|---|
| 虚拟机 | QEMU 10.2.1,Ubuntu 包版本 1:10.2.1+ds-1ubuntu3.3,KVM、q35、2 vCPU、2560 MiB 客体内存 |
| 客体 | Debian 13,内核 6.12.115+deb13.1-cloud-amd64 |
| Wine | Debian wine-10.0 (10.0~repack-6);fresh win64 prefix,兼容 32 位和 64 位程序 |
| 网络 | 运行样本时没有虚拟网卡,外层也有独立网络命名空间;客体只有 loopback、没有外部路由 |
| 文件交换 | 通过只读样本 ISO 输入,写入落在本次客体临时盘;没有宿主 home、共享目录、剪贴板、显示或 DBus 套接字 |
| 外层权限 | QEMU 为非特权进程,capabilities 为零,NoNewPrivs 与 seccomp 开启 |
| 资源边界 | 外层内存上限 5 GiB、96 tasks、CPU 配额 180%,独立运行上限 5 分钟 |
| 观察窗口 | 第一轮原始 EXE 最长观察 60 秒,补齐 CLR 后为 90 秒;启动前校验 EXE、DLL 哈希 |
| 观察记录 | 客体 root 保存 strace,样本以普通 analyst 用户运行;脱离控制终端,输入为 /dev/null |
准备合法系统和 Wine 时允许安装软件,样本没有挂载到准备阶段;实际样本进入环境后,网络设备已移除。
无害探针先核对了客体不能读取或写入未映射的宿主哨兵,不能连接准备阶段的网关或宿主控制端口,并分别成功运行 Wine 的 32 位、64 位内置命令程序。
外层检查也确认了运行中的 QEMU 看不到宿主个人目录,独立超时能终止整个 scope。
QEMU 的安全文档把硬件虚拟化、最小权限、命名空间和资源约束列为相关隔离机制,也明确指出虚拟设备本身仍然是攻击面。
这些设置减少了暴露面,不构成“绝对不可能逃逸”的保证。
第一轮已经走到了 Python 和持久化写入
原始 EXE 启动后,Wine 加载了 krpt.dll,随后出现 cmd.exe、伪装解压器和改名的 Python 程序。
document.pdf 没能打开,日志中 winebrowser 报告找不到合适应用,后续解包与 Python 启动仍然继续。
| 核对位置 | 运行记录 |
|---|---|
| DLL 与载体 | C:\sample\krpt.dll 作为 native 模块装入;系统调用记录成功打开命令载体 |
| 解包与 Python | PNG 外观的程序作为 native 模块启动;Public 下出现解包目录;改名程序加载 python314.dll、_ctypes.pyd,随后以标记参数再次启动自身 |
| 登录启动入口 | 用户注册表新增 UserInitMprLogonScript,以及 Run 下的任务触发值 |
| 计划任务 | 调用了 TaskFolder_RegisterTaskDefinition,但 Wine 标为 stub,无法确认任务已注册成功 |
| 下一阶段 | CLR 宿主报告 Wine Mono is not installed,本轮没有确认 .NET 载荷执行 |
native 在这些 Wine 日志里表示按外部 Windows 模块加载,与 Wine 的 builtin 内建实现相对,不表示这是原生 Windows 上的测试。
stub 表示相关接口在该环境中是桩实现,所以“调用过任务接口”和“下次登录一定能启动”之间还缺一段证据。

来自客体运行记录的排版摘录;持久化值写入、任务接口的 stub 和 CLR 错误分别保留。
缺少 Wine Mono 是实验环境的兼容性终点,不能据此推断它在 Windows 上也会停住。
Python 脚本虽然包含虚拟机检查函数,但对应的主入口提前退出条件经常量折叠为 if 0,现有记录也不支持把这次停止归因于反虚拟机检测。
补齐运行时后,Mono 与微软 CLR 得到不同结果
我又准备了 Wine Mono 和微软 .NET Framework 4.8 两种环境,先用无害的 x86 探针核对运行时,再观察原始投递链。
Wine Mono 是 .NET Framework 的替代实现,与微软原版 CLR 需要分开看待。
| 环境 | 确认运行时的依据 | 样本的观察终点 |
|---|---|---|
| Wine Mono 9.4.0 | 安装包哈希与 Wine 10.0 源码一致;无害探针运行成功 | 内存程序集 Hwteqj 装入;方法追踪看到了初始化执行,随后出现无效 IL 和类型初始化异常,未观察到已知主入口进入 |
| 微软 .NET Framework 4.8 | 官方离线包;无害探针成功;实际加载 native mscoree.dll、clr.dll,注册表 Release 为 528049 |
原链 Python 进程反复请求解析一个域名;伴随周期性异常,达到 90 秒上限后由实验机制终止 |
Wine Mono 的版本与安装包哈希可对照 Wine 10.0 安装源码,微软运行时来自 .NET Framework 4.8 官方下载页。
Mono 中确认执行的是初始化路径;微软 CLR 中确认的是模块加载和该进程的域名解析请求,两者都不足以宣布最终功能完整运行。

两种运行时的结果并列展示;DNS 报文来自断网客体记录。
CLR 技术细节:探针、参数回退、初始化异常与终止方式
Python 通过 CLR v4.0.30319 和 COM 接口加载程序集;可以对照 CLRCreateInstance 的官方说明理解宿主接口的获取方式。
两种实现都能让无害探针打印下面的结果,但相同的版本字符串,不代表它们能以相同方式处理这份经过保护和混淆的程序集。
BENIGN_CLR_OK Version=4.0.30319.42000 PointerBytes=4 System=v4.0.30319BENIGN_REFLECTION_OK mscorlibMono 中,初始化已经开始,主入口仍未证实
原始加载器先带着一个 string[] 参数调用入口,但 REA 读出的入口签名是无参数的 static void。
方法追踪确实先记录到 TargetParameterCountException,不过加载器并没有停在这里:它原本就有一次无参数调用的回退。
回退以后,日志出现了 <Module>:.cctor,以及名称以 <Module>{…} 开头的类型初始化器,说明已经执行到托管初始化代码。
随后,Mono 在一个混淆方法的 IL_0229: stloc 3 位置报告 InvalidProgramException,后面又出现了 TypeInitializationException 和 TargetInvocationException。
EXCEPTION handling: System.Reflection.TargetParameterCountExceptionENTER:c <Module>:.cctorENTER:c <Module>{…}:.cctorEXCEPTION handling: System.InvalidProgramException: Invalid IL code ... RMFVJx67UD (bool&): IL_0229: stloc 3EXCEPTION handling: System.TypeInitializationExceptionEXCEPTION handling: System.Reflection.TargetInvocationException<Module>{…} 是原日志中某个类型名的缩写,不能由这个名字推断它是另一个程序集;节选只保留调用顺序和异常类型。
完整追踪中没有出现已知入口 jyKqla8aC 的方法进入记录。
所以,这次能确认的是“初始化代码开始执行后失败”,还不是“最终恶意功能全部跑完”。
微软 CLR 的周期异常与实验终止
在另一轮微软 .NET Framework 4.8 对照中,Wine 日志里约每五秒出现一组 0x80131604 与 0x80131500 异常信息,分别对应反射目标调用异常和一般异常。
它们是异常派发记录里的信息字段,不是完整的 COM 返回值清单;缺少内部异常和托管调用栈,无法仅凭这个周期定位失败方法,也不能认定是 C2 重连。
原版 CLR 实验达到 90 秒观察上限后,由实验的超时机制终止,最终 launcher 返回 137,不是程序自然退出。
实验结束后,Wine 进程停止,客体关机,QEMU 退出,宿主上用于检查文件变化的哨兵文件哈希保持不变。
域名出现在日志里,但记录停在本地 DNS
微软 CLR 那轮观察中,原链重新启动的 Python 进程向客体本地解析器 127.0.0.53:53 请求了 tea[.]vexexo[.]com 的 A 和 AAAA 记录,分别用于寻找 IPv4 和 IPv6 地址。
这些请求都没有得到地址答案。
回复码是 5,按 RFC 1035表示 REFUSED。
拒绝来自客体本地 DNS 解析器,样本没有因此成功联系上域名对应的外部服务器。
DNS 记录定位了待解析的名字,没有给出上层协议、请求路径或传输内容。
它是这份样本的一条候选通信线索,尚不能被叫作已经确认的上传地址。
另行查询的公开记录与第三方信誉
样本观察结束后,我另行查询了公共 DNS、域名登记与路由信息。
以下是 2026 年 11 月 4 日晚取得的公开快照,与样本在客体里产生的流量是两组记录,之后也可能变化。
| 查询对象 | 当晚结果 | 解释范围 |
|---|---|---|
tea[.]vexexo[.]com 的 A 记录 |
185.139.214.102 |
公共解析器另行取得的地址,客体实验中并未解析成功 |
vexexo[.]com 的权威名称服务器 |
tani.ns.cloudflare.com、leland.ns.cloudflare.com |
使用这些名称服务器,不足以证明内容由 Cloudflare 托管或代理 |
| IP 网段与路由 | 185.139.214.0/24,登记网络名称 AlexHost,路由 AS200019 |
网络资源记录,不等于攻击者身份或机器实际所在地 |
记录可以分别对照 Google 公共 DNS、Verisign 域名登记、RIPE 地址登记及路由信息;这些是公开信息服务的查询入口。
同一天晚上的 isMalicious 域名报告还把这个名字标为 Medium Risk(中等风险)。

2026 年 11 月 4 日查询时的原始页面截图,评级与收录状态可能变化。
| 页面字段 | 当时内容 |
|---|---|
| 风险等级 | Medium Risk |
| 名单和来源 | 8 份名单、7 个来源;同一发布者或镜像按一个来源计数 |
| 分类 | malware、phishing、c2、malicious、threat-intel |
| 最近出现在名单中 | 2026-11-04 |
| 数据集首次记录 | 2026-08-28 |
| 页面记录更新日期 | 2026-10-20 |
这份聚合记录表明该域名已经出现在第三方威胁情报中,多个名单也可能共享上游数据,不能把 7 个来源直接当成 7 次独立样本分析。
其中 C2 指 Command and Control,即恶意程序与控制端之间的指令和通信机制。
名单标签提供了调查方向,不能代替这份样本的通信协议还原,更不证明实验中发生了数据上传。
已确认的危险,已经足以拒绝这个包
这份包将售后图片、视频的查看请求,变成了改名 EXE、DLL 侧加载、解包、Python 和内存载荷加载的执行链。
运行记录还确认了两处登录启动值写入,危害判断不必等到恶意软件家族有了名字才成立。
| 证据层次 | 可以得出的结论 |
|---|---|
| 邮件与下载 | 外链实际交付可执行程序包,与所谓开箱视频不符 |
| 文件与静态代码 | EXE 依赖旁边 DLL,DLL 解码命令,脚本包含持久化和 .NET 加载逻辑 |
| 隔离运行 | 实际发生 DLL 装载、解包、Python 启动和自重启,以及用户注册表写入 |
| 兼容性与网络边界 | 计划任务注册成功未证实;Mono 初始化失败;微软 CLR 中观察到本地 DNS 查询,没有成功的外部连接 |
| 仍未确认 | 最终载荷的完整行为、恶意软件家族、控制协议、密码窃取、实际数据外传,以及原生 Windows 下的完整运行结果 |
DLL 侧加载通常先以宿主进程的权限执行,不会因此自动取得管理员权限。
普通用户权限下的代码仍可能访问该用户可读的文档和敏感数据,具体范围受系统权限与应用保护约束。
这是未知程序执行带来的风险范围,当前记录没有证明这份样本已经完成了上述窃取。
实验中的注册表与 Public 目录属于客体 Wine 环境,也不表示日常 Linux 宿主已被感染。
已有调查使用了相同的 WPS 宿主文件
我取得的 WPS EXE,还能与 OSArmor 在 2026 年 10 月 23 日发表的原始调查对应上:两份 EXE 的 SHA-256 完全相同,DLL 的哈希和大小则不同。
该报告中的邮件写成日文,同样以商品损坏为由,通过 AppSheet 和 Google 短链投递改名 WPS EXE 与 krpt.dll。
文件哈希相同支持“复用了相同宿主文件”的判断,相似包装不能直接确定同一个攻击者,也不能把另一份 DLL 的行为套在本样本上。
供专业人员比对的文件大小与 SHA-256
哈希用于核对文件身份,本文不提供样本下载。
ZIP(138900867 字节)28e53894076225e468fd237bcfb1cdd8d683448c9757770d8cfdd7a948ae13b5
Complete_Item_Assessment_mp4.exe(1321728 字节)c73e3b98b5f631669fb3d4b114e98ad55d2b015e2c55003c265e0b75e0c31d9e
krpt.dll(126144000 字节)8f7eb86566d45ecfa85a7dd46c72b8db55a24a7a0cc7882fe5929d8506664bd4
从原包恢复出的 .NET 载荷(433152 字节)5e9488d34be24163c971679f5d5d5d3a32c7ae94978c5d01dcbd8793fd6b0fdd收到类似邮件,先核对业务,再核对文件用途
普通收件人不需要会反编译,也能在下载前或启动前识别这条链上的问题。
先回到自己已有的订单系统、工单和客户联系方式,确认是否真有这笔业务,再决定如何接收材料。
| 遇到的情况 | 可采取的做法 |
|---|---|
| 陌生人要求退货,却没有订单号、商品信息 | 在已有业务渠道核对,不依赖这封邮件自己提供的身份信息 |
| 以“文件太大”为由要求访问外链 | 要求通过已知售后平台提交,检查跳转后的实际域名 |
| 入口经过 Google、微软等正规服务 | 继续核对最终目的地和文件,入口品牌不能为内容背书 |
| 照片、视频变成 EXE、脚本或“专用查看器” | 停止打开,查看完整扩展名,不凭图标或名称中的 mp4 判断 |
| 程序有签名,旁边还有 DLL | 不把单个程序的签名当成整包安全证明 |
| 页面要求关闭防护、放行或绕过系统提示 | 停止操作,通过独立渠道确认,组织设备向安全人员反馈 |
| 已确认是诱导下载的邮件 | 在 Outlook 中报告钓鱼,保留必要证据,不把可运行样本转给别人试 |
在 Outlook 中,可以选中可疑邮件,再通过“报告 → 报告钓鱼”提交,具体入口见微软的 Outlook 中文帮助。
报告钓鱼并不等于阻止发件人继续发信,是否加入阻止列表需要另行处理。
已运行程序时,处置重点转向隔离和排查
是否执行过文件、是否提交过凭据,会改变下一步处理方式。
| 已经发生的操作 | 下一步 |
|---|---|
| 只阅读邮件 | 不再访问可疑链接,报告邮件 |
| 只打开网页,没有提交信息或运行下载文件 | 关闭可疑页面、检查下载记录,保持浏览器和系统更新。 仅访问不等于已经感染,也不能据此排除浏览器漏洞等其他风险。 |
| 下载 ZIP,但未执行内容 | 不解压试运行,不通过 Wine 打开;按留证需要交给安全人员处理,或删除不需保留的样本 |
| 输入密码、验证码或授权登录 | 从干净设备访问真实服务,修改凭据,撤销可疑会话或授权,检查登录记录 |
| 已运行 EXE 或脚本 | 立即断开该设备网络,停止在上面登录敏感账户,联系 IT 或安全人员排查;组织设备遵循事件响应流程 |
如果 Windows 设备已经运行过未知程序,可以参考微软 Defender 脱机扫描的中文说明,了解在常规 Windows 环境之外进行扫描的方法。
一次未检出不能代替完整排查,也不能证明程序留下的登录启动项已经清除。
如果还向可疑页面提交过 Outlook 或 Microsoft 账户凭据,账户与设备要分别处理。
在受影响电脑上更改密码之前,应先排查其中的恶意软件;也可以使用另一台干净设备处理账户。
微软的账户恢复中文帮助列出了扫描设备、更改或重置密码,以及检查连接账户、邮件转发和自动答复设置的步骤。
这封退货邮件没有把危险写在第一句话里。
它让“帮客户看一下损坏情况”成为打开外链的理由,又把本该直接查看的材料换成了程序包。
我能从这份样本确认到后台加载、持久化写入和一次次被本地拒绝的域名解析,最终载荷的完整行为仍然未知。
对真正处理售后的人来说,可以更早停在那个文件名面前:看开箱视频,为什么需要运行 .exe?
一封退货邮件的陷阱:“开箱视频”背后的 WPS、隐藏 DLL 与恶意加载链
作者:xingwangzhe
本文链接:https://xingwangzhe.fun/posts/outlook-return-malware/
本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
留言评论
评论资源加载失败,请重试。