mirror of
https://github.com/0xflux/sanctum
synced 2026-06-06 15:04:29 +00:00
Merge pull request #77 from 0xflux/new-injection-experimental
Use APC for DLL injection
This commit is contained in:
Vendored
+1
@@ -43,6 +43,7 @@
|
||||
"IOCTLS",
|
||||
"Irql",
|
||||
"KAPC",
|
||||
"kernelbase",
|
||||
"KEVENT",
|
||||
"KIRQL",
|
||||
"KLDR",
|
||||
|
||||
Generated
+3
-2
@@ -108,9 +108,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.97"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "app"
|
||||
@@ -4188,6 +4188,7 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
name = "um_engine"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"md-5",
|
||||
"reqwest",
|
||||
"serde",
|
||||
|
||||
Generated
+137
-152
@@ -13,9 +13,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.20"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -28,9 +28,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.11"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@@ -63,9 +63,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
@@ -89,17 +89,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
version = "2.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.10"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab"
|
||||
checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -127,10 +127,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.31"
|
||||
version = "1.2.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
|
||||
checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
@@ -145,9 +146,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
@@ -162,9 +163,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.43"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f"
|
||||
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -182,9 +183,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.43"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65"
|
||||
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -194,9 +195,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.41"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
||||
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -224,14 +225,20 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3"
|
||||
|
||||
[[package]]
|
||||
name = "fs4"
|
||||
version = "0.12.0"
|
||||
@@ -244,9 +251,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -283,18 +290,18 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.176"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.53.3",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -305,24 +312,24 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
@@ -342,12 +349,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
version = "0.50.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -362,12 +368,6 @@ version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
@@ -382,9 +382,9 @@ checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.36"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
@@ -392,65 +392,50 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
version = "1.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
@@ -473,9 +458,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@@ -487,6 +472,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
name = "sanctum"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shared_no_std",
|
||||
@@ -501,33 +487,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52"
|
||||
checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -536,14 +533,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.142"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -605,9 +603,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -616,18 +614,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -688,14 +686,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
@@ -706,9 +704,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
@@ -824,28 +822,6 @@ dependencies = [
|
||||
"wdk-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.58.0"
|
||||
@@ -893,9 +869,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
@@ -940,7 +916,16 @@ version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.3",
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -961,19 +946,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.3"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -984,9 +969,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@@ -996,9 +981,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -1008,9 +993,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
@@ -1020,9 +1005,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -1032,9 +1017,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -1044,9 +1029,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@@ -1056,9 +1041,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -1068,6 +1053,6 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
+4
-3
@@ -16,10 +16,10 @@ default = []
|
||||
nightly = ["wdk/nightly", "wdk-sys/nightly"]
|
||||
|
||||
[dependencies]
|
||||
wdk = "0.3.1"
|
||||
wdk-alloc = "0.3.1"
|
||||
wdk = "0.3"
|
||||
wdk-alloc = "0.3"
|
||||
wdk-sys = "0.4"
|
||||
wdk-panic = "0.3.1"
|
||||
wdk-panic = "0.3"
|
||||
# For local testing of my wdk open-source contributions
|
||||
# wdk = { path="../../windows-drivers-rs/crates/wdk", version = "0.3"}
|
||||
# wdk-alloc = { path="../../windows-drivers-rs/crates/wdk-alloc", version = "0.3"}
|
||||
@@ -32,6 +32,7 @@ serde_json = {version = "1.0", default-features = false, features = ["alloc"] }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"]}
|
||||
wdk-mutex = "1.1.0"
|
||||
strum = { version = "0.27", default-features = false, features = ["derive"] }
|
||||
anyhow = { version = "1.0", default-features = false }
|
||||
# wdk-mutex = {version = "1.1.0", path = "../../wdk_mutex"}
|
||||
|
||||
[profile.dev]
|
||||
|
||||
@@ -55,8 +55,6 @@ const NT_DEVICE_IO_CONTROL_FILE: u32 = 0x0007;
|
||||
const NT_CREATE_FILE_SSN: u32 = 0x0055;
|
||||
const NT_TRACE_EVENT_SSN: u32 = 0x005e;
|
||||
|
||||
pub static g_alt_syscalls_enabled: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub struct AltSyscalls;
|
||||
|
||||
#[repr(C)]
|
||||
@@ -136,7 +134,7 @@ impl AltSyscalls {
|
||||
// SAFETY: Check the offset size will fit into a u32
|
||||
if rva_offset_callback > u32::MAX as _ {
|
||||
println!(
|
||||
"[sanctum] [-] OFfset calculation very wrong? Offset: {:#x}",
|
||||
"[sanctum] [-] Offset calculation very wrong? Offset: {:#x}",
|
||||
rva_offset_callback
|
||||
);
|
||||
return;
|
||||
@@ -176,7 +174,7 @@ impl AltSyscalls {
|
||||
}
|
||||
|
||||
// Enumerate all active processes and threads, and enable the relevant bits so that the alt syscall 'machine' can work :)
|
||||
Self::walk_active_processes_and_set_bits(AltSyscallStatus::Enable, None);
|
||||
// Self::walk_active_processes_and_set_bits(AltSyscallStatus::Enable, None);
|
||||
}
|
||||
|
||||
/// Sets the required context bits in memory on thread and KTHREAD.
|
||||
@@ -266,15 +264,6 @@ impl AltSyscalls {
|
||||
status: AltSyscallStatus,
|
||||
isolated_processes: Option<&[&str]>,
|
||||
) {
|
||||
match status {
|
||||
AltSyscallStatus::Enable => {
|
||||
g_alt_syscalls_enabled.store(true, Ordering::SeqCst);
|
||||
}
|
||||
AltSyscallStatus::Disable => {
|
||||
g_alt_syscalls_enabled.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let current_process = unsafe { IoGetCurrentProcess() };
|
||||
if current_process.is_null() {
|
||||
println!("[sanctum] [-] current_process was NULL");
|
||||
@@ -459,7 +448,7 @@ pub unsafe extern "system" fn syscall_handler(
|
||||
) -> i32 {
|
||||
// todo remove once ready for mass testing
|
||||
let proc_name = get_process_name().to_lowercase();
|
||||
if !proc_name.contains("malware.e") {
|
||||
if !proc_name.contains("notepad.e") && !proc_name.contains("alware.e") {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
use core::{ffi::c_void, iter::once, ptr::null_mut, sync::atomic::Ordering};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use anyhow::{Result, bail};
|
||||
use wdk::{nt_success, println};
|
||||
use wdk_sys::{
|
||||
_MODE::{KernelMode, UserMode},
|
||||
HANDLE, IO_NO_INCREMENT, KAPC, MEM_COMMIT, PAGE_EXECUTE_READ, PAGE_READWRITE, PKTHREAD,
|
||||
POOL_FLAG_NON_PAGED, PRKAPC, PVOID, UNICODE_STRING,
|
||||
ntddk::{
|
||||
ExAllocatePool2, ExFreePool, RtlCopyMemoryNonTemporal, RtlInitUnicodeString,
|
||||
ZwAllocateVirtualMemory,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
core::process_monitor::{MONITORED_FN_PTRS, SensitiveAPI},
|
||||
ffi::{
|
||||
GetCurrentThread, KeInitializeApc, KeInsertQueueApc, KeTestAlertThread, PKNORMAL_ROUTINE,
|
||||
ZwProtectVirtualMemory,
|
||||
},
|
||||
};
|
||||
|
||||
const SANCTUM_HOOK_DLL_PATH: &str = r"sanctum.dll";
|
||||
|
||||
/// Injects the sanctum DLL which hooks NTDLL into the current process (must be called from an image load callback).
|
||||
///
|
||||
/// ### With massive thanks to:
|
||||
/// - eversinc33 https://x.com/eversinc33 - who provided me access to his src for getting this to work :3
|
||||
/// - Dennis A. Babkin & Rbmm - helpful blog https://dennisbabkin.com/blog/?t=depths-of-windows-apc-aspects-of-asynchronous-procedure-call-internals-from-kernel-mode
|
||||
/// - 0xrepnz https://x.com/0xrepnz - cool blog https://repnz.github.io/posts/apc/kernel-user-apc-api/
|
||||
pub fn inject_dll() -> Result<()> {
|
||||
// Inject shellcode into the process to bootstrap the DLL injection
|
||||
let shellcode_va = write_shellcode_in_process_for_injection()?;
|
||||
// Queue and force the APC to execute LdrLoadDll to inject the DLL
|
||||
let _ = queue_apc_run_shellcode(shellcode_va, GetCurrentThread())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Queues two APC's to execute LdrLoadDll in the process which is being created. The first APC is the user
|
||||
/// routine which runs a shellcode bootstrap. The second is a kernel APC which forces the thread to become
|
||||
/// alertable, thus, immediately executing our usermode APC.
|
||||
fn queue_apc_run_shellcode(shellcode_addr: *const c_void, thread: PKTHREAD) -> Result<()> {
|
||||
if shellcode_addr.is_null() {
|
||||
bail!("Shellcode address was null.");
|
||||
}
|
||||
|
||||
//
|
||||
// Initialise kernel APC
|
||||
//
|
||||
|
||||
let kapc = unsafe {
|
||||
ExAllocatePool2(
|
||||
POOL_FLAG_NON_PAGED,
|
||||
size_of::<KAPC>() as u64,
|
||||
u32::from_le_bytes(*b"sanc"),
|
||||
)
|
||||
} as *mut KAPC;
|
||||
|
||||
unsafe {
|
||||
KeInitializeApc(
|
||||
&mut *kapc,
|
||||
thread,
|
||||
crate::ffi::_KAPC_ENVIRONMENT::OriginalApcEnvironment,
|
||||
kernel_prepare_inject_apc as *const c_void,
|
||||
rundown as *const c_void,
|
||||
null_mut(),
|
||||
KernelMode as i8,
|
||||
null_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize user mode APC to call LdrLoadDll
|
||||
//
|
||||
|
||||
let apc = unsafe {
|
||||
ExAllocatePool2(
|
||||
POOL_FLAG_NON_PAGED,
|
||||
size_of::<KAPC>() as u64,
|
||||
u32::from_le_bytes(*b"sanc"),
|
||||
)
|
||||
} as *mut KAPC;
|
||||
|
||||
unsafe {
|
||||
KeInitializeApc(
|
||||
&mut *apc,
|
||||
thread,
|
||||
crate::ffi::_KAPC_ENVIRONMENT::OriginalApcEnvironment,
|
||||
apc_callback_inject_sanctum as *const c_void, // failure = access violation
|
||||
rundown as *const c_void,
|
||||
shellcode_addr,
|
||||
UserMode as i8,
|
||||
null_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
let status =
|
||||
unsafe { KeInsertQueueApc(&mut *apc, null_mut(), null_mut(), IO_NO_INCREMENT as _) };
|
||||
if !nt_success(status as _) {
|
||||
bail!("Failed to insert APC for shellcode execution. Code: {status:#X}");
|
||||
}
|
||||
|
||||
let status =
|
||||
unsafe { KeInsertQueueApc(&mut *kapc, null_mut(), null_mut(), IO_NO_INCREMENT as _) };
|
||||
if !nt_success(status as _) {
|
||||
bail!("Failed to insert KAPC for shellcode execution. Code: {status:#X}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe extern "C" fn rundown(apc: PRKAPC) {
|
||||
unsafe {
|
||||
ExFreePool(apc as _);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn apc_callback_inject_sanctum(
|
||||
apc: PRKAPC,
|
||||
_normal_routine: *mut c_void,
|
||||
_normal_context: *mut PVOID,
|
||||
_system_arg_1: *mut PVOID,
|
||||
_system_arg_2: *mut PVOID,
|
||||
) {
|
||||
unsafe { rundown(apc) };
|
||||
}
|
||||
|
||||
unsafe extern "C" fn kernel_prepare_inject_apc(
|
||||
apc: PRKAPC,
|
||||
_normal_routine: PKNORMAL_ROUTINE,
|
||||
_normal_context: *mut PVOID,
|
||||
_system_arg_1: *mut PVOID,
|
||||
_system_arg_2: *mut PVOID,
|
||||
) {
|
||||
unsafe { KeTestAlertThread(UserMode as i8) };
|
||||
unsafe { rundown(apc) };
|
||||
}
|
||||
|
||||
/// Write shellcode into the **current** process for which this is called. The shellcode written causes
|
||||
/// LdrLoadDll to load the Sanctum DLL into the target process.
|
||||
///
|
||||
/// # Returns
|
||||
/// The virtual address within the target process of where the shellcode was written, or an error.
|
||||
fn write_shellcode_in_process_for_injection() -> Result<*const c_void> {
|
||||
let path: Vec<u16> = SANCTUM_HOOK_DLL_PATH
|
||||
.encode_utf16()
|
||||
.chain(once(0))
|
||||
.collect();
|
||||
|
||||
let mut dll_path_to_inject = UNICODE_STRING::default();
|
||||
|
||||
unsafe { RtlInitUnicodeString(&mut dll_path_to_inject, path.as_ptr()) };
|
||||
|
||||
//
|
||||
// Shellcode to load a DLL into a process via LdrLoadDll
|
||||
//
|
||||
let mut shellcode = [
|
||||
0x48u8, 0x83, 0xEC, 0x28, // sub rsp, 0x28
|
||||
0x48, 0x31, 0xD2, // xor rdx, rdx
|
||||
0x48, 0x31, 0xC9, // xor rcx, rcx
|
||||
0x49, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov r8, [remoteUnicodeString]
|
||||
0x49, 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, // mov r9, [handleOut]
|
||||
0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov rax, [LdrLoadDll]
|
||||
0xFF, 0xD0, // call rax
|
||||
0x48, 0x83, 0xC4, 0x28, // add rsp, 0x28
|
||||
0xC3, // ret
|
||||
];
|
||||
|
||||
//
|
||||
// Allocate memory for the DLL name and the unicode_string struct
|
||||
//
|
||||
let dll_name_len: usize = dll_path_to_inject.Length as usize + size_of::<u16>(); // include space for null terminator
|
||||
let mut shellcode_size = shellcode.len() as u64;
|
||||
let mut total_size: u64 = shellcode_size
|
||||
+ size_of::<UNICODE_STRING>() as u64
|
||||
+ dll_name_len as u64
|
||||
+ size_of::<*const c_void>() as u64;
|
||||
let mut remote_shellcode_memory = null_mut();
|
||||
let mut remote_memory = null_mut();
|
||||
|
||||
let cur_proc_handle: HANDLE = (-1isize) as HANDLE;
|
||||
|
||||
let status = unsafe {
|
||||
ZwAllocateVirtualMemory(
|
||||
cur_proc_handle,
|
||||
&mut remote_shellcode_memory,
|
||||
0,
|
||||
&mut shellcode_size,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE,
|
||||
)
|
||||
};
|
||||
|
||||
if !nt_success(status) {
|
||||
bail!("DLL injection failed on ZwAllocateVirtualMemory with status: {status:#X}");
|
||||
}
|
||||
|
||||
let status = unsafe {
|
||||
ZwAllocateVirtualMemory(
|
||||
cur_proc_handle,
|
||||
&mut remote_memory,
|
||||
0,
|
||||
&mut total_size,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE,
|
||||
)
|
||||
};
|
||||
|
||||
if !nt_success(status) {
|
||||
bail!("DLL injection failed on ZwAllocateVirtualMemory 2 with status: {status:#X}");
|
||||
}
|
||||
|
||||
//
|
||||
// Structure of memory:
|
||||
//
|
||||
// Alloc 1 - Shellcode R(W)X (remote_shellcode_memory)
|
||||
//
|
||||
// Alloc 2 - UNICODE_STRING RW (remote_memory)
|
||||
// - OUT HANDLE
|
||||
// - Dll Name
|
||||
//
|
||||
|
||||
let remote_unicode_string = remote_memory;
|
||||
let remote_handle_out = unsafe { remote_memory.add(size_of::<UNICODE_STRING>()) };
|
||||
let remote_dll_name = (remote_memory as usize
|
||||
+ size_of::<UNICODE_STRING>()
|
||||
+ size_of::<*mut c_void>()) as *mut c_void;
|
||||
|
||||
let ldr_ld_dll_addr = {
|
||||
let p_mon_apis = MONITORED_FN_PTRS.load(Ordering::SeqCst);
|
||||
let mut addr: usize = 0;
|
||||
|
||||
if !p_mon_apis.is_null() {
|
||||
let mon = unsafe { &*p_mon_apis };
|
||||
|
||||
for api in &mon.inner {
|
||||
if api.1.1 == SensitiveAPI::LdrLoadDll {
|
||||
addr = *api.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addr
|
||||
};
|
||||
|
||||
if ldr_ld_dll_addr == 0 {
|
||||
bail!("Failed to get address of LdrLoadDll whilst trying DLL injection.")
|
||||
}
|
||||
|
||||
//
|
||||
// Memory patching
|
||||
//
|
||||
|
||||
const OFF_R8_IMM: usize = 12;
|
||||
const OFF_R9_IMM: usize = 22;
|
||||
const OFF_RAX_IMM: usize = 32;
|
||||
const PTR_WIDTH: usize = size_of::<usize>();
|
||||
|
||||
let val_r8 = remote_memory as usize;
|
||||
let val_r9 = remote_handle_out as usize;
|
||||
let val_rax = ldr_ld_dll_addr as usize;
|
||||
|
||||
//
|
||||
// Write to the shellcode block with the newly allocated addresses and addr of LdrLoadDll
|
||||
//
|
||||
shellcode[OFF_R8_IMM..OFF_R8_IMM + PTR_WIDTH].copy_from_slice(&val_r8.to_le_bytes());
|
||||
shellcode[OFF_R9_IMM..OFF_R9_IMM + PTR_WIDTH].copy_from_slice(&val_r9.to_le_bytes());
|
||||
shellcode[OFF_RAX_IMM..OFF_RAX_IMM + PTR_WIDTH].copy_from_slice(&val_rax.to_le_bytes());
|
||||
|
||||
unsafe {
|
||||
// Patch in the shellcode to the remote region in the target process
|
||||
RtlCopyMemoryNonTemporal(
|
||||
remote_shellcode_memory,
|
||||
shellcode.as_ptr() as *const _,
|
||||
shellcode_size,
|
||||
);
|
||||
|
||||
// Write the DLL name
|
||||
RtlCopyMemoryNonTemporal(
|
||||
remote_dll_name,
|
||||
dll_path_to_inject.Buffer as *const _,
|
||||
dll_name_len as u64,
|
||||
);
|
||||
|
||||
let mut remote_unicode = UNICODE_STRING::default();
|
||||
remote_unicode.Length = dll_path_to_inject.Length;
|
||||
remote_unicode.MaximumLength = dll_path_to_inject.MaximumLength;
|
||||
remote_unicode.Buffer = remote_dll_name as *mut u16;
|
||||
|
||||
RtlCopyMemoryNonTemporal(
|
||||
remote_unicode_string,
|
||||
&remote_unicode as *const UNICODE_STRING as *const c_void,
|
||||
size_of::<UNICODE_STRING>() as u64,
|
||||
);
|
||||
|
||||
//
|
||||
// Make shellcode executable
|
||||
//
|
||||
let mut op = 0;
|
||||
let status = ZwProtectVirtualMemory(
|
||||
cur_proc_handle,
|
||||
&mut remote_shellcode_memory,
|
||||
&mut shellcode_size,
|
||||
PAGE_EXECUTE_READ,
|
||||
&mut op,
|
||||
);
|
||||
|
||||
if !nt_success(status) {
|
||||
println!("Failed to mark shellcode memory as executable. Status: {status:#X}");
|
||||
|
||||
// todo free memory
|
||||
}
|
||||
}
|
||||
|
||||
Ok(remote_shellcode_memory)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod etw_mon;
|
||||
pub mod injection;
|
||||
pub mod process_callbacks;
|
||||
pub mod process_monitor;
|
||||
pub mod registry;
|
||||
|
||||
@@ -8,13 +8,13 @@ use core::{
|
||||
sync::atomic::Ordering,
|
||||
time::Duration,
|
||||
};
|
||||
use shared_no_std::driver_ipc::{HandleObtained, ProcessStarted, ProcessTerminated};
|
||||
use shared_no_std::driver_ipc::{HandleObtained, ProcessStarted};
|
||||
use wdk::println;
|
||||
use wdk_sys::{
|
||||
_IMAGE_INFO,
|
||||
_MODE::KernelMode,
|
||||
_OB_PREOP_CALLBACK_STATUS::OB_PREOP_SUCCESS,
|
||||
_UNICODE_STRING, APC_LEVEL, HANDLE, LARGE_INTEGER, NTSTATUS, OB_CALLBACK_REGISTRATION,
|
||||
_UNICODE_STRING, APC_LEVEL, HANDLE, NTSTATUS, OB_CALLBACK_REGISTRATION,
|
||||
OB_FLT_REGISTRATION_VERSION, OB_OPERATION_HANDLE_CREATE, OB_OPERATION_HANDLE_DUPLICATE,
|
||||
OB_OPERATION_REGISTRATION, OB_PRE_OPERATION_INFORMATION, OB_PREOP_CALLBACK_STATUS, PEPROCESS,
|
||||
PROCESS_ALL_ACCESS, PS_CREATE_NOTIFY_INFO, PsProcessType, STATUS_SUCCESS, STATUS_UNSUCCESSFUL,
|
||||
@@ -28,9 +28,12 @@ use wdk_sys::{
|
||||
|
||||
use crate::{
|
||||
DRIVER_MESSAGES, REGISTRATION_HANDLE,
|
||||
core::process_monitor::{LoadedModule, ProcessMonitor},
|
||||
core::{
|
||||
injection::inject_dll,
|
||||
process_monitor::{LoadedModule, ProcessMonitor},
|
||||
},
|
||||
device_comms::ImageLoadQueueForInjector,
|
||||
utils::unicode_to_string,
|
||||
utils::{duration_to_large_int, get_process_name, unicode_to_string},
|
||||
};
|
||||
|
||||
/// Callback function for a new process being created on the system.
|
||||
@@ -281,14 +284,22 @@ extern "C" fn image_load_callback(
|
||||
}
|
||||
|
||||
// SAFETY: Pointers validated above
|
||||
let image_name = unsafe { *image_name };
|
||||
let image_info = unsafe { *image_info };
|
||||
let image_name_string = get_image_name(image_name);
|
||||
let process_name = get_process_name();
|
||||
let pid = pid as u32;
|
||||
|
||||
let name_slice = slice_from_raw_parts(image_name.Buffer, (image_name.Length / 2) as usize);
|
||||
let name = String::from_utf16_lossy(unsafe { &*name_slice }).to_lowercase();
|
||||
// Gate-keep what processes we are monitoring
|
||||
if !(process_name.contains("otepad.e") || process_name.contains("alware.e")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if image_name_string.contains("kernel32.dll") {
|
||||
let _ = inject_dll();
|
||||
}
|
||||
|
||||
// In the event it is a DLL load, we want to grab & track its mappings
|
||||
if name.contains(".dll") && !name.contains("sanctum.dll") {
|
||||
if image_name_string.contains(".dll") {
|
||||
// todo hash check on the sanctum DLL to make sure an adversary isn't calling their malicious DLL `sanctum.dll`
|
||||
// which would interfere with what we are doing in this segment.
|
||||
|
||||
@@ -296,64 +307,62 @@ extern "C" fn image_load_callback(
|
||||
// on it.
|
||||
|
||||
let lm = LoadedModule::new(image_info.ImageBase as _, image_info.ImageSize as _);
|
||||
|
||||
ProcessMonitor::add_loaded_module(lm, &name, pid as u32);
|
||||
ProcessMonitor::add_loaded_module(lm, &image_name_string, pid);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we are into the 'meat' of the callback routine. To see why we are doing what we are doing here,
|
||||
// please refer to the function definition. In a nutshell, queue the process creation, the usermode engine
|
||||
// will poll the driver for new processes; the driver will wait for notification our DLL is injected.
|
||||
//
|
||||
// We can get around waiting on an IOCTL to come back from usermode by seeing when "sanctum.dll" is mapped into
|
||||
// the PID. This presents one potential 'vulnerability' in that a malicious process could attempt to inject a DLL
|
||||
// named "sanctum.dll" into our process; we can get around this by maintaining a second Grt mutex which contains
|
||||
// the PIDs that are pending the sanctum dll being injected. In the event the PID has been removed (aka we have a
|
||||
// sanctum.dll injected in) we know either foul play is detected (a TA is trying to exploit this vulnerability in the
|
||||
// implementation), or a unforeseen sanctum related error has occurred.
|
||||
// We force the sanctum DLL to load before kernel32 is loaded, therefore we need to block at kernelbase.
|
||||
// We cannot block at kernel32 as we need the thread to continue with its execution in order to have the Sanctum
|
||||
// loaded in.
|
||||
//
|
||||
// **NOTE**: Handling the draining of the `ImageLoadQueueForInjector` and adding the pid to the pending `Grt` is handled
|
||||
// in the `driver_communication` module - we dont need to worry about that implementation here, it will happen here
|
||||
// as if 'by magic'. See the implementation there for more details.
|
||||
// The thread loading kernelbase will loop until the sanctum DLL has notified the driver it has loaded and the
|
||||
// relocations have taken place.
|
||||
//
|
||||
// In either case; we can freeze the process and alert the user to possible malware / dump the process / kill the process
|
||||
// etc.
|
||||
//
|
||||
// Depending on performance; we could also fast hash the "sanctum.dll" bytes to see whether it matches the expected DLL -
|
||||
// this *may* be more performant than accessing the Grt, but for now, this works.
|
||||
//
|
||||
// todo would be nice to make an API for this as we will likely want to use this in various other places in the EDR.
|
||||
block_until_sanctum_loaded(&image_name_string, pid);
|
||||
}
|
||||
|
||||
// todo the match here should be done on the full path to accidental prevent name collisions
|
||||
if name.ends_with("sanctum.dll") {
|
||||
if ImageLoadQueueForInjector::remove_pid_from_injection_waitlist(pid as usize).is_err() {
|
||||
// todo handle threat detection here
|
||||
}
|
||||
}
|
||||
fn block_until_sanctum_loaded(image_name_string: &String, pid: u32) {
|
||||
if image_name_string.contains("kernelbase.dll") {
|
||||
let mut thread_sleep_time = duration_to_large_int(Duration::from_secs(1));
|
||||
let mut count = 0;
|
||||
|
||||
// For now, only inject into these processes whilst we test
|
||||
if !name.contains("malware.exe") {
|
||||
return;
|
||||
}
|
||||
loop {
|
||||
let _ = unsafe {
|
||||
KeDelayExecutionThread(KernelMode as _, TRUE as _, &mut thread_sleep_time)
|
||||
};
|
||||
|
||||
ImageLoadQueueForInjector::queue_process_for_usermode(pid as usize);
|
||||
if ProcessMonitor::is_sanc_dll_initialised(pid) {
|
||||
break;
|
||||
}
|
||||
|
||||
let delay_as_duration = Duration::from_millis(300);
|
||||
let mut thread_sleep_time = LARGE_INTEGER {
|
||||
QuadPart: -((delay_as_duration.as_nanos() / 100) as i64),
|
||||
};
|
||||
count += 1;
|
||||
if count > 4 {
|
||||
// todo some telemetry, this is either a bug or threat, we need this in otherwise the driver will go into
|
||||
// UB with current implementation :)
|
||||
println!(
|
||||
"Process started: {}, but did not load Sanctum dll, or it did not initialise. PID: {}",
|
||||
get_process_name(),
|
||||
unsafe { PsGetCurrentProcessId() as u32 }
|
||||
);
|
||||
|
||||
loop {
|
||||
// todo I'd rather use a KEVENT than a loop - just need to think about the memory model for it.
|
||||
// Tried implementing this now, but as im at POC phase it required quite a bit of a refactor, so i'll do this in the
|
||||
// future more likely. Leaving the todo in to work on this later :)
|
||||
// The least we can do is make the threat alertable so we aren't starving too many resources.
|
||||
let _ =
|
||||
unsafe { KeDelayExecutionThread(KernelMode as _, TRUE as _, &mut thread_sleep_time) };
|
||||
|
||||
if !ImageLoadQueueForInjector::pid_in_waitlist(pid as usize) {
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the image name of the process for which the image is being loaded, provided by the
|
||||
/// callback routine (we convert to a rust String).
|
||||
fn get_image_name(image_name: *mut UNICODE_STRING) -> String {
|
||||
if image_name.is_null() {
|
||||
// todo proper error
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let image_name = unsafe { *image_name };
|
||||
|
||||
let name_slice = slice_from_raw_parts(image_name.Buffer, (image_name.Length / 2) as usize);
|
||||
String::from_utf16_lossy(unsafe { &*name_slice }).to_lowercase()
|
||||
}
|
||||
|
||||
@@ -59,7 +59,10 @@ use crate::{
|
||||
device_comms::IoctlBuffer,
|
||||
ffi::{InitializeObjectAttributes, NtQueryInformationProcess},
|
||||
response::{ReportEventType, ReportInfo, contain_and_report},
|
||||
utils::{DriverError, eprocess_to_process_name, scan_usermode_module_for_function_address},
|
||||
utils::{
|
||||
DriverError, eprocess_to_process_name, get_process_name,
|
||||
scan_usermode_module_for_function_address,
|
||||
},
|
||||
};
|
||||
|
||||
pub static MONITORED_FN_PTRS: AtomicPtr<MonitoredApis> = AtomicPtr::new(null_mut());
|
||||
@@ -72,6 +75,8 @@ pub struct MonitoredApis {
|
||||
}
|
||||
|
||||
mod process {
|
||||
use core::sync::atomic::AtomicBool;
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
|
||||
use crate::{
|
||||
@@ -81,7 +86,7 @@ mod process {
|
||||
|
||||
/// A `Process` is a Sanctum driver representation of a Windows process so that actions it preforms, and is performed
|
||||
/// onto it, can be tracked and monitored.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Process {
|
||||
pub pid: u32,
|
||||
/// Parent pid
|
||||
@@ -103,6 +108,7 @@ mod process {
|
||||
// we don't readily have this data. If the driver is loaded as ELAM then this wouldn't be such
|
||||
// a problem.
|
||||
pub loaded_modules: Option<LoadedModules>,
|
||||
pub process_ready_for_ghost_hunting: AtomicBool,
|
||||
}
|
||||
|
||||
impl Process {
|
||||
@@ -439,11 +445,50 @@ impl ProcessMonitor {
|
||||
let _ = process_lock.remove(&pid);
|
||||
}
|
||||
|
||||
/// Marks a process as being ready for ghost hunting once everything has been loaded and switched on.
|
||||
///
|
||||
/// This function should be called after the Sanctum DLL is loaded into the process, and alt syscalls are turned on
|
||||
/// on the image load notification.
|
||||
pub fn mark_process_ready_for_ghost_hunting(pid: u32) {
|
||||
let mut process_lock = ProcessMonitor::get_mtx_inner();
|
||||
|
||||
if let Some(process) = process_lock.get_mut(&pid) {
|
||||
process
|
||||
.process_ready_for_ghost_hunting
|
||||
.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_sanc_dll_initialised(pid: u32) -> bool {
|
||||
let process_lock = ProcessMonitor::get_mtx_inner();
|
||||
|
||||
if let Some(process) = process_lock.get(&pid) {
|
||||
return process
|
||||
.process_ready_for_ghost_hunting
|
||||
.load(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
// todo this is an error..
|
||||
false
|
||||
}
|
||||
|
||||
/// Notifies the Ghost Hunting management that a new huntable event has occurred.
|
||||
pub fn ghost_hunt_add_event(signal: Syscall) {
|
||||
let mut process_lock = ProcessMonitor::get_mtx_inner();
|
||||
|
||||
if let Some(process) = process_lock.get_mut(&signal.pid) {
|
||||
// If the process is not yet ready for ghost hunting (aka the Sanc DLL isn't fully
|
||||
// loaded yet)
|
||||
if !process
|
||||
.process_ready_for_ghost_hunting
|
||||
.load(Ordering::SeqCst)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Process is ready for GH, so add..
|
||||
println!("[sanctum] [*******] Adding event.. {signal:?}");
|
||||
|
||||
let mut current_time = LARGE_INTEGER::default();
|
||||
unsafe { KeQuerySystemTimePrecise(&mut current_time) };
|
||||
|
||||
@@ -492,6 +537,8 @@ impl ProcessMonitor {
|
||||
// We can use the `extract_if` unstable API for `Vec`
|
||||
//
|
||||
|
||||
println!("Number of timers: {}", process.ghost_hunting_timers.len());
|
||||
|
||||
for timer in process.ghost_hunting_timers.extract_if(.., |t| {
|
||||
let mut current_time = LARGE_INTEGER::default();
|
||||
unsafe { KeQuerySystemTimePrecise(&mut current_time) };
|
||||
@@ -499,7 +546,10 @@ impl ProcessMonitor {
|
||||
let time_delta = unsafe { current_time.QuadPart - t.timer_start.QuadPart };
|
||||
time_delta > unsafe { max_time_allowed.QuadPart }
|
||||
}) {
|
||||
println!("GH timer expired. {timer:?}");
|
||||
println!(
|
||||
"GH timer expired. [{} {}], {timer:?}",
|
||||
process.pid, process.process_image
|
||||
);
|
||||
processes_to_terminate.push((process.pid, timer.clone()));
|
||||
}
|
||||
}
|
||||
@@ -508,7 +558,7 @@ impl ProcessMonitor {
|
||||
|
||||
if !processes_to_terminate.is_empty() {
|
||||
for p in processes_to_terminate {
|
||||
respond_to_gh_timer_expiry(p.0, &p.1);
|
||||
// respond_to_gh_timer_expiry(p.0, &p.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,7 +679,7 @@ unsafe extern "C" fn process_monitor_worker_thread(_: *mut c_void) {
|
||||
QuadPart: -((delay_as_duration.as_nanos() / 100) as i64),
|
||||
};
|
||||
|
||||
let max_time_allowed_for_ghost_hunting_delta = Duration::from_secs(1);
|
||||
let max_time_allowed_for_ghost_hunting_delta = Duration::from_secs(2);
|
||||
let max_time_allowed_for_ghost_hunting_delta = LARGE_INTEGER {
|
||||
QuadPart: ((max_time_allowed_for_ghost_hunting_delta.as_nanos() / 100) as i64),
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@ use crate::{
|
||||
|
||||
/// Whether to allow the syscall to dispatch by the dispatcher
|
||||
#[repr(i32)]
|
||||
#[derive(Debug)]
|
||||
pub enum AllowSyscall {
|
||||
No = 0x0,
|
||||
Yes = 0x1,
|
||||
@@ -93,6 +94,7 @@ static SYSCALL_PP_ACTIVE: AtomicBool = AtomicBool::new(false);
|
||||
static SYSCALL_CANCEL_THREAD: AtomicBool = AtomicBool::new(false);
|
||||
static SYSCALL_THREAD_HANDLE: AtomicPtr<c_void> = AtomicPtr::new(null_mut());
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KernelSyscallIntercept {
|
||||
pub syscall: Syscall,
|
||||
}
|
||||
@@ -108,6 +110,8 @@ impl KernelSyscallIntercept {
|
||||
//
|
||||
let rax = ktrap_frame.Rax as u32;
|
||||
|
||||
// println!("Intercepting {rax:#X}");
|
||||
|
||||
let syscall_data: (Option<Syscall>, AllowSyscall) = match rax {
|
||||
SSN_NT_ALLOCATE_VIRTUAL_MEMORY => Self::nt_allocate_vm(ktrap_frame),
|
||||
SSN_NT_OPEN_PROCESS => Self::nt_open_process(ktrap_frame),
|
||||
|
||||
@@ -9,7 +9,7 @@ use wdk_sys::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
alt_syscalls::{AltSyscallStatus, AltSyscalls, g_alt_syscalls_enabled},
|
||||
alt_syscalls::{AltSyscallStatus, AltSyscalls},
|
||||
utils::thread_to_process_name,
|
||||
};
|
||||
|
||||
@@ -37,12 +37,10 @@ pub unsafe extern "C" fn thread_callback(
|
||||
thread_id: *mut c_void,
|
||||
create: BOOLEAN,
|
||||
) {
|
||||
let pid = pid as u32;
|
||||
let thread_id_u32 = thread_id as u32;
|
||||
let _pid = pid as u32;
|
||||
let _thread_id_u32 = thread_id as u32;
|
||||
|
||||
if g_alt_syscalls_enabled.load(Ordering::SeqCst) {
|
||||
thread_reg_alt_callbacks(thread_id);
|
||||
}
|
||||
thread_reg_alt_callbacks(thread_id);
|
||||
}
|
||||
|
||||
pub fn thread_reg_alt_callbacks(thread_id: *mut c_void) {
|
||||
@@ -66,7 +64,7 @@ pub fn thread_reg_alt_callbacks(thread_id: *mut c_void) {
|
||||
};
|
||||
|
||||
AltSyscalls::configure_thread_for_alt_syscalls(ke_thread as *mut _, AltSyscallStatus::Enable);
|
||||
AltSyscalls::configure_process_for_alt_syscalls(ke_thread as *mut _);
|
||||
// AltSyscalls::configure_process_for_alt_syscalls(ke_thread as *mut _);
|
||||
|
||||
unsafe { ObfDereferenceObject(ke_thread as *mut _) };
|
||||
}
|
||||
|
||||
@@ -651,6 +651,61 @@ pub fn ioctl_dll_hook_syscall(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tells the driver a given process is ready for ghost hunting (to be called after successful re-locations of ntdll by
|
||||
/// sanctum.dll).
|
||||
pub fn ioctl_process_finished_sanc_dll_load(
|
||||
p_stack_location: *mut _IO_STACK_LOCATION,
|
||||
pirp: PIRP,
|
||||
) -> NTSTATUS {
|
||||
let mut ioctl_buffer = IoctlBuffer::new(p_stack_location, pirp);
|
||||
if ioctl_buffer.receive().is_err() {
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
let input_data = ioctl_buffer.buf as *const _ as *const u32;
|
||||
if input_data.is_null() {
|
||||
println!("[sanctum] [-] Error receiving input data for ioctl_proc_r_gh.");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// SAFETY: Pointer validity checked above
|
||||
let pid = unsafe { *input_data };
|
||||
|
||||
ProcessMonitor::mark_process_ready_for_ghost_hunting(pid);
|
||||
|
||||
STATUS_SUCCESS
|
||||
}
|
||||
|
||||
pub fn ioctl_failed_to_inject_dll(
|
||||
p_stack_location: *mut _IO_STACK_LOCATION,
|
||||
pirp: PIRP,
|
||||
) -> Result<(), NTSTATUS> {
|
||||
let mut ioctl_buffer = IoctlBuffer::new(p_stack_location, pirp);
|
||||
ioctl_buffer.receive()?; // receive the data
|
||||
|
||||
let input_data = ioctl_buffer.buf as *const _ as *const u8;
|
||||
if input_data.is_null() {
|
||||
println!("[sanctum] [-] Error receiving input data ioctl_failed_to_inject_dll.");
|
||||
return Err(STATUS_UNSUCCESSFUL);
|
||||
}
|
||||
|
||||
// SAFETY: Pointer validity checked above
|
||||
let input_data = unsafe { from_raw_parts(input_data, ioctl_buffer.len as usize) };
|
||||
let pid: u32 = match serde_json::from_slice(&input_data) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
println!("Failed to parse JSON from user: {:?}", e);
|
||||
return Err(STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
};
|
||||
|
||||
if ImageLoadQueueForInjector::remove_pid_from_injection_waitlist(pid as usize).is_err() {
|
||||
// todo handle threat detection here (n.b. duplicate in image callbacks)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ImageLoadQueueSelector {
|
||||
Cache,
|
||||
@@ -777,9 +832,9 @@ impl ImageLoadQueueForInjector {
|
||||
|
||||
if lock.insert(pid) == false {
|
||||
println!(
|
||||
"[sanctum] [i] ImageLoadQueuePendingInjection had duplicate key for pid: {pid}, this should not occur."
|
||||
"[sanctum] [i] ImageLoadQueuePendingInjection had duplicate key for pid: {pid}. This requires some further \
|
||||
investigation at some point."
|
||||
);
|
||||
panic!(); // maybe bsod here? this state should never occur
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+63
-6
@@ -1,12 +1,13 @@
|
||||
// FFI for functions not yet implemented in the Rust Windows Driver project
|
||||
|
||||
use core::{ffi::c_void, ptr::null_mut};
|
||||
use core::{arch::asm, ffi::c_void, ptr::null_mut};
|
||||
|
||||
use wdk_sys::{
|
||||
_EVENT_TYPE::SynchronizationEvent,
|
||||
ACCESS_MASK, DISPATCH_LEVEL, FALSE, FAST_MUTEX, FM_LOCK_BIT, HANDLE, HANDLE_PTR, LIST_ENTRY,
|
||||
NTSTATUS, OBJECT_ATTRIBUTES, PDRIVER_OBJECT, PHANDLE, PIO_STACK_LOCATION, PIRP,
|
||||
POBJECT_ATTRIBUTES, PROCESSINFOCLASS, PSECURITY_DESCRIPTOR, PULONG, PUNICODE_STRING, ULONG,
|
||||
ACCESS_MASK, BOOLEAN, DISPATCH_LEVEL, FALSE, FAST_MUTEX, FM_LOCK_BIT, HANDLE, HANDLE_PTR,
|
||||
KPRIORITY, KPROCESSOR_MODE, LIST_ENTRY, NTSTATUS, OBJECT_ATTRIBUTES, PDRIVER_OBJECT, PHANDLE,
|
||||
PIO_STACK_LOCATION, PIRP, PKAPC, PKTHREAD, POBJECT_ATTRIBUTES, PRKAPC, PROCESSINFOCLASS,
|
||||
PSECURITY_DESCRIPTOR, PSIZE_T, PULONG, PUNICODE_STRING, PVOID, SIZE_T, ULONG,
|
||||
ntddk::{KeGetCurrentIrql, KeInitializeEvent},
|
||||
};
|
||||
|
||||
@@ -71,9 +72,7 @@ unsafe extern "system" {
|
||||
len: ULONG,
|
||||
return_len: PULONG,
|
||||
) -> NTSTATUS;
|
||||
}
|
||||
|
||||
unsafe extern "system" {
|
||||
pub unsafe fn ZwGetNextProcess(
|
||||
handle: HANDLE,
|
||||
access: ACCESS_MASK,
|
||||
@@ -90,8 +89,54 @@ unsafe extern "system" {
|
||||
flags: ULONG,
|
||||
new_thread_handle: PHANDLE,
|
||||
) -> NTSTATUS;
|
||||
|
||||
pub fn KeInitializeApc(
|
||||
Apc: PKAPC,
|
||||
Thread: PKTHREAD,
|
||||
ApcStateIndex: KAPC_ENVIRONMENT,
|
||||
KernelRoutine: *const c_void,
|
||||
RundownRoutine: *const c_void,
|
||||
NormalRoutine: *const c_void,
|
||||
ApcMode: KPROCESSOR_MODE,
|
||||
NormalContext: PVOID,
|
||||
);
|
||||
|
||||
pub fn KeInsertQueueApc(
|
||||
Apc: PKAPC,
|
||||
SystemArgument1: PVOID,
|
||||
SystemArgument2: PVOID,
|
||||
Increment: KPRIORITY,
|
||||
) -> BOOLEAN;
|
||||
|
||||
pub unsafe fn PsGetCurrentProcess() -> *const c_void;
|
||||
|
||||
pub fn ZwProtectVirtualMemory(
|
||||
ProcessHandle: HANDLE,
|
||||
BaseAddress: *mut PVOID,
|
||||
RegionSize: PSIZE_T,
|
||||
NewProtect: ULONG,
|
||||
OldProtect: PULONG,
|
||||
) -> NTSTATUS;
|
||||
|
||||
pub fn KeTestAlertThread(AlertMode: KPROCESSOR_MODE);
|
||||
}
|
||||
|
||||
pub type PKNORMAL_ROUTINE = unsafe extern "C" fn(PVOID, PVOID, PVOID);
|
||||
pub type PKRUNDOWN_ROUTINE = unsafe extern "C" fn(PRKAPC);
|
||||
pub type PKKERNEL_ROUTINE =
|
||||
unsafe extern "C" fn(PRKAPC, PKNORMAL_ROUTINE, *mut PVOID, *mut PVOID, *mut PVOID);
|
||||
|
||||
#[repr(C)]
|
||||
pub enum _KAPC_ENVIRONMENT {
|
||||
OriginalApcEnvironment,
|
||||
AttachedApcEnvironment,
|
||||
CurrentApcEnvironment,
|
||||
InsertApcEnvironment,
|
||||
}
|
||||
|
||||
pub type KAPC_ENVIRONMENT = _KAPC_ENVIRONMENT;
|
||||
pub type PKAPC_ENVIRONMENT = *mut _KAPC_ENVIRONMENT;
|
||||
|
||||
#[repr(C, packed(2))]
|
||||
pub struct IMAGE_DOS_HEADER {
|
||||
pub e_magic: u16,
|
||||
@@ -203,3 +248,15 @@ pub struct PEB_LDR_DATA {
|
||||
pub Reserved2: [*mut c_void; 3],
|
||||
pub InMemoryOrderModuleList: LIST_ENTRY,
|
||||
}
|
||||
|
||||
pub fn GetCurrentThread() -> PKTHREAD {
|
||||
let mut k_thread: *const c_void = null_mut();
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov {}, gs:[0x188]",
|
||||
out(reg) k_thread,
|
||||
);
|
||||
}
|
||||
|
||||
k_thread as _
|
||||
}
|
||||
|
||||
+16
-5
@@ -42,10 +42,10 @@ use ffi::IoGetCurrentIrpStackLocation;
|
||||
use shared_no_std::{
|
||||
constants::{DOS_DEVICE_NAME, NT_DEVICE_NAME, VERSION_DRIVER},
|
||||
ioctl::{
|
||||
SANC_IOCTL_CHECK_COMPATIBILITY, SANC_IOCTL_DLL_SYSCALL, SANC_IOCTL_DRIVER_GET_IMAGE_LOADS,
|
||||
SANC_IOCTL_DRIVER_GET_IMAGE_LOADS_LEN, SANC_IOCTL_DRIVER_GET_MESSAGE_LEN,
|
||||
SANC_IOCTL_DRIVER_GET_MESSAGES, SANC_IOCTL_PING, SANC_IOCTL_PING_WITH_STRUCT,
|
||||
SANC_IOCTL_SEND_BASE_ADDRS,
|
||||
SANC_IOCTL_CHECK_COMPATIBILITY, SANC_IOCTL_DLL_INJECT_FAILED, SANC_IOCTL_DLL_SYSCALL,
|
||||
SANC_IOCTL_DRIVER_GET_IMAGE_LOADS, SANC_IOCTL_DRIVER_GET_IMAGE_LOADS_LEN,
|
||||
SANC_IOCTL_DRIVER_GET_MESSAGE_LEN, SANC_IOCTL_DRIVER_GET_MESSAGES, SANC_IOCTL_PING,
|
||||
SANC_IOCTL_PING_WITH_STRUCT, SANC_IOCTL_PROC_R_GH, SANC_IOCTL_SEND_BASE_ADDRS,
|
||||
},
|
||||
};
|
||||
use utils::{Log, LogLevel};
|
||||
@@ -75,7 +75,10 @@ mod utils;
|
||||
|
||||
use wdk_alloc::WdkAllocator;
|
||||
|
||||
use crate::core::process_monitor::{MONITORED_FN_PTRS, set_monitored_dll_fn_ptrs};
|
||||
use crate::{
|
||||
core::process_monitor::{MONITORED_FN_PTRS, set_monitored_dll_fn_ptrs},
|
||||
device_comms::{ioctl_failed_to_inject_dll, ioctl_process_finished_sanc_dll_load},
|
||||
};
|
||||
#[global_allocator]
|
||||
static GLOBAL_ALLOCATOR: WdkAllocator = WdkAllocator;
|
||||
|
||||
@@ -495,6 +498,14 @@ unsafe extern "C" fn handle_ioctl(_device: *mut DEVICE_OBJECT, pirp: PIRP) -> NT
|
||||
set_monitored_dll_fn_ptrs(p_stack_location, pirp);
|
||||
STATUS_SUCCESS
|
||||
}
|
||||
SANC_IOCTL_DLL_INJECT_FAILED => {
|
||||
if let Err(e) = ioctl_failed_to_inject_dll(p_stack_location, pirp) {
|
||||
return e;
|
||||
}
|
||||
|
||||
STATUS_SUCCESS
|
||||
}
|
||||
SANC_IOCTL_PROC_R_GH => ioctl_process_finished_sanc_dll_load(p_stack_location, pirp),
|
||||
|
||||
_ => {
|
||||
println!(
|
||||
|
||||
@@ -3,11 +3,11 @@ use core::{ffi::c_void, ptr::null_mut};
|
||||
use wdk::{nt_success, println};
|
||||
use wdk_sys::{
|
||||
CLIENT_ID, NTSTATUS, OBJ_KERNEL_HANDLE, OBJECT_ATTRIBUTES, PASSIVE_LEVEL, PROCESS_ALL_ACCESS,
|
||||
STATUS_UNSUCCESSFUL,
|
||||
STATUS_PROCESS_IS_TERMINATING, STATUS_UNSUCCESSFUL,
|
||||
ntddk::{KeGetCurrentIrql, ZwOpenProcess, ZwTerminateProcess},
|
||||
};
|
||||
|
||||
use crate::ffi::InitializeObjectAttributes;
|
||||
use crate::{ffi::InitializeObjectAttributes, utils::get_process_name};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DriverMode {
|
||||
@@ -23,7 +23,7 @@ pub struct Containment {}
|
||||
|
||||
impl Containment {
|
||||
pub fn contain_process(pid: u32) {
|
||||
println!("[sanctum] [i] Containing process: {pid}");
|
||||
println!("[sanctum] [i] Containing process: {pid}",);
|
||||
// todo actual containment
|
||||
|
||||
let _ = terminate_process(pid);
|
||||
@@ -66,7 +66,7 @@ fn terminate_process(pid: u32) -> NTSTATUS {
|
||||
|
||||
let status = unsafe { ZwTerminateProcess(handle, 1) };
|
||||
|
||||
if !nt_success(status) {
|
||||
if !nt_success(status) && status != STATUS_PROCESS_IS_TERMINATING {
|
||||
println!("[sanctum] [-] Error terminating process. Error code: {status:#X}");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use integrity::start_ntdll_integrity_monitor;
|
||||
use shared_no_std::ghost_hunting::DLLMessage;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::c_void;
|
||||
use std::mem;
|
||||
@@ -21,6 +22,7 @@ use windows::{
|
||||
core::PCSTR,
|
||||
};
|
||||
|
||||
use crate::ipc::send_ipc_to_engine;
|
||||
use crate::stubs::{
|
||||
nt_create_thread_ex_intercept, nt_open_process, nt_write_virtual_memory, virtual_alloc_ex,
|
||||
};
|
||||
@@ -75,6 +77,8 @@ unsafe extern "system" fn initialise_injected_dll(_: *mut c_void) -> u32 {
|
||||
// this must be called after the patching and BEFORE the resumption of all threads
|
||||
start_ntdll_integrity_monitor();
|
||||
|
||||
ioctl_initialised();
|
||||
|
||||
resume_all_threads(suspended_handles);
|
||||
|
||||
STATUS_SUCCESS.0 as _
|
||||
@@ -429,3 +433,8 @@ pub static SYSCALL_NUMBER: LazyLock<BTreeMap<&'static str, u32>> = LazyLock::new
|
||||
|
||||
syscall_num_repo
|
||||
});
|
||||
|
||||
/// Send an IOCTL to the driver which informs it the process is ready for ghost hunting
|
||||
fn ioctl_initialised() {
|
||||
send_ipc_to_engine(DLLMessage::ProcessReadyForGhostHunting);
|
||||
}
|
||||
|
||||
+31
-37
@@ -1,10 +1,14 @@
|
||||
//! Stubs that act as callback functions from syscalls.
|
||||
|
||||
use crate::{integrity::get_base_and_sz_ntdll, ipc::{send_ipc_to_engine}, SYSCALL_NUMBER};
|
||||
use crate::{SYSCALL_NUMBER, integrity::get_base_and_sz_ntdll, ipc::send_ipc_to_engine};
|
||||
use shared_no_std::ghost_hunting::{
|
||||
DLLMessage, NtAllocateVirtualMemoryData, NtCreateThreadExData, NtFunction, NtOpenProcessData, NtWriteVirtualMemoryData, Syscall, SyscallEventSource
|
||||
DLLMessage, NtAllocateVirtualMemoryData, NtCreateThreadExData, NtFunction, NtOpenProcessData,
|
||||
NtWriteVirtualMemoryData, Syscall, SyscallEventSource,
|
||||
};
|
||||
use std::{
|
||||
arch::{asm, naked_asm},
|
||||
ffi::c_void,
|
||||
};
|
||||
use std::{arch::{asm, naked_asm}, ffi::c_void};
|
||||
use windows::Win32::{
|
||||
Foundation::HANDLE,
|
||||
System::{
|
||||
@@ -28,21 +32,21 @@ pub fn nt_open_process(
|
||||
if !client_id.is_null() {
|
||||
let target_pid = unsafe { (*client_id).UniqueProcess.0 } as u32;
|
||||
let pid = unsafe { GetCurrentProcessId() };
|
||||
|
||||
let data = DLLMessage::SyscallWrapper(
|
||||
Syscall::from_sanctum_dll(
|
||||
pid,
|
||||
NtFunction::NtOpenProcess(
|
||||
NtOpenProcessData {
|
||||
target_pid,
|
||||
desired_mask: desired_access,
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// send the telemetry to the engine
|
||||
send_ipc_to_engine(data);
|
||||
// Currently only interested in foreign process handles..
|
||||
if target_pid != pid {
|
||||
println!("PID: {pid}, target: {target_pid}");
|
||||
let data = DLLMessage::SyscallWrapper(Syscall::from_sanctum_dll(
|
||||
pid,
|
||||
NtFunction::NtOpenProcess(NtOpenProcessData {
|
||||
target_pid,
|
||||
desired_mask: desired_access,
|
||||
}),
|
||||
));
|
||||
|
||||
// send the telemetry to the engine
|
||||
send_ipc_to_engine(data);
|
||||
}
|
||||
}
|
||||
|
||||
let ssn = *SYSCALL_NUMBER
|
||||
@@ -101,12 +105,7 @@ pub fn virtual_alloc_ex(
|
||||
protect_flags: protect,
|
||||
});
|
||||
|
||||
let syscall = DLLMessage::SyscallWrapper(
|
||||
Syscall::from_sanctum_dll(
|
||||
pid,
|
||||
data
|
||||
)
|
||||
);
|
||||
let syscall = DLLMessage::SyscallWrapper(Syscall::from_sanctum_dll(pid, data));
|
||||
|
||||
send_ipc_to_engine(syscall);
|
||||
}
|
||||
@@ -165,8 +164,8 @@ pub fn nt_write_virtual_memory(
|
||||
target_pid: remote_pid,
|
||||
base_address: base_addr_as_usize,
|
||||
buf_len: buf_len_as_usize,
|
||||
})
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
send_ipc_to_engine(DLLMessage::SyscallWrapper(data));
|
||||
|
||||
@@ -292,16 +291,16 @@ pub fn nt_create_thread_ex_intercept(
|
||||
target_pid: remote_pid,
|
||||
start_routine: start_routine as usize,
|
||||
argument: arg as usize,
|
||||
})
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
send_ipc_to_engine(DLLMessage::SyscallWrapper(data));
|
||||
}
|
||||
|
||||
|
||||
// proceed with the syscall
|
||||
let ssn = *SYSCALL_NUMBER
|
||||
.get("NtCreateThreadEx")
|
||||
.expect("failed to find function hook for NtCreateThreadEx");
|
||||
.get("NtCreateThreadEx")
|
||||
.expect("failed to find function hook for NtCreateThreadEx");
|
||||
|
||||
nt_create_thread_ex_naked(
|
||||
thread_handle,
|
||||
@@ -334,10 +333,5 @@ extern "system" fn nt_create_thread_ex_naked(
|
||||
attribute_list: *const c_void,
|
||||
ssn: u32,
|
||||
) {
|
||||
naked_asm!(
|
||||
"mov r10, rcx",
|
||||
"mov eax, [rsp+0x60]",
|
||||
"syscall",
|
||||
"ret",
|
||||
)
|
||||
}
|
||||
naked_asm!("mov r10, rcx", "mov eax, [rsp+0x60]", "syscall", "ret",)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ pub enum SyscallEventSource {
|
||||
pub enum DLLMessage {
|
||||
SyscallWrapper(Syscall),
|
||||
NtdllOverwrite,
|
||||
ProcessReadyForGhostHunting,
|
||||
}
|
||||
|
||||
/****************************** SYSCALLS *******************************/
|
||||
@@ -38,10 +39,7 @@ pub struct Syscall {
|
||||
}
|
||||
|
||||
impl Syscall {
|
||||
pub fn from_kernel(
|
||||
process_initiating_pid: u32,
|
||||
data: NtFunction
|
||||
) -> Self {
|
||||
pub fn from_kernel(process_initiating_pid: u32, data: NtFunction) -> Self {
|
||||
Self {
|
||||
pid: process_initiating_pid,
|
||||
source: SyscallEventSource::EventSourceKernel,
|
||||
@@ -49,10 +47,7 @@ impl Syscall {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_sanctum_dll(
|
||||
process_initiating_pid: u32,
|
||||
data: NtFunction
|
||||
) -> Self {
|
||||
pub fn from_sanctum_dll(process_initiating_pid: u32, data: NtFunction) -> Self {
|
||||
Self {
|
||||
pid: process_initiating_pid,
|
||||
source: SyscallEventSource::EventSourceSyscallHook,
|
||||
@@ -62,10 +57,10 @@ impl Syscall {
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, EnumIter)]
|
||||
/// A representation of an Nt function which contains an inner data carrier for arguments we wish
|
||||
/// A representation of an Nt function which contains an inner data carrier for arguments we wish
|
||||
/// to monitor related to that syscall directly.
|
||||
///
|
||||
/// This is also represented as a C style numbered enum which can be OR'ed into a flag. To see the
|
||||
///
|
||||
/// This is also represented as a C style numbered enum which can be OR'ed into a flag. To see the
|
||||
/// numeric types, see individual enum docs. To access this functionality, see [`NtFunction::as_mask`]
|
||||
pub enum NtFunction {
|
||||
/// None is provided to allow `EnumIter` to work, this should never match anything
|
||||
@@ -78,11 +73,11 @@ pub enum NtFunction {
|
||||
}
|
||||
|
||||
impl NtFunction {
|
||||
pub const M_NONE: u64 = 0x0;
|
||||
pub const M_NT_OPEN_PROCESS: u64 = 1 << 0;
|
||||
pub const M_NT_WRITE_VM: u64 = 1 << 1;
|
||||
pub const M_NT_ALLOC_VM: u64 = 1 << 2;
|
||||
pub const M_CREATE_THREAD_EX: u64 = 1 << 3;
|
||||
pub const M_NONE: u64 = 0x0;
|
||||
pub const M_NT_OPEN_PROCESS: u64 = 1 << 0;
|
||||
pub const M_NT_WRITE_VM: u64 = 1 << 1;
|
||||
pub const M_NT_ALLOC_VM: u64 = 1 << 2;
|
||||
pub const M_CREATE_THREAD_EX: u64 = 1 << 3;
|
||||
|
||||
pub fn as_mask(&self) -> u64 {
|
||||
let m = match self {
|
||||
@@ -128,4 +123,4 @@ pub struct NtCreateThreadExData {
|
||||
pub target_pid: u32,
|
||||
pub start_routine: usize,
|
||||
pub argument: usize,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,13 @@ pub const SANC_IOCTL_DLL_SYSCALL: u32 =
|
||||
pub const SANC_IOCTL_SEND_BASE_ADDRS: u32 =
|
||||
CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
|
||||
pub const SANC_IOCTL_DLL_INJECT_FAILED: u32 =
|
||||
CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
|
||||
/// Process ready for Ghost Hunting IOCTL
|
||||
pub const SANC_IOCTL_PROC_R_GH: u32 =
|
||||
CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x810, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
|
||||
// ****************** IOCTL MSG STRUCTS ******************
|
||||
|
||||
#[repr(C)]
|
||||
|
||||
@@ -27,4 +27,5 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tokio = {version="1", features = ["full"] }
|
||||
md-5 = "0.10"
|
||||
reqwest = {version = "0.12", features = []}
|
||||
reqwest = {version = "0.12", features = []}
|
||||
anyhow = "1.0.100"
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
utils::log::{Log, LogLevel},
|
||||
};
|
||||
|
||||
use super::{ipc_etw_consumer::run_ipc_for_etw, ipc_injected_dll::run_ipc_for_injected_dll};
|
||||
use super::ipc_injected_dll::run_ipc_for_injected_dll;
|
||||
|
||||
/// The core struct contains information on the core of the usermode engine where decisions are being made, and directly communicates
|
||||
/// with the kernel.
|
||||
@@ -104,10 +104,26 @@ impl Core {
|
||||
|
||||
if let Some(image_loads) = image_loads {
|
||||
for pid in image_loads {
|
||||
println!("[i] Target process detected, injecting EDR DLL...");
|
||||
// println!("[i] Target process detected, injecting EDR DLL into PID: {pid}...");
|
||||
if let Err(e) = inject_edr_dll(pid as _) {
|
||||
logger.log(LogLevel::Error, &format!("Error injecting DLL: {:?}", e));
|
||||
};
|
||||
println!("[-] Error injecting DLL: {e:?}");
|
||||
logger.log(LogLevel::Error, &format!("Error injecting DLL: {e:?}"));
|
||||
|
||||
//
|
||||
// We do get the occasional error here; most likely something we simply cannot inject into,
|
||||
// such as PPL / AppContainers, etc.
|
||||
// In the cases the injection failed, this is mostly OK. The DLL is at this point (thanks to
|
||||
// alt syscalls) detecting the abuse of direct / indirect syscalls.
|
||||
// Any process we cannot touch, the adversary will also have a hard time touching; thus we
|
||||
// aren't too bothered. As the Alt Syscalls can do everything the EDR's DLL was, then, we can
|
||||
// just keep the logic there.
|
||||
//
|
||||
// We do however want to send down an IOCTL to tell the driver we failed to inject, as to not
|
||||
// ghost hunt that process.
|
||||
//
|
||||
let mut mtx = driver_manager.lock().await;
|
||||
mtx.ioctl_dll_inject_failed(pid as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ use tokio::{
|
||||
};
|
||||
use windows::Win32::{Foundation::HANDLE, System::Pipes::GetNamedPipeClientProcessId};
|
||||
|
||||
use crate::utils::log::{Log, LogLevel};
|
||||
use crate::{
|
||||
driver_manager::SanctumDriverManager,
|
||||
utils::log::{Log, LogLevel},
|
||||
};
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
/// Starts the IPC server for the DLL injected into processes to communicate with
|
||||
pub async fn run_ipc_for_injected_dll(tx: Sender<Syscall>) {
|
||||
@@ -79,26 +83,10 @@ pub async fn run_ipc_for_injected_dll(tx: Sender<Syscall>) {
|
||||
Ok(message) => {
|
||||
match message {
|
||||
DLLMessage::SyscallWrapper(syscall) => {
|
||||
//
|
||||
// As part of the Ghost Hunting technique, one way I have thought up to bypass this would be to spoof an
|
||||
// IPC from the malware saying you are performing an operation via a hooked syscall; when in actuality you are
|
||||
// using direct syscalls to evade detection etc.
|
||||
//
|
||||
// Therefore, in order to combat this we can enforce IPC messages to contain the HasPid trait, so that all inbound
|
||||
// IPC messages contain a pid. We can then compare the pid offered by the message, with the PID the pipe actually came
|
||||
// from to verify the message authenticity.
|
||||
//
|
||||
let pipe_pid = match get_pid_from_pipe(&connected_client) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
// todo this is bad and should do something
|
||||
eprintln!("!!!!!!!!!!!!! GOT NO PID");
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
if pipe_pid != syscall.pid {
|
||||
// todo this is bad and should do something
|
||||
eprintln!("!!!!!!!!!!! PIDS DONT MATCH!");
|
||||
if let Err(e) = get_pipe_pid(syscall.pid, &connected_client)
|
||||
{
|
||||
println!("{e}");
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(e) = tx_cl.send(syscall).await {
|
||||
@@ -109,6 +97,14 @@ pub async fn run_ipc_for_injected_dll(tx: Sender<Syscall>) {
|
||||
// todo this needs handling
|
||||
println!("[i] NTDLL manipulation detected!");
|
||||
}
|
||||
DLLMessage::ProcessReadyForGhostHunting => {
|
||||
let mut m = SanctumDriverManager::new();
|
||||
let pid = get_pid_from_pipe(&connected_client)
|
||||
.expect("could not get pid");
|
||||
if let Err(e) = m.ioctl_notify_process_ready_for_gh(pid) {
|
||||
println!("Error notifying process for GH: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => logger.log(
|
||||
@@ -126,6 +122,32 @@ pub async fn run_ipc_for_injected_dll(tx: Sender<Syscall>) {
|
||||
});
|
||||
}
|
||||
|
||||
fn get_pipe_pid(syscall_pid: u32, connected_client: &NamedPipeServer) -> Result<u32> {
|
||||
//
|
||||
// As part of the Ghost Hunting technique, one way I have thought up to bypass this would be to spoof an
|
||||
// IPC from the malware saying you are performing an operation via a hooked syscall; when in actuality you are
|
||||
// using direct syscalls to evade detection etc.
|
||||
//
|
||||
// Therefore, in order to combat this we can enforce IPC messages to contain the HasPid trait, so that all inbound
|
||||
// IPC messages contain a pid. We can then compare the pid offered by the message, with the PID the pipe actually came
|
||||
// from to verify the message authenticity.
|
||||
//
|
||||
let pipe_pid = match get_pid_from_pipe(connected_client) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
// todo this is bad and should do something
|
||||
eprintln!("!!!!!!!!!!!!! GOT NO PID");
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
if pipe_pid != syscall_pid {
|
||||
// todo this is bad and should do something
|
||||
bail!("!!!!!!!!!!! PIDS DONT MATCH!");
|
||||
}
|
||||
|
||||
Ok(pipe_pid)
|
||||
}
|
||||
|
||||
/// Gets the PID that sent the named pipe, to ensure the pid we receive the message from is the same as the
|
||||
/// pid wrapped inside the message - prevents false messages being sent to the server where an attacker may wish
|
||||
/// to use a raw syscall and spoof the pipe message.
|
||||
|
||||
@@ -2,11 +2,19 @@ use std::ffi::c_void;
|
||||
|
||||
use shared_no_std::constants::SANCTUM_DLL_RELATIVE_PATH;
|
||||
use windows::{
|
||||
Win32::System::{
|
||||
Diagnostics::Debug::WriteProcessMemory,
|
||||
LibraryLoader::{GetModuleHandleA, GetProcAddress},
|
||||
Memory::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, VirtualAllocEx},
|
||||
Threading::{CreateRemoteThread, OpenProcess, PROCESS_VM_OPERATION, PROCESS_VM_WRITE},
|
||||
Win32::{
|
||||
Foundation::GetLastError,
|
||||
System::{
|
||||
Diagnostics::Debug::WriteProcessMemory,
|
||||
LibraryLoader::{GetModuleHandleA, GetProcAddress},
|
||||
Memory::{
|
||||
MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE, VirtualAllocEx,
|
||||
},
|
||||
Threading::{
|
||||
CreateRemoteThread, OpenProcess, PROCESS_CREATE_THREAD,
|
||||
PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE,
|
||||
},
|
||||
},
|
||||
},
|
||||
core::s,
|
||||
};
|
||||
@@ -17,11 +25,23 @@ use crate::utils::env::get_logged_in_username;
|
||||
/// processes which are newly created.
|
||||
pub fn inject_edr_dll(pid: u64) -> Result<(), ProcessErrors> {
|
||||
// Open the process
|
||||
let h_process =
|
||||
unsafe { OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, false, pid as u32) };
|
||||
let h_process = unsafe {
|
||||
OpenProcess(
|
||||
PROCESS_VM_OPERATION
|
||||
| PROCESS_VM_WRITE
|
||||
| PROCESS_CREATE_THREAD
|
||||
| PROCESS_QUERY_LIMITED_INFORMATION,
|
||||
false,
|
||||
pid as u32,
|
||||
)
|
||||
};
|
||||
let h_process = match h_process {
|
||||
Ok(h) => h,
|
||||
Err(_) => return Err(ProcessErrors::FailedToOpenProcess),
|
||||
Err(_) => {
|
||||
return Err(ProcessErrors::FailedToOpenProcess(unsafe {
|
||||
GetLastError().0 as i32
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Get a handle to Kernel32.dll
|
||||
@@ -51,7 +71,7 @@ pub fn inject_edr_dll(pid: u64) -> Result<(), ProcessErrors> {
|
||||
None,
|
||||
path_len,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
PAGE_READWRITE,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -94,7 +114,9 @@ pub fn inject_edr_dll(pid: u64) -> Result<(), ProcessErrors> {
|
||||
};
|
||||
|
||||
if h_thread.is_err() {
|
||||
return Err(ProcessErrors::FailedToCreateRemoteThread);
|
||||
return Err(ProcessErrors::FailedToCreateRemoteThread(unsafe {
|
||||
GetLastError().0 as _
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -108,6 +130,6 @@ pub enum ProcessErrors {
|
||||
BadFnAddress,
|
||||
BaseAddressNull,
|
||||
FailedToWriteMemory,
|
||||
FailedToCreateRemoteThread,
|
||||
FailedToOpenProcess,
|
||||
FailedToCreateRemoteThread(i32),
|
||||
FailedToOpenProcess(i32),
|
||||
}
|
||||
|
||||
@@ -3,17 +3,25 @@
|
||||
use crate::utils::log::LogLevel;
|
||||
|
||||
use super::driver_manager::SanctumDriverManager;
|
||||
use anyhow::{Result, bail};
|
||||
use core::str;
|
||||
use shared_no_std::{
|
||||
constants::VERSION_CLIENT,
|
||||
driver_ipc::ImageLoadQueues,
|
||||
ghost_hunting::Syscall,
|
||||
ioctl::{
|
||||
BaseAddressesOfMonitoredDlls, DriverMessages, SancIoctlPing, SANC_IOCTL_CHECK_COMPATIBILITY, SANC_IOCTL_DLL_SYSCALL, SANC_IOCTL_DRIVER_GET_IMAGE_LOADS, SANC_IOCTL_DRIVER_GET_IMAGE_LOADS_LEN, SANC_IOCTL_DRIVER_GET_MESSAGES, SANC_IOCTL_DRIVER_GET_MESSAGE_LEN, SANC_IOCTL_PING, SANC_IOCTL_PING_WITH_STRUCT, SANC_IOCTL_SEND_BASE_ADDRS
|
||||
BaseAddressesOfMonitoredDlls, DriverMessages, SANC_IOCTL_CHECK_COMPATIBILITY,
|
||||
SANC_IOCTL_DLL_INJECT_FAILED, SANC_IOCTL_DLL_SYSCALL, SANC_IOCTL_DRIVER_GET_IMAGE_LOADS,
|
||||
SANC_IOCTL_DRIVER_GET_IMAGE_LOADS_LEN, SANC_IOCTL_DRIVER_GET_MESSAGE_LEN,
|
||||
SANC_IOCTL_DRIVER_GET_MESSAGES, SANC_IOCTL_PING, SANC_IOCTL_PING_WITH_STRUCT,
|
||||
SANC_IOCTL_PROC_R_GH, SANC_IOCTL_SEND_BASE_ADDRS, SancIoctlPing,
|
||||
},
|
||||
};
|
||||
use std::{ffi::c_void, slice::from_raw_parts};
|
||||
use windows::{core::w, Win32::System::{LibraryLoader::GetModuleHandleW, IO::DeviceIoControl}};
|
||||
use windows::{
|
||||
Win32::System::{IO::DeviceIoControl, LibraryLoader::GetModuleHandleW},
|
||||
core::w,
|
||||
};
|
||||
|
||||
impl SanctumDriverManager {
|
||||
/// Checks the driver compatibility between the driver and user mode applications.
|
||||
@@ -99,8 +107,12 @@ impl SanctumDriverManager {
|
||||
}
|
||||
}
|
||||
|
||||
let k32_base = unsafe { GetModuleHandleW(w!("Kernel32.dll")) }.expect("Could not get k32 handle").0 as usize;
|
||||
let ntdll_base = unsafe { GetModuleHandleW(w!("ntdll.dll")) }.expect("Could not get ntdll handle").0 as usize;
|
||||
let k32_base = unsafe { GetModuleHandleW(w!("Kernel32.dll")) }
|
||||
.expect("Could not get k32 handle")
|
||||
.0 as usize;
|
||||
let ntdll_base = unsafe { GetModuleHandleW(w!("ntdll.dll")) }
|
||||
.expect("Could not get ntdll handle")
|
||||
.0 as usize;
|
||||
|
||||
let data = BaseAddressesOfMonitoredDlls {
|
||||
kernel32: k32_base,
|
||||
@@ -130,6 +142,38 @@ impl SanctumDriverManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Send an ioctl to the driver to notify the process is ready for ghost hunting
|
||||
pub fn ioctl_notify_process_ready_for_gh(&mut self, pid: u32) -> Result<()> {
|
||||
if self.handle_via_path.handle.is_none() {
|
||||
self.init_handle_via_registry();
|
||||
if self.handle_via_path.handle.is_none() {
|
||||
bail!("could not get handle to driver");
|
||||
}
|
||||
}
|
||||
|
||||
let result = unsafe {
|
||||
DeviceIoControl(
|
||||
self.handle_via_path.handle.unwrap(),
|
||||
SANC_IOCTL_PROC_R_GH,
|
||||
Some(&pid as *const _ as *const _),
|
||||
size_of::<u32>() as _,
|
||||
None,
|
||||
0,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
let msg = format!("Error from attempting IOCTL call. {e}");
|
||||
self.log.log(LogLevel::Error, &msg);
|
||||
|
||||
bail!(msg);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ping the driver from usermode
|
||||
pub fn ioctl_ping_driver(&mut self) -> String {
|
||||
//
|
||||
@@ -162,13 +206,6 @@ impl SanctumDriverManager {
|
||||
|
||||
// attempt the call
|
||||
let result = unsafe {
|
||||
// todo implementation for WriteFile
|
||||
// WriteFile(
|
||||
// self.handle_via_path.handle.unwrap(),
|
||||
// Some(message),
|
||||
// Some(&mut bytes_returned),
|
||||
// None,
|
||||
// )
|
||||
DeviceIoControl(
|
||||
self.handle_via_path.handle.unwrap(),
|
||||
SANC_IOCTL_PING,
|
||||
@@ -500,4 +537,38 @@ impl SanctumDriverManager {
|
||||
println!("[-] Failed to send IOCTL for DLL syscall event. {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ioctl_dll_inject_failed(&mut self, pid: u32) {
|
||||
//
|
||||
// Check the handle to the driver is valid, if not, attempt to initialise it.
|
||||
//
|
||||
|
||||
// todo improve how the error handling happens..
|
||||
if self.handle_via_path.handle.is_none() {
|
||||
// try 1 more time
|
||||
self.init_handle_via_registry();
|
||||
if self.handle_via_path.handle.is_none() {
|
||||
println!("[-] Error getting driver handle to send syscall ioctl from dll");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let message = serde_json::to_vec(&pid).expect("could not serialise Syscall to vector");
|
||||
|
||||
// attempt the call
|
||||
if let Err(e) = unsafe {
|
||||
DeviceIoControl(
|
||||
self.handle_via_path.handle.unwrap(),
|
||||
SANC_IOCTL_DLL_INJECT_FAILED,
|
||||
Some(message.as_ptr() as *const _),
|
||||
message.len() as u32,
|
||||
None,
|
||||
0,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
} {
|
||||
println!("[-] Failed to send IOCTL. {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
|
||||
use engine::Engine;
|
||||
use utils::log::Log;
|
||||
use windows::Win32::{
|
||||
Foundation::LUID,
|
||||
Security::{
|
||||
AdjustTokenPrivileges, LUID_AND_ATTRIBUTES, LookupPrivilegeValueW, SE_PRIVILEGE_ENABLED,
|
||||
TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,
|
||||
},
|
||||
System::Threading::{GetCurrentProcess, OpenProcessToken},
|
||||
};
|
||||
|
||||
mod core;
|
||||
mod driver_manager;
|
||||
@@ -18,6 +26,9 @@ mod utils;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
elevate("SeDebugPrivilege");
|
||||
elevate("SeImpersonatePrivilege");
|
||||
|
||||
//
|
||||
// Start the engine, this will kick off and run the application; note this should never return,
|
||||
// unless an error occurred.
|
||||
@@ -30,3 +41,30 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
error
|
||||
));
|
||||
}
|
||||
|
||||
fn elevate(name: &str) {
|
||||
println!("Elevating..");
|
||||
unsafe {
|
||||
let mut tok = Default::default();
|
||||
OpenProcessToken(
|
||||
GetCurrentProcess(),
|
||||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
||||
&mut tok,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
let mut luid = LUID::default();
|
||||
LookupPrivilegeValueW(None, &windows::core::HSTRING::from(name), &mut luid)
|
||||
.ok()
|
||||
.unwrap();
|
||||
let tp = TOKEN_PRIVILEGES {
|
||||
PrivilegeCount: 1,
|
||||
Privileges: [LUID_AND_ATTRIBUTES {
|
||||
Luid: luid,
|
||||
Attributes: SE_PRIVILEGE_ENABLED,
|
||||
}],
|
||||
};
|
||||
let res = AdjustTokenPrivileges(tok, false, Some(&tp), 0, None, None);
|
||||
println!("Result of altering token: {res:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user