1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Utility functions that provide the bulk of ~pwninit~ functionality

mod cpu_arch;
mod elf;
mod fetch_ld;
mod libc_deb;
mod libc_version;
pub mod opts;
mod pwninit;
mod set_exec;
mod solvepy;
mod unstrip_libc;
mod warn;

pub use crate::pwninit::run;
pub use crate::pwninit::Result;

use crate::elf::detect::is_elf;
pub use crate::fetch_ld::fetch_ld;
use crate::libc_version::LibcVersion;
use crate::opts::Opts;
pub use crate::set_exec::set_exec;
pub use crate::unstrip_libc::unstrip_libc;
use crate::warn::Warn;
use crate::warn::WarnResult;

use std::os::unix::ffi::OsStrExt;
use std::path::Path;

use colored::Colorize;
use ex::io;
use is_executable::IsExecutable;
use twoway::find_bytes;

/// Detect if `path` is the provided pwn binary
pub fn is_bin(path: &Path) -> elf::detect::Result<bool> {
    Ok(is_elf(path)? && !is_libc(path)? && !is_ld(path)?)
}

/// Does the filename of `path` contain `pattern`?
fn path_contains(path: &Path, pattern: &[u8]) -> bool {
    path.file_name()
        .map(|name| find_bytes(name.as_bytes(), pattern).is_some())
        .unwrap_or(false)
}

/// Detect if `path` is the provided libc
pub fn is_libc(path: &Path) -> elf::detect::Result<bool> {
    Ok(is_elf(path)? && path_contains(path, b"libc"))
}

/// Detect if `path` is the provided linker
pub fn is_ld(path: &Path) -> elf::detect::Result<bool> {
    Ok(is_elf(path)? && path_contains(path, b"ld-"))
}

/// Same as `fetch_ld()`, but doesn't do anything if an existing linker is
/// detected
fn maybe_fetch_ld(opts: &Opts, ver: &LibcVersion) -> fetch_ld::Result {
    match opts.ld {
        Some(_) => Ok(()),
        None => fetch_ld(ver),
    }
}

/// Top-level function for libc-dependent tasks
///   1. Download linker if not found
///   2. Unstrip libc if libc is stripped
fn visit_libc(opts: &Opts, libc: &Path) {
    let ver = match LibcVersion::detect(libc) {
        Ok(ver) => ver,
        Err(err) => {
            err.warn();
            return;
        }
    };
    maybe_fetch_ld(opts, &ver).warn();
    unstrip_libc(libc, &ver).warn();
}

/// Same as `visit_libc()`, but doesn't do anything if no libc is found
pub fn maybe_visit_libc(opts: &Opts) {
    if let Some(libc) = &opts.libc {
        visit_libc(opts, &libc)
    }
}

/// Set the binary executable
pub fn set_bin_exec(opts: &Opts) -> io::Result<()> {
    match &opts.bin {
        Some(bin) => {
            if !bin.is_executable() {
                println!(
                    "{}",
                    format!("setting {} executable", bin.display())
                        .bright_blue()
                        .bold()
                );
                set_exec(&bin)?;
            }
        }
        None => "binary not found".warn(),
    }

    Ok(())
}

/// Set the detected linker executable
pub fn set_ld_exec(opts: &Opts) -> io::Result<()> {
    match &opts.ld {
        Some(ld) if !ld.is_executable() => {
            println!(
                "{}",
                format!("setting {} executable", ld.display())
                    .green()
                    .bold()
            );
            set_exec(&ld)
        }
        _ => Ok(()),
    }
}