Compare commits

..

21 Commits

Author SHA1 Message Date
Cooper Quintin f2b722ad5f version update 2025-06-04 10:12:08 -07:00
Cooper Quintin 5e2058e7ac update make to use firmware profile 2025-06-04 10:12:08 -07:00
cooperq 60daf4b716 update win docs 2025-06-04 10:11:12 -07:00
Cooper Quintin 4df317b028 dig deeper in the json tree. Fixes #360 2025-06-04 10:10:18 -07:00
Sashanoraa d7fb8b9c85 Move most serial commands to usb shared USB claim with adb 2025-06-04 09:11:06 -07:00
Markus Unterwaditzer d399532494 Add documentation for key input 2025-06-03 14:29:04 -07:00
Cooper Quintin 45df91a364 Update using-rayhunter.md 2025-06-03 14:23:50 -07:00
Matej Kovacic 672ed8c6c6 Update using-rayhunter.md 2025-06-03 14:23:50 -07:00
Matej Kovacic 5c7c7cd766 Add files via upload 2025-06-03 14:21:19 -07:00
Markus Unterwaditzer f41a8d38fe move analysis into diag reader thread as well 2025-06-03 13:58:47 -07:00
Markus Unterwaditzer f9c8c4671e Add basic key input 2025-06-03 13:58:47 -07:00
Markus Unterwaditzer 723b20541e Move business logic out of axum handlers 2025-06-03 13:58:47 -07:00
cooperq 272a4aeabf update docs 2025-06-03 11:49:20 -07:00
cooperq 6ae70556ba fix windows powershell installer and rust installer root process 2025-06-03 11:49:20 -07:00
Markus Unterwaditzer 2915dea9e9 Remove dead code and default features 2025-05-30 10:21:30 -07:00
Markus Unterwaditzer 6941bc57b6 Fix issues on TP-Link v9
* Fix autostart by adding another port trigger for rayhunter-daemon
* Use Orbic's IOCTL params as fallback
* Fix sdcard path and make it configurable
* Update docs to indicate support
* Add uninstalling instructions for TP-Link
2025-05-30 10:14:51 -07:00
Cooper Quintin 5b9dd856a8 version bump 2025-05-30 10:13:34 -07:00
Cooper Quintin 5007cb0b36 cargo fmt 2025-05-30 10:13:34 -07:00
Cooper Quintin 1b244122df add adb shell command 2025-05-30 10:13:34 -07:00
Cooper Quintin 3c4cb56ce6 surpress error messages by default, fixes #347 2025-05-30 10:13:34 -07:00
Cooper Quintin 58843413b5 update adb version which fixes adb key missing error. Fixes #330 2025-05-30 10:13:34 -07:00
29 changed files with 465 additions and 223 deletions
+9 -1
View File
@@ -74,7 +74,15 @@ jobs:
npm install npm install
npm run build npm run build
popd popd
cargo build --bin rayhunter-daemon --target armv7-unknown-linux-musleabihf --profile=firmware --no-default-features --features ${{ matrix.device.name }} # Run with -p so that cargo will select the minimum feature set for this package.
#
# Otherwise, it will consider the union of all requested features
# from all packages in the workspace. For example, if installer
# requires tokio with "full" feature, it will be included no matter
# what the feature selection in rayhunter-daemon is.
#
# https://github.com/rust-lang/cargo/issues/4463
cargo build -p rayhunter-daemon --bin rayhunter-daemon --target armv7-unknown-linux-musleabihf --profile=firmware --no-default-features --features ${{ matrix.device.name }}
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: rayhunter-daemon-${{ matrix.device.name }} name: rayhunter-daemon-${{ matrix.device.name }}
+1
View File
@@ -1,2 +1,3 @@
/target /target
/book /book
.DS_Store
Generated
+7 -25
View File
@@ -5,7 +5,7 @@ version = 4
[[package]] [[package]]
name = "adb_client" name = "adb_client"
version = "2.1.11" version = "2.1.11"
source = "git+https://github.com/gaykitty/adb_client.git?rev=1fb0f4f5cbcc95bbbb98db4ee2f1e53a1005aa81#1fb0f4f5cbcc95bbbb98db4ee2f1e53a1005aa81" source = "git+https://github.com/gaykitty/adb_client.git?rev=e732fc178a0eb237138e4091059ff5ffa241385a#e732fc178a0eb237138e4091059ff5ffa241385a"
dependencies = [ dependencies = [
"async-io", "async-io",
"base64", "base64",
@@ -944,7 +944,6 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor",
"futures-io", "futures-io",
"futures-sink", "futures-sink",
"futures-task", "futures-task",
@@ -967,17 +966,6 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 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]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.31" version = "0.3.31"
@@ -1026,13 +1014,10 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io",
"futures-macro", "futures-macro",
"futures-sink", "futures-sink",
"futures-task", "futures-task",
"memchr",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab", "slab",
@@ -1448,7 +1433,7 @@ dependencies = [
[[package]] [[package]]
name = "installer" name = "installer"
version = "0.3.1" version = "0.3.3"
dependencies = [ dependencies = [
"adb_client", "adb_client",
"anyhow", "anyhow",
@@ -2322,15 +2307,13 @@ dependencies = [
[[package]] [[package]]
name = "rayhunter" name = "rayhunter"
version = "0.3.1" version = "0.3.3"
dependencies = [ dependencies = [
"bytes", "bytes",
"chrono", "chrono",
"crc", "crc",
"deku", "deku",
"env_logger 0.10.2",
"futures", "futures",
"futures-core",
"libc", "libc",
"log", "log",
"nix", "nix",
@@ -2343,14 +2326,13 @@ dependencies = [
[[package]] [[package]]
name = "rayhunter-daemon" name = "rayhunter-daemon"
version = "0.3.1" version = "0.3.3"
dependencies = [ dependencies = [
"axum", "axum",
"chrono", "chrono",
"clap", "clap",
"env_logger 0.10.2", "env_logger 0.11.8",
"futures", "futures",
"futures-core",
"futures-macro", "futures-macro",
"image", "image",
"include_dir", "include_dir",
@@ -2471,7 +2453,7 @@ checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
[[package]] [[package]]
name = "rootshell" name = "rootshell"
version = "0.3.1" version = "0.3.3"
dependencies = [ dependencies = [
"nix", "nix",
] ]
@@ -2844,7 +2826,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]] [[package]]
name = "telcom-parser" name = "telcom-parser"
version = "0.3.1" version = "0.3.3"
dependencies = [ dependencies = [
"asn1-codecs", "asn1-codecs",
"asn1-compiler", "asn1-compiler",
+6 -7
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rayhunter-daemon" name = "rayhunter-daemon"
version = "0.3.1" version = "0.3.3"
edition = "2021" edition = "2021"
[features] [features]
@@ -22,20 +22,19 @@ path = "src/check.rs"
rayhunter = { path = "../lib" } rayhunter = { path = "../lib" }
toml = "0.8.8" toml = "0.8.8"
serde = { version = "1.0.193", features = ["derive"] } serde = { version = "1.0.193", features = ["derive"] }
tokio = { version = "1.44.2", features = ["full"] } tokio = { version = "1.44.2", default-features = false, features = ["fs", "signal", "process", "rt-multi-thread"] }
axum = "0.8" axum = { version = "0.8", default-features = false, features = ["http1", "tokio", "json"] }
futures-core = "0.3.30"
thiserror = "1.0.52" thiserror = "1.0.52"
libc = "0.2.150" libc = "0.2.150"
log = "0.4.20" log = "0.4.20"
env_logger = "0.10.1" env_logger = { version = "0.11", default-features = false }
tokio-util = { version = "0.7.10", features = ["rt", "io"] } tokio-util = { version = "0.7.10", features = ["rt", "io"] }
futures-macro = "0.3.30" futures-macro = "0.3.30"
include_dir = "0.7.3" include_dir = "0.7.3"
mime_guess = "2.0.4" mime_guess = "2.0.4"
chrono = { version = "0.4.31", features = ["serde"] } chrono = { version = "0.4.31", features = ["serde"] }
tokio-stream = "0.1.14" tokio-stream = { version = "0.1.14", default-features = false }
futures = "0.3.30" futures = { version = "0.3.30", default-features = false }
clap = { version = "4.5.2", features = ["derive"] } clap = { version = "4.5.2", features = ["derive"] }
serde_json = "1.0.114" serde_json = "1.0.114"
image = { version = "0.25.1", default-features = false, features = ["png", "gif"] } image = { version = "0.25.1", default-features = false, features = ["png", "gif"] }
+2
View File
@@ -11,6 +11,7 @@ pub struct Config {
pub ui_level: u8, pub ui_level: u8,
pub enable_dummy_analyzer: bool, pub enable_dummy_analyzer: bool,
pub colorblind_mode: bool, pub colorblind_mode: bool,
pub key_input_mode: u8,
} }
impl Default for Config { impl Default for Config {
@@ -22,6 +23,7 @@ impl Default for Config {
ui_level: 1, ui_level: 1,
enable_dummy_analyzer: false, enable_dummy_analyzer: false,
colorblind_mode: false, colorblind_mode: false,
key_input_mode: 1,
} }
} }
} }
+9 -4
View File
@@ -4,6 +4,7 @@ mod diag;
mod display; mod display;
mod dummy_analyzer; mod dummy_analyzer;
mod error; mod error;
mod key_input;
mod pcap; mod pcap;
mod qmdl_store; mod qmdl_store;
mod server; mod server;
@@ -175,7 +176,7 @@ async fn main() -> Result<(), RayhunterError> {
let store = init_qmdl_store(&config).await?; let store = init_qmdl_store(&config).await?;
let analysis_status = AnalysisStatus::new(&store); let analysis_status = AnalysisStatus::new(&store);
let qmdl_store_lock = Arc::new(RwLock::new(store)); let qmdl_store_lock = Arc::new(RwLock::new(store));
let (tx, rx) = mpsc::channel::<DiagDeviceCtrlMessage>(1); let (diag_tx, diag_rx) = mpsc::channel::<DiagDeviceCtrlMessage>(1);
let (ui_update_tx, ui_update_rx) = mpsc::channel::<display::DisplayState>(1); let (ui_update_tx, ui_update_rx) = mpsc::channel::<display::DisplayState>(1);
let (analysis_tx, analysis_rx) = mpsc::channel::<AnalysisCtrlMessage>(5); let (analysis_tx, analysis_rx) = mpsc::channel::<AnalysisCtrlMessage>(5);
let mut maybe_ui_shutdown_tx = None; let mut maybe_ui_shutdown_tx = None;
@@ -193,13 +194,17 @@ async fn main() -> Result<(), RayhunterError> {
run_diag_read_thread( run_diag_read_thread(
&task_tracker, &task_tracker,
dev, dev,
rx, diag_rx,
ui_update_tx.clone(), ui_update_tx.clone(),
qmdl_store_lock.clone(), qmdl_store_lock.clone(),
analysis_tx.clone(),
config.enable_dummy_analyzer, config.enable_dummy_analyzer,
); );
info!("Starting UI"); info!("Starting UI");
display::update_ui(&task_tracker, &config, ui_shutdown_rx, ui_update_rx); display::update_ui(&task_tracker, &config, ui_shutdown_rx, ui_update_rx);
info!("Starting Key Input service");
key_input::run_key_input_thread(&task_tracker, &config, diag_tx.clone());
} }
let (server_shutdown_tx, server_shutdown_rx) = oneshot::channel::<()>(); let (server_shutdown_tx, server_shutdown_rx) = oneshot::channel::<()>();
info!("create shutdown thread"); info!("create shutdown thread");
@@ -213,7 +218,7 @@ async fn main() -> Result<(), RayhunterError> {
); );
run_ctrl_c_thread( run_ctrl_c_thread(
&task_tracker, &task_tracker,
tx.clone(), diag_tx.clone(),
server_shutdown_tx, server_shutdown_tx,
maybe_ui_shutdown_tx, maybe_ui_shutdown_tx,
qmdl_store_lock.clone(), qmdl_store_lock.clone(),
@@ -221,7 +226,7 @@ async fn main() -> Result<(), RayhunterError> {
); );
let state = Arc::new(ServerState { let state = Arc::new(ServerState {
qmdl_store_lock: qmdl_store_lock.clone(), qmdl_store_lock: qmdl_store_lock.clone(),
diag_device_ctrl_sender: tx, diag_device_ctrl_sender: diag_tx,
ui_update_sender: ui_update_tx, ui_update_sender: ui_update_tx,
debug_mode: config.debug_mode, debug_mode: config.debug_mode,
analysis_status_lock, analysis_status_lock,
+44 -63
View File
@@ -7,7 +7,7 @@ use axum::http::header::CONTENT_TYPE;
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
use futures::{StreamExt, TryStreamExt}; use futures::{StreamExt, TryStreamExt};
use log::{debug, error, info}; use log::{debug, error, info, warn};
use rayhunter::diag::DataType; use rayhunter::diag::DataType;
use rayhunter::diag_device::DiagDevice; use rayhunter::diag_device::DiagDevice;
use rayhunter::qmdl::QmdlWriter; use rayhunter::qmdl::QmdlWriter;
@@ -24,7 +24,7 @@ use crate::server::ServerState;
pub enum DiagDeviceCtrlMessage { pub enum DiagDeviceCtrlMessage {
StopRecording, StopRecording,
StartRecording((QmdlWriter<File>, File)), StartRecording,
Exit, Exit,
} }
@@ -34,6 +34,7 @@ pub fn run_diag_read_thread(
mut qmdl_file_rx: Receiver<DiagDeviceCtrlMessage>, mut qmdl_file_rx: Receiver<DiagDeviceCtrlMessage>,
ui_update_sender: Sender<display::DisplayState>, ui_update_sender: Sender<display::DisplayState>,
qmdl_store_lock: Arc<RwLock<RecordingStore>>, qmdl_store_lock: Arc<RwLock<RecordingStore>>,
analysis_sender: Sender<AnalysisCtrlMessage>,
enable_dummy_analyzer: bool, enable_dummy_analyzer: bool,
) { ) {
task_tracker.spawn(async move { task_tracker.spawn(async move {
@@ -46,20 +47,56 @@ pub fn run_diag_read_thread(
tokio::select! { tokio::select! {
msg = qmdl_file_rx.recv() => { msg = qmdl_file_rx.recv() => {
match msg { match msg {
Some(DiagDeviceCtrlMessage::StartRecording((new_writer, new_analysis_file))) => { Some(DiagDeviceCtrlMessage::StartRecording) => {
maybe_qmdl_writer = Some(new_writer); let mut qmdl_store = qmdl_store_lock.write().await;
let (qmdl_file, new_analysis_file) = match qmdl_store.new_entry().await {
Ok(x) => x,
Err(e) => {
error!("couldn't create new qmdl entry: {}", e);
continue;
}
};
maybe_qmdl_writer = Some(QmdlWriter::new(qmdl_file));
if let Some(analysis_writer) = maybe_analysis_writer { if let Some(analysis_writer) = maybe_analysis_writer {
analysis_writer.close().await.expect("failed to close analysis writer"); analysis_writer.close().await.expect("failed to close analysis writer");
} }
maybe_analysis_writer = Some(AnalysisWriter::new(new_analysis_file, enable_dummy_analyzer).await maybe_analysis_writer = Some(AnalysisWriter::new(new_analysis_file, enable_dummy_analyzer).await
.expect("failed to write to analysis file")); .expect("failed to write to analysis file"));
if let Err(e) = ui_update_sender.send(display::DisplayState::Recording).await {
warn!("couldn't send ui update message: {}", e);
}
}, },
Some(DiagDeviceCtrlMessage::StopRecording) => { Some(DiagDeviceCtrlMessage::StopRecording) => {
let mut qmdl_store = qmdl_store_lock.write().await;
match qmdl_store.get_current_entry() {
Some((_, entry)) => {
if let Err(e) = analysis_sender
.send(AnalysisCtrlMessage::RecordingFinished(
entry.name.to_string(),
))
.await {
warn!("couldn't send analysis message: {}", e);
}
}
None => todo!(),
}
if let Err(e) = qmdl_store.close_current_entry().await {
error!("couldn't close current entry: {}", e);
}
maybe_qmdl_writer = None; maybe_qmdl_writer = None;
if let Some(analysis_writer) = maybe_analysis_writer { if let Some(analysis_writer) = maybe_analysis_writer {
analysis_writer.close().await.expect("failed to close analysis writer"); analysis_writer.close().await.expect("failed to close analysis writer");
} }
maybe_analysis_writer = None; maybe_analysis_writer = None;
if let Err(e) = ui_update_sender.send(display::DisplayState::Paused).await {
warn!("couldn't send ui update message: {}", e);
}
}, },
// None means all the Senders have been dropped, so it's // None means all the Senders have been dropped, so it's
// time to go // time to go
@@ -125,37 +162,15 @@ pub async fn start_recording(
if state.debug_mode { if state.debug_mode {
return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string()));
} }
let mut qmdl_store = state.qmdl_store_lock.write().await;
let (qmdl_file, analysis_file) = qmdl_store.new_entry().await.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("couldn't create new qmdl entry: {}", e),
)
})?;
let qmdl_writer = QmdlWriter::new(qmdl_file);
state state
.diag_device_ctrl_sender .diag_device_ctrl_sender
.send(DiagDeviceCtrlMessage::StartRecording(( .send(DiagDeviceCtrlMessage::StartRecording)
qmdl_writer,
analysis_file,
)))
.await .await
.map_err(|e| { .map_err(|e| {
( (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("couldn't send stop recording message: {}", e), format!("couldn't send start recording message: {}", e),
)
})?;
let display_state = display::DisplayState::Recording;
state
.ui_update_sender
.send(display_state)
.await
.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("couldn't send ui update message: {}", e),
) )
})?; })?;
@@ -168,30 +183,6 @@ pub async fn stop_recording(
if state.debug_mode { if state.debug_mode {
return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string()));
} }
let mut qmdl_store = state.qmdl_store_lock.write().await;
match qmdl_store.get_current_entry() {
Some((_, entry)) => {
state
.analysis_sender
.send(AnalysisCtrlMessage::RecordingFinished(
entry.name.to_string(),
))
.await
.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("couldn't send AnalysisCtrlMessage: {}", e),
)
})?;
}
None => todo!(),
}
qmdl_store.close_current_entry().await.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("couldn't close current qmdl entry: {}", e),
)
})?;
state state
.diag_device_ctrl_sender .diag_device_ctrl_sender
.send(DiagDeviceCtrlMessage::StopRecording) .send(DiagDeviceCtrlMessage::StopRecording)
@@ -202,16 +193,6 @@ pub async fn stop_recording(
format!("couldn't send stop recording message: {}", e), format!("couldn't send stop recording message: {}", e),
) )
})?; })?;
state
.ui_update_sender
.send(display::DisplayState::Paused)
.await
.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("couldn't send ui update message: {}", e),
)
})?;
Ok((StatusCode::ACCEPTED, "ok".to_string())) Ok((StatusCode::ACCEPTED, "ok".to_string()))
} }
+100
View File
@@ -0,0 +1,100 @@
use log::error;
use std::time::{Duration, Instant};
use tokio::fs::File;
use tokio::io::AsyncReadExt;
use tokio::sync::mpsc::Sender;
use tokio_util::task::TaskTracker;
use crate::config;
use crate::diag::DiagDeviceCtrlMessage;
#[derive(Debug)]
enum Event {
KeyDown,
KeyUp,
}
const INPUT_EVENT_SIZE: usize = 32;
pub fn run_key_input_thread(
task_tracker: &TaskTracker,
config: &config::Config,
diag_tx: Sender<DiagDeviceCtrlMessage>,
) {
if config.key_input_mode == 0 {
return;
}
task_tracker.spawn(async move {
// Open the input device
let mut file = match File::open("/dev/input/event0").await {
Ok(file) => file,
Err(e) => {
error!("Failed to open /dev/input/event0: {}", e);
return;
}
};
let mut buffer = [0u8; INPUT_EVENT_SIZE];
let mut last_keyup: Option<Instant> = None;
loop {
if let Err(e) = file.read_exact(&mut buffer).await {
error!("failed to read key input: {}", e);
return;
}
let event = parse_event(buffer);
match event {
Event::KeyUp => {
if last_keyup.is_some()
&& last_keyup.unwrap().elapsed() < Duration::from_millis(500)
{
if let Err(e) = diag_tx.send(DiagDeviceCtrlMessage::StopRecording).await {
error!("Failed to send StopRecording: {}", e);
}
if let Err(e) = diag_tx.send(DiagDeviceCtrlMessage::StartRecording).await {
error!("Failed to send StartRecording: {}", e);
}
last_keyup = None;
} else {
last_keyup = Some(Instant::now());
}
}
Event::KeyDown => {}
}
}
});
}
fn parse_event(input: [u8; INPUT_EVENT_SIZE]) -> Event {
if input[12] == 0 {
Event::KeyUp
} else {
Event::KeyDown
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_event_keydown_m7350_v5() {
let input = [
0x57, 0x6c, 0x09, 0x00, 0x7c, 0xfb, 0x03, 0x00, 0x01, 0x00, 0x74, 0x00, 0x01, 0x00,
0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
assert!(matches!(parse_event(input), Event::KeyDown));
}
#[test]
fn test_parse_event_keyup_m7350_v5() {
let input = [
0x57, 0x6c, 0x09, 0x00, 0x1b, 0x15, 0x05, 0x00, 0x01, 0x00, 0x74, 0x00, 0x00, 0x00,
0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
assert!(matches!(parse_event(input), Event::KeyUp));
}
}
+3 -3
View File
@@ -72,7 +72,7 @@ export function parse_finished_report(report_json: NewlineDeliminatedJson): Anal
const events: Event[] = analysis_json.events.map((event_json: any): Event | null => { const events: Event[] = analysis_json.events.map((event_json: any): Event | null => {
if (event_json === null) { if (event_json === null) {
return null; return null;
} else if (event_json.event_type === "Informational") { } else if (event_json.event_type.type === "Informational") {
num_informational_logs += 1; num_informational_logs += 1;
return { return {
type: EventType.Informational, type: EventType.Informational,
@@ -82,8 +82,8 @@ export function parse_finished_report(report_json: NewlineDeliminatedJson): Anal
num_warnings += 1; num_warnings += 1;
return { return {
type: EventType.Warning, type: EventType.Warning,
severity: event_json.severity === "High" ? Severity.High : severity: event_json.event_type.severity === "High" ? Severity.High :
event_json.severity === "Medium" ? Severity.Medium : Severity.Low, event_json.event_type.severity === "Medium" ? Severity.Medium : Severity.Low,
message: event_json.message, message: event_json.message,
}; };
} }
@@ -22,7 +22,7 @@
Storage Storage
</th> </th>
<td class={table_cell_classes}> <td class={table_cell_classes}>
{stats.disk_stats.used_percent} used ({stats.disk_stats.used_size} / {stats.disk_stats.available_size}) {stats.disk_stats.used_percent} used ({stats.disk_stats.used_size} used / {stats.disk_stats.available_size} available)
</td> </td>
</tr> </tr>
<tr class="border-b"> <tr class="border-b">
+5 -1
View File
@@ -14,5 +14,9 @@ colorblind_mode = false
# #
# TP-Link with one-bit display: # TP-Link with one-bit display:
# 0 = invisible mode # 0 = invisible mode
# 1..3 = show emoji for status. :) for running, :( for warnings, no mouth for paused. # 1..3 = show emoji for status. :) for running, ! for warnings, no mouth for paused.
ui_level = 1 ui_level = 1
# 0 = rayhunter does not read button presses
# 1 = double-tapping the power button starts/stops recordings
key_input_mode = 1
Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

+16 -6
View File
@@ -11,12 +11,22 @@ Windows support in Rayhunter's installer is a work-in-progress. Depending on the
## Orbic ## Orbic
1. Install the [Zadig WinUSB driver](https://zadig.akeo.ie/). 1. Connect the device to your computer using the provided USB cable.
1. Install the [Zadig WinUSB driver installer](https://zadig.akeo.ie/).
1. Open Zadig, click options->show all devices
![Zadig](./zadig2.png)
1. Select 'RNDIS (Interface 0)'
![Zadig](./zadig.png)
1. Click 'install driver' and wait for it to finish.
2. Download the latest `rayhunter-vX.X.X.zip` from the [Rayhunter releases page](https://github.com/EFForg/rayhunter/releases). The version you download will have numbers instead of X 2. Download the latest `rayhunter-vX.X.X.zip` from the [Rayhunter releases page](https://github.com/EFForg/rayhunter/releases). The version you download will have numbers instead of X
3. Unzip `rayhunter-vX.X.X` . 3. Unzip `rayhunter-vX.X.X` .
4. Save the [`install.ps1` file here](https://github.com/EFForg/rayhunter/blob/powershell/installer/install.ps1) in top of the folder that was unzipped from release.zip. 1. Open a powershell terminal by pressing Win+R and typing `powershell` and hitting enter.
5. Run the following powershell command `Set-ExecutionPolicy remotesigned` 5. Type `cd ~\Downloads\rayhunter-v<x.x.x>\installer-windows-x86_64` (**Replace <x.x.x> with the rayhunter version you just unzipped**) and hit enter.
5. Run the install script by double clicking on `install.ps1`. A powershell window will launch. 5. Run the install script: `.\installer.exe orbic` and hit enter.
The device will restart multiple times over the next few minutes. - The device will restart multiple times over the next few minutes.
You will know it is done when you see terminal output that says `checking for rayhunter server...success!` - You will know it is done when you see terminal output that says `checking for rayhunter server...success!`
6. Rayhunter should now be running! You can verify this by following the instructions below to [view the web UI](#usage-viewing-the-web-ui). You should also see a green line flash along the top of top the display on the device. 6. Rayhunter should now be running! You can verify this by following the instructions below to [view the web UI](#usage-viewing-the-web-ui). You should also see a green line flash along the top of top the display on the device.
+2 -2
View File
@@ -20,8 +20,8 @@ Make sure you've got one of Rayhunter's [supported devices](./supported-devices.
First, enter the correct subfolder for your operating system: First, enter the correct subfolder for your operating system:
- for Ubuntu on x64 arhitecture: `cd installer-ubuntu-24` - for Ubuntu on x64 arhitecture: `cd installer-ubuntu-24`
- for Ubuntu on ARM64 arhitecture: `cd installer-ubuntu-24-aarch64` - for Ubuntu on ARM64 arhitecture: `cd installer-ubuntu-24-aarch64`
- for MacOS on Intel architecture: `cd installer-macos-intel` - for MacOS on Intel (old macbooks) architecture: `cd installer-macos-intel`
- for MacOS on ARM achitecture: `cd installer-macos-arm` - for MacOS on ARM (M1/M2 etc.) achitecture: `cd installer-macos-arm`
- for Windows: `cd installer-windows-x86_64` - for Windows: `cd installer-windows-x86_64`
```bash ```bash
+12 -3
View File
@@ -7,10 +7,10 @@ The TP-Link M7350 is supported by Rayhunter from 0.3.0 release. TP-Link M7350 su
The TP-Link comes in many different *hardware versions*. Support for installation varies: The TP-Link comes in many different *hardware versions*. Support for installation varies:
* `1.0`, `2.0`: **Not suported**, probably impossible to obtain anymore (even second-hand), however there is one report that installation is possible on `1.0` (but no reports if it is working or not) * `1.0`, `2.0`: **Not suported**, probably impossible to obtain anymore (even second-hand), however there is one report that installation is possible on `1.0` (but no reports if it is working or not)
* `3.0`, `3.2`, `5.0`, `5.2`, `7.0`, `8.0`: **Tested, no known issues.** * `3.0`, `3.2`, `5.0`, `5.2`, `7.0`, `8.0`: **Tested, no known issues since 0.3.0.**
* `6.2`: **One user reported it is working** * `6.2`: **One user reported it is working**
* `4.0`: **Not working yet** ([issue](https://github.com/EFForg/rayhunter/issues/332)), however [it could be installed manually](https://github.com/m0veax/rayhunter-tplink-m7350/)) * `4.0`: **Manual firmware downgrade required** ([issue](https://github.com/EFForg/rayhunter/issues/332))
* `9.0`: **Not working yet** ([issue](https://github.com/EFForg/rayhunter/issues/325), however opening/rooting works, Rayhunter could be installed manually, but does not work because of [IOCTL error](#302)) * `9.0`: **Working since 0.3.2.**
TP-Link versions newer than `3.0` have cyan packaging and a color display. Version `3.0` has a one-bit display and white packaging. TP-Link versions newer than `3.0` have cyan packaging and a color display. Version `3.0` has a one-bit display and white packaging.
@@ -62,6 +62,15 @@ You can change the `port` (default is `8080`) where Rayhunter is listening for i
By default the device will go to sleep after N minutes of no devices being connected. In that mode it will also turn off connections to cell phone towers. By default the device will go to sleep after N minutes of no devices being connected. In that mode it will also turn off connections to cell phone towers.
In order for Rayhunter to record continuously, you have to turn off this sleep mode in TP-Link's admin panel (go to **Advanced** - **Power Saving**) or keep e.g. your phone connectd on the TP-Link's WiFi. In order for Rayhunter to record continuously, you have to turn off this sleep mode in TP-Link's admin panel (go to **Advanced** - **Power Saving**) or keep e.g. your phone connectd on the TP-Link's WiFi.
## Port triggers
On hardware revisions starting with v4.0, the installer will modify settings to
add two port triggers. You can look at `Settings > NAT Settings > Port
Triggers` in TP-Link's admin UI to see them.
1. One port trigger "rayhunter-root" to launch the telnet shell. This is only needed for installation, and can be removed after upgrade. You can reinstall it using `./installer util tplink-start-telnet`.
2. One port trigger "rayhunter-daemon" to auto-start rayhunter on boot. If you remove this, rayhunter will have to be started manually from shell.
## Other links ## Other links
For more information on the device and instructions on how to install Rayhunter without an installer (i.e. manually), please see [rayhunter-tplink-m7350](https://github.com/m0veax/rayhunter-tplink-m7350/) For more information on the device and instructions on how to install Rayhunter without an installer (i.e. manually), please see [rayhunter-tplink-m7350](https://github.com/m0veax/rayhunter-tplink-m7350/)
+6 -1
View File
@@ -16,4 +16,9 @@ Your device is now Rayhunter-free, and should no longer be in a rooted ADB-enabl
## TPLink ## TPLink
TODO 1. Run `./installer util tplink-start-telnet`
2. Telnet into the device `telnet 192.168.0.1`
3. `rm /data/rayhunter /etc/init.d/rayhunter_daemon`
4. `update-rc.d rayhunter_daemon remove`
5. (hardware revision v4.0+ only) In `Settings > NAT Settings > Port Triggers` in TP-Link's admin UI, remove any leftover port triggers.
+16 -5
View File
@@ -2,19 +2,30 @@
Once installed, Rayhunter will run automatically whenever your device is running. You'll see a green line on top of the device's display to indicate that it's running and recording. [The line will turn red](#red) once a potential IMSI catcher has been found, until the device is rebooted or a new recording is started through the web UI. Once installed, Rayhunter will run automatically whenever your device is running. You'll see a green line on top of the device's display to indicate that it's running and recording. [The line will turn red](#red) once a potential IMSI catcher has been found, until the device is rebooted or a new recording is started through the web UI.
![Rayhunter_0 3 2](./Rayhunter_0.3.2.png)
It also serves a web UI that provides some basic controls, such as being able to start/stop recordings, download captures, delete captures, and view heuristic analyses of captures. It also serves a web UI that provides some basic controls, such as being able to start/stop recordings, download captures, delete captures, and view heuristic analyses of captures.
## The web UI
You can access this UI in one of two ways: You can access this UI in one of two ways:
* **Connect over wifi:** Connect your phone/laptop to your device's wifi * **Connect over WiFi:** Connect your phone/laptop to your device's WiFi
network and visit [http://192.168.1.1:8080](http://192.168.1.1:8080) (orbic) network and visit [http://192.168.1.1:8080](http://192.168.1.1:8080) (orbic)
or [http://192.168.0.1:8080](http://192.168.0.1:8080) (tplink). or [http://192.168.0.1:8080](http://192.168.0.1:8080) (tplink).
Click past your browser warning you about the connection not being secure, Rayhunter doesn't have HTTPS yet. Click past your browser warning you about the connection not being secure, Rayhunter doesn't have HTTPS yet.
On the Orbic, you can find the wifi network password by going to the Orbic's menu > 2.4 GHz WIFI Info > Enter > find the 8-character password next to the lock 🔒 icon. On the **Orbic**, you can find the WiFi network password by going to the Orbic's menu > 2.4 GHz WIFI Info > Enter > find the 8-character password next to the lock 🔒 icon.
* **Connect over USB (orbic):** Connect your device to your laptop via USB. Run `adb forward tcp:8080 tcp:8080`, then visit [http://localhost:8080](http://localhost:8080). On the **TP-Link**, you can find the WiFi network password by going to the TP-Link's menu > Advanced > Wireless > Basic Settings.
* **Connect over USB (Orbic):** Connect your device to your laptop via USB. Run `adb forward tcp:8080 tcp:8080`, then visit [http://localhost:8080](http://localhost:8080).
* For this you will need to install the Android Debug Bridge (ADB) on your computer, you can copy the version that was downloaded inside the `releases/platform-tools/` folder to somewhere else in your path or you can install it manually. * For this you will need to install the Android Debug Bridge (ADB) on your computer, you can copy the version that was downloaded inside the `releases/platform-tools/` folder to somewhere else in your path or you can install it manually.
* You can find instructions for doing so on your platform [here](https://www.xda-developers.com/install-adb-windows-macos-linux/#how-to-set-up-adb-on-your-computer), (don't worry about instructions for installing it on a phone/device yet). * You can find instructions for doing so on your platform [here](https://www.xda-developers.com/install-adb-windows-macos-linux/#how-to-set-up-adb-on-your-computer), (don't worry about instructions for installing it on a phone/device yet).
* On macOS, the easiest way to install ADB is with Homebrew: First [install Homebrew](https://brew.sh/), then run `brew install android-platform-tools`. * On MacOS, the easiest way to install ADB is with Homebrew: First [install Homebrew](https://brew.sh/), then run `brew install android-platform-tools`.
* **Connect over USB (tplink):** Plug in the TP-Link and use USB tethering to establish a network connection. ADB support can be enabled on the device, but the installer won't do it for you.
* **Connect over USB (TP-Link):** Plug in the TP-Link and use USB tethering to establish a network connection. ADB support can be enabled on the device, but the installer won't do it for you.
## Key shortcuts
As of 0.3.3, you can start a new recording by double-tapping the power button. Any current recording will be stopped and a new recording will be started, resetting the red line as well.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

+3 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "installer" name = "installer"
version = "0.3.1" version = "0.3.3"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
@@ -22,12 +22,12 @@ tokio-stream = "0.1.17"
[target.'cfg(target_os = "linux")'.dependencies.adb_client] [target.'cfg(target_os = "linux")'.dependencies.adb_client]
git = "https://github.com/gaykitty/adb_client.git" git = "https://github.com/gaykitty/adb_client.git"
rev = "1fb0f4f5cbcc95bbbb98db4ee2f1e53a1005aa81" rev = "e732fc178a0eb237138e4091059ff5ffa241385a"
default-features = false default-features = false
features = ["trans-nusb"] features = ["trans-nusb"]
[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies.adb_client] [target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies.adb_client]
git = "https://github.com/gaykitty/adb_client.git" git = "https://github.com/gaykitty/adb_client.git"
rev = "1fb0f4f5cbcc95bbbb98db4ee2f1e53a1005aa81" rev = "e732fc178a0eb237138e4091059ff5ffa241385a"
default-features = false default-features = false
features = ["trans-libusb"] features = ["trans-libusb"]
+17 -16
View File
@@ -2,20 +2,14 @@ $global:adb = ".\platform-tools-latest-windows\platform-tools\adb.exe"
$global:serial = ".\installer-windows-x86_64\installer.exe" $global:serial = ".\installer-windows-x86_64\installer.exe"
function _adb_push { function _adb_push {
& $global:adb -d push @args | Out-Null & $global:adb -d push @args *> $null
$exitCode = $LASTEXITCODE $exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
write-host "push exited with exit code $($exitCode)"
}
return $exitCode return $exitCode
} }
function _adb_shell { function _adb_shell {
& $global:adb -d shell @args | Out-Null & $global:adb -d shell @args *> $null
$exitCode = $LASTEXITCODE $exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
write-host "shell exited with exit code $($exitCode)"
}
return $exitCode return $exitCode
} }
@@ -36,7 +30,7 @@ function _wait_for_atfwd_daemon {
function force_debug_mode { function force_debug_mode {
write-host "Using adb at $($global:adb)" write-host "Using adb at $($global:adb)"
write-host "Forcing a switch into debug mode to enable ADB" write-host "Forcing a switch into debug mode to enable ADB"
_serial "util serial --root" | Out-Host _serial "--root" | Out-Host
write-host "adb enabled, waiting for reboot..." -nonewline write-host "adb enabled, waiting for reboot..." -nonewline
_wait_for_adb_shell _wait_for_adb_shell
write-host " it's alive!" write-host " it's alive!"
@@ -58,7 +52,8 @@ function _serial {
} }
function setup_rootshell { function setup_rootshell {
_adb_push "rootshell" "/tmp" write-host "setting up rootshell"
_adb_push "rootshell" "/tmp" | Out-null
write-host "cp..." write-host "cp..."
_serial "AT+SYSCMD=cp /tmp/rootshell /bin/rootshell" | Out-Host _serial "AT+SYSCMD=cp /tmp/rootshell /bin/rootshell" | Out-Host
start-sleep -seconds 1 start-sleep -seconds 1
@@ -68,19 +63,20 @@ function setup_rootshell {
write-host "chmod..." write-host "chmod..."
_serial "AT+SYSCMD=chmod 4755 /bin/rootshell" | Out-Host _serial "AT+SYSCMD=chmod 4755 /bin/rootshell" | Out-Host
start-sleep -seconds 1 start-sleep -seconds 1
_adb_shell '/bin/rootshell -c id' _adb_shell '/bin/rootshell -c id' | Out-null
write-host "we have root!" write-host "we have root!"
} }
function setup_rayhunter { function setup_rayhunter {
write-host "installing rayhunter..."
_serial "AT+SYSCMD=mkdir -p /data/rayhunter" | Out-Host _serial "AT+SYSCMD=mkdir -p /data/rayhunter" | Out-Host
_adb_push "config.toml.example" "/tmp/config.toml" _adb_push "config.toml.example" "/tmp/config.toml" | Out-Null
_serial "AT+SYSCMD=mv /tmp/config.toml /data/rayhunter" | Out-Host _serial "AT+SYSCMD=mv /tmp/config.toml /data/rayhunter" | Out-Host
_adb_push "rayhunter-daemon-orbic/rayhunter-daemon" "/tmp/rayhunter-daemon" _adb_push "rayhunter-daemon-orbic/rayhunter-daemon" "/tmp/rayhunter-daemon" | Out-Null
_serial "AT+SYSCMD=mv /tmp/rayhunter-daemon /data/rayhunter" | Out-Host _serial "AT+SYSCMD=mv /tmp/rayhunter-daemon /data/rayhunter" | Out-Host
_adb_push "scripts/rayhunter_daemon" "/tmp/rayhunter_daemon" _adb_push "scripts/rayhunter_daemon" "/tmp/rayhunter_daemon" | Out-Null
_serial "AT+SYSCMD=mv /tmp/rayhunter_daemon /etc/init.d/rayhunter_daemon" | Out-Host _serial "AT+SYSCMD=mv /tmp/rayhunter_daemon /etc/init.d/rayhunter_daemon" | Out-Host
_adb_push "scripts/misc-daemon" "/tmp/misc-daemon" _adb_push "scripts/misc-daemon" "/tmp/misc-daemon" | Out-Null
_serial "AT+SYSCMD=mv /tmp/misc-daemon /etc/init.d/misc-daemon" | Out-Host _serial "AT+SYSCMD=mv /tmp/misc-daemon /etc/init.d/misc-daemon" | Out-Host
_serial "AT+SYSCMD=chmod 755 /data/rayhunter/rayhunter-daemon" | Out-Host _serial "AT+SYSCMD=chmod 755 /data/rayhunter/rayhunter-daemon" | Out-Host
@@ -108,7 +104,12 @@ function test_rayhunter {
write-host "checking for rayhunter server..." -nonewline write-host "checking for rayhunter server..." -nonewline
$seconds = 0 $seconds = 0
do { do {
$resp = invoke-webrequest -uri $URL try {
$resp = invoke-webrequest -uri $URL
} catch {
# Fail silently
$resp = $null
}
if ($resp.statuscode -eq 200) { if ($resp.statuscode -eq 200) {
write-host "success!" write-host "success!"
write-host "you can access rayhunter at $($URL)" write-host "you can access rayhunter at $($URL)"
+19 -1
View File
@@ -1,5 +1,6 @@
use anyhow::{Context, Error, bail}; use anyhow::{Context, Error, bail};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use env_logger::Env;
mod orbic; mod orbic;
mod tplink; mod tplink;
@@ -34,6 +35,17 @@ struct InstallTpLink {
/// IP address for TP-Link admin interface, if custom. /// IP address for TP-Link admin interface, if custom.
#[arg(long, default_value = "192.168.0.1")] #[arg(long, default_value = "192.168.0.1")]
admin_ip: String, admin_ip: String,
/// For advanced users: Specify the path of the SD card to be mounted explicitly.
///
/// The default (empty string) is to use whichever sdcard path the device would use natively to
/// mount storage on. On most TP-Link this is /media/card, but on hardware versions 9+ this is
/// /media/sdcard
///
/// Only override this when the installer does not work on your hardware version, as otherwise
/// your custom path may conflict with the builtin storage functionality.
#[arg(long, default_value = "")]
sdcard_path: String,
} }
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@@ -49,6 +61,8 @@ struct Util {
enum UtilSubCommand { enum UtilSubCommand {
/// Send a serial command to the Orbic. /// Send a serial command to the Orbic.
Serial(Serial), Serial(Serial),
/// Start an ADB shell
Shell(Shell),
/// Root the tplink and launch telnetd. /// Root the tplink and launch telnetd.
TplinkStartTelnet(TplinkStartTelnet), TplinkStartTelnet(TplinkStartTelnet),
} }
@@ -67,8 +81,11 @@ struct Serial {
command: Vec<String>, command: Vec<String>,
} }
#[derive(Parser, Debug)]
struct Shell {}
async fn run() -> Result<(), Error> { async fn run() -> Result<(), Error> {
env_logger::init(); env_logger::Builder::from_env(Env::default().default_filter_or("off")).init();
let Args { command } = Args::parse(); let Args { command } = Args::parse();
match command { match command {
@@ -93,6 +110,7 @@ async fn run() -> Result<(), Error> {
} }
} }
} }
UtilSubCommand::Shell(_) => orbic::shell().await.context("\nFailed to open shell on Orbic RC400L")?,
UtilSubCommand::TplinkStartTelnet(options) => { UtilSubCommand::TplinkStartTelnet(options) => {
tplink::start_telnet(&options.admin_ip).await?; tplink::start_telnet(&options.admin_ip).await?;
} }
+83 -36
View File
@@ -22,16 +22,24 @@ const ORBIC_BUSY: &str = r#"The Orbic is plugged in but is being used by another
Please close any program that might be using your USB devices. Please close any program that might be using your USB devices.
If you have adb installed you may need to kill the adb daemon"#; If you have adb installed you may need to kill the adb daemon"#;
#[cfg(target_os = "macos")] #[cfg(any(target_os = "macos", target_os = "windows"))]
const ORBIC_BUSY_MAC: &str = r#"Permission denied. const ORBIC_BUSY_MAC: &str = r#"Permission denied.
On macOS this might be caused by another program using the Orbic. On macOS or windows this might be caused by another program using the Orbic.
Please close any program that might be using your Orbic. Please close any program that might be using your Orbic.
If you have adb installed you may need to kill the adb daemon"#; If you have adb installed you may need to kill the adb daemon"#;
const VENDOR_ID: u16 = 0x05c6; const VENDOR_ID: u16 = 0x05c6;
const PRODUCT_ID: u16 = 0xf601; const PRODUCT_ID: u16 = 0xf601;
const INTERFACE: u8 = 1;
#[cfg(target_os = "windows")]
const RNDIS_INTERFACE: u8 = 0;
#[cfg(not(target_os = "windows"))]
const RNDIS_INTERFACE: u8 = 1;
macro_rules! echo { macro_rules! echo {
($($arg:tt)*) => { ($($arg:tt)*) => {
print!($($arg)*); print!($($arg)*);
@@ -41,12 +49,11 @@ macro_rules! echo {
pub async fn install() -> Result<()> { pub async fn install() -> Result<()> {
let mut adb_device = force_debug_mode().await?; let mut adb_device = force_debug_mode().await?;
let serial_interface = open_orbic()?.ok_or_else(|| anyhow!(ORBIC_NOT_FOUND))?;
echo!("Installing rootshell... "); echo!("Installing rootshell... ");
setup_rootshell(&serial_interface, &mut adb_device).await?; setup_rootshell(&mut adb_device).await?;
println!("done"); println!("done");
echo!("Installing rayhunter... "); echo!("Installing rayhunter... ");
let mut adb_device = setup_rayhunter(&serial_interface, adb_device).await?; let mut adb_device = setup_rayhunter(adb_device).await?;
println!("done"); println!("done");
echo!("Testing rayhunter... "); echo!("Testing rayhunter... ");
test_rayhunter(&mut adb_device).await?; test_rayhunter(&mut adb_device).await?;
@@ -54,11 +61,19 @@ pub async fn install() -> Result<()> {
Ok(()) Ok(())
} }
pub async fn shell() -> Result<()> {
println!("opening shell");
let mut adb_device = get_adb().await?;
adb_device.shell(&mut std::io::stdin(), Box::new(std::io::stdout()))?;
Ok(())
}
async fn force_debug_mode() -> Result<ADBUSBDevice> { async fn force_debug_mode() -> Result<ADBUSBDevice> {
println!("Forcing a switch into the debug mode to enable ADB"); println!("Forcing a switch into the debug mode to enable ADB");
enable_command_mode()?; enable_command_mode()?;
echo!("ADB enabled, waiting for reboot... "); echo!("ADB enabled, waiting for reboot... ");
let mut adb_device = get_adb().await?; let mut adb_device = get_adb().await?;
adb_setup_serial(&mut adb_device).await?;
println!("it's alive!"); println!("it's alive!");
echo!("Waiting for atfwd_daemon to startup... "); echo!("Waiting for atfwd_daemon to startup... ");
adb_command(&mut adb_device, &["pgrep", "atfwd_daemon"])?; adb_command(&mut adb_device, &["pgrep", "atfwd_daemon"])?;
@@ -67,22 +82,20 @@ async fn force_debug_mode() -> Result<ADBUSBDevice> {
} }
async fn setup_rootshell( async fn setup_rootshell(
serial_interface: &Interface,
adb_device: &mut ADBUSBDevice, adb_device: &mut ADBUSBDevice,
) -> Result<()> { ) -> Result<()> {
let rootshell_bin = include_bytes!(env!("FILE_ROOTSHELL")); let rootshell_bin = include_bytes!(env!("FILE_ROOTSHELL"));
install_file( install_file(
serial_interface,
adb_device, adb_device,
"/bin/rootshell", "/bin/rootshell",
rootshell_bin, rootshell_bin,
) )
.await?; .await?;
tokio::time::sleep(Duration::from_secs(1)).await; tokio::time::sleep(Duration::from_secs(1)).await;
at_syscmd(serial_interface, "chown root /bin/rootshell").await?; adb_at_syscmd(adb_device, "chown root /bin/rootshell").await?;
tokio::time::sleep(Duration::from_secs(1)).await; tokio::time::sleep(Duration::from_secs(1)).await;
at_syscmd(serial_interface, "chmod 4755 /bin/rootshell").await?; adb_at_syscmd(adb_device, "chmod 4755 /bin/rootshell").await?;
let output = adb_command(adb_device, &["/bin/rootshell", "-c", "id"])?; let output = adb_command(adb_device, &["/bin/rootshell", "-c", "id"])?;
if !output.contains("uid=0") { if !output.contains("uid=0") {
bail!("rootshell is not giving us root."); bail!("rootshell is not giving us root.");
@@ -91,45 +104,40 @@ async fn setup_rootshell(
} }
async fn setup_rayhunter( async fn setup_rayhunter(
serial_interface: &Interface,
mut adb_device: ADBUSBDevice, mut adb_device: ADBUSBDevice,
) -> Result<ADBUSBDevice> { ) -> Result<ADBUSBDevice> {
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON_ORBIC")); let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON_ORBIC"));
at_syscmd(serial_interface, "mkdir -p /data/rayhunter").await?; adb_at_syscmd(&mut adb_device, "mkdir -p /data/rayhunter").await?;
install_file( install_file(
serial_interface,
&mut adb_device, &mut adb_device,
"/data/rayhunter/rayhunter-daemon", "/data/rayhunter/rayhunter-daemon",
rayhunter_daemon_bin, rayhunter_daemon_bin,
) )
.await?; .await?;
install_file( install_file(
serial_interface,
&mut adb_device, &mut adb_device,
"/data/rayhunter/config.toml", "/data/rayhunter/config.toml",
CONFIG_TOML.as_bytes(), CONFIG_TOML.as_bytes(),
) )
.await?; .await?;
install_file( install_file(
serial_interface,
&mut adb_device, &mut adb_device,
"/etc/init.d/rayhunter_daemon", "/etc/init.d/rayhunter_daemon",
RAYHUNTER_DAEMON_INIT.as_bytes(), RAYHUNTER_DAEMON_INIT.as_bytes(),
) )
.await?; .await?;
install_file( install_file(
serial_interface,
&mut adb_device, &mut adb_device,
"/etc/init.d/misc-daemon", "/etc/init.d/misc-daemon",
include_bytes!("../../dist/scripts/misc-daemon"), include_bytes!("../../dist/scripts/misc-daemon"),
) )
.await?; .await?;
at_syscmd(serial_interface, "chmod 755 /etc/init.d/rayhunter_daemon").await?; adb_at_syscmd(&mut adb_device, "chmod 755 /etc/init.d/rayhunter_daemon").await?;
at_syscmd(serial_interface, "chmod 755 /etc/init.d/misc-daemon").await?; adb_at_syscmd(&mut adb_device, "chmod 755 /etc/init.d/misc-daemon").await?;
println!("done"); println!("done");
echo!("Waiting for reboot... "); echo!("Waiting for reboot... ");
at_syscmd(serial_interface, "shutdown -r -t 1 now").await?; adb_at_syscmd(&mut adb_device, "shutdown -r -t 1 now").await?;
// first wait for shutdown (it can take ~10s) // first wait for shutdown (it can take ~10s)
tokio::time::timeout(Duration::from_secs(30), async { tokio::time::timeout(Duration::from_secs(30), async {
while let Ok(dev) = adb_echo_test(adb_device).await { while let Ok(dev) = adb_echo_test(adb_device).await {
@@ -162,7 +170,6 @@ async fn test_rayhunter(adb_device: &mut ADBUSBDevice) -> Result<()> {
} }
async fn install_file( async fn install_file(
serial_interface: &Interface,
adb_device: &mut ADBUSBDevice, adb_device: &mut ADBUSBDevice,
dest: &str, dest: &str,
payload: &[u8], payload: &[u8],
@@ -170,7 +177,7 @@ async fn install_file(
const MAX_FAILURES: u32 = 5; const MAX_FAILURES: u32 = 5;
let mut failures = 0; let mut failures = 0;
loop { loop {
match install_file_impl(serial_interface, adb_device, dest, payload).await { match install_file_impl(adb_device, dest, payload).await {
Ok(()) => return Ok(()), Ok(()) => return Ok(()),
Err(e) => { Err(e) => {
if failures > MAX_FAILURES { if failures > MAX_FAILURES {
@@ -185,7 +192,6 @@ async fn install_file(
} }
async fn install_file_impl( async fn install_file_impl(
serial_interface: &Interface,
adb_device: &mut ADBUSBDevice, adb_device: &mut ADBUSBDevice,
dest: &str, dest: &str,
mut payload: &[u8], mut payload: &[u8],
@@ -202,7 +208,7 @@ async fn install_file_impl(
let file_hash_bytes = hasher.finalize(); let file_hash_bytes = hasher.finalize();
let file_hash = format!("{file_hash_bytes:x}"); let file_hash = format!("{file_hash_bytes:x}");
adb_device.push(&mut payload, &push_tmp_path)?; adb_device.push(&mut payload, &push_tmp_path)?;
at_syscmd(serial_interface, &format!("mv {push_tmp_path} {dest}")).await?; adb_at_syscmd(adb_device, &format!("mv {push_tmp_path} {dest}")).await?;
let file_info = adb_device let file_info = adb_device
.stat(dest) .stat(dest)
.context("Failed to stat transfered file")?; .context("Failed to stat transfered file")?;
@@ -244,7 +250,7 @@ async fn get_adb() -> Result<ADBUSBDevice> {
Err(RustADBError::IOError(e)) if e.kind() == ErrorKind::ResourceBusy => { Err(RustADBError::IOError(e)) if e.kind() == ErrorKind::ResourceBusy => {
bail!(ORBIC_BUSY); bail!(ORBIC_BUSY);
} }
#[cfg(target_os = "macos")] #[cfg(any(target_os = "macos", target_os="windows"))]
Err(RustADBError::IOError(e)) if e.kind() == ErrorKind::PermissionDenied => { Err(RustADBError::IOError(e)) if e.kind() == ErrorKind::PermissionDenied => {
bail!(ORBIC_BUSY_MAC); bail!(ORBIC_BUSY_MAC);
} }
@@ -321,9 +327,58 @@ async fn wait_for_usb_device(vendor_id: u16, product_id: u16) -> Result<()> {
} }
} }
async fn at_syscmd(interface: &Interface, command: &str) -> Result<()> { async fn adb_setup_serial(adb_device: &mut ADBUSBDevice) -> Result<()> {
send_serial_cmd(interface, &format!("AT+SYSCMD={command}")).await Ok(adb_device.get_transport_mut().claim_interface(INTERFACE)?)
} }
async fn adb_at_syscmd(adb_device: &mut ADBUSBDevice, command: &str) -> Result<()> {
adb_serial_cmd(adb_device, &format!("AT+SYSCMD={command}")).await
}
async fn adb_serial_cmd(adb_device: &mut ADBUSBDevice, command: &str) -> Result<()> {
let mut data = String::new();
data.push_str("\r\n");
data.push_str(command);
data.push_str("\r\n");
let timeout = Duration::from_secs(2);
let mut response = [0; 256];
// Set up the serial port appropriately
adb_device
.get_transport_mut()
.send_usb_class_control_msg(INTERFACE, 0x22, 3, 1, &[], timeout)
.context("Failed to send control request")?;
// Send the command
adb_device
.get_transport_mut()
.usb_bulk_write(INTERFACE, 0x2, data.as_bytes(), timeout)
.context("Failed to write command")?;
// Consume the echoed command
adb_device
.get_transport_mut()
.usb_bulk_read(INTERFACE, 0x82, &mut response, timeout)
.context("Failed to read submitted command")?;
// Read the actual response
adb_device
.get_transport_mut()
.usb_bulk_read(INTERFACE, 0x82, &mut response, timeout)
.context("Failed to read response")?;
// For some reason, on macOS the response buffer gets filled with garbage data that's
// rarely valid UTF-8. Luckily we only care about the first couple bytes, so just drop
// the garbage with `from_utf8_lossy` and look for our expected success string.
let responsestr = String::from_utf8_lossy(&response);
if !responsestr.contains("\r\nOK\r\n") {
bail!("Received unexpected response: {0}", responsestr);
}
Ok(())
}
/// Sends an AT command to the usb device over the serial port /// Sends an AT command to the usb device over the serial port
/// ///
/// First establish a USB handle and context by calling `open_orbic(<T>) /// First establish a USB handle and context by calling `open_orbic(<T>)
@@ -400,7 +455,7 @@ pub fn enable_command_mode() -> Result<()> {
index: 0, index: 0,
}; };
let interface = device let interface = device
.detach_and_claim_interface(1) .detach_and_claim_interface(RNDIS_INTERFACE)
.context("detach_and_claim_interface(1) failed")?; .context("detach_and_claim_interface(1) failed")?;
if let Err(e) = interface.control_out_blocking(enable_command_mode, &[], timeout) { if let Err(e) = interface.control_out_blocking(enable_command_mode, &[], timeout) {
// If the device reboots while the command is still executing we // If the device reboots while the command is still executing we
@@ -421,7 +476,7 @@ pub fn open_orbic() -> Result<Option<Interface>> {
// Device after initial mode switch // Device after initial mode switch
if let Some(device) = open_usb_device(VENDOR_ID, PRODUCT_ID)? { if let Some(device) = open_usb_device(VENDOR_ID, PRODUCT_ID)? {
let interface = device let interface = device
.detach_and_claim_interface(1) // will reattach drivers on release .detach_and_claim_interface(INTERFACE) // will reattach drivers on release
.context("detach_and_claim_interface(1) failed")?; .context("detach_and_claim_interface(1) failed")?;
return Ok(Some(interface)); return Ok(Some(interface));
} }
@@ -429,15 +484,7 @@ pub fn open_orbic() -> Result<Option<Interface>> {
// Device with rndis enabled as well // Device with rndis enabled as well
if let Some(device) = open_usb_device(VENDOR_ID, 0xf622)? { if let Some(device) = open_usb_device(VENDOR_ID, 0xf622)? {
let interface = device let interface = device
.detach_and_claim_interface(1) // will reattach drivers on release .detach_and_claim_interface(INTERFACE) // will reattach drivers on release
.context("detach_and_claim_interface(1) failed")?;
return Ok(Some(interface));
}
// Another device with rndis enabled as well
if let Some(device) = open_usb_device(VENDOR_ID, 0xf626)? {
let interface = device
.detach_and_claim_interface(1) // will reattach drivers on release
.context("detach_and_claim_interface(1) failed")?; .context("detach_and_claim_interface(1) failed")?;
return Ok(Some(interface)); return Ok(Some(interface));
} }
+69 -20
View File
@@ -27,10 +27,11 @@ pub async fn main_tplink(
InstallTpLink { InstallTpLink {
skip_sdcard, skip_sdcard,
admin_ip, admin_ip,
sdcard_path,
}: InstallTpLink, }: InstallTpLink,
) -> Result<(), Error> { ) -> Result<(), Error> {
start_telnet(&admin_ip).await?; let is_v3 = start_telnet(&admin_ip).await?;
tplink_run_install(skip_sdcard, admin_ip).await tplink_run_install(skip_sdcard, admin_ip, sdcard_path, is_v3).await
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@@ -38,7 +39,7 @@ struct V3RootResponse {
result: u64, result: u64,
} }
pub async fn start_telnet(admin_ip: &str) -> Result<(), Error> { pub async fn start_telnet(admin_ip: &str) -> Result<bool, Error> {
let qcmap_web_cgi_endpoint = format!("http://{admin_ip}/cgi-bin/qcmap_web_cgi"); let qcmap_web_cgi_endpoint = format!("http://{admin_ip}/cgi-bin/qcmap_web_cgi");
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -51,7 +52,9 @@ pub async fn start_telnet(admin_ip: &str) -> Result<(), Error> {
.send() .send()
.await?; .await?;
if response.status() == 404 { let is_v3 = response.status() != 404;
if !is_v3 {
println!("Got a 404 trying to run exploit for hardware revision v3, trying v5 exploit"); println!("Got a 404 trying to run exploit for hardware revision v3, trying v5 exploit");
tplink_launch_telnet_v5(admin_ip).await?; tplink_launch_telnet_v5(admin_ip).await?;
} else { } else {
@@ -82,20 +85,49 @@ pub async fn start_telnet(admin_ip: &str) -> Result<(), Error> {
println!( println!(
"Succeeded in rooting the device! Now you can use 'telnet {admin_ip}' to get a root shell. Use './installer util tplink-start-telnet' to root again without installing rayhunter." "Succeeded in rooting the device! Now you can use 'telnet {admin_ip}' to get a root shell. Use './installer util tplink-start-telnet' to root again without installing rayhunter."
); );
Ok(()) Ok(is_v3)
} }
async fn tplink_run_install(skip_sdcard: bool, admin_ip: String) -> Result<(), Error> { async fn tplink_run_install(
skip_sdcard: bool,
admin_ip: String,
mut sdcard_path: String,
is_v3: bool,
) -> Result<(), Error> {
println!("Connecting via telnet to {admin_ip}"); println!("Connecting via telnet to {admin_ip}");
let addr = SocketAddr::from_str(&format!("{admin_ip}:23")).unwrap(); let addr = SocketAddr::from_str(&format!("{admin_ip}:23")).unwrap();
if !skip_sdcard { if !skip_sdcard {
println!("Mounting sdcard"); if sdcard_path.is_empty() {
if telnet_send_command(addr, "mount | grep -q /media/card", "exit code 0") if telnet_send_command(addr, "ls /media/card", "exit code 0")
.await .await
.is_err() .is_ok()
{
// TP-Link hardware less than v9.0
sdcard_path = "/media/card".to_owned();
} else if telnet_send_command(addr, "ls /media/sdcard", "exit code 0")
.await
.is_ok()
{
// TP-Link hardware v9.0
sdcard_path = "/media/sdcard".to_owned();
} else {
anyhow::bail!(
"unable to determine sdcard path. this is a bug. please file an issue with your hardware version."
);
}
}
println!("Mounting sdcard on {sdcard_path}");
if telnet_send_command(
addr,
&format!("mount | grep -q {sdcard_path}"),
"exit code 0",
)
.await
.is_err()
{ {
telnet_send_command(addr, "mount /dev/mmcblk0p1 /media/card", "exit code 0").await.context("Rayhunter needs a FAT-formatted SD card to function for more than a few minutes. Insert one and rerun this installer, or pass --skip-sdcard")?; telnet_send_command(addr, &format!("mount /dev/mmcblk0p1 {sdcard_path}"), "exit code 0").await.context("Rayhunter needs a FAT-formatted SD card to function for more than a few minutes. Insert one and rerun this installer, or pass --skip-sdcard")?;
} else { } else {
println!("sdcard already mounted"); println!("sdcard already mounted");
} }
@@ -105,28 +137,38 @@ async fn tplink_run_install(skip_sdcard: bool, admin_ip: String) -> Result<(), E
// expects things to be at this location // expects things to be at this location
telnet_send_command(addr, "rm -rf /data/rayhunter", "exit code 0").await?; telnet_send_command(addr, "rm -rf /data/rayhunter", "exit code 0").await?;
telnet_send_command(addr, "mkdir -p /data", "exit code 0").await?; telnet_send_command(addr, "mkdir -p /data", "exit code 0").await?;
telnet_send_command(addr, "ln -sf /media/card /data/rayhunter", "exit code 0").await?; telnet_send_command(
addr,
&format!("ln -sf {sdcard_path} /data/rayhunter"),
"exit code 0",
)
.await?;
telnet_send_file( telnet_send_file(
addr, addr,
"/media/card/config.toml", &format!("{sdcard_path}/config.toml"),
crate::CONFIG_TOML.as_bytes(), crate::CONFIG_TOML.as_bytes(),
) )
.await?; .await?;
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON_TPLINK")); let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON_TPLINK"));
telnet_send_file(addr, "/media/card/rayhunter-daemon", rayhunter_daemon_bin).await?; telnet_send_file(
addr,
&format!("{sdcard_path}/rayhunter-daemon"),
rayhunter_daemon_bin,
)
.await?;
telnet_send_file( telnet_send_file(
addr, addr,
"/etc/init.d/rayhunter_daemon", "/etc/init.d/rayhunter_daemon",
get_rayhunter_daemon().as_bytes(), get_rayhunter_daemon(&sdcard_path).as_bytes(),
) )
.await?; .await?;
telnet_send_command( telnet_send_command(
addr, addr,
"chmod ugo+x /media/card/rayhunter-daemon", &format!("chmod ugo+x {sdcard_path}/rayhunter-daemon"),
"exit code 0", "exit code 0",
) )
.await?; .await?;
@@ -136,7 +178,13 @@ async fn tplink_run_install(skip_sdcard: bool, admin_ip: String) -> Result<(), E
"exit code 0", "exit code 0",
) )
.await?; .await?;
telnet_send_command(addr, "update-rc.d rayhunter_daemon defaults", "exit code 0").await?;
// if the device is not v3, the JS-based root exploit already added rayhunter_daemon as a
// startup script. tplink v9 does not have update-rc.d, and it was reported that *sometimes* it
// is unreliable on other hardware revisions too.
if is_v3 {
telnet_send_command(addr, "update-rc.d rayhunter_daemon defaults", "exit code 0").await?;
}
println!( println!(
"Done. Rebooting device. After it's started up again, check out the web interface at http://{admin_ip}:8080" "Done. Rebooting device. After it's started up again, check out the web interface at http://{admin_ip}:8080"
@@ -278,6 +326,7 @@ async fn handler(state: State<AppState>, mut req: Request) -> Result<Response, S
// inject some javascript into the admin UI to get us a telnet shell. // inject some javascript into the admin UI to get us a telnet shell.
data.extend(br#";window.rayhunterPoll = window.setInterval(() => { data.extend(br#";window.rayhunterPoll = window.setInterval(() => {
Globals.models.PTModel.add({applicationName: "rayhunter-root", enableState: 1, entryId: 1, openPort: "2300-2400", openProtocol: "TCP", triggerPort: "$(busybox telnetd -l /bin/sh)", triggerProtocol: "TCP"}); Globals.models.PTModel.add({applicationName: "rayhunter-root", enableState: 1, entryId: 1, openPort: "2300-2400", openProtocol: "TCP", triggerPort: "$(busybox telnetd -l /bin/sh)", triggerProtocol: "TCP"});
Globals.models.PTModel.add({applicationName: "rayhunter-daemon", enableState: 1, entryId: 2, openPort: "2400-2500", openProtocol: "TCP", triggerPort: "$(/etc/init.d/rayhunter_daemon start)", triggerProtocol: "TCP"});
alert("Success! You can go back to the rayhunter installer."); alert("Success! You can go back to the rayhunter installer.");
window.clearInterval(window.rayhunterPoll); window.clearInterval(window.rayhunterPoll);
}, 1000);"#); }, 1000);"#);
@@ -324,7 +373,7 @@ async fn tplink_launch_telnet_v5(admin_ip: &str) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn get_rayhunter_daemon() -> String { fn get_rayhunter_daemon(sdcard_path: &str) -> String {
// Even though TP-Link eventually auto-mounts the SD card, it sometimes does so too late. And // Even though TP-Link eventually auto-mounts the SD card, it sometimes does so too late. And
// changing the order in which daemons are started up seems to not work reliably. // changing the order in which daemons are started up seems to not work reliably.
// //
@@ -332,12 +381,12 @@ fn get_rayhunter_daemon() -> String {
// specific to a particular hardware revision here. // specific to a particular hardware revision here.
crate::RAYHUNTER_DAEMON_INIT.replace( crate::RAYHUNTER_DAEMON_INIT.replace(
"#RAYHUNTER-PRESTART", "#RAYHUNTER-PRESTART",
"mount /dev/mmcblk0p1 /media/card || true", &format!("mount /dev/mmcblk0p1 {sdcard_path} || true"),
) )
} }
#[test] #[test]
fn test_get_rayhunter_daemon() { fn test_get_rayhunter_daemon() {
let s = get_rayhunter_daemon(); let s = get_rayhunter_daemon("/media/card");
assert!(s.contains("mount /dev/mmcblk0p1 /media/card")); assert!(s.contains("mount /dev/mmcblk0p1 /media/card"));
} }
+3 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rayhunter" name = "rayhunter"
version = "0.3.1" version = "0.3.3"
edition = "2021" edition = "2021"
description = "Realtime cellular data decoding and analysis for IMSI catcher detection" description = "Realtime cellular data decoding and analysis for IMSI catcher detection"
@@ -19,14 +19,12 @@ bytes = "1.5.0"
chrono = "0.4.31" chrono = "0.4.31"
crc = "3.0.1" crc = "3.0.1"
deku = { version = "0.18.0", features = ["logging"] } deku = { version = "0.18.0", features = ["logging"] }
env_logger = "0.10.1"
libc = "0.2.150" libc = "0.2.150"
log = "0.4.20" log = "0.4.20"
nix = { version = "0.29.0", features = ["feature"] } nix = { version = "0.29.0", features = ["feature"] }
pcap-file-tokio = "0.1.0" pcap-file-tokio = "0.1.0"
thiserror = "1.0.50" thiserror = "1.0.50"
telcom-parser = { path = "../telcom-parser" } telcom-parser = { path = "../telcom-parser" }
tokio = { version = "1.44.2", features = ["full"] } tokio = { version = "1.44.2", default-features = false }
futures-core = "0.3.30" futures = { version = "0.3.30", default-features = false }
futures = "0.3.30"
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.197", features = ["derive"] }
+28 -16
View File
@@ -6,7 +6,7 @@ use crate::hdlc::hdlc_encapsulate;
use crate::log_codes; use crate::log_codes;
use deku::prelude::*; use deku::prelude::*;
use futures_core::TryStream; use futures::TryStream;
use log::{error, info}; use log::{error, info};
use std::io::ErrorKind; use std::io::ErrorKind;
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
@@ -251,6 +251,7 @@ impl DiagDevice {
// //
// TPLINK M7350 v5 source code can be downloaded at https://www.tp-link.com/de/support/gpl-code/?app=omada // TPLINK M7350 v5 source code can be downloaded at https://www.tp-link.com/de/support/gpl-code/?app=omada
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy)]
struct diag_logging_mode_param_t { struct diag_logging_mode_param_t {
req_mode: u32, req_mode: u32,
peripheral_mask: u32, peripheral_mask: u32,
@@ -261,30 +262,41 @@ struct diag_logging_mode_param_t {
fn enable_frame_readwrite(fd: i32, mode: u32) -> DiagResult<()> { fn enable_frame_readwrite(fd: i32, mode: u32) -> DiagResult<()> {
unsafe { unsafe {
if libc::ioctl(fd, DIAG_IOCTL_SWITCH_LOGGING, mode, 0, 0, 0) < 0 { if libc::ioctl(fd, DIAG_IOCTL_SWITCH_LOGGING, mode, 0, 0, 0) < 0 {
let mut params = if cfg!(feature = "tplink") { let try_params: &[diag_logging_mode_param_t] = &[
// tplink M7350 HW revision 3-8 need this mode
#[cfg(feature = "tplink")]
diag_logging_mode_param_t { diag_logging_mode_param_t {
req_mode: mode, req_mode: mode,
peripheral_mask: 0, peripheral_mask: 0,
mode_param: 1, mode_param: 1,
} },
} else { // tplink M7350 HW revision v9 requires the same parameters as orbic
diag_logging_mode_param_t { diag_logging_mode_param_t {
req_mode: mode, req_mode: mode,
peripheral_mask: u32::MAX, peripheral_mask: u32::MAX,
mode_param: 0, mode_param: 0,
} },
}; ];
let mut ret = 0;
for params in try_params {
let mut params = *params;
ret = libc::ioctl(
fd,
DIAG_IOCTL_SWITCH_LOGGING,
&mut params as *mut diag_logging_mode_param_t,
std::mem::size_of::<diag_logging_mode_param_t>(),
0,
0,
0,
0,
);
if ret == 0 {
break;
}
}
let ret = libc::ioctl(
fd,
DIAG_IOCTL_SWITCH_LOGGING,
&mut params as *mut _,
std::mem::size_of::<diag_logging_mode_param_t>(),
0,
0,
0,
0,
);
if ret < 0 { if ret < 0 {
let msg = format!( let msg = format!(
"DIAG_IOCTL_SWITCH_LOGGING ioctl failed with error code {}", "DIAG_IOCTL_SWITCH_LOGGING ioctl failed with error code {}",
+2 -2
View File
@@ -2,8 +2,8 @@
pushd bin/web pushd bin/web
npm run build npm run build
popd popd
cargo build --release --target="armv7-unknown-linux-musleabihf" #--features debug cargo build --profile firmware --target="armv7-unknown-linux-musleabihf" #--features debug
adb shell '/bin/rootshell -c "/etc/init.d/rayhunter_daemon stop"' adb shell '/bin/rootshell -c "/etc/init.d/rayhunter_daemon stop"'
adb push target/armv7-unknown-linux-musleabihf/release/rayhunter-daemon /data/rayhunter/rayhunter-daemon adb push target/armv7-unknown-linux-musleabihf/firmware/rayhunter-daemon /data/rayhunter/rayhunter-daemon
echo "rebooting the device..." echo "rebooting the device..."
adb shell '/bin/rootshell -c "reboot"' adb shell '/bin/rootshell -c "reboot"'
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rootshell" name = "rootshell"
version = "0.3.1" version = "0.3.3"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "telcom-parser" name = "telcom-parser"
version = "0.3.1" version = "0.3.3"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html