1. 项目概述:当恶意软件“藏”进图片里
最近在分析一些安全事件报告时,我注意到一个越来越普遍的现象:一些看似无害的图片文件,比如公司官网上的Logo,或者一封普通邮件里的附件截图,背后却可能藏着完整的恶意软件。这听起来有点像电影情节,但这就是“隐写术”在恶意软件领域的现实应用。简单来说,攻击者把恶意代码像“墨水”一样,巧妙地“写”进了一张图片的像素数据里,而这张图片本身看起来完全正常,甚至能正常打开和显示。当这个携带了“秘密”的图片文件被一个特殊的加载器(通常是一个看似正常的EXE程序)读取时,隐藏在图片里的恶意代码就会被提取、解密并执行,从而绕过传统的基于文件签名或行为的防病毒软件的检测。
这个项目的核心,就是带你从零开始,彻底搞懂这种利用图像隐写术来隐藏恶意代码,并最终通过一个“干净”的EXE加载器来执行的技术链条。我们会从最基础的“什么是隐写术”讲起,一直深入到如何手动逆向分析一个真实的、使用了这种技术的恶意样本,把攻击者的思路、手法和工具链完整地复现出来。无论你是安全分析的新手,还是想深化对高级逃避技术理解的老手,这篇长文都能给你提供一套完整的、可实操的分析框架和实战经验。收藏这一篇,下次再遇到“图片变病毒”的奇案,你就能心中有谱,手中有术了。
2. 核心原理:隐写术如何成为恶意软件的“隐身衣”
要理解攻击,必须先理解其依赖的技术原理。隐写术本身是一门古老的艺术,现代数字隐写术的核心思想是利用载体文件(如图像、音频、视频)中存在的冗余信息或人类感知的局限性,将秘密信息嵌入其中,而不引起载体文件可感知的质量变化。
2.1 图像文件格式与数据冗余
为什么图像是理想的隐写载体?这得从图像文件的存储方式说起。以最常见的24位位图为例,每个像素由红、绿、蓝三个通道组成,每个通道用8位(1字节)表示,范围是0-255。理论上,改变一个像素的一个通道值,图像颜色就会发生微小变化。然而,人眼对颜色的细微变化并不敏感。例如,将某个像素的红色通道值从120改为121,你几乎无法用肉眼分辨出来。
更重要的是,许多图像文件格式存在大量的“无关紧要”的数据区域。例如:
- BMP文件末尾的未使用空间 :BMP文件头结构固定,但实际图像数据可能并不完全填满文件,末尾会留有空白。
-
PNG文件的辅助数据块
:PNG格式允许包含
tEXt、zTXt、iTXt等文本数据块,这些本用于存储标题、作者等信息,但也可以被用来藏匿数据。 - JPEG的DCT系数 :JPEG压缩过程中,会对图像分块进行离散余弦变换,得到一系列系数。修改这些系数的最低有效位,对重建后的图像视觉质量影响极小。
攻击者正是利用了这些特性。他们不是简单地把恶意代码附加在图片文件后面(这很容易被检测),而是将代码拆分、编码后,精细地“涂抹”或“替换”到这些冗余的、不显眼的数据位上。
2.2 恶意软件隐写的典型工作流程
一个完整的利用图像隐写术投递恶意软件的流程,通常涉及两个分离的组件:
- 载体文件 :即包含隐藏数据的图像文件。它可以通过任何正常渠道传播,如钓鱼邮件附件、被攻陷的网站、论坛分享等。
-
加载器
:一个独立的、功能合法的EXE程序。它的公开功能可能是图片查看器、文档转换器或一个小游戏。但其内部包含一段特殊的代码,用于执行以下操作:
- 定位与下载 :从硬编码的URL或通过某种算法生成URL,去获取那个特定的载体图像文件。
- 提取与解码 :按照与隐藏时相反的算法,从图像的特定位置(如从第1024个字节开始,或读取每个像素B通道的最低有效位)提取出经过编码的数据。
- 重组与执行 :将提取出的数据解码(可能是Base64、XOR解密、AES解密等),在内存中重组为完整的PE文件(即可执行的EXE/DLL),然后通过进程注入、反射式DLL加载等技术,直接在内存中执行,避免文件落地,进一步规避检测。
这种“载荷分离”的方式好处极大。加载器本身可能因为其签名合法、行为简单而通过审查。而载体图像在网络传输和静态存储时,也毫无恶意特征。只有当两者在受害者机器上结合时,恶意行为才会发生。
注意 :在实际分析中,你可能会遇到更复杂的变种,比如加载器先从一个无害的图片中提取出一段配置信息(如C2服务器地址),再用这个配置去下载真正的恶意载荷图像。
3. 实战环境搭建与分析工具箱准备
工欲善其事,必先利其器。分析这类样本,你需要一个隔离的、可控的分析环境,以及一套趁手的工具。别担心,大部分工具都是免费且开源的。
3.1 安全分析环境配置
绝对不要在物理机或日常使用的主机上直接分析恶意软件! 这是铁律。
- 虚拟机 :使用VMware Workstation或VirtualBox创建一个Windows 10/11虚拟机。务必在虚拟机设置中禁用“拖放”、“共享文件夹”和“剪贴板共享”功能,防止恶意软件逃逸。
- 系统快照 :在安装完基础工具后,创建一个干净的快照。每次分析样本前,都恢复到该快照,确保分析环境纯净。
-
网络控制
:将虚拟机网络设置为“Host-Only”或“NAT”模式,并根据需要,在主机上使用像
inetSim这样的工具模拟网络服务,或者使用防火墙规则严格限制虚拟机的出站连接,防止样本连接真实C2服务器造成危害。
3.2 必备静态与动态分析工具
你的工具链应该覆盖从初步侦查到深度逆向的各个环节。
静态分析工具(不运行程序):
-
PE识别工具
:
Exeinfo PE或Detect It Easy。快速查看EXE加载器的编译语言、加壳情况、导入函数等基本信息。如果显示“UPX”、“ASPack”等,说明程序被压缩或加密了,需要先脱壳。 -
十六进制编辑器
:
HxD或010 Editor。用于直接查看和搜索文件二进制内容,是分析图像载体和EXE中硬编码数据的关键。010 Editor还支持模板解析文件结构,非常强大。 -
反编译器
:
Ghidra、IDA Pro、Binary Ninja。用于将机器代码反编译成可读性更高的伪C代码。Ghidra是NSA开源的神器,功能强大且免费,是初学者的首选。 -
字符串提取工具
:
Strings或集成在FLOSS中的功能。从二进制文件中提取可读的ASCII和Unicode字符串,常用于发现URL、路径、API函数名、错误信息等线索。
动态分析工具(运行程序并观察):
-
行为监控
:
Process Monitor和Process Explorer。前者可以实时监控程序对文件系统、注册表、网络的每一个操作;后者可以查看进程树、加载的DLL、线程、句柄等信息。这是理解加载器行为的第一步。 -
网络分析
:
Wireshark。捕获所有网络流量,查看加载器试图与哪些IP地址通信,传输了什么数据。结合inetSim,可以安全地响应这些请求,观察后续行为。 -
调试器
:
x64dbg。当静态分析遇到瓶颈,或者需要跟踪数据在内存中的变化过程时,就需要动态调试。你可以设置断点,单步执行,观察寄存器和内存值的变化。
专项分析工具:
-
图像分析
:除了通用的十六进制编辑器,
StegSolve是一款专门为图像隐写分析设计的工具,它可以轻松地切换查看图像的不同位平面(Bit Planes),这对于发现LSB隐写非常直观。 -
脚本编写
:
Python配合PIL库。当你理解了隐写算法后,很可能需要自己编写脚本来从图片中提取数据。Python是完成这类任务的绝佳选择。
4. 逆向工程实战:拆解一个图像隐写加载器
假设我们拿到了一个可疑的样本包,里面有一个
viewer.exe
和一个
logo.png
。我们的目标是弄清楚
viewer.exe
如何利用
logo.png
。
4.1 初步侦查与静态分析
首先,用
Exeinfo PE
检查
viewer.exe
。
File: viewer.exe
Size: 312KB
Entropy: 7.89 (较高,可能加壳或包含加密数据)
Type: PE32 executable (GUI) Intel 80386, for MS Windows
Compiler: Microsoft Visual C++ 2019
Protection: Not packed (但高熵值值得警惕)
高熵值提示文件内部可能包含压缩或加密的数据段。我们再用
Strings
扫一下,看看有没有明显的线索。
...
C:\Users\Public\Pictures\logo.png
http://malicious-site.com/update.png
kernel32.dll
CreateFileA
ReadFile
VirtualAlloc
VirtualProtect
CreateThread
...
发现关键字符串!它提到了一个本地路径和一个URL,都指向PNG文件。这强烈暗示了程序会读取图片文件。同时,它导入了
VirtualAlloc
和
CreateThread
,这是进程注入或内存执行代码的典型标志。
接下来,用Ghidra打开
viewer.exe
。在
main
或
WinMain
函数附近,搜索对
CreateFileA
和
ReadFile
的交叉引用。我们很快会找到类似下面的代码逻辑(已简化和反混淆):
// 伪代码
hFile = CreateFileA("C:\\Users\\Public\\Pictures\\logo.png", GENERIC_READ, ...);
if (hFile != INVALID_HANDLE_VALUE) {
fileSize = GetFileSize(hFile, NULL);
pBuffer = VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_READWRITE);
ReadFile(hFile, pBuffer, fileSize, &bytesRead, NULL);
CloseHandle(hFile);
// 关键:对pBuffer中的图像数据进行处理
processedData = decode_stego_data(pBuffer, fileSize);
// 在内存中准备执行
exec_memory(processedData);
}
4.2 深入核心:隐写解码函数分析
现在需要找到并分析
decode_stego_data
这个函数。在Ghidra中跟踪函数调用,我们可能会发现一个函数,它接收图像缓冲区和大小,然后进行循环操作。
一个典型的LSB隐写解码逻辑可能如下:
void* decode_lsb(BYTE* imgData, int dataOffset, int hiddenDataSize) {
// 假设图像数据从 dataOffset 开始(跳过了PNG文件头等信息)
BYTE* pixelData = imgData + dataOffset;
int totalBits = hiddenDataSize * 8;
BYTE* output = malloc(hiddenDataSize);
for (int i = 0; i < totalBits; i++) {
int byteIndex = i / 8;
int bitIndex = i % 8;
// 计算在图像数据中的位置,每个字节(像素的一个通道)取最后一位
int imgByteIndex = i; // 简单情况:1像素1位
BYTE imageByte = pixelData[imgByteIndex];
// 获取最低有效位
BYTE bit = imageByte & 1;
// 将bit设置到输出字节的相应位置
if (bit == 1) {
output[byteIndex] |= (1 << bitIndex);
} else {
output[byteIndex] &= ~(1 << bitIndex);
}
}
return output;
}
在逆向时,你需要关注的汇编或反编译代码特征包括:
- 对数据指针进行循环操作。
-
使用位操作指令,如
AND(与)、OR(或)、SHR(右移)。 -
可能存在一个固定的偏移量(
dataOffset),用于跳过文件头。 -
在提取数据后,可能紧接着调用
CryptDecrypt或一个自定义的XOR解密循环。
4.3 动态行为验证与数据提取
静态分析给出了假设,动态分析则是验证和获取确凿证据的关键。
-
运行与监控
:在配置好网络隔离的虚拟机中,运行
Process Monitor,设置过滤器只显示viewer.exe的进程。然后运行viewer.exe。 -
观察文件访问
:在
Process Monitor的日志中,你应该能看到它尝试访问C:\Users\Public\Pictures\logo.png。如果文件不存在,它会返回错误。为了让它继续执行,我们可以在该路径预先放置我们的logo.png(分析用的样本图片)。 -
网络行为
:同时运行
Wireshark。如果本地文件访问失败,程序可能会回退到尝试从http://malicious-site.com/update.png下载。由于我们隔离了网络,这个请求会失败,但你可以在Wireshark中看到发出的DNS查询和HTTP GET请求。 -
内存转储
:这是最关键的一步。当程序将图片读入内存并解码后,最终会通过
CreateThread或类似方式执行。我们可以使用x64dbg进行调试。-
在
x64dbg中打开viewer.exe,在VirtualAlloc和CreateThread函数上设置断点。 -
运行程序,当断点命中
CreateThread时,查看其参数lpStartAddress(线程起始地址)。这个地址很可能指向一段刚刚解密出来的、可执行的内存区域。 -
在这个地址上右键,选择“在内存窗口中转到”。你可能会看到熟悉的
MZ头(PE文件标志)。此时,你可以将这片内存区域转储到磁盘文件(右键 -> 二进制 -> 保存到文件),保存为dump.bin。
-
在
-
分析转储文件
:用
Exeinfo PE检查dump.bin,现在它很可能被识别为一个完整的、未加壳的PE文件(DLL或EXE)。你可以用Strings提取更多字符串,或者用Ghidra进行二次逆向,分析这个最终载荷的功能(可能是远控、勒索软件或信息窃取器)。
4.4 载体图像分析:定位隐藏数据
现在回过头来分析
logo.png
。用
StegSolve
打开它。
-
使用
Analyse -> Frame Browser查看各个数据块,检查是否有异常的tEXt或zTXt块。 -
使用
Analyse -> Extract Plane,分别查看RGB三个通道的各个位平面(Bit 0 是最低有效位)。如果使用了LSB隐写,在最低位平面(Bit 0)你可能会看到明显的、非随机的纹理或图案,这很可能就是隐藏数据的视觉化表现。
更直接的方法是使用Python脚本。根据逆向
viewer.exe
时推测出的算法(例如,从文件偏移0x1000开始,连续读取每个字节的最后两位,每8位组成一个新字节),编写提取脚本:
from PIL import Image
import struct
def extract_lsb(image_path, output_path, start_offset=0x1000):
with open(image_path, 'rb') as f:
data = f.read()
# 从指定偏移开始,假设后面都是像素数据
pixel_data = data[start_offset:]
extracted_bits = []
for byte in pixel_data:
# 取最低有效位
lsb = byte & 1
extracted_bits.append(lsb)
# 如果隐写用了多个位平面,这里可能是 (byte & 3) 取最后两位
# 将比特流重组为字节
extracted_bytes = bytearray()
for i in range(0, len(extracted_bits), 8):
byte_bits = extracted_bits[i:i+8]
if len(byte_bits) < 8:
break
byte_val = 0
for j, bit in enumerate(byte_bits):
byte_val |= (bit << j) # 注意位序,可能需要调整
extracted_bytes.append(byte_val)
with open(output_path, 'wb') as f:
f.write(extracted_bytes)
print(f"数据已提取到 {output_path},大小 {len(extracted_bytes)} 字节")
# 使用
extract_lsb('logo.png', 'extracted.bin')
运行脚本后,得到
extracted.bin
。用十六进制编辑器查看,如果开头是
MZ
或者能看到一些可读字符串,说明提取成功。这个文件很可能还需要进一步的解密(如XOR),解密密钥可能硬编码在
viewer.exe
中。
5. 高级技巧与深度对抗
攻击者不会一直使用简单的LSB。随着分析技术的普及,更高级的隐写和反逆向技术被采用。
5.1 复杂隐写算法识别
-
DCT域隐写
:针对JPEG图像。修改的是量化后的DCT系数,而不是直接的像素值。在Ghidra中,你可能会发现程序链接了
libjpeg库或包含复杂的离散余弦变换运算代码。提取数据需要完整解码JPEG,操作系数,再重新编码,难度较大。 -
自适应隐写
:根据图像区域纹理复杂度,动态调整嵌入数据的强度和位置。在代码中可能表现为复杂的
if-else逻辑,根据像素邻域方差决定操作。 - 加密后隐写 :先对恶意载荷用AES/RC4等加密,再将密文嵌入图像。这样即使你成功提取出数据,得到的也是一堆乱码,必须找到密钥。密钥可能被硬编码、通过网络获取、或者由系统信息派生。
5.2 加载器的反逆向与混淆技术
- 字符串加密 :所有敏感的字符串(URL、API函数名、解密密钥)在二进制文件中都是加密的,运行时动态解密。在Ghidra中你看不到明文字符串,只会看到一堆数据引用和一个解密函数在循环调用。
-
控制流扁平化
:将正常的
if-else、switch-case逻辑打乱,用一个大switch语句和状态变量来模拟,极大地增加逆向阅读难度。 -
动态API解析
:不直接导入
CreateThread这样的函数,而是通过LoadLibrary和GetProcAddress动态获取函数地址,使得导入表看起来非常干净。 -
多阶段加载
:
viewer.exe可能只是一个下载器,它下载的logo.png中隐藏的也不是最终载荷,而是一个中间加载器。这个中间加载器会再进行网络通信,下载第二、第三阶段的组件。这种“套娃”战术极大地增加了分析复杂度。
5.3 自动化分析与YARA规则编写
对于大批量样本分析,手动逆向效率太低。我们可以将分析经验转化为YARA规则,进行快速筛选和分类。
一条检测图像隐写加载器的YARA规则可能包含以下特征:
rule Stego_Loader_Generic {
meta:
description = "Detects potential steganography-based malware loaders"
author = "Your Name"
date = "2023-10-27"
strings:
$s1 = "CreateFile" wide ascii
$s2 = "ReadFile" wide ascii
$s3 = "VirtualAlloc" wide ascii
$s4 = "VirtualProtect" wide ascii
$s5 = "CreateThread" wide ascii
$s6 = ".png" wide ascii
$s7 = ".jpg" wide ascii
$s8 = ".bmp" wide ascii
$op1 = { 80 3? 00 74 ?? } // 常见解密循环:cmp byte ptr [reg], 0; je ...
$op2 = { 30 ?? } // XOR 操作:xor byte ptr [reg], cl/al等
condition:
(uint16(0) == 0x5A4D) and // 是PE文件
filesize < 2MB and // 通常加载器不会太大
(3 of ($s*) or 1 of ($op*)) and
pe.imports("kernel32.dll", "CreateFileA")
}
这条规则寻找同时具备文件操作、内存分配、线程创建和图像文件扩展名引用特征的PE文件。它很粗糙,但可以作为初筛的起点。在实际使用中,你需要结合具体样本的特征(如特定的解密常数、代码片段)来编写更精确的规则。
6. 防御视角:如何检测与防范此类威胁
作为防御方,理解攻击是为了更好地防御。针对图像隐写恶意软件,传统的防病毒软件基于签名的检测往往失效,需要采用更综合的策略。
6.1 终端检测与响应策略
-
行为监控
:部署EDR解决方案。重点关注进程行为链,例如:一个图片查看器进程(
viewer.exe)在读取一个图片文件后,立即申请了一大块可读可写可执行的内存(PAGE_EXECUTE_READWRITE),然后创建了远程线程。这是一个极高的风险行为,无论文件签名如何,都应立即告警。 -
内存扫描
:防病毒软件可以定期扫描进程内存,寻找隐藏的PE文件头(
MZ)、Shellcode特征或已知恶意代码的片段。 - 机器学习模型 :训练模型识别“良性程序的不良性为”。例如,一个被标记为“图像编辑器”的软件,如果其网络通信模式突然变得像下载器,或者其内存分配模式异常,模型可以将其标记为可疑。
6.2 网络与网关防护
-
深度文件内容检测
:下一代防火墙或邮件安全网关不应只检查文件扩展名和简单签名。它们应该能够:
- 解压嵌套的压缩包。
- 对图像文件进行隐写分析,计算其统计特征(如LSB平面的随机性),异常的文件可以隔离或进行沙箱动态检测。
- 模拟渲染或解码文件,触发潜在的漏洞利用或提取行为。
-
沙箱动态分析
:将可疑文件(包括EXE和图片)在隔离的沙箱环境中运行。沙箱会记录所有系统调用、网络请求和内存操作。如果
viewer.exe在沙箱中试图从图片中提取并执行代码,这个完整的行为链会被清晰记录,并产生明确的恶意报告。
6.3 安全意识与流程管控
-
最小权限原则
:确保用户账户没有不必要的本地管理员权限。即使
viewer.exe成功运行,没有高权限,其破坏力也有限。 -
应用程序白名单
:在企业环境中,只允许运行经过批准的、签名的应用程序。任何未知的
viewer.exe都无法执行。 - 用户教育 :持续培训用户识别钓鱼邮件,不要随意打开来历不明的附件,即使是图片文件。告知他们新型威胁的存在,比如“图片也可能携带病毒”。
7. 从分析到精通:构建你自己的分析框架
经过一次完整的实战分析,你应该已经对流程有了感性认识。但要达到“精通”,需要将这个过程系统化、工具化。
- 建立分析清单 :创建一个标准操作程序清单,每次分析新样本都按步骤进行。清单包括:环境快照恢复、静态信息收集、字符串分析、导入表分析、初步行为监控、关键API断点设置、内存转储、载荷提取等。
-
构建工具脚本库
:将常用的操作写成脚本。比如,一个自动从进程内存中搜索和转储PE文件的Python脚本(使用
pymem或frida);一个根据常见算法(LSB, XOR)从给定偏移提取图像数据的脚本模板。 -
参与社区与挑战
:在如
MalwareBazaar、theZoo等平台下载真实样本进行练习。参与CrackMe挑战或隐写术CTF比赛(如Stegano类题目),这能极大地锻炼你的思维和技能。 - 持续学习 :恶意软件技术在不断进化。关注安全研究博客、论文,了解最新的隐写技术(如基于GAN的隐写)、加载技术(如进程空洞、ETW绕过)和混淆方法。
逆向工程分析图像隐写恶意软件,就像一场数字世界的侦探游戏。攻击者费尽心机隐藏线索,而我们则抽丝剥茧,让真相大白。这个过程没有一成不变的公式,需要的是对细节的敏锐观察、对系统原理的深刻理解,以及大量的实践积累。每一次成功的分析,不仅解决了一个具体威胁,更是在你的知识图谱上添加了一块坚实的拼图。当你再看到一张图片或一个普通的EXE时,你的视角会穿透表象,直抵其可能隐藏的复杂逻辑与意图,这才是安全分析师真正的能力所在。

2828

被折叠的 条评论
为什么被折叠?



