Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

[WIP] expose reg_write_generic #56

Merged
merged 9 commits into from May 5, 2019
Merged

Conversation

endeav0r
Copy link
Contributor

In order to write X86Mmr to the GDTR register, we need to expose reg_write_generic.

This pull request builds on pull request 55 #55

For an example of what this looks like in the C API, see https://github.com/unicorn-engine/unicorn/blob/master/samples/sample_x86_32_gdt_and_seg_regs.c#L228

@endeav0r endeav0r changed the title expose reg_write_generic [WIP] expose reg_write_generic Apr 15, 2019
@ekse
Copy link
Collaborator

ekse commented Apr 16, 2019

This is marked as WIP, let us know when it will be ready to review.

@ekse
Copy link
Collaborator

ekse commented Apr 16, 2019

Also while you're there could you have a look at #32 ? If you still need it I'll rebase my pull request and merge it.

@turnersr
Copy link

I was able to create a working GDT with this patch. You can use it like

    unsafe {
        match emu.reg_write_generic(unicorn::RegisterX86::GDTR, mmr) {
            Ok(w) => w,
            Err(e) => println!("[reg_write_generic] {}", e.msg()),
        }
    }

@turnersr
Copy link

turnersr commented Apr 18, 2019

Here is a more complete example that follows the C and Python examples:

extern crate byteorder;
extern crate unicorn;

use unicorn::Cpu;
use unicorn::CpuX86;

pub fn gdt_entry(base: u64, limit: u64, access: u64, flags: u64) -> Vec<u8> {
    let mut entry = limit & 0xffff;
    entry |= (base & 0xffffff) << 16;
    entry |= (access & 0xff) << 40;
    entry |= (flags & 0xff) << 52;
    entry |= ((base >> 24) & 0xff) << 56;

    let mut buf = Vec::new();
    buf.write_u64::<LittleEndian>(entry).unwrap();
    return buf;
}

pub fn gdt_entry_flags(
    gr: u64,
    sz: u64,
    pr: u64,
    privl: u64,
    ex: u64,
    dc: u64,
    rw: u64,
    ac: u64,
) -> u64 {
    let mut flags = ac & 1;
    flags |= (rw & 1) << 1;
    flags |= (dc & 1) << 2;
    flags |= (ex & 1) << 3;
    flags |= 1 << 4;
    flags |= (privl & 0b11) << 5;
    flags |= (pr & 1) << 7;
    flags |= (sz & 1) << 10;
    flags |= (gr & 1) << 11;

    return flags;
}

pub fn segment_selector(index: u64, ti: u64, rpl: u64) -> u64 {
    let mut sel = rpl;
    sel |= ti << 2;
    sel |= index << 3;
    return sel;
}

// 0x1000
pub fn align(address: u32, alignment: u32) -> u32 {
    return (address + alignment - 1) & !(alignment - 1);
}

pub fn write_gdt(emu: &unicorn::CpuX86, gdt: &Vec<Vec<u8>>, mem: u64, entry_size: u64) {
    for index in 0..gdt.len() {
        let offset = index as u64 * entry_size;
        let value = &gdt[index];
        let _ = match emu.mem_write(mem + offset, value) {
            Ok(r) => r,
            Err(e) => println!("[write_gdt] {} {}", index, e.msg()),
        };
    }
}

fn main() {
    let callback = move |_: &unicorn::Unicorn,
                         _mem_type: unicorn::MemType,
                         address: u64,
                         size: usize,
                         value: i64| {
        println!("[Inside callback] {:#x}+{:#x}={:#x} {}", address, size, address+size as u64, value);
        return false;
    };

    let x86_code32: Vec<u8> = vec![0x65, 0x33, 0x0d, 0x18, 0x0, 0x0, 0x0]; // xor ecx, dword ptr gs:[0x18]
    let data: Vec<u8> = vec![0x42, 0x42, 0x42, 0x42];

    let code_addr = 0x40000;
    let code_size = 0x10000;

    let gdt_addr = 0x3000;
    let gdt_limit = 0x1000;
    let gdt_entry_size = 0x8;

    let gs_segment_addr = 0x5000;
    let gs_segment_size = 0x1000;


    let a_present = 0x80;

    let a_priv_3 = 0x60;
    let a_priv_0 = 0x0;

    let a_code = 0x10;
    let a_data = 0x10;

    let a_exec = 0x8;
    let a_data_writeable = 0x2;
    let a_code_readable = 0x2;

    let a_dir_con_bit = 0x4;
    let s_priv_0 = 0x0;

    // Initialize emulator in X86-32bit mode
    let mut emu = CpuX86::new(unicorn::Mode::MODE_32).expect("failed to instantiate emulator");

    let _ = match emu.mem_map(
        gdt_addr,
        gdt_limit,
        unicorn::PROT_READ | unicorn::PROT_WRITE,
    ) {
        Ok(r) => r,
        Err(e) => println!("[mem_map GDT] {}", e.msg()),
    };

    println!(
        "Mapping GDT {:#x} <-> {:#x}+{:#x}={:#x}",
        gdt_addr,
        gdt_addr,
        gdt_limit,
        gdt_addr + gdt_limit as u64
    );

    let _ = match emu.mem_map(
        code_addr,
        code_size,
        unicorn::PROT_ALL,
    ) {
        Ok(r) => r,
        Err(e) => println!("[mem_map Code] {}", e.msg()),
    };

    println!(
        "Mapping Code {:#x} <-> {:#x}+{:#x}={:#x}",
        code_addr,
        code_addr,
        code_size,
        code_addr + code_size as u64
    );


    let _ = match emu.mem_map(
        gs_segment_addr,
        gs_segment_size,
        unicorn::PROT_READ | unicorn::PROT_WRITE,
    ) {
        Ok(r) => r,
        Err(e) => println!("[mem_map GS] {}", e.msg()),
    };

    println!(
        "Mapping GS {:#x} <-> {:#x}+{:#x}={:#x}",
        gs_segment_addr,
        gs_segment_addr,
        gs_segment_size,
        gs_segment_addr + gs_segment_size as u64
    );


    let _ = emu.mem_write(code_addr, &x86_code32).unwrap();

    let mut gdt: Vec<Vec<u8>> = Vec::new();

    for _ in 0..31 {
        gdt.push(gdt_entry(0, 0, 0, 0));
    }

    gdt[15] = gdt_entry(
        gs_segment_addr,
        gs_segment_size as u64,
        a_present | a_data | a_data_writeable | a_priv_3 | a_dir_con_bit,
        f_prot_32,
    );
    gdt[16] = gdt_entry(
        0,
        0xfffff000,
        a_present | a_data | a_data_writeable | a_priv_3 | a_dir_con_bit,
        f_prot_32,
    ); // Data Segment
    gdt[17] = gdt_entry(
        0,
        0xfffff000,
        a_present | a_code | a_code_readable | a_priv_3 | a_exec | a_dir_con_bit,
        f_prot_32,
    ); // Code Segment
    gdt[18] = gdt_entry(
        0,
        0xfffff000,
        a_present | a_data | a_data_writeable | a_priv_0 | a_dir_con_bit,
        f_prot_32,
    ); // Stack Segment

    write_gdt(&emu, &gdt, gdt_addr, gdt_entry_size);

    let mmr = unicorn::X86Mmr {
        selector: 0,
        base: gdt_addr as u64,
        limit: ((gdt.len() as u64 * gdt_entry_size) - 1) as u32,
        flags: 0,
    };

    unsafe {
        match emu.reg_write_generic(unicorn::RegisterX86::GDTR, mmr) {
            Ok(w) => w,
            Err(e) => println!("[reg_write_generic] {}", e.msg()),
        }
    }

    emu.reg_read(unicorn::RegisterX86::GDTR).unwrap();

    let selector = segment_selector(15, s_gdt, s_priv_3);
    emu.reg_write(unicorn::RegisterX86::GS, selector).expect("failed to write GS");

    let selector = segment_selector(16, s_gdt, s_priv_3);
    emu.reg_write(unicorn::RegisterX86::DS, selector).expect("failed to write DS");

    let selector = segment_selector(17, s_gdt, s_priv_3);
    emu.reg_write(unicorn::RegisterX86::CS, selector).expect("failed to write CS");

    let selector = segment_selector(18, s_gdt, s_priv_0);
    emu.reg_write(unicorn::RegisterX86::SS, selector).expect("failed to write SS");

    emu.mem_write(gs_segment_addr + 0x18, &data).expect("failed to write data to from GS");

    match emu.reg_write(unicorn::RegisterX86::ECX, 0x0) {
        Ok(x) => println!("[write] good"),
        Err(x) => println!("[write] failed => {}", x.msg()),
    }

    emu.add_mem_hook(unicorn::MemHookType::MEM_READ, 0, std::u64::MAX, callback).expect("failed to add memory hook");

    let _start = match emu.emu_start(code_addr, code_addr+x86_code32.len() as u64, 0, 0) {
        Ok(s) => s,
        Err(e) => println!("[emu_start] {}", e.msg()),
    };

    match emu.reg_read(unicorn::RegisterX86::ECX) {
        Ok(x) => {
                let mut buf = Vec::new();
                buf.write_u64::<LittleEndian>(x).unwrap();
                println!("[reg_read] {:?}", buf);
        },
        Err(x) => println!("[reg_read] {}", x.msg()),
    }
}

@ekse
Copy link
Collaborator

ekse commented Apr 18, 2019

Cool stuff! I don't want to rush @endeav0r but once its ready I will merge it and make a new release of the crate.

@ekse ekse self-requested a review May 5, 2019 18:59
Copy link
Collaborator

@ekse ekse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will merge this PR, we can make adjustments later if needed.

@ekse ekse merged commit 800374d into unicorn-rs:master May 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants