mirror of
https://github.com/b1tg/rust-windows-shellcode
synced 2026-06-08 13:14:11 +00:00
Ready to publish
This commit is contained in:
+2
-2
@@ -1,2 +1,2 @@
|
||||
/target
|
||||
a.txt
|
||||
target/
|
||||
shellcode/target/
|
||||
|
||||
Generated
+113
-71
@@ -1,108 +1,150 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
name = "anyhow"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.3.1"
|
||||
name = "goblin"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
|
||||
checksum = "669cdc3826f69a51d3f8fc3f86de81c2378110254f678b8407977736122057a4"
|
||||
dependencies = [
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"log",
|
||||
"plain",
|
||||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.3.1"
|
||||
name = "iced-x86"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
|
||||
checksum = "b0be3ac4fec5cdbfd39a653e9145789e7a989359874da3cf2f18611468937767"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"lazy_static",
|
||||
"rustc_version",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.42"
|
||||
name = "plain"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.2"
|
||||
name = "quote"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "wo"
|
||||
name = "rust_windows_shellcode"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"num",
|
||||
"num-traits",
|
||||
"winapi",
|
||||
"anyhow",
|
||||
"goblin",
|
||||
"iced-x86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec"
|
||||
dependencies = [
|
||||
"scroll_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll_derive"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
+4
-51
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "wo"
|
||||
name = "rust_windows_shellcode"
|
||||
version = "0.1.0"
|
||||
authors = ["b1tg <b1tg@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
@@ -7,53 +7,6 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
# these two cut code size by 2/3
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
# these two cut code size by 2/3
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
||||
[features]
|
||||
# default = ["-mmx"]
|
||||
[dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = [
|
||||
"ntdef",
|
||||
"winnt",
|
||||
"dbghelp",
|
||||
"minwindef",
|
||||
|
||||
"impl-debug"
|
||||
]
|
||||
|
||||
|
||||
[build-dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = [
|
||||
"ntdef",
|
||||
"winnt",
|
||||
"dbghelp",
|
||||
"minwindef",
|
||||
"libloaderapi",
|
||||
|
||||
"impl-debug"
|
||||
]
|
||||
|
||||
[dependencies.num]
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
[dependencies.arrayvec]
|
||||
version = "0.5.2"
|
||||
default-features = false
|
||||
goblin = "0.3.0"
|
||||
anyhow = "1.0.34"
|
||||
iced-x86 = "1.10.0"
|
||||
|
||||
@@ -1,18 +1,69 @@
|
||||
# ref links
|
||||
|
||||
https://docs.rs/winapi/0.3.9/winapi/ctypes/type.wchar_t.html
|
||||
https://github.com/mcountryman/min-sized-rust-windows
|
||||
> https://news.ycombinator.com/item?id=25258461
|
||||
|
||||
https://github.com/hasherezade/masm_shc/blob/master/demos/peb_lookup.h
|
||||
https://github.com/Trantect/win_driver_example
|
||||
https://not-matthias.github.io/kernel-driver-with-rust/
|
||||
https://github.com/pravic/winapi-kmd-rs
|
||||
https://github.com/johnthagen/min-sized-rust
|
||||
# Write Windows Shellcode in Rust
|
||||
|
||||
|
||||
edit
|
||||
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4acc288a658ce23cb2c3da66aa818b8d
|
||||
## Project overview
|
||||
|
||||
Windows shellcode project is located in `shellcode/`, it can build into a PE file with only `.text` section
|
||||
and has no external dependencies.
|
||||
|
||||

|
||||
|
||||
Then we can dump the `.text` section and do some patches to make it position-independent. this idea
|
||||
was from [hasherezade](https://twitter.com/hasherezade)'s project [masm_shc](https://github.com/hasherezade/masm_shc).
|
||||
|
||||
|
||||
https://stackoverflow.com/questions/46134477/how-can-i-call-a-raw-address-from-rust
|
||||
## How to build it
|
||||
|
||||
(Only tested on Win10 x64)
|
||||
|
||||
|
||||
### Build shellcode binary
|
||||
|
||||
|
||||
|
||||
```sh
|
||||
rustup default nightly-x86_64-pc-windows-msvc
|
||||
cd shellcode/
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
If everthing goes well, we will get `shellcode\target\x86_64-pc-windows-msvc\release\shellcode.exe`
|
||||
|
||||
### Dump .text section and do some patches
|
||||
|
||||
We patch at the start of `.text` section, make it jump to entry point. In this way, we can have some strings store in the merged section, or we have to use `u8` and `u16` bytes array on stack to represent string.
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
cargo run
|
||||
```
|
||||
|
||||
|
||||
We will get `shellcode\target\x86_64-pc-windows-msvc\release\shellcode.bin`, this is the final shellcode file.
|
||||
|
||||
|
||||
### Test shellcode
|
||||
|
||||
Test the shellcode use your favorite shellcode loader, i use my own little tool [rs_shellcode](https://github.com/b1tg/rs_shellcode) for demonstration.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/b1tg/rs_shellcode
|
||||
cd rs_shellcode/
|
||||
cargo build
|
||||
./target/debug/rs_shellcode.exe -f "shellcode\target\x86_64-pc-windows-msvc\release\shellcode.bin"
|
||||
```
|
||||
|
||||
This demo shellcode will popup a message box and print some log use `OutputDebugStringA`, you can check it out in [debugview](https://docs.microsoft.com/en-us/sysinternals/downloads/debugview) or windbg.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- https://github.com/mcountryman/min-sized-rust-windows
|
||||
- https://github.com/hasherezade/masm_shc
|
||||
- https://github.com/Trantect/win_driver_example
|
||||
- https://not-matthias.github.io/kernel-driver-with-rust/
|
||||
- https://github.com/pravic/winapi-kmd-rs
|
||||
- https://github.com/johnthagen/min-sized-rust
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fn main() {
|
||||
let input = "OutputDebugStringA";
|
||||
let bytes= input.as_bytes().to_owned();
|
||||
let mut bytes: Vec<String> = bytes.into_iter().map(|a:u8| format!("{}", a)).collect();
|
||||
bytes.push("0".to_owned());
|
||||
let bb = bytes.join(", ");
|
||||
dbg!(&bb);
|
||||
let input_up = input.to_uppercase();
|
||||
let res = format!("const {} :&[u16] = [{}];", input_up, &bb);
|
||||
dbg!(res);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 177 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 133 KiB |
Generated
+78
@@ -0,0 +1,78 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
|
||||
dependencies = [
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellcode"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"num",
|
||||
"num-traits",
|
||||
]
|
||||
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "shellcode"
|
||||
version = "0.1.0"
|
||||
authors = ["b1tg <b1tg@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
# these two cut code size by 2/3
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
# these two cut code size by 2/3
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
||||
[dependencies.num]
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
@@ -0,0 +1,138 @@
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(overflowing_literals)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(asm)]
|
||||
#![feature(link_args)]
|
||||
use core::ptr::null_mut;
|
||||
mod binds;
|
||||
mod utils;
|
||||
use binds::*;
|
||||
use utils::*;
|
||||
#[panic_handler]
|
||||
fn panic(_: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
// const KERNEL32_DLL: &str = concat!("KERNEL32.DLL", "\0");
|
||||
const USER32_DLL: &str = concat!("user32.dll", "\0");
|
||||
const OutputDebugStringA_: &str = concat!("OutputDebugStringA", "\0");
|
||||
const LoadLibraryA_: &str = concat!("LoadLibraryA", "\0");
|
||||
const GetProcAddress_: &str = concat!("GetProcAddress", "\0");
|
||||
const MessageBoxA_: &str = concat!("MessageBoxA", "\0");
|
||||
|
||||
pub type LoadLibraryAFn = extern "system" fn(lpFileName: LPCSTR) -> PVOID;
|
||||
pub type GetProcAddressFn = extern "system" fn(hmodule: PVOID, name: LPCSTR) -> PVOID;
|
||||
pub type MessageBoxAFn = extern "system" fn(h: PVOID, text: LPCSTR, cation: LPCSTR, t: u32) -> u32;
|
||||
pub type OutputDebugStringAFn = extern "C" fn(*const i8);
|
||||
pub type DbgPrintFn = extern "C" fn(Format: *const u8, ...) -> NTSTATUS;
|
||||
#[no_mangle]
|
||||
pub extern "C" fn main() -> ! {
|
||||
unsafe {
|
||||
// clean argc and argv
|
||||
asm!("mov rcx, 0", "mov rdx, 0",);
|
||||
}
|
||||
let KERNEL32_STR: [u16; 13] = [75, 69, 82, 78, 69, 76, 51, 50, 46, 68, 76, 76, 0];
|
||||
let kernel32_ptr = get_module_by_name(KERNEL32_STR.as_ptr());
|
||||
let dbg_addr = get_func_by_name(kernel32_ptr, OutputDebugStringA_.as_ptr());
|
||||
let load_library_ptr = get_func_by_name(kernel32_ptr, LoadLibraryA_.as_ptr());
|
||||
let get_proc = get_func_by_name(kernel32_ptr, GetProcAddress_.as_ptr());
|
||||
let LoadLibraryA: LoadLibraryAFn = unsafe { core::mem::transmute(load_library_ptr) };
|
||||
|
||||
// make stack align
|
||||
unsafe { asm!("push rax") };
|
||||
let u32_dll = LoadLibraryA(USER32_DLL.as_ptr() as *const i8);
|
||||
let GetProcAddress: GetProcAddressFn = unsafe { core::mem::transmute(get_proc) };
|
||||
let message_box_ptr = GetProcAddress(u32_dll, MessageBoxA_.as_ptr() as *const i8);
|
||||
let MessageBoxA: MessageBoxAFn = unsafe { core::mem::transmute(message_box_ptr) };
|
||||
|
||||
let OutputDebugStringA: OutputDebugStringAFn = unsafe { core::mem::transmute(dbg_addr) };
|
||||
#[macro_export]
|
||||
macro_rules! debug_print {
|
||||
($msg:expr) => {
|
||||
OutputDebugStringA(concat!($msg, "\n\0").as_ptr() as _)
|
||||
};
|
||||
}
|
||||
debug_print!("We made it! see me in DebugView or Debugger");
|
||||
debug_print!("Let's popup a messagebox");
|
||||
MessageBoxA(
|
||||
null_mut(),
|
||||
"Windows Shellcode with Rust!\0".as_ptr() as *const i8,
|
||||
"rocks\0".as_ptr() as _,
|
||||
0x30,
|
||||
);
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn get_module_by_name(module_name: *const u16) -> PVOID {
|
||||
let peb: *mut PEB;
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov {}, gs:[0x60]",
|
||||
out(reg) peb,
|
||||
);
|
||||
let ldr = (*peb).Ldr;
|
||||
let list_entry = &((*ldr).InLoadOrderModuleList);
|
||||
let mut cur_module: *const LDR_DATA_TABLE_ENTRY = &list_entry as *const _ as *const _;
|
||||
loop {
|
||||
if cur_module.is_null() || (*cur_module).BaseAddress.is_null() {
|
||||
// TODO: when to break
|
||||
}
|
||||
let cur_name = (*cur_module).BaseDllName.Buffer;
|
||||
if !cur_name.is_null() {
|
||||
if compare_raw_str(module_name, cur_name) {
|
||||
return (*cur_module).BaseAddress;
|
||||
}
|
||||
}
|
||||
let flink = (*cur_module).InLoadOrderModuleList.Flink;
|
||||
cur_module = flink as *const LDR_DATA_TABLE_ENTRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_func_by_name(module: PVOID, func_name: *const u8) -> PVOID {
|
||||
let idh: *const IMAGE_DOS_HEADER = module as *const _;
|
||||
unsafe {
|
||||
if (*idh).e_magic != IMAGE_DOS_SIGNATURE {
|
||||
return null_mut();
|
||||
}
|
||||
let e_lfanew = (*idh).e_lfanew;
|
||||
let nt_headers: *const IMAGE_NT_HEADERS =
|
||||
(module as *const u8).offset(e_lfanew as isize) as *const _;
|
||||
let op_header = &(*nt_headers).OptionalHeader;
|
||||
let virtual_addr = (&op_header.DataDirectory[0]).VirtualAddress;
|
||||
let export_dir: *const IMAGE_EXPORT_DIRECTORY =
|
||||
(module as *const u8).offset(virtual_addr as _) as _;
|
||||
let number_of_names = (*export_dir).NumberOfNames;
|
||||
let addr_of_funcs = (*export_dir).AddressOfFunctions;
|
||||
let addr_of_names = (*export_dir).AddressOfNames;
|
||||
let addr_of_ords = (*export_dir).AddressOfNameOrdinals;
|
||||
for i in 0..number_of_names {
|
||||
let name_rva_p: *const DWORD =
|
||||
(module as *const u8).offset((addr_of_names + i * 4) as isize) as *const _;
|
||||
let name_index_p: *const WORD =
|
||||
(module as *const u8).offset((addr_of_ords + i * 2) as isize) as *const _;
|
||||
let name_index = name_index_p.as_ref().unwrap();
|
||||
let mut off: u32 = (4 * name_index) as u32;
|
||||
off = off + addr_of_funcs;
|
||||
let func_rva: *const DWORD = (module as *const u8).offset(off as _) as *const _;
|
||||
|
||||
let name_rva = name_rva_p.as_ref().unwrap();
|
||||
let curr_name = (module as *const u8).offset(*name_rva as isize);
|
||||
|
||||
if *curr_name == 0 {
|
||||
continue;
|
||||
}
|
||||
if compare_raw_str(func_name, curr_name) {
|
||||
let res = (module as *const u8).offset(*func_rva as isize);
|
||||
return res as _;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null_mut();
|
||||
}
|
||||
// #[allow(unused_attributes)]
|
||||
// #[cfg(target_env = "msvc")]
|
||||
// #[link_args = "/GS- /MERGE:.rdata=.text /MERGE:.pdata=.text /NODEFAULTLIB /EMITPOGOPHASEINFO /DEBUG:NONE"]
|
||||
// extern "C" {}
|
||||
+87
-129
@@ -1,136 +1,94 @@
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(overflowing_literals)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(asm)]
|
||||
#![feature(link_args)]
|
||||
use core::ptr::null_mut;
|
||||
mod binds;
|
||||
mod utils;
|
||||
use binds::*;
|
||||
use utils::*;
|
||||
#[panic_handler]
|
||||
fn panic(_: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
// const KERNEL32_DLL: &str = concat!("KERNEL32.DLL", "\0");
|
||||
const USER32_DLL: &str = concat!("user32.dll", "\0");
|
||||
const OutputDebugStringA_: &str = concat!("OutputDebugStringA", "\0");
|
||||
const LoadLibraryA_: &str = concat!("LoadLibraryA", "\0");
|
||||
const GetProcAddress_: &str = concat!("GetProcAddress", "\0");
|
||||
const MessageBoxA_: &str = concat!("MessageBoxA", "\0");
|
||||
use anyhow::{Result};
|
||||
use goblin::pe::PE;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufWriter;
|
||||
use iced_x86::{Decoder, DecoderOptions, Formatter, Instruction, NasmFormatter};
|
||||
|
||||
pub type LoadLibraryAFn = extern "system" fn(lpFileName: LPCSTR) -> PVOID;
|
||||
pub type GetProcAddressFn = extern "system" fn(hmodule: PVOID, name: LPCSTR) -> PVOID;
|
||||
pub type MessageBoxAFn = extern "system" fn(h: PVOID, text: LPCSTR, cation: LPCSTR, t: u32) -> u32;
|
||||
pub type OutputDebugStringAFn = extern "C" fn(*const i8);
|
||||
pub type DbgPrintFn = extern "C" fn(Format: *const u8, ...) -> NTSTATUS;
|
||||
#[no_mangle]
|
||||
pub extern "C" fn main() -> ! {
|
||||
unsafe {
|
||||
// clean argc and argv
|
||||
asm!("mov rcx, 0", "mov rdx, 0",);
|
||||
}
|
||||
let KERNEL32_STR: [u16; 13] = [75, 69, 82, 78, 69, 76, 51, 50, 46, 68, 76, 76, 0];
|
||||
let kernel32_ptr = get_module_by_name(KERNEL32_STR.as_ptr());
|
||||
let dbg_addr = get_func_by_name(kernel32_ptr, OutputDebugStringA_.as_ptr());
|
||||
let load_library_ptr = get_func_by_name(kernel32_ptr, LoadLibraryA_.as_ptr());
|
||||
let get_proc = get_func_by_name(kernel32_ptr, GetProcAddress_.as_ptr());
|
||||
let LoadLibraryA: LoadLibraryAFn = unsafe { core::mem::transmute(load_library_ptr) };
|
||||
|
||||
// make stack align
|
||||
unsafe { asm!("push rax") };
|
||||
let u32_dll = LoadLibraryA(USER32_DLL.as_ptr() as *const i8);
|
||||
let GetProcAddress: GetProcAddressFn = unsafe { core::mem::transmute(get_proc) };
|
||||
let message_box_ptr = GetProcAddress(u32_dll, MessageBoxA_.as_ptr() as *const i8);
|
||||
let MessageBoxA: MessageBoxAFn = unsafe { core::mem::transmute(message_box_ptr) };
|
||||
MessageBoxA(
|
||||
null_mut(),
|
||||
MessageBoxA_.as_ptr() as *const i8,
|
||||
MessageBoxA_.as_ptr() as _,
|
||||
0x30,
|
||||
);
|
||||
let OutputDebugStringA: OutputDebugStringAFn = unsafe { core::mem::transmute(dbg_addr) };
|
||||
#[macro_export]
|
||||
macro_rules! debug_print {
|
||||
($msg:expr) => {
|
||||
OutputDebugStringA(concat!($msg, "\0").as_ptr() as _)
|
||||
};
|
||||
}
|
||||
debug_print!("we made it! see me in DebugView or Debugger");
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn get_module_by_name(module_name: *const u16) -> PVOID {
|
||||
let peb: *mut PEB;
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov {}, gs:[0x60]",
|
||||
out(reg) peb,
|
||||
);
|
||||
let ldr = (*peb).Ldr;
|
||||
let list_entry = &((*ldr).InLoadOrderModuleList);
|
||||
let mut cur_module: *const LDR_DATA_TABLE_ENTRY = &list_entry as *const _ as *const _;
|
||||
loop {
|
||||
if cur_module.is_null() || (*cur_module).BaseAddress.is_null() {
|
||||
// TODO: when to break
|
||||
}
|
||||
let cur_name = (*cur_module).BaseDllName.Buffer;
|
||||
if !cur_name.is_null() {
|
||||
if compare_raw_str(module_name, cur_name) {
|
||||
return (*cur_module).BaseAddress;
|
||||
}
|
||||
}
|
||||
let flink = (*cur_module).InLoadOrderModuleList.Flink;
|
||||
cur_module = flink as *const LDR_DATA_TABLE_ENTRY;
|
||||
fn main() -> Result<()> {
|
||||
let src_path = "shellcode\\target\\x86_64-pc-windows-msvc\\release\\wo.exe";
|
||||
let mut file = File::open(src_path)?;
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer)?;
|
||||
let pe = PE::parse(&mut buffer)?;
|
||||
let standard_fileds = pe.header.optional_header.unwrap().standard_fields;
|
||||
let entry_offset = standard_fileds.address_of_entry_point - standard_fileds.base_of_code;
|
||||
for section in pe.sections {
|
||||
let name = String::from_utf8(section.name.to_vec())?;
|
||||
if !name.starts_with(".text") {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_func_by_name(module: PVOID, func_name: *const u8) -> PVOID {
|
||||
let idh: *const IMAGE_DOS_HEADER = module as *const _;
|
||||
unsafe {
|
||||
if (*idh).e_magic != IMAGE_DOS_SIGNATURE {
|
||||
return null_mut();
|
||||
let start = section.pointer_to_raw_data as usize;
|
||||
let size = section.size_of_raw_data as usize;
|
||||
let dst_path = "shellcode\\target\\x86_64-pc-windows-msvc\\release\\shellcode.bin";
|
||||
let shellcode = File::create(dst_path)?;
|
||||
let mut buf_writer = BufWriter::new(shellcode);
|
||||
println!("[*] section text addr: 0x{:x}, size: 0x{:x}", start, size);
|
||||
println!("[*] entry offset: 0x{:x}", entry_offset);
|
||||
println!("== before patch ==");
|
||||
show_disassemble(&buffer[start..start+size], 5);
|
||||
if entry_offset >= 0x100 {
|
||||
buffer[0+start] = 0xe9;
|
||||
let hi = (entry_offset-2) / 0x100;
|
||||
let li = (entry_offset-2) % 0x100;
|
||||
dbg!(hi, li);
|
||||
buffer[1+start] = li as _;
|
||||
buffer[2+start] = hi as _;
|
||||
buffer[3+start] = 0 as _;
|
||||
buffer[4+start] = 0 as _;
|
||||
} else if entry_offset >= 0x80 {
|
||||
buffer[0+start] = 0xe9;
|
||||
buffer[1+start] = (entry_offset-5) as _;
|
||||
buffer[2+start] = 0 as _;
|
||||
buffer[3+start] = 0 as _;
|
||||
buffer[4+start] = 0 as _;
|
||||
} else {
|
||||
buffer[0+start] = 0xeb;
|
||||
buffer[1+start] = (entry_offset-2) as _;
|
||||
}
|
||||
let e_lfanew = (*idh).e_lfanew;
|
||||
let nt_headers: *const IMAGE_NT_HEADERS =
|
||||
(module as *const u8).offset(e_lfanew as isize) as *const _;
|
||||
let op_header = &(*nt_headers).OptionalHeader;
|
||||
let virtual_addr = (&op_header.DataDirectory[0]).VirtualAddress;
|
||||
let export_dir: *const IMAGE_EXPORT_DIRECTORY =
|
||||
(module as *const u8).offset(virtual_addr as _) as _;
|
||||
let number_of_names = (*export_dir).NumberOfNames;
|
||||
let addr_of_funcs = (*export_dir).AddressOfFunctions;
|
||||
let addr_of_names = (*export_dir).AddressOfNames;
|
||||
let addr_of_ords = (*export_dir).AddressOfNameOrdinals;
|
||||
for i in 0..number_of_names {
|
||||
let name_rva_p: *const DWORD =
|
||||
(module as *const u8).offset((addr_of_names + i * 4) as isize) as *const _;
|
||||
let name_index_p: *const WORD =
|
||||
(module as *const u8).offset((addr_of_ords + i * 2) as isize) as *const _;
|
||||
let name_index = name_index_p.as_ref().unwrap();
|
||||
let mut off: u32 = (4 * name_index) as u32;
|
||||
off = off + addr_of_funcs;
|
||||
let func_rva: *const DWORD = (module as *const u8).offset(off as _) as *const _;
|
||||
|
||||
let name_rva = name_rva_p.as_ref().unwrap();
|
||||
let curr_name = (module as *const u8).offset(*name_rva as isize);
|
||||
|
||||
if *curr_name == 0 {
|
||||
continue;
|
||||
}
|
||||
if compare_raw_str(func_name, curr_name) {
|
||||
let res = (module as *const u8).offset(*func_rva as isize);
|
||||
return res as _;
|
||||
println!("== after patch ==");
|
||||
show_disassemble(&buffer[start..start+size], 5);
|
||||
for i in start..start+size {
|
||||
buf_writer.write(&[buffer[i]])?;
|
||||
}
|
||||
buf_writer.flush().unwrap();
|
||||
println!("done! shellcode saved in {}", dst_path);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn show_disassemble(bytes: &[u8], max_line: u32) {
|
||||
let mut decoder = Decoder::new(EXAMPLE_CODE_BITNESS, bytes, DecoderOptions::NONE);
|
||||
decoder.set_ip(EXAMPLE_CODE_RIP);
|
||||
let mut formatter = NasmFormatter::new();
|
||||
formatter.options_mut().set_digit_separator("`");
|
||||
formatter.options_mut().set_first_operand_char_index(10);
|
||||
let mut output = String::new();
|
||||
let mut instruction = Instruction::default();
|
||||
let mut i = 0;
|
||||
while decoder.can_decode() {
|
||||
i +=1;
|
||||
if i> max_line {
|
||||
println!("....\n");
|
||||
break;
|
||||
}
|
||||
decoder.decode_out(&mut instruction);
|
||||
output.clear();
|
||||
formatter.format(&instruction, &mut output);
|
||||
print!("{:016X} ", instruction.ip());
|
||||
let start_index = (instruction.ip() - EXAMPLE_CODE_RIP) as usize;
|
||||
let instr_bytes = &bytes[start_index..start_index + instruction.len()];
|
||||
for b in instr_bytes.iter() {
|
||||
print!("{:02X}", b);
|
||||
}
|
||||
if instr_bytes.len() < HEXBYTES_COLUMN_BYTE_LENGTH {
|
||||
for _ in 0..HEXBYTES_COLUMN_BYTE_LENGTH - instr_bytes.len() {
|
||||
print!(" ");
|
||||
}
|
||||
}
|
||||
println!(" {}", output);
|
||||
}
|
||||
return null_mut();
|
||||
}
|
||||
// #[allow(unused_attributes)]
|
||||
// #[cfg(target_env = "msvc")]
|
||||
// #[link_args = "/GS- /MERGE:.rdata=.text /MERGE:.pdata=.text /NODEFAULTLIB /EMITPOGOPHASEINFO /DEBUG:NONE"]
|
||||
// extern "C" {}
|
||||
|
||||
const HEXBYTES_COLUMN_BYTE_LENGTH: usize = 10;
|
||||
const EXAMPLE_CODE_BITNESS: u32 = 64;
|
||||
const EXAMPLE_CODE_RIP: u64 = 0x0000_0001_4000_1000; // 0000 0001 4000 1000
|
||||
|
||||
Reference in New Issue
Block a user