23 Commits

Author SHA1 Message Date
Duncan Ogilvie 0fc786f5f1 Merge pull request #22 from icicle-emu/uninitialized-memory
Map memory as initialized unless `track_uninitialized` is enabled
2024-12-28 10:28:04 +01:00
Duncan Ogilvie 583932dd33 Map memory as initialized unless track_uninitialized is enabled
Related to #9
2024-12-28 10:21:28 +01:00
Duncan Ogilvie ad481a2124 Minor code simplifications 2024-12-28 10:20:22 +01:00
Duncan Ogilvie aef686faa6 Merge pull request #20 from icicle-emu/x86-eflags-hook
X86 eflags hook
2024-12-27 17:15:26 +01:00
Duncan Ogilvie 556eb71fe7 Add custom register handler for eflags on x86
Closes #19
2024-12-27 16:24:51 +01:00
Duncan Ogilvie de16238d15 Add rust test for #19 2024-12-27 16:23:50 +01:00
Duncan Ogilvie 7a095640b4 Downgrade softprops/action-gh-release to v2.0.9
https://github.com/softprops/action-gh-release/issues/555
2024-12-27 15:49:20 +01:00
Duncan Ogilvie 6ad80d947e Bump icicle-emu
https://github.com/icicle-emu/icicle-emu/issues/64
2024-12-25 01:19:23 +01:00
Duncan Ogilvie 3d3b1f7e9a Merge pull request #15 from icicle-emu/ci-bump
CI version bumps
2024-12-25 01:06:52 +01:00
Duncan Ogilvie 91470e40f9 Bump pypa/gh-action-pypi-publish and softprops/action-gh-release 2024-12-25 00:57:31 +01:00
Duncan Ogilvie a703630151 Bump to Python 3.12 for windows and macos
Closes #8
2024-12-25 00:57:07 +01:00
Duncan Ogilvie 829517fcba Merge pull request #11 from icicle-emu/rattle-bug-repros
Add a few tests to reproduce emulator bugs
2024-12-25 00:45:06 +01:00
Duncan Ogilvie 2e4dea36ec Add rust test for #13 2024-12-25 00:41:46 +01:00
Duncan Ogilvie d648e73ba1 Add rust test for #9 2024-12-25 00:41:46 +01:00
Duncan Ogilvie 46a937a0e1 Simplify icicle vm construction in tests 2024-12-25 00:41:46 +01:00
Duncan Ogilvie d8673ecdfd Add rust test for #10 2024-12-25 00:41:46 +01:00
Duncan Ogilvie 38d64a466c Chance icicle initialization parameters 2024-12-25 00:41:46 +01:00
Duncan Ogilvie 5853b82603 Fix test execution by defining GHIDRA_SRC 2024-12-25 00:41:46 +01:00
Duncan Ogilvie 70cfc3260a Switch to a [workspace]
https://github.com/PyO3/pyo3/issues/340
2024-12-25 00:41:46 +01:00
Duncan Ogilvie 1ae0f43fce Move rust tests to a separate crate
https://github.com/PyO3/pyo3/issues/340
2024-12-25 00:41:46 +01:00
Duncan Ogilvie 30b3c7c357 Merge pull request #14 from icicle-emu/write-reg-sp
Fix bug where `reg_write("rip", ...)` would not work after stepping
2024-12-25 00:41:26 +01:00
Duncan Ogilvie a228304530 Add getter/setter for PC and SP registers 2024-12-25 00:38:30 +01:00
Duncan Ogilvie ae409c8f1f Redirect reg_write("rip") to write_pc
Closes #13
2024-12-25 00:38:23 +01:00
12 changed files with 1625 additions and 238 deletions
+2
View File
@@ -0,0 +1,2 @@
[env]
GHIDRA_SRC = { value = "python/icicle", relative = true }
+4 -4
View File
@@ -19,7 +19,7 @@ jobs:
- name: Python environment
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.12'
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
@@ -63,7 +63,7 @@ jobs:
- name: Python environment
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.12'
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
@@ -150,13 +150,13 @@ jobs:
path: dist
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@b7f401de30cb6434a1e19f805ff006643653240e # v1.8.10
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.13
with:
password: ${{ secrets.PYPI_API_TOKEN }}
packages-dir: dist/
- name: Release
uses: softprops/action-gh-release@c9b46fe7aad9f02afd89b12450b780f52dacfb2d # master 2023-03-26
uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9
with:
generate_release_notes: true
files: dist/*
Generated
+52 -44
View File
@@ -115,9 +115,9 @@ dependencies = [
[[package]]
name = "cranelift"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61fcb428a7799e67267ed1ea29d5903b87bec745f44c3decc5c621e97b9b7105"
checksum = "27711210499725bafe52c320a988e27283e6cf477ee8edac57e8275bef8ea550"
dependencies = [
"cranelift-codegen",
"cranelift-frontend",
@@ -126,24 +126,24 @@ dependencies = [
[[package]]
name = "cranelift-bforest"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5e7afe85cadb55c4c1176268a2ac046fdff8dfaeca39e18581b9dc319ca9e"
checksum = "540b193ff98b825a1f250a75b3118911af918a734154c69d80bcfcf91e7e9522"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-bitset"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ab25ef3be935a80680e393183e1f94ef507e93a24a8369494d2c6818aedb3e3"
checksum = "c7cb269598b9557ab942d687d3c1086d77c4b50dcf35813f3a65ba306fd42279"
[[package]]
name = "cranelift-codegen"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900a19b84545924f1851cbfe386962edfc4ecbc3366a254825cf1ecbcda8ba08"
checksum = "46566d7c83a8bff4150748d66020f4c7224091952aa4b4df1ec4959c39d937a1"
dependencies = [
"bumpalo",
"cranelift-bforest",
@@ -164,42 +164,42 @@ dependencies = [
[[package]]
name = "cranelift-codegen-meta"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c73b2395ffe9e7b4fdf7e2ebc052e7e27af13f68a964985346be4da477a5fc"
checksum = "2df8a86a34236cc75a8a6a271973da779c2aeb36c43b6e14da474cf931317082"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d9ed0854e96a4ff0879bff39d078de8dea7f002721c9494c1fdb4e1baa86ccc"
checksum = "cf75340b6a57b7c7c1b74f10d3d90883ee6d43a554be8131a4046c2ebcf5eb65"
[[package]]
name = "cranelift-control"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4aca921dd422e781409de0129c255768fec5dec1dae83239b497fb9138abb89"
checksum = "2e84495bc5d23d86aad8c86f8ade4af765b94882af60d60e271d3153942f1978"
dependencies = [
"arbitrary",
]
[[package]]
name = "cranelift-entity"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d770e6605eccee15b49decdd82cd26f2b6404767802471459ea49c57379a98"
checksum = "963c17147b80df351965e57c04d20dbedc85bcaf44c3436780a59a3f1ff1b1c2"
dependencies = [
"cranelift-bitset",
]
[[package]]
name = "cranelift-frontend"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29268711cb889cb39215b10faf88b9087d4c9e1d2633581e4f722a2bf4bb4ef9"
checksum = "727f02acbc4b4cb2ba38a6637101d579db50190df1dd05168c68e762851a3dd5"
dependencies = [
"cranelift-codegen",
"log",
@@ -209,15 +209,15 @@ dependencies = [
[[package]]
name = "cranelift-isle"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc65156f010aed1985767ad1bff0eb8d186743b7b03e23d0c17604a253e3f356"
checksum = "32b00cc2e03c748f2531eea01c871f502b909d30295fdcad43aec7bf5c5b4667"
[[package]]
name = "cranelift-jit"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ba6b46367a4f466cfb1abe32793fa1a0f96d862251491b01a44726b8ed9445"
checksum = "f74630af581f32b99c8f4e06ee45799383ecc0795e3ff8aa86b7584bb2d643fd"
dependencies = [
"anyhow",
"cranelift-codegen",
@@ -235,9 +235,9 @@ dependencies = [
[[package]]
name = "cranelift-module"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "007607022a4883ebdffc46c0925e2e10babf2a565ae78518034ade722aa825d2"
checksum = "6aaa16c4f18a15be310df221ea544f516acc42fc58ca96e09a3d08651744efa1"
dependencies = [
"anyhow",
"cranelift-codegen",
@@ -246,9 +246,9 @@ dependencies = [
[[package]]
name = "cranelift-native"
version = "0.113.0"
version = "0.113.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8bf9b361eaf5a7627647270fabf1dc910d993edbeaf272a652c107861ebe9c2"
checksum = "bbeaf978dc7c1a2de8bbb9162510ed218eb156697bc45590b8fbdd69bb08e8de"
dependencies = [
"cranelift-codegen",
"libc",
@@ -612,9 +612,9 @@ dependencies = [
[[package]]
name = "pyo3"
version = "0.23.2"
version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f54b3d09cbdd1f8c20650b28e7b09e338881482f4aa908a5f61a00c98fba2690"
checksum = "e484fd2c8b4cb67ab05a318f1fd6fa8f199fcc30819f08f07d200809dba26c15"
dependencies = [
"cfg-if",
"indexmap",
@@ -631,9 +631,9 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
version = "0.23.2"
version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3015cf985888fe66cfb63ce0e321c603706cd541b7aec7ddd35c281390af45d8"
checksum = "dc0e0469a84f208e20044b98965e1561028180219e35352a2afaf2b942beff3b"
dependencies = [
"once_cell",
"target-lexicon",
@@ -641,9 +641,9 @@ dependencies = [
[[package]]
name = "pyo3-ffi"
version = "0.23.2"
version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fca7cd8fd809b5ac4eefb89c1f98f7a7651d3739dfb341ca6980090f554c270"
checksum = "eb1547a7f9966f6f1a0f0227564a9945fe36b90da5a93b3933fc3dc03fae372d"
dependencies = [
"libc",
"pyo3-build-config",
@@ -651,9 +651,9 @@ dependencies = [
[[package]]
name = "pyo3-macros"
version = "0.23.2"
version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34e657fa5379a79151b6ff5328d9216a84f55dc93b17b08e7c3609a969b73aa0"
checksum = "fdb6da8ec6fa5cedd1626c886fc8749bdcbb09424a86461eb8cdf096b7c33257"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
@@ -663,9 +663,9 @@ dependencies = [
[[package]]
name = "pyo3-macros-backend"
version = "0.23.2"
version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "295548d5ffd95fd1981d2d3cf4458831b21d60af046b729b6fd143b0ba7aee2f"
checksum = "38a385202ff5a92791168b1136afae5059d3ac118457bb7bc304c197c2d33e7d"
dependencies = [
"heck",
"proc-macro2",
@@ -728,9 +728,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hash"
version = "2.0.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
[[package]]
name = "rustix"
@@ -860,6 +860,14 @@ version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tests"
version = "0.0.0"
dependencies = [
"icicle-python",
"pyo3",
]
[[package]]
name = "thiserror"
version = "1.0.40"
@@ -995,9 +1003,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasmtime-jit-debug"
version = "26.0.0"
version = "26.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "077d8382176594ded9e7d837db2f320b45915d40b99f4319b2bd1061bbdf5f4f"
checksum = "f02a0118d471de665565ed200bc56673eaa10cc8e223dfe2cef5d50ed0d9d143"
dependencies = [
"object",
"rustix",
@@ -1006,9 +1014,9 @@ dependencies = [
[[package]]
name = "wasmtime-jit-icache-coherence"
version = "26.0.0"
version = "26.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e458e6a1a010a53f86ac8d75837c0c6b2ce3e54b7503b2f1dc5629a4a541f5a"
checksum = "da47fba49af72581bc0dc67c8faaf5ee550e6f106e285122a184a675193701a5"
dependencies = [
"anyhow",
"cfg-if",
@@ -1018,9 +1026,9 @@ dependencies = [
[[package]]
name = "wasmtime-versioned-export-macros"
version = "26.0.0"
version = "26.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abe01058e422966659e1af00af833147d54658b07c7e74606d73ca9af3f1690a"
checksum = "db8efb877c9e5e67239d4553bb44dd2a34ae5cfb728f3cf2c5e64439c6ca6ee7"
dependencies = [
"proc-macro2",
"quote",
+14 -4
View File
@@ -1,4 +1,6 @@
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
members = ["tests"]
exclude = ["icicle-emu"]
[package]
name = "icicle-python"
@@ -7,18 +9,26 @@ edition = "2021"
[lib]
name = "icicle"
crate-type = ["cdylib"]
crate-type = ["cdylib", "lib"]
[workspace.dependencies]
pyo3 = { version = "0.23.3", features = ["indexmap", "abi3-py37"] }
[dependencies]
pyo3 = { workspace = true }
icicle-cpu = { path = "icicle-emu/icicle-cpu" }
icicle-vm = { path = "icicle-emu/icicle-vm" }
pcode = { path = "icicle-emu/sleigh/pcode" }
sleigh-runtime = { path = "icicle-emu/sleigh/sleigh-runtime" }
indexmap = "2.2.6"
pyo3 = { version = "0.23.2", features = ["extension-module", "indexmap", "abi3-py37"] }
target-lexicon = "0.12.7"
tracing = "*"
tracing-subscriber = "0.3.17"
[build-dependencies]
pyo3-build-config = "0.23.2"
pyo3-build-config = "0.23.3"
# Reference: https://github.com/PyO3/pyo3/issues/340
[features]
extension-module = ["pyo3/extension-module"]
default = ["extension-module"]
+4
View File
@@ -100,6 +100,10 @@ class Icicle:
icount_limit: int
pc: int
sp: int
# TODO: API to get memory information?
def mem_map(self, address: int, size: int, protection: MemoryProtection): ...
+65 -12
View File
@@ -1,19 +1,35 @@
use std::borrow::Cow;
use std::collections::HashMap;
use icicle_cpu::mem::{Mapping, MemError, perm};
use icicle_cpu::{ExceptionCode, VmExit};
use icicle_cpu::{Cpu, ExceptionCode, ValueSource, VmExit};
use pyo3::prelude::*;
use icicle_vm;
use icicle_vm::linux::LinuxCpu;
use pyo3::exceptions::*;
use target_lexicon;
use indexmap::IndexMap;
use target_lexicon::Architecture;
use sleigh_runtime::NamedRegister;
// References:
// - https://pyo3.rs/main/conversions/tables
// - https://pyo3.rs/main/class
struct X86FlagsRegHandler {
pub eflags: pcode::VarNode,
}
impl icicle_cpu::RegHandler for X86FlagsRegHandler {
fn read(&mut self, cpu: &mut Cpu) {
let eflags = icicle_vm::x86::eflags(cpu);
cpu.write_var::<u32>(self.eflags, eflags);
}
fn write(&mut self, cpu: &mut Cpu) {
let eflags = cpu.read_var::<u32>(self.eflags);
icicle_vm::x86::set_eflags(cpu, eflags);
}
}
#[pyclass(eq, eq_int, module = "icicle")]
#[derive(Clone, Debug, PartialEq)]
pub enum MemoryProtection {
@@ -227,7 +243,7 @@ pub struct Icicle {
}
fn reg_find<'a>(i: &'a Icicle, name: &str) -> PyResult<&'a NamedRegister> {
let sleigh = i.vm.cpu.sleigh();
let sleigh = &i.vm.cpu.arch.sleigh;
match sleigh.get_reg(name) {
None => {
i.regs.get(name.to_lowercase().as_str())
@@ -253,7 +269,7 @@ impl Icicle {
#[getter]
pub fn get_icount(&mut self) -> u64 {
return self.vm.cpu.icount;
self.vm.cpu.icount
}
#[setter]
@@ -276,6 +292,26 @@ impl Icicle {
self.architecture.to_string()
}
#[getter]
pub fn get_pc(&self) -> u64 {
self.vm.cpu.read_pc()
}
#[setter]
pub fn set_pc(&mut self, address: u64) {
self.vm.cpu.write_pc(address)
}
#[getter]
pub fn get_sp(&mut self) -> u64 {
self.vm.cpu.read_reg(self.vm.cpu.arch.reg_sp)
}
#[setter]
pub fn set_sp(&mut self, address: u64) {
self.vm.cpu.write_reg(self.vm.cpu.arch.reg_sp, address)
}
#[new]
#[pyo3(signature = (
architecture,
@@ -315,7 +351,7 @@ impl Icicle {
}
}
// Setup the CPU state for the target triple
// Set up the CPU state for the target triple
let mut config = icicle_vm::cpu::Config::from_target_triple(
format!("{architecture}-none").as_str()
);
@@ -334,19 +370,29 @@ impl Icicle {
config.optimize_instructions = optimize_instructions;
config.optimize_block = optimize_block;
let vm = icicle_vm::build(&config)
let mut vm = icicle_vm::build(&config)
.map_err(|e| {
PyException::new_err(format!("VM build error: {e}"))
})?;
// Populate the lowercase register map
let mut regs = HashMap::new();
let sleigh = vm.cpu.sleigh();
let sleigh = &vm.cpu.arch.sleigh;
for reg in &sleigh.named_registers {
let name = sleigh.get_str(reg.name);
regs.insert(name.to_lowercase(), reg.clone());
}
// Special handling for x86 flags
match config.triple.architecture {
Architecture::X86_32(_) | Architecture::X86_64 | Architecture::X86_64h => {
let eflags = sleigh.get_reg("eflags").unwrap().var;
let reg_handler = X86FlagsRegHandler { eflags };
vm.cpu.add_reg_handler(eflags.id, Box::new(reg_handler));
}
_ => {}
}
Ok(Icicle {
architecture,
vm,
@@ -365,8 +411,9 @@ impl Icicle {
}
pub fn mem_map(&mut self, address: u64, size: u64, protection: MemoryProtection) -> PyResult<()> {
let init_perm = if self.vm.cpu.mem.track_uninitialized { perm::NONE } else { perm::INIT };
let mapping = Mapping {
perm: convert_protection(protection),
perm: convert_protection(protection) | init_perm,
value: 0,
};
if self.vm.cpu.mem.map_memory_len(address, size, mapping) {
@@ -418,7 +465,7 @@ impl Icicle {
e,
)
})?;
return Ok(Cow::Owned(buffer));
Ok(Cow::Owned(buffer))
}
pub fn mem_write(&mut self, address: u64, data: Vec<u8>) -> PyResult<()> {
@@ -434,12 +481,12 @@ impl Icicle {
pub fn reg_list(&self) -> PyResult<IndexMap<String, (u32, u8)>> {
let mut result = IndexMap::new();
let sleigh = self.vm.cpu.sleigh();
let sleigh = &self.vm.cpu.arch.sleigh;
for reg in &sleigh.named_registers {
let name = sleigh.get_str(reg.name);
result.insert(name.to_string(), (reg.offset, reg.var.size));
}
return Ok(result);
Ok(result)
}
pub fn reg_offset(&self, name: &str) -> PyResult<u32> {
@@ -455,7 +502,13 @@ impl Icicle {
}
pub fn reg_write(&mut self, name: &str, value: u64) -> PyResult<()> {
Ok(self.vm.cpu.write_reg(reg_find(self, name)?.var, value))
let var = reg_find(self, name)?.var;
if var == self.vm.cpu.arch.reg_pc {
self.vm.cpu.write_pc(value);
} else {
self.vm.cpu.write_reg(var, value);
}
Ok(())
}
pub fn reset(&mut self) {
-173
View File
@@ -1,173 +0,0 @@
#![allow(special_module_name)]
mod lib;
use lib::*;
use pyo3::PyResult;
fn nx_start() -> PyResult<()> {
let mut vm: Icicle = Icicle::new(
"x86_64".to_string(),
false,
true,
true,
true,
false,
true,
true,
false,
)?;
let page = 0x10000;
vm.mem_map(page, 0x1000, MemoryProtection::ReadOnly)?;
// <non-executable memory> inc eax; ret
vm.mem_write(page, b"\xFF\xC0\xC3".to_vec())?;
vm.reg_write("rip", page)?;
let status = vm.run();
println!("status: {:?}", status);
println!("rip: {:#x}", vm.reg_read("rip")?);
Ok(())
}
fn nx_middle() -> PyResult<()> {
let mut vm: Icicle = Icicle::new(
"x86_64".to_string(),
false,
true,
true,
true,
false,
true,
true,
false,
)?;
let page = 0x10000;
vm.mem_map(page, 0x2000, MemoryProtection::ExecuteRead)?;
vm.mem_protect(page + 0x1000, 0x1000, MemoryProtection::ReadOnly)?;
// inc eax; inc eax; <transition to non-executable region>; ret
let rip = page + 0x1000 - 2;
vm.mem_write(rip, b"\xFF\xC0\xFF\xC0\xC3".to_vec())?;
vm.reg_write("rip", rip)?;
let status = vm.run();
println!("status: {:?}", status);
println!("rip: {:#x}", vm.reg_read("rip")?);
println!("rax: {:#x}", vm.reg_read("rax")?);
Ok(())
}
fn inv_start() -> PyResult<()> {
let mut vm: Icicle = Icicle::new(
"x86_64".to_string(),
false,
true,
true,
true,
false,
true,
true,
false,
)?;
let page = 0x10000;
vm.mem_map(page, 0x1000, MemoryProtection::ExecuteRead)?;
// <invalid>; ret
vm.mem_write(page, b"\xFF\xFF\xC3".to_vec())?;
vm.reg_write("rip", page)?;
let status = vm.run();
println!("status: {:?}", status);
println!("rip: {:#x}", vm.reg_read("rip")?);
println!("rax: {:#x}", vm.reg_read("rax")?);
Ok(())
}
fn inv_middle() -> PyResult<()> {
let mut vm: Icicle = Icicle::new(
"x86_64".to_string(),
false,
true,
true,
true,
false,
true,
true,
false,
)?;
let page = 0x10000;
vm.mem_map(page, 0x1000, MemoryProtection::ExecuteRead)?;
// inc eax; <invalid>; ret
vm.mem_write(page, b"\xFF\xC0\xFF\xFF\xC3".to_vec())?;
vm.reg_write("rip", page)?;
let status = vm.run();
println!("status: {:?}", status);
println!("rip: {:#x}", vm.reg_read("rip")?);
println!("rax: {:#x}", vm.reg_read("rax")?);
Ok(())
}
fn block_optimization() -> PyResult<()> {
let mut vm: Icicle = Icicle::new(
"x86_64".to_string(), // architecture
true, // jit
true, // jit_mem
true, // shadow_stack
true, // recompilation
false, // track_uninitialized
true, // optimize_instructions
true, // optimize_block
false, // tracing
)?;
// Memory setup
let addr: u64 = 0x140001A73;
let heap: u64 = 0x71000;
vm.mem_map(heap, 0x1000, MemoryProtection::ReadWrite)?;
vm.mem_write(heap + 4, b"\x37\x13\x00\x00".to_vec())?;
vm.mem_map(addr & !0xFFF, 0x1000, MemoryProtection::ExecuteRead)?;
vm.mem_write(addr, b"\x41\xc1\xea\x07\x41\x83\xe2\x1f\x74\x08\x44\x89\xd0\x48\x89\x54\xc6\x08\x49\x83\xc1\x04\x4c\x89\x0e\x4c\x89\xc9\x44\x8b\x11\x44\x89\xd0\xf7\xd0\x49\x89\xc9\xa8\x03\x0f\x84\x88\xf6\xff\xff\xeb\x4c".to_vec())?;
// Register setup
vm.reg_write("r9", heap)?;
vm.reg_write("r10", 0x13)?;
vm.reg_write("rip", addr)?;
vm.reg_write("rsi", heap + 0x100)?;
// Step through instructions
for i in 0..11 {
let rip = vm.reg_read("rip")?;
let rcx = vm.reg_read("rcx")?;
let r9 = vm.reg_read("r9")?;
println!("[{}] RIP: {:#x}, RCX: {:#x}, R9: {:#x}", i, rip, rcx, r9);
if rip == 0x140001A8F {
vm.reg_write("r9", 0x13370900)?;
}
vm.step(1);
if rip == 0x140001A9A {
if rcx != r9 {
println!("[BUG] expected rcx({:#x}) == r9({:#x})", rcx, r9);
} else {
println!("Everything works!");
}
}
}
Ok(())
}
fn main() {
#![allow(unused_must_use)]
println!("=== NX (block start) ===");
nx_start();
println!("=== NX (block middle) ===");
nx_middle();
println!("=== Invalid instruction (block start) ===");
inv_start();
println!("=== Invalid instruction (block middle) ===");
inv_middle();
println!("=== Block optimization bug ===");
block_optimization();
}
+2
View File
@@ -0,0 +1,2 @@
[env]
GHIDRA_SRC = { value = "../python/icicle", relative = true }
+1163
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "tests"
edition = "2021"
[[bin]]
name = "tests"
path = "tests.rs"
[dependencies]
icicle-python = { path = "..", default-features = false }
pyo3 = { workspace = true, features = ["auto-initialize"] }
+307
View File
@@ -0,0 +1,307 @@
use icicle::*;
use pyo3::PyResult;
use std::process::exit;
// NOTE: https://github.com/rust-lang/rust-analyzer/issues/18752
#[test]
fn example() -> PyResult<()> {
Err(pyo3::exceptions::PyException::new_err("test"))
}
fn new_vm(jit: bool) -> PyResult<Icicle> {
Icicle::new(
"x86_64".to_string(),
jit,
true,
false,
true,
false,
true,
false,
false,
)
}
fn nx_start() -> PyResult<()> {
let mut vm = new_vm(false)?;
let page = 0x10000;
vm.mem_map(page, 0x1000, MemoryProtection::ReadOnly)?;
// <non-executable memory> inc eax; ret
vm.mem_write(page, b"\xFF\xC0\xC3".to_vec())?;
vm.reg_write("rip", page)?;
let status = vm.run();
println!("status: {:?}", status);
println!("rip: {:#x}", vm.reg_read("rip")?);
Ok(())
}
fn nx_middle() -> PyResult<()> {
let mut vm = new_vm(false)?;
let page = 0x10000;
vm.mem_map(page, 0x2000, MemoryProtection::ExecuteRead)?;
vm.mem_protect(page + 0x1000, 0x1000, MemoryProtection::ReadOnly)?;
// inc eax; inc eax; <transition to non-executable region>; ret
let rip = page + 0x1000 - 2;
vm.mem_write(rip, b"\xFF\xC0\xFF\xC0\xC3".to_vec())?;
vm.reg_write("rip", rip)?;
let status = vm.run();
println!("status: {:?}", status);
println!("rip: {:#x}", vm.reg_read("rip")?);
println!("rax: {:#x}", vm.reg_read("rax")?);
Ok(())
}
fn inv_start() -> PyResult<()> {
let mut vm = new_vm(false)?;
let page = 0x10000;
vm.mem_map(page, 0x1000, MemoryProtection::ExecuteRead)?;
// <invalid>; ret
vm.mem_write(page, b"\xFF\xFF\xC3".to_vec())?;
vm.reg_write("rip", page)?;
let status = vm.run();
println!("status: {:?}", status);
println!("rip: {:#x}", vm.reg_read("rip")?);
println!("rax: {:#x}", vm.reg_read("rax")?);
Ok(())
}
fn inv_middle() -> PyResult<()> {
let mut vm = new_vm(false)?;
let page = 0x10000;
vm.mem_map(page, 0x1000, MemoryProtection::ExecuteRead)?;
// inc eax; <invalid>; ret
vm.mem_write(page, b"\xFF\xC0\xFF\xFF\xC3".to_vec())?;
vm.reg_write("rip", page)?;
let status = vm.run();
println!("status: {:?}", status);
println!("rip: {:#x}", vm.reg_read("rip")?);
println!("rax: {:#x}", vm.reg_read("rax")?);
Ok(())
}
fn block_optimization() -> PyResult<()> {
let mut vm: Icicle = Icicle::new(
"x86_64".to_string(),
true,
true,
true,
true,
false,
true,
true,
false,
)?;
// Memory setup
let addr: u64 = 0x140001A73;
let heap: u64 = 0x71000;
vm.mem_map(heap, 0x1000, MemoryProtection::ReadWrite)?;
vm.mem_write(heap + 4, b"\x37\x13\x00\x00".to_vec())?;
vm.mem_map(addr & !0xFFF, 0x1000, MemoryProtection::ExecuteRead)?;
vm.mem_write(addr, b"\x41\xc1\xea\x07\x41\x83\xe2\x1f\x74\x08\x44\x89\xd0\x48\x89\x54\xc6\x08\x49\x83\xc1\x04\x4c\x89\x0e\x4c\x89\xc9\x44\x8b\x11\x44\x89\xd0\xf7\xd0\x49\x89\xc9\xa8\x03\x0f\x84\x88\xf6\xff\xff\xeb\x4c".to_vec())?;
// Register setup
vm.reg_write("r9", heap)?;
vm.reg_write("r10", 0x13)?;
vm.reg_write("rip", addr)?;
vm.reg_write("rsi", heap + 0x100)?;
// Step through instructions
for i in 0..11 {
let rip = vm.reg_read("rip")?;
let rcx = vm.reg_read("rcx")?;
let r9 = vm.reg_read("r9")?;
println!("[{}] RIP: {:#x}, RCX: {:#x}, R9: {:#x}", i, rip, rcx, r9);
if rip == 0x140001A8F {
vm.reg_write("r9", 0x13370900)?;
}
vm.step(1);
if rip == 0x140001A9A {
if rcx != r9 {
println!("[BUG] expected rcx({:#x}) == r9({:#x})", rcx, r9);
} else {
println!("Everything works!");
}
}
}
Ok(())
}
fn rewind() -> PyResult<()> {
let mut vm = new_vm(true)?;
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteRead)?;
vm.mem_map(0x200, 0x20, MemoryProtection::ReadOnly)?;
vm.mem_write(0x100, b"\x55".to_vec())?; // push rbp
vm.reg_write("rbp", 0xF00)?;
vm.reg_write("rsp", 0x210)?;
vm.reg_write("rip", 0x100)?;
let status = vm.step(1);
println!("run status : {:?}", status);
println!("exception code : {:?}", vm.get_exception_code());
println!("exception value : {:#x}", vm.get_exception_value());
println!("stack pointer : {:#x}", vm.reg_read("rsp")?);
Ok(())
}
fn execute_only() -> PyResult<()> {
let mut vm = new_vm(true)?;
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteOnly)?;
vm.mem_write(0x100, b"\x90".to_vec())?; // nop
vm.reg_write("rip", 0x100)?;
let status = vm.step(1);
println!("run status : {:?}", status);
println!("exception code : {:?}", vm.get_exception_code());
println!("exception value : {:#x}", vm.get_exception_value());
Ok(())
}
fn step_modify_rip() -> PyResult<()> {
let mut vm = new_trace_vm(false)?;
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteRead)?;
// 0x100: 48 01 d8 add rax,rbx
// 0x103: 48 83 e9 05 sub rcx,0x5
// 0x107: 48 89 d9 mov rcx,rbx
// 0x10a: 90 nop
// 0x10b: 90 nop
vm.mem_write(0x100, b"\x48\x01\xD8\x48\x83\xE9\x05\x48\x89\xD9\x90\x90".to_vec())?;
vm.reg_write("rax", 0xF00)?;
vm.reg_write("rbx", 0x210)?;
vm.reg_write("rip", 0x100)?;
println!("starting run at {:#x}", vm.reg_read("rip")?);
let mut status = vm.step(1);
println!(
"ending run at {:#x} (status: {:?})",
vm.reg_read("rip")?,
status
);
vm.reg_write("rip", 0x100)?;
//vm.write_pc(0x100);
//println!("pc: {:#x}", vm.read_pc());
println!("rip rewritten {:#x}", vm.reg_read("rip")?);
status = vm.step(1);
println!(
"ending run at {:#x} (status: {:?})",
vm.reg_read("rip")?,
status
);
Ok(())
}
fn eflags_reconstruction() -> PyResult<()> {
let mut vm = new_vm(false)?;
vm.mem_map(0x100, 0x20, MemoryProtection::ExecuteRead)?;
vm.mem_write(0x100, b"\x48\x01\xD8".to_vec())?;
vm.reg_write("rax", 0x7FFFFFFFFFFFFFFF)?;
vm.reg_write("rbx", 0x1)?;
let of_mask = (1 << 11) as u64;
{
let eflags = vm.reg_read("eflags")?;
let of = vm.reg_read("OF")?;
let of_set = (eflags & of_mask) == of_mask;
println!("[pre] eflags: {:#x}, OF: {:#x} == {}", eflags, of, of_set);
}
vm.set_pc(0x100);
let status = vm.step(1);
println!("run status: {:?}", status);
{
let eflags = vm.reg_read("eflags")?;
let rflags = vm.reg_read("rflags")?;
let of = vm.reg_read("OF")?;
let of_set = (eflags & of_mask) == of_mask;
println!("[post] eflags: {:#x} == {:#x}, OF: {:#x} == {}", eflags, rflags, of, of_set);
}
{
vm.reg_write("OF", 0)?;
let eflags = vm.reg_read("eflags")?;
let of = vm.reg_read("OF")?;
let of_set = (eflags >> 11) & 1;
println!("[OF=0] eflags: {:#x}, OF: {:#x} == {}", eflags, of, of_set);
}
{
let mut eflags = vm.reg_read("eflags")?;
eflags |= of_mask;
vm.reg_write("rflags", eflags)?;
let of = vm.reg_read("OF")?;
let of_set = (eflags >> 11) & 1;
println!("[rflags|={:#x}] eflags: {:#x}, OF: {:#x} == {}", of_mask, eflags, of, of_set);
}
Ok(())
}
fn main() {
// Make sure the GHIDRA_SRC environment variable is valid
match std::env::var("GHIDRA_SRC") {
Ok(ghidra_src) => {
// Make sure the directory $GHIDRA_SRC/Ghidra/Processors exists
if !std::path::Path::new(&ghidra_src)
.join("Ghidra")
.join("Processors")
.exists()
{
println!("GHIDRA_SRC environment variable invalid!");
exit(1);
}
println!("GHIDRA_SRC: {}", ghidra_src);
}
Err(_) => {
println!("GHIDRA_SRC environment variable not set!");
exit(1);
}
}
let tests: Vec<(&str, fn() -> PyResult<()>)> = vec![
("NX (block start)", nx_start),
("NX (block middle)", nx_middle),
("Invalid instruction (block start)", inv_start),
("Invalid instruction (block middle)", inv_middle),
("Block optimization bug", block_optimization),
("Rewind", rewind),
("Execute only", execute_only),
("Step modify rip", step_modify_rip),
("EFlags reconstruction", eflags_reconstruction),
];
let mut success = 0;
for (name, f) in tests.iter() {
println!("=== {} ===", name);
match f() {
Ok(_) => {
success += 1;
println!("[OK]");
}
Err(e) => {
println!("[ERROR] {}", e);
}
}
}
println!("{}/{} tests passed", success, tests.len());
exit(if success == tests.len() { 0 } else { 1 });
}