Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
Shelter — 基于ROP的睡眠混淆以规避内存扫描器 | Kitploit
工具/GitHubGitHub/kudaes/shelter
漏洞利用框架内存取证红队
GitHubkudaes/shelter

Shelter

基于ROP的睡眠混淆以规避内存扫描器

查看仓库
390481年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

Shelter

Shelter 是一种完全武器化的睡眠混淆技术,它通过广泛使用 ROP 来实现内存中 payload 的完全加密。

该 crate 具有以下特点:

  • AES-128 加密。
  • 完整 PE 加密能力。
  • 在睡眠期间移除执行权限。
  • 不使用 APC/HWBP/定时器,仅使用 ROP 实现混淆。
  • 使用 Unwinder 在执行 ROP 链之前实现调用栈欺骗。
  • 提供不同的执行方式以适应各种情况。
  • 其他 OPSEC 考虑:DInvoke_rs、间接系统调用、字符串字面量加密等。

内容

  • 用法
  • 示例
    • fluctuate
    • fluctuate_from_address
    • fluctuate_from_pattern
  • 测试模块
  • 待办事项

用法

通过将以下行添加到你的 cargo.toml 来将此 crate 导入到你的项目中:

root@kitploit:~
[dependencies]
shelter = "=0.1.2"

然后,以 --release 模式编译你的项目。

此 crate 的主要功能已封装在三个函数中:

  • fluctuate() 可以加密当前内存区域或整个 PE。此函数需要 PE 的 MZ 字节存在,以便动态获取其基址。
  • fluctuate_from_address() 完全加密 PE。此函数期望输入参数为 PE 的基址。
  • fluctuate_from_pattern() 也完全加密 PE。此函数期望输入参数为自定义的两个字节,用于确定 PE 的基址。这些自定义魔字节替换了经典的 MZ 模式。

当整个 PE 被加密时,原始节区的内存保护被存储在堆中,以便之后恢复。

Shelter 使用 NtWaitForSingleObject 来睡眠。除了指示你想睡眠的秒数外,你还可以传递一个事件句柄,并在任何时候通过信号通知它(例如使用 SetEvent)以在超时前返回。请注意,如果你的整个 payload 都被加密了(我想这才是重点),那么在你无限期睡眠的情况下,你需要一种替代方式来发送事件信号。

示例

fluctuate

该函数期望以下参数:

  • 一个布尔值,指示是加密整个 PE 还是仅加密当前内存区域。传递 true 要求内存中存在 MZ 字节。
  • 程序将睡眠的秒数。如果设为 None,超时将是无限的,这意味着执行将不会返回,直到传递给 NtWaitForSingleObject 的事件被信号通知。
  • 传递给 NtWaitForSingleObject 的事件句柄。此参数可以为 None。如果你将此参数和超时都设为 None,程序将会卡住。
root@kitploit:~
let time_to_sleep = Some(10); // 睡眠10秒
let _ = shelter::fluctuate(false, time_to_sleep, None); // 仅加密当前内存区域
root@kitploit:~
let time_to_sleep = Some(10); // 睡眠10秒
let _ = shelter::fluctuate(true, time_to_sleep, None); // 加密整个PE
root@kitploit:~
pub type CreateEventW = unsafe extern "system" fn (*const SECURITY_ATTRIBUTES, i32, i32, *const u16) -> HANDLE;

let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll"); 
let create_event: CreateEventW;
let event_handle: Option<HANDLE>;
dinvoke_rs::dinvoke::dynamic_invoke!(k32,"CreateEventW",create_event,event_handle,ptr::null_mut(),0,0,ptr::null());
let time_to_sleep = None; // 无限期睡眠
let _ = shelter::fluctuate(true, time_to_sleep, event_handle); // 加密整个PE,直到事件被信号通知

fluctuate_from_address

该函数期望以下参数:

  • 程序将睡眠的秒数。如果设为 None,超时将是无限的,这意味着执行将不会返回,直到传递给 NtWaitForSingleObject 的事件被信号通知。
  • 传递给 NtWaitForSingleObject 的事件句柄。此参数可以为 None。如果你将此参数和超时都设为 None,程序将会卡住。
  • PE 被映射的基地址。

使用此函数的一种方式是使用 DInvoke_rs 手动映射我们的 payload。这样,加载器可以将 payload 自身的基地址发送给它,然后 payload 可以在需要时使用它来混淆自身。这样,加载器可以安全地移除 PE 的头部,以实现一定程度的隐蔽性。

加载器示例:

root@kitploit:~
let payload: Vec<u8> = your_download_function();
let mut m = dinvoke_rs::manualmap::manually_map_module(payload.as_ptr(), true).unwrap();
println!("The dll is loaded at base address 0x{:x}", m.1);
let dll_exported_function = dinvoke::get_function_address(m.1, "run");

let run: unsafe extern "Rust" fn (usize) = std::mem::transmute(dll_exported_function);
run(m.1 as usize);

Payload 示例:

root@kitploit:~
#[no_mangle]
fn run(base_address: usize)
{
	...
	let time_to_sleep = Some(10); // 睡眠10秒
	let _ = shelter::fluctuate_from_address(time_to_sleep, None, base_address); // 从该特定基地址加密整个PE
	...
}

fluctuate_from_pattern

该函数期望以下参数:

  • 程序将睡眠的秒数。如果设为 None,超时将是无限的,这意味着执行将不会返回,直到传递给 NtWaitForSingleObject 的事件被信号通知。
  • 传递给 NtWaitForSingleObject 的事件句柄。此参数可以为 None。如果你将此参数和超时都设为 None,程序将会卡住。
  • 一个 [u8;2] 数组,包含用于查找 PE 基地址的自定义魔字节。

创建此函数的目的是允许加载器移除 PE 的头部和其他签名,包括经典的 MZ 字节。这样,这些字节可以被自定义模式替换,Shelter 将查找该模式以检索 PE 的基地址。

root@kitploit:~
let time_to_sleep = Some(10); // 睡眠10秒
let pattern = [0x29,0x07];
let _ = shelter::fluctuate_from_pattern(time_to_sleep, None, pattern); // 使用自定义模式作为魔字节加密整个PE

测试模块

为了测试该技术的实现,主要使用了 PE-sieve。默认情况下,PE-sieve 会在可执行内存区域内查找植入物,这意味着即使仅混淆当前内存区域(.text)也足以避免检测:

当前内存区域混淆。 当前内存区域混淆(Process Hacker)。

请注意,由于我们使用了 Unwinder,调用栈被欺骗了,因此 /threads 标志也无法检测到映射的 DLL。

现在,PE-sieve 允许通过使用 /data 标志来检查非可执行内存区域。根据该工具的官方文档,将此标志设置为 always 可能会“产生大量噪声/误报”。尽管如此,我们决定使用它来检查整个 PE 加密功能的有效性,因为它可以隐藏 PE 的数据区域,这些区域可能包含内存中植入物的存在指示。

当前内存区域混淆被 PE-sieve 检测到。 整个 PE 混淆保持未被检测。

如图所示,第一张图片显示,当 PE-sieve 扫描非可执行内存页面时,仅混淆 .text 段是不够的,因为某些区域可能包含暴露 DLL 存在的字符串(MZ、DOS 头、段名称等)。另一方面,第二张图片显示,通过使用 Shelter 的整个 PE 混淆机制可以解决这个问题。无论如何,正如 PE-sieve 的维基中所说,此选项会导致大量误报,因为堆中仅仅出现像 ".data" 或 "rdata" 这样的字符串就已经警告可能存在植入的 PE,尽管它无法从内存中转储任何内容(因为该区域没有任何真正的 PE 内容)。

最后,PE-sieve 有一个相对较新的选项,可以通过查找高熵内存区域来检测混淆植入物的存在。此选项(/obfusc)与 /data 结合使用,能够检测到 payload 的存在,因为它所在的内存区域具有高熵(尽管由于完全加密而无法检索到 PE):

整个 PE 混淆检测。 整个 PE 混淆(Process Hacker)。

待办事项

尽管 Shelter 已准备好使用,并且开发时考虑到了 OPSEC,但仍有一些增强功能将在不久的将来添加:

  • 降低整个 PE 被加密时的熵。
  • 将 BCryptEncrypt/BCryptDecrypt 替换为相应的 Nt 函数。
  • 为 gadget 选择过程添加一些随机性。

先前工作

  • Gargoyle
  • Ekko
  • Cronos
下载工具