panda_macros/
lib.rs

1use darling::{FromDeriveInput, FromField};
2use proc_macro::TokenStream;
3use quote::{quote, ToTokens};
4use std::iter;
5
6/// (**Required** Callback) Called when the plugin is being uninitialized
7///
8///### Example
9///
10/// ```rust
11///use panda::PluginHandle;
12///
13/// #[panda::init]
14/// fn start(_: &mut PluginHandle) {
15///     println!("Plugin started up!");
16/// }
17/// ```
18///
19/// ### Allowed Return Types
20///
21/// - `()`: plugin load always succeeds
22/// - `bool`: plugin load is aborted if `false` is returned
23/// - `Result<T, E>`: plugin load is aborted on `Err`, prints the error beforehand
24/// - `i32`: plugin load succeeds if `0` is returned, otherwise abort
25#[proc_macro_attribute]
26pub fn init(_: TokenStream, function: TokenStream) -> TokenStream {
27    let func = syn::parse_macro_input!(function as syn::ItemFn);
28
29    let args = if func.sig.inputs.is_empty() {
30        None
31    } else {
32        Some(quote!(unsafe { &mut *plugin }))
33    };
34
35    let func_name = &func.sig.ident;
36
37    quote!(
38        mod __panda_internal {
39            use super::*;
40
41            #[no_mangle]
42            pub unsafe extern "C" fn init_plugin(plugin: *mut ::panda::PluginHandle) -> bool {
43                ::panda::set_plugin_ref(plugin);
44
45                for cb in ::panda::inventory::iter::<::panda::InternalCallback> {
46                    ::panda::sys::panda_register_callback(plugin as _, cb.cb_type, ::core::mem::transmute(cb.fn_pointer));
47                }
48
49                for cb in ::panda::inventory::iter::<::panda::PPPCallbackSetup> {
50                    cb.0();
51                }
52
53                ::panda::InitReturn::into_init_bool(#func_name(#args))
54            }
55
56            #[no_mangle]
57             pub unsafe extern "C" fn uninit_plugin(plugin: *mut ::panda::PluginHandle) {
58                for cb in ::panda::inventory::iter::<::panda::UninitCallback> {
59                    cb.0(unsafe { &mut *plugin });
60                }
61            }
62        }
63
64        #func
65    ).into()
66}
67
68/// (Callback) Called when the plugin is being uninitialized
69#[proc_macro_attribute]
70pub fn uninit(_: TokenStream, function: TokenStream) -> TokenStream {
71    let func = syn::parse_macro_input!(function as syn::ItemFn);
72    let func_name = &func.sig.ident;
73
74    quote!(
75        ::panda::inventory::submit! {
76            #![crate = ::panda]
77            ::panda::UninitCallback(#func_name)
78        }
79
80        #func
81    )
82    .into()
83}
84
85/// An attribute to declare a function for hooking using the PANDA 'hooks' plugin,
86/// enabling the ability to add callbacks for when a specifc instruction is hit,
87/// with control over the address space, kernel mode, and callback type to use.
88///
89/// ## Example
90///
91/// ```
92/// use panda::plugins::proc_start_linux::AuxvValues;
93/// use panda::plugins::hooks::Hook;
94/// use panda::prelude::*;
95///
96/// #[panda::hook]
97/// fn entry_hook(_: &mut CPUState, _: &mut TranslationBlock, _: u8, hook: &mut Hook) {
98///     println!("\n\nHit entry hook!\n");
99///
100///     // only run hook once
101///     hook.enabled = false;
102/// }
103///
104/// #[panda::on_rec_auxv]
105/// fn on_proc_start(_: &mut CPUState, _: &mut TranslationBlock, auxv: &AuxvValues) {
106///     // when a process starts, hook the entrypoint
107///     entry_hook::hook()
108///         .after_block_exec()
109///         .at_addr(auxv.entry)
110/// }
111///
112/// Panda::new()
113///     .generic("x86_64")
114///     .replay("test")
115///     .run();
116/// ```
117///
118/// ## Supported Callback Types
119///
120/// ### Standard callbacks
121///
122/// These callbacks take the form of:
123///
124/// ```
125/// #[panda::hook]
126/// fn my_callback(cpu: &mut CPUState, tb: &mut TranslationBlock, hook: &mut Hook);
127/// ```
128///
129/// |         Callback        | Info |
130/// |:------------------------|------|
131/// | `before_tcg_codegen`    | Callback at the start of the tcg IR being generated |
132/// | `after_block_translate` | Callback after the block the hooked instruction is in gets translated |
133/// | `before_block_exec`     | Callback before the block the given instruction is in gets run |
134/// | `start_block_exec`      | Callback at the first instruction in the block the instruction is in |
135/// | `end_block_exec`        | Callback after the last instruction in the block the hooked instruction is in |
136///
137/// ### Other Callbacks
138///
139/// These callbacks each have their own unique required function signature.
140///
141/// |          Callback        | Required Signature | Info |
142/// |:-------------------------|--------------------|------|
143/// | `before_block_translate` | `fn(cpu: &mut CPUState, pc: target_ptr_t, hook: &mut Hook)` | Callback that runs before the block the hooked instruction is translated to tcg |
144/// | `after_block_exec`       | `fn(cpu: &mut CPUState, tb: &mut TranslationBlock, exitCode: u8, hook: &mut Hook)` | Callback that runs after the given block is executed |
145/// | `before_block_exec_invalidate_opt` | `fn(env: &mut CPUState, tb: &mut TranslationBlock, hook: &mut Hook) -> bool` | Callback on translate to provide the option to invalidate the block the hooked instruction is generated in |
146#[proc_macro_attribute]
147pub fn hook(_: TokenStream, func: TokenStream) -> TokenStream {
148    let mut function = syn::parse_macro_input!(func as syn::ItemFn);
149    function.sig.abi = Some(syn::parse_quote!(extern "C"));
150    let vis = &function.vis;
151    let func = &function.sig.ident;
152    let cfgs = crate::get_cfg_attrs(&function);
153
154    let args = &function.sig.inputs;
155    let ret = &function.sig.output;
156    let ty: syn::Type = syn::parse_quote! { extern "C" fn(  #args ) #ret };
157
158    quote!(
159        #( #cfgs )*
160        #vis mod #func {
161            use super::*;
162
163            pub fn hook() -> <#ty as ::panda::plugins::hooks::IntoHookBuilder>::BuilderType {
164                <#ty as ::panda::plugins::hooks::IntoHookBuilder>::hook(#func)
165            }
166        }
167
168        #function
169    )
170    .into()
171}
172
173mod guest_type;
174use guest_type::GuestTypeInput;
175
176#[proc_macro_derive(GuestType)]
177pub fn derive_guest_type(input: TokenStream) -> TokenStream {
178    let input = syn::parse_macro_input!(input as syn::DeriveInput);
179    match GuestTypeInput::from_derive_input(&input) {
180        Ok(input) => input.to_tokens().into(),
181        Err(err) => err.write_errors().into(),
182    }
183}
184
185mod osi_type;
186use osi_type::OsiTypeInput;
187
188#[proc_macro_derive(OsiType, attributes(osi))]
189pub fn derive_osi_type(input: TokenStream) -> TokenStream {
190    let input = syn::parse_macro_input!(input as syn::DeriveInput);
191    match OsiTypeInput::from_derive_input(&input) {
192        Ok(input) => input.to_tokens().into(),
193        Err(err) => err.write_errors().into(),
194    }
195}
196
197mod osi_static;
198use osi_static::OsiStatics;
199
200#[proc_macro]
201pub fn osi_static(input: TokenStream) -> TokenStream {
202    let input = syn::parse_macro_input!(input as OsiStatics);
203
204    input.into_token_stream().into()
205}
206
207#[proc_macro_attribute]
208pub fn channel_recv(_: TokenStream, func: TokenStream) -> TokenStream {
209    let mut func = syn::parse_macro_input!(func as syn::ItemFn);
210
211    let name = std::mem::replace(&mut func.sig.ident, syn::parse_quote!(inner));
212
213    quote!(
214        extern "C" fn #name(channel_id: u32, data: *const u8, len: usize) {
215            #func
216
217            let msg = unsafe {
218                ::panda::plugins::guest_plugin_manager::FromChannelMessage::from_channel_message(
219                    data, len
220                )
221            };
222
223            match msg {
224                Ok(msg) => {
225                    inner(
226                        channel_id,
227                        msg
228                    );
229                },
230                Err(err) => {
231                    println!("Warning: could not parse channel message, {}", err);
232                }
233            }
234        }
235    )
236    .into()
237}
238
239// derive PandaArgs
240include!("panda_args.rs");
241
242struct Idents(syn::Ident, syn::Ident);
243
244impl syn::parse::Parse for Idents {
245    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
246        Ok(Idents(input.parse()?, input.parse()?))
247    }
248}
249
250fn get_cfg_attrs(func: &syn::ItemFn) -> Vec<syn::Attribute> {
251    func.attrs
252        .iter()
253        .filter(|attr| attr.path.get_ident().map(|x| *x == "cfg").unwrap_or(false))
254        .cloned()
255        .collect()
256}
257
258macro_rules! define_callback_attributes {
259    ($(
260        $($doc:literal)*
261        ($attr_name:ident, $const_name:ident, ($($arg_name:ident : $arg:ty),*) $(-> $ret:ty)?)
262    ),*) => {
263        $(
264            doc_comment::doc_comment!{
265                concat!(
266                    "(Callback) ",
267                    $($doc, "\n",)*
268                    "\n\nCallback arguments: (",
269                    $("`", stringify!($arg), "`, ",)*
270                    ")\n### Example\n```rust\nuse panda::prelude::*;\n\n#[panda::",
271                    stringify!($attr_name),
272                    "]\nfn callback(",
273                    $("_: ", stringify!($arg), ", ", )* ")",
274                    $(" -> ", stringify!($ret),)?
275                    " {\n    // do stuff\n}\n```"),
276                #[proc_macro_attribute]
277                pub fn $attr_name(_: TokenStream, function: TokenStream) -> TokenStream {
278                    let mut function = syn::parse_macro_input!(function as syn::ItemFn);
279                    function.sig.abi = Some(syn::parse_quote!(extern "C"));
280                    let vis = &function.vis;
281                    let func = &function.sig.ident;
282                    let cfgs = crate::get_cfg_attrs(&function);
283
284                    quote!(
285                        #(
286                            #cfgs
287                         )*
288                        const _: fn() = || {
289                            use ::panda::sys::*;
290                            fn assert_callback_arg_types(_ : extern "C" fn($($arg),*) $(-> $ret)?) {}
291
292                            assert_callback_arg_types(#func);
293                        };
294
295                        ::panda::inventory::submit! {
296                            #![crate = ::panda]
297                            ::panda::InternalCallback::new(
298                                ::panda::sys::$const_name,
299                                #func as *const ()
300                            )
301                        }
302
303                        #vis mod #func {
304                            pub fn enable() {
305                                unsafe {
306                                    ::panda::sys::panda_enable_callback(
307                                        ::panda::sys::panda_get_plugin_by_name(
308                                            ::std::concat!(
309                                                ::std::env!("CARGO_PKG_NAME"),
310                                                "\0"
311                                            ).as_ptr() as _
312                                        ),
313                                        ::panda::sys::$const_name,
314                                        ::std::mem::transmute(super::#func as *const ())
315                                    );
316                                }
317                            }
318
319                            pub fn disable() {
320                                unsafe {
321                                    ::panda::sys::panda_disable_callback(
322                                        ::panda::sys::panda_get_plugin_by_name(
323                                            ::std::concat!(
324                                                ::std::env!("CARGO_PKG_NAME"),
325                                                "\0"
326                                            ).as_ptr() as _
327                                        ),
328                                        ::panda::sys::$const_name,
329                                        ::std::mem::transmute(super::#func as *const ())
330                                    );
331                                }
332                            }
333                        }
334
335                        #function
336                    ).into()
337                }
338            }
339        )*
340
341        #[proc_macro]
342        pub fn define_closure_callbacks(_: TokenStream) -> TokenStream {
343            quote!(
344                impl Callback {
345                    $(
346                        /// Installs the given callback, assigning it to this `Callback`'s
347                        /// slot. Any callbacks previously stored in that slot will be
348                        /// freed.
349                        ///
350                        $(
351                            #[doc = $doc]
352                        )*
353                        pub fn $attr_name<F>(self, callback: F)
354                            where F: FnMut($($arg),*) $(-> $ret)? + 'static
355                        {
356                            unsafe extern "C" fn trampoline(context: *mut c_void, $($arg_name: $arg),*) $(-> $ret)? {
357                                let closure: &mut &mut (
358                                    dyn FnMut($($arg),*) $(-> $ret)?
359                                ) = unsafe { std::mem::transmute(
360                                    context as *mut *mut c_void
361                                )};
362                                closure($($arg_name),*)
363                            }
364
365                            unsafe fn drop_fn(this: *mut *mut c_void) {
366                                let _: Box<Box<dyn FnMut($($arg),*) $(-> $ret)?>> = unsafe {
367                                    std::mem::transmute(this)
368                                };
369                            }
370
371                            let closure_ref: *mut *mut c_void = unsafe {
372                                let x: Box<Box<
373                                    dyn FnMut($($arg),*) $(-> $ret)?
374                                >> = Box::new(
375                                    Box::new(callback) as Box<_>
376                                );
377
378                                std::mem::transmute(x)
379                            };
380
381                            install_closure_callback(self.0, ClosureCallback {
382                                closure_ref,
383                                drop_fn,
384                                trampoline: sys::panda_cb_with_context {
385                                    $attr_name: Some(unsafe {
386                                        std::mem::transmute(trampoline as *const c_void)
387                                    })
388                                },
389                                cb_kind: sys::$const_name,
390                            });
391                        }
392                    )*
393                }
394            ).into()
395        }
396    }
397}
398
399#[cfg(not(feature = "ppc"))]
400macro_rules! define_syscalls_callbacks {
401    ($(
402        $($doc:literal)*
403        (
404            $attr_name:ident,
405            $cb_name:ident,
406            $syscall_name:ident,
407            $before_or_after:literal,
408            ($($arg_name:ident : $arg:ty),* $(,)?)
409        )
410    ),* $(,)?) => {
411        $(
412            doc_comment::doc_comment!{
413                concat!(
414                    "(Callback) A callback that runs ",
415                    $before_or_after,
416                    " the ",
417                    stringify!($syscall_name),
418                    " syscall runs.\n\nCallback arguments: (",
419                    $("`", stringify!($arg), "`,",)*
420                    ")\n### Example\n```rust\nuse panda::prelude::*;\n\n#[panda::on_sys::",
421                    stringify!($syscall_name),
422                    "_enter",
423                    "]\nfn callback(",
424                    $("_: ", stringify!($arg), ", ",)*
425                    ") {\n    // do stuff\n}\n```"
426                ),
427                #[proc_macro_attribute]
428                pub fn $attr_name(_: TokenStream, function: TokenStream) -> TokenStream {
429                    let mut function = syn::parse_macro_input!(function as syn::ItemFn);
430                    function.sig.abi = Some(syn::parse_quote!(extern "C"));
431                    let func = &function.sig.ident;
432                    let cfgs = crate::get_cfg_attrs(&function);
433
434                    quote!(
435                        #(
436                            #cfgs
437                         )*
438                        ::panda::inventory::submit! {
439                            #![crate = ::panda]
440                            ::panda::PPPCallbackSetup(
441                                || {
442                                    ::panda::plugins::syscalls2::SYSCALLS.$cb_name(#func);
443                                }
444                            )
445                        }
446
447                        #function
448                    ).into()
449                }
450            }
451        )*
452
453        /// For internal use only
454        #[proc_macro]
455        #[doc(hidden)]
456        pub fn generate_syscalls_callbacks(_: TokenStream) -> TokenStream {
457            quote!(
458                plugin_import!{
459                    static SYSCALLS: Syscalls2 = extern "syscalls2" {
460                        callbacks {
461                            $(
462                                fn $attr_name(
463                                    $($arg_name : $arg),*
464                                );
465                            )*
466
467                            fn on_all_sys_enter(cpu: &mut CPUState, pc: SyscallPc, callno: target_ulong);
468                            fn on_all_sys_return(cpu: &mut CPUState, pc: SyscallPc, callno: target_ulong);
469                        }
470                    };
471                }
472            ).into()
473        }
474
475        /// Callback that runs when any syscall is entered
476        ///
477        /// ### Args
478        ///
479        /// * `cpu` - a reference to the currently executing [`CPUState`] object
480        /// * `pc` - the current program counter of the system when the syscall callback is hit
481        /// * `callno` - the syscall number called
482        ///
483        /// ### Example
484        /// ```rust
485        /// use panda::prelude::*;
486        ///
487        /// #[panda::on_all_sys_enter]
488        /// fn callback(cpu: &mut CPUState, pc: target_ulong, callno: target_ulong) {
489        ///     // do stuff
490        /// }
491        /// ```
492        ///
493        /// [`CPUState`]: https://docs.rs/panda-re/*/panda/prelude/struct.CPUState.html
494        #[proc_macro_attribute]
495        pub fn on_all_sys_enter(_: TokenStream, function: TokenStream) -> TokenStream {
496            let mut function = syn::parse_macro_input!(function as syn::ItemFn);
497            function.sig.abi = Some(syn::parse_quote!(extern "C"));
498            let func = &function.sig.ident;
499            let cfgs = crate::get_cfg_attrs(&function);
500
501            quote!(
502                #(
503                    #cfgs
504                 )*
505                ::panda::inventory::submit! {
506                    #![crate = ::panda]
507                    ::panda::PPPCallbackSetup(
508                        || {
509                            ::panda::plugins::syscalls2::SYSCALLS.add_callback_on_all_sys_enter(#func);
510                        }
511                    )
512                }
513
514                #function
515            ).into()
516        }
517
518        /// Callback that runs when any syscall returns.
519        ///
520        /// Note that some syscalls do not return and thus will not have this callback run.
521        ///
522        /// ### Args
523        ///
524        /// * `cpu` - a reference to the currently executing [`CPUState`] object
525        /// * `pc` - the current program counter of the system when the syscall callback is hit
526        /// * `callno` - the syscall number called
527        ///
528        /// ### Example
529        /// ```rust
530        /// use panda::prelude::*;
531        ///
532        /// #[panda::on_all_sys_return]
533        /// fn callback(cpu: &mut CPUState, pc: target_ulong, callno: target_ulong) {
534        ///     // do stuff
535        /// }
536        /// ```
537        ///
538        /// [`CPUState`]: https://docs.rs/panda-re/*/panda/prelude/struct.CPUState.html
539        #[proc_macro_attribute]
540        pub fn on_all_sys_return(_: TokenStream, function: TokenStream) -> TokenStream {
541            let mut function = syn::parse_macro_input!(function as syn::ItemFn);
542            function.sig.abi = Some(syn::parse_quote!(extern "C"));
543            let func = &function.sig.ident;
544            let cfgs = crate::get_cfg_attrs(&function);
545
546            quote!(
547                #(
548                    #cfgs
549                 )*
550                ::panda::inventory::submit! {
551                    #![crate = ::panda]
552                    ::panda::PPPCallbackSetup(
553                        || {
554                            ::panda::plugins::syscalls2::SYSCALLS.add_callback_on_all_sys_return(#func);
555                        }
556                    )
557                }
558
559                #function
560            ).into()
561        }
562    };
563}
564
565/// (Callback) Runs when proc_start_linux recieves the [`AuxvValues`] for a given process.
566///
567/// Can be treated as a "on program start" callback, but one which provides a lot of
568/// info about the contents of the initial program state and how it is being loaded.
569/// The state at time of callback is before the C runtime is initialized, and before
570/// the entrypoint is jumped to.
571///
572/// See [`AuxvValues`] to get a better understanding of the values provided.
573///
574/// ### Args
575///
576/// * `cpu` - a reference to the currently executing [`CPUState`] object
577/// * `tb` - the current [`TranslationBlock`] at time of recieving
578/// * `auxv` - the auxillary vector ([`AuxvValues`]) of the program starting
579///
580/// ### Example
581/// ```rust
582/// use panda::prelude::*;
583/// use panda::plugins::proc_start_linux::AuxvValues;
584///
585/// #[panda::on_rec_auxv]
586/// fn on_proc_start(cpu: &mut CPUState, tb: &mut TranslationBlock, auxv: AuxvValues) {
587///     // do stuff when a process starts
588/// }
589/// ```
590///
591/// [`CPUState`]: https://docs.rs/panda-re/*/panda/prelude/struct.CPUState.html
592/// [`TranslationBlock`]: https://docs.rs/panda-re/*/panda/prelude/struct.TranslationBlock.html
593/// [`AuxvValues`]: https://docs.rs/panda-re/*/panda/plugins/proc_start_linux/struct.AuxvValues.html
594#[proc_macro_attribute]
595pub fn on_rec_auxv(_: TokenStream, function: TokenStream) -> TokenStream {
596    let mut function = syn::parse_macro_input!(function as syn::ItemFn);
597    function.sig.abi = Some(syn::parse_quote!(extern "C"));
598    let func = &function.sig.ident;
599    let cfgs = crate::get_cfg_attrs(&function);
600
601    quote!(
602        #(
603            #cfgs
604         )*
605        ::panda::inventory::submit! {
606            #![crate = ::panda]
607            ::panda::PPPCallbackSetup(
608                || {
609                    ::panda::plugins::proc_start_linux::PROC_START_LINUX.add_callback_on_rec_auxv(#func);
610                }
611            )
612        }
613
614        #function
615    ).into()
616}
617
618macro_rules! define_hooks2_callbacks {
619    ($(
620        $($doc:literal)*
621        fn($cb_name:ident) $attr_name:ident ($($arg_name:ident : $arg:ty),* $(,)?);
622    )*) => {
623        $(
624            doc_comment::doc_comment!{
625                concat!("(Callback) ", $($doc, "\n",)* "\n\nCallback arguments: ("$(, "`", stringify!($arg), "`")*, ")\n### Example\n```rust\nuse panda::prelude::*;\n\n#[panda::", stringify!($attr_name),"]\nfn callback(", $(", _: ", stringify!($arg), )* ") {\n    // do stuff\n}\n```"),
626                #[proc_macro_attribute]
627                pub fn $attr_name(_: TokenStream, function: TokenStream) -> TokenStream {
628                    let mut function = syn::parse_macro_input!(function as syn::ItemFn);
629                    function.sig.abi = Some(syn::parse_quote!(extern "C"));
630                    let func = &function.sig.ident;
631                    let cfgs = crate::get_cfg_attrs(&function);
632
633                    quote!(
634                        #(
635                            #cfgs
636                         )*
637                        ::panda::inventory::submit! {
638                            #![crate = ::panda]
639                            ::panda::PPPCallbackSetup(
640                                || {
641                                    ::panda::plugins::hooks2::HOOKS.$cb_name(#func);
642                                }
643                            )
644                        }
645
646                        #function
647                    ).into()
648                }
649            }
650        )*
651
652        /// For internal use only
653        #[doc(hidden)]
654        #[proc_macro]
655        pub fn generate_hooks2_callbacks(_: TokenStream) -> TokenStream {
656            quote!(
657
658                plugin_import!{
659                    static HOOKS: Hooks2 = extern "hooks2" {
660                        callbacks {
661                            $(
662                                fn $attr_name(
663                                    $($arg_name : $arg),*
664                                );
665                            )*
666                        }
667                    };
668                }
669            ).into()
670        }
671    };
672}
673
674include!("base_callbacks.rs");
675include!("hooks2.rs");
676
677#[cfg(feature = "x86_64")]
678include!("syscalls/x86_64.rs");
679
680#[cfg(feature = "i386")]
681include!("syscalls/i386.rs");
682
683#[cfg(feature = "arm")]
684include!("syscalls/arm.rs");
685
686#[cfg(feature = "aarch64")]
687include!("syscalls/aarch64.rs");
688
689// PANDA doesn't have PPC syscalls support!
690//#[cfg(feature = "ppc")]
691//include!("syscalls/ppc.rs");
692
693#[cfg(feature = "mips")]
694include!("syscalls/mips.rs");
695
696#[cfg(feature = "mipsel")]
697include!("syscalls/mipsel.rs");
698
699#[cfg(feature = "mips64")]
700include!("syscalls/mips64.rs");
701
702#[cfg(feature = "mips64el")]
703include!("syscalls/mips64el.rs");