This repository has been archived by the owner on Apr 22, 2023. It is now read-only.
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is marked as WIP, let us know when it will be ready to review. |
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. |
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()),
}
} |
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()),
}
} |
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
approved these changes
May 5, 2019
There was a problem hiding this 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.
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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