scanmem alternative with concurrent memory scanning

Init, innit?

Victor Bjelkholm a91233aa

+1
.gitignore
···
···
+
/target
+677
Cargo.lock
···
···
+
# This file is automatically @generated by Cargo.
+
# It is not intended for manual editing.
+
version = 4
+
+
[[package]]
+
name = "aho-corasick"
+
version = "1.1.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+
dependencies = [
+
"memchr",
+
]
+
+
[[package]]
+
name = "anstream"
+
version = "0.6.18"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+
dependencies = [
+
"anstyle",
+
"anstyle-parse",
+
"anstyle-query",
+
"anstyle-wincon",
+
"colorchoice",
+
"is_terminal_polyfill",
+
"utf8parse",
+
]
+
+
[[package]]
+
name = "anstyle"
+
version = "1.0.10"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+
[[package]]
+
name = "anstyle-parse"
+
version = "0.2.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+
dependencies = [
+
"utf8parse",
+
]
+
+
[[package]]
+
name = "anstyle-query"
+
version = "1.1.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+
dependencies = [
+
"windows-sys 0.59.0",
+
]
+
+
[[package]]
+
name = "anstyle-wincon"
+
version = "3.0.7"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+
dependencies = [
+
"anstyle",
+
"once_cell",
+
"windows-sys 0.59.0",
+
]
+
+
[[package]]
+
name = "anyhow"
+
version = "1.0.96"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
+
+
[[package]]
+
name = "bitflags"
+
version = "2.9.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+
+
[[package]]
+
name = "byteorder"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+
[[package]]
+
name = "cfg-if"
+
version = "1.0.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+
[[package]]
+
name = "cfg_aliases"
+
version = "0.2.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+
[[package]]
+
name = "clap"
+
version = "4.5.31"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
+
dependencies = [
+
"clap_builder",
+
"clap_derive",
+
]
+
+
[[package]]
+
name = "clap_builder"
+
version = "4.5.31"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
+
dependencies = [
+
"anstream",
+
"anstyle",
+
"clap_lex",
+
"strsim",
+
]
+
+
[[package]]
+
name = "clap_derive"
+
version = "4.5.28"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
+
dependencies = [
+
"heck",
+
"proc-macro2",
+
"quote",
+
"syn",
+
]
+
+
[[package]]
+
name = "clap_lex"
+
version = "0.7.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+
[[package]]
+
name = "clipboard-win"
+
version = "5.4.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
+
dependencies = [
+
"error-code",
+
]
+
+
[[package]]
+
name = "colorchoice"
+
version = "1.0.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+
[[package]]
+
name = "crossbeam-deque"
+
version = "0.8.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+
dependencies = [
+
"crossbeam-epoch",
+
"crossbeam-utils",
+
]
+
+
[[package]]
+
name = "crossbeam-epoch"
+
version = "0.9.18"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+
dependencies = [
+
"crossbeam-utils",
+
]
+
+
[[package]]
+
name = "crossbeam-utils"
+
version = "0.8.21"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+
[[package]]
+
name = "either"
+
version = "1.14.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
+
+
[[package]]
+
name = "endian-type"
+
version = "0.1.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
+
+
[[package]]
+
name = "env_filter"
+
version = "0.1.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
+
dependencies = [
+
"log",
+
"regex",
+
]
+
+
[[package]]
+
name = "env_logger"
+
version = "0.11.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
+
dependencies = [
+
"anstream",
+
"anstyle",
+
"env_filter",
+
"humantime",
+
"log",
+
]
+
+
[[package]]
+
name = "errno"
+
version = "0.3.10"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+
dependencies = [
+
"libc",
+
"windows-sys 0.59.0",
+
]
+
+
[[package]]
+
name = "error-code"
+
version = "3.3.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
+
+
[[package]]
+
name = "fd-lock"
+
version = "4.0.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
+
dependencies = [
+
"cfg-if",
+
"rustix",
+
"windows-sys 0.52.0",
+
]
+
+
[[package]]
+
name = "getrandom"
+
version = "0.3.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
+
dependencies = [
+
"cfg-if",
+
"libc",
+
"wasi",
+
"windows-targets",
+
]
+
+
[[package]]
+
name = "heck"
+
version = "0.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+
[[package]]
+
name = "home"
+
version = "0.5.11"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+
dependencies = [
+
"windows-sys 0.59.0",
+
]
+
+
[[package]]
+
name = "humantime"
+
version = "2.1.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+
[[package]]
+
name = "is_terminal_polyfill"
+
version = "1.70.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+
[[package]]
+
name = "libc"
+
version = "0.2.170"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
+
+
[[package]]
+
name = "linux-raw-sys"
+
version = "0.4.15"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+
[[package]]
+
name = "log"
+
version = "0.4.26"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
+
+
[[package]]
+
name = "memchr"
+
version = "2.7.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+
[[package]]
+
name = "memmut"
+
version = "0.1.0"
+
dependencies = [
+
"anyhow",
+
"clap",
+
"env_logger",
+
"libc",
+
"log",
+
"rand",
+
"rayon",
+
"rustyline",
+
]
+
+
[[package]]
+
name = "nibble_vec"
+
version = "0.1.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
+
dependencies = [
+
"smallvec",
+
]
+
+
[[package]]
+
name = "nix"
+
version = "0.29.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+
dependencies = [
+
"bitflags",
+
"cfg-if",
+
"cfg_aliases",
+
"libc",
+
]
+
+
[[package]]
+
name = "once_cell"
+
version = "1.20.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
+
+
[[package]]
+
name = "ppv-lite86"
+
version = "0.2.20"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+
dependencies = [
+
"zerocopy 0.7.35",
+
]
+
+
[[package]]
+
name = "proc-macro2"
+
version = "1.0.93"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+
dependencies = [
+
"unicode-ident",
+
]
+
+
[[package]]
+
name = "quote"
+
version = "1.0.38"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+
dependencies = [
+
"proc-macro2",
+
]
+
+
[[package]]
+
name = "radix_trie"
+
version = "0.2.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
+
dependencies = [
+
"endian-type",
+
"nibble_vec",
+
]
+
+
[[package]]
+
name = "rand"
+
version = "0.9.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
+
dependencies = [
+
"rand_chacha",
+
"rand_core",
+
"zerocopy 0.8.21",
+
]
+
+
[[package]]
+
name = "rand_chacha"
+
version = "0.9.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+
dependencies = [
+
"ppv-lite86",
+
"rand_core",
+
]
+
+
[[package]]
+
name = "rand_core"
+
version = "0.9.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+
dependencies = [
+
"getrandom",
+
]
+
+
[[package]]
+
name = "rayon"
+
version = "1.10.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+
dependencies = [
+
"either",
+
"rayon-core",
+
]
+
+
[[package]]
+
name = "rayon-core"
+
version = "1.12.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+
dependencies = [
+
"crossbeam-deque",
+
"crossbeam-utils",
+
]
+
+
[[package]]
+
name = "regex"
+
version = "1.11.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+
dependencies = [
+
"aho-corasick",
+
"memchr",
+
"regex-automata",
+
"regex-syntax",
+
]
+
+
[[package]]
+
name = "regex-automata"
+
version = "0.4.9"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+
dependencies = [
+
"aho-corasick",
+
"memchr",
+
"regex-syntax",
+
]
+
+
[[package]]
+
name = "regex-syntax"
+
version = "0.8.5"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+
[[package]]
+
name = "rustix"
+
version = "0.38.44"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+
dependencies = [
+
"bitflags",
+
"errno",
+
"libc",
+
"linux-raw-sys",
+
"windows-sys 0.59.0",
+
]
+
+
[[package]]
+
name = "rustyline"
+
version = "15.0.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f"
+
dependencies = [
+
"bitflags",
+
"cfg-if",
+
"clipboard-win",
+
"fd-lock",
+
"home",
+
"libc",
+
"log",
+
"memchr",
+
"nix",
+
"radix_trie",
+
"unicode-segmentation",
+
"unicode-width",
+
"utf8parse",
+
"windows-sys 0.59.0",
+
]
+
+
[[package]]
+
name = "smallvec"
+
version = "1.14.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
+
+
[[package]]
+
name = "strsim"
+
version = "0.11.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+
[[package]]
+
name = "syn"
+
version = "2.0.98"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
+
dependencies = [
+
"proc-macro2",
+
"quote",
+
"unicode-ident",
+
]
+
+
[[package]]
+
name = "unicode-ident"
+
version = "1.0.17"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
+
+
[[package]]
+
name = "unicode-segmentation"
+
version = "1.12.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+
[[package]]
+
name = "unicode-width"
+
version = "0.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+
+
[[package]]
+
name = "utf8parse"
+
version = "0.2.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+
[[package]]
+
name = "wasi"
+
version = "0.13.3+wasi-0.2.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
+
dependencies = [
+
"wit-bindgen-rt",
+
]
+
+
[[package]]
+
name = "windows-sys"
+
version = "0.52.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+
dependencies = [
+
"windows-targets",
+
]
+
+
[[package]]
+
name = "windows-sys"
+
version = "0.59.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+
dependencies = [
+
"windows-targets",
+
]
+
+
[[package]]
+
name = "windows-targets"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+
dependencies = [
+
"windows_aarch64_gnullvm",
+
"windows_aarch64_msvc",
+
"windows_i686_gnu",
+
"windows_i686_gnullvm",
+
"windows_i686_msvc",
+
"windows_x86_64_gnu",
+
"windows_x86_64_gnullvm",
+
"windows_x86_64_msvc",
+
]
+
+
[[package]]
+
name = "windows_aarch64_gnullvm"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+
[[package]]
+
name = "windows_aarch64_msvc"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+
[[package]]
+
name = "windows_i686_gnu"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+
[[package]]
+
name = "windows_i686_gnullvm"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+
[[package]]
+
name = "windows_i686_msvc"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+
[[package]]
+
name = "windows_x86_64_gnu"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+
[[package]]
+
name = "windows_x86_64_gnullvm"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+
[[package]]
+
name = "windows_x86_64_msvc"
+
version = "0.52.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+
[[package]]
+
name = "wit-bindgen-rt"
+
version = "0.33.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
+
dependencies = [
+
"bitflags",
+
]
+
+
[[package]]
+
name = "zerocopy"
+
version = "0.7.35"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+
dependencies = [
+
"byteorder",
+
"zerocopy-derive 0.7.35",
+
]
+
+
[[package]]
+
name = "zerocopy"
+
version = "0.8.21"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478"
+
dependencies = [
+
"zerocopy-derive 0.8.21",
+
]
+
+
[[package]]
+
name = "zerocopy-derive"
+
version = "0.7.35"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+
dependencies = [
+
"proc-macro2",
+
"quote",
+
"syn",
+
]
+
+
[[package]]
+
name = "zerocopy-derive"
+
version = "0.8.21"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2"
+
dependencies = [
+
"proc-macro2",
+
"quote",
+
"syn",
+
]
+14
Cargo.toml
···
···
+
[package]
+
name = "memmut"
+
version = "0.1.0"
+
edition = "2024"
+
+
[dependencies]
+
anyhow = "1.0.96"
+
clap = {version = "4.5.31", features = ["derive"]}
+
env_logger = "0.11.6"
+
libc = "0.2.170"
+
log = "0.4.26"
+
rand = "0.9.0"
+
rayon = "1.10.0"
+
rustyline = "15.0.0"
+129
examples/pointers.rs
···
···
+
use rand::Rng;
+
use std::{
+
io::{self, Read, Write},
+
sync::mpsc,
+
thread,
+
time::Duration,
+
};
+
+
// This is a practice program to test if the main programs behaviour works fine
+
// This program has been validated and confirmed to work as expected with scanmem
+
+
// Structure to hold the "real" game state
+
struct GameState {
+
// Internal values that control the actual game state
+
values: Vec<i32>,
+
}
+
+
// Structure to manage what's displayed on screen
+
struct GameDisplay {
+
// Pointers to copies of values shown to the player
+
display_values: Vec<Box<i32>>,
+
}
+
+
impl GameState {
+
fn new(count: usize, rng: &mut impl Rng) -> Self {
+
let mut values = Vec::with_capacity(count);
+
for _ in 0..count {
+
values.push(rng.gen_range(1..10));
+
}
+
Self { values }
+
}
+
+
fn get_value(&self, index: usize) -> i32 {
+
self.values[index]
+
}
+
+
fn set_value(&mut self, index: usize, value: i32) {
+
self.values[index] = value;
+
}
+
}
+
+
impl GameDisplay {
+
fn new(count: usize) -> Self {
+
let mut display_values = Vec::with_capacity(count);
+
for _ in 0..count {
+
display_values.push(Box::new(0));
+
}
+
Self { display_values }
+
}
+
+
fn update_from_state(&mut self, state: &GameState) {
+
for (i, val) in state.values.iter().enumerate() {
+
*self.display_values[i] = *val;
+
}
+
}
+
+
fn get_display_ptr(&self, index: usize) -> *const i32 {
+
&*self.display_values[index] as *const i32
+
}
+
+
fn get_displayed_value(&self, index: usize) -> i32 {
+
*self.display_values[index]
+
}
+
}
+
+
fn main() {
+
let mut rng = rand::thread_rng();
+
+
const LEN: usize = 10;
+
+
let mut game_state = GameState::new(LEN, &mut rng);
+
let mut game_display = GameDisplay::new(LEN);
+
+
game_display.update_from_state(&game_state);
+
+
let (tx, rx) = mpsc::channel();
+
+
thread::spawn(move || {
+
let mut buffer = [0; 1];
+
loop {
+
if let Ok(_) = io::stdin().read_exact(&mut buffer) {
+
let _ = tx.send(());
+
}
+
}
+
});
+
+
let mut refresh_counter = 0;
+
+
loop {
+
print!("\x1B[2J\x1B[1;1H");
+
+
refresh_counter += 1;
+
if refresh_counter >= 3 {
+
refresh_counter = 0;
+
game_display.update_from_state(&game_state);
+
println!("(Display refreshed from game state)");
+
}
+
+
println!(
+
"Memory Scanner Target (Realistic Game Structure) - Press ENTER for new values, Ctrl+C to quit"
+
);
+
println!("Displayed values (what you see on screen):");
+
+
for i in 0..LEN {
+
let display_value = game_display.get_displayed_value(i);
+
let display_ptr = game_display.get_display_ptr(i);
+
println!(
+
"Value {}: {} (displayed at {:p})",
+
i, display_value, display_ptr
+
);
+
}
+
+
println!("\nInternal game state (would be hidden in a real game):");
+
for i in 0..LEN {
+
println!("Value {}: {}", i, game_state.get_value(i));
+
}
+
+
println!("\nPress ENTER to change all values...");
+
io::stdout().flush().unwrap();
+
+
if rx.recv_timeout(Duration::from_secs(1)).is_ok() {
+
for i in 0..LEN {
+
game_state.set_value(i, rng.gen_range(1..10));
+
}
+
game_display.update_from_state(&game_state);
+
refresh_counter = 0;
+
}
+
}
+
}
+55
examples/simple.rs
···
···
+
use rand::Rng;
+
use std::{
+
io::{self, Read, Write},
+
sync::mpsc,
+
thread,
+
time::Duration,
+
};
+
+
// This is a practice program to test if the main programs behaviour works fine
+
// This program has been validated and confirmed to work as expected with scanmem
+
+
fn main() {
+
let mut rng = rand::rng();
+
+
// Create an array of 10 random numbers
+
// const LEN: usize = 10_000_000;
+
const LEN: usize = 1000;
+
+
let mut numbers: Vec<i32> = vec![0; LEN];
+
+
for i in 0..LEN {
+
numbers[i] = rng.random_range(1..10);
+
}
+
+
let (tx, rx) = mpsc::channel();
+
+
thread::spawn(move || {
+
let mut buffer = [0; 1];
+
loop {
+
if let Ok(_) = io::stdin().read_exact(&mut buffer) {
+
let _ = tx.send(());
+
}
+
}
+
});
+
+
loop {
+
print!("\x1B[2J\x1B[1;1H");
+
+
println!("Memory Scanner Target - Press ENTER for new values, Ctrl+C to quit");
+
println!("Current values:");
+
+
for (i, &num) in numbers.iter().take(10).enumerate() {
+
println!("Index {}: {}", i, num);
+
}
+
+
println!("\nPress ENTER to generate new values...");
+
io::stdout().flush().unwrap();
+
+
if rx.recv_timeout(Duration::from_secs(1)).is_ok() {
+
for i in 0..LEN {
+
numbers[i] = rng.random_range(1..10);
+
}
+
}
+
}
+
}
+4
projectory.toml
···
···
+
name = "memmut"
+
description = "scanmem alternative with concurrent memory scanning"
+
tags = ["cli", "utility"]
+
langs = ["rust"]
+118
src/cli.rs
···
···
+
use anyhow::Result;
+
use rustyline::Editor;
+
use rustyline::error::ReadlineError;
+
+
use crate::process::Process;
+
use crate::scanner::{MemoryScanner, ScanValueType};
+
+
pub fn run_interactive_mode(process_name: &str) -> Result<()> {
+
println!(
+
"# Scanning for process \"{}\" like how pgrep -f works",
+
process_name
+
);
+
+
let process = Process::find_by_name(process_name)?;
+
let mut scanner = MemoryScanner::new(process)?;
+
+
println!("Please enter current value, or \"help\" for other commands.");
+
+
let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new()?;
+
let mut first_scan = true;
+
+
loop {
+
let prompt = if first_scan {
+
"> ".to_string()
+
} else {
+
format!("{}> ", scanner.get_matches_count())
+
};
+
+
let readline = rl.readline(&prompt);
+
match readline {
+
Ok(line) => {
+
let _ = rl.add_history_entry(&line);
+
let line = line.trim();
+
+
if line.is_empty() {
+
continue;
+
}
+
+
if line == "help" {
+
print_help();
+
continue;
+
}
+
+
if line == "exit" || line == "quit" {
+
break;
+
}
+
+
if line.starts_with("set ") {
+
handle_set_command(&mut scanner, line)?;
+
continue;
+
}
+
match parse_value(line) {
+
Ok(value) => {
+
if first_scan {
+
match scanner.scan_for_value(value) {
+
Ok(_) => first_scan = false,
+
Err(e) => println!("Error scanning: {}", e),
+
}
+
} else {
+
match scanner.filter_matches(value) {
+
Ok(_) => {}
+
Err(e) => println!("Error filtering: {}", e),
+
}
+
}
+
}
+
Err(e) => println!("Invalid value: {}", e),
+
}
+
}
+
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
+
println!("Exiting...");
+
break;
+
}
+
Err(err) => {
+
println!("Error: {}", err);
+
break;
+
}
+
}
+
}
+
+
Ok(())
+
}
+
+
fn print_help() {
+
println!("Available commands:");
+
println!(" <value> - Search for value or narrow down existing results");
+
println!(" set <idx> <value> - Set value at the memory address at index <idx>");
+
println!(" help - Show this help message");
+
println!(" exit/quit - Exit the program");
+
}
+
+
fn handle_set_command(scanner: &mut MemoryScanner, command: &str) -> Result<()> {
+
let parts: Vec<&str> = command.split_whitespace().collect();
+
if parts.len() != 3 {
+
anyhow::bail!("Invalid set command format. Use: set <idx> <value>");
+
}
+
+
let idx = match parts[1].parse::<usize>() {
+
Ok(idx) => idx,
+
Err(_) => anyhow::bail!("Invalid index: {}", parts[1]),
+
};
+
+
let value = match parse_value(parts[2])? {
+
ScanValueType::I32(val) => val,
+
_ => anyhow::bail!("Only i32 values are currently supported for setting"),
+
};
+
+
scanner.set_value(idx, value)
+
}
+
+
fn parse_value(value_str: &str) -> Result<ScanValueType> {
+
if let Ok(val) = value_str.parse::<i32>() {
+
return Ok(ScanValueType::I32(val));
+
}
+
+
// TODO missing implementation for all the other types
+
+
anyhow::bail!("Could not parse '{}' as a valid number", value_str)
+
}
+28
src/main.rs
···
···
+
mod cli;
+
mod memory;
+
mod process;
+
mod scanner;
+
+
use anyhow::Result;
+
use clap::Parser;
+
use log::error;
+
+
#[derive(Parser)]
+
#[command(author, version, about, long_about = None)]
+
struct Args {
+
/// Process name to attach to
+
process_name: String,
+
}
+
+
fn main() -> Result<()> {
+
env_logger::init();
+
let args = Args::parse();
+
+
match cli::run_interactive_mode(&args.process_name) {
+
Ok(_) => Ok(()),
+
Err(e) => {
+
error!("Error: {}", e);
+
Err(e)
+
}
+
}
+
}
+83
src/memory.rs
···
···
+
use anyhow::Result;
+
use libc::{c_void, iovec, process_vm_readv, process_vm_writev};
+
use log::info;
+
+
use crate::process::{MemoryRegion, Process};
+
+
pub struct MemoryReader {
+
process: Process,
+
}
+
+
impl MemoryReader {
+
pub fn new(process: Process) -> Self {
+
Self { process }
+
}
+
+
pub fn read_memory(&self, address: usize, size: usize) -> Result<Vec<u8>> {
+
let mut buffer = vec![0u8; size];
+
+
let local_iov = iovec {
+
iov_base: buffer.as_mut_ptr() as *mut c_void,
+
iov_len: size,
+
};
+
+
let remote_iov = iovec {
+
iov_base: address as *mut c_void,
+
iov_len: size,
+
};
+
+
let result = unsafe {
+
process_vm_readv(
+
self.process.pid as libc::pid_t,
+
&local_iov,
+
1,
+
&remote_iov,
+
1,
+
0,
+
)
+
};
+
+
if result == -1 {
+
let err = std::io::Error::last_os_error();
+
anyhow::bail!("Failed to read memory at 0x{:x}: {}", address, err);
+
}
+
+
Ok(buffer)
+
}
+
+
pub fn write_memory(&self, address: usize, data: &[u8]) -> Result<()> {
+
let local_iov = iovec {
+
iov_base: data.as_ptr() as *mut c_void,
+
iov_len: data.len(),
+
};
+
+
let remote_iov = iovec {
+
iov_base: address as *mut c_void,
+
iov_len: data.len(),
+
};
+
+
let result = unsafe {
+
process_vm_writev(
+
self.process.pid as libc::pid_t,
+
&local_iov,
+
1,
+
&remote_iov,
+
1,
+
0,
+
)
+
};
+
+
if result == -1 {
+
let err = std::io::Error::last_os_error();
+
anyhow::bail!("Failed to write memory at 0x{:x}: {}", address, err);
+
}
+
+
info!("setting *0x{:x} to {:?}...", address, data);
+
Ok(())
+
}
+
+
pub fn read_region(&self, region: &MemoryRegion) -> Result<Vec<u8>> {
+
let size = region.end_addr - region.start_addr;
+
self.read_memory(region.start_addr, size)
+
}
+
}
+109
src/process.rs
···
···
+
use anyhow::Result;
+
use log::info;
+
use std::fs;
+
use std::path::Path;
+
use std::str::FromStr;
+
+
pub struct Process {
+
pub pid: i32,
+
#[allow(dead_code)]
+
pub name: String,
+
}
+
+
#[derive(Debug, Clone)]
+
pub struct MemoryRegion {
+
pub start_addr: usize,
+
pub end_addr: usize,
+
#[allow(dead_code)]
+
pub permissions: String,
+
#[allow(dead_code)]
+
pub offset: usize,
+
#[allow(dead_code)]
+
pub pathname: String,
+
}
+
+
impl Process {
+
pub fn find_by_name(name: &str) -> Result<Self> {
+
// Search for process using pgrep-like functionality
+
let proc_dir = Path::new("/proc");
+
let mut found_pid = None;
+
+
for entry in fs::read_dir(proc_dir)? {
+
let entry = entry?;
+
let path = entry.path();
+
+
if !path.is_dir() {
+
continue;
+
}
+
+
let file_name = path.file_name().unwrap().to_string_lossy();
+
if !file_name.chars().all(|c| c.is_digit(10)) {
+
continue;
+
}
+
+
let pid = i32::from_str(&file_name)?;
+
let cmdline_path = path.join("cmdline");
+
+
if !cmdline_path.exists() {
+
continue;
+
}
+
+
let cmdline = fs::read_to_string(cmdline_path)?;
+
if cmdline.contains(name) {
+
println!("Process found with PID: {}", pid);
+
found_pid = Some(pid);
+
break;
+
}
+
}
+
+
match found_pid {
+
Some(pid) => Ok(Process {
+
pid,
+
name: name.to_string(),
+
}),
+
None => anyhow::bail!("Process '{}' not found", name),
+
}
+
}
+
+
pub fn get_memory_maps(&self) -> Result<Vec<MemoryRegion>> {
+
let maps_path = format!("/proc/{}/maps", self.pid);
+
info!("maps file located at {} opened.", maps_path);
+
+
let maps_content = fs::read_to_string(&maps_path)?;
+
let mut regions = Vec::new();
+
+
for line in maps_content.lines() {
+
let parts: Vec<&str> = line.split_whitespace().collect();
+
if parts.len() < 5 {
+
continue;
+
}
+
+
let addr_range: Vec<&str> = parts[0].split('-').collect();
+
if addr_range.len() != 2 {
+
continue;
+
}
+
+
let start_addr = usize::from_str_radix(addr_range[0], 16)?;
+
let end_addr = usize::from_str_radix(addr_range[1], 16)?;
+
let permissions = parts[1].to_string();
+
+
// Only add regions with read permission
+
if permissions.contains('r') {
+
regions.push(MemoryRegion {
+
start_addr,
+
end_addr,
+
permissions,
+
offset: usize::from_str_radix(parts[2], 16)?,
+
pathname: if parts.len() > 5 {
+
parts[5..].join(" ")
+
} else {
+
String::new()
+
},
+
});
+
}
+
}
+
+
info!("{} suitable regions found.", regions.len());
+
Ok(regions)
+
}
+
}
+207
src/scanner.rs
···
···
+
use anyhow::Result;
+
use log::{debug, info};
+
use rayon::prelude::*;
+
use std::sync::{Arc, Mutex};
+
+
use crate::memory::MemoryReader;
+
use crate::process::{MemoryRegion, Process};
+
+
#[derive(Clone, Debug)]
+
pub enum ScanValueType {
+
#[allow(dead_code)]
+
U8(u8),
+
#[allow(dead_code)]
+
U16(u16),
+
#[allow(dead_code)]
+
U32(u32),
+
#[allow(dead_code)]
+
U64(u64),
+
#[allow(dead_code)]
+
I8(i8),
+
#[allow(dead_code)]
+
I16(i16),
+
I32(i32),
+
#[allow(dead_code)]
+
I64(i64),
+
#[allow(dead_code)]
+
F32(f32),
+
#[allow(dead_code)]
+
F64(f64),
+
}
+
+
#[derive(Clone, Debug)]
+
pub struct MemoryMatch {
+
pub address: usize,
+
#[allow(dead_code)]
+
pub value_type: ScanValueType,
+
}
+
+
pub struct MemoryScanner {
+
reader: MemoryReader,
+
regions: Vec<MemoryRegion>,
+
matches: Vec<MemoryMatch>,
+
}
+
+
impl MemoryScanner {
+
pub fn new(process: Process) -> Result<Self> {
+
let regions = process.get_memory_maps()?;
+
let reader = MemoryReader::new(process);
+
+
println!(
+
"Successfully attached to process. Found {} readable memory regions.",
+
regions.len()
+
);
+
+
Ok(Self {
+
reader,
+
regions,
+
matches: Vec::new(),
+
})
+
}
+
+
pub fn scan_for_value(&mut self, value: ScanValueType) -> Result<()> {
+
let matches = Arc::new(Mutex::new(Vec::new()));
+
+
self.regions
+
.par_iter()
+
.enumerate()
+
.for_each(|(idx, region)| {
+
println!(
+
"{:02}/{:03} searching {:x} - {:x}...",
+
idx + 1,
+
self.regions.len(),
+
region.start_addr,
+
region.end_addr
+
);
+
+
match self.scan_region(region, &value) {
+
Ok(region_matches) => {
+
let mut matches_guard = matches.lock().unwrap();
+
matches_guard.extend(region_matches);
+
}
+
Err(e) => {
+
debug!(
+
"Error scanning region {:x}-{:x}: {}",
+
region.start_addr, region.end_addr, e
+
);
+
}
+
}
+
+
print!("........ok\n");
+
});
+
+
self.matches = Arc::try_unwrap(matches)
+
.expect("Failed to unwrap Arc")
+
.into_inner()
+
.expect("Failed to unlock Mutex");
+
+
info!("we currently have {} matches.", self.matches.len());
+
Ok(())
+
}
+
+
pub fn get_matches_count(&self) -> usize {
+
self.matches.len()
+
}
+
+
fn scan_region(
+
&self,
+
region: &MemoryRegion,
+
value: &ScanValueType,
+
) -> Result<Vec<MemoryMatch>> {
+
// Basic implementation that scans a single region for matches
+
let mut region_matches = Vec::new();
+
+
match self.reader.read_region(region) {
+
Ok(memory) => {
+
// Simple implementation searching for i32 values
+
if let ScanValueType::I32(target) = value {
+
let target_bytes = target.to_ne_bytes();
+
+
for i in (0..memory.len().saturating_sub(4)).step_by(4) {
+
let mut matches = true;
+
for j in 0..4 {
+
if memory[i + j] != target_bytes[j] {
+
matches = false;
+
break;
+
}
+
}
+
+
if matches {
+
region_matches.push(MemoryMatch {
+
address: region.start_addr + i,
+
value_type: value.clone(),
+
});
+
}
+
}
+
}
+
// TODO missing handlers for all other types
+
}
+
Err(e) => {
+
println!(
+
"Error reading region {:x}-{:x}: {}",
+
region.start_addr, region.end_addr, e
+
);
+
}
+
}
+
+
Ok(region_matches)
+
}
+
pub fn filter_matches(&mut self, value: ScanValueType) -> Result<()> {
+
let old_matches = std::mem::take(&mut self.matches);
+
+
for m in old_matches {
+
let addr = m.address;
+
+
let data = match &value {
+
ScanValueType::I32(_) => {
+
match self.reader.read_memory(addr, 4) {
+
Ok(data) => data,
+
Err(_) => continue, // Skip if we can't read
+
}
+
}
+
_ => continue,
+
};
+
+
let matched = match &value {
+
ScanValueType::I32(target) => {
+
if data.len() >= 4 {
+
let value_bytes = target.to_ne_bytes();
+
data[0] == value_bytes[0]
+
&& data[1] == value_bytes[1]
+
&& data[2] == value_bytes[2]
+
&& data[3] == value_bytes[3]
+
} else {
+
false
+
}
+
}
+
_ => false,
+
};
+
+
if matched {
+
self.matches.push(MemoryMatch {
+
address: addr,
+
value_type: value.clone(),
+
});
+
}
+
}
+
+
println!("..........ok");
+
info!("we currently have {} matches.", self.matches.len());
+
Ok(())
+
}
+
+
pub fn set_value(&self, index: usize, value: i32) -> Result<()> {
+
if index >= self.matches.len() {
+
anyhow::bail!(
+
"Index {} out of range (have {} matches)",
+
index,
+
self.matches.len()
+
);
+
}
+
+
let address = self.matches[index].address;
+
let data = value.to_ne_bytes();
+
+
self.reader.write_memory(address, &data)
+
}
+
}