mirror of
https://github.com/radareorg/r2pipe.rs.git
synced 2026-11-08 17:42:45 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c72b4cba60 | ||
|
|
391641f2ff | ||
|
|
1208b28183 | ||
|
|
522a9f7a4f | ||
|
|
89db8759ce | ||
|
|
a66ddad123 | ||
|
|
50c689f4bc | ||
|
|
c0260db239 | ||
|
|
14079c97e9 | ||
|
|
43b68268ca | ||
|
|
8cb8d248a8 | ||
|
|
e67a685a5b | ||
|
|
08335660d4 | ||
|
|
32572abbb2 | ||
|
|
ee3fee9bb4 | ||
|
|
4ee88ba031 | ||
|
|
3796776e11 | ||
|
|
a622e4926c | ||
|
|
9310015b18 | ||
|
|
8dae2fcb73 | ||
|
|
15a83699ae | ||
|
|
3950fe4f0d | ||
|
|
591ac5c1b8 | ||
|
|
c2858b09d7 | ||
|
|
eec57de9d6 | ||
|
|
924eff860d | ||
|
|
08ab5a8f32 | ||
|
|
5f1c34eb8d | ||
|
|
3b631b623e | ||
|
|
71f3fa40f0 | ||
|
|
b8a8c07ab6 | ||
|
|
76fa9f540b | ||
|
|
a951524738 | ||
|
|
7c189bd4a7 | ||
|
|
f2fe7c74f9 | ||
|
|
6abb084735 | ||
|
|
43ad9657df | ||
|
|
61aa661f32 | ||
|
|
2494daf0ab | ||
|
|
c1135945a6 | ||
|
|
07e22c8c01 | ||
|
|
c75fd773da | ||
|
|
c7476920c4 | ||
|
|
5ddfda5a5d | ||
|
|
a2323b759f | ||
|
|
09622db2aa | ||
|
|
1972186180 | ||
|
|
110dc301e2 | ||
|
|
fdd1c5a329 | ||
|
|
23018f8320 | ||
|
|
3ee0bc88a6 | ||
|
|
c2aabb0daf | ||
|
|
bed6e45e9c |
No files matched your search
@@ -0,0 +1,38 @@
|
||||
name: R2pipe Rust CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
name: R2pipe.rs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: wget https://github.com/radareorg/radare2/releases/download/5.8.0/radare2_5.8.0_amd64.deb && sudo dpkg -i radare2*.deb
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- run: rustup component add rustfmt clippy
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release --all-features
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
@@ -0,0 +1,3 @@
|
||||
target
|
||||
Cargo.lock
|
||||
*.swp
|
||||
+27
-21
@@ -1,24 +1,30 @@
|
||||
# travis.yml for testing r2pipe.rs. Initially written in 2016, by pancake.
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
cache:
|
||||
- ccache
|
||||
- apt
|
||||
|
||||
language: rust
|
||||
|
||||
rust: stable
|
||||
|
||||
sudo: false
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- rust: 1.41.0
|
||||
env:
|
||||
- ALL=' '
|
||||
- rust: stable
|
||||
env:
|
||||
- CHECKFMT=1
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
- rust: nightly
|
||||
env:
|
||||
- BENCH=1
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
before_script:
|
||||
- |
|
||||
if [ "${CHECKFMT}" == "1"]; then
|
||||
rustup component add rustfmt --toolchain stable
|
||||
fi
|
||||
script:
|
||||
- cargo build
|
||||
- cargo test
|
||||
- |
|
||||
cargo build --verbose --no-default-features &&
|
||||
cargo test --verbose --no-default-features &&
|
||||
if [ "${CHECKFMT}" == "1"]; then
|
||||
cargo +stable fmt -- --check
|
||||
fi
|
||||
|
||||
notifications:
|
||||
irc: "chat.freenode.net#radare"
|
||||
on_success: never
|
||||
on_failure: always
|
||||
+9
-6
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "r2pipe"
|
||||
version = "0.4.0"
|
||||
version = "0.7.0"
|
||||
authors = [
|
||||
"pancake <[email protected]>",
|
||||
"sushant94 <[email protected]>"
|
||||
]
|
||||
|
||||
edition = "2018"
|
||||
description = "Library to interact with radare2 using r2pipes."
|
||||
|
||||
# Repository
|
||||
@@ -21,9 +21,12 @@ readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
docsrs = []
|
||||
|
||||
[dependencies]
|
||||
libc = "*"
|
||||
rustc-serialize = "*"
|
||||
clippy = {version = "0.0.61", optional = true}
|
||||
libloading = "0.8"
|
||||
libc = "0.2"
|
||||
serde = "1.0.118"
|
||||
serde_json = "1.0.60"
|
||||
serde_derive = "1.0.118"
|
||||
thiserror = "1"
|
||||
@@ -1,19 +1,27 @@
|
||||

|
||||
[](https://travis-ci.org/radareorg/r2pipe.rs)
|
||||
[](https://docs.rs/r2pipe)
|
||||
[](https://crates.io/crates/r2pipe)
|
||||
[](
|
||||
https://github.com/radareorg/r2pipe.rs)
|
||||
|
||||
r2pipe.rs
|
||||
=========
|
||||
|
||||
| **TravisCI** | [](https://travis-ci.org/radare/r2pipe.rs)|
|
||||
|
||||
The Rust Crate to interact with radare2.
|
||||
Please check [Documentation](https://radare.github.io/r2pipe.rs) to get
|
||||
Please check [Documentation](https://docs.rs/r2pipe/latest/r2pipe/) to get
|
||||
started.
|
||||
|
||||
**NOTICE**
|
||||
|
||||
Structures and API functions have been moved to [radare2-r2pipe-api](https://github.com/radare/radare2-r2pipe-api)
|
||||
repository. Please make sure you update your dependencies and imports.
|
||||
|
||||
TODO
|
||||
----
|
||||
* Support async API
|
||||
* Support HTTP connectivity
|
||||
* Support futures API
|
||||
* Add custom r2 start flags for spawn method
|
||||
* Add plain TCP api
|
||||
* Better error handling
|
||||
|
||||
## License
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
clone_depth: 1
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
install:
|
||||
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
|
||||
- rustup-init.exe -y --default-host %TARGET% --default-toolchain nightly
|
||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
- rustc -Vv
|
||||
- cargo -Vv
|
||||
build_script:
|
||||
- cargo build -vv
|
||||
test_script:
|
||||
- cargo test -vv
|
||||
deploy: off
|
||||
+21
-18
@@ -1,11 +1,10 @@
|
||||
extern crate r2pipe;
|
||||
use r2pipe::R2Pipe;
|
||||
|
||||
use std::thread;
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
const FILENAME: &'static str = "/bin/ls";
|
||||
|
||||
@@ -14,7 +13,7 @@ pub struct R2PipeAsync {
|
||||
rx: Receiver<String>,
|
||||
tx2: Sender<String>,
|
||||
rx2: Receiver<String>,
|
||||
cbs: Vec<Arc<Fn(String)>>,
|
||||
cbs: Vec<Arc<dyn Fn(String)>>,
|
||||
}
|
||||
|
||||
impl R2PipeAsync {
|
||||
@@ -30,7 +29,7 @@ impl R2PipeAsync {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmd(&mut self, str: &'static str, cb: Arc<Fn(String)>) {
|
||||
pub fn cmd(&mut self, str: &'static str, cb: Arc<dyn Fn(String)>) {
|
||||
self.cbs.insert(0, cb);
|
||||
self.tx.send(str.to_string()).unwrap();
|
||||
}
|
||||
@@ -44,10 +43,10 @@ impl R2PipeAsync {
|
||||
let child_tx = self.tx2.clone();
|
||||
let child = thread::spawn(move || {
|
||||
let mut r2p = match R2Pipe::in_session() {
|
||||
Some(_) => R2Pipe::open(),
|
||||
None => R2Pipe::spawn(FILENAME.to_owned(), None),
|
||||
}
|
||||
.unwrap();
|
||||
Some(_) => R2Pipe::open(),
|
||||
None => R2Pipe::spawn(FILENAME.to_owned(), None),
|
||||
}
|
||||
.unwrap();
|
||||
loop {
|
||||
let msg = child_rx.recv().unwrap();
|
||||
if msg == "q" {
|
||||
@@ -82,14 +81,18 @@ impl R2PipeAsync {
|
||||
|
||||
fn main() {
|
||||
let mut r2pa = R2PipeAsync::open();
|
||||
r2pa.cmd("?e One",
|
||||
Arc::new(|x| {
|
||||
println!("One: {}", x);
|
||||
}));
|
||||
r2pa.cmd("?e Two",
|
||||
Arc::new(|x| {
|
||||
println!("Two: {}", x);
|
||||
}));
|
||||
r2pa.cmd(
|
||||
"?e One",
|
||||
Arc::new(|x| {
|
||||
println!("One: {}", x);
|
||||
}),
|
||||
);
|
||||
r2pa.cmd(
|
||||
"?e Two",
|
||||
Arc::new(|x| {
|
||||
println!("Two: {}", x);
|
||||
}),
|
||||
);
|
||||
r2pa.quit();
|
||||
r2pa.mainloop();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use r2pipe::Result;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
#[cfg(feature = "http")]
|
||||
{
|
||||
use r2pipe::R2Pipe;
|
||||
use serde_json;
|
||||
|
||||
let mut r2p = R2Pipe::http("http://localhost:9080")?;
|
||||
|
||||
let json = r2p.cmdj("ij")?;
|
||||
println!("{}", serde_json::to_string_pretty(&json)?);
|
||||
println!("ARCH {}", json["bin"]["arch"]);
|
||||
println!("BITS {}", json["bin"]["bits"]);
|
||||
println!("Disasm:\n{}", r2p.cmd("pd 20")?);
|
||||
println!("Hexdump:\n{}", r2p.cmd("px 64")?);
|
||||
r2p.close();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+24
-30
@@ -1,44 +1,38 @@
|
||||
#[macro_use] extern crate r2pipe;
|
||||
use r2pipe::R2Pipe;
|
||||
use r2pipe::R2PipeSpawnOptions;
|
||||
use std::process;
|
||||
use serde_json;
|
||||
|
||||
fn test_trim() {
|
||||
let mut ns = R2Pipe::spawn("/bin/ls".to_owned(), None).unwrap();
|
||||
println!("(({}))", ns.cmd("\n\n?e hello world\n\n").unwrap());
|
||||
println!("(({}))", ns.cmd("\n\n?e hello world\n\n").unwrap());
|
||||
println!("(({}))", ns.cmd("\n\n?e hello world\n\n").unwrap());
|
||||
use r2pipe::R2PipeSpawnOptions;
|
||||
use r2pipe::{R2Pipe, Result};
|
||||
|
||||
fn test_trim() -> Result<()> {
|
||||
let mut ns = R2Pipe::spawn("/bin/ls".to_owned(), None)?;
|
||||
println!("(({}))", ns.cmd("\n\n?e hello world\n\n")?);
|
||||
println!("(({}))", ns.cmd("\n\n?e hello world\n\n")?);
|
||||
println!("(({}))", ns.cmd("\n\n?e hello world\n\n")?);
|
||||
ns.close();
|
||||
// process::exit(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_trim();
|
||||
fn main() -> Result<()> {
|
||||
test_trim()?;
|
||||
|
||||
// let mut r2p = open_pipe!().unwrap();
|
||||
let opts = R2PipeSpawnOptions {
|
||||
exepath: "radare2".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut r2p = match R2Pipe::in_session() {
|
||||
Some(_) => R2Pipe::open(),
|
||||
None => R2Pipe::spawn("/bin/ls".to_owned(), Some(opts)),
|
||||
}
|
||||
.unwrap();
|
||||
Some(_) => R2Pipe::open()?,
|
||||
None => R2Pipe::spawn("/bin/ls".to_owned(), Some(opts))?,
|
||||
};
|
||||
|
||||
println!("{}", r2p.cmd("?e Hello World").unwrap());
|
||||
println!("{}", r2p.cmd("?e Hello World")?);
|
||||
|
||||
let json = r2p.cmdj("ij").unwrap();
|
||||
println!("{}", json.pretty());
|
||||
println!("ARCH {}", json.find_path(&["bin", "arch"]).unwrap());
|
||||
|
||||
// println!("BITS 0x{:x}",json.find_path(&["bin","bits"]).unwrap().as_u64().unwrap());
|
||||
if let Some(bits) = json.find_path(&["bin", "bits"]) {
|
||||
if let Some(n_bits) = bits.as_u64() {
|
||||
println!("BITS 0x{:x}", n_bits);
|
||||
}
|
||||
}
|
||||
println!("Disasm:\n{}", r2p.cmd("pd 20").unwrap());
|
||||
println!("Hexdump:\n{}", r2p.cmd("px 64").unwrap());
|
||||
let json = r2p.cmdj("ij")?;
|
||||
println!("{}", serde_json::to_string_pretty(&json)?);
|
||||
println!("ARCH {}", json["bin"]["arch"]);
|
||||
println!("BITS {}", json["bin"]["bits"]);
|
||||
println!("Disasm:\n{}", r2p.cmd("pd 20")?);
|
||||
println!("Hexdump:\n{}", r2p.cmd("px 64")?);
|
||||
r2p.close();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+5
-6
@@ -1,10 +1,8 @@
|
||||
#[macro_use]
|
||||
extern crate r2pipe;
|
||||
use r2pipe::R2Pipe;
|
||||
use r2pipe::{R2Pipe, Result};
|
||||
|
||||
fn main() {
|
||||
let mut r2p = R2Pipe::spawn("/bin/ls".to_owned(), None).unwrap();
|
||||
println!("{}", r2p.cmd("?e Hello").unwrap());
|
||||
fn main() -> Result<()> {
|
||||
let mut r2p = R2Pipe::spawn("/bin/ls".to_owned(), None)?;
|
||||
println!("{}", r2p.cmd("?e Hello")?);
|
||||
if let Err(_) = r2p.cmd("q") {
|
||||
// !killall r2") {
|
||||
println!("Quit happens!");
|
||||
@@ -18,4 +16,5 @@ fn main() {
|
||||
}
|
||||
}
|
||||
println!("Byebye");
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#[macro_use] extern crate r2pipe;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use r2pipe::r2::R2;
|
||||
use rustc_serialize::json;
|
||||
|
||||
fn main() {
|
||||
let path = "/bin/ls";
|
||||
let mut r2 = R2::new(Some(path)).expect("Failed to spawn r2");
|
||||
r2.init();
|
||||
println!("{}", json::encode(&r2.fn_list().expect("Failed to get Function data")).expect("Serialize failed"));
|
||||
println!("{:#?}", r2.sections().expect("Failed to get section data"));
|
||||
println!("{:#?}", r2.fn_list().expect("Failed to get function data"));
|
||||
println!("{:#?}", r2.flag_info().expect("Failed to get flag data"));
|
||||
println!("{:#?}", r2.strings(true).expect("Failed to get strings data"));
|
||||
println!("{:#?}", r2.strings(false).expect("Failed to get strings data"));
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
use serde_json;
|
||||
|
||||
use r2pipe::{R2Pipe, Result};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut r2p = R2Pipe::tcp("localhost:9080")?;
|
||||
|
||||
println!("{}", r2p.cmd("?e Hello World")?);
|
||||
|
||||
let json = r2p.cmdj("ij")?;
|
||||
println!("{}", serde_json::to_string_pretty(&json)?);
|
||||
println!("ARCH {}", json["bin"]["arch"]);
|
||||
println!("BITS {}", json["bin"]["bits"]);
|
||||
println!("Disasm:\n{}", r2p.cmd("pd 20")?);
|
||||
println!("Hexdump:\n{}", r2p.cmd("px 64")?);
|
||||
r2p.close();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use r2pipe::{R2Pipe, Result};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Lets spawn some r2pipes to open some binaries
|
||||
// First two arguments for R2Pipe::threads() are the same as for R2Pipe::spawn() but inside vectors
|
||||
// Third and last argument is an option of a callback function
|
||||
let pipes = match R2Pipe::threads(
|
||||
vec!["/bin/ls", "/bin/id", "/bin/cat"],
|
||||
vec![None, None, None],
|
||||
None,
|
||||
) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
println!("Error spawning Pipes: {}", e);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
// At this point we can iter through all of our r2pipes and send some commands
|
||||
for p in pipes.iter() {
|
||||
if let Ok(_) = p.send("ij".to_string()) {};
|
||||
}
|
||||
|
||||
// do_other_stuff_here();
|
||||
|
||||
// Lets iter again and see what the pipes got
|
||||
for p in pipes.iter() {
|
||||
// this will block, do "p.recv(false)" for non-blocking receive inside a loop
|
||||
if let Ok(msg) = p.recv(true) {
|
||||
println!("Pipe #{} says: {}", p.id, msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally properly close all pipes
|
||||
// Note: For "join()" we need to borrow so pipes.iter() won't work for this
|
||||
for p in pipes {
|
||||
if let Ok(_) = p.send("q".to_string()) {};
|
||||
p.handle.join().unwrap()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use r2pipe::{R2Pipe, Result};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// First we define a callback. It doesn't block and gets called after a thread receives output from r2pipe
|
||||
// Note: First argument to the callback is the thread id, second one the r2pipe output
|
||||
let callback = Arc::new(|id, result| {
|
||||
println!("Pipe #{} says: {}", id, result);
|
||||
});
|
||||
|
||||
// First two arguments for R2Pipe::threads() are the same as for R2Pipe::spawn() but inside vectors
|
||||
// Third and last argument is an option to a callback function
|
||||
// The callback function takes two Arguments: Thread ID and r2pipe output
|
||||
let pipes = match R2Pipe::threads(
|
||||
vec!["/bin/ls", "/bin/id", "/bin/cat"],
|
||||
vec![None, None, None],
|
||||
Some(callback),
|
||||
) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
println!("Error spawning Pipes: {}", e);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
// At this point we can iter through all of our r2pipes and send some commands
|
||||
for p in pipes.iter() {
|
||||
if let Ok(_) = p.send("ij".to_string()) {};
|
||||
}
|
||||
|
||||
// Meanwhile: Expecting callbacks
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||
|
||||
// Finally properly close all pipes
|
||||
// Note: For "join()" we need to borrow so pipes.iter() won't work for this
|
||||
for p in pipes {
|
||||
if let Ok(_) = p.send("q".to_string()) {};
|
||||
p.handle.join().unwrap()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
format_strings = true
|
||||
reorder_imports = true
|
||||
take_source_hints = false
|
||||
struct_lit_multiline_style = "ForceMulti"
|
||||
@@ -0,0 +1,29 @@
|
||||
use crate::Result;
|
||||
use libloading::{Library, Symbol};
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// Contains a handle to the dynamically loaded library.
|
||||
pub struct LibHandle(Mutex<Library>);
|
||||
|
||||
impl LibHandle {
|
||||
/// Load a shared library by name with platform-specific extension.
|
||||
pub fn new(name: &str, end: Option<&str>) -> Result<LibHandle> {
|
||||
let ext = if cfg!(windows) {
|
||||
".dll"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
".dylib"
|
||||
} else {
|
||||
".so"
|
||||
};
|
||||
let lib_name = format!("{}{}{}", name, ext, end.unwrap_or(""));
|
||||
let lib = unsafe { Library::new(&lib_name) }?;
|
||||
Ok(LibHandle(Mutex::new(lib)))
|
||||
}
|
||||
|
||||
/// Load a symbol from the library and transmute it to the desired type.
|
||||
pub unsafe fn load_sym<T>(&mut self, name: &str) -> Result<T> {
|
||||
let lib = self.0.lock().unwrap();
|
||||
let sym: Symbol<T> = lib.get(name.as_bytes())?;
|
||||
Ok(std::mem::transmute_copy::<_, T>(&*sym))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use std::sync::mpsc::{RecvError, SendError, TryRecvError};
|
||||
use std::{io, str};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Custom `Error` for r2pipe.rs.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// An I/O error occurred.
|
||||
#[error("I/O error")]
|
||||
Io(#[from] io::Error),
|
||||
|
||||
/// No open radare2 session, perhaps path was not specified.
|
||||
#[error("No open session")]
|
||||
NoSession,
|
||||
|
||||
/// Response had invalid/missing JSON.
|
||||
#[error("Empty response from JSON")]
|
||||
EmptyResponse,
|
||||
|
||||
/// Incorrect number of arguments, or incorrect format.
|
||||
#[error("Argument mismatch")]
|
||||
ArgumentMismatch,
|
||||
|
||||
/// An error occurred inside of serde.
|
||||
#[error("Serde deserialization error")]
|
||||
SerdeError(#[from] serde_json::Error),
|
||||
|
||||
/// Error during UTF-8 decoding.
|
||||
#[error("UTF-8 decoding error")]
|
||||
Utf8(#[from] str::Utf8Error),
|
||||
|
||||
/// Error receiving data from channel.
|
||||
#[error("Receive channel data error")]
|
||||
ChannelReceiveError(#[from] RecvError),
|
||||
|
||||
/// Error trying to receive data from channel.
|
||||
#[error("Trying receive channel data error")]
|
||||
ChannelTryReceiveError(#[from] TryRecvError),
|
||||
|
||||
/// Error sending data through channel.
|
||||
#[error("Send channel data error")]
|
||||
ChannelSendError(#[from] SendError<String>),
|
||||
|
||||
/// Error loading radare2 shared library.
|
||||
#[error("Shared library error: {0}")]
|
||||
SharedLibraryError(#[from] libloading::Error),
|
||||
}
|
||||
+19
-13
@@ -1,4 +1,6 @@
|
||||
//! `R2Pipe` provides functions to interact with [radare2](http://rada.re/r/)
|
||||
//! `R2Pipe` provides functions to interact with [radare2](http://rada.re/r/).
|
||||
//! This aims to be a raw API. For more higher-level functions and structs to abstract
|
||||
//! over the generated output, see [r2pipe.rs-frontend]().
|
||||
//!
|
||||
//! Hence this requires you to have radare2 installed on you system. For more
|
||||
//! information refer to the r2 [repository](https://github.com/radare/radare2).
|
||||
@@ -31,34 +33,38 @@
|
||||
//! ```no_run
|
||||
//! #[macro_use]
|
||||
//! extern crate r2pipe;
|
||||
//! extern crate serde_json;
|
||||
//! use r2pipe::R2Pipe;
|
||||
//! fn main() {
|
||||
//! let path = Some("/bin/ls".to_owned());
|
||||
//! let mut r2p = open_pipe!(path).unwrap();
|
||||
//! println!("{}", r2p.cmd("?e Hello World").unwrap());
|
||||
//! if let Ok(json) = r2p.cmdj("ij") {
|
||||
//! println!("{}", json.pretty());
|
||||
//! println!("ARCH {}", json.find_path(&["bin","arch"]).unwrap());
|
||||
//! println!("{}", serde_json::to_string_pretty(&json).unwrap());
|
||||
//! println!("ARCH {}", json["bin"]["arch"]);
|
||||
//! }
|
||||
//! r2p.close();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The crate offers various methods to interact with r2pipe, eg. via process (multi-threadable), http or tcp.
|
||||
//! Check the examples/ dir for more complete examples.
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![doc(html_root_url = "https://radare.github.io/r2pipe.rs/")]
|
||||
|
||||
// Support for extra lints for clippy
|
||||
#![cfg_attr(feature="clippy", feature(plugin))]
|
||||
#![cfg_attr(feature="clippy", plugin(clippy))]
|
||||
|
||||
extern crate libc;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
#[macro_use]
|
||||
pub mod r2pipe;
|
||||
mod dlfcn;
|
||||
pub mod r2;
|
||||
pub mod structs;
|
||||
|
||||
mod error;
|
||||
pub use error::*;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub type Result<T> = std::result::Result<T, error::Error>;
|
||||
|
||||
// Rexport to bring it out one module.
|
||||
pub use self::r2pipe::R2PipeSpawnOptions;
|
||||
pub use self::r2pipe::R2Pipe;
|
||||
pub use self::r2::R2;
|
||||
pub use self::r2pipe::R2Pipe;
|
||||
pub use self::r2pipe::R2PipeSpawnOptions;
|
||||
@@ -5,26 +5,14 @@
|
||||
// This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! `R2` struct to make interaction with r2 easier
|
||||
//! Few functions for initialization, communication and termination of r2.
|
||||
//!
|
||||
//! This module is for convenience purposes. It provides a nicer way to
|
||||
//! interact with r2 by
|
||||
//! parsing the JSON into structs. Note that all commands are not supported and
|
||||
//! this
|
||||
//! module is updated only on a need basis. r2 commands that are not supported
|
||||
//! by this module can
|
||||
//! still be called by using the send() and recv() that `R2` provides. If this
|
||||
//! is a command that
|
||||
//! you see yourself using frequently and feel it is important to have nice
|
||||
//! rust wrappers
|
||||
//! feel free to raise an issue, or better yet a pull request implementing the
|
||||
//! same.
|
||||
//! If you wish to write wrappers for certain r2 functionalities,
|
||||
//! contribute to the r2pipe.rs-frontend project. This aims to be a
|
||||
//! barebones implementation of the pipe concept.
|
||||
|
||||
use r2pipe::R2Pipe;
|
||||
use rustc_serialize::json::{DecodeResult, Json};
|
||||
use rustc_serialize::json;
|
||||
|
||||
use super::structs::*;
|
||||
use crate::{r2pipe::R2Pipe, Error, Result};
|
||||
use serde_json::Value;
|
||||
|
||||
pub struct R2 {
|
||||
pipe: R2Pipe,
|
||||
@@ -41,26 +29,21 @@ impl Default for R2 {
|
||||
// i.e. The ones that are not currently abstracted by the R2 API.
|
||||
// Ideally, all commonly used commands must be supported for easier use.
|
||||
impl R2 {
|
||||
// TODO: Use an error type
|
||||
pub fn new<T: AsRef<str>>(path: Option<T>) -> Result<R2, String> {
|
||||
pub fn new<T: AsRef<str>>(path: Option<T>) -> Result<R2> {
|
||||
if path.is_none() && !R2::in_session() {
|
||||
let e = "No r2 session open. Please specify path!".to_owned();
|
||||
return Err(e);
|
||||
return Err(Error::NoSession);
|
||||
}
|
||||
|
||||
// This means that path is `Some` or we have an open session.
|
||||
let pipe = open_pipe!(path.as_ref()).unwrap();
|
||||
let pipe = open_pipe!(path.as_ref())?;
|
||||
Ok(R2 {
|
||||
pipe: pipe,
|
||||
pipe,
|
||||
readin: String::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn in_session() -> bool {
|
||||
match R2Pipe::in_session() {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
R2Pipe::in_session().is_some()
|
||||
}
|
||||
|
||||
pub fn from(r2p: R2Pipe) -> R2 {
|
||||
@@ -70,19 +53,14 @@ impl R2 {
|
||||
}
|
||||
}
|
||||
|
||||
// Does some basic configurations (sane defaults).
|
||||
pub fn init(&mut self) {
|
||||
self.send("e asm.esil = true");
|
||||
self.send("e scr.color = false");
|
||||
self.analyze();
|
||||
pub fn close(&mut self) -> Result<()> {
|
||||
self.send("q!")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.send("q!");
|
||||
}
|
||||
|
||||
pub fn send(&mut self, cmd: &str) {
|
||||
self.readin = self.pipe.cmd(cmd).unwrap();
|
||||
pub fn send(&mut self, cmd: &str) -> Result<()> {
|
||||
self.readin = self.pipe.cmd(cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv(&mut self) -> String {
|
||||
@@ -91,83 +69,16 @@ impl R2 {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn recv_json(&mut self) -> Json {
|
||||
let mut res = self.recv().replace("\n", "");
|
||||
pub fn recv_json(&mut self) -> Result<Value> {
|
||||
let mut res = self.recv().replace('\n', "");
|
||||
if res.is_empty() {
|
||||
res = "{}".to_owned();
|
||||
}
|
||||
Json::from_str(&res).unwrap()
|
||||
|
||||
Ok(serde_json::from_str(&res)?)
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) {
|
||||
self.readin = String::from("");
|
||||
}
|
||||
|
||||
pub fn analyze(&mut self) {
|
||||
self.send("aaaaaaaa");
|
||||
self.flush();
|
||||
}
|
||||
|
||||
pub fn function(&mut self, func: &str) -> DecodeResult<LFunctionInfo> {
|
||||
let cmd = format!("pdfj @ {}", func);
|
||||
self.send(&cmd);
|
||||
let raw_json = self.recv();
|
||||
// Handle Error here.
|
||||
json::decode(&raw_json)
|
||||
}
|
||||
|
||||
// get 'n' (or 16) instructions at 'offset' (or current position if offset in
|
||||
// `None`)
|
||||
pub fn insts(&mut self, n: Option<u64>, offset: Option<&str>) -> DecodeResult<Vec<LOpInfo>> {
|
||||
let n = n.unwrap_or(16);
|
||||
let offset: &str = offset.unwrap_or_default();
|
||||
let mut cmd = format!("pdj{}", n);
|
||||
if !offset.is_empty() {
|
||||
cmd = format!("{} @ {}", cmd, offset);
|
||||
}
|
||||
self.send(&cmd);
|
||||
let raw_json = self.recv();
|
||||
json::decode(&raw_json)
|
||||
}
|
||||
|
||||
pub fn reg_info(&mut self) -> DecodeResult<LRegInfo> {
|
||||
self.send("drpj");
|
||||
let raw_json = self.recv();
|
||||
json::decode(&raw_json)
|
||||
}
|
||||
|
||||
pub fn flag_info(&mut self) -> DecodeResult<Vec<LFlagInfo>> {
|
||||
self.send("fj");
|
||||
let raw_json = self.recv();
|
||||
json::decode(&raw_json)
|
||||
}
|
||||
|
||||
pub fn bin_info(&mut self) -> DecodeResult<LBinInfo> {
|
||||
self.send("ij");
|
||||
let raw_json = self.recv();
|
||||
json::decode(&raw_json)
|
||||
}
|
||||
|
||||
pub fn fn_list(&mut self) -> DecodeResult<Vec<FunctionInfo>> {
|
||||
self.send("aflj");
|
||||
let raw_json = self.recv();
|
||||
json::decode(&raw_json)
|
||||
}
|
||||
|
||||
pub fn sections(&mut self) -> DecodeResult<Vec<LSectionInfo>> {
|
||||
self.send("Sj");
|
||||
json::decode(&self.recv())
|
||||
}
|
||||
|
||||
pub fn strings(&mut self, data_only: bool) -> DecodeResult<Vec<LStringInfo>> {
|
||||
if data_only {
|
||||
self.send("izj");
|
||||
json::decode(&self.recv())
|
||||
} else {
|
||||
self.send("izzj");
|
||||
#[derive(RustcDecodable)] struct Foo { strings: Vec<LStringInfo> };
|
||||
let x: DecodeResult<Foo> = json::decode(&self.recv());
|
||||
x.map(|i| i.strings)
|
||||
}
|
||||
}
|
||||
}
|
||||
+333
-134
@@ -2,19 +2,24 @@
|
||||
//!
|
||||
//! Please check crate level documentation for more details and example.
|
||||
|
||||
use std::os::unix::io::FromRawFd;
|
||||
use rustc_serialize::json::Json;
|
||||
use crate::dlfcn;
|
||||
use crate::{Error, Result};
|
||||
|
||||
use libc;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use std::process;
|
||||
use std::env;
|
||||
use std::str;
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use std::str;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
/// File descriptors to the parent r2 process.
|
||||
pub struct R2PipeLang {
|
||||
@@ -26,74 +31,112 @@ pub struct R2PipeLang {
|
||||
pub struct R2PipeSpawn {
|
||||
read: BufReader<process::ChildStdout>,
|
||||
write: process::ChildStdin,
|
||||
child: Option<process::Child>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
/// Stores the socket address of the r2 process.
|
||||
pub struct R2PipeTcp {
|
||||
socket_addr: SocketAddr,
|
||||
}
|
||||
|
||||
pub struct R2PipeHttp {
|
||||
host: String,
|
||||
}
|
||||
|
||||
/// Stores thread metadata
|
||||
/// It stores both a sending and receiving end to the thread, allowing convenient interaction
|
||||
/// So we can send commands using R2PipeThread::send() and fetch outputs using R2PipeThread::recv()
|
||||
pub struct R2PipeThread {
|
||||
r2recv: mpsc::Receiver<String>,
|
||||
r2send: mpsc::Sender<String>,
|
||||
pub id: u16,
|
||||
pub handle: thread::JoinHandle<Result<()>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct R2PipeSpawnOptions {
|
||||
pub exepath: String,
|
||||
pub args: Vec<&'static str>,
|
||||
}
|
||||
|
||||
/// Provides abstraction between the two invocation methods.
|
||||
pub enum R2Pipe {
|
||||
Pipe(R2PipeSpawn),
|
||||
Lang(R2PipeLang),
|
||||
}
|
||||
impl Default for R2PipeSpawnOptions {
|
||||
fn default() -> Self {
|
||||
let exepath = if cfg!(windows) { "radare2.exe" } else { "r2" };
|
||||
|
||||
fn atoi(k: &str) -> i32 {
|
||||
match k.parse::<i32>() {
|
||||
Ok(val) => val,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
fn getenv(k: &str) -> i32 {
|
||||
match env::var(k) {
|
||||
Ok(val) => atoi(&val),
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
fn process_result(res: Vec<u8>) -> Result<String, String> {
|
||||
let len = res.len();
|
||||
let out = if len > 0 {
|
||||
let res_without_zero = &res[..len - 1];
|
||||
if let Ok(utf8str) = str::from_utf8(res_without_zero) {
|
||||
String::from(utf8str.trim())
|
||||
} else {
|
||||
return Err("Failed".to_owned());
|
||||
R2PipeSpawnOptions {
|
||||
exepath: exepath.to_string(),
|
||||
args: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides abstraction between the three invocation methods.
|
||||
pub struct R2Pipe(Box<dyn Pipe>);
|
||||
pub trait Pipe {
|
||||
fn cmd(&mut self, cmd: &str) -> Result<String>;
|
||||
fn cmdj(&mut self, cmd: &str) -> Result<Value> {
|
||||
let result = self.cmd(cmd)?;
|
||||
if result.is_empty() {
|
||||
return Err(Error::EmptyResponse);
|
||||
}
|
||||
Ok(serde_json::from_str(&result)?)
|
||||
}
|
||||
/// Escape the command before executing, valid only as of r2 v.5.8.0 "icebucket"
|
||||
fn call(&mut self, cmd: &str) -> Result<String> {
|
||||
self.cmd(&format!("\"\"{}", cmd))
|
||||
}
|
||||
/// Escape the command before executing and convert it to a json value,
|
||||
/// valid only as of r2 v.5.8.0 "icebucket"
|
||||
fn callj(&mut self, cmd: &str) -> Result<Value> {
|
||||
self.cmdj(&format!("\"\"{}", cmd))
|
||||
}
|
||||
fn close(&mut self) {}
|
||||
}
|
||||
fn getenv(k: &str) -> Option<i32> {
|
||||
match env::var(k) {
|
||||
Ok(val) => val.parse::<i32>().ok(),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn process_result(res: Vec<u8>) -> Result<String> {
|
||||
let len = res.len();
|
||||
if len == 0 {
|
||||
Err(Error::EmptyResponse)
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
Ok(out)
|
||||
Ok(str::from_utf8(&res[..len - 1])?.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! open_pipe {
|
||||
() => {
|
||||
R2Pipe::open(),
|
||||
() => {
|
||||
R2Pipe::open(),
|
||||
};
|
||||
($x: expr) => {
|
||||
match $x {
|
||||
Some(path) => R2Pipe::load_native(&path.clone()).or_else(|_| R2Pipe::spawn(path, None)),
|
||||
None => R2Pipe::open(),
|
||||
}
|
||||
};
|
||||
($x: expr) => {
|
||||
match $x {
|
||||
Some(path) => R2Pipe::spawn(path, None),
|
||||
None => R2Pipe::open(),
|
||||
}
|
||||
};
|
||||
($x: expr, $y: expr) => {
|
||||
match $x $y {
|
||||
Some(path, opts) => R2Pipe::spawn(path, opts),
|
||||
(None, None) => R2Pipe::open(),
|
||||
}
|
||||
}
|
||||
($x: expr, $y: expr) => {
|
||||
match $x $y {
|
||||
Some(path, opts) => R2Pipe::spawn(path, opts),
|
||||
(None, None) => R2Pipe::open(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl R2Pipe {
|
||||
pub fn open() -> Result<R2Pipe, &'static str> {
|
||||
let (f_in, f_out) = match R2Pipe::in_session() {
|
||||
Some(x) => x,
|
||||
None => return Err("Pipe not open. Please run from r2"),
|
||||
};
|
||||
pub fn load_native<T: AsRef<str>>(path: T) -> Result<R2Pipe> {
|
||||
Ok(R2Pipe(Box::new(R2PipeNative::open(path.as_ref())?)))
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
pub fn open() -> Result<R2Pipe> {
|
||||
use std::os::unix::io::FromRawFd;
|
||||
|
||||
let (f_in, f_out) = R2Pipe::in_session().ok_or(Error::NoSession)?;
|
||||
|
||||
let res = unsafe {
|
||||
// dup file descriptors to avoid from_raw_fd ownership issue
|
||||
let (d_in, d_out) = (libc::dup(f_in), libc::dup(f_out));
|
||||
@@ -102,36 +145,37 @@ impl R2Pipe {
|
||||
write: File::from_raw_fd(d_out),
|
||||
}
|
||||
};
|
||||
Ok(R2Pipe::Lang(res))
|
||||
Ok(R2Pipe(Box::new(res)))
|
||||
}
|
||||
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String, String> {
|
||||
match *self {
|
||||
R2Pipe::Pipe(ref mut x) => x.cmd(cmd.trim()),
|
||||
R2Pipe::Lang(ref mut x) => x.cmd(cmd.trim()),
|
||||
}
|
||||
#[cfg(windows)]
|
||||
pub fn open() -> Result<R2Pipe> {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
self.0.cmd(cmd.trim())
|
||||
}
|
||||
|
||||
pub fn cmdj(&mut self, cmd: &str) -> Result<Json, String> {
|
||||
match *self {
|
||||
R2Pipe::Pipe(ref mut x) => x.cmdj(cmd.trim()),
|
||||
R2Pipe::Lang(ref mut x) => x.cmdj(cmd.trim()),
|
||||
}
|
||||
pub fn cmdj(&mut self, cmd: &str) -> Result<Value> {
|
||||
self.0.cmdj(cmd.trim())
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
match *self {
|
||||
R2Pipe::Pipe(ref mut x) => x.close(),
|
||||
R2Pipe::Lang(ref mut x) => x.close(),
|
||||
}
|
||||
self.0.close();
|
||||
}
|
||||
/// Escape the command before executing, valid only as of r2 v.5.8.0 "icebucket"
|
||||
pub fn call(&mut self, cmd: &str) -> Result<String> {
|
||||
self.0.call(cmd)
|
||||
}
|
||||
/// Escape the command before executing and convert it to a json value,
|
||||
/// valid only as of r2 v.5.8.0 "icebucket"
|
||||
pub fn callj(&mut self, cmd: &str) -> Result<Value> {
|
||||
self.0.callj(cmd)
|
||||
}
|
||||
|
||||
pub fn in_session() -> Option<(i32, i32)> {
|
||||
let f_in = getenv("R2PIPE_IN");
|
||||
let f_out = getenv("R2PIPE_OUT");
|
||||
if f_in < 0 || f_out < 0 {
|
||||
return None;
|
||||
}
|
||||
let f_in = getenv("R2PIPE_IN")?;
|
||||
let f_out = getenv("R2PIPE_OUT")?;
|
||||
Some((f_in, f_out))
|
||||
}
|
||||
|
||||
@@ -144,98 +188,253 @@ impl R2Pipe {
|
||||
}
|
||||
|
||||
/// Creates a new R2PipeSpawn.
|
||||
pub fn spawn<T: AsRef<str>>(name: T, opts: Option<R2PipeSpawnOptions>) -> Result<R2Pipe, &'static str> {
|
||||
if name.as_ref() == "" {
|
||||
if let Some(_) = R2Pipe::in_session() {
|
||||
return R2Pipe::open();
|
||||
}
|
||||
pub fn spawn<T: AsRef<str>>(name: T, mut opts: Option<R2PipeSpawnOptions>) -> Result<R2Pipe> {
|
||||
if name.as_ref() == "" && R2Pipe::in_session().is_some() {
|
||||
return R2Pipe::open();
|
||||
}
|
||||
|
||||
let exepath = match opts {
|
||||
Some(ref opt) => opt.exepath.clone(),
|
||||
_ => "r2".to_owned(),
|
||||
};
|
||||
let args = match opts {
|
||||
Some(ref opt) => opt.args.clone(),
|
||||
_ => vec![],
|
||||
};
|
||||
let R2PipeSpawnOptions { exepath, args } = opts.take().unwrap_or_default();
|
||||
|
||||
let path = Path::new(name.as_ref());
|
||||
let child = match Command::new(exepath)
|
||||
let mut child = Command::new(exepath)
|
||||
.arg("-q0")
|
||||
.args(&args)
|
||||
.arg(path)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn() {
|
||||
Ok(c) => c,
|
||||
Err(_) => return Err("Unable to spawn r2."),
|
||||
};
|
||||
.spawn()?;
|
||||
|
||||
let sin = child.stdin.unwrap();
|
||||
let mut sout = child.stdout.unwrap();
|
||||
// If stdin/stdout is not available, hard error
|
||||
let sin = child.stdin.take().unwrap();
|
||||
let mut sout = child.stdout.take().unwrap();
|
||||
|
||||
// flush out the initial null byte.
|
||||
let mut w = [0; 1];
|
||||
sout.read(&mut w).unwrap();
|
||||
sout.read_exact(&mut w)?;
|
||||
|
||||
let res = R2PipeSpawn {
|
||||
read: BufReader::new(sout),
|
||||
write: sin,
|
||||
child: Some(child),
|
||||
};
|
||||
|
||||
Ok(R2Pipe::Pipe(res))
|
||||
Ok(R2Pipe(Box::new(res)))
|
||||
}
|
||||
|
||||
/// Creates a new R2PipeTcp
|
||||
pub fn tcp<A: ToSocketAddrs>(addr: A) -> Result<R2Pipe> {
|
||||
// use `connect` to figure out which socket address works
|
||||
let stream = TcpStream::connect(addr)?;
|
||||
let addr = stream.peer_addr()?;
|
||||
Ok(R2Pipe(Box::new(R2PipeTcp { socket_addr: addr })))
|
||||
}
|
||||
|
||||
/// Creates a new R2PipeHttp
|
||||
pub fn http(host: &str) -> R2Pipe {
|
||||
R2Pipe(Box::new(R2PipeHttp {
|
||||
host: host.to_string(),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Creates new pipe threads
|
||||
/// First two arguments for R2Pipe::threads() are the same as for R2Pipe::spawn() but inside vectors
|
||||
/// Third and last argument is an option to a callback function
|
||||
/// The callback function takes two Arguments: Thread ID and r2pipe output
|
||||
pub fn threads(
|
||||
names: Vec<&'static str>,
|
||||
opts: Vec<Option<R2PipeSpawnOptions>>,
|
||||
callback: Option<Arc<dyn Fn(u16, String) + Sync + Send>>,
|
||||
) -> Result<Vec<R2PipeThread>> {
|
||||
if names.len() != opts.len() {
|
||||
return Err(Error::ArgumentMismatch);
|
||||
}
|
||||
|
||||
let mut pipes = Vec::new();
|
||||
|
||||
for n in 0..names.len() {
|
||||
let (htx, rx) = mpsc::channel();
|
||||
let (tx, hrx) = mpsc::channel();
|
||||
let name = names[n];
|
||||
let opt = opts[n].clone();
|
||||
let cb = callback.clone();
|
||||
let t = thread::spawn(move || -> Result<()> {
|
||||
let mut r2 = R2Pipe::spawn(name, opt)?;
|
||||
loop {
|
||||
let cmd: String = hrx.recv()?;
|
||||
if cmd == "q" {
|
||||
break;
|
||||
}
|
||||
let res = r2.cmdj(&cmd)?.to_string();
|
||||
htx.send(res.clone())?;
|
||||
if let Some(cbs) = cb.clone() {
|
||||
thread::spawn(move || {
|
||||
cbs(n as u16, res);
|
||||
});
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
pipes.push(R2PipeThread {
|
||||
r2recv: rx,
|
||||
r2send: tx,
|
||||
id: n as u16,
|
||||
handle: t,
|
||||
});
|
||||
}
|
||||
Ok(pipes)
|
||||
}
|
||||
}
|
||||
|
||||
impl R2PipeThread {
|
||||
pub fn send(&self, cmd: String) -> Result<()> {
|
||||
Ok(self.r2send.send(cmd)?)
|
||||
}
|
||||
|
||||
pub fn recv(&self, block: bool) -> Result<String> {
|
||||
if block {
|
||||
Ok(self.r2recv.recv()?)
|
||||
} else {
|
||||
Ok(self.r2recv.try_recv()?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipe for R2PipeSpawn {
|
||||
fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
let cmd = cmd.to_owned() + "\n";
|
||||
self.write.write_all(cmd.as_bytes())?;
|
||||
|
||||
let mut res: Vec<u8> = Vec::new();
|
||||
self.read.read_until(0u8, &mut res)?;
|
||||
process_result(res)
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
let _ = self.cmd("q!");
|
||||
if let Some(child) = &mut self.child {
|
||||
let _ = child.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl R2PipeSpawn {
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String, String> {
|
||||
let cmd_ = cmd.to_owned() + "\n";
|
||||
if let Err(e) = self.write.write(cmd_.as_bytes()) {
|
||||
return Err(e.to_string());
|
||||
}
|
||||
/// Attempts to take the pipes underlying child process handle.
|
||||
/// On success the handle is returned.
|
||||
/// If `None` is returned the child handle was already taken previously.
|
||||
/// By using this method you take over the responsibility to `wait()` the child process in order to free all of it's resources.
|
||||
pub fn take_child(&mut self) -> Option<process::Child> {
|
||||
self.child.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipe for R2PipeLang {
|
||||
fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
self.write.write_all(cmd.as_bytes())?;
|
||||
let mut res: Vec<u8> = Vec::new();
|
||||
if let Err(e) = self.read.read_until(0u8, &mut res) {
|
||||
return Err(e.to_string());
|
||||
}
|
||||
self.read.read_until(0u8, &mut res)?;
|
||||
process_result(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmdj(&mut self, cmd: &str) -> Result<Json, String> {
|
||||
if let Ok(res) = self.cmd(cmd) {
|
||||
if res == "" {
|
||||
return Err("Empty JSON".to_string());
|
||||
}
|
||||
if let Ok(jstr) = Json::from_str(&res) {
|
||||
Ok(jstr)
|
||||
} else {
|
||||
Err(format!("Invalid JSON for {} ({})", cmd, res))
|
||||
}
|
||||
impl Pipe for R2PipeHttp {
|
||||
fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
let host = if self.host.starts_with("http://") {
|
||||
&self.host[7..]
|
||||
} else {
|
||||
Err("oops cmd".to_string())
|
||||
}
|
||||
}
|
||||
&self.host
|
||||
};
|
||||
let mut stream = TcpStream::connect(host)?;
|
||||
let req = format!("GET /cmd/{} HTTP/1.1\r\n", cmd);
|
||||
let mut resp = Vec::with_capacity(1024);
|
||||
stream.write_all(req.as_bytes())?;
|
||||
stream.read_to_end(&mut resp)?;
|
||||
|
||||
pub fn close(&mut self) {
|
||||
let _ = self.cmd("q!");
|
||||
// index of the start of response body
|
||||
let index = resp
|
||||
.windows(4)
|
||||
.position(|w| w == "\r\n\r\n".as_bytes())
|
||||
.map(|i| i + 4)
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(str::from_utf8(&resp[index..]).map(|s| s.to_string())?)
|
||||
}
|
||||
}
|
||||
|
||||
impl R2PipeLang {
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String, String> {
|
||||
self.write.write(cmd.as_bytes()).unwrap();
|
||||
impl Pipe for R2PipeTcp {
|
||||
fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
let mut stream = TcpStream::connect(self.socket_addr)?;
|
||||
stream.write_all(cmd.as_bytes())?;
|
||||
let mut res: Vec<u8> = Vec::new();
|
||||
self.read.read_until(0u8, &mut res).unwrap();
|
||||
stream.read_to_end(&mut res)?;
|
||||
res.push(0);
|
||||
process_result(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmdj(&mut self, cmd: &str) -> Result<Json, String> {
|
||||
let res = try!(self.cmd(cmd));
|
||||
Ok(Json::from_str(&res).unwrap())
|
||||
}
|
||||
pub struct R2PipeNative {
|
||||
lib: dlfcn::LibHandle,
|
||||
r_core: std::sync::Mutex<*mut libc::c_void>,
|
||||
r_core_cmd_str_handle: fn(*mut libc::c_void, *const libc::c_char) -> *mut libc::c_char,
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
// self.read.close();
|
||||
// self.write.close();
|
||||
impl R2PipeNative {
|
||||
pub fn open(file: &str) -> Result<R2PipeNative> {
|
||||
let mut lib = dlfcn::LibHandle::new("libr_core", None)?;
|
||||
let r_core_new: fn() -> *mut libc::c_void = unsafe { lib.load_sym("r_core_new")? };
|
||||
let r_core_cmd_str_handle = unsafe { lib.load_sym("r_core_cmd_str")? };
|
||||
let r_core = r_core_new();
|
||||
if r_core.is_null() {
|
||||
return Err(Error::EmptyResponse);
|
||||
}
|
||||
let mut ret = R2PipeNative {
|
||||
lib,
|
||||
r_core: std::sync::Mutex::new(r_core),
|
||||
r_core_cmd_str_handle,
|
||||
};
|
||||
ret.cmd(&format!("o {}", file))?;
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipe for R2PipeNative {
|
||||
fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
let r_core = *self.r_core.lock().unwrap();
|
||||
let cmd = std::ffi::CString::new(cmd).map_err(|_| Error::ArgumentMismatch)?;
|
||||
let res = (self.r_core_cmd_str_handle)(r_core, cmd.as_ptr());
|
||||
if res.is_null() {
|
||||
Err(Error::EmptyResponse)
|
||||
} else {
|
||||
Ok(unsafe { std::ffi::CStr::from_ptr(res).to_str()?.to_string() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for R2PipeNative {
|
||||
fn drop(&mut self) {
|
||||
let r_core = *self.r_core.lock().unwrap();
|
||||
if let Ok(r_core_free) =
|
||||
unsafe { self.lib.load_sym::<fn(*mut libc::c_void)>("r_core_free") }
|
||||
{
|
||||
r_core_free(r_core);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Pipe;
|
||||
use super::R2PipeNative;
|
||||
use crate::R2Pipe;
|
||||
|
||||
#[test]
|
||||
fn spawn_test() {
|
||||
let mut pipe = R2Pipe::spawn("/bin/ls", None).unwrap();
|
||||
assert_eq!(pipe.cmd("echo test").unwrap(), "test\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
let mut r2p = R2PipeNative::open("/bin/ls").unwrap();
|
||||
assert_eq!("a\n", r2p.cmd("echo a").unwrap());
|
||||
}
|
||||
}
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
// Copyright (c) 2015, The Radare Project. All rights reserved.
|
||||
// See the COPYING file at the top-level directory of this distribution.
|
||||
// Licensed under the BSD 3-Clause License:
|
||||
// <http://opensource.org/licenses/BSD-3-Clause>
|
||||
// This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Basic structs for JSON encoding and decoding.
|
||||
use rustc_serialize::{Decodable, Decoder, Encoder, Encodable};
|
||||
|
||||
macro_rules! impl_encode_decode {
|
||||
(for $st:ident, mapping { $($internal:ident: $external:expr),* }) => {
|
||||
impl Decodable for $st {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<$st, D::Error> {
|
||||
d.read_struct("root", 0, |dd| {
|
||||
let decoded = $st {
|
||||
$(
|
||||
$internal: dd.read_struct_field($external, 0, |d| Decodable::decode(d)).ok(),
|
||||
)*
|
||||
};
|
||||
Ok(decoded)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for $st {
|
||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
let mut i = 0;
|
||||
s.emit_struct("root", 1, |s| {
|
||||
$(
|
||||
try!(s.emit_struct_field($external, i, |s| self.$internal.encode(s)));
|
||||
i = 1;
|
||||
)*
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_encode_decode!(for LOpInfo, mapping { esil: "esil",
|
||||
offset: "offset",
|
||||
opcode: "opcode",
|
||||
optype: "type",
|
||||
size: "size" });
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct LOpInfo {
|
||||
pub esil: Option<String>,
|
||||
pub offset: Option<u64>,
|
||||
pub opcode: Option<String>,
|
||||
pub optype: Option<String>,
|
||||
pub size: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LFunctionInfo {
|
||||
pub addr: Option<u64>,
|
||||
pub name: Option<String>,
|
||||
pub ops: Option<Vec<LOpInfo>>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LRegInfo {
|
||||
pub alias_info: Vec<LAliasInfo>,
|
||||
pub reg_info: Vec<LRegProfile>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LAliasInfo {
|
||||
pub reg: String,
|
||||
pub role: u64,
|
||||
pub role_str: String,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LRegProfile {
|
||||
pub name: String,
|
||||
pub offset: usize,
|
||||
pub size: usize,
|
||||
pub type_str: String,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LFlagInfo {
|
||||
pub offset: u64,
|
||||
pub name: String,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LBinInfo {
|
||||
pub core: Option<LCoreInfo>,
|
||||
pub bin: Option<LBin>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LCoreInfo {
|
||||
pub file: Option<String>,
|
||||
pub size: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LBin {
|
||||
pub arch: Option<String>,
|
||||
}
|
||||
|
||||
impl_encode_decode!(for FunctionInfo, mapping { callrefs: "callrefs",
|
||||
calltype: "calltype",
|
||||
codexrefs: "codexrefs",
|
||||
datarefs: "datarefs",
|
||||
dataxrefs: "dataxrefs",
|
||||
name: "name",
|
||||
offset: "offset",
|
||||
realsz: "realsz",
|
||||
size: "size",
|
||||
ftype: "type" });
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct FunctionInfo {
|
||||
pub callrefs: Option<Vec<LCallInfo>>,
|
||||
pub calltype: Option<String>,
|
||||
pub codexrefs: Option<Vec<LCallInfo>>,
|
||||
pub datarefs: Option<Vec<u64>>,
|
||||
pub dataxrefs: Option<Vec<u64>>,
|
||||
pub name: Option<String>,
|
||||
pub offset: Option<u64>,
|
||||
pub realsz: Option<u64>,
|
||||
pub size: Option<u64>,
|
||||
pub ftype: Option<String>,
|
||||
}
|
||||
|
||||
impl_encode_decode!(for LCallInfo, mapping { addr: "addr", call_type: "type" });
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct LCallInfo {
|
||||
pub addr: Option<u64>,
|
||||
pub call_type: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
|
||||
pub struct LSectionInfo {
|
||||
pub flags: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub paddr: Option<u64>,
|
||||
pub size: Option<u64>,
|
||||
pub vaddr: Option<u64>,
|
||||
pub vsize: Option<u64>,
|
||||
}
|
||||
|
||||
impl_encode_decode!(for LStringInfo, mapping { length: "length",
|
||||
ordinal: "ordinal",
|
||||
paddr: "paddr",
|
||||
section: "section",
|
||||
size: "size",
|
||||
string: "string",
|
||||
vaddr: "vaddr",
|
||||
stype: "type" });
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct LStringInfo {
|
||||
pub length: Option<u64>,
|
||||
pub ordinal: Option<u64>,
|
||||
pub paddr: Option<u64>,
|
||||
pub section: Option<String>,
|
||||
pub size: Option<u64>,
|
||||
pub string: Option<String>,
|
||||
pub vaddr: Option<u64>,
|
||||
pub stype: Option<String>,
|
||||
}
|
||||
Reference in new issue
Block a user