ropr 0.2.25

A blazing fast multithreaded ROP Gadget finder. ropper / ropgadget alternative
Documentation
use iced_x86::{Code, FlowControl, Instruction, Mnemonic, OpKind, Register};

fn is_ret(instr: &Instruction) -> bool { matches!(instr.mnemonic(), Mnemonic::Ret) }

fn is_sys(instr: &Instruction) -> bool {
	match instr.mnemonic() {
		Mnemonic::Syscall => true,
		Mnemonic::Int => matches!(instr.try_immediate(0).unwrap(), 0x80),
		Mnemonic::Iret | Mnemonic::Iretd | Mnemonic::Iretq => true,
		Mnemonic::Sysret | Mnemonic::Sysretq | Mnemonic::Sysexit | Mnemonic::Sysexitq => true,
		_ => false,
	}
}

fn is_jop(instr: &Instruction, noisy: bool) -> bool {
	match instr.mnemonic() {
		Mnemonic::Jmp => {
			if noisy {
				!matches!(
					instr.op0_kind(),
					OpKind::NearBranch64 | OpKind::NearBranch32 | OpKind::NearBranch16
				)
			}
			else {
				match instr.op0_kind() {
					OpKind::Register => true,
					OpKind::Memory => !matches!(instr.memory_base(), Register::EIP | Register::RIP),
					_ => false,
				}
			}
		}
		Mnemonic::Call => {
			if noisy {
				!matches!(
					instr.op0_kind(),
					OpKind::NearBranch64 | OpKind::NearBranch32 | OpKind::NearBranch16
				)
			}
			else {
				match instr.op0_kind() {
					OpKind::Register => true,
					OpKind::Memory => !matches!(instr.memory_base(), Register::EIP | Register::RIP),
					_ => false,
				}
			}
		}
		_ => false,
	}
}

fn is_invalid(instr: &Instruction) -> bool { matches!(instr.code(), Code::INVALID) }

pub fn is_gadget_tail(instr: &Instruction, rop: bool, sys: bool, jop: bool, noisy: bool) -> bool {
	if is_invalid(instr) {
		return false;
	}
	if instr.flow_control() == FlowControl::Next {
		return false;
	}
	if rop && is_ret(instr) {
		return true;
	}
	if sys && is_sys(instr) {
		return true;
	}
	if jop && is_jop(instr, noisy) {
		return true;
	}
	false
}

pub fn is_rop_gadget_head(instr: &Instruction, noisy: bool) -> bool {
	if is_invalid(instr) {
		return false;
	}
	if !noisy
		&& (instr.has_lock_prefix()
			|| instr.has_rep_prefix()
			|| instr.has_repe_prefix()
			|| instr.has_repne_prefix()
			|| instr.has_xacquire_prefix()
			|| instr.has_xrelease_prefix())
	{
		return false;
	}
	match instr.flow_control() {
		FlowControl::Next | FlowControl::Interrupt => true,
		FlowControl::ConditionalBranch => noisy,
		FlowControl::Call => instr.mnemonic() != Mnemonic::Call,
		_ => false,
	}
}

pub fn is_stack_pivot_head(instr: &Instruction) -> bool {
	let reg0 = instr.op0_register();
	let kind1 = instr.op1_kind();
	let reg1 = instr.op1_register();
	match instr.mnemonic() {
		Mnemonic::Adc
		| Mnemonic::Adcx
		| Mnemonic::Add
		| Mnemonic::Sbb
		| Mnemonic::Sub
		| Mnemonic::Bndmov
		| Mnemonic::Cmova
		| Mnemonic::Cmovae
		| Mnemonic::Cmovb
		| Mnemonic::Cmovbe
		| Mnemonic::Cmove
		| Mnemonic::Cmovg
		| Mnemonic::Cmovge
		| Mnemonic::Cmovl
		| Mnemonic::Cmovle
		| Mnemonic::Cmovne
		| Mnemonic::Cmovno
		| Mnemonic::Cmovnp
		| Mnemonic::Cmovns
		| Mnemonic::Cmovo
		| Mnemonic::Cmovp
		| Mnemonic::Cmovs
		| Mnemonic::Cmpxchg
		| Mnemonic::Cmpxchg16b
		| Mnemonic::Cmpxchg8b
		| Mnemonic::Pop
		| Mnemonic::Popa
		| Mnemonic::Popad => {
			matches!(reg0, Register::RSP | Register::ESP | Register::SP)
				&& matches!(
					kind1,
					OpKind::Immediate8
						| OpKind::Immediate8_2nd | OpKind::Immediate16
						| OpKind::Immediate32 | OpKind::Immediate64
						| OpKind::Immediate8to16 | OpKind::Immediate8to32
						| OpKind::Immediate8to64 | OpKind::Immediate32to64
						| OpKind::Register
				)
		}
		Mnemonic::Mov | Mnemonic::Movbe | Mnemonic::Movd => {
			matches!(reg0, Register::RSP | Register::ESP | Register::SP)
				&& (matches!(kind1, OpKind::Register) || instr.memory_base() != Register::None)
		}
		Mnemonic::Xadd | Mnemonic::Xchg => {
			matches!(reg0, Register::RSP | Register::ESP | Register::SP)
				|| matches!(reg1, Register::RSP | Register::ESP | Register::SP)
		}
		Mnemonic::Leave => true,
		_ => false,
	}
}

pub fn is_stack_pivot_tail(instr: &Instruction) -> bool { is_ret(instr) }

pub fn is_base_pivot_head(instr: &Instruction) -> bool {
	let reg0 = instr.op0_register();
	let kind1 = instr.op1_kind();
	let reg1 = instr.op1_register();
	match instr.mnemonic() {
		Mnemonic::Adc
		| Mnemonic::Adcx
		| Mnemonic::Add
		| Mnemonic::Sbb
		| Mnemonic::Sub
		| Mnemonic::Bndmov
		| Mnemonic::Cmova
		| Mnemonic::Cmovae
		| Mnemonic::Cmovb
		| Mnemonic::Cmovbe
		| Mnemonic::Cmove
		| Mnemonic::Cmovg
		| Mnemonic::Cmovge
		| Mnemonic::Cmovl
		| Mnemonic::Cmovle
		| Mnemonic::Cmovne
		| Mnemonic::Cmovno
		| Mnemonic::Cmovnp
		| Mnemonic::Cmovns
		| Mnemonic::Cmovo
		| Mnemonic::Cmovp
		| Mnemonic::Cmovs
		| Mnemonic::Cmpxchg
		| Mnemonic::Cmpxchg16b
		| Mnemonic::Cmpxchg8b
		| Mnemonic::Pop
		| Mnemonic::Popa
		| Mnemonic::Popad => {
			matches!(reg0, Register::RBP | Register::EBP | Register::BP)
				&& matches!(
					kind1,
					OpKind::Immediate8
						| OpKind::Immediate8_2nd | OpKind::Immediate16
						| OpKind::Immediate32 | OpKind::Immediate64
						| OpKind::Immediate8to16 | OpKind::Immediate8to32
						| OpKind::Immediate8to64 | OpKind::Immediate32to64
						| OpKind::Register
				)
		}
		Mnemonic::Mov | Mnemonic::Movbe | Mnemonic::Movd => {
			matches!(reg0, Register::RBP | Register::EBP | Register::BP)
				&& (matches!(kind1, OpKind::Register) || instr.memory_base() != Register::None)
		}
		Mnemonic::Xadd | Mnemonic::Xchg => {
			matches!(reg0, Register::RBP | Register::EBP | Register::BP)
				|| matches!(reg1, Register::RBP | Register::EBP | Register::BP)
		}
		Mnemonic::Enter => true,
		_ => false,
	}
}