Icicle progress

This commit is contained in:
Maurice Heumann
2025-03-27 15:50:33 +01:00
committed by momo5502
parent 985dd49059
commit 63f54df78c
4 changed files with 286 additions and 56 deletions

View File

@@ -1,14 +1,182 @@
#define ICICLE_EMULATOR_IMPL
#include "icicle_x64_emulator.hpp"
extern "C" void test_rust();
using icicle_emulator = struct icicle_emulator_;
extern "C"
{
icicle_emulator* icicle_create_emulator();
int32_t icicle_map_memory(icicle_emulator*, uint64_t address, uint64_t length, uint8_t permissions);
int32_t icicle_unmap_memory(icicle_emulator*, uint64_t address, uint64_t length);
void icicle_destroy_emulator(icicle_emulator*);
}
namespace icicle
{
class icicle_x64_emulator : public x64_emulator
{
public:
icicle_x64_emulator()
: emu_(icicle_create_emulator())
{
if (!this->emu_)
{
throw std::runtime_error("Failed to create icicle emulator instance");
}
}
~icicle_x64_emulator() override
{
if (this->emu_)
{
icicle_destroy_emulator(this->emu_);
this->emu_ = nullptr;
}
}
void start(const uint64_t start, const uint64_t end, std::chrono::nanoseconds timeout,
const size_t count) override
{
if (timeout.count() < 0)
{
timeout = {};
}
}
void stop() override
{
}
size_t write_raw_register(const int reg, const void* value, const size_t size) override
{
throw std::runtime_error("Not implemented");
}
size_t read_raw_register(const int reg, void* value, const size_t size) override
{
throw std::runtime_error("Not implemented");
}
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
mmio_write_callback write_cb) override
{
throw std::runtime_error("Not implemented");
}
void map_memory(const uint64_t address, const size_t size, memory_permission permissions) override
{
const auto res = icicle_map_memory(this->emu_, address, size, static_cast<uint8_t>(permissions));
if (!res)
{
throw std::runtime_error("Failed to map memory");
}
}
void unmap_memory(const uint64_t address, const size_t size) override
{
const auto res = icicle_unmap_memory(this->emu_, address, size);
if (!res)
{
throw std::runtime_error("Failed to map memory");
}
}
bool try_read_memory(const uint64_t address, void* data, const size_t size) const override
{
throw std::runtime_error("Not implemented");
}
void read_memory(const uint64_t address, void* data, const size_t size) const override
{
if (!this->try_read_memory(address, data, size))
{
throw std::runtime_error("Failed to read memory");
}
}
void write_memory(const uint64_t address, const void* data, const size_t size) override
{
throw std::runtime_error("Not implemented");
}
void apply_memory_protection(const uint64_t address, const size_t size, memory_permission permissions) override
{
throw std::runtime_error("Not implemented");
}
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override
{
throw std::runtime_error("Not implemented");
}
emulator_hook* hook_basic_block(basic_block_hook_callback callback) override
{
throw std::runtime_error("Not implemented");
}
emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) override
{
throw std::runtime_error("Not implemented");
}
emulator_hook* hook_interrupt(interrupt_hook_callback callback) override
{
throw std::runtime_error("Not implemented");
}
emulator_hook* hook_memory_violation(uint64_t address, size_t size,
memory_violation_hook_callback callback) override
{
throw std::runtime_error("Not implemented");
}
emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter,
complex_memory_hook_callback callback) override
{
if (filter == memory_permission::none)
{
return nullptr;
}
throw std::runtime_error("Not implemented");
}
void delete_hook(emulator_hook* hook) override
{
throw std::runtime_error("Not implemented");
}
void serialize_state(utils::buffer_serializer& buffer, const bool is_snapshot) const override
{
throw std::runtime_error("Not implemented");
}
void deserialize_state(utils::buffer_deserializer& buffer, const bool is_snapshot) override
{
throw std::runtime_error("Not implemented");
}
std::vector<std::byte> save_registers() override
{
throw std::runtime_error("Not implemented");
}
void restore_registers(const std::vector<std::byte>& register_data) override
{
throw std::runtime_error("Not implemented");
}
bool has_violation() const override
{
return false;
}
private:
icicle_emulator* emu_{};
};
std::unique_ptr<x64_emulator> create_x64_emulator()
{
test_rust();
return {};
return std::make_unique<icicle_x64_emulator>();
}
}

65
src/icicle/src/icicle.rs Normal file
View File

@@ -0,0 +1,65 @@
fn create_x64_vm() -> icicle_vm::Vm {
let cpu_config = icicle_vm::cpu::Config::from_target_triple("x86_64-none");
let vm = icicle_vm::build(&cpu_config).unwrap();
return vm;
}
fn map_permissions(foreign_permissions: u8) -> u8 {
const FOREIGN_READ: u8 = 1 << 0;
const FOREIGN_WRITE: u8 = 1 << 1;
const FOREIGN_EXEC: u8 = 1 << 2;
let mut permissions: u8 = 0;
if (foreign_permissions & FOREIGN_READ) != 0 {
permissions |= icicle_vm::cpu::mem::perm::READ;
}
if (foreign_permissions & FOREIGN_WRITE) != 0 {
permissions |= icicle_vm::cpu::mem::perm::WRITE;
}
if (foreign_permissions & FOREIGN_EXEC) != 0 {
permissions |= icicle_vm::cpu::mem::perm::EXEC;
}
return permissions;
}
pub struct IcicleEmulator {
vm: icicle_vm::Vm,
}
impl IcicleEmulator {
pub fn new() -> Self {
Self {
vm: create_x64_vm(),
}
}
pub fn map_memory(&mut self, address: u64, length: u64, permissions: u8) -> bool {
const MAPPING_PERMISSIONS: u8 = icicle_vm::cpu::mem::perm::MAP
| icicle_vm::cpu::mem::perm::INIT
| icicle_vm::cpu::mem::perm::IN_CODE_CACHE;
let native_permissions = map_permissions(permissions);
let mapping = icicle_vm::cpu::mem::Mapping {
perm: native_permissions | MAPPING_PERMISSIONS,
value: 0x0,
};
let layout = icicle_vm::cpu::mem::AllocLayout {
addr: Some(address),
size: length,
align: 0x1000,
};
let res = self.vm.cpu.mem.alloc_memory(layout, mapping);
return res.is_ok();
}
pub fn unmap_memory(&mut self, address: u64, length: u64) -> bool {
return self.vm.cpu.mem.unmap_memory_len(address, length);
}
}

View File

@@ -1,58 +1,54 @@
mod icicle;
use icicle::IcicleEmulator;
use std::os::raw::c_void;
#[unsafe(no_mangle)]
pub fn test_rust() {
// Setup the CPU state for the target triple
let cpu_config = icicle_vm::cpu::Config::from_target_triple("x86_64-none");
let mut vm = icicle_vm::build(&cpu_config).unwrap();
// Setup an environment to run inside of.
//let mut env = icicle_vm::env::build_auto(&mut vm).unwrap();
// Load a binary into the environment.
//env.load(&mut vm.cpu, b"./test.elf").unwrap();
//vm.env = env;
let mapping = icicle_vm::cpu::mem::Mapping { perm: icicle_vm::cpu::mem::perm::ALL, value: 0x0 };
let alloc1 =
vm.cpu.mem.alloc_memory(icicle_vm::cpu::mem::AllocLayout { addr: Some(0x10000), size: 0x100, align: 0x100 }, mapping).unwrap();
// Add instrumentation
let counter = vm.cpu.trace.register_store(vec![0_u64]);
vm.add_injector(BlockCounter { counter });
// Run until the VM exits.
let exit = vm.run();
println!("{exit:?}\n{}", icicle_vm::debug::current_disasm(&mut vm));
// Read instrumentation data.
let blocks_hit = vm.cpu.trace[counter].as_any().downcast_ref::<Vec<u64>>().unwrap()[0];
let blocks_executed = blocks_hit.saturating_sub(1);
println!("{blocks_executed} blocks were executed");
pub fn icicle_create_emulator() -> *mut c_void {
let emulator = Box::new(IcicleEmulator::new());
return Box::into_raw(emulator) as *mut c_void;
}
struct BlockCounter {
counter: icicle_vm::cpu::StoreRef,
#[unsafe(no_mangle)]
pub fn icicle_map_memory(
ptr: *mut c_void,
address: u64,
length: u64,
permissions: u8,
) -> i32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
let res = emulator.map_memory(address, length, permissions);
if res {
return 1;
}
return 0;
}
}
impl icicle_vm::CodeInjector for BlockCounter {
fn inject(
&mut self,
_cpu: &mut icicle_vm::cpu::Cpu,
group: &icicle_vm::cpu::BlockGroup,
code: &mut icicle_vm::BlockTable,
) {
let store_id = self.counter.get_store_id();
for block in &mut code.blocks[group.range()] {
// counter += 1
let counter = block.pcode.alloc_tmp(8);
let instrumentation = [
(counter, pcode::Op::Load(store_id), 0_u64).into(),
(counter, pcode::Op::IntAdd, (counter, 1_u64)).into(),
(pcode::Op::Store(store_id), (0_u64, counter)).into(),
];
#[unsafe(no_mangle)]
pub fn icicle_unmap_memory(ptr: *mut c_void, address: u64, length: u64) -> i32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
let res = emulator.unmap_memory(address, length);
// Inject the instrumentation at the start of the block.
block.pcode.instructions.splice(..0, instrumentation);
}
}
}
if res {
return 1;
}
return 0;
}
}
#[unsafe(no_mangle)]
pub fn icicle_destroy_emulator(ptr: *mut c_void) {
if ptr.is_null() {
return;
}
unsafe {
let _ = Box::from_raw(ptr as *mut IcicleEmulator);
}
}

View File

@@ -16,6 +16,7 @@ endif()
target_link_libraries(windows-emulator PRIVATE
unicorn-emulator
icicle-emulator
)
target_link_libraries(windows-emulator PUBLIC emulator)