smallworld.emulators

class smallworld.emulators.Emulator(platform: Platform)

An emulation environment.

Parameters:

platform – Platform metadata for emulation.

abstractmethod read_register_content(name: str) int

Read the content of a register.

Parameters:

name – The name of the register.

Returns:

The register’s content.

Raises:

SymbolicValueError – If the register contains a non-collapsible symbolic value

read_register_symbolic(name: str) BV

Read the content of a register, allowing symbolic output.

If the implementation of read_register_content() raises SymbolicValueError, this must be implemented.

Parameters:

name – The name of the register

Returns:

The register’s content as a z3 expression

read_register_type(name: str) Any | None

Read the type of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:

name – The name of the register.

Returns:

The register’s type

read_register_label(name: str) str | None

Read the label of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:

name – The name of the register.

Returns:

The register’s label.

read_register(name: str) int

A helper to read the content of a register.

Parameters:

name – The name of the register.

Returns:

The register’s content.

abstractmethod write_register_content(name: str, content: None | int | BV) None

Write some content to a register.

Parameters:
  • name – The name of the register to write.

  • content – The content to write.

Raises:

SymbolicValueError – If content is a bitvector expression, and this emulator doesn’t support them.

write_register_type(name: str, type: Any | None = None) None

Write the type of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • name – The name of the register.

  • type – The type of the register to write. To unset the register type, this may be set to None.

write_register_label(name: str, label: str | None = None) None

Write the label of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • name – The name of the register.

  • label – The label of the register to write. To unset the register label, this may be set to None.

write_register(name: str, content: None | int | BV) None

A helper to write the content of a register.

Parameters:
  • name – The name of the register.

  • content – The content to write.

Raises:

TypeError – If content is a BV, and this emulator cannot handle bitvector expressions

abstractmethod read_memory_content(address: int, size: int) bytes

Read memory content from a specific address.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

size bytes read from address

Raises:

SymbolicValueError – If the memory range contains a non-collapsible symbolic value

read_memory_symbolic(address: int, size: int) BV

Read memory content from a specific address as a symbolic expression.

If the implementation of read_register_content() raises SymbolicValueError, this must be implemented.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

A z3 expression of size * 8 bits read from address

read_memory_type(address: int, size: int) Any | None

Read memory type from a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

The memory region’s type.

read_memory_label(address: int, size: int) str | None

Read memory label from a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

The memory region’s label.

read_memory(address: int, size: int) bytes

A helper to read memory content from a specific address.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

size bytes read from address.

Raises:

SymbolicValueError – If the memory range contains a non-collapsible symbolic value

abstractmethod map_memory(address: int, size: int) None

Map memory of a given size.

If the requested allocation overlaps with existing regions, this will fill in the gaps.

Parameters:
  • address – The requested address of the allocation.

  • size – The size of the allocation.

abstractmethod get_memory_map() List[Tuple[int, int]]

Retrieve the memory map as understood by the emulator.

Returns:

The list of tuples (start, end) of the mapped segments

abstractmethod write_memory_content(address: int, content: bytes | BV) None

Write content to memory at a specific address.

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

Raises:

TypeError – If content is a BV, and this emulator can’t handle them

write_memory_type(address: int, size: int, type: Any | None = None) None

Set the type of memory at a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region

  • type – The type of the register to write. To unset the register type, this may be set to None.

write_memory_label(address: int, size: int, label: str | None = None) None

Set the label of memory at a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region

  • label – The label of the register to write. To unset the register label, this may be set to None.

write_memory(address: int, content: bytes | BV) None

A helper to write content to memory at a specific address.

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

Raises:

SymbolicValueError – If content is a bitvector expression, and this emulator doesn’t support them.

write_code(address: int, content: bytes) None

Write executable memory at a specific address.

Implementations can take advantage of this if they store code and memory differently.

Otherwise, it should be identical to write_memory().

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

get_bounds() List[Tuple[int, int]]

Get a list of all registered execution bounds.

Returns:

A list of registered execution bounds.

add_bound(start: int, end: int) None

Add valid execution bounds.

If execution leaves these bounds Emulators should raise EmulationBoundsError.

If execution bounds are not specified, Emulators should allow execution anywhere.

Parameters:
  • start – The start address of a valid executable region.

  • end – The end address of a valid executable region.

get_exit_points() Set[int]

Get a list of all registered exit points.

Returns:

A list of registered exit points.

add_exit_point(address: int) None

Add an exit point.

If execution reaches an exit point emulation should stop.

Parameters:

address – The address of the exit point.

abstractmethod step_instruction() None

Single instruction step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

abstractmethod step_block() None

Single block step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

step() None

Helper for single instruction step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

abstractmethod run() None

Run execution indefinitely.

Emulation should stop if an exit point is reached or execution leaves valid bounds.

Raises:

EmulationBoundsError – if execution steps out of bounds.

get_thumb() bool

For applicable platforms, checks if the CPU is in ARM or Thumb mode.

Returns:

True if in Thumb mode, otherwise False.

Raises:
  • ConfigurationError – if not using an ARM32 platform.

  • NotImplementedError – if not using a supported emulator.

set_thumb(enabled=True) None

For applicable platforms, set the CPU to Thumb or ARM mode.

Parameters:

enabled – True for Thumb mode, False for ARM mode.

Raises:
  • ConfigurationError – if not using an ARM32 platform.

  • NotImplementedError – if not using a supported emulator.

abstract property description: str

A description of this analysis.

Descriptions should be a single sentence, lowercase, with no final punctuation for proper formatting.

abstract property name: str

The name of this analysis.

Names should be kebab-case, all lowercase, no whitespace for proper formatting.

abstract property version: str

The version string for this analysis.

We recommend using Semantic Versioning

class smallworld.emulators.InstructionHookable

An Emulator mixin that supports instruction hooking.

abstractmethod hook_instruction(address: int, function: Callable[[Emulator], None]) None

Hook a specific instruction by address.

Parameters:
  • address – The address of the hook.

  • function – The function to execute when the address is reached.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
abstractmethod unhook_instruction(address: int) None

Unhook a specific instruction by address.

Parameters:

address – The address of the hook to remove.

abstractmethod unhook_instructions() None

Unhook all system interrupts.

class smallworld.emulators.FunctionHookable

An Emulator mixin that supports function hooking.

abstractmethod hook_function(address: int, function: Callable[[Emulator], None]) None

Hook a specific function by address.

After the hook function is called, the Emulator will step out of the current function so that the hook essentially replaces a function call to the given address.

Parameters:
  • address – The address of the hook.

  • function – The function to execute when the address is reached.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
abstractmethod unhook_function(address: int) None

Unhook a specific function by address.

Parameters:

address – The address of the hook to remove.

class smallworld.emulators.MemoryReadHookable

An Emulator mixin that supports memory read hooking.

abstractmethod hook_memory_read(start: int, end: int, function: Callable[[Emulator, int, int, bytes], bytes | None]) None

Hook memory reads within a given range, handling concrete values

Note that this will trigger for any read that overlaps the specified range, and will report all data read, not just the overlap. Hooks should expect to handle partial or oddly-sized reads.

If the hook chooses to override the data being read, it must return data of the same size as the original read.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: bytes
) -> bytes:
    # "content" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_read_symbolic(start: int, end: int, function: Callable[[Emulator, int, int, BV], BV | None]) None

Hook memory reads within a given range, handling symbolic values

Note that this will trigger for any read that overlaps the specified range, and will report all data read, not just the overlap. Hooks should expect to handle partial or oddly-sized reads.

If the hook chooses to override the data being read, it must return data of the same size as the original read.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is read.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: claripy.ast.bv.BV
) -> typing.Optional[claripy.ast.bv.BV]:
    # "content" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
abstractmethod unhook_memory_read(start: int, end: int) None

Unhook a specific memory region read by address range.

Parameters:
  • start – The start address of the memory read hook to remove.

  • end – The end address of the memory read hook to remove.

abstractmethod hook_memory_reads(function: Callable[[Emulator, int, int, bytes], bytes | None]) None

Hook all memory reads, handling concrete values

Parameters:

function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    data: bytes
) -> typing.Optional[bytes]:
    # "data" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_reads_symbolic(function: Callable[[Emulator, int, int, BV], BV | None]) None

Hook all memory reads, handling concrete values

Parameters:

function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: claripy.ast.bv.BV
) -> typing.Optional[claripy.ast.bv.BV]:
    # "data" represents the data fetched by the emulator
    # The return value is what should be reported to the guest
    ...
abstractmethod unhook_memory_reads() None

Unhook any ‘all reads’ handlers

class smallworld.emulators.MemoryWriteHookable

An Emulator mixin that supports memory write hooking.

abstractmethod hook_memory_write(start: int, end: int, function: Callable[[Emulator, int, int, bytes], None]) None

Hook memory writes within a given range, handling concrete values.

Note that this will trigger for any write that overlaps the specified range, and will report all data written, not just the overlap. Hooks should expect to handle partial or oddly-sized writes.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is written.

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: bytes) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_write_symbolic(start: int, end: int, function: Callable[[Emulator, int, int, BV], None]) None

Hook memory writes within a given range, handling symbolic values

Note that this will trigger for any write that overlaps the specified range, and will report all data written, not just the overlap. Hooks should expect to handle partial or oddly-sized writes.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is written.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: claripy.ast.bv.BV) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
abstractmethod unhook_memory_write(start: int, end: int) None

Unhook a specific memory region write by address range.

Parameters:
  • start – The start address of the memory write hook to remove.

  • end – The end address of the memory write hook to remove.

abstractmethod hook_memory_writes(function: Callable[[Emulator, int, int, bytes], None]) None

Hook all memory writes, handling concrete values.

Parameters:

function – The function to execute when the memory region is written.

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: bytes) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_writes_symbolic(function: Callable[[Emulator, int, int, BV], None]) None

Hook all memory writes, handling symbolic values

Parameters:

function – The function to execute when the memory region is written.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: claripy.ast.bv.BV) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
abstractmethod unhook_memory_writes() None

Unhook any global memory write hook.

class smallworld.emulators.InterruptHookable

An Emulator mixin that supports interrupt hooking.

abstractmethod hook_interrupts(function: Callable[[Emulator, int], None]) None

Hook any system interrupts.

Parameters:

function – The function to execute when an interrupt is triggered.

Example

The hook function looks like:

def hook(emulator: Emulator, interrupt: int) -> None:
    ...
abstractmethod unhook_interrupts() None

Unhook all system interrupts.

abstractmethod hook_interrupt(interrupt: int, function: Callable[[Emulator], None]) None

Hook a specific system interrupt.

Parameters:

function – The function to execute when the interrupt is triggered.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
abstractmethod unhook_interrupt(interupt: int) None

Unhook a specific system interrupt.

Parameters:

interrupt – The interrupt to unhook.

class smallworld.emulators.ConstrainedEmulator

Emulator that supports constraints

It must also support some means of evaluating constraints, probably an SMT solver or similar.

abstractmethod add_constraint(expr: Bool) None

Add a constraint to the emulator

A constraint is an expression that this emulator will use to limit the possible values of unbound variables. It will only consider execution states where all constraints can evaluate to True.

Constraints must be Boolean expressions; the easiest form is the equality or inequality of two bitvector expressions.

Parameters:

expr – The constraint expression to add

abstractmethod get_constraints() List[Bool]

Retrieve all constraints applied to this emulator.

Returns:

A list of constraint expressions

abstractmethod satisfiable(extra_constraints: List[Bool] = []) bool

Check if the current set of constraints is satisfiable

This checks if there’s a way to assign variables such that all constraint expressions evaluate to “True”. If not, the state can’t exist as described.

The emulator tracks its own set of constraints, added by the harness or built up durring execution. The caller can provide additional constraints for testing. These are not permanently added to the emulator.

Parameters:

extra_constraints – A list of additional constraints to consider.

Returns:

True if the constraint system is satisfiable. False otherwise.

abstractmethod eval_atmost(expr: BV, most: int) List[int]

Find a maximum number of solutions to a bitvector expression

This attempts to find concrete solutions to expr given the constraints on the emulator.

It will return between 1 and most solutions, inclusive. It will raise exceptions if there are no solutions, or more than requested.

Parameters:
  • expr – The expression to evaluate

  • most – The inclusive upper limit on solutions

Returns:

A list of integer solutions to expr

Raises:
  • UnsatError – If there are no solutions for expr given constraints

  • SymbolicValueError – If there are more than most solutions for expr given constraints

abstractmethod eval_atleast(expr: BV, least: int) List[int]

Find a minimum number of solutions to a bitvector expression

This attempts to find concrete solutions to expr given the constraints on the emulator.

It will return least solutions. It will raise an exception if there are fewer than least solutions possible.

Parameters:
  • expr – The expression to evaluate

  • least – The number of solutions to retrieve

Returns:

A list of integer solutions to expr

Raises:
  • UnsatError – If there are no solutions for expr given constraints

  • SymbolicValueError – If there are fewer than least solutions for expr given constraints

class smallworld.emulators.UnicornEmulator(platform: Platform)

An emulator for the Unicorn emulation engine.

read_register_content(name: str) int

Read the content of a register.

Parameters:

name – The name of the register.

Returns:

The register’s content.

Raises:

SymbolicValueError – If the register contains a non-collapsible symbolic value

read_register_label(name: str) str | None

Read the label of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:

name – The name of the register.

Returns:

The register’s label.

read_register(name: str) int

A helper to read the content of a register.

Parameters:

name – The name of the register.

Returns:

The register’s content.

write_register_content(name: str, content: None | int | BV) None

Write some content to a register.

Parameters:
  • name – The name of the register to write.

  • content – The content to write.

Raises:

SymbolicValueError – If content is a bitvector expression, and this emulator doesn’t support them.

write_register_label(name: str, label: str | None = None) None

Write the label of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • name – The name of the register.

  • label – The label of the register to write. To unset the register label, this may be set to None.

write_register(name: str, content: None | int | BV) None

A helper to write the content of a register.

Parameters:
  • name – The name of the register.

  • content – The content to write.

Raises:

TypeError – If content is a BV, and this emulator cannot handle bitvector expressions

read_memory_content(address: int, size: int) bytes

Read memory content from a specific address.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

size bytes read from address

Raises:

SymbolicValueError – If the memory range contains a non-collapsible symbolic value

read_memory_label(address: int, size: int) str | None

Read memory label from a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

The memory region’s label.

read_memory(address: int, size: int) bytes

A helper to read memory content from a specific address.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

size bytes read from address.

Raises:

SymbolicValueError – If the memory range contains a non-collapsible symbolic value

map_memory(address: int, size: int) None

Map memory of a given size.

If the requested allocation overlaps with existing regions, this will fill in the gaps.

Parameters:
  • address – The requested address of the allocation.

  • size – The size of the allocation.

get_memory_map() List[Tuple[int, int]]

Retrieve the memory map as understood by the emulator.

Returns:

The list of tuples (start, end) of the mapped segments

write_memory_content(address: int, content: bytes | BV) None

Write content to memory at a specific address.

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

Raises:

TypeError – If content is a BV, and this emulator can’t handle them

write_memory_label(address: int, size: int, label: str | None = None) None

Set the label of memory at a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region

  • label – The label of the register to write. To unset the register label, this may be set to None.

write_memory(address: int, content: bytes | BV) None

A helper to write content to memory at a specific address.

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

Raises:

SymbolicValueError – If content is a bitvector expression, and this emulator doesn’t support them.

hook_instruction(address: int, function: Callable[[Emulator], None]) None

Hook a specific instruction by address.

Parameters:
  • address – The address of the hook.

  • function – The function to execute when the address is reached.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
hook_function(address: int, function: Callable[[Emulator], None]) None

Hook a specific function by address.

After the hook function is called, the Emulator will step out of the current function so that the hook essentially replaces a function call to the given address.

Parameters:
  • address – The address of the hook.

  • function – The function to execute when the address is reached.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
get_thumb() bool

For applicable platforms, checks if the CPU is in ARM or Thumb mode.

Returns:

True if in Thumb mode, otherwise False.

Raises:
  • ConfigurationError – if not using an ARM32 platform.

  • NotImplementedError – if not using a supported emulator.

set_thumb(enabled=True) None

For applicable platforms, set the CPU to Thumb or ARM mode.

Parameters:

enabled – True for Thumb mode, False for ARM mode.

Raises:
  • ConfigurationError – if not using an ARM32 platform.

  • NotImplementedError – if not using a supported emulator.

step_instruction() None

Single instruction step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

step_block() None

Single block step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

run() None

Run execution indefinitely.

Emulation should stop if an exit point is reached or execution leaves valid bounds.

Raises:

EmulationBoundsError – if execution steps out of bounds.

add_bound(start: int, end: int) None

Add valid execution bounds.

If execution leaves these bounds Emulators should raise EmulationBoundsError.

If execution bounds are not specified, Emulators should allow execution anywhere.

Parameters:
  • start – The start address of a valid executable region.

  • end – The end address of a valid executable region.

add_exit_point(address: int) None

Add an exit point.

If execution reaches an exit point emulation should stop.

Parameters:

address – The address of the exit point.

get_bounds() List[Tuple[int, int]]

Get a list of all registered execution bounds.

Returns:

A list of registered execution bounds.

get_exit_points() Set[int]

Get a list of all registered exit points.

Returns:

A list of registered exit points.

hook_interrupt(intno: int, function: Callable[[Emulator], None]) None

Hook a specific system interrupt.

Parameters:

function – The function to execute when the interrupt is triggered.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
hook_interrupts(function: Callable[[Emulator, int], None]) None

Hook any system interrupts.

Parameters:

function – The function to execute when an interrupt is triggered.

Example

The hook function looks like:

def hook(emulator: Emulator, interrupt: int) -> None:
    ...
hook_memory_read(start: int, end: int, function: Callable[[Emulator, int, int, bytes], bytes | None]) None

Hook memory reads within a given range, handling concrete values

Note that this will trigger for any read that overlaps the specified range, and will report all data read, not just the overlap. Hooks should expect to handle partial or oddly-sized reads.

If the hook chooses to override the data being read, it must return data of the same size as the original read.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: bytes
) -> bytes:
    # "content" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_read_symbolic(start: int, end: int, function: Callable[[Emulator, int, int, BV], BV | None]) None

Hook memory reads within a given range, handling symbolic values

Note that this will trigger for any read that overlaps the specified range, and will report all data read, not just the overlap. Hooks should expect to handle partial or oddly-sized reads.

If the hook chooses to override the data being read, it must return data of the same size as the original read.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is read.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: claripy.ast.bv.BV
) -> typing.Optional[claripy.ast.bv.BV]:
    # "content" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_reads(function: Callable[[Emulator, int, int, bytes], bytes | None]) None

Hook all memory reads, handling concrete values

Parameters:

function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    data: bytes
) -> typing.Optional[bytes]:
    # "data" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_reads_symbolic(function: Callable[[Emulator, int, int, BV], BV | None]) None

Hook all memory reads, handling concrete values

Parameters:

function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: claripy.ast.bv.BV
) -> typing.Optional[claripy.ast.bv.BV]:
    # "data" represents the data fetched by the emulator
    # The return value is what should be reported to the guest
    ...
hook_memory_write(start: int, end: int, function: Callable[[Emulator, int, int, bytes], None]) None

Hook memory writes within a given range, handling concrete values.

Note that this will trigger for any write that overlaps the specified range, and will report all data written, not just the overlap. Hooks should expect to handle partial or oddly-sized writes.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is written.

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: bytes) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_write_symbolic(start: int, end: int, function: Callable[[Emulator, int, int, BV], None]) None

Hook memory writes within a given range, handling symbolic values

Note that this will trigger for any write that overlaps the specified range, and will report all data written, not just the overlap. Hooks should expect to handle partial or oddly-sized writes.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is written.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: claripy.ast.bv.BV) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_writes(function: Callable[[Emulator, int, int, bytes], None]) None

Hook all memory writes, handling concrete values.

Parameters:

function – The function to execute when the memory region is written.

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: bytes) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_writes_symbolic(function: Callable[[Emulator, int, int, BV], None]) None

Hook all memory writes, handling symbolic values

Parameters:

function – The function to execute when the memory region is written.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: claripy.ast.bv.BV) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
read_memory_symbolic(address: int, size: int) BV

Read memory content from a specific address as a symbolic expression.

If the implementation of read_register_content() raises SymbolicValueError, this must be implemented.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

A z3 expression of size * 8 bits read from address

read_memory_type(address: int, size: int) Any | None

Read memory type from a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

The memory region’s type.

read_register_symbolic(name: str) BV

Read the content of a register, allowing symbolic output.

If the implementation of read_register_content() raises SymbolicValueError, this must be implemented.

Parameters:

name – The name of the register

Returns:

The register’s content as a z3 expression

read_register_type(name: str) Any | None

Read the type of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:

name – The name of the register.

Returns:

The register’s type

step() None

Helper for single instruction step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

unhook_function(address: int) None

Unhook a specific function by address.

Parameters:

address – The address of the hook to remove.

unhook_instruction(address: int) None

Unhook a specific instruction by address.

Parameters:

address – The address of the hook to remove.

unhook_instructions()

Unhook all system interrupts.

unhook_interrupt(intno: int) None

Unhook a specific system interrupt.

Parameters:

interrupt – The interrupt to unhook.

unhook_interrupts()

Unhook all system interrupts.

unhook_memory_read(start: int, end: int) None

Unhook a specific memory region read by address range.

Parameters:
  • start – The start address of the memory read hook to remove.

  • end – The end address of the memory read hook to remove.

unhook_memory_reads()

Unhook any ‘all reads’ handlers

unhook_memory_write(start: int, end: int) None

Unhook a specific memory region write by address range.

Parameters:
  • start – The start address of the memory write hook to remove.

  • end – The end address of the memory write hook to remove.

unhook_memory_writes()

Unhook any global memory write hook.

write_code(address: int, content: bytes) None

Write executable memory at a specific address.

Implementations can take advantage of this if they store code and memory differently.

Otherwise, it should be identical to write_memory().

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

write_memory_type(address: int, size: int, type: Any | None = None) None

Set the type of memory at a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region

  • type – The type of the register to write. To unset the register type, this may be set to None.

write_register_type(name: str, type: Any | None = None) None

Write the type of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • name – The name of the register.

  • type – The type of the register to write. To unset the register type, this may be set to None.

exception smallworld.emulators.UnicornEmulationError(uc_err: UcError, pc: int, msg: str, details: dict)
add_note()

Exception.add_note(note) – add a note to the exception

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception smallworld.emulators.UnicornEmulationMemoryReadError(uc_err: UcError, pc: int, msg: str, details: dict)
add_note()

Exception.add_note(note) – add a note to the exception

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception smallworld.emulators.UnicornEmulationMemoryWriteError(uc_err: UcError, pc: int, msg: str, details: dict)
add_note()

Exception.add_note(note) – add a note to the exception

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception smallworld.emulators.UnicornEmulationExecutionError(uc_err: UcError, pc: int, msg: str, details: dict)
add_note()

Exception.add_note(note) – add a note to the exception

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

class smallworld.emulators.AngrEmulator(platform: Platform, preinit=None, init=None)

Angr symbolic execution emulator

This is primarily designed to support symbolic execution, although subclasses can configure angr however they like.

One challenge with symbolic execution is that it doesn’t work on a single machine state, but rather multiple machine states representing parallel execution paths.

As such, this interface doesn’t yet fully support all features of the base Emulator class; it’s not clear what reading or writing machine state means when there’s more than one state.

initialize()

Initialize the emulator

To take advantage of CLE, we need to know about all code before we initialize the angr state objects. However, applying register and memory changes requires access to the angr state object.

SmallWorld doesn’t support ordering how state is applied, so I need to collect it, then apply it once emulation starts.

This function is invoked automatically when you cycle the emulator, but you’re free to invoke it early if you want.

read_register_symbolic(name: str) BV

Read the content of a register, allowing symbolic output.

If the implementation of read_register_content() raises SymbolicValueError, this must be implemented.

Parameters:

name – The name of the register

Returns:

The register’s content as a z3 expression

read_register_content(name: str) int

Read the content of a register.

Parameters:

name – The name of the register.

Returns:

The register’s content.

Raises:

SymbolicValueError – If the register contains a non-collapsible symbolic value

read_register_type(name: str) Any | None

Read the type of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:

name – The name of the register.

Returns:

The register’s type

read_register_label(name: str) str | None

Read the label of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:

name – The name of the register.

Returns:

The register’s label.

write_register_content(name: str, content: None | int | BV) None

Write some content to a register.

Parameters:
  • name – The name of the register to write.

  • content – The content to write.

Raises:

SymbolicValueError – If content is a bitvector expression, and this emulator doesn’t support them.

write_register_type(name: str, type: Any | None = None) None

Write the type of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • name – The name of the register.

  • type – The type of the register to write. To unset the register type, this may be set to None.

write_register_label(name: str, label: str | None = None) None

Write the label of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • name – The name of the register.

  • label – The label of the register to write. To unset the register label, this may be set to None.

read_memory_content(address: int, size: int) bytes

Read memory content from a specific address.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

size bytes read from address

Raises:

SymbolicValueError – If the memory range contains a non-collapsible symbolic value

read_memory_symbolic(address: int, size: int) BV

Read memory content from a specific address as a symbolic expression.

If the implementation of read_register_content() raises SymbolicValueError, this must be implemented.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

A z3 expression of size * 8 bits read from address

read_memory_type(address: int, size: int) Any | None

Read memory type from a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

The memory region’s type.

read_memory_label(address: int, size: int) str | None

Read memory label from a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

The memory region’s label.

map_memory(address: int, size: int) None

Map memory of a given size.

If the requested allocation overlaps with existing regions, this will fill in the gaps.

Parameters:
  • address – The requested address of the allocation.

  • size – The size of the allocation.

get_memory_map() List[Tuple[int, int]]

Retrieve the memory map as understood by the emulator.

Returns:

The list of tuples (start, end) of the mapped segments

write_memory_content(address: int, content: bytes | BV) None

Write content to memory at a specific address.

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

Raises:

TypeError – If content is a BV, and this emulator can’t handle them

write_memory_type(address: int, size: int, type: Any | None = None) None

Set the type of memory at a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region

  • type – The type of the register to write. To unset the register type, this may be set to None.

write_memory_label(address: int, size: int, label: str | None = None) None

Set the label of memory at a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region

  • label – The label of the register to write. To unset the register label, this may be set to None.

write_code(address: int, content: bytes)

Write executable memory at a specific address.

Implementations can take advantage of this if they store code and memory differently.

Otherwise, it should be identical to write_memory().

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

hook_instruction(address: int, function: Callable[[Emulator], None]) None

Hook a specific instruction by address.

Parameters:
  • address – The address of the hook.

  • function – The function to execute when the address is reached.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
unhook_instruction(address: int) None

Unhook a specific instruction by address.

Parameters:

address – The address of the hook to remove.

unhook_instructions() None

Unhook all system interrupts.

hook_function(address: int, function: Callable[[Emulator], None]) None

Hook a specific function by address.

After the hook function is called, the Emulator will step out of the current function so that the hook essentially replaces a function call to the given address.

Parameters:
  • address – The address of the hook.

  • function – The function to execute when the address is reached.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
unhook_function(address: int) None

Unhook a specific function by address.

Parameters:

address – The address of the hook to remove.

hook_syscall(number: int, function: Callable[[Emulator], None]) None

Hook a specific syscall by number.

This hook will fire if emulation hits a platform-specific syscall instruction invoking the specified syscall number.

Emulation will resume at the instruction after the syscall. To change this, the handler should modify the PC register in the emulator.

Parameters:
  • number – The syscall number to handle

  • function – The handler function for this syscall

unhook_syscall(number: int) None

Unhook a specific syscall by number.

Parameters:

number – The syscall number to unhook

hook_syscalls(function: Callable[[Emulator, int], None]) None

Hook all syscalls

This hook will fire if emulation hits a platform-specific syscall instruction.

Emulation will resume at the instruction after the syscall. To change this, the handler should modify the PC register in the emulator.

Parameters:

function – The handler function for all syscalls

unhook_syscalls() None

Unhook all syscalls

hook_memory_read_symbolic(start: int, end: int, function: Callable[[Emulator, int, int, BV], BV | None]) None

Hook memory reads within a given range, handling symbolic values

Note that this will trigger for any read that overlaps the specified range, and will report all data read, not just the overlap. Hooks should expect to handle partial or oddly-sized reads.

If the hook chooses to override the data being read, it must return data of the same size as the original read.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is read.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: claripy.ast.bv.BV
) -> typing.Optional[claripy.ast.bv.BV]:
    # "content" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_read(start: int, end: int, function: Callable[[Emulator, int, int, bytes], bytes | None])

Hook memory reads within a given range, handling concrete values

Note that this will trigger for any read that overlaps the specified range, and will report all data read, not just the overlap. Hooks should expect to handle partial or oddly-sized reads.

If the hook chooses to override the data being read, it must return data of the same size as the original read.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: bytes
) -> bytes:
    # "content" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
unhook_memory_read(start: int, end: int)

Unhook a specific memory region read by address range.

Parameters:
  • start – The start address of the memory read hook to remove.

  • end – The end address of the memory read hook to remove.

hook_memory_reads_symbolic(function: Callable[[Emulator, int, int, BV], BV | None]) None

Hook all memory reads, handling concrete values

Parameters:

function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: claripy.ast.bv.BV
) -> typing.Optional[claripy.ast.bv.BV]:
    # "data" represents the data fetched by the emulator
    # The return value is what should be reported to the guest
    ...
hook_memory_reads(function: Callable[[Emulator, int, int, bytes], bytes | None])

Hook all memory reads, handling concrete values

Parameters:

function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    data: bytes
) -> typing.Optional[bytes]:
    # "data" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
unhook_memory_reads() None

Unhook any ‘all reads’ handlers

hook_memory_write_symbolic(start: int, end: int, function: Callable[[Emulator, int, int, BV], None]) None

Hook memory writes within a given range, handling symbolic values

Note that this will trigger for any write that overlaps the specified range, and will report all data written, not just the overlap. Hooks should expect to handle partial or oddly-sized writes.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is written.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: claripy.ast.bv.BV) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_write(start: int, end: int, function: Callable[[Emulator, int, int, bytes], None]) None

Hook memory writes within a given range, handling concrete values.

Note that this will trigger for any write that overlaps the specified range, and will report all data written, not just the overlap. Hooks should expect to handle partial or oddly-sized writes.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is written.

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: bytes) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
unhook_memory_write(start: int, end: int) None

Unhook a specific memory region write by address range.

Parameters:
  • start – The start address of the memory write hook to remove.

  • end – The end address of the memory write hook to remove.

hook_memory_writes_symbolic(function: Callable[[Emulator, int, int, BV], None]) None

Hook all memory writes, handling symbolic values

Parameters:

function – The function to execute when the memory region is written.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: claripy.ast.bv.BV) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_writes(function: Callable[[Emulator, int, int, bytes], None]) None

Hook all memory writes, handling concrete values.

Parameters:

function – The function to execute when the memory region is written.

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: bytes) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
unhook_memory_writes() None

Unhook any global memory write hook.

step_instruction() None

Single instruction step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

step_block() None

Single block step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

step() None

Helper for single instruction step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

run()

Run execution indefinitely.

Emulation should stop if an exit point is reached or execution leaves valid bounds.

Raises:

EmulationBoundsError – if execution steps out of bounds.

visit_states(function: Callable[[Emulator], None], stash: str = 'active') None

Visit every state in the selected frontier

This lets you work around the fact that most operations only work at emulation start, or in linear mode.

enable_linear()

Enable linear execution

This doesn’t actually concretize anything; it just kills execution when it hits an unconstrained branch.

get_bounds() List[Tuple[int, int]]

Get a list of all registered execution bounds.

Returns:

A list of registered execution bounds.

add_bound(start: int, end: int) None

Add valid execution bounds.

If execution leaves these bounds Emulators should raise EmulationBoundsError.

If execution bounds are not specified, Emulators should allow execution anywhere.

Parameters:
  • start – The start address of a valid executable region.

  • end – The end address of a valid executable region.

get_exit_points() Set[int]

Get a list of all registered exit points.

Returns:

A list of registered exit points.

add_exit_point(address: int) None

Add an exit point.

If execution reaches an exit point emulation should stop.

Parameters:

address – The address of the exit point.

add_constraint(expr: Bool) None

Add a constraint to the emulator

A constraint is an expression that this emulator will use to limit the possible values of unbound variables. It will only consider execution states where all constraints can evaluate to True.

Constraints must be Boolean expressions; the easiest form is the equality or inequality of two bitvector expressions.

Parameters:

expr – The constraint expression to add

get_constraints() List[Bool]

Retrieve all constraints applied to this emulator.

Returns:

A list of constraint expressions

satisfiable(extra_constraints: List[Bool] = []) bool

Check if the current set of constraints is satisfiable

This checks if there’s a way to assign variables such that all constraint expressions evaluate to “True”. If not, the state can’t exist as described.

The emulator tracks its own set of constraints, added by the harness or built up durring execution. The caller can provide additional constraints for testing. These are not permanently added to the emulator.

Parameters:

extra_constraints – A list of additional constraints to consider.

Returns:

True if the constraint system is satisfiable. False otherwise.

eval_atmost(expr: BV, most: int) List[int]

Find a maximum number of solutions to a bitvector expression

This attempts to find concrete solutions to expr given the constraints on the emulator.

It will return between 1 and most solutions, inclusive. It will raise exceptions if there are no solutions, or more than requested.

Parameters:
  • expr – The expression to evaluate

  • most – The inclusive upper limit on solutions

Returns:

A list of integer solutions to expr

Raises:
  • UnsatError – If there are no solutions for expr given constraints

  • SymbolicValueError – If there are more than most solutions for expr given constraints

eval_atleast(expr: BV, least: int) List[int]

Find a minimum number of solutions to a bitvector expression

This attempts to find concrete solutions to expr given the constraints on the emulator.

It will return least solutions. It will raise an exception if there are fewer than least solutions possible.

Parameters:
  • expr – The expression to evaluate

  • least – The number of solutions to retrieve

Returns:

A list of integer solutions to expr

Raises:
  • UnsatError – If there are no solutions for expr given constraints

  • SymbolicValueError – If there are fewer than least solutions for expr given constraints

add_extension(name: str, ext: Any) None

Add extra data to the machine state.

This allows users to tie data structures to the emulator’s execution states, allowing path-specific data tracking.

Only one extension with a given name can be tied to a given state. Usually, extensions are assigned before emulation starts, but it’s perfectly possible to assign them later, either in linear mode, or through the emulator stub provided to a hook.

All extensions must be compatible with copy.deepcopy().

Parameters:
  • name – ID string used to retrieve the extension

  • ext – The extension

Raises:

KeyErrorname is already taken

get_extension(name: str) Any

Fetch extra data from the machine state

Parameters:

name – ID string used to retrieve the extension

Returns:

Whatever was assigned to name using add_extension

Raises:

KeyError – If no extension exists tied to name

property byteorder: Literal['big', 'little']

Get the byteorder string for this platform

Returns:

‘big’ or ‘little’, appropriately-typed as literals.

get_thumb() bool

For applicable platforms, checks if the CPU is in ARM or Thumb mode.

Returns:

True if in Thumb mode, otherwise False.

Raises:
  • ConfigurationError – if not using an ARM32 platform.

  • NotImplementedError – if not using a supported emulator.

read_memory(address: int, size: int) bytes

A helper to read memory content from a specific address.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

size bytes read from address.

Raises:

SymbolicValueError – If the memory range contains a non-collapsible symbolic value

read_register(name: str) int

A helper to read the content of a register.

Parameters:

name – The name of the register.

Returns:

The register’s content.

set_thumb(enabled=True) None

For applicable platforms, set the CPU to Thumb or ARM mode.

Parameters:

enabled – True for Thumb mode, False for ARM mode.

Raises:
  • ConfigurationError – if not using an ARM32 platform.

  • NotImplementedError – if not using a supported emulator.

write_memory(address: int, content: bytes | BV) None

A helper to write content to memory at a specific address.

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

Raises:

SymbolicValueError – If content is a bitvector expression, and this emulator doesn’t support them.

write_register(name: str, content: None | int | BV) None

A helper to write the content of a register.

Parameters:
  • name – The name of the register.

  • content – The content to write.

Raises:

TypeError – If content is a BV, and this emulator cannot handle bitvector expressions

exception smallworld.emulators.PathTerminationSignal

Exception allowing an analysis to terminate an execution path.

add_note()

Exception.add_note(note) – add a note to the exception

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

class smallworld.emulators.PandaEmulator(platform: Platform, arg_overrides: Dict[str, str] = {})

An emulator for the Panda emulation engine.

Parameters:
  • arch – Architecture ID string

  • mode – Mode ID string

  • byteorder – Byteorder

class ThreadState(*values)
class PandaThread(manager, thread_state, arg_overrides: Dict[str, str] = {})
run()

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.

property daemon

A boolean value indicating whether this thread is a daemon thread.

This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.

The entire Python program exits when only daemon threads are left.

getName()

Return a string used for identification purposes only.

This method is deprecated, use the name attribute instead.

property ident

Thread identifier of this thread or None if it has not been started.

This is a nonzero integer. See the get_ident() function. Thread identifiers may be recycled when a thread exits and another thread is created. The identifier is available even after the thread has exited.

isDaemon()

Return whether this thread is a daemon.

This method is deprecated, use the daemon attribute instead.

is_alive()

Return whether the thread is alive.

This method returns True just before the run() method starts until just after the run() method terminates. See also the module function enumerate().

join(timeout=None)

Wait until the thread terminates.

This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception or until the optional timeout occurs.

When the timeout argument is present and not None, it should be a floating-point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.

When the timeout argument is not present or None, the operation will block until the thread terminates.

A thread can be join()ed many times.

join() raises a RuntimeError if an attempt is made to join the current thread as that would cause a deadlock. It is also an error to join() a thread before it has been started and attempts to do so raises the same exception.

property name

A string used for identification purposes only.

It has no semantics. Multiple threads may be given the same name. The initial name is set by the constructor.

property native_id

Native integral thread ID of this thread, or None if it has not been started.

This is a non-negative integer. See the get_native_id() function. This represents the Thread ID as reported by the kernel.

setDaemon(daemonic)

Set whether this thread is a daemon.

This method is deprecated, use the .daemon property instead.

setName(name)

Set the name string for this thread.

This method is deprecated, use the name attribute instead.

start()

Start the thread’s activity.

It must be called at most once per thread object. It arranges for the object’s run() method to be invoked in a separate thread of control.

This method will raise a RuntimeError if called more than once on the same thread object.

read_register_content(name: str) int

Read the content of a register.

Parameters:

name – The name of the register.

Returns:

The register’s content.

Raises:

SymbolicValueError – If the register contains a non-collapsible symbolic value

write_register_content(name: str, content: None | int | BV) None

Write some content to a register.

Parameters:
  • name – The name of the register to write.

  • content – The content to write.

Raises:

SymbolicValueError – If content is a bitvector expression, and this emulator doesn’t support them.

read_memory_content(address: int, size: int) bytes

Read memory content from a specific address.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

size bytes read from address

Raises:

SymbolicValueError – If the memory range contains a non-collapsible symbolic value

map_memory(address: int, size: int) None

Map memory of a given size.

If the requested allocation overlaps with existing regions, this will fill in the gaps.

Parameters:
  • address – The requested address of the allocation.

  • size – The size of the allocation.

get_memory_map() List[Tuple[int, int]]

Retrieve the memory map as understood by the emulator.

Returns:

The list of tuples (start, end) of the mapped segments

write_memory_content(address: int, content: bytes | BV) None

Write content to memory at a specific address.

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

Raises:

TypeError – If content is a BV, and this emulator can’t handle them

run() None

Run execution indefinitely.

Emulation should stop if an exit point is reached or execution leaves valid bounds.

Raises:

EmulationBoundsError – if execution steps out of bounds.

step_block() None

Single block step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

add_bound(start: int, end: int) None

Add valid execution bounds.

If execution leaves these bounds Emulators should raise EmulationBoundsError.

If execution bounds are not specified, Emulators should allow execution anywhere.

Parameters:
  • start – The start address of a valid executable region.

  • end – The end address of a valid executable region.

add_exit_point(address: int) None

Add an exit point.

If execution reaches an exit point emulation should stop.

Parameters:

address – The address of the exit point.

get_bounds() List[Tuple[int, int]]

Get a list of all registered execution bounds.

Returns:

A list of registered execution bounds.

get_exit_points() Set[int]

Get a list of all registered exit points.

Returns:

A list of registered exit points.

get_thumb() bool

For applicable platforms, checks if the CPU is in ARM or Thumb mode.

Returns:

True if in Thumb mode, otherwise False.

Raises:
  • ConfigurationError – if not using an ARM32 platform.

  • NotImplementedError – if not using a supported emulator.

hook_function(address: int, function: Callable[[Emulator], None]) None

Hook a specific function by address.

After the hook function is called, the Emulator will step out of the current function so that the hook essentially replaces a function call to the given address.

Parameters:
  • address – The address of the hook.

  • function – The function to execute when the address is reached.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
hook_instruction(address: int, function: Callable[[Emulator], None]) None

Hook a specific instruction by address.

Parameters:
  • address – The address of the hook.

  • function – The function to execute when the address is reached.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
hook_interrupt(intno: int, function: Callable[[Emulator], None]) None

Hook a specific system interrupt.

Parameters:

function – The function to execute when the interrupt is triggered.

Example

The hook function looks like:

def hook(emulator: Emulator) -> None:
    ...
hook_interrupts(function: Callable[[Emulator, int], None]) None

Hook any system interrupts.

Parameters:

function – The function to execute when an interrupt is triggered.

Example

The hook function looks like:

def hook(emulator: Emulator, interrupt: int) -> None:
    ...
hook_memory_read(start: int, end: int, function: Callable[[Emulator, int, int, bytes], bytes | None]) None

Hook memory reads within a given range, handling concrete values

Note that this will trigger for any read that overlaps the specified range, and will report all data read, not just the overlap. Hooks should expect to handle partial or oddly-sized reads.

If the hook chooses to override the data being read, it must return data of the same size as the original read.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: bytes
) -> bytes:
    # "content" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_read_symbolic(start: int, end: int, function: Callable[[Emulator, int, int, BV], BV | None]) None

Hook memory reads within a given range, handling symbolic values

Note that this will trigger for any read that overlaps the specified range, and will report all data read, not just the overlap. Hooks should expect to handle partial or oddly-sized reads.

If the hook chooses to override the data being read, it must return data of the same size as the original read.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is read.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: claripy.ast.bv.BV
) -> typing.Optional[claripy.ast.bv.BV]:
    # "content" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_reads(function: Callable[[Emulator, int, int, bytes], bytes | None]) None

Hook all memory reads, handling concrete values

Parameters:

function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    data: bytes
) -> typing.Optional[bytes]:
    # "data" represents the information the emulator would have fetched.
    # The return value is what should be reported to the emulator.
    # If return is "None", the original value of "content" will be reported.
    ...
hook_memory_reads_symbolic(function: Callable[[Emulator, int, int, BV], BV | None]) None

Hook all memory reads, handling concrete values

Parameters:

function – The function to execute when the memory region is read.

Example

The hook function looks like:

def hook(
    emulator: Emulator,
    address: int,
    size: int,
    content: claripy.ast.bv.BV
) -> typing.Optional[claripy.ast.bv.BV]:
    # "data" represents the data fetched by the emulator
    # The return value is what should be reported to the guest
    ...
hook_memory_write(start: int, end: int, function: Callable[[Emulator, int, int, bytes], None]) None

Hook memory writes within a given range, handling concrete values.

Note that this will trigger for any write that overlaps the specified range, and will report all data written, not just the overlap. Hooks should expect to handle partial or oddly-sized writes.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is written.

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: bytes) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_write_symbolic(start: int, end: int, function: Callable[[Emulator, int, int, BV], None]) None

Hook memory writes within a given range, handling symbolic values

Note that this will trigger for any write that overlaps the specified range, and will report all data written, not just the overlap. Hooks should expect to handle partial or oddly-sized writes.

Parameters:
  • start – The start address of the memory range to hook.

  • end – The end address of the memory range to hook.

  • function – The function to execute when the memory region is written.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: claripy.ast.bv.BV) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_writes(function: Callable[[Emulator, int, int, bytes], None]) None

Hook all memory writes, handling concrete values.

Parameters:

function – The function to execute when the memory region is written.

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: bytes) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
hook_memory_writes_symbolic(function: Callable[[Emulator, int, int, BV], None]) None

Hook all memory writes, handling symbolic values

Parameters:

function – The function to execute when the memory region is written.

Raises:

NotImplementedError – If this emulator doesn’t support symbolic operations

Example

The hook function looks like:

def hook(emulator: Emulator, address: int, size: int, content: claripy.ast.bv.BV) -> None:
    # "content" is the value written by the guest.
    # Hooks are responsible for completing the write to the emulator's state
    ...
read_memory(address: int, size: int) bytes

A helper to read memory content from a specific address.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

size bytes read from address.

Raises:

SymbolicValueError – If the memory range contains a non-collapsible symbolic value

read_memory_label(address: int, size: int) str | None

Read memory label from a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

The memory region’s label.

read_memory_symbolic(address: int, size: int) BV

Read memory content from a specific address as a symbolic expression.

If the implementation of read_register_content() raises SymbolicValueError, this must be implemented.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

A z3 expression of size * 8 bits read from address

read_memory_type(address: int, size: int) Any | None

Read memory type from a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region.

Returns:

The memory region’s type.

read_register(name: str) int

A helper to read the content of a register.

Parameters:

name – The name of the register.

Returns:

The register’s content.

read_register_label(name: str) str | None

Read the label of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:

name – The name of the register.

Returns:

The register’s label.

read_register_symbolic(name: str) BV

Read the content of a register, allowing symbolic output.

If the implementation of read_register_content() raises SymbolicValueError, this must be implemented.

Parameters:

name – The name of the register

Returns:

The register’s content as a z3 expression

read_register_type(name: str) Any | None

Read the type of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:

name – The name of the register.

Returns:

The register’s type

set_thumb(enabled=True) None

For applicable platforms, set the CPU to Thumb or ARM mode.

Parameters:

enabled – True for Thumb mode, False for ARM mode.

Raises:
  • ConfigurationError – if not using an ARM32 platform.

  • NotImplementedError – if not using a supported emulator.

step() None

Helper for single instruction step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

step_instruction() None

Single instruction step execution.

Raises:

EmulationBoundsError – if execution steps out of bounds.

unhook_function(address: int) None

Unhook a specific function by address.

Parameters:

address – The address of the hook to remove.

unhook_instruction(address: int) None

Unhook a specific instruction by address.

Parameters:

address – The address of the hook to remove.

unhook_instructions()

Unhook all system interrupts.

unhook_interrupt(intno: int) None

Unhook a specific system interrupt.

Parameters:

interrupt – The interrupt to unhook.

unhook_interrupts()

Unhook all system interrupts.

unhook_memory_read(start: int, end: int) None

Unhook a specific memory region read by address range.

Parameters:
  • start – The start address of the memory read hook to remove.

  • end – The end address of the memory read hook to remove.

unhook_memory_reads()

Unhook any ‘all reads’ handlers

unhook_memory_write(start: int, end: int) None

Unhook a specific memory region write by address range.

Parameters:
  • start – The start address of the memory write hook to remove.

  • end – The end address of the memory write hook to remove.

unhook_memory_writes()

Unhook any global memory write hook.

write_code(address: int, content: bytes) None

Write executable memory at a specific address.

Implementations can take advantage of this if they store code and memory differently.

Otherwise, it should be identical to write_memory().

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

write_memory(address: int, content: bytes | BV) None

A helper to write content to memory at a specific address.

Note

Written memory should already be mapped by some call to map_memory().

Parameters:
  • address – The address of the memory region.

  • content – The content to write.

Raises:

SymbolicValueError – If content is a bitvector expression, and this emulator doesn’t support them.

write_memory_label(address: int, size: int, label: str | None = None) None

Set the label of memory at a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region

  • label – The label of the register to write. To unset the register label, this may be set to None.

write_memory_type(address: int, size: int, type: Any | None = None) None

Set the type of memory at a specific address.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • address – The address of the memory region.

  • size – The size of the memory region

  • type – The type of the register to write. To unset the register type, this may be set to None.

write_register(name: str, content: None | int | BV) None

A helper to write the content of a register.

Parameters:
  • name – The name of the register.

  • content – The content to write.

Raises:

TypeError – If content is a BV, and this emulator cannot handle bitvector expressions

write_register_label(name: str, label: str | None = None) None

Write the label of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • name – The name of the register.

  • label – The label of the register to write. To unset the register label, this may be set to None.

write_register_type(name: str, type: Any | None = None) None

Write the type of a register.

Note

Implementing this behavior is optional and not necessarily supported by all Emulators.

Parameters:
  • name – The name of the register.

  • type – The type of the register to write. To unset the register type, this may be set to None.

smallworld.emulators.GhidraEmulator(platform: Platform) AbstractGhidraEmulator

Factory for creating a GhidraEmulator

Importing any of the pyghidra packages requires booting up a JVM, which takes several seconds. It also doesn’t work if Java and Ghidra are not configured.

Rather than requiring all SmallWorld users to sit through this process, export a factory method that looks exactly like an Emulator constructor. Only boot pyghidra if the factory is called, and only import GhidraEmulator if successful.

See GhidraEmulator in pcode.py for the actua emulator class.

Parameters:

platform – The platform to use when creating the emulator

Returns:

A GhidraEmulator object