adb_kit/
remote.rs

1use crate::device::ADB;
2use crate::error::{ADBError, ADBResult};
3use log::debug;
4use std::path::PathBuf;
5use std::thread;
6use std::time::Duration;
7
8impl ADB {
9    /// 启用设备远程调试
10    pub fn enable_remote_debugging(
11        &self,
12        device_id: &str,
13        port: u16,
14    ) -> ADBResult<String> {
15        // 获取设备 IP 地址
16        let ip_cmd = "ip addr show wlan0 | grep 'inet ' | cut -d' ' -f6 | cut -d/ -f1";
17        let ip = self.shell(device_id, ip_cmd)?;
18        let ip = ip.trim();
19
20        if ip.is_empty() {
21            return Err(ADBError::DeviceError(
22                "无法获取设备 IP 地址".to_string(),
23            ));
24        }
25
26        // 设置设备监听端口
27        let cmd = format!("setprop service.adb.tcp.port {}", port);
28        self.shell(device_id, &cmd)?;
29
30        // 重启 ADB 服务
31        self.shell(device_id, "stop adbd && start adbd")?;
32
33        // 返回连接地址
34        let conn_addr = format!("{}:{}", ip, port);
35        debug!("远程调试已启用: {}", conn_addr);
36
37        Ok(conn_addr)
38    }
39
40    /// 获取设备架构
41    pub fn get_device_architecture(&self, device_id: &str) -> ADBResult<String> {
42        let output = self.shell(device_id, "getprop ro.product.cpu.abi")?;
43        let arch = output.trim();
44
45        // 将 Android 架构名称映射到 Frida 服务器架构名称
46        let frida_arch = match arch {
47            "armeabi-v7a" | "armeabi" => "arm",
48            "arm64-v8a" => "arm64",
49            "x86" => "x86",
50            "x86_64" => "x86_64",
51            _ => {
52                return Err(ADBError::DeviceError(format!(
53                    "不支持的架构: {}",
54                    arch
55                )))
56            }
57        };
58
59        debug!("设备架构: {}", frida_arch);
60        Ok(frida_arch.to_string())
61    }
62
63    /// 在设备上启动 Frida 服务器
64    pub fn start_frida_server(
65        &self,
66        device_id: &str,
67        frida_server_path: &str,
68        port: u16,
69        server_name: Option<&str>,
70        use_root: Option<bool>,
71    ) -> ADBResult<()> {
72        // 确定服务器名称
73        let server_name = server_name.unwrap_or("frida-server");
74        let use_root = use_root.unwrap_or(true);
75
76        // 检查 Frida 服务器是否已在运行
77        let ps_output = self
78            .shell(device_id, &format!("ps -A | grep {}", server_name))?;
79        if !ps_output.trim().is_empty() {
80            debug!("{} 已经在设备 {} 上运行", server_name, device_id);
81
82            // 检查是否在正确的端口上运行
83            let netstat_output = self
84                .shell(
85                    device_id,
86                    &format!("netstat -ano | grep LISTEN | grep :{}", port),
87                )?;
88            if !netstat_output.trim().is_empty() {
89                debug!("{} 已在端口 {} 上监听", server_name, port);
90                return Ok(());
91            } else {
92                // 如果在运行但端口不对,停止现有服务器
93                debug!(
94                    "停止现有 {} 以在正确的端口上重启",
95                    server_name
96                );
97                self.stop_frida_server(device_id, Some(server_name))?;
98            }
99        }
100
101        // 确定设备上的 Frida 服务器路径
102        let device_frida_path = if PathBuf::from(frida_server_path).exists() {
103            // 如果是本地路径,先确定设备架构
104            let arch = self.get_device_architecture(device_id)?;
105
106            // 根据架构选择正确的 frida-server 二进制文件
107            let arch_specific_path = format!("{}-{}", frida_server_path, arch);
108            let local_frida_path = if PathBuf::from(&arch_specific_path).exists() {
109                arch_specific_path
110            } else {
111                frida_server_path.to_string()
112            };
113
114            // 推送到设备上并指定名称
115            let device_path = format!("/data/local/tmp/{}", server_name);
116            self.push(device_id, &local_frida_path, &device_path, None)?;
117
118            // 设置可执行权限
119            self.shell(device_id, &format!("chmod 755 {}", device_path))?;
120            device_path
121        } else {
122            // 如果不是本地路径,假设它已经在设备上
123            frida_server_path.to_string()
124        };
125
126        // 根据 use_root 参数确定启动命令
127        let start_cmd = if use_root {
128            format!("su -c '{} -l 0.0.0.0:{}'", device_frida_path, port)
129        } else {
130            format!("{} -l 0.0.0.0:{}", device_frida_path, port)
131        };
132
133        debug!("使用命令启动 {}: {}", server_name, start_cmd);
134        self.shell_no_wait(device_id, &start_cmd)?;
135
136        // 给服务器一些启动时间
137        thread::sleep(Duration::from_secs(2));
138
139        // 验证服务器是否在运行
140        let verification_attempts = 3;
141        for attempt in 1..=verification_attempts {
142            let ps_output = self
143                .shell(device_id, &format!("ps -A | grep {}", server_name))?;
144            if !ps_output.trim().is_empty() {
145                debug!(
146                    "{} 成功启动 (尝试 {})",
147                    server_name, attempt
148                );
149                return Ok(());
150            }
151
152            if attempt < verification_attempts {
153                debug!(
154                    "等待 {} 启动 (尝试 {}/{})",
155                    server_name, attempt, verification_attempts
156                );
157                thread::sleep(Duration::from_secs(1));
158            }
159        }
160
161        Err(ADBError::CommandError(format!(
162            "无法启动 {}。请检查权限或服务器二进制文件。",
163            server_name
164        )))
165    }
166
167    /// 停止在设备上运行的 Frida 服务器
168    pub fn stop_frida_server(
169        &self,
170        device_id: &str,
171        server_name: Option<&str>,
172    ) -> ADBResult<()> {
173        let server_name = server_name.unwrap_or("frida-server");
174
175        // 尝试不使用 root 权限停止
176        let output = self
177            .shell(device_id, &format!("pkill {}", server_name))?;
178
179        // 如果失败,尝试使用 root 权限
180        if output.contains("Operation not permitted") {
181            self.shell(device_id, &format!("su -c 'pkill {}'", server_name))?;
182        }
183
184        // 验证服务器是否已停止
185        thread::sleep(Duration::from_secs(1));
186        let ps_output = self
187            .shell(device_id, &format!("ps -A | grep {}", server_name))?;
188        if !ps_output.trim().is_empty() {
189            return Err(ADBError::CommandError(format!(
190                "无法停止 {}",
191                server_name
192            )));
193        }
194
195        debug!("{} 成功停止", server_name);
196        Ok(())
197    }
198
199    /// 重启设备到正常模式
200    pub fn reboot(&self, device_id: &str) -> ADBResult<()> {
201        self.with_retry(|| {
202            let mut cmd = std::process::Command::new(&self.config.path);
203            if !device_id.is_empty() {
204                cmd.arg("-s").arg(device_id);
205            }
206
207            let output = cmd.arg("reboot")
208                .output()
209                .map_err(|e| ADBError::CommandError(format!("无法执行重启命令: {}", e)))?;
210
211            if !output.status.success() {
212                let stderr = String::from_utf8_lossy(&output.stderr);
213                return Err(ADBError::CommandError(format!("重启命令失败: {}", stderr)));
214            }
215
216            debug!("已发送重启命令到设备 {}", device_id);
217            Ok(())
218        })
219    }
220
221    /// 重启设备到恢复模式
222    pub fn reboot_recovery(&self, device_id: &str) -> ADBResult<()> {
223        self.with_retry(|| {
224            let mut cmd = std::process::Command::new(&self.config.path);
225            if !device_id.is_empty() {
226                cmd.arg("-s").arg(device_id);
227            }
228
229            let output = cmd.arg("reboot")
230                .arg("recovery")
231                .output()
232                .map_err(|e| ADBError::CommandError(format!("无法执行重启到恢复模式命令: {}", e)))?;
233
234            if !output.status.success() {
235                let stderr = String::from_utf8_lossy(&output.stderr);
236                return Err(ADBError::CommandError(format!("重启到恢复模式命令失败: {}", stderr)));
237            }
238
239            debug!("已发送重启到恢复模式命令到设备 {}", device_id);
240            Ok(())
241        })
242    }
243
244    /// 重启设备到引导加载程序模式
245    pub fn reboot_bootloader(&self, device_id: &str) -> ADBResult<()> {
246        self.with_retry(|| {
247            let mut cmd = std::process::Command::new(&self.config.path);
248            if !device_id.is_empty() {
249                cmd.arg("-s").arg(device_id);
250            }
251
252            let output = cmd.arg("reboot")
253                .arg("bootloader")
254                .output()
255                .map_err(|e| ADBError::CommandError(format!("无法执行重启到引导加载程序模式命令: {}", e)))?;
256
257            if !output.status.success() {
258                let stderr = String::from_utf8_lossy(&output.stderr);
259                return Err(ADBError::CommandError(format!("重启到引导加载程序模式命令失败: {}", stderr)));
260            }
261
262            debug!("已发送重启到引导加载程序模式命令到设备 {}", device_id);
263            Ok(())
264        })
265    }
266}