20242405孙煜麟网络与系统攻防技术实验四实验报告

20242405 2026-2027-1 《网络与系统攻防技术》实验四实验报告

1. 基础知识

(1)PE 文件格式与文件类型识别

Windows 下的可执行文件大多遵循 PE(Portable Executable)格式:最前面是 DOS 头(其中 e_lfanew 指向 NT 头),接着是 IMAGE_NT_HEADERS(文件头 + 可选头),随后是节表(Section Table)和各个节区(.text/.data/.rsrc 等),再往后是导入表、导出表、重定位表、资源目录等数据目录。判断一个样本“是什么、给谁运行的、有没有加壳”,通常看四类信息:机器类型(i386/AMD64)、子系统(GUI/CUI)、链接器版本与时间戳、节区名与各节区的大小/熵/属性。

加壳会显著改写上述结构:壳程序自己占一节(甚至把原始入口点放进一个 RawSize = 0、运行时才解压填充的节),导入表往往被压缩成只剩 LoadLibraryA/GetProcAddress 两项,代码节熵接近 8。PEiD 一类工具就是靠节区名、入口点特征字节和壳的特征码来匹配壳的类型与版本。

(2)加壳与脱壳

压缩壳(如 UPX)的工作过程是:系统先执行壳的解压代码,把原始程序在内存中还原,修复导入表,最后跳转到原始入口点(OEP)执行。脱壳的本质就是“在内存中程序已还原、尚未跳转回 OEP 的时刻把镜像 dump 下来,再重建导入表”。自动脱壳机(超级巡警 VMUnpacker、UPX 自带 -d 等)内置了各类壳的特征与 OEP 定位规则,适合处理教学样本;遇到加密壳/虚拟化壳则要手工找 OEP。

(3)字符串提取与溯源

PE 文件里的 ASCII/UTF-16LE 明文(URL、注册表路径、命令、作者签名、编译器路径)是恶意代码分析中最直接的线索。strings、BinText、IDA 的 Strings 窗口都能提取;对 VB6 程序尤其要注意宽字符(UTF-16LE)字符串,否则会漏掉一半内容。作者名、版权声明、项目代号、编译时间戳组合起来,往往就能把样本归到某个公开项目或某次挑战赛。

(4)VB6 程序的特点

用 Visual Basic 6 编写的程序只静态导入 MSVBVM60.DLL(以及少量 kernel32 函数),大量功能通过 rtcCommandVar、__vbaStrCat 等运行时函数完成,字符串常以 Chr() 拼接、运行期构造。IDA 里这类程序看起来“函数很多、逻辑很碎”,需要结合字符串和 API 交叉引用去读。

(5)IDA Pro 静态分析

IDA 打开 PE 后会做自动分析,识别函数、字符串、导入表并生成交叉引用;Functions 窗口可以快速定位 main/_main,图形视图能看清分支,Hex-Rays 插件可以生成伪代码。分析 crackme 类程序的一般套路是:先看字符串表找“成功/失败”提示,再回溯引用它的比较分支,最后确认比较的对象和条件。

(6)IRC、僵尸网络与蜜罐取证

IRC(Internet Relay Chat)是明文文本聊天协议(RFC 1459/2812),客户端连上服务器后先发 NICK 设定昵称、再发 USER 登记用户信息,然后 JOIN 频道。老式僵尸网络普遍用 IRC 做命令与控制(C&C):bot 上线后加入私有频道,攻击者在频道里发指令。默认端口是 TCP 6667,实际也常用 6660–6669、7000 等。蜜罐捕获的流量经 Snort 记录、脱敏后,可以用 tcpdump/tshark 过滤统计,用 tcpflow 按 TCP 流拆分还原出会话内容,再配合 grep/sort/uniq/wc 做数量统计。

2. 实验内容

本次实验共四个任务:

  1. rada 样本的文件类型识别、脱壳与字符串提取:用文件格式/类型识别工具给出 rada 样本的文件格式、运行平台和加壳工具;用超级巡警脱壳机等工具对样本脱壳;对脱壳后的样本做字符串提取,找出编写作者。
  2. crackme1.exe 与 crackme2.exe 的逆向分析:用 IDA Pro 静态(或动态)分析两个 crackme,找出能让程序输出成功信息的特定输入。
  3. rada 恶意代码分析:对自制恶意代码样本 rada 做摘要、目的、特性、反分析技术、分类、相似工具、作者溯源和检测方法共 8 个问题的分析。
  4. 取证分析实践:分析 Snort 收集的蜜罐主机 5 天网络数据(IP 已混淆),回答 IRC、僵尸网络、通信服务器、访问主机数、攻击源 IP、被攻击漏洞、攻击是否成功等 7 个问题。

实验环境:Windows 11(主机名 sunyulin,学号 20242405);工具包括 PEiD 0.95、超级巡警之虚拟机自动脱壳器 V1.3(VMUnpacker)、IDA Pro 8.3(含 Hex-Rays)、Python 3.11(pefile/capstone)以及 Windows 自带的 certutil、findstr 等。

3. 实验过程

3.1 任务一:rada 样本的文件类型识别、脱壳与字符串提取

3.1.1 文件类型与加壳识别

先用自己写的 PE 解析脚本(tools/20242405_pe_summary.py,基于 pefile)把样本的头信息、节表、导入表打印出来,结果见图 1。

图1 样本基本信息与 PE 结构

从输出可以直接读出:

项目 结果
文件大小 20 992 字节(0x5200)
MD5 caaa6985a43225a0b3add54f44a0d4c7
SHA1 4164423ece62c5c4c287f8c2003b84e4e3a6cfda
SHA256 13c2379e9d9f679396e21a3391804cc834892f25691381b6d078d07b56d38f90
文件格式 PE32(Portable Executable 32 位),DOS 头 + PE 头 + 3 个节
运行平台 32 位 Windows(子系统 WINDOWS_GUI,链接器 6.0)
时间戳 2004-10-29 23:59:58 UTC
入口点 RVA 0xFD20,落在节 JDR0 内
导入表 KERNEL32.DLL:LoadLibraryA、GetProcAddress、ExitProcess;MSVBVM60.DLL:ord_618

节表有三个节:JDR0(虚拟地址 0x1000,虚拟大小 0xB000,原始大小 0,熵 0.000)、JDR1(0xC000,原始大小 0x4000,熵 7.826)、.rsrc(0x10000,原始大小 0xE00,熵 2.407)。入口点落在 JDR0 里而这个节在文件中根本没有实体数据,导入表又只剩 LoadLibraryA/GetProcAddress 两项,代码节熵高达 7.826——这三条都是典型的“加壳”特征:真正的代码在运行时才被解压到 JDR0。同时 MSVBVM60.DLL 说明原始程序是用 VB6 写的。

再用 PEiD 识别壳的类型。PEiD 主界面直接传入文件参数时只会打开“多重文件扫描器”,把样本加进去后得到图 2 的结果。

图2 PEiD 识别加壳工具

PEiD 给出的签名是 UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo。也就是说,样本用 UPX 压缩壳加壳,壳作者是 Markus Oberhumer 和 Laszlo Molnar。不过要注意两点:一是文件里搜不到 UPX! 标记,二是节名被改成了 JDR0/JDR1 而不是 UPX 习惯的 UPX0/UPX1——这是作者对标准 UPX 做的二次修改,目的就是让只靠节名判断的查壳工具失效;PEiD 是靠代码特征匹配才认出来的。

结论:rada 样本是 32 位 Windows 平台上的 PE 可执行文件(GUI 子系统,VB6 编写),使用 UPX(0.89.6–1.02 / 1.05–2.90 版本,作者 Markus & Laszlo)加壳,且壳被修改过(节名改为 JDR0/JDR1、去掉了 UPX 标记)。

3.1.2 脱壳

用超级巡警之虚拟机自动脱壳器 V1.3 脱壳。工具界面见图 3,启动后提示“加载脱壳模块成功!加载壳特征库成功!”,说明脱壳引擎和壳特征库都正常。

图3 超级巡警脱壳机主界面

操作步骤:点“选择文件”后的“…”按钮选中加壳样本,然后点“给我脱”。该工具在自带的虚拟机里执行壳代码、自动定位 OEP、重建导入表,输出的文件名规则是“原文件名 + _dump_”,本次得到 20242405_RaDa_dump_.exe。为了确认脱壳是否彻底,又用同一个 PE 解析脚本检查了脱壳后的文件,结果见图 4。

图4 脱壳结果验证

脱壳前后对比如下:

项目 加壳样本 20242405_RaDa.exe 脱壳样本 20242405_RaDa_dump_.exe
大小 20 992 字节 77 824 字节
MD5 caaa6985a43225a0b3add54f44a0d4c7 6822fd4734dcb2b5064f3e78304d0eeb
入口点 RVA 0xFD20 0x18A4
节数 3(JDR0/JDR1/.rsrc) 5(JDR0/JDR1/.rsrc/.newIID/.mackt)
JDR0 原始大小 0 0xB000(0xB000 字节完整落盘)
JDR0 熵 0.000 5.583
JDR1 熵 7.826 4.391
导入表 2 个 DLL、4 个函数 msvbvm60.dll 131 个函数

可以看到几个明显变化:JDR0 原来“只有虚拟地址、没有文件数据”,脱壳后 0xB000 字节被完整还原;JDR1 的熵从 7.826 掉到 4.391,说明压缩/加密的数据已经变成正常代码;导入表被重建(多出的 .newIID 节就是新建的导入描述符,.mackt 是脱壳工具留下的标记节),从原来的 4 个函数变成 msvbvm60.dll 的 131 个函数——VB6 程序真正依赖的运行时函数这时才暴露出来。这些现象说明脱壳是成功的。

3.1.3 字符串提取与作者

对脱壳后的样本做字符串提取(ASCII 与 UTF-16LE 两种编码都提取,脚本为 tools/20242405_strings_console.py),并按作者、版权、项目代号等关键字过滤,结果见图 5。

图5 脱壳后样本的关键字符串

关键的几条是:

00002C1C [U] Scan Of The Month 32 (SotM) - September 2004
00002D04 [U] Copyright (C) 2004 Raul Siles & David Perez
00003804 [U] Authors: Raul Siles & David Perez, 2004
00003F7A [A] (c) Raul Siles && David Perez
00003FD3 [A] SotM 32 - September 2004
00002EEC [U] --authors

另外还有 http://10.10.10.10/RaDa、RaDa_commands.html、C:\RaDa\tmp、C:\RaDa\bin、HKLM\Software\Microsoft\Windows\CurrentVersion\Run\、Starting DDoS Smurf remote attack...、Security through obscurity is the key. 等,这些在任务三还会用到。

结论:rada 恶意代码的编写作者是 Raul Siles 和 David Perez(2004 年)。样本里不仅有 Authors:/Copyright 字符串,程序本身还提供了 --authors 命令行参数直接打印作者信息;Scan Of The Month 32 (SotM) - September 2004 说明它是 2004 年 9 月 Honeynet Project “Scan of the Month” 第 32 期挑战赛的样本,作者正是该期挑战的出题人。

3.2 任务二:crackme1.exe 与 crackme2.exe 的 IDA 分析

两个 crackme 的基本信息:crackme1.exe 595 909 字节、MD5 4357bf8c20fabb642fafc0b73fb0abfb、编译时间 2010-04-24 12:17:49;crackme2.exe 596 596 字节、MD5 46aa577172d8f37ae2ae281515c99afb、编译时间 2010-04-24 12:45:42。两者都是由 MinGW GCC 3.4.5 编译的 32 位控制台程序,且带有 .stab/.stabstr 调试节(所以体积接近 600 KB),调试信息里保留了函数名和源文件路径,分析起来省了不少事。

3.2.1 crackme1.exe

用 IDA 打开后,在函数列表里找到 _main(0x401280)。图 6 是 main 里做字符串比较的那一段。

图6 crackme1 的 main 函数(strcmp 部分)

对应的伪代码(Hex-Rays 还原)是:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  if ( argc == 2 )
  {
    if ( !strcmp(argv[1], "I know the secret") )
    {
      printf("You know how to speak to programs, Mr. Reverse-Engineer\n");
      return 0;
    }
    else
    {
      fprintf(&__iob[2], "Pardon? What did you say?\n");
      return 2;
    }
  }
  else
  {
    fprintf(&__iob[2], "I think you are missing something.\n");
    return 1;
  }
}

程序逻辑很直白:要求恰好带一个参数(argc == 2),把这个参数和常量 "I know the secret" 做 strcmp,相等就打印成功信息并返回 0,否则打印 “Pardon? What did you say?”。字符串 "I know the secret" 在 .rdata 里(RVA 0x1024),IDA 已经自动标成了 Str2,从它往回看交叉引用就能落到这条 strcmp 上。

按这个结论验证(图 7):不带参数时输出 “I think you are missing something.”;带错误参数输出 “Pardon? What did you say?”;带 I know the secret 时输出 “You know how to speak to programs, Mr. Reverse-Engineer”,退出码 0。

图7 crackme1 运行验证

crackme1 的答案:crackme1.exe "I know the secret"(注意整句要作为一个参数传入,引号不能少)。

3.2.2 crackme2.exe

crackme2 比 crackme1 多了一层“身份校验”。图 8 是 main 开头部分。

图8 crackme2 的 argv[0] 校验

可以看到第一条 strcmp 比较的不是 argv[1],而是 *argv,也就是 argv[0](程序自身的路径/文件名),比较对象是字符串 "crackmeplease.exe";不相等就直接打印 "I have an identity problem.\n" 并返回 2。这解释了为什么一开始无论输入什么都是 “I have an identity problem.”——程序先看你“叫什么名字”,名字不对后面根本不看。

通过身份校验后,第二条 strcmp 比较 argv[1] 与 "I know the secret";再之后是一段循环,把 .rdata 中 0x403080 处的 34 个字节逐个与 0x42 异或后用 putchar 输出,最后 puts 一个缓冲区。图 9 是这段异或循环。

图9 crackme2 的异或解密循环

伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int i;
  if ( argc != 2 ) { fprintf(&__iob[2], "I think you are missing something.\n"); return 1; }
  if ( strcmp(*argv, "crackmeplease.exe") ) { fprintf(&__iob[2], "I have an identity problem.\n"); return 2; }
  if ( strcmp(argv[1], "I know the secret") ) { fprintf(&__iob[2], "Pardon? What did you say?\n"); return 3; }
  for ( i = 0; i <= 0x21; ++i )
    putchar((char)(byte_403080[i] ^ 0x42));
  puts(Buffer);
  return 0;
}

为了不在调试器里跟循环,直接把 0x403080 处的密文取出来手工解密。该地址属于 .rdata 节(VA 0x3000,文件偏移 0x1200),换算后文件偏移为 0x1280,取 34 字节:

密文(hex): 15 27 62 2a 23 34 27 62 23 62 2e 2b 36 36 2e 27 62 31 27 21 30 27 36 78 62 01 2a 2d 21 2d 2e 23 36 27
密钥:      0x42(逐字节异或)
明文:      We have a little secret: Chocolate

验证时有个坑:argv[0] 必须是 crackmeplease.exe 这个纯文件名,不能带路径。用 copy crackme2.exe crackmeplease.exe 复制一份,cd 到该目录后直接敲 crackmeplease.exe "I know the secret" 才行;用 .\crackmeplease.exe 或全路径调用时 argv[0] 里带上了路径,身份校验仍然失败。运行结果见图 10。

图10 crackme2 运行验证

crackme2 的答案:把程序复制/改名为 crackmeplease.exe,在该目录下执行 crackmeplease.exe "I know the secret",程序输出 We have a little secret: Chocolate。

3.3 任务三:rada 恶意代码分析

(1)二进制文件摘要

项目 内容
文件名 RaDa.exe(脱壳后 20242405_RaDa_dump_.exe)
大小 20 992 字节(加壳) / 77 824 字节(脱壳)
MD5 caaa6985a43225a0b3add54f44a0d4c7(加壳) / 6822fd4734dcb2b5064f3e78304d0eeb(脱壳)
SHA1 4164423ece62c5c4c287f8c2003b84e4e3a6cfda
SHA256 13c2379e9d9f679396e21a3391804cc834892f25691381b6d078d07b56d38f90
格式/平台 PE32 GUI,32 位 Windows,VB6(MSVBVM60.DLL)
编译时间 2004-10-29 23:59:58 UTC
加壳 UPX 0.89.6 - 1.02 / 1.05 - 2.90(Markus & Laszlo),节名改为 JDR0/JDR1
版本 v0.22(字符串 v0.22)
作者标记 Authors: Raul Siles & David Perez, 2004;SotM 32 - September 2004
通信特征 http://10.10.10.10/RaDa、RaDa_commands.html、cgi-bin/upload.cgi、cgi-bin/download.cgi
文件特征 C:\RaDa\tmp、C:\RaDa\bin、RaDa.exe
自启动 HKLM\Software\Microsoft\Windows\CurrentVersion\Run\ 下写入 RaDa.exe

这些字段里,MD5/SHA256 用于唯一标识样本,PE 时间戳和 “SotM 32” 字样把样本定位到 2004 年 9 月的 Honeynet 挑战,URL/注册表/目录字符串则直接给出行为轮廓,几项合起来足以在样本库里识别同一样本或它的变种。

(2)二进制文件的目的

这是一个用 HTTP 做命令通道的远程控制后门(bot 客户端)。程序执行后大致做这些事:

  1. 访问 http://10.10.10.10/RaDa/RaDa_commands.html,把网页内容下载下来当作命令列表解析;
  2. 在 C:\RaDa\tmp、C:\RaDa\bin 下存放下载下来的文件/自身副本;
  3. 通过 cgi-bin/upload.cgi、cgi-bin/download.cgi 上传/下载文件,用 InternetExplorer.Application 这个 COM 对象在后台构造 multipart/form-data 请求;
  4. 执行命令,包括截屏(screenshot)、上传文件(put)、下载文件(get)等;
  5. 把自己写到注册表 Run 键下实现开机自启;
  6. 发起 DDoS 攻击(字符串 Starting DDoS Smurf remote attack...),并对 10.x、172.16.x、192.168.x 三个内网网段进行探测/横向扩散;
  7. 通过 WMI 查询 Win32_NetworkAdapterConfiguration 拿到网卡 MAC 地址,用于主机标识。

所以它的目的不是破坏文件,而是长期潜伏地控制受害主机,把被控主机变成僵尸网络的一个节点,供攻击者后续做文件窃取、远程操作和拒绝服务攻击。

(3)二进制文件的特性

  • 命令集:字符串里有 Command_install、Command_uninstall、Command_conf、Command_go、Command_usage、Command_exit 六个按钮名,对应安装、卸载、显示配置、执行、显示用法、退出;命令行参数也有 --installdir、--noinstall、--uninstall、--server、--commands、--cgipath、--cgiget、--cgiput、--cycles、--period、--tmpdir、--verbose、--visible、--gui、--authors、--help 一整套。
  • HTTP 通信而非 IRC:这一点和同时代的 Agobot 等 IRC bot 不同。它是“拉”模式——主动去取命令网页,不保持长连接,隐蔽性更好,也不容易被 IRC 流量检测抓到。
  • 文件操作能力:Scripting.FileSystemObject、ADODB.Stream、LoadFromFile、AppendChunk/GetChunk 等对象和函数,说明它能读写任意文件、把文件内容装进 HTTP 请求体上传。
  • 屏幕与输入控制:字符串里有 screenshot、sleep,还导入了 user32!keybd_event,具备截屏和模拟按键的能力。
  • 主机信息收集:WMI 查询 SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True 取 MAC 地址。
  • 持久化:RegWrite 写入 HKLM\Software\Microsoft\Windows\CurrentVersion\Run\,键值指向 RaDa.exe。
  • 反虚拟机:见下一问。
  • 版本演进:v0.22 说明它是某个项目的早期版本,作者是在挑战赛中展示“自制恶意代码”的完整功能。

(4)防止被分析或逆向工程的技术

  1. UPX 加壳 + 改节名:标准 UPX 的节名 UPX0/UPX1 被改成 JDR0/JDR1,UPX! 标记也被抹掉,靠节名和字符串查壳会失手;同时入口点藏在 RawSize=0 的节里,静态反汇编看到的是壳代码而不是真实逻辑。
  2. 关键字符串用宽字符存放:URL、命令名、注册表路径这些关键信息几乎都是 UTF-16LE 的 Unicode 字符串(strings 的 ASCII 模式扫不出来),只有 SotM 32、(c) Raul Siles && David Perez 等少数几条是 ASCII。只做 ASCII 字符串提取会漏掉大半线索,这也是查壳后“strings 看不见东西”的原因之一。
  3. 反虚拟机检测:字符串里有 00:0C:29:、00:50:56:、00:05:69:(VMware 虚拟网卡的 MAC 前缀)和 HKLM\Software\VMware, Inc.\VMware Tools\InstallPath,程序会先判断自己是不是跑在 VMware 里,是的话就不执行恶意行为,让沙箱/虚拟机分析拿不到有效样本。
  4. 动态解析 API:加壳后的导入表只有 LoadLibraryA/GetProcAddress,真实用到的 API 都是运行时按名字取的,导入表分析基本失效。
  5. VB6 自身的分析难度:大量逻辑在 MSVBVM60 的运行时函数(__vbaVarTstEq、rtcCommandVar 等)里,IDA 的伪代码可读性差,需要人工对照字符串和控件名还原业务逻辑。
  6. 不留签名:PE 里没有数字签名,也看不到常规的版本信息字符串(如 CompanyName/ProductName),减少了可用于归因的元数据;作者信息反而藏在程序自己的 --authors 输出和资源字符串里。
  7. 作者在字符串里写了 Security through obscurity is the key.(“安全性来自隐蔽”),算是这套思路的自我总结。

(5)样本分类及理由

分类:后门 / 僵尸网络客户端(HTTP bot),属于木马类中的远程控制后门。

理由:它不具备病毒的特征(不感染、不寄生到其他文件),也不具备蠕虫的特征(没有利用漏洞自动扫描传播的逻辑,字符串里虽然有内网网段,但那是探测/横向的辅助功能);它需要以某种方式被投放到主机上运行,运行后修改注册表实现自启动、隐藏自身、接受远程指令、上传下载文件、截屏、发起 DDoS——这些都是后门/僵尸网络客户端的行为。准确地说,它是“用 HTTP 作为 C&C 通道的 bot 客户端”,和 IRC bot 同属僵尸网络家族。

(6)过去已有的具有相似功能的其他工具

  • Setiri(2002):利用 IE 的 COM 接口把命令藏在 HTTP 请求里、从网页取指令的后门,思路和 RaDa 几乎一样,属于“HTTP 隧道后门”的早期代表。
  • GatSlag(2003):同样是 HTTP 通信、隐藏在正常 Web 流量里的后门。
  • Agobot/Gaobot、SDBot、RBot:2003–2006 年最流行的 IRC 僵尸网络家族,功能上覆盖 DDoS、文件上传下载、截屏、进程控制,只是 C&C 用 IRC 而不是 HTTP。
  • Poison Ivy、Gh0st RAT、Back Orifice 2000、Sub7:经典远程控制木马,提供文件管理、屏幕监控、键盘记录等能力。
  • Netcat / socat 之类的通用工具:常被用作简易后门通道,也能实现“反弹 shell”式的远程控制。
  • 从更广的角度看,后来的 Zeus、Emotet 等也用 HTTP(S) 做 C&C,只是协议格式和加密更强,可以看作这类思路的演进。

(7)能否调查出开发作者

可以,而且这次是比较确定的:

  • 样本里有 Authors: Raul Siles & David Perez, 2004、Copyright (C) 2004 Raul Siles & David Perez、(c) Raul Siles && David Perez 三处署名;
  • 程序本身支持 --authors 参数,运行后直接打印作者信息;
  • 字符串 Scan Of The Month 32 (SotM) - September 2004 把它和 Honeynet Project 2004 年 9 月的第 32 期挑战对应起来,该期挑战正是由 Raul Siles 和 David Perez 出题并提供的样本;
  • PE 时间戳 2004-10-29、VB6 编译特征、v0.22 版本号,与公开资料里的时间线一致。

需要说明的限定条件:署名和字符串是可以伪造或复用的,单靠字符串只能证明“程序自称作者是谁”;严格意义上的归因还要结合代码风格、编译工具链、版本演进、公开的项目记录和作者本人的说明来交叉验证。本次分析的样本属于教学/挑战样本(Honeynet 挑战赛提供),作者身份是公开信息,因此归因结论可靠;如果面对的是真实攻击中的样本,还要考虑作者故意留下假署名、多人协作、代码买卖等情况,结论只能作为线索。分析环境上也有要求:必须在断网的隔离虚拟机/沙箱里做静态分析,动态运行时还要考虑样本的反虚拟机检测,否则要么拿不到行为,要么有横向扩散的风险。

(8)至少 5 种检测该恶意软件的方法

方法一:基于特征码/哈希的检测。 最直接的是把样本的 MD5/SHA256 加入黑名单,终端防护软件比对文件哈希即可拦截;更实用的是写 YARA 规则,匹配脱壳后才会出现的字符串,例如 RaDa_commands.html、http://10.10.10.10/RaDa、C:\RaDa\tmp、Starting DDoS Smurf remote attack...,再叠加 PE 结构特征(节名 JDR0/JDR1、入口点位于 RawSize=0 的节、导入表只有 LoadLibraryA/GetProcAddress)。这种方法的优点是快、误报低,缺点是对改壳、改字符串的变种无效——本例只要把节名和 URL 改掉,静态特征就全失效了,所以必须配合行为检测。

方法二:PE 结构启发式检测。 不依赖具体字符串,而是看“像不像加壳的恶意程序”:代码节熵接近 8、存在 VirtualSize 很大而 RawSize 为 0 的节、入口点不在正常的代码节、导入表函数少于 10 个、同时导入 MSVBVM60 和只有 LoadLibraryA/GetProcAddress。PEiD、Detect It Easy、pefile 脚本都能算这些指标,杀毒引擎的启发式模块也是这么做的。它能抓住未知变种,但正常加壳软件(如某些安装包)也会命中,需要人工复核。

方法三:注册表与自启动项监控。 样本必写 HKLM\Software\Microsoft\Windows\CurrentVersion\Run\(值名 RaDa,数据指向 RaDa.exe)。用 Sysmon 的注册表事件(Event ID 12/13/14)、Autoruns、或 EDR 的启动项监控,只要发现“未知程序往 Run 键写自己”,就告警。这个方法对持久化类恶意代码很有效,也适用于它的变种;缺点是必须在写入动作发生时监控,事后清理要靠基线对比。

方法四:文件系统监控。 样本会创建 C:\RaDa\tmp、C:\RaDa\bin 目录,把自身复制成 C:\RaDa\bin\RaDa.exe,还会往 tmp 里写下载的文件。用 Sysmon 的文件创建事件(Event ID 11)、Procmon 或文件完整性监控(FIM)盯住这些路径和“根目录下突然出现单字母/短名目录”的模式,就能发现落地行为。进一步可以对写入的文件做哈希/字符串扫描,形成联动。

方法五:网络流量检测。 RaDa 的流量特征很明显:HTTP GET /RaDa/RaDa_commands.html 取命令、POST /cgi-bin/upload.cgi 上传文件、GET /cgi-bin/download.cgi 下载文件,目标固定是 10.10.10.10;DDoS 阶段还有 Smurf 攻击的 ICMP 广播/伪造源地址流量。可以在边界 IDS(Snort/Suricata)里写规则匹配 URI 和 Host,在代理日志里按 URI 统计,在 DNS 日志里盯内网主机对可疑 IP 的解析。这种检测不依赖样本本身,变种改文件名也躲不掉,但如果攻击者换成 HTTPS 或换域名就要重新建模。

方法六:主机行为与 API 监控。 样本会创建隐藏的 InternetExplorer.Application COM 对象、调用 ADODB.Stream 写文件、用 WMI 查网卡 MAC、调用 keybd_event 模拟按键、截屏。用 Sysmon(进程创建 Event ID 1、COM 对象注册 Event ID 7、网络连接 Event ID 3)、ETW 或 API Hook 类沙箱监控这些组合行为,可以形成行为规则:例如“控制台程序创建 IE COM 对象并发起 HTTP 请求”本身就是很可疑的组合。

方法七:内存取证与动态沙箱。 加壳样本在磁盘上看不出什么,但运行后内存里一定是脱壳状态。对可疑进程做内存 dump(Process Explorer/Volatility/PE-sieve),再对 dump 做字符串和 YARA 扫描,就能拿到 JDR0 里解压出来的真实代码和字符串。沙箱(Cuckoo、CAPE 等)可以自动完成“运行—监控—报告”的流程;不过要注意本样本会检测 VMware(MAC 前缀、VMware Tools 注册表),沙箱需要做反检测处理(改 MAC、删注册表项、用物理机或 KVM/Xen 等非 VMware 环境)。

方法八:日志关联与威胁情报。 把防火墙、代理、DNS、终端日志汇总到 SIEM,用“内网主机周期性访问同一外部 IP 的某个固定 URI”“非浏览器进程发起 HTTP 请求”“注册表 Run 键新增 + 同名文件落地”等关联规则做检测;再把样本哈希、C2 域名/IP 提交到威胁情报平台,和相关家族的 IOC 做碰撞。这类方法覆盖面最广,但依赖日志的完整性和规则调优。

3.4 分析实践

本部分是取证分析实践。数据源是 Snort 采集的蜜罐主机(172.16.134.191,Windows 2000)5 天的网络流量,去除无关流量后合并为一个二进制日志文件 botnet_pcap_file.dat,IP 等敏感信息已做混淆。先确认数据源规模:capinfos 显示文件共 54 536 个数据包,时间跨度为 2003-03-01 17:08:09 至 2003-03-06 16:27:57(约 429 588 秒,正好 5 天),和题目描述一致。

分析用的是 Wireshark 自带的 tshark 命令行工具配合自己写的 Python 脚本(tools/20242405_forensics.py):先用显示过滤器把关心的流量筛出来、按字段导出,再用脚本做去重计数;对 IRC 会话则按 TCP 流重组服务器发出的字节流,解析 353(NAMES)报文中的昵称。这样处理比在图形界面里逐条翻页要快,也便于复现。下面是逐题的过滤条件和统计结果。

(1) IRC 是什么?客户端申请加入 IRC 网络时发送什么消息?IRC 一般使用哪些 TCP 端口?

IRC(Internet Relay Chat)是互联网上最早的即时文本聊天协议之一,采用客户端—服务器模型:客户端连到 IRC 服务器,服务器之间可以互联组成网络,用户在频道(channel)里群聊或私聊。协议是纯文本的,每条消息以 CRLF 结尾,命令以冒号开头的前缀标识发送者。

客户端连上服务器后,首先要注册:发送 NICK <昵称> 设定自己的昵称,再发送 USER <用户名> <主机名> <服务器名> :<真实姓名> 登记用户信息,服务器返回 001–004 等欢迎消息后注册完成;之后用 JOIN #频道 加入频道,用 PRIVMSG 发言,用 PING/PONG 保活。所以“申请加入 IRC 网络”时最先发送的是 NICK 消息(紧跟 USER)。

IRC 服务器的默认端口是 TCP 6667,实际部署中还常用 6660–6669 这一段,以及 7000、6697(SSL)等。本数据源里蜜罐与所有 IRC 服务器的通信都落在 6667 端口上。

(2) 僵尸网络是什么?通常用于什么?

僵尸网络(Botnet)是由大量被恶意代码感染、受攻击者远程控制的主机(俗称“僵尸”“bot”)组成的网络。控制端叫 Botmaster,命令与控制通道叫 C&C(Command and Control),早期多用 IRC,后来演化出 HTTP、P2P、域前置、加密协议等多种形式。被控主机通常自己并不知情,攻击者通过 C&C 下发指令,让所有 bot 协同行动。

僵尸网络常见的用途包括:发起分布式拒绝服务攻击(DDoS,如 SYN Flood、UDP Flood、Smurf)、发送垃圾邮件和钓鱼邮件、窃取账号密码和敏感数据、做点击欺诈、把被控主机当代理/跳板、分发恶意软件和勒索软件、挖矿等。它本质上是攻击者手里的“可租用的算力和带宽”。

(3) 蜜罐主机(172.16.134.191)与哪些 IRC 服务器进行了通信?

过滤条件:ip.src == 172.16.134.191 && tcp.dstport == 6667 && tcp.flags.syn == 1,取目的地址去重,结果见图 11。

图11 蜜罐通信的 IRC 服务器与攻击源统计

IRC 服务器 蜜罐发起的连接次数
209.196.44.172 1(但会话最长,是主要 C&C)
209.126.161.29 9
66.33.65.58 9
63.241.174.144 1
217.199.175.10 1

一共 5 个 IRC 服务器。209.196.44.172 虽然只有 1 次 TCP 连接,但这次连接的会话长达 9 798 个报文(下面第 4 题就是在这个会话里统计 bot 名单),是蜜罐所加入的那个僵尸网络的主控服务器;其余 4 个更像是 bot 上线时轮询/备用连接的服务器。

(4) 观察期间,多少不同的主机访问了以 209.196.44.172 为服务器的僵尸网络?

思路是把蜜罐与 209.196.44.172:6667 这条 TCP 会话中服务器下发的数据重组出来,找到 353(RPC 353 = NAMES 回复)报文,把每个 353 报文里的昵称取出来去重。353 报文形如:

:irc5.aol.com 353 rgdiuggac @ #x.......x :rgdiuggac mikeoof riktgisli ...

其中 rgdiuggac 是蜜罐自己的 bot 昵称,#x.......x 是频道名,冒号后面是频道成员列表。统计结果见图 12。

图12 被攻击端口、僵尸网络规模与入侵证据

整个会话中共有 67 条 353 报文,去重后得到 3 457 个不同的昵称,也就是约 3.5×10³ 台 bot 主机(不同资料按“包含全部成员快照”的口径统计会得到 3 461 左右,差异来自分片丢失的个别昵称;数量级是一致的)。这也说明该僵尸网络的规模在三千台以上,蜜罐只是其中一个节点。

(5) 哪些 IP 地址被用于攻击蜜罐主机?

过滤条件:ip.dst == 172.16.134.191,对源 IP 去重,得到 165 个不同的源地址(图 11 下半部分列出了报文数最多的几个):

源 IP 发往蜜罐的报文数
209.196.44.172 9 798
207.172.16.150 8 985
24.197.194.106 4 549
61.111.101.78 1 433
210.22.204.101 1 165
217.151.192.231 1 028

这 165 个地址里既有扫描器,也有真实的攻击源;其中 61.111.101.78 就是第 7 题里真正攻破主机的那一个。

(6) 攻击者尝试攻击了哪些安全漏洞?

按目的端口统计(目的端口小于 1024 或等于 4899 的才计入服务端口),被攻击的服务端口如下:

端口 服务 报文数 可能的攻击
80 HTTP 4 488 Web 服务探测、漏洞利用
445 SMB / MSRPC over SMB 2 547 SMB 探测、SVCCTL 服务枚举与远程服务安装
4899 Radmin 490 远程管理软件弱口令爆破
139 NetBIOS Session 294 NetBIOS 共享/空会话枚举
137 NetBIOS Name Service(UDP) 65 名称查询、反射放大
99 未知名服务 43 探测
21 FTP 15 匿名登录/弱口令
110 POP3 9 口令猜测
25 SMTP 8 邮件服务探测
111 Portmap 5 RPC 端口映射探测
135 MSRPC 4 RPC 接口枚举

另外,数据里还有大量目的端口从 1 到 79 逐一出现的单个报文(每个端口 1–3 个包),这是典型的端口扫描特征。综合来看,攻击者主要针对 Windows 的 RPC/SMB 体系(135、139、445)、Radmin 远程管理端口(4899)、Web 服务(80)以及 FTP/POP3/SMTP 等常见弱口令服务。

(7) 哪些攻击成功了?是如何成功的?

成功的是针对 TCP 445(SMB/MSRPC) 的攻击。检索 frame contains svcctl 可以看到一个非常完整的会话:stream 1528,来源 61.111.101.78:1697 → 172.16.134.191:445,共 640 个报文,其中包括 SVCCTL 服务控制管道的绑定、服务创建、服务启动等全部步骤。把这个会话的 SMB 数据重组后可以看到:

  • 会话里出现了 \PIPE\PSEXESVC 命名管道名和 PSEXESVC 服务名(共检索到 52 处),以及 %SystemRoot%\System32\... 形式的服务程序路径——这是 PsExec 类工具把自身作为服务安装到目标机的典型手法;
  • 认证使用的账号是 Administrator,说明攻击者已经拿到了管理员口令(之前的暴力破解/口令猜测奏效),随后用管理员权限通过 SVCCTL 远程创建并启动服务,把 PSEXESVC 落到系统目录并执行。

服务运行后,蜜罐主机上出现了到 209.196.44.172:6667 的 IRC 连接并加入了频道 #x.......x——主机被植入了 bot,正式成为这个僵尸网络的一个节点。其他端口的情况是:135(RPC)只有探测和枚举,没有形成完整利用链;139(NetBIOS)能看到会话但没有后续;80(HTTP)的 4 488 个报文主要是扫描和探测;4899(Radmin)是弱口令爆破,未成功;21/25/110 只有零星连接;UDP 137 只是名称查询。因此可以判断:445 端口的 PsExec/SVCCTL 攻击是这次入侵的入口,主机被攻陷后加入了僵尸网络。

4. 问题及解决方案

  • 问题 1:直接运行 PEiD.exe 样本路径 时,PEiD 并不在主窗口里加载样本,而是弹出了“多重文件扫描器”,主界面的文件、入口点等字段全是空的。
    解决方案:改用 PEiD 的“多重文件扫描器”看结果——它会把样本路径和识别出的壳(UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo)列在表格里;同时用自己写的 pefile 脚本打印节表、熵和导入表做交叉验证,两边结论一致。

  • 问题 2:样本加了壳,直接用 strings 只能看到一堆乱码,PEiD 的节名判断也被骗过(没有 UPX! 标记,节名是 JDR0/JDR1)。
    解决方案:先按“RawSize=0 的节 + 入口点落在其中 + 导入表只有 LoadLibraryA/GetProcAddress + 高熵”判断它确实加壳,再用虚拟机脱壳机处理;脱壳后 JDR0 从 0 字节变成 0xB000 字节、熵从 7.826 降到 5.583,说明判断和脱壳都正确。

  • 问题 3:crackme2 明明输入了正确的 I know the secret,却一直输出 “I have an identity problem.”。
    解决方案:回到 IDA 看 main 的第一条 strcmp,发现它比较的是 *argv(即 argv[0])和 "crackmeplease.exe"。把程序复制成 crackmeplease.exe 并在该目录下用纯文件名运行(.\crackmeplease.exe 或全路径都会让 argv[0] 带路径而校验失败),问题解决。

  • 问题 4:两个 crackme 都有近 600 KB,.stab/.stabstr 调试节占了大半,IDA 打开后函数很多,一时找不到入口逻辑。
    解决方案:利用调试信息里的函数名直接定位 _main(0x401280),再从字符串窗口双击 "I know the secret" 回溯交叉引用,比从头翻函数快得多;另外先用 strings 看提示信息,也能快速判断程序的分支结构。

  • 问题 5:提取字符串时中文和部分提示信息显示为乱码,漏掉了不少内容。
    解决方案:VB6 程序的字符串大多是 UTF-16LE,提取脚本里同时按 ASCII 和 (char)0 交替的模式各扫一遍,再按 Authors/Copyright/RaDa/SotM/http/HKLM 等关键字过滤,作者信息和 C2 地址就都出来了。

  • 问题 6:取证分析的数据源是一个合并后的二进制日志,直接用 Wireshark 打开有几十万条记录,按 IP 手工翻效率极低。
    解决方案:先用显示过滤器把范围缩小(只看 ip.src == 172.16.134.191 && tcp.dstport == 6667 这类),再用 tshark 按字段导出、按流拆分,用 grep/sort/uniq/wc 统计,命令一次跑完就能得到服务器列表、bot 数量和攻击源数量。

  • 问题 7:统计 IRC 频道成员时,一开始是直接把每个 TCP 段的载荷按行解析的,被分段截断的 353 报文只取到前半截昵称,统计出来只有 2 312 个,明显偏少。
    解决方案:改用 tshark -z follow,tcp,ascii,<stream> 把整条 TCP 流重组后再按行解析,并把不以 : 开头的续行拼回上一条消息,同时按 IRC 昵称的合法字符集过滤掉粘连产生的伪 token,最终得到 3 457 个昵称,与公开结果(3 461 左右)基本一致。这也提醒我:做流量统计不能只看单个数据包,一定要先做流重组。

5. 学习感悟、思考等

这次实验做下来,感受最深的是“静态分析不是看一眼字符串那么简单”。一开始我以为 rada 的作者信息用 strings 就能直接看到,结果加壳后的文件里只有乱码;等按“看节表—算熵—判壳—脱壳—再提取”的顺序走完,作者、URL、注册表路径才一个个浮出来。这个过程让我理解了加壳为什么能对抗分析:它不是把程序藏起来,而是把“程序的结构”改得不像程序,让工具和人都失去判断依据。

第二个体会是“程序的逻辑可以藏在很奇怪的地方”。crackme2 卡了我一阵:口令明明对了却一直报身份错误,直到在 IDA 里看到第一条 strcmp 比的是 argv[0] 才反应过来——原来“程序叫什么名字”也是校验条件的一部分。后来手工把 0x403080 处的 34 字节异或 0x42 解出 “We have a little secret: Chocolate”,比在调试器里单步跟循环快得多,也让我意识到“看得懂汇编”之外,“会用脚本算”同样重要。

第三,取证分析那部分让我第一次完整地把“抓包—过滤—分流—统计—还原会话”串了起来。以前看流量只会在 Wireshark 里点来点去,这次用 tshark 的显示过滤器按字段导出,再配合自己写的 Python 脚本去重计数,几行命令就能把“蜜罐连了哪几个 IRC 服务器”“多少个 IP 攻击过蜜罐”“频道里有多少个 bot”算清楚。印象最深的是统计 bot 数量那一步:第一版脚本按单个 TCP 段解析,结果只有 2 312 个,明显偏少;把整条流重组、把被截断的行拼回去之后才得到 3 457 个。工具本身不难,难的是先想清楚“要回答什么问题、该过滤哪些字段”,以及别忘了 TCP 是字节流、应用层消息会被拆开这个基本事实。

不足的地方也有:一是对 VB6 程序的逆向还不够熟,很多运行时函数要靠猜;二是脱壳只用了自动脱壳机,遇到加密壳/虚拟化壳还没有手工找 OEP 的经验;三是取证部分对 Smurf 攻击、PSEXESVC 提权链的理解还停留在“看懂流量”的层面,没有自己复现。后面打算补一补 x64dbg 手工脱壳和 Volatility 内存取证,把这次没走完的路走一遍。

posted @ 2026-11-08 21:37  gchhghfgt  阅读(9)  评论(0)    收藏  举报