Mapping errors from Command::new("/bin/bash") arguments - rust

I am executing a bash command, for which I would like to catch an error when the move argument fails. Since there is one more command after move, my goal is to capture specifically whether the move operation got successfully executed to a status code and to break out of the entire Rust program.
Is there a way to accomplish this? I have provided below the source code.
let path : String = "/home/directory/".to_string();
let command = Command::new("bin/bash")
.arg("-c")
.arg("mv somefile1.txt /home/")
.arg("cp ~/somefile2.txt .")
.stdout(Stdio::piped())
.output();

bash -c doesn't accept two commands like that. You could try splitting it into two separate Commands:
Command::new("bash")
.arg("-c")
.arg("mv somefile1.txt /home/")
.status()?
.success() // bool
.then(|| ()) // convert bool to Option
.ok_or("mv failed")?; // convert Option to Result
Command::new("bash")
.arg("-c")
.arg("cp ~/somefile2.txt .")
.status()?
.success() // bool
.then(|| ()) // convert bool to Option
.ok_or("cp failed")?; // convert Option to Result
Or joining them into a single command with &&:
Command::new("bash")
.arg("-c")
.arg("mv somefile1.txt /home/ && cp ~/somefile2.txt .")
.status()?
.success() # bool
.then(|| ()) # convert bool to Option
.ok_or("failed")?; # convert Option to Result
Better yet, use native Rust functions:
mv → std::fs::rename
cp → std::fs::copy
~ → home::home_dir
This avoids the overhead of calling out to bash and is portable to non-Unix operating systems.
use std::fs;
use dirs::home_dir;
if let Some(home) = home_dir() {
fs::rename("somefile1.txt", home.join("somefile1.txt"))?;
fs::copy(home.join("somefile2.txt"), ".")?;
}

Related

Frida: spawn a Windows/Linux process with command-line arguments

When starting frida you can provide path to an executable that frida should execute and attach to it:
frida -l myscript.js process_to_spawn.exe
I have an executable needs to be started with additional parameters, otherwise it directly terminates. Is there a way to start a new executable and provide command-line arguments for the newly started process?
I already tried
frida -l myscript.js process_to_spawn.exe --argForProcess
frida -l myscript.js "process_to_spawn.exe --argForProcess"
but both variants don't work. Frida tries to interpret all arguments and thus can not pass arguments to the spawned process. And the second variant also does not work because frida is not able to find the executable to be started.
Is there a way to spawn an executable on the local OS (e.g. Windows or Linux) by frida and provide command-line arguments?
I can not attach to a running process because the functions I want to hook are only executed once directly after start-up, so I have to spawn the process using frida.
The problem seems to be that the argument starts with a -. For regular arguments that don't start with a - using the frida option -f works:
frida -l myscript.js -f process_to_spawn.exe argForProcess
But as I need the argument --argForProcess the only way I found was hooking the main method which processes the command-line arguments and modify the arguments before main is called.
The following code works on Windows 10 which seem to pass the arguments as wchar/"Unicode"/UTF-16 string. It changes argc and argv parameters of main from one argument (the executable itself) to two (the executable plus one argument).
let mainPointer = DebugSymbol.fromName("main").address;
Interceptor.attach(mainPointer, {
onEnter(args) {
// args[0] = int argc
// args[1] = wchar *argv[]
let myarg1 = Memory.allocUtf16String("Myexecutable.exe");
let myarg2 = Memory.allocUtf16String("--argumentX");
let newArgv = Memory.alloc(2 * Process.pointerSize); // allocate space for the two argument pointers
newArgv.writePointer(myarg1);
newArgv.add(Process.pointerSize).writePointer(myarg2);
// save all created memory blocks so they don't get garbage collected before main method is completed
this.myarg1 = myarg1;
this.myarg2 = myarg2;
this.newArgs = newArgv;
// Overwrite the argument counter and the argument char**
args[0] = ptr(2);
args[1] = newArgs;
console.log("main(" + args[0] + ", " + args[1].readPointer().readUtf16String() + ", " + args[1].add(Process.pointerSize).readPointer().readUtf16String() + ")");
}
});

What are some good ways to implement a read line or a sleep timer in Rust

I've finished my CLI but it exits too quickly for people to use it, Anyone know how would I go towards implementing code in my main.rs without breaking the compiler lol.
I was thinking of maybe a for loop that prints , read and execute and then start again. Or maybe a read line function so it stays up long enough to output the display.
Where would you guys implement that ? Thanks!
use structopt::StructOpt;
mod cli;
mod task;
use cli::{Action::*, CommandLineArgs};
use task::Task;
fn main() {
// Get the command-line arguments.
let CommandLineArgs {
action,
todo_file,
} = CommandLineArgs::from_args();
// Unpack the todo file.
let todo_file = todo_file.expect("Failed to find todo file");
// Perform the action.
match action {
Add { text } => task::add_task(todo_file,
Task::new(text)),
List => task::list_tasks(todo_file),
Done { position } =>
task::complete_task(todo_file, position),
}
.expect("Failed to perform action")
}
From the example, it seems you're getting the arguments from the command line. If you instead wanted the program to wait for a user to enter some text, interpret that text as a command and run it, and then wait for input again, until the user exits the program, then you'll probably want https://doc.rust-lang.org/std/io/struct.Stdin.html or possibly a higher level crate such as https://docs.rs/rustyline/8.0.0/rustyline/
If you were using stdin directly, you can call io::stdin().read_line() which will wait until the user has entered a line of text and pressed enter before the function will return. You can then parse that string to get the action to perform. The input/parsing/action code can be surrounded in a loop {} and one of the actions can be an exit command that will exit the loop.

Include long options without short options for help and version

I would like to include --help and --version long options without the -h and -V short options. Is this possible?
I'm using clap with yaml. The closest I've been able to come up with is to use hidden (unused) args that mask the short options.
# main.rs
use clap::{load_yaml, App};
fn main() {
let y = load_yaml!("cli.yaml");
let m = App::from_yaml(y).get_matches();
println!("_help {}", m.is_present("_help"));
println!("_version {}", m.is_present("_version"));
}
# Cargo.toml
[package]
name = "app"
version = "0.0.1"
edition = "2018"
[dependencies]
clap = {version = "~2.33.0", features = ["yaml"]}
# cli.yaml
name: app
version: 0.0.1
args:
- opt: { short: "o", long: "opt" }
- _help: { short: "h", hidden: true }
- _version: { short: "V", hidden: true }
$ cargo -q run -- --help
app 0.0.1
USAGE:
app [FLAGS]
FLAGS:
--help Prints help information
-o, --opt
--version Prints version information
$ cargo -q run -- -h
_help true
_version false
$ cargo -q run -- -o
_help false
_version false
$ cargo -q run -- -V
_help false
_version true
$ cargo -q run -- -x
error: Found argument '-x' which wasn't expected, or isn't valid in this context
USAGE:
app [FLAGS]
For more information try --help
This doesn't feel like a very clean approach. Is there another/better way?
Clap is designed more towards creating an args parser. So there aren't really that much functionality for getting and removing existing arguments.
If you simply want to rename "-h" to e.g. "-?", then you can do that with help_short("-?") (see also version_short().)
However, there are ways to work around it.
Assuming you're using e.g. clap = "2.33". Then similarly to what you're already doing, you can override/replace the help and version args, and in that way "remove" the short versions. (For brevity, I'll only include examples for help.)
You can of course keep it in cli.yaml if you want, but I'll add it to main.rs. In short, you want to add a new "help" arg and only give it a long version. It's important that you include help("Prints help information") as this is replacing the existing help arg, so if you don't it won't have the default help message for --help.
The downside to overring "help" is that you'd need to handle print_help() yourself.
use clap::{load_yaml, App, Arg};
fn main() {
let y = load_yaml!("cli.yaml");
let mut app = App::from_yaml(y)
.arg(
Arg::with_name("help")
.long("help")
.help("Prints help information"),
);
let m = app.clone().get_matches();
if m.is_present("help") {
app.print_help().unwrap();
// std::process::exit(0);
// or just
return;
}
}
However, if you're using clap = "3.0.0-beta.2" then that simplifies things, with the introduction of mut_arg(). Because that allows us to mutate the argument. Thereby, we no longer need to call print_help() ourselves.
use clap::{load_yaml, App, Arg};
fn main() {
let y = load_yaml!("cli.yaml");
let m = App::from(y)
.mut_arg("help", |h| {
Arg::new("help")
.long("help")
.about("Prints help information")
})
.get_matches();
}
Note that App::from_yaml() is now App::from(), while Arg::with_name() has become Arg::new(), and help() is now about().

How to get the exit code of a Bash script running from inside a Rust program?

I've been trying to wrap my head around a few simple implementations of systems programming involving the ability to call Bash from C and Rust. I was curious if there was a way to modify the following statement, in Rust specifically, to allow me to get a return value of 4 from a Bash script that runs in the following manner:
let status = Command::new("pathtoscript").status().expect("failed to execute process");
Rust String stuff confuses me initially, but any combination of actions that leads to status granting me access to the value of 4 being returned to the parent process would be great. Thank you so much in advance for helping me. I have checked the Rust documentation but I haven't found anything for getting things BACK to the parent process, only to the child process.
I should say it goes without saying for my application writing to a file and reading from that file is not sufficient or secure enough.
If you need the exit code 4 use status.code() :
use std::process::Command;
fn main() {
let status = Command::new("./script.sh")
.status()
.expect("failed to execute process");
println!("{}", status.code().unwrap()); // 4
}
My script.sh file:
#!/bin/bash
# Will exit with status of last command.
# exit $?
# echo $?
# Will return 4 to shell.
exit 4
And see this too:
use std::process::Command;
let status = Command::new("mkdir")
.arg("projects")
.status()
.expect("failed to execute mkdir");
match status.code() {
Some(code) => println!("Exited with status code: {}", code),
None => println!("Process terminated by signal")
}

Handling long probe paths in systemtap userspace scripts (variables)?

I am trying to instrument a user-space program using systemtap, Linux 4.2.0-42-generic #49~14.04.1-Ubuntu SMP; stap --version says: "Systemtap translator/driver (version 2.3/0.158, Debian version 2.3-1ubuntu1.4 (trusty))"
So I first tried getting a list of all callable functions, with:
stap -L 'process("/home/username/pathone/subpathtwo/subpaththree/subpathfour/subpathFive/subpathsix/subpathSeven/subpatheight/myprogramab").function("*").call' 2>&1 | tee /tmp/stap
This worked - however, note that the absolute path to my program is massive. So from the /tmp/stap file, I read some probes of interest, however, they are even longer, such as:
process("/home/username/pathone/subpathtwo/subpaththree/subpathfour/subpathFive/subpathsix/subpathSeven/subpatheight/myprogramab").function("DoSomething#/home/username/pathone/subpathtwo/subpaththree/subpathfour/subpathFive/src/BasicTestCodeFileInterface.cpp:201").call
... and as this is very difficult to read/unreadable for me, I wanted to do something to split this line into more readable pieces. The first thing I thought of was using variables, so I tried this test.stp script:
#!/usr/bin/env stap
global exepath = "/home/username/pathone/subpathtwo/subpaththree/subpathfour/subpathFive/subpathsix/subpathSeven/subpatheight/myprogramab"
global srcpath = "/home/username/pathone/subpathtwo/subpaththree/subpathfour/subpathFive/src"
probe begin {
printf("%s\n", exepath)
exit() # must have; else probe end runs only upon Ctrl-C
}
probe end {
newstr = "DoSomething#" . srcpath # concatenate strings
printf("%s\n", newstr)
}
This works, I can run sudo stap /path/to/test.stp, and I get the two strings printed.
However, when I try to use these strings in probes, they fail:
Writing a probe like this:
probe process(exepath).function("DoSomething#".srcpath."/BasicTestCodeFileInterface.cpp:201").call { ...
... fails with: parse error: expected literal string or number; saw: identifier 'exepath' ....
Trying to put the "process" part in a variable:
global tproc = process("/home/username/pathone/subpathtwo/subpaththree/subpathfour/subpathFive/subpathsix/subpathSeven/subpatheight/myprogramab")
... fails with parse error: expected literal string or number; saw: identifier 'process' ....
So, what options do I have, to somehow shorten the probe lines in a script?
Your best bet is probably to use macros:
#define part1 %( "/path/part/1" %)
#define part2 %( "/part/2" %)
probe process(#part1 #part2).function("...") { }
Note no explicit concatenation operator between the (macros that expand to) string literals. The parser will auto-concatenate them, just as in C.

Resources