#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
pub struct Encoder {
buffer: Vec<u8>,
}
impl Encoder {
pub fn new() -> Self {
Self {
buffer: Vec::with_capacity(64),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
buffer: Vec::with_capacity(capacity),
}
}
pub fn bytes(&self) -> &[u8] {
&self.buffer
}
pub fn into_bytes(self) -> Vec<u8> {
self.buffer
}
pub fn len(&self) -> usize {
self.buffer.len()
}
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
pub fn clear(&mut self) {
self.buffer.clear();
}
pub fn raw(&mut self, bytes: &[u8]) -> &mut Self {
self.buffer.extend_from_slice(bytes);
self
}
pub fn byte(&mut self, b: u8) -> &mut Self {
self.buffer.push(b);
self
}
pub fn nop(&mut self) -> &mut Self {
self.buffer.push(0x90);
self
}
pub fn nop_sled(&mut self, count: usize) -> &mut Self {
let mut remaining = count;
while remaining > 0 {
match remaining {
1 => {
self.buffer.push(0x90);
remaining -= 1;
}
2 => {
self.buffer.extend_from_slice(&[0x66, 0x90]);
remaining -= 2;
}
3 => {
self.buffer.extend_from_slice(&[0x0F, 0x1F, 0x00]);
remaining -= 3;
}
4 => {
self.buffer.extend_from_slice(&[0x0F, 0x1F, 0x40, 0x00]);
remaining -= 4;
}
5 => {
self.buffer
.extend_from_slice(&[0x0F, 0x1F, 0x44, 0x00, 0x00]);
remaining -= 5;
}
6 => {
self.buffer
.extend_from_slice(&[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00]);
remaining -= 6;
}
7 => {
self.buffer
.extend_from_slice(&[0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00]);
remaining -= 7;
}
_ => {
self.buffer
.extend_from_slice(&[0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]);
remaining -= 8;
}
}
}
self
}
pub fn int3(&mut self) -> &mut Self {
self.buffer.push(0xCC);
self
}
pub fn ret(&mut self) -> &mut Self {
self.buffer.push(0xC3);
self
}
pub fn push_imm32(&mut self, value: u32) -> &mut Self {
self.buffer.push(0x68);
self.buffer.extend_from_slice(&value.to_le_bytes());
self
}
pub fn push_imm8(&mut self, value: i8) -> &mut Self {
self.buffer.push(0x6A);
self.buffer.push(value as u8);
self
}
pub fn jmp_rel32(&mut self, from: usize, to: usize) -> &mut Self {
let offset = (to as i64 - from as i64 - 5) as i32;
self.buffer.push(0xE9);
self.buffer.extend_from_slice(&offset.to_le_bytes());
self
}
pub fn jmp_rel8(&mut self, offset: i8) -> &mut Self {
self.buffer.push(0xEB);
self.buffer.push(offset as u8);
self
}
pub fn call_rel32(&mut self, from: usize, to: usize) -> &mut Self {
let offset = (to as i64 - from as i64 - 5) as i32;
self.buffer.push(0xE8);
self.buffer.extend_from_slice(&offset.to_le_bytes());
self
}
#[cfg(target_arch = "x86_64")]
pub fn jmp_abs64(&mut self, target: u64) -> &mut Self {
self.buffer
.extend_from_slice(&[0xFF, 0x25, 0x00, 0x00, 0x00, 0x00]);
self.buffer.extend_from_slice(&target.to_le_bytes());
self
}
#[cfg(target_arch = "x86_64")]
pub fn mov_rax_imm64(&mut self, value: u64) -> &mut Self {
self.buffer.extend_from_slice(&[0x48, 0xB8]);
self.buffer.extend_from_slice(&value.to_le_bytes());
self
}
#[cfg(target_arch = "x86_64")]
pub fn jmp_rax(&mut self) -> &mut Self {
self.buffer.extend_from_slice(&[0xFF, 0xE0]);
self
}
#[cfg(target_arch = "x86_64")]
pub fn call_rax(&mut self) -> &mut Self {
self.buffer.extend_from_slice(&[0xFF, 0xD0]);
self
}
#[cfg(target_arch = "x86_64")]
pub fn call_abs64(&mut self, target: u64) -> &mut Self {
self.buffer
.extend_from_slice(&[0xFF, 0x15, 0x00, 0x00, 0x00, 0x00]);
self.buffer.extend_from_slice(&target.to_le_bytes());
self
}
#[cfg(target_arch = "x86_64")]
pub fn push_rax(&mut self) -> &mut Self {
self.buffer.push(0x50);
self
}
#[cfg(target_arch = "x86_64")]
pub fn pop_rax(&mut self) -> &mut Self {
self.buffer.push(0x58);
self
}
#[cfg(target_arch = "x86_64")]
pub fn sub_rsp_imm8(&mut self, value: i8) -> &mut Self {
self.buffer.extend_from_slice(&[0x48, 0x83, 0xEC]);
self.buffer.push(value as u8);
self
}
#[cfg(target_arch = "x86_64")]
pub fn add_rsp_imm8(&mut self, value: i8) -> &mut Self {
self.buffer.extend_from_slice(&[0x48, 0x83, 0xC4]);
self.buffer.push(value as u8);
self
}
#[cfg(target_arch = "x86")]
pub fn push_eax(&mut self) -> &mut Self {
self.buffer.push(0x50);
self
}
#[cfg(target_arch = "x86")]
pub fn pop_eax(&mut self) -> &mut Self {
self.buffer.push(0x58);
self
}
#[cfg(target_arch = "x86")]
pub fn push_ebp(&mut self) -> &mut Self {
self.buffer.push(0x55);
self
}
#[cfg(target_arch = "x86")]
pub fn mov_ebp_esp(&mut self) -> &mut Self {
self.buffer.extend_from_slice(&[0x8B, 0xEC]);
self
}
#[cfg(target_arch = "x86")]
pub fn jmp_abs32(&mut self, target: u32) -> &mut Self {
self.push_imm32(target);
self.ret();
self
}
}
impl Default for Encoder {
fn default() -> Self {
Self::new()
}
}
impl AsRef<[u8]> for Encoder {
fn as_ref(&self) -> &[u8] {
&self.buffer
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nop() {
let mut enc = Encoder::new();
enc.nop();
assert_eq!(enc.bytes(), &[0x90]);
}
#[test]
fn test_nop_sled() {
for size in 1..=16 {
let mut enc = Encoder::new();
enc.nop_sled(size);
assert_eq!(enc.len(), size);
}
}
#[test]
fn test_push_ret() {
let mut enc = Encoder::new();
enc.push_imm32(0xDEADBEEF).ret();
assert_eq!(enc.bytes(), &[0x68, 0xEF, 0xBE, 0xAD, 0xDE, 0xC3]);
}
#[test]
fn test_jmp_rel32() {
let mut enc = Encoder::new();
enc.jmp_rel32(0x1000, 0x1100);
assert_eq!(enc.len(), 5);
assert_eq!(enc.bytes()[0], 0xE9);
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_jmp_abs64() {
let mut enc = Encoder::new();
enc.jmp_abs64(0xDEADBEEF12345678);
assert_eq!(enc.len(), 14);
assert_eq!(&enc.bytes()[0..6], &[0xFF, 0x25, 0x00, 0x00, 0x00, 0x00]);
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_mov_jmp_rax() {
let mut enc = Encoder::new();
enc.mov_rax_imm64(0xDEADBEEF12345678).jmp_rax();
assert_eq!(enc.len(), 12);
}
}