From e82ba39c5501cefe4da0850e6d2ca40df1c5944f Mon Sep 17 00:00:00 2001
From: LIAUD Corentin
Date: Fri, 25 Oct 2024 18:01:25 +0200
Subject: [PATCH] feat: add adb push over USB
---
Cargo.toml | 4 +-
README.md | 19 ++-
adb_cli/README.md | 25 ++++
adb_cli/src/commands/usb.rs | 2 +
adb_cli/src/main.rs | 15 ++-
adb_client/README.md | 55 ++++++--
adb_client/src/adb_device_ext.rs | 3 +
.../src/server/adb_server_device_commands.rs | 4 +
adb_client/src/server/device_commands/send.rs | 2 +-
adb_client/src/usb/adb_usb_device.rs | 127 +++++++++++++++---
adb_client/src/usb/adb_usb_device_commands.rs | 41 +++++-
11 files changed, 249 insertions(+), 48 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 6818113..6031fe8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,12 +4,12 @@ resolver = "2"
[workspace.package]
edition = "2021"
+keywords = ["adb", "android", "tcp", "usb"]
license = "MIT"
repository = "https://github.com/cocool97/adb_client"
version = "1.0.7"
-keywords = ["adb", "android"]
-# To build locally when working on a new version
+# To build locally when working on a new release
[patch.crates-io]
adb_client = { path = "./adb_client" }
diff --git a/README.md b/README.md
index e06f65e..a35cf55 100644
--- a/README.md
+++ b/README.md
@@ -14,31 +14,30 @@
-Main features :
+Main features of this library:
-- Full Rust, no need to use `adb *` shell commands
-- Currently only support server TCP/IP protocol
+- Full Rust, don't use `adb *` shell commands to interact with devices
+- Supports:
+ - **TCP/IP** protocol, using ADB server as a proxy (standard behavior when using `adb` CLI)
+ - **USB** protocol, interacting directly with end devices
+- Implements hidden `adb` features, like `framebuffer`
- Highly configurable
- Easy to use !
## adb_client
-Rust library implementing ADB protocol and providing high-level abstraction over commands.
+Rust library implementing both ADB protocols and providing a high-level abstraction over many supported commands.
Improved documentation [here](./adb_client/README.md).
## adb_cli
-Rust binary providing an improved version of `adb` CLI, using `adb_client` library. Can be used as an usage example of the library.
+Rust binary providing an improved version of official `adb` CLI, wrapping `adb_client` library. Can act as an usage example of the library.
Improved documentation [here](./adb_cli/README.md).
-## Missing features
-
-- USB protocol (Work in progress)
-
## Related publications
- [Diving into ADB protocol internals (1/2)](https://www.synacktiv.com/publications/diving-into-adb-protocol-internals-12)
-All pull requests are welcome !
+Some features may still be missing, all pull requests are welcome !
diff --git a/adb_cli/README.md b/adb_cli/README.md
index c30201c..29ba48f 100644
--- a/adb_cli/README.md
+++ b/adb_cli/README.md
@@ -15,6 +15,8 @@ cargo install adb_cli
Usage is quite simple, and tends to look like `adb`:
+- To use ADB server as a proxy:
+
```bash
user@laptop ~/adb_client (main)> adb_cli --help
Rust ADB (Android Debug Bridge) CLI
@@ -49,3 +51,26 @@ Options:
-h, --help Print help
-V, --version Print version
```
+
+- To interact directly with end devices
+
+```bash
+user@laptop ~/adb_client (main)> adb_cli usb --help
+Device commands via USB, no server needed
+
+Usage: adb_cli usb [OPTIONS] --vendor-id --product-id
+
+Commands:
+ shell Spawn an interactive shell or run a list of commands on the device
+ pull Pull a file from device
+ push Push a file on device
+ stat Stat a file on device
+ reboot Reboot the device
+ help Print this message or the help of the given subcommand(s)
+
+Options:
+ -v, --vendor-id Hexadecimal vendor id of this USB device
+ -p, --product-id Hexadecimal product id of this USB device
+ -k, --private-key Path to a custom private key to use for authentication
+ -h, --help Print help
+```
\ No newline at end of file
diff --git a/adb_cli/src/commands/usb.rs b/adb_cli/src/commands/usb.rs
index 3c43365..9aff77d 100644
--- a/adb_cli/src/commands/usb.rs
+++ b/adb_cli/src/commands/usb.rs
@@ -30,6 +30,8 @@ pub enum UsbCommands {
Shell { commands: Vec },
/// Pull a file from device
Pull { source: String, destination: String },
+ /// Push a file on device
+ Push { filename: String, path: String },
/// Stat a file on device
Stat { path: String },
/// Reboot the device
diff --git a/adb_cli/src/main.rs b/adb_cli/src/main.rs
index a317767..0f21b92 100644
--- a/adb_cli/src/main.rs
+++ b/adb_cli/src/main.rs
@@ -36,7 +36,7 @@ fn main() -> Result<()> {
}
LocalCommand::Push { filename, path } => {
let mut input = File::open(Path::new(&filename))?;
- device.send(&mut input, &path)?;
+ device.push(&mut input, &path)?;
log::info!("Uploaded {filename} to {path}");
}
LocalCommand::List { path } => {
@@ -158,8 +158,12 @@ fn main() -> Result<()> {
}
}
Command::Usb(usb) => {
- let mut device =
- ADBUSBDevice::new(usb.vendor_id, usb.product_id, usb.path_to_private_key)?;
+ let mut device = match usb.path_to_private_key {
+ Some(pk) => {
+ ADBUSBDevice::new_with_custom_private_key(usb.vendor_id, usb.product_id, pk)?
+ }
+ None => ADBUSBDevice::new(usb.vendor_id, usb.product_id)?,
+ };
match usb.commands {
UsbCommands::Shell { commands } => {
@@ -197,6 +201,11 @@ fn main() -> Result<()> {
log::info!("Reboots device in mode {:?}", reboot_type);
device.reboot(reboot_type.into())?
}
+ UsbCommands::Push { filename, path } => {
+ let mut input = File::open(Path::new(&filename))?;
+ device.push(&mut input, &path)?;
+ log::info!("Uploaded {filename} to {path}");
+ }
}
}
}
diff --git a/adb_client/README.md b/adb_client/README.md
index a44ed8d..f29bca5 100644
--- a/adb_client/README.md
+++ b/adb_client/README.md
@@ -17,16 +17,6 @@ adb_client = "*"
## Examples
-### Launch a command on device via ADB server
-
-```rust no_run
-use adb_client::{ADBServer, ADBDeviceExt};
-
-let mut server = ADBServer::default();
-let mut device = server.get_device().expect("cannot get device");
-device.shell_command(["df", "-h"],std::io::stdout());
-```
-
### Get available ADB devices
```rust no_run
@@ -41,7 +31,19 @@ let mut server = ADBServer::new(SocketAddrV4::new(server_ip, server_port));
server.devices();
```
-### Push a file to the device
+### Using ADB server as proxy
+
+#### [TCP] Launch a command on device
+
+```rust no_run
+use adb_client::{ADBServer, ADBDeviceExt};
+
+let mut server = ADBServer::default();
+let mut device = server.get_device().expect("cannot get device");
+device.shell_command(["df", "-h"],std::io::stdout());
+```
+
+#### [TCP] Push a file to the device
```rust no_run
use adb_client::ADBServer;
@@ -51,6 +53,33 @@ use std::path::Path;
let mut server = ADBServer::default();
let mut device = server.get_device().expect("cannot get device");
-let mut input = File::open(Path::new("/tmp")).expect("Cannot open file");
-device.send(&mut input, "/data/local/tmp");
+let mut input = File::open(Path::new("/tmp/f")).expect("Cannot open file");
+device.push(&mut input, "/data/local/tmp");
+```
+
+### Interacting directly with device
+
+#### [USB] Launch a command on device
+
+```rust no_run
+use adb_client::{ADBUSBDevice, ADBDeviceExt};
+
+let vendor_id = 0x04e8;
+let product_id = 0x6860;
+let mut device = ADBUSBDevice::new(vendor_id, product_id).expect("cannot find device");
+device.shell_command(["df", "-h"],std::io::stdout());
+```
+
+#### [USB] Push a file to the device
+
+```rust no_run
+use adb_client::{ADBUSBDevice, ADBDeviceExt};
+use std::fs::File;
+use std::path::Path;
+
+let vendor_id = 0x04e8;
+let product_id = 0x6860;
+let mut device = ADBUSBDevice::new(vendor_id, product_id).expect("cannot find device");
+let mut input = File::open(Path::new("/tmp/f")).expect("Cannot open file");
+device.push(&mut input, "/data/local/tmp");
```
diff --git a/adb_client/src/adb_device_ext.rs b/adb_client/src/adb_device_ext.rs
index 93d32bf..b392ced 100644
--- a/adb_client/src/adb_device_ext.rs
+++ b/adb_client/src/adb_device_ext.rs
@@ -23,6 +23,9 @@ pub trait ADBDeviceExt {
/// Pull the remote file pointed to by [source] and write its contents into [`output`]
fn pull, W: Write>(&mut self, source: A, output: W) -> Result<()>;
+ /// Push [stream] to [path] on the device.
+ fn push>(&mut self, stream: R, path: A) -> Result<()>;
+
/// Reboots the device using given reboot type
fn reboot(&mut self, reboot_type: RebootType) -> Result<()>;
}
diff --git a/adb_client/src/server/adb_server_device_commands.rs b/adb_client/src/server/adb_server_device_commands.rs
index de636e1..16e7796 100644
--- a/adb_client/src/server/adb_server_device_commands.rs
+++ b/adb_client/src/server/adb_server_device_commands.rs
@@ -117,4 +117,8 @@ impl ADBDeviceExt for ADBServerDevice {
fn reboot(&mut self, reboot_type: crate::RebootType) -> Result<()> {
self.reboot(reboot_type)
}
+
+ fn push>(&mut self, stream: R, path: A) -> Result<()> {
+ self.push(stream, path)
+ }
}
diff --git a/adb_client/src/server/device_commands/send.rs b/adb_client/src/server/device_commands/send.rs
index a631897..2b7eff7 100644
--- a/adb_client/src/server/device_commands/send.rs
+++ b/adb_client/src/server/device_commands/send.rs
@@ -43,7 +43,7 @@ impl Write for ADBSendCommandWriter {
impl ADBServerDevice {
/// Send [stream] to [path] on the device.
- pub fn send>(&mut self, stream: R, path: A) -> Result<()> {
+ pub fn push>(&mut self, stream: R, path: A) -> Result<()> {
log::info!("Sending data to {}", path.as_ref());
let serial = self.identifier.clone();
self.connect()?
diff --git a/adb_client/src/usb/adb_usb_device.rs b/adb_client/src/usb/adb_usb_device.rs
index 6fd4cc8..49b5aeb 100644
--- a/adb_client/src/usb/adb_usb_device.rs
+++ b/adb_client/src/usb/adb_usb_device.rs
@@ -4,12 +4,14 @@ use std::fs::read_to_string;
use std::io::Cursor;
use std::io::Read;
use std::io::Seek;
+use std::path::Path;
use std::path::PathBuf;
use std::time::Duration;
use byteorder::LittleEndian;
use super::{ADBRsaKey, ADBUsbMessage};
+use crate::constants::BUFFER_SIZE;
use crate::models::AdbStatResponse;
use crate::usb::adb_usb_message::{AUTH_RSAPUBLICKEY, AUTH_SIGNATURE, AUTH_TOKEN};
use crate::{
@@ -24,16 +26,8 @@ pub struct ADBUSBDevice {
pub(crate) transport: USBTransport,
}
-fn read_adb_private_key(private_key_path: Option) -> Result