10 Commits
1.0 ... 1.2.1

Author SHA1 Message Date
pyro57000
ec23b722f1 added some basic filtering 2025-10-20 13:10:55 -05:00
pyro57000
c97e05008d added ascii art 2025-10-17 10:51:07 -05:00
pyro57000
e08d19c565 updated the cargo.toml to optimize the binary
also removed some debugging print statements to
clean up the output when running in a real
environment.
2025-10-17 09:40:10 -05:00
Pyro57000
f1e482529c Update README.md 2025-10-16 17:48:21 -05:00
pyro57000
0b1dd6759f fixed formatting more 2025-10-16 17:46:58 -05:00
pyro57000
ad65897eff fixed readme formatting 2025-10-16 17:46:39 -05:00
Pyro57000
2573d9aef9 Update README.md 2025-10-16 17:43:57 -05:00
pyro57000
095da32fb8 updated the readme some more 2025-10-16 17:43:09 -05:00
pyro57000
bcee32dc90 updated the readme! 2025-10-16 17:39:33 -05:00
pyro57000
1e29992068 re-wrote the tool, it actually works now! 2025-10-16 17:34:16 -05:00
5 changed files with 1122 additions and 486 deletions

313
Cargo.lock generated
View File

@@ -2,26 +2,11 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.19" version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@@ -34,9 +19,9 @@ dependencies = [
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.11" version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
@@ -49,50 +34,29 @@ dependencies = [
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.1.3" version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.9" version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"once_cell_polyfill", "once_cell_polyfill",
"windows-sys 0.59.0", "windows-sys 0.60.2",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.1" version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]] [[package]]
name = "bytes" name = "bytes"
@@ -102,15 +66,15 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.1" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.41" version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -118,9 +82,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.41" version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -130,9 +94,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.41" version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@@ -142,9 +106,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.5" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
@@ -161,29 +125,12 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "io-uring"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.1" version = "1.70.1"
@@ -192,35 +139,19 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.174" version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.13" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [ dependencies = [
"autocfg",
"scopeguard", "scopeguard",
] ]
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.0.4" version = "1.0.4"
@@ -232,15 +163,6 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell_polyfill" name = "once_cell_polyfill"
version = "1.70.1" version = "1.70.1"
@@ -249,9 +171,9 @@ checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.4" version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [ dependencies = [
"lock_api", "lock_api",
"parking_lot_core", "parking_lot_core",
@@ -259,15 +181,15 @@ dependencies = [
[[package]] [[package]]
name = "parking_lot_core" name = "parking_lot_core"
version = "0.9.11" version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"windows-targets", "windows-link",
] ]
[[package]] [[package]]
@@ -278,37 +200,31 @@ checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.95" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.40" version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.13" version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@@ -326,19 +242,13 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.5" version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "slab"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.1" version = "1.15.1"
@@ -347,7 +257,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]] [[package]]
name = "snaferrous" name = "snaferrous"
version = "0.1.0" version = "1.2.0"
dependencies = [ dependencies = [
"clap", "clap",
"colored", "colored",
@@ -357,12 +267,12 @@ dependencies = [
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.10" version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -373,9 +283,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.104" version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -384,29 +294,26 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.46.1" version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
dependencies = [ dependencies = [
"backtrace",
"bytes", "bytes",
"io-uring",
"libc", "libc",
"mio", "mio",
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"slab",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.52.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
name = "tokio-macros" name = "tokio-macros"
version = "2.5.0" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -415,9 +322,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
@@ -443,21 +350,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.9" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
name = "windows-sys" name = "windows-link"
version = "0.52.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
dependencies = [
"windows-targets",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
@@ -465,7 +369,25 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"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]] [[package]]
@@ -474,14 +396,31 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu", "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm", "windows_i686_gnullvm 0.52.6",
"windows_i686_msvc", "windows_i686_msvc 0.52.6",
"windows_x86_64_gnu", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc", "windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"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]] [[package]]
@@ -490,44 +429,92 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"

View File

@@ -1,10 +1,17 @@
[package] [package]
name = "snaferrous" name = "snaferrous"
version = "0.1.0" version = "1.2.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
clap = { version = "4.5.41", features = ["derive"] } clap = { version = "4.5.41", features = ["derive"] }
colored = "3.0.0" colored = "3.0.0"
tokio = { version = "1.46.1", features = ["full"] } tokio = { version = "1.48.0", features = ["full"] }
walkdir = "2.5.0" walkdir = "2.5.0"
[profile.release]
opt-level ="z"
lto = true
strip = true
codegen-units = 1
panic = "abort"

View File

@@ -1,2 +1,70 @@
# snaferrous # snaferrous
snafflerish but rusty! snafflerish but rusty!
Currently it just looks for some hardcoded keywords to determine if a file has sensitive infomation, but it works!
# USAGE:
snaferrous [OPTIONS]
Options:
-o, --outfile <OUTFILE> path to save output file Defaults to not saving output.
--threads <THREADS> number of threads to use, default to 10.
-t, --targets <TARGETS> specific targets. should be comma separated.
-f, --filter_targets <targets> specific targets that should be ingored. Comma separated.
-l, --local scan the current hot's file system (defaults to false)
-d --disable_network disable network file discovery
-v, --verbose echo all found files to the console, regardless of keyword matching. (all files will still be saved to the log file)
-h, --help Print help (see more with '--help')
-V, --version Print version
# Compiling:
`git clone https://github.com/Pyro57000/snaferrous.git`
`cd snafferous`
`cargo build --target x86_64-pc-windows-gnu --release`
then your .exe will be in the targets/x86_64-pc-windows-gnu/release folder!
# Tool Output.
By default the tool will only print the found shares and files with keyword matches to the console.
If you give it the -v flag then it will print all files it finds to the console.
By default it only gives output to the console, but if you give it an outfile with the -o flag it will save findings to that file.
If it can't open the output file or write to it for any reason it will ask you if you want to continue without saving anyway.
Findings will be structured like the following:
shares - share found! {path to the share}
keyword matchs - keyword match at {path to the file}
file - file found at {path to the file}

425
src/main-bkup.rs Normal file
View File

@@ -0,0 +1,425 @@
/*
Author: Kevin (Kaged Pyro) Gunter
Purpose: I got tired of snaffler getting caught, so I rewrote it in rust, which edrs have trouble detecting.
*/
use clap::Parser;
use std::fmt::Debug;
use std::fs;
use std::fs::read_to_string;
use std::fs::OpenOptions;
use std::io::Write;
use std::ops::Index;
use std::path::PathBuf;
use std::process::exit;
use std::process::Command;
use std::thread;
use std::time::Duration;
use tokio;
use tokio::sync::mpsc::{channel, Sender, Receiver};
use colored::Colorize;
#[derive(Parser, Debug)]
#[command(version, about, long_about = Some("finds shares, but its written in rust which sometimes gets past EDR!"))]
struct Args{
#[arg(short, long, help = "path to save output file Defaults to not saving output.")]
outfile: Option<PathBuf>,
#[arg(short, long, help = "number of threads to use, default to 10. \nNote thre thread count will be doubled, one set for share finder tasks, and one set for file and infor finding tasks.")]
threads: Option<usize>,
#[arg(short, long, help = "specific targets. should be comma separated.")]
targets: Option<String>,
}
struct ShareFinder{
id: usize,
tx: Sender<Message>,
}
#[derive(Clone)]
struct Message{
source: MessageType,
destination: MessageType,
content: String,
}
#[derive(Clone, PartialEq)]
enum MessageType{
ShareMessage,
InfoMessage,
ControlMessage,
}
async fn find_shares(task: ShareFinder, mut rx: Receiver<Message>){
println!("{} share task started!", task.id);
let ping_recv = rx.recv().await;
if ping_recv.is_some(){
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: String::from("pong!")};
task.tx.send(message).await.unwrap();
}
loop{
if rx.capacity() == 0{
println!("rx is full for share finder {}", task.id);
}
let rx_res = rx.recv().await;
if rx_res.is_some(){
let computer = rx_res.unwrap().content;
if computer == String::from("||DONE||"){
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: format!("{}:||DONE||", task.id)};
task.tx.send(message).await.unwrap();
break;
}
println!("scanning {}", computer);
let share_list_res = Command::new("net").arg("view").arg(computer.clone()).arg("/all").output();
let mut error_string = String::new();
let mut success_string = String::new();
if share_list_res.is_ok(){
let output = share_list_res.unwrap();
if output.stdout.len() > 0{
success_string = String::from_utf8_lossy(&output.stdout).to_string();
}
if output.stderr.len() > 0{
error_string = String::from_utf8_lossy(&output.stderr).to_string();
}
}
else{
error_string = share_list_res.err().unwrap().to_string();
}
if error_string.len() > 0{
eprintln!("{}", "Error listing shares!".red());
eprint!("{}", error_string.red());
}
else if success_string.len() > 0{
for line in success_string.lines(){
if line.contains("Disk"){
let share_name = line.split_whitespace().collect::<Vec<&str>>()[0];
let share_path = format!("\\\\{}\\{}", computer, share_name);
let message = Message{source: MessageType::ShareMessage, destination: MessageType::InfoMessage, content: format!("{}:{}", task.id, share_path)};
task.tx.send(message).await.unwrap();
}
}
}
}
}
}
async fn find_info(task: ShareFinder, mut rx: Receiver<Message>){
println!("{} file task started!", task.id);
let ping_recv = rx.recv().await;
if ping_recv.is_some(){
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: String::from("pong!")};
task.tx.send(message).await.unwrap();
}
let files_to_read = vec![
".txt",
".ini",
".xml",
".json",
".config",
".conf",
".bat",
".cmd",
".sql",
".ps1",
".py",
".vbscript"
];
let interesting_info = vec![
"password",
"pass",
"user",
"api",
"key",
"credit card",
"cc",
"ssn",
"social Security",
"tax",
"i9",
"it",
"identified",
"username",
];
loop{
let rx_res = rx.recv().await;
if rx_res.is_some(){
let message = rx_res.unwrap();
let message_vec: Vec<&str> = message.content.split(":").collect();
let path = message_vec[1];
if path.contains("||DONE||"){
let done_message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:||DONE||", task.id)};
task.tx.send(done_message).await.unwrap();
}
for entry_res in walkdir::WalkDir::new(path){
if entry_res.is_ok(){
let entry = entry_res.unwrap();
let file_path = entry.into_path();
let mut file_name = String::new();
let mut file_content = String::new();
if file_path.file_name().is_some(){
file_name = file_path.file_name().unwrap().to_string_lossy().to_string();
}
for extension in &files_to_read{
if file_name.contains(extension){
let file_content_res = read_to_string(&file_path);
if file_content_res.is_ok(){
file_content = file_content_res.unwrap();
}
}
}
for thing in &interesting_info{
if file_name.contains(thing) || file_content.contains(thing){
let message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:Keyword match at {}", task.id, file_path.display())};
task.tx.send(message).await.unwrap();
}
else{
let message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:file found at {}", task.id, file_path.display())};
task.tx.send(message).await.unwrap();
}
}
}
}
}
}
}
#[tokio::main]
async fn main(){
let args = Args::parse();
let mut outfile = PathBuf::new();
let mut file_threads = 1;
let mut share_threads = 1;
let mut save = false;
let mut computers = Vec::new();
if args.outfile.is_some(){
outfile = args.outfile.unwrap();
save = true;
}
if args.threads.is_some(){
let threads = args.threads.unwrap() / 2;
file_threads = threads;
share_threads = threads;
}
if args.targets.is_some(){
println!("gathering the targets you gave me.");
let targets = args.targets.unwrap();
if targets.contains(","){
let split_targets: Vec<&str> = targets.split(",").collect();
for target in split_targets{
computers.push(target.to_string());
}
}
else{
computers.push(targets);
}
}
else{
println!("no targets given, proceeding with domain computer enumeration...");
println!("finding computers...");
let command_string = String::from("net group \"domain computers\" /domain");
let mut temp_file = fs::File::create("./temp.bat").unwrap();
write!(temp_file, "{}", command_string).unwrap();
let computer_res = Command::new(".\\temp.bat").output();
let mut error_string = String::new();
let mut success_string = String::new();
fs::remove_file("./temp.bat").unwrap();
if computer_res.is_ok(){
let output = computer_res.unwrap();
if output.stdout.len() > 0{
success_string = String::from_utf8_lossy(&output.stdout).to_string();
}
else if output.stderr.len() > 0{
error_string = String::from_utf8_lossy(&output.stderr).to_string();
}
}
else{
error_string = computer_res.err().unwrap().to_string();
}
if error_string.len() > 0{
eprintln!("{}", "error getting computers!".red());
eprintln!("{}", error_string.red());
exit(1);
}
if success_string.len() > 0{
for line in success_string.lines(){
if line.contains("$"){
let words:Vec<&str> = line.split_whitespace().collect();
for word in words{
let mut computer_name = word.to_string();
computer_name.pop();
println!("{} {}", "found".green(), computer_name.green());
computers.push(computer_name);
}
}
}
}
}
if share_threads > computers.len(){
share_threads = computers.len();
//file_threads = computers.len();
}
let mut share_handles = Vec::new();
let mut file_handles = Vec::new();
println!("computer enumeration finished, starting task finder threads...");
let (maintx, mut mainrx) = channel(1024);
let mut share_tasks = Vec::new();
let mut share_txes = Vec::new();
let mut file_tasks = Vec::new();
let mut file_txes = Vec::new();
for id in 0..share_threads{
println!("starting share task {}...", id);
let (share_tx,share_rx) = channel(1);
let new_share_task = ShareFinder{id, tx: maintx.clone()};
share_handles.push(tokio::spawn(find_shares(new_share_task, share_rx)));
share_tasks.push(id);
share_txes.push(share_tx.clone());
let ping_message = Message{source: MessageType::ControlMessage, destination: MessageType::ShareMessage, content: String::from("ping!")};
share_tx.send(ping_message).await.unwrap();
loop{
let rx_recv = mainrx.recv().await;
if rx_recv.is_some(){
let message = rx_recv.unwrap();
if message.content == String::from("pong!"){
println!("{} ready!", id);
break;
}
}
println!("didn't recieve file pong from {}", id);
}
}
for id in 0..file_threads{
println!("starting file task {}...", id);
let (file_tx, file_rx) = channel(1);
let new_file_task = ShareFinder{id, tx: maintx.clone()};
file_handles.push(tokio::spawn(find_info(new_file_task, file_rx)));
file_tasks.push(id);
file_txes.push(file_tx.clone());
let ping_message = Message{source: MessageType::ControlMessage, destination: MessageType::ShareMessage, content: String::from("ping!")};
file_tx.send(ping_message).await.unwrap();
loop{
let rx_recv = mainrx.recv().await;
if rx_recv.is_some(){
let message = rx_recv.unwrap();
if message.content == String::from("pong!"){
println!("{} ready!", id);
break;
}
}
println!("didn't recieve file pong from {}", id);
}
}
let mut current_computer = 0;
let mut shares_finished = false;
let mut files_finished = false;
let mut file_buffer = Vec::new();
let mut finished_counter = 0;
let mut empty_counter = 0;
let mut handled_lines = Vec::new();
loop {
if files_finished && shares_finished{
exit(0);
}
if !mainrx.is_empty(){
finished_counter = 0;
empty_counter = 0;
let rx_res = mainrx.recv().await;
if rx_res.is_some(){
let message = rx_res.unwrap();
match message.destination{
MessageType::ControlMessage => {
let message_vec: Vec<&str> = message.content.split(":").collect();
let _id = message_vec[0];
let message_content = message_vec[1].to_string();
match message_content{
_ => {
if !handled_lines.contains(&message_content){
if save{
let open_res = OpenOptions::new().append(true).create(true).open(&outfile);
if open_res.is_ok(){
let mut file = open_res.unwrap();
let write_res = write!(file, "{}\n", message_content);
if write_res.is_err(){
eprintln!("{}", "error writing to outfile!".red());
eprintln!("{}", write_res.err().unwrap().to_string().red());
}
}
}
println!("{}", message_content.green());
handled_lines.push(message_content);
}
}
}
}
MessageType::InfoMessage => {
file_buffer.push(message.content);
}
MessageType::ShareMessage => {}
}
}
}
let mut sent = false;
if !shares_finished{
for tx in &share_txes{
if tx.capacity() > 0{
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ShareMessage, content: computers[current_computer].clone()};
tx.send(message).await.unwrap();
sent = true;
break;
}
}
if sent{
current_computer +=1;
if current_computer == computers.len() {
shares_finished = true;
}
}
}
if shares_finished{
if file_buffer.len() == 0{
empty_counter += 1;
println!("empty counter: {}", empty_counter);
}
if empty_counter >= 100{
finished_counter +=1;
println!("finished counter: {}", finished_counter);
thread::sleep(Duration::from_millis(50));
}
}
if file_buffer.len() > 0{
let mut sent_index = Vec::new();
empty_counter = 0;
finished_counter = 0;
let mut current_tx = 0;
for index in 0 .. file_buffer.len() - 1{
let mut sent = false;
let message = Message{source: MessageType::ControlMessage, destination: MessageType::InfoMessage, content: file_buffer[index].clone()};
if file_txes[current_tx].capacity()> 0{
file_txes[current_tx].send(message).await.unwrap();
sent = true;
}
else{
current_tx += 1;
if current_tx == file_txes.len(){
current_tx = 0;
}
}
if sent{
sent_index.push(index);
}
}
for index in sent_index{
file_buffer.remove(index);
}
}
if finished_counter == 10{
files_finished = true;
}
}
}

View File

@@ -1,23 +1,34 @@
/*
Author: Kevin (Kaged Pyro) Gunter
Purpose: I got tired of snaffler getting caught, so I rewrote it in rust, which edrs have trouble detecting.
*/
use clap::Parser; use clap::Parser;
use std::fmt::Debug; use std::fmt::Debug;
use std::fs; use std::fs;
use std::fs::read_to_string; use std::fs::read_to_string;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::ops::Index;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use std::process::Command; use std::process::Command;
use std::thread;
use std::time::Duration;
use tokio;
use tokio::sync::mpsc::{channel, Sender, Receiver};
use colored::Colorize; use colored::Colorize;
use tokio;
use tokio::sync::mpsc::{channel, Sender};
/*
Author: Kevin (Kaged Pyro) Gunter
Purpose: I got tired of snaffler getting caught, so I rewrote it in rust, which edrs have trouble detecting.
*/
/*#[derive(Parser, Debug)]
#[command(version, about, long_about = Some("finds shares, but its written in rust which sometimes gets past EDR!"))]
struct Args{
#[arg(short, long, help = "path to save output file Defaults to not saving output.")]
outfile: Option<PathBuf>,
#[arg(short, long, help = "number of threads to use, default to 10. \\nNote thre thread count will be doubled, one set for share finder tasks, and one set for file and infor finding tasks.")]
threads: Option<usize>,
#[arg(short, long, help = "specific targets. should be comma separated.")]
targets: Option<String>,
}*/
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version, about, long_about = Some("finds shares, but its written in rust which sometimes gets past EDR!"))] #[command(version, about, long_about = Some("finds shares, but its written in rust which sometimes gets past EDR!"))]
@@ -25,186 +36,268 @@ struct Args{
#[arg(short, long, help = "path to save output file Defaults to not saving output.")] #[arg(short, long, help = "path to save output file Defaults to not saving output.")]
outfile: Option<PathBuf>, outfile: Option<PathBuf>,
#[arg(short, long, help = "number of threads to use, default to 10. \nNote thre thread count will be doubled, one set for share finder tasks, and one set for file and infor finding tasks.")] #[arg(long, help = "number of threads to use, default to 10.")]
threads: Option<usize>, threads: Option<usize>,
#[arg(short, long, help = "specific targets. should be comma separated.")] #[arg(short, long, help = "specific targets. should be comma separated.")]
targets: Option<String>, targets: Option<String>,
}
struct ShareFinder{ #[arg(short, long, help = "specific targets that should be ignored. comma separated.")]
id: usize, filter_targets: Option<String>,
tx: Sender<Message>,
#[arg(short, long, help = "echo all found files to the console, regardless of keyword matching. (all files will still be saved to the log file)")]
verbose: bool,
#[arg(short, long, help = "scan only the current host's files")]
local: bool,
#[arg(short, long, help = "disable network discovery")]
diable_network: bool
} }
#[derive(Clone)] #[derive(Clone)]
struct FinderTask{
id: usize,
target: String,
tasktype: TaskType,
}
#[derive(Clone)]
struct Finding{
path: String,
keyword: Option<bool>
}
struct Message{ struct Message{
source: MessageType, source: usize,
destination: MessageType, tasktype: TaskType,
content: String, finding: Option<Finding>,
task_finished: bool,
} }
#[derive(Clone, PartialEq)] #[derive(Clone)]
enum MessageType{ enum TaskType{
ShareMessage, Share,
InfoMessage, File,
ControlMessage, Info,
} }
async fn find_shares(task: ShareFinder, mut rx: Receiver<Message>){ async fn task_handler(id: usize, current_task: FinderTask, tx: Sender<Message>){
println!("{} share task started!", task.id); match current_task.tasktype{
let ping_recv = rx.recv().await; TaskType::Share => {
if ping_recv.is_some(){ println!("scanning {}", current_task.target);
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: String::from("pong!")}; let share_list_res = Command::new("net").arg("view").arg(current_task.target.clone()).arg("/all").output();
task.tx.send(message).await.unwrap();
}
loop{
if rx.capacity() == 0{
println!("rx is full for share finder {}", task.id);
}
let rx_res = rx.recv().await;
if rx_res.is_some(){
let computer = rx_res.unwrap().content;
if computer == String::from("||DONE||"){
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: format!("{}:||DONE||", task.id)};
task.tx.send(message).await.unwrap();
break;
}
println!("scanning {}", computer);
let share_list_res = Command::new("net").arg("view").arg(computer.clone()).arg("/all").output();
let mut error_string = String::new();
let mut success_string = String::new(); let mut success_string = String::new();
if share_list_res.is_ok(){ if share_list_res.is_ok(){
let output = share_list_res.unwrap(); let output = share_list_res.unwrap();
if output.stdout.len() > 0{ if output.stdout.len() > 0{
success_string = String::from_utf8_lossy(&output.stdout).to_string(); success_string = String::from_utf8_lossy(&output.stdout).to_string();
} }
if output.stderr.len() > 0{
error_string = String::from_utf8_lossy(&output.stderr).to_string();
}
} }
else{ if success_string.len() > 0{
error_string = share_list_res.err().unwrap().to_string(); let mut sent_lines = Vec::new();
}
if error_string.len() > 0{
eprintln!("{}", "Error listing shares!".red());
eprint!("{}", error_string.red());
}
else if success_string.len() > 0{
for line in success_string.lines(){ for line in success_string.lines(){
if line.contains("Disk"){ if line.contains("Disk"){
let share_name = line.split_whitespace().collect::<Vec<&str>>()[0]; let share_name = line.split_whitespace().collect::<Vec<&str>>()[0];
let share_path = format!("\\\\{}\\{}", computer, share_name); let share_path = format!("\\\\{}\\{}", current_task.target, share_name);
let message = Message{source: MessageType::ShareMessage, destination: MessageType::InfoMessage, content: format!("{}:{}", task.id, share_path)}; if !sent_lines.contains(&share_path){
task.tx.send(message).await.unwrap(); sent_lines.push(share_path.clone());
let finding = Finding{path: share_path, keyword: None};
let message = Message{source: id, tasktype: TaskType::Share, finding: Some(finding), task_finished: false};
tx.send(message).await.unwrap();
}
} }
} }
} }
} }
} TaskType::File => {
} let mut sent_lines = Vec::new();
for entry_res in walkdir::WalkDir::new(current_task.target.clone()){
async fn find_info(task: ShareFinder, mut rx: Receiver<Message>){
println!("{} file task started!", task.id);
let ping_recv = rx.recv().await;
if ping_recv.is_some(){
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: String::from("pong!")};
task.tx.send(message).await.unwrap();
}
let files_to_read = vec![
".txt",
".ini",
".xml",
".json",
".config",
".conf",
".bat",
".cmd",
".sql",
".ps1",
".py",
".vbscript"
];
let interesting_info = vec![
"password",
"pass",
"user",
"api",
"key",
"credit card",
"cc",
"ssn",
"social Security",
"tax",
"i9",
"it",
"identified",
"username",
];
loop{
let rx_res = rx.recv().await;
if rx_res.is_some(){
let message = rx_res.unwrap();
let message_vec: Vec<&str> = message.content.split(":").collect();
let path = message_vec[1];
if path.contains("||DONE||"){
let done_message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:||DONE||", task.id)};
task.tx.send(done_message).await.unwrap();
}
for entry_res in walkdir::WalkDir::new(path){
if entry_res.is_ok(){ if entry_res.is_ok(){
let entry = entry_res.unwrap(); let entry = entry_res.unwrap();
let file_path = entry.into_path(); let file_path = entry.into_path();
let mut file_name = String::new();
let mut file_content = String::new();
if file_path.file_name().is_some(){ if file_path.file_name().is_some(){
file_name = file_path.file_name().unwrap().to_string_lossy().to_string(); let file_path_string = file_path.display().to_string();
} if !sent_lines.contains(&file_path_string){
for extension in &files_to_read{ sent_lines.push(file_path_string.clone());
if file_name.contains(extension){ let finding = Finding{path: file_path_string, keyword: None};
let file_content_res = read_to_string(&file_path); let message = Message{source: id, tasktype: TaskType::File, finding: Some(finding), task_finished: false};
if file_content_res.is_ok(){ tx.send(message).await.unwrap();
file_content = file_content_res.unwrap();
}
}
}
for thing in &interesting_info{
if file_name.contains(thing) || file_content.contains(thing){
let message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:Keyword match at {}", task.id, file_path.display())};
task.tx.send(message).await.unwrap();
}
else{
let message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:file found at {}", task.id, file_path.display())};
task.tx.send(message).await.unwrap();
} }
} }
} }
} }
} }
TaskType::Info => {
let files_to_read = vec![
".txt",
".ini",
".xml",
".json",
".config",
".conf",
".bat",
".cmd",
".sql",
".ps1",
".py",
".vbscript"
];
let interesting_info = vec![
"password",
"pass",
"user",
"api",
"key",
"credit card",
"cc",
"ssn",
"social Security",
"tax",
"i9",
"it",
"identified",
"username",
"admin",
"administrator",
];
let mut file_content = String::new();
for extension in &files_to_read{
if current_task.target.contains(extension){
let file_content_res = read_to_string(&current_task.target);
if file_content_res.is_ok(){
file_content = file_content_res.unwrap();
}
}
}
let mut sent_lines = Vec::new();
for thing in &interesting_info{
let file_name = current_task.target.clone();
if file_name.contains(thing) || file_content.contains(thing){
let sent_line = format!("keyword {}", file_name);
if !sent_lines.contains(&sent_line){
sent_lines.push(sent_line);
let finding = Finding{path: file_name, keyword: Some(true)};
let message = Message{source: id, tasktype: TaskType::Info, finding: Some(finding), task_finished: false};
tx.send(message).await.unwrap();
}
}
else{
let sent_line = format!("file {}", file_name);
if !sent_lines.contains(&sent_line){
sent_lines.push(sent_line);
let finding = Finding{path: file_name, keyword: Some(false)};
let message = Message{source:id, tasktype: TaskType::Info, finding: Some(finding),task_finished: false};
tx.send(message).await.unwrap();
}
}
}
}
}
let message = Message{source: id, tasktype: TaskType::Share, finding: None, task_finished: true};
let send_res = tx.send(message).await;
if send_res.is_ok(){
send_res.unwrap();
}
else{
println!("{}", send_res.err().unwrap());
} }
} }
#[tokio::main] #[tokio::main]
async fn main(){ async fn main(){
print!{
"
██▒▒
▒▒████ ▓▓████████▓▓
▒▒██████████ ██████████████
██████████████ ▒▒██████████████
████████████████ ████████████████
████████████████▓▓ ▓▓████████████████
██████████████████▓▓██████████████████████
██████████████████████████████████████████ ▓▓██
██████████████████████████████████████████ ▒▒████████
▒▒████ ██████████████████████████████████████████████ ██████████████
████████▓▓ ▒▒██████████████████████████████████████████████████████████████████
████████████████▓▓████████████████████████████████████████████████████████████████████████
██████████████████████████████████████████████████████████████████████████████████████████▓▓
████████████████████████████████████████▓▓ ▓▓██████████████████████████████████
██████████████████████████████████▓▓ ████████████████████████████▒▒
██████████████████████████████ ████████████████████████
████████████████████████▓▓ ▓▓██████████████████
▒▒████████████████████▒▒ ████████████████
▒▒████████████████▒▒ /----------------------------------\\ ██████████████
██████████████▓▓ / \\ ████████████▓▓
██████████████ / \\ ▓▓████████████████████▓▓▓▓
▓▓████████████░░ / \\ ████████████████████████
░░██████████████ / |-| |-| \\ ▓▓██████████████████████▓▓
████████████████████████░░ / | | | | \\ ████████████████████████
▓▓██████████████████████████ / | | | | \\ ████████████████████████
██████████████████████████▒▒ / | | | | \\ ▒▒██████████████████████
██████████████████████████ / | | | | \\ ██████████████████████
░░██████████████████████████ / | | | | \\ ████████████████████▓▓
▒▒██████████████████████████ / | | | | \\ ████████████████▓▓
▓▓██████████████████████████ \\ | | | | / ██████████████
▓▓██████████████████████ \\ | | | | / ████████████
▓▓████████████████ \\ | | | | / ████████████
▓▓████████████ \\ | | | | / ████████████
████████████ \\ | | | | / ████████████
████████████▒▒ \\ | | | | / ██████████████▒▒
██████████████ \\ |-| |-| / ▓▓████████████████▓▓
██████████████ \\ / ██████████████████████
▒▒██████████████████ \\ / ▒▒████████████████████████
██████████████████████ \\ / ████████████████████████
██████████████████████████ \\----------------------------------/ ▓▓████████████████████████
██████████████████████████████ ░░████████████████████████▓▓
██████████████████████████████▒▒ ██████████████████████████
░░██████████████████████████████░░ ██████████████░░ ▒▒██████
████████████████████████████████ ████████████████
▒▒████████████████████████████████░░ ▒▒████████████████
██████▓▓▒▒ ██████████████████ ████████████████████
████████████████████▒▒ ▒▒████████████████████████
██████████████████████████▓▓▓▓▓▓████████████████████████████████▒▒
██████████████████████████████████████████████████████████████████
██████████████████████████████████████████████████████████████████
▓▓████████████████████████████████████████████████████████████████████
██████████████████████████████████████████████ ░░██████████████████
░░████████████████▒▒ ▒▒██████████████████ ████████████▒▒
▒▒██████████████ ████████████████ ██████
▓▓████████ ▓▓██████████████ ▒▒
▒▒████▒▒ ██████████████
██████████████
██████████████
__ __
/ _\\_ __ __ _ / _| ___ _ __ _ __ ___ _ _ ___
\\ \\| '_ \\ / _` | |_ / _ \\ '__| '__/ _ \\| | | / __|
_\\ \\ | | | (_| | _| __/ | | | | (_) | |_| \\__ \\
\\__/_| |_|\\__,_|_| \\___|_| |_| \\___/ \\__,_|___/
"
}
let args = Args::parse(); let args = Args::parse();
let mut outfile = PathBuf::new(); let mut outfile = PathBuf::new();
let mut file_threads = 5; let mut threads = 10;
let mut share_threads = 5;
let mut save = false; let mut save = false;
let mut computers = Vec::new(); let mut computers = Vec::new();
let mut filter_computers = Vec::new();
let mut network = true;
let file_filter = vec![
String::from("ADMIN$")
];
if args.outfile.is_some(){ if args.outfile.is_some(){
outfile = args.outfile.unwrap(); outfile = args.outfile.unwrap();
save = true; save = true;
} }
if args.threads.is_some(){ if args.threads.is_some(){
let threads = args.threads.unwrap() / 2; threads = args.threads.unwrap();
file_threads = threads; }
share_threads = threads; if args.diable_network{
network = false;
} }
if args.targets.is_some(){ if args.targets.is_some(){
println!("gathering the targets you gave me."); println!("gathering the targets you gave me.");
@@ -212,218 +305,274 @@ async fn main(){
if targets.contains(","){ if targets.contains(","){
let split_targets: Vec<&str> = targets.split(",").collect(); let split_targets: Vec<&str> = targets.split(",").collect();
for target in split_targets{ for target in split_targets{
computers.push(target.to_string()); computers.push(target.trim().to_lowercase());
} }
} }
else{ else{
computers.push(targets); computers.push(targets);
} }
} }
else{ if args.filter_targets.is_some(){
println!("no targets given, proceeding with domain computer enumeration..."); println!("gathering the filter you specified!");
println!("finding computers..."); let given_filter = args.filter_targets.unwrap();
let command_string = String::from("net group \"domain computers\" /domain"); let filters: Vec<&str> = given_filter.split(",").collect();
let mut temp_file = fs::File::create("./temp.bat").unwrap(); for filter in filters{
write!(temp_file, "{}", command_string).unwrap(); filter_computers.push(filter.trim().to_lowercase());
let computer_res = Command::new(".\\temp.bat").output(); }
let mut error_string = String::new(); }
let mut success_string = String::new(); let hostname_res = Command::new("hostname").output();
fs::remove_file("./temp.bat").unwrap(); if hostname_res.is_ok(){
if computer_res.is_ok(){ let hostname_output = hostname_res.unwrap();
let output = computer_res.unwrap(); if hostname_output.stdout.len() > 0{
if output.stdout.len() > 0{ let hostname_string = String::from_utf8_lossy(&hostname_output.stdout).to_string();
success_string = String::from_utf8_lossy(&output.stdout).to_string(); if args.local{
computers.push(hostname_string.trim().to_lowercase());
} }
else if output.stderr.len() > 0{ else{
error_string = String::from_utf8_lossy(&output.stderr).to_string(); filter_computers.push(hostname_string.trim().to_lowercase());
} }
} }
else{ if network{
error_string = computer_res.err().unwrap().to_string(); println!("no targets given, proceeding with domain computer enumeration...");
} println!("finding computers...");
if error_string.len() > 0{ let command_string = String::from("net group \"domain computers\" /domain");
eprintln!("{}", "error getting computers!".red()); let mut temp_file = fs::File::create("./temp.bat").unwrap();
eprintln!("{}", error_string.red()); write!(temp_file, "{}", command_string).unwrap();
exit(1); let computer_res = Command::new(".\\\\temp.bat").output();
} let mut error_string = String::new();
if success_string.len() > 0{ let mut success_string = String::new();
for line in success_string.lines(){ fs::remove_file("./temp.bat").unwrap();
if line.contains("$"){ if computer_res.is_ok(){
let words:Vec<&str> = line.split_whitespace().collect(); let output = computer_res.unwrap();
for word in words{ if output.stdout.len() > 0{
let mut computer_name = word.to_string(); success_string = String::from_utf8_lossy(&output.stdout).to_string();
computer_name.pop(); }
println!("{} {}", "found".green(), computer_name.green()); else if output.stderr.len() > 0{
computers.push(computer_name); error_string = String::from_utf8_lossy(&output.stderr).to_string();
}
}
else{
error_string = computer_res.err().unwrap().to_string();
}
if error_string.len() > 0{
eprintln!("{}", "error getting computers!".red());
eprintln!("{}", error_string.red());
exit(1);
}
if success_string.len() > 0{
for line in success_string.lines(){
if line.contains("$"){
let words:Vec<&str> = line.split_whitespace().collect();
for word in words{
let mut computer_name = word.to_string();
computer_name.pop();
computers.push(computer_name.trim().to_lowercase());
}
} }
} }
} }
} }
} }
if share_threads > computers.len(){ if filter_computers.len() > 0{
share_threads = computers.len(); computers.retain(|x| !filter_computers.iter().any(|y| y==x));
//file_threads = computers.len(); }
let mut tasks = Vec::new();
let mut id_counter = 0;
for computer in &computers{
println!("found {}", computer);
let new_task = FinderTask{id: id_counter, target: computer.clone(), tasktype: TaskType::Share};
tasks.push(new_task);
id_counter += 1;
} }
let mut share_handles = Vec::new();
let mut file_handles = Vec::new();
println!("computer enumeration finished, starting task finder threads..."); println!("computer enumeration finished, starting task finder threads...");
let (maintx, mut mainrx) = channel(1024); let (tx, mut rx) = channel(1024);
let mut share_tasks = Vec::new(); let mut running = Vec::new();
let mut share_txes = Vec::new(); let mut continue_wihtout_save = false;
let mut file_tasks = Vec::new(); loop{
let mut file_txes = Vec::new(); if running.len() < threads{
for id in 0..share_threads{ for _i in 0 .. threads{
println!("starting share task {}...", id); if tasks.len() > 0{
let (share_tx,share_rx) = channel(1); let task = tasks[0].clone();
let new_share_task = ShareFinder{id, tx: maintx.clone()}; tasks.remove(0);
share_handles.push(tokio::spawn(find_shares(new_share_task, share_rx))); running.push(task.id.clone());
share_tasks.push(id); tokio::spawn(task_handler(task.id, task, tx.clone()));
share_txes.push(share_tx.clone()); if running.len() >= threads{
let ping_message = Message{source: MessageType::ControlMessage, destination: MessageType::ShareMessage, content: String::from("ping!")}; break;
share_tx.send(ping_message).await.unwrap(); }
loop{ }
let rx_recv = mainrx.recv().await; else{
if rx_recv.is_some(){
let message = rx_recv.unwrap();
if message.content == String::from("pong!"){
println!("{} ready!", id);
break; break;
} }
} }
println!("didn't recieve file pong from {}", id);
} }
} if running.len() > 0{
for id in 0..file_threads{ let rxres = rx.try_recv();
println!("starting file task {}...", id); if rxres.is_ok(){
let (file_tx, file_rx) = channel(1); let mesage = rxres.unwrap();
let new_file_task = ShareFinder{id, tx: maintx.clone()}; if mesage.task_finished{
file_handles.push(tokio::spawn(find_info(new_file_task, file_rx))); for index in 0 .. running.len(){
file_tasks.push(id); if index == running.len(){
file_txes.push(file_tx.clone()); break;
let ping_message = Message{source: MessageType::ControlMessage, destination: MessageType::ShareMessage, content: String::from("ping!")}; }
file_tx.send(ping_message).await.unwrap(); else{
loop{ if running[index] == mesage.source{
let rx_recv = mainrx.recv().await; running.remove(index);
if rx_recv.is_some(){ }
let message = rx_recv.unwrap(); }
if message.content == String::from("pong!"){ }
println!("{} ready!", id);
break;
} }
} else {
println!("didn't recieve file pong from {}", id); let finding = mesage.finding.unwrap();
} match mesage.tasktype{
} TaskType::Share => {
let mut current_computer = 0; println!("{} {}", "share found!".green(), finding.path);
let mut shares_finished = false; if save{
let mut files_finished = false; let open_res = OpenOptions::new().create(true).append(true).open(&outfile);
let mut file_buffer = Vec::new(); if open_res.is_err(){
let mut finished_counter = 0; if !continue_wihtout_save{
let mut empty_counter = 0; eprintln!("{}", "error opening save file!".red());
let mut started = true; eprintln!("{}", open_res.err().unwrap().to_string().red());
let mut handled_lines = Vec::new(); let mut proceed = String::new();
loop { println!("continue anyway?");
if files_finished && shares_finished{ std::io::stdin().read_line(&mut proceed).unwrap();
break; if proceed.to_lowercase().contains("y"){
} continue_wihtout_save = true;
if !mainrx.is_empty(){ }
finished_counter = 0; else{
empty_counter = 0; exit(1);
started = true; }
let rx_res = mainrx.recv().await; }
if rx_res.is_some(){ }
let message = rx_res.unwrap(); else{
match message.destination{ let mut save_file = open_res.unwrap();
MessageType::ControlMessage => { let write_res = write!(save_file,"share found! {}\\n", finding.path);
let message_vec: Vec<&str> = message.content.split(":").collect(); if write_res.is_err(){
let _id = message_vec[0]; if !continue_wihtout_save{
let message_content = message_vec[1].to_string(); eprintln!("{}", "error writing to save file!".red());
match message_content{ eprintln!("{}", write_res.err().unwrap().to_string().red());
_ => { let mut proceed = String::new();
if !handled_lines.contains(&message_content){ println!("proceed without saving?");
if save{ std::io::stdin().read_line(&mut proceed).unwrap();
let open_res = OpenOptions::new().append(true).create(true).open(&outfile); if proceed.to_lowercase().contains("y"){
if open_res.is_ok(){ continue_wihtout_save = true;
let mut file = open_res.unwrap(); }
let write_res = write!(file, "{}\n", message_content); else{
if write_res.is_err(){ exit(1);
eprintln!("{}", "error writing to outfile!".red());
eprintln!("{}", write_res.err().unwrap().to_string().red());
} }
} }
} }
println!("{}", message_content.green()); else{
handled_lines.push(message_content); write_res.unwrap();
}
} }
} }
} if !file_filter.contains(&finding.path){
} let new_task = FinderTask{id: id_counter, tasktype: TaskType::File, target: finding.path};
MessageType::InfoMessage => { tasks.push(new_task);
file_buffer.push(message.content); id_counter += 1;
} }
MessageType::ShareMessage => {} }
} TaskType::File => {
} let new_task = FinderTask{id: id_counter, tasktype: TaskType::Info, target: finding.path};
} tasks.push(new_task);
let mut sent = false; id_counter += 1;
if !shares_finished{ }
for tx in &share_txes{ TaskType::Info => {
if tx.capacity() > 0{ if finding.keyword.unwrap(){
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ShareMessage, content: computers[current_computer].clone()}; println!("{} {}", "keyword match at".green(), finding.path.green());
tx.send(message).await.unwrap(); if save{
sent = true; let open_res = OpenOptions::new().create(true).append(true).open(&outfile);
break; if open_res.is_err(){
} if !continue_wihtout_save{
} eprintln!("{}", "error opening save file!".red());
if sent{ eprintln!("{}", open_res.err().unwrap().to_string().red());
current_computer +=1; let mut proceed = String::new();
if current_computer == computers.len() { println!("continue anyway?");
shares_finished = true; std::io::stdin().read_line(&mut proceed).unwrap();
} if proceed.to_lowercase().contains("y"){
} continue_wihtout_save = true;
} }
if shares_finished{ else{
if file_buffer.len() == 0{ exit(1);
empty_counter += 1; }
} }
if empty_counter >= 100{ }
finished_counter +=1; else{
thread::sleep(Duration::from_millis(50)); let mut save_file = open_res.unwrap();
} let write_res = write!(save_file,"keyword match at {}\\n", finding.path);
} if write_res.is_err(){
if file_buffer.len() > 0{ if !continue_wihtout_save{
let mut sent_index = Vec::new(); eprintln!("{}", "error writing to save file!".red());
empty_counter = 0; eprintln!("{}", write_res.err().unwrap().to_string().red());
finished_counter = 0; let mut proceed = String::new();
let mut current_tx = 0; println!("proceed without saving?");
for index in 0 .. file_buffer.len() - 1{ std::io::stdin().read_line(&mut proceed).unwrap();
let mut sent = false; if proceed.to_lowercase().contains("y"){
let message = Message{source: MessageType::ControlMessage, destination: MessageType::InfoMessage, content: file_buffer[index].clone()}; continue_wihtout_save = true;
if file_txes[current_tx].capacity()> 0{ }
file_txes[current_tx].send(message).await.unwrap(); else{
sent = true; exit(1);
} }
else{ }
current_tx += 1; }
if current_tx == file_txes.len(){ else{
current_tx = 0; write_res.unwrap();
}
}
}
}
else{
if args.verbose{
println!("{} {}", "file found at".green(), finding.path.green());
}
if save{
let open_res = OpenOptions::new().create(true).append(true).open(&outfile);
if open_res.is_err(){
if !continue_wihtout_save{
eprintln!("{}", "error opening save file!".red());
eprintln!("{}", open_res.err().unwrap().to_string().red());
let mut proceed = String::new();
println!("continue anyway?");
std::io::stdin().read_line(&mut proceed).unwrap();
if proceed.to_lowercase().contains("y"){
continue_wihtout_save = true;
}
else{
exit(1);
}
}
}
else{
let mut save_file = open_res.unwrap();
let write_res = write!(save_file,"file found! {}\\n", finding.path);
if write_res.is_err(){
if !continue_wihtout_save{
eprintln!("{}", "error writing to save file!".red());
eprintln!("{}", write_res.err().unwrap().to_string().red());
let mut proceed = String::new();
println!("proceed without saving?");
std::io::stdin().read_line(&mut proceed).unwrap();
if proceed.to_lowercase().contains("y"){
continue_wihtout_save = true;
}
else{
exit(1);
}
}
}
else{
write_res.unwrap();
}
}
}
}
}
} }
} }
if sent{
sent_index.push(index);
}
}
for index in sent_index{
file_buffer.remove(index);
} }
} }
if finished_counter == 10{ if running.len() == 0 && tasks.len() == 0 && rx.is_empty(){
for tx in &file_txes{ break;
let done_message = Message{source: MessageType::ControlMessage, destination: MessageType::InfoMessage, content: String::from("0:||DONE||")};
tx.send(done_message).await.unwrap();
}
files_finished = true;
} }
} }
} }