Skip to main content

imping_ir/
function.rs

1//! Functions, their calling conventions, parameters and parameter storage.
2
3use crate::TypeId;
4use serde::{Deserialize, Serialize};
5
6/// A function in a module.
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
9pub struct Function {
10    /// The function's name, without namespace. Overloads may share a name.
11    pub name: String,
12    /// The namespace path, outermost first (e.g. `["Engine", "Entity"]`). Empty
13    /// for a free function.
14    #[serde(default, skip_serializing_if = "Vec::is_empty")]
15    pub namespace: Vec<String>,
16    /// The function's address, relative to the module's image base.
17    #[serde(with = "crate::hex::int")]
18    #[cfg_attr(feature = "schema", schemars(with = "String"))]
19    pub rva: u32,
20    /// The calling convention.
21    pub calling_convention: CallingConvention,
22    /// The return type (the `void` primitive for no return value).
23    pub return_type: TypeId,
24    /// The parameters, in order.
25    #[serde(default)]
26    pub parameters: Vec<Parameter>,
27    /// Whether the function is variadic.
28    #[serde(default)]
29    pub variadic: bool,
30    /// Whether the parameters use non-standard storage, given by their
31    /// [`Storage`], instead of the calling convention's defaults.
32    #[serde(default)]
33    pub custom_storage: bool,
34    /// Free-form tags attached to the function (e.g. in Ghidra).
35    #[serde(default, skip_serializing_if = "Vec::is_empty")]
36    pub tags: Vec<String>,
37    /// The bytes expected at the start of the function, used to verify the
38    /// build before hooking. Empty if none were recorded.
39    #[serde(
40        default,
41        skip_serializing_if = "Vec::is_empty",
42        with = "crate::hex::bytes"
43    )]
44    #[cfg_attr(feature = "schema", schemars(with = "String"))]
45    pub verification: Vec<u8>,
46    /// The same bytes as `verification`, with the ones that hold addresses as
47    /// `??`, e.g. `"48 8d 05 ?? ?? ?? ?? c3"`. Checks the function is still the
48    /// same code where its addresses may have changed. Empty if none were
49    /// recorded.
50    #[serde(default, skip_serializing_if = "String::is_empty")]
51    pub shape: String,
52    /// A byte pattern that finds the function when the bytes at `rva` don't
53    /// match `verification`, as after an update to the module.
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub pattern: Option<Pattern>,
56}
57
58/// A byte pattern that finds a [`Function`] by its code instead of its address.
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
61pub struct Pattern {
62    /// Hex bytes separated by spaces, with `??` or `?` for any byte, e.g.
63    /// `"48 89 5c 24 ?? 57"`. It must match exactly once in the module's code.
64    pub bytes: String,
65    /// Bytes from the start of the match to the function, or to the call when
66    /// `follow_call` is set. Negative steps back.
67    #[serde(default, skip_serializing_if = "is_zero")]
68    pub offset: i32,
69    /// Whether there's a `call` or `jmp` to the function at `offset`, rather
70    /// than the function itself.
71    #[serde(default, skip_serializing_if = "is_false")]
72    pub follow_call: bool,
73}
74
75fn is_zero(value: &i32) -> bool {
76    *value == 0
77}
78
79fn is_false(value: &bool) -> bool {
80    !*value
81}
82
83/// A function's calling convention.
84///
85/// On x64 there is a single convention, [`Win64`](Self::Win64); the others
86/// apply to x86.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
88#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
89#[serde(rename_all = "lowercase")]
90pub enum CallingConvention {
91    /// x86 `__cdecl`.
92    Cdecl,
93    /// x86 `__stdcall`.
94    Stdcall,
95    /// x86 `__fastcall`.
96    Fastcall,
97    /// x86 `__thiscall`.
98    Thiscall,
99    /// The single x64 convention.
100    Win64,
101}
102
103/// A function parameter.
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
106pub struct Parameter {
107    /// The parameter's name, if known.
108    #[serde(default, skip_serializing_if = "Option::is_none")]
109    pub name: Option<String>,
110    /// The parameter's type.
111    #[serde(rename = "type")]
112    pub type_id: TypeId,
113    /// Where the parameter is passed.
114    pub storage: Storage,
115}
116
117/// Where a [`Parameter`] is passed.
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
119#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
120#[serde(tag = "kind", rename_all = "snake_case")]
121pub enum Storage {
122    /// Passed in a register (e.g. `"rcx"`, `"xmm0"`).
123    Register {
124        /// The register name, lowercase.
125        value: String,
126    },
127    /// Passed on the stack, at the given byte offset.
128    Stack {
129        /// The byte offset of the parameter.
130        offset: i64,
131    },
132}