mirror of
https://codeberg.org/smukx/Dyncvoke
synced 2026-06-06 20:23:06 +00:00
Dyncvoke
This commit is contained in:
+4
-21
@@ -1,21 +1,4 @@
|
||||
# Build output
|
||||
target/
|
||||
**/target/
|
||||
|
||||
# Editor scratch
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS scratch
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
|
||||
# Sub-crate lockfiles (only the root Cargo.lock should be tracked)
|
||||
data/Cargo.lock
|
||||
dmanager/Cargo.lock
|
||||
dyncvoke_core/Cargo.lock
|
||||
manualmap/Cargo.lock
|
||||
overload/Cargo.lock
|
||||
/target
|
||||
**/target
|
||||
Cargo.lock.bak
|
||||
*.pdb
|
||||
|
||||
Generated
+72
-2
@@ -41,6 +41,26 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitfield"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21ba6517c6b0f2bf08be60e187ab64b038438f22dd755614d8fe4d4098c46419"
|
||||
dependencies = [
|
||||
"bitfield-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitfield-macros"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -62,6 +82,16 @@ dependencies = [
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
@@ -78,6 +108,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
name = "data"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"obfstr",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -106,13 +137,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dyncvoke"
|
||||
version = "0.1.1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"dmanager",
|
||||
"dyncvoke_core",
|
||||
"manualmap",
|
||||
"overload",
|
||||
"spoof",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -121,7 +153,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"libc",
|
||||
"litcrypt2",
|
||||
"nanorand",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
@@ -148,6 +179,12 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
@@ -227,6 +264,15 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4"
|
||||
|
||||
[[package]]
|
||||
name = "nasm-rs"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "706bf8a5e8c8ddb99128c3291d31bd21f4bcde17f0f4c20ec678d85c74faa149"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
@@ -239,6 +285,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "obfstr"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee"
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.4"
|
||||
@@ -498,6 +550,24 @@ version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "spoof"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitfield",
|
||||
"cc",
|
||||
"data",
|
||||
"dyncvoke_core",
|
||||
"memchr",
|
||||
"nasm-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
||||
+17
-6
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "dyncvoke"
|
||||
version = "0.1.1"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Dynamically invoke arbitrary unmanaged code"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@@ -10,7 +10,7 @@ documentation = "README.md"
|
||||
readme = "README.md"
|
||||
keywords = ["windows_sys", "dynamic", "redteam"]
|
||||
categories = ["command-line-utilities"]
|
||||
authors = ["5mukx", "staffs@5mukx.site"]
|
||||
authors = ["5mukx"]
|
||||
|
||||
[lib]
|
||||
name = "dyncvoke"
|
||||
@@ -25,8 +25,19 @@ strip = true
|
||||
lto = false
|
||||
|
||||
[dependencies]
|
||||
dyncvoke_core = { path = "dyncvoke_core" }
|
||||
manualmap = { path = "manualmap" }
|
||||
data = { path = "data" }
|
||||
overload = { path = "overload" }
|
||||
dmanager = { path = "dmanager" }
|
||||
dyncvoke_core = { path = "dyncvoke_core", optional = true }
|
||||
manualmap = { path = "manualmap", optional = true }
|
||||
overload = { path = "overload", optional = true }
|
||||
dmanager = { path = "dmanager", optional = true }
|
||||
spoof = { path = "spoof", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["syscall"]
|
||||
syscall = ["dep:dyncvoke_core"]
|
||||
manualmap = ["dep:manualmap", "syscall"]
|
||||
overload = ["dep:overload", "manualmap"]
|
||||
dmanager = ["dep:dmanager", "overload"]
|
||||
spoof = ["dep:spoof", "syscall"]
|
||||
spoof-desync = ["spoof", "spoof/desync"]
|
||||
full = ["syscall", "manualmap", "overload", "dmanager", "spoof", "spoof-desync"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# dyncvoke_rs
|
||||
# Dyncvoke_rs
|
||||
|
||||
A Rust crate for indirect syscalls and dynamic invocation on Windows. The point is calling NT functions without leaving import-table traces and without tripping inline hooks on ntdll exports.
|
||||
|
||||
@@ -18,6 +18,7 @@ dyncvoke_core::sys Tartarus Gate plus the variadic do_syscall gateway
|
||||
manualmap PE mapping with relocations and IAT rewriting
|
||||
overload section overloading, module stomping, template stomping
|
||||
dmanager the fluctuation manager
|
||||
spoof call stack spoofing, synthetic and desync modes
|
||||
data shared types and FFI signatures
|
||||
```
|
||||
|
||||
@@ -27,24 +28,41 @@ This is still moving. Test before shipping anything you care about.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
dyncvoke = { git = "https://github.com/Whitecat18/Dyncvoke" }
|
||||
dyncvoke = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
```
|
||||
|
||||
Or from a local checkout:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
dyncvoke = { path = "path/to/dyncvoke_rs" }
|
||||
dyncvoke = { path = "path/to/Dyncvoke" }
|
||||
```
|
||||
|
||||
Just the core:
|
||||
Core only:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
dyncvoke_core = { path = "path/to/dyncvoke_rs/dyncvoke_core" }
|
||||
dyncvoke_core = { path = "path/to/Dyncvoke/dyncvoke_core" }
|
||||
```
|
||||
|
||||
No feature flags. The syscall path is always on for x86_64.
|
||||
Features:
|
||||
|
||||
- `syscall` (default): indirect syscall path. Pulls in `dyncvoke_core`.
|
||||
- `manualmap`: PE manual mapping. Implies `syscall`.
|
||||
- `overload`: section overloading and module stomping. Implies `manualmap`.
|
||||
- `dmanager`: module fluctuation manager. Implies `overload`.
|
||||
- `spoof`: call stack spoofing, synthetic mode.
|
||||
- `spoof-desync`: spoof with the desync mode toggled on.
|
||||
- `full`: everything above.
|
||||
|
||||
Picking a non-default set looks like this:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
dyncvoke = { git = "https://codeberg.org/smukx/Dyncvoke.git", default-features = false, features = ["syscall", "spoof", "spoof-desync"] }
|
||||
```
|
||||
|
||||
x86_64 only.
|
||||
|
||||
## Using it
|
||||
|
||||
@@ -203,13 +221,61 @@ fn main() {
|
||||
|
||||
x64 only.
|
||||
|
||||
### Call stack spoofing (spoof crate)
|
||||
|
||||
Enable the `spoof` feature for synthetic mode, or `spoof-desync` for desync mode. Synthetic mode builds a fake stack rooted at `RtlUserThreadStart` and runs from any thread, including pool threads. Desync mode finds a real `BaseThreadInitThunk` return address on the current thread's stack and splices spoofed frames on top. Use desync only on threads started through the normal user-thread chain.
|
||||
|
||||
The `spoof_syscall!` macro spoofs the call stack and dispatches an indirect syscall in one step. SSN resolution runs through the same Tartarus Gate path as `dyncvoke_core::syscall!`.
|
||||
|
||||
```rust
|
||||
use spoof::spoof_syscall;
|
||||
use core::ffi::c_void;
|
||||
|
||||
fn main() {
|
||||
let mut addr: *mut c_void = core::ptr::null_mut();
|
||||
let mut size: usize = 0x1000;
|
||||
|
||||
let status = spoof_syscall!(
|
||||
"NtAllocateVirtualMemory",
|
||||
-1isize, // NtCurrentProcess
|
||||
&mut addr as *mut _,
|
||||
0usize,
|
||||
&mut size as *mut _,
|
||||
0x3000u32, // MEM_COMMIT | MEM_RESERVE
|
||||
0x04u32 // PAGE_READWRITE
|
||||
).map(|p| p as i32).unwrap_or(-1);
|
||||
|
||||
println!("NTSTATUS = 0x{:08X}", status as u32);
|
||||
}
|
||||
```
|
||||
|
||||
For non-ntdll calls, resolve the address with `dyncvoke_core` and dispatch through `spoof::spoof!` instead.
|
||||
|
||||
```rust
|
||||
use dyncvoke::dyncvoke_core;
|
||||
use spoof::spoof;
|
||||
|
||||
fn main() {
|
||||
let kernel32 = dyncvoke_core::get_module_base_address("kernel32.dll");
|
||||
let virtual_alloc = dyncvoke_core::get_function_address(kernel32, "VirtualAlloc");
|
||||
|
||||
let p = unsafe {
|
||||
spoof!(
|
||||
virtual_alloc,
|
||||
core::ptr::null_mut::<core::ffi::c_void>(),
|
||||
1 << 12,
|
||||
0x3000u32,
|
||||
0x04u32
|
||||
)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT LICENSE](./LICENSE)
|
||||
|
||||
## Credits
|
||||
|
||||
My inspirations:-
|
||||
|
||||
- [DInvoke_rs](https://github.com/Kudaes/DInvoke_rs/tree/main) by Kudaes.
|
||||
- [dinvk](https://github.com/joaoviictorti/dinvk) by João Victor.
|
||||
- [DInvoke_rs](https://github.com/Kudaes/DInvoke_rs/tree/main)
|
||||
- [dinvk](https://github.com/joaoviictorti/dinvk)
|
||||
Generated
+90
@@ -0,0 +1,90 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "data"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"obfstr",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "obfstr"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
@@ -7,6 +7,7 @@ edition = "2021"
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
obfstr = "0.4"
|
||||
|
||||
[dependencies.windows-sys]
|
||||
version = "0.59"
|
||||
|
||||
+32
-9
@@ -1,7 +1,26 @@
|
||||
// dyncvoke - Data Types
|
||||
// @5mukx
|
||||
|
||||
use std::{collections::BTreeMap, ffi::c_void};
|
||||
#![no_std]
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{collections::BTreeMap, string::String, vec::Vec};
|
||||
use core::ffi::c_void;
|
||||
|
||||
// obfstr is the no_std-compatible replacement for litcrypt2. Re-exported so
|
||||
// the workspace-wide lc!() macro below can resolve obfstr::obfstring via
|
||||
// $crate::obfstr::obfstring from any consumer crate.
|
||||
pub use obfstr;
|
||||
|
||||
/// Compile-time obfuscated string. Replaces litcrypt2's lc!() with an
|
||||
/// obfstr-backed expansion that returns alloc::string::String and works in
|
||||
/// no_std. Call sites are unchanged.
|
||||
#[macro_export]
|
||||
macro_rules! lc {
|
||||
($s:literal) => {
|
||||
$crate::obfstr::obfstring!($s)
|
||||
};
|
||||
}
|
||||
use windows_sys::Win32::Foundation::BOOL;
|
||||
use windows_sys::Win32::Graphics::Printing::{DOC_INFO_1A, PRINTER_DEFAULTSA};
|
||||
use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
|
||||
@@ -42,7 +61,7 @@ pub struct OBJECT_ATTRIBUTES {
|
||||
|
||||
impl Default for OBJECT_ATTRIBUTES {
|
||||
fn default() -> Self {
|
||||
unsafe { std::mem::zeroed() }
|
||||
unsafe { core::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +146,7 @@ pub type CryptSignHashW = unsafe extern "system" fn (usize, u32, *mut u16, u32,
|
||||
pub type CreateEventW = unsafe extern "system" fn (*const SECURITY_ATTRIBUTES, i32, i32, *const u16) -> HANDLE;
|
||||
/// Counted ANSI string as the kernel expects. LdrGetProcedureAddress reads
|
||||
/// 16 bytes here (USHORT Length, USHORT MaximumLength, PCHAR Buffer + 4
|
||||
/// bytes of natural alignment padding on x64).
|
||||
/// bytes of natural alignment padding on x64). Bug 10 in AUDIT.md.
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
pub struct ANSI_STRING {
|
||||
@@ -263,8 +282,8 @@ impl Default for PeMetadata {
|
||||
pe: u32::default(),
|
||||
is_32_bit: false,
|
||||
image_file_header: ImageFileHeader::default(),
|
||||
opt_header_32: std::mem::zeroed(),
|
||||
opt_header_64: std::mem::zeroed(),
|
||||
opt_header_32: core::mem::zeroed(),
|
||||
opt_header_64: core::mem::zeroed(),
|
||||
sections: Vec::default(),
|
||||
}
|
||||
}
|
||||
@@ -456,7 +475,7 @@ pub struct ClientId {
|
||||
|
||||
impl Default for ClientId {
|
||||
fn default() -> Self {
|
||||
unsafe { std::mem::zeroed() }
|
||||
unsafe { core::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,9 +734,13 @@ pub struct ThreadBasicInformation {
|
||||
// Tests
|
||||
// ============================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core::ptr;
|
||||
|
||||
// Test UNICODE_STRING structure
|
||||
#[test]
|
||||
@@ -733,7 +756,7 @@ mod tests {
|
||||
let us = UNICODE_STRING {
|
||||
Length: 10,
|
||||
MaximumLength: 12,
|
||||
Buffer: std::ptr::null_mut(),
|
||||
Buffer: ptr::null_mut(),
|
||||
};
|
||||
assert_eq!(us.Length, 10);
|
||||
assert_eq!(us.MaximumLength, 12);
|
||||
@@ -948,14 +971,14 @@ mod tests {
|
||||
// Test HANDLE type
|
||||
#[test]
|
||||
fn test_handle_type() {
|
||||
let null_handle: HANDLE = std::ptr::null_mut();
|
||||
let null_handle: HANDLE = ptr::null_mut();
|
||||
assert!(null_handle.is_null());
|
||||
}
|
||||
|
||||
// Test HINSTANCE type
|
||||
#[test]
|
||||
fn test_hinstance_type() {
|
||||
let null_hinstance: HINSTANCE = std::ptr::null_mut();
|
||||
let null_hinstance: HINSTANCE = ptr::null_mut();
|
||||
assert!(null_hinstance.is_null());
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+688
@@ -0,0 +1,688 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "data"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dmanager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"dyncvoke_core",
|
||||
"litcrypt2",
|
||||
"manualmap",
|
||||
"nanorand",
|
||||
"overload",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyncvoke_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"libc",
|
||||
"litcrypt2",
|
||||
"nanorand",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"failure_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "litcrypt2"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4126aa57ac1b3dd20a5bc827a2972cdf74c619a4d6ae5660656408289e5bc60d"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "manualmap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"dyncvoke_core",
|
||||
"litcrypt2",
|
||||
"os_info",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-cloud-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-data"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-image"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-location"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-text"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"block2",
|
||||
"libc",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"block2",
|
||||
"objc2",
|
||||
"objc2-cloud-kit",
|
||||
"objc2-core-data",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-core-image",
|
||||
"objc2-core-location",
|
||||
"objc2-core-text",
|
||||
"objc2-foundation",
|
||||
"objc2-quartz-core",
|
||||
"objc2-user-notifications",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-user-notifications"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"log",
|
||||
"nix",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
"objc2-ui-kit",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"dyncvoke_core",
|
||||
"litcrypt2",
|
||||
"manualmap",
|
||||
"nanorand",
|
||||
"windows-sys 0.59.0",
|
||||
"winproc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
|
||||
|
||||
[[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 = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winproc"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "979e07b33c6af27e5c454e42d1b946b403ec222e7bad52ef020820708e087b25"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"failure",
|
||||
"widestring",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.115",
|
||||
]
|
||||
Generated
+112
@@ -0,0 +1,112 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "data"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"obfstr",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyncvoke_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"libc",
|
||||
"nanorand",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4"
|
||||
|
||||
[[package]]
|
||||
name = "obfstr"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
@@ -8,11 +8,10 @@ strip = true
|
||||
|
||||
[dependencies]
|
||||
data = { path = "../data" }
|
||||
libc = "0.2.182"
|
||||
litcrypt2 = "=0.1.2"
|
||||
libc = { version = "0.2.182", default-features = false }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
nanorand = { version = "0.8.0" }
|
||||
nanorand = { version = "0.8.0", default-features = false, features = ["wyrand"] }
|
||||
|
||||
[dependencies.windows-sys]
|
||||
version = "0.59"
|
||||
|
||||
+149
-202
@@ -4,28 +4,33 @@
|
||||
// This library provides dynamic Windows API invocation, PE manipulation,
|
||||
// indirect syscalls, and various red team techniques.
|
||||
|
||||
#[macro_use]
|
||||
extern crate litcrypt2;
|
||||
use_litcrypt!();
|
||||
#![no_std]
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{
|
||||
collections::BTreeMap,
|
||||
ffi::CString,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{mem::size_of, ptr};
|
||||
|
||||
use data::lc;
|
||||
use data::LARGE_INTEGER;
|
||||
use libc::c_void;
|
||||
use litcrypt2::lc;
|
||||
use std::ffi::{CString, OsString};
|
||||
use std::mem::size_of;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
use std::panic;
|
||||
use std::{collections::HashMap, ptr};
|
||||
use windows_sys::Win32::Foundation::BOOL;
|
||||
use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, SetThreadContext};
|
||||
use windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION;
|
||||
use windows_sys::Win32::System::ProcessStatus::{
|
||||
EnumProcessModules, GetModuleBaseNameW, GetModuleFileNameExW,
|
||||
};
|
||||
use windows_sys::Win32::System::SystemInformation::SYSTEM_INFO;
|
||||
use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use windows_sys::Win32::System::Threading::GetCurrentProcessId;
|
||||
|
||||
pub mod peb;
|
||||
|
||||
pub use data::HANDLE;
|
||||
pub use data::HINSTANCE;
|
||||
@@ -41,8 +46,14 @@ pub use sys::{extract_ssn, get_syscall_address, resolve_syscall, SyscallError};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use sys::asm::do_syscall;
|
||||
|
||||
// Constants and shared types. Importing them once unconditionally keeps the
|
||||
// unit-test module (which references them) in scope on every build cfg.
|
||||
// Constants and shared types — same on every cfg. Importing them once
|
||||
// unconditionally keeps the unit-test module (which references them) in
|
||||
// scope on every build configuration. Bug 9 in AUDIT.md.
|
||||
//
|
||||
// A subset of the constants is only used by the non-syscall prepare_syscall
|
||||
// path (the syscall variant doesn't allocate shellcode). #[allow] keeps the
|
||||
// build clean under --features syscall until Phase 3 removes the feature
|
||||
// flag entirely.
|
||||
#[allow(unused_imports)]
|
||||
use data::{
|
||||
ApiSetNamespace, ApiSetNamespaceEntry, ApiSetValueEntry, ClientId, EntryPoint,
|
||||
@@ -53,7 +64,7 @@ use data::{
|
||||
PROCESS_QUERY_LIMITED_INFORMATION, PVOID, TLS_OUT_OF_INDEXES,
|
||||
};
|
||||
|
||||
// Types that are only used on x86_64. Keep the cfg-gate on these so x86
|
||||
// Types that are only used on x86_64 — keep the cfg-gate on these so x86
|
||||
// builds don't drag them in.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use data::{
|
||||
@@ -130,7 +141,7 @@ pub fn set_hardware_breakpoint(address: usize) {
|
||||
use windows_sys::Win32::System::Diagnostics::Debug::CONTEXT;
|
||||
|
||||
unsafe {
|
||||
let mut context: CONTEXT = std::mem::zeroed();
|
||||
let mut context: CONTEXT = core::mem::zeroed();
|
||||
context.ContextFlags = 0x100000 | 0x10; // CONTEXT_DEBUG_REGISTERS
|
||||
let _ = GetThreadContext(GetCurrentThread(), &mut context);
|
||||
|
||||
@@ -163,44 +174,44 @@ pub unsafe extern "system" fn breakpoint_handler(exceptioninfo: *mut ExceptionPo
|
||||
(*(*exceptioninfo).context_record).R10 =
|
||||
NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
|
||||
(*(*exceptioninfo).context_record).Rdx =
|
||||
std::mem::transmute(NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.base_address);
|
||||
core::mem::transmute(NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.base_address);
|
||||
}
|
||||
ExceptionHandleFunction::NtProtectVirtualMemory => {
|
||||
(*(*exceptioninfo).context_record).R10 =
|
||||
NT_PROTECT_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
|
||||
(*(*exceptioninfo).context_record).Rdx =
|
||||
std::mem::transmute(NT_PROTECT_VIRTUAL_MEMORY_ARGS.base_address);
|
||||
core::mem::transmute(NT_PROTECT_VIRTUAL_MEMORY_ARGS.base_address);
|
||||
(*(*exceptioninfo).context_record).R8 =
|
||||
std::mem::transmute(NT_PROTECT_VIRTUAL_MEMORY_ARGS.size);
|
||||
core::mem::transmute(NT_PROTECT_VIRTUAL_MEMORY_ARGS.size);
|
||||
(*(*exceptioninfo).context_record).R9 =
|
||||
NT_PROTECT_VIRTUAL_MEMORY_ARGS.protection as u64;
|
||||
}
|
||||
ExceptionHandleFunction::NtOpenProcess => {
|
||||
(*(*exceptioninfo).context_record).R10 =
|
||||
std::mem::transmute(NT_OPEN_PROCESS_ARGS.handle);
|
||||
core::mem::transmute(NT_OPEN_PROCESS_ARGS.handle);
|
||||
(*(*exceptioninfo).context_record).Rdx = NT_OPEN_PROCESS_ARGS.access as u64;
|
||||
(*(*exceptioninfo).context_record).R8 =
|
||||
std::mem::transmute(NT_OPEN_PROCESS_ARGS.attributes);
|
||||
core::mem::transmute(NT_OPEN_PROCESS_ARGS.attributes);
|
||||
(*(*exceptioninfo).context_record).R9 =
|
||||
std::mem::transmute(NT_OPEN_PROCESS_ARGS.client_id);
|
||||
core::mem::transmute(NT_OPEN_PROCESS_ARGS.client_id);
|
||||
}
|
||||
ExceptionHandleFunction::NtWriteVirtualMemory => {
|
||||
(*(*exceptioninfo).context_record).R10 =
|
||||
NT_WRITE_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
|
||||
(*(*exceptioninfo).context_record).Rdx =
|
||||
std::mem::transmute(NT_WRITE_VIRTUAL_MEMORY_ARGS.base_address);
|
||||
core::mem::transmute(NT_WRITE_VIRTUAL_MEMORY_ARGS.base_address);
|
||||
(*(*exceptioninfo).context_record).R8 =
|
||||
std::mem::transmute(NT_WRITE_VIRTUAL_MEMORY_ARGS.buffer);
|
||||
core::mem::transmute(NT_WRITE_VIRTUAL_MEMORY_ARGS.buffer);
|
||||
(*(*exceptioninfo).context_record).R9 =
|
||||
NT_WRITE_VIRTUAL_MEMORY_ARGS.size as u64;
|
||||
}
|
||||
ExceptionHandleFunction::NtCreateThreadEx => {
|
||||
(*(*exceptioninfo).context_record).R10 =
|
||||
std::mem::transmute(NT_CREATE_THREAD_EX_ARGS.thread);
|
||||
core::mem::transmute(NT_CREATE_THREAD_EX_ARGS.thread);
|
||||
(*(*exceptioninfo).context_record).Rdx =
|
||||
NT_CREATE_THREAD_EX_ARGS.access as u64;
|
||||
(*(*exceptioninfo).context_record).R8 =
|
||||
std::mem::transmute(NT_CREATE_THREAD_EX_ARGS.attributes);
|
||||
core::mem::transmute(NT_CREATE_THREAD_EX_ARGS.attributes);
|
||||
(*(*exceptioninfo).context_record).R9 =
|
||||
NT_CREATE_THREAD_EX_ARGS.process as u64;
|
||||
}
|
||||
@@ -258,11 +269,11 @@ pub fn hook_function(src_address: usize, dst_address: usize) -> bool {
|
||||
unsafe {
|
||||
let original_address = src_address;
|
||||
let handle = INVALID_HANDLE_VALUE;
|
||||
let base_address: *mut PVOID = std::mem::transmute(&original_address);
|
||||
let base_address: *mut PVOID = core::mem::transmute(&original_address);
|
||||
let size = 4096 as usize;
|
||||
let size: *mut usize = std::mem::transmute(&size);
|
||||
let size: *mut usize = core::mem::transmute(&size);
|
||||
let o = u32::default();
|
||||
let old_protection: *mut u32 = std::mem::transmute(&o);
|
||||
let old_protection: *mut u32 = core::mem::transmute(&o);
|
||||
|
||||
let z = nt_protect_virtual_memory(
|
||||
handle,
|
||||
@@ -304,7 +315,7 @@ pub fn hook_function(src_address: usize, dst_address: usize) -> bool {
|
||||
HOOKED_FUNCTIONS_INFO.push((src_address, original_bytes));
|
||||
|
||||
let u = 0u32;
|
||||
let unused: *mut u32 = std::mem::transmute(&u);
|
||||
let unused: *mut u32 = core::mem::transmute(&u);
|
||||
|
||||
let z = nt_protect_virtual_memory(handle, base_address, size, *old_protection, unused);
|
||||
|
||||
@@ -361,11 +372,11 @@ pub fn unhook_function(address: usize) -> bool {
|
||||
|
||||
let original_address = unhook_info.0;
|
||||
let handle = INVALID_HANDLE_VALUE;
|
||||
let base_address: *mut PVOID = std::mem::transmute(&original_address);
|
||||
let base_address: *mut PVOID = core::mem::transmute(&original_address);
|
||||
let size = 4096 as usize;
|
||||
let size: *mut usize = std::mem::transmute(&size);
|
||||
let size: *mut usize = core::mem::transmute(&size);
|
||||
let o = u32::default();
|
||||
let old_protection: *mut u32 = std::mem::transmute(&o);
|
||||
let old_protection: *mut u32 = core::mem::transmute(&o);
|
||||
|
||||
let z = nt_protect_virtual_memory(
|
||||
handle,
|
||||
@@ -386,7 +397,7 @@ pub fn unhook_function(address: usize) -> bool {
|
||||
}
|
||||
|
||||
let u = 0u32;
|
||||
let unused: *mut u32 = std::mem::transmute(&u);
|
||||
let unused: *mut u32 = core::mem::transmute(&u);
|
||||
|
||||
let z = nt_protect_virtual_memory(handle, base_address, size, *old_protection, unused);
|
||||
|
||||
@@ -416,99 +427,13 @@ pub fn unhook_function(address: usize) -> bool {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_module_base_address(module_name: &str) -> usize {
|
||||
let modules = get_modules_list().unwrap();
|
||||
|
||||
for m in modules {
|
||||
let name = get_module_name(m);
|
||||
let path = get_module_path(m);
|
||||
|
||||
if name.is_err() || path.is_err() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if name.unwrap().to_ascii_lowercase() == module_name.to_ascii_lowercase()
|
||||
|| path.unwrap().to_ascii_lowercase() == module_name.to_ascii_lowercase()
|
||||
{
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
peb::get_module_by_name(module_name)
|
||||
}
|
||||
|
||||
fn get_modules_list() -> Result<Vec<usize>, u32> {
|
||||
unsafe {
|
||||
let mut mod_handles: Vec<usize> = Vec::new();
|
||||
let mut reserved = 0;
|
||||
let mut needed = 0;
|
||||
|
||||
let current_process = GetCurrentProcess();
|
||||
|
||||
// Code extracted from winproc
|
||||
let enum_mods = |mod_handles: &mut Vec<usize>, needed: *mut u32| {
|
||||
let cb = mod_handles.len() as u32;
|
||||
|
||||
let res = EnumProcessModules(
|
||||
current_process,
|
||||
mod_handles.as_mut_ptr() as *mut _,
|
||||
cb,
|
||||
needed,
|
||||
);
|
||||
|
||||
if res == 0 {
|
||||
Err(get_last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
// Code extracted from winproc
|
||||
loop {
|
||||
enum_mods(&mut mod_handles, &mut needed)?;
|
||||
if needed <= reserved {
|
||||
break;
|
||||
}
|
||||
reserved = needed;
|
||||
mod_handles.resize(needed as usize, 0);
|
||||
}
|
||||
|
||||
Ok(mod_handles)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_module_name(hmodule: usize) -> Result<String, u32> {
|
||||
unsafe {
|
||||
let current_process = GetCurrentProcess();
|
||||
let mut buffer: [u16; MAX_PATH as _] = std::mem::zeroed();
|
||||
|
||||
let res = GetModuleBaseNameW(current_process, hmodule as _, buffer.as_mut_ptr(), MAX_PATH);
|
||||
if res == 0 {
|
||||
Err(-1i32 as u32)
|
||||
} else {
|
||||
// Code extracted from winproc
|
||||
Ok(OsString::from_wide(&buffer[0..res as usize])
|
||||
.to_string_lossy()
|
||||
.into_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_module_path(hmodule: usize) -> Result<String, u32> {
|
||||
unsafe {
|
||||
let current_process = GetCurrentProcess();
|
||||
let mut buffer: [u16; MAX_PATH as _] = std::mem::zeroed();
|
||||
|
||||
let res =
|
||||
GetModuleFileNameExW(current_process, hmodule as _, buffer.as_mut_ptr(), MAX_PATH);
|
||||
if res == 0 {
|
||||
Err(-1i32 as u32)
|
||||
} else {
|
||||
// Code extracted from winproc
|
||||
Ok(OsString::from_wide(&buffer[0..res as usize])
|
||||
.to_string_lossy()
|
||||
.into_owned())
|
||||
}
|
||||
}
|
||||
/// Hash-based module lookup. Pass the djb2 hash of the lowercased ascii name
|
||||
/// (compute via [`peb::hash_name`]). The plaintext never lands in `.rdata`.
|
||||
pub fn get_module_base_address_h(name_hash: u32) -> usize {
|
||||
peb::get_module_by_hash(name_hash)
|
||||
}
|
||||
|
||||
/// Retrieves the address of an exported function from the specified module.
|
||||
@@ -627,18 +552,14 @@ fn get_forward_address(function_ptr: *mut u8) -> usize {
|
||||
|
||||
let api_set = get_api_mapping();
|
||||
|
||||
let prev_hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|_| {}));
|
||||
let result = panic::catch_unwind(|| {
|
||||
let _ = format!(
|
||||
"{}{}",
|
||||
&forwarded_module_name[..forwarded_module_name.len() - 2],
|
||||
".dll"
|
||||
);
|
||||
});
|
||||
panic::set_hook(prev_hook);
|
||||
|
||||
if result.is_err() {
|
||||
// PE forwarder strings end in "_<ordinal>" or ".<ordinal>" so we drop
|
||||
// the last 2 bytes before tacking ".dll" on. Slicing at len-2 on
|
||||
// arbitrary UTF-8 could panic mid-codepoint; the old code wrapped
|
||||
// this in panic::catch_unwind. PE export names are always ASCII so
|
||||
// a is_char_boundary check is sufficient and works in no_std.
|
||||
if forwarded_module_name.len() < 2
|
||||
|| !forwarded_module_name.is_char_boundary(forwarded_module_name.len() - 2)
|
||||
{
|
||||
return function_ptr as usize;
|
||||
}
|
||||
|
||||
@@ -672,11 +593,11 @@ fn get_forward_address(function_ptr: *mut u8) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_api_mapping() -> HashMap<String, String> {
|
||||
pub fn get_api_mapping() -> BTreeMap<String, String> {
|
||||
unsafe {
|
||||
let handle = GetCurrentProcess();
|
||||
let p: PROCESS_BASIC_INFORMATION = std::mem::zeroed();
|
||||
let process_information: *mut c_void = std::mem::transmute(&p);
|
||||
let p: PROCESS_BASIC_INFORMATION = core::mem::zeroed();
|
||||
let process_information: *mut c_void = core::mem::transmute(&p);
|
||||
let _ret = nt_query_information_process(
|
||||
handle,
|
||||
0,
|
||||
@@ -686,7 +607,7 @@ pub fn get_api_mapping() -> HashMap<String, String> {
|
||||
);
|
||||
|
||||
let process_information_ptr: *mut PROCESS_BASIC_INFORMATION =
|
||||
std::mem::transmute(process_information);
|
||||
core::mem::transmute(process_information);
|
||||
|
||||
let api_set_map_offset: usize;
|
||||
|
||||
@@ -696,12 +617,12 @@ pub fn get_api_mapping() -> HashMap<String, String> {
|
||||
api_set_map_offset = 0x68;
|
||||
}
|
||||
|
||||
let mut api_set_dict: HashMap<String, String> = HashMap::new();
|
||||
let mut api_set_dict: BTreeMap<String, String> = BTreeMap::new();
|
||||
|
||||
let api_set_namespace_ptr = *(((*process_information_ptr).PebBaseAddress as usize
|
||||
+ api_set_map_offset) as *mut usize);
|
||||
let api_set_namespace_ptr: *mut ApiSetNamespace =
|
||||
std::ptr::with_exposed_provenance_mut::<ApiSetNamespace>(api_set_namespace_ptr);
|
||||
core::ptr::with_exposed_provenance_mut::<ApiSetNamespace>(api_set_namespace_ptr);
|
||||
let namespace = *api_set_namespace_ptr;
|
||||
|
||||
for i in 0..namespace.count {
|
||||
@@ -735,7 +656,7 @@ pub fn get_api_mapping() -> HashMap<String, String> {
|
||||
if set_entry.value_length == 1 {
|
||||
let value =
|
||||
(api_set_namespace_ptr as usize + set_entry.value_offset as usize) as *mut u8;
|
||||
set_value_ptr = std::mem::transmute(value);
|
||||
set_value_ptr = core::mem::transmute(value);
|
||||
} else if set_entry.value_length > 1 {
|
||||
for x in 0..set_entry.value_length {
|
||||
let host_ptr = (api_set_entry_name_ptr as usize
|
||||
@@ -819,7 +740,7 @@ pub fn call_module_entry_point(
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let main: EntryPoint = std::mem::transmute(entry_point);
|
||||
let main: EntryPoint = core::mem::transmute(entry_point);
|
||||
let module = entry_point as HINSTANCE;
|
||||
let ret = main(module, DLL_PROCESS_ATTACH, ptr::null_mut());
|
||||
|
||||
@@ -867,11 +788,11 @@ pub fn fork() -> i32 {
|
||||
unsafe {
|
||||
let process_handle = 0;
|
||||
let thread_handle = 0;
|
||||
let process_handle: *mut HANDLE = std::mem::transmute(&process_handle);
|
||||
let thread_handle: *mut HANDLE = std::mem::transmute(&thread_handle);
|
||||
let mut create_info: PsCreateInfo = std::mem::zeroed();
|
||||
let process_handle: *mut HANDLE = core::mem::transmute(&process_handle);
|
||||
let thread_handle: *mut HANDLE = core::mem::transmute(&thread_handle);
|
||||
let mut create_info: PsCreateInfo = core::mem::zeroed();
|
||||
create_info.size = size_of::<PsCreateInfo>();
|
||||
let ps_create_info: *mut PsCreateInfo = std::mem::transmute(&create_info);
|
||||
let ps_create_info: *mut PsCreateInfo = core::mem::transmute(&create_info);
|
||||
|
||||
let ret = nt_create_user_process(
|
||||
process_handle, // NULL
|
||||
@@ -917,15 +838,15 @@ pub fn ldr_get_procedure_address(module_handle: usize, function_name: &str, ordi
|
||||
unsafe {
|
||||
let ret: Option<i32>;
|
||||
let func_ptr: data::LdrGetProcedureAddress;
|
||||
let hmodule: PVOID = std::ptr::with_exposed_provenance_mut::<libc::c_void>(module_handle);
|
||||
let hmodule: PVOID = core::ptr::with_exposed_provenance_mut::<libc::c_void>(module_handle);
|
||||
let r = usize::default();
|
||||
let return_address: *mut c_void = std::mem::transmute(&r);
|
||||
let return_address: *mut PVOID = std::mem::transmute(return_address);
|
||||
let return_address: *mut c_void = core::mem::transmute(&r);
|
||||
let return_address: *mut PVOID = core::mem::transmute(return_address);
|
||||
|
||||
// LdrGetProcedureAddress wants a PANSI_STRING (16 bytes on x64),
|
||||
// not a Rust String (24 bytes, different layout). We hold the
|
||||
// CString alive for the duration of the call to keep .as_ptr()
|
||||
// valid; the ANSI_STRING borrows from it.
|
||||
// Bug 10 in AUDIT.md: LdrGetProcedureAddress wants a PANSI_STRING
|
||||
// (16 bytes on x64), not a Rust String (24 bytes, different layout).
|
||||
// We hold the CString alive for the duration of the call to keep the
|
||||
// .as_ptr() valid; the ANSI_STRING borrows from it.
|
||||
let c_name = if function_name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
@@ -1019,7 +940,7 @@ pub fn load_library_a_tp(module: &str) -> usize {
|
||||
let ret: Option<i32>;
|
||||
let func_ptr: data::RtlQueueWorkItem;
|
||||
let name = CString::new(module.to_string()).expect("");
|
||||
let module_name: PVOID = std::mem::transmute(name.as_ptr());
|
||||
let module_name: PVOID = core::mem::transmute(name.as_ptr());
|
||||
let k32 = get_module_base_address(&lc!("kernel32.dll"));
|
||||
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
|
||||
let load_library = get_function_address(k32, &lc!("LoadLibraryA"));
|
||||
@@ -1038,10 +959,7 @@ pub fn load_library_a_tp(module: &str) -> usize {
|
||||
if x != 0 {
|
||||
return 0;
|
||||
} else {
|
||||
use std::{thread, time};
|
||||
let ten_millis = time::Duration::from_millis(500);
|
||||
thread::sleep(ten_millis);
|
||||
|
||||
nt_sleep_millis(500);
|
||||
return get_module_base_address(module);
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +970,30 @@ pub fn load_library_a_tp(module: &str) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Block the current thread for `ms` milliseconds via NtDelayExecution.
|
||||
/// no_std-friendly replacement for std::thread::sleep that also avoids the
|
||||
/// kernel32!Sleep hook surface; the call lands directly in ntdll.
|
||||
pub fn nt_sleep_millis(ms: u64) {
|
||||
type NtDelayExecution = unsafe extern "system" fn(u8, *mut LARGE_INTEGER) -> i32;
|
||||
unsafe {
|
||||
let mut interval = LARGE_INTEGER {
|
||||
QuadPart: -(ms as i64 * 10_000),
|
||||
};
|
||||
let ret: Option<i32>;
|
||||
let func_ptr: NtDelayExecution;
|
||||
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
|
||||
dynamic_invoke!(
|
||||
ntdll,
|
||||
&lc!("NtDelayExecution"),
|
||||
func_ptr,
|
||||
ret,
|
||||
0u8,
|
||||
&mut interval as *mut _
|
||||
);
|
||||
let _ = ret;
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamically calls LoadLibraryA.
|
||||
///
|
||||
/// It will return either the module's base address or 0.
|
||||
@@ -1068,7 +1010,7 @@ pub fn load_library_a(module: &str) -> usize {
|
||||
let ret: Option<usize>;
|
||||
let func_ptr: data::LoadLibraryA;
|
||||
let name = CString::new(module.to_string()).expect("");
|
||||
let module_name: *mut u8 = std::mem::transmute(name.as_ptr());
|
||||
let module_name: *mut u8 = core::mem::transmute(name.as_ptr());
|
||||
let k32 = get_module_base_address(&lc!("kernel32.dll"));
|
||||
dynamic_invoke!(k32, &lc!("LoadLibraryA"), func_ptr, ret, module_name);
|
||||
|
||||
@@ -1146,7 +1088,7 @@ pub fn create_file_a(
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return std::ptr::null_mut(),
|
||||
None => return core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1196,7 +1138,7 @@ pub fn create_file_mapping_w(
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return std::ptr::null_mut(),
|
||||
None => return core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1309,7 +1251,7 @@ pub fn open_process(desired_access: u32, inherit_handle: i32, process_id: u32) -
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return std::ptr::null_mut(),
|
||||
None => return core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1346,7 +1288,7 @@ pub fn open_thread(desired_access: u32, inherit_handle: i32, thread_id: u32) ->
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return std::ptr::null_mut(),
|
||||
None => return core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1712,8 +1654,8 @@ pub fn nt_write_virtual_memory(
|
||||
let ret;
|
||||
let func_ptr: data::NtWriteVirtualMemory;
|
||||
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
|
||||
let _decoy_buf: Vec<u8>;
|
||||
|
||||
let _decoy_buf: Vec<u8>;
|
||||
if HARDWARE_BREAKPOINTS {
|
||||
let addr = get_function_address(ntdll, &lc!("NtWriteVirtualMemory")) as usize;
|
||||
HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtWriteVirtualMemory;
|
||||
@@ -1894,7 +1836,7 @@ pub fn nt_protect_virtual_memory(
|
||||
handle = INVALID_HANDLE_VALUE;
|
||||
base_address = ptr::null_mut();
|
||||
let s = 10usize;
|
||||
size = std::mem::transmute(&s);
|
||||
size = core::mem::transmute(&s);
|
||||
new_protection = PAGE_READONLY;
|
||||
}
|
||||
|
||||
@@ -1976,15 +1918,15 @@ pub fn nt_open_process(
|
||||
set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
|
||||
|
||||
let h = INVALID_HANDLE_VALUE;
|
||||
handle = std::mem::transmute(&h);
|
||||
handle = core::mem::transmute(&h);
|
||||
access = PROCESS_QUERY_LIMITED_INFORMATION;
|
||||
let a: OBJECT_ATTRIBUTES = std::mem::zeroed();
|
||||
attributes = std::mem::transmute(&a);
|
||||
let a: OBJECT_ATTRIBUTES = core::mem::zeroed();
|
||||
attributes = core::mem::transmute(&a);
|
||||
let c = ClientId {
|
||||
unique_process: std::process::id() as *mut std::ffi::c_void,
|
||||
unique_thread: std::ptr::null_mut(),
|
||||
unique_process: GetCurrentProcessId() as *mut core::ffi::c_void,
|
||||
unique_thread: core::ptr::null_mut(),
|
||||
};
|
||||
client_id = std::mem::transmute(&c);
|
||||
client_id = core::mem::transmute(&c);
|
||||
}
|
||||
|
||||
dynamic_invoke!(
|
||||
@@ -2355,10 +2297,10 @@ pub fn nt_create_thread_ex(
|
||||
set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
|
||||
|
||||
let h = INVALID_HANDLE_VALUE;
|
||||
thread = std::mem::transmute(&h);
|
||||
thread = core::mem::transmute(&h);
|
||||
access = PROCESS_QUERY_LIMITED_INFORMATION;
|
||||
let a: OBJECT_ATTRIBUTES = std::mem::zeroed();
|
||||
attributes = std::mem::transmute(&a);
|
||||
let a: OBJECT_ATTRIBUTES = core::mem::zeroed();
|
||||
attributes = core::mem::transmute(&a);
|
||||
process = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
@@ -2507,11 +2449,11 @@ pub fn nt_read_virtual_memory(
|
||||
/// let ptr = dyncvoke_core::get_module_base_address("ntdll.dll");
|
||||
/// let function_ptr: LdrGetProcedureAddress;
|
||||
/// let ret: Option<i32>;
|
||||
/// let hmodule: PVOID = std::mem::transmute(ptr);
|
||||
/// let hmodule: PVOID = core::mem::transmute(ptr);
|
||||
/// let fun_name: *mut String = ptr::null_mut();
|
||||
/// let ordinal = 8 as u32;
|
||||
/// let return_address: *mut c_void = std::mem::transmute(&usize::default());
|
||||
/// let return_address: *mut PVOID = std::mem::transmute(return_address);
|
||||
/// let return_address: *mut c_void = core::mem::transmute(&usize::default());
|
||||
/// let return_address: *mut PVOID = core::mem::transmute(return_address);
|
||||
/// //dyncvoke_core::dynamic_invoke(usize,&str,<function_type>,Option<return_type>,[arguments])
|
||||
/// dyncvoke_core::dynamic_invoke!(ptr,"LdrGetProcedureAddress",function_ptr,ret,hmodule,fun_name,ordinal,return_address);
|
||||
///
|
||||
@@ -2539,7 +2481,7 @@ macro_rules! dynamic_invoke {
|
||||
let function_ptr = $crate::get_function_address($a, $b);
|
||||
if function_ptr != 0
|
||||
{
|
||||
$c = std::mem::transmute(function_ptr);
|
||||
$c = core::mem::transmute(function_ptr);
|
||||
$d = Some($c($($e)*));
|
||||
}
|
||||
else
|
||||
@@ -2557,16 +2499,17 @@ macro_rules! dynamic_invoke {
|
||||
/// failed (the syscall was never executed); `Ok(status)` returns the raw
|
||||
/// NTSTATUS the kernel gave us.
|
||||
///
|
||||
/// Supports arbitrary arity, zero-arg syscalls (e.g. NtYieldExecution),
|
||||
/// and trailing commas.
|
||||
/// Replaces the deleted `execute_syscall!` / `indirect_syscall!` macros.
|
||||
/// Supports arbitrary arity (kills AUDIT.md Bug 8), zero-arg syscalls
|
||||
/// (e.g. NtYieldExecution), and trailing commas.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// use dyncvoke_core::syscall;
|
||||
/// use std::ffi::c_void;
|
||||
/// use core::ffi::c_void;
|
||||
///
|
||||
/// let mut handle: *mut c_void = std::ptr::null_mut();
|
||||
/// let mut handle: *mut c_void = core::ptr::null_mut();
|
||||
/// // ... fill in object attributes / client id ...
|
||||
/// let status = syscall!("NtOpenProcess",
|
||||
/// &mut handle as *mut *mut c_void,
|
||||
@@ -2590,7 +2533,7 @@ macro_rules! syscall {
|
||||
///
|
||||
/// Skips resolution entirely. Useful for caching SSNs at init or for
|
||||
/// resolving via a non-default mechanism (e.g. a hand-rolled SSDT walker
|
||||
/// or a remote-process resolver). Always returns the raw NTSTATUS. no
|
||||
/// or a remote-process resolver). Always returns the raw NTSTATUS — no
|
||||
/// `Result` wrapping.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -2616,6 +2559,9 @@ macro_rules! do_syscall {
|
||||
// Tests
|
||||
// ============================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -2623,9 +2569,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_invalid_handle_value() {
|
||||
// Windows defines INVALID_HANDLE_VALUE as (HANDLE)(LONG_PTR)-1,
|
||||
// the NtCurrentProcess pseudo-handle. NULL is rejected by NT memory
|
||||
// syscalls with STATUS_INVALID_HANDLE.
|
||||
// Windows defines INVALID_HANDLE_VALUE as (HANDLE)(LONG_PTR)-1
|
||||
// (the NtCurrentProcess pseudo-handle). Bug 5 in AUDIT.md changed
|
||||
// it from core::ptr::null_mut() which the kernel rejected with
|
||||
// STATUS_INVALID_HANDLE.
|
||||
assert_eq!(INVALID_HANDLE_VALUE as isize, -1);
|
||||
}
|
||||
|
||||
@@ -2690,7 +2637,7 @@ mod tests {
|
||||
let mut us = UNICODE_STRING {
|
||||
Length: 0,
|
||||
MaximumLength: 0,
|
||||
Buffer: std::ptr::null_mut(),
|
||||
Buffer: core::ptr::null_mut(),
|
||||
};
|
||||
|
||||
// Call rtl_init_unicode_string to initialize it
|
||||
@@ -2744,7 +2691,7 @@ mod tests {
|
||||
fn test_nt_allocate_virtual_memory_args() {
|
||||
let args = NtAllocateVirtualMemoryArgs {
|
||||
handle: INVALID_HANDLE_VALUE,
|
||||
base_address: std::ptr::null_mut(),
|
||||
base_address: core::ptr::null_mut(),
|
||||
};
|
||||
assert_eq!(args.handle as isize, -1);
|
||||
assert!(args.base_address.is_null());
|
||||
@@ -2754,10 +2701,10 @@ mod tests {
|
||||
#[test]
|
||||
fn test_nt_open_process_args() {
|
||||
let args = NtOpenProcessArgs {
|
||||
handle: std::ptr::null_mut(),
|
||||
handle: core::ptr::null_mut(),
|
||||
access: 0x1000,
|
||||
attributes: std::ptr::null_mut(),
|
||||
client_id: std::ptr::null_mut(),
|
||||
attributes: core::ptr::null_mut(),
|
||||
client_id: core::ptr::null_mut(),
|
||||
};
|
||||
assert!(args.handle.is_null());
|
||||
assert_eq!(args.access, 0x1000);
|
||||
@@ -2768,8 +2715,8 @@ mod tests {
|
||||
fn test_nt_protect_virtual_memory_args() {
|
||||
let args = NtProtectVirtualMemoryArgs {
|
||||
handle: INVALID_HANDLE_VALUE,
|
||||
base_address: std::ptr::null_mut(),
|
||||
size: std::ptr::null_mut(),
|
||||
base_address: core::ptr::null_mut(),
|
||||
size: core::ptr::null_mut(),
|
||||
protection: PAGE_EXECUTE_READWRITE,
|
||||
};
|
||||
assert_eq!(args.handle as isize, -1);
|
||||
@@ -2781,8 +2728,8 @@ mod tests {
|
||||
fn test_nt_write_virtual_memory_args() {
|
||||
let args = NtWriteVirtualMemoryArgs {
|
||||
handle: INVALID_HANDLE_VALUE,
|
||||
base_address: std::ptr::null_mut(),
|
||||
buffer: std::ptr::null_mut(),
|
||||
base_address: core::ptr::null_mut(),
|
||||
buffer: core::ptr::null_mut(),
|
||||
size: 0,
|
||||
};
|
||||
assert_eq!(args.handle as isize, -1);
|
||||
@@ -2792,9 +2739,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_nt_create_thread_ex_args() {
|
||||
let args = NtCreateThreadExArgs {
|
||||
thread: std::ptr::null_mut(),
|
||||
thread: core::ptr::null_mut(),
|
||||
access: 0x1F03FF,
|
||||
attributes: std::ptr::null_mut(),
|
||||
attributes: core::ptr::null_mut(),
|
||||
process: INVALID_HANDLE_VALUE,
|
||||
};
|
||||
assert!(args.thread.is_null());
|
||||
@@ -2820,7 +2767,7 @@ mod tests {
|
||||
// Test type sizes match expected Windows sizes
|
||||
#[test]
|
||||
fn test_type_sizes() {
|
||||
assert_eq!(std::mem::size_of::<HANDLE>(), std::mem::size_of::<*mut std::ffi::c_void>());
|
||||
assert_eq!(std::mem::size_of::<PVOID>(), std::mem::size_of::<*mut std::ffi::c_void>());
|
||||
assert_eq!(core::mem::size_of::<HANDLE>(), core::mem::size_of::<*mut core::ffi::c_void>());
|
||||
assert_eq!(core::mem::size_of::<PVOID>(), core::mem::size_of::<*mut core::ffi::c_void>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
//! Direct PEB walker. Replaces the EnumProcessModules / GetModuleBaseNameW /
|
||||
//! OsString::from_wide chain with reads from the in-process LDR data.
|
||||
//!
|
||||
//! Two reasons this exists:
|
||||
//!
|
||||
//! 1. It removes the only no_std blocker in module enumeration — the old path
|
||||
//! needed OsString::from_wide which is std-only.
|
||||
//! 2. It removes any user-mode-hookable kernel32/psapi call from the module
|
||||
//! lookup path. Walking the PEB is invisible to inline hooks placed on
|
||||
//! EnumProcessModules / GetModuleBaseNameW.
|
||||
//!
|
||||
//! There are two lookup modes:
|
||||
//!
|
||||
//! - [`get_module_by_name`] takes a `&str` and matches case-insensitively
|
||||
//! against each LDR entry's BaseDllName. Used by call sites that already
|
||||
//! have a runtime-discovered name (PE imports, ApiSet mapping, etc.).
|
||||
//! - [`get_module_by_hash`] takes a precomputed djb2 hash of the lowercased
|
||||
//! ascii name. Used by hot OPSEC paths so the plaintext name never
|
||||
//! materializes in `.rdata` at all. Compute the hash at compile time via
|
||||
//! the [`hash_name`] const fn.
|
||||
|
||||
use core::ffi::c_void;
|
||||
|
||||
/// LIST_ENTRY — Windows doubly-linked list head.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ListEntry {
|
||||
pub flink: *mut ListEntry,
|
||||
pub blink: *mut ListEntry,
|
||||
}
|
||||
|
||||
/// UNICODE_STRING — Windows counted UTF-16 string. Mirrors the layout of the
|
||||
/// kernel struct exactly.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UnicodeString {
|
||||
pub length: u16,
|
||||
pub maximum_length: u16,
|
||||
pub buffer: *mut u16,
|
||||
}
|
||||
|
||||
/// LDR_DATA_TABLE_ENTRY — one loaded module's record. The PEB's Ldr field
|
||||
/// points at a PEB_LDR_DATA whose InLoadOrderModuleList head is the start of
|
||||
/// a circular linked list of these.
|
||||
#[repr(C)]
|
||||
pub struct LdrDataTableEntry {
|
||||
pub in_load_order_links: ListEntry,
|
||||
pub in_memory_order_links: ListEntry,
|
||||
pub in_initialization_order_links: ListEntry,
|
||||
pub dll_base: *mut c_void,
|
||||
pub entry_point: *mut c_void,
|
||||
pub size_of_image: u32,
|
||||
pub full_dll_name: UnicodeString,
|
||||
pub base_dll_name: UnicodeString,
|
||||
}
|
||||
|
||||
/// PEB_LDR_DATA — the loader's in-memory bookkeeping for the current process.
|
||||
///
|
||||
/// Internal layout (WRK / ReactOS, not winternl.h's public abbreviation):
|
||||
/// 0x00 ULONG Length
|
||||
/// 0x04 BOOLEAN Initialized + 3 bytes padding to align next pointer
|
||||
/// 0x08 PVOID SsHandle
|
||||
/// 0x10 LIST_ENTRY InLoadOrderModuleList <- we want this one
|
||||
/// 0x20 LIST_ENTRY InMemoryOrderModuleList
|
||||
/// 0x30 LIST_ENTRY InInitializationOrderModuleList
|
||||
#[repr(C)]
|
||||
pub struct PebLdrData {
|
||||
pub _length_and_init: [u8; 8],
|
||||
pub _ss_handle: *mut c_void,
|
||||
pub in_load_order_module_list: ListEntry,
|
||||
}
|
||||
|
||||
/// PEB — only the Ldr pointer is exposed.
|
||||
///
|
||||
/// Public winternl.h layout:
|
||||
/// 0x00 BYTE Reserved1[2]
|
||||
/// 0x02 BYTE BeingDebugged
|
||||
/// 0x03 BYTE Reserved2[1]
|
||||
/// 0x08 PVOID Reserved3[2] (on x64, aligned)
|
||||
/// 0x18 PPEB_LDR_DATA Ldr <- we want this
|
||||
#[repr(C)]
|
||||
pub struct Peb {
|
||||
pub _reserved1: [u8; 2],
|
||||
pub being_debugged: u8,
|
||||
pub _reserved2: [u8; 1],
|
||||
pub _padding: [u8; 4],
|
||||
pub _reserved3: [*mut c_void; 2],
|
||||
pub ldr: *mut PebLdrData,
|
||||
}
|
||||
|
||||
/// Return the current process's PEB pointer.
|
||||
#[inline(always)]
|
||||
pub fn current_peb() -> *mut Peb {
|
||||
let peb: *mut Peb;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
// PEB pointer lives at gs:[0x60] on x64.
|
||||
core::arch::asm!(
|
||||
"mov {peb}, gs:[0x60]",
|
||||
peb = out(reg) peb,
|
||||
options(nostack, preserves_flags, readonly)
|
||||
);
|
||||
}
|
||||
#[cfg(target_arch = "x86")]
|
||||
unsafe {
|
||||
// PEB pointer lives at fs:[0x30] on x86.
|
||||
core::arch::asm!(
|
||||
"mov {peb}, fs:[0x30]",
|
||||
peb = out(reg) peb,
|
||||
options(nostack, preserves_flags, readonly)
|
||||
);
|
||||
}
|
||||
peb
|
||||
}
|
||||
|
||||
/// djb2 over the ascii-lowercased input. Stable, branch-light, const-evaluable
|
||||
/// so call sites can write `hash_name(b"ntdll.dll")` to embed a u32 instead
|
||||
/// of the literal.
|
||||
pub const fn hash_name(bytes: &[u8]) -> u32 {
|
||||
let mut hash: u32 = 5381;
|
||||
let mut i = 0;
|
||||
while i < bytes.len() {
|
||||
let mut c = bytes[i];
|
||||
if c >= b'A' && c <= b'Z' {
|
||||
c += 32;
|
||||
}
|
||||
hash = hash.wrapping_mul(33).wrapping_add(c as u32);
|
||||
i += 1;
|
||||
}
|
||||
hash
|
||||
}
|
||||
|
||||
/// Compare a UNICODE_STRING against ascii bytes case-insensitively. Walks the
|
||||
/// UTF-16 buffer one code unit at a time, lowercases ascii ranges, and bails
|
||||
/// on any non-ascii unit (no module we care about has non-ascii in its name).
|
||||
unsafe fn unicode_eq_ascii_ci(us: &UnicodeString, target: &[u8]) -> bool {
|
||||
let len = (us.length / 2) as usize;
|
||||
if len != target.len() {
|
||||
return false;
|
||||
}
|
||||
for i in 0..len {
|
||||
let mut wide = *us.buffer.add(i);
|
||||
if wide >= 0x80 {
|
||||
return false;
|
||||
}
|
||||
let mut byte = wide as u8;
|
||||
if byte >= b'A' && byte <= b'Z' {
|
||||
byte += 32;
|
||||
}
|
||||
let mut tgt = target[i];
|
||||
if tgt >= b'A' && tgt <= b'Z' {
|
||||
tgt += 32;
|
||||
}
|
||||
wide = byte as u16;
|
||||
let _ = wide;
|
||||
if byte != tgt {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Compute djb2 over a UNICODE_STRING's buffer, lowercasing ascii. Mirrors
|
||||
/// [`hash_name`] so a precomputed const hash matches what we derive at
|
||||
/// runtime from the loader's records.
|
||||
unsafe fn hash_unicode_string(us: &UnicodeString) -> u32 {
|
||||
let len = (us.length / 2) as usize;
|
||||
let mut hash: u32 = 5381;
|
||||
for i in 0..len {
|
||||
let wide = *us.buffer.add(i);
|
||||
if wide >= 0x80 {
|
||||
return 0;
|
||||
}
|
||||
let mut byte = wide as u8;
|
||||
if byte >= b'A' && byte <= b'Z' {
|
||||
byte += 32;
|
||||
}
|
||||
hash = hash.wrapping_mul(33).wrapping_add(byte as u32);
|
||||
}
|
||||
hash
|
||||
}
|
||||
|
||||
/// Iterate the InLoadOrderModuleList and call `visit` for each entry. Returns
|
||||
/// the first non-null DllBase the visitor accepts.
|
||||
unsafe fn walk_modules<F>(mut visit: F) -> usize
|
||||
where
|
||||
F: FnMut(&LdrDataTableEntry) -> bool,
|
||||
{
|
||||
let peb = current_peb();
|
||||
if peb.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let ldr = (*peb).ldr;
|
||||
if ldr.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let head = &(*ldr).in_load_order_module_list as *const ListEntry as *mut ListEntry;
|
||||
let mut cursor = (*head).flink;
|
||||
while !cursor.is_null() && cursor != head {
|
||||
let entry = cursor as *mut LdrDataTableEntry;
|
||||
if visit(&*entry) {
|
||||
return (*entry).dll_base as usize;
|
||||
}
|
||||
cursor = (*cursor).flink;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Look up a loaded module by its base name (e.g. `"ntdll.dll"`). ASCII
|
||||
/// case-insensitive. Returns 0 if no entry matches.
|
||||
pub fn get_module_by_name(name: &str) -> usize {
|
||||
unsafe { walk_modules(|entry| unicode_eq_ascii_ci(&entry.base_dll_name, name.as_bytes())) }
|
||||
}
|
||||
|
||||
/// Look up a loaded module by the djb2 hash of its lowercased ascii base name.
|
||||
/// The plaintext name never appears in the calling binary's image. Returns
|
||||
/// 0 if no entry matches.
|
||||
pub fn get_module_by_hash(hash: u32) -> usize {
|
||||
unsafe { walk_modules(|entry| hash_unicode_string(&entry.base_dll_name) == hash) }
|
||||
}
|
||||
@@ -1,18 +1,25 @@
|
||||
//! Variadic syscall gateway.
|
||||
//! Hell's Hall variadic gateway.
|
||||
//!
|
||||
//! `do_syscall(ssn, syscall_addr, n_args, ...)` dispatches an indirect
|
||||
//! syscall by jumping to the `syscall` instruction inside ntdll itself.
|
||||
//! Single C-callable variadic function that receives `(ssn, syscall_addr,
|
||||
//! n_args, ...)` and dispatches an indirect syscall by jumping to the real
|
||||
//! `syscall` instruction inside ntdll. Replaces the old fixed-arity
|
||||
//! gateway with cmp-ladder + 11 per-arity handlers + Configuration struct +
|
||||
//! restore proc — see AUDIT.md Bugs 1/6/7/8 and PLAN.md Phase 3 for the
|
||||
//! design notes.
|
||||
//!
|
||||
//! Stack discipline at the moment of `jmp rcx` (which lands on ntdll's
|
||||
//! `syscall` instruction):
|
||||
//!
|
||||
//! - rsp is unchanged from function entry. The x64 red zone holds the
|
||||
//! non-volatile saves (rsi, rdi, r12), so no `sub rsp` is needed.
|
||||
//! `[rsp]` is the return address back to the Rust caller.
|
||||
//! - After the kernel returns to the next instruction in ntdll's stub
|
||||
//! (`ret`), the `ret` pops `[rsp]` and lands back in the Rust caller.
|
||||
//! - rsp is unchanged from function entry (we used the x64 red zone for
|
||||
//! non-volatile saves, not `sub rsp`). So `[rsp]` is the return address
|
||||
//! back to the Rust caller.
|
||||
//! - After the kernel syscall returns to the next instruction in ntdll's
|
||||
//! stub (`ret`), the `ret` pops `[rsp]` and lands back in the Rust
|
||||
//! caller — no bespoke `restore` proc needed.
|
||||
//! - Args 5..N have been slid down from `[rsp + 0x40]` to `[rsp + 0x28]`
|
||||
//! via `rep movsq` so the kernel reads them where it expects.
|
||||
//!
|
||||
//! Mirrors `dinvk/src/sys/asm.rs:1-37`.
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
core::arch::global_asm!("
|
||||
@@ -54,15 +61,15 @@ do_syscall:
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe extern "C" {
|
||||
/// Variadic syscall dispatcher.
|
||||
/// Hell's Hall variadic syscall dispatcher.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `syscall_addr` must point at a valid `syscall` instruction inside
|
||||
/// ntdll (use [`crate::resolve_syscall`]). `n_args` must match the
|
||||
/// number of variadic arguments that follow. Passing wrong-sized
|
||||
/// integer types in the varargs (e.g. a u32 where the syscall expects
|
||||
/// u64) will land garbage in the upper bits and the syscall will fail
|
||||
/// in non-obvious ways.
|
||||
/// number of variadic args that follow. Passing wrong-sized integer
|
||||
/// types in the varargs (e.g. a u32 where the syscall expects u64)
|
||||
/// will land garbage in the upper bits and the syscall will fail in
|
||||
/// non-obvious ways.
|
||||
pub fn do_syscall(ssn: u16, syscall_addr: usize, n_args: u32, ...) -> i32;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
//! Tartarus Gate SSN resolution and strict syscall-instruction location.
|
||||
//! Tartarus Gate SSN resolution + strict syscall-instruction location.
|
||||
//!
|
||||
//! Replaces the old BTreeMap-position approach (AUDIT.md Bug 1). Mirrors the
|
||||
//! layout of `dinvk/src/sys/mod.rs` so future improvements there can be ported
|
||||
//! cleanly here.
|
||||
//!
|
||||
//! Three modes for the SSN derivation, in order of preference:
|
||||
//!
|
||||
//! 1. Hell's Gate: stub has the clean `MOV R10, RCX; MOV EAX, <ssn>`
|
||||
//! 1. **Hell's Gate** — stub has the clean `MOV R10, RCX; MOV EAX, <ssn>`
|
||||
//! prologue, read the SSN straight out of `stub[4..6]`.
|
||||
//! 2. Halo's Gate: stub starts with `E9 ...` (hook at entry); walk
|
||||
//! 2. **Halo's Gate** — stub starts with `E9 …` (hook at entry); walk
|
||||
//! ±32-byte neighbors until we find a clean stub, then adjust the SSN by
|
||||
//! the offset distance (each stub is 32 bytes apart on x64 ntdll).
|
||||
//! 3. Tartarus Gate: stub has `E9` at offset 3 (the hook landed after
|
||||
//! `MOV R10, RCX`, which preserves r10 but redirects flow). Same
|
||||
//! neighbor walk as Halo's Gate.
|
||||
//! 3. **Tartarus Gate** — stub has `E9` at offset 3 (the hook landed *after*
|
||||
//! `MOV R10, RCX`, which preserves r10 but redirects flow). Same neighbor
|
||||
//! walk as Halo's Gate.
|
||||
|
||||
use alloc::string::String;
|
||||
use core::ptr::read;
|
||||
|
||||
use litcrypt2::lc;
|
||||
use data::lc;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod asm;
|
||||
@@ -137,11 +142,12 @@ impl core::fmt::Display for SyscallError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SyscallError {}
|
||||
impl core::error::Error for SyscallError {}
|
||||
|
||||
/// Resolve (SSN, syscall_instr_addr) for a Zw/Nt export of ntdll.
|
||||
///
|
||||
/// Returns a typed error on every failure mode. Never hangs.
|
||||
/// Replaces the old `prepare_syscall` + `get_ntdll_eat` + `get_syscall_id`
|
||||
/// pipeline. Returns a typed error on every failure mode — never hangs.
|
||||
pub fn resolve_syscall(name: &str) -> Result<(u16, usize), SyscallError> {
|
||||
let ntdll = crate::get_module_base_address(&lc!("ntdll.dll"));
|
||||
if ntdll == 0 {
|
||||
|
||||
+9
-6
@@ -1,10 +1,13 @@
|
||||
// dmanager's xor_key derivation folds a 15-byte random buffer with
|
||||
// `rand_bytes.iter().fold(0u8, |acc, b| acc ^ b)`.
|
||||
// Regression for AUDIT.md Bug 2.
|
||||
//
|
||||
// dmanager's xor_key derivation used to be a C-style null-terminated walk
|
||||
// over a 15-byte random buffer. ~94% of runs read past the array. After the
|
||||
// fix it's `rand_bytes.iter().fold(0u8, |acc, b| acc ^ b)`.
|
||||
//
|
||||
// This test mirrors the production derivation against a controlled buffer
|
||||
// with a sentinel one byte past the intended end. The sentinel must not
|
||||
// participate in the key, proving exactly 15 bytes are consumed and no
|
||||
// out-of-bounds read happens regardless of buffer content.
|
||||
// with a sentinel one byte past the intended end. The sentinel must NOT
|
||||
// participate in the key — proving exactly 15 bytes are consumed and no
|
||||
// OOB read happens regardless of buffer content.
|
||||
|
||||
#[test]
|
||||
fn xor_key_fold_consumes_exactly_15_bytes_no_oob() {
|
||||
@@ -24,7 +27,7 @@ fn xor_key_fold_consumes_exactly_15_bytes_no_oob() {
|
||||
assert_eq!(from_array, 0xFF, "fold of 15 × 0xFF should yield 0xFF (odd count)");
|
||||
assert_eq!(
|
||||
from_backing_slice, from_array,
|
||||
"fold over backing[..15] must equal fold over the array, sentinel must not leak in"
|
||||
"fold over backing[..15] must equal fold over the array — sentinel must not leak in"
|
||||
);
|
||||
|
||||
// For total certainty: change only the sentinel and recompute the fold
|
||||
+10
-6
@@ -1,22 +1,26 @@
|
||||
// Broad coverage sweep across every Zw* export in ntdll.
|
||||
// Hell's Gate sweep — broad coverage across the entire Zw* export space of
|
||||
// ntdll. Replaces the two old audit_hellsgate* tests that depended on the
|
||||
// deleted BTreeMap-position API.
|
||||
//
|
||||
// For every Zw* export:
|
||||
// 1. Read the canonical SSN directly from the stub bytes (4C 8B D1 B8
|
||||
// <lo> <hi> 00 00), without touching the neighbor walker.
|
||||
// 2. Run resolve_syscall(name).
|
||||
// 3. The two must agree for any stub that has the canonical prologue.
|
||||
// 3. The two MUST agree for any stub that has the canonical prologue.
|
||||
//
|
||||
// Stubs that don't have the canonical prologue (e.g. NtQuerySystemTime,
|
||||
// which reads from KUSER_SHARED_DATA without a syscall) are tracked
|
||||
// separately. They exercise the Tartarus neighbor walk and we just log
|
||||
// them.
|
||||
// separately and OK — they exercise the Tartarus neighbor walk and we just
|
||||
// log them.
|
||||
//
|
||||
// Catches OS-wide drift on a new Windows build: if a future build
|
||||
// rearranges the SSDT, this fails loudly with the offending name and SSNs.
|
||||
|
||||
use dyncvoke_core::{get_function_address, get_module_base_address, resolve_syscall};
|
||||
|
||||
/// Walk the ntdll EAT and collect every `Zw*` export's name.
|
||||
/// Walk the ntdll EAT and collect every `Zw*` export's name. Mirrors what
|
||||
/// the deleted `get_ntdll_eat` did but discards the BTreeMap layer that
|
||||
/// gave Bug 1 its second-order behavior.
|
||||
fn collect_zw_exports(ntdll: usize) -> Vec<String> {
|
||||
let mut out = Vec::new();
|
||||
unsafe {
|
||||
@@ -97,7 +101,7 @@ fn resolve_syscall_matches_canonical_byte_derived_ssn_for_every_zw_export() {
|
||||
}
|
||||
None => {
|
||||
// Atypical stub (e.g. NtQuerySystemTime). resolve_syscall
|
||||
// recovered the SSN via the neighbor walk, that's fine.
|
||||
// recovered the SSN via the neighbor walk — that's fine.
|
||||
atypical.push((name.clone(), resolver_ssn));
|
||||
}
|
||||
}
|
||||
+10
-6
@@ -1,10 +1,14 @@
|
||||
// The Windows INVALID_HANDLE_VALUE constant is (HANDLE)(LONG_PTR)-1,
|
||||
// the NtCurrentProcess pseudo-handle that NT memory syscalls accept.
|
||||
// NULL is rejected with STATUS_INVALID_HANDLE (0xC0000008).
|
||||
// Regression for AUDIT.md Bug 5.
|
||||
//
|
||||
// This test asserts both directions: (a) the OS-level invariant that
|
||||
// NULL is rejected and -1 succeeds, and (b) the lib-level invariant
|
||||
// that dyncvoke_core's constant points at -1.
|
||||
// Before the fix, `dyncvoke_core::INVALID_HANDLE_VALUE` was NULL, which the
|
||||
// kernel rejects with STATUS_INVALID_HANDLE (0xC0000008). The Windows
|
||||
// constant is (HANDLE)(LONG_PTR)-1 — that's the NtCurrentProcess pseudo-
|
||||
// handle and is what the NT memory syscalls actually expect.
|
||||
//
|
||||
// This test now asserts both directions: (a) the OS-level invariant that
|
||||
// NULL is rejected and -1 succeeds, and (b) the lib-level invariant that
|
||||
// dyncvoke_core's constant points at -1 so any caller threading it through
|
||||
// is using a valid pseudo-handle.
|
||||
|
||||
use dyncvoke_core::{nt_protect_virtual_memory, INVALID_HANDLE_VALUE};
|
||||
use std::ffi::c_void;
|
||||
+11
-7
@@ -1,9 +1,13 @@
|
||||
// LdrGetProcedureAddress wants a PANSI_STRING (16 bytes on x64) as its
|
||||
// second parameter. The wrapper sends a real `ANSI_STRING` value backed
|
||||
// by a CString.
|
||||
// Regression for AUDIT.md Bug 10.
|
||||
//
|
||||
// This test resolves a few exports by name through ldr_get_procedure_address
|
||||
// and asserts the result matches get_function_address, proving the kernel
|
||||
// LdrGetProcedureAddress wants a PANSI_STRING (16 bytes on x64) as its 2nd
|
||||
// parameter. Dyncvoke used to pass `*mut String` — a Rust 24-byte struct
|
||||
// with a different field order. Calls by name were silently broken; only
|
||||
// ordinal lookups (where the name pointer is NULL) worked. The fix sends a
|
||||
// real `ANSI_STRING` value backed by a CString.
|
||||
//
|
||||
// This test resolves NtClose by name through ldr_get_procedure_address and
|
||||
// asserts the result matches get_function_address — proving the kernel
|
||||
// actually understood the name we sent.
|
||||
|
||||
use dyncvoke_core::{get_function_address, get_module_base_address, ldr_get_procedure_address};
|
||||
@@ -20,7 +24,7 @@ fn ldr_get_procedure_address_by_name_matches_get_function_address() {
|
||||
let by_ldr = ldr_get_procedure_address(ntdll, name, 0);
|
||||
assert!(
|
||||
by_ldr != 0,
|
||||
"LdrGetProcedureAddress returned 0 for {}, name was not understood",
|
||||
"LdrGetProcedureAddress returned 0 for {} — name was not understood",
|
||||
name
|
||||
);
|
||||
|
||||
@@ -34,7 +38,7 @@ fn ldr_get_procedure_address_by_name_matches_get_function_address() {
|
||||
|
||||
#[test]
|
||||
fn ldr_get_procedure_address_by_ordinal_still_works() {
|
||||
// Empty function_name path (NULL ANSI_STRING) must continue to work.
|
||||
// Empty function_name path (NULL ANSI_STRING) — must continue to work.
|
||||
let ntdll = get_module_base_address("ntdll.dll");
|
||||
assert!(ntdll != 0);
|
||||
// Ordinal 8 is RtlDispatchAPC on most Win10/Win11 builds. We don't pin
|
||||
+12
-12
@@ -1,16 +1,16 @@
|
||||
// Two malformed-PE cases the relocation walker must handle:
|
||||
// Regression for AUDIT.md Bugs 3 + 4.
|
||||
//
|
||||
// (a) A relocation block with SizeOfBlock = 0 must not spin the loop
|
||||
// (the pointer would never advance).
|
||||
// Bug 3: a relocation block with SizeOfBlock = 0 used to spin the loop
|
||||
// forever because the pointer never advanced.
|
||||
//
|
||||
// (b) A PE with no relocation directory (VirtualAddress = 0) must not
|
||||
// fall into reading the DOS header as IMAGE_BASE_RELOCATION, which
|
||||
// would produce a VirtualAddress of 0x00905A4D and corrupt memory.
|
||||
// Bug 4: a PE with no relocation directory (VirtualAddress = 0) used to fall
|
||||
// straight into reading the DOS header as IMAGE_BASE_RELOCATION, producing
|
||||
// a VirtualAddress of 0x00905A4D and corrupting memory.
|
||||
//
|
||||
// manualmap::relocate_module and overload::relocate_text_section guard
|
||||
// both: early-return on `dir.VirtualAddress == 0 || dir.Size == 0`, treat
|
||||
// `SizeOfBlock < sizeof(IMAGE_BASE_RELOCATION)` as end-of-table, and
|
||||
// bound the walk by `dir.Size`.
|
||||
// The fix in manualmap::relocate_module and overload::relocate_text_section
|
||||
// is: early-return on `dir.VirtualAddress == 0 || dir.Size == 0`, treat
|
||||
// `SizeOfBlock < sizeof(IMAGE_BASE_RELOCATION)` as end-of-table, and bound
|
||||
// the walk by `dir.Size`.
|
||||
//
|
||||
// We emulate exactly the fixed loop pattern over a synthetic block and
|
||||
// assert termination.
|
||||
@@ -25,7 +25,7 @@ struct ImageBaseRelocation {
|
||||
#[test]
|
||||
fn fixed_loop_breaks_on_size_zero_block() {
|
||||
// A block with non-zero VirtualAddress but zero SizeOfBlock is the
|
||||
// The walker must break out instead of spinning.
|
||||
// malformed case from Bug 3. The fix breaks on it.
|
||||
let mut storage: Vec<u8> = vec![0u8; 32];
|
||||
unsafe {
|
||||
let p = storage.as_mut_ptr() as *mut ImageBaseRelocation;
|
||||
@@ -60,7 +60,7 @@ fn fixed_loop_breaks_on_size_zero_block() {
|
||||
|
||||
#[test]
|
||||
fn fixed_loop_no_reloc_directory_returns_early() {
|
||||
// dir.VirtualAddress == 0 (PE has no relocation directory). The
|
||||
// Bug 4: dir.VirtualAddress == 0 (PE has no relocation directory). The
|
||||
// fix is a `return` before any read. We mirror just the predicate here
|
||||
// and assert we never enter the loop.
|
||||
let virtual_address: u32 = 0;
|
||||
+2
-2
@@ -52,7 +52,7 @@ fn tartarus_recovers_ssn_on_halos_gate_hook() {
|
||||
let target = unsafe { copy_base.add(offset) };
|
||||
|
||||
// Plant `E9 XX XX XX XX` (jmp rel32) at offset 0. The actual rel32
|
||||
// value doesn't matter. we only execute up to extract_ssn's bytewise
|
||||
// value doesn't matter — we only execute up to extract_ssn's bytewise
|
||||
// inspection of the prologue.
|
||||
unsafe {
|
||||
*target = 0xE9;
|
||||
@@ -111,7 +111,7 @@ fn tartarus_recovers_ssn_on_post_mov_r10_rcx_hook() {
|
||||
fn tartarus_returns_none_on_unrecoverable_stub() {
|
||||
// Allocate a window full of 0xCC (INT3, definitely not a syscall stub).
|
||||
// Neither Hell's Gate prologue nor any neighbor stub exists, so
|
||||
// extract_ssn must return None. no hang, no panic.
|
||||
// extract_ssn must return None — no hang, no panic.
|
||||
let size = WINDOW_HALF * 2;
|
||||
let buf = alloc_rwx(size);
|
||||
unsafe { std::ptr::write_bytes(buf, 0xCC, size) };
|
||||
@@ -1,7 +1,7 @@
|
||||
// Exercises 1, 4, 6, and 11 argument calls through `syscall!`, confirming
|
||||
// the variadic gateway correctly shuffles registers, performs the
|
||||
// `rep movsq` slide for args 5+, and returns the real NTSTATUS to the
|
||||
// caller.
|
||||
// Variadic Hell's Hall dispatch regression — exercises 1, 4, 6, and 11
|
||||
// argument calls through `syscall!`, confirming the gateway correctly
|
||||
// shuffles registers, performs the `rep movsq` slide for args 5+, and
|
||||
// returns the real NTSTATUS to the caller.
|
||||
|
||||
use dyncvoke_core::syscall;
|
||||
use std::ffi::c_void;
|
||||
@@ -9,14 +9,15 @@ use std::ptr::null_mut;
|
||||
|
||||
#[test]
|
||||
fn one_arg_nt_close() {
|
||||
// Pure register dispatch, no stack copy. Verifies the r10 = arg1 setup.
|
||||
// Pure register dispatch — no stack copy. Verifies the r10 = arg1
|
||||
// setup.
|
||||
let status = syscall!("NtClose", 0xDEADBEEFusize as *mut c_void).expect("resolve");
|
||||
assert_eq!(status as u32, 0xC0000008, "expected STATUS_INVALID_HANDLE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn four_arg_nt_query_information_process() {
|
||||
// All four args in registers (r10, rdx, r8, r9), no stack copy still.
|
||||
// All four args in registers (r10, rdx, r8, r9) — no stack copy still.
|
||||
// ProcessBasicInformation = 0. Use the current process pseudo-handle.
|
||||
let mut pbi = [0u8; 48]; // PROCESS_BASIC_INFORMATION is 48 bytes on x64.
|
||||
let mut return_length: u32 = 0;
|
||||
@@ -52,7 +53,7 @@ fn six_arg_alloc_then_free() {
|
||||
assert_eq!(alloc_status, 0, "alloc NTSTATUS=0x{:08X}", alloc_status as u32);
|
||||
assert!(!base.is_null());
|
||||
|
||||
// Quick liveness probe, write and read back.
|
||||
// Quick liveness probe — write and read back.
|
||||
unsafe { *(base as *mut u32) = 0xCAFEBABE };
|
||||
let got = unsafe { *(base as *mut u32) };
|
||||
assert_eq!(got, 0xCAFEBABE);
|
||||
@@ -1,11 +1,12 @@
|
||||
// End-to-end smoke check covering the full syscall path:
|
||||
// End-to-end smoke test for the entire post-rewrite stack:
|
||||
//
|
||||
// syscall! macro
|
||||
// -> resolve_syscall (extract_ssn + get_syscall_address)
|
||||
// -> variadic do_syscall
|
||||
// -> jump into ntdll's `syscall` instruction
|
||||
// -> Tartarus Gate resolve_syscall (Phase 2)
|
||||
// -> extract_ssn(stub) and get_syscall_address(stub)
|
||||
// -> Hell's Hall variadic do_syscall (Phase 3)
|
||||
// -> jump into ntdll's `syscall` instruction
|
||||
//
|
||||
// Three syscalls in sequence:
|
||||
// Three real syscalls in sequence:
|
||||
//
|
||||
// 1. NtAllocateVirtualMemory -> get a writable page
|
||||
// 2. NtProtectVirtualMemory -> flip it to PAGE_EXECUTE_READ
|
||||
@@ -50,7 +51,7 @@ fn smoke_alloc_protect_free_round_trip() {
|
||||
}
|
||||
|
||||
// 2. reprotect to executable-read. The protect syscall takes a *mut PVOID
|
||||
// for the base and a *mut SIZE_T for the size, both inout.
|
||||
// for the base and a *mut SIZE_T for the size — both inout.
|
||||
let mut old_prot: u32 = 0;
|
||||
let prot_status = syscall!(
|
||||
"NtProtectVirtualMemory",
|
||||
@@ -91,9 +92,9 @@ fn smoke_alloc_protect_free_round_trip() {
|
||||
|
||||
#[test]
|
||||
fn smoke_do_syscall_macro_with_cached_resolution() {
|
||||
// Same dance, but resolve once and use the do_syscall! macro for
|
||||
// the hot path. Confirms the bypass macro lines up bit-for-bit with
|
||||
// the top-level syscall! macro.
|
||||
// Same dance, but resolve once and use the do_syscall! macro for the
|
||||
// hot path — confirms the bypass macro lines up bit-for-bit with the
|
||||
// top-level syscall! macro.
|
||||
use dyncvoke_core::{do_syscall, resolve_syscall};
|
||||
|
||||
let (alloc_ssn, alloc_addr) = resolve_syscall("NtAllocateVirtualMemory").unwrap();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// End-to-end check that the variadic do_syscall dispatches correctly.
|
||||
// Phase 3 acceptance — Hell's Hall variadic do_syscall works end-to-end.
|
||||
|
||||
use dyncvoke_core::{do_syscall, resolve_syscall};
|
||||
use std::ffi::c_void;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Resolves a handful of known syscalls via the byte-derived API and
|
||||
// Sanity check for the new sys module — Phase 2 acceptance.
|
||||
//
|
||||
// Resolves a handful of known syscalls via the new byte-derived API and
|
||||
// asserts (a) the SSNs match expected values for Win11 24H2, and (b) the
|
||||
// syscall instruction address found is exactly 18 bytes into the stub
|
||||
// (standard Hell's Gate stub layout) for clean stubs.
|
||||
@@ -9,9 +11,9 @@ use dyncvoke_core::{
|
||||
|
||||
#[test]
|
||||
fn resolve_known_syscalls() {
|
||||
// Expected SSNs on Win11 24H2 build 26200. If this test starts failing
|
||||
// on a different build, that's the kernel SSDT shifting, not a regression
|
||||
// in our resolver, but a useful signal.
|
||||
// Expected SSNs from audit_hellsgate.rs on Win11 24H2 build 26200.
|
||||
// If this test starts failing on a different build, that's the kernel
|
||||
// SSDT shifting — not a regression in our resolver, but a useful signal.
|
||||
for (name, expected_ssn) in &[
|
||||
("NtClose", 15u16),
|
||||
("NtAllocateVirtualMemory", 24),
|
||||
|
||||
Generated
+499
@@ -0,0 +1,499 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "data"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyncvoke_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"libc",
|
||||
"litcrypt2",
|
||||
"nanorand",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "litcrypt2"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4126aa57ac1b3dd20a5bc827a2972cdf74c619a4d6ae5660656408289e5bc60d"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "manualmap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"dyncvoke_core",
|
||||
"litcrypt2",
|
||||
"os_info",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-cloud-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-data"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-image"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-location"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-text"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block2",
|
||||
"libc",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block2",
|
||||
"objc2",
|
||||
"objc2-cloud-kit",
|
||||
"objc2-core-data",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-core-image",
|
||||
"objc2-core-location",
|
||||
"objc2-core-text",
|
||||
"objc2-foundation",
|
||||
"objc2-quartz-core",
|
||||
"objc2-user-notifications",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-user-notifications"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"log",
|
||||
"nix",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
"objc2-ui-kit",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@@ -9,7 +9,7 @@ use data::LARGE_INTEGER;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ffi::c_void;
|
||||
use std::mem::size_of;
|
||||
use std::{collections::HashMap, path::Path};
|
||||
use std::path::Path;
|
||||
use std::{fs, ptr};
|
||||
|
||||
use data::{RuntimeFunction, OBJECT_ATTRIBUTES, UNICODE_STRING};
|
||||
@@ -333,10 +333,10 @@ pub fn relocate_module(pe_info: &PeMetadata, image_ptr: *mut c_void) {
|
||||
image_delta = module_memory_base as isize - pe_info.opt_header_64.image_base as isize;
|
||||
}
|
||||
|
||||
// Guard: PE has its relocation directory stripped (fixed-base EXE,
|
||||
// or DLL built with /FIXED). Without this check the loop reads the
|
||||
// DOS header as an IMAGE_BASE_RELOCATION block and starts patching
|
||||
// memory at module_base + 0x00905A4D + reloc_patch.
|
||||
// Bug 4 guard: PE has its relocation directory stripped (fixed-base
|
||||
// EXE, or DLL built with /FIXED). Without this check the loop reads
|
||||
// the DOS header as an IMAGE_BASE_RELOCATION block and proceeds to
|
||||
// patch memory at module_base + 0x00905A4D + reloc_patch.
|
||||
if image_data_directory.VirtualAddress == 0 || image_data_directory.Size == 0 {
|
||||
return;
|
||||
}
|
||||
@@ -353,9 +353,9 @@ pub fn relocate_module(pe_info: &PeMetadata, image_ptr: *mut c_void) {
|
||||
let ibr: *mut IMAGE_BASE_RELOCATION = std::mem::transmute(reloc_table_ptr);
|
||||
let image_base_relocation = *ibr;
|
||||
|
||||
// Guard: a block claiming SizeOfBlock < header size has no
|
||||
// Bug 3 guard: a block claiming SizeOfBlock < header size has no
|
||||
// entries and advancing by it would loop forever (SizeOfBlock = 0)
|
||||
// or walk backwards. Treat as end-of-table.
|
||||
// or backwards. Treat as end-of-table.
|
||||
if image_base_relocation.VirtualAddress == 0
|
||||
|| (image_base_relocation.SizeOfBlock as usize)
|
||||
< size_of::<IMAGE_BASE_RELOCATION>()
|
||||
@@ -433,7 +433,8 @@ pub fn rewrite_module_iat(pe_info: &PeMetadata, image_ptr: *mut c_void) -> Resul
|
||||
|
||||
let info = os_info::get();
|
||||
let version = info.version().to_string();
|
||||
let mut api_set_dict: HashMap<String, String> = HashMap::new();
|
||||
let mut api_set_dict: std::collections::BTreeMap<String, String> =
|
||||
std::collections::BTreeMap::new();
|
||||
if version >= "10".to_string() {
|
||||
api_set_dict = dyncvoke_core::get_api_mapping();
|
||||
}
|
||||
|
||||
Generated
+675
@@ -0,0 +1,675 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "data"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyncvoke_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"libc",
|
||||
"litcrypt2",
|
||||
"nanorand",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"failure_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "litcrypt2"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4126aa57ac1b3dd20a5bc827a2972cdf74c619a4d6ae5660656408289e5bc60d"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "manualmap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"dyncvoke_core",
|
||||
"litcrypt2",
|
||||
"os_info",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-cloud-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-data"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-image"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-location"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-text"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"block2",
|
||||
"libc",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"block2",
|
||||
"objc2",
|
||||
"objc2-cloud-kit",
|
||||
"objc2-core-data",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-core-image",
|
||||
"objc2-core-location",
|
||||
"objc2-core-text",
|
||||
"objc2-foundation",
|
||||
"objc2-quartz-core",
|
||||
"objc2-user-notifications",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-user-notifications"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"log",
|
||||
"nix",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
"objc2-ui-kit",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"dyncvoke_core",
|
||||
"litcrypt2",
|
||||
"manualmap",
|
||||
"nanorand",
|
||||
"windows-sys 0.59.0",
|
||||
"winproc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
|
||||
|
||||
[[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 = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winproc"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "979e07b33c6af27e5c454e42d1b946b403ec222e7bad52ef020820708e087b25"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"failure",
|
||||
"widestring",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
+4
-8
@@ -837,9 +837,7 @@ fn relocate_text_section(
|
||||
image_delta = module_memory_base as isize - pe_info.opt_header_64.image_base as isize;
|
||||
}
|
||||
|
||||
// Guard: PE has no relocation directory (stripped, fixed-base).
|
||||
// Without this, the loop reads the DOS header as IMAGE_BASE_RELOCATION
|
||||
// and starts patching memory at module_base + 0x00905A4D + offset.
|
||||
// Bug 4 guard: no relocation directory at all.
|
||||
if image_data_directory.VirtualAddress == 0 || image_data_directory.Size == 0 {
|
||||
return;
|
||||
}
|
||||
@@ -856,9 +854,7 @@ fn relocate_text_section(
|
||||
let ibr: *mut IMAGE_BASE_RELOCATION = std::mem::transmute(reloc_table_ptr);
|
||||
let image_base_relocation = *ibr;
|
||||
|
||||
// Guard: malformed block with SizeOfBlock < header size has
|
||||
// no entries; advancing by it would spin (SizeOfBlock = 0) or
|
||||
// walk backwards. Treat as end-of-table.
|
||||
// Bug 3 guard.
|
||||
if image_base_relocation.VirtualAddress == 0
|
||||
|| (image_base_relocation.SizeOfBlock as usize)
|
||||
< size_of::<IMAGE_BASE_RELOCATION>()
|
||||
@@ -1044,8 +1040,8 @@ mod tests {
|
||||
fn test_module_handle_types() {
|
||||
use dyncvoke_core::INVALID_HANDLE_VALUE;
|
||||
|
||||
// Must be (HANDLE)(-1), the NtCurrentProcess pseudo-handle.
|
||||
// NULL is rejected by NT memory syscalls with STATUS_INVALID_HANDLE.
|
||||
// Bug 5 in AUDIT.md: must be (HANDLE)(-1), the NtCurrentProcess
|
||||
// pseudo-handle, not NULL.
|
||||
assert_eq!(INVALID_HANDLE_VALUE as isize, -1);
|
||||
|
||||
// Test valid handle would be non-zero
|
||||
|
||||
Generated
+222
@@ -0,0 +1,222 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitfield"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21ba6517c6b0f2bf08be60e187ab64b038438f22dd755614d8fe4d4098c46419"
|
||||
dependencies = [
|
||||
"bitfield-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitfield-macros"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"obfstr",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyncvoke_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"data",
|
||||
"libc",
|
||||
"nanorand",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4"
|
||||
|
||||
[[package]]
|
||||
name = "nasm-rs"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "706bf8a5e8c8ddb99128c3291d31bd21f4bcde17f0f4c20ec678d85c74faa149"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "obfstr"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "spoof"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitfield",
|
||||
"cc",
|
||||
"data",
|
||||
"dyncvoke_core",
|
||||
"memchr",
|
||||
"nasm-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "spoof"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Call stack spoofing primitives for Dyncvoke (synthetic + desync)."
|
||||
license = "MIT OR Apache-2.0"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
data = { path = "../data" }
|
||||
dyncvoke_core = { path = "../dyncvoke_core" }
|
||||
bitfield = "0.19"
|
||||
memchr = { version = "2.7", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.2"
|
||||
nasm-rs = "0.3"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
desync = []
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
if env::var("DOCS_RS").is_ok() {
|
||||
println!("cargo:warning=Skipping ASM build for docs.rs");
|
||||
return;
|
||||
}
|
||||
|
||||
let target = env::var("TARGET").expect("missing TARGET");
|
||||
let out_dir = env::var("OUT_DIR").expect("missing OUT_DIR");
|
||||
|
||||
if !target.contains("x86_64") {
|
||||
panic!("spoof crate only supports x86_64 targets (got {})", target);
|
||||
}
|
||||
|
||||
if target.contains("msvc") {
|
||||
cc::Build::new()
|
||||
.file("src/asm/msvc/synthetic.asm")
|
||||
.file("src/asm/msvc/desync.asm")
|
||||
.compile("spoof_asm");
|
||||
} else if target.contains("gnu") {
|
||||
let sources = ["src/asm/gnu/synthetic.asm", "src/asm/gnu/desync.asm"];
|
||||
nasm_rs::compile_library("spoof_asm", &sources)
|
||||
.unwrap_or_else(|e| panic!("nasm compile failed: {}", e));
|
||||
for s in &sources {
|
||||
println!("cargo:rerun-if-changed={}", s);
|
||||
}
|
||||
println!("cargo:rustc-link-search=native={}", out_dir);
|
||||
println!("cargo:rustc-link-lib=static=spoof_asm");
|
||||
} else {
|
||||
panic!("unsupported target: {}", target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
use data::lc;
|
||||
|
||||
fn main() {
|
||||
let canary = lc!("DYNCVOKE_CANARY_STRING_2026");
|
||||
let len = canary.len();
|
||||
std::process::exit(len as i32);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
use core::ffi::c_void;
|
||||
use core::ptr;
|
||||
|
||||
use data::lc;
|
||||
use spoof::{spoof_syscall, AsPointer};
|
||||
|
||||
fn main() {
|
||||
let mut addr: *mut c_void = ptr::null_mut();
|
||||
let mut size: usize = 0x1000;
|
||||
|
||||
let alloc = lc!("NtAllocateVirtualMemory");
|
||||
let status = unsafe {
|
||||
spoof_syscall!(
|
||||
alloc.as_str(),
|
||||
-1isize,
|
||||
addr.as_ptr_mut(),
|
||||
0usize,
|
||||
size.as_ptr_mut(),
|
||||
0x3000u32,
|
||||
0x04u32
|
||||
)
|
||||
};
|
||||
|
||||
let code = match status {
|
||||
Ok(_) => 0,
|
||||
Err(_) => 1,
|
||||
};
|
||||
std::process::exit(code);
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
;;
|
||||
;; Code responsible for Call Stack Spoofing Via Desync (NASM)
|
||||
;;
|
||||
[BITS 64]
|
||||
|
||||
;;
|
||||
;; Export
|
||||
;;
|
||||
GLOBAL Spoof
|
||||
|
||||
[SECTION .data]
|
||||
|
||||
;;
|
||||
;; Configuration structure passed to the spoof ASM routine
|
||||
;;
|
||||
STRUC Config
|
||||
.RtlUserThreadStartAddr RESQ 1
|
||||
.RtlUserThreadStartFrameSize RESQ 1
|
||||
|
||||
.BaseThreadInitThunkAddr RESQ 1
|
||||
.BaseThreadInitThunkFrameSize RESQ 1
|
||||
|
||||
.FirstFrame RESQ 1
|
||||
.SecondFrame RESQ 1
|
||||
.JmpRbxGadget RESQ 1
|
||||
.AddRspXGadget RESQ 1
|
||||
|
||||
.FirstFrameSize RESQ 1
|
||||
.SecondFrameSize RESQ 1
|
||||
.JmpRbxGadgetFrameSize RESQ 1
|
||||
.AddRspXGadgetFrameSize RESQ 1
|
||||
|
||||
.RbpOffset RESQ 1
|
||||
|
||||
.SpooFunction RESQ 1
|
||||
.ReturnAddress RESQ 1
|
||||
|
||||
.IsSyscall RESD 1
|
||||
.Ssn RESD 1
|
||||
|
||||
.NArgs RESQ 1
|
||||
.Arg01 RESQ 1
|
||||
.Arg02 RESQ 1
|
||||
.Arg03 RESQ 1
|
||||
.Arg04 RESQ 1
|
||||
.Arg05 RESQ 1
|
||||
.Arg06 RESQ 1
|
||||
.Arg07 RESQ 1
|
||||
.Arg08 RESQ 1
|
||||
.Arg09 RESQ 1
|
||||
.Arg10 RESQ 1
|
||||
.Arg11 RESQ 1
|
||||
ENDSTRUC
|
||||
|
||||
[SECTION .text]
|
||||
|
||||
;;
|
||||
;; Function responsible for Call Stack Spoofing
|
||||
;;
|
||||
Spoof:
|
||||
;;
|
||||
;; Saving non-vol registers
|
||||
;;
|
||||
push rbp
|
||||
push rbx
|
||||
|
||||
;;
|
||||
;; Return main
|
||||
;;
|
||||
mov rbp, rsp
|
||||
|
||||
;;
|
||||
;; Creating stack pointer to Restore PROC
|
||||
;;
|
||||
lea rax, [rel Restore]
|
||||
push rax
|
||||
lea rbx, [rsp]
|
||||
|
||||
;;
|
||||
;; First Frame (Fake origin)
|
||||
;;
|
||||
push QWORD [rcx + Config.FirstFrame]
|
||||
|
||||
mov rax, [rcx + Config.ReturnAddress]
|
||||
sub rax, [rcx + Config.FirstFrameSize]
|
||||
|
||||
sub rsp, [rcx + Config.SecondFrameSize]
|
||||
mov r10, [rcx + Config.RbpOffset]
|
||||
mov [rsp + r10], rax
|
||||
|
||||
;;
|
||||
;; ROP Frames
|
||||
;;
|
||||
push QWORD [rcx + Config.SecondFrame]
|
||||
|
||||
;;
|
||||
;; JMP [RBX] Gadget / Stack Pivot (To restore original Control Flow Stack)
|
||||
;;
|
||||
sub rsp, [rcx + Config.JmpRbxGadgetFrameSize]
|
||||
push QWORD [rcx + Config.JmpRbxGadget]
|
||||
|
||||
sub rsp, [rcx + Config.AddRspXGadgetFrameSize]
|
||||
push QWORD [rcx + Config.AddRspXGadget]
|
||||
|
||||
;;
|
||||
;; Set the pointer to the function to call in R11
|
||||
;;
|
||||
mov r11, [rcx + Config.SpooFunction]
|
||||
jmp Parameters
|
||||
|
||||
;;
|
||||
;; Set the parameters to pass to the target function
|
||||
;;
|
||||
Parameters:
|
||||
mov r12, rcx
|
||||
mov rax, [r12 + Config.NArgs]
|
||||
|
||||
; Arg01 (rcx)
|
||||
cmp rax, 1
|
||||
jb skip_1
|
||||
mov rcx, [r12 + Config.Arg01]
|
||||
|
||||
skip_1:
|
||||
; Arg02 (rdx)
|
||||
cmp rax, 2
|
||||
jb skip_2
|
||||
mov rdx, [r12 + Config.Arg02]
|
||||
|
||||
skip_2:
|
||||
; Arg03 (r8)
|
||||
cmp rax, 3
|
||||
jb skip_3
|
||||
mov r8, [r12 + Config.Arg03]
|
||||
|
||||
skip_3:
|
||||
; Arg04 (r9)
|
||||
cmp rax, 4
|
||||
jb skip_4
|
||||
mov r9, [r12 + Config.Arg04]
|
||||
|
||||
skip_4:
|
||||
; Stack-based args
|
||||
lea r13, [rsp]
|
||||
|
||||
cmp rax, 5
|
||||
jb skip_5
|
||||
mov r10, [r12 + Config.Arg05]
|
||||
mov [r13 + 28h], r10
|
||||
|
||||
skip_5:
|
||||
; Arg06
|
||||
cmp rax, 6
|
||||
jb skip_6
|
||||
mov r10, [r12 + Config.Arg06]
|
||||
mov [r13 + 30h], r10
|
||||
|
||||
skip_6:
|
||||
; Arg07
|
||||
cmp rax, 7
|
||||
jb skip_7
|
||||
mov r10, [r12 + Config.Arg07]
|
||||
mov [r13 + 38h], r10
|
||||
|
||||
skip_7:
|
||||
; Arg08
|
||||
cmp rax, 8
|
||||
jb skip_8
|
||||
mov r10, [r12 + Config.Arg08]
|
||||
mov [r13 + 40h], r10
|
||||
|
||||
skip_8:
|
||||
; Arg09
|
||||
cmp rax, 9
|
||||
jb skip_9
|
||||
mov r10, [r12 + Config.Arg09]
|
||||
mov [r13 + 48h], r10
|
||||
|
||||
skip_9:
|
||||
; Arg10
|
||||
cmp rax, 10
|
||||
jb skip_10
|
||||
mov r10, [r12 + Config.Arg10]
|
||||
mov [r13 + 50h], r10
|
||||
|
||||
skip_10:
|
||||
; Arg11
|
||||
cmp rax, 11
|
||||
jb skip_11
|
||||
mov r10, [r12 + Config.Arg11]
|
||||
mov [r13 + 58h], r10
|
||||
|
||||
skip_11:
|
||||
cmp BYTE [r12 + Config.IsSyscall], 1
|
||||
je ExecuteSyscall
|
||||
|
||||
jmp Execute
|
||||
|
||||
;;
|
||||
;; Restores the original stack frame
|
||||
;;
|
||||
Restore:
|
||||
mov rsp, rbp
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
;;
|
||||
;; Executes the target function
|
||||
;;
|
||||
Execute:
|
||||
jmp r11
|
||||
|
||||
;;
|
||||
;; Executes a native Windows system call using the spoofed context
|
||||
;;
|
||||
ExecuteSyscall:
|
||||
mov r10, rcx
|
||||
mov eax, DWORD [r12 + Config.Ssn]
|
||||
jmp r11
|
||||
@@ -0,0 +1,247 @@
|
||||
;;
|
||||
;; Code responsible for Call Stack Spoofing Via Synthetic (NASM)
|
||||
;;
|
||||
[BITS 64]
|
||||
|
||||
;;
|
||||
;; Export
|
||||
;;
|
||||
GLOBAL SpoofSynthetic
|
||||
|
||||
[SECTION .data]
|
||||
|
||||
;;
|
||||
;; Configuration structure passed to the spoof ASM routine
|
||||
;;
|
||||
STRUC Config
|
||||
.RtlUserThreadStartAddr RESQ 1
|
||||
.RtlUserThreadStartFrameSize RESQ 1
|
||||
|
||||
.BaseThreadInitThunkAddr RESQ 1
|
||||
.BaseThreadInitThunkFrameSize RESQ 1
|
||||
|
||||
.FirstFrame RESQ 1
|
||||
.SecondFrame RESQ 1
|
||||
.JmpRbxGadget RESQ 1
|
||||
.AddRspXGadget RESQ 1
|
||||
|
||||
.FirstFrameSize RESQ 1
|
||||
.SecondFrameSize RESQ 1
|
||||
.JmpRbxGadgetFrameSize RESQ 1
|
||||
.AddRspXGadgetFrameSize RESQ 1
|
||||
|
||||
.RbpOffset RESQ 1
|
||||
|
||||
.SpooFunction RESQ 1
|
||||
.ReturnAddress RESQ 1
|
||||
|
||||
.IsSyscall RESD 1
|
||||
.Ssn RESD 1
|
||||
|
||||
.NArgs RESQ 1
|
||||
.Arg01 RESQ 1
|
||||
.Arg02 RESQ 1
|
||||
.Arg03 RESQ 1
|
||||
.Arg04 RESQ 1
|
||||
.Arg05 RESQ 1
|
||||
.Arg06 RESQ 1
|
||||
.Arg07 RESQ 1
|
||||
.Arg08 RESQ 1
|
||||
.Arg09 RESQ 1
|
||||
.Arg10 RESQ 1
|
||||
.Arg11 RESQ 1
|
||||
ENDSTRUC
|
||||
|
||||
[SECTION .text]
|
||||
|
||||
;;
|
||||
;; Function responsible for Call Stack Spoofing
|
||||
;;
|
||||
SpoofSynthetic:
|
||||
;;
|
||||
;; Saving non-vol registers
|
||||
;;
|
||||
push QWORD rbp
|
||||
push QWORD rbx
|
||||
push QWORD r15
|
||||
|
||||
;;
|
||||
;; Everything between RSP and RBP is our new stack frame for unwinding
|
||||
;;
|
||||
sub rsp, 210h
|
||||
mov rbp, rsp
|
||||
|
||||
;;
|
||||
;; Creating stack pointer to Restore PROC
|
||||
;;
|
||||
lea rax, [rel RestoreSynthetic]
|
||||
push rax
|
||||
lea rbx, [rsp]
|
||||
|
||||
;;
|
||||
;; Cutting the call stack. The 0 pushed in this position will be the return address
|
||||
;; of the next frame "RtlUserThreadStart", making it effectively the originating function
|
||||
;;
|
||||
xor rax, rax
|
||||
push rax
|
||||
|
||||
;;
|
||||
;; RtlUserThreadStart
|
||||
;;
|
||||
sub rsp, [rcx + Config.RtlUserThreadStartFrameSize]
|
||||
push QWORD [rcx + Config.RtlUserThreadStartAddr]
|
||||
add QWORD [rsp], 21h
|
||||
|
||||
;;
|
||||
;; BaseThreadInitThunk
|
||||
;;
|
||||
sub rsp, [rcx + Config.BaseThreadInitThunkFrameSize]
|
||||
push QWORD [rcx + Config.BaseThreadInitThunkAddr]
|
||||
add QWORD [rsp], 14h
|
||||
|
||||
;;
|
||||
;; Return Adress
|
||||
;;
|
||||
mov rax, rsp
|
||||
|
||||
;;
|
||||
;; First Frame (Fake origin)
|
||||
;;
|
||||
push QWORD [rcx + Config.FirstFrame]
|
||||
sub rax, [rcx + Config.FirstFrameSize]
|
||||
|
||||
sub rsp, [rcx + Config.SecondFrameSize]
|
||||
mov r10, [rcx + Config.RbpOffset]
|
||||
mov [rsp + r10], rax
|
||||
|
||||
;;
|
||||
;; ROP Frames
|
||||
;;
|
||||
push QWORD [rcx + Config.SecondFrame]
|
||||
|
||||
;;
|
||||
;; JMP [RBX] Gadget / Stack Pivot (To restore original Control Flow Stack)
|
||||
;;
|
||||
sub rsp, [rcx + Config.JmpRbxGadgetFrameSize]
|
||||
push QWORD [rcx + Config.JmpRbxGadget]
|
||||
|
||||
sub rsp, [rcx + Config.AddRspXGadgetFrameSize]
|
||||
push QWORD [rcx + Config.AddRspXGadget]
|
||||
|
||||
;;
|
||||
;; Set the pointer to the function to call in R11
|
||||
;;
|
||||
mov r11, [rcx + Config.SpooFunction]
|
||||
jmp ParametersSynthetic
|
||||
|
||||
;;
|
||||
;; Set the parameters to pass to the target function
|
||||
;;
|
||||
ParametersSynthetic:
|
||||
mov r12, rcx
|
||||
mov rax, [r12 + Config.NArgs]
|
||||
|
||||
; Arg01 (rcx)
|
||||
cmp rax, 1
|
||||
jb skip_1
|
||||
mov rcx, [r12 + Config.Arg01]
|
||||
|
||||
skip_1:
|
||||
; Arg02 (rdx)
|
||||
cmp rax, 2
|
||||
jb skip_2
|
||||
mov rdx, [r12 + Config.Arg02]
|
||||
|
||||
skip_2:
|
||||
; Arg03 (r8)
|
||||
cmp rax, 3
|
||||
jb skip_3
|
||||
mov r8, [r12 + Config.Arg03]
|
||||
|
||||
skip_3:
|
||||
; Arg04 (r9)
|
||||
cmp rax, 4
|
||||
jb skip_4
|
||||
mov r9, [r12 + Config.Arg04]
|
||||
|
||||
skip_4:
|
||||
; Stack-based args
|
||||
lea r13, [rsp]
|
||||
|
||||
cmp rax, 5
|
||||
jb skip_5
|
||||
mov r10, [r12 + Config.Arg05]
|
||||
mov [r13 + 28h], r10
|
||||
|
||||
skip_5:
|
||||
; Arg06
|
||||
cmp rax, 6
|
||||
jb skip_6
|
||||
mov r10, [r12 + Config.Arg06]
|
||||
mov [r13 + 30h], r10
|
||||
|
||||
skip_6:
|
||||
; Arg07
|
||||
cmp rax, 7
|
||||
jb skip_7
|
||||
mov r10, [r12 + Config.Arg07]
|
||||
mov [r13 + 38h], r10
|
||||
|
||||
skip_7:
|
||||
; Arg08
|
||||
cmp rax, 8
|
||||
jb skip_8
|
||||
mov r10, [r12 + Config.Arg08]
|
||||
mov [r13 + 40h], r10
|
||||
|
||||
skip_8:
|
||||
; Arg09
|
||||
cmp rax, 9
|
||||
jb skip_9
|
||||
mov r10, [r12 + Config.Arg09]
|
||||
mov [r13 + 48h], r10
|
||||
|
||||
skip_9:
|
||||
; Arg10
|
||||
cmp rax, 10
|
||||
jb skip_10
|
||||
mov r10, [r12 + Config.Arg10]
|
||||
mov [r13 + 50h], r10
|
||||
|
||||
skip_10:
|
||||
; Arg11
|
||||
cmp rax, 11
|
||||
jb skip_11
|
||||
mov r10, [r12 + Config.Arg11]
|
||||
mov [r13 + 58h], r10
|
||||
|
||||
skip_11:
|
||||
cmp BYTE [r12 + Config.IsSyscall], 1
|
||||
je ExecuteSyscallSynthetic
|
||||
|
||||
jmp ExecuteSynthetic
|
||||
|
||||
;;
|
||||
;; Restores the original stack frame
|
||||
;;
|
||||
RestoreSynthetic:
|
||||
mov rsp, rbp
|
||||
add QWORD rsp, 210h
|
||||
pop QWORD r15
|
||||
pop QWORD rbx
|
||||
pop QWORD rbp
|
||||
ret
|
||||
|
||||
;;
|
||||
;; Executes the target function
|
||||
;;
|
||||
ExecuteSynthetic:
|
||||
jmp r11
|
||||
|
||||
;;
|
||||
;; Executes a native Windows system call using the spoofed context
|
||||
;;
|
||||
ExecuteSyscallSynthetic:
|
||||
mov r10, rcx
|
||||
mov eax, DWORD [r12 + Config.Ssn]
|
||||
jmp r11
|
||||
@@ -0,0 +1,225 @@
|
||||
;;
|
||||
;; Code responsible for Call Stack Spoofing Via Desync (MASM)
|
||||
;;
|
||||
|
||||
;;
|
||||
;; Export
|
||||
;;
|
||||
Spoof proto
|
||||
|
||||
.data
|
||||
|
||||
;;
|
||||
;; Configuration structure passed to the spoof ASM routine
|
||||
;;
|
||||
Config STRUCT
|
||||
RtlUserThreadStartAddr DQ 1
|
||||
RtlUserThreadStartFrameSize DQ 1
|
||||
|
||||
BaseThreadInitThunkAddr DQ 1
|
||||
BaseThreadInitThunkFrameSize DQ 1
|
||||
|
||||
FirstFrame DQ 1
|
||||
SecondFrame DQ 1
|
||||
JmpRbxGadget DQ 1
|
||||
AddRspXGadget DQ 1
|
||||
|
||||
FirstFrameSize DQ 1
|
||||
SecondFrameSize DQ 1
|
||||
JmpRbxGadgetFrameSize DQ 1
|
||||
AddRspXGadgetFrameSize DQ 1
|
||||
|
||||
RbpOffset DQ 1
|
||||
|
||||
SpooFunction DQ 1
|
||||
ReturnAddress DQ 1
|
||||
|
||||
IsSyscall DD 0
|
||||
Ssn DD 0
|
||||
|
||||
NArgs DQ 1
|
||||
Arg01 DQ 1
|
||||
Arg02 DQ 1
|
||||
Arg03 DQ 1
|
||||
Arg04 DQ 1
|
||||
Arg05 DQ 1
|
||||
Arg06 DQ 1
|
||||
Arg07 DQ 1
|
||||
Arg08 DQ 1
|
||||
Arg09 DQ 1
|
||||
Arg10 DQ 1
|
||||
Arg11 DQ 1
|
||||
Config ENDS
|
||||
|
||||
.code
|
||||
|
||||
;;
|
||||
;; Function responsible for Call Stack Spoofing
|
||||
;;
|
||||
Spoof PROC
|
||||
;;
|
||||
;; Saving non-vol registers
|
||||
;;
|
||||
push rbp
|
||||
push rbx
|
||||
|
||||
;;
|
||||
;; Return main
|
||||
;;
|
||||
mov rbp, rsp
|
||||
|
||||
;;
|
||||
;; Creating stack pointer to Restore PROC
|
||||
;;
|
||||
lea rax, Restore
|
||||
push rax
|
||||
lea rbx, [rsp]
|
||||
|
||||
;;
|
||||
;; First Frame (Fake origin)
|
||||
;;
|
||||
push [rcx].Config.FirstFrame
|
||||
|
||||
mov rax, [rcx].Config.ReturnAddresS
|
||||
sub rax, [rcx].Config.FirstFrameSize
|
||||
|
||||
sub rsp, [rcx].Config.SecondFrameSize
|
||||
mov r10, [rcx].Config.RbpOffset
|
||||
mov [rsp + r10], rax
|
||||
|
||||
;;
|
||||
;; ROP Frames
|
||||
;;
|
||||
push [rcx].Config.SecondFrame
|
||||
|
||||
;;
|
||||
;; JMP [RBX] Gadget / Stack Pivot (To restore original Control Flow Stack)
|
||||
;;
|
||||
sub rsp, [rcx].Config.JmpRbxGadgetFrameSize
|
||||
push [rcx].Config.JmpRbxGadget
|
||||
|
||||
sub rsp, [rcx].Config.AddRspXGadgetFrameSize
|
||||
push [rcx].Config.AddRspXGadget
|
||||
|
||||
;;
|
||||
;; Set the pointer to the function to call in R11
|
||||
;;
|
||||
mov r11, [rcx].Config.SpooFunction
|
||||
jmp Parameters
|
||||
Spoof ENDP
|
||||
|
||||
;;
|
||||
;; Set the parameters to pass to the target function
|
||||
;;
|
||||
Parameters PROC
|
||||
mov r12, rcx
|
||||
mov rax, [r12].Config.NArgs
|
||||
|
||||
; Arg01 (rcx)
|
||||
cmp rax, 1
|
||||
jb skip_1
|
||||
mov rcx, [r12].Config.Arg01
|
||||
|
||||
skip_1:
|
||||
; Arg02 (rdx)
|
||||
cmp rax, 2
|
||||
jb skip_2
|
||||
mov rdx, [r12].Config.Arg02
|
||||
|
||||
skip_2:
|
||||
; Arg03 (r8)
|
||||
cmp rax, 3
|
||||
jb skip_3
|
||||
mov r8, [r12].Config.Arg03
|
||||
|
||||
skip_3:
|
||||
; Arg04 (r9)
|
||||
cmp rax, 4
|
||||
jb skip_4
|
||||
mov r9, [r12].Config.Arg04
|
||||
|
||||
skip_4:
|
||||
; Stack-based args
|
||||
lea r13, [rsp]
|
||||
|
||||
cmp rax, 5
|
||||
jb skip_5
|
||||
mov r10, [r12].Config.Arg05
|
||||
mov [r13 + 28h], r10
|
||||
|
||||
skip_5:
|
||||
; Arg06
|
||||
cmp rax, 6
|
||||
jb skip_6
|
||||
mov r10, [r12].Config.Arg06
|
||||
mov [r13 + 30h], r10
|
||||
|
||||
skip_6:
|
||||
; Arg07
|
||||
cmp rax, 7
|
||||
jb skip_7
|
||||
mov r10, [r12].Config.Arg07
|
||||
mov [r13 + 38h], r10
|
||||
|
||||
skip_7:
|
||||
; Arg08
|
||||
cmp rax, 8
|
||||
jb skip_8
|
||||
mov r10, [r12].Config.Arg08
|
||||
mov [r13 + 40h], r10
|
||||
|
||||
skip_8:
|
||||
; Arg09
|
||||
cmp rax, 9
|
||||
jb skip_9
|
||||
mov r10, [r12].Config.Arg09
|
||||
mov [r13 + 48h], r10
|
||||
|
||||
skip_9:
|
||||
; Arg10
|
||||
cmp rax, 10
|
||||
jb skip_10
|
||||
mov r10, [r12].Config.Arg10
|
||||
mov [r13 + 50h], r10
|
||||
|
||||
skip_10:
|
||||
; Arg11
|
||||
cmp rax, 11
|
||||
jb skip_11
|
||||
mov r10, [r12].Config.Arg11
|
||||
mov [r13 + 58h], r10
|
||||
|
||||
skip_11:
|
||||
cmp [r12].Config.IsSyscall, 1
|
||||
je ExecuteSyscall
|
||||
|
||||
jmp Execute
|
||||
Parameters ENDP
|
||||
|
||||
;;
|
||||
;; Restores the original stack frame
|
||||
;;
|
||||
Restore PROC
|
||||
mov rsp, rbp
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Restore ENDP
|
||||
|
||||
;;
|
||||
;; Executes the target function
|
||||
;;
|
||||
Execute PROC
|
||||
jmp QWORD PTR r11
|
||||
Execute ENDP
|
||||
|
||||
;;
|
||||
;; Executes a native Windows system call using the spoofed context
|
||||
;;
|
||||
ExecuteSyscall PROC
|
||||
mov r10, rcx
|
||||
mov eax, [r12].Config.Ssn
|
||||
jmp QWORD PTR r11
|
||||
ExecuteSyscall ENDP
|
||||
|
||||
END
|
||||
@@ -0,0 +1,253 @@
|
||||
;;
|
||||
;; Code responsible for Call Stack Spoofing Via Synthetic (MASM)
|
||||
;;
|
||||
|
||||
;;
|
||||
;; Export
|
||||
;;
|
||||
SpoofSynthetic proto
|
||||
|
||||
.data
|
||||
|
||||
;;
|
||||
;; Configuration structure passed to the spoof ASM routine
|
||||
;;
|
||||
Config STRUCT
|
||||
RtlUserThreadStartAddr DQ 1
|
||||
RtlUserThreadStartFrameSize DQ 1
|
||||
|
||||
BaseThreadInitThunkAddr DQ 1
|
||||
BaseThreadInitThunkFrameSize DQ 1
|
||||
|
||||
FirstFrame DQ 1
|
||||
SecondFrame DQ 1
|
||||
JmpRbxGadget DQ 1
|
||||
AddRspXGadget DQ 1
|
||||
|
||||
FirstFrameSize DQ 1
|
||||
SecondFrameSize DQ 1
|
||||
JmpRbxGadgetFrameSize DQ 1
|
||||
AddRspXGadgetFrameSize DQ 1
|
||||
|
||||
RbpOffset DQ 1
|
||||
|
||||
SpooFunction DQ 1
|
||||
ReturnAddress DQ 1
|
||||
|
||||
IsSyscall DD 0
|
||||
Ssn DD 0
|
||||
|
||||
NArgs DQ 1
|
||||
Arg01 DQ 1
|
||||
Arg02 DQ 1
|
||||
Arg03 DQ 1
|
||||
Arg04 DQ 1
|
||||
Arg05 DQ 1
|
||||
Arg06 DQ 1
|
||||
Arg07 DQ 1
|
||||
Arg08 DQ 1
|
||||
Arg09 DQ 1
|
||||
Arg10 DQ 1
|
||||
Arg11 DQ 1
|
||||
Config ENDS
|
||||
|
||||
.code
|
||||
|
||||
;;
|
||||
;; Function responsible for Call Stack Spoofing
|
||||
;;
|
||||
SpoofSynthetic PROC
|
||||
;;
|
||||
;; Saving non-vol registers
|
||||
;;
|
||||
push rbp
|
||||
push rbx
|
||||
push r15
|
||||
|
||||
;;
|
||||
;; Everything between RSP and RBP is our new stack frame for unwinding
|
||||
;;
|
||||
sub rsp, 210h
|
||||
mov rbp, rsp
|
||||
|
||||
;;
|
||||
;; Creating stack pointer to Restore PROC
|
||||
;;
|
||||
lea rax, RestoreSynthetic
|
||||
push rax
|
||||
lea rbx, [rsp]
|
||||
|
||||
;;
|
||||
;; Cutting the call stack. The 0 pushed in this position will be the return address
|
||||
;; of the next frame "RtlUserThreadStart", making it effectively the originating function
|
||||
;;
|
||||
xor rax, rax
|
||||
push rax
|
||||
|
||||
;;
|
||||
;; RtlUserThreadStart
|
||||
;;
|
||||
sub rsp, [rcx].Config.RtlUserThreadStartFrameSize
|
||||
push [rcx].Config.RtlUserThreadStartAddr
|
||||
add QWORD PTR [rsp], 21h
|
||||
|
||||
;;
|
||||
;; BaseThreadInitThunk
|
||||
;;
|
||||
sub rsp, [rcx].Config.BaseThreadInitThunkFrameSize
|
||||
push [rcx].Config.BaseThreadInitThunkAddr
|
||||
add QWORD PTR [rsp], 14h
|
||||
|
||||
;;
|
||||
;; Return address
|
||||
;;
|
||||
mov rax, rsp
|
||||
|
||||
;;
|
||||
;; First Frame (Fake origin)
|
||||
;;
|
||||
push [rcx].Config.FirstFrame
|
||||
sub rax, [rcx].Config.FirstFrameSize
|
||||
|
||||
sub rsp, [rcx].Config.SecondFrameSize
|
||||
mov r10, [rcx].Config.RbpOffset
|
||||
mov [rsp + r10], rax
|
||||
|
||||
;;
|
||||
;; ROP Frames
|
||||
;;
|
||||
push [rcx].Config.SecondFrame
|
||||
|
||||
;;
|
||||
;; JMP [RBX] Gadget / Stack Pivot (To restore original Control Flow Stack)
|
||||
;;
|
||||
sub rsp, [rcx].Config.JmpRbxGadgetFrameSize
|
||||
push [rcx].Config.JmpRbxGadget
|
||||
|
||||
sub rsp, [rcx].Config.AddRspXGadgetFrameSize
|
||||
push [rcx].Config.AddRspXGadget
|
||||
|
||||
;;
|
||||
;; Set the pointer to the function to call in R11
|
||||
;;
|
||||
mov r11, [rcx].Config.SpooFunction
|
||||
jmp ParametersSynthetic
|
||||
SpoofSynthetic ENDP
|
||||
|
||||
;;
|
||||
;; Set the parameters to pass to the target function
|
||||
;;
|
||||
ParametersSynthetic PROC
|
||||
mov r12, rcx
|
||||
mov rax, [r12].Config.NArgs
|
||||
|
||||
; Arg01 (rcx)
|
||||
cmp rax, 1
|
||||
jb skip_1
|
||||
mov rcx, [r12].Config.Arg01
|
||||
|
||||
skip_1:
|
||||
; Arg02 (rdx)
|
||||
cmp rax, 2
|
||||
jb skip_2
|
||||
mov rdx, [r12].Config.Arg02
|
||||
|
||||
skip_2:
|
||||
; Arg03 (r8)
|
||||
cmp rax, 3
|
||||
jb skip_3
|
||||
mov r8, [r12].Config.Arg03
|
||||
|
||||
skip_3:
|
||||
; Arg04 (r9)
|
||||
cmp rax, 4
|
||||
jb skip_4
|
||||
mov r9, [r12].Config.Arg04
|
||||
|
||||
skip_4:
|
||||
; Stack-based args
|
||||
lea r13, [rsp]
|
||||
|
||||
cmp rax, 5
|
||||
jb skip_5
|
||||
mov r10, [r12].Config.Arg05
|
||||
mov [r13 + 28h], r10
|
||||
|
||||
skip_5:
|
||||
; Arg06
|
||||
cmp rax, 6
|
||||
jb skip_6
|
||||
mov r10, [r12].Config.Arg06
|
||||
mov [r13 + 30h], r10
|
||||
|
||||
skip_6:
|
||||
; Arg07
|
||||
cmp rax, 7
|
||||
jb skip_7
|
||||
mov r10, [r12].Config.Arg07
|
||||
mov [r13 + 38h], r10
|
||||
|
||||
skip_7:
|
||||
; Arg08
|
||||
cmp rax, 8
|
||||
jb skip_8
|
||||
mov r10, [r12].Config.Arg08
|
||||
mov [r13 + 40h], r10
|
||||
|
||||
skip_8:
|
||||
; Arg09
|
||||
cmp rax, 9
|
||||
jb skip_9
|
||||
mov r10, [r12].Config.Arg09
|
||||
mov [r13 + 48h], r10
|
||||
|
||||
skip_9:
|
||||
; Arg10
|
||||
cmp rax, 10
|
||||
jb skip_10
|
||||
mov r10, [r12].Config.Arg10
|
||||
mov [r13 + 50h], r10
|
||||
|
||||
skip_10:
|
||||
; Arg11
|
||||
cmp rax, 11
|
||||
jb skip_11
|
||||
mov r10, [r12].Config.Arg11
|
||||
mov [r13 + 58h], r10
|
||||
|
||||
skip_11:
|
||||
cmp [r12].Config.IsSyscall, 1
|
||||
je ExecuteSyscallSynthetic
|
||||
|
||||
jmp ExecuteSynthetic
|
||||
ParametersSynthetic ENDP
|
||||
|
||||
;;
|
||||
;; Restores the original stack frame
|
||||
;;
|
||||
RestoreSynthetic PROC
|
||||
mov rsp, rbp
|
||||
add rsp, 210h
|
||||
pop r15
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
RestoreSynthetic ENDP
|
||||
|
||||
;;
|
||||
;; Executes the target function
|
||||
;;
|
||||
ExecuteSynthetic PROC
|
||||
jmp QWORD PTR r11
|
||||
ExecuteSynthetic ENDP
|
||||
|
||||
;;
|
||||
;; Executes a native Windows system call using the spoofed context
|
||||
;;
|
||||
ExecuteSyscallSynthetic PROC
|
||||
mov r10, rcx
|
||||
mov eax, [r12].Config.Ssn
|
||||
jmp QWORD PTR r11
|
||||
ExecuteSyscallSynthetic ENDP
|
||||
|
||||
END
|
||||
@@ -0,0 +1,515 @@
|
||||
//! Call stack spoofing for Dyncvoke.
|
||||
//!
|
||||
//! Two modes, picked at compile time:
|
||||
//!
|
||||
//! - **Synthetic** (default): build a fake call stack rooted at
|
||||
//! `RtlUserThreadStart -> BaseThreadInitThunk -> ...`. Works from any
|
||||
//! thread including pool threads. Selected when the `desync` feature is
|
||||
//! off.
|
||||
//!
|
||||
//! - **Desync** (`desync` feature): locate an existing
|
||||
//! `BaseThreadInitThunk` return address on the current thread's real
|
||||
//! stack and splice spoofed frames on top. Most realistic-looking stack,
|
||||
//! but only works on threads that started through the normal user-thread
|
||||
//! chain.
|
||||
//!
|
||||
//! The trampoline implementations live in `src/asm/{msvc,gnu}/*.asm`. They
|
||||
//! read the `Config` struct laid out in [`types::Config`] — do not reorder
|
||||
//! its fields.
|
||||
//!
|
||||
//! SSN resolution for `spoof_syscall!` uses dyncvoke_core's existing
|
||||
//! Tartarus Gate (`dyncvoke_core::resolve_syscall`). No second resolver,
|
||||
//! no second PE parser for module/function lookup — those come from
|
||||
//! dyncvoke_core too.
|
||||
|
||||
#![no_std]
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::ffi::c_void;
|
||||
|
||||
use data::lc;
|
||||
|
||||
pub mod pe;
|
||||
pub mod types;
|
||||
pub mod unwind;
|
||||
pub mod util;
|
||||
|
||||
use crate::pe::{function_by_rva, runtime_function_table};
|
||||
use crate::types::{Config, ImageRuntimeFunction};
|
||||
use crate::unwind::{rbp_offset, stack_frame};
|
||||
#[cfg(not(feature = "desync"))]
|
||||
use crate::unwind::ignoring_set_fpreg;
|
||||
use crate::util::{find_gadget, find_valid_instruction_offset, shuffle};
|
||||
|
||||
#[cfg(feature = "desync")]
|
||||
use crate::util::find_base_thread_return_address;
|
||||
|
||||
// Compile-time-selected asm trampoline. Synthetic mode calls SpoofSynthetic,
|
||||
// desync calls Spoof. The asm files compile both — the linker drops the
|
||||
// unused symbol.
|
||||
#[cfg(feature = "desync")]
|
||||
unsafe extern "C" {
|
||||
fn Spoof(config: &mut Config) -> *mut c_void;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "desync"))]
|
||||
unsafe extern "C" {
|
||||
fn SpoofSynthetic(config: &mut Config) -> *mut c_void;
|
||||
}
|
||||
|
||||
/// Spoof a direct function call. Resolves the spoofing scaffolding once
|
||||
/// and then jumps to `addr` with the supplied args under a fake stack.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let kernel32 = dyncvoke_core::get_module_base_address("kernel32.dll");
|
||||
/// let virtual_alloc = dyncvoke_core::get_function_address(kernel32, "VirtualAlloc");
|
||||
/// let addr = spoof::spoof!(
|
||||
/// virtual_alloc,
|
||||
/// core::ptr::null_mut::<core::ffi::c_void>(),
|
||||
/// 1 << 12,
|
||||
/// 0x3000u32,
|
||||
/// 0x04u32
|
||||
/// )?;
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! spoof {
|
||||
($addr:expr, $($arg:expr),+ $(,)?) => {
|
||||
unsafe {
|
||||
$crate::__private::spoof(
|
||||
$addr as *mut ::core::ffi::c_void,
|
||||
&[$(::core::mem::transmute($arg as usize)),*],
|
||||
$crate::SpoofKind::Function,
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Spoof an indirect syscall. SSN is resolved via dyncvoke_core's Tartarus
|
||||
/// Gate, then the syscall instruction inside ntdll is dispatched under a
|
||||
/// fake stack.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// use spoof::{spoof_syscall, AsPointer};
|
||||
/// let mut addr = core::ptr::null_mut::<core::ffi::c_void>();
|
||||
/// let mut size = (1 << 12) as usize;
|
||||
/// let status = spoof_syscall!(
|
||||
/// "NtAllocateVirtualMemory",
|
||||
/// -1isize,
|
||||
/// addr.as_ptr_mut(),
|
||||
/// 0usize,
|
||||
/// size.as_ptr_mut(),
|
||||
/// 0x3000u32,
|
||||
/// 0x04u32
|
||||
/// )? as i32;
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! spoof_syscall {
|
||||
($name:expr, $($arg:expr),* $(,)?) => {
|
||||
unsafe {
|
||||
$crate::__private::spoof(
|
||||
::core::ptr::null_mut(),
|
||||
&[$(::core::mem::transmute($arg as usize)),*],
|
||||
$crate::SpoofKind::Syscall($name),
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Selects which mode the public entry uses.
|
||||
pub enum SpoofKind<'a> {
|
||||
/// Spoof a direct function call.
|
||||
Function,
|
||||
/// Spoof an indirect syscall by ntdll export name.
|
||||
Syscall(&'a str),
|
||||
}
|
||||
|
||||
/// Where a failure happened. Carried by SpoofError variants as a non-string
|
||||
/// tag so the error path doesn't leak plaintext module/function names into
|
||||
/// `.rdata`. Display impl decrypts via obfstr at format time only.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum SpoofTag {
|
||||
Kernelbase,
|
||||
Kernel32,
|
||||
Ntdll,
|
||||
RtlUserThreadStart,
|
||||
BaseThreadInitThunk,
|
||||
AddRspGadget,
|
||||
JmpRbxGadget,
|
||||
}
|
||||
|
||||
/// Spoofing errors. Variants carry [`SpoofTag`] rather than literal strings.
|
||||
#[derive(Debug)]
|
||||
pub enum SpoofError {
|
||||
TooManyArguments,
|
||||
NullFunctionAddress,
|
||||
ModuleNotLoaded(SpoofTag),
|
||||
FunctionNotFound(SpoofTag),
|
||||
PdataMissing(SpoofTag),
|
||||
UnwindInfoMissing(SpoofTag),
|
||||
PrologueNotFound,
|
||||
PushRbpPrologueNotFound,
|
||||
GadgetNotFound(SpoofTag),
|
||||
BaseThreadReturnNotFound,
|
||||
SsnResolutionFailed,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for SpoofError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let tag_str = |t: SpoofTag| match t {
|
||||
SpoofTag::Kernelbase => lc!("kernelbase.dll"),
|
||||
SpoofTag::Kernel32 => lc!("kernel32.dll"),
|
||||
SpoofTag::Ntdll => lc!("ntdll.dll"),
|
||||
SpoofTag::RtlUserThreadStart => lc!("RtlUserThreadStart"),
|
||||
SpoofTag::BaseThreadInitThunk => lc!("BaseThreadInitThunk"),
|
||||
SpoofTag::AddRspGadget => lc!("add rsp,0x58; ret"),
|
||||
SpoofTag::JmpRbxGadget => lc!("jmp [rbx]"),
|
||||
};
|
||||
match self {
|
||||
SpoofError::TooManyArguments => write!(f, "{}", lc!("too many arguments")),
|
||||
SpoofError::NullFunctionAddress => write!(f, "{}", lc!("null function address")),
|
||||
SpoofError::ModuleNotLoaded(t) => write!(f, "{}: {}", lc!("module"), tag_str(*t)),
|
||||
SpoofError::FunctionNotFound(t) => write!(f, "{}: {}", lc!("export"), tag_str(*t)),
|
||||
SpoofError::PdataMissing(t) => write!(f, "{}: {}", lc!("pdata"), tag_str(*t)),
|
||||
SpoofError::UnwindInfoMissing(t) => write!(f, "{}: {}", lc!("unwind"), tag_str(*t)),
|
||||
SpoofError::PrologueNotFound => write!(f, "{}", lc!("no prologue")),
|
||||
SpoofError::PushRbpPrologueNotFound => write!(f, "{}", lc!("no rbp prologue")),
|
||||
SpoofError::GadgetNotFound(t) => write!(f, "{}: {}", lc!("gadget"), tag_str(*t)),
|
||||
SpoofError::BaseThreadReturnNotFound => write!(f, "{}", lc!("no thread return")),
|
||||
SpoofError::SsnResolutionFailed => write!(f, "{}", lc!("ssn fail")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::error::Error for SpoofError {}
|
||||
|
||||
/// AsPointer — uwd's ergonomic trait. Lets call sites write `addr.as_ptr_mut()`
|
||||
/// in macro args instead of `&mut addr as *mut _ as *mut c_void`.
|
||||
pub trait AsPointer {
|
||||
fn as_ptr_const(&self) -> *const c_void;
|
||||
fn as_ptr_mut(&mut self) -> *mut c_void;
|
||||
}
|
||||
|
||||
impl<T> AsPointer for T {
|
||||
#[inline(always)]
|
||||
fn as_ptr_const(&self) -> *const c_void {
|
||||
self as *const _ as *const c_void
|
||||
}
|
||||
#[inline(always)]
|
||||
fn as_ptr_mut(&mut self) -> *mut c_void {
|
||||
self as *mut _ as *mut c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod __private {
|
||||
use super::*;
|
||||
#[allow(unused_imports)]
|
||||
use alloc::string::String;
|
||||
|
||||
/// Synthetic-mode entry. Builds the full scaffolding from scratch and
|
||||
/// hands it to the asm trampoline. Works on any thread.
|
||||
#[cfg(not(feature = "desync"))]
|
||||
pub unsafe fn spoof(
|
||||
addr: *mut c_void,
|
||||
args: &[*const c_void],
|
||||
kind: SpoofKind,
|
||||
) -> Result<*mut c_void, SpoofError> {
|
||||
if args.len() > 11 {
|
||||
return Err(SpoofError::TooManyArguments);
|
||||
}
|
||||
if matches!(kind, SpoofKind::Function) && addr.is_null() {
|
||||
return Err(SpoofError::NullFunctionAddress);
|
||||
}
|
||||
|
||||
let mut config = Config::default();
|
||||
|
||||
let kernelbase = dyncvoke_core::get_module_base_address(&lc!("kernelbase.dll"));
|
||||
if kernelbase == 0 {
|
||||
return Err(SpoofError::ModuleNotLoaded(SpoofTag::Kernelbase));
|
||||
}
|
||||
let ntdll = dyncvoke_core::get_module_base_address(&lc!("ntdll.dll"));
|
||||
if ntdll == 0 {
|
||||
return Err(SpoofError::ModuleNotLoaded(SpoofTag::Ntdll));
|
||||
}
|
||||
let kernel32 = dyncvoke_core::get_module_base_address(&lc!("kernel32.dll"));
|
||||
if kernel32 == 0 {
|
||||
return Err(SpoofError::ModuleNotLoaded(SpoofTag::Kernel32));
|
||||
}
|
||||
|
||||
let rtl_user_addr =
|
||||
dyncvoke_core::get_function_address(ntdll, &lc!("RtlUserThreadStart"));
|
||||
if rtl_user_addr == 0 {
|
||||
return Err(SpoofError::FunctionNotFound(SpoofTag::RtlUserThreadStart));
|
||||
}
|
||||
let base_thread_addr =
|
||||
dyncvoke_core::get_function_address(kernel32, &lc!("BaseThreadInitThunk"));
|
||||
if base_thread_addr == 0 {
|
||||
return Err(SpoofError::FunctionNotFound(SpoofTag::BaseThreadInitThunk));
|
||||
}
|
||||
|
||||
config.rtl_user_addr = rtl_user_addr as *const c_void;
|
||||
config.base_thread_addr = base_thread_addr as *const c_void;
|
||||
|
||||
let kb_table = runtime_function_table(kernelbase as *mut c_void)
|
||||
.ok_or(SpoofError::PdataMissing(SpoofTag::Kernelbase))?;
|
||||
let ntdll_table = runtime_function_table(ntdll as *mut c_void)
|
||||
.ok_or(SpoofError::PdataMissing(SpoofTag::Ntdll))?;
|
||||
let k32_table = runtime_function_table(kernel32 as *mut c_void)
|
||||
.ok_or(SpoofError::PdataMissing(SpoofTag::Kernel32))?;
|
||||
|
||||
let rtl_user_runtime =
|
||||
function_by_rva(ntdll_table, (rtl_user_addr - ntdll) as u32)
|
||||
.ok_or(SpoofError::UnwindInfoMissing(SpoofTag::RtlUserThreadStart))?;
|
||||
let base_thread_runtime =
|
||||
function_by_rva(k32_table, (base_thread_addr - kernel32) as u32)
|
||||
.ok_or(SpoofError::UnwindInfoMissing(SpoofTag::BaseThreadInitThunk))?;
|
||||
|
||||
config.rtl_user_thread_size =
|
||||
ignoring_set_fpreg(ntdll as *mut c_void, rtl_user_runtime)
|
||||
.ok_or(SpoofError::UnwindInfoMissing(SpoofTag::RtlUserThreadStart))? as u64;
|
||||
config.base_thread_size =
|
||||
ignoring_set_fpreg(kernel32 as *mut c_void, base_thread_runtime)
|
||||
.ok_or(SpoofError::UnwindInfoMissing(SpoofTag::BaseThreadInitThunk))? as u64;
|
||||
|
||||
let first_prolog = find_prolog(kernelbase as *mut c_void, kb_table)
|
||||
.ok_or(SpoofError::PrologueNotFound)?;
|
||||
config.first_frame_fp =
|
||||
(first_prolog.frame + first_prolog.offset as u64) as *const c_void;
|
||||
config.first_frame_size = first_prolog.stack_size as u64;
|
||||
|
||||
let second_prolog = find_push_rbp(kernelbase as *mut c_void, kb_table)
|
||||
.ok_or(SpoofError::PushRbpPrologueNotFound)?;
|
||||
config.second_frame_fp =
|
||||
(second_prolog.frame + second_prolog.offset as u64) as *const c_void;
|
||||
config.second_frame_size = second_prolog.stack_size as u64;
|
||||
config.rbp_stack_offset = second_prolog.rbp_offset as u64;
|
||||
|
||||
let (add_rsp_addr, size) = find_gadget(
|
||||
kernelbase as *mut c_void,
|
||||
&[0x48, 0x83, 0xC4, 0x58, 0xC3],
|
||||
kb_table,
|
||||
)
|
||||
.ok_or(SpoofError::GadgetNotFound(SpoofTag::AddRspGadget))?;
|
||||
config.add_rsp_gadget = add_rsp_addr as *const c_void;
|
||||
config.add_rsp_frame_size = size as u64;
|
||||
|
||||
let (jmp_rbx_addr, size) =
|
||||
find_gadget(kernelbase as *mut c_void, &[0xFF, 0x23], kb_table)
|
||||
.ok_or(SpoofError::GadgetNotFound(SpoofTag::JmpRbxGadget))?;
|
||||
config.jmp_rbx_gadget = jmp_rbx_addr as *const c_void;
|
||||
config.jmp_rbx_frame_size = size as u64;
|
||||
|
||||
config.number_args = args.len() as u64;
|
||||
write_args(&mut config, args);
|
||||
|
||||
match kind {
|
||||
SpoofKind::Function => config.spoof_function = addr as *const c_void,
|
||||
SpoofKind::Syscall(name) => {
|
||||
let (ssn, syscall_addr) = dyncvoke_core::resolve_syscall(name)
|
||||
.map_err(|_| SpoofError::SsnResolutionFailed)?;
|
||||
config.is_syscall = 1;
|
||||
config.ssn = ssn as u32;
|
||||
config.spoof_function = syscall_addr as *const c_void;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SpoofSynthetic(&mut config))
|
||||
}
|
||||
|
||||
/// Desync-mode entry. Reuses the current thread's real
|
||||
/// BaseThreadInitThunk return record. Won't work on pool threads.
|
||||
#[cfg(feature = "desync")]
|
||||
pub unsafe fn spoof(
|
||||
addr: *mut c_void,
|
||||
args: &[*const c_void],
|
||||
kind: SpoofKind,
|
||||
) -> Result<*mut c_void, SpoofError> {
|
||||
if args.len() > 11 {
|
||||
return Err(SpoofError::TooManyArguments);
|
||||
}
|
||||
if matches!(kind, SpoofKind::Function) && addr.is_null() {
|
||||
return Err(SpoofError::NullFunctionAddress);
|
||||
}
|
||||
|
||||
let mut config = Config::default();
|
||||
|
||||
let kernelbase = dyncvoke_core::get_module_base_address(&lc!("kernelbase.dll"));
|
||||
if kernelbase == 0 {
|
||||
return Err(SpoofError::ModuleNotLoaded(SpoofTag::Kernelbase));
|
||||
}
|
||||
let kernel32 = dyncvoke_core::get_module_base_address(&lc!("kernel32.dll"));
|
||||
if kernel32 == 0 {
|
||||
return Err(SpoofError::ModuleNotLoaded(SpoofTag::Kernel32));
|
||||
}
|
||||
|
||||
let base_thread_addr =
|
||||
dyncvoke_core::get_function_address(kernel32, &lc!("BaseThreadInitThunk"));
|
||||
if base_thread_addr == 0 {
|
||||
return Err(SpoofError::FunctionNotFound(SpoofTag::BaseThreadInitThunk));
|
||||
}
|
||||
|
||||
let k32_table = runtime_function_table(kernel32 as *mut c_void)
|
||||
.ok_or(SpoofError::PdataMissing(SpoofTag::Kernel32))?;
|
||||
let base_thread_runtime =
|
||||
function_by_rva(k32_table, (base_thread_addr - kernel32) as u32)
|
||||
.ok_or(SpoofError::UnwindInfoMissing(SpoofTag::BaseThreadInitThunk))?;
|
||||
|
||||
let base_thread_size =
|
||||
(base_thread_runtime.EndAddress - base_thread_runtime.BeginAddress) as usize;
|
||||
let stack_slot = find_base_thread_return_address(
|
||||
kernel32 as *mut c_void,
|
||||
base_thread_addr as *mut c_void,
|
||||
base_thread_size,
|
||||
)
|
||||
.ok_or(SpoofError::BaseThreadReturnNotFound)?;
|
||||
config.return_address = stack_slot as *const c_void;
|
||||
|
||||
let kb_table = runtime_function_table(kernelbase as *mut c_void)
|
||||
.ok_or(SpoofError::PdataMissing(SpoofTag::Kernelbase))?;
|
||||
|
||||
let first_prolog = find_prolog(kernelbase as *mut c_void, kb_table)
|
||||
.ok_or(SpoofError::PrologueNotFound)?;
|
||||
config.first_frame_fp =
|
||||
(first_prolog.frame + first_prolog.offset as u64) as *const c_void;
|
||||
config.first_frame_size = first_prolog.stack_size as u64;
|
||||
|
||||
let second_prolog = find_push_rbp(kernelbase as *mut c_void, kb_table)
|
||||
.ok_or(SpoofError::PushRbpPrologueNotFound)?;
|
||||
config.second_frame_fp =
|
||||
(second_prolog.frame + second_prolog.offset as u64) as *const c_void;
|
||||
config.second_frame_size = second_prolog.stack_size as u64;
|
||||
config.rbp_stack_offset = second_prolog.rbp_offset as u64;
|
||||
|
||||
let (add_rsp_addr, size) = find_gadget(
|
||||
kernelbase as *mut c_void,
|
||||
&[0x48, 0x83, 0xC4, 0x58, 0xC3],
|
||||
kb_table,
|
||||
)
|
||||
.ok_or(SpoofError::GadgetNotFound(SpoofTag::AddRspGadget))?;
|
||||
config.add_rsp_gadget = add_rsp_addr as *const c_void;
|
||||
config.add_rsp_frame_size = size as u64;
|
||||
|
||||
let (jmp_rbx_addr, size) =
|
||||
find_gadget(kernelbase as *mut c_void, &[0xFF, 0x23], kb_table)
|
||||
.ok_or(SpoofError::GadgetNotFound(SpoofTag::JmpRbxGadget))?;
|
||||
config.jmp_rbx_gadget = jmp_rbx_addr as *const c_void;
|
||||
config.jmp_rbx_frame_size = size as u64;
|
||||
|
||||
config.number_args = args.len() as u64;
|
||||
write_args(&mut config, args);
|
||||
|
||||
match kind {
|
||||
SpoofKind::Function => config.spoof_function = addr as *const c_void,
|
||||
SpoofKind::Syscall(name) => {
|
||||
let (ssn, syscall_addr) = dyncvoke_core::resolve_syscall(name)
|
||||
.map_err(|_| SpoofError::SsnResolutionFailed)?;
|
||||
config.is_syscall = 1;
|
||||
config.ssn = ssn as u32;
|
||||
config.spoof_function = syscall_addr as *const c_void;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Spoof(&mut config))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_args(cfg: &mut Config, args: &[*const c_void]) {
|
||||
for (i, &a) in args.iter().enumerate() {
|
||||
match i {
|
||||
0 => cfg.arg01 = a,
|
||||
1 => cfg.arg02 = a,
|
||||
2 => cfg.arg03 = a,
|
||||
3 => cfg.arg04 = a,
|
||||
4 => cfg.arg05 = a,
|
||||
5 => cfg.arg06 = a,
|
||||
6 => cfg.arg07 = a,
|
||||
7 => cfg.arg08 = a,
|
||||
8 => cfg.arg09 = a,
|
||||
9 => cfg.arg10 = a,
|
||||
10 => cfg.arg11 = a,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Selected decoy frame metadata.
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct Prolog {
|
||||
frame: u64,
|
||||
stack_size: u32,
|
||||
offset: u32,
|
||||
rbp_offset: u32,
|
||||
}
|
||||
|
||||
/// Scan kernelbase's runtime function table for a function whose unwind
|
||||
/// info describes a spoof-compatible RSP-based frame. Shuffles the survivors
|
||||
/// and returns the first one — picks a different decoy each run.
|
||||
fn find_prolog(module_base: *mut c_void, runtime_table: &[ImageRuntimeFunction]) -> Option<Prolog> {
|
||||
let mut prologs: Vec<Prolog> = runtime_table
|
||||
.iter()
|
||||
.filter_map(|runtime| {
|
||||
let (is_valid, stack_size) = unsafe { stack_frame(module_base, runtime) }?;
|
||||
if !is_valid {
|
||||
return None;
|
||||
}
|
||||
let offset = find_valid_instruction_offset(module_base, runtime)?;
|
||||
let frame = module_base as u64 + runtime.BeginAddress as u64;
|
||||
Some(Prolog {
|
||||
frame,
|
||||
stack_size,
|
||||
offset,
|
||||
..Default::default()
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
if prologs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
shuffle(&mut prologs);
|
||||
prologs.first().copied()
|
||||
}
|
||||
|
||||
/// Same idea but for an RBP-pushing prologue — these are needed for the
|
||||
/// inner spoofed frame where the unwinder will reconstruct rbp from the
|
||||
/// stored slot.
|
||||
fn find_push_rbp(
|
||||
module_base: *mut c_void,
|
||||
runtime_table: &[ImageRuntimeFunction],
|
||||
) -> Option<Prolog> {
|
||||
let mut prologs: Vec<Prolog> = runtime_table
|
||||
.iter()
|
||||
.filter_map(|runtime| {
|
||||
let (rbp_off, stack_size) = unsafe { rbp_offset(module_base, runtime) }?;
|
||||
if rbp_off == 0 || stack_size == 0 || stack_size <= rbp_off {
|
||||
return None;
|
||||
}
|
||||
let offset = find_valid_instruction_offset(module_base, runtime)?;
|
||||
let frame = module_base as u64 + runtime.BeginAddress as u64;
|
||||
Some(Prolog {
|
||||
frame,
|
||||
stack_size,
|
||||
offset,
|
||||
rbp_offset: rbp_off,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
if prologs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
// First match is consistently unsuitable on most Windows builds; drop it.
|
||||
prologs.remove(0);
|
||||
if prologs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
shuffle(&mut prologs);
|
||||
prologs.first().copied()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//! Minimal PE walker — just enough to locate the .pdata RUNTIME_FUNCTION
|
||||
//! table for a loaded module. We deliberately don't reuse dyncvoke_core's PE
|
||||
//! parser because that one was built for manual mapping (full optional-header
|
||||
//! deserialization, etc.). For spoofing we only need the data-directory at
|
||||
//! IMAGE_DIRECTORY_ENTRY_EXCEPTION.
|
||||
|
||||
use core::ffi::c_void;
|
||||
use core::slice;
|
||||
|
||||
use crate::types::{ImageDataDirectory, ImageRuntimeFunction, IMAGE_DIRECTORY_ENTRY_EXCEPTION};
|
||||
|
||||
/// Read the exception data directory's runtime function table for a loaded
|
||||
/// module. Returns None if the headers are malformed or the directory is
|
||||
/// empty.
|
||||
pub unsafe fn runtime_function_table<'a>(
|
||||
module_base: *mut c_void,
|
||||
) -> Option<&'a [ImageRuntimeFunction]> {
|
||||
let base = module_base as *const u8;
|
||||
|
||||
// PE signature offset lives at DOS header offset 0x3C.
|
||||
let nt_offset = *(base.add(0x3C) as *const i32);
|
||||
if nt_offset <= 0 {
|
||||
return None;
|
||||
}
|
||||
let nt_header = base.add(nt_offset as usize);
|
||||
|
||||
// "PE\0\0" signature check.
|
||||
if *(nt_header as *const u32) != 0x0000_4550 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Magic byte at NT+0x18 distinguishes PE32 (0x10b) from PE32+ (0x20b).
|
||||
// We only support x64 / PE32+ here.
|
||||
let optional_header = nt_header.add(0x18);
|
||||
let magic = *(optional_header as *const u16);
|
||||
if magic != 0x020b {
|
||||
return None;
|
||||
}
|
||||
|
||||
// DataDirectory[] sits at optional_header+0x70 on PE32+. Each entry is 8
|
||||
// bytes (VA + Size).
|
||||
let data_dirs_base = optional_header.add(0x70);
|
||||
let dir_ptr = data_dirs_base
|
||||
.add(IMAGE_DIRECTORY_ENTRY_EXCEPTION * size_of::<ImageDataDirectory>())
|
||||
as *const ImageDataDirectory;
|
||||
let dir = *dir_ptr;
|
||||
|
||||
if dir.VirtualAddress == 0 || dir.Size == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let table_addr = (module_base as usize + dir.VirtualAddress as usize) as *const ImageRuntimeFunction;
|
||||
let count = dir.Size as usize / size_of::<ImageRuntimeFunction>();
|
||||
|
||||
Some(slice::from_raw_parts(table_addr, count))
|
||||
}
|
||||
|
||||
/// Find the [`ImageRuntimeFunction`] whose BeginAddress matches `rva`.
|
||||
pub fn function_by_rva<'a>(
|
||||
table: &'a [ImageRuntimeFunction],
|
||||
rva: u32,
|
||||
) -> Option<&'a ImageRuntimeFunction> {
|
||||
table.iter().find(|f| f.BeginAddress == rva)
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#![allow(non_snake_case, non_camel_case_types)]
|
||||
|
||||
use core::ffi::c_void;
|
||||
|
||||
/// Indicates the presence of an exception handler in the function.
|
||||
pub const UNW_FLAG_EHANDLER: u8 = 0x1;
|
||||
|
||||
/// Indicates chained unwind information is present.
|
||||
pub const UNW_FLAG_CHAININFO: u8 = 0x4;
|
||||
|
||||
/// IMAGE_DIRECTORY_ENTRY_EXCEPTION — index into the optional header's
|
||||
/// DataDirectory[] where the .pdata RUNTIME_FUNCTION table lives.
|
||||
pub const IMAGE_DIRECTORY_ENTRY_EXCEPTION: usize = 3;
|
||||
|
||||
/// IMAGE_RUNTIME_FUNCTION_ENTRY — one entry of the .pdata exception table.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct ImageRuntimeFunction {
|
||||
pub BeginAddress: u32,
|
||||
pub EndAddress: u32,
|
||||
pub UnwindData: u32,
|
||||
}
|
||||
|
||||
/// IMAGE_DATA_DIRECTORY — VA + size pair used by the optional header.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct ImageDataDirectory {
|
||||
pub VirtualAddress: u32,
|
||||
pub Size: u32,
|
||||
}
|
||||
|
||||
/// Config struct read by the asm trampoline. Layout must match
|
||||
/// `src/asm/{msvc,gnu}/synthetic.asm` and `desync.asm` byte-for-byte.
|
||||
/// Field reordering will corrupt argument passing — do not touch.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
pub rtl_user_addr: *const c_void,
|
||||
pub rtl_user_thread_size: u64,
|
||||
pub base_thread_addr: *const c_void,
|
||||
pub base_thread_size: u64,
|
||||
pub first_frame_fp: *const c_void,
|
||||
pub second_frame_fp: *const c_void,
|
||||
pub jmp_rbx_gadget: *const c_void,
|
||||
pub add_rsp_gadget: *const c_void,
|
||||
pub first_frame_size: u64,
|
||||
pub second_frame_size: u64,
|
||||
pub jmp_rbx_frame_size: u64,
|
||||
pub add_rsp_frame_size: u64,
|
||||
pub rbp_stack_offset: u64,
|
||||
pub spoof_function: *const c_void,
|
||||
pub return_address: *const c_void,
|
||||
pub is_syscall: u32,
|
||||
pub ssn: u32,
|
||||
pub number_args: u64,
|
||||
pub arg01: *const c_void,
|
||||
pub arg02: *const c_void,
|
||||
pub arg03: *const c_void,
|
||||
pub arg04: *const c_void,
|
||||
pub arg05: *const c_void,
|
||||
pub arg06: *const c_void,
|
||||
pub arg07: *const c_void,
|
||||
pub arg08: *const c_void,
|
||||
pub arg09: *const c_void,
|
||||
pub arg10: *const c_void,
|
||||
pub arg11: *const c_void,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
unsafe { core::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
/// x86_64 general-purpose register encoding used by unwind codes.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Registers {
|
||||
Rax = 0,
|
||||
Rcx,
|
||||
Rdx,
|
||||
Rbx,
|
||||
Rsp,
|
||||
Rbp,
|
||||
Rsi,
|
||||
Rdi,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15,
|
||||
}
|
||||
|
||||
impl PartialEq<usize> for Registers {
|
||||
fn eq(&self, other: &usize) -> bool {
|
||||
*self as usize == *other
|
||||
}
|
||||
}
|
||||
|
||||
/// One unwind code slot. The full union: either a packed 16-bit slot
|
||||
/// (CodeOffset/UnwindOp/OpInfo) or a raw FrameOffset for following slots.
|
||||
#[repr(C)]
|
||||
pub union UnwindCode {
|
||||
pub FrameOffset: u16,
|
||||
pub Anonymous: UnwindCode0,
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct UnwindCode0(u16);
|
||||
pub u8, CodeOffset, SetCodeOffset: 7, 0;
|
||||
pub u8, UnwindOp, SetUnwindOp: 11, 8;
|
||||
pub u8, OpInfo, SetOpInfo: 15, 12;
|
||||
}
|
||||
|
||||
/// Optional Exception/ChainInfo slot in UNWIND_INFO.
|
||||
#[repr(C)]
|
||||
pub union UnwindInfo0 {
|
||||
pub ExceptionHandler: u32,
|
||||
pub FunctionEntry: u32,
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct UnwindVersionFlags(u8);
|
||||
pub u8, Version, SetVersion: 2, 0;
|
||||
pub u8, Flags, SetFlags: 7, 3;
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct UnwindFrameInfo(u8);
|
||||
pub u8, FrameRegister, SetFrameRegister: 3, 0;
|
||||
pub u8, FrameOffset, SetFrameOffset: 7, 4;
|
||||
}
|
||||
|
||||
/// UNWIND_INFO header at the front of every function's unwind record.
|
||||
#[repr(C)]
|
||||
pub struct UnwindInfo {
|
||||
pub VersionFlags: UnwindVersionFlags,
|
||||
pub SizeOfProlog: u8,
|
||||
pub CountOfCodes: u8,
|
||||
pub FrameInfo: UnwindFrameInfo,
|
||||
pub UnwindCode: UnwindCode,
|
||||
pub Anonymous: UnwindInfo0,
|
||||
pub ExceptionData: u32,
|
||||
}
|
||||
|
||||
/// x64 unwind operation codes (subset we care about for stack-size
|
||||
/// calculation). MS docs: learn.microsoft.com/cpp/build/exception-handling-x64
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
pub enum UnwindOpCode {
|
||||
UWOP_PUSH_NONVOL = 0,
|
||||
UWOP_ALLOC_LARGE = 1,
|
||||
UWOP_ALLOC_SMALL = 2,
|
||||
UWOP_SET_FPREG = 3,
|
||||
UWOP_SAVE_NONVOL = 4,
|
||||
UWOP_SAVE_NONVOL_BIG = 5,
|
||||
UWOP_EPILOG = 6,
|
||||
UWOP_SPARE_CODE = 7,
|
||||
UWOP_SAVE_XMM128 = 8,
|
||||
UWOP_SAVE_XMM128BIG = 9,
|
||||
UWOP_PUSH_MACH_FRAME = 10,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for UnwindOpCode {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0..=10 => Ok(unsafe { core::mem::transmute::<u8, UnwindOpCode>(value) }),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
//! Walk UNWIND_INFO records to derive stack sizes and RBP offsets. Direct
|
||||
//! port of uwd's three walkers (rbp_offset, stack_frame, ignoring_set_fpreg),
|
||||
//! kept verbatim because their correctness was verified against live ntdll /
|
||||
//! kernelbase / kernel32 prologues.
|
||||
|
||||
use core::ffi::c_void;
|
||||
|
||||
use crate::types::{
|
||||
ImageRuntimeFunction, Registers, UnwindCode, UnwindInfo, UnwindOpCode::*,
|
||||
UnwindOpCode, UNW_FLAG_CHAININFO, UNW_FLAG_EHANDLER,
|
||||
};
|
||||
|
||||
/// Determine RBP push location and total stack size. Returns None if the
|
||||
/// frame layout isn't spoof-compatible (RSP saved as nonvol, RBP pushed
|
||||
/// twice, etc.).
|
||||
pub unsafe fn rbp_offset(
|
||||
module: *mut c_void,
|
||||
runtime: &ImageRuntimeFunction,
|
||||
) -> Option<(u32, u32)> {
|
||||
let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UnwindInfo;
|
||||
let unwind_code = (unwind_info as *mut u8).add(4) as *mut UnwindCode;
|
||||
let flag = (*unwind_info).VersionFlags.Flags();
|
||||
|
||||
let mut i = 0usize;
|
||||
let mut total_stack = 0u32;
|
||||
let mut rbp_pushed = false;
|
||||
let mut stack_offset = 0u32;
|
||||
|
||||
while i < (*unwind_info).CountOfCodes as usize {
|
||||
let unwind_code = unwind_code.add(i);
|
||||
let op_info = (*unwind_code).Anonymous.OpInfo() as usize;
|
||||
let unwind_op = (*unwind_code).Anonymous.UnwindOp();
|
||||
|
||||
match UnwindOpCode::try_from(unwind_op) {
|
||||
Ok(UWOP_PUSH_NONVOL) => {
|
||||
if Registers::Rsp == op_info {
|
||||
return None;
|
||||
}
|
||||
if Registers::Rbp == op_info {
|
||||
if rbp_pushed {
|
||||
return None;
|
||||
}
|
||||
rbp_pushed = true;
|
||||
stack_offset = total_stack;
|
||||
}
|
||||
total_stack += 8;
|
||||
i += 1;
|
||||
}
|
||||
Ok(UWOP_ALLOC_LARGE) => {
|
||||
if (*unwind_code).Anonymous.OpInfo() == 0 {
|
||||
let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;
|
||||
total_stack += frame_offset as u32;
|
||||
i += 2;
|
||||
} else {
|
||||
let frame_offset = *(unwind_code.add(1) as *mut i32);
|
||||
total_stack += frame_offset as u32;
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
Ok(UWOP_ALLOC_SMALL) => {
|
||||
total_stack += ((op_info + 1) * 8) as u32;
|
||||
i += 1;
|
||||
}
|
||||
Ok(UWOP_SAVE_NONVOL) => {
|
||||
if Registers::Rsp == op_info {
|
||||
return None;
|
||||
}
|
||||
if Registers::Rbp == op_info {
|
||||
if rbp_pushed {
|
||||
return None;
|
||||
}
|
||||
let offset = (*unwind_code.add(1)).FrameOffset * 8;
|
||||
stack_offset = total_stack + offset as u32;
|
||||
rbp_pushed = true;
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
Ok(UWOP_SAVE_NONVOL_BIG) => {
|
||||
if Registers::Rsp == op_info {
|
||||
return None;
|
||||
}
|
||||
if Registers::Rbp == op_info {
|
||||
if rbp_pushed {
|
||||
return None;
|
||||
}
|
||||
let offset = *(unwind_code.add(1) as *mut u32);
|
||||
stack_offset = total_stack + offset;
|
||||
rbp_pushed = true;
|
||||
}
|
||||
i += 3;
|
||||
}
|
||||
Ok(UWOP_SET_FPREG) => return None,
|
||||
Ok(UWOP_SAVE_XMM128) => i += 2,
|
||||
Ok(UWOP_SAVE_XMM128BIG) => i += 3,
|
||||
Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,
|
||||
Ok(UWOP_PUSH_MACH_FRAME) => {
|
||||
total_stack += if op_info == 0 { 0x40 } else { 0x48 };
|
||||
i += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & UNW_FLAG_CHAININFO) != 0 {
|
||||
let count = (*unwind_info).CountOfCodes as usize;
|
||||
let index = if count & 1 == 1 { count + 1 } else { count };
|
||||
let runtime = unwind_code.add(index) as *const ImageRuntimeFunction;
|
||||
if let Some((_, child_total)) = rbp_offset(module, &*runtime) {
|
||||
total_stack += child_total;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some((stack_offset, total_stack))
|
||||
}
|
||||
|
||||
/// Compute total stack size, rejecting any frame that uses RBP/RSP as a
|
||||
/// non-volatile save target. Used when picking the first decoy prologue.
|
||||
pub unsafe fn stack_frame(
|
||||
module: *mut c_void,
|
||||
runtime: &ImageRuntimeFunction,
|
||||
) -> Option<(bool, u32)> {
|
||||
let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UnwindInfo;
|
||||
let unwind_code = (unwind_info as *mut u8).add(4) as *mut UnwindCode;
|
||||
let flag = (*unwind_info).VersionFlags.Flags();
|
||||
|
||||
let mut i = 0usize;
|
||||
let mut set_fpreg_hit = false;
|
||||
let mut total_stack = 0i32;
|
||||
|
||||
while i < (*unwind_info).CountOfCodes as usize {
|
||||
let unwind_code = unwind_code.add(i);
|
||||
let op_info = (*unwind_code).Anonymous.OpInfo() as usize;
|
||||
let unwind_op = (*unwind_code).Anonymous.UnwindOp();
|
||||
|
||||
match UnwindOpCode::try_from(unwind_op) {
|
||||
Ok(UWOP_PUSH_NONVOL) => {
|
||||
if Registers::Rsp == op_info && !set_fpreg_hit {
|
||||
return None;
|
||||
}
|
||||
total_stack += 8;
|
||||
i += 1;
|
||||
}
|
||||
Ok(UWOP_ALLOC_SMALL) => {
|
||||
total_stack += ((op_info + 1) * 8) as i32;
|
||||
i += 1;
|
||||
}
|
||||
Ok(UWOP_ALLOC_LARGE) => {
|
||||
if (*unwind_code).Anonymous.OpInfo() == 0 {
|
||||
let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;
|
||||
total_stack += frame_offset;
|
||||
i += 2;
|
||||
} else {
|
||||
let frame_offset = *(unwind_code.add(1) as *mut i32);
|
||||
total_stack += frame_offset;
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
Ok(UWOP_SAVE_NONVOL) => {
|
||||
if Registers::Rsp == op_info || Registers::Rbp == op_info {
|
||||
return None;
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
Ok(UWOP_SAVE_NONVOL_BIG) => {
|
||||
if Registers::Rsp == op_info || Registers::Rbp == op_info {
|
||||
return None;
|
||||
}
|
||||
i += 3;
|
||||
}
|
||||
Ok(UWOP_SAVE_XMM128) => i += 2,
|
||||
Ok(UWOP_SAVE_XMM128BIG) => i += 3,
|
||||
Ok(UWOP_SET_FPREG) => {
|
||||
if (flag & UNW_FLAG_EHANDLER) != 0 && (flag & UNW_FLAG_CHAININFO) != 0 {
|
||||
return None;
|
||||
}
|
||||
if (*unwind_info).FrameInfo.FrameRegister() != Registers::Rbp as u8 {
|
||||
return None;
|
||||
}
|
||||
set_fpreg_hit = true;
|
||||
let offset = ((*unwind_info).FrameInfo.FrameOffset() as i32) << 4;
|
||||
total_stack -= offset;
|
||||
i += 1;
|
||||
}
|
||||
Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,
|
||||
Ok(UWOP_PUSH_MACH_FRAME) => {
|
||||
total_stack += if op_info == 0 { 0x40 } else { 0x48 };
|
||||
i += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & UNW_FLAG_CHAININFO) != 0 {
|
||||
let count = (*unwind_info).CountOfCodes as usize;
|
||||
let index = if count & 1 == 1 { count + 1 } else { count };
|
||||
let runtime = unwind_code.add(index) as *const ImageRuntimeFunction;
|
||||
if let Some((chained_fpreg_hit, chained_stack)) = stack_frame(module, &*runtime) {
|
||||
total_stack += chained_stack as i32;
|
||||
set_fpreg_hit |= chained_fpreg_hit;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some((set_fpreg_hit, total_stack as u32))
|
||||
}
|
||||
|
||||
/// Total stack size, treating set_fpreg as a no-op. Used for sizing the
|
||||
/// frames we splice in (RtlUserThreadStart, BaseThreadInitThunk, gadget
|
||||
/// frames).
|
||||
pub unsafe fn ignoring_set_fpreg(
|
||||
module: *mut c_void,
|
||||
runtime: &ImageRuntimeFunction,
|
||||
) -> Option<u32> {
|
||||
let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UnwindInfo;
|
||||
let unwind_code = (unwind_info as *mut u8).add(4) as *mut UnwindCode;
|
||||
let flag = (*unwind_info).VersionFlags.Flags();
|
||||
|
||||
let mut i = 0usize;
|
||||
let mut total_stack = 0u32;
|
||||
|
||||
while i < (*unwind_info).CountOfCodes as usize {
|
||||
let unwind_code = unwind_code.add(i);
|
||||
let op_info = (*unwind_code).Anonymous.OpInfo() as usize;
|
||||
let unwind_op = (*unwind_code).Anonymous.UnwindOp();
|
||||
|
||||
match UnwindOpCode::try_from(unwind_op) {
|
||||
Ok(UWOP_PUSH_NONVOL) => {
|
||||
if Registers::Rsp == op_info {
|
||||
return None;
|
||||
}
|
||||
total_stack += 8;
|
||||
i += 1;
|
||||
}
|
||||
Ok(UWOP_ALLOC_SMALL) => {
|
||||
total_stack += ((op_info + 1) * 8) as u32;
|
||||
i += 1;
|
||||
}
|
||||
Ok(UWOP_ALLOC_LARGE) => {
|
||||
if (*unwind_code).Anonymous.OpInfo() == 0 {
|
||||
let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;
|
||||
total_stack += frame_offset as u32;
|
||||
i += 2;
|
||||
} else {
|
||||
let frame_offset = *(unwind_code.add(1) as *mut i32);
|
||||
total_stack += frame_offset as u32;
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
Ok(UWOP_SAVE_NONVOL) => {
|
||||
if Registers::Rsp == op_info {
|
||||
return None;
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
Ok(UWOP_SAVE_NONVOL_BIG) => {
|
||||
if Registers::Rsp == op_info {
|
||||
return None;
|
||||
}
|
||||
i += 3;
|
||||
}
|
||||
Ok(UWOP_SAVE_XMM128) => i += 2,
|
||||
Ok(UWOP_SAVE_XMM128BIG) => i += 3,
|
||||
Ok(UWOP_SET_FPREG) => i += 1,
|
||||
Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,
|
||||
Ok(UWOP_PUSH_MACH_FRAME) => {
|
||||
total_stack += if op_info == 0 { 0x40 } else { 0x48 };
|
||||
i += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & UNW_FLAG_CHAININFO) != 0 {
|
||||
let count = (*unwind_info).CountOfCodes as usize;
|
||||
let index = if count & 1 == 1 { count + 1 } else { count };
|
||||
let runtime = unwind_code.add(index) as *const ImageRuntimeFunction;
|
||||
if let Some(chained_stack) = ignoring_set_fpreg(module, &*runtime) {
|
||||
total_stack += chained_stack;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(total_stack)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
use alloc::vec::Vec;
|
||||
use core::{ffi::c_void, slice};
|
||||
|
||||
use crate::types::ImageRuntimeFunction;
|
||||
use crate::unwind::ignoring_set_fpreg;
|
||||
|
||||
/// Scan a function body for `call qword ptr [rip+0]` (48 FF 15 00 00 00 00).
|
||||
/// Returns the offset *past* the gadget — that's the address the unwinder
|
||||
/// will treat as the post-call site when the spoofed frame is on the stack.
|
||||
pub fn find_valid_instruction_offset(
|
||||
module: *mut c_void,
|
||||
runtime: &ImageRuntimeFunction,
|
||||
) -> Option<u32> {
|
||||
let start = module as u64 + runtime.BeginAddress as u64;
|
||||
let end = module as u64 + runtime.EndAddress as u64;
|
||||
let size = end - start;
|
||||
let pattern = &[0x48u8, 0xFFu8, 0x15u8];
|
||||
|
||||
unsafe {
|
||||
let bytes = slice::from_raw_parts(start as *const u8, size as usize);
|
||||
memchr::memmem::find(bytes, pattern).map(|pos| (pos + 7) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the first occurrence of `pattern` in any function body of `module`
|
||||
/// (restricted to ranges from the RUNTIME_FUNCTION table — anything else
|
||||
/// might be data or alignment padding). Returns the address and the size of
|
||||
/// the function that contains it.
|
||||
pub fn find_gadget(
|
||||
module: *mut c_void,
|
||||
pattern: &[u8],
|
||||
runtime_table: &[ImageRuntimeFunction],
|
||||
) -> Option<(*mut u8, u32)> {
|
||||
unsafe {
|
||||
let mut gadgets: Vec<(*mut u8, u32)> = runtime_table
|
||||
.iter()
|
||||
.filter_map(|runtime| {
|
||||
let start = module as u64 + runtime.BeginAddress as u64;
|
||||
let end = module as u64 + runtime.EndAddress as u64;
|
||||
let size = end.saturating_sub(start);
|
||||
|
||||
let bytes = slice::from_raw_parts(start as *const u8, size as usize);
|
||||
let pos = memchr::memmem::find(bytes, pattern)?;
|
||||
let addr = (start as *mut u8).wrapping_add(pos);
|
||||
let frame_size = ignoring_set_fpreg(module, runtime)?;
|
||||
if frame_size == 0 {
|
||||
return None;
|
||||
}
|
||||
Some((addr, frame_size))
|
||||
})
|
||||
.collect();
|
||||
|
||||
if gadgets.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
shuffle(&mut gadgets);
|
||||
gadgets.first().copied()
|
||||
}
|
||||
}
|
||||
|
||||
/// Fisher-Yates shuffle using rdtsc as a cheap entropy source. Same approach
|
||||
/// uwd uses — the goal isn't cryptographic randomness, it's reducing the
|
||||
/// chance that an EDR signature pinned to a specific gadget address survives
|
||||
/// across runs.
|
||||
pub fn shuffle<T>(list: &mut [T]) {
|
||||
let mut seed = unsafe { core::arch::x86_64::_rdtsc() };
|
||||
for i in (1..list.len()).rev() {
|
||||
seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
|
||||
let j = seed as usize % (i + 1);
|
||||
list.swap(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
/// Walk the current thread's stack looking for a return address that lands
|
||||
/// inside the BaseThreadInitThunk function body. Used by desync mode to
|
||||
/// splice our spoofed frames on top of a legitimately-resident kernel32
|
||||
/// return record.
|
||||
///
|
||||
/// Returns the stack address holding the return, not the return value
|
||||
/// itself — the asm trampoline rewrites the slot in place.
|
||||
#[cfg(feature = "desync")]
|
||||
pub unsafe fn find_base_thread_return_address(
|
||||
kernel32: *mut c_void,
|
||||
base_thread_addr: *mut c_void,
|
||||
base_thread_size: usize,
|
||||
) -> Option<usize> {
|
||||
let _ = kernel32;
|
||||
let base_addr = base_thread_addr as usize;
|
||||
|
||||
// Read TEB.StackBase (gs:[0x08]) and TEB.StackLimit (gs:[0x10]) directly
|
||||
// — same OPSEC reason we use the PEB walker in dyncvoke_core: no
|
||||
// kernel32/psapi calls, nothing to hook.
|
||||
let stack_base: usize;
|
||||
let stack_limit: usize;
|
||||
core::arch::asm!(
|
||||
"mov {sb}, gs:[0x08]",
|
||||
"mov {sl}, gs:[0x10]",
|
||||
sb = out(reg) stack_base,
|
||||
sl = out(reg) stack_limit,
|
||||
options(nostack, preserves_flags, readonly),
|
||||
);
|
||||
|
||||
let mut rsp = stack_base.saturating_sub(8);
|
||||
while rsp >= stack_limit {
|
||||
let val = *(rsp as *const usize);
|
||||
if val >= base_addr && val < base_addr + base_thread_size {
|
||||
return Some(rsp);
|
||||
}
|
||||
rsp = rsp.saturating_sub(8);
|
||||
if rsp == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//! Desync-mode spoofed syscall. Only enabled under the desync feature.
|
||||
//! Verifies the BaseThreadInitThunk return-address splicing path.
|
||||
|
||||
#![cfg(feature = "desync")]
|
||||
|
||||
use core::ffi::c_void;
|
||||
use core::ptr;
|
||||
|
||||
use spoof::{spoof_syscall, AsPointer};
|
||||
|
||||
#[test]
|
||||
fn alloc_via_desync_syscall() {
|
||||
let mut addr: *mut c_void = ptr::null_mut();
|
||||
let mut size: usize = 0x1000;
|
||||
|
||||
let status = spoof_syscall!(
|
||||
"NtAllocateVirtualMemory",
|
||||
-1isize,
|
||||
addr.as_ptr_mut(),
|
||||
0usize,
|
||||
size.as_ptr_mut(),
|
||||
0x3000u32,
|
||||
0x04u32
|
||||
)
|
||||
.expect("desync spoof_syscall returned Err") as i32;
|
||||
|
||||
assert_eq!(status, 0, "NTSTATUS = 0x{:08X}", status);
|
||||
assert!(!addr.is_null());
|
||||
|
||||
let mut zero_size: usize = 0;
|
||||
let _ = spoof_syscall!(
|
||||
"NtFreeVirtualMemory",
|
||||
-1isize,
|
||||
addr.as_ptr_mut(),
|
||||
zero_size.as_ptr_mut(),
|
||||
0x8000u32
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//! Verify that obfstr actually obfuscates literals in the final binary.
|
||||
|
||||
use data::lc;
|
||||
|
||||
#[test]
|
||||
fn canary_is_obfuscated() {
|
||||
let decoded = lc!("DYNCVOKE_CANARY_STRING_2026");
|
||||
assert_eq!(decoded, "DYNCVOKE_CANARY_STRING_2026");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//! Spoofed direct function call. Resolves VirtualAlloc by name, then calls
|
||||
//! it under the synthetic stack. The returned address is the allocation, not
|
||||
//! the spoof scaffolding — confirms the asm trampoline isn't corrupting the
|
||||
//! return value.
|
||||
|
||||
use core::ffi::c_void;
|
||||
use core::ptr;
|
||||
|
||||
use spoof::spoof;
|
||||
|
||||
#[test]
|
||||
fn virtual_alloc_via_spoof() {
|
||||
let kernel32 = dyncvoke_core::get_module_base_address("kernel32.dll");
|
||||
assert!(kernel32 != 0, "kernel32 not loaded");
|
||||
let virtual_alloc = dyncvoke_core::get_function_address(kernel32, "VirtualAlloc");
|
||||
assert!(virtual_alloc != 0, "VirtualAlloc not found");
|
||||
|
||||
let addr = spoof!(
|
||||
virtual_alloc,
|
||||
ptr::null_mut::<c_void>(),
|
||||
1usize << 12,
|
||||
0x3000u32,
|
||||
0x04u32
|
||||
)
|
||||
.expect("spoof returned Err");
|
||||
|
||||
assert!(!addr.is_null());
|
||||
unsafe { *(addr as *mut u8) = 0x42; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//! Live test of the synthetic-stack spoof path. Allocates 4KB via
|
||||
//! NtAllocateVirtualMemory through the spoofed trampoline, then frees it.
|
||||
//! If the asm setup, prologue/gadget discovery, or SSN resolution is wrong
|
||||
//! this either crashes or returns a non-zero NTSTATUS.
|
||||
|
||||
use core::ffi::c_void;
|
||||
use core::ptr;
|
||||
|
||||
use spoof::{spoof_syscall, AsPointer};
|
||||
|
||||
#[test]
|
||||
fn alloc_via_spoofed_syscall() {
|
||||
let mut addr: *mut c_void = ptr::null_mut();
|
||||
let mut size: usize = 0x1000;
|
||||
|
||||
let status = spoof_syscall!(
|
||||
"NtAllocateVirtualMemory",
|
||||
-1isize,
|
||||
addr.as_ptr_mut(),
|
||||
0usize,
|
||||
size.as_ptr_mut(),
|
||||
0x3000u32,
|
||||
0x04u32
|
||||
)
|
||||
.expect("spoof_syscall returned Err") as i32;
|
||||
|
||||
assert_eq!(status, 0, "NtAllocateVirtualMemory NTSTATUS = 0x{:08X}", status);
|
||||
assert!(!addr.is_null());
|
||||
|
||||
// Touch the page so we know it's actually committed.
|
||||
unsafe { *(addr as *mut u8) = 0x42; }
|
||||
|
||||
let mut zero_size: usize = 0;
|
||||
let status = spoof_syscall!(
|
||||
"NtFreeVirtualMemory",
|
||||
-1isize,
|
||||
addr.as_ptr_mut(),
|
||||
zero_size.as_ptr_mut(),
|
||||
0x8000u32
|
||||
)
|
||||
.expect("spoof_syscall free returned Err") as i32;
|
||||
|
||||
assert_eq!(status, 0, "NtFreeVirtualMemory NTSTATUS = 0x{:08X}", status);
|
||||
}
|
||||
+13
-1
@@ -1,6 +1,18 @@
|
||||
#![no_std]
|
||||
|
||||
pub use data;
|
||||
|
||||
#[cfg(feature = "syscall")]
|
||||
pub use dyncvoke_core;
|
||||
pub use dmanager;
|
||||
|
||||
#[cfg(feature = "manualmap")]
|
||||
pub use manualmap;
|
||||
|
||||
#[cfg(feature = "overload")]
|
||||
pub use overload;
|
||||
|
||||
#[cfg(feature = "dmanager")]
|
||||
pub use dmanager;
|
||||
|
||||
#[cfg(feature = "spoof")]
|
||||
pub use spoof;
|
||||
|
||||
Reference in New Issue
Block a user