Personal commit

This commit is contained in:
vance 2024-08-21 19:01:00 -07:00
parent ba567d1bba
commit bdaa48defd
14 changed files with 424 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea/
target/
Cargo.lock

27
Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[workspace]
resolver = "2"
members = [
"app",
"shellcode",
"codeloader",
"shellcode-util",
]
[workspace.dependencies]
region = "3.0.2"
utf16_lit = "2.0.2"
[workspace.dependencies.windows-sys]
version = "0.59.0"
features = [
"Win32",
"Win32_Foundation",
"Win32_System",
"Win32_System_Diagnostics",
"Win32_System_Diagnostics_Debug",
"Win32_System_Kernel",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_System_WindowsProgramming"
]

9
app/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "app"
version = "0.1.0"
edition = "2021"
[dependencies]
shellcode-util = { path = "../shellcode-util" }
utf16_lit = { workspace = true }
windows-sys = { workspace = true }

57
app/src/main.rs Normal file
View File

@ -0,0 +1,57 @@
extern crate core;
use core::{ffi::c_void, mem, ptr};
use shellcode_util::{get_module_handle_from_peb, get_procedure_from_edt};
use utf16_lit::utf16;
use windows_sys::{
core::PCSTR,
Win32::Foundation::{BOOL, HANDLE, HMODULE, HWND},
};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
fn main() -> Result<()> {
unsafe { shellcode() }
Ok(())
}
unsafe fn shellcode() {
// Initial Imports
let kernel32 = get_module_handle_from_peb(&utf16!("KERNEL32.DLL")).unwrap();
let LoadLibraryA: extern "system" fn(PCSTR) -> HMODULE =
get_procedure_from_edt(kernel32, "LoadLibraryA\0").unwrap();
let GetProcAddress: extern "system" fn(HMODULE, PCSTR) -> *const () =
get_procedure_from_edt(kernel32, "GetProcAddress\0").unwrap();
// Console output
let GetStdHandle: extern "system" fn(u32) -> HANDLE =
mem::transmute(GetProcAddress(kernel32, "GetStdHandle\0".as_ptr()));
let output_handle = GetStdHandle(u32::MAX - 11);
let WriteConsoleA: extern "system" fn(HANDLE, PCSTR, u32, *mut u32, *mut c_void) -> BOOL =
mem::transmute(GetProcAddress(kernel32, "WriteConsoleA\0".as_ptr()));
let text = "Hello from Console!\n\0";
WriteConsoleA(
output_handle,
text.as_ptr(),
text.len() as u32,
ptr::null_mut(),
ptr::null_mut(),
);
// Messagebox
let user32 = LoadLibraryA("user32.dll\0".as_ptr());
let MessageBoxA: extern "system" fn(HWND, PCSTR, PCSTR, u32) -> i32 =
mem::transmute(GetProcAddress(user32, "MessageBoxA\0".as_ptr()));
let text = "Hello World!\0";
MessageBoxA(ptr::null_mut(), text.as_ptr(), text.as_ptr(), 0);
let text = "Bye World\0";
MessageBoxA(ptr::null_mut(), text.as_ptr(), text.as_ptr(), 0);
// Exit
let ExitProcess: extern "system" fn(u32) =
mem::transmute(GetProcAddress(kernel32, "ExitProcess\0".as_ptr()));
ExitProcess(5);
}

View File

@ -0,0 +1,5 @@
[build]
target = "i686-pc-windows-msvc"
[alias]
runcode = "run -- ../target/i686-unknown-none/release/code.bin"

7
codeloader/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "codeloader"
version = "0.1.0"
edition = "2021"
[dependencies]
region = { workspace = true }

20
codeloader/src/main.rs Normal file
View File

@ -0,0 +1,20 @@
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + 'static>>;
fn main() -> Result<()> {
let file = std::env::args().nth(1).unwrap_or("code.bin".to_string());
let code = std::fs::read(file)?;
let code: extern "C" fn() = unsafe {
region::protect(
code.as_ptr(),
code.len(),
region::Protection::READ_WRITE_EXECUTE,
)?;
std::mem::transmute(code.as_ptr())
};
code();
Ok(())
}

View File

@ -0,0 +1,8 @@
[package]
name = "shellcode-util"
version = "0.1.0"
edition = "2021"
[dependencies]
utf16_lit = { workspace = true }
windows-sys = {workspace = true }

128
shellcode-util/src/lib.rs Normal file
View File

@ -0,0 +1,128 @@
#![no_std]
#![feature(core_intrinsics)]
#![allow(internal_features)]
#![allow(unused_imports)]
use core::{arch::asm, ffi::CStr, intrinsics, slice};
use windows_sys::{
core::PCSTR,
Win32::{
Foundation::{HMODULE, UNICODE_STRING},
System::{
Diagnostics::Debug::{
IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_NT_HEADERS32, IMAGE_NT_HEADERS64,
},
SystemServices::{IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY},
Threading::TEB,
WindowsProgramming::LDR_DATA_TABLE_ENTRY,
},
},
};
pub unsafe fn get_module_handle_from_peb(module_name: &[u16]) -> Option<HMODULE> {
// Start at the top boys
let mut teb: *mut TEB;
#[cfg(target_arch = "x86")]
asm!(
"mov {}, fs:[0x18]",
out(reg) teb
);
#[cfg(target_arch = "x86_64")]
asm!(
"mov {}, gs:[0x30]",
out(reg) teb
);
// I will dereference every pointer
let teb = *teb;
let peb = *(teb.ProcessEnvironmentBlock);
let ldr_data = *(peb.Ldr);
// Hidden fields of PEB_LDR_DATA and LDR_DATA_TABLE_ENTRY abound
// Reserved2[0] = SsHandle, Reserved[1] = InLoadOrderLinks.Flink, Reserved[2] = InLoadOrderLinks.Blink
let mut module_list = *(ldr_data.Reserved2[1] as *mut LDR_DATA_TABLE_ENTRY);
while !module_list.DllBase.is_null() {
// BaseDllName
let base_dll_name = *(module_list.Reserved4.as_ptr() as *mut UNICODE_STRING);
let base_dll_name = slice::from_raw_parts(
base_dll_name.Buffer,
base_dll_name.Length as usize / size_of::<u16>(),
);
if module_name == base_dll_name {
return Some(module_list.DllBase as HMODULE);
}
// Reserved1[0] = InLoadOrderLinks.Flink, Reserved1[1] = InLoadOrderLinks.Blink
module_list = *(module_list.Reserved1[0] as *mut LDR_DATA_TABLE_ENTRY);
}
None
}
pub unsafe fn get_procedure_from_edt<F>(module: HMODULE, proc_name: &str) -> Option<F> {
let module = module as usize;
let proc_name = CStr::from_ptr(proc_name.as_ptr() as *const _);
let dos_headers = *(module as *const IMAGE_DOS_HEADER);
#[cfg(target_arch = "x86")]
let nt_headers = *((module + dos_headers.e_lfanew as usize) as *const IMAGE_NT_HEADERS32);
#[cfg(target_arch = "x86_64")]
let nt_headers = *((module + dos_headers.e_lfanew as usize) as *const IMAGE_NT_HEADERS64);
let export_directory = *((module
+ nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT as usize]
.VirtualAddress as usize)
as *const IMAGE_EXPORT_DIRECTORY);
let functions_table_ptr =
(module + export_directory.AddressOfFunctions as usize) as *const usize;
let names_table_ptr = (module + export_directory.AddressOfNames as usize) as *const usize;
let ordinals_table_ptr =
(module + export_directory.AddressOfNameOrdinals as usize) as *const u16;
let functions_table = slice::from_raw_parts(
functions_table_ptr,
export_directory.NumberOfFunctions as usize,
);
let names_table =
slice::from_raw_parts(names_table_ptr, export_directory.NumberOfNames as usize);
let ordinals_table =
slice::from_raw_parts(ordinals_table_ptr, export_directory.NumberOfNames as usize);
for i in 0..names_table.len() {
let name = CStr::from_ptr((module + names_table[i]) as *const _);
let ordinal = ordinals_table[i];
let address = functions_table[ordinal as usize];
if name == proc_name {
return Some(intrinsics::transmute_unchecked(module + address));
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use utf16_lit::utf16;
fn get_module_test() {
let module = unsafe { get_module_handle_from_peb(&utf16!("KERNEL32.DLL")) };
assert!(!module.is_some());
}
fn get_procedure_test() {
let module = unsafe { get_module_handle_from_peb(&utf16!("KERNEL32.DLL")) };
assert!(module.is_some());
let procedure = unsafe {
get_procedure_from_edt::<extern "system" fn() -> u32>(module.unwrap(), "GetProcessId\0")
};
assert!(procedure.is_some());
let procedure = procedure.unwrap();
assert_ne!(procedure(), 0)
}
}

29
shellcode.ld Normal file
View File

@ -0,0 +1,29 @@
/* has a header still? objcopy -O binary output is accurate */
/* OUTPUT_FORMAT("binary") */
ENTRY(_start);
SECTIONS
{
. = SIZEOF_HEADERS;
.text :
{
*(.text._start)
*(.text .text.*)
*(.rodata .rodata.*)
}
.data :
{
*(.got .got.*)
*(.data .data.*)
*(.bss .bss.*)
}
/DISCARD/ :
{
*(.comment)
*(.debug_frame)
*(.interp)
*(.note .note.*)
}
}

View File

@ -0,0 +1,22 @@
[build]
target = "i686-unknown-none.json"
rustflags = [
"-Z", "no-unique-section-names=yes",
"-C", "link-args=--script=shellcode.ld --build-id=none"
]
[unstable]
build-std-features = ["compiler-builtins-mem"]
build-std = ["core", "compiler_builtins", "alloc"]
trim-paths = true
[profile.release]
debug = false
debug-assertions = false
overflow-checks = false
strip = true
lto = "fat"
opt-level = "z"
panic = "abort"
trim-paths = "all"

15
shellcode/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
cargo-features = ["different-binary-name"]
[package]
name = "code"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "code"
filename = "code.bin"
[dependencies]
shellcode-util = { path = "../shellcode-util" }
utf16_lit = { workspace = true }
windows-sys = { workspace = true }

View File

@ -0,0 +1,32 @@
{
"arch": "x86",
"code-model": "kernel",
"cpu": "i686",
"crt-objects-fallback": "false",
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
"disable-redzone": true,
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float",
"linker": "rust-lld",
"linker-flavor": "gnu-lld",
"llvm-target": "i686-unknown-none-elf",
"max-atomic-width": 32,
"metadata": {
"description": "Freestanding/bare-metal i686 softfloat",
"host_tools": false,
"std": false,
"tier": 2
},
"panic-strategy": "abort",
"plt-by-default": false,
"position-independent-executables": true,
"relro-level": "full",
"stack-probes": {
"kind": "inline"
},
"static-position-independent-executables": true,
"supported-sanitizers": [
"kcfi",
"kernel-address"
],
"target-pointer-width": "32"
}

62
shellcode/src/main.rs Normal file
View File

@ -0,0 +1,62 @@
#![no_std]
#![no_main]
extern crate core;
use core::{
arch::asm,
ffi::c_void,
mem,
ptr
};
use shellcode_util::{get_module_handle_from_peb, get_procedure_from_edt};
use utf16_lit::utf16;
use windows_sys::{
core::PCSTR,
Win32::Foundation::{BOOL, HANDLE, HMODULE, HWND},
};
#[panic_handler]
pub fn panic(_panic: &core::panic::PanicInfo) -> ! {
loop {}
}
// Always ensure _start() is at beginning of .text, despite what the linker thinks it should do.
// Placed at top of .text section in shellcode.ld, may not be needed since wildcard matches alphabetically
#[no_mangle]
#[link_section = ".text._start"]
unsafe extern "C" fn _start() {
let kernel32 = get_module_handle_from_peb(&utf16!("KERNEL32.DLL")).unwrap();
let LoadLibraryA: extern "system" fn(PCSTR) -> HMODULE =
get_procedure_from_edt(kernel32, "LoadLibraryA\0").unwrap();
let GetProcAddress: extern "system" fn(HMODULE, PCSTR) -> *const () =
get_procedure_from_edt(kernel32, "GetProcAddress\0").unwrap();
let user32 = LoadLibraryA("user32.dll\0".as_ptr());
let MessageBoxA: extern "system" fn(HWND, PCSTR, PCSTR, u32) -> i32 =
mem::transmute(GetProcAddress(user32, "MessageBoxA\0".as_ptr()));
let GetStdHandle: extern "system" fn(u32) -> HANDLE =
mem::transmute(GetProcAddress(kernel32, "GetStdHandle\0".as_ptr()));
let output_handle = GetStdHandle(u32::MAX - 11);
let WriteConsoleA: extern "system" fn(HANDLE, PCSTR, u32, *mut u32, *mut c_void) -> BOOL =
mem::transmute(GetProcAddress(kernel32, "WriteConsoleA\0".as_ptr()));
let text = "Hello from Console!\n\0";
WriteConsoleA(
output_handle,
text.as_ptr(),
text.len() as u32,
ptr::null_mut(),
ptr::null_mut(),
);
let text1 = "Hello World!\0";
MessageBoxA(ptr::null_mut(), text1.as_ptr(), text1.as_ptr(), 0);
let text2 = "Bye World\0";
MessageBoxA(ptr::null_mut(), text2.as_ptr(), text2.as_ptr(), 0);
let ExitProcess: extern "system" fn(u32) =
mem::transmute(GetProcAddress(kernel32, "ExitProcess\0".as_ptr()));
ExitProcess(4);
}