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

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

实验名称:恶意代码分析实践


1. 基础知识

这一节把实验里反复用到的概念先理一遍,后面写过程时就不再解释名词了。

1.1 PE 文件与加壳

Windows 下的可执行文件是 PE 格式,结构上分三部分:DOS 头(开头那个 MZ,后面 e_lfanew 指向真正的 PE 头)、PE 头(含机器类型、节区数量、可选头、入口点 AddressOfEntryPoint)、节区表(每个节区记录名字、大小、虚拟地址 VMA、文件偏移、属性)。

加壳的思路是:把一个可执行文件压缩成数据块,塞进新文件的一个节区里,再把入口点改到一小段解压代码上。程序运行时先执行这段代码,把原始程序在内存里还原,再跳回原来的入口点。所以加壳后的文件在磁盘上看不到原始的导入表和字符串,strings 抓出来的全是压缩后的乱码。

UPX 是最常见的压缩壳,压缩后典型特征是出现两个节区 UPX0(解压目标区)和 UPX1(解压代码 + 压缩数据)。文件末尾还有 32 字节的 packheader,里面记录未压缩长度、压缩长度、两个 Adler-32 校验值和格式版本号——这几个字段就是自动脱壳的"地图",缺了它工具不知道该还原多长。

1.2 脱壳的两条路

一条是工具脱壳:upx -d、超级巡警、PEiD 插件这类,原理都是读 packheader 把数据解压回去,再把入口点、导入表、节区表修好。这条路快,前提是壳没被改过。

另一条是内存 dump:让程序跑起来,等壳自己把原始代码解压到内存,再把进程内存整块抓下来重建 PE。这条路不管壳怎么改都能用,代价是要有能跑样本的环境(虚拟机)和 dump 工具(LordPE、Scylla、Process Hacker)。本实验最后走的就是这条路。

1.3 常见的反分析手段

按"成本由低到高"排:改壳的特征(段名混淆,让 strings、PEiD 认不出来);破坏脱壳元数据(清零 packheader,让 upx -d 直接失败);反调试(IsDebuggerPresent、检查 PEB.BeingDebugged);反虚拟机(读 VMware / VirtualBox 的注册表键、设备名、MAC 前缀)。

前两种只影响静态分析,后两种要样本真正跑起来才起作用。

1.4 VB6 程序的特征

VB6 编译出来的 PE 依赖 MSVBVM60.DLL,字符串常量普遍是 UTF-16LE(每个字符两字节,后面跟一个 00),字符串操作靠 __vbaStrCopy、__vbaStrCat、__vbaStrMove 这些运行时函数,文件操作常借用 Scripting.FileSystemObject 这个 COM 对象。

这一点对分析影响很大:IDA 的字符串窗口默认只列 ASCII,strings 默认也只按单字节解析,所以 VB6 样本里最关键的那些串(URL、路径、注册表键)用常规办法一条都搜不到,得用 strings -a -el 或把 IDA 的字符串类型改成 Unicode。

1.5 argc / argv 与 strcmp

C 程序的入口是 main(int argc, char **argv)。argc 是参数个数,包含程序名自身;argv[0] 是程序路径或名字,argv[1] 才是第一个真正的参数。32 位下指针 4 字节,所以 argv + 1 在汇编里写作 add eax, 4。

strcmp(a, b) 相等时返回 0,所以后面跟的通常是 test eax, eax + jz(为零则跳)。这两个细节在读 crackme 的分支时是基本盘。

还有一个容易忽略的点:argv[0] 的内容由调用方决定。cmd 里敲 crackme2.exe,传进去的就是 crackme2.exe;PowerShell 里敲 .\crackme2.exe,传进去的是 .\crackme2.exe。程序如果拿 argv[0] 做比较,换个 shell 结果就不一样。

1.6 IRC 与僵尸网络

IRC(Internet Relay Chat)是 1988 年出现的文本聊天协议,标准是 RFC 1459 / 2812,走 TCP。客户端连上服务器后先注册:PASS(可选,服务器口令)→ NICK(昵称)→ USER(用户名、主机名、真实名、服务器名),服务器接受后回 001 RPL_WELCOME。

几个后面要用到的数字回复:

编号 名称 含义
353 RPL_NAMREPLY 频道成员名单
265 RPL_LOCALUSERS 本服务器在线用户数与峰值
266 RPL_GLOBALUSERS 整个网络在线用户数与峰值

常用端口 6667(明文)、6697(TLS),6660–6669 这一段也常用。

僵尸网络(botnet)是攻击者通过 C&C 集中控制的一批失陷主机。选 IRC 当 C&C 的原因很实际:服务器软件现成、支持频道广播(一条命令全网下发)、协议是纯文本、流量混在正常聊天里不显眼。

1.7 蜜罐与 pcap 取证

蜜罐是故意留出漏洞让人打的靶机,价值在于记录。Honeynet 项目在蜜罐内核里挂了 sebek 模块,把攻击者的操作在系统调用层记下来,再和网络流量对照。

分析 pcap 的基本动作有三个:一是看元信息(capinfos 的时间跨度、包数,tshark -z io,phs 的协议分布),先判断这份数据里有什么;二是按字段统计(tshark -T fields 抽 IP、端口,sort | uniq -c | sort -rn 排频次),找异常;三是会话重组(tcpflow、Wireshark 的 Follow TCP Stream),看应用层到底传了什么。前两步定位"谁跟谁说话",第三步才能回答"说了什么"。


2. 实验内容

本次实验分四个任务:

  1. 恶意代码文件类型标识、脱壳与字符串提取:对提供的 RaDa 样本判断文件格式、运行平台、加壳工具,完成脱壳,并从脱壳后的样本里提取编写作者信息。
  2. crackme 逆向:用 IDA Pro 静态或动态分析 crackme1.exe 与 crackme2.exe,找出能输出成功信息的特定输入。
  3. RaDa 样本分析:回答八个问题——二进制摘要、程序目的、特殊特性、反分析技术、样本分类、相似工具、作者溯源条件、至少五种检测方法。
  4. 取证分析:分析蜜罐主机(172.16.134.191,Windows 2000)被攻破并加入僵尸网络的 5 天流量,回答 IRC 协议、僵尸网络、通信对象、规模、攻击源、漏洞、攻击结果七个问题。

3. 实验过程

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

3.1.1 确认文件是什么

样本放在 ~/lab4 下,先看目录和文件类型:

cd ~/lab4
ls -l
file RaDa.exe

图1

图 1 样本目录清单与 file 类型识别结果

file 回显 PE32 executable for MS Windows 4.00 (GUI), Intel i386, 3 sections。三个节区这个数字先记下来——正常编译的 VB6 程序一般不止三个节区,后面会发现这正是加壳的痕迹。

再取摘要和 PE 头信息:

md5sum RaDa.exe rada_dump_.exe
exiftool RaDa.exe | grep -iE "Time Stamp|File Type|Machine Type"

图2

图 2 md5sum 与 exiftool 读出的 PE 头信息

MD5 是 caaa6985a43225a0b3add54f44a0d4c7,和公开样本库里的一致,说明拿到的是同一份文件,后面所有结论都建立在这个前提上。exiftool 读出的时间戳是 2004:10:29 19:59:58+04:00,这是 PE 头里的链接器时间戳,也就是编译链接时间;时区是 +04:00,换算到东八区是 10 月 30 日凌晨,所以不同人做这个实验看到 10-29 还是 10-30 都正常,写报告时带上时区就不会有歧义。

3.1.2 判断加壳工具

按常规路子先看节区表:

objdump -h RaDa.exe | head -20

图3

图 3 加壳版样本的节区表,段名为 JDR0 / JDR1

段名是 JDR0、JDR1,不是常见的 UPX0、UPX1。一开始我以为这不是 UPX 壳,因为 strings -a RaDa.exe | grep -i upx 一条都搜不到。

后来对比了 UPX 加壳文件的典型结构才反应过来:JDR0 的尺寸(0xb000)、虚拟地址(0x401000)、属性(CONTENTS, ALLOC, CODE)和标准 UPX 的 UPX0 完全对得上,JDR1 也对得上 UPX1。壳还是那个壳,只是段名被人改掉了,专门让 strings、PEiD 这类按关键字扫的工具失效。

再让 UPX 自己去判断:

upx -l RaDa.exe

图4

图 4 upx -l 返回 CantUnpackException

回显 upx: RaDa.exe: CantUnpackException: file is modified/hacked/protected; take care!!!。UPX 认得出这是自己的壳(能定位到 packheader),但校验不通过,拒绝处理。两个证据合起来,结论是:UPX 加壳,且壳被改动过。

3.1.3 脱壳失败及其原因

既然只是段名被改,理论上把段名改回 UPX0/UPX1 就能让 upx 认出来。但改之前先看一眼文件尾部,packheader 就在最后 32 个字节:

tail -c 32 RaDa.exe | xxd

图5

图 5 文件末尾 32 字节全为 0x00

全是 0。

这 32 个字节里原本放着未压缩长度、压缩长度、两段 Adler-32 校验和以及格式版本号。被清零之后,UPX 不知道要解压出多长、该校验什么,只能报 modified/hacked/protected。所以改段名也没用,问题不在名字上。

再试强制脱壳:

upx -d -f RaDa.exe -o RaDa_unpacked.exe

图6

图 6 upx -d -f 返回 Unpacked 0 files

-f 是跳过"文件被修改"的校验,但它连基本的长度信息都拿不到,跳过校验也解不出东西,返回 Unpacked 0 files.。

这里要说明一点:脱壳失败不是命令敲错了,而是样本本身做了反脱壳处理。这个判断后面正好用得上,它和任务三第 4 问"样本用了哪些防分析技术"是同一件事。

3.1.4 改用课程提供的内存 dump 版本

课程样本包里还有一个 rada_dump_.exe,77824 字节,日期与 RaDa.exe 相同。从命名和体积看是内存 dump 出来的未加壳版本。先验证它和原样本是否同源:

file rada_dump_.exe
objdump -h rada_dump_.exe | head -20
strings -a rada_dump_.exe | grep -E "Raul|Siles|Perez"
strings -a rada_dump_.exe | grep -E "RaDa_commands|download.cgi|10\.10\.10\.10"

图7

图 7 dump 版本的节区表,共 5 个节区

图8

图 8 strings 直接搜到作者字符串

四条都对上了:

  • file 显示 PE32 GUI,5 个节区;
  • 节区名仍然是 JDR0/JDR1,尺寸和 VMA 与加壳版完全一样(0xb000 @ 0x401000、0x4000 @ 0x40c000),但磁盘文件从 20992 字节变成 77824 字节;
  • strings 能明文搜到 Raul Siles && David Perez,也能搜到 RaDa_commands.html、download.cgi、10.10.10.10。

节区名没变、体积却变大,我的理解是:dump 保留了原来的 PE 头,而 JDR0 在加壳版里只是"解压目标占位区",磁盘上并没有真实数据;脱壳后 0xb000 字节的明文才真正落盘,所以文件变大而段名不变。多出来的 .newIID、.mackt 两个节区是 dump 工具附加的,.newIID 用来重建导入目录,属正常现象。

因此后续所有静态分析都在 rada_dump_.exe 上做。这一步必须写清楚,否则"没脱壳为什么能看到明文"这个问题不好回答。

3.1.5 作者信息与版本时间

用 IDA 打开 rada_dump_.exe,等左下角显示 AU: idle 之后操作。Shift+F12 打开字符串窗口,过滤 Raul:

图9

图 9 IDA 中的作者与版本字符串

地址 内容
0x00403F75 (c) Raul Siles && David Perez
0x00403FB8 (c) 1 - September 2004

这两个时间和前面 exiftool 那个不是一回事:exiftool 的 2004-10-29 是 PE 头里的链接器时间戳,即编译链接时间;IDA 里这句 September 2004 是程序自己写在代码里的版本/版权字符串。两个都对,含义不同,分开写。

答案:作者是 Raul Siles 和 David Perez(西班牙安全研究者,2004 年前后在 Honeynet 项目里发布过这个样本)。

另外,IDA 识别出了 vbaStrCopy、rtcMidVar、__vbaFreeObj、rtcR8ValFromBstr 这些函数名,这是 FLIRT 签名匹配到 VB6 运行时库的结果,说明样本是用 Visual Basic 6 写的。这个信息在任务三很有用,因为 VB6 的字符串大量以 UTF-16 存放,普通 ASCII 的 strings 会漏掉一大半。

3.2 任务二:用 IDA 分析 crackme1 与 crackme2

两个样本都在 Windows 虚拟机的 cmd 里运行(不用 PowerShell,原因见第 4 节问题 6)。

3.2.1 crackme1 的运行结果

crackme1.exe
crackme1.exe 1
crackme1.exe x "I know the secret"
crackme1.exe "I know the secret"

图10

图 10 crackme1 四种参数情况的输出

命令 argc 输出
crackme1.exe 1 I think you are missing something.
crackme1.exe 1 2 Pardon? What did you say?
crackme1.exe x "I know the secret" 3 I think you are missing something.
crackme1.exe "I know the secret" 2 You know how to speak to programs, Mr. Reverse-Engineer

第三条最值得注意:明明把口令带上了,却还报参数不对。说明程序判断的是参数个数,而且要求的个数不是 3。去掉多余的 x,只给一个参数就通过了。

所以规则是 argc 必须等于 2,也就是程序名加一个额外参数,且这个参数必须是 I know the secret。

这里踩了一个坑:网上不少资料写的是"输入两个参数,第二个是口令",照着敲反而失败。以实测为准。

3.2.2 crackme1 的静态分析

IDA 打开后 Shift+F12 找字符串,四条关键字符串的 DATA XREF 全部指向同一个函数 main:

图11

图 11 crackme1 四条关键字符串及其交叉引用

字符串 地址 交叉引用
I think you are missing something. 00403000 main+33
I know the secret 00403024 main+5B
Pardon? What did you say? 00403036 main+71
You know how to speak to programs, ... 00403054 main+93

这也顺带说明一个问题:不少 writeup 里函数名是 sub_401280,而我的样本里 IDA 直接识别成了 main。原因是编译器和符号信息不同,函数名不能照抄,靠字符串反查更可靠。

双击 main+5B 跳进去,按空格切图形视图:

图12

图 12 crackme1 main 函数的控制流图

再按一次空格回到文本视图:

图13

图 13 crackme1 main 函数的完整汇编

主逻辑是这样的:

main            proc near
arg_0           = dword ptr  8       ; argc
argv            = dword ptr  10h     ; argv
                push    ebp
                mov     ebp, esp
                call    __main
                cmp     [ebp+arg_0], 2       ; 参数个数是不是 2
                jz      short loc_401002
                push    offset Format        ; "I think you are missing something.\n"
                call    _printf
                jmp     short loc_401327
loc_401002:
                mov     eax, [ebp+argv]
                add     eax, 4               ; argv + 1,指向 argv[1]
                push    eax
                push    offset Str2          ; "I know the secret"
                push    dword ptr [eax]      ; Str1 = argv[1]
                call    _strcmp
                add     esp, 10h
                test    eax, eax
                jz      short loc_401310
                mov     eax, ds:__imp___iob
                push    offset aPardonWhatdidY ; "Pardon? What did you say?\n"
                mov     eax, [eax+8]
                push    eax
                call    _fprintf
                jmp     short loc_401327
loc_401310:
                push    offset aYouKnowHowToSp ; "You know how to speak to programs, ..."
                call    _printf
                mov     [ebp+var_4], 0
loc_401327:
                mov     eax, [ebp+var_4]
                leave
                retn

有三处值得写下来:

第一,arg_0、argv 这两个名字是 IDA 自动恢复的,说明它按 main(int argc, char **argv) 的标准签名还原了栈帧。这比看入口点、看段名都可靠,可以直接证明这是正常的 C 程序入口。

第二,add eax, 4 是 32 位下的指针算术。argv 是 char **,加 4 字节正好跳过 argv[0] 指向 argv[1];紧接着 push dword ptr [eax] 取出的是指针指向的字符串首地址,不是指针本身。这两步是理解 strcmp 两个参数怎么来的关键。

第三,两个 jz 的方向是反的。argc == 2 时用 jz 跳过错误分支,strcmp == 0 时用 jz 进入成功分支。画流程图时不留神,箭头很容易画反。

3.2.3 crackme2 的运行结果

先直接给对口令:

crackme2.exe "I know the secret"

图14

图 14 crackme2 直接运行失败,copy 命令也写错了

还是不通。按提示把文件名改掉再试:

copy crackme2.exe crackmeplease.exe
crackmeplease.exe "I know the secret"

图15

图 15 改名为 crackmeplease.exe 后通过

输出 We have a little secret: Chocolate,成功了。

顺带记一条:copy 必须写全目标文件名。我第一次只敲了 copy crackme2.exe,cmd 理解为"复制到自己",直接报"文件无法自身复制"。

3.2.4 crackme2 的静态分析

图形视图里能看到它是两道串联的门:

图16

图 16 crackme2 的两道判断与逐字符输出循环

loc_4012D5:
                mov     eax, [ebp+argv]
                push    offset Str2         ; "crackmeplease.exe"
                push    dword ptr [eax]     ; Str1 = argv[0]
                call    _strcmp
                test    eax, eax
                jz      short loc_401313
loc_401313:
                mov     eax, [ebp+argv]
                add     eax, 4              ; argv + 1
                push    offset aIKnowTheSecret ; "I know the secret"
                push    dword ptr [eax]     ; Str1 = argv[1]
                call    _strcmp
                test    eax, eax
                jz      short loc_401351

和 crackme1 相比,唯一的区别在第一段:这里 eax 没有 +4,取的是 argv[0],和 "crackmeplease.exe" 比较。也就是说它先确认"是不是我自己取的名字",通过了才检查口令。

这解释了那个现象:只改内容不行,只改名也不行,必须两件事都做——因为它是串联逻辑,不是并列判断。

再往下看成功之后干了什么:

loc_401351:
                mov     [ebp+var_4], 0
loc_401358:
                cmp     [ebp+var_4], 21h      ; 0x21 = 33
                ja      short loc_401381
                mov     eax, [ebp+var_4]
                movsx   eax, byte ptr unk_403880[eax]   ; 按索引取字节
                mov     eax, 42h
                xor     eax, 42h              ; 与 0x42 异或
                call    _putchar
                add     esp, 10h
                lea     eax, [ebp+var_4]
                inc     dword ptr [eax]
                jmp     short loc_401358

cmp [ebp+var_4], 21h 里的 0x21 是 33,ja 表示大于 33 才跳出,所以循环执行 0 到 33 共 34 次。而 We have a little secret: Chocolate 这句话正好 34 个字符。

所以这句成功提示不是明文常量,是以 XOR 0x42 编码存在 0x403880 处,运行时逐字节取出、异或还原后用 putchar 打出来的。这也解释了为什么字符串窗口里搜 Chocolate 搜不到——磁盘上压根没有可读的 ASCII。

可以顺手验证密钥:W 是 0x57,0x57 ^ 0x42 = 0x15;e 是 0x65,0x65 ^ 0x42 = 0x27。跳到 0x403880 看 Hex,头两个字节确实是 15 27。

3.3 任务三:RaDa 样本分析

3.3.1 二进制摘要(问题 1)

项目 结果
文件名 RaDa.exe
大小 20992 字节(加壳)/ 77824 字节(dump 版)
MD5 caaa6985a43225a0b3add54f44a0d4c7
文件类型 PE32 executable (GUI) Intel 80386, 3 sections
加壳情况 UPX 加壳,节区名混淆为 JDR0/JDR1,packheader 被清零
编译时间 2004-10-29 19:59:58(+04:00)
语言 Visual Basic 6(FLIRT 识别出 VB6 运行时函数)
作者 Raul Siles && David Perez
版本字符串 (c) 1 - September 2004

这些字段里,MD5 用于唯一标识样本,节区结构与壳特征用于判断是否为变种,版本字符串和作者串用于溯源,编译时间用于和同一批样本横向比对。

3.3.2 程序目的(问题 2)

在 rada_dump_.exe 里按 UTF-16 找字符串(ASCII 的 strings 看不到,要用 strings -a -el 或在 IDA 里把字符串类型改成 Unicode),一共九条关键字符串,交叉引用几乎全指向同一个函数 sub_404FB0:

图17

图 17 九条关键字符串及其交叉引用(全部指向 sub_404FB0)

# 字符串 地址 交叉引用
1 http://10.10.10.10/RaDa 004023A4 sub_404FB0+3110、sub_404AA0+C3D0
2 RaDa_commands.html 004023C4 sub_404FB0+6310
3 cgi-bin 00402404 sub_404FB0+6F10
4 download.cgi 00402414 sub_404FB0+7B10
5 upload.cgi 00402438 sub_404FB0+8710
6 C:\RaDa\tmp 0040245C sub_404FB0+9310
7 C:\RaDa\bin 00402518 sub_404FB0+D310
8 REG_SZ 00402504 sub_404FB0+C710
9 HKLM\Software\Microsoft\Windows\CurrentVersion\Run 00402488 sub_404FB0+9F1

九条里七条指向 sub_404FB0,可以认定这是主逻辑函数。另外 http://10.10.10.10/RaDa 还被 sub_404AA0+C3D0 引用了一次,说明除主逻辑外还有第二个函数处理 C&C 地址(见图 18 的 DATA XREF)。

图18

图 18 引用 C&C 地址的指令

读代码时还发现一个细节:sub_404FB0 在 0x0040A5013 到 0x0040A50C5 这一段里连续执行了九次 __vbaStrCopy,把上面这些字符串逐条复制进全局变量 dword_40C030 到 dword_40C060:

图19

图 19 连续九次 __vbaStrCopy,初始化配置结构体

之后再用 __vbaStrCat 把 '\' 和 "RaDa.exe" 拼成完整路径:

图20

图 20 用 __vbaStrCat 拼接自身驻留路径

也就是说,程序启动阶段先把配置结构体初始化好,再用字符串拼接生成自己的驻留路径。这种写法在 VB6 写的后门里很常见,因为 VB 不做编译期常量合并,全靠运行时拼。

把这几条串起来,行为链条就出来了:

  1. 运行时创建 C:\RaDa\tmp 当临时目录,C:\RaDa\bin 当程序目录;
  2. 把自身复制到 C:\RaDa\bin\RaDa.exe;
  3. 往 HKLM\Software\Microsoft\Windows\CurrentVersion\Run 写一个 REG_SZ 值指向这个路径,实现开机自启;
  4. 连 http://10.10.10.10/RaDa 拉 RaDa_commands.html 取指令;
  5. 按指令调用 download.cgi 下载后续程序,或调用 upload.cgi 把窃取的数据传回去。

结论:这是一个远程控制后门。核心功能是驻留(写 Run 键自启)→ 连 C&C(HTTP 访问固定 IP)→ 取指令 → 下载执行后续载荷或上传窃取的数据。

除远控之外,样本里还有一条字符串 Starting DDos Smurf remote attack...(0x0040258C,交叉引用 sub_405E40+9410),说明它还具备发起 Smurf 放大式 DDoS 攻击的能力。所以它不只是"控制一台机器",还能把这台机器当攻击节点用。

3.3.3 与普通程序不同的特性(问题 3)

  • 加了压缩壳,真实代码和字符串在磁盘上都不可见;
  • 硬编码远程地址,主动向外交连,不依赖用户操作;
  • 写注册表 Run 键实现开机自启;
  • 工作目录是运行时动态创建的(C:\RaDa\tmp),安装阶段不留痕;
  • 通信用 HTTP 协议走 80 端口,看起来像正常网页访问,容易穿过防火墙;
  • 会读 VMware 的注册表键判断自己是不是在虚拟机里(见下一问);
  • 具备发起 DDoS 的能力,不是单纯的控制通道。

3.3.4 防分析 / 逆向技术(问题 4)

实测到四种,前三种一个比一个更彻底:

  1. UPX 加壳。压缩代码段、改入口点,静态看不出真实导入表和字符串。
  2. 壳名混淆。节区名被改成 JDR0/JDR1,尺寸和 VMA 却和 UPX0/UPX1 一致,专门让按关键字扫描的工具失效。strings -a RaDa.exe | grep -i upx 一条都搜不到,就是这么来的。
  3. 破坏脱壳元数据。文件末尾 32 字节的 UPX packheader 被全部清零,未压缩长度、压缩长度和校验值都丢了,导致 upx -d 和 upx -d -f 都返回 Unpacked 0 files。这是让自动脱壳彻底失效的关键一步。
  4. 虚拟机检测。样本读取 HKLM\Software\VMware, Inc.\VMware Tools\InstallPath(字符串在 0x0040248C,交叉引用 sub_405E40+C41),键存在就说明自己跑在 VMware 里,可以据此改变行为或直接退出。

图21

图 21 Run 自启键、VMware 检测键与 DDoS 字符串

图里三组字符串:aHklmSoftwareMi 是自启动键的完整路径,aHklmSoftwareVM 是 VMware 检测键,aStartingDDosS 是 DDoS 提示。

对应的应对办法:壳头和段名都被改过时,自动脱壳工具基本没戏,只能靠动态内存 dump(本实验用的 rada_dump_.exe 就是这条路);虚拟机检测可以通过改注册表、换物理机或加反-反调试插件绕过。

3.3.5 样本分类与理由(问题 5)

归为后门程序(Backdoor),同时兼具僵尸程序(Bot)的特征。

理由:它没有自我复制能力,也不感染其他文件,不是蠕虫也不是传统病毒;核心行为是建立并维持一条隐蔽的远程访问通道,并把自己挂进自启动项常驻。而 Starting DDos Smurf remote attack... 这条字符串说明它还会按控制端指令发起攻击,这一点更接近 Bot。

严格按分类学,它属于"后门 + 远控木马"的复合体;在这个数据集里,它被植入蜜罐的路径是 445 弱口令,之后又被拉进 IRC 僵尸网络,实际扮演了 bot 的角色。

3.3.6 功能相似的其他工具(问题 6)

工具 相似点 不同点
灰鸽子 国产远控,注册表自启 + 上线通知 有完整图形控制端,功能更全
meterpreter(msfvenom 生成) 内存驻留、模块化、可提权 走反弹连接,不是 HTTP 轮询
Cobalt Strike Beacon HTTP/HTTPS 心跳拉指令,可插桩 商业级,带 Malleable C2 伪装
SubSeven、Poisonsnake 等早期木马 同期同类,注册表自启 + 固定端口 多为自有协议,不用 HTTP
nc -e 反弹 shell 一句话拿 shell 无持久化、无指令分发

共性都是"客户端连回控制端、以本机权限执行任意命令"。RaDa 的差别在于是客户端主动拉指令(pull),比反弹 shell 的 push 模式更难被防火墙拦——因为它的流量看起来就是一次普通的 HTTP GET。

3.3.7 能否查出作者(问题 7)

能。脱壳后在 IDA 里能看到明文的 (c) Raul Siles && David Perez,还有版本字符串 (c) 1 - September 2004。

能查出来需要三个前提:

  1. 作者自己在代码里留了标识(本例是版权注释进了字符串表);
  2. 这些字符串以明文形式存在——加壳情况下必须先脱壳,本例壳被破坏,只能靠内存 dump;
  3. 有能把数据段按正确编码解析的工具。VB6 的串是 UTF-16,用 ASCII 的 strings 会全部漏掉,必须 -el 或 IDA 里切 Unicode。

环境与限制条件也要说清楚:这是 2004 年的教学样本,作者主动署名,属于"作者愿意被找到"的情况。真实恶意代码绝大多数不留名,能追到的只是编译工具链、语言、代码风格、复用片段这些间接指纹,再结合样本投放时间、C&C 域名注册信息、上传路径等外部线索做关联,很难直接定位到人。所以本题的结论只适用于这类样本。

3.3.8 至少五种检测方法(问题 8)

方法 原理 优点 缺点
特征码检测 与病毒库特征串比对,包括壳的特征(如 JDR0/JDR1 段名、packheader 全零模式) 开销小,对已知样本又快又准 对未知样本和改过壳的变种基本无效
启发式 / 静态规则 按节区异常(可写可执行、入口点不在 .text)、导入表异常、字符串熵值打分 能发现未知变种,不依赖跑样本 误报率偏高,规则要不断调
沙箱动态分析 在隔离环境里运行,监控文件、注册表、网络行为链 能看到完整落地行为,不怕加壳 开销大、慢;样本会检测虚拟机和沙箱
网络流量检测 监测异常外连、恶意 IP(如 10.10.10.10)、周期性 HTTP 请求、URI 里的固定路径(/RaDa/RaDa_commands.html) 不受加壳影响,能发现已失陷主机 加密流量难分析,需要流量基线
主机日志与注册表审计 查 Run 键新增项、C:\RaDa\bin 目录出现、服务注册记录 适合事后溯源,证据链清楚 日志量大,攻击者会清日志
内存镜像分析 dump 进程内存,找解密后的代码和字符串 能绕过加壳,直接拿到明文 要求样本已运行,分析门槛高

最后一条是这个样本逼出来的:壳头和 packheader 都被破坏,磁盘上的文件已经没多少可读信息,反而是内存 dump 版本能直接看到全部字符串。实际部署时通常把"特征码 + 沙箱 + 流量"三层串起来用,单靠任何一层都有盲区。

3.4 任务四:蜜罐 pcap 取证分析

样本 botnet_pcap_file.dat,18119637 字节,分析在 Kali 的 ~/lab4 下进行。先确认文件完整、能被解析:

tcpdump -n -nn -r botnet_pcap_file.dat | head -5

开头几包就是 219.118.31.42 对 172.16.134.191 的探测(UDP 名字服务查询 + TCP 139 三次握手),说明抓包起点落在扫描阶段。蜜罐内网 IP 是 172.16.134.191,后续分析都围绕它。

3.4.1 IRC 与僵尸网络(问题 1、2)

IRC 是什么:Internet Relay Chat,基于文本的实时多用户、多频道聊天协议,走 TCP,标准是 RFC 1459/2812。僵尸网络喜欢拿它当 C&C,原因是服务器软件现成、支持频道广播、协议简单、容易被滥用。

加入网络时发什么:PASS(可选)、NICK、USER 三条消息,服务器回 001 RPL_WELCOME 完成注册。真正标识身份的是 NICK 加 USER 两部分。

常用端口:6667(明文,最常见)、6697(SSL/TLS),以及 6660–6669 这一段。

僵尸网络是什么:攻击者通过 C&C 集中控制的一批被感染主机。用来干什么:DDoS、发垃圾邮件、当跳板、窃密、挖矿、点击欺诈。这份数据里能看到的就是"扫描 + 扩张"——频道里下发的指令是让肉鸡去打另一台 IRC 服务器。

3.4.2 流量总体情况

capinfos botnet_pcap_file.dat | head -20

图22

图 22 capinfos 输出:包数、时长与时间范围

项目 数值
数据包数 54536
文件大小 18119637 字节(17 MB)
抓包时长 429588 秒,约 4.97 天
起始时间 2003-03-01 04:08:09
结束时间 2003-03-06 03:27:57
平均速率 40 字节/秒
tshark -r botnet_pcap_file.dat -q -z io,phs

图23

图 23 协议层次统计

TCP 54350 帧(99.66%),UDP 186 帧(0.34%)。TCP 里再往下拆:nbss 2590、smb 2479、smb_pipe 1251、dcerpc 1233,其中 samr 522、svcctl 648、srvsvc 4;http 1443,含 image-gif 145、image-jpeg 10。UDP 里主要是 nbns 129。

还有一帧 sebek。这是 Honeynet 项目的隐蔽监控模块,说明这份数据是从蜜罐系统的内核记录里导出来的,不是普通旁路抓包。

samr 是 SAM 用户枚举接口,svcctl 是服务控制接口,这两个同时出现基本可以判断:攻击者不只在扫 445,而是已经在尝试枚举账号、创建并启动服务了。

3.4.3 与哪些 IRC 服务器通信(问题 3)

tshark -r botnet_pcap_file.dat -Y "tcp.port==6667" -T fields -e ip.src -e ip.dst \
  | tr '\t' '\n' | grep -v '^172\.16\.134\.191$' | sort | uniq -c | sort -rn

图27

图 24 与蜜罐通信的五台 IRC 服务器

18700  209.196.44.172
   17  63.241.174.144
   14  217.199.175.10
    9  66.33.65.58
    9  209.126.161.29

五台。209.196.44.172 承担了几乎所有指令流量,是主 C&C;后四台只有十几个甚至几个包,说明蜜罐只在初始阶段跟它们握过手。主 C&C 加备用 C&C 是僵尸网络的常见冗余设计。

3.4.4 多少主机访问了这个僵尸网络(问题 4)

这是本题的重点,三种方法的结果并不一致,得解释清楚差别在哪。

方法一:读服务器广播的在线人数

一开始按网上常见写法搜 current_global_users,一条都没有。后来查 IRC 协议才明白,标准做法是看数字回复 265(本地)和 266(全局):

grep -a " 266 " $F | head -5
grep -a " 265 " $F | head -5

图33

图 25 353 昵称统计结果与 265 / 266 回复

:irc5.aol.com 266 rgdiuggac :Current global users: 4752  Max: 4765
:irc5.aol.com 265 rgdiuggac :Current local users: 346  Max: 348

全局在线 4752,历史峰值 4765。

方法二:统计 JOIN / QUIT 的主机

先把 6667 会话按方向拆开:

tcpflow -r botnet_pcap_file.dat "host 209.196.44.172 and port 6667"
ls -l *209.196.044.172*

图29

图 26 tcpflow 拆出的两个方向文件

1052 字节     172.016.134.191.01152-209.196.044.172.06667   蜜罐 → 服务器
1082018 字节  209.196.044.172.06667-172.016.134.191.01152   服务器 → 蜜罐

方向怎么判断:看谁大。 服务器要往频道广播成员名单和消息,所以那个 1MB 的文件才是服务器发下来的;蜜罐自己只发了几百字节。

F=209.196.044.172.06667-172.016.134.191.01152
grep -a "JOIN" $F | cut -d '@' -f 2 | cut -d ' ' -f 1 | sort -u > join_hosts
grep -a "QUIT" $F | cut -d '@' -f 2 | cut -d ' ' -f 1 | sort -u > quit_hosts
cat join_hosts quit_hosts | sort -u | wc -l

图32

图 27 JOIN / QUIT 去重统计结果为 5581

结果 5581。

方法三:统计 353 名单里的昵称

:irc5.aol.com 353 redguard = #xxxxxxxx :昵称1 昵称2 昵称3 ...
grep -a "^:irc5.aol.com 353" $F \
  | sed "s/^:irc5.aol.com 353 [^ ]* [^ ]* [^ ]* ://" \
  | tr ' ' '\n' | tr -d '\r' | grep -v '^$' | sort -u | wc -l

命令拆开看:sed 把"服务器名 + 353 + 接收者 + 符号 + 频道 + 冒号"这一整段前缀删掉;tr ' ' '\n' 一个昵称一行;tr -d '\r' 去掉 Windows 换行残留;sort -u 去重;wc -l 计数。冒号要一起删,否则第一个昵称会多带一个 :。

结果 3461(见图 25 上半部分)。

三个数字怎么取舍

方法 结果 判断
服务器 266 回复 4752 瞬时在线数,最权威
JOIN/QUIT 去重 5581 比服务器峰值 4765 还大,明显高估
353 昵称去重 3461 明显低估

方法二的结果超过了服务器自己记录的历史最高值,这在逻辑上不可能。原因是 JOIN/QUIT 覆盖了整个 5 天的抓包窗口,统计的是"累计出现过"的主机;而同一台僵尸主机掉线重连后,nick!user@host 里的 user 部分会变,sort -u 合并不了,一台机器就被算成好几台。所以 5581 只能当上界看。

方法三偏低,是因为 353 只推送当时在频道里的昵称,而且超长名单服务器会分批下发,脚本只读到部分批次;没发言、没进频道的主机完全不在名单里。

结论:约 4752 台。

另外注意别把 265 的 346 混进来:那是本服务器本地连接数,4752 是整个 IRC 网络的全局数,两个口径不同。

3.4.5 频道里下发了什么指令

这一步一开始漏了,后来补做,结果比预期有价值。

grep -a "PRIVMSG" $F | head -12

图30

图 28 频道内的 PRIVMSG 指令

一共只有 9 条:

:jozix!~JasonFkiddles.are.lame PRIVMSG #xxxxxxxx :lmr PAN 206.167.75.78 6667 1800 2702
:jozix!~JasonFkiddles.are.lame PRIVMSG #xxxxxxxx :lrs PAN 206.167.75.78 6667 1800 2702
:jozix!~JasonFkiddles.are.lame PRIVMSG #xxxxxxxx :lke PAN 206.167.75.78 6667 1200 2702

能读出三件事:

第一,控制端是人。发送者昵称 jozix,mask ~JasonFkiddles.are.lame,是坐在电脑前的 operator 手动发的,不是自动轮询。

第二,指令有固定格式:<命令字> PAN <目标IP> <目标端口> <时长> <线程或次数>。

第三,目标是 206.167.75.78:6667,另一台 IRC 服务器。也就是说这个僵尸网络不只回连 C&C 等指令,它还会带着肉鸡去扫别的 IRC 网络,把人家的服务器也拉进来。命令字 lmr、lrs、lke 是样本私有的缩写(分别出现 5、2、2 次),从参数结构看属于扫描或攻击类指令。

3.4.6 哪些 IP 攻击了蜜罐(问题 5)

tcpdump -n -nn -r botnet_pcap_file.dat 'dst host 172.16.134.191' \
  | awk -F " " '{print $3}' | grep -E '^[0-9]+\.' \
  | cut -d '.' -f 1-4 | sort -u > 20242301_2301.txt
wc -l 20242301_2301.txt

图26

图 29 访问过蜜罐的外部 IP 共 165 个

165 20242301_2301.txt

165 个外部 IP 访问过蜜罐。

命令里有个地方要注意:grep -E '^[0-9]+\.' 不能省。tcpdump 输出里会混进 reading from file ...、packets captured 这类非数据行,不过滤的话统计出来的 IP 数会偏大。

再看整体流量排名,谁在打、谁在陪跑一目了然:

图24

图 30 出现次数最多的 15 个 IP

207.172.16.150(14334 包)和 24.197.194.106(11436 包)流量最大,但它们的大流量来自反复尝试不同路径,不是大文件传输。

3.4.7 攻击者用了哪些端口和漏洞(问题 6)

先看目标端口分布:

图25

图 31 目标端口分布

11179  80
 9798  1152
 8934  6667
 8906  1061
 2547  445
 1063  1697
 1033  1077
  526  2773
  498  4899
  294  139
  290  1051

这里有两个容易看错的项:1152 和 1061 不是被攻击的端口,它们是蜜罐自己发起 IRC 连接时使用的临时源端口,这两项统计的其实是"服务器发回蜜罐的回程包",说明僵尸网络会话是双向长连接。写报告时把这两项排除,否则端口排序会误导。

真正的攻击面集中在:80(Web)、445(SMB)、139(NetBIOS)、4899(Radmin)。

80 端口

tshark -r botnet_pcap_file.dat -Y "http.request" -T fields -e ip.src -e http.request.uri \
  | cut -c1-90 | sort | uniq -c | sort -rn | head -15

图31

图 32 80 端口的请求归类

两类攻击:

210.22.204.101 发了 10 次 /NULL.ida?CCCC...(224 个 C)...==,这是针对 IIS 索引服务 .ida 映射的缓冲区溢出,末尾的 == 是覆盖返回地址用的填充。这个 IP 在 445 端口也是主角,说明它手上有一套完整的工具。

24.197.194.106 换了一种打法,把六个虚拟目录全试了一遍:

/msadc/../../../../..//winnt/system32/cmd.exe?/c+dir
/_vti_bin/../../../../../../winnt/system32/cmd.exe?/c+dir
/_vti_cnf/../../../../../../winnt/system32/cmd.exe?/c+dir
/scripts/../../../../../../winnt/system32/cmd.exe?/c+dir
/samples/../../../../../../winnt/system32/cmd.exe?/c+dir
/Rpc/../../../../../../winnt/system32/cmd.exe?/c+dir

这是目录穿越:用 ../ 跳出 Web 根目录,去执行 winnt/system32/cmd.exe,?/c+dir 就是给它传 /c dir,想看服务器目录结构。每个目录还配了一个 check.bat?../.. 的变体,是在试探哪些目录可写、能不能落脚本。

另外 68.169.174.108 请求了 /pagerror.gif,这是 IIS 的默认错误页图片,用来确认对方是不是 IIS;213.23.49.158 只发了一个 HEAD / 就断开,是看响应头里的 Server 字段。这三个动作连起来正好是"信息收集 → 漏洞确认 → 溢出利用"的顺序。

25 和 135 端口

这两个端口在本数据集里流量很少,合计只有 10 个包(195.36.247.77 4 个、24.197.194.106 6 个),都是有完整三次握手、建立后无应用层数据的连接,属于典型的 Connect 扫描,没有后续利用。

139 端口

294 个包,主要是 NBS 会话请求,流里能看到 \\PC0191\C 这样的共享访问尝试,属于 SMB 共享查点。没有出现口令爆破(smb.nt_status == 0xC000006D 过滤为空),也没有 SMBv1 溢出(超长协商包过滤为空)。

445 端口

图28

图 33 445 与 4899 端口的通信对象统计

4479  172.16.134.191   (蜜罐响应)
2513  61.111.101.78
 878  210.22.204.101
 304  209.45.125.69
 302  66.139.10.15
 286  129.116.182.239

前两家是真正动手的,后面几个只有两三百包,属于查点或者握手后就放弃了。

4899 端口

1036  172.16.134.191
1024  210.22.204.101
  12  24.197.194.106

210.22.204.101 在 4899 上有 1024 个包,而 4899 是 Radmin 的默认控制端口。这条线索和 445 端口上传的文件正好对上。

3.4.8 哪些攻击成功了(问题 7)

判断"成功"不能靠猜,得找落地证据。先在全包里搜两个关键文件名:

tshark -r botnet_pcap_file.dat -Y 'frame contains "r_server"' -T fields -e frame.number -e ip.src
tshark -r botnet_pcap_file.dat -Y 'frame contains "PSEXESVC"' -T fields -e frame.number -e ip.src

图34

图 34 r_server 与 PSEXESVC 的命中帧及来源 IP

1762   210.22.204.101
1800   210.22.204.101

33375  61.111.101.78     33381  61.111.101.78
33511  61.111.101.78     33519  61.111.101.78
33618  61.111.101.78     33623  61.111.101.78
34480  61.111.101.78     ...     ...

两个都命中了,来源 IP 清晰。

案例一:210.22.204.101,弱口令 + 植入 Radmin

把它的 SMB 会话落盘后看内容:

tcpflow -r botnet_pcap_file.dat "host 210.22.204.101 and port 445"
grep -al "r_server" 210.022.204.101.*
strings -a 210.022.204.101.03945-172.016.134.191.00445 \
  | grep -ai "r_server\|radmin\|raddrv\|admdll" | head

图36

图 35 Radmin 内部字符串与各 opnum 的发送方统计

抽出来的字符串是:

Unloading Rdrv driver
ADMDLL.dll
AdmDllInit
AdmDllStart
AdmDllInitAccessCheck
SYSTEM\RAdmin\v2.0\Server\Users
C:\WINNT\System32\r_server.exe /service

这些全是 Radmin 服务端的内部实现细节——镜像驱动名、管理 DLL 的初始化函数、服务端注册表键、服务安装命令行。能抽出这些,说明整个可执行文件都在会话里传输过,不是只在请求里提了一下文件名。最后那行 /service 直接暴露了目的:把自己注册成系统服务。

服务操作序列也对得上:

opnum 24 发送方: 9 172.16.134.191 | 9 61.111.101.78
opnum 11 发送方: 5 129.116.182.239 | 22 172.16.134.191 | 5 195.36.247.77 | 10 210.22.204.101 | 1 66.139.10.15
opnum 16 发送方: 7 172.16.134.191 | 1 209.45.125.69 | 5 210.22.204.101 | 1 66.139.10.15
opnum 17 发送方: 7 172.16.134.191 | 1 209.45.125.69 | 5 210.22.204.101 | 1 66.139.10.15

对 210.22.204.101 而言:opnum 16(OpenSCManagerW)5 次、opnum 17(OpenServiceW)5 次、opnum 11(StartServiceW)10 次。打开服务管理器 → 打开服务句柄 → 启动服务,和 r_server.exe /service 的安装流程完全吻合。

再往后,4899 端口出现 1024 个包的控制会话。445 上传 → 注册服务 → 4899 被控,这条链是闭合的。

案例二:61.111.101.78,PsExec 横向植入

PSEXESVC.EXE 是 PsExec 自动投放到目标 System32 的服务程序,它出现就意味着远程命令执行真的发生了。

opnum 24 是 CreateServiceW,9 次请求、9 次响应,一一对应,说明服务创建全部成功。这个 IP 的 StartServiceW 有 22 次,QueryServiceStatus(opnum 6)有 196 次——启动服务后不停查状态,是在判断程序到底跑起来没有。

图35

图 36 1762 / 1800 帧的报文详情与 opnum 分布

从图 36 还能看出一点:帧 1762 和 1800 都是纯 TCP 数据段(Len=1460 和 Len=296),tshark 没在这一帧上重组出 SMB 层,所以 frame contains 命中说明的是载荷里含有该字符串,不是解析出来的文件名字段。这一点在第 4 节问题 9 里还会提到。

实际情况是程序没跑稳,因为目标机器缺 VC 运行时库,服务起来就崩,所以才有这么多次状态查询。这个案例算"远程执行成功、持久化未完成"。

其余 IP 为什么算失败

129.116.182.239、195.36.247.77 都完成了 SAMR 枚举,列出了 5 个账户(Administrator、Guest、IUSR_PC0191、IWAM_PC0191、TsInternetUser),也通过 srvsvc 枚举了共享(IPC$、ADMIN$、C$),但后面没有 CreateServiceW,也没有文件落地。

209.45.125.69 猜了口令,全部返回 STATUS_LOGON_FAILURE。66.8.163.125、66.139.10.15 认证直接被拒。

判断依据很直接:没有 CreateServiceW 成功记录、没有恶意文件落地、没有控制端口出现会话,三者缺一即不算失陷。

3.4.9 小结

这台蜜罐(172.16.134.191,Windows 2000)在 2003 年 3 月 1 日到 6 日这不到五天里,被 165 个外部 IP 访问过。绝大部分是自动化扫描,真正打穿的只有两台:

  • 210.22.204.101 用弱口令登进 445,传了 Radmin 服务端,注册成服务,随后在 4899 被控;
  • 61.111.101.78 用 PsExec 投放 PSEXESVC.EXE 并成功创建服务,虽然程序因为缺库崩了,但远程执行这一步已经完成。

蜜罐随后在 6667 连上了 209.196.44.172 等五台 IRC 服务器,加入了一个规模约 4752 台的僵尸网络,频道里还收到过针对 206.167.75.78 的扫描指令。

失败的攻击大多是因为目标环境不匹配:没有 IIS 索引服务,.ida 溢出打不中;没有开启相应服务,135、25 端口扫了也白扫;口令强度够的账户猜不进去。


4. 问题及解决方案

问题 1:strings -a RaDa.exe | grep -i upx 完全没有输出,一开始以为样本没有加壳。

问题 1 解决方案:先看 objdump -h 的节区表,发现段名是 JDR0/JDR1,但尺寸、VMA、属性与 UPX0/UPX1 完全对应;再用 upx -l 让工具自己判断,返回 CantUnpackException: file is modified/hacked/protected。两个证据合起来才确定是"UPX 加壳 + 段名混淆"。结论:判断加壳不能只看字符串,段区表和工具自检更可靠。

问题 2:upx -d 和 upx -d -f 都脱不了壳。

问题 2 解决方案:先怀疑是段名问题,试过把段名改回 UPX0/UPX1 再脱,还是失败。后来用 tail -c 32 RaDa.exe | xxd 看文件尾部,发现 32 字节全是 0x00——UPX 的 packheader 被清零,长度和校验信息全丢,任何自动脱壳都救不回来。于是改用课程附带的内存 dump 版本 rada_dump_.exe,并用 file、objdump -h、strings 三重验证它和原样本同源(节区尺寸一致、作者字符串一致)。

问题 3:在 IDA 里选中字符串按 X,状态栏报 JumpOKref 失败,没有交叉引用。

问题 3 解决方案:原因是光标停在了 IDA 自动生成的那行 ; const char Str2[] 上,那只是类型注释,不是真正的数据项。往下移一行,停在 db 'I know the secret',0 上再按 X 就正常了。后来发现更快的办法:直接双击右侧注释里的 main+5B 蓝字,一步跳过去,连 X 都不用按。

问题 4:crackme2 明明输出了 We have a little secret: Chocolate,但字符串窗口里搜不到这段文字。

问题 4 解决方案:读代码才明白,这句话在磁盘上是 XOR 0x42 编码的,运行时逐字节解码再用 putchar 输出,所以不存在明文。验证方法是看 0x403880 处的头两个字节,应该是 15 27(0x15^0x42='W',0x27^0x42='e'),实际核对一致。

问题 5:crackme1 按资料写的"输入两个参数"运行,反而报参数错误。

问题 5 解决方案:实测四种参数组合后确定,程序要求 argc == 2,即程序名加一个参数。资料里的"两个参数"是把程序名算进去了,或者用的是另一个样本版本。以实测为准,报告按实测写。

问题 6:crackme2 在 PowerShell 里改完名也永远失败。

问题 6 解决方案:PowerShell 下必须写 .\crackmeplease.exe,这个 .\ 会被当成 argv[0] 的一部分传给程序,和 "crackmeplease.exe" 比较自然不相等。换到 cmd 运行就正常了。程序逻辑没错,只是不同 shell 对 argv[0] 的取值不同——这一点写进报告比单纯说"改名就成功"更准确。

问题 7:用 tcpflow 落盘后统计 JOIN,得到 0 条。

问题 7 解决方案:读错了方向。一个连接会生成两个文件,JOIN/QUIT 是服务器广播给客户端的,必须读以服务器 IP 开头的那份(209.196.044.172.06667-172.016.134.191.01152),另一份里几乎没有。之后的习惯是遇到双向会话先 ls -l 看体积再决定读哪个。

问题 8:按网上教程搜 current_global_users,一条都搜不到。

问题 8 解决方案:这个字段名是某些 ircd 的私有格式,这台服务器不发。查 RFC 后改用数字回复 265/266,一次就找到了,还额外拿到了 Max 值(4765)——正是它让我发现方法二的 5581 是重复计数。

问题 9:tshark -Y "smb2.cmd==5" 和 -e smb.filename 都没有输出。

问题 9 解决方案:这份 2003 年的流量全是 SMB1,没有 SMB2 协议,smb2.* 字段自然为空;SMB1 的文件名字段在旧版解析器里也不叫 smb.filename。改用 frame contains "r_server" 直接搜字节,再配合 tcpflow 落盘后用 strings 看,比依赖字段名可靠。

问题 10:在 Kali 的终端里粘贴多行 Python 脚本和 heredoc,全部报 ParserError。

问题 10 解决方案:当时终端里先执行了 pwsh,提示符变成 PS>,此时 <<'EOF' 这类 bash 语法不成立。先敲 bash 切到 $ 提示符再执行,或者干脆改用单行命令。判断方法很简单:看提示符是 $ 还是 PS>。


5. 学习感悟

这次实验花的时间比前三次数倍,主要是任务三和任务四都不是"跑一条命令出结果"的活,得反复试、反复改判断。

最大的感受是工具的输出不能直接当结论。strings 说没有 upx,upx -l 又说文件被改过,tail 显示 packheader 全是 0——三条信息单独看都能得出不同结论,只有合起来才能还原出"壳被做了两层手脚"这个事实。任务四里也一样,JOIN/QUIT 统计出 5581 台,比服务器自己记录的历史峰值 4765 还大,如果不去核对那个 Max 值,就会把一个明显错误的数字当成结论写进报告。

第二点体会是方向搞错比命令敲错更难发现。tcpflow 那两个文件,一个是 1MB 一个是 1KB,如果不看大小随便挑一个读,JOIN 统计出来是 0,很容易误判成"这个僵尸网络没有频道广播"。

第三点是VB6 的样本和 C 的样本分析思路不一样。crackme 那种 C 程序,字符串是 ASCII,Shift+F12 一打开全在里面。RaDa 是 VB6 写的,字符串大量是 UTF-16,默认的 ASCII 过滤会漏掉一大半,http://10.10.10.10/RaDa 这种关键地址一开始根本搜不到,最后是 strings -a -el 才确认它确实存在。这种差异不查资料想不到。

第四点来自任务四的两个成功案例。以前对"攻击成功"的理解很模糊,觉得有流量就算打进去了。这次才知道判断失陷要看具体证据:CreateServiceW 有没有成功返回、有没有可执行文件落地、控制端口有没有出现会话。61.111.101.78 那个案例,服务创建成功了、PSEXESVC.EXE 也落地了,但程序因为缺 VC 库崩了——这算成功还是失败?我的理解是"远程执行成功了,但持久化没成",报告里也应该这么写,不能简单二分。

最后说一点方法论:先确认数据是什么,再决定怎么分析。不管是样本还是 pcap,一开始都容易急着找答案,结果在错误的方向上耗时间。先跑 file、md5sum、capinfos、协议层次这类"元信息"命令,花五分钟,能省掉后面一小时。


参考资料

  1. 诸葛建伟.《网络攻防技术与实践》. 电子工业出版社. https://book.douban.com/subject/26851579/
  2. UPX 官方文档. https://upx.github.io/
  3. Microsoft PE and COFF Specification. https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
  4. J. Oikarinen, D. Reed. RFC 1459: Internet Relay Chat Protocol. https://www.rfc-editor.org/rfc/rfc1459
  5. C. Kalt. RFC 2812: Internet Relay Chat: Client Protocol. https://www.rfc-editor.org/rfc/rfc2812
  6. tshark 手册页. https://www.wireshark.org/docs/man-pages/tshark.html
  7. The Honeynet Project. https://www.honeynet.org/
  8. Hex-Rays. IDA Pro 文档. https://docs.hex-rays.com/
posted @ 2026-11-09 09:17  Henryyyy  阅读(4)  评论(0)    收藏  举报