use crate::*;
use std::convert::TryInto;
const DATA_ADDR: u64 = 0x7000;
#[test]
fn test_push_rax() {
let code = [
0x48, 0xc7, 0xc0, 0x11, 0x22, 0x33, 0x44, 0x50, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x0FF8, "RSP should be decremented by 8");
let mut stack_val = [0u8; 8];
stack_val = emu
.maps
.read_bytes(0x0FF8, stack_val.len())
.try_into()
.unwrap();
let pushed_val = u64::from_le_bytes(stack_val);
assert_eq!(pushed_val, 0x44332211, "Pushed value should be on stack");
}
#[test]
fn test_push_rbx() {
let code = [
0x48, 0xc7, 0xc3, 0xaa, 0xbb, 0xcc, 0xdd, 0x53, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x2000 - (0x2000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x2000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x1FF8, "RSP decremented by 8");
let mut stack_val = [0u8; 8];
stack_val = emu
.maps
.read_bytes(0x1FF8, stack_val.len())
.try_into()
.unwrap();
assert_eq!(
u64::from_le_bytes(stack_val),
0xFFFFFFFFDDCCBBAA,
"RBX value on stack"
);
}
#[test]
fn test_push_rcx() {
let code = [0x51, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.regs_mut().rcx = 0x1234567890ABCDEF;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x0FF8, "RSP decremented");
let mut stack_val = [0u8; 8];
stack_val = emu
.maps
.read_bytes(0x0FF8, stack_val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(stack_val), 0x1234567890ABCDEF);
}
#[test]
fn test_push_all_gp_registers() {
let code = [
0x50, 0x53, 0x51, 0x52, 0x56, 0x57, 0x55, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.regs_mut().rax = 0x1111111111111111;
emu.regs_mut().rbx = 0x2222222222222222;
emu.regs_mut().rcx = 0x3333333333333333;
emu.regs_mut().rdx = 0x4444444444444444;
emu.regs_mut().rsi = 0x5555555555555555;
emu.regs_mut().rdi = 0x6666666666666666;
emu.regs_mut().rbp = 0x7777777777777777;
emu.run(None).unwrap();
assert_eq!(
emu.regs().rsp,
0x1000 - 7 * 8,
"RSP decremented by 7*8 = 56"
);
let mut val = [0u8; 8];
val = emu
.maps
.read_bytes(0x1000 - 8, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 0x1111111111111111, "RAX");
val = emu
.maps
.read_bytes(0x1000 - 16, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 0x2222222222222222, "RBX");
val = emu
.maps
.read_bytes(0x1000 - 24, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 0x3333333333333333, "RCX");
}
#[test]
fn test_push_r8() {
let code = [0x41, 0x50, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.regs_mut().r8 = 0xAAAAAAAAAAAAAAAA;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x0FF8);
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0xAAAAAAAAAAAAAAAA);
}
#[test]
fn test_push_r15() {
let code = [0x41, 0x57, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.regs_mut().r15 = 0xBBBBBBBBBBBBBBBB;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x0FF8);
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0xBBBBBBBBBBBBBBBB);
}
#[test]
fn test_push_imm8() {
let code = [0x6a, 0x42, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x0FF8);
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0x42);
}
#[test]
fn test_push_imm8_negative() {
let code = [0x6a, 0xff, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x0FF8);
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0xFFFFFFFFFFFFFFFF);
}
#[test]
fn test_push_imm32() {
let code = [0x68, 0x78, 0x56, 0x34, 0x12, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x0FF8);
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0x12345678);
}
#[test]
fn test_push_zero() {
let code = [0x6a, 0x00, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0);
}
#[test]
fn test_multiple_push() {
let code = [
0x48, 0xc7, 0xc0, 0x11, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc3, 0x22, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc1, 0x33, 0x00, 0x00, 0x00, 0x50, 0x53, 0x51, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x1000 - 24, "RSP decremented by 3*8");
let mut val = [0u8; 8];
val = emu
.maps
.read_bytes(0x1000 - 8, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 0x11, "First push (RAX)");
val = emu
.maps
.read_bytes(0x1000 - 16, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 0x22, "Second push (RBX)");
val = emu
.maps
.read_bytes(0x1000 - 24, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 0x33, "Third push (RCX)");
}
#[test]
fn test_push_preserves_flags() {
let code = [
0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x48, 0x83, 0xc0, 0x01, 0x50, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert!(emu.flags().dump() & 0x40 != 0, "ZF should still be set");
}
#[test]
fn test_push_rsp() {
let code = [0x54, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x0FF8, "RSP decremented");
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0x1000, "Original RSP value pushed");
}
#[test]
fn test_stack_grows_down() {
let code = [
0x6a, 0x01, 0x6a, 0x02, 0x6a, 0x03, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x1000 - 24, "Stack pointer decreased");
let mut val = [0u8; 8];
val = emu
.maps
.read_bytes(emu.regs().rsp, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 3, "Top of stack is 3");
}
#[test]
fn test_push_max_value() {
let code = [0x50, 0xf4]; let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.regs_mut().rax = 0xFFFFFFFFFFFFFFFF;
emu.run(None).unwrap();
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0xFFFFFFFFFFFFFFFF);
}
#[test]
fn test_push_then_modify() {
let code = [
0x48, 0xc7, 0xc0, 0x42, 0x00, 0x00, 0x00, 0x50, 0x48, 0xc7, 0xc0, 0x99, 0x00, 0x00, 0x00, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x99, "RAX modified");
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0x42, "Stack has original value");
}
#[test]
fn test_push_practical_function_prologue() {
let code = [
0x55, 0x48, 0x89, 0xe5, 0x50, 0x53, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.regs_mut().rbp = 0x2000;
emu.regs_mut().rax = 0x1111;
emu.regs_mut().rbx = 0x2222;
emu.run(None).unwrap();
assert_eq!(emu.regs().rbp, 0x0FF8, "RBP set to RSP after first push");
assert_eq!(emu.regs().rsp, 0x0FF8 - 16, "RSP after pushing RAX and RBX");
let mut val = [0u8; 8];
val = emu.maps.read_bytes(0x0FF8, val.len()).try_into().unwrap();
assert_eq!(u64::from_le_bytes(val), 0x2000, "Old RBP saved");
}
#[test]
fn test_push_near_stack_bottom() {
let code = [
0x50, 0x53, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x0,
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x10;
emu.regs_mut().rax = 0xAAAA;
emu.regs_mut().rbx = 0xBBBB;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x10 - 16, "RSP at near-zero address");
}
#[test]
fn test_push_chain_order() {
let code = [
0x6a, 0x0a, 0x6a, 0x14, 0x6a, 0x1e, 0x6a, 0x28, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.run(None).unwrap();
let mut val = [0u8; 8];
val = emu
.maps
.read_bytes(emu.regs().rsp, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 40, "Top of stack");
val = emu
.maps
.read_bytes(emu.regs().rsp + 8, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 30);
val = emu
.maps
.read_bytes(emu.regs().rsp + 16, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 20);
val = emu
.maps
.read_bytes(emu.regs().rsp + 24, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 10, "Bottom of our pushes");
}
#[test]
fn test_push_all_extended_regs() {
let code = [
0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0xf4, ];
let mut emu = emu64();
emu.load_code_bytes(&code);
emu.maps
.create_map(
"stack_test",
0x1000 - (0x1000 / 2),
0x1000,
crate::maps::mem64::Permission::READ_WRITE_EXECUTE,
)
.unwrap();
emu.regs_mut().rsp = 0x1000;
emu.regs_mut().r8 = 0x08;
emu.regs_mut().r9 = 0x09;
emu.regs_mut().r10 = 0x0A;
emu.regs_mut().r11 = 0x0B;
emu.regs_mut().r12 = 0x0C;
emu.regs_mut().r13 = 0x0D;
emu.regs_mut().r14 = 0x0E;
emu.regs_mut().r15 = 0x0F;
emu.run(None).unwrap();
assert_eq!(emu.regs().rsp, 0x1000 - 64, "8 registers * 8 bytes");
let mut val = [0u8; 8];
val = emu
.maps
.read_bytes(emu.regs().rsp, val.len())
.try_into()
.unwrap();
assert_eq!(u64::from_le_bytes(val), 0x0F, "R15 on top");
}