diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b45be19abb..17700897d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + submodules: true - uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0 with: @@ -61,6 +62,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + submodules: true - uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0 with: @@ -89,6 +91,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + submodules: true - uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0 with: @@ -126,6 +129,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + submodules: true - run: | brew install rustup @@ -161,6 +165,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + submodules: true - uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0 with: diff --git a/.github/workflows/deny.yml b/.github/workflows/deny.yml index a81bb07b6f..06cb44784a 100644 --- a/.github/workflows/deny.yml +++ b/.github/workflows/deny.yml @@ -7,16 +7,16 @@ on: pull_request: types: [opened, synchronize] paths: - - "Cargo.lock" - - "deny.toml" - - ".github/workflows/deny.yml" + - 'Cargo.lock' + - 'deny.toml' + - '.github/workflows/deny.yml' push: branches: - main paths: - - "Cargo.lock" - - "deny.toml" - - ".github/workflows/deny.yml" + - 'Cargo.lock' + - 'deny.toml' + - '.github/workflows/deny.yml' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} @@ -30,6 +30,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + submodules: true - uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6302186729..52a95fe520 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,6 +23,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + submodules: true - uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.2 with: @@ -93,6 +94,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + submodules: true - uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4 @@ -120,8 +122,8 @@ jobs: - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version-file: .node-version - registry-url: "https://npm.pkg.github.com" - scope: "@voidzero-dev" + registry-url: 'https://npm.pkg.github.com' + scope: '@voidzero-dev' package-manager-cache: false - run: npm install -g npm@latest # For trusted publishing support diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 22b536430b..476977bc92 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -7,13 +7,13 @@ on: pull_request: types: [opened, synchronize] paths: - - ".github/workflows/**" + - '.github/workflows/**' push: branches: - main - - "renovate/**" + - 'renovate/**' paths: - - ".github/workflows/**" + - '.github/workflows/**' jobs: zizmor: @@ -26,6 +26,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: persist-credentials: false + submodules: true - uses: taiki-e/install-action@ae97ff9daf1cd2e216671a047d80ff48461e30bb # v2.49.1 with: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..ab8b378fe1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "crates/fspy_detours_sys/detours"] + path = crates/fspy_detours_sys/detours + url = https://github.com/microsoft/Detours diff --git a/.typos.toml b/.typos.toml index 700244c2b5..038573c4f5 100644 --- a/.typos.toml +++ b/.typos.toml @@ -5,4 +5,6 @@ PUNICODE = "PUNICODE" [files] extend-exclude = [ "**/snap-tests/**/snap.txt", + "crates/fspy_detours_sys/detours", + "crates/fspy_detours_sys/src/generated_bindings.rs", ] diff --git a/Cargo.lock b/Cargo.lock index f6529b9cd1..3ee7838b93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,9 +466,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.71.1" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags 2.9.4", "cexpr", @@ -645,9 +645,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.36" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" dependencies = [ "find-msvc-tools", "shlex", @@ -1457,9 +1457,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" [[package]] name = "fixedbitset" @@ -1535,6 +1535,7 @@ dependencies = [ "csv-async", "ctor 0.4.3", "flate2", + "fspy_detours_sys", "fspy_preload_unix", "fspy_preload_windows", "fspy_seccomp_unotify", @@ -1543,7 +1544,6 @@ dependencies = [ "futures-util", "libc", "memmap2", - "ms-detours", "nix 0.30.1", "ouroboros", "passfd 0.2.0 (git+https://github.com/polachok/passfd)", @@ -1562,6 +1562,15 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "fspy_detours_sys" +version = "0.0.0" +dependencies = [ + "bindgen", + "cc", + "winapi", +] + [[package]] name = "fspy_e2e" version = "0.0.0" @@ -1612,8 +1621,8 @@ dependencies = [ "constcat", "dashmap", "derive-where", + "fspy_detours_sys", "fspy_shared", - "ms-detours", "ntapi", "path-dedot", "ref-cast", @@ -1655,7 +1664,6 @@ dependencies = [ "bytemuck", "derive-where", "libc", - "ms-detours", "nix 0.30.1", "os_str_bytes", "phf", @@ -2565,17 +2573,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "ms-detours" -version = "4.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "787ffff01b8cccea08858cf318738c6122aa1ca9b20ed6505036fefe466c2029" -dependencies = [ - "bindgen", - "cc", - "winapi", -] - [[package]] name = "napi" version = "3.3.0" diff --git a/Cargo.toml b/Cargo.toml index 22fd8fa824..ce240eb5e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ backon = "1.3.0" bincode = "2.0.1" brush-parser = "0.2.18" bstr = "1.12.0" +cc = "1.2.39" clap = "4.5.40" color-eyre = "0.6.5" compact_str = "0.9.0" @@ -48,6 +49,7 @@ directories = "6.0.0" edit = "0.1.5" flate2 = "1.0.35" fspy = { path = "crates/fspy" } +fspy_detours_sys = { path = "crates/fspy_detours_sys" } fspy_preload_unix = { path = "crates/fspy_preload_unix", artifact = "cdylib" } fspy_preload_windows = { path = "crates/fspy_preload_windows", artifact = "cdylib" } fspy_seccomp_unotify = { path = "crates/fspy_seccomp_unotify" } @@ -102,6 +104,7 @@ vite_path = { path = "crates/vite_path" } vite_str = { path = "crates/vite_str" } vite_task = { path = "crates/vite_task" } wax = "0.6.0" +winapi = "0.3.9" napi = { version = "3.0.0", default-features = false, features = ["async", "error_anyhow"] } napi-build = "2" diff --git a/crates/fspy/Cargo.toml b/crates/fspy/Cargo.toml index a7b2e98e9c..bfa2c7c284 100644 --- a/crates/fspy/Cargo.toml +++ b/crates/fspy/Cargo.toml @@ -27,19 +27,17 @@ allocator-api2 = { version = "0.2.21", default-features = false, features = [ tokio-seqpacket = "0.8.0" arrayvec = "0.7.6" nix = { version = "0.30.1", features = ["uio"] } -fspy_seccomp_unotify = { workspace = true, features = ["supervisor"]} -blink-alloc = { version = "0.3.1", features = ["sync"]} +fspy_seccomp_unotify = { workspace = true, features = ["supervisor"] } +blink-alloc = { version = "0.3.1", features = ["sync"] } thread_local = "1.1.9" tokio = { version = "1.44.2", features = ["bytes"] } -syscalls = { version = "0.6.18", default-features = false, features = ["std"]} +syscalls = { version = "0.6.18", default-features = false, features = ["std"] } [target.'cfg(unix)'.dependencies] fspy_shared_unix = { workspace = true } fspy_preload_unix = { workspace = true } nix = { version = "0.30.1", features = ["fs", "process", "socket", "feature"] } -passfd = { git = "https://github.com/polachok/passfd", features = [ - "async", -] } +passfd = { git = "https://github.com/polachok/passfd", features = ["async"] } memmap2 = "0.9.7" # asyncfd = "0.1.2" @@ -53,14 +51,14 @@ const_format = { version = "0.2.34", features = ["fmt"] } [target.'cfg(target_os = "windows")'.dependencies] -ms-detours = "4.0.5" winsafe = { version = "0.0.24", features = ["kernel"] } -winapi = { version = "0.3.9", features = [ +winapi = { workspace = true, features = [ "winbase", "securitybaseapi", "handleapi", ] } fspy_preload_windows = { workspace = true } +fspy_detours_sys = { workspace = true } [target.'cfg(target_os = "macos")'.dev-dependencies] tempfile = "3.19.1" diff --git a/crates/fspy/src/windows/mod.rs b/crates/fspy/src/windows/mod.rs index 8a2af19190..0add51f3cd 100644 --- a/crates/fspy/src/windows/mod.rs +++ b/crates/fspy/src/windows/mod.rs @@ -10,12 +10,12 @@ use std::{ use bincode::borrow_decode_from_slice; use const_format::formatcp; +use fspy_detours_sys::{DetourCopyPayloadToProcess, DetourUpdateProcessWithDll}; use fspy_shared::{ ipc::{BINCODE_CONFIG, PathAccess}, windows::{PAYLOAD_ID, Payload}, }; use futures_util::FutureExt; -use ms_detours::{DetourCopyPayloadToProcess, DetourUpdateProcessWithDll}; use tokio::{ io::AsyncReadExt, net::windows::named_pipe::{PipeMode, ServerOptions}, diff --git a/crates/fspy_detours_sys/Cargo.toml b/crates/fspy_detours_sys/Cargo.toml new file mode 100644 index 0000000000..87301eb0dd --- /dev/null +++ b/crates/fspy_detours_sys/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "fspy_detours_sys" +version = "0.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[build-dependencies] +cc = { workspace = true } + +[dependencies] +winapi = { workspace = true, features = ["minwindef", "libloaderapi", "processthreadsapi", "windef"] } + +[lints] +workspace = true + +[dev-dependencies] +bindgen = "0.72.1" diff --git a/crates/fspy_detours_sys/README.md b/crates/fspy_detours_sys/README.md new file mode 100644 index 0000000000..2b7d1dc765 --- /dev/null +++ b/crates/fspy_detours_sys/README.md @@ -0,0 +1,3 @@ +# fspy_detours_sys + +Raw FFI bindings to [Detours](https://github.com/Microsoft/Detours) diff --git a/crates/fspy_detours_sys/build.rs b/crates/fspy_detours_sys/build.rs new file mode 100644 index 0000000000..121aac5525 --- /dev/null +++ b/crates/fspy_detours_sys/build.rs @@ -0,0 +1,18 @@ +fn main() { + if std::env::var_os("CARGO_CFG_TARGET_OS").unwrap() != "windows" { + return; + } + println!("cargo:rerun-if-changed=detours/src"); + // https://github.com/Berrysoft/detours/blob/c9bc2ad6e9cd8f5f7b74cfa65365d61ecc45203f/detours-sys/build.rs + cc::Build::new() + .include("detours/src") + .define("WIN32_LEAN_AND_MEAN", "1") + .define("_WIN32_WINNT", "0x501") + .file("detours/src/detours.cpp") + .file("detours/src/modules.cpp") + .file("detours/src/disasm.cpp") + .file("detours/src/image.cpp") + .file("detours/src/creatwth.cpp") + .cpp(true) + .compile("detours"); +} diff --git a/crates/fspy_detours_sys/detours b/crates/fspy_detours_sys/detours new file mode 160000 index 0000000000..9764cebcb1 --- /dev/null +++ b/crates/fspy_detours_sys/detours @@ -0,0 +1 @@ +Subproject commit 9764cebcb1a75940e68fa83d6730ffaf0f669401 diff --git a/crates/fspy_detours_sys/src/generated_bindings.rs b/crates/fspy_detours_sys/src/generated_bindings.rs new file mode 100644 index 0000000000..daab4d09c3 --- /dev/null +++ b/crates/fspy_detours_sys/src/generated_bindings.rs @@ -0,0 +1,463 @@ +use winapi::shared::minwindef::*; +use winapi::um::winnt::*; +use winapi::um::winnt::INT; +use winapi::um::minwinbase::*; +use winapi::um::processthreadsapi::*; +use winapi::shared::guiddef::*; +use winapi::shared::windef::*; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _DETOUR_TRAMPOLINE { + _unused: [u8; 0], +} +pub type PDETOUR_TRAMPOLINE = *mut _DETOUR_TRAMPOLINE; +/// Binary Typedefs. +pub type PF_DETOUR_BINARY_BYWAY_CALLBACK = ::std::option::Option< + unsafe extern "system" fn( + pContext: PVOID, + pszFile: LPCSTR, + ppszOutFile: *mut LPCSTR, + ) -> BOOL, +>; +pub type PF_DETOUR_BINARY_FILE_CALLBACK = ::std::option::Option< + unsafe extern "system" fn( + pContext: PVOID, + pszOrigFile: LPCSTR, + pszFile: LPCSTR, + ppszOutFile: *mut LPCSTR, + ) -> BOOL, +>; +pub type PF_DETOUR_BINARY_SYMBOL_CALLBACK = ::std::option::Option< + unsafe extern "system" fn( + pContext: PVOID, + nOrigOrdinal: ULONG, + nOrdinal: ULONG, + pnOutOrdinal: *mut ULONG, + pszOrigSymbol: LPCSTR, + pszSymbol: LPCSTR, + ppszOutSymbol: *mut LPCSTR, + ) -> BOOL, +>; +pub type PF_DETOUR_BINARY_COMMIT_CALLBACK = ::std::option::Option< + unsafe extern "system" fn(pContext: PVOID) -> BOOL, +>; +pub type PF_DETOUR_ENUMERATE_EXPORT_CALLBACK = ::std::option::Option< + unsafe extern "system" fn( + pContext: PVOID, + nOrdinal: ULONG, + pszName: LPCSTR, + pCode: PVOID, + ) -> BOOL, +>; +pub type PF_DETOUR_IMPORT_FILE_CALLBACK = ::std::option::Option< + unsafe extern "system" fn(pContext: PVOID, hModule: HMODULE, pszFile: LPCSTR) -> BOOL, +>; +pub type PF_DETOUR_IMPORT_FUNC_CALLBACK = ::std::option::Option< + unsafe extern "system" fn( + pContext: PVOID, + nOrdinal: DWORD, + pszFunc: LPCSTR, + pvFunc: PVOID, + ) -> BOOL, +>; +pub type PF_DETOUR_IMPORT_FUNC_CALLBACK_EX = ::std::option::Option< + unsafe extern "system" fn( + pContext: PVOID, + nOrdinal: DWORD, + pszFunc: LPCSTR, + ppvFunc: *mut PVOID, + ) -> BOOL, +>; +pub type PDETOUR_BINARY = *mut ::std::os::raw::c_void; +unsafe extern "system" { + /// Transaction APIs. + pub fn DetourTransactionBegin() -> LONG; +} +unsafe extern "system" { + pub fn DetourTransactionAbort() -> LONG; +} +unsafe extern "system" { + pub fn DetourTransactionCommit() -> LONG; +} +unsafe extern "system" { + pub fn DetourTransactionCommitEx(pppFailedPointer: *mut *mut PVOID) -> LONG; +} +unsafe extern "system" { + pub fn DetourUpdateThread(hThread: HANDLE) -> LONG; +} +unsafe extern "system" { + pub fn DetourAttach(ppPointer: *mut PVOID, pDetour: PVOID) -> LONG; +} +unsafe extern "system" { + pub fn DetourAttachEx( + ppPointer: *mut PVOID, + pDetour: PVOID, + ppRealTrampoline: *mut PDETOUR_TRAMPOLINE, + ppRealTarget: *mut PVOID, + ppRealDetour: *mut PVOID, + ) -> LONG; +} +unsafe extern "system" { + pub fn DetourDetach(ppPointer: *mut PVOID, pDetour: PVOID) -> LONG; +} +unsafe extern "system" { + pub fn DetourSetIgnoreTooSmall(fIgnore: BOOL) -> BOOL; +} +unsafe extern "system" { + pub fn DetourSetRetainRegions(fRetain: BOOL) -> BOOL; +} +unsafe extern "system" { + pub fn DetourSetSystemRegionLowerBound(pSystemRegionLowerBound: PVOID) -> PVOID; +} +unsafe extern "system" { + pub fn DetourSetSystemRegionUpperBound(pSystemRegionUpperBound: PVOID) -> PVOID; +} +unsafe extern "system" { + /// Code Functions. + pub fn DetourFindFunction(pszModule: LPCSTR, pszFunction: LPCSTR) -> PVOID; +} +unsafe extern "system" { + pub fn DetourCodeFromPointer(pPointer: PVOID, ppGlobals: *mut PVOID) -> PVOID; +} +unsafe extern "system" { + pub fn DetourCopyInstruction( + pDst: PVOID, + ppDstPool: *mut PVOID, + pSrc: PVOID, + ppTarget: *mut PVOID, + plExtra: *mut LONG, + ) -> PVOID; +} +unsafe extern "system" { + pub fn DetourSetCodeModule(hModule: HMODULE, fLimitReferencesToModule: BOOL) -> BOOL; +} +unsafe extern "system" { + pub fn DetourAllocateRegionWithinJumpBounds( + pbTarget: LPCVOID, + pcbAllocatedSize: PDWORD, + ) -> PVOID; +} +unsafe extern "system" { + pub fn DetourIsFunctionImported(pbCode: PBYTE, pbAddress: PBYTE) -> BOOL; +} +unsafe extern "system" { + /// Loaded Binary Functions. + pub fn DetourGetContainingModule(pvAddr: PVOID) -> HMODULE; +} +unsafe extern "system" { + pub fn DetourEnumerateModules(hModuleLast: HMODULE) -> HMODULE; +} +unsafe extern "system" { + pub fn DetourGetEntryPoint(hModule: HMODULE) -> PVOID; +} +unsafe extern "system" { + pub fn DetourGetModuleSize(hModule: HMODULE) -> ULONG; +} +unsafe extern "system" { + pub fn DetourEnumerateExports( + hModule: HMODULE, + pContext: PVOID, + pfExport: PF_DETOUR_ENUMERATE_EXPORT_CALLBACK, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourEnumerateImports( + hModule: HMODULE, + pContext: PVOID, + pfImportFile: PF_DETOUR_IMPORT_FILE_CALLBACK, + pfImportFunc: PF_DETOUR_IMPORT_FUNC_CALLBACK, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourEnumerateImportsEx( + hModule: HMODULE, + pContext: PVOID, + pfImportFile: PF_DETOUR_IMPORT_FILE_CALLBACK, + pfImportFuncEx: PF_DETOUR_IMPORT_FUNC_CALLBACK_EX, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourFindPayload( + hModule: HMODULE, + rguid: *const GUID, + pcbData: *mut DWORD, + ) -> PVOID; +} +unsafe extern "system" { + pub fn DetourFindPayloadEx(rguid: *const GUID, pcbData: *mut DWORD) -> PVOID; +} +unsafe extern "system" { + pub fn DetourGetSizeOfPayloads(hModule: HMODULE) -> DWORD; +} +unsafe extern "system" { + pub fn DetourFreePayload(pvData: PVOID) -> BOOL; +} +unsafe extern "system" { + /// Persistent Binary Functions. + pub fn DetourBinaryOpen(hFile: HANDLE) -> PDETOUR_BINARY; +} +unsafe extern "system" { + pub fn DetourBinaryEnumeratePayloads( + pBinary: PDETOUR_BINARY, + pGuid: *mut GUID, + pcbData: *mut DWORD, + pnIterator: *mut DWORD, + ) -> PVOID; +} +unsafe extern "system" { + pub fn DetourBinaryFindPayload( + pBinary: PDETOUR_BINARY, + rguid: *const GUID, + pcbData: *mut DWORD, + ) -> PVOID; +} +unsafe extern "system" { + pub fn DetourBinarySetPayload( + pBinary: PDETOUR_BINARY, + rguid: *const GUID, + pData: PVOID, + cbData: DWORD, + ) -> PVOID; +} +unsafe extern "system" { + pub fn DetourBinaryDeletePayload( + pBinary: PDETOUR_BINARY, + rguid: *const GUID, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourBinaryPurgePayloads(pBinary: PDETOUR_BINARY) -> BOOL; +} +unsafe extern "system" { + pub fn DetourBinaryResetImports(pBinary: PDETOUR_BINARY) -> BOOL; +} +unsafe extern "system" { + pub fn DetourBinaryEditImports( + pBinary: PDETOUR_BINARY, + pContext: PVOID, + pfByway: PF_DETOUR_BINARY_BYWAY_CALLBACK, + pfFile: PF_DETOUR_BINARY_FILE_CALLBACK, + pfSymbol: PF_DETOUR_BINARY_SYMBOL_CALLBACK, + pfCommit: PF_DETOUR_BINARY_COMMIT_CALLBACK, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourBinaryWrite(pBinary: PDETOUR_BINARY, hFile: HANDLE) -> BOOL; +} +unsafe extern "system" { + pub fn DetourBinaryClose(pBinary: PDETOUR_BINARY) -> BOOL; +} +unsafe extern "system" { + /// Create Process & Load Dll. + pub fn DetourFindRemotePayload( + hProcess: HANDLE, + rguid: *const GUID, + pcbData: *mut DWORD, + ) -> PVOID; +} +pub type PDETOUR_CREATE_PROCESS_ROUTINEA = ::std::option::Option< + unsafe extern "system" fn( + lpApplicationName: LPCSTR, + lpCommandLine: LPSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCSTR, + lpStartupInfo: LPSTARTUPINFOA, + lpProcessInformation: LPPROCESS_INFORMATION, + ) -> BOOL, +>; +pub type PDETOUR_CREATE_PROCESS_ROUTINEW = ::std::option::Option< + unsafe extern "system" fn( + lpApplicationName: LPCWSTR, + lpCommandLine: LPWSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCWSTR, + lpStartupInfo: LPSTARTUPINFOW, + lpProcessInformation: LPPROCESS_INFORMATION, + ) -> BOOL, +>; +unsafe extern "system" { + pub fn DetourCreateProcessWithDllA( + lpApplicationName: LPCSTR, + lpCommandLine: LPSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCSTR, + lpStartupInfo: LPSTARTUPINFOA, + lpProcessInformation: LPPROCESS_INFORMATION, + lpDllName: LPCSTR, + pfCreateProcessA: PDETOUR_CREATE_PROCESS_ROUTINEA, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourCreateProcessWithDllW( + lpApplicationName: LPCWSTR, + lpCommandLine: LPWSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCWSTR, + lpStartupInfo: LPSTARTUPINFOW, + lpProcessInformation: LPPROCESS_INFORMATION, + lpDllName: LPCSTR, + pfCreateProcessW: PDETOUR_CREATE_PROCESS_ROUTINEW, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourCreateProcessWithDllExA( + lpApplicationName: LPCSTR, + lpCommandLine: LPSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCSTR, + lpStartupInfo: LPSTARTUPINFOA, + lpProcessInformation: LPPROCESS_INFORMATION, + lpDllName: LPCSTR, + pfCreateProcessA: PDETOUR_CREATE_PROCESS_ROUTINEA, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourCreateProcessWithDllExW( + lpApplicationName: LPCWSTR, + lpCommandLine: LPWSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCWSTR, + lpStartupInfo: LPSTARTUPINFOW, + lpProcessInformation: LPPROCESS_INFORMATION, + lpDllName: LPCSTR, + pfCreateProcessW: PDETOUR_CREATE_PROCESS_ROUTINEW, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourCreateProcessWithDllsA( + lpApplicationName: LPCSTR, + lpCommandLine: LPSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCSTR, + lpStartupInfo: LPSTARTUPINFOA, + lpProcessInformation: LPPROCESS_INFORMATION, + nDlls: DWORD, + rlpDlls: *mut LPCSTR, + pfCreateProcessA: PDETOUR_CREATE_PROCESS_ROUTINEA, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourCreateProcessWithDllsW( + lpApplicationName: LPCWSTR, + lpCommandLine: LPWSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCWSTR, + lpStartupInfo: LPSTARTUPINFOW, + lpProcessInformation: LPPROCESS_INFORMATION, + nDlls: DWORD, + rlpDlls: *mut LPCSTR, + pfCreateProcessW: PDETOUR_CREATE_PROCESS_ROUTINEW, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourProcessViaHelperA( + dwTargetPid: DWORD, + lpDllName: LPCSTR, + pfCreateProcessA: PDETOUR_CREATE_PROCESS_ROUTINEA, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourProcessViaHelperW( + dwTargetPid: DWORD, + lpDllName: LPCSTR, + pfCreateProcessW: PDETOUR_CREATE_PROCESS_ROUTINEW, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourProcessViaHelperDllsA( + dwTargetPid: DWORD, + nDlls: DWORD, + rlpDlls: *mut LPCSTR, + pfCreateProcessA: PDETOUR_CREATE_PROCESS_ROUTINEA, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourProcessViaHelperDllsW( + dwTargetPid: DWORD, + nDlls: DWORD, + rlpDlls: *mut LPCSTR, + pfCreateProcessW: PDETOUR_CREATE_PROCESS_ROUTINEW, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourUpdateProcessWithDll( + hProcess: HANDLE, + rlpDlls: *mut LPCSTR, + nDlls: DWORD, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourUpdateProcessWithDllEx( + hProcess: HANDLE, + hImage: HMODULE, + bIs32Bit: BOOL, + rlpDlls: *mut LPCSTR, + nDlls: DWORD, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourCopyPayloadToProcess( + hProcess: HANDLE, + rguid: *const GUID, + pvData: LPCVOID, + cbData: DWORD, + ) -> BOOL; +} +unsafe extern "system" { + pub fn DetourCopyPayloadToProcessEx( + hProcess: HANDLE, + rguid: *const GUID, + pvData: LPCVOID, + cbData: DWORD, + ) -> PVOID; +} +unsafe extern "system" { + pub fn DetourRestoreAfterWith() -> BOOL; +} +unsafe extern "system" { + pub fn DetourRestoreAfterWithEx(pvData: PVOID, cbData: DWORD) -> BOOL; +} +unsafe extern "system" { + pub fn DetourIsHelperProcess() -> BOOL; +} +unsafe extern "system" { + pub fn DetourFinishHelperProcess( + arg1: HWND, + arg2: HINSTANCE, + arg3: LPSTR, + arg4: INT, + ); +} diff --git a/crates/fspy_detours_sys/src/lib.rs b/crates/fspy_detours_sys/src/lib.rs new file mode 100644 index 0000000000..46396b47fd --- /dev/null +++ b/crates/fspy_detours_sys/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg(windows)] + +#[allow(non_camel_case_types, non_snake_case)] +#[rustfmt::skip] // generated code is formatted by prettyplease, not rustfmt +mod generated_bindings; + +pub use generated_bindings::*; diff --git a/crates/fspy_detours_sys/tests/bindings.rs b/crates/fspy_detours_sys/tests/bindings.rs new file mode 100644 index 0000000000..24d97262e6 --- /dev/null +++ b/crates/fspy_detours_sys/tests/bindings.rs @@ -0,0 +1,66 @@ +#![cfg(windows)] + +use std::{env, fs}; + +#[test] +fn detours_bindings() { + let bindings = bindgen::Builder::default() + .clang_args(["-Idetours/src", "-DWIN32_LEAN_AND_MEAN"]) + .header_contents("wrapper.h", "#include \n#include \n") + .allowlist_function("Detour.*") + .blocklist_type("LP.*") + .blocklist_type("_GUID") + .blocklist_type("GUID") + .blocklist_type("ULONG") + .blocklist_type("PVOID") + .blocklist_type("DWORD") + .blocklist_type("wchar_t") + .blocklist_type("BOOL") + .blocklist_type("BYTE") + .blocklist_type("WORD") + .blocklist_type("PBYTE") + .blocklist_type("PDWORD") + .blocklist_type("INT") + .blocklist_type("CHAR") + .blocklist_type("LONG") + .blocklist_type("WCHAR") + .blocklist_type("HANDLE") + .blocklist_type("HMODULE") + .blocklist_type("HINSTANCE.*") + .blocklist_type("HWND.*") + .blocklist_type("_SECURITY_ATTRIBUTES") + .blocklist_type("_PROCESS_INFORMATION") + .blocklist_type("_STARTUPINFOA") + .blocklist_type("_STARTUPINFOW") + .disable_header_comment() + .raw_line("use winapi::shared::minwindef::*;") + .raw_line("use winapi::um::winnt::*;") + .raw_line("use winapi::um::winnt::INT;") + .raw_line("use winapi::um::minwinbase::*;") + .raw_line("use winapi::um::processthreadsapi::*;") + .raw_line("use winapi::shared::guiddef::*;") + .raw_line("use winapi::shared::windef::*;") + .layout_tests(false) + .formatter(bindgen::Formatter::Prettyplease) + // Detour functions are stdcall on 32-bit Windows + .override_abi(bindgen::Abi::System, ".*") + .generate() + .expect("Unable to generate bindings"); + + // bindgen produces raw_lines with \r\n line endings on Windows; + // Git on Windows may check out files using CRLF line endings, depending on user config. + // To avoid unnecessary diffs, normalize all line endings to \n. + let bindings_content = bindings.to_string().replace("\r\n", "\n"); + let bindings_path = "src/generated_bindings.rs"; + + if env::var("FSPY_DETOURS_WRITE_BINDINGS").as_deref() == Ok("1") { + fs::write(bindings_path, bindings_content).unwrap(); + } else { + let existing_bindings_content = + fs::read_to_string(bindings_path).unwrap_or_default().replace("\r\n", "\n"); + assert_eq!( + existing_bindings_content, bindings_content, + "Bindings are out of date. Run this test with FSPY_DETOURS_WRITE_BINDINGS=1 to update them." + ); + } +} diff --git a/crates/fspy_preload_windows/Cargo.toml b/crates/fspy_preload_windows/Cargo.toml index 7ff64bacef..4023c3df3d 100644 --- a/crates/fspy_preload_windows/Cargo.toml +++ b/crates/fspy_preload_windows/Cargo.toml @@ -9,8 +9,7 @@ crate-type = ["cdylib"] [target.'cfg(target_os = "windows")'.dependencies] # windows-sys = { version = "0.59.0", features = ["Win32_Foundation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_Security", "Win32_System_LibraryLoader"] } winsafe = { version = "0.0.24", features = ["kernel"] } -ms-detours = "4.0.5" -winapi = { version = "0.3.9", features = [ +winapi = { workspace = true, features = [ "winerror", "winbase", "namedpipeapi", @@ -29,6 +28,7 @@ path-dedot = "3.1.1" ref-cast = "1.0.24" which = "7.0.3" fspy_shared = { workspace = true } +fspy_detours_sys = { workspace = true } ntapi = "0.4.1" diff --git a/crates/fspy_preload_windows/src/windows/client.rs b/crates/fspy_preload_windows/src/windows/client.rs index 1e3699e51a..abb7eb3850 100644 --- a/crates/fspy_preload_windows/src/windows/client.rs +++ b/crates/fspy_preload_windows/src/windows/client.rs @@ -7,11 +7,11 @@ use std::{ use bincode::{borrow_decode_from_slice, encode_into_std_write, encode_to_vec}; use dashmap::DashSet; +use fspy_detours_sys::DetourCopyPayloadToProcess; use fspy_shared::{ ipc::{BINCODE_CONFIG, PathAccess}, windows::{PAYLOAD_ID, Payload}, }; -use ms_detours::DetourCopyPayloadToProcess; use ntapi::ntobapi::DUPLICATE_SAME_ACCESS; use smallvec::SmallVec; use winapi::{ diff --git a/crates/fspy_preload_windows/src/windows/detour.rs b/crates/fspy_preload_windows/src/windows/detour.rs index 75a05ab0fb..0b8a2ddc40 100644 --- a/crates/fspy_preload_windows/src/windows/detour.rs +++ b/crates/fspy_preload_windows/src/windows/detour.rs @@ -1,6 +1,6 @@ use std::{cell::UnsafeCell, ffi::CStr, mem::transmute_copy, os::raw::c_void, ptr::null_mut}; -use ms_detours::{DetourAttach, DetourDetach}; +use fspy_detours_sys::{DetourAttach, DetourDetach}; use winapi::{ shared::minwindef::HMODULE, um::libloaderapi::{GetProcAddress, LoadLibraryA}, diff --git a/crates/fspy_preload_windows/src/windows/detours/create_process.rs b/crates/fspy_preload_windows/src/windows/detours/create_process.rs index e0cfcb89ce..505b8a3d13 100644 --- a/crates/fspy_preload_windows/src/windows/detours/create_process.rs +++ b/crates/fspy_preload_windows/src/windows/detours/create_process.rs @@ -1,7 +1,7 @@ use std::ffi::CStr; +use fspy_detours_sys::{DetourCreateProcessWithDllExA, DetourCreateProcessWithDllExW}; use fspy_shared::ipc::{AccessMode, NativeStr, PathAccess}; -use ms_detours::{DetourCreateProcessWithDllExA, DetourCreateProcessWithDllExW}; use widestring::U16CStr; use winapi::{ shared::{ diff --git a/crates/fspy_preload_windows/src/windows/mod.rs b/crates/fspy_preload_windows/src/windows/mod.rs index 4b82217f15..d806e9c517 100644 --- a/crates/fspy_preload_windows/src/windows/mod.rs +++ b/crates/fspy_preload_windows/src/windows/mod.rs @@ -8,11 +8,11 @@ use std::slice; use client::{Client, set_global_client}; use detours::DETOURS; -use fspy_shared::windows::PAYLOAD_ID; -use ms_detours::{ +use fspy_detours_sys::{ DetourFindPayloadEx, DetourIsHelperProcess, DetourRestoreAfterWith, DetourTransactionBegin, DetourTransactionCommit, DetourUpdateThread, }; +use fspy_shared::windows::PAYLOAD_ID; use winapi::{ shared::minwindef::{BOOL, DWORD, FALSE, HINSTANCE, TRUE}, um::{ diff --git a/crates/fspy_shared/Cargo.toml b/crates/fspy_shared/Cargo.toml index 179d4d00ef..1fa383f34e 100644 --- a/crates/fspy_shared/Cargo.toml +++ b/crates/fspy_shared/Cargo.toml @@ -11,8 +11,7 @@ allocator-api2 = { version = "0.2.21", default-features = false, features = ["st # stable_deref_trait = { version = "1.2.0", optional = true } [target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3.9", features = ["std"] } -ms-detours = "4.0.5" +winapi = { workspace = true, features = ["std"] } bytemuck = { version = "1.23.0", features = ["must_cast", "extern_crate_alloc"] } winsafe = { version = "0.0.24", features = ["kernel"] } os_str_bytes = { workspace = true }