mirror of
https://github.com/radareorg/r2pipe.rs.git
synced 2026-11-09 04:29:32 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c72b4cba60 | ||
|
|
391641f2ff | ||
|
|
1208b28183 | ||
|
|
522a9f7a4f | ||
|
|
89db8759ce | ||
|
|
a66ddad123 | ||
|
|
50c689f4bc | ||
|
|
c0260db239 | ||
|
|
14079c97e9 | ||
|
|
43b68268ca | ||
|
|
8cb8d248a8 | ||
|
|
e67a685a5b | ||
|
|
08335660d4 |
No files matched your search
@@ -13,6 +13,7 @@ jobs:
|
||||
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:
|
||||
@@ -35,7 +36,3 @@ jobs:
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
|
||||
|
||||
|
||||
+6
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "r2pipe"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
authors = [
|
||||
"pancake <[email protected]>",
|
||||
"sushant94 <[email protected]>"
|
||||
@@ -20,8 +20,12 @@ readme = "README.md"
|
||||
# License information
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
docsrs = []
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.81"
|
||||
libloading = "0.8"
|
||||
libc = "0.2"
|
||||
serde = "1.0.118"
|
||||
serde_json = "1.0.60"
|
||||
serde_derive = "1.0.118"
|
||||
|
||||
@@ -10,7 +10,7 @@ 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**
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -40,4 +40,8 @@ pub enum Error {
|
||||
/// 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),
|
||||
}
|
||||
+2
-1
@@ -50,11 +50,12 @@
|
||||
//! 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(doc_cfg, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![doc(html_root_url = "https://radare.github.io/r2pipe.rs/")]
|
||||
|
||||
#[macro_use]
|
||||
pub mod r2pipe;
|
||||
mod dlfcn;
|
||||
pub mod r2;
|
||||
|
||||
mod error;
|
||||
|
||||
@@ -70,7 +70,7 @@ impl R2 {
|
||||
}
|
||||
|
||||
pub fn recv_json(&mut self) -> Result<Value> {
|
||||
let mut res = self.recv().replace("\n", "");
|
||||
let mut res = self.recv().replace('\n', "");
|
||||
if res.is_empty() {
|
||||
res = "{}".to_owned();
|
||||
}
|
||||
|
||||
+159
-113
@@ -2,6 +2,7 @@
|
||||
//!
|
||||
//! Please check crate level documentation for more details and example.
|
||||
|
||||
use crate::dlfcn;
|
||||
use crate::{Error, Result};
|
||||
|
||||
use std::env;
|
||||
@@ -52,28 +53,49 @@ pub struct R2PipeThread {
|
||||
pub handle: thread::JoinHandle<Result<()>>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct R2PipeSpawnOptions {
|
||||
pub exepath: String,
|
||||
pub args: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl Default for R2PipeSpawnOptions {
|
||||
fn default() -> Self {
|
||||
let exepath = if cfg!(windows) { "radare2.exe" } else { "r2" };
|
||||
|
||||
R2PipeSpawnOptions {
|
||||
exepath: exepath.to_string(),
|
||||
args: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides abstraction between the three invocation methods.
|
||||
pub enum R2Pipe {
|
||||
Pipe(R2PipeSpawn),
|
||||
Lang(R2PipeLang),
|
||||
Tcp(R2PipeTcp),
|
||||
Http(R2PipeHttp),
|
||||
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 atoi(k: &str) -> i32 {
|
||||
k.parse::<i32>().unwrap_or(-1)
|
||||
}
|
||||
|
||||
fn getenv(k: &str) -> i32 {
|
||||
fn getenv(k: &str) -> Option<i32> {
|
||||
match env::var(k) {
|
||||
Ok(val) => atoi(&val),
|
||||
Err(_) => -1,
|
||||
Ok(val) => val.parse::<i32>().ok(),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,24 +110,27 @@ fn process_result(res: Vec<u8>) -> Result<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 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;
|
||||
@@ -120,47 +145,37 @@ impl R2Pipe {
|
||||
write: File::from_raw_fd(d_out),
|
||||
}
|
||||
};
|
||||
Ok(R2Pipe::Lang(res))
|
||||
Ok(R2Pipe(Box::new(res)))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn open() -> Result<R2Pipe> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
match *self {
|
||||
R2Pipe::Pipe(ref mut x) => x.cmd(cmd.trim()),
|
||||
R2Pipe::Lang(ref mut x) => x.cmd(cmd.trim()),
|
||||
R2Pipe::Tcp(ref mut x) => x.cmd(cmd.trim()),
|
||||
R2Pipe::Http(ref mut x) => x.cmd(cmd.trim()),
|
||||
}
|
||||
self.0.cmd(cmd.trim())
|
||||
}
|
||||
|
||||
pub fn cmdj(&mut self, cmd: &str) -> Result<Value> {
|
||||
match *self {
|
||||
R2Pipe::Pipe(ref mut x) => x.cmdj(cmd.trim()),
|
||||
R2Pipe::Lang(ref mut x) => x.cmdj(cmd.trim()),
|
||||
R2Pipe::Tcp(ref mut x) => x.cmdj(cmd.trim()),
|
||||
R2Pipe::Http(ref mut x) => x.cmdj(cmd.trim()),
|
||||
}
|
||||
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(),
|
||||
R2Pipe::Tcp(ref mut x) => x.close(),
|
||||
R2Pipe::Http(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))
|
||||
}
|
||||
|
||||
@@ -173,19 +188,13 @@ impl R2Pipe {
|
||||
}
|
||||
|
||||
/// Creates a new R2PipeSpawn.
|
||||
pub fn spawn<T: AsRef<str>>(name: T, opts: Option<R2PipeSpawnOptions>) -> Result<R2Pipe> {
|
||||
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 mut child = Command::new(exepath)
|
||||
.arg("-q0")
|
||||
@@ -209,7 +218,7 @@ impl R2Pipe {
|
||||
child: Some(child),
|
||||
};
|
||||
|
||||
Ok(R2Pipe::Pipe(res))
|
||||
Ok(R2Pipe(Box::new(res)))
|
||||
}
|
||||
|
||||
/// Creates a new R2PipeTcp
|
||||
@@ -217,14 +226,14 @@ impl R2Pipe {
|
||||
// use `connect` to figure out which socket address works
|
||||
let stream = TcpStream::connect(addr)?;
|
||||
let addr = stream.peer_addr()?;
|
||||
Ok(R2Pipe::Tcp(R2PipeTcp { socket_addr: addr }))
|
||||
Ok(R2Pipe(Box::new(R2PipeTcp { socket_addr: addr })))
|
||||
}
|
||||
|
||||
/// Creates a new R2PipeHttp
|
||||
pub fn http(host: &str) -> R2Pipe {
|
||||
R2Pipe::Http(R2PipeHttp {
|
||||
R2Pipe(Box::new(R2PipeHttp {
|
||||
host: host.to_string(),
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
/// Creates new pipe threads
|
||||
@@ -290,8 +299,8 @@ impl R2PipeThread {
|
||||
}
|
||||
}
|
||||
|
||||
impl R2PipeSpawn {
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
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())?;
|
||||
|
||||
@@ -300,23 +309,7 @@ impl R2PipeSpawn {
|
||||
process_result(res)
|
||||
}
|
||||
|
||||
pub 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)?)
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
fn close(&mut self) {
|
||||
let _ = self.cmd("q!");
|
||||
if let Some(child) = &mut self.child {
|
||||
let _ = child.wait();
|
||||
@@ -324,28 +317,27 @@ impl R2PipeSpawn {
|
||||
}
|
||||
}
|
||||
|
||||
impl R2PipeLang {
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
impl R2PipeSpawn {
|
||||
/// 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();
|
||||
self.read.read_until(0u8, &mut res)?;
|
||||
process_result(res)
|
||||
}
|
||||
|
||||
pub fn cmdj(&mut self, cmd: &str) -> Result<Value> {
|
||||
let res = self.cmd(cmd)?;
|
||||
|
||||
Ok(serde_json::from_str(&res)?)
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
// self.read.close();
|
||||
// self.write.close();
|
||||
}
|
||||
}
|
||||
|
||||
impl R2PipeHttp {
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
impl Pipe for R2PipeHttp {
|
||||
fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
let host = if self.host.starts_with("http://") {
|
||||
&self.host[7..]
|
||||
} else {
|
||||
@@ -366,17 +358,10 @@ impl R2PipeHttp {
|
||||
|
||||
Ok(str::from_utf8(&resp[index..]).map(|s| s.to_string())?)
|
||||
}
|
||||
|
||||
pub fn cmdj(&mut self, cmd: &str) -> Result<Value> {
|
||||
let res = self.cmd(cmd)?;
|
||||
Ok(serde_json::from_str(&res)?)
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {}
|
||||
}
|
||||
|
||||
impl R2PipeTcp {
|
||||
pub fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
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();
|
||||
@@ -384,11 +369,72 @@ impl R2PipeTcp {
|
||||
res.push(0);
|
||||
process_result(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmdj(&mut self, cmd: &str) -> Result<Value> {
|
||||
let res = self.cmd(cmd)?;
|
||||
Ok(serde_json::from_str(&res)?)
|
||||
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,
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {}
|
||||
#[test]
|
||||
fn native_test() {
|
||||
let mut r2p = R2PipeNative::open("/bin/ls").unwrap();
|
||||
assert_eq!("a\n", r2p.cmd("echo a").unwrap());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user