Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
athre0z committed Oct 24, 2023
1 parent 4f874bb commit 182c85b
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 6 deletions.
222 changes: 222 additions & 0 deletions src/encoder.rs
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");
}
}
18 changes: 18 additions & 0 deletions src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ pub(crate) const MAX_INSTRUCTION_SEGMENT_COUNT: usize = 9;
/// Maximum number of encoder operands.
pub const ENCODER_MAX_OPERANDS: usize = 5;

/// Combination of all user-encodable prefixes.
pub const ENCODABLE_PREFIXES: u64 = InstructionAttributes::HAS_LOCK.bits()
| InstructionAttributes::HAS_REP.bits()
| InstructionAttributes::HAS_REPE.bits()
| InstructionAttributes::HAS_REPNE.bits()
| InstructionAttributes::HAS_BND.bits()
| InstructionAttributes::HAS_XACQUIRE.bits()
| InstructionAttributes::HAS_XRELEASE.bits()
| InstructionAttributes::HAS_BRANCH_NOT_TAKEN.bits()
| InstructionAttributes::HAS_BRANCH_TAKEN.bits()
| InstructionAttributes::HAS_NOTRACK.bits()
| InstructionAttributes::HAS_SEGMENT_CS.bits()
| InstructionAttributes::HAS_SEGMENT_SS.bits()
| InstructionAttributes::HAS_SEGMENT_DS.bits()
| InstructionAttributes::HAS_SEGMENT_ES.bits()
| InstructionAttributes::HAS_SEGMENT_FS.bits()
| InstructionAttributes::HAS_SEGMENT_GS.bits();

impl Mnemonic {
/// Returns the static string corresponding to this mnemonic.
///
Expand Down
12 changes: 6 additions & 6 deletions src/ffi/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct OperandPointer {
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct Operand {
pub struct EncoderOperand {
pub ty: OperandType,
pub reg: OperandRegister,
pub mem: OperandMemory,
Expand Down Expand Up @@ -68,7 +68,7 @@ pub struct MvexFeatures {
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct Request {
pub struct EncoderRequest {
pub machine_mode: MachineMode,
pub allowed_encodings: EncodableEncoding,
pub mnemonic: Mnemonic,
Expand All @@ -78,21 +78,21 @@ pub struct Request {
pub address_size_hint: AddressSizeHint,
pub operand_size_hint: OperandSizeHint,
pub operand_count: u8,
pub operands: [Operand; ENCODER_MAX_OPERANDS],
pub operands: [EncoderOperand; ENCODER_MAX_OPERANDS],
pub evex: EvexFeatures,
pub mvex: MvexFeatures,
}

extern "C" {
pub fn ZydisEncoderEncodeInstructionAbsolute(
request: *const Request,
request: *const EncoderRequest,
buffer: *mut c_void,
length: *mut usize,
runtime_address: u64,
) -> Status;

pub fn ZydisEncoderEncodeInstruction(
request: *const Request,
request: *const EncoderRequest,
buffer: *mut c_void,
length: *mut usize,
) -> Status;
Expand All @@ -101,7 +101,7 @@ extern "C" {
instruction: *const DecodedInstruction,
operands: *const DecodedOperand,
operand_count: u8,
request: *mut Request,
request: *mut EncoderRequest,
) -> Status;

pub fn ZydisEncoderNopFill(buffer: *mut c_void, length: usize) -> Status;
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
#[macro_use]
mod status;
mod decoder;
#[cfg(feature = "encoder")]
mod encoder;
mod enums;
pub mod ffi;
#[cfg(feature = "formatter")]
mod formatter;

pub use decoder::*;
#[cfg(feature = "encoder")]
pub use encoder::*;
pub use enums::*;
#[cfg(feature = "formatter")]
pub use formatter::*;
Expand Down

0 comments on commit 182c85b

Please sign in to comment.