Basic working icicle support (#181)

This commit is contained in:
Maurice Heumann
2025-04-07 09:31:42 +02:00
committed by GitHub
31 changed files with 2239 additions and 859 deletions

View File

@@ -20,6 +20,14 @@ on:
options:
- "true"
- "false"
icicle_debug:
description: "Run debug tests with icicle emulator"
type: choice
required: false
default: "false"
options:
- "true"
- "false"
#concurrency:
# group: ${{ github.ref }}
@@ -270,7 +278,7 @@ jobs:
- macOS x86_64
emulator:
- Unicorn
#- Icicle
- Icicle
emulation-root:
- Windows 2025
- Windows 2022
@@ -336,6 +344,7 @@ jobs:
run: cp build/${{matrix.preset}}/artifacts/test-sample.exe build/${{matrix.preset}}/artifacts/root/filesys/c/
- name: CMake Test
if: "${{ matrix.emulator != 'Icicle' || matrix.configuration != 'Debug' || github.event.inputs.icicle_debug == 'true' || github.event.inputs.icicle_debug == 'true' || github.event_name == 'schedule' }}"
run: cd build/${{matrix.preset}} && ctest --verbose -j
env:
EMULATOR_ROOT: ${{github.workspace}}/build/${{matrix.preset}}/artifacts/root
@@ -354,7 +363,7 @@ jobs:
#- arm64-v8a
emulator:
- Unicorn
#- Icicle
- Icicle
emulation-root:
- Windows 2025
- Windows 2022
@@ -401,6 +410,7 @@ jobs:
run: cp build/${{matrix.preset}}/artifacts/test-sample.exe build/${{matrix.preset}}/artifacts/root/filesys/c/
- name: Run Test
if: "${{ matrix.emulator != 'Icicle' || matrix.configuration != 'Debug' || github.event.inputs.icicle_debug == 'true' || github.event.inputs.icicle_debug == 'true' || github.event_name == 'schedule' }}"
uses: reactivecircus/android-emulator-runner@v2.34.0
with:
api-level: 29

View File

@@ -12,7 +12,7 @@ A high-performance Windows process emulator that operates at syscall level, prov
Perfect for security research, malware analysis, and DRM research where fine-grained control over process execution is required.
Built in C++ and powered by the Unicorn Engine.
Built in C++ and powered by the [Unicorn Engine](https://github.com/unicorn-engine/unicorn) (or the [icicle-emu](https://github.com/icicle-emu/icicle-emu) 🆕).
## Key Features

View File

@@ -42,13 +42,15 @@ namespace
win_emu.emu().hook_memory_write(
win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
[&win_emu, cache_logging, params_hook, modules](const uint64_t address, size_t,
const uint64_t value) mutable {
[&win_emu, cache_logging, params_hook, modules](const uint64_t address, const void*, size_t) mutable {
const auto target_address = win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters);
if (address == target_address)
{
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{win_emu.emu(), value};
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{
win_emu.emu(),
win_emu.emu().read_memory<uint64_t>(address),
};
win_emu.emu().delete_hook(params_hook);
params_hook = watch_object(win_emu, modules, obj, cache_logging);
@@ -236,7 +238,7 @@ namespace
continue;
}
auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
auto read_handler = [&, section, concise_logging](const uint64_t address, const void*, size_t) {
const auto rip = win_emu->emu().read_instruction_pointer();
if (!win_emu->mod_manager.executable->is_within(rip))
{
@@ -258,7 +260,7 @@ namespace
section.name.c_str(), address, rip);
};
const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
const auto write_handler = [&, section, concise_logging](const uint64_t address, const void*, size_t) {
const auto rip = win_emu->emu().read_instruction_pointer();
if (!win_emu->mod_manager.executable->is_within(rip))
{

View File

@@ -11,7 +11,7 @@ emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, s
return emu.emu().hook_memory_read(
object.value(), object.size(),
[i = std::move(info), object, &emu, cache_logging, modules](const uint64_t address, size_t, uint64_t) {
[i = std::move(info), object, &emu, cache_logging, modules](const uint64_t address, const void*, size_t) {
const auto rip = emu.emu().read_instruction_pointer();
const auto* mod = emu.mod_manager.find_by_address(rip);
const auto is_main_access = mod == emu.mod_manager.executable || modules.contains(mod->name);

View File

@@ -40,11 +40,11 @@ using edge_generation_hook_callback =
using basic_block_hook_callback = std::function<void(const basic_block& block)>;
using instruction_hook_callback = std::function<instruction_hook_continuation()>;
using interrupt_hook_callback = std::function<void(int interrupt)>;
using simple_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value)>;
using complex_memory_hook_callback =
std::function<void(uint64_t address, size_t size, uint64_t value, memory_operation operation)>;
using memory_access_hook_callback = std::function<void(uint64_t address, const void* data, size_t size)>;
using memory_execution_hook_callback = std::function<void(uint64_t address)>;
using memory_violation_hook_callback = std::function<memory_violation_continuation(
uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>;
@@ -53,49 +53,18 @@ class hook_interface
public:
virtual ~hook_interface() = default;
virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size,
memory_violation_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_execution(uint64_t address, memory_execution_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_read(uint64_t address, size_t size, memory_access_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_write(uint64_t address, size_t size, memory_access_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter,
complex_memory_hook_callback callback) = 0;
virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0;
virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) = 0;
virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0;
virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0;
virtual void delete_hook(emulator_hook* hook) = 0;
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback)
{
return this->hook_memory_violation(0, std::numeric_limits<size_t>::max(), std::move(callback));
}
emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
{
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read);
}
emulator_hook* hook_memory_write(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
{
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::write);
}
emulator_hook* hook_memory_execution(const uint64_t address, const size_t size,
simple_memory_hook_callback callback)
{
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::exec);
}
private:
emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size,
simple_memory_hook_callback callback, const memory_operation operation)
{
assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0);
return this->hook_memory_access(address, size, operation,
[c = std::move(callback)](const uint64_t a, const size_t s,
const uint64_t value,
memory_operation) { c(a, s, value); });
}
};

View File

@@ -7,8 +7,13 @@ class scoped_hook
scoped_hook() = default;
scoped_hook(emulator& emu, emulator_hook* hook)
: scoped_hook(emu, std::vector{hook})
{
}
scoped_hook(emulator& emu, std::vector<emulator_hook*> hooks)
: emu_(&emu),
hook_(hook)
hooks_(std::move(hooks))
{
}
@@ -31,9 +36,9 @@ class scoped_hook
{
this->remove();
this->emu_ = obj.emu_;
this->hook_ = obj.hook_;
this->hooks_ = std::move(obj.hooks_);
obj.hook_ = {};
obj.hooks_ = {};
}
return *this;
@@ -41,14 +46,22 @@ class scoped_hook
void remove()
{
if (this->hook_)
auto hooks = std::move(this->hooks_);
this->hooks_ = {};
for (auto* hook : hooks)
{
this->emu_->delete_hook(this->hook_);
this->hook_ = {};
try
{
this->emu_->delete_hook(hook);
}
catch (...)
{
}
}
}
private:
emulator* emu_{};
emulator_hook* hook_{};
std::vector<emulator_hook*> hooks_{};
};

View File

@@ -35,7 +35,9 @@ namespace
void forward_emulator(windows_emulator& win_emu)
{
const auto target = win_emu.mod_manager.executable->find_export("vulnerable");
win_emu.emu().hook_memory_execution(target, 1, [&](uint64_t, size_t, uint64_t) { win_emu.emu().stop(); });
win_emu.emu().hook_memory_execution(target, [&](uint64_t) {
win_emu.emu().stop(); //
});
run_emulation(win_emu);
}
@@ -64,7 +66,9 @@ namespace
const auto ret = emu.emu().read_stack(0);
emu.emu().hook_memory_execution(ret, 1, [&](uint64_t, size_t, uint64_t) { emu.emu().stop(); });
emu.emu().hook_memory_execution(ret, [&](uint64_t) {
emu.emu().stop(); //
});
}
void restore_emulator()

View File

@@ -7,14 +7,16 @@ using icicle_emulator = struct icicle_emulator_;
extern "C"
{
using raw_func = void(void*);
using ptr_func = void(void*, uint64_t);
using violation_func = int32_t(void*, uint64_t address, uint8_t operation, int32_t unmapped);
using data_accessor_func = void(void* user, const void* data, size_t length);
using icicle_mmio_read_func = void(void* user, uint64_t address, void* data, size_t length);
using icicle_mmio_write_func = void(void* user, uint64_t address, const void* data, size_t length);
using raw_func = void(void*);
using ptr_func = void(void*, uint64_t);
using interrupt_func = void(void*, int32_t);
using violation_func = int32_t(void*, uint64_t address, uint8_t operation, int32_t unmapped);
using data_accessor_func = void(void* user, const void* data, size_t length);
using memory_access_func = icicle_mmio_write_func;
icicle_emulator* icicle_create_emulator();
int32_t icicle_protect_memory(icicle_emulator*, uint64_t address, uint64_t length, uint8_t permissions);
int32_t icicle_map_memory(icicle_emulator*, uint64_t address, uint64_t length, uint8_t permissions);
@@ -26,9 +28,13 @@ extern "C"
int32_t icicle_save_registers(icicle_emulator*, data_accessor_func* accessor, void* accessor_data);
int32_t icicle_restore_registers(icicle_emulator*, const void* data, size_t length);
uint32_t icicle_add_syscall_hook(icicle_emulator*, raw_func* callback, void* data);
uint32_t icicle_add_execution_hook(icicle_emulator*, ptr_func* callback, void* data);
uint32_t icicle_add_interrupt_hook(icicle_emulator*, interrupt_func* callback, void* data);
uint32_t icicle_add_execution_hook(icicle_emulator*, uint64_t address, ptr_func* callback, void* data);
uint32_t icicle_add_generic_execution_hook(icicle_emulator*, ptr_func* callback, void* data);
uint32_t icicle_add_violation_hook(icicle_emulator*, violation_func* callback, void* data);
void icicle_remove_syscall_hook(icicle_emulator*, uint32_t id);
uint32_t icicle_add_read_hook(icicle_emulator*, uint64_t start, uint64_t end, memory_access_func* cb, void* data);
uint32_t icicle_add_write_hook(icicle_emulator*, uint64_t start, uint64_t end, memory_access_func* cb, void* data);
void icicle_remove_hook(icicle_emulator*, uint32_t id);
size_t icicle_read_register(icicle_emulator*, int reg, void* data, size_t length);
size_t icicle_write_register(icicle_emulator*, int reg, const void* data, size_t length);
void icicle_start(icicle_emulator*, size_t count);
@@ -77,21 +83,6 @@ namespace icicle
{
return std::make_unique<function_object<T>>(std::move(func));
}
template <typename T>
std::unique_ptr<utils::object> wrap_shared(std::shared_ptr<T> shared_ptr)
{
struct shard_wrapper : utils::object
{
std::shared_ptr<T> ptr{};
~shard_wrapper() override = default;
};
auto wrapper = std::make_unique<shard_wrapper>();
wrapper->ptr = std::move(shared_ptr);
return wrapper;
}
}
class icicle_x64_emulator : public x64_emulator
@@ -271,18 +262,21 @@ namespace icicle
emulator_hook* hook_interrupt(interrupt_hook_callback callback) override
{
// TODO
(void)callback;
return nullptr;
// throw std::runtime_error("Not implemented");
auto obj = make_function_object(std::move(callback));
auto* ptr = obj.get();
auto* wrapper = +[](void* user, const int32_t code) {
const auto& func = *static_cast<decltype(ptr)>(user);
func(code);
};
const auto id = icicle_add_interrupt_hook(this->emu_, wrapper, ptr);
this->hooks_[id] = std::move(obj);
return wrap_hook(id);
}
emulator_hook* hook_memory_violation(const uint64_t address, const size_t size,
memory_violation_hook_callback callback) override
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) override
{
(void)address;
(void)size;
auto obj = make_function_object(std::move(callback));
auto* ptr = obj.get();
auto* wrapper =
@@ -302,36 +296,66 @@ namespace icicle
return wrap_hook(id);
}
emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter,
complex_memory_hook_callback callback) override
emulator_hook* hook_memory_execution(const uint64_t address, memory_execution_hook_callback callback) override
{
if (filter == memory_permission::none)
{
return nullptr;
}
auto object = make_function_object(std::move(callback));
auto* ptr = object.get();
auto* wrapper = +[](void* user, const uint64_t addr) {
const auto& func = *static_cast<decltype(ptr)>(user);
(func)(addr);
};
const auto shared_callback = std::make_shared<complex_memory_hook_callback>(std::move(callback));
const auto id = icicle_add_execution_hook(this->emu_, address, wrapper, ptr);
this->hooks_[id] = std::move(object);
if ((filter & memory_permission::exec) == memory_permission::exec)
{
if (address != 0 || size != std::numeric_limits<size_t>::max())
{
throw std::runtime_error("Not supported!");
}
return wrap_hook(id);
}
auto* ptr = shared_callback.get();
auto wrapper = wrap_shared(shared_callback);
auto* func = +[](void* user, const uint64_t ptr) {
(*static_cast<complex_memory_hook_callback*>(user))(ptr, 0, 0, memory_permission::exec);
};
emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) override
{
auto object = make_function_object(std::move(callback));
auto* ptr = object.get();
auto* wrapper = +[](void* user, const uint64_t addr) {
const auto& func = *static_cast<decltype(ptr)>(user);
(func)(addr);
};
const auto id = icicle_add_execution_hook(this->emu_, func, ptr);
this->hooks_[id] = std::move(wrapper);
const auto id = icicle_add_generic_execution_hook(this->emu_, wrapper, ptr);
this->hooks_[id] = std::move(object);
return wrap_hook(id);
}
return wrap_hook(id);
}
return nullptr;
emulator_hook* hook_memory_read(const uint64_t address, const size_t size,
memory_access_hook_callback callback) override
{
auto obj = make_function_object(std::move(callback));
auto* ptr = obj.get();
auto* wrapper = +[](void* user, const uint64_t address, const void* data, size_t length) {
const auto& func = *static_cast<decltype(ptr)>(user);
func(address, data, length);
};
const auto id = icicle_add_read_hook(this->emu_, address, address + size, wrapper, ptr);
this->hooks_[id] = std::move(obj);
return wrap_hook(id);
}
emulator_hook* hook_memory_write(const uint64_t address, const size_t size,
memory_access_hook_callback callback) override
{
auto obj = make_function_object(std::move(callback));
auto* ptr = obj.get();
auto* wrapper = +[](void* user, const uint64_t address, const void* data, size_t length) {
const auto& func = *static_cast<decltype(ptr)>(user);
func(address, data, length);
};
const auto id = icicle_add_write_hook(this->emu_, address, address + size, wrapper, ptr);
this->hooks_[id] = std::move(obj);
return wrap_hook(id);
}
void delete_hook(emulator_hook* hook) override
@@ -343,7 +367,7 @@ namespace icicle
return;
}
icicle_remove_syscall_hook(this->emu_, id);
icicle_remove_hook(this->emu_, id);
this->hooks_.erase(entry);
}

42
src/icicle/Cargo.lock generated
View File

@@ -278,9 +278,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.10"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.59.0",
@@ -294,9 +294,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "flate2"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -370,7 +370,7 @@ dependencies = [
[[package]]
name = "icicle-cpu"
version = "0.1.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
dependencies = [
"addr2line",
"ahash",
@@ -390,7 +390,7 @@ dependencies = [
[[package]]
name = "icicle-jit"
version = "0.2.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
dependencies = [
"cranelift",
"cranelift-codegen",
@@ -408,7 +408,7 @@ dependencies = [
[[package]]
name = "icicle-linux"
version = "0.1.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
dependencies = [
"bitflags 2.9.0",
"bstr",
@@ -424,7 +424,7 @@ dependencies = [
[[package]]
name = "icicle-mem"
version = "0.3.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
dependencies = [
"tracing",
]
@@ -432,7 +432,7 @@ dependencies = [
[[package]]
name = "icicle-vm"
version = "0.2.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
dependencies = [
"anyhow",
"icicle-cpu",
@@ -458,9 +458,9 @@ checksum = "365a784774bb381e8c19edb91190a90d7f2625e057b55de2bc0f6b57bc779ff2"
[[package]]
name = "indexmap"
version = "2.8.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
@@ -519,9 +519,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.8.5"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
dependencies = [
"adler2",
]
@@ -542,14 +542,14 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.21.1"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "pcode"
version = "0.2.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
[[package]]
name = "pin-project-lite"
@@ -681,7 +681,7 @@ dependencies = [
[[package]]
name = "sleigh-compile"
version = "0.3.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
dependencies = [
"pcode",
"serde",
@@ -694,12 +694,12 @@ dependencies = [
[[package]]
name = "sleigh-parse"
version = "0.3.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
[[package]]
name = "sleigh-runtime"
version = "0.1.0"
source = "git+https://github.com/icicle-emu/icicle-emu#6e9fd3e34aec440ac92c1f49f4a70fc288949de9"
source = "git+https://github.com/momo5502/icicle-emu#ec78c91d68d0e724d2e9225778a33e447bb0d960"
dependencies = [
"pcode",
"sleigh-parse",
@@ -713,9 +713,9 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
[[package]]
name = "smallvec"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "stable_deref_trait"

View File

@@ -7,6 +7,6 @@ edition = "2024"
crate-type = ["staticlib"]
[dependencies]
icicle-vm = { git = "https://github.com/icicle-emu/icicle-emu" }
icicle-cpu = { git = "https://github.com/icicle-emu/icicle-emu" }
pcode = { git = "https://github.com/icicle-emu/icicle-emu" }
icicle-vm = { git = "https://github.com/momo5502/icicle-emu" }
icicle-cpu = { git = "https://github.com/momo5502/icicle-emu" }
pcode = { git = "https://github.com/momo5502/icicle-emu" }

File diff suppressed because it is too large Load Diff

View File

@@ -575,43 +575,48 @@
:CMPXCHG^lockx m8,Reg8 is vexMode=0 & lockx & unlock & byte=0xf; byte=0xb0; m8 & Reg8 ...
{
build lockx;
build m8;
subflags(AL,m8);
local tmp=AL-m8;
resultflags(tmp);
local diff = m8^Reg8;
m8 = m8 ^ (ZF*diff);
diff = AL ^ m8; AL = AL ^ ((ZF==0)*diff);
build unlock;
local dest = m8;
subflags(AL,dest);
local diff = AL-dest;
resultflags(diff);
if (ZF) goto <equal>;
AL = dest;
goto <inst_end>;
<equal>
m8 = Reg8;
<inst_end>
build unlock;
}
:CMPXCHG^lockx m16,Reg16 is vexMode=0 & lockx & unlock & opsize=0 & byte=0xf; byte=0xb1; m16 & Reg16 ...
{
build lockx;
build m16;
subflags(AX,m16);
local tmp=AX-m16;
resultflags(tmp);
local diff = m16^Reg16;
m16 = m16 ^ (zext(ZF) * diff);
diff = AX ^ m16;
AX = AX ^ (zext(ZF==0) * diff);
local dest = m16;
subflags(AX,dest);
local diff = AX-dest;
resultflags(diff);
if (ZF) goto <equal>;
AX = dest;
goto <inst_end>;
<equal>
m16 = Reg16;
<inst_end>
build unlock;
}
:CMPXCHG^lockx m32,Reg32 is vexMode=0 & lockx & unlock & opsize=1 & byte=0xf; byte=0xb1; m32 & Reg32 ... & check_EAX_dest ...
{
build lockx;
build m32;
#this instruction writes to either EAX or m32
#in 64-bit mode, a 32-bit register that is written to
#(and only the register that is written to)
#this instruction writes to either EAX or Rmr32
#in 64-bit mode, a 32-bit register that is written to
#(and only the register that is written to)
#must be zero-extended to 64 bits
subflags(EAX,m32);
local tmp=EAX-m32;
resultflags(tmp);
if (ZF==1) goto <equal>;
EAX = m32;
local dest = m32;
subflags(EAX,dest);
local diff = EAX-dest;
resultflags(diff);
if (ZF) goto <equal>;
EAX = dest;
build check_EAX_dest;
goto <inst_end>;
<equal>
@@ -624,26 +629,28 @@
:CMPXCHG^lockx m64,Reg64 is $(LONGMODE_ON) & vexMode=0 & lockx & unlock & opsize=2 & byte=0xf; byte=0xb1; m64 & Reg64 ...
{
build lockx;
build m64;
subflags(RAX,m64);
local tmp=RAX-m64;
resultflags(tmp);
local diff = m64^Reg64;
m64 = m64 ^ (zext(ZF) * diff);
diff = RAX ^ m64;
RAX = RAX ^ (zext(ZF==0) * diff);
build unlock;
local dest = m64;
subflags(RAX,dest);
local diff = RAX-dest;
resultflags(diff);
if (ZF) goto <equal>;
RAX = dest;
goto <inst_end>;
<equal>
m64 = Reg64;
<inst_end>
build unlock;
}
@endif
:CMPXCHG8B^lockx m64 is vexMode=0 & lockx & unlock & byte=0xf; byte=0xc7; ( mod != 0b11 & reg_opcode=1 ) ... & m64
{
build lockx;
build m64;
ZF = ((zext(EDX) << 32) | zext(EAX)) == m64;
local dest = m64;
ZF = ((zext(EDX) << 32) | zext(EAX)) == dest;
if (ZF == 1) goto <equal>;
EDX = m64(4);
EAX = m64:4;
EDX = dest(4);
EAX = dest:4;
goto <done>;
<equal>
m64 = (zext(ECX) << 32) | zext(EBX);
@@ -654,11 +661,11 @@
@ifdef IA64
:CMPXCHG16B^lockx m128 is $(LONGMODE_ON) & vexMode=0 & lockx & unlock & opsize=2 & byte=0xf; byte=0xc7; ( mod != 0b11 & reg_opcode=1 ) ... & ( m128 ) {
build lockx;
build m128;
ZF = ((zext(RDX) << 64) | zext(RAX)) == m128;
local dest = m128;
ZF = ((zext(RDX) << 64) | zext(RAX)) == dest;
if (ZF == 1) goto <equal>;
RDX = m128(8);
RAX = m128:8;
RDX = dest(8);
RAX = dest:8;
goto <done>;
<equal>
m128 = ((zext(RCX) << 64) | zext(RBX));

View File

@@ -1,3 +1,3 @@
macro conditionalAssign(dest, cond, trueVal, falseVal) {
dest = zext(cond) * trueVal | zext(!cond) * falseVal;
}
}

View File

@@ -0,0 +1,324 @@
<?xml version="1.0" encoding="UTF-8"?>
<compiler_spec>
<data_organization>
<absolute_max_alignment value="0" />
<machine_alignment value="2" />
<default_alignment value="1" />
<default_pointer_alignment value="4" />
<pointer_size value="4" />
<wchar_size value="4" /> <!-- matches go's 'rune' -->
<short_size value="2" />
<integer_size value="4" />
<long_size value="8" />
<long_long_size value="8" />
<float_size value="4" />
<double_size value="8" />
<long_double_size value="16" />
<size_alignment_map>
<entry size="1" alignment="1" />
<entry size="2" alignment="2" />
<entry size="4" alignment="4" />
<entry size="8" alignment="4" />
</size_alignment_map>
</data_organization>
<global>
<range space="ram"/>
</global>
<context_data>
</context_data>
<stackpointer register="ESP" space="ram"/>
<returnaddress>
<varnode space="stack" offset="0" size="4"/>
</returnaddress>
<default_proto>
<prototype name="abi0" extrapop="4" stackshift="4">
<input>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="4" space="stack"/>
</pentry>
</input>
<output>
</output>
<unaffected>
<register name="ESP"/>
<register name="EBP"/>
</unaffected>
</prototype>
</default_proto>
<prototype name="duffzero" extrapop="4" stackshift="4">
<input>
<pentry minsize="1" maxsize="4">
<register name="EDI"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="4">
<register name="EDI"/>
</pentry>
</output>
<killedbycall>
<register name="EDI"/>
</killedbycall>
<unaffected>
<register name="ESP"/>
<register name="EBP"/>
</unaffected>
</prototype>
<prototype name="duffcopy" extrapop="4" stackshift="4">
<input>
<pentry minsize="1" maxsize="4">
<register name="EDI"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="ESI"/>
</pentry>
</input>
<output>
</output>
<killedbycall>
<register name="EDI"/>
<register name="ESI"/>
<register name="ECX"/>
</killedbycall>
<unaffected>
<register name="ESP"/>
<register name="EBP"/>
</unaffected>
</prototype>
<prototype name="__cdeclf" extrapop="4" stackshift="4">
<input>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="4" space="stack"/>
</pentry>
</input>
<output killedbycall="true">
<pentry minsize="1" maxsize="10">
<register name="ST0"/>
</pentry>
</output>
<unaffected>
<register name="ESP"/>
<register name="EBP"/>
<register name="ESI"/>
<register name="EDI"/>
<register name="EBX"/>
</unaffected>
<killedbycall>
<register name="ECX"/>
<register name="EDX"/>
</killedbycall>
<likelytrash>
<register name="EAX"/>
</likelytrash>
</prototype>
<prototype name="__thiscall" extrapop="4" stackshift="4">
<input>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="4" space="stack"/>
</pentry>
</input>
<output killedbycall="true">
<pentry minsize="4" maxsize="10" metatype="float" extension="float">
<register name="ST0"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
<pentry minsize="5" maxsize="8">
<addr space="join" piece1="EDX" piece2="EAX"/>
</pentry>
</output>
<unaffected>
<register name="ESP"/>
<register name="EBP"/>
<register name="ESI"/>
<register name="EDI"/>
<register name="EBX"/>
</unaffected>
<killedbycall>
<register name="ECX"/>
<register name="EDX"/>
<register name="ST0"/>
<register name="ST1"/>
</killedbycall>
<likelytrash>
<register name="EAX"/>
</likelytrash>
</prototype>
<prototype name="__regparm3" extrapop="4" stackshift="4"> <!-- Used particularly by linux kernel -->
<input>
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EDX"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="ECX"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="4" space="stack"/>
</pentry>
</input>
<output killedbycall="true">
<pentry minsize="4" maxsize="10" metatype="float" extension="float">
<register name="ST0"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
<pentry minsize="5" maxsize="8">
<addr space="join" piece1="EDX" piece2="EAX"/>
</pentry>
</output>
<unaffected>
<register name="ESP"/>
<register name="EBP"/>
<register name="ESI"/>
<register name="EDI"/>
<register name="EBX"/>
</unaffected>
<killedbycall>
<register name="ECX"/>
<register name="EDX"/>
<register name="ST0"/>
<register name="ST1"/>
</killedbycall>
<likelytrash>
<register name="EAX"/>
</likelytrash>
</prototype>
<prototype name="__regparm2" extrapop="4" stackshift="4">
<input>
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EDX"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="4" space="stack"/>
</pentry>
</input>
<output killedbycall="true">
<pentry minsize="4" maxsize="10" metatype="float" extension="float">
<register name="ST0"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
<pentry minsize="5" maxsize="8">
<addr space="join" piece1="EDX" piece2="EAX"/>
</pentry>
</output>
<unaffected>
<register name="ESP"/>
<register name="EBP"/>
<register name="ESI"/>
<register name="EDI"/>
<register name="EBX"/>
</unaffected>
<killedbycall>
<register name="ECX"/>
<register name="EDX"/>
<register name="ST0"/>
<register name="ST1"/>
</killedbycall>
<likelytrash>
<register name="EAX"/>
</likelytrash>
</prototype>
<prototype name="__regparm1" extrapop="4" stackshift="4">
<input>
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="4" space="stack"/>
</pentry>
</input>
<output killedbycall="true">
<pentry minsize="4" maxsize="10" metatype="float" extension="float">
<register name="ST0"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
<pentry minsize="5" maxsize="8">
<addr space="join" piece1="EDX" piece2="EAX"/>
</pentry>
</output>
<unaffected>
<register name="ESP"/>
<register name="EBP"/>
<register name="ESI"/>
<register name="EDI"/>
<register name="EBX"/>
</unaffected>
<killedbycall>
<register name="ECX"/>
<register name="EDX"/>
<register name="ST0"/>
<register name="ST1"/>
</killedbycall>
<likelytrash>
<register name="EAX"/>
</likelytrash>
</prototype>
<prototype name="syscall" extrapop="4" stackshift="4">
<input>
<pentry minsize="1" maxsize="4">
<register name="EBX"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="ECX"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EDX"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="ESI"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EDI"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="EBP"/>
</pentry>
</input>
<output killedbycall="true">
<pentry minsize="1" maxsize="4">
<register name="EAX"/>
</pentry>
</output>
<unaffected>
<register name="EBX"/>
<register name="ECX"/>
<register name="EDX"/>
<register name="EBP"/>
<register name="EDI"/>
<register name="ESI"/>
<register name="ESP"/>
<register name="DF"/>
</unaffected>
<killedbycall>
<register name="EAX"/>
</killedbycall>
</prototype>
</compiler_spec>

View File

@@ -0,0 +1,9 @@
<golang>
<register_info versions="all">
<int_registers list=""/>
<float_registers list=""/>
<stack initialoffset="4" maxalign="4"/>
<current_goroutine register=""/>
<zero_register register=""/>
</register_info>
</golang>

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<processor_spec>
<properties>
<property key="useOperandReferenceAnalyzerSwitchTables" value="true"/>
<property key="assemblyRating:x86:LE:64:compat32" value="GOLD"/>
</properties>
<programcounter register="RIP"/>
<context_data>
<context_set space="ram">
<set name="addrsize" val="1"/>
<set name="opsize" val="1"/>
<set name="rexprefix" val="0"/>
<set name="longMode" val="0"/>
</context_set>
<tracked_set space="ram">
<set name="DF" val="0"/>
</tracked_set>
</context_data>
<register_data>
<register name="DR0" group="DEBUG"/>
<register name="DR1" group="DEBUG"/>
<register name="DR2" group="DEBUG"/>
<register name="DR3" group="DEBUG"/>
<register name="DR4" group="DEBUG"/>
<register name="DR5" group="DEBUG"/>
<register name="DR6" group="DEBUG"/>
<register name="DR7" group="DEBUG"/>
<register name="DR8" group="DEBUG"/>
<register name="DR9" group="DEBUG"/>
<register name="DR10" group="DEBUG"/>
<register name="DR11" group="DEBUG"/>
<register name="DR12" group="DEBUG"/>
<register name="DR13" group="DEBUG"/>
<register name="DR14" group="DEBUG"/>
<register name="DR15" group="DEBUG"/>
<register name="CR0" group="CONTROL"/>
<register name="CR1" group="CONTROL"/>
<register name="CR2" group="CONTROL"/>
<register name="CR3" group="CONTROL"/>
<register name="CR4" group="CONTROL"/>
<register name="CR5" group="CONTROL"/>
<register name="CR6" group="CONTROL"/>
<register name="CR7" group="CONTROL"/>
<register name="CR8" group="CONTROL"/>
<register name="CR9" group="CONTROL"/>
<register name="CR10" group="CONTROL"/>
<register name="CR11" group="CONTROL"/>
<register name="CR12" group="CONTROL"/>
<register name="CR13" group="CONTROL"/>
<register name="CR14" group="CONTROL"/>
<register name="CR15" group="CONTROL"/>
<register name="C0" group="Cx"/>
<register name="C1" group="Cx"/>
<register name="C2" group="Cx"/>
<register name="C3" group="Cx"/>
<register name="ST0" group="ST"/>
<register name="ST1" group="ST"/>
<register name="ST2" group="ST"/>
<register name="ST3" group="ST"/>
<register name="ST4" group="ST"/>
<register name="ST5" group="ST"/>
<register name="ST6" group="ST"/>
<register name="ST7" group="ST"/>
<register name="FPUControlWord" group="FPU"/>
<register name="FPUStatusWord" group="FPU"/>
<register name="FPUTagWord" group="FPU"/>
<register name="FPUDataPointer" group="FPU"/>
<register name="FPUInstructionPointer" group="FPU"/>
<register name="FPULastInstructionOpcode" group="FPU"/>
<register name="MM0" group="MMX"/>
<register name="MM1" group="MMX"/>
<register name="MM2" group="MMX"/>
<register name="MM3" group="MMX"/>
<register name="MM4" group="MMX"/>
<register name="MM5" group="MMX"/>
<register name="MM6" group="MMX"/>
<register name="MM7" group="MMX"/>
<register name="YMM0" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM1" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM2" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM3" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM4" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM5" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM6" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM7" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM8" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM9" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM10" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM11" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM12" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM13" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM14" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="YMM15" group="AVX" vector_lane_sizes="1,2,4,8"/>
<register name="XMM0" vector_lane_sizes="1,2,4,8"/>
<register name="XMM1" vector_lane_sizes="1,2,4,8"/>
<register name="XMM2" vector_lane_sizes="1,2,4,8"/>
<register name="XMM3" vector_lane_sizes="1,2,4,8"/>
<register name="XMM4" vector_lane_sizes="1,2,4,8"/>
<register name="XMM5" vector_lane_sizes="1,2,4,8"/>
<register name="XMM6" vector_lane_sizes="1,2,4,8"/>
<register name="XMM7" vector_lane_sizes="1,2,4,8"/>
<register name="XMM8" vector_lane_sizes="1,2,4,8"/>
<register name="XMM9" vector_lane_sizes="1,2,4,8"/>
<register name="XMM10" vector_lane_sizes="1,2,4,8"/>
<register name="XMM11" vector_lane_sizes="1,2,4,8"/>
<register name="XMM12" vector_lane_sizes="1,2,4,8"/>
<register name="XMM13" vector_lane_sizes="1,2,4,8"/>
<register name="XMM14" vector_lane_sizes="1,2,4,8"/>
<register name="XMM15" vector_lane_sizes="1,2,4,8"/>
<register name="CF" group="FLAGS"/>
<register name="F1" group="FLAGS"/>
<register name="PF" group="FLAGS"/>
<register name="F3" group="FLAGS"/>
<register name="AF" group="FLAGS"/>
<register name="F5" group="FLAGS"/>
<register name="ZF" group="FLAGS"/>
<register name="SF" group="FLAGS"/>
<register name="TF" group="FLAGS"/>
<register name="IF" group="FLAGS"/>
<register name="DF" group="FLAGS"/>
<register name="OF" group="FLAGS"/>
<register name="IOPL" group="FLAGS"/>
<register name="NT" group="FLAGS"/>
<register name="F15" group="FLAGS"/>
<register name="RF" group="FLAGS"/>
<register name="VM" group="FLAGS"/>
<register name="AC" group="FLAGS"/>
<register name="VIF" group="FLAGS"/>
<register name="VIP" group="FLAGS"/>
<register name="ID" group="FLAGS"/>
<register name="rflags" group="FLAGS"/>
<register name="eflags" group="FLAGS"/>
<register name="flags" group="FLAGS"/>
<register name="bit64" hidden="true"/>
<register name="segover" hidden="true"/>
<register name="repneprefx" hidden="true"/>
<register name="repprefx" hidden="true"/>
<register name="rexWprefix" hidden="true"/>
<register name="rexRprefix" hidden="true"/>
<register name="rexXprefix" hidden="true"/>
<register name="rexBprefix" hidden="true"/>
<register name="xmmTmp1" hidden="true"/>
<register name="xmmTmp1_Qa" hidden="true"/>
<register name="xmmTmp1_Da" hidden="true"/>
<register name="xmmTmp1_Db" hidden="true"/>
<register name="xmmTmp1_Qb" hidden="true"/>
<register name="xmmTmp1_Dc" hidden="true"/>
<register name="xmmTmp1_Dd" hidden="true"/>
<register name="xmmTmp2" hidden="true"/>
<register name="xmmTmp2_Qa" hidden="true"/>
<register name="xmmTmp2_Da" hidden="true"/>
<register name="xmmTmp2_Db" hidden="true"/>
<register name="xmmTmp2_Qb" hidden="true"/>
<register name="xmmTmp2_Dc" hidden="true"/>
<register name="xmmTmp2_Dd" hidden="true"/>
<register name="rexprefix" hidden="true"/>
</register_data>
</processor_spec>

View File

@@ -239,4 +239,135 @@
<register name="RBP"/>
</returnaddress>
</prototype>
<callfixup name="x86_return_thunk">
<target name="__x86_return_thunk"/>
<pcode>
<body><![CDATA[
RIP = *:8 RSP;
RSP = RSP + 8;
return [RIP];
]]></body>
</pcode>
</callfixup>
<callfixup name="fentry">
<target name="__fentry__"/>
<pcode>
<body><![CDATA[
temp:1 = 0;
]]></body>
</pcode>
</callfixup>
<callfixup name="mcount">
<target name="mcount"/>
<pcode>
<body><![CDATA[
temp:1 = 0;
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_rbp">
<target name="__x86_indirect_thunk_rbp"/>
<pcode>
<body><![CDATA[
call [RBP];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_rax">
<target name="__x86_indirect_thunk_rax"/>
<pcode>
<body><![CDATA[
call [RAX];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_rbx">
<target name="__x86_indirect_thunk_rbx"/>
<pcode>
<body><![CDATA[
call [RBX];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_rcx">
<target name="__x86_indirect_thunk_rcx"/>
<pcode>
<body><![CDATA[
call [RCX];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_rdx">
<target name="__x86_indirect_thunk_rdx"/>
<pcode>
<body><![CDATA[
call [RDX];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_r8">
<target name="__x86_indirect_thunk_r8"/>
<pcode>
<body><![CDATA[
call [R8];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_r9">
<target name="__x86_indirect_thunk_r9"/>
<pcode>
<body><![CDATA[
call [R9];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_r10">
<target name="__x86_indirect_thunk_r10"/>
<pcode>
<body><![CDATA[
call [R10];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_r11">
<target name="__x86_indirect_thunk_r11"/>
<pcode>
<body><![CDATA[
call [R11];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_r12">
<target name="__x86_indirect_thunk_r12"/>
<pcode>
<body><![CDATA[
call [R12];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_r13">
<target name="__x86_indirect_thunk_r13"/>
<pcode>
<body><![CDATA[
call [R13];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_r14">
<target name="__x86_indirect_thunk_r14"/>
<pcode>
<body><![CDATA[
call [R14];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_r15">
<target name="__x86_indirect_thunk_r15"/>
<pcode>
<body><![CDATA[
call [R15];
]]></body>
</pcode>
</callfixup>
</compiler_spec>

View File

@@ -0,0 +1,428 @@
<?xml version="1.0" encoding="UTF-8"?>
<compiler_spec>
<data_organization>
<absolute_max_alignment value="0" />
<machine_alignment value="2" />
<default_alignment value="1" />
<default_pointer_alignment value="8" />
<pointer_size value="8" />
<wchar_size value="4" /> <!-- matches go's 'rune' -->
<short_size value="2" />
<integer_size value="8" />
<long_size value="8" />
<long_long_size value="8" />
<float_size value="4" />
<double_size value="8" />
<long_double_size value="16" />
<size_alignment_map>
<entry size="1" alignment="1" />
<entry size="2" alignment="2" />
<entry size="4" alignment="4" />
<entry size="8" alignment="8" />
</size_alignment_map>
</data_organization>
<global>
<range space="ram"/>
</global>
<context_data>
</context_data>
<stackpointer register="RSP" space="ram"/>
<returnaddress>
<varnode space="stack" offset="0" size="8"/>
</returnaddress>
<default_proto>
<prototype name="abi-internal" extrapop="8" stackshift="8">
<input>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM1_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM2_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM3_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM4_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM5_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM6_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM7_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RAX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RBX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RCX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RSI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R8"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R9"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R10"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R11"/>
</pentry>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="8" space="stack"/>
</pentry>
</input>
<output>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RAX"/>
</pentry>
<pentry minsize="9" maxsize="16">
<addr space="join" piece2="RAX" piece1="RBX"/>
</pentry>
<pentry minsize="17" maxsize="24">
<addr space="join" piece3="RAX" piece2="RBX" piece1="RCX"/>
</pentry>
<pentry minsize="25" maxsize="32">
<addr space="join" piece4="RAX" piece3="RBX" piece2="RCX" piece1="RDI"/>
</pentry>
<pentry minsize="33" maxsize="40">
<addr space="join" piece5="RAX" piece4="RBX" piece3="RCX" piece2="RDI" piece1="RSI"/>
</pentry>
<pentry minsize="41" maxsize="48">
<addr space="join" piece6="RAX" piece5="RBX" piece4="RCX" piece3="RDI" piece2="RSI" piece1="R8"/>
</pentry>
<pentry minsize="49" maxsize="56">
<addr space="join" piece7="RAX" piece6="RBX" piece5="RCX" piece4="RDI" piece3="RSI" piece2="R8" piece1="R9"/>
</pentry>
<pentry minsize="57" maxsize="64">
<addr space="join" piece8="RAX" piece7="RBX" piece6="RCX" piece5="RDI" piece4="RSI" piece3="R8" piece2="R9" piece1="R10"/>
</pentry>
<pentry minsize="65" maxsize="72">
<addr space="join" piece9="RAX" piece8="RBX" piece7="RCX" piece6="RDI" piece5="RSI" piece4="R8" piece3="R9" piece2="R10" piece1="R11"/>
</pentry>
</output>
<killedbycall>
<register name="RAX"/>
<register name="RBX"/>
<register name="RCX"/>
<register name="RDI"/>
<register name="RSI"/>
<register name="R8"/>
<register name="R9"/>
<register name="R10"/>
<register name="R11"/>
</killedbycall>
<unaffected>
<register name="RSP"/>
<register name="RBP"/>
<register name="R14"/>
</unaffected>
</prototype>
</default_proto>
<prototype name="abi0" extrapop="8" stackshift="8">
<input>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="8" space="stack"/>
</pentry>
</input>
<output>
</output>
<killedbycall>
<register name="RAX"/>
<register name="RBX"/>
<register name="RCX"/>
<register name="RDI"/>
<register name="RSI"/>
<register name="R8"/>
<register name="R9"/>
<register name="R10"/>
<register name="R11"/>
</killedbycall>
<unaffected>
<register name="RSP"/>
<register name="RBP"/>
<register name="R14"/>
</unaffected>
</prototype>
<prototype name="duffzero" extrapop="8" stackshift="8">
<input>
<pentry minsize="1" maxsize="8">
<register name="RDI"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="8">
<register name="RDI"/>
</pentry>
</output>
<killedbycall>
<register name="RDI"/>
</killedbycall>
<unaffected>
<register name="RSP"/>
<register name="RBP"/>
<register name="R14"/>
</unaffected>
</prototype>
<prototype name="duffcopy" extrapop="8" stackshift="8">
<input>
<pentry minsize="1" maxsize="8">
<register name="RDI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RSI"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="8">
<register name="RDI"/>
</pentry>
<pentry minsize="9" maxsize="16">
<addr space="join" piece2="RDI" piece1="RSI"/>
</pentry>
</output>
<killedbycall>
<register name="RDI"/>
<register name="RSI"/>
</killedbycall>
<unaffected>
<register name="RAX"/>
<register name="RBX"/>
<register name="RCX"/>
<register name="RDI"/>
<register name="RSI"/>
<register name="R8"/>
<register name="R9"/>
<register name="R10"/>
<register name="R11"/>
<register name="RSP"/>
<register name="RBP"/>
<register name="R14"/>
</unaffected>
</prototype>
<prototype name="__stdcall" extrapop="8" stackshift="8">
<!-- Derived from "System V Application Binary Interface AMD64 Architecture Processor Supplement" April 2016 -->
<input>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM1_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM2_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM3_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM4_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM5_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM6_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM7_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RSI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RCX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R8"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R9"/>
</pentry>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="8" space="stack"/>
</pentry>
</input>
<output>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RAX"/>
</pentry>
<pentry minsize="9" maxsize="16">
<addr space="join" piece1="RDX" piece2="RAX"/>
</pentry>
</output>
<killedbycall>
<register name="RAX"/>
<register name="RDX"/>
<register name="XMM0"/>
</killedbycall>
<unaffected>
<register name="RBX"/>
<register name="RSP"/>
<register name="RBP"/>
<register name="R12"/>
<register name="R13"/>
<register name="R14"/>
<register name="R15"/>
</unaffected>
</prototype>
<prototype name="MSABI" extrapop="8" stackshift="8">
<input pointermax="8">
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM1_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM2_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM3_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RCX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R8"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R9"/>
</pentry>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="40" space="stack"/>
</pentry>
</input>
<output>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RAX"/>
</pentry>
</output>
<unaffected>
<varnode space="ram" offset="0" size="8"/>
<register name="RBX"/>
<register name="RBP"/>
<register name="RDI"/>
<register name="RSI"/>
<register name="RSP"/>
<register name="R12"/>
<register name="R13"/>
<register name="R14"/>
<register name="R15"/>
<register name="DF"/>
</unaffected>
<killedbycall>
<register name="RAX"/>
<register name="XMM0"/>
</killedbycall>
<localrange>
<range space="stack" first="0xfffffffffff0bdc1" last="0xffffffffffffffff"/>
<range space="stack" first="8" last="39"/>
</localrange>
</prototype>
<prototype name="syscall" extrapop="8" stackshift="8">
<input pointermax="8">
<pentry minsize="1" maxsize="8">
<register name="RDI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RSI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R10"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R8"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R9"/>
</pentry>
</input>
<output killedbycall="true">
<pentry minsize="1" maxsize="8">
<register name="RAX"/>
</pentry>
</output>
<unaffected>
<varnode space="ram" offset="0" size="8"/>
<register name="RBX"/>
<register name="RDX"/>
<register name="RBP"/>
<register name="RDI"/>
<register name="RSI"/>
<register name="RSP"/>
<register name="R8"/>
<register name="R9"/>
<register name="R10"/>
<register name="R12"/>
<register name="R13"/>
<register name="R14"/>
<register name="R15"/>
<register name="DF"/>
</unaffected>
<killedbycall>
<register name="RCX"/>
<register name="R11"/>
</killedbycall>
</prototype>
</compiler_spec>

View File

@@ -0,0 +1,10 @@
<golang>
<!-- see https://github.com/golang/go/blob/master/src/internal/abi/abi_amd64.go -->
<register_info versions="V1_17,V1_18,V1_19,V1_20"> <!-- "all", or comma list of: V1_2,V1_16,V1_17,V1_18 -->
<int_registers list="RAX,RBX,RCX,RDI,RSI,R8,R9,R10,R11"/>
<float_registers list="XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,XMM8,XMM9,XMM10,XMM11,XMM12,XMM13,XMM14"/>
<stack initialoffset="8" maxalign="8"/>
<current_goroutine register="R14"/>
<zero_register register="XMM15"/>
</register_info>
</golang>

View File

@@ -201,6 +201,7 @@
</localrange>
</prototype>
<modelalias name="__cdecl" parent="__fastcall"/>
<modelalias name="__stdcall" parent="__fastcall"/>
<callfixup name="alloca_probe">
<target name="_alloca_probe"/>
<target name="_alloca_probe2"/>

View File

@@ -9,7 +9,6 @@
<context_data>
<context_set space="ram">
<set name="addrsize" val="2"/>
<set name="bit64" val="1"/>
<set name="opsize" val="1"/>
<set name="rexprefix" val="0"/>
<set name="longMode" val="1"/>

View File

@@ -16,6 +16,7 @@
<compiler name="gcc" spec="x86gcc.cspec" id="gcc"/>
<compiler name="Borland C++" spec="x86borland.cspec" id="borlandcpp"/>
<compiler name="Delphi" spec="x86delphi.cspec" id="borlanddelphi"/>
<compiler name="golang" spec="x86-32-golang.cspec" id="golang"/>
<external_name tool="gnu" name="i386:intel"/>
<external_name tool="IDA-PRO" name="8086"/>
<external_name tool="IDA-PRO" name="80486p"/>
@@ -28,6 +29,7 @@
<external_name tool="IDA-PRO" name="p4"/>
<external_name tool="IDA-PRO" name="metapc"/>
<external_name tool="DWARF.register.mapping.file" name="x86.dwarf"/>
<external_name tool="Golang.register.info.file" name="x86-32-golang.register.info"/>
</language>
<language processor="x86"
endian="little"
@@ -61,7 +63,7 @@
<external_name tool="IDA-PRO" name="metapc"/>
<external_name tool="gnu" name="i8086"/>
<external_name tool="gdis.disassembler.options.file" name="x86-16.gdis"/>
</language>
</language>
<language processor="x86"
endian="little"
size="16"
@@ -76,8 +78,8 @@
<external_name tool="IDA-PRO" name="8086p"/>
<external_name tool="gnu" name="i8086"/>
<external_name tool="gdis.disassembler.options.file" name="x86-16.gdis"/>
</language>
<language processor="x86"
</language>
<language processor="x86"
endian="little"
size="64"
variant="default"
@@ -90,9 +92,25 @@
<compiler name="Visual Studio" spec="x86-64-win.cspec" id="windows"/>
<compiler name="clang" spec="x86-64-win.cspec" id="clangwindows"/>
<compiler name="gcc" spec="x86-64-gcc.cspec" id="gcc"/>
<compiler name="golang" spec="x86-64-golang.cspec" id="golang"/>
<external_name tool="gnu" name="i386:x86-64:intel"/>
<external_name tool="gnu" name="i386:x86-64"/>
<external_name tool="IDA-PRO" name="metapc"/>
<external_name tool="DWARF.register.mapping.file" name="x86-64.dwarf"/>
<external_name tool="Golang.register.info.file" name="x86-64-golang.register.info"/>
</language>
<language processor="x86"
endian="little"
size="64"
variant="compat32"
version="2.14"
slafile="x86-64.sla"
processorspec="x86-64-compat32.pspec"
manualindexfile="../manuals/x86.idx"
id="x86:LE:64:compat32">
<description>Intel/AMD 64-bit x86 in 32-bit compatibility mode (long mode off)</description>
<compiler name="Visual Studio" spec="x86win.cspec" id="windows"/>
<compiler name="gcc" spec="x86gcc.cspec" id="gcc"/>
<external_name tool="DWARF.register.mapping.file" name="x86.dwarf"/>
</language>
</language_definitions>

View File

@@ -1,16 +1,17 @@
<opinions>
<!-- NOTE: variant="default" is specified for 64-bit to give preference to the default variant -->
<constraint loader="Portable Executable (PE)">
<constraint compilerSpecID="windows">
<constraint primary="332" processor="x86" endian="little" size="32" />
<constraint primary="333" processor="x86" endian="little" size="32" />
<constraint primary="334" processor="x86" endian="little" size="32" />
<constraint primary="34404" processor="x86" endian="little" size="64" />
<constraint primary="34404" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint compilerSpecID="clangwindows">
<constraint primary="332" secondary="clang" processor="x86" endian="little" size="32" />
<constraint primary="333" secondary="clang" processor="x86" endian="little" size="32" />
<constraint primary="334" secondary="clang" processor="x86" endian="little" size="32" />
<constraint primary="34404" secondary="clang" processor="x86" endian="little" size="64" />
<constraint primary="34404" secondary="clang" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint compilerSpecID="borlandcpp">
<constraint primary="332" secondary="borlandcpp" processor="x86" endian="little" size="32" />
@@ -22,16 +23,20 @@
<constraint primary="333" secondary="borlanddelphi" processor="x86" endian="little" size="32" />
<constraint primary="334" secondary="borlanddelphi" processor="x86" endian="little" size="32" />
</constraint>
<constraint compilerSpecID="golang">
<constraint primary="332" secondary="golang" processor="x86" endian="little" size="32" />
<constraint primary="34404" secondary="golang" processor="x86" endian="little" size="64" variant="default" />
</constraint>
</constraint>
<constraint loader="Debug Symbols (DBG)" compilerSpecID="windows">
<constraint primary="332" processor="x86" endian="little" size="32" />
<constraint primary="333" processor="x86" endian="little" size="32" />
<constraint primary="334" processor="x86" endian="little" size="32" />
<constraint primary="34404" processor="x86" endian="little" size="64" />
<constraint primary="34404" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint loader="Executable and Linking Format (ELF)" compilerSpecID="gcc">
<constraint primary="3" processor="x86" endian="little" size="32" />
<constraint primary="62" processor="x86" endian="little" size="64" />
<constraint primary="62" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint loader="Module Definition (DEF)" compilerSpecID="windows">
<constraint primary="0" processor="x86" endian="little" size="32" />
@@ -47,18 +52,18 @@
</constraint>
<constraint loader="Mac OS X Mach-O" compilerSpecID="gcc">
<constraint primary="7" processor="x86" endian="little" size="32" />
<constraint primary="16777223" processor="x86" endian="little" size="64" />
<constraint primary="16777223" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint loader="DYLD Cache" compilerSpecID="gcc">
<constraint primary="x86_64" processor="x86" endian="little" size="64" />
<constraint primary="x86_64" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint loader="Common Object File Format (COFF)" compilerSpecID="gcc">
<constraint primary="332" processor="x86" endian="little" size="32" />
<constraint primary="-31132" processor="x86" endian="little" size="64" />
<constraint primary="-31132" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint loader="MS Common Object File Format (COFF)" compilerSpecID="windows">
<constraint primary="332" processor="x86" endian="little" size="32" />
<constraint primary="-31132" processor="x86" endian="little" size="64" />
<constraint primary="-31132" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint loader="Assembler Output (AOUT)" compilerSpecID="gcc">
<constraint primary="134" processor="x86" endian="little" size="32" />

View File

@@ -331,6 +331,17 @@
</pcode>
</callfixup>
<callfixup name="get_pc_thunk.bp">
<target name="__i686.get_pc_thunk.bp"/>
<target name="__x86.get_pc_thunk.bp"/>
<pcode>
<body><![CDATA[
EBP = * ESP;
ESP = ESP + 4;
]]></body>
</pcode>
</callfixup>
<callfixup name="get_pc_thunk_bx">
<target name="__i686.get_pc_thunk.bx"/>
<target name="__x86.get_pc_thunk.bx"/>
@@ -364,7 +375,7 @@
</pcode>
</callfixup>
<callfixup name="get_pc_thunk_si">
<callfixup name="get_pc_thunk_si">
<target name="__i686.get_pc_thunk.si"/>
<target name="__x86.get_pc_thunk.si"/>
<pcode>
@@ -374,4 +385,79 @@
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_return_thunk">
<target name="__x86_return_thunk"/>
<pcode>
<body><![CDATA[
EIP = *:4 ESP;
ESP = ESP + 4;
return [EIP];
]]></body>
</pcode>
</callfixup>
<callfixup name="fentry">
<target name="__fentry__"/>
<pcode>
<body><![CDATA[
temp:1 = 0;
]]></body>
</pcode>
</callfixup>
<callfixup name="mcount">
<target name="mcount"/>
<pcode>
<body><![CDATA[
temp:1 = 0;
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_ebp">
<target name="__x86_indirect_thunk_ebp"/>
<pcode>
<body><![CDATA[
call [EBP];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_eax">
<target name="__x86_indirect_thunk_eax"/>
<pcode>
<body><![CDATA[
call [EAX];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_ebx">
<target name="__x86_indirect_thunk_ebx"/>
<pcode>
<body><![CDATA[
call [EBX];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_ecx">
<target name="__x86_indirect_thunk_ecx"/>
<pcode>
<body><![CDATA[
call [ECX];
]]></body>
</pcode>
</callfixup>
<callfixup name="x86_indirect_thunk_edx">
<target name="__x86_indirect_thunk_edx"/>
<pcode>
<body><![CDATA[
call [EDX];
]]></body>
</pcode>
</callfixup>
</compiler_spec>

View File

@@ -19,7 +19,7 @@
<entry size="1" alignment="1" />
<entry size="2" alignment="2" />
<entry size="4" alignment="4" />
<entry size="8" alignment="4" />
<entry size="8" alignment="8" />
</size_alignment_map>
<bitfield_packing>
<use_MS_convention value="true"/>
@@ -377,4 +377,12 @@
</pcode>
</callfixup>
<callfixup name="__RTC_CheckEsp">
<target name="__RTC_CheckEsp"/>
<pcode>
<body><![CDATA[
temp:4 = 0;
]]></body>
</pcode>
</callfixup>
</compiler_spec>

View File

@@ -1,5 +1,5 @@
use icicle_cpu::ValueSource;
use icicle_cpu::ExceptionCode;
use icicle_cpu::ValueSource;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use crate::registers;
@@ -46,8 +46,10 @@ enum HookType {
Syscall = 1,
Read,
Write,
Execute,
ExecuteGeneric,
ExecuteSpecific,
Violation,
Interrupt,
Unknown,
}
@@ -123,20 +125,107 @@ impl icicle_vm::CodeInjector for InstructionHookInjector {
}
}
std::mem::swap(
&mut tmp_block.instructions,
&mut block.pcode.instructions,
);
std::mem::swap(&mut tmp_block.instructions, &mut block.pcode.instructions);
}
}
}
struct ExecutionHooks {
stop: Rc<RefCell<bool>>,
generic_hooks: HookContainer<dyn Fn(u64)>,
specific_hooks: HookContainer<dyn Fn(u64)>,
address_mapping: HashMap<u64, Vec<u32>>,
}
impl ExecutionHooks {
pub fn new(stop_value: Rc<RefCell<bool>>) -> Self {
Self {
stop: stop_value,
generic_hooks: HookContainer::new(),
specific_hooks: HookContainer::new(),
address_mapping: HashMap::new(),
}
}
fn run_hooks(&self, address: u64) {
for (_key, func) in self.generic_hooks.get_hooks() {
func(address);
}
let mapping = self.address_mapping.get(&address);
if mapping.is_none(){
return;
}
for id in mapping.unwrap() {
let func = self.specific_hooks.get_hooks().get(&id);
if func.is_some() {
func.unwrap()(address);
}
}
}
pub fn execute(&mut self,cpu: &mut icicle_cpu::Cpu, address: u64) {
self.run_hooks(address);
if *self.stop.borrow() {
cpu.exception.code = ExceptionCode::InstructionLimit as u32;
cpu.exception.value = address;
}
}
pub fn add_generic_hook(&mut self, callback: Box<dyn Fn(u64)>) -> u32 {
self.generic_hooks.add_hook(callback)
}
pub fn add_specific_hook(&mut self, address: u64, callback: Box<dyn Fn(u64)>) -> u32 {
let id = self.specific_hooks.add_hook(callback);
let mapping = self.address_mapping.entry(address).or_insert_with(Vec::new);
mapping.push(id);
return id;
}
pub fn remove_generic_hook(&mut self, id: u32) {
self.generic_hooks.remove_hook(id);
}
pub fn remove_specific_hook(&mut self, id: u32) {
self.address_mapping.retain(|_, vec| {
vec.retain(|&x| x != id);
!vec.is_empty()
});
self.specific_hooks.remove_hook(id);
}
}
pub struct IcicleEmulator {
executing_thread: std::thread::ThreadId,
vm: icicle_vm::Vm,
reg: registers::X64RegisterNodes,
syscall_hooks: HookContainer<dyn Fn()>,
interrupt_hooks: HookContainer<dyn Fn(i32)>,
violation_hooks: HookContainer<dyn Fn(u64, u8, bool) -> bool>,
execution_hooks: Rc<RefCell<HookContainer<dyn Fn(u64)>>>,
execution_hooks: Rc<RefCell<ExecutionHooks>>,
stop: Rc<RefCell<bool>>,
}
struct MemoryHook {
callback: Box<dyn Fn(u64, &[u8])>,
}
impl icicle_cpu::mem::WriteHook for MemoryHook {
fn write(&mut self, _mem: &mut icicle_cpu::Mmu, addr: u64, value: &[u8]) {
(self.callback)(addr, value);
}
}
impl icicle_cpu::mem::ReadAfterHook for MemoryHook {
fn read(&mut self, _mem: &mut icicle_cpu::Mmu, addr: u64, value: &[u8]) {
(self.callback)(addr, value);
}
}
pub struct MmioHandler {
@@ -171,23 +260,25 @@ impl icicle_cpu::mem::IoMemory for MmioHandler {
impl IcicleEmulator {
pub fn new() -> Self {
let mut virtual_machine = create_x64_vm();
let exec_hooks: Rc<RefCell<HookContainer<dyn Fn(u64)>>> = Rc::new(RefCell::new(HookContainer::new()));
let stop_value = Rc::new(RefCell::new(false));
let exec_hooks = Rc::new(RefCell::new(ExecutionHooks::new(stop_value.clone())));
let exec_hooks_clone = Rc::clone(&exec_hooks);
let hook = icicle_cpu::InstHook::new(move |_: &mut icicle_cpu::Cpu, addr: u64| {
for (_key, func) in exec_hooks_clone.borrow().get_hooks() {
func(addr);
}
let hook = icicle_cpu::InstHook::new(move |cpu: &mut icicle_cpu::Cpu, addr: u64| {
exec_hooks_clone.borrow_mut().execute(cpu, addr);
});
let hook = virtual_machine.cpu.add_hook(hook);
virtual_machine.add_injector(InstructionHookInjector { hook });
Self {
stop: stop_value,
executing_thread: std::thread::current().id(),
reg: registers::X64RegisterNodes::new(&virtual_machine.cpu.arch),
vm: virtual_machine,
syscall_hooks: HookContainer::new(),
interrupt_hooks: HookContainer::new(),
violation_hooks: HookContainer::new(),
execution_hooks: exec_hooks,
}
@@ -198,12 +289,20 @@ impl IcicleEmulator {
}
pub fn start(&mut self, count: u64) {
self.executing_thread = std::thread::current().id();
self.vm.icount_limit = match count {
0 => u64::MAX,
_ => self.vm.cpu.icount + count,
_ => self.vm.cpu.icount.saturating_add(count),
};
loop {
self.vm.cpu.block_id = u64::MAX;
self.vm.cpu.block_offset = 0;
self.vm.cpu.pending_exception = None;
self.vm.cpu.exception.clear();
*self.stop.borrow_mut() = false;
let reason = self.vm.run();
match reason {
@@ -211,14 +310,22 @@ impl IcicleEmulator {
icicle_vm::VmExit::UnhandledException((code, value)) => {
let continue_execution = self.handle_exception(code, value);
if !continue_execution {
break
break;
}
},
}
_ => break,
};
}
}
fn handle_interrupt(&self, code: i32) -> bool {
for (_key, func) in self.interrupt_hooks.get_hooks() {
func(code);
}
return true;
}
fn handle_exception(&mut self, code: ExceptionCode, value: u64) -> bool {
let continue_execution = match code {
ExceptionCode::Syscall => self.handle_syscall(),
@@ -226,8 +333,9 @@ impl IcicleEmulator {
ExceptionCode::WritePerm => self.handle_violation(value, FOREIGN_WRITE, false),
ExceptionCode::ReadUnmapped => self.handle_violation(value, FOREIGN_READ, true),
ExceptionCode::WriteUnmapped => self.handle_violation(value, FOREIGN_WRITE, true),
ExceptionCode::ExecViolation => self.handle_violation(value, FOREIGN_EXEC, true),
_ => false
ExceptionCode::InvalidInstruction => self.handle_interrupt(6),
ExceptionCode::DivisionException => self.handle_interrupt(0),
_ => false,
};
return continue_execution;
@@ -242,13 +350,13 @@ impl IcicleEmulator {
let mut continue_execution = true;
for (_key, func) in self.violation_hooks.get_hooks() {
continue_execution &= func(address, permission, unmapped );
continue_execution &= func(address, permission, unmapped);
}
return continue_execution;
}
fn handle_syscall(&mut self) -> bool{
fn handle_syscall(&mut self) -> bool {
for (_key, func) in self.syscall_hooks.get_hooks() {
func();
}
@@ -258,17 +366,26 @@ impl IcicleEmulator {
}
pub fn stop(&mut self) {
self.vm.icount_limit = 0;
self.vm.icount_limit = 0;
if self.executing_thread == std::thread::current().id() {
*self.stop.borrow_mut() = true;
}
}
pub fn add_violation_hook(&mut self, callback: Box<dyn Fn(u64, u8, bool) -> bool>) -> u32 {
let hook_id = self.violation_hooks.add_hook(callback);
return qualify_hook_id(hook_id, HookType::Violation);
}
pub fn add_execution_hook(&mut self, address:u64, callback: Box<dyn Fn(u64)>) -> u32 {
let hook_id = self.execution_hooks.borrow_mut().add_specific_hook(address, callback);
return qualify_hook_id(hook_id, HookType::ExecuteSpecific);
}
pub fn add_execution_hook(&mut self, callback: Box<dyn Fn(u64)>) -> u32 {
let hook_id = self.execution_hooks.borrow_mut().add_hook(callback);
return qualify_hook_id(hook_id, HookType::Execute);
pub fn add_generic_execution_hook(&mut self, callback: Box<dyn Fn(u64)>) -> u32 {
let hook_id = self.execution_hooks.borrow_mut().add_generic_hook(callback);
return qualify_hook_id(hook_id, HookType::ExecuteGeneric);
}
pub fn add_syscall_hook(&mut self, callback: Box<dyn Fn()>) -> u32 {
@@ -276,13 +393,50 @@ impl IcicleEmulator {
return qualify_hook_id(hook_id, HookType::Syscall);
}
pub fn add_interrupt_hook(&mut self, callback: Box<dyn Fn(i32)>) -> u32 {
let hook_id = self.interrupt_hooks.add_hook(callback);
return qualify_hook_id(hook_id, HookType::Interrupt);
}
pub fn add_read_hook(
&mut self,
start: u64,
end: u64,
callback: Box<dyn Fn(u64, &[u8])>,
) -> u32 {
let id = self.get_mem().add_read_after_hook(start, end, Box::new(MemoryHook { callback }));
if id.is_none() {
return 0;
}
return qualify_hook_id(id.unwrap(), HookType::Read);
}
pub fn add_write_hook(
&mut self,
start: u64,
end: u64,
callback: Box<dyn Fn(u64, &[u8])>,
) -> u32 {
let id = self.get_mem().add_write_hook(start, end, Box::new(MemoryHook { callback }));
if id.is_none() {
return 0;
}
return qualify_hook_id(id.unwrap(), HookType::Write);
}
pub fn remove_hook(&mut self, id: u32) {
let (hook_id, hook_type) = split_hook_id(id);
match hook_type {
HookType::Syscall => self.syscall_hooks.remove_hook(hook_id),
HookType::Violation => self.violation_hooks.remove_hook(hook_id),
HookType::Execute => self.execution_hooks.borrow_mut().remove_hook(hook_id),
HookType::Interrupt => self.interrupt_hooks.remove_hook(hook_id),
HookType::ExecuteGeneric => self.execution_hooks.borrow_mut().remove_generic_hook(hook_id),
HookType::ExecuteSpecific => self.execution_hooks.borrow_mut().remove_specific_hook(hook_id),
HookType::Read => {self.get_mem().remove_read_after_hook(hook_id);()},
HookType::Write => {self.get_mem().remove_write_hook(hook_id);()},
_ => {}
}
}
@@ -377,7 +531,7 @@ impl IcicleEmulator {
};
}
pub fn read_register(&mut self, reg: registers::X64Register, buffer: &mut [u8]) -> usize {
fn read_generic_register(&mut self, reg: registers::X64Register, buffer: &mut [u8]) -> usize {
let reg_node = self.reg.get_node(reg);
let res = self.vm.cpu.read_dynamic(pcode::Value::Var(reg_node));
@@ -389,7 +543,58 @@ impl IcicleEmulator {
return reg_node.size.into();
}
fn read_flags<T>(&mut self, data: &mut [u8]) -> usize
{
const REAL_SIZE: usize = std::mem::size_of::<u64>();
let limit: usize = std::mem::size_of::<T>();
let size = std::cmp::min(REAL_SIZE, limit);
let flags: u64 = self.reg.get_flags(&mut self.vm.cpu);
let copy_size = std::cmp::min(data.len(), size);
data[..copy_size].copy_from_slice(&flags.to_ne_bytes()[..copy_size]);
return limit;
}
pub fn read_register(&mut self, reg: registers::X64Register, data: &mut [u8]) -> usize {
match reg {
registers::X64Register::Rflags => self.read_flags::<u64>(data),
registers::X64Register::Eflags => self.read_flags::<u32>(data),
registers::X64Register::Flags => self.read_flags::<u16>(data),
_ => self.read_generic_register(reg, data),
}
}
fn write_flags<T>(&mut self, data: &[u8]) -> usize
{
const REAL_SIZE: usize = std::mem::size_of::<u64>();
let limit: usize = std::mem::size_of::<T>();
let size = std::cmp::min(REAL_SIZE, limit);
let copy_size = std::cmp::min(data.len(), size);
let mut buffer = [0u8; REAL_SIZE];
self.read_flags::<u64>(&mut buffer);
buffer[..copy_size].copy_from_slice(&data[..copy_size]);
let flags = u64::from_ne_bytes(buffer);
self.reg.set_flags(&mut self.vm.cpu, flags);
return limit;
}
pub fn write_register(&mut self, reg: registers::X64Register, data: &[u8]) -> usize {
match reg {
registers::X64Register::Rflags => self.write_flags::<u64>(data),
registers::X64Register::Eflags => self.write_flags::<u32>(data),
registers::X64Register::Flags => self.write_flags::<u16>(data),
_ => self.write_generic_register(reg, data),
}
}
fn write_generic_register(&mut self, reg: registers::X64Register, data: &[u8]) -> usize {
let reg_node = self.reg.get_node(reg);
let mut buffer = [0u8; 32];
@@ -399,71 +604,25 @@ impl IcicleEmulator {
//let value = icicle_cpu::regs::DynamicValue::new(buffer, reg_node.size.into());
//self.vm.cpu.write_trunc(reg_node, value);
let cpu = &mut self.vm.cpu;
match reg_node.size {
1 => self
.vm
.cpu
.write_var::<[u8; 1]>(reg_node, buffer[..1].try_into().expect("")),
2 => self
.vm
.cpu
.write_var::<[u8; 2]>(reg_node, buffer[..2].try_into().expect("")),
3 => self
.vm
.cpu
.write_var::<[u8; 3]>(reg_node, buffer[..3].try_into().expect("")),
4 => self
.vm
.cpu
.write_var::<[u8; 4]>(reg_node, buffer[..4].try_into().expect("")),
5 => self
.vm
.cpu
.write_var::<[u8; 5]>(reg_node, buffer[..5].try_into().expect("")),
6 => self
.vm
.cpu
.write_var::<[u8; 6]>(reg_node, buffer[..6].try_into().expect("")),
7 => self
.vm
.cpu
.write_var::<[u8; 7]>(reg_node, buffer[..7].try_into().expect("")),
8 => self
.vm
.cpu
.write_var::<[u8; 8]>(reg_node, buffer[..8].try_into().expect("")),
9 => self
.vm
.cpu
.write_var::<[u8; 9]>(reg_node, buffer[..9].try_into().expect("")),
10 => self
.vm
.cpu
.write_var::<[u8; 10]>(reg_node, buffer[..10].try_into().expect("")),
11 => self
.vm
.cpu
.write_var::<[u8; 11]>(reg_node, buffer[..11].try_into().expect("")),
12 => self
.vm
.cpu
.write_var::<[u8; 12]>(reg_node, buffer[..12].try_into().expect("")),
13 => self
.vm
.cpu
.write_var::<[u8; 13]>(reg_node, buffer[..13].try_into().expect("")),
14 => self
.vm
.cpu
.write_var::<[u8; 14]>(reg_node, buffer[..14].try_into().expect("")),
15 => self
.vm
.cpu
.write_var::<[u8; 15]>(reg_node, buffer[..15].try_into().expect("")),
16 => self
.vm
.cpu
.write_var::<[u8; 16]>(reg_node, buffer[..16].try_into().expect("")),
1 => cpu.write_var::<[u8; 1]>(reg_node, buffer[..1].try_into().unwrap()),
2 => cpu.write_var::<[u8; 2]>(reg_node, buffer[..2].try_into().unwrap()),
3 => cpu.write_var::<[u8; 3]>(reg_node, buffer[..3].try_into().unwrap()),
4 => cpu.write_var::<[u8; 4]>(reg_node, buffer[..4].try_into().unwrap()),
5 => cpu.write_var::<[u8; 5]>(reg_node, buffer[..5].try_into().unwrap()),
6 => cpu.write_var::<[u8; 6]>(reg_node, buffer[..6].try_into().unwrap()),
7 => cpu.write_var::<[u8; 7]>(reg_node, buffer[..7].try_into().unwrap()),
8 => cpu.write_var::<[u8; 8]>(reg_node, buffer[..8].try_into().unwrap()),
9 => cpu.write_var::<[u8; 9]>(reg_node, buffer[..9].try_into().unwrap()),
10 => cpu.write_var::<[u8; 10]>(reg_node, buffer[..10].try_into().unwrap()),
11 => cpu.write_var::<[u8; 11]>(reg_node, buffer[..11].try_into().unwrap()),
12 => cpu.write_var::<[u8; 12]>(reg_node, buffer[..12].try_into().unwrap()),
13 => cpu.write_var::<[u8; 13]>(reg_node, buffer[..13].try_into().unwrap()),
14 => cpu.write_var::<[u8; 14]>(reg_node, buffer[..14].try_into().unwrap()),
15 => cpu.write_var::<[u8; 15]>(reg_node, buffer[..15].try_into().unwrap()),
16 => cpu.write_var::<[u8; 16]>(reg_node, buffer[..16].try_into().unwrap()),
_ => panic!("invalid dynamic value size"),
}

View File

@@ -41,6 +41,8 @@ type DataFunction = extern "C" fn(*mut c_void, *const c_void, usize);
type MmioReadFunction = extern "C" fn(*mut c_void, u64, *mut c_void, usize);
type MmioWriteFunction = extern "C" fn(*mut c_void, u64, *const c_void, usize);
type ViolationFunction = extern "C" fn(*mut c_void, u64, u8, i32) -> i32;
type InterruptFunction = extern "C" fn(*mut c_void, i32);
type MemoryAccessFunction = MmioWriteFunction;
#[unsafe(no_mangle)]
pub fn icicle_map_mmio(
@@ -164,6 +166,16 @@ pub fn icicle_read_memory(ptr: *mut c_void, address: u64, data: *mut c_void, siz
}
}
#[unsafe(no_mangle)]
pub fn icicle_add_interrupt_hook(ptr: *mut c_void, callback: InterruptFunction, data: *mut c_void) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_interrupt_hook(Box::new(
move |code: i32| callback(data, code),
));
}
}
#[unsafe(no_mangle)]
pub fn icicle_add_violation_hook(ptr: *mut c_void, callback: ViolationFunction, data: *mut c_void) -> u32 {
unsafe {
@@ -181,6 +193,30 @@ pub fn icicle_add_violation_hook(ptr: *mut c_void, callback: ViolationFunction,
}
}
#[unsafe(no_mangle)]
pub fn icicle_add_read_hook(ptr: *mut c_void, start: u64, end: u64, callback: MemoryAccessFunction, user: *mut c_void) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_read_hook(start, end, Box::new(
move |address: u64, data: &[u8]| {
callback(user, address, data.as_ptr() as *const c_void, data.len());
},
));
}
}
#[unsafe(no_mangle)]
pub fn icicle_add_write_hook(ptr: *mut c_void, start: u64, end: u64, callback: MemoryAccessFunction, user: *mut c_void) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_write_hook(start, end, Box::new(
move |address: u64, data: &[u8]| {
callback(user, address, data.as_ptr() as *const c_void, data.len());
},
));
}
}
#[unsafe(no_mangle)]
pub fn icicle_add_syscall_hook(ptr: *mut c_void, callback: RawFunction, data: *mut c_void) -> u32 {
unsafe {
@@ -190,15 +226,23 @@ pub fn icicle_add_syscall_hook(ptr: *mut c_void, callback: RawFunction, data: *m
}
#[unsafe(no_mangle)]
pub fn icicle_add_execution_hook(ptr: *mut c_void, callback: PtrFunction, data: *mut c_void) -> u32 {
pub fn icicle_add_generic_execution_hook(ptr: *mut c_void, callback: PtrFunction, data: *mut c_void) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_execution_hook(Box::new(move |ptr: u64| callback(data, ptr)));
return emulator.add_generic_execution_hook(Box::new(move |ptr: u64| callback(data, ptr)));
}
}
#[unsafe(no_mangle)]
pub fn icicle_remove_syscall_hook(ptr: *mut c_void, id: u32) {
pub fn icicle_add_execution_hook(ptr: *mut c_void, address: u64, callback: PtrFunction, data: *mut c_void) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_execution_hook(address, Box::new(move |ptr: u64| callback(data, ptr)));
}
}
#[unsafe(no_mangle)]
pub fn icicle_remove_hook(ptr: *mut c_void, id: u32) {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
emulator.remove_hook(id);

View File

@@ -262,7 +262,6 @@ pub(crate) struct X64RegisterNodes {
r14: pcode::VarNode,
r15: pcode::VarNode,
rip: pcode::VarNode,
eflags: pcode::VarNode,
cs: pcode::VarNode,
ds: pcode::VarNode,
es: pcode::VarNode,
@@ -467,18 +466,22 @@ pub(crate) struct X64RegisterNodes {
mxcsr: pcode::VarNode,
fs_base: pcode::VarNode,
gs_base: pcode::VarNode,
flags: pcode::VarNode,
rflags: pcode::VarNode,
fip: pcode::VarNode,
//fcs: pcode::VarNode,
fcs: pcode::VarNode,
fdp: pcode::VarNode,
//fds: pcode::VarNode,
fds: pcode::VarNode,
fop: pcode::VarNode,
flags: Vec<pcode::VarNode>,
}
impl X64RegisterNodes {
pub fn new(arch: &icicle_cpu::Arch) -> Self {
let r = |name: &str| arch.sleigh.get_reg(name).unwrap().var;
let nodes = [
"CF", "F1", "PF", "F3", "AF", "F5", "ZF", "SF", "TF", "IF", "DF", "OF", "IOPL", "NT",
"F15", "RF", "VM", "AC", "VIF", "VIP", "ID",
];
Self {
rax: r("RAX"),
rbx: r("RBX"),
@@ -497,7 +500,6 @@ impl X64RegisterNodes {
r14: r("R14"),
r15: r("R15"),
rip: r("RIP"),
eflags: r("eflags"),
cs: r("CS"),
ds: r("DS"),
es: r("ES"),
@@ -699,16 +701,33 @@ impl X64RegisterNodes {
fpcw: r("FPUControlWord"),
fptag: r("FPUTagWord"),
mxcsr: r("MXCSR"),
flags: r("flags"),
rflags: r("rflags"),
fip: r("FPUInstructionPointer"),
fdp: r("FPUDataPointer"),
fop: r("FPULastInstructionOpcode"),
/*fds: r("FDS"),
msr: r("MSR"),
fcs: r("FCS"),*/
fds: r("DS"), // ?
fcs: r("CS"), // ?
//msr: r("MSR"),
fs_base: r("FS_OFFSET"),
gs_base: r("GS_OFFSET"),
flags: nodes.map(|name: &str| r(name)).to_vec(),
}
}
pub fn get_flags(&self, cpu: &mut icicle_cpu::Cpu) -> u64 {
let mut res: u64 = 0;
for (index, element) in self.flags.iter().enumerate() {
let flag = cpu.read_reg(*element);
res |= (flag & 1) << index;
}
res
}
pub fn set_flags(&self, cpu: &mut icicle_cpu::Cpu, value: u64) {
for (index, element) in self.flags.iter().enumerate() {
let flag = (value >> index) & 1;
cpu.write_reg(*element, flag);
}
}
@@ -731,7 +750,6 @@ impl X64RegisterNodes {
X64Register::R14 => self.r14,
X64Register::R15 => self.r15,
X64Register::Rip => self.rip,
X64Register::Eflags => self.eflags,
X64Register::Cs => self.cs,
X64Register::Ds => self.ds,
X64Register::Es => self.es,
@@ -936,12 +954,10 @@ impl X64RegisterNodes {
X64Register::Mxcsr => self.mxcsr,
X64Register::FsBase => self.fs_base,
X64Register::GsBase => self.gs_base,
X64Register::Flags => self.flags,
X64Register::Rflags => self.rflags,
X64Register::Fip => self.fip,
//X64Register::Fcs => self.fcs,
X64Register::Fcs => self.fcs,
X64Register::Fdp => self.fdp,
//X64Register::Fds => self.fds,
X64Register::Fds => self.fds,
X64Register::Fop => self.fop,
_ => panic!("Unsupported register"),
}

View File

@@ -60,6 +60,7 @@ namespace unicorn
{
case UC_MEM_READ:
case UC_MEM_READ_PROT:
case UC_MEM_READ_AFTER:
case UC_MEM_READ_UNMAPPED:
return memory_operation::read;
case UC_MEM_WRITE:
@@ -175,63 +176,6 @@ namespace unicorn
size_t size_{};
};
void add_read_hook(uc_engine* uc, const uint64_t address, const size_t size, hook_container& container,
const std::shared_ptr<complex_memory_hook_callback>& callback)
{
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
[callback](uc_engine*, const uc_mem_type type, const uint64_t address, const int size, const int64_t) {
const auto operation = map_memory_operation(type);
if (operation != memory_permission::none)
{
(*callback)(address, static_cast<uint64_t>(size), 0, operation);
}
});
unicorn_hook hook{uc};
uce(uc_hook_add(uc, hook.make_reference(), UC_HOOK_MEM_READ, wrapper.get_function(),
wrapper.get_user_data(), address, address + size));
container.add(std::move(wrapper), std::move(hook));
}
void add_write_hook(uc_engine* uc, const uint64_t address, const size_t size, hook_container& container,
const std::shared_ptr<complex_memory_hook_callback>& callback)
{
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
[callback](uc_engine*, const uc_mem_type type, const uint64_t address, const int size,
const uint64_t value) {
const auto operation = map_memory_operation(type);
if (operation != memory_permission::none)
{
(*callback)(address, static_cast<uint64_t>(size), value, operation);
}
});
unicorn_hook hook{uc};
uce(uc_hook_add(uc, hook.make_reference(), UC_HOOK_MEM_WRITE, wrapper.get_function(),
wrapper.get_user_data(), address, address + size));
container.add(std::move(wrapper), std::move(hook));
}
void add_exec_hook(uc_engine* uc, const uint64_t address, const size_t size, hook_container& container,
const std::shared_ptr<complex_memory_hook_callback>& callback)
{
function_wrapper<void, uc_engine*, uint64_t, uint32_t> wrapper(
[callback](uc_engine*, const uint64_t address, const uint32_t size) {
(*callback)(address, size, 0, memory_permission::exec);
});
unicorn_hook hook{uc};
uce(uc_hook_add(uc, hook.make_reference(), UC_HOOK_CODE, wrapper.get_function(), wrapper.get_user_data(),
address, address + size));
container.add(std::move(wrapper), std::move(hook));
}
basic_block map_block(const uc_tb& translation_block)
{
basic_block block{};
@@ -439,7 +383,7 @@ namespace unicorn
uce(uc_mem_protect(*this, address, size, static_cast<uint32_t>(permissions)));
}
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override
emulator_hook* hook_instruction(const int instruction_type, instruction_hook_callback callback) override
{
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
@@ -537,8 +481,7 @@ namespace unicorn
return result;
}
emulator_hook* hook_memory_violation(uint64_t address, size_t size,
memory_violation_hook_callback callback) override
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) override
{
function_wrapper<bool, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
[c = std::move(callback), this](uc_engine*, const uc_mem_type type, const uint64_t address,
@@ -573,7 +516,7 @@ namespace unicorn
auto container = std::make_unique<hook_container>();
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_INVALID, wrapper.get_function(),
wrapper.get_user_data(), address, size));
wrapper.get_user_data(), 0, std::numeric_limits<uint64_t>::max()));
container->add(std::move(wrapper), std::move(hook));
@@ -582,38 +525,93 @@ namespace unicorn
return result;
}
emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter,
complex_memory_hook_callback callback) override
emulator_hook* hook_memory_execution(const uint64_t address, const uint64_t size,
memory_execution_hook_callback callback)
{
if (filter == memory_permission::none)
{
return nullptr;
}
auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address,
const uint32_t /*size*/) {
c(address); //
};
const auto shared_callback = std::make_shared<complex_memory_hook_callback>(std::move(callback));
function_wrapper<void, uc_engine*, uint64_t, uint32_t> wrapper(std::move(exec_wrapper));
unicorn_hook hook{*this};
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_CODE, wrapper.get_function(),
wrapper.get_user_data(), address, address + size));
auto* container = this->create_hook_container();
container->add(std::move(wrapper), std::move(hook));
return container->as_opaque_hook();
}
emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) override
{
return this->hook_memory_execution(0, std::numeric_limits<uint64_t>::max(), std::move(callback));
}
emulator_hook* hook_memory_execution(const uint64_t address,
memory_execution_hook_callback callback) override
{
return this->hook_memory_execution(address, 1, std::move(callback));
}
emulator_hook* hook_memory_read(const uint64_t address, const size_t size,
memory_access_hook_callback callback) override
{
auto read_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type,
const uint64_t address, const int size,
const uint64_t value) {
const auto operation = map_memory_operation(type);
if (operation == memory_operation::read && size > 0)
{
c(address, &value, std::min(static_cast<size_t>(size), sizeof(value)));
}
};
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
std::move(read_wrapper));
unicorn_hook hook{*this};
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_READ_AFTER, wrapper.get_function(),
wrapper.get_user_data(), address, address + size));
auto* container = this->create_hook_container();
container->add(std::move(wrapper), std::move(hook));
return container->as_opaque_hook();
}
emulator_hook* hook_memory_write(const uint64_t address, const size_t size,
memory_access_hook_callback callback) override
{
auto write_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t addr,
const int length, const uint64_t value) {
const auto operation = map_memory_operation(type);
if (operation == memory_operation::write && length > 0)
{
c(addr, &value, std::min(static_cast<size_t>(length), sizeof(value)));
}
};
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
std::move(write_wrapper));
unicorn_hook hook{*this};
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_WRITE, wrapper.get_function(),
wrapper.get_user_data(), address, address + size));
auto* container = this->create_hook_container();
container->add(std::move(wrapper), std::move(hook));
return container->as_opaque_hook();
}
hook_container* create_hook_container()
{
auto container = std::make_unique<hook_container>();
if ((filter & memory_operation::read) != memory_operation::none)
{
add_read_hook(*this, address, size, *container, shared_callback);
}
if ((filter & memory_operation::write) != memory_operation::none)
{
add_write_hook(*this, address, size, *container, shared_callback);
}
if ((filter & memory_operation::exec) != memory_operation::none)
{
add_exec_hook(*this, address, size, *container, shared_callback);
}
auto* result = container->as_opaque_hook();
auto* ptr = container.get();
this->hooks_.push_back(std::move(container));
return result;
return ptr;
}
void delete_hook(emulator_hook* hook) override

View File

@@ -517,9 +517,9 @@ void windows_emulator::setup_hooks()
return memory_violation_continuation::resume;
});
this->emu().hook_memory_execution(
0, std::numeric_limits<size_t>::max(),
[&](const uint64_t address, const size_t, const uint64_t) { this->on_instruction_execution(address); });
this->emu().hook_memory_execution([&](const uint64_t address) {
this->on_instruction_execution(address); //
});
}
void windows_emulator::start(size_t count)

View File

@@ -8,26 +8,6 @@
#include "x64_register_mapping.hpp"
#include "x64_target_descriptions.hpp"
inline memory_operation map_breakpoint_type(const gdb_stub::breakpoint_type type)
{
using enum gdb_stub::breakpoint_type;
switch (type)
{
case software:
case hardware_exec:
return memory_operation::exec;
case hardware_read:
return memory_permission::read;
case hardware_write:
return memory_permission::write;
case hardware_read_write:
return memory_permission::read_write;
default:
throw std::runtime_error("Bad bp type");
}
}
struct breakpoint_key
{
size_t addr{};
@@ -196,11 +176,9 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
try
{
return this->hooks_.access<bool>([&](hook_map& hooks) {
hooks[{addr, size, type}] = scoped_hook(
*this->emu_, this->emu_->hook_memory_access(addr, size, map_breakpoint_type(type),
[this](uint64_t, size_t, uint64_t, memory_operation) {
this->on_interrupt(); //
}));
auto hook_vector = this->create_hook(type, addr, size);
hooks[{addr, size, type}] = scoped_hook(*this->emu_, std::move(hook_vector));
return true;
});
@@ -264,4 +242,64 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
using hook_map = std::unordered_map<breakpoint_key, scoped_hook>;
utils::concurrency::container<hook_map> hooks_{};
std::vector<emulator_hook*> create_execute_hook(const uint64_t addr, const size_t size)
{
std::vector<emulator_hook*> hooks{};
hooks.reserve(size);
for (size_t i = 0; i < size; ++i)
{
auto* hook = this->emu_->hook_memory_execution(addr + i, [this](const uint64_t) {
this->on_interrupt(); //
});
hooks.push_back(hook);
}
return hooks;
}
std::vector<emulator_hook*> create_read_hook(const uint64_t addr, const size_t size)
{
auto* hook = this->emu_->hook_memory_read(addr, size, [this](const uint64_t, const void*, const size_t) {
this->on_interrupt(); //
});
return {hook};
}
std::vector<emulator_hook*> create_write_hook(const uint64_t addr, const size_t size)
{
auto* hook = this->emu_->hook_memory_write(addr, size, [this](const uint64_t, const void*, const size_t) {
this->on_interrupt(); //
});
return {hook};
}
std::vector<emulator_hook*> create_hook(const gdb_stub::breakpoint_type type, const uint64_t addr,
const size_t size)
{
using enum gdb_stub::breakpoint_type;
switch (type)
{
case software:
case hardware_exec:
return this->create_execute_hook(addr, size);
case hardware_read:
return this->create_read_hook(addr, size);
case hardware_write:
return this->create_write_hook(addr, size);
case hardware_read_write: {
auto hooks1 = this->create_hook(hardware_read, addr, size);
auto hooks2 = this->create_hook(hardware_write, addr, size);
hooks1.insert(hooks1.end(), hooks2.begin(), hooks2.end());
return hooks1;
}
default:
throw std::runtime_error("Bad bp type");
}
}
};