Pick some memory-related fixes (#200)

This commit is contained in:
Maurice Heumann
2025-04-14 08:12:55 +02:00
committed by GitHub
7 changed files with 60 additions and 29 deletions

View File

@@ -243,6 +243,7 @@ namespace
bool run(const analysis_options& options, const std::span<const std::string_view> args)
{
const auto win_emu = setup_emulator(options, args);
win_emu->log.log("Using emulator: %s\n", win_emu->emu().get_name().c_str());
(void)&watch_system_objects;
watch_system_objects(*win_emu, options.modules, options.concise_logging);

View File

@@ -153,7 +153,7 @@ impl ExecutionHooks {
}
let mapping = self.address_mapping.get(&address);
if mapping.is_none(){
if mapping.is_none() {
return;
}
@@ -165,7 +165,7 @@ impl ExecutionHooks {
}
}
pub fn execute(&mut self,cpu: &mut icicle_cpu::Cpu, address: u64) {
pub fn execute(&mut self, cpu: &mut icicle_cpu::Cpu, address: u64) {
self.run_hooks(address);
if *self.stop.borrow() {
@@ -188,7 +188,7 @@ impl ExecutionHooks {
}
pub fn remove_generic_hook(&mut self, id: u32) {
self.generic_hooks.remove_hook(id);
self.generic_hooks.remove_hook(id);
}
pub fn remove_specific_hook(&mut self, id: u32) {
@@ -198,7 +198,7 @@ impl ExecutionHooks {
});
self.specific_hooks.remove_hook(id);
}
}
}
pub struct IcicleEmulator {
@@ -328,11 +328,12 @@ impl IcicleEmulator {
fn handle_exception(&mut self, code: ExceptionCode, value: u64) -> bool {
let continue_execution = match code {
ExceptionCode::Syscall => self.handle_syscall(),
ExceptionCode::Syscall => self.handle_syscall(value),
ExceptionCode::ReadPerm => self.handle_violation(value, FOREIGN_READ, false),
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::SoftwareBreakpoint => self.handle_interrupt(3),
ExceptionCode::InvalidInstruction => self.handle_interrupt(6),
ExceptionCode::DivisionException => self.handle_interrupt(0),
_ => false,
@@ -356,7 +357,11 @@ impl IcicleEmulator {
return continue_execution;
}
fn handle_syscall(&mut self) -> bool {
fn handle_syscall(&mut self, value: u64) -> bool {
if value != 0 {
return self.handle_interrupt(value as i32);
}
for (_key, func) in self.syscall_hooks.get_hooks() {
func();
}
@@ -377,9 +382,12 @@ impl IcicleEmulator {
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);
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);
}
@@ -404,7 +412,9 @@ impl IcicleEmulator {
end: u64,
callback: Box<dyn Fn(u64, &[u8])>,
) -> u32 {
let id = self.get_mem().add_read_after_hook(start, end, Box::new(MemoryHook { callback }));
let id = self
.get_mem()
.add_read_after_hook(start, end, Box::new(MemoryHook { callback }));
if id.is_none() {
return 0;
}
@@ -418,7 +428,9 @@ impl IcicleEmulator {
end: u64,
callback: Box<dyn Fn(u64, &[u8])>,
) -> u32 {
let id = self.get_mem().add_write_hook(start, end, Box::new(MemoryHook { callback }));
let id = self
.get_mem()
.add_write_hook(start, end, Box::new(MemoryHook { callback }));
if id.is_none() {
return 0;
}
@@ -433,10 +445,22 @@ impl IcicleEmulator {
HookType::Syscall => self.syscall_hooks.remove_hook(hook_id),
HookType::Violation => self.violation_hooks.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);()},
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);
()
}
_ => {}
}
}
@@ -543,9 +567,7 @@ impl IcicleEmulator {
return reg_node.size.into();
}
fn read_flags<T>(&mut self, data: &mut [u8]) -> usize
{
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);
@@ -567,8 +589,7 @@ impl IcicleEmulator {
}
}
fn write_flags<T>(&mut self, data: &[u8]) -> usize
{
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);

View File

@@ -126,8 +126,8 @@ typedef struct _MEMORY_REGION_INFORMATION
std::int64_t RegionSize;
std::int64_t CommitSize;
DWORD64 PartitionId; // 19H1
DWORD64 NodePreference; // 20H1
// DWORD64 PartitionId; // 19H1
// DWORD64 NodePreference; // 20H1
} MEMORY_REGION_INFORMATION64, *PMEMORY_REGION_INFORMATION64;
// NOLINTEND(modernize-use-using)

View File

@@ -78,6 +78,7 @@ namespace utils
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
{
buffer.write(region.is_mmio);
buffer.write(region.initial_permission);
buffer.write<uint64_t>(region.length);
buffer.write_map(region.committed_regions);
}
@@ -85,6 +86,7 @@ namespace utils
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
{
buffer.read(region.is_mmio);
buffer.read(region.initial_permission);
region.length = static_cast<size_t>(buffer.read<uint64_t>());
buffer.read_map(region.committed_regions);
}
@@ -258,6 +260,7 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size,
.try_emplace(address,
reserved_region{
.length = size,
.initial_permission = permissions,
})
.first;
@@ -497,6 +500,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
result.start = MIN_ALLOCATION_ADDRESS;
result.length = MAX_ALLOCATION_ADDRESS - result.start;
result.permissions = memory_permission::none;
result.initial_permissions = memory_permission::none;
result.allocation_base = {};
result.allocation_length = result.length;
result.is_committed = false;
@@ -532,6 +536,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
result.allocation_length = reserved_region.length;
result.start = result.allocation_base;
result.length = result.allocation_length;
result.initial_permissions = entry->second.initial_permission;
if (committed_regions.empty())
{

View File

@@ -18,6 +18,7 @@ struct region_info : basic_memory_region
size_t allocation_length{};
bool is_reserved{};
bool is_committed{};
memory_permission initial_permissions{};
};
using mmio_read_callback = std::function<void(uint64_t addr, void* data, size_t size)>;
@@ -42,6 +43,7 @@ class memory_manager : public memory_interface
struct reserved_region
{
size_t length{};
memory_permission initial_permission{};
committed_region_map committed_regions{};
bool is_mmio{false};
};

View File

@@ -219,20 +219,22 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
binary.image_base = optional_header.ImageBase;
binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
if (!memory.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
if (!memory.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::all))
{
binary.image_base = memory.find_free_allocation_base(binary.size_of_image);
const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
const auto has_dynamic_base = optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
const auto is_relocatable = is_dll || has_dynamic_base;
if (!is_relocatable ||
!memory.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
if (!is_relocatable || !memory.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::all))
{
throw std::runtime_error("Memory range not allocatable");
}
}
// TODO: Make sure to match kernel allocation patterns to attain correct initial permissions!
memory.protect_memory(binary.image_base, binary.size_of_image, memory_permission::read);
binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);

View File

@@ -43,11 +43,11 @@ namespace syscalls
image_info.State = region_info.is_committed ? MEM_COMMIT : state;
image_info.BaseAddress = reinterpret_cast<void*>(region_info.start);
image_info.AllocationBase = reinterpret_cast<void*>(region_info.allocation_base);
image_info.AllocationProtect = 0;
image_info.PartitionId = 0;
image_info.RegionSize = static_cast<int64_t>(region_info.length);
image_info.Protect = map_emulator_to_nt_protection(region_info.permissions);
image_info.AllocationProtect = map_emulator_to_nt_protection(region_info.initial_permissions);
image_info.Type = MEM_PRIVATE;
});
@@ -91,7 +91,7 @@ namespace syscalls
return_length.write(sizeof(MEMORY_REGION_INFORMATION64));
}
if (memory_information_length != sizeof(MEMORY_REGION_INFORMATION64))
if (memory_information_length < sizeof(MEMORY_REGION_INFORMATION64))
{
return STATUS_BUFFER_OVERFLOW;
}
@@ -108,8 +108,8 @@ namespace syscalls
memset(&image_info, 0, sizeof(image_info));
image_info.AllocationBase = reinterpret_cast<void*>(region_info.allocation_base);
image_info.AllocationProtect = 0;
image_info.PartitionId = 0;
image_info.AllocationProtect = map_emulator_to_nt_protection(region_info.initial_permissions);
// image_info.PartitionId = 0;
image_info.RegionSize = static_cast<int64_t>(region_info.allocation_length);
image_info.Reserved = 0x10;
});