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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
use std::{
    collections::HashMap,
    ffi::c_void,
    sync::{
        atomic::{AtomicU64, Ordering},
        Mutex,
    },
};

/// A reference to a given callback slot which can be used to install,
/// enable, disable, or otherwise reference, a closure-based callback for
/// PANDA plugin-to-plugin ("PPP") callbacks.
///
/// Since this is a reference to a callback slot and does not include storage
/// for the callback itself, it can be trivially copied, as well as included in
/// the callback itself (for the purposes of enabling/disabling).
///
/// In order to actually install the callback, you will need to import the trait
/// for the specific plugin whose callbacks you want to run. In the example below,
/// the [`ProcStartLinuxCallbacks`] trait provides the [`on_rec_auxv`] method in
/// order to add the callback.
///
/// [`ProcStartLinuxCallbacks`]: crate::plugins::proc_start_linux::ProcStartLinuxCallbacks
/// [`on_rec_auxv`]: crate::plugins::proc_start_linux::ProcStartLinuxCallbacks::on_rec_auxv
///
/// ## Example
///
/// ```
/// use panda::plugins::proc_start_linux::ProcStartLinuxCallbacks;
/// use panda::PppCallback;
/// use panda::prelude::*;
///
/// PppCallback::new().on_rec_auxv(|_, _, auxv| {
///     dbg!(auxv);
/// });
///
/// Panda::new().generic("x86_64").replay("test").run();
/// ```
///
/// The above installs a callback to print out the contents of the auxillary vector
/// using [`dbg`] whenever a new process is spawned in the guest.
///
/// Example output:
///
/// ```no_run
/// ...
/// [panda-rs/examples/closures.rs:18] auxv = AuxvValues {
///     argc: 3,
///     argv_ptr_ptr: 0x7fffffffebb8,
///     arg_ptr: [
///         0x7fffffffede6,
///         0x7fffffffedeb,
///         0x7fffffffedee,
///     ],
///     argv: [
///         "bash",
///         "-c",
///         "echo test2",
///     ],
///     envc: 20,
///     env_ptr_ptr: 0x7fffffffebd8,
///     env_ptr: [
///         0x7fffffffedf9,
///         0x7fffffffee04,
///         // ...
///         0x7fffffffefc2,
///         0x7fffffffefe2,
///     ],
///     envp: [
///         "LS_COLORS=",
///         "LESSCLOSE=/usr/bin/lesspipe %s %s",
///         "LANG=C.UTwcap2: 0,F-8",
///         "INVOCATION_ID=0b2d5ea4eb39435388bf53e507047b2f",
///         "XDG_SESSION_ID=1",
///         "HUSHLOGIN=FALSE",
///         "USER=root",
///         "PWD=/root",
///         "HOME=/root",
///         "JOURNAL_STREAM=9:16757",
///         "XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop",
///         "MAIL=/var/mail/root",
///         "SHELL=/bin/bash",
///         "TERM=vt220",
///         "SHLVL=1",
///         "LOGNAME=root",
///         "XDG_RUNTIME_DIR=/run/user/0",
///         "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
///         "LESSOPEN=| /usr/bin/lesspipe %s",
///         "_=/bin/bash",
///     ],
///     execfn_ptr: 0x7fffffffefee,
///     execfn: "/bin/bash",
///     phdr: 0x555555554040,
///     entry: 0x555555585520,
///     ehdr: 0x7ffff7ffa000,
///     // ...
/// }
/// Replay completed successfully
/// Exiting cpu_handle_exception loop
/// ```
///
/// ## Note
///
/// Callback closures must have a `'static` lifetime in order to live past the end of the
/// function. This means that the only references a callback can include are references
/// to static variables or leaked objects on the heap (See [`Box::leak`] for more info).
///
/// If you'd like to reference shared data without leaking, this can be accomplished via
/// reference counting. See [`Arc`] for more info. If you want to capture data owned
/// by the current function without sharing it, you can mark your closure as `move` in
/// order to move all the variables you capture into your closure. (Such as in the above
/// example, where `count` is moved into the closure for modification)
///
/// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PppCallback(pub(crate) u64);

static CURRENT_CALLBACK_ID: AtomicU64 = AtomicU64::new(0);

impl PppCallback {
    /// Create a new callback slot which can then be used to install or modify
    /// a given callback.
    pub fn new() -> Self {
        Self(CURRENT_CALLBACK_ID.fetch_add(1, Ordering::SeqCst))
    }

    /// Enable the callback assigned to the given slot, if any.
    pub fn enable(&self) {
        let mut callbacks = CALLBACKS.lock().unwrap();
        if let Some(callback) = callbacks.get_mut(&self.0) {
            if !callback.is_enabled {
                unsafe {
                    (callback.enable)(callback.closure_ref);
                }
                callback.is_enabled = true;
            }
        }
    }

    /// Disable the callback assigned to the given slot, if any.
    pub fn disable(&self) {
        let mut callbacks = CALLBACKS.lock().unwrap();

        if let Some(callback) = callbacks.get_mut(&self.0) {
            if callback.is_enabled {
                unsafe {
                    (callback.disable)(callback.closure_ref);
                }
                callback.is_enabled = false;
            }
        }
    }
}

lazy_static::lazy_static! {
    static ref CALLBACKS: Mutex<HashMap<u64, InternalPppClosureCallback>> = Mutex::new(HashMap::new());
}

#[doc(hidden)]
pub struct InternalPppClosureCallback {
    pub closure_ref: *mut c_void,
    pub enable: unsafe fn(*mut c_void),
    pub disable: unsafe fn(*mut c_void),
    pub drop_fn: unsafe fn(*mut c_void),
    pub is_enabled: bool,
}

unsafe impl Sync for InternalPppClosureCallback {}
unsafe impl Send for InternalPppClosureCallback {}

#[doc(hidden)]
pub unsafe fn __internal_install_ppp_closure_callback(
    id: u64,
    mut callback: InternalPppClosureCallback,
) {
    (callback.enable)(callback.closure_ref);
    callback.is_enabled = true;

    let mut callbacks = CALLBACKS.lock().unwrap();
    callbacks.insert(id, callback);
}