恶意软件分析入门:从静态分析到动态分析,再到反分析对抗

前言:这篇文章整理自 TryHackMe 的 Malware Analysis 相关内容,并结合个人理解做了重新梳理。全文围绕"拿到一个可疑样本之后该怎么办"这条主线展开,适合刚入门恶意代码分析、想搞清楚整体方法论的同学。文中所有操作都应当在隔离环境中进行,切勿在生产机或日常办公机上尝试。


一、什么是恶意软件

Malware 这个词由 MALicious + softWARE 拼接而来。换句话说,只要一个程序的目的是恶意的,它就属于恶意软件的范畴。

按行为特征,恶意软件还能进一步细分成勒索软件、木马、蠕虫、后门、间谍软件等等。不过本文不打算纠缠分类学,而是聚焦一个更实际的问题:

当我们怀疑某台机器上存在恶意软件时,应该按什么步骤去分析它?


二、为什么要做恶意软件分析

恶意软件分析在安全行业里不是一个"小众爱好",而是多个岗位的日常工作。不同团队做分析的动机也不一样:

团队分析目的
安全运营(SOC)提炼恶意行为特征,编写检测规则
应急响应(IR)判断环境被破坏到什么程度,如何修复与还原
威胁狩猎(Threat Hunting)提取 IOC,在网络中主动搜寻同类样本
安全厂商研究员把检测能力集成进安全产品
操作系统厂商(如 Microsoft、Google)定位被利用的漏洞,在 OS / 应用层加固

可以看到,同一份样本,不同角色关心的"答案"是不一样的。这也决定了你分析的深度该到哪里为止。


三、动手之前:先谈安全

恶意软件是武器,操作不当会伤到自己。 在开始任何分析之前,请务必遵守以下几条纪律:

  1. 专机专用。绝不在非分析专用的机器上碰样本。
  2. 样本加密存放。不分析、只是转移样本时,统一放在带密码的 zip / rar 压缩包里,避免误双击"引爆"。
  3. 只在隔离环境里解压。而且只在真正要分析的那一刻解压。
  4. 使用可回滚的虚拟机。分析专用 VM,做好干净快照。
  5. 管住网络。断网,或者至少做到全程可监控。
  6. 用完即还原。每次分析结束把 VM 回滚到干净快照,防止上一次执行留下的残留污染下一次分析结果。

第 6 点尤其容易被忽略。上一个样本改过的注册表、留下的服务,很可能让你对下一个样本的行为判断完全跑偏。


四、分析方法的两大分支

恶意软件分析很像拼图:用各种工具和技巧找到碎片,把碎片拼起来,才能看清样本到底想干什么。

我们最常拿到的样本形态有三种:

  • PE 文件(Portable Executable,可执行文件/二进制文件)——最主流
  • 恶意文档(带宏、带漏洞利用的 doc/pdf 等)
  • 网络流量包(pcap)

而找碎片的手段,大体分成两类:

4.1 静态分析(Static Analysis)

不运行样本,只观察它的静态属性。

典型手段包括:

  • 对样本跑 strings 提取可见字符串
  • 解析 PE 头,查看节区、导入表等信息
  • 用反汇编器阅读代码

静态分析的优势是绝对安全——样本从头到尾没跑起来。

但它有个天敌:加壳与混淆。恶意软件作者非常清楚分析人员会先做静态分析,所以会想尽办法把关键信息藏起来。这时候就需要动态分析来破局。

4.2 动态分析(Dynamic Analysis)

这里有一个很有意思的悖论:

恶意软件面临一个两难困境:它必须执行才能实现目的;而无论代码混淆做得多好,一旦运行起来,它就成了容易被发现的目标。

这句话基本是动态分析的全部哲学基础。

动态分析就是在受控环境里把样本跑起来,观察它的实际行为:手工用监控工具盯着,或者丢进沙箱让它自动跑。因为环境完全由我们控制,可以把正常用户活动、Windows 服务这些"噪声"降到最低——于是观察到的几乎一切变化,都可以归因于恶意软件本身。这一点是动态分析最大的价值。

当然,对手也会反制:由于动态分析基本都在受控环境里做,恶意软件会想办法识别自己是否处在受控环境中,一旦识别成功,就走一条"人畜无害"的代码分支糊弄你。这部分放在第七节细讲。

4.3 高级分析(Advanced Analysis)

当基础的静态 + 动态都被绕过时,就得上反汇编器和调试器了:

  • 反汇编器:把二进制翻译成汇编,静态阅读指令
  • 调试器:附加到运行中的进程,可以随时下断点、单步执行,同时观察内存与 CPU 状态

这块内容深度较大,本文不展开。


五、静态分析实战:PE 文件头

PE 文件头里保存着这个可执行文件的元数据,是静态分析最重要的信息来源之一。这里挑两个最关键的部分说。

5.1 导入表与导出表(Imports / Exports)

一个 PE 文件几乎不可能包含它运行所需的全部代码。绝大多数时候,它会复用操作系统提供的功能——既节省体积,也没必要重复造轮子。

举个例子:开发者想读一个注册表值,他会直接导入微软提供的 RegQueryValue,而不是自己实现。因为可以合理假定这个函数在任何 Windows 上都存在,不需要打包进 PE 文件本身。

这就给了我们一个巨大的突破口:既然大部分工作都要通过 Windows API 完成,那么导入表基本就泄露了样本的能力边界。

导入的函数暴露的意图
InternetOpen会进行网络通信
URLDownloadToFile会从网上下载东西
RegSetValueEx会写注册表(常见于持久化)
CreateService / StartService会创建并启动服务
CryptEncrypt 系列涉及加密(勒索软件重点关注)

Windows API 的命名通常见名知意;拿不准的时候,直接查微软官方文档确认即可。

至于 Exports,一般和 DLL 绑定出现——非 DLL 的 PE 文件带大量导出函数是不太正常的。

5.2 节区(Sections)

PE 文件被切分成若干节区,各司其职。具体有哪些节区取决于编译器或加壳工具,但最常见的是这几个:

节区作用
.text存放实际执行的 CPU 指令,标记为可执行
.data存放全局变量等全局数据
.rsrc存放图标、图片等资源

5.3 上手:用 pecheck 解析 WannaCry

Remnux 里自带 pecheck,可以直接解析 PE 头:

user@machine$ pecheck wannacry
PE check for 'wannacry':
Entropy: 7.995471 (Min=0.0, Max=8.0)
MD5     hash: 84c82835a5d21bbcf75a61706d8ab549
SHA-1   hash: 5ff465afaabcbf0150d1a3ab2c2e74f3a4426467
SHA-256 hash: ed01ebfbc9eb5bbea545af4d01bf5f1071661840480439c6e5babe8e080e41aa
.text  entropy: 6.404235
.rdata entropy: 6.663571
.data  entropy: 4.455750
.rsrc  entropy: 7.999868

从输出里我们一眼能拿到几样东西:

  • 四个标准节区:.text / .rdata / .data / .rsrc,结构很"正常"
  • 各类哈希值:可以直接拿去威胁情报平台查
  • 熵值(Entropy):衡量数据随机程度,范围 0~8。熵越高,越可能被压缩或加密过。 注意这里 .rsrc 的熵高达 7.999868 ——资源节区里藏了高度随机的东西,这是个非常值得追下去的线索

继续往下看导入表:

[IMAGE_IMPORT_DESCRIPTOR]

ADVAPI32.dll.CreateServiceA        Hint[100]
ADVAPI32.dll.OpenServiceA          Hint[431]
ADVAPI32.dll.StartServiceA         Hint[585]
ADVAPI32.dll.CloseServiceHandle    Hint[62]
ADVAPI32.dll.CryptReleaseContext   Hint[160]
ADVAPI32.dll.RegCreateKeyW         Hint[467]
ADVAPI32.dll.RegSetValueExA        Hint[516]
ADVAPI32.dll.RegQueryValueExA      Hint[503]
ADVAPI32.dll.RegCloseKey           Hint[459]
ADVAPI32.dll.OpenSCManagerA        Hint[429]

不用运行,光看这十行就能推断出不少东西:

  • 打开服务控制管理器 → 创建服务 → 启动服务:典型的持久化手法
  • 大量注册表读写:配置存储 / 持久化
  • 出现 Crypt* 系列:涉及加密操作

对一个已知是勒索软件的样本来说,这些导入完全对得上号。这就是静态分析的威力——在样本还"睡着"的时候,你就已经猜到它醒来要干什么了。

pecheck 的输出远不止这些,但作为入门,抓住节区信息和导入表这两条主线就够了。


六、动态分析实战:沙箱

⚠️ 再次警告:动态分析要运行真实的恶意样本,具有破坏性。务必在专用隔离机器上进行,分析前打干净快照,分析后回滚。绝不要在非分析用途的活跃机器上做这件事。

6.1 什么是沙箱

Sandbox 这个词其实是从军事领域借来的:一箱沙子堆出作战地形,部队在上面推演各种预案,以判断可能的结果。

放到恶意软件分析里,沙箱就是一个模拟真实目标环境的隔离环境,分析人员在里面跑样本,以了解它的行为。它高度依赖能打快照、能一键还原的实验机器。

6.2 一个好用的沙箱需要什么

  • 尽可能贴近样本真实目标环境的实验机
  • 快照 + 还原能力
  • 系统监控工具:Procmon、ProcExplorer、Regshot 等
  • 网络监控工具:Wireshark、tcpdump 等
  • 通过伪 DNS 服务器和 Web 服务器控制网络
  • 一套把日志和样本安全进出实验机的机制

最后一条是个大坑:如果你在运行恶意软件时还挂着共享目录,那么共享目录里的所有文件都可能一起遭殃。切记在引爆前断开共享。

6.3 开源沙箱

Cuckoo Sandbox

社区里名气最大的沙箱,起源于 2010 年的 Google Summer of Code 项目。优势是社区庞大、文档友好、可定制程度高,自带海量社区特征库。

⚠️ 但注意:Cuckoo 项目已归档,且不支持 Python 3,目前基本处于过时状态。

CAPE Sandbox

可以理解为 Cuckoo 的进阶版。它支持调试和内存转储,因此能够辅助脱壳。目前仍在积极维护,支持 Python 3。入门者也能用,但要充分发挥它的能力需要一定功底。官方提供在线社区版,可以先试用再决定是否自建。

6.4 在线沙箱

自建沙箱耗时耗力,在线沙箱是很好的补充:

  • Online Cuckoo Sandbox
  • Any.run
  • Intezer
  • Hybrid Analysis

🔴 一条重要纪律:除非你非常清楚自己在做什么,否则不要随便把样本上传到在线沙箱。上传本身可能造成敏感信息泄露(想象一下定向攻击你公司的样本里嵌了内部路径或凭据),也可能打草惊蛇,让攻击者知道自己被发现了。

更稳妥的做法是:先拿哈希去搜。 如果别人已经提交过,你直接看报告就行。

6.5 用 Hybrid Analysis 看 WannaCry 报告

按上面的原则,我们不提交样本,而是拿 WannaCry 的 MD5 去搜。会发现它已经被提交过很多次,可以从中挑一份(比如 Windows 7 64-bit 环境下的那份)来看。

报告里值得关注的部分:

① 结论区

  • 判定:Malicious
  • 威胁评分:100/100
  • AV 检出率:95%

② MITRE ATT&CK 映射

点开 “view all details” 可以看到样本行为到 ATT&CK 技战术的完整映射。这一块对写检测规则的同学价值极大。

③ 动态分析区(核心)

这里能看到样本运行时的进程树。其中 cmd.exe 的调用尤其值得注意——报告显示样本在运行脚本文件,并删除备份和卷影副本(Volume Shadow Copies)。

这是一个教科书级的信号:删卷影副本是勒索软件的标志性动作,目的就是掐断受害者通过备份恢复文件的退路。看到这个行为,基本可以直接给样本定性了。

④ 网络分析区

样本的网络行为、连接的域名和 IP,都是提取 IOC 的直接来源。

⑤ 提取的字符串与文件

前面进程树里看到的那些批处理脚本,内容就能在这里找到。

⑥ 社区评论

报告最后的社区讨论有时候能捡到别人踩过的坑。


七、对抗:恶意软件作者的反制手段

安全研究员在造工具,恶意软件作者也在想办法让这些工具失效。这是一场持续的军备竞赛。下面是几类常见的反分析手段。

7.1 加壳与混淆(Packing & Obfuscation)

壳(Packer) 会对恶意软件的内容进行混淆、压缩或加密。最直接的后果是:静态分析基本瞎了。

来看一个加壳样本 zmsuz3pinwl,先跑 strings:

user@machine$ strings zmsuz3pinwl
!This program cannot be run in DOS mode.
RichH
.rsrc
.data
.adata
dApB
Qtq5
wn;3b:TC,n
*tVlr
D6j[
^sZ"4V
JIoL
...

全是垃圾字符串,一点有用信息都没有。对比一下正常样本能提取出的路径、URL、注册表键名,差距一目了然。

再跑 pecheck:

user@machine$ pecheck zmsuz3pinwl
PE check for 'zmsuz3pinwl':
Entropy: 7.978052 (Min=0.0, Max=8.0)
MD5     hash: 1ebb1e268a462d56a389e8e1d06b4945
SHA-256 hash: 98c6cf0b129438ec62a628e8431e790b114ba0d82b76e625885ceedef286d6f5
 entropy: 7.999788
 entropy: 7.961048
 entropy: 7.554513
.rsrc  entropy: 6.938747
 entropy: 0.000000
.data  entropy: 7.866646
.adata entropy: 0.000000

----------Parsing Warnings----------

Suspicious flags set for section 0. Both IMAGE_SCN_MEM_WRITE and
IMAGE_SCN_MEM_EXECUTE are set. This might indicate a packed executable.
...(section 1 ~ 6 同样告警)

Imported symbols contain entries typical of packed executables.

这份输出里的三个信号非常关键:

现象含义
没有 .text 节区正常 PE 的代码放在 .text,这里压根没有——代码是运行时才解出来的
多个节区同时可写 + 可执行强烈的加壳特征。正常程序几乎不需要"既能写又能执行"的内存,而脱壳过程恰恰要往某块内存写入解密后的代码再跳过去执行
节区名为空、熵值逼近 8.0内容高度随机 = 被压缩或加密过
导入表异常稀少真实 API 调用被隐藏,运行时才动态解析

所以,面对加壳样本,分析的第一步通常是脱壳(unpacking)。 这是个进阶话题,需要用到调试器和内存转储(CAPE Sandbox 在这方面能帮上忙)。

💡 小结一个实用经验:熵值 > 7.0 就该警觉,> 7.5 基本可以认定是加密或压缩数据。这是一个成本极低、效果极好的初筛指标。

7.2 沙箱逃逸(Sandbox Evasion)

既然静态被挡住了,那丢沙箱总行吧?——作者也早有准备。常见招数:

① 长时间 sleep

沙箱的运行时间是有限的。于是样本执行后先睡上很久,啥也不干,把沙箱耗到超时。等沙箱判定"无害"收工了,它再开始干活。

② 检测用户活动

沙箱里没有真人,所以不会有鼠标移动和键盘输入。样本会一直等,直到检测到用户活动才开始作恶。更高级的样本甚至会分析鼠标移动的轨迹模式——自动化沙箱模拟出来的鼠标轨迹往往过于规整、笔直,一眼假。

③ 用户痕迹取证(Footprinting)

检查机器上有没有"生活气息":MS Office 最近打开的文档记录、浏览器历史记录等等。如果发现这台机器干净得不像有人用过,那它就判定这是沙箱,直接退出。

④ 检测虚拟机

沙箱跑在实验机上,而实验机会留下痕迹。比如 VMware 或 VirtualBox 安装的特定驱动,就是明显的破绽。恶意软件作者往往把"是虚拟机"直接约等于"是沙箱",一旦检测到就自我了断。

这个列表并不完备,但足以说明一个道理:分析环境越"像"真实用户的机器,你能观察到的行为就越真实。 给你的分析 VM 塞点浏览历史、装几个常用软件、随手建几个 Word 文档,有时比换工具管用。


八、总结

回顾一下整条分析主线:

拿到可疑样本
    │
    ├─ 【纪律】隔离环境 + 加密存放 + 干净快照
    │
    ├─ 【静态分析】不运行,先摸底
    │       ├─ strings → 全是乱码?→ 怀疑加壳
    │       ├─ pecheck → 看熵值、节区、导入表
    │       └─ 导入表基本泄露了样本的能力边界
    │
    ├─ 【动态分析】跑起来,看行为
    │       ├─ 优先查哈希,别急着上传样本
    │       ├─ 沙箱报告:进程树 / 网络 / ATT&CK 映射
    │       └─ 删卷影副本 → 勒索软件实锤
    │
    └─ 【遇阻】加壳 / 沙箱逃逸
            └─ 上调试器与反汇编器(高级分析)

几个值得记住的要点:

  1. 静态和动态不是二选一,而是互补。静态被加壳挡住,就上动态;动态被逃逸绕开,就回头脱壳静态看。
  2. 熵值是性价比最高的初筛指标。
  3. 导入表是最便宜的情报来源。
  4. "节区同时可写 + 可执行"是加壳的强信号。
  5. 纪律比技术更重要。再厉害的分析技巧,也救不了一次在主力机上的误双击。
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐