Merge pull request #77 from 0xflux/new-injection-experimental

Use APC for DLL injection
This commit is contained in:
flux
2025-10-11 17:21:58 +01:00
committed by GitHub
25 changed files with 1013 additions and 357 deletions
+1
View File
@@ -43,6 +43,7 @@
"IOCTLS",
"Irql",
"KAPC",
"kernelbase",
"KEVENT",
"KIRQL",
"KLDR",
Generated
+3 -2
View File
@@ -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",
+137 -152
View File
@@ -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
View File
@@ -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]
+3 -14
View File
@@ -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;
}
+319
View File
@@ -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
View File
@@ -1,4 +1,5 @@
pub mod etw_mon;
pub mod injection;
pub mod process_callbacks;
pub mod process_monitor;
pub mod registry;
+63 -54
View File
@@ -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()
}
+55 -5
View File
@@ -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),
};
+4
View File
@@ -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),
+5 -7
View File
@@ -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 _) };
}
+57 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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!(
+4 -4
View File
@@ -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}");
}
+9
View File
@@ -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
View File
@@ -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",)
}
+12 -17
View File
@@ -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,
}
}
+7
View File
@@ -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)]
+2 -1
View File
@@ -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"
+20 -4
View File
@@ -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);
}
}
}
}
+43 -21
View File
@@ -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.
+34 -12
View File
@@ -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),
}
+82 -11
View File
@@ -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);
}
}
}
+38
View File
@@ -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:?}");
}
}