-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
250 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
use crate::{ffi, *}; | ||
use core::{ | ||
mem::MaybeUninit, | ||
ops::{Deref, DerefMut}, | ||
}; | ||
|
||
#[repr(transparent)] | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
pub struct EncoderRequest(ffi::EncoderRequest); | ||
|
||
impl Deref for EncoderRequest { | ||
type Target = ffi::EncoderRequest; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl EncoderRequest { | ||
pub fn new32(mnemonic: Mnemonic) -> Self { | ||
Self::new(MachineMode::LONG_COMPAT_32, mnemonic) | ||
} | ||
|
||
pub fn new64(mnemonic: Mnemonic) -> Self { | ||
Self::new(MachineMode::LONG_64, mnemonic) | ||
} | ||
|
||
/// Create a new encoder request from scratch. | ||
pub fn new(machine_mode: MachineMode, mnemonic: Mnemonic) -> Self { | ||
let mut request: ffi::EncoderRequest = unsafe { core::mem::zeroed() }; | ||
request.machine_mode = machine_mode; | ||
request.mnemonic = mnemonic; | ||
Self(request) | ||
} | ||
|
||
/// Sets the branch type. | ||
/// | ||
/// Required for branching instructions only. The default of | ||
/// `ZYDIS_BRANCH_TYPE_NONE` lets the encoder pick size-optimal branch type | ||
/// automatically (`short` and `near` are prioritized over `far`). | ||
pub fn set_branch_type(mut self, branch_type: BranchType) -> Self { | ||
self.0.branch_type = branch_type; | ||
self | ||
} | ||
|
||
/// Sets the branch width. | ||
/// | ||
/// Specifies physical size for relative immediate operands. Use | ||
/// `ZYDIS_BRANCH_WIDTH_NONE` to let encoder pick size-optimal branch width | ||
/// automatically. For segment:offset `far` branches this field applies to | ||
/// physical size of the offset part. For branching instructions without | ||
/// relative operands this field affects effective operand size attribute. | ||
pub fn set_branch_width(mut self, branch_width: BranchWidth) -> Self { | ||
self.0.branch_width = branch_width; | ||
self | ||
} | ||
|
||
/// Sets the address size hint. | ||
pub fn set_address_size_hint(mut self, address_size_hint: AddressSizeHint) -> Self { | ||
self.0.address_size_hint = address_size_hint; | ||
self | ||
} | ||
|
||
/// Sets the operand size hint. | ||
pub fn set_operand_size_hint(mut self, operand_size_hint: OperandSizeHint) -> Self { | ||
self.0.operand_size_hint = operand_size_hint; | ||
self | ||
} | ||
|
||
/// Gets a slice of the operands. | ||
pub fn operands(&self) -> &[EncoderOperand] { | ||
unsafe { | ||
core::slice::from_raw_parts( | ||
self.0.operands.as_ptr() as *const EncoderOperand, | ||
self.0.operand_count as usize, | ||
) | ||
} | ||
} | ||
|
||
/// Adds an operand to the request. | ||
pub fn add_operand(mut self, operand: EncoderOperand) -> Self { | ||
self.0.operands[self.0.operand_count as usize] = operand.0; | ||
self.0.operand_count += 1; | ||
self | ||
} | ||
|
||
/// Clears the operand list. | ||
pub fn clear_operands(mut self) -> Self { | ||
self.0.operand_count = 0; | ||
self | ||
} | ||
|
||
/// Encodes the instruction into the given buffer. | ||
pub fn encode_into(&self, buf: &mut [u8]) -> Result<usize> { | ||
unsafe { | ||
let mut length = buf.len(); | ||
ffi::ZydisEncoderEncodeInstruction(&self.0, buf.as_ptr() as _, &mut length) | ||
.as_result()?; | ||
Ok(length) | ||
} | ||
} | ||
|
||
/// Encodes the instruction into a new buffer. | ||
pub fn encode(&self) -> Result<Vec<u8>> { | ||
let mut out = vec![0; MAX_INSTRUCTION_LENGTH]; | ||
let length = self.encode_into(&mut out[..])?; | ||
out.resize(length, 0); | ||
Ok(out) | ||
} | ||
} | ||
|
||
impl<const N: usize> From<Instruction<OperandArrayVec<N>>> for EncoderRequest { | ||
fn from(instr: Instruction<OperandArrayVec<N>>) -> Self { | ||
unsafe { | ||
let mut request = MaybeUninit::uninit(); | ||
ffi::ZydisEncoderDecodedInstructionToEncoderRequest( | ||
&*instr, | ||
instr.operands().as_ptr(), | ||
instr.operands().len() as _, | ||
request.as_mut_ptr(), | ||
) | ||
.as_result() | ||
.expect( | ||
"our rust wrapper for instructions is immutable and unchanged decoded \ | ||
instructions should always be convertible", | ||
); | ||
Self(request.assume_init()) | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
#[repr(transparent)] | ||
pub struct EncoderOperand(ffi::EncoderOperand); | ||
|
||
impl EncoderOperand { | ||
/// Creates a new register operand. | ||
pub fn reg(reg: Register) -> Self { | ||
Self(ffi::EncoderOperand { | ||
ty: OperandType::REGISTER, | ||
reg: ffi::OperandRegister { | ||
value: reg as _, | ||
is4: false, | ||
}, | ||
mem: unsafe { core::mem::zeroed() }, | ||
ptr: unsafe { core::mem::zeroed() }, | ||
imm: 0, | ||
}) | ||
} | ||
|
||
/// Creates a new memory operand. | ||
pub fn mem(mem: ffi::OperandMemory) -> Self { | ||
Self(ffi::EncoderOperand { | ||
ty: OperandType::MEMORY, | ||
reg: unsafe { core::mem::zeroed() }, | ||
mem, | ||
ptr: unsafe { core::mem::zeroed() }, | ||
imm: 0, | ||
}) | ||
} | ||
|
||
/// Creates a new pointer operand. | ||
pub fn ptr(segment: u16, offset: u32) -> Self { | ||
Self(ffi::EncoderOperand { | ||
ty: OperandType::POINTER, | ||
reg: unsafe { core::mem::zeroed() }, | ||
mem: unsafe { core::mem::zeroed() }, | ||
ptr: ffi::OperandPointer { | ||
segment, | ||
offset, | ||
}, | ||
imm: 0, | ||
}) | ||
} | ||
|
||
/// Creates a new immediate operand (unsigned). | ||
pub fn imm(imm: u64) -> Self { | ||
Self(ffi::EncoderOperand { | ||
ty: OperandType::IMMEDIATE, | ||
reg: unsafe { core::mem::zeroed() }, | ||
mem: unsafe { core::mem::zeroed() }, | ||
ptr: unsafe { core::mem::zeroed() }, | ||
imm, | ||
}) | ||
} | ||
|
||
/// Creates a new immediate operand (signed). | ||
pub fn imm_signed(imm: i64) -> Self { | ||
Self::imm(imm as _) | ||
} | ||
} | ||
|
||
impl Deref for EncoderOperand { | ||
type Target = ffi::EncoderOperand; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn encode_int3() { | ||
let req = EncoderRequest::new64(Mnemonic::INT3); | ||
let enc = req.encode().unwrap(); | ||
assert_eq!(enc, vec![0xCC]); | ||
} | ||
|
||
#[test] | ||
fn encode_mov() { | ||
let mov = EncoderRequest::new64(Mnemonic::MOV) | ||
.add_operand(EncoderOperand::reg(Register::RAX)) | ||
.add_operand(EncoderOperand::imm(0x1337)) | ||
.encode() | ||
.unwrap(); | ||
|
||
assert_eq!(mov, b"\x48\xC7\xC0\x37\x13\x00\x00"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters