Skip to content

Instantly share code, notes, and snippets.

@m1el
Last active April 26, 2020 19:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m1el/1a0c41afe9b672140a7cc204ecfd0c5a to your computer and use it in GitHub Desktop.
Save m1el/1a0c41afe9b672140a7cc204ecfd0c5a to your computer and use it in GitHub Desktop.
all:
rustc -O -Clto=fat -Cpanic=abort --crate-type=staticlib --emit=obj shellcode.rs -o shellcode.obj
cl.exe /Zi /nologo test.c shellcode.obj
test.exe
#![no_std]
#![feature(llvm_asm, lang_items)]
#![feature(global_asm)]
#[allow(non_snake_case, non_camel_case_types, dead_code, unused_variables)]
mod syscalls {
use core::ffi::c_void;
pub type PVOID = *mut c_void;
pub type HANDLE = *mut c_void;
pub type DWORD = u32;
pub type BOOLEAN = u32;
pub type NTSTATUS = u32;
pub type USHORT = u16;
pub type ULONG = u32;
pub type ULONG_PTR = usize;
pub type SIZE_T = usize;
pub type LONG = i32;
pub type VP_STATUS = LONG;
pub type PWSTR = *mut u16;
pub type LARGE_INTEGER = u64;
pub type ACCESS_MASK = DWORD;
pub mod AccessMask {
use super::ACCESS_MASK;
pub const DELETE : ACCESS_MASK = 0x00010000;
pub const READ_CONTROL : ACCESS_MASK = 0x00020000;
pub const WRITE_DAC : ACCESS_MASK = 0x00040000;
pub const WRITE_OWNER : ACCESS_MASK = 0x00080000;
pub const SYNCHRONIZE : ACCESS_MASK = 0x00100000;
pub const STANDARD_RIGHTS_REQUIRED: ACCESS_MASK = 0x000F0000;
pub const STANDARD_RIGHTS_READ : ACCESS_MASK = READ_CONTROL;
pub const STANDARD_RIGHTS_WRITE : ACCESS_MASK = READ_CONTROL;
pub const STANDARD_RIGHTS_EXECUTE : ACCESS_MASK = READ_CONTROL;
pub const FILE_READ_DATA : ACCESS_MASK = 0x0001; // file & pipe
pub const FILE_LIST_DIRECTORY : ACCESS_MASK = 0x0001; // directory
pub const FILE_WRITE_DATA : ACCESS_MASK = 0x0002; // file & pipe
pub const FILE_ADD_FILE : ACCESS_MASK = 0x0002; // directory
pub const FILE_APPEND_DATA : ACCESS_MASK = 0x0004; // file
pub const FILE_ADD_SUBDIRECTORY : ACCESS_MASK = 0x0004; // directory
pub const FILE_CREATE_PIPE_INSTANCE: ACCESS_MASK = 0x0004; // named pipe
pub const FILE_READ_EA : ACCESS_MASK = 0x0008; // file & directory
pub const FILE_WRITE_EA : ACCESS_MASK = 0x0010; // file & directory
pub const FILE_EXECUTE : ACCESS_MASK = 0x0020; // file
pub const FILE_TRAVERSE : ACCESS_MASK = 0x0020; // directory
pub const FILE_DELETE_CHILD : ACCESS_MASK = 0x0040; // directory
pub const FILE_READ_ATTRIBUTES : ACCESS_MASK = 0x0080; // all
pub const FILE_WRITE_ATTRIBUTES : ACCESS_MASK = 0x0100; // all
pub const FILE_ALL_ACCESS: ACCESS_MASK = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF;
pub const FILE_GENERIC_READ: ACCESS_MASK = STANDARD_RIGHTS_READ |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
SYNCHRONIZE;
pub const FILE_GENERIC_WRITE: ACCESS_MASK = STANDARD_RIGHTS_WRITE |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
SYNCHRONIZE;
pub const FILE_GENERIC_EXECUTE: ACCESS_MASK = STANDARD_RIGHTS_EXECUTE |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
SYNCHRONIZE;
}
pub mod FileAttributes {
use super::DWORD;
pub const FILE_ATTRIBUTE_ARCHIVE: DWORD = 0x20;
pub const FILE_ATTRIBUTE_COMPRESSED: DWORD = 0x800;
pub const FILE_ATTRIBUTE_DEVICE: DWORD = 0x40;
pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;
pub const FILE_ATTRIBUTE_ENCRYPTED: DWORD = 0x4000;
pub const FILE_ATTRIBUTE_HIDDEN: DWORD = 0x2;
pub const FILE_ATTRIBUTE_INTEGRITY_STREAM: DWORD = 0x8000;
pub const FILE_ATTRIBUTE_NORMAL: DWORD = 0x80;
pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: DWORD = 0x2000;
pub const FILE_ATTRIBUTE_NO_SCRUB_DATA: DWORD = 0x20000;
pub const FILE_ATTRIBUTE_OFFLINE: DWORD = 0x1000;
pub const FILE_ATTRIBUTE_READONLY: DWORD = 0x1;
pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS: DWORD = 0x400000;
pub const FILE_ATTRIBUTE_RECALL_ON_OPEN: DWORD = 0x40000;
pub const FILE_ATTRIBUTE_REPARSE_POINT: DWORD = 0x400;
pub const FILE_ATTRIBUTE_SPARSE_FILE: DWORD = 0x200;
pub const FILE_ATTRIBUTE_SYSTEM: DWORD = 0x4;
pub const FILE_ATTRIBUTE_TEMPORARY: DWORD = 0x100;
pub const FILE_ATTRIBUTE_VIRTUAL: DWORD = 0x10000;
}
pub mod CreateDisposition {
use super::DWORD;
pub const CREATE_NEW: DWORD = 1;
pub const CREATE_ALWAYS: DWORD = 2;
pub const OPEN_EXISTING: DWORD = 3;
pub const OPEN_ALWAYS: DWORD = 4;
pub const TRUNCATE_EXISTING: DWORD = 5;
}
pub mod ShareMode {
use super::DWORD;
pub const FILE_SHARE_DELETE: DWORD = 0x00000004;
pub const FILE_SHARE_READ: DWORD = 0x00000001;
pub const FILE_SHARE_WRITE: DWORD = 0x00000002;
}
pub mod CreateOptions {
use super::DWORD;
pub const FILE_OPEN: DWORD = 0x00000001;
pub const FILE_CREATE: DWORD = 0x00000002;
pub const FILE_OPEN_IF: DWORD = 0x00000003;
pub const FILE_OVERWRITE: DWORD = 0x00000004;
pub const FILE_OVERWRITE_IF: DWORD = 0x00000005;
pub const FILE_NON_DIRECTORY_FILE: DWORD = 0x00000040;
}
pub mod ObjectAttributes {
use super::DWORD;
pub const OBJ_INHERIT : DWORD = 0x00000002;
pub const OBJ_PERMANENT : DWORD = 0x00000010;
pub const OBJ_EXCLUSIVE : DWORD = 0x00000020;
pub const OBJ_CASE_INSENSITIVE: DWORD = 0x00000040;
pub const OBJ_OPENIF : DWORD = 0x00000080;
pub const OBJ_OPENLINK : DWORD = 0x00000100;
pub const OBJ_VALID_ATTRIBUTES: DWORD = 0x000001F2;
}
#[repr(C)]
pub struct UNICODE_STRING {
pub Length: USHORT,
pub MaximumLength: USHORT,
pub Buffer: PWSTR,
}
#[repr(C)]
pub struct OBJECT_ATTRIBUTES {
pub Length: ULONG,
pub RootDirectory: HANDLE,
pub ObjectName: *mut UNICODE_STRING,
pub Attributes: ULONG,
pub SecurityDescriptor: PVOID,
pub SecurityQualityOfService: PVOID,
}
#[repr(C)]
pub union IO_STATUS_BLOCK_u {
pub Status: VP_STATUS,
pub Pointer: PVOID,
}
#[repr(C)]
pub struct IO_STATUS_BLOCK {
pub u: IO_STATUS_BLOCK_u,
pub Information: ULONG_PTR,
}
#[repr(C)]
pub struct MEMORY_BASIC_INFORMATION {
pub BaseAddress: PVOID,
pub AllocationBase: PVOID,
pub AllocationProtect: DWORD,
pub RegionSize: usize,
pub State: DWORD,
pub Protect: DWORD,
pub Type: DWORD,
}
#[repr(u32)]
pub enum MEMORY_INFORMATION_CLASS {
MemoryBasicInformation = 0, // MEMORY_BASIC_INFORMATION
MemoryWorkingSetInformation = 1, // MEMORY_WORKING_SET_INFORMATION
MemoryMappedFilenameInformation = 2, // UNICODE_STRING
MemoryRegionInformation = 3, // MEMORY_REGION_INFORMATION
MemoryWorkingSetExInformation = 4, // MEMORY_WORKING_SET_EX_INFORMATION
MemorySharedCommitInformation = 5, // MEMORY_SHARED_COMMIT_INFORMATION
}
// type IO_APC_ROUTINE = extern fn(PVOID, *mut IO_STATUS_BLOCK, u32);
type IO_APC_ROUTINE = PVOID;
#[repr(u32)]
pub enum EVENT_TYPE {
NotificationEvent = 0,
SynchronizationEvent = 1,
}
pub mod EventAccess {
use super::DWORD;
pub const DELETE: DWORD = 0x00010000;
pub const READ_CONTROL: DWORD = 0x00020000;
pub const SYNCHRONIZE: DWORD = 0x00100000;
pub const WRITE_DAC: DWORD = 0x00040000;
pub const WRITE_OWNER: DWORD = 0x00080000;
pub const EVENT_ALL_ACCESS: DWORD = 0x1F0003;
pub const EVENT_MODIFY_STATE: DWORD = 0x0002;
pub const MUTEX_ALL_ACCESS: DWORD = 0x1F0001;
pub const MUTEX_MODIFY_STATE: DWORD = 0x0001;
pub const SEMAPHORE_ALL_ACCESS: DWORD = 0x1F0003;
pub const SEMAPHORE_MODIFY_STATE: DWORD = 0x0002;
pub const TIMER_ALL_ACCESS: DWORD = 0x1F0003;
pub const TIMER_MODIFY_STATE: DWORD = 0x0002;
pub const TIMER_QUERY_STATE: DWORD = 0x0001;
}
#[repr(u32)]
pub enum MemoryState {
MEM_COMMIT = 0x1000,
MEM_FREE = 0x10000,
MEM_RESERVE = 0x2000,
}
pub const STATUS_SUCCESS: NTSTATUS = 0x00000000;
pub const STATUS_PENDING: NTSTATUS = 0x00000103;
extern {
pub fn NtCreateFile(
FileHandle: *mut HANDLE,
DesiredAccess: ACCESS_MASK,
ObjectAttributes: *mut OBJECT_ATTRIBUTES,
IoStatusBlock: *mut IO_STATUS_BLOCK,
AllocationSize: *mut LARGE_INTEGER,
FileAttributes: ULONG,
ShareAccess: ULONG,
CreateDisposition: ULONG,
CreateOptions: ULONG,
EaBuffer: PVOID,
EaLength: ULONG,
) -> NTSTATUS;
pub fn NtWriteFile(
FileHandle: HANDLE,
Event: HANDLE,
ApcRoutine: IO_APC_ROUTINE,
ApcContext: PVOID,
IoStatusBlock: *mut IO_STATUS_BLOCK,
Buffer: PVOID,
Length: ULONG,
ByteOffset: *mut LARGE_INTEGER,
Key: *mut ULONG,
) -> NTSTATUS;
pub fn NtQueryVirtualMemory(
ProcessHandle: HANDLE,
BaseAddress: PVOID,
MemoryInformationClass: MEMORY_INFORMATION_CLASS,
MemoryInformation: PVOID,
MemoryInformationLength: SIZE_T,
ReturnLength: *mut SIZE_T,
) -> NTSTATUS;
pub fn NtCreateEvent(
EventHandle: *mut HANDLE,
DesiredAccess: ACCESS_MASK,
ObjectAttributes: *mut OBJECT_ATTRIBUTES,
EventType: EVENT_TYPE,
InitialState: BOOLEAN,
) -> NTSTATUS;
pub fn NtWaitForSingleObject(
ObjectHandle: HANDLE,
Alertable: BOOLEAN,
TimeOut: *mut LARGE_INTEGER,
) -> NTSTATUS;
pub fn NtResetEvent(
EventHandle: HANDLE,
PreviousState: *mut LONG) -> NTSTATUS;
pub fn NtClose(Handle: HANDLE) -> NTSTATUS;
}
}
global_asm!(r#"
.intel_syntax
.macro define_syscall name, id
.global \name
\name:
mov r10, rcx
mov eax, \id
syscall
ret
.endm
define_syscall NtWaitForSingleObject, 0x04
define_syscall NtWriteFile, 0x08
define_syscall NtClose, 0x0f
define_syscall NtQueryVirtualMemory, 0x23
define_syscall NtCreateEvent, 0x48
define_syscall NtResetEvent, 0x172
define_syscall NtCreateFile, 0x55
.att_syntax"#);
// convert ascii string to wide string
fn to_wstr<'a>(src: &str, dst: &'a mut[u16]) -> Option<&'a[u16]> {
let src = src.as_bytes();
if src.len() > dst.len() {
return None;
}
unsafe {
for index in 0..src.len() {
let chr = *src.get_unchecked(index);
if chr > 127 { return None; }
*dst.get_unchecked_mut(index) = chr as u16;
}
Some(dst.get_unchecked_mut(..src.len()))
}
}
use syscalls::*;
//use core::ffi::{c_void};
use core::fmt::{self};
use core::ptr::{null_mut};
#[lang = "eh_personality"] extern fn eh_personality() {}
#[panic_handler]
unsafe fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
loop { }
}
struct Cursor<'a> {
buf: &'a mut [u8],
written: usize,
}
impl<'a> Cursor<'a> {
fn new<'b>(buf: &'b mut [u8]) -> Cursor<'b> {
Cursor {
buf,
written: 0
}
}
fn get(&self) -> &[u8] {
&self.buf[..self.written]
}
fn write_bytes(&mut self, data: &[u8]) -> Option<()> {
let data_len = data.len();
if self.buf.len() < data_len + self.written {
return None;
}
self.buf[self.written..(data_len + self.written)]
.copy_from_slice(data);
self.written += data_len;
Some(())
}
}
impl<'a> fmt::Write for Cursor<'a> {
fn write_str(&mut self, data: &str) -> fmt::Result {
self.write_bytes(data.as_bytes())
.ok_or(fmt::Error)
}
}
unsafe fn for_each_memory_region<F>(mut callback: F)
where F: FnMut(&MEMORY_BASIC_INFORMATION)
{
let mut ptr = null_mut();
let mut memory_info: MEMORY_BASIC_INFORMATION = core::mem::zeroed();
let mut return_length = 0_usize;
loop {
let status = NtQueryVirtualMemory(!0 as _, ptr,
MEMORY_INFORMATION_CLASS::MemoryBasicInformation,
(&mut memory_info) as *mut MEMORY_BASIC_INFORMATION as _,
core::mem::size_of::<MEMORY_BASIC_INFORMATION>(),
&mut return_length);
if status != 0 { return; }
callback(&memory_info);
ptr = (memory_info.BaseAddress as usize + memory_info.RegionSize) as _;
}
}
#[no_mangle]
pub unsafe fn shellcode() -> usize {
let mut file_handle: HANDLE = null_mut();
let mut attrib = core::mem::zeroed::<OBJECT_ATTRIBUTES>();
attrib.Length = core::mem::size_of::<OBJECT_ATTRIBUTES>() as u32;
attrib.Attributes = ObjectAttributes::OBJ_CASE_INSENSITIVE as u32;
let mut buf = [0u16; 512];
let w_path = if let Some(part) = to_wstr(r#"\??\c:\users\null\memory_dump.bin"#, &mut buf) {
part
} else {
return 0x42;
};
let path_len = core::mem::size_of_val(w_path);
let mut object_name = UNICODE_STRING {
Length: path_len as u16,
MaximumLength: path_len as u16,
Buffer: w_path.as_ptr() as _,
};
attrib.ObjectName = &mut object_name;
let mut io_status = core::mem::zeroed::<IO_STATUS_BLOCK>();
let mut allocation_size = 0_u64;
let status = NtCreateFile(
&mut file_handle, AccessMask::FILE_GENERIC_WRITE,
&mut attrib, &mut io_status, &mut allocation_size,
FileAttributes::FILE_ATTRIBUTE_NORMAL as u32,
ShareMode::FILE_SHARE_WRITE as u32,
CreateDisposition::TRUNCATE_EXISTING as u32,
CreateOptions::FILE_NON_DIRECTORY_FILE as u32,
null_mut(), 0);
if status != 0 { return status as usize; }
//let mut wait_event = null_mut();
//let status = NtCreateEvent(
// &mut wait_event, EventAccess::EVENT_ALL_ACCESS, null_mut(),
// EVENT_TYPE::SynchronizationEvent, 0);
// if status != 0 { return status as usize; }
let buf = &mut [0_u8; 128];
let mut offset = 0;
for_each_memory_region(|memory_info| {
if memory_info.State != MemoryState::MEM_COMMIT as u32 { return; }
let mut cursor = Cursor::new(buf);
cursor.write_bytes(&(memory_info.BaseAddress as u64).to_ne_bytes());
cursor.write_bytes(&memory_info.RegionSize.to_ne_bytes());
cursor.write_bytes(&memory_info.State.to_ne_bytes());
cursor.write_bytes(&memory_info.Protect.to_ne_bytes());
cursor.write_bytes(&memory_info.Type.to_ne_bytes());
let result = cursor.get();
let status = NtWriteFile(
file_handle, null_mut(), null_mut(), null_mut(), &mut io_status,
result.as_ptr() as _, result.len() as u32, &mut offset, null_mut());
assert!(status == 0 || status == STATUS_PENDING);
offset += result.len() as u64;
});
{
let result = 0_u64.to_ne_bytes();
let status = NtWriteFile(
file_handle, null_mut(), null_mut(), null_mut(), &mut io_status,
result.as_ptr() as _, result.len() as u32, &mut offset, null_mut());
assert!(status == 0 || status == STATUS_PENDING);
offset += result.len() as u64;
}
for_each_memory_region(|memory_info| {
if memory_info.State != MemoryState::MEM_COMMIT as u32 { return; }
let status = NtWriteFile(
file_handle, null_mut(), null_mut(), null_mut(), &mut io_status,
memory_info.BaseAddress, memory_info.RegionSize as u32, &mut offset, null_mut());
assert!(status == 0 || status == STATUS_PENDING);
offset += memory_info.RegionSize as u64;
});
// NtClose(wait_event);
NtClose(file_handle);
return status as usize;
}
#include <stdio.h>
#include <stdint.h>
extern uint64_t shellcode();
int main() {
int status = shellcode();
printf("%x\n", status);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment