-
Notifications
You must be signed in to change notification settings - Fork 749
Memory Management
Effective memory management is crucial for stable and accurate binary emulation. Qiling provides a comprehensive set of tools for interacting with and manipulating the memory of the emulated process.
When Qiling loads a binary, it creates a virtual address space for the process. This layout typically includes:
-
Code Section (
.text
): The executable code of the binary. -
Data Sections (
.data
,.bss
): Initialized and uninitialized data. -
Heap: Dynamically allocated memory (e.g., from
malloc
). - Stack: Used for function calls, local variables, and return addresses.
- Memory Mapped Regions: Shared libraries and other memory-mapped files.
Qiling's loaders handle the initial mapping of the binary and its dependencies into this address space.
The primary interface for memory operations is the ql.mem
object. It provides methods for reading, writing, and inspecting memory.
You can read data from any valid memory address.
Example:
# Read 8 bytes from a specific address
address = 0x400000
data = ql.mem.read(address, 8)
print(f"Data at {address:#x}: {data}")
# Read a null-terminated string
string_address = 0x401000
string = ql.mem.string(string_address)
print(f"String at {string_address:#x}: {string}")
You can also write data to the emulated memory.
Example:
# Write a byte string to an address
address = 0x402000
ql.mem.write(address, b"\xDE\xAD\xBE\xEF")
# Write an integer to memory (Qiling handles packing)
int_address = 0x403000
ql.mem.write_int(int_address, 0x12345678, 4) # 4 bytes
Qiling respects memory permissions (Read, Write, Execute). If the emulated program attempts to write to a read-only memory region or execute from a non-executable region, Qiling will raise an exception, just like a real OS would. You can inspect the memory map to see the permissions for different regions.
Example:
# Print the memory map
for m in ql.mem.get_map():
print(f"Start: {m[0]:#x}, End: {m[1]:#x}, Perms: {m[2]}")
Qiling provides convenient methods for interacting with the stack.
Example:
# Get the stack pointer value
sp = ql.reg.sp
# Push a value onto the stack
ql.stack_push(0xCAFEBABE)
# Pop a value from the stack
value = ql.stack_pop()
print(f"Popped value: {value:#x}")
# Read from the stack without modifying the stack pointer
value_at_offset = ql.stack_read(0x10) # Read from SP + 0x10
Qiling emulates heap allocation functions like malloc
and free
. When the emulated program requests memory, Qiling allocates a new memory region and returns the address to the program. This is handled transparently by the OS emulation layer.
You can also manually allocate memory in the emulated process, which is useful for injecting data or setting up custom data structures.
Example:
# Allocate 64 bytes of memory with RW permissions
size = 64
address = ql.mem.alloc(size)
print(f"Allocated {size} bytes at {address:#x}")
# You can then write to this memory
ql.mem.write(address, b"This is my custom allocated memory!")
# Free the allocated memory
ql.mem.free(address)
Understanding these memory operations is fundamental for building powerful analysis scripts, modifying program behavior, and setting up complex emulation scenarios.
- Home
- Getting Started
- Core Concepts
- Usage
- Features
- Tutorials
- Development
- Resources