mirror of
https://github.com/icicle-emu/icicle-python
synced 2026-06-21 13:53:41 +00:00
405 lines
11 KiB
Rust
405 lines
11 KiB
Rust
#![allow(unused)]
|
|
|
|
use icicle::*;
|
|
use pyo3::PyResult;
|
|
use std::process::exit;
|
|
|
|
// NOTE: https://github.com/rust-lang/rust-analyzer/issues/18752
|
|
#[test]
|
|
fn example() -> PyResult<()> {
|
|
Err(pyo3::exceptions::PyException::new_err("test"))
|
|
}
|
|
|
|
fn new_i686() -> PyResult<()> {
|
|
let mut vm = Icicle::new("i686".to_string(), true, true, false, true, false, true, false, false)?;
|
|
assert_eq!(vm.get_architecture(), "i686");
|
|
Ok(())
|
|
}
|
|
|
|
fn new_vm(jit: bool) -> PyResult<Icicle> {
|
|
Icicle::new(
|
|
"x86_64".to_string(),
|
|
jit,
|
|
true,
|
|
false,
|
|
true,
|
|
false,
|
|
true,
|
|
false,
|
|
false,
|
|
)
|
|
}
|
|
|
|
fn new_trace_vm(jit: bool) -> PyResult<Icicle> {
|
|
Icicle::new(
|
|
"x86_64".to_string(),
|
|
jit,
|
|
true,
|
|
false,
|
|
true,
|
|
false,
|
|
true,
|
|
false,
|
|
true,
|
|
)
|
|
}
|
|
|
|
fn nx_start() -> PyResult<()> {
|
|
let mut vm = new_vm(false)?;
|
|
let page = 0x10000;
|
|
vm.mem_map(page, 0x1000, MemoryProtection::ReadOnly)?;
|
|
// <non-executable memory> inc eax; ret
|
|
vm.mem_write(page, b"\xFF\xC0\xC3".to_vec())?;
|
|
vm.reg_write("rip", page)?;
|
|
let status = vm.run();
|
|
println!("status: {:?}", status);
|
|
println!("rip: {:#x}", vm.reg_read("rip")?);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn nx_middle() -> PyResult<()> {
|
|
let mut vm = new_vm(false)?;
|
|
let page = 0x10000;
|
|
vm.mem_map(page, 0x2000, MemoryProtection::ExecuteRead)?;
|
|
vm.mem_protect(page + 0x1000, 0x1000, MemoryProtection::ReadOnly)?;
|
|
// inc eax; inc eax; <transition to non-executable region>; ret
|
|
let rip = page + 0x1000 - 2;
|
|
vm.mem_write(rip, b"\xFF\xC0\xFF\xC0\xC3".to_vec())?;
|
|
vm.reg_write("rip", rip)?;
|
|
let status = vm.run();
|
|
println!("status: {:?}", status);
|
|
println!("rip: {:#x}", vm.reg_read("rip")?);
|
|
println!("rax: {:#x}", vm.reg_read("rax")?);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn inv_start() -> PyResult<()> {
|
|
let mut vm = new_vm(false)?;
|
|
let page = 0x10000;
|
|
vm.mem_map(page, 0x1000, MemoryProtection::ExecuteRead)?;
|
|
// <invalid>; ret
|
|
vm.mem_write(page, b"\xFF\xFF\xC3".to_vec())?;
|
|
vm.reg_write("rip", page)?;
|
|
let status = vm.run();
|
|
println!("status: {:?}", status);
|
|
println!("rip: {:#x}", vm.reg_read("rip")?);
|
|
println!("rax: {:#x}", vm.reg_read("rax")?);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn inv_middle() -> PyResult<()> {
|
|
let mut vm = new_vm(false)?;
|
|
let page = 0x10000;
|
|
vm.mem_map(page, 0x1000, MemoryProtection::ExecuteRead)?;
|
|
// inc eax; <invalid>; ret
|
|
vm.mem_write(page, b"\xFF\xC0\xFF\xFF\xC3".to_vec())?;
|
|
vm.reg_write("rip", page)?;
|
|
let status = vm.run();
|
|
println!("status: {:?}", status);
|
|
println!("rip: {:#x}", vm.reg_read("rip")?);
|
|
println!("rax: {:#x}", vm.reg_read("rax")?);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn block_optimization() -> PyResult<()> {
|
|
let mut vm: Icicle = Icicle::new(
|
|
"x86_64".to_string(),
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
false,
|
|
true,
|
|
true,
|
|
false,
|
|
)?;
|
|
|
|
// Memory setup
|
|
let addr: u64 = 0x140001A73;
|
|
let heap: u64 = 0x71000;
|
|
vm.mem_map(heap, 0x1000, MemoryProtection::ReadWrite)?;
|
|
vm.mem_write(heap + 4, b"\x37\x13\x00\x00".to_vec())?;
|
|
vm.mem_map(addr & !0xFFF, 0x1000, MemoryProtection::ExecuteRead)?;
|
|
vm.mem_write(addr, b"\x41\xc1\xea\x07\x41\x83\xe2\x1f\x74\x08\x44\x89\xd0\x48\x89\x54\xc6\x08\x49\x83\xc1\x04\x4c\x89\x0e\x4c\x89\xc9\x44\x8b\x11\x44\x89\xd0\xf7\xd0\x49\x89\xc9\xa8\x03\x0f\x84\x88\xf6\xff\xff\xeb\x4c\xcc".to_vec())?;
|
|
|
|
// Register setup
|
|
vm.reg_write("r9", heap)?;
|
|
vm.reg_write("r10", 0x13)?;
|
|
vm.reg_write("rip", addr)?;
|
|
vm.reg_write("rsi", heap + 0x100)?;
|
|
|
|
// Step through instructions
|
|
for i in 0..11 {
|
|
let rip = vm.reg_read("rip")?;
|
|
let rcx = vm.reg_read("rcx")?;
|
|
let r9 = vm.reg_read("r9")?;
|
|
|
|
println!("[{}] RIP: {:#x}, RCX: {:#x}, R9: {:#x}", i, rip, rcx, r9);
|
|
|
|
if rip == 0x140001A8F {
|
|
vm.reg_write("r9", 0x13370900)?;
|
|
}
|
|
|
|
vm.step(1);
|
|
|
|
if rip == 0x140001A9A {
|
|
if rcx != r9 {
|
|
println!("[BUG] expected rcx({:#x}) == r9({:#x})", rcx, r9);
|
|
} else {
|
|
println!("Everything works!");
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn rewind() -> PyResult<()> {
|
|
let mut vm = new_vm(true)?;
|
|
|
|
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteRead)?;
|
|
vm.mem_map(0x200, 0x20, MemoryProtection::ReadOnly)?;
|
|
|
|
vm.mem_write(0x100, b"\x55\xCC".to_vec())?; // push rbp
|
|
vm.reg_write("rbp", 0xF00)?;
|
|
vm.reg_write("rsp", 0x210)?;
|
|
vm.reg_write("rip", 0x100)?;
|
|
let status = vm.step(1);
|
|
println!("run status : {:?}", status);
|
|
println!("exception code : {:?}", vm.get_exception_code());
|
|
println!("exception value : {:#x}", vm.get_exception_value());
|
|
println!("stack pointer : {:#x}", vm.reg_read("rsp")?);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn execute_uninitialized() -> PyResult<()> {
|
|
let mut vm = Icicle::new(
|
|
"x86_64".to_string(),
|
|
true,
|
|
true,
|
|
false,
|
|
true,
|
|
true, // NOTE: setting this to true is not properly supported
|
|
true,
|
|
false,
|
|
false,
|
|
)?;
|
|
|
|
// \x48\x8d\x05\x01\x00\x00\x00\x90\x8a\x18\x90
|
|
|
|
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteOnly)?;
|
|
vm.mem_write(0x100, b"\x90\xFF\xC0".to_vec())?; // inc eax
|
|
vm.reg_write("rip", 0x100)?;
|
|
{
|
|
println!("[pre1] icount: {}", vm.get_icount());
|
|
let status = vm.step(2);
|
|
// NOTE: the real reason is that INIT is not set
|
|
println!("run status : {:?}", status);
|
|
println!("exception code : {:?}", vm.get_exception_code());
|
|
println!("exception value : {:#x}", vm.get_exception_value());
|
|
println!("rax : {:#x}", vm.reg_read("rax")?);
|
|
}
|
|
|
|
{
|
|
println!("[pre2] icount: {}", vm.get_icount());
|
|
let status = vm.step(1);
|
|
// NOTE: the real reason is that INIT is not set
|
|
println!("run status : {:?}", status);
|
|
println!("exception code : {:?}", vm.get_exception_code());
|
|
println!("exception value : {:#x}", vm.get_exception_value());
|
|
println!("rax : {:#x}", vm.reg_read("rax")?);
|
|
println!("[post] icount: {}", vm.get_icount());
|
|
}
|
|
|
|
// TODO: status is now UnhandledException, should be InstructionLimit
|
|
// on the next stpe it should be UnhandledException -> ExecViolation
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn execute_only() -> PyResult<()> {
|
|
let mut vm = new_vm(false)?;
|
|
|
|
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteOnly)?;
|
|
/*
|
|
0x100: lea rax, [rip]
|
|
0x107: nop
|
|
0x108: mov bl, byte ptr [rax]
|
|
0x10A: int3
|
|
*/
|
|
vm.mem_write(
|
|
0x100,
|
|
b"\x48\x8d\x05\x00\x00\x00\x00\x90\x8a\x18\xCC".to_vec(),
|
|
)?; // nop
|
|
vm.reg_write("rip", 0x100)?;
|
|
vm.step(2);
|
|
let status = vm.step(1);
|
|
// NOTE: the real reason is that INIT is not set
|
|
println!("run status : {:?}", status);
|
|
println!("exception code : {:?}", vm.get_exception_code());
|
|
println!("exception value : {:#x}", vm.get_exception_value());
|
|
println!("bl: {:#x}", vm.reg_read("bl")?);
|
|
println!("rip: {:#x}", vm.reg_read("rip")?);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn self_modifying() -> PyResult<()> {
|
|
// TODO: add a self-modifying code check (where the previously-executed code is written to)
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn step_modify_rip() -> PyResult<()> {
|
|
let mut vm = new_vm(false)?;
|
|
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteRead)?;
|
|
|
|
// 0x100: 48 01 d8 add rax,rbx
|
|
// 0x103: 48 83 e9 05 sub rcx,0x5
|
|
// 0x107: 48 89 d9 mov rcx,rbx
|
|
// 0x10a: 90 nop
|
|
// 0x10b: 90 nop
|
|
vm.mem_write(
|
|
0x100,
|
|
b"\x48\x01\xD8\x48\x83\xE9\x05\x48\x89\xD9\x90\x90\xCC".to_vec(),
|
|
)?;
|
|
|
|
vm.reg_write("rax", 0xF00)?;
|
|
vm.reg_write("rbx", 0x210)?;
|
|
vm.reg_write("rip", 0x100)?;
|
|
|
|
println!("starting run at {:#x}", vm.reg_read("rip")?);
|
|
let mut status = vm.step(1);
|
|
|
|
println!(
|
|
"ending run at {:#x} (status: {:?})",
|
|
vm.reg_read("rip")?,
|
|
status
|
|
);
|
|
vm.reg_write("rip", 0x100)?;
|
|
//vm.write_pc(0x100);
|
|
//println!("pc: {:#x}", vm.read_pc());
|
|
println!("rip rewritten {:#x}", vm.reg_read("rip")?);
|
|
status = vm.step(1);
|
|
println!(
|
|
"ending run at {:#x} (status: {:?})",
|
|
vm.reg_read("rip")?,
|
|
status
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn eflags_reconstruction() -> PyResult<()> {
|
|
let mut vm = new_vm(false)?;
|
|
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteRead)?;
|
|
|
|
vm.mem_write(0x100, b"\x48\x01\xD8\xCC".to_vec())?;
|
|
vm.reg_write("rax", 0x7FFFFFFFFFFFFFFF)?;
|
|
vm.reg_write("rbx", 0x1)?;
|
|
|
|
let of_mask = (1 << 11) as u64;
|
|
|
|
{
|
|
let eflags = vm.reg_read("eflags")?;
|
|
let of = vm.reg_read("OF")?;
|
|
let of_set = (eflags & of_mask) == of_mask;
|
|
println!("[pre] eflags: {:#x}, OF: {:#x} == {}", eflags, of, of_set);
|
|
}
|
|
|
|
vm.set_pc(0x100);
|
|
let status = vm.step(1);
|
|
println!("run status: {:?}", status);
|
|
|
|
{
|
|
let eflags = vm.reg_read("eflags")?;
|
|
let rflags = vm.reg_read("rflags")?;
|
|
let of = vm.reg_read("OF")?;
|
|
let of_set = (eflags & of_mask) == of_mask;
|
|
println!(
|
|
"[post] eflags: {:#x} == {:#x}, OF: {:#x} == {}",
|
|
eflags, rflags, of, of_set
|
|
);
|
|
}
|
|
|
|
{
|
|
vm.reg_write("OF", 0)?;
|
|
let eflags = vm.reg_read("eflags")?;
|
|
let of = vm.reg_read("OF")?;
|
|
let of_set = (eflags >> 11) & 1;
|
|
println!("[OF=0] eflags: {:#x}, OF: {:#x} == {}", eflags, of, of_set);
|
|
}
|
|
|
|
{
|
|
let mut eflags = vm.reg_read("eflags")?;
|
|
eflags |= of_mask;
|
|
vm.reg_write("rflags", eflags)?;
|
|
let of = vm.reg_read("OF")?;
|
|
let of_set = (eflags >> 11) & 1;
|
|
println!(
|
|
"[rflags|={:#x}] eflags: {:#x}, OF: {:#x} == {}",
|
|
of_mask, eflags, of, of_set
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn main() {
|
|
// Make sure the GHIDRA_SRC environment variable is valid
|
|
match std::env::var("GHIDRA_SRC") {
|
|
Ok(ghidra_src) => {
|
|
// Make sure the directory $GHIDRA_SRC/Ghidra/Processors exists
|
|
if !std::path::Path::new(&ghidra_src)
|
|
.join("Ghidra")
|
|
.join("Processors")
|
|
.exists()
|
|
{
|
|
println!("GHIDRA_SRC environment variable invalid!");
|
|
exit(1);
|
|
}
|
|
println!("GHIDRA_SRC: {}", ghidra_src);
|
|
}
|
|
Err(_) => {
|
|
println!("GHIDRA_SRC environment variable not set!");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
let tests: Vec<(&str, fn() -> PyResult<()>)> = vec![
|
|
("New i686", new_i686),
|
|
("NX (block start)", nx_start),
|
|
("NX (block middle)", nx_middle),
|
|
("Invalid instruction (block start)", inv_start),
|
|
("Invalid instruction (block middle)", inv_middle),
|
|
("Block optimization bug", block_optimization),
|
|
("Rewind", rewind),
|
|
("Execute only", execute_only),
|
|
("Execute uninitialized", execute_uninitialized),
|
|
("Step modify rip", step_modify_rip),
|
|
("EFlags reconstruction", eflags_reconstruction),
|
|
];
|
|
|
|
let mut success = 0;
|
|
for (name, f) in tests.iter() {
|
|
println!("=== {} ===", name);
|
|
match f() {
|
|
Ok(_) => {
|
|
success += 1;
|
|
println!("[OK]");
|
|
}
|
|
Err(e) => {
|
|
println!("[ERROR] {}", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
println!("{}/{} tests passed", success, tests.len());
|
|
exit(if success == tests.len() { 0 } else { 1 });
|
|
}
|