MalDev Myths
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
多年来,我见过很多 MalDev(恶意代码开发)领域的技术早已过时,或者被错误应用。下面是简要清单。
做 MalDev 时,首先要考虑你的代码要防御的对象:
-
自动化检测系统(如 AntiVirus(AV)、EDR、沙箱等)会检测并阻断你的恶意代码 -
针对你的恶意代码功能进行手动逆向工程
我主要关注第一类,因为我只想要一个能正常工作的 beacon(信标)。如果 SOC(安全运营中心)逆向了我的恶意代码,我请他们喝啤酒。本文面向 Red Team(红队)场景,聚焦于 loader 和 dropper(装载器与投递器),即如何将恶意代码存储在文件或内存中。
自动化检测主要指 AntiVirus(AV)、EDR(端点检测与响应)以及沙箱等。本文不讨论手动逆向工程的场景。
本清单基于 Sektor7 的课程(maldev essentials、intermediate、Windows evasion),但我发现这些过时的信息到处都是——黑客圈似乎没跟上发展,还在复读 90 年代的东西。更糟糕的是,几乎所有 MalDev 课程和培训都只是随意堆砌技术,希望蒙混过关,而不是先分析安全软件,找出盲点。例如,建议先了解 EDR 的工作原理。
这些信息不是靠经验得来的,而是基于对计算机基础、Windows 原理和安全产品的理解。
有效载荷(payload)存储位置
如果你想在 exe 中存储数据(比如 dropper 场景),可选位置有:
-
.text -
.data -
.rsrc
选哪个?其实无所谓。
如果你修改现有 exe,.text
和.data
通常没有多少空闲空间(也许只有几百字节)。加太多数据会破坏程序正常运行。.rsrc
通常空间充足。
如果你自己生成 exe,放哪都行。EDR 其实看不到数据在进程空间内的流转——它无法区分数据来源。
做静态分析的安全软件可能会稍微影响你的选择。
结论:
-
不要纠结数据放哪。 -
代码放 .text
,数据放.data
或.rsrc
,看起来正常即可。 -
后门化 exe 时,优先用 .rsrc
或其他不影响原有数据的区段。 -
千万别新建 PE section,这会很显眼。
熵(Entropy)
如果你把加密或压缩后的内容放进 exe,熵值会高于周围数据。这可能被检测到,比如:
在.text 区段 0x1234 到 0x5432 之间发现高熵。
图示如下:
但这不是可靠的检测方式。安全软件如果仅凭高熵就阻断可执行文件,会误杀大量正常程序。上图就是 DIE-engine 分析自身的截图。
讨论熵很酷,但对自动化分析其实没那么重要。
结论:
-
熵分析对手动逆向有用,但很少是自动化检测的可靠指标。 -
如果担心熵分析,直接 base64 编码你的数据。 -
总体原则是“融入环境”,让你的编码方式和周围数据类似。
有效载荷的加密/编码方式
很多 shellcode loader 实现了多种加密/编码方式,比如 XOR、RC4、AES、Hex-Encoding 等(见1 2 3)。
选哪种都无所谓。EDR 或 AV 不可能“魔法破解”你的加密——它们又没量子计算机。甚至连你用的加密算法都不知道,更别说你是否加密/解密了。它们无法追踪所有指令,还原你的意图。唯一例外是你用 Windows 标准 API(如 crypt32.dll 的 AES 或 Base64)或带有已知签名的加密库。详见 Shellcode Obfuscation Lab。
静态分析软件如果有足够时间(如 virustotal 的异步分析),会尝试所有标准解码方式,比如 base64、255 种单字节 XOR 等,针对全局变量等数据。
结论:
-
随便怎么加密都行,没太大区别。 -
尽量别用 Windows API 做加密。 -
数据在磁盘上是加密的,运行时在内存中解密(注意内存扫描器!)。 -
避免单字节 XOR,容易被特征检测,至少用双字节 XOR。 -
如果你的数据很容易被识别,别用标准方法(base64、单字节 XOR 等),可以加个随机前缀。
函数调用混淆 / IAT 启发式检测
.exe 文件有个 IAT(Import Address Table,导入地址表),列出所有用到的 DLL 函数。一些安全软件会分析 IAT,判断软件是否恶意。如果结果是“可能恶意”,那就是启发式分析(heuristics)。
本质上这不可靠,因为大多数 Windows 软件本身就会导入很多“看起来恶意”的函数。
比如 cffexplorer.exe 自身的导入表,算恶意吗?
你可以分析 putty、process monitor 等工具,看看它们的 IAT 有多“异常”。
结论:
-
不要只导入“潜在恶意”函数,要看起来正常。 -
尽量让 IAT 看起来正常(比如 API stuffing,或后门化现有 exe)。 -
如果担心,可以用 GetModuleHandle()
和GetProcAddress()
,这两个 API 本身就很常见,但也可能成为 IOC(Indicator of Compromise,威胁指示器)。 -
也可以自己实现 GetModuleHandle()
和GetProcAddress()
(比如 PEB 遍历),但注意:网上流传的 PEB 遍历代码本身可能已被签名检测。
签名 EXE / 证书
如果你正确签名了一个可执行文件,会不会被 AV 跳过?EDR 会不会白名单?我不知道,但我很怀疑。这高度依赖具体安全工具。
泄露的代码签名证书很常见——无论过期、吊销与否。用这种证书签你的恶意代码,会提高通过率还是更显眼?安全软件真的能依赖证书吗?毕竟有泄露证书和 DLL sideloading。自签证书有用吗?
我认为有些安全厂商为了省事、提升性能和减少误报,会跳过已签名的可执行文件。靠谱的安全软件大多会忽略签名。
结论:
-
签了就试试。我一般不签。
代码/进程注入(Code/Process injection)
想往其他进程注入代码?别这么干。
通常有三步:
-
访问目标进程( OpenProcess()
) -
在远程进程分配并写入内存( VirtualAllocEx()
、WriteProcessMemory()
) -
执行( CreateRemoteThread()
)
EDR 会重点监控这三步的各种变体。跨进程操作是 EDR 重点关注对象。
结论:
-
如果非要做进程注入,至少跳过其中一步。 -
入门建议:别用 CreateRemoteThread()
,找别的方法。 -
优先考虑 DLL sideloading。
“EDR 绕过” / ntdll unhooking
有些 EDR 会 hook(挂钩)ntdll.dll
,而不是用 ETW。只有差劲的 EDR 才这么干。很多优秀 EDR(如 MDE、Elastic、Cortex)早就不用这招了。
ntdll hooking 早就过时了,而且永远都能被直接/间接 syscall、ntdll 恢复或重打补丁绕过。EDR 也可以检测自己的 hook 是否被移除——我很难理解这不是标配。直接/间接 syscall 可以被 ETW 和调用栈分析可靠检测,是很强的 IOC。公开的 syscall 实现也经常被签名检测。
如果你非得用 ntdll syscall,记得加调用栈伪造(callstack spoofing)。
结论:
-
如果你面对的是老旧 EDR,且它用 ntdll hooking,可以用非签名的直接/间接 syscall 实现,并加上调用栈伪造。
ETW Patching
ETW(Event Tracing for Windows,Windows 事件跟踪)类似 Syslog 或 Windows 事件日志。进程可以自己生成 ETW 事件,比如性能监控或应用专用事件。Windows 内核也会根据进程行为生成 ETW 事件。
除非你在内核态,否则无法禁用 ETW。你只能通过 patch 进程内的 ETW 来阻断应用专用事件——但这些事件其实不重要。大部分 ETW 事件不是进程自己生成的。
Process OS EDR
┌─────────────┐ EtwWrite() ┌──────────┐ ┌──────────────┐
│ ├───────────►│ │ │ │
│ │ │ ────────►│ │ │
│ │ │ │ ETW │ │
│ │ │ ├─────────►│ │
│ │ │ │ │ │
│ │ syscalls │ ────────►│ │ │
│ ├───────────►│ │ └──────────────┘
│ │ │ │
│ │ └──────────┘
│ │
│ │
└─────────────┘
主要的混淆点在于,实际上存在“两种 ETW(Event Tracing for Windows)”:一种可以被影响和 patch(进程内 patch),另一种则包含关键数据,无法被 patch(内核/系统级 ETW)。此外,AMSI(Antimalware Scan Interface)和 Defender 自身也会生成大量 ETW 事件,记录其行为,这进一步加剧了混淆。
结论:
-
别搞 ETW patch,除非你能 100% 证明它真的有用。
VirusTotal 2/99
VirusTotal 主要做静态分析(static analysis),也就是扫描文件已知签名。它并不会像 Sandbox 或 EDR(Endpoint Detection & Response)那样做大量动态分析(runtime analysis,实际执行文件)。
绕过 VirusTotal 非常简单:
-
加密你的 shellcode(shellcode 加密) -
解密时不要用带签名的代码 -
加入执行防护(execution guardrails)
结论:
-
如果你发 x/y 的 VT(VirusTotal)检测结果来证明“FUD”(Fully UnDetectable),我基本可以断定你的东西不行。
非常规 C2 通道(Unusual C2 Channels)
一个 beacon 通常通过 HTTP GET 和 POST 与 C2(Command & Control)服务器通信。
有些人会用其他服务:
-
Google Docs、Sheets -
Twitter、Xing、Linkedin -
Pastebin -
Slack、Discord、Teams -
Azure Cloud Services -
ETH 智能合约(通过 Web API)
本质上就是把 HTTP 请求发到 Y 而不是 X(加点合适的 header)。这没什么技术含量。如果用的是新潮或冷门的服务(比如 Teams、区块链、LLM),媒体报道会多一些。
理论上,这能让 C2 流量在 SOC(Security Operation Center)眼里更隐蔽——但实际很少见。更有用的是绕过域名白名单,但这更罕见。
结论:
-
如果默认 HTTP 请求被检测,直接更新 C2 HTTP 模板(malleable C2 profile)就行。
实例
下面是我遇到的一些 loader(加载器)案例。
Pwntricks
参考博客 Bypass Cortexxdr And Sophos Edr Like Real Red Teamer:
-
Patch ETW:这里其实没用到 ETW,没意义 -
用 Windows 函数 AES 解密:没意义 -
ntdll unhook:Cortex 不 hook,Sophos 会,但也没意义 -
Module Stomping:好 -
本地线程劫持(Local thread hijacking):好 -
自定义 Havoc C2 profile:好 -
文件膨胀绕过沙箱(Sandbox bypass with file bloating):好
前面三种技术已经过时,基本没必要,只会增加被检测概率。后面四种才是真正有用的 bypass 手段。
Hunter
https://github.com/S3N4T0R-0X0/Hunter
-
API Unhooking:不重要 -
Syscall Dispatcher:和 API unhooking 一起,也不重要 -
ETW Nullification:没用 -
进程终止方法:无关 -
反分析(Anti-Analysis):简单,没意义 -
沙箱检测(Sandbox Detection):好!
这个项目唯一有用的就是沙箱检测,其他部分只会增加被检测概率。
正确做法
我推荐的 shellcode loader 架构:
┌──────────┐
│ encrypted│
│ Payload │
└────┬─────┘
│
│
▼
┌──────┐ ┌────────────┐ ┌───────────┐ ┌─────────────┐ ┌──────────┐ ┌────────────┐
│ EXE │ │ Execution │ │ Anti │ │EDR │ │ Alloc RW │ │ Payload │
│ File ├───►│ Guardrails ├───►│ Emulation ├───►│deconditioner├──►│ Decode/Cp├──►│ Execution │
│ │ │ │ │ │ │ │ │ RX │ │ │
│ │ │ │ │ │ │ │ │ Exec │ │ │
└──────┘ └────────────┘ └───────────┘ └─────────────┘ └──────────┘ └────────────┘
执行防护措施(Execution Guardrails)
首先应检测当前环境是否为预期目标。例如,可通过检查环境变量(ENV variables)、判断主机是否处于指定的 Active Directory(AD)域等方式。
这样可以有效阻止中间设备(如 VirusTotal、Proxy AV、沙箱(Sandbox)等)执行你的代码,确保仅在目标主机(配有其自身的 AV 和 EDR)上运行。
反仿真(Anti Emulation)
许多杀毒软件(AV)会对二进制文件进行仿真(emulation),即解释执行指令并扫描内存中的已知特征码(signature)。此时你的加密内容可能会被暴露。
仿真通常有超时(cutoff),即在执行一定时间或指令数后终止分析。因此,在 loader(加载器)开头可执行数十万条无害指令以消耗仿真资源。
手动解密 shellcode(自解码 shellcode)本身就可能触发仿真超时。这也是“加密越多越难被检测”这一误区的来源。
EDR 疲劳机制(EDR deconditioner)
要执行解密后的 shellcode,通常流程为:
-
分配 RW(可读写)内存 -
拷贝 shellcode -
修改内存权限为 RX(可读可执行) -
执行 shellcode
EDR deconditioner 的做法是多次重复第 1-3 步,且每次用无害的伪造数据,令 EDR(端点检测与响应)引擎“疲劳”扫描。最后再真正执行 1-4 步。
shellcode 执行方式
尽量以最“正常”的方式执行 shellcode。直接跳转(jmp
)到 shellcode 即可。
避免使用常见的执行原语(execution primitives),如 CreateThread()
。除非你确定该方式不易被检测(例如利用 Windows 回调函数等)。
综合应用
先按上述建议实现 loader。
如果仍被检测到,接下来才是“乐趣”开始。思考检测原理,尝试绕过。每当找到新 bypass 技巧时,回头分析其原理,判断假设是否成立,还是仅仅因为其他偶然因素未被检测。
更多信息参考:
-
SuperMega Shellcode Loader -
Cordyceps Injection Technique -
Anti EDR Compendium -
幻灯片 My First and Last Shellcode Loader
原文始发于微信公众号(securitainment):恶意代码开发的常见误区
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论