news 2026/10/17 5:50:55

Natas幽灵王病毒汇编级分析:从壳到入口点完整拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Natas幽灵王病毒汇编级分析:从壳到入口点完整拆解

我整理老样本库时碰到的这个 Natas 幽灵王病毒,其实是某套汇编教程里第 105 讲的案例,但你别小看它,把它从壳到入口点完整拆一遍,基本上能把汇编语言里跟 PE 结构、反调试、解密循环相关的知识点全串起来。这类感染型病毒不像远程控制木马那样喜欢挂一堆花指令,它的代码逻辑很集中,特别适合用来做汇编级分析训练。这篇文章我不想讲太虚的东西,就直接以这个样本为主角,从头到尾说清楚我是怎么从汇编指令层一步步确认它的执行流程、感染方式和修复思路。如果你对 PE 文件结构有基本了解,又恰好想知道恶意样本分析到底从哪下手,这篇文章应该能给你一条比较顺的路径。

1. 样本速览与“为什么非看不可”

1.1 幽灵王到底是什么样的样本

从文件名和内部字符串来看,这个样本在病毒库里的代号是 Natas,中文社区习惯叫它“幽灵王”。它属于比较典型的老式文件感染型病毒,目标优先选择同目录或指定目录下的 32 位 PE 可执行文件。整体体积不大,几十 KB 左右,但加了一层自定义壳,主程序代码在运行前会先经过一段解密例程,所以直接看静态缓冲区的汇编指令会看到一堆乱码,必须先定位解密循环。

它的爆发逻辑并不复杂:一旦被运行,它会在当前目录和几个常见路径下扫描可执行文件,找到没有被它标记过的 PE 文件以后,把自己附加到文件的空白区或者新增一个节里,同时保存并修改原始入口点,把执行流程改成先去执行病毒代码,等病毒逻辑跑完后再跳回原始入口点继续运行正常程序。这么做的好处很明显——宿主程序看起来能正常打开,用户根本不会意识到出了问题,但病毒已经完成了自我复制。

这种感染方式放到几十年前可能很有效,放到现在反而是最好的教学样本。因为它的代码结构清晰、感染流程固定,没有太多网络对抗和复杂的反调试逻辑,非常适合拿来做汇编级源码解读。特别是对你理解“入口点覆盖”“PE 节表扩展”这类概念,比看一百页文档都直观。

1.2 分析它需要哪些汇编底子

给你一个比较实际的结论:不需要你成为汇编高手,但有四个知识点必须掌握,否则后面查代码会很痛苦。

第一是寄存器的作用。至少要知道 EAX 通常被当作累加器、ECX 常用于循环计数、ESI 和 EDI 经常被当作源和目的地址、EBP 和 ESP 跟栈帧有关。幽灵王病毒的解密循环里大量用到 ESI 和 ECX,如果你对这几个寄存器的职责不敏感,看一小段就会迷路。

第二是栈与调用约定。病毒代码里频繁调用系统 API,它压参数的方式是 stdcall 还是 cdecl,决定了你追踪参数时该看栈顶还是栈底。好在样本里的调用都是比较标准的 stdcall,参数从右往左压栈,被调用方负责平衡栈,所以重点盯住 ESP 的变化就够了。

第三是常见指令的组合逻辑。MOV、LEA、XOR、TEST、CMP、JMP、CALL、LOOP,这八个指令就覆盖了它 70% 以上的操作。特别是 XOR,很多壳和加密循环都用它做数据和单字节密钥的异或运算,我在后面章节里会展开讲。

第四是 PE 文件结构的基本认知。你不用背完整结构,但至少要知道 DOS 头、NT 头、节表、AddressOfEntryPoint 这几个字段在哪。因为感染型病毒的本质就是改 PE 结构,你只有先把结构顺序搞清楚,才能在入口点变化时一眼看出异常。

如果你这四个底子基本具备,那这份分析你可以直接上手。如果还差一点,建议先把这几个概念翻出来过一遍,不然跟着走的时候会频繁卡壳。

2. 静态分析:先用汇编视角确认目标

2.1 先做“壳”识别,再看入口点

我拿到这个样本后的第一步不是直接扔进调试器,而是先做静态识别。原因很简单——动态调试工具只能告诉你程序当前在干什么,却不能直接告诉你它为什么这么干。对一个有壳的样本直接下断点,你只会看到一段段莫名的 push、pop 和跳转,很容易被带偏。

这个阶段的工具不需要多高级,我习惯用查壳工具配合十六进制编辑器一起看。先用常见的查壳工具跑一遍,确认这个样本有没有已知的壳特征。幽灵王样本给出的结果比较模糊,既有可能是未知壳,又有可能只是普通程序被混淆过。这时候就需要用十六进制编辑器手工确认。

我照着 PE 头定位到节表,发现了一个奇怪的现象:默认的节区名称不是常见的.text、.data这类,而是一串看似随机的名字,最后一个节的 RawSize 明显比 VirtualSize 大很多。这说明节表里被人动过手脚,通常是为了容纳解密后的代码或者附加病毒数据。看到这里基本可以判断,样本外层有一层自写的封装壳,必须先找到壳的入口才能看到真正的病毒主体。

明确了这一点以后,我再看 PE 头里的 AddressOfEntryPoint。它指向的位置不在任何一个正常节的起始范围附近,而是指向了一个节区的中后段。你可以这么理解——正常程序入口一般位于.text节开头附近,像一个住宅楼的大门;这个样本的入口却指向电梯井里的某个夹层,摆明了就是一个暗门。你初步判断这几点以后,静态分析的“目标画像”就算完成了。

2.2 用“三个关键窗口”快速熟悉文件结构

静态分析不像动态分析,你没法用断点去感知程序的运行节奏,所以得靠三个“窗口”来建立整体认知。

第一个窗口是反汇编窗口。用反汇编器打开样本后,先把 CPU 视角切到入口点地址,观察从这里开始的指令序列。如果有大量形如push ebp; mov ebp, esp; sub esp, xx的栈帧代码,说明这段代码是用高级语言编译器生成的普通函数;如果看到很多没有逻辑的跳转和连续push操作,就得怀疑是壳在混淆。幽灵王样本从入口点开始的一段就是典型的壳代码,它会先把壳所在的内存区域解密,再跳转到真正的病毒主模块。

第二个窗口是导入表窗口。查看样本导入的函数列表,关注它导入了哪些系统 DLL 的哪些函数。这个样本的导入表非常有意思,它没有直接导入文件操作相关的函数,而是只导入了 LoadLibraryA 和 GetProcAddress 两个动态加载函数。这就说明它很可能是先画出最小导入集,再在运行过程中用动态方式加载真正需要的 API。这种做法的目的是增加静态分析的难度,让你在导入表里找不到病毒真正调用的文件函数。

第三个窗口是字符串窗口。把样本里的 ASCII 和 Unicode 字符串都提取出来,你会看到很多像文件路径、特定扩展名、错误提示之类的内容。幽灵王样本里能明显看到一些类似“感染标记”的字符串,通常是病毒用来判断目标文件是否已经被感染的特征码。只要把这三个窗口的信息交叉比对,基本上就能还原出样本的总体框架了。

2.3 定位真实入口:从哪里切入最省力

很多人静态分析失败,是因为错误地选择了切入位置。面对这种带壳样本,你不能从入口点一路单步到天亮,正确做法是先通过节表信息找到壳的尾部,也就是壳解密完以后跳转的位置。这种跳转在汇编里通常表现为一个间接跳转,比如jmp eax或者call [ebx+offset],因为它要跳到的地址是在程序运行过程中算出来的。

我的做法是先在反汇编器里做一遍模拟执行,看看壳代码有没有可能被脚本自动还原。如果不行,就手动跟踪跳转目标寄存器。幽灵王这个样本的壳比较简单,它最后一个步骤是把解密后的地址放入 EDI,然后执行jmp edi。所以在静态阶段,我只需要定位到这条指令,就能确定真正的病毒代码区域大概是在哪个节哪一段。

确定真实入口后再重新加载反汇编器,强制把起始地址设置为这个位置,文字注释和函数识别会立刻清晰很多,病毒的主逻辑也就暴露出来了。这一步相当于绕开了壳的干扰,直接拿到了内部代码的执行入口,后续分析效率会成倍提高。

3. 汇编级代码拆解与关键算法

3.1 解码循环怎么看

进入病毒主体以后,最先映入眼帘的往往是一个大循环,这个循环本质上是把加密后存放在数据节里的内容逐字节还原。幽灵王病毒的解码循环非常典型,大致结构是这个样子:

loop_start: mov al, byte ptr [esi] xor al, 0x56 rol al, 3 mov byte ptr [edi], al inc esi inc edi dec ecx jnz loop_start

这个循环的格式很标准,ESI 指向加密数据源,EDI 指向解密后的目标缓冲区,ECX 是总字节数。它先取当前字节,做一次异或运算,再做一次循环左移,完成后写入新地址。你可以把它想象成一道两道手续的密码锁,每次取出一个字符,转一下锁盘,再擦掉重写。

分析这种循环的时候,千万不要急着记下密文和密钥,重点要搞清楚三件事:循环长度多少,也就是 ECX 的初始值;解密后的缓冲区指向哪里,也就是 EDI 的最终地址;以及解密完以后控制流去了哪里。因为你最终的目的是找到它把这个解密完的数据交给谁,而不是复现它的解密过程。

实际分析时我会在解密循环结束后第一条指令处设一个内存断点,看数据被写到哪个区域。幽灵王样本在解密完成以后,会构造一个函数指针并调用它,这块解密出来的区域基本就是病毒真正干活的核心模块。你把核心模块从内存里 dump 出来,再单独分析,会比直接分析原始文件轻松得多。

3.2 感染逻辑的调用顺序

核心模块里最值得分析的是它的文件感染子程序。这段程序的汇编代码并不算长,但逻辑层次分明。一般来说,它会按以下顺序执行:

  1. 调用FindFirstFileA/FindNextFileA遍历目标目录,找出后缀为.exe的候选文件。
  2. 对每个候选文件,读取它的 PE 头,检查是不是合法 PE 格式。
  3. 检查文件头部或者节表里有没有特定的感染标记,如果已经有标记就跳过。
  4. 如果没被感染过,就打开文件,把自己加载到内存映射里。
  5. 修改节表和入口点,把原始入口点记录下来。
  6. 关闭文件,继续遍历下一个文件。

从汇编指令来看,这个顺序会反映在一串call指令和分支跳转上。你不需要挨个函数研究,最省事的方法是把导入表里动态加载的函数调用点全部标出来,然后看每个调用点附近的参数。比如调用WriteFile之前,它一定会先压入文件句柄和写入长度参数。你按照这个线索去逆推,就能知道它是往哪个文件、哪段数据、写入了多少内容。

整个过程最需要注意的地方是检查感染标记这一段。很多分析者容易忽略它,但它恰恰是修复合修复的关键。看到感染标记以后,要记录下它的偏移量和长度,因为这意味着修复受感染文件时,你可以根据这个标记判断文件是否被处理过,以及原始入口点信息大概存在哪个结构里。

3.3 反分析特征怎么判断

这个样本虽然技术不算特别高明,但在对抗分析师的细节上还是动了不少脑筋。比如它会用动态加载的方式调用CreateFileA和WriteFile,而不是直接出现在导入表里。这会让只懂看导入表的新手误判它没有文件操作能力,但一旦你把它的程序逻辑完整串起来,就会发现它其实在频繁调用这些 API。

另外还有一个非常典型的特征——它把关键字符串分段保存在不同区域,用的时候临时拼接。比如“Path”和“FindFirstFileA”这些词不会完整地出现在数据段里,而是被拆成几段,在运行时按需组装。这种做法的目的在于绕过字符串扫描,让你的静态字符串列表变脏,看不到完整的关键信息。

还有一段花指令出现在感染子程序的入口附近,正常函数会先建立栈帧,但它会在前面插入几个看似没有意义的 MOV 和 JMP,让反汇编器的线性扫描算法迷失方向。应对这种手段,我的习惯做法是直接看跳转关系,因为真实代码最终一定会通过绝对跳转或者条件跳转连接起来。你只要把跳转箭头都打开,花指令区域的“假指令”就会现出原形。反分析特征不是洪水猛兽,本质上就是让人多绕几个弯,你没必要焦虑,按部就班地顺藤摸瓜就好。

4. 动态验证与行为核实

4.1 在虚拟机里调试要注意的前提

静态分析做到一定阶段以后,就要开始动态验证了。这步有个大前提——必须在隔离环境里做。我通常会准备一台专门的虚拟机,快照设在干净状态,所有网络共享和剪贴板共享都关掉,模拟目标系统设置为老一点的 Windows 版本,因为这类老样本对高版本系统的 API 兼容性并不好。

跑动态调试之前,我还会把静态阶段分析出的关键地址列表提前记好。比如解密循环结束后的跳转地址、感染标记字符串所在的内存地址、动态调用的 API 地址。等调试器跑起来以后,我会在这些地址先设好断点,让程序一开始就往这些位置走,节省不必要的单步时间。

必须提醒一句,感染型病毒在虚拟机里运行,它可不会只感染一个文件。它一旦跑起来就会在当前目录和系统目录里乱翻,所以调试之前文件夹里只放几个无价值的测试文件,避免宿主测试程序被反复感染导致环境变得一团糟。我见过有人直接在安装了完整开发环境的虚拟机里调试,结果整个目录里所有 exe 全被感染,最后环境只能重建。

4.2 断点战术:等待“新模块”接管

动态调试这种带壳样本,我的核心战术是“等新模块”。因为在壳解密完成以前,病毒主体并没有出现在内存里,你很难跟踪到有效的代码路径。与其在壳里挣扎,不如让程序跑起来,然后在关键转换点设断点。

具体操作上,我会在 LoadLibraryA 和 GetProcAddress 两个函数上设置断点。因为幽灵王病毒需要动态加载大量 API,每次加载时调试器就会停下来,我就能看到它正在准备调用哪个函数、参数是什么,反向推断它下一步要做什么。这个方法的妙处在于,你不需要去手动定位解密完的代码,所有隐藏的函数调用都会自己送上门来。

等运行到它第一次调用WriteFile的时候,基本就能确认它已经开始感染文件了。此时我会检查当前进程打开的文件句柄和写入缓冲区,对照静态分析时记录的感染标记,确认我刚才的判断是否准确。如果两者吻合,说明这段感染逻辑的静态分析没有跑偏。

4.3 行为对照表:静态推测 vs 动态实况

动态分析的价值在于验证,而不是替代静态分析。我建议你建一张简单的对照表,把静态阶段每个推测记下来,再和动态阶段的观察结果做对比。

拿这个样本举例,我在静态阶段推测它会遍历当前目录下的 exe 文件,动态阶段就观察到它在当前目录下创建了文件快照并逐一读取;静态阶段判断它会检查感染标记,动态阶段就往测试文件头部特定偏移处写入了标记字符串。两边一比,分析结论的正确率就能立刻确认。

如果出现静态和动态不一致的情况,比如静态推测它跳到了某个地址,但动态阶段发现那个地址是一条无效指令,那就说明有地方分析错了。常见原因有壳解密长度搞错、感染逻辑里存在多分支、或者原始入口点被做了多层跳转包装。遇到这种情况别硬撑,回到反汇编器里重新过一遍跳转关系往往会更快。

5. 感染识别与手动修复

5.1 从 PE 视角识别被感染文件

分析完病毒的感染逻辑以后,你最大的收获其实是能够准确识别哪些文件被它感染过。这类感染型病毒因为要附加代码或者新增节,必然会在 PE 结构上留下痕迹。

第一个识别特征是入口点异常。正常可执行文件的入口点应该在可执行节区的上半段附近,而被感染文件的入口点往往指向一个非常靠后的偏移,甚至落在一个名称异常的节里。第二个特征是节表里存在多余节,不常见名称,且 RawSize 和 VirtualSize 相差悬殊,因为病毒需要把实际数据填进去。第三个特征是在文件末尾能看到明显追加的一块数据,这段区域通常就是病毒代码或者感染标记。

你还可以直接从十六进制数据里搜索感染标记字符串,一般病毒设计者都会把它做得比较独特,保证不会和正常程序内容混淆。一旦在文件里搜索到该标记,基本可以宣告这个文件已经被处理过。这个判定方法对企业内部的应急响应非常实用,因为你可以写一个简单脚本批量扫描整个服务器目录,而不是靠人工逐个检查。

5.2 手动修复流程的思路与限制

聊到修复,我得先泼一盆冷水:手动修复感染文件不是每个人都能直接上手,因为需要比较扎实的 PE 结构知识,而且对新手来说,改错哪怕一个字节都可能导致文件直接损坏无法运行。如果你只是想恢复一台机器的正常运行,唯一的办法是重新安装软件或者从干净备份恢复文件。

但如果你确实想练手修复,我可以简单讲一下流程。第一步,用十六进制编辑器打开被感染文件,找到节表中新增的那个异常节,记录它的尺寸和偏移。第二步,搜索感染标记,在附近寻找原始入口点字段。很多感染型病毒会把原始入口点保存在一个固定偏移处,找到这个值以后,把它写回到 PE 头里的 AddressOfEntryPoint 字段。第三步,把新增节从节表中删除,并把节数量字段改回原来的值。第四步,保存文件再用 PE 查看器重新检查一遍,确认入口点和节表都恢复正常。

这里有一个很关键的心态要摆正:修复操作本质上是一种外科手术,而病毒运行时对文件的改动是不可逆的,稍微处理不好会造成二次伤害。所以我建议所有想练手的人,先在副本上反复实验,不要一上来就动原文件,更不要拿重要文件当测试对象。

5.3 组织层面的应急防御思路

处理完单个样本以后,我更关心的是怎么防止它在团队环境里扩散。这款病毒靠文件共享和可移动介质传播,所以最直接的防御就是限制可执行文件在网络共享目录中被大量存放和双击运行。把共享目录里的 exe 文件权限收紧,日常只允许读取,不允许执行,就能阻断大部分传播路径。

部署杀毒软件的时候,也要注意不是装完就完事。感染型病毒往往有多个变种,特征码可能随时变化,所以最好同时开启文件实时监控和启发式检测。同时,应急预案里要加入批量扫描脚本,第一时间把所有共享盘和本地盘扫一遍,把所有命中感染标记的文件隔离出来。

我见过很多企业在遭遇这类样本时,最耽搁时间的不是查杀本身,而是排查范围不全。有人清了当前磁盘,结果漏掉了移动硬盘里的备份,第二天又中了一遍。所以清理过后一定要做全网范围的二次扫描,确保所有接进来的存储设备都被扫过,才能算是真正的应急闭环。

6. 实操心得与高频错误汇总

6.1 初学者最常见的四个翻车点

聊了这么多技术细节,最后我想把实战里踩过的坑集中整理一下,基本都是初学汇编病毒分析时最容易翻车的点,每一条我都有切身体会。

第一,一上来就单步跟踪而不做静态规划。幽灵王这种样本还好,如果你直接单步走它那几十步解密循环,很浪费时间,而且容易迷失。正确顺序永远是先静态分析确定关键地址,再动态验证。

第二,看到壳就慌,一门心思去脱壳。其实很多壳你不需要手动脱,你只需要知道它解密完以后跳到哪就行。我的经验是,判断“控制流去哪”比“数据怎么解”更重要,尤其对于这种老式样本来说,跳转目标地址才是关键路径。

第三,忽略文件的熵值和尾部数据。很多分析师看入口点没问题就认为文件是干净的,可这类感染型病毒有时候并不会修改入口点,而是通过其他方式劫持流程。所以你扫描时不仅要看入口点,还要看节表有没有奇怪的多余节和明显的文件尾部追加数据。

第四,动态调试忘记关中断优先级。某些反调试代码很擅长利用调试器的单步异常机制来干扰你,提醒你陷入断点地狱的时候,先别怀疑自己,回头检查一下哪些异常被设置成了无条件中断,把非必要的异常全部忽略掉,调试体验会好非常多。

6.2 从这次分析里沉淀出的逆向习惯

最后再分享一个小习惯,是这次分析以后我一直保留的。面对任何未知样本,我会先在一张纸上画出它的“生命周期图”:样本启动 → 壳解密 → 跳转到真实模块 → 动态加载 API → 搜索目标 → 执行感染 → 返回原始入口点。这张图一旦画出来,你的分析就有了坐标,所有后续工作都围绕这张图去验证和补全。

另一个习惯是把分析过程中每一个关键跳转地址都记录下来,无论是壳的跳板地址、真实模块入口、还是调用系统 API 的 call 目标。等后面需要动态验证时,这些地址就是你的断点地图,能让你在调试器里始终占领先机。

这类的老式感染型病毒在今天的真实网络里已经不算主流威胁,但作为汇编语言和 PE 结构的学习载体,它依然是很好的素材。每次重新复盘,我都还能从里头找到一些之前没注意到的细节,而这份细抠代码的耐心,恰恰是逆向工作里最不能丢的东西。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 5:50:21

联盟营销传播规模预测:基于时空动态网络的两阶段框架解析

联盟营销场景里做过投放增长的朋友应该都有体会:一个活动链接发出去之后,到底能裂变出多大的传播规模,这件事在事前几乎只能靠拍脑袋。拍得准了,预算、库存、客服人力都能接得住;拍得偏了,要么流量爆了承接…

作者头像 李华
网站建设 2026/10/17 5:48:27

Tomcat 7 Windows x64 部署指南:从解压、JDK配置到server.xml调优与避坑

简介:apache-tomcat-7.0.103-windows-x64.zip 是一款面向 Java Web 开发与运维人员的 Tomcat 7 免安装发行包,适用于本地调试 Servlet/JSP 项目、搭建轻量级测试服务器或快速构建中小型 Web 运行环境。压缩包解压后即可直接启动使用,无需额外…

作者头像 李华
网站建设 2026/10/17 5:48:27

Dynaslam CPU版编译实战:Ubuntu 18.04下无GPU运行视觉SLAM

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 5:47:37

轻量化+开放云+多芯生态:中小银行金融级云底座破局之道

中小银行云化转型:以“轻量化、开放云、多芯生态”满足金融级要求中小银行上云这件事,喊了很多年,但真正落地的痛点一直没变:机房没地方、运维没几个人、预算年年紧巴巴。市面上主流的云方案,动辄就是几百台节点的起步…

作者头像 李华
网站建设 2026/10/17 5:47:34

Redis分布式缓存实战:从穿透、击穿到一致性设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 5:44:12

GCD求和难题解析:容斥原理与欧拉函数反演

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华