unicorn_taint/
unicorn_taint.rs

1use panda::prelude::*;
2use panda::regs::{set_reg, set_pc, Reg};
3use panda::mem::{map_memory, physical_memory_write, PAGE_SIZE};
4use panda::taint;
5
6// inc rax
7// add rbx, rax
8// inc rcx
9const X86_CODE: &[u8] = b"\x48\xFF\xC0\x48\x01\xC3\x48\xFF\xC1";
10
11const ADDRESS: target_ulong = 0x1000;
12const STOP_ADDR: target_ulong = ADDRESS + (X86_CODE.len() as target_ulong);
13
14// configure our state before running
15#[panda::after_machine_init] // <--- runs immediately after the QEMU machine is accessible
16fn setup(cpu: &mut CPUState) {
17    // Map 2MB memory for this emulation
18    map_memory("mymem", 2 * 1024 * PAGE_SIZE, ADDRESS).unwrap();
19
20    // Write code into memory
21    physical_memory_write(ADDRESS, X86_CODE);
22
23    // Setup registers
24    set_reg(cpu, Reg::RAX, 0x1);
25    set_reg(cpu, Reg::RBX, 0x2);
26    set_reg(cpu, Reg::RCX, 0x3);
27    set_reg(cpu, Reg::RDX, 0x4);
28
29    // Iterate over registers to show initial taint
30    for reg in [Reg::RAX, Reg::RBX, Reg::RCX, Reg::RDX] {
31        println!("{:?} is tained? {:?}", reg, taint::check_reg(reg));
32    }
33
34    println!("Tainting RAX with label '1'...");
35    taint::label_reg(Reg::RAX, 1);
36
37    println!("Tainting RBX with label '2'...");
38    taint::label_reg(Reg::RBX, 2);
39
40    // Set starting PC
41    set_pc(cpu, ADDRESS);
42}
43
44// In order to ensure our `insn_exec` callback runs every instruction, we must instruct PANDA to
45// translate and instrument (i.e. add code to call our callback) each instruction.
46#[panda::insn_translate] // <--- runs every instruction
47fn insn_translate(_cpu: &mut CPUState, _pc: target_ptr_t) -> bool {
48    true
49}
50
51#[panda::insn_exec] // <--- runs every instrumented instruction
52fn insn_exec(cpu: &mut CPUState, pc: target_ptr_t) {
53    println!("pc: {:#x?}", pc);
54
55    // if we've reached the end of our shellcode, dump the registers and taint to stdout, then quit
56    if pc == STOP_ADDR {
57        println!("Final CPU state:");
58        panda::regs::dump_regs(cpu);
59
60        for reg in [Reg::RAX, Reg::RBX, Reg::RCX, Reg::RDX] {
61            println!("{:?} is tained? {:?}", reg, taint::check_reg(reg));
62
63            if taint::check_reg(reg) {
64                println!("(Tainted by {:?})", taint::get_reg(reg));
65            }
66        }
67
68        unsafe {
69            panda::sys::exit(0);
70        }
71    }
72}
73
74// When the example runs, start up a new PANDA instance. It's marked `.configurable()` so that
75// instead of running a built-in QEMU system, we can just build our own baremetal execution
76// environment
77fn main() {
78    Panda::new()
79        .arch(panda::Arch::x86_64)
80        .configurable()
81        .run();
82}