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");