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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
use crate::arch::{
    arm, arm64, evm, m680x, m68k, mips, mos65xx, ppc, sparc, sysz, tms320c64x, x86, xcore,
    InsnGroup, Reg,
};
use crate::{sys, util, Arch};
use core::marker::PhantomData;

const MNEMONIC_SIZE: usize = 32;

/// Information about a disassembled instruction.
#[repr(C)]
pub struct Insn<'a> {
    /// Instruction ID (basically a numeric ID for the instruction mnemonic)
    /// Find the instruction id in the '\[ARCH\]_insn' enum in the header file
    /// of corresponding architecture, such as 'arm_insn' in arm.h for ARM,
    /// 'x86_insn' in x86.h for X86, etc...
    /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF
    /// NOTE: in Skipdata mode, "data" instruction has 0 for this id field.
    pub(crate) id: libc::c_uint,

    /// Address (EIP) of this instruction
    /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF
    address: u64,

    /// Size of this instruction
    /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF
    size: u16,

    /// Machine bytes of this instruction, with number of bytes indicated by @size above
    /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF
    bytes: [u8; 24],

    /// Ascii text of instruction mnemonic
    /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF
    mnemonic: [libc::c_char; MNEMONIC_SIZE],

    /// Ascii text of instruction operands
    /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF
    op_str: [libc::c_char; 160],

    /// Pointer to cs_detail.
    /// NOTE: detail pointer is only valid when both requirements below are met:
    /// (1) CS_OP_DETAIL = CS_OPT_ON
    /// (2) Engine is not in Skipdata mode (CS_OP_SKIPDATA option set to CS_OPT_ON)
    ///
    /// NOTE 2: when in Skipdata mode, or when detail mode is OFF, even if this pointer
    ///     is not NULL, its content is still irrelevant.
    pub(crate) detail: *mut DetailsInner,

    /// Phantom data to tie the lifetime of the Insn to the Capstone instance.
    _phan: PhantomData<&'a ()>,
}

impl<'a> Insn<'a> {
    /// Returns trhe address of this instruction.
    #[inline]
    pub fn address(&self) -> u64 {
        self.address
    }

    /// Returns the size of this instruction in bytes.
    #[inline]
    pub fn size(&self) -> usize {
        self.size as usize
    }

    /// Returns the machine bytes of this instruction.
    /// The returned slice will have the same size as the return
    /// value of [`Insn::size`]
    #[inline]
    pub fn bytes(&self) -> &[u8] {
        unsafe { core::slice::from_raw_parts(self.bytes.as_ptr(), self.size()) }
    }

    /// Returns the instruction mnemonic.
    #[inline]
    pub fn mnemonic(&self) -> &str {
        unsafe { util::cstr(self.mnemonic.as_ptr(), MNEMONIC_SIZE) }
    }

    /// Returns the instruction operands as a string.
    #[inline]
    pub fn operands(&self) -> &str {
        unsafe { util::cstr(self.op_str.as_ptr(), 160) }
    }
}

/// A buffer of disassembled instructions.
pub struct InsnBuffer<'a> {
    inner: *mut Insn<'a>,
    count: usize,
    _phan: PhantomData<&'a Insn<'a>>,
}

impl<'a> InsnBuffer<'a> {
    pub(crate) fn new(insn: *mut Insn<'a>, count: usize) -> InsnBuffer<'a> {
        InsnBuffer {
            inner: insn,
            count,
            _phan: PhantomData,
        }
    }

    /// Frees the `Insn`(`cs_insn`) if it is not currently null
    /// then clears the pointer and count.
    fn free(&mut self) {
        if self.count == 0 || self.inner.is_null() {
            return;
        }
        unsafe { sys::cs_free(self.inner as *mut Insn, self.count as libc::size_t) };
        self.inner = core::ptr::null_mut();
        self.count = 0;
    }
}

impl<'a> core::ops::Deref for InsnBuffer<'a> {
    type Target = [Insn<'a>];

    #[inline]
    fn deref(&self) -> &Self::Target {
        unsafe { core::slice::from_raw_parts(self.inner, self.count) }
    }
}

impl<'a> Drop for InsnBuffer<'a> {
    fn drop(&mut self) {
        self.free();
    }
}

/// Iterator that lazily disassembles a binary blob of machine code.
/// This is constructed using [`crate::Capstone::disasm_iter`].
pub struct InsnIter<'a> {
    caps: &'a super::Capstone,
    insn: *mut Insn<'a>,
    code: *const u8,
    size: libc::size_t,
    addr: u64,
}

impl<'a> InsnIter<'a> {
    pub(crate) fn new(
        caps: &'a super::Capstone,
        insn: *mut Insn<'a>,
        code: *const u8,
        size: libc::size_t,
        addr: u64,
    ) -> InsnIter<'a> {
        InsnIter {
            caps,
            insn,
            code,
            size,
            addr,
        }
    }

    /// Frees the `Insn`(`cs_insn`) if it is not currently null
    /// then clears the pointer.
    fn free(&mut self) {
        if self.insn.is_null() {
            return;
        }
        unsafe { sys::cs_free(self.insn as *mut Insn, 1) };
        self.insn = core::ptr::null_mut();
    }
}

impl<'a> Iterator for InsnIter<'a> {
    type Item = Result<&'a Insn<'a>, super::Error>;

    fn next(&mut self) -> Option<Self::Item> {
        let success = unsafe {
            sys::cs_disasm_iter(
                self.caps.handle,
                &mut self.code,
                &mut self.size,
                &mut self.addr,
                self.insn,
            )
        };

        #[cfg(feature = "std")]
        self.caps.resume_panic();

        if !success {
            match self.caps.errno() {
                Ok(_) => return None,
                Err(err) => return Some(Err(err)),
            }
        }

        Ok(unsafe { self.insn.as_ref() }).transpose()
    }
}

impl<'a> Drop for InsnIter<'a> {
    fn drop(&mut self) {
        self.free();
    }
}

/// Extra details about an isntruction.
#[derive(Copy, Clone)]
pub struct Details<'i> {
    arch: Arch,
    inner: &'i DetailsInner,
}

impl<'i> Details<'i> {
    pub(crate) fn wrap(arch: Arch, inner: &'i DetailsInner) -> Details<'i> {
        Details { arch, inner }
    }

    /// Returns a list of registers that are **implicitly** read from by an instruction.
    /// For explicitly read registers, use the architecture specific details to access
    /// the operands of the instruction.
    pub fn regs_read(self) -> &'i [Reg] {
        unsafe {
            &*(&self.inner.regs_read[..self.inner.regs_read_count as usize] as *const [u16]
                as *const [Reg])
        }
    }

    /// Returns a list of registers that are **implicitly** written to by this instruction.
    /// For registers that are explicitly written to, use the architecture specific details
    /// to access the operands of the instruction.
    pub fn regs_write(self) -> &'i [Reg] {
        unsafe {
            &*(&self.inner.regs_write[..self.inner.regs_write_count as usize] as *const [u16]
                as *const [Reg])
        }
    }

    pub fn groups(self) -> &'i [InsnGroup] {
        unsafe {
            &*(&self.inner.groups[..self.inner.groups_count as usize] as *const [u8]
                as *const [InsnGroup])
        }
    }

    /// Returns architecture specific details.
    pub fn arch(self) -> ArchDetails<'i> {
        match self.arch {
            Arch::Arm => ArchDetails::Arm(unsafe { &self.inner.arch.arm }),
            Arch::Arm64 => ArchDetails::Arm64(unsafe { &self.inner.arch.arm64 }),
            Arch::Mips => ArchDetails::Mips(unsafe { &self.inner.arch.mips }),
            Arch::X86 => ArchDetails::X86(unsafe { &self.inner.arch.x86 }),
            Arch::PowerPc => ArchDetails::PowerPc(unsafe { &self.inner.arch.ppc }),
            Arch::Sparc => ArchDetails::Sparc(unsafe { &self.inner.arch.sparc }),
            Arch::SystemZ => ArchDetails::SystemZ(unsafe { &self.inner.arch.sysz }),
            Arch::XCore => ArchDetails::XCore(unsafe { &self.inner.arch.xcore }),
            Arch::M68K => ArchDetails::M68K(unsafe { &self.inner.arch.m68k }),
            Arch::Tms320C64X => ArchDetails::Tms320C64X(unsafe { &self.inner.arch.tms320c64x }),
            Arch::M680X => ArchDetails::M680X(unsafe { &self.inner.arch.m680x }),
            Arch::Evm => ArchDetails::Evm(unsafe { &self.inner.arch.evm }),
            Arch::Mos65xx => ArchDetails::Mos65xx(unsafe { &self.inner.arch.mos65xx }),
        }
    }

    /// If these are details for an x86 instruction, this will return
    /// x86 specific details. If these are not details for an x86 instruction
    /// this will return [`Option::None`].
    pub fn x86(self) -> Option<&'i x86::Details<'i>> {
        if self.arch == Arch::X86 {
            Some(unsafe { &self.inner.arch.x86 })
        } else {
            None
        }
    }
}

/// Wrapper around cs_detail.
#[repr(C)]
pub(crate) struct DetailsInner {
    /// List of implicit registers read by this insn.
    regs_read: [u16; 16],

    /// Number of implicit registers read by this insn.
    regs_read_count: u8,

    /// List of implicit registers modified by this insn.
    regs_write: [u16; 20],

    /// Number of implicit registers modified by this insn.
    regs_write_count: u8,

    /// List of group this instruction belong to.
    groups: [u8; 8],

    /// Number of groups this insn belongs to.
    groups_count: u8,

    /// Architecture specific details.
    pub(crate) arch: ArchDetailsUnion,
}

#[repr(C)]
pub(crate) union ArchDetailsUnion {
    pub x86: x86::Details<'static>,
    pub arm64: arm64::Details<'static>,
    pub arm: arm::Details<'static>,
    pub m68k: m68k::Details<'static>,
    pub mips: mips::Details<'static>,
    pub ppc: ppc::Details<'static>,
    pub sparc: sparc::Details<'static>,
    pub sysz: sysz::Details<'static>,
    pub xcore: xcore::Details<'static>,
    pub tms320c64x: tms320c64x::Details<'static>,
    pub m680x: m680x::Details<'static>,
    pub evm: evm::Details<'static>,
    pub mos65xx: mos65xx::Details<'static>,
}

#[derive(Copy, Clone)]
pub enum ArchDetails<'i> {
    X86(&'i x86::Details<'i>),
    Arm64(&'i arm64::Details<'i>),
    Arm(&'i arm::Details<'i>),
    M68K(&'i m68k::Details<'i>),
    Mips(&'i mips::Details<'i>),
    PowerPc(&'i ppc::Details<'i>),
    Sparc(&'i sparc::Details<'i>),
    SystemZ(&'i sysz::Details<'i>),
    XCore(&'i xcore::Details<'i>),
    Tms320C64X(&'i tms320c64x::Details<'i>),
    M680X(&'i m680x::Details<'i>),
    Evm(&'i evm::Details<'i>),
    Mos65xx(&'i mos65xx::Details<'i>),
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::sys;

    #[test]
    fn detail_size_and_alignment() {
        assert_eq!(
            core::mem::size_of::<DetailsInner>(),
            sys::get_test_val("sizeof(cs_detail)")
        );

        assert_eq!(
            core::mem::align_of::<DetailsInner>(),
            sys::get_test_val("alignof(cs_detail)")
        );
    }

    #[test]
    fn insn_size_and_alignment() {
        assert_eq!(
            core::mem::size_of::<Insn>(),
            sys::get_test_val("sizeof(cs_insn)")
        );

        assert_eq!(
            core::mem::align_of::<Insn>(),
            sys::get_test_val("alignof(cs_insn)")
        );
    }
}