#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Register {
Eax,
Ecx,
Edx,
Ebx,
Esp,
Ebp,
Esi,
Edi,
Rax,
Rcx,
Rdx,
Rbx,
Rsp,
Rbp,
Rsi,
Rdi,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
Al,
Cl,
Dl,
Bl,
Ah,
Ch,
Dh,
Bh,
Ax,
Cx,
Dx,
Bx,
Sp,
Bp,
Si,
Di,
Es,
Cs,
Ss,
Ds,
Fs,
Gs,
}
impl X86Register {
#[inline]
#[must_use]
pub fn size(&self) -> u8 {
match self {
X86Register::Al
| X86Register::Cl
| X86Register::Dl
| X86Register::Bl
| X86Register::Ah
| X86Register::Ch
| X86Register::Dh
| X86Register::Bh => 1,
X86Register::Ax
| X86Register::Cx
| X86Register::Dx
| X86Register::Bx
| X86Register::Sp
| X86Register::Bp
| X86Register::Si
| X86Register::Di
| X86Register::Es
| X86Register::Cs
| X86Register::Ss
| X86Register::Ds
| X86Register::Fs
| X86Register::Gs => 2,
X86Register::Eax
| X86Register::Ecx
| X86Register::Edx
| X86Register::Ebx
| X86Register::Esp
| X86Register::Ebp
| X86Register::Esi
| X86Register::Edi => 4,
X86Register::Rax
| X86Register::Rcx
| X86Register::Rdx
| X86Register::Rbx
| X86Register::Rsp
| X86Register::Rbp
| X86Register::Rsi
| X86Register::Rdi
| X86Register::R8
| X86Register::R9
| X86Register::R10
| X86Register::R11
| X86Register::R12
| X86Register::R13
| X86Register::R14
| X86Register::R15 => 8,
}
}
#[inline]
#[must_use]
pub fn base_index(&self) -> u8 {
match self {
X86Register::Al
| X86Register::Ah
| X86Register::Ax
| X86Register::Eax
| X86Register::Rax => 0,
X86Register::Cl
| X86Register::Ch
| X86Register::Cx
| X86Register::Ecx
| X86Register::Rcx => 1,
X86Register::Dl
| X86Register::Dh
| X86Register::Dx
| X86Register::Edx
| X86Register::Rdx => 2,
X86Register::Bl
| X86Register::Bh
| X86Register::Bx
| X86Register::Ebx
| X86Register::Rbx => 3,
X86Register::Sp | X86Register::Esp | X86Register::Rsp => 4,
X86Register::Bp | X86Register::Ebp | X86Register::Rbp => 5,
X86Register::Si | X86Register::Esi | X86Register::Rsi => 6,
X86Register::Di | X86Register::Edi | X86Register::Rdi => 7,
X86Register::R8 => 8,
X86Register::R9 => 9,
X86Register::R10 => 10,
X86Register::R11 => 11,
X86Register::R12 => 12,
X86Register::R13 => 13,
X86Register::R14 => 14,
X86Register::R15 => 15,
X86Register::Es => 16,
X86Register::Cs => 17,
X86Register::Ss => 18,
X86Register::Ds => 19,
X86Register::Fs => 20,
X86Register::Gs => 21,
}
}
#[inline]
#[must_use]
pub fn is_stack_pointer(&self) -> bool {
matches!(self, X86Register::Sp | X86Register::Esp | X86Register::Rsp)
}
#[inline]
#[must_use]
pub fn is_base_pointer(&self) -> bool {
matches!(self, X86Register::Bp | X86Register::Ebp | X86Register::Rbp)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct X86Memory {
pub base: Option<X86Register>,
pub index: Option<X86Register>,
pub scale: u8,
pub displacement: i64,
pub size: u8,
}
impl X86Memory {
#[must_use]
pub fn base_disp(base: X86Register, displacement: i64, size: u8) -> Self {
Self {
base: Some(base),
index: None,
scale: 1,
displacement,
size,
}
}
#[must_use]
pub fn base_index_scale_disp(
base: X86Register,
index: X86Register,
scale: u8,
displacement: i64,
size: u8,
) -> Self {
Self {
base: Some(base),
index: Some(index),
scale,
displacement,
size,
}
}
#[must_use]
pub fn absolute(displacement: i64, size: u8) -> Self {
Self {
base: None,
index: None,
scale: 1,
displacement,
size,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86Operand {
Register(X86Register),
Immediate(i64),
Memory(X86Memory),
}
impl X86Operand {
#[must_use]
pub fn size(&self) -> Option<u8> {
match self {
X86Operand::Register(reg) => Some(reg.size()),
X86Operand::Immediate(_) => None, X86Operand::Memory(mem) => Some(mem.size),
}
}
#[must_use]
pub fn is_register(&self) -> bool {
matches!(self, X86Operand::Register(_))
}
#[must_use]
pub fn is_immediate(&self) -> bool {
matches!(self, X86Operand::Immediate(_))
}
#[must_use]
pub fn is_memory(&self) -> bool {
matches!(self, X86Operand::Memory(_))
}
#[must_use]
pub fn as_register(&self) -> Option<X86Register> {
match self {
X86Operand::Register(r) => Some(*r),
_ => None,
}
}
#[must_use]
pub fn as_immediate(&self) -> Option<i64> {
match self {
X86Operand::Immediate(v) => Some(*v),
_ => None,
}
}
#[must_use]
pub fn as_memory(&self) -> Option<&X86Memory> {
match self {
X86Operand::Memory(m) => Some(m),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Condition {
E,
Ne,
L,
Ge,
Le,
G,
B,
Ae,
Be,
A,
S,
Ns,
O,
No,
P,
Np,
}
impl X86Condition {
#[must_use]
pub fn negate(&self) -> Self {
match self {
X86Condition::E => X86Condition::Ne,
X86Condition::Ne => X86Condition::E,
X86Condition::L => X86Condition::Ge,
X86Condition::Ge => X86Condition::L,
X86Condition::Le => X86Condition::G,
X86Condition::G => X86Condition::Le,
X86Condition::B => X86Condition::Ae,
X86Condition::Ae => X86Condition::B,
X86Condition::Be => X86Condition::A,
X86Condition::A => X86Condition::Be,
X86Condition::S => X86Condition::Ns,
X86Condition::Ns => X86Condition::S,
X86Condition::O => X86Condition::No,
X86Condition::No => X86Condition::O,
X86Condition::P => X86Condition::Np,
X86Condition::Np => X86Condition::P,
}
}
}
#[derive(Debug, Clone)]
pub enum X86Instruction {
Mov {
dst: X86Operand,
src: X86Operand,
},
Movzx {
dst: X86Operand,
src: X86Operand,
},
Movsx {
dst: X86Operand,
src: X86Operand,
},
Lea {
dst: X86Register,
src: X86Memory,
},
Push {
src: X86Operand,
},
Pop {
dst: X86Register,
},
Xchg {
dst: X86Operand,
src: X86Operand,
},
Add {
dst: X86Operand,
src: X86Operand,
},
Sub {
dst: X86Operand,
src: X86Operand,
},
Imul {
dst: X86Register,
src: X86Operand,
src2: Option<X86Operand>,
},
Mul {
src: X86Operand,
},
Neg {
dst: X86Operand,
},
Inc {
dst: X86Operand,
},
Dec {
dst: X86Operand,
},
And {
dst: X86Operand,
src: X86Operand,
},
Or {
dst: X86Operand,
src: X86Operand,
},
Xor {
dst: X86Operand,
src: X86Operand,
},
Not {
dst: X86Operand,
},
Shl {
dst: X86Operand,
count: X86Operand,
},
Shr {
dst: X86Operand,
count: X86Operand,
},
Sar {
dst: X86Operand,
count: X86Operand,
},
Rol {
dst: X86Operand,
count: X86Operand,
},
Ror {
dst: X86Operand,
count: X86Operand,
},
Cmp {
left: X86Operand,
right: X86Operand,
},
Test {
left: X86Operand,
right: X86Operand,
},
Jmp {
target: u64,
},
Jcc {
condition: X86Condition,
target: u64,
},
Call {
target: u64,
},
Ret,
Nop,
Cdq,
Cwde,
Adc {
dst: X86Operand,
src: X86Operand,
},
Sbb {
dst: X86Operand,
src: X86Operand,
},
Div {
src: X86Operand,
},
Idiv {
src: X86Operand,
},
Bswap {
dst: X86Register,
},
Cmovcc {
condition: X86Condition,
dst: X86Register,
src: X86Operand,
},
Setcc {
condition: X86Condition,
dst: X86Operand,
},
Bsf {
dst: X86Register,
src: X86Operand,
},
Bsr {
dst: X86Register,
src: X86Operand,
},
Bt {
src: X86Operand,
bit: X86Operand,
},
Xadd {
dst: X86Operand,
src: X86Operand,
},
Unsupported {
offset: u64,
mnemonic: String,
},
}
impl X86Instruction {
#[must_use]
pub fn is_control_flow(&self) -> bool {
matches!(
self,
X86Instruction::Jmp { .. }
| X86Instruction::Jcc { .. }
| X86Instruction::Call { .. }
| X86Instruction::Ret
)
}
#[must_use]
pub fn is_terminator(&self) -> bool {
matches!(
self,
X86Instruction::Jmp { .. }
| X86Instruction::Jcc { .. }
| X86Instruction::Call { .. }
| X86Instruction::Ret
)
}
#[must_use]
pub fn is_unconditional_jump(&self) -> bool {
matches!(self, X86Instruction::Jmp { .. })
}
#[must_use]
pub fn is_conditional_jump(&self) -> bool {
matches!(self, X86Instruction::Jcc { .. })
}
#[must_use]
pub fn jump_targets(&self) -> Vec<u64> {
match self {
X86Instruction::Jmp { target }
| X86Instruction::Jcc { target, .. }
| X86Instruction::Call { target } => vec![*target],
_ => vec![],
}
}
#[must_use]
pub fn writes_memory(&self) -> bool {
match self {
X86Instruction::Mov { dst, .. }
| X86Instruction::Add { dst, .. }
| X86Instruction::Sub { dst, .. }
| X86Instruction::And { dst, .. }
| X86Instruction::Or { dst, .. }
| X86Instruction::Xor { dst, .. }
| X86Instruction::Not { dst }
| X86Instruction::Neg { dst }
| X86Instruction::Inc { dst }
| X86Instruction::Dec { dst }
| X86Instruction::Shl { dst, .. }
| X86Instruction::Shr { dst, .. }
| X86Instruction::Sar { dst, .. }
| X86Instruction::Rol { dst, .. }
| X86Instruction::Ror { dst, .. }
| X86Instruction::Adc { dst, .. }
| X86Instruction::Sbb { dst, .. }
| X86Instruction::Setcc { dst, .. }
| X86Instruction::Xadd { dst, .. } => dst.is_memory(),
X86Instruction::Push { .. } => true, _ => false,
}
}
#[must_use]
pub fn reads_memory(&self) -> bool {
match self {
X86Instruction::Mov { src, .. }
| X86Instruction::Movzx { src, .. }
| X86Instruction::Movsx { src, .. }
| X86Instruction::Add { src, .. }
| X86Instruction::Sub { src, .. }
| X86Instruction::And { src, .. }
| X86Instruction::Or { src, .. }
| X86Instruction::Xor { src, .. }
| X86Instruction::Cmp { right: src, .. }
| X86Instruction::Test { right: src, .. }
| X86Instruction::Shl { count: src, .. }
| X86Instruction::Shr { count: src, .. }
| X86Instruction::Sar { count: src, .. }
| X86Instruction::Rol { count: src, .. }
| X86Instruction::Ror { count: src, .. }
| X86Instruction::Mul { src }
| X86Instruction::Div { src }
| X86Instruction::Idiv { src }
| X86Instruction::Adc { src, .. }
| X86Instruction::Sbb { src, .. }
| X86Instruction::Xadd { src, .. } => src.is_memory(),
X86Instruction::Imul { src, src2, .. } => {
src.is_memory() || src2.as_ref().is_some_and(X86Operand::is_memory)
}
X86Instruction::Cmovcc { src, .. }
| X86Instruction::Bsf { src, .. }
| X86Instruction::Bsr { src, .. } => src.is_memory(),
X86Instruction::Bt { src, bit } => src.is_memory() || bit.is_memory(),
X86Instruction::Pop { .. } => true, _ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86DecodedInstruction {
pub offset: u64,
pub length: usize,
pub instruction: X86Instruction,
}
impl X86DecodedInstruction {
#[inline]
#[must_use]
pub fn end_offset(&self) -> u64 {
self.offset + self.length as u64
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PrologueKind {
DynCipher,
Standard32,
Standard64,
StackFrame {
is_64bit: bool,
},
None,
}
#[derive(Debug, Clone)]
pub struct X86PrologueInfo {
pub kind: X86PrologueKind,
pub size: usize,
pub arg_count: usize,
}
#[derive(Debug, Clone)]
pub struct X86EpilogueInfo {
pub offset: u64,
pub size: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86EdgeKind {
Unconditional,
ConditionalTrue {
condition: X86Condition,
},
ConditionalFalse {
condition: X86Condition,
},
Call {
target: u64,
},
IndirectJump,
Return,
}
impl X86EdgeKind {
#[must_use]
pub const fn is_conditional(&self) -> bool {
matches!(
self,
X86EdgeKind::ConditionalTrue { .. } | X86EdgeKind::ConditionalFalse { .. }
)
}
#[must_use]
pub const fn is_unconditional(&self) -> bool {
matches!(self, X86EdgeKind::Unconditional)
}
#[must_use]
pub const fn is_call(&self) -> bool {
matches!(self, X86EdgeKind::Call { .. })
}
#[must_use]
pub const fn is_indirect(&self) -> bool {
matches!(self, X86EdgeKind::IndirectJump)
}
#[must_use]
pub const fn is_return(&self) -> bool {
matches!(self, X86EdgeKind::Return)
}
}