Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d544cf6c3 |
16 changed files with 646 additions and 153588 deletions
551
Cargo.lock
generated
551
Cargo.lock
generated
|
|
@ -4,24 +4,18 @@ version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adbdguard"
|
name = "adbdguard"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"aya",
|
|
||||||
"aya-log",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"crc32fast",
|
||||||
"crossbeam-channel",
|
|
||||||
"futures",
|
|
||||||
"libc",
|
|
||||||
"nix",
|
"nix",
|
||||||
"notify",
|
"notify",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rayon",
|
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.16",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
|
@ -51,100 +45,12 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "allocator-api2"
|
|
||||||
version = "0.2.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android-tzdata"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android_system_properties"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.99"
|
version = "1.0.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "assert_matches"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aya"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d18bc4e506fbb85ab7392ed993a7db4d1a452c71b75a246af4a80ab8c9d2dd50"
|
|
||||||
dependencies = [
|
|
||||||
"assert_matches",
|
|
||||||
"aya-obj",
|
|
||||||
"bitflags 2.9.3",
|
|
||||||
"bytes",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"object",
|
|
||||||
"once_cell",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aya-log"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b600d806c1d07d3b81ab5f4a2a95fd80f479a0d3f1d68f29064d660865f85f02"
|
|
||||||
dependencies = [
|
|
||||||
"aya",
|
|
||||||
"aya-log-common",
|
|
||||||
"bytes",
|
|
||||||
"log",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aya-log-common"
|
|
||||||
version = "0.1.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "befef9fe882e63164a2ba0161874e954648a72b0e1c4b361f532d590638c4eec"
|
|
||||||
dependencies = [
|
|
||||||
"num_enum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aya-obj"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c51b96c5a8ed8705b40d655273bc4212cbbf38d4e3be2788f36306f154523ec7"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"core-error",
|
|
||||||
"hashbrown",
|
|
||||||
"log",
|
|
||||||
"object",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.75"
|
version = "0.3.75"
|
||||||
|
|
@ -172,27 +78,12 @@ version = "2.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
|
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.19.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.10.1"
|
version = "1.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.2.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
|
|
||||||
dependencies = [
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
|
@ -205,35 +96,6 @@ 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 = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chrono"
|
|
||||||
version = "0.4.41"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
|
||||||
dependencies = [
|
|
||||||
"android-tzdata",
|
|
||||||
"iana-time-zone",
|
|
||||||
"js-sys",
|
|
||||||
"num-traits",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-error"
|
|
||||||
version = "0.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "efcdb2972eb64230b4c50646d8498ff73f5128d196a90c7236eec4cbe8619b8f"
|
|
||||||
dependencies = [
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-foundation-sys"
|
|
||||||
version = "0.8.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -243,58 +105,6 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-channel"
|
|
||||||
version = "0.5.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[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.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equivalent"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foldhash"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fsevent-sys"
|
name = "fsevent-sys"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
|
|
@ -304,146 +114,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-executor",
|
|
||||||
"futures-io",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-channel"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-core"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-executor"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-io"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-macro"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-sink"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-io",
|
|
||||||
"futures-macro",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"memchr",
|
|
||||||
"pin-project-lite",
|
|
||||||
"pin-utils",
|
|
||||||
"slab",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.31.1"
|
version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.15.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
|
||||||
dependencies = [
|
|
||||||
"allocator-api2",
|
|
||||||
"equivalent",
|
|
||||||
"foldhash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone"
|
|
||||||
version = "0.1.63"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
|
||||||
dependencies = [
|
|
||||||
"android_system_properties",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"iana-time-zone-haiku",
|
|
||||||
"js-sys",
|
|
||||||
"log",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone-haiku"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "indexmap"
|
|
||||||
version = "2.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
|
|
||||||
dependencies = [
|
|
||||||
"equivalent",
|
|
||||||
"hashbrown",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inotify"
|
name = "inotify"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
@ -481,16 +157,6 @@ version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "js-sys"
|
|
||||||
version = "0.3.77"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kqueue"
|
name = "kqueue"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
|
|
@ -602,45 +268,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_enum"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a"
|
|
||||||
dependencies = [
|
|
||||||
"num_enum_derive",
|
|
||||||
"rustversion",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_enum_derive"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
|
||||||
"hashbrown",
|
|
||||||
"indexmap",
|
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -662,12 +295,6 @@ version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.101"
|
version = "1.0.101"
|
||||||
|
|
@ -686,26 +313,6 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.11.2"
|
version = "1.11.2"
|
||||||
|
|
@ -741,12 +348,6 @@ version = "0.1.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
|
|
@ -803,12 +404,6 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.11"
|
version = "0.4.11"
|
||||||
|
|
@ -842,33 +437,13 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror"
|
|
||||||
version = "1.0.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror-impl 1.0.69",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.16"
|
version = "2.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.16",
|
"thiserror-impl",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror-impl"
|
|
||||||
version = "1.0.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -898,6 +473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
|
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
|
"bytes",
|
||||||
"io-uring",
|
"io-uring",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
|
|
@ -988,12 +564,6 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
|
@ -1010,64 +580,6 @@ version = "0.11.1+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"rustversion",
|
|
||||||
"wasm-bindgen-macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-backend"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"log",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"wasm-bindgen-macro-support",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro-support"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"wasm-bindgen-backend",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-shared"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
@ -1099,65 +611,12 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.61.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement",
|
|
||||||
"windows-interface",
|
|
||||||
"windows-link",
|
|
||||||
"windows-result",
|
|
||||||
"windows-strings",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-implement"
|
|
||||||
version = "0.60.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-interface"
|
|
||||||
version = "0.59.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-result"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-strings"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
|
|
|
||||||
35
Cargo.toml
35
Cargo.toml
|
|
@ -1,49 +1,24 @@
|
||||||
[package]
|
[package]
|
||||||
name = "adbdguard"
|
name = "adbdguard"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.99"
|
anyhow = "1.0.99"
|
||||||
aya = "0.13.1"
|
|
||||||
aya-log = "0.2.1"
|
|
||||||
bytes = "1.10.1"
|
bytes = "1.10.1"
|
||||||
chrono = "0.4.41"
|
crc32fast = "1.5.0"
|
||||||
crossbeam-channel = "0.5.15"
|
nix = { version = "0.30.1", features = ["ptrace", "process", "uio", "signal"] }
|
||||||
futures = "0.3.31"
|
|
||||||
libc = "0.2.175"
|
|
||||||
nix = { version = "0.30.1", features = [
|
|
||||||
"ptrace",
|
|
||||||
"process",
|
|
||||||
"uio",
|
|
||||||
"signal",
|
|
||||||
"sched",
|
|
||||||
"fs",
|
|
||||||
] }
|
|
||||||
notify = "8.2.0"
|
notify = "8.2.0"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
rayon = "1.11.0"
|
|
||||||
regex = "1.11.2"
|
regex = "1.11.2"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.143"
|
serde_json = "1.0.143"
|
||||||
thiserror = "2.0.16"
|
thiserror = "2.0.16"
|
||||||
tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "time"] }
|
tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "time", "net", "io-util", "fs"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = { version = "0.3.19", features = ["fmt", "ansi"] }
|
tracing-subscriber = { version = "0.3.19", features = ["fmt", "ansi"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ffs_proxy"]
|
default = ["ffs_proxy"]
|
||||||
ffs_proxy = []
|
|
||||||
ptrace = []
|
ptrace = []
|
||||||
ptrace_parallel = []
|
ffs_proxy = []
|
||||||
ptracev2 = []
|
|
||||||
uprobes = []
|
|
||||||
|
|
||||||
# For cross-compilation to Android
|
|
||||||
[profile.release]
|
|
||||||
strip = true
|
|
||||||
lto = true
|
|
||||||
opt-level = "z" # Optimize for size
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
debug = true
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
INCDIR=/usr/include
|
|
||||||
clang -O2 -g -target bpf \
|
|
||||||
-D__TARGET_ARCH_arm64 \
|
|
||||||
-I"$INCDIR" \
|
|
||||||
-c sendrecv.bpf.c -o sendrecv.bpf.o
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
#include "vmlinux.h"
|
|
||||||
#include <asm/types.h>
|
|
||||||
|
|
||||||
#include <linux/bpf_common.h>
|
|
||||||
// #include <bpf/bpf.h>
|
|
||||||
|
|
||||||
#include <bpf/bpf_helpers.h>
|
|
||||||
|
|
||||||
// Target: arm64 calling convention
|
|
||||||
#ifndef __TARGET_ARCH_arm64
|
|
||||||
# define __TARGET_ARCH_arm64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __PT_PARM1_REG
|
|
||||||
struct user_pt_regs {
|
|
||||||
__u64 regs[31];
|
|
||||||
__u64 sp;
|
|
||||||
__u64 pc;
|
|
||||||
__u64 pstate;
|
|
||||||
};
|
|
||||||
#define __PT_PARM1_REG regs[0]
|
|
||||||
#define __PT_PARM2_REG regs[1]
|
|
||||||
#define __PT_PARM3_REG regs[2]
|
|
||||||
#define __PT_PARM4_REG regs[3]
|
|
||||||
#define __PT_PARM5_REG regs[4]
|
|
||||||
#define __PT_PARM6_REG regs[5]
|
|
||||||
#define __PT_RC_REG regs[0]
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <bpf/bpf_tracing.h>
|
|
||||||
#include <bpf/bpf_core_read.h>
|
|
||||||
|
|
||||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
|
||||||
|
|
||||||
#define MAX_PEEK 256
|
|
||||||
|
|
||||||
struct call_ctx {
|
|
||||||
uint64_t buf;
|
|
||||||
uint64_t len;
|
|
||||||
uint32_t is_send; // 1=send/write, 0=recv/read
|
|
||||||
};
|
|
||||||
|
|
||||||
struct event {
|
|
||||||
uint32_t pid, tid;
|
|
||||||
uint32_t is_send;
|
|
||||||
uint32_t len;
|
|
||||||
uint8_t data[MAX_PEEK];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
|
||||||
__uint(type, BPF_MAP_TYPE_HASH);
|
|
||||||
__type(key, uint32_t); // tid
|
|
||||||
__type(value, struct call_ctx);
|
|
||||||
__uint(max_entries, 8192);
|
|
||||||
} in_flight SEC(".maps");
|
|
||||||
|
|
||||||
struct {
|
|
||||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
|
||||||
__uint(max_entries, 1 << 22);
|
|
||||||
} rb SEC(".maps");
|
|
||||||
|
|
||||||
// helpers
|
|
||||||
static __always_inline void save_ctx(uint32_t tid, uint64_t buf, uint64_t len, uint32_t is_send) {
|
|
||||||
struct call_ctx ctx = {.buf = buf, .len = len, .is_send = is_send};
|
|
||||||
bpf_map_update_elem(&in_flight, &tid, &ctx, BPF_ANY);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int emit_ret(uint32_t tid, int64_t ret, uint32_t is_send) {
|
|
||||||
struct call_ctx *ctx = bpf_map_lookup_elem(&in_flight, &tid);
|
|
||||||
if (!ctx) return 0;
|
|
||||||
if (ret <= 0) { bpf_map_delete_elem(&in_flight, &tid); return 0; }
|
|
||||||
|
|
||||||
uint64_t want = ctx->len < (unsigned long)ret ? ctx->len : (unsigned long)ret;
|
|
||||||
if (want > MAX_PEEK) want = MAX_PEEK;
|
|
||||||
|
|
||||||
struct event *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
|
|
||||||
if (!e) { bpf_map_delete_elem(&in_flight, &tid); return 0; }
|
|
||||||
e->pid = bpf_get_current_pid_tgid() >> 32;
|
|
||||||
e->tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
e->is_send = is_send;
|
|
||||||
e->len = (uint32_t)want;
|
|
||||||
|
|
||||||
if (want > 0 && ctx->buf) {
|
|
||||||
bpf_probe_read_user(e->data, want, (void*)ctx->buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
bpf_ringbuf_submit(e, 0);
|
|
||||||
bpf_map_delete_elem(&in_flight, &tid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- send/write entry (x1 = buf, x2 = len)
|
|
||||||
SEC("uprobe/libc_send")
|
|
||||||
int BPF_KPROBE(up_send) {
|
|
||||||
uint32_t tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
uint64_t buf = PT_REGS_PARM2(ctx);
|
|
||||||
uint64_t len = PT_REGS_PARM3(ctx);
|
|
||||||
save_ctx(tid, buf, len, 1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SEC("uretprobe/libc_send")
|
|
||||||
int BPF_KRETPROBE(ur_send) {
|
|
||||||
uint32_t tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
return emit_ret(tid, PT_REGS_RC(ctx), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// write(int fd, const void *buf, size_t count)
|
|
||||||
SEC("uprobe/libc_write")
|
|
||||||
int BPF_KPROBE(up_write) {
|
|
||||||
uint32_t tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
uint64_t buf = PT_REGS_PARM2(ctx);
|
|
||||||
uint64_t len = PT_REGS_PARM3(ctx);
|
|
||||||
save_ctx(tid, buf, len, 1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SEC("uretprobe/libc_write")
|
|
||||||
int BPF_KRETPROBE(ur_write) {
|
|
||||||
uint32_t tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
return emit_ret(tid, PT_REGS_RC(ctx), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- recv/read entry (x1 = buf, x2 = len)
|
|
||||||
SEC("uprobe/libc_recv")
|
|
||||||
int BPF_KPROBE(up_recv) {
|
|
||||||
uint32_t tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
uint64_t buf = PT_REGS_PARM2(ctx);
|
|
||||||
uint64_t len = PT_REGS_PARM3(ctx);
|
|
||||||
save_ctx(tid, buf, len, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SEC("uretprobe/libc_recv")
|
|
||||||
int BPF_KRETPROBE(ur_recv) {
|
|
||||||
uint32_t tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
return emit_ret(tid, PT_REGS_RC(ctx), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// read(int fd, void *buf, size_t count)
|
|
||||||
SEC("uprobe/libc_read")
|
|
||||||
int BPF_KPROBE(up_read) {
|
|
||||||
uint32_t tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
uint64_t buf = PT_REGS_PARM2(ctx);
|
|
||||||
uint64_t len = PT_REGS_PARM3(ctx);
|
|
||||||
save_ctx(tid, buf, len, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SEC("uretprobe/libc_read")
|
|
||||||
int BPF_KRETPROBE(ur_read) {
|
|
||||||
uint32_t tid = (uint32_t)bpf_get_current_pid_tgid();
|
|
||||||
return emit_ret(tid, PT_REGS_RC(ctx), 0);
|
|
||||||
}
|
|
||||||
150324
bpf/vmlinux.h
150324
bpf/vmlinux.h
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,5 @@
|
||||||
#[cfg(feature = "ptrace")]
|
#[cfg(feature = "ptrace")]
|
||||||
pub mod ptrace;
|
pub mod ptrace;
|
||||||
|
|
||||||
#[cfg(feature = "ptracev2")]
|
#[cfg(feature = "ffs_proxy")]
|
||||||
pub mod ptrace_v2;
|
pub mod proxy;
|
||||||
|
|
||||||
#[cfg(feature = "ptrace_parallel")]
|
|
||||||
pub mod ptrace_prl;
|
|
||||||
|
|
||||||
#[cfg(feature = "uprobes")]
|
|
||||||
pub mod uprobes;
|
|
||||||
|
|
|
||||||
510
src/backend/proxy.rs
Normal file
510
src/backend/proxy.rs
Normal file
|
|
@ -0,0 +1,510 @@
|
||||||
|
use std::{
|
||||||
|
ffi::CString,
|
||||||
|
fs::OpenOptions,
|
||||||
|
io::Write,
|
||||||
|
iter::Enumerate,
|
||||||
|
os::fd::{AsRawFd, FromRawFd},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{Context, anyhow};
|
||||||
|
use bytes::{Buf, BufMut, BytesMut};
|
||||||
|
use crc32fast::Hasher;
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncReadExt, AsyncWriteExt},
|
||||||
|
net::TcpStream,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::traits::EnumerateValue;
|
||||||
|
|
||||||
|
pub const FFS_EP0: &str = "/dev/usb-ffs/adb/ep0";
|
||||||
|
pub const FFS_EP1: &str = "/dev/usb-ffs/adb/ep1";
|
||||||
|
pub const FFS_EP2: &str = "/dev/usb-ffs/adb/ep2";
|
||||||
|
|
||||||
|
pub const ENDPOINT_XFER_BULK: u8 = 2;
|
||||||
|
|
||||||
|
// FFS Descriptors
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct FfsDescHeadV2 {
|
||||||
|
magic: u32, // FUNCTIONFS_DESCRIPTORS_MAGIC_V2 = 0xB8 0x4B 0x3F 0x94
|
||||||
|
length: u32,
|
||||||
|
flags: u32, // FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct UsbInterfaceDescriptor {
|
||||||
|
bLength: u8,
|
||||||
|
bDescriptiorType: u8,
|
||||||
|
bInterfaceNumber: u8,
|
||||||
|
bAlternateSetting: u8,
|
||||||
|
bNumEndpoints: u8,
|
||||||
|
bInterfaceClass: u8,
|
||||||
|
bInterfaceSubClass: u8,
|
||||||
|
bInterfaceProtocol: u8,
|
||||||
|
iInterface: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// REF: https://vovkos.github.io/doxyrest/samples/libusb/struct_libusb_endpoint_descriptor.html
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct UsbEndpointDescriptor {
|
||||||
|
/// Size of descriptor (bytes)
|
||||||
|
bLength: u8,
|
||||||
|
/// Descriptor type
|
||||||
|
bDescriptorType: u8,
|
||||||
|
/// Address of endpoint
|
||||||
|
/// Bit 0-3 = endpoint number
|
||||||
|
/// Bit 4-6 = reserved
|
||||||
|
/// Bit 7 = direction
|
||||||
|
bEndpointAddress: u8,
|
||||||
|
/// Attributes apply to endpoint
|
||||||
|
/// Bit 0-1 = transfer type
|
||||||
|
/// Bit 2-3 = isochronus endpoint
|
||||||
|
/// Bit 4-5 = isochronus endpoint
|
||||||
|
/// Bit 6-7 = reserved
|
||||||
|
bmAttributes: u8,
|
||||||
|
/// Packet size capable of send/receive
|
||||||
|
wMaxPacketSize: u16,
|
||||||
|
/// Interval for polling endpoint for data transfer
|
||||||
|
bInterval: u8,
|
||||||
|
/// Audio only: rate at which synchronization feedback is provided
|
||||||
|
bRefresh: u8, // not used for bulk
|
||||||
|
/// Audio only: address if synch endpoint
|
||||||
|
bSynchAddress: u8, // not used for bulk
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum DescriptorType {
|
||||||
|
LIBUSB_DT_DEVICE = 0x01,
|
||||||
|
LIBUSB_DT_CONFIG,
|
||||||
|
LIBUSB_DT_STRING,
|
||||||
|
LIBUSB_DT_INTERFACE,
|
||||||
|
LIBUSB_DT_ENDPOINT,
|
||||||
|
LIBUSB_DT_BOS = 0x0f,
|
||||||
|
LIBUSB_DT_DEVICE_CAPABILITY,
|
||||||
|
LIBUSB_DT_HID = 0x21,
|
||||||
|
LIBUSB_DT_REPORT,
|
||||||
|
LIBUSB_DT_PHYSICAL,
|
||||||
|
LIBUSB_DT_HUB = 0x29,
|
||||||
|
LIBUSB_DT_SUPERSPEED_HUB,
|
||||||
|
LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnumerateValue<u8> for DescriptorType {
|
||||||
|
fn name(&self) -> String
|
||||||
|
where
|
||||||
|
Self: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
format!("{:?}", self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value(&self) -> u8
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
{
|
||||||
|
self.clone() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(val: u8) -> Self {
|
||||||
|
return unsafe { std::mem::transmute::<_, Self>(val) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum EndpointDirection {
|
||||||
|
ENDPOINT_IN = 0x80, // device -> host
|
||||||
|
ENDPOINT_OUT = 0x00, // host -> device
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnumerateValue<u8> for EndpointDirection {
|
||||||
|
fn name(&self) -> String
|
||||||
|
where
|
||||||
|
Self: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
format!("{:?}", self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value(&self) -> u8
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
{
|
||||||
|
self.clone() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(val: u8) -> Self {
|
||||||
|
return unsafe { std::mem::transmute::<_, Self>(val) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn any_as_bytes<T: Sized>(p: &T) -> &[u8] {
|
||||||
|
std::slice::from_raw_parts((p as *const T) as *const u8, size_of::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build tiny FS+HS descriptor set for ADB
|
||||||
|
/// class 0xFF
|
||||||
|
/// subclass 0x42
|
||||||
|
/// proto 0x01
|
||||||
|
fn write_ffs_descriptors(ep0: &mut std::fs::File) -> anyhow::Result<()> {
|
||||||
|
let interface = UsbInterfaceDescriptor {
|
||||||
|
bLength: size_of::<UsbInterfaceDescriptor>() as u8,
|
||||||
|
bDescriptiorType: DescriptorType::LIBUSB_DT_INTERFACE.value(),
|
||||||
|
bInterfaceNumber: 0,
|
||||||
|
bAlternateSetting: 0,
|
||||||
|
bNumEndpoints: 2,
|
||||||
|
bInterfaceClass: 0xFF,
|
||||||
|
bInterfaceSubClass: 0x42,
|
||||||
|
bInterfaceProtocol: 0x01,
|
||||||
|
iInterface: 1,
|
||||||
|
};
|
||||||
|
let fs_out = UsbEndpointDescriptor {
|
||||||
|
bLength: 7,
|
||||||
|
bDescriptorType: DescriptorType::LIBUSB_DT_ENDPOINT.value(),
|
||||||
|
bEndpointAddress: 0x01 | EndpointDirection::ENDPOINT_OUT.value(),
|
||||||
|
bmAttributes: ENDPOINT_XFER_BULK,
|
||||||
|
wMaxPacketSize: 64u16.to_le(),
|
||||||
|
bInterval: 0,
|
||||||
|
bRefresh: 0,
|
||||||
|
bSynchAddress: 0,
|
||||||
|
};
|
||||||
|
let fs_in = UsbEndpointDescriptor {
|
||||||
|
bLength: 7,
|
||||||
|
bDescriptorType: DescriptorType::LIBUSB_DT_ENDPOINT.value(),
|
||||||
|
bEndpointAddress: 0x82 | EndpointDirection::ENDPOINT_IN.value(),
|
||||||
|
bmAttributes: ENDPOINT_XFER_BULK,
|
||||||
|
wMaxPacketSize: 64u16.to_le(),
|
||||||
|
bInterval: 0,
|
||||||
|
bRefresh: 0,
|
||||||
|
bSynchAddress: 0,
|
||||||
|
};
|
||||||
|
let hs_out = UsbEndpointDescriptor {
|
||||||
|
wMaxPacketSize: 512u16.to_le(),
|
||||||
|
..fs_out
|
||||||
|
};
|
||||||
|
let hs_in = UsbEndpointDescriptor {
|
||||||
|
wMaxPacketSize: 512u16.to_le(),
|
||||||
|
..fs_in
|
||||||
|
};
|
||||||
|
|
||||||
|
// header
|
||||||
|
const MAGIC_V2: u32 = 0xB84B3F94;
|
||||||
|
// body
|
||||||
|
let body_len = size_of::<UsbInterfaceDescriptor>()
|
||||||
|
+ 2 * size_of::<UsbEndpointDescriptor>()
|
||||||
|
+ size_of::<UsbInterfaceDescriptor>()
|
||||||
|
+ 2 * size_of::<UsbEndpointDescriptor>();
|
||||||
|
|
||||||
|
let header = FfsDescHeadV2 {
|
||||||
|
magic: MAGIC_V2,
|
||||||
|
length: (size_of::<FfsDescHeadV2>() + body_len) as u32,
|
||||||
|
flags: 0x3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// write descriptors
|
||||||
|
let mut buf = Vec::with_capacity(header.length as usize);
|
||||||
|
buf.extend_from_slice(unsafe { any_as_bytes(&header) });
|
||||||
|
buf.extend_from_slice(unsafe { any_as_bytes(&interface) });
|
||||||
|
buf.extend_from_slice(unsafe { any_as_bytes(&fs_out) });
|
||||||
|
buf.extend_from_slice(unsafe { any_as_bytes(&fs_in) });
|
||||||
|
buf.extend_from_slice(unsafe { any_as_bytes(&interface) });
|
||||||
|
buf.extend_from_slice(unsafe { any_as_bytes(&hs_out) });
|
||||||
|
buf.extend_from_slice(unsafe { any_as_bytes(&hs_in) });
|
||||||
|
|
||||||
|
ep0.write_all(&buf)?;
|
||||||
|
|
||||||
|
// header v2
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct FfsStrHeadV2 {
|
||||||
|
magic: u32,
|
||||||
|
length: u32,
|
||||||
|
str_count: u32,
|
||||||
|
lang_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const STR_MAGIC_V2: u32 = 0xC6 | (0x85 << 8) | (0x93 << 16) | (0x9F << 24);
|
||||||
|
let sh = FfsStrHeadV2 {
|
||||||
|
magic: STR_MAGIC_V2,
|
||||||
|
length: 0,
|
||||||
|
str_count: 1,
|
||||||
|
lang_count: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let s0 = CString::new("ADB Interface").unwrap();
|
||||||
|
let mut sbuf = Vec::new();
|
||||||
|
sbuf.extend_from_slice(unsafe { any_as_bytes(&sh) });
|
||||||
|
sbuf.extend_from_slice(&0x0409u16.to_le_bytes());
|
||||||
|
sbuf.extend_from_slice(s0.as_bytes_with_nul());
|
||||||
|
|
||||||
|
let len_le = (sbuf.len() as u32).to_le_bytes();
|
||||||
|
sbuf[4..8].copy_from_slice(&len_le);
|
||||||
|
ep0.write_all(&sbuf)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADB Helper
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct AdbHdr {
|
||||||
|
cmd: u32,
|
||||||
|
arg0: u32,
|
||||||
|
arg1: u32,
|
||||||
|
data_len: u32,
|
||||||
|
data_crc32: u32,
|
||||||
|
magic: u32,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum ADB_COMMAND {
|
||||||
|
CNXN = 0x4E584E43,
|
||||||
|
AUTH = 0x48545541,
|
||||||
|
OPEN = 0x4E45504F,
|
||||||
|
OKAY = 0x59414B4F,
|
||||||
|
CLSE = 0x45534C43,
|
||||||
|
WRTE = 0x45545257,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnumerateValue<u32> for ADB_COMMAND {
|
||||||
|
fn name(&self) -> String
|
||||||
|
where
|
||||||
|
Self: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
format!("{:?}", self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value(&self) -> u32
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
{
|
||||||
|
self.clone() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(val: u32) -> Self {
|
||||||
|
return unsafe { std::mem::transmute::<_, Self>(val) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hdr_ok(h: &AdbHdr) -> bool {
|
||||||
|
h.magic == (h.cmd ^ 0xffffffff)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_frame_r<R: AsyncReadExt + Unpin>(
|
||||||
|
r: &mut R,
|
||||||
|
buf: &mut BytesMut,
|
||||||
|
) -> anyhow::Result<(AdbHdr, Vec<u8>)> {
|
||||||
|
while buf.len() < 24 {
|
||||||
|
let n = r.read_buf(buf).await?;
|
||||||
|
if n == 0 {
|
||||||
|
return Err(anyhow!("eof"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut h = AdbHdr {
|
||||||
|
cmd: u32::from_le_bytes(buf[0..4].try_into().unwrap()),
|
||||||
|
arg0: u32::from_le_bytes(buf[4..8].try_into().unwrap()),
|
||||||
|
arg1: u32::from_le_bytes(buf[8..12].try_into().unwrap()),
|
||||||
|
data_len: u32::from_le_bytes(buf[12..16].try_into().unwrap()),
|
||||||
|
data_crc32: u32::from_le_bytes(buf[16..20].try_into().unwrap()),
|
||||||
|
magic: u32::from_le_bytes(buf[20..24].try_into().unwrap()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !hdr_ok(&h) {
|
||||||
|
return Err(anyhow!("bad magic"));
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.advance(24);
|
||||||
|
while buf.len() < h.data_len as usize {
|
||||||
|
let n = r.read_buf(buf).await?;
|
||||||
|
if n == 0 {
|
||||||
|
return Err(anyhow!("eof"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let payload = buf.split_to(h.data_len as usize).to_vec();
|
||||||
|
|
||||||
|
Ok((h, payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_frame_w<W: AsyncWriteExt + Unpin>(
|
||||||
|
w: &mut W,
|
||||||
|
mut h: AdbHdr,
|
||||||
|
payload: &[u8],
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut hasher = Hasher::new();
|
||||||
|
hasher.update(payload);
|
||||||
|
h.data_len = payload.len() as u32;
|
||||||
|
h.data_crc32 = hasher.finalize();
|
||||||
|
h.magic = h.cmd ^ 0xffffffff;
|
||||||
|
|
||||||
|
let mut b = BytesMut::with_capacity(24 + payload.len());
|
||||||
|
for v in [h.cmd, h.arg0, h.arg1, h.data_len, h.data_crc32, h.magic] {
|
||||||
|
b.put_u32_le(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
b.extend_from_slice(payload);
|
||||||
|
w.write_all(&b).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn service_from_open_payload(bytes: &[u8]) -> String {
|
||||||
|
let s = std::str::from_utf8(bytes).unwrap_or_default();
|
||||||
|
s.trim_end_matches('\0').to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suspicious(service: &str) -> bool {
|
||||||
|
let s = service.to_ascii_lowercase();
|
||||||
|
s.starts_with("shell:su")
|
||||||
|
|| s.contains("setenforce")
|
||||||
|
|| s.starts_with("tcpip:")
|
||||||
|
|| s.contains("pm grant")
|
||||||
|
|| s.contains("appops")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reject_stream_to_host<W: AsyncWriteExt + Unpin>(
|
||||||
|
w: &mut W,
|
||||||
|
local_id: u32,
|
||||||
|
reason: &str,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
// OKAY L,0 (ack stream so host accepts WRTE)
|
||||||
|
write_frame_w(
|
||||||
|
w,
|
||||||
|
AdbHdr {
|
||||||
|
cmd: ADB_COMMAND::OKAY.value(),
|
||||||
|
arg0: local_id,
|
||||||
|
arg1: 0,
|
||||||
|
data_len: 0,
|
||||||
|
data_crc32: 0,
|
||||||
|
magic: 0,
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
// WRTE L,0 with reason
|
||||||
|
write_frame_w(
|
||||||
|
w,
|
||||||
|
AdbHdr {
|
||||||
|
cmd: ADB_COMMAND::WRTE.value(),
|
||||||
|
arg0: local_id,
|
||||||
|
arg1: 0,
|
||||||
|
data_len: 0,
|
||||||
|
data_crc32: 0,
|
||||||
|
magic: 0,
|
||||||
|
},
|
||||||
|
reason.as_bytes(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
// CLSE L,0
|
||||||
|
write_frame_w(
|
||||||
|
w,
|
||||||
|
AdbHdr {
|
||||||
|
cmd: ADB_COMMAND::CLSE.value(),
|
||||||
|
arg0: local_id,
|
||||||
|
arg1: 0,
|
||||||
|
data_len: 0,
|
||||||
|
data_crc32: 0,
|
||||||
|
magic: 0,
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// probing check IN vs OUT
|
||||||
|
fn open_ffs_endpoints() -> anyhow::Result<(std::fs::File, std::fs::File)> {
|
||||||
|
let f1 = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(FFS_EP1)
|
||||||
|
.with_context(|| format!("open {FFS_EP1}"))?;
|
||||||
|
let f2 = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(FFS_EP2)
|
||||||
|
.with_context(|| format!("open {FFS_EP2}"))?;
|
||||||
|
|
||||||
|
let mut a = f1.try_clone()?;
|
||||||
|
let mut b = f2.try_clone()?;
|
||||||
|
let w1 = a.write(&[0u8; 0]).map(|_| true).unwrap_or(false);
|
||||||
|
let w2 = b.write(&[0u8; 0]).map(|_| true).unwrap_or(false);
|
||||||
|
|
||||||
|
let w1b = if !w1 {
|
||||||
|
a.write(&[0x41]).map(|n| n == 1).unwrap_or(false)
|
||||||
|
} else {
|
||||||
|
w1
|
||||||
|
};
|
||||||
|
let w2b = if !w2 {
|
||||||
|
b.write(&[0x41]).map(|n| n == 1).unwrap_or(false)
|
||||||
|
} else {
|
||||||
|
w2
|
||||||
|
};
|
||||||
|
|
||||||
|
if w1b && !w2b {
|
||||||
|
Ok((f1, f2))
|
||||||
|
} else if w2b && !w1b {
|
||||||
|
Ok((f2, f1))
|
||||||
|
} else {
|
||||||
|
Ok((f2, f1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run() -> anyhow::Result<()> {
|
||||||
|
let mut ep0 = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(FFS_EP0)
|
||||||
|
.context("open ep0")?;
|
||||||
|
write_ffs_descriptors(&mut ep0).context("write FFS descriptors")?;
|
||||||
|
|
||||||
|
let (ep_in_file, ep_out_file) = open_ffs_endpoints().context("open endpoints")?;
|
||||||
|
|
||||||
|
let mut ffs_in =
|
||||||
|
tokio::fs::File::from_std(unsafe { std::fs::File::from_raw_fd(ep_in_file.as_raw_fd()) });
|
||||||
|
let mut ffs_out =
|
||||||
|
tokio::fs::File::from_std(unsafe { std::fs::File::from_raw_fd(ep_out_file.as_raw_fd()) });
|
||||||
|
|
||||||
|
let mut adbd = loop {
|
||||||
|
match TcpStream::connect("127.0.0.1:5566").await {
|
||||||
|
Ok(s) => break s,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("wait adbd 5566: {e}");
|
||||||
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut hb = BytesMut::with_capacity(64 * 1024);
|
||||||
|
let mut db = BytesMut::with_capacity(64 * 1024);
|
||||||
|
|
||||||
|
println!("proxy ready");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
h2d = read_frame_r(&mut ffs_out, &mut hb) => {
|
||||||
|
let (h, payload) = match h2d { Ok(v)=>v, Err(e)=> { eprintln!("ffs_out read: {e}"); break; } };
|
||||||
|
if h.cmd == ADB_COMMAND::OPEN.value() {
|
||||||
|
let service = service_from_open_payload(&payload);
|
||||||
|
if suspicious(&service) {
|
||||||
|
let reason = format!("rejected by policy: {service}");
|
||||||
|
if let Err(e) = reject_stream_to_host(&mut ffs_in, h.arg0, &reason).await {
|
||||||
|
eprintln!("reject write: {e}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue; // do not forward to adbd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(e) = write_frame_w(&mut adbd, h, &payload).await {
|
||||||
|
eprintln!("to adbd: {e}"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d2h = read_frame_r(&mut adbd, &mut db) => {
|
||||||
|
let (h, payload) = match d2h { Ok(v)=>v, Err(e)=> { eprintln!("adbd read: {e}"); break; } };
|
||||||
|
if let Err(e) = write_frame_w(&mut ffs_in, h, &payload).await {
|
||||||
|
eprintln!("to host: {e}"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -2,27 +2,24 @@ use crate::{adb, broadcaster, rule::RuleSet};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use nix::{
|
use nix::{
|
||||||
errno::Errno,
|
libc,
|
||||||
libc::{self, iovec},
|
|
||||||
sys::{
|
sys::{
|
||||||
ptrace::{self, Options},
|
ptrace,
|
||||||
|
signal::Signal,
|
||||||
wait::{WaitPidFlag, WaitStatus, waitpid},
|
wait::{WaitPidFlag, WaitStatus, waitpid},
|
||||||
},
|
},
|
||||||
unistd::Pid,
|
unistd::Pid,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::HashMap, hash::Hash, time::{Duration, Instant}
|
||||||
fs,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
};
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
// FALLBACK constants, in case of unsupported os while compiling
|
|
||||||
const PTRACE_GETREGSET_FALLBACK: libc::c_int = 0x4204;
|
const PTRACE_GETREGSET_FALLBACK: libc::c_int = 0x4204;
|
||||||
// const PTRACE_SEIZE_FALLBACK: libc::c_int = 0x4206;
|
const PTRACE_SEIZE_FALLBACK: libc::c_int = 0x4206;
|
||||||
// const PTRACE_INTERRUPT: libc::c_int = 0x4207;
|
const PTRACE_INTERRUPT: libc::c_int = 0x4207;
|
||||||
const NT_PRSTATUS_FALLBACK: libc::c_int = 1;
|
const NT_PRSTATUS_FALLBACK: libc::c_int = 1;
|
||||||
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
struct UserPtRegs {
|
struct UserPtRegs {
|
||||||
|
|
@ -34,101 +31,26 @@ struct UserPtRegs {
|
||||||
|
|
||||||
fn get_regs(pid: Pid) -> Result<UserPtRegs> {
|
fn get_regs(pid: Pid) -> Result<UserPtRegs> {
|
||||||
let mut regs = UserPtRegs::default();
|
let mut regs = UserPtRegs::default();
|
||||||
let mut iovec = iovec {
|
|
||||||
iov_base: (&mut regs as *mut UserPtRegs).cast(),
|
|
||||||
iov_len: std::mem::size_of::<UserPtRegs>(),
|
|
||||||
};
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::ptrace(
|
let r = libc::ptrace(
|
||||||
PTRACE_GETREGSET_FALLBACK.try_into().unwrap(),
|
PTRACE_GETREGSET_FALLBACK.try_into().unwrap(),
|
||||||
pid.as_raw(),
|
pid.as_raw(),
|
||||||
NT_PRSTATUS_FALLBACK,
|
NT_PRSTATUS_FALLBACK,
|
||||||
&mut iovec,
|
&mut regs as *mut _,
|
||||||
);
|
);
|
||||||
if r < 0 {
|
if r < 0 {
|
||||||
let e = std::io::Error::last_os_error();
|
return Err(anyhow::anyhow!("GETREGSET failed"));
|
||||||
return Err(anyhow::anyhow!("GETREGSET failed: {e}"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(regs)
|
Ok(regs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List all thread ids under `pid`
|
|
||||||
fn list_tids(pid: i32) -> Vec<Pid> {
|
|
||||||
let mut out = Vec::new();
|
|
||||||
if let Ok(rd) = fs::read_dir(format!("/proc/{pid}/task")) {
|
|
||||||
for e in rd.flatten() {
|
|
||||||
if let Ok(s) = e.file_name().into_string() {
|
|
||||||
if let Ok(t) = s.parse::<i32>() {
|
|
||||||
out.push(Pid::from_raw(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
fn attach_and_prepare(tid: Pid) -> anyhow::Result<()> {
|
|
||||||
match ptrace::attach(tid) {
|
|
||||||
Ok(_) => {
|
|
||||||
info!("attached to {tid}");
|
|
||||||
}
|
|
||||||
Err(e) if e == Errno::EPERM || e == Errno::EBUSY => {
|
|
||||||
// already traced
|
|
||||||
}
|
|
||||||
Err(e) => return Err(anyhow::anyhow!("attach({tid}) failed: {e}")),
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match waitpid(tid, Some(WaitPidFlag::__WALL))? {
|
|
||||||
WaitStatus::Stopped(_, _)
|
|
||||||
| WaitStatus::PtraceSyscall(_)
|
|
||||||
| WaitStatus::PtraceEvent(_, _, _) => break,
|
|
||||||
WaitStatus::Exited(_, _) | WaitStatus::Signaled(_, _, _) => {
|
|
||||||
return Err(anyhow::anyhow!("tid {tid} exited while attaching"));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ptrace::setoptions(
|
|
||||||
tid,
|
|
||||||
Options::PTRACE_O_TRACESYSGOOD
|
|
||||||
| Options::PTRACE_O_TRACECLONE
|
|
||||||
| Options::PTRACE_O_TRACEEXEC
|
|
||||||
| Options::PTRACE_O_TRACEEXIT,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
info!("{tid} setup completed!");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resume_syscall(tid: Pid) -> anyhow::Result<()> {
|
|
||||||
ptrace::syscall(tid, None)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn geteventmsg_u32(tid: Pid) -> u32 {
|
|
||||||
let mut val: libc::c_ulong = 0;
|
|
||||||
unsafe {
|
|
||||||
libc::ptrace(
|
|
||||||
libc::PTRACE_GETEVENTMSG,
|
|
||||||
tid.as_raw(),
|
|
||||||
0,
|
|
||||||
&mut val as *mut _,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
val as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
// x8 holds syscall
|
// x8 holds syscall
|
||||||
fn syscall_no(regs: &UserPtRegs) -> u64 {
|
fn syscall_no(regs: &UserPtRegs) -> u64 {
|
||||||
regs.regs[8]
|
regs.regs[8]
|
||||||
}
|
}
|
||||||
|
|
||||||
// x0 1st arg fd/buf
|
// x0 1st arg
|
||||||
fn arg0(regs: &UserPtRegs) -> u64 {
|
fn arg0(regs: &UserPtRegs) -> u64 {
|
||||||
regs.regs[0]
|
regs.regs[0]
|
||||||
}
|
}
|
||||||
|
|
@ -138,173 +60,106 @@ fn arg1(regs: &UserPtRegs) -> u64 {
|
||||||
regs.regs[1]
|
regs.regs[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// x2 count (write/sento)
|
|
||||||
fn arg2(regs: &UserPtRegs) -> u64 {
|
fn arg2(regs: &UserPtRegs) -> u64 {
|
||||||
regs.regs[2]
|
regs.regs[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn retval(regs: &UserPtRegs) -> i64 {
|
const SYS_write: u64 = 64;
|
||||||
regs.regs[0] as i64
|
const SYS_sendto: u64 = 206;
|
||||||
}
|
const SYS_read: u64 = 63;
|
||||||
|
const SYS_recvfrom: u64 = 207;
|
||||||
// unsafe fn ptrace_cmd(cmd: libc::c_int, pid: Pid, addr: usize) -> nix::Result<()> {
|
|
||||||
// let mut regs = UserPtRegs::default();
|
|
||||||
// let r = unsafe {
|
|
||||||
// libc::ptrace(
|
|
||||||
// cmd.try_into().unwrap(),
|
|
||||||
// pid.as_raw(),
|
|
||||||
// addr as *mut libc::c_int,
|
|
||||||
// &mut regs as *mut _,
|
|
||||||
// )
|
|
||||||
// };
|
|
||||||
// if r == -1 {
|
|
||||||
// return Err(nix::errno::Errno::last());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn read_remote(pid: Pid, addr: usize, want: usize, out: &mut Vec<u8>) -> anyhow::Result<usize> {
|
|
||||||
if want == 0 {
|
|
||||||
return Ok(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const PAGE: usize = 4096;
|
|
||||||
// let start = addr;
|
|
||||||
let end = addr + want;
|
|
||||||
let first_clip = ((addr + PAGE) & !(PAGE - 1)).min(end);
|
|
||||||
let take = first_clip - addr;
|
|
||||||
|
|
||||||
out.resize(take, 0);
|
|
||||||
let mut local = iovec {
|
|
||||||
iov_base: out.as_mut_ptr().cast(),
|
|
||||||
iov_len: take,
|
|
||||||
};
|
|
||||||
let mut remote = iovec {
|
|
||||||
iov_base: addr as *mut _,
|
|
||||||
iov_len: take,
|
|
||||||
};
|
|
||||||
let n = unsafe { libc::process_vm_readv(pid.as_raw(), &mut local, 1, &mut remote, 1, 0) };
|
|
||||||
|
|
||||||
if n < 0 {
|
|
||||||
let e = std::io::Error::last_os_error();
|
|
||||||
anyhow::bail!("process vm ready failed at 0x{addr:x} len={take}: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(n as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// const SYS_write: u64 = 64;
|
|
||||||
// const SYS_sendto: u64 = 206;
|
|
||||||
// const SYS_read: u64 = 63;
|
|
||||||
// const SYS_recvfrom: u64 = 207;
|
|
||||||
|
|
||||||
fn is_send_syscall(no: u64) -> bool {
|
|
||||||
no as i64 == libc::SYS_write || no as i64 == libc::SYS_sendto
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_recv_syscall(no: u64) -> bool {
|
|
||||||
no as i64 == libc::SYS_read || no as i64 == libc::SYS_recvfrom
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ConnState {
|
struct ConnState {
|
||||||
buf: BytesMut,
|
buf: BytesMut,
|
||||||
last_alert: Instant,
|
last_alert: Instant
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnState {
|
impl ConnState {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
buf: BytesMut::with_capacity(4096),
|
buf: BytesMut::with_capacity(4096),
|
||||||
last_alert: Instant::now() - Duration::from_secs(3600),
|
last_alert: Instant::now() - Duration::from_secs(3600)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// v2: rework to also read all threads under pid
|
unsafe fn ptrace_cmd(cmd: libc::c_int, pid: Pid, addr: usize) -> nix::Result<()> {
|
||||||
pub fn run(
|
let mut regs = UserPtRegs::default();
|
||||||
pid: i32,
|
let r = unsafe { libc::ptrace(cmd.try_into().unwrap(), pid.as_raw(), addr as *mut libc::c_int, &mut regs as *mut _) };
|
||||||
rules: RuleSet,
|
if r == -1 {
|
||||||
action: String,
|
return Err(nix::errno::Errno::last());
|
||||||
debounce: Duration,
|
|
||||||
peek: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut tids: HashSet<Pid> = list_tids(pid).into_iter().collect();
|
|
||||||
anyhow::ensure!(!tids.is_empty(), "no threads for adbd");
|
|
||||||
|
|
||||||
info!("threads: {:?}", tids);
|
|
||||||
|
|
||||||
// attach to all tid
|
|
||||||
for t in &tids {
|
|
||||||
attach_and_prepare(*t)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set all to resume state
|
Ok(())
|
||||||
for t in &tids {
|
|
||||||
resume_syscall(*t)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection state of (tid, fd)
|
pub fn run(pid: i32, rules: RuleSet, action: String, debounce: Duration, peek: usize) -> Result<()> {
|
||||||
let mut conns: HashMap<(i32, i32), ConnState> = HashMap::new();
|
let pid = Pid::from_raw(pid);
|
||||||
|
// cannot compiled
|
||||||
|
// ptrace::seize(pid, ptrace::Options::PTRACE_O_TRACESYSGOOD)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptrace_cmd(PTRACE_SEIZE_FALLBACK.try_into().unwrap(), pid, 0)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cannot compiled
|
||||||
|
// ptrace::interrupt(pid)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptrace_cmd(PTRACE_INTERRUPT.try_into().unwrap(), pid, 0)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
waitpid(pid, None);
|
||||||
|
|
||||||
|
let mut conns: HashMap<i32, ConnState> = HashMap::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let st = waitpid(Pid::from_raw(-1), Some(WaitPidFlag::__WALL))?;
|
ptrace::syscall(pid, None)?;
|
||||||
|
let st = waitpid(pid, Some(WaitPidFlag::__WALL))?;
|
||||||
|
let WSIG = |ws: &WaitStatus| matches!(ws, WaitStatus::PtraceSyscall(_));
|
||||||
|
if !WSIG(&st){continue;}
|
||||||
|
|
||||||
match st {
|
// syscall entry
|
||||||
// syscall stop -> entry
|
let regs = get_regs(pid)?;
|
||||||
WaitStatus::PtraceSyscall(tid) => {
|
let scn = syscall_no(®s);
|
||||||
let regs_e = get_regs(tid)?;
|
let is_out = scn == SYS_write || scn == SYS_sendto;
|
||||||
let no = syscall_no(®s_e);
|
let is_in = scn == SYS_read || scn == SYS_recvfrom;
|
||||||
|
if !(is_in || is_out){
|
||||||
let (fd, buf_ptr, count) = if is_send_syscall(no) {
|
|
||||||
(
|
|
||||||
arg0(®s_e) as i32,
|
|
||||||
arg1(®s_e) as usize,
|
|
||||||
arg2(®s_e) as usize,
|
|
||||||
)
|
|
||||||
} else if is_recv_syscall(no) {
|
|
||||||
(
|
|
||||||
arg0(®s_e) as i32,
|
|
||||||
arg0(®s_e) as usize,
|
|
||||||
arg1(®s_e) as usize,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
//
|
|
||||||
resume_syscall(tid)?;
|
|
||||||
let _ = waitpid(tid, Some(WaitPidFlag::__WALL))?;
|
|
||||||
resume_syscall(tid)?;
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fd = arg0(®s) as i32;
|
||||||
|
let buf_ptr = if is_out {
|
||||||
|
arg1(®s)
|
||||||
|
} else {
|
||||||
|
arg0(®s)
|
||||||
};
|
};
|
||||||
|
let count = if is_out {
|
||||||
|
arg2(®s)
|
||||||
|
} else {
|
||||||
|
arg1(®s)
|
||||||
|
} as usize;
|
||||||
|
let take = count.min(peek);
|
||||||
|
|
||||||
info!("syscall({},{},{})", fd, buf_ptr, count);
|
// syscall run to exit, return value
|
||||||
|
ptrace::syscall(pid, None)?;
|
||||||
|
let _ = waitpid(pid, Some(WaitPidFlag::__WALL))?;
|
||||||
|
|
||||||
// run to exit
|
// copy user buffer
|
||||||
resume_syscall(tid)?;
|
if take > 0 {
|
||||||
let st2 = waitpid(tid, Some(WaitPidFlag::__WALL))?;
|
let mut data = vec![0u8; take];
|
||||||
if let WaitStatus::PtraceSyscall(_tid2) = st2 {
|
let local_iov = libc::iovec { iov_base: data.as_mut_ptr() as *mut _, iov_len: take};
|
||||||
let regs_x = get_regs(tid)?;
|
let remote_iov = libc::iovec { iov_base: buf_ptr as *mut _, iov_len: take};
|
||||||
let ret = retval(®s_x);
|
unsafe {
|
||||||
|
let n = libc::process_vm_readv(pid.as_raw(), &local_iov as *const _, 1, &remote_iov as *const _, 1, 0);
|
||||||
|
if n as isize <= 0 {continue;}
|
||||||
|
}
|
||||||
|
let s = conns.entry(fd).or_insert_with(ConnState::new);
|
||||||
|
s.buf.extend_from_slice(&data);
|
||||||
|
|
||||||
info!("regX({ret})");
|
|
||||||
|
|
||||||
if ret > 0 {
|
|
||||||
// peek byte from user buffer
|
|
||||||
let want = (ret as usize).min(peek).min(count);
|
|
||||||
if want > 0 {
|
|
||||||
let mut tmp = Vec::new();
|
|
||||||
if let Ok(n) = read_remote(tid, buf_ptr, want, &mut tmp) {
|
|
||||||
info!("read_remote({tid}, {buf_ptr}, {want}, {n}) = {tmp:?}");
|
|
||||||
if n > 0 {
|
|
||||||
let key = (tid.as_raw(), fd);
|
|
||||||
let s = conns.entry(key).or_insert_with(ConnState::new);
|
|
||||||
s.buf.extend_from_slice(&tmp[..n]);
|
|
||||||
|
|
||||||
// try parsing
|
|
||||||
while let Some(frame) = adb::try_parse(&mut s.buf){
|
while let Some(frame) = adb::try_parse(&mut s.buf){
|
||||||
info!("[cmd] {}, {:?}", frame.cmd, frame.payload);
|
|
||||||
if frame.cmd == adb::CMD_OPEN {
|
if frame.cmd == adb::CMD_OPEN {
|
||||||
let service =
|
let service = String::from_utf8_lossy(&frame.payload).to_string();
|
||||||
String::from_utf8_lossy(&frame.payload).to_string();
|
|
||||||
if let Some(rule) = rules.match_service(&service) {
|
if let Some(rule) = rules.match_service(&service) {
|
||||||
if s.last_alert.elapsed() >= debounce {
|
if s.last_alert.elapsed() >= debounce {
|
||||||
broadcaster::broadcast(&action, &service, rule);
|
broadcaster::broadcast(&action, &service, rule);
|
||||||
|
|
@ -316,47 +171,3 @@ pub fn run(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
resume_syscall(tid)?;
|
|
||||||
} else {
|
|
||||||
// error but resume to keep thread running
|
|
||||||
resume_syscall(tid)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WaitStatus::Stopped(tid, _sig) => {
|
|
||||||
info!("{tid} stopped");
|
|
||||||
// signal stop -> put back to syscall
|
|
||||||
let _ = resume_syscall(tid);
|
|
||||||
}
|
|
||||||
WaitStatus::PtraceEvent(tid, _sig, ev) => {
|
|
||||||
let ev_i = ev as i32;
|
|
||||||
info!("{tid} ev: {ev_i}");
|
|
||||||
if ev_i == libc::PTRACE_EVENT_CLONE {
|
|
||||||
// has new tid
|
|
||||||
let new_tid = Pid::from_raw(geteventmsg_u32(tid) as i32);
|
|
||||||
if !tids.contains(&new_tid) {
|
|
||||||
attach_and_prepare(new_tid)?;
|
|
||||||
resume_syscall(new_tid)?;
|
|
||||||
tids.insert(new_tid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// resume parent
|
|
||||||
let _ = resume_syscall(tid);
|
|
||||||
}
|
|
||||||
WaitStatus::Exited(tid, _) | WaitStatus::Signaled(tid, _, _) => {
|
|
||||||
info!("{tid} exitted");
|
|
||||||
tids.remove(&tid);
|
|
||||||
// cleanup
|
|
||||||
conns.retain(|(t, _fd), _| *t != tid.as_raw());
|
|
||||||
if tids.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// ignored other statuses
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,631 +0,0 @@
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use bytes::{Bytes, BytesMut};
|
|
||||||
use crossbeam_channel as xch;
|
|
||||||
use nix::{
|
|
||||||
errno::Errno,
|
|
||||||
sys::{
|
|
||||||
ptrace::{self, Options},
|
|
||||||
uio::process_vm_readv,
|
|
||||||
wait::{WaitPidFlag, WaitStatus, waitpid},
|
|
||||||
},
|
|
||||||
unistd::Pid,
|
|
||||||
};
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use std::{
|
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
fs,
|
|
||||||
io::Read,
|
|
||||||
path::PathBuf,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
thread,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
use crate::{adb, broadcaster, rule::RuleSet};
|
|
||||||
|
|
||||||
// ====== arch & regs (aarch64) ======
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Default)]
|
|
||||||
struct UserPtRegs {
|
|
||||||
regs: [u64; 31],
|
|
||||||
sp: u64,
|
|
||||||
pc: u64,
|
|
||||||
pstate: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn sys_no(r: &UserPtRegs) -> i64 {
|
|
||||||
r.regs[8] as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn arg0(r: &UserPtRegs) -> u64 {
|
|
||||||
r.regs[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn arg1(r: &UserPtRegs) -> u64 {
|
|
||||||
r.regs[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn arg2(r: &UserPtRegs) -> u64 {
|
|
||||||
r.regs[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn retval(r: &UserPtRegs) -> i64 {
|
|
||||||
r.regs[0] as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
// FALLBACK constants, in case of unsupported os while compiling
|
|
||||||
const PTRACE_GETREGSET_FALLBACK: nix::libc::c_int = 0x4204;
|
|
||||||
// const PTRACE_SEIZE_FALLBACK: libc::c_int = 0x4206;
|
|
||||||
// const PTRACE_INTERRUPT: libc::c_int = 0x4207;
|
|
||||||
const NT_PRSTATUS_FALLBACK: nix::libc::c_int = 1;
|
|
||||||
const SYS_fork: nix::libc::c_long = 57;
|
|
||||||
const SYS_vfork: nix::libc::c_long = 58;
|
|
||||||
|
|
||||||
fn get_regs(tid: Pid) -> Result<UserPtRegs> {
|
|
||||||
use nix::libc::iovec;
|
|
||||||
let mut regs = UserPtRegs::default();
|
|
||||||
let mut iov = iovec {
|
|
||||||
iov_base: (&mut regs as *mut UserPtRegs).cast(),
|
|
||||||
iov_len: core::mem::size_of::<UserPtRegs>(),
|
|
||||||
};
|
|
||||||
let rc = unsafe {
|
|
||||||
nix::libc::ptrace(
|
|
||||||
PTRACE_GETREGSET_FALLBACK.try_into().unwrap(),
|
|
||||||
tid.as_raw(),
|
|
||||||
NT_PRSTATUS_FALLBACK,
|
|
||||||
&mut iov,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if rc == -1 {
|
|
||||||
Err(anyhow::anyhow!(
|
|
||||||
"GETREGSET({tid}) {}",
|
|
||||||
std::io::Error::last_os_error()
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(regs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====== tiny helpers ======
|
|
||||||
fn read_to_string(p: impl AsRef<std::path::Path>) -> Option<String> {
|
|
||||||
let mut f = fs::File::open(p).ok()?;
|
|
||||||
let mut s = String::new();
|
|
||||||
f.read_to_string(&mut s).ok()?;
|
|
||||||
Some(s)
|
|
||||||
}
|
|
||||||
fn list_tids(pid: i32) -> Vec<i32> {
|
|
||||||
fs::read_dir(format!("/proc/{pid}/task"))
|
|
||||||
.ok()
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|it| it.filter_map(|e| e.ok()))
|
|
||||||
.filter_map(|e| e.file_name().into_string().ok())
|
|
||||||
.filter_map(|s| s.parse::<i32>().ok())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
fn hex_port_to_u16(hex: &str) -> Option<u16> {
|
|
||||||
u16::from_str_radix(hex, 16).ok()
|
|
||||||
}
|
|
||||||
fn parse_tcp_inodes(target_port: u16) -> HashSet<u64> {
|
|
||||||
fn grab(file: &str, target_port: u16) -> HashSet<u64> {
|
|
||||||
let mut set = HashSet::new();
|
|
||||||
if let Some(s) = read_to_string(file) {
|
|
||||||
for line in s.lines().skip(1) {
|
|
||||||
let cols: Vec<_> = line.split_whitespace().collect();
|
|
||||||
if cols.len() < 10 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let lp = cols[1].split(':').nth(1).unwrap_or("");
|
|
||||||
if let Some(p) = hex_port_to_u16(lp) {
|
|
||||||
if p == target_port {
|
|
||||||
if let Ok(i) = cols[9].parse::<u64>() {
|
|
||||||
set.insert(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
}
|
|
||||||
let mut s = grab("/proc/net/tcp", target_port);
|
|
||||||
s.extend(grab("/proc/net/tcp6", target_port));
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Discover TIDs that touch ADB transport FDs.
|
|
||||||
fn discover_transport_tids(pid: i32, port: u16) -> HashSet<i32> {
|
|
||||||
let tcp_inodes = parse_tcp_inodes(port);
|
|
||||||
let mut tids = HashSet::new();
|
|
||||||
for tid in list_tids(pid) {
|
|
||||||
let fd_dir = format!("/proc/{pid}/task/{tid}/fd");
|
|
||||||
let Ok(rd) = fs::read_dir(&fd_dir) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
'fdloop: for fd in rd.flatten() {
|
|
||||||
let target = fs::read_link(fd.path()).unwrap_or(PathBuf::new());
|
|
||||||
let s = target.to_string_lossy();
|
|
||||||
if s.contains("usb-ffs") && s.contains("/adb/") {
|
|
||||||
tids.insert(tid);
|
|
||||||
break 'fdloop;
|
|
||||||
}
|
|
||||||
if let Some(idx) = s.find("socket:[") {
|
|
||||||
if let Ok(inode) = s[idx + 8..].trim_end_matches(']').parse::<u64>() {
|
|
||||||
if tcp_inodes.contains(&inode) {
|
|
||||||
tids.insert(tid);
|
|
||||||
break 'fdloop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tids
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ResumeGuard(Pid);
|
|
||||||
impl Drop for ResumeGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = nix::sys::ptrace::syscall(self.0, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====== ptrace bits ======
|
|
||||||
fn attach_one(tid: Pid) -> Result<()> {
|
|
||||||
match ptrace::attach(tid) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) if e == Errno::EPERM || e == Errno::EBUSY => return Ok(()),
|
|
||||||
Err(e) => return Err(anyhow::anyhow!("attach({tid}) {e}")),
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
match waitpid(tid, Some(WaitPidFlag::__WALL))? {
|
|
||||||
WaitStatus::Stopped(_, _)
|
|
||||||
| WaitStatus::PtraceSyscall(_)
|
|
||||||
| WaitStatus::PtraceEvent(_, _, _) => break,
|
|
||||||
WaitStatus::Exited(_, _) | WaitStatus::Signaled(_, _, _) => {
|
|
||||||
return Err(anyhow::anyhow!("tid {tid} exited"));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ptrace::setoptions(
|
|
||||||
tid,
|
|
||||||
Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACECLONE | Options::PTRACE_O_TRACEEXIT,
|
|
||||||
)?;
|
|
||||||
ptrace::syscall(tid, None)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn detach_one(tid: Pid) {
|
|
||||||
let _ = ptrace::detach(tid, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Saftety read each page
|
|
||||||
fn read_user_page_safe(tid: Pid, addr: usize, want: usize, out: &mut Vec<u8>) -> Result<usize> {
|
|
||||||
use nix::libc::{iovec, process_vm_readv};
|
|
||||||
if want == 0 {
|
|
||||||
return Ok(0);
|
|
||||||
}
|
|
||||||
const PAGE: usize = 4096;
|
|
||||||
let page_end = ((addr + PAGE) & !(PAGE - 1)).max(addr);
|
|
||||||
let take = want.min(page_end.saturating_sub(addr));
|
|
||||||
out.resize(take, 0);
|
|
||||||
let mut liov = iovec {
|
|
||||||
iov_base: out.as_mut_ptr().cast(),
|
|
||||||
iov_len: take,
|
|
||||||
};
|
|
||||||
let mut riov = iovec {
|
|
||||||
iov_base: addr as *mut _,
|
|
||||||
iov_len: take,
|
|
||||||
};
|
|
||||||
let n = unsafe { process_vm_readv(tid.as_raw(), &mut liov, 1, &mut riov, 1, 0) };
|
|
||||||
if n < 0 {
|
|
||||||
Err(anyhow::anyhow!(
|
|
||||||
"process_vm_readv {tid} 0x{addr:x} {take}: {}",
|
|
||||||
std::io::Error::last_os_error()
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(n as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_cstring(pid: Pid, mut addr: usize, max: usize) -> anyhow::Result<String> {
|
|
||||||
const PAGE: usize = 4096;
|
|
||||||
let mut out = Vec::new();
|
|
||||||
|
|
||||||
while out.len() < max {
|
|
||||||
let end = ((addr + PAGE) & !(PAGE - 1)).max(addr);
|
|
||||||
let take = (end - addr).min(max - out.len());
|
|
||||||
let mut buf = vec![0u8; take];
|
|
||||||
let n = read_user_page_safe(pid, addr, take, &mut buf)?;
|
|
||||||
if n == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if let Some(z) = buf[..n].iter().position(|&b| b == 0) {
|
|
||||||
out.extend_from_slice(&buf[..z]);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
out.extend_from_slice(&buf[..n]);
|
|
||||||
addr += n;
|
|
||||||
if n < take {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(String::from_utf8_lossy(&out).to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_argv(
|
|
||||||
pid: Pid,
|
|
||||||
argv_ptr: usize,
|
|
||||||
max_args: usize,
|
|
||||||
max_len: usize,
|
|
||||||
) -> anyhow::Result<Vec<String>> {
|
|
||||||
use nix::libc::{iovec, process_vm_readv};
|
|
||||||
let ps = std::mem::size_of::<usize>();
|
|
||||||
let mut res = Vec::new();
|
|
||||||
|
|
||||||
for i in 0..max_args {
|
|
||||||
let mut pbuf = [0u8; 8];
|
|
||||||
let mut liov = iovec {
|
|
||||||
iov_base: pbuf.as_mut_ptr().cast(),
|
|
||||||
iov_len: ps,
|
|
||||||
};
|
|
||||||
let mut riov = iovec {
|
|
||||||
iov_base: (argv_ptr + i * ps) as *mut _,
|
|
||||||
iov_len: ps,
|
|
||||||
};
|
|
||||||
let n = unsafe { process_vm_readv(pid.as_raw(), &mut liov, 1, &mut riov, 1, 0) };
|
|
||||||
if n as usize != ps {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let ptr = usize::from_ne_bytes(pbuf);
|
|
||||||
if ptr == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
res.push(read_cstring(pid, ptr, max_len).unwrap_or_default());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fd_target(tid: i32, fd: i32) -> Option<String> {
|
|
||||||
fs::read_link(format!("/proc/{tid}/fd/{fd}"))
|
|
||||||
.ok()
|
|
||||||
.map(|p| p.to_string_lossy().into_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ascii_preview(s: &[u8]) -> String {
|
|
||||||
let mut o = String::new();
|
|
||||||
for &b in s {
|
|
||||||
match b {
|
|
||||||
b'\n' => o.push_str("\\n"),
|
|
||||||
b'\r' => o.push_str("\\r"),
|
|
||||||
b'\t' => o.push_str("\\t"),
|
|
||||||
0x20..=0x7e => o.push(b as char),
|
|
||||||
_ => o.push_str(&format!("\\x{:02x}", b)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
o
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_send(no: i64) -> bool {
|
|
||||||
// no == nix::libc::SYS_write as i64 || no == nix::libc::SYS_sendto as i64
|
|
||||||
false
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_recv(no: i64) -> bool {
|
|
||||||
no == nix::libc::SYS_read as i64 || no == nix::libc::SYS_recvfrom as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_exec(no: i64) -> bool {
|
|
||||||
no == nix::libc::SYS_execve as i64 || no == nix::libc::SYS_execveat as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_clone(no: i64) -> bool {
|
|
||||||
no == nix::libc::SYS_clone as i64 || no == SYS_fork as i64 || no == SYS_vfork as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====== public entry ======
|
|
||||||
|
|
||||||
/// Start the combined reactor. Call this from your tokio::main.
|
|
||||||
pub async fn run_with_tokio_rayon(
|
|
||||||
adbd_pid: i32,
|
|
||||||
rules: RuleSet,
|
|
||||||
broadcast_action: String,
|
|
||||||
debounce: Duration,
|
|
||||||
peek: usize,
|
|
||||||
adb_tcp_port: u16,
|
|
||||||
) -> Result<()> {
|
|
||||||
// -------- channels --------
|
|
||||||
type Slice = (i32 /*tid*/, i32 /*fd*/, Bytes);
|
|
||||||
// ptrace → consumers (bounded to never block ptrace thread)
|
|
||||||
let (tx_slice, rx_slice) = xch::bounded::<Slice>(1024);
|
|
||||||
// control: desired TID set (rescan task → ptrace thread)
|
|
||||||
let (tx_ctl, rx_ctl) = xch::bounded::<HashSet<i32>>(4);
|
|
||||||
|
|
||||||
// -------- ptrace thread (blocking) --------
|
|
||||||
thread::spawn({
|
|
||||||
let tx = tx_slice.clone();
|
|
||||||
move || {
|
|
||||||
let mut attached: HashSet<i32> = HashSet::new();
|
|
||||||
loop {
|
|
||||||
// Non-blockingly apply control updates
|
|
||||||
while let Ok(want) = rx_ctl.try_recv() {
|
|
||||||
// attach new
|
|
||||||
for tid in want.difference(&attached.clone()) {
|
|
||||||
if attach_one(Pid::from_raw(*tid)).is_ok() {
|
|
||||||
tracing::info!("attached transport tid={}", tid);
|
|
||||||
attached.insert(*tid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// detach stale
|
|
||||||
for tid in attached.clone() {
|
|
||||||
if !want.contains(&tid) {
|
|
||||||
detach_one(Pid::from_raw(tid));
|
|
||||||
attached.remove(&tid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if attached.is_empty() {
|
|
||||||
thread::sleep(Duration::from_millis(20));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for any stop (from our tracees)
|
|
||||||
let st = match waitpid(Pid::from_raw(-1), Some(WaitPidFlag::__WALL)) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(Errno::ECHILD) => {
|
|
||||||
attached.clear();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::warn!("waitpid: {e}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match st {
|
|
||||||
WaitStatus::PtraceSyscall(tid) => {
|
|
||||||
let _rg = ResumeGuard(tid);
|
|
||||||
// entry
|
|
||||||
let regs_e = match get_regs(tid) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(_) => {
|
|
||||||
let _ = ptrace::syscall(tid, None);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let no = sys_no(®s_e);
|
|
||||||
|
|
||||||
let is_in_target =
|
|
||||||
is_send(no) || is_recv(no) || is_clone(no) || is_exec(no);
|
|
||||||
|
|
||||||
if !is_in_target {
|
|
||||||
let _ = ptrace::syscall(tid, None);
|
|
||||||
let _ = waitpid(tid, Some(WaitPidFlag::__WALL));
|
|
||||||
let _ = ptrace::syscall(tid, None);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------ ENTRY actions ---------
|
|
||||||
if is_exec(no) {
|
|
||||||
// execve(path=a0, argv=a1)
|
|
||||||
// execveat(path=a1, argv=a2)
|
|
||||||
let (path_ptr, argv_ptr) = if no == nix::libc::SYS_execve as i64 {
|
|
||||||
(arg0(®s_e) as usize, arg1(®s_e) as usize)
|
|
||||||
} else {
|
|
||||||
(arg1(®s_e) as usize, arg2(®s_e) as usize)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(path) = read_cstring(tid, path_ptr, 512) {
|
|
||||||
let argv = read_argv(tid, argv_ptr, 8, 256).unwrap_or_default();
|
|
||||||
info!("execve({}, {:?})", path, argv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run to exit
|
|
||||||
let _ = ptrace::syscall(tid, None);
|
|
||||||
if let Ok(WaitStatus::PtraceSyscall(_)) =
|
|
||||||
waitpid(tid, Some(WaitPidFlag::__WALL))
|
|
||||||
{
|
|
||||||
let regs_x = match get_regs(tid) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(_) => {
|
|
||||||
let _ = ptrace::syscall(tid, None);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let ret = retval(®s_x);
|
|
||||||
if ret > 0 && (is_recv(no) || is_send(no)) {
|
|
||||||
let (fd, buf_ptr, count) = (
|
|
||||||
arg0(®s_e) as i32,
|
|
||||||
arg1(®s_e) as usize,
|
|
||||||
arg2(®s_e) as usize,
|
|
||||||
);
|
|
||||||
let want = (ret as usize).min(peek).min(count);
|
|
||||||
if want > 0 {
|
|
||||||
let mut tmp = Vec::new();
|
|
||||||
if let Ok(n) = read_user_page_safe(tid, buf_ptr, want, &mut tmp)
|
|
||||||
{
|
|
||||||
// resume ASAP
|
|
||||||
// let _ = ptrace::syscall(tid, None);
|
|
||||||
// // push slice (drop if full)
|
|
||||||
// let _ = tx.try_send((
|
|
||||||
// tid.as_raw(),
|
|
||||||
// fd,
|
|
||||||
// Bytes::copy_from_slice(&tmp[..n]),
|
|
||||||
// ));
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// log pty i/o
|
|
||||||
if let Some(tgt) = fd_target(tid.as_raw(), fd) {
|
|
||||||
if tgt.contains("/dev/pts") {
|
|
||||||
let what =
|
|
||||||
if is_recv(no) { "read" } else { "write" };
|
|
||||||
info!(
|
|
||||||
"{} fd={} {}: {}",
|
|
||||||
what,
|
|
||||||
fd,
|
|
||||||
tgt,
|
|
||||||
ascii_preview(&tmp[..n])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// resume even if no data
|
|
||||||
let _ = ptrace::syscall(tid, None);
|
|
||||||
} else {
|
|
||||||
let _ = ptrace::syscall(tid, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// plain signal stops → keep moving
|
|
||||||
WaitStatus::Stopped(tid, _sig) => {
|
|
||||||
let _rg = ResumeGuard(tid);
|
|
||||||
}
|
|
||||||
WaitStatus::PtraceEvent(tid, _sig, ev) => {
|
|
||||||
let _rg = ResumeGuard(tid);
|
|
||||||
|
|
||||||
match (ev as i32) {
|
|
||||||
1..=3 => {
|
|
||||||
// CLONE/FORK/VFORK
|
|
||||||
let new_tid = {
|
|
||||||
let mut v: nix::libc::c_ulong = 0;
|
|
||||||
unsafe {
|
|
||||||
nix::libc::ptrace(
|
|
||||||
nix::libc::PTRACE_GETEVENTMSG,
|
|
||||||
tid.as_raw(),
|
|
||||||
0,
|
|
||||||
&mut v as *mut _,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
v as i32
|
|
||||||
};
|
|
||||||
|
|
||||||
let ntid = Pid::from_raw(new_tid);
|
|
||||||
if let Ok(_) = ptrace::attach(ntid) {
|
|
||||||
let _ = waitpid(ntid, Some(WaitPidFlag::__WALL));
|
|
||||||
let _ = ptrace::setoptions(
|
|
||||||
ntid,
|
|
||||||
Options::PTRACE_O_TRACESYSGOOD
|
|
||||||
| Options::PTRACE_O_TRACECLONE
|
|
||||||
| Options::PTRACE_O_TRACEFORK
|
|
||||||
| Options::PTRACE_O_TRACEVFORK
|
|
||||||
| Options::PTRACE_O_TRACEEXEC,
|
|
||||||
);
|
|
||||||
let _ = ptrace::syscall(ntid, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4 => {
|
|
||||||
// EXEC
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// always resume
|
|
||||||
let _ = ptrace::syscall(tid, None);
|
|
||||||
}
|
|
||||||
// exits
|
|
||||||
WaitStatus::Exited(tid, _) | WaitStatus::Signaled(tid, _, _) => {
|
|
||||||
let _rg = ResumeGuard(tid);
|
|
||||||
attached.remove(&tid.as_raw());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// -------- Tokio task: periodic rescan & control updates --------
|
|
||||||
tokio::spawn({
|
|
||||||
let tx_ctl = tx_ctl.clone();
|
|
||||||
async move {
|
|
||||||
loop {
|
|
||||||
let want = discover_transport_tids(adbd_pid, adb_tcp_port);
|
|
||||||
// best effort; if the channel is full, the next tick will update
|
|
||||||
let _ = tx_ctl.try_send(want);
|
|
||||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// -------- Dispatcher → Rayon workers --------
|
|
||||||
// Shared per-conn state (tid,fd) → buffer + last_alert
|
|
||||||
let state = Arc::new(Mutex::new(HashMap::<(i32, i32), (BytesMut, Instant)>::new()));
|
|
||||||
let rules = Arc::new(rules);
|
|
||||||
let action = broadcast_action.clone();
|
|
||||||
|
|
||||||
// Blocking dispatcher thread to drain crossbeam receiver efficiently
|
|
||||||
thread::spawn({
|
|
||||||
let state = Arc::clone(&state);
|
|
||||||
let rules = Arc::clone(&rules);
|
|
||||||
move || {
|
|
||||||
// rayon pool defaults are fine; configure if you want:
|
|
||||||
// rayon::ThreadPoolBuilder::new().num_threads(...).build_global().ok();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Build a batch (blocking for first item)
|
|
||||||
let mut batch: Vec<(i32, i32, Bytes)> = Vec::with_capacity(256);
|
|
||||||
match rx_slice.recv() {
|
|
||||||
Ok(first) => batch.push(first),
|
|
||||||
Err(_) => break, // channel closed
|
|
||||||
}
|
|
||||||
while let Ok(item) = rx_slice.try_recv() {
|
|
||||||
batch.push(item);
|
|
||||||
if batch.len() >= 1024 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process in parallel
|
|
||||||
batch.par_iter().for_each(|(tid, fd, chunk)| {
|
|
||||||
let mut to_broadcast: Option<String> = None;
|
|
||||||
let mut matched_rule: Option<&str> = None;
|
|
||||||
{
|
|
||||||
let mut st = state.lock().unwrap();
|
|
||||||
let entry = st.entry((*tid, *fd)).or_insert_with(|| {
|
|
||||||
(BytesMut::with_capacity(8192), Instant::now() - debounce * 2)
|
|
||||||
});
|
|
||||||
entry.0.extend_from_slice(chunk);
|
|
||||||
|
|
||||||
while let Some(f) = adb::try_parse(&mut entry.0) {
|
|
||||||
if f.cmd == adb::CMD_OPEN {
|
|
||||||
let service = String::from_utf8_lossy(&f.payload).to_string();
|
|
||||||
if let Some(rule) = rules.match_service(&service) {
|
|
||||||
matched_rule = Some(rule);
|
|
||||||
if entry.1.elapsed() >= debounce {
|
|
||||||
entry.1 = Instant::now();
|
|
||||||
to_broadcast = Some(service);
|
|
||||||
break; // one alert per batch per conn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if entry.0.len() > 64 * 1024 {
|
|
||||||
entry.0.truncate(4096);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(service) = to_broadcast {
|
|
||||||
if matched_rule.is_some() {
|
|
||||||
crate::broadcaster::broadcast(&action, &service, matched_rule.unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// keep the async runtime alive (your main probably does more)
|
|
||||||
futures::future::pending::<()>().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,982 +0,0 @@
|
||||||
// V2
|
|
||||||
// - Fully write in daemon
|
|
||||||
// - Will use seize for asynchronus (not blocking the process)
|
|
||||||
// Note: this may not able to capture suspicious and block immediately
|
|
||||||
|
|
||||||
use nix::{
|
|
||||||
sys::{
|
|
||||||
signal::{self, SigHandler, Signal},
|
|
||||||
wait::{WaitPidFlag, WaitStatus, waitpid},
|
|
||||||
},
|
|
||||||
unistd::{ForkResult, Pid, fork, setsid},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
fs::{self, File, OpenOptions},
|
|
||||||
io::Write,
|
|
||||||
os::fd::{AsFd, AsRawFd},
|
|
||||||
path::Path,
|
|
||||||
sync::{Arc, Mutex, atomic::AtomicBool},
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
use tracing::{debug, error, info, warn};
|
|
||||||
|
|
||||||
use crate::nr::NR_CODES;
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
const DAEMON_NAME: &str = "adbdguard";
|
|
||||||
const PID_FILE: &str = "/data/local/adbdguard/adbdguard.pid";
|
|
||||||
const LOG_FILE: &str = "/data/local/adbdguard/adbdg.log";
|
|
||||||
const CONFIG_FILE: &str = "/data/local/adbdguard/adbdguard.conf";
|
|
||||||
const CONTROL_SOCKET: &str = "/data/local/adbdguard/adbdguard.sock";
|
|
||||||
|
|
||||||
// Global State
|
|
||||||
static DAEMON_RUNNING: AtomicBool = AtomicBool::new(false);
|
|
||||||
static RELOAD_CONFIG: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
pub type DefaultResult = Result<(), Box<dyn std::error::Error>>;
|
|
||||||
|
|
||||||
// Daemon config
|
|
||||||
// - this config is not from adbdguard.json
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct DaemonConfig {
|
|
||||||
pub log_level: LogLevel,
|
|
||||||
pub monitor_syscalls: bool,
|
|
||||||
pub monitor_network: bool,
|
|
||||||
pub monitor_files: bool,
|
|
||||||
pub rotation_size: u64,
|
|
||||||
/// seconds
|
|
||||||
pub status_interval: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum LogLevel {
|
|
||||||
Error,
|
|
||||||
Warn,
|
|
||||||
Info,
|
|
||||||
Debug,
|
|
||||||
Trace,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DaemonConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
log_level: LogLevel::Debug,
|
|
||||||
monitor_syscalls: true,
|
|
||||||
monitor_network: true,
|
|
||||||
monitor_files: false,
|
|
||||||
rotation_size: 10 * 1024 * 1024,
|
|
||||||
status_interval: 30,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------- Process --------------------
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ProcessInfo {
|
|
||||||
pid: Pid,
|
|
||||||
name: String,
|
|
||||||
cmdline: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options for SEIZE (does not use from lib)
|
|
||||||
const PTRACE_O_TRACESYSGOOD: i32 = 0x00000001;
|
|
||||||
const PTRACE_O_TRACEFORK: i32 = 0x00000002;
|
|
||||||
const PTRACE_O_TRACEVFORK: i32 = 0x00000004;
|
|
||||||
const PTRACE_O_TRACECLONE: i32 = 0x00000008;
|
|
||||||
const PTRACE_O_TRACEEXEC: i32 = 0x00000010;
|
|
||||||
const PTRACE_O_TRACEVFORKDONE: i32 = 0x00000020;
|
|
||||||
const PTRACE_O_TRACEEXIT: i32 = 0x00000040;
|
|
||||||
const PTRACE_O_TRACESECCOMP: i32 = 0x00000080;
|
|
||||||
|
|
||||||
const PTRACE_GETREGSET_FALLBACK: nix::libc::c_int = 0x4204;
|
|
||||||
const PTRACE_SEIZE_FALLBACK: libc::c_int = 0x4206;
|
|
||||||
const PTRACE_INTERRUPT: libc::c_int = 0x4207;
|
|
||||||
const NT_PRSTATUS_FALLBACK: nix::libc::c_int = 1;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DaemonLogger {
|
|
||||||
file: Arc<Mutex<File>>,
|
|
||||||
config: Arc<Mutex<DaemonConfig>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DaemonLogger {
|
|
||||||
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
|
||||||
let file = OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.append(true)
|
|
||||||
.open(LOG_FILE)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
file: Arc::new(Mutex::new(file)),
|
|
||||||
config: Arc::new(Mutex::new(DaemonConfig::default())),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log(&self, level: LogLevel, message: &str) {
|
|
||||||
let config = self.config.lock().unwrap();
|
|
||||||
if self.should_log(&level, &config.log_level) {
|
|
||||||
drop(config);
|
|
||||||
|
|
||||||
let timestamp = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC");
|
|
||||||
let log_line = format!("[{}] {:?}: {}\n", timestamp, level, message);
|
|
||||||
|
|
||||||
if let Ok(mut file) = self.file.lock() {
|
|
||||||
let _ = file.write_all(log_line.as_bytes());
|
|
||||||
let _ = file.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also print to stdout if not fully daemonized (for debugging)
|
|
||||||
if std::env::var("ADBDGUARD_DEBUG").is_ok() {
|
|
||||||
match level {
|
|
||||||
LogLevel::Error => error!("{}", message),
|
|
||||||
LogLevel::Warn => warn!("{}", message),
|
|
||||||
LogLevel::Info => info!("{}", message),
|
|
||||||
LogLevel::Debug => debug!("{}", message),
|
|
||||||
LogLevel::Trace => info!("{}", message),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_log(&self, msg_level: &LogLevel, config_level: &LogLevel) -> bool {
|
|
||||||
let def_level = |il: &LogLevel| -> i32 {
|
|
||||||
match il {
|
|
||||||
LogLevel::Error => 0,
|
|
||||||
LogLevel::Warn => 1,
|
|
||||||
LogLevel::Info => 2,
|
|
||||||
LogLevel::Debug => 3,
|
|
||||||
LogLevel::Trace => 4,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
def_level(msg_level) <= def_level(config_level)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_config(&self, new_config: DaemonConfig) {
|
|
||||||
*self.config.lock().unwrap() = new_config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Default)]
|
|
||||||
struct UserPtRegs {
|
|
||||||
regs: [u64; 31],
|
|
||||||
sp: u64,
|
|
||||||
pc: u64,
|
|
||||||
pstate: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PtraceDaemon {
|
|
||||||
target_processes: Vec<ProcessInfo>,
|
|
||||||
seized_pids: HashMap<Pid, bool>,
|
|
||||||
monitoring_active: bool,
|
|
||||||
logger: Arc<DaemonLogger>,
|
|
||||||
config: Arc<Mutex<DaemonConfig>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PtraceDaemon {
|
|
||||||
pub fn new(logger: Arc<DaemonLogger>) -> Self {
|
|
||||||
Self {
|
|
||||||
target_processes: Vec::new(),
|
|
||||||
seized_pids: HashMap::new(),
|
|
||||||
monitoring_active: false,
|
|
||||||
logger: logger.clone(),
|
|
||||||
config: Arc::new(Mutex::new(DaemonConfig::default())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// find adbd processes in the system
|
|
||||||
pub fn find_adbd_processes(&mut self) -> DefaultResult {
|
|
||||||
self.target_processes.clear();
|
|
||||||
let proc_dir = fs::read_dir("proc")?;
|
|
||||||
|
|
||||||
let mut adbd_pid: Option<Pid> = None;
|
|
||||||
|
|
||||||
for entry in proc_dir {
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
|
|
||||||
if let Some(pid_str) = path.file_name().and_then(|n| n.to_str()) {
|
|
||||||
if let Ok(pid_num) = pid_str.parse::<i32>() {
|
|
||||||
let pid = Pid::from_raw(pid_num);
|
|
||||||
let comm_path = path.join("comm");
|
|
||||||
if let Ok(comm) = fs::read_to_string(&comm_path) {
|
|
||||||
let process_name = comm.trim();
|
|
||||||
|
|
||||||
if (process_name.contains("adbd") || process_name == "adbd")
|
|
||||||
&& !process_name.contains("adbdguard")
|
|
||||||
{
|
|
||||||
let cmdline_path = path.join("cmdline");
|
|
||||||
let cmdline = fs::read_to_string(&cmdline_path)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.replace('\0', " ");
|
|
||||||
|
|
||||||
self.target_processes.push(ProcessInfo {
|
|
||||||
pid,
|
|
||||||
name: process_name.to_string(),
|
|
||||||
cmdline: cmdline.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Info,
|
|
||||||
&format!(
|
|
||||||
"Found adbd process: PID={}, Name={}, Cmdline={}",
|
|
||||||
pid, process_name, cmdline
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// seize main adbd
|
|
||||||
if let Err(e) = self.seize_process(pid) {
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Error,
|
|
||||||
&format!("Failed to seize PID {}: {}", pid, e),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Enable syscall tracing
|
|
||||||
if let Err(e) = self.enable_syscall_tracing(pid) {
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Warn,
|
|
||||||
&format!("Syscall tracing failed for PID {}: {}", pid, e),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
adbd_pid = Some(pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.target_processes.is_empty() {
|
|
||||||
self.logger.log(LogLevel::Warn, "No adbd processes found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if adbd_pid.is_some() {
|
|
||||||
if let Err(e) = self.seize_adb_tasks(adbd_pid.unwrap()) {
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Error,
|
|
||||||
&format!("Unable to seize task under adb: {e}"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// find tasks under adbd
|
|
||||||
fn seize_adb_tasks(&mut self, adbd_pid: Pid) -> DefaultResult {
|
|
||||||
let mut subtask = Vec::new();
|
|
||||||
// `/proc/{adbd_pid}/task`
|
|
||||||
if let Ok(rd) = fs::read_dir(format!("/proc/{}/task", adbd_pid.as_raw())) {
|
|
||||||
for e in rd.flatten() {
|
|
||||||
if let Ok(s) = e.file_name().into_string() {
|
|
||||||
if let Ok(t) = s.parse::<i32>() {
|
|
||||||
subtask.push(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// seize process
|
|
||||||
for pid_raw in subtask {
|
|
||||||
if pid_raw == adbd_pid.as_raw() {
|
|
||||||
self.logger.log(LogLevel::Debug, "skip root");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let expected_process_comm = format!("/proc/{pid_raw}/comm");
|
|
||||||
let expected_process_cmdline = format!("/proc/{pid_raw}/cmdline");
|
|
||||||
|
|
||||||
let comm_res = fs::read_to_string(&expected_process_comm);
|
|
||||||
let cmdline_res = fs::read_to_string(&expected_process_cmdline);
|
|
||||||
|
|
||||||
if comm_res.is_ok() && cmdline_res.is_ok() {
|
|
||||||
let pid = Pid::from_raw(pid_raw);
|
|
||||||
let name = comm_res.unwrap().to_string();
|
|
||||||
let cmdline = cmdline_res.unwrap().clone();
|
|
||||||
self.target_processes.push(ProcessInfo {
|
|
||||||
pid,
|
|
||||||
name: name.clone(),
|
|
||||||
cmdline: cmdline.clone(),
|
|
||||||
});
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Info,
|
|
||||||
&format!(
|
|
||||||
"Found adbd process: PID={}, Name={}, Cmdline={}",
|
|
||||||
pid,
|
|
||||||
name.clone(),
|
|
||||||
cmdline.clone()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = self.seize_process(pid) {
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Error,
|
|
||||||
&format!("Failed to seize PID {}: {}", pid, e),
|
|
||||||
);
|
|
||||||
return Err(format!("Failed to seize PID {}: {}", pid, e).into());
|
|
||||||
} else {
|
|
||||||
if let Err(e) = self.enable_syscall_tracing(pid) {
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Warn,
|
|
||||||
&format!("Syscall tracing failed for PID {}: {}", pid, e),
|
|
||||||
);
|
|
||||||
return Err(format!("Syscall tracing failed for PID {}: {}", pid, e).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// seize the process
|
|
||||||
pub fn seize_process(&mut self, pid: Pid) -> DefaultResult {
|
|
||||||
self.logger
|
|
||||||
.log(LogLevel::Debug, &format!("attempting seize pid {}", pid));
|
|
||||||
|
|
||||||
let options = PTRACE_O_TRACESYSGOOD
|
|
||||||
| PTRACE_O_TRACEFORK
|
|
||||||
| PTRACE_O_TRACECLONE
|
|
||||||
| PTRACE_O_TRACEEXEC
|
|
||||||
| PTRACE_O_TRACEEXIT
|
|
||||||
| PTRACE_O_TRACESECCOMP;
|
|
||||||
|
|
||||||
let result = unsafe {
|
|
||||||
libc::ptrace(
|
|
||||||
PTRACE_SEIZE_FALLBACK.try_into().unwrap(),
|
|
||||||
pid.as_raw(),
|
|
||||||
std::ptr::null_mut::<libc::c_void>(),
|
|
||||||
options as libc::c_ulong,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if result == -1 {
|
|
||||||
let error = nix::Error::last();
|
|
||||||
return Err(format!("failed to seize pid {}: {}", pid, error).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.logger
|
|
||||||
.log(LogLevel::Info, &format!("successfully seized pid {}", pid));
|
|
||||||
self.seized_pids.insert(pid, true);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// start monitoring loop
|
|
||||||
pub fn start_monitoring(&mut self) -> DefaultResult {
|
|
||||||
if self.seized_pids.is_empty() {
|
|
||||||
return Err(format!("No seized processes to monitor").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.monitoring_active = true;
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Info,
|
|
||||||
&format!("total processes to monitor: {}", self.seized_pids.len()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut event_count = 0u64;
|
|
||||||
let start_time = Instant::now();
|
|
||||||
let mut last_status = start_time;
|
|
||||||
|
|
||||||
while self.monitoring_active && DAEMON_RUNNING.load(std::sync::atomic::Ordering::SeqCst) {
|
|
||||||
if RELOAD_CONFIG.load(std::sync::atomic::Ordering::SeqCst) {
|
|
||||||
self.reload_configuration()?;
|
|
||||||
RELOAD_CONFIG.store(false, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
let pids: Vec<Pid> = self.seized_pids.keys().cloned().collect();
|
|
||||||
let mut any_activity = false;
|
|
||||||
|
|
||||||
for pid in pids {
|
|
||||||
let _status = waitpid(pid, Some(WaitPidFlag::WNOHANG))?;
|
|
||||||
if _status != WaitStatus::StillAlive {
|
|
||||||
self.logger
|
|
||||||
.log(LogLevel::Info, &format!("{pid}: {_status:?}"));
|
|
||||||
}
|
|
||||||
match _status {
|
|
||||||
WaitStatus::StillAlive => continue,
|
|
||||||
WaitStatus::PtraceSyscall(stopped_pid) => {
|
|
||||||
event_count += 1;
|
|
||||||
any_activity = true;
|
|
||||||
self.handle_syscall_event(stopped_pid)?;
|
|
||||||
|
|
||||||
nix::sys::ptrace::syscall(stopped_pid, None)?;
|
|
||||||
}
|
|
||||||
WaitStatus::Stopped(stopped_pid, signal) => {
|
|
||||||
event_count += 1;
|
|
||||||
any_activity = true;
|
|
||||||
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Debug,
|
|
||||||
&format!("pid {stopped_pid} stopped with code: {:?}", signal),
|
|
||||||
);
|
|
||||||
|
|
||||||
nix::sys::ptrace::cont(stopped_pid, None)?;
|
|
||||||
}
|
|
||||||
WaitStatus::PtraceEvent(stopped_pid, signal, event) => {
|
|
||||||
event_count += 1;
|
|
||||||
any_activity = true;
|
|
||||||
self.handle_ptrace_event(stopped_pid, signal, event)?;
|
|
||||||
|
|
||||||
nix::sys::ptrace::cont(stopped_pid, None)?;
|
|
||||||
}
|
|
||||||
WaitStatus::Exited(exited_pid, exit_code) => {
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Info,
|
|
||||||
&format!("pid {exited_pid} exitted with code {exit_code}"),
|
|
||||||
);
|
|
||||||
self.seized_pids.remove(&exited_pid);
|
|
||||||
any_activity = true;
|
|
||||||
|
|
||||||
if self.seized_pids.is_empty() {
|
|
||||||
self.logger
|
|
||||||
.log(LogLevel::Info, "All monitored processes exited");
|
|
||||||
self.monitoring_active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WaitStatus::Signaled(signaled_pid, signal, core_dump) => {
|
|
||||||
self.logger.log(LogLevel::Warn, &format!("pid {signaled_pid} terminated by signal {signal:?} (core {core_dump})"));
|
|
||||||
self.seized_pids.remove(&signaled_pid);
|
|
||||||
any_activity = true;
|
|
||||||
if self.seized_pids.is_empty() {
|
|
||||||
self.logger
|
|
||||||
.log(LogLevel::Info, "All monitored processes exited");
|
|
||||||
self.monitoring_active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
status => {
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Debug,
|
|
||||||
&format!("Unsupported status for pid {}: {:?}", pid, status),
|
|
||||||
);
|
|
||||||
any_activity = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// status report
|
|
||||||
let now = Instant::now();
|
|
||||||
if now.duration_since(last_status).as_secs()
|
|
||||||
>= self.config.lock().unwrap().status_interval
|
|
||||||
{
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Info,
|
|
||||||
&format!(
|
|
||||||
"Status: {} events in {:?}, {} processes active",
|
|
||||||
event_count,
|
|
||||||
now.duration_since(start_time),
|
|
||||||
self.seized_pids.len()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
last_status = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !any_activity {
|
|
||||||
std::thread::sleep(Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Info,
|
|
||||||
&format!("Monitoring stopped. Total events: {}", event_count),
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// clone processes info
|
|
||||||
pub fn clone_processes_info(self) -> Vec<ProcessInfo> {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
|
|
||||||
for pif in self.target_processes {
|
|
||||||
result.push(pif.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// clone seized pid state
|
|
||||||
pub fn clone_seized_pids(self) -> HashMap<Pid, bool> {
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
|
|
||||||
for (pid, state) in self.seized_pids.clone().iter() {
|
|
||||||
result.insert(pid.clone(), state.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
// handler ----------------------------
|
|
||||||
|
|
||||||
fn handle_syscall_event(&self, pid: Pid) -> DefaultResult {
|
|
||||||
use nix::libc::iovec;
|
|
||||||
let mut regs = UserPtRegs::default();
|
|
||||||
let mut iov = iovec {
|
|
||||||
iov_base: (&mut regs as *mut UserPtRegs).cast(),
|
|
||||||
iov_len: core::mem::size_of::<UserPtRegs>(),
|
|
||||||
};
|
|
||||||
let rc = unsafe {
|
|
||||||
nix::libc::ptrace(
|
|
||||||
PTRACE_GETREGSET_FALLBACK.try_into().unwrap(),
|
|
||||||
pid.as_raw(),
|
|
||||||
NT_PRSTATUS_FALLBACK,
|
|
||||||
&mut iov,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let syscall_num = regs.regs[8];
|
|
||||||
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Debug,
|
|
||||||
&format!("{} [{syscall_num}]>>[{rc}]", pid.as_raw()),
|
|
||||||
);
|
|
||||||
|
|
||||||
if rc == -1 {
|
|
||||||
return Err(format!("GETREGSET({pid}) {}", std::io::Error::last_os_error()).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_scoped_syscall(syscall_num) {
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Debug,
|
|
||||||
&format!(
|
|
||||||
"pid {} syscall: {} ({})",
|
|
||||||
pid,
|
|
||||||
syscall_num,
|
|
||||||
self.syscall_name(syscall_num)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_ptrace_event(&self, pid: Pid, _signal: Signal, event: i32) -> DefaultResult {
|
|
||||||
let event_desc = match event {
|
|
||||||
1 => "forked",
|
|
||||||
2 => "vforked",
|
|
||||||
3 => "cloned",
|
|
||||||
4 => "exec'd",
|
|
||||||
5 => "vfork done",
|
|
||||||
6 => "exiting",
|
|
||||||
7 => "seccomp event",
|
|
||||||
_ => "unknown event",
|
|
||||||
};
|
|
||||||
|
|
||||||
self.logger
|
|
||||||
.log(LogLevel::Info, &format!("PID {} {}", pid, event_desc));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------
|
|
||||||
|
|
||||||
fn reload_configuration(&mut self) -> DefaultResult {
|
|
||||||
self.logger.log(LogLevel::Info, "Reload configuration");
|
|
||||||
|
|
||||||
if Path::new(CONFIG_FILE).exists() {
|
|
||||||
let config_str = fs::read_to_string(CONFIG_FILE)?;
|
|
||||||
let mut config = DaemonConfig::default();
|
|
||||||
|
|
||||||
for line in config_str.lines() {
|
|
||||||
let parts: Vec<&str> = line.splitn(2, '=').collect();
|
|
||||||
if parts.len() == 2 {
|
|
||||||
match parts[0].trim() {
|
|
||||||
"log_level" => {
|
|
||||||
config.log_level = match parts[1].trim() {
|
|
||||||
"error" => LogLevel::Error,
|
|
||||||
"info" => LogLevel::Info,
|
|
||||||
"debug" => LogLevel::Debug,
|
|
||||||
"warn" => LogLevel::Warn,
|
|
||||||
"trace" => LogLevel::Trace,
|
|
||||||
_ => LogLevel::Debug,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
"monitor_syscalls" => {
|
|
||||||
config.monitor_syscalls = parts[1].trim().parse().unwrap_or(true);
|
|
||||||
}
|
|
||||||
"monitor_network" => {
|
|
||||||
config.monitor_network = parts[1].trim().parse().unwrap_or(false);
|
|
||||||
}
|
|
||||||
"status_interval" => {
|
|
||||||
config.status_interval = parts[1].trim().parse().unwrap_or(30);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*self.config.lock().unwrap() = config.clone();
|
|
||||||
self.logger.update_config(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_scoped_syscall(&self, syscall_num: u64) -> bool {
|
|
||||||
// match syscall_num {
|
|
||||||
// 41..=50 => true, // socket family
|
|
||||||
// 2..=6 => true, // file operations
|
|
||||||
// 57..=59 => true, // process operations
|
|
||||||
// 16 | 17 => true, // ioctl, pread64
|
|
||||||
// _ => false,
|
|
||||||
// }
|
|
||||||
|
|
||||||
let code = NR_CODES::get(syscall_num);
|
|
||||||
|
|
||||||
code.value() != u64::max_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn syscall_name(&self, syscall_num: u64) -> String {
|
|
||||||
// match syscall_num {
|
|
||||||
// 2 => "open",
|
|
||||||
// 3 => "close",
|
|
||||||
// 4 => "stat",
|
|
||||||
// 5 => "fstat",
|
|
||||||
// 6 => "lstat",
|
|
||||||
// 16 => "ioctl",
|
|
||||||
// 17 => "pread64",
|
|
||||||
// 41 => "socket",
|
|
||||||
// 42 => "connect",
|
|
||||||
// 43 => "accept",
|
|
||||||
// 44 => "sendto",
|
|
||||||
// 45 => "recvfrom",
|
|
||||||
// 46 => "sendmsg",
|
|
||||||
// 47 => "recvmsg",
|
|
||||||
// 57 => "fork",
|
|
||||||
// 58 => "vfork",
|
|
||||||
// 59 => "execve",
|
|
||||||
// _ => "unknown",
|
|
||||||
// }
|
|
||||||
|
|
||||||
let nr_code = NR_CODES::get(syscall_num);
|
|
||||||
let nr_code_name = nr_code.name();
|
|
||||||
|
|
||||||
nr_code_name
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable_syscall_tracing(&self, pid: Pid) -> DefaultResult {
|
|
||||||
if !self.seized_pids.contains_key(&pid) {
|
|
||||||
return Err(format!("PID {} not seized", pid).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.logger.log(
|
|
||||||
LogLevel::Debug,
|
|
||||||
&format!("Enabling syscall tracing for PID: {}", pid),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Interrupt and start syscall tracing
|
|
||||||
let interrupt_result = unsafe {
|
|
||||||
libc::ptrace(
|
|
||||||
PTRACE_INTERRUPT.try_into().unwrap(),
|
|
||||||
pid.as_raw(),
|
|
||||||
std::ptr::null_mut::<libc::c_void>(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if interrupt_result != -1 {
|
|
||||||
std::thread::sleep(Duration::from_millis(10));
|
|
||||||
nix::sys::ptrace::syscall(pid, None)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cleanup(&mut self) {
|
|
||||||
let pids: Vec<Pid> = self.seized_pids.keys().cloned().collect();
|
|
||||||
for pid in pids {
|
|
||||||
let _ = nix::sys::ptrace::detach(pid, None);
|
|
||||||
}
|
|
||||||
self.seized_pids.clear();
|
|
||||||
self.monitoring_active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Signal handlers for daemon control
|
|
||||||
fn setup_signal_handlers() -> Result<(), nix::Error> {
|
|
||||||
extern "C" fn handle_sigterm(_: libc::c_int) {
|
|
||||||
DAEMON_RUNNING.store(false, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn handle_sighup(_: libc::c_int) {
|
|
||||||
RELOAD_CONFIG.store(true, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn handle_sigint(_: libc::c_int) {
|
|
||||||
DAEMON_RUNNING.store(false, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
signal::signal(Signal::SIGTERM, SigHandler::Handler(handle_sigterm))?;
|
|
||||||
signal::signal(Signal::SIGHUP, SigHandler::Handler(handle_sighup))?;
|
|
||||||
signal::signal(Signal::SIGINT, SigHandler::Handler(handle_sigint))?;
|
|
||||||
signal::signal(Signal::SIGPIPE, SigHandler::SigIgn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write daemon PID to file
|
|
||||||
fn write_pid_file() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let pid = std::process::id();
|
|
||||||
fs::write(PID_FILE, pid.to_string())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove PID file
|
|
||||||
fn remove_pid_file() {
|
|
||||||
let _ = fs::remove_file(PID_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if daemon is already running
|
|
||||||
fn is_daemon_running() -> bool {
|
|
||||||
if let Ok(pid_str) = fs::read_to_string(PID_FILE) {
|
|
||||||
if let Ok(pid) = pid_str.trim().parse::<i32>() {
|
|
||||||
// Check if process exists
|
|
||||||
Path::new(&format!("/proc/{}", pid)).exists()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum DaemonCommand {
|
|
||||||
Start,
|
|
||||||
Stop,
|
|
||||||
Restart,
|
|
||||||
Status,
|
|
||||||
Reload,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn daemon_main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
// Setup logging
|
|
||||||
let logger = Arc::new(DaemonLogger::new()?);
|
|
||||||
logger.log(LogLevel::Info, "ADB Ptrace Daemon starting");
|
|
||||||
|
|
||||||
// Setup signal handlers
|
|
||||||
setup_signal_handlers()?;
|
|
||||||
|
|
||||||
// Write PID file
|
|
||||||
write_pid_file()?;
|
|
||||||
|
|
||||||
// Create daemon instance
|
|
||||||
let mut daemon = PtraceDaemon::new(logger.clone());
|
|
||||||
|
|
||||||
DAEMON_RUNNING.store(true, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
|
|
||||||
// Main daemon loop
|
|
||||||
loop {
|
|
||||||
if !DAEMON_RUNNING.load(std::sync::atomic::Ordering::SeqCst) {
|
|
||||||
logger.log(LogLevel::Info, "Shutdown signal received");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find and seize adbd processes
|
|
||||||
if let Err(e) = daemon.find_adbd_processes() {
|
|
||||||
logger.log(
|
|
||||||
LogLevel::Error,
|
|
||||||
&format!("Failed to find adbd processes: {}", e),
|
|
||||||
);
|
|
||||||
std::thread::sleep(Duration::from_secs(5));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = daemon.start_monitoring() {
|
|
||||||
logger.log(LogLevel::Error, &format!("Monitoring error: {}", e));
|
|
||||||
}
|
|
||||||
|
|
||||||
daemon.cleanup();
|
|
||||||
|
|
||||||
// Brief pause before retrying if processes disappeared
|
|
||||||
if DAEMON_RUNNING.load(std::sync::atomic::Ordering::SeqCst) {
|
|
||||||
std::thread::sleep(Duration::from_secs(5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
daemon.cleanup();
|
|
||||||
logger.log(LogLevel::Info, "ADB Ptrace Daemon stopped");
|
|
||||||
remove_pid_file();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_args() -> DaemonCommand {
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
|
||||||
if args.len() < 2 {
|
|
||||||
return DaemonCommand::Start;
|
|
||||||
}
|
|
||||||
|
|
||||||
match args[1].as_str() {
|
|
||||||
"start" => DaemonCommand::Start,
|
|
||||||
"stop" => DaemonCommand::Stop,
|
|
||||||
"restart" => DaemonCommand::Restart,
|
|
||||||
"status" => DaemonCommand::Status,
|
|
||||||
"reload" => DaemonCommand::Reload,
|
|
||||||
_ => {
|
|
||||||
eprintln!("Usage: {} [start|stop|restart|status|reload]", args[0]);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(command: DaemonCommand) -> DefaultResult {
|
|
||||||
match command {
|
|
||||||
DaemonCommand::Start => {
|
|
||||||
if is_daemon_running() {
|
|
||||||
println!("Daemon is already running");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Starting ADB Ptrace Daemon...");
|
|
||||||
|
|
||||||
// Check if we should run in foreground (for debugging)
|
|
||||||
if std::env::var("ADBD_TRACKER_FOREGROUND").is_ok() {
|
|
||||||
return daemon_main();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fork to background
|
|
||||||
match unsafe { fork() } {
|
|
||||||
Ok(ForkResult::Parent { .. }) => {
|
|
||||||
println!("Daemon started successfully");
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
Ok(ForkResult::Child) => {
|
|
||||||
// Become session leader
|
|
||||||
setsid()?;
|
|
||||||
|
|
||||||
// Change to root directory
|
|
||||||
std::env::set_current_dir("/")?;
|
|
||||||
|
|
||||||
// Close stdin, stdout, stderr
|
|
||||||
let devnull = fs::File::open("/dev/null")?;
|
|
||||||
nix::unistd::dup2_stdin(devnull.as_fd())?;
|
|
||||||
nix::unistd::dup2_stdout(devnull.as_fd())?;
|
|
||||||
nix::unistd::dup2_stderr(devnull.as_fd())?;
|
|
||||||
|
|
||||||
return daemon_main();
|
|
||||||
}
|
|
||||||
Err(e) => return Err(format!("Fork failed: {}", e).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DaemonCommand::Stop => {
|
|
||||||
if let Ok(pid_str) = fs::read_to_string(PID_FILE) {
|
|
||||||
if let Ok(pid) = pid_str.trim().parse::<i32>() {
|
|
||||||
println!("Stopping daemon (PID: {})...", pid);
|
|
||||||
unsafe {
|
|
||||||
libc::kill(pid, libc::SIGTERM);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for daemon to stop
|
|
||||||
for _ in 0..30 {
|
|
||||||
if !Path::new(&format!("/proc/{}", pid)).exists() {
|
|
||||||
println!("Daemon stopped successfully");
|
|
||||||
remove_pid_file();
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Daemon did not stop gracefully, sending SIGKILL");
|
|
||||||
unsafe {
|
|
||||||
libc::kill(pid, libc::SIGKILL);
|
|
||||||
}
|
|
||||||
remove_pid_file();
|
|
||||||
} else {
|
|
||||||
println!("Invalid PID file");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("Daemon is not running");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DaemonCommand::Restart => {
|
|
||||||
// Stop if running
|
|
||||||
if is_daemon_running() {
|
|
||||||
if let Ok(pid_str) = fs::read_to_string(PID_FILE) {
|
|
||||||
if let Ok(pid) = pid_str.trim().parse::<i32>() {
|
|
||||||
println!("Stopping daemon...");
|
|
||||||
unsafe {
|
|
||||||
libc::kill(pid, libc::SIGTERM);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for stop
|
|
||||||
for _ in 0..30 {
|
|
||||||
if !Path::new(&format!("/proc/{}", pid)).exists() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start again
|
|
||||||
println!("Starting daemon...");
|
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
|
||||||
|
|
||||||
match unsafe { fork() } {
|
|
||||||
Ok(ForkResult::Parent { .. }) => {
|
|
||||||
println!("Daemon restarted successfully");
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
Ok(ForkResult::Child) => {
|
|
||||||
setsid()?;
|
|
||||||
std::env::set_current_dir("/")?;
|
|
||||||
|
|
||||||
let devnull = fs::File::open("/dev/null")?;
|
|
||||||
nix::unistd::dup2_stdin(devnull.as_fd())?;
|
|
||||||
nix::unistd::dup2_stdout(devnull.as_fd())?;
|
|
||||||
nix::unistd::dup2_stderr(devnull.as_fd())?;
|
|
||||||
|
|
||||||
return daemon_main();
|
|
||||||
}
|
|
||||||
Err(e) => return Err(format!("Fork failed: {}", e).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DaemonCommand::Status => {
|
|
||||||
if is_daemon_running() {
|
|
||||||
if let Ok(pid_str) = fs::read_to_string(PID_FILE) {
|
|
||||||
println!("Daemon is running (PID: {})", pid_str.trim());
|
|
||||||
|
|
||||||
// Show log tail if available
|
|
||||||
if Path::new(LOG_FILE).exists() {
|
|
||||||
println!("\nRecent log entries:");
|
|
||||||
let _ = std::process::Command::new("tail")
|
|
||||||
.args(&["-n", "10", LOG_FILE])
|
|
||||||
.status();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("Daemon appears to be running but PID file is invalid");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("Daemon is not running");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DaemonCommand::Reload => {
|
|
||||||
if let Ok(pid_str) = fs::read_to_string(PID_FILE) {
|
|
||||||
if let Ok(pid) = pid_str.trim().parse::<i32>() {
|
|
||||||
println!("Reloading daemon configuration...");
|
|
||||||
unsafe {
|
|
||||||
libc::kill(pid, libc::SIGHUP);
|
|
||||||
}
|
|
||||||
println!("Reload signal sent");
|
|
||||||
} else {
|
|
||||||
println!("Invalid PID file");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("Daemon is not running");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,204 +0,0 @@
|
||||||
use anyhow::{Context, Result};
|
|
||||||
// use libbpf_rs::{MapCore, MapMut, ObjectBuilder, ProgramMut, RingBufferBuilder, UprobeOpts};
|
|
||||||
use aya::{Ebpf, maps::RingBuf, programs::UProbe};
|
|
||||||
use bytes::BytesMut;
|
|
||||||
use std::{fs, io::BufRead, path::PathBuf, time::Instant};
|
|
||||||
use tracing::error;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct Event {
|
|
||||||
pid: u32,
|
|
||||||
tid: u32,
|
|
||||||
is_send: u32,
|
|
||||||
len: u32,
|
|
||||||
data: [u8; 256],
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_libc_path_for(pid: i32) -> Result<PathBuf> {
|
|
||||||
let f = fs::File::open(format!("/proc/{pid}/maps"))?;
|
|
||||||
let rdr = std::io::BufReader::new(f);
|
|
||||||
let mut candidates = Vec::new();
|
|
||||||
for line in rdr.lines().flatten() {
|
|
||||||
if line.contains("libc.so") && line.contains("/lib64/") {
|
|
||||||
if let Some(path) = line.split_whitespace().last() {
|
|
||||||
if path.starts_with('/') {
|
|
||||||
candidates.push(PathBuf::from(path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
candidates
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.context("libc.so path not found in maps")
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn attach_sym(prog: &Arc<ProgramMut>, path: &str, sym: &str, pid: i32) -> Result<()> {
|
|
||||||
// let _ = prog.attach_uprobe_with_opts(
|
|
||||||
// pid,
|
|
||||||
// path,
|
|
||||||
// 0,
|
|
||||||
// UprobeOpts {
|
|
||||||
// func_name: Some(sym.to_string()),
|
|
||||||
// ..Default::default()
|
|
||||||
// },
|
|
||||||
// )?;
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Find the BPF map with the given name, panic if it does not exist.
|
|
||||||
// #[track_caller]
|
|
||||||
// pub fn get_map_mut<'obj>(object: &'obj mut libbpf_rs::Object, name: &str) -> MapMut<'obj> {
|
|
||||||
// object
|
|
||||||
// .maps_mut()
|
|
||||||
// .find(|map| map.name() == name)
|
|
||||||
// .unwrap_or_else(|| panic!("failed to find map `{name}`"))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Find the BPF program with the given name, panic if it does not exist.
|
|
||||||
// #[track_caller]
|
|
||||||
// pub fn get_prog_mut<'obj>(object: &'obj mut libbpf_rs::Object, name: &str) -> ProgramMut<'obj> {
|
|
||||||
// object
|
|
||||||
// .progs_mut()
|
|
||||||
// .find(|map| map.name() == name)
|
|
||||||
// .unwrap_or_else(|| panic!("failed to find program `{name}`"))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Find the BPF program with the given vector of symbol names.
|
|
||||||
// #[track_caller]
|
|
||||||
// pub fn get_multiple_prog_mut<'obj>(
|
|
||||||
// object: &'obj mut libbpf_rs::Object,
|
|
||||||
// names: Vec<String>,
|
|
||||||
// ) -> Arc<HashMap<String, Arc<ProgramMut<'obj>>>> {
|
|
||||||
// let mut result = HashMap::new();
|
|
||||||
|
|
||||||
// for name in names {
|
|
||||||
// let prog = object.progs_mut().find(|map| *map.name() == *name).take();
|
|
||||||
|
|
||||||
// if prog.is_some() {
|
|
||||||
// result.insert(name.clone(), Arc::new(prog.unwrap()));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Arc::new(result)
|
|
||||||
// }
|
|
||||||
|
|
||||||
struct PollFd<T>(T);
|
|
||||||
fn poll_fd<T>(t: T) -> PollFd<T> {
|
|
||||||
PollFd(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> PollFd<T> {
|
|
||||||
fn readable(&mut self) -> Guard<'_, T> {
|
|
||||||
Guard(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Guard<'a, T>(&'a mut PollFd<T>);
|
|
||||||
|
|
||||||
impl<T> Guard<'_, T> {
|
|
||||||
fn inner_mut(&mut self) -> &mut T {
|
|
||||||
let Guard(PollFd(t)) = self;
|
|
||||||
t
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_read(&mut self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_bpf_bytes() -> anyhow::Result<Vec<u8>> {
|
|
||||||
let bytes = include_bytes!("../../bpf/sendrecv.bpf.o");
|
|
||||||
if bytes.len() >= 4 && &bytes[..4] == b"\x7fELF" {
|
|
||||||
return Ok(bytes.to_vec());
|
|
||||||
}
|
|
||||||
// fallback
|
|
||||||
let p = "/data/local/adbdguard/bpf/sendrecv.bpf.o";
|
|
||||||
let v = std::fs::read(p)?;
|
|
||||||
if v.len() >= 4 && &v[..4] == b"\x7fELF" {
|
|
||||||
return Ok(v);
|
|
||||||
}
|
|
||||||
anyhow::bail!(
|
|
||||||
"BPF object is not ELF: embeded={} disk={}",
|
|
||||||
bytes.len(),
|
|
||||||
v.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(
|
|
||||||
adbd_pid: i32,
|
|
||||||
rules: crate::rule::RuleSet,
|
|
||||||
action: String,
|
|
||||||
debounce: std::time::Duration,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
// 1) Load object
|
|
||||||
let data = load_bpf_bytes().context("load bpf obj bytes")?; // or read from file
|
|
||||||
let mut skel = Ebpf::load(&data).context("load bpf obj")?;
|
|
||||||
let mut frames: std::collections::HashMap<i32, BytesMut> = Default::default();
|
|
||||||
let mut last_alert: std::collections::HashMap<i32, Instant> = Default::default();
|
|
||||||
|
|
||||||
// attach
|
|
||||||
let libc_path = find_libc_path_for(adbd_pid)?;
|
|
||||||
|
|
||||||
for (sec, sym, alt) in vec![
|
|
||||||
("up_send", "send", "sendto"),
|
|
||||||
("ur_send", "send", "sendto"),
|
|
||||||
("up_recv", "recv", "recvfrom"),
|
|
||||||
("ur_recv", "recv", "recvfrom"),
|
|
||||||
("up_write", "write", "write"),
|
|
||||||
("up_read", "read", "read"),
|
|
||||||
("ur_write", "write", "write"),
|
|
||||||
("ur_read", "read", "read"),
|
|
||||||
] {
|
|
||||||
let _prog: &mut UProbe = skel.program_mut(sec).unwrap().try_into()?;
|
|
||||||
_prog.load()?;
|
|
||||||
if let Err(e) = _prog.attach(Some(sym), 0, libc_path.clone(), Some(adbd_pid)) {
|
|
||||||
error!("retry {sec} with {alt}: {e}");
|
|
||||||
let _prog_retry: &mut UProbe = skel.program_mut(alt).unwrap().try_into()?;
|
|
||||||
_prog_retry.load()?;
|
|
||||||
let _ = _prog_retry.attach(Some(alt), 0, libc_path.clone(), Some(adbd_pid))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _rb = skel.map_mut("rb");
|
|
||||||
if _rb.is_some() {
|
|
||||||
let rb = RingBuf::try_from(_rb.unwrap())?;
|
|
||||||
let mut poll = poll_fd(rb);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut guard = poll.readable();
|
|
||||||
let ringbuf = guard.inner_mut();
|
|
||||||
|
|
||||||
while let Some(item) = ringbuf.next() {
|
|
||||||
if item.len() < std::mem::size_of::<Event>() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let evt: &Event = unsafe { &*(item.as_ptr() as *const Event) };
|
|
||||||
|
|
||||||
let key = evt.tid as i32;
|
|
||||||
let entry = frames
|
|
||||||
.entry(key)
|
|
||||||
.or_insert_with(|| BytesMut::with_capacity(8192));
|
|
||||||
entry.extend_from_slice(&evt.data[..evt.len as usize]);
|
|
||||||
|
|
||||||
while let Some(f) = crate::adb::try_parse(entry) {
|
|
||||||
if f.cmd == crate::adb::CMD_OPEN {
|
|
||||||
let service = String::from_utf8_lossy(&f.payload).to_string();
|
|
||||||
if let Some(rule) = rules.match_service(&service) {
|
|
||||||
let last = last_alert
|
|
||||||
.entry(key)
|
|
||||||
.or_insert(Instant::now() - debounce * 2);
|
|
||||||
if last.elapsed() >= debounce {
|
|
||||||
crate::broadcaster::broadcast(&action, &service, rule);
|
|
||||||
*last = Instant::now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard.clear_read();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,6 @@ use std::{fs, time::Duration};
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Rule {
|
pub struct Rule {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub when: String,
|
pub when: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub contains: Vec<String>,
|
pub contains: Vec<String>,
|
||||||
|
|
@ -17,7 +16,6 @@ pub struct Rule {
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[serde(default = "default_debounce")]
|
#[serde(default = "default_debounce")]
|
||||||
pub debounce_ms: u64,
|
pub debounce_ms: u64,
|
||||||
#[allow(dead_code)]
|
|
||||||
#[serde(default = "default_peek")]
|
#[serde(default = "default_peek")]
|
||||||
pub max_payload_peek: usize,
|
pub max_payload_peek: usize,
|
||||||
pub broadcast_action: String,
|
pub broadcast_action: String,
|
||||||
|
|
|
||||||
99
src/main.rs
99
src/main.rs
|
|
@ -4,8 +4,6 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "ptracev2")]
|
|
||||||
use backend::ptrace_v2;
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use notify::{RecommendedWatcher, Watcher};
|
use notify::{RecommendedWatcher, Watcher};
|
||||||
use rule::RuleSet;
|
use rule::RuleSet;
|
||||||
|
|
@ -16,8 +14,8 @@ mod adb;
|
||||||
mod backend;
|
mod backend;
|
||||||
mod broadcaster;
|
mod broadcaster;
|
||||||
mod config;
|
mod config;
|
||||||
mod nr;
|
|
||||||
mod rule;
|
mod rule;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
fn pidof(name: &str) -> Option<i32> {
|
fn pidof(name: &str) -> Option<i32> {
|
||||||
let out = std::process::Command::new("pidof")
|
let out = std::process::Command::new("pidof")
|
||||||
|
|
@ -42,101 +40,12 @@ fn watch_config(path: PathBuf, cfg: Arc<Mutex<Config>>) -> notify::Result<Recomm
|
||||||
w.watch(&saved_path, notify::RecursiveMode::NonRecursive)?;
|
w.watch(&saved_path, notify::RecursiveMode::NonRecursive)?;
|
||||||
Ok(w)
|
Ok(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
#[cfg(not(feature = "ptracev2"))]
|
#[cfg(feature = "ffs_proxy")]
|
||||||
{
|
{
|
||||||
let sub = FmtSubscriber::builder()
|
backend::proxy::run().await?;
|
||||||
.without_time()
|
|
||||||
.with_target(false)
|
|
||||||
.log_internal_errors(true)
|
|
||||||
.finish();
|
|
||||||
tracing::subscriber::set_global_default(sub).ok();
|
|
||||||
|
|
||||||
let mut args = std::env::args().skip(1);
|
|
||||||
let cfg_path = args
|
|
||||||
.next()
|
|
||||||
.filter(|s| s == "--config")
|
|
||||||
.and_then(|_| args.next())
|
|
||||||
.unwrap_or_else(|| "/data/adb/modules/adbguard/adbguard.json".into());
|
|
||||||
let cfg0 = Config::load(&cfg_path)?;
|
|
||||||
let cfg = Arc::new(Mutex::new(cfg0));
|
|
||||||
let _w = watch_config((&cfg_path).into(), cfg.clone()).ok();
|
|
||||||
|
|
||||||
#[cfg(feature = "ptrace_parallel")]
|
|
||||||
{
|
|
||||||
let pid = loop {
|
|
||||||
if let Some(p) = pidof("adbd") {
|
|
||||||
break p;
|
|
||||||
}
|
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("attaching to adbd {}", pid);
|
|
||||||
|
|
||||||
let snapshot = { cfg.lock().unwrap().clone() };
|
|
||||||
let rules = RuleSet::from_cfg(&snapshot);
|
|
||||||
let action = snapshot.broadcast_action.clone();
|
|
||||||
let peek = 256usize;
|
|
||||||
let adb_tcp_port = 5555u16;
|
|
||||||
|
|
||||||
return backend::ptrace_prl::run_with_tokio_rayon(
|
|
||||||
pid,
|
|
||||||
rules,
|
|
||||||
action,
|
|
||||||
snapshot.debounce(),
|
|
||||||
peek,
|
|
||||||
adb_tcp_port,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "ptrace_parallel"))]
|
Ok(())
|
||||||
loop {
|
|
||||||
let pid = loop {
|
|
||||||
if let Some(p) = pidof("adbd") {
|
|
||||||
break p;
|
|
||||||
}
|
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("attaching to adbd {}", pid);
|
|
||||||
|
|
||||||
let snapshot = { cfg.lock().unwrap().clone() };
|
|
||||||
let rules = RuleSet::from_cfg(&snapshot);
|
|
||||||
let action = snapshot.broadcast_action.clone();
|
|
||||||
|
|
||||||
#[cfg(feature = "ptrace")]
|
|
||||||
{
|
|
||||||
if let Err(e) = backend::ptrace::run(
|
|
||||||
pid,
|
|
||||||
rules,
|
|
||||||
action,
|
|
||||||
snapshot.debounce(),
|
|
||||||
snapshot.max_payload_peek,
|
|
||||||
) {
|
|
||||||
error!("backend exited, {e:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "uprobes")]
|
|
||||||
{
|
|
||||||
backend::uprobes::run(pid, rules, action, snapshot.debounce())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "ptracev2")]
|
|
||||||
{
|
|
||||||
let command = ptrace_v2::parse_args();
|
|
||||||
match ptrace_v2::run(command) {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(e) => {
|
|
||||||
anyhow::bail!("{}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
321
src/nr.rs
321
src/nr.rs
|
|
@ -1,321 +0,0 @@
|
||||||
///
|
|
||||||
/// Android Syscall Table (Android 10)
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// https://android.googlesource.com/platform/bionic/+/refs/heads/android10-release/libc/kernel/uapi/asm-generic/unistd.h
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
#[repr(u64)]
|
|
||||||
pub enum NR_CODES {
|
|
||||||
__NR_io_setup = 0u64,
|
|
||||||
__NR_io_destroy,
|
|
||||||
__NR_io_submit,
|
|
||||||
__NR_io_cancel,
|
|
||||||
__NR_io_getevents,
|
|
||||||
__NR_setxattr,
|
|
||||||
__NR_lsetxattr,
|
|
||||||
__NR_fsetxattr,
|
|
||||||
__NR_getxattr,
|
|
||||||
__NR_lgetxattr,
|
|
||||||
__NR_fgetxattr,
|
|
||||||
__NR_listxattr,
|
|
||||||
__NR_llistxattr,
|
|
||||||
__NR_flistxattr,
|
|
||||||
__NR_removexattr,
|
|
||||||
__NR_lremovexattr,
|
|
||||||
__NR_fremovexattr,
|
|
||||||
__NR_getcwd,
|
|
||||||
__NR_lookup_dcookie,
|
|
||||||
__NR_eventfd2,
|
|
||||||
__NR_epoll_create1,
|
|
||||||
__NR_epoll_ctl,
|
|
||||||
__NR_epoll_pwait,
|
|
||||||
__NR_dup,
|
|
||||||
__NR_dup3,
|
|
||||||
__NR3264_fcntl,
|
|
||||||
__NR_inotify_init1,
|
|
||||||
__NR_inotify_add_watch,
|
|
||||||
__NR_inotify_rm_watch,
|
|
||||||
__NR_ioctl,
|
|
||||||
__NR_ioprio_set,
|
|
||||||
__NR_ioprio_get,
|
|
||||||
__NR_flock,
|
|
||||||
__NR_mknodat,
|
|
||||||
__NR_mkdirat,
|
|
||||||
__NR_unlinkat,
|
|
||||||
__NR_symlinkat,
|
|
||||||
__NR_linkat,
|
|
||||||
__NR_renameat,
|
|
||||||
__NR_umount2,
|
|
||||||
__NR_mount,
|
|
||||||
__NR_pivot_root,
|
|
||||||
__NR_nfsservctl,
|
|
||||||
__NR3264_statfs,
|
|
||||||
__NR3264_fstatfs,
|
|
||||||
__NR3264_truncate,
|
|
||||||
__NR3264_ftruncate,
|
|
||||||
__NR_fallocate,
|
|
||||||
__NR_faccessat,
|
|
||||||
__NR_chdir,
|
|
||||||
__NR_fchdir,
|
|
||||||
__NR_chroot,
|
|
||||||
__NR_fchmod,
|
|
||||||
__NR_fchmodat,
|
|
||||||
__NR_fchownat,
|
|
||||||
__NR_fchown,
|
|
||||||
__NR_openat,
|
|
||||||
__NR_close,
|
|
||||||
__NR_vhangup,
|
|
||||||
__NR_pipe2,
|
|
||||||
__NR_quotactl,
|
|
||||||
__NR_getdents64 ,
|
|
||||||
__NR3264_lseek,
|
|
||||||
__NR_read,
|
|
||||||
__NR_write,
|
|
||||||
__NR_readv,
|
|
||||||
__NR_writev,
|
|
||||||
__NR_pread64,
|
|
||||||
__NR_pwrite64,
|
|
||||||
__NR_preadv,
|
|
||||||
__NR_pwritev,
|
|
||||||
__NR3264_sendfile,
|
|
||||||
__NR_pselect6,
|
|
||||||
__NR_ppoll,
|
|
||||||
__NR_signalfd4,
|
|
||||||
__NR_vmsplice,
|
|
||||||
__NR_splice,
|
|
||||||
__NR_tee,
|
|
||||||
__NR_readlinkat,
|
|
||||||
__NR3264_fstatat,
|
|
||||||
__NR3264_fstat,
|
|
||||||
__NR_sync,
|
|
||||||
__NR_fsync,
|
|
||||||
__NR_fdatasync,
|
|
||||||
__NR_sync_file_range,
|
|
||||||
__NR_timerfd_create,
|
|
||||||
__NR_timerfd_settime,
|
|
||||||
__NR_timerfd_gettime,
|
|
||||||
__NR_utimensat,
|
|
||||||
__NR_acct,
|
|
||||||
__NR_capget,
|
|
||||||
__NR_capset,
|
|
||||||
__NR_personality,
|
|
||||||
__NR_exit,
|
|
||||||
__NR_exit_group,
|
|
||||||
__NR_waitid,
|
|
||||||
__NR_set_tid_address,
|
|
||||||
__NR_unshare,
|
|
||||||
__NR_futex,
|
|
||||||
__NR_set_robust_list,
|
|
||||||
__NR_get_robust_list,
|
|
||||||
__NR_nanosleep,
|
|
||||||
__NR_getitimer,
|
|
||||||
__NR_setitimer,
|
|
||||||
__NR_kexec_load,
|
|
||||||
__NR_init_module,
|
|
||||||
__NR_delete_module,
|
|
||||||
__NR_timer_create,
|
|
||||||
__NR_timer_gettime,
|
|
||||||
__NR_timer_getoverrun,
|
|
||||||
__NR_timer_settime,
|
|
||||||
__NR_timer_delete,
|
|
||||||
__NR_clock_settime,
|
|
||||||
__NR_clock_gettime,
|
|
||||||
__NR_clock_getres,
|
|
||||||
__NR_clock_nanosleep,
|
|
||||||
__NR_syslog,
|
|
||||||
__NR_ptrace,
|
|
||||||
__NR_sched_setparam,
|
|
||||||
__NR_sched_setscheduler,
|
|
||||||
__NR_sched_getscheduler,
|
|
||||||
__NR_sched_getparam,
|
|
||||||
__NR_sched_setaffinity,
|
|
||||||
__NR_sched_getaffinity,
|
|
||||||
__NR_sched_yield,
|
|
||||||
__NR_sched_get_priority_max,
|
|
||||||
__NR_sched_get_priority_min,
|
|
||||||
__NR_sched_rr_get_interval,
|
|
||||||
__NR_restart_syscall,
|
|
||||||
__NR_kill,
|
|
||||||
__NR_tkill,
|
|
||||||
__NR_tgkill,
|
|
||||||
__NR_sigaltstack,
|
|
||||||
__NR_rt_sigsuspend,
|
|
||||||
__NR_rt_sigaction,
|
|
||||||
__NR_rt_sigprocmask,
|
|
||||||
__NR_rt_sigpending,
|
|
||||||
__NR_rt_sigtimedwait,
|
|
||||||
__NR_rt_sigqueueinfo,
|
|
||||||
__NR_rt_sigreturn,
|
|
||||||
__NR_setpriority,
|
|
||||||
__NR_getpriority,
|
|
||||||
__NR_reboot,
|
|
||||||
__NR_setregid,
|
|
||||||
__NR_setgid,
|
|
||||||
__NR_setreuid,
|
|
||||||
__NR_setuid,
|
|
||||||
__NR_setresuid,
|
|
||||||
__NR_getresuid,
|
|
||||||
__NR_setresgid,
|
|
||||||
__NR_getresgid,
|
|
||||||
__NR_setfsuid,
|
|
||||||
__NR_setfsgid,
|
|
||||||
__NR_times,
|
|
||||||
__NR_setpgid,
|
|
||||||
__NR_getpgid,
|
|
||||||
__NR_getsid,
|
|
||||||
__NR_setsid,
|
|
||||||
__NR_getgroups,
|
|
||||||
__NR_setgroups,
|
|
||||||
__NR_uname,
|
|
||||||
__NR_sethostname,
|
|
||||||
__NR_setdomainname,
|
|
||||||
__NR_getrlimit,
|
|
||||||
__NR_setrlimit,
|
|
||||||
__NR_getrusage,
|
|
||||||
__NR_umask,
|
|
||||||
__NR_prctl,
|
|
||||||
__NR_getcpu,
|
|
||||||
__NR_gettimeofday,
|
|
||||||
__NR_settimeofday,
|
|
||||||
__NR_adjtimex,
|
|
||||||
__NR_getpid,
|
|
||||||
__NR_getppid,
|
|
||||||
__NR_getuid,
|
|
||||||
__NR_geteuid,
|
|
||||||
__NR_getgid,
|
|
||||||
__NR_getegid,
|
|
||||||
__NR_gettid,
|
|
||||||
__NR_sysinfo,
|
|
||||||
__NR_mq_open,
|
|
||||||
__NR_mq_unlink,
|
|
||||||
__NR_mq_timedsend,
|
|
||||||
__NR_mq_timedreceive,
|
|
||||||
__NR_mq_notify,
|
|
||||||
__NR_mq_getsetattr,
|
|
||||||
__NR_msgget,
|
|
||||||
__NR_msgctl,
|
|
||||||
__NR_msgrcv,
|
|
||||||
__NR_msgsnd,
|
|
||||||
__NR_semget,
|
|
||||||
__NR_semctl,
|
|
||||||
__NR_semtimedop,
|
|
||||||
__NR_semop,
|
|
||||||
__NR_shmget,
|
|
||||||
__NR_shmctl,
|
|
||||||
__NR_shmat,
|
|
||||||
__NR_shmdt,
|
|
||||||
__NR_socket,
|
|
||||||
__NR_socketpair,
|
|
||||||
__NR_bind,
|
|
||||||
__NR_listen,
|
|
||||||
__NR_accept,
|
|
||||||
__NR_connect,
|
|
||||||
__NR_getsockname,
|
|
||||||
__NR_getpeername,
|
|
||||||
__NR_sendto,
|
|
||||||
__NR_recvfrom,
|
|
||||||
__NR_setsockopt,
|
|
||||||
__NR_getsockopt,
|
|
||||||
__NR_shutdown,
|
|
||||||
__NR_sendmsg,
|
|
||||||
__NR_recvmsg,
|
|
||||||
__NR_readahead,
|
|
||||||
__NR_brk,
|
|
||||||
__NR_munmap,
|
|
||||||
__NR_mremap,
|
|
||||||
__NR_add_key,
|
|
||||||
__NR_request_key,
|
|
||||||
__NR_keyctl,
|
|
||||||
__NR_clone,
|
|
||||||
__NR_execve,
|
|
||||||
__NR3264_mmap,
|
|
||||||
__NR3264_fadvise64,
|
|
||||||
__NR_swapon,
|
|
||||||
__NR_swapoff,
|
|
||||||
__NR_mprotect,
|
|
||||||
__NR_msync,
|
|
||||||
__NR_mlock,
|
|
||||||
__NR_munlock,
|
|
||||||
__NR_mlockall,
|
|
||||||
__NR_munlockall,
|
|
||||||
__NR_mincore,
|
|
||||||
__NR_madvise,
|
|
||||||
__NR_remap_file_pages,
|
|
||||||
__NR_mbind,
|
|
||||||
__NR_get_mempolicy,
|
|
||||||
__NR_set_mempolicy,
|
|
||||||
__NR_migrate_pages,
|
|
||||||
__NR_move_pages ,
|
|
||||||
__NR_rt_tgsigqueueinfo,
|
|
||||||
__NR_perf_event_open,
|
|
||||||
__NR_accept4,
|
|
||||||
__NR_recvmmsg,
|
|
||||||
__NR_arch_specific_syscall,
|
|
||||||
__NR_wait4 = 260u64,
|
|
||||||
__NR_prlimit64,
|
|
||||||
__NR_fanotify_init,
|
|
||||||
__NR_fanotify_mark,
|
|
||||||
__NR_name_to_handle_at,
|
|
||||||
__NR_open_by_handle_at,
|
|
||||||
__NR_clock_adjtime,
|
|
||||||
__NR_syncfs,
|
|
||||||
__NR_setns,
|
|
||||||
__NR_sendmmsg,
|
|
||||||
__NR_process_vm_readv,
|
|
||||||
__NR_process_vm_writev,
|
|
||||||
__NR_kcmp,
|
|
||||||
__NR_finit_module,
|
|
||||||
__NR_sched_setattr,
|
|
||||||
__NR_sched_getattr,
|
|
||||||
__NR_renameat2,
|
|
||||||
__NR_seccomp,
|
|
||||||
__NR_getrandom,
|
|
||||||
__NR_memfd_create,
|
|
||||||
__NR_bpf,
|
|
||||||
__NR_execveat,
|
|
||||||
__NR_userfaultfd,
|
|
||||||
__NR_membarrier,
|
|
||||||
__NR_mlock2,
|
|
||||||
__NR_copy_file_range,
|
|
||||||
__NR_preadv2,
|
|
||||||
__NR_pwritev2,
|
|
||||||
__NR_pkey_mprotect,
|
|
||||||
__NR_pkey_alloc,
|
|
||||||
__NR_pkey_free,
|
|
||||||
__NR_statx,
|
|
||||||
__NR_io_pgetevents,
|
|
||||||
__NR_rseq,
|
|
||||||
__NR_kexec_file_load,
|
|
||||||
__NR_syscalls,
|
|
||||||
unknown = u64::max_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NR_CODES {
|
|
||||||
|
|
||||||
/// get enum name
|
|
||||||
pub fn name(&self) -> String {
|
|
||||||
format!("{:?}", self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get value of enum
|
|
||||||
pub fn value(&self) -> u64 {
|
|
||||||
self.clone() as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get enum from u64
|
|
||||||
///
|
|
||||||
/// value may return `unknown` if more than 0x127 or in range between 0xF4 and 0x104
|
|
||||||
pub fn get(val: u64) -> NR_CODES {
|
|
||||||
|
|
||||||
if (val > 0x127) || (val > 0xF4 && val < 0x104) {
|
|
||||||
return NR_CODES::unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
let nr_code = unsafe {
|
|
||||||
std::mem::transmute::<_, NR_CODES>(val)
|
|
||||||
};
|
|
||||||
|
|
||||||
return nr_code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::config::Config;
|
use crate::config::{Config, Rule};
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
|
|
|
||||||
19
src/traits.rs
Normal file
19
src/traits.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
pub trait EnumerateValue<T> {
|
||||||
|
/// get enum name
|
||||||
|
fn name(&self) -> String
|
||||||
|
where
|
||||||
|
Self: Debug,
|
||||||
|
{
|
||||||
|
format!("{:?}", self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get value of enum
|
||||||
|
fn value(&self) -> T
|
||||||
|
where
|
||||||
|
Self: Clone;
|
||||||
|
|
||||||
|
/// get enum from expected type
|
||||||
|
fn get(val: T) -> Self;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue