Memory Control
What is Memory Control?
Memory control refers to reading, writing, inspecting, allocating, freeing, and protecting memory.
Example: Memory Control
"""
Example: Memory Control (64 bit)
"""
import queue
import sys
from x64dbg_automate import X64DbgClient
from x64dbg_automate.events import DbgEvent, EventType
from x64dbg_automate.models import MemPage, PageRightsConfiguration
if len(sys.argv) != 2:
print("Usage: python hello64.py <x64dbg_path>")
quit(1)
print('[+] Creating a new x64dbg Automate session')
client = X64DbgClient(x64dbg_path=sys.argv[1])
client.start_session(r'c:\Windows\system32\winver.exe')
client.go() # Continue from system breakpoint
client.wait_until_stopped()
client.go() # Continue from entrypoint
print('[+] Registering a callback for debug string events')
received: queue.Queue[DbgEvent] = queue.Queue()
callback = lambda x: received.put(x)
client.watch_debug_event(EventType.EVENT_OUTPUT_DEBUG_STRING, callback)
print('[+] Allocating memory for shellcode and debug string')
shellcode = client.virt_alloc()
debug_string = client.virt_alloc()
print('[+] Retrieving memory protection on debug string memory')
mem: MemPage = client.virt_query(debug_string)
print(f'\tMemory Protection: 0x{mem.allocation_protect:X}')
print('[+] Setting memory protection on debug string memory to readonly')
client.virt_protect(debug_string, PageRightsConfiguration.ReadOnly)
print('[+] Writing debug string to debugee memory')
client.write_memory(debug_string, b'https://www.youtube.com/watch?v=FKROYzWRiQ0')
print('[+] Writing shellcode to debugee memory')
i = shellcode
i = i + client.assemble_at(i, 'push rcx')
i = i + client.assemble_at(i, 'push rcx')
i = i + client.assemble_at(i, 'push rcx')
i = i + client.assemble_at(i, 'mov rax, OutputDebugStringA')
i = i + client.assemble_at(i, 'call rax')
i = i + client.assemble_at(i, 'pop rcx')
i = i + client.assemble_at(i, 'pop rcx')
i = i + client.assemble_at(i, 'pop rcx')
i = i + client.assemble_at(i, 'ret')
print('[+] Executing shellcode')
client.thread_create(shellcode, debug_string)
ev = received.get(timeout=4)
print('[+] Received debug string event')
print('\tEvent Type:', ev.event_type)
print('\tlpDebugStringData:', ev.event_data.lpDebugStringData.decode('utf-8').strip('\0'))
print('[+] Freeing memory')
client.virt_free(shellcode)
client.virt_free(debug_string)
print('[+] Retrieving full memory map and finding all *.DLL references')
pages: list[MemPage] = client.memmap()
for page in pages:
if '.dll' in page.info.lower():
print(f'\t{page.allocation_base:X} - {page.region_size:X} {page.protect} {page.info}')
print('[+] Terminating the session')
client.terminate_session()
[+] Creating a new x64dbg Automate session
[+] Registering a callback for debug string events
[+] Allocating memory for shellcode and debug string
[+] Retrieving memory protection on debug string memory
Memory Protection: 0x40
[+] Setting memory protection on debug string memory to readonly
[+] Writing debug string to debugee memory
[+] Writing shellcode to debugee memory
[+] Executing shellcode
[+] Received debug string event
Event Type: EVENT_OUTPUT_DEBUG_STRING
lpDebugStringData: https://www.youtube.com/watch?v=FKROYzWRiQ0
[+] Freeing memory
[+] Retrieving full memory map and finding all *.DLL references
13B701B0000 - 63000 2 \Device\HarddiskVolume3\Windows\System32\en-US\shell32.dll.mui
13B70250000 - 3000 2 \Device\HarddiskVolume3\Windows\WinSxS\amd64_microsoft.windows.c..-controls.resources_6595b64144ccf1df_6.0.26100.1591_en-us_541af4fe0fd3faf0\comctl32.dll.mui
13B70270000 - 3000 2 \Device\HarddiskVolume3\Windows\System32\oleaccrc.dll
7FFC726F0000 - 1000 2 oleacc.dll
7FFC77140000 - 1000 2 comctl32.dll
7FFC80AD0000 - 1000 2 textshaping.dll
7FFC82A30000 - 1000 2 textinputframework.dll
7FFC84210000 - 1000 2 winbrand.dll
7FFC8DA80000 - 1000 2 coreuicomponents.dll
7FFC903D0000 - 1000 2 coremessaging.dll
7FFC910D0000 - 1000 2 wintypes.dll
7FFC92380000 - 1000 2 uxtheme.dll
7FFC93F10000 - 1000 2 kernel.appcore.dll
7FFC94620000 - 1000 2 cryptbase.dll
7FFC95290000 - 1000 2 ucrtbase.dll
7FFC95470000 - 1000 2 gdi32full.dll
7FFC955A0000 - 1000 2 win32u.dll
7FFC955D0000 - 1000 2 kernelbase.dll
7FFC95990000 - 1000 2 msvcp_win.dll
7FFC95A40000 - 1000 2 bcryptprimitives.dll
7FFC95D40000 - 1000 2 shcore.dll
7FFC95E30000 - 1000 2 gdi32.dll
7FFC95FD0000 - 1000 2 user32.dll
7FFC96260000 - 1000 2 combase.dll
7FFC96A80000 - 1000 2 oleaut32.dll
7FFC96BB0000 - 1000 2 shell32.dll
7FFC972C0000 - 1000 2 advapi32.dll
7FFC97380000 - 1000 2 kernel32.dll
7FFC97450000 - 1000 2 msctf.dll
7FFC975B0000 - 1000 2 sechost.dll
7FFC976D0000 - 1000 2 shlwapi.dll
7FFC97730000 - 1000 2 clbcatq.dll
7FFC97860000 - 1000 2 msvcrt.dll
7FFC97930000 - 1000 2 imm32.dll
7FFC97970000 - 1000 2 rpcrt4.dll
7FFC97D40000 - 1000 2 ntdll.dll
[+] Terminating the session
API Method Reference
write_memory(addr, data)
Writes data to the debugee's memory
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
The address to write to |
required |
data
|
bytes
|
The data to be written |
required |
Returns:
Type | Description |
---|---|
bool
|
Success |
Source code in x64dbg_automate/commands_xauto.py
def write_memory(self, addr: int, data: bytes) -> bool:
"""
Writes data to the debugee's memory
Args:
addr: The address to write to
data: The data to be written
Returns:
Success
"""
return self._send_request(XAutoCommand.XAUTO_REQ_DBG_WRITE_MEMORY, addr, data)
read_memory(addr, size)
Reads data from the debugee's memory
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
The address to read from |
required |
size
|
int
|
The number of bytes to read |
required |
Returns:
Type | Description |
---|---|
bytes
|
Bytes read from memory |
Source code in x64dbg_automate/commands_xauto.py
def read_memory(self, addr: int, size: int) -> bytes:
"""
Reads data from the debugee's memory
Args:
addr: The address to read from
size: The number of bytes to read
Returns:
Bytes read from memory
"""
return self._send_request(XAutoCommand.XAUTO_REQ_DBG_READ_MEMORY, addr, size)
read_word(addr)
Reads word size data from the debugee's memory
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
The address to read from |
required |
Returns:
Type | Description |
---|---|
int
|
The word read from memory |
Source code in x64dbg_automate/commands_xauto.py
def read_word(self, addr: int) -> int:
"""
Reads word size data from the debugee's memory
Args:
addr: The address to read from
Returns:
The word read from memory
"""
mem = self._send_request(XAutoCommand.XAUTO_REQ_DBG_READ_MEMORY, addr, 2)
return int.from_bytes(mem, 'little')
read_dword(addr)
Reads dword size data from the debugee's memory
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
The address to read from |
required |
Returns:
Type | Description |
---|---|
int
|
The dword read from memory |
Source code in x64dbg_automate/commands_xauto.py
def read_dword(self, addr: int) -> int:
"""
Reads dword size data from the debugee's memory
Args:
addr: The address to read from
Returns:
The dword read from memory
"""
mem = self._send_request(XAutoCommand.XAUTO_REQ_DBG_READ_MEMORY, addr, 4)
return int.from_bytes(mem, 'little')
read_qword(addr)
Reads qword size data from the debugee's memory
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
The address to read from |
required |
Returns:
Type | Description |
---|---|
int
|
The qword read from memory |
Source code in x64dbg_automate/commands_xauto.py
def read_qword(self, addr: int) -> int:
"""
Reads qword size data from the debugee's memory
Args:
addr: The address to read from
Returns:
The qword read from memory
"""
mem = self._send_request(XAutoCommand.XAUTO_REQ_DBG_READ_MEMORY, addr, 8)
return int.from_bytes(mem, 'little')
memmap()
Retrieves the memory map of the debugee
Returns:
Type | Description |
---|---|
list[MemPage]
|
A list of MemPage objects |
Source code in x64dbg_automate/commands_xauto.py
def memmap(self) -> list[MemPage]:
"""
Retrieves the memory map of the debugee
Returns:
A list of MemPage objects
"""
resp = self._send_request(XAutoCommand.XAUTO_REQ_DBG_MEMMAP)
pages = []
for page in resp:
pages.append(MemPage(**{k: v for k, v in zip(MemPage.model_fields.keys(), page)}))
return pages
virt_alloc(n=4096, addr=0)
Allocates memory in the debugee's address space
Parameters:
Name | Type | Description | Default |
---|---|---|---|
n
|
int
|
Size of memory to allocate |
4096
|
addr
|
int
|
Address to allocate memory at |
0
|
Returns:
Type | Description |
---|---|
int
|
Address of the allocated memory |
Source code in x64dbg_automate/hla_xauto.py
def virt_alloc(self, n: int = 0x1000, addr: int = 0) -> int:
"""
Allocates memory in the debugee's address space
Args:
n: Size of memory to allocate
addr: Address to allocate memory at
Returns:
Address of the allocated memory
"""
if not self.cmd_sync(f"alloc 0x{n:x}, 0x{addr:x}"):
raise ValueError("Failed to allocate memory")
addr, success = self.eval_sync("$result")
if not success:
raise ValueError("Failed to evaluate result")
return addr
virt_protect(addr, page_rights, guard=False)
Changes a pages memory protection in the debugee's address space, optionally setting a page guard.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
Address to operate on |
required |
page_rights
|
PageRightsConfiguration
|
New memory protection configuration |
required |
guard
|
bool
|
page guard toggle |
False
|
Returns:
Type | Description |
---|---|
bool
|
Success |
Source code in x64dbg_automate/hla_xauto.py
def virt_protect(self, addr: int, page_rights: PageRightsConfiguration, guard: bool = False) -> bool:
"""
Changes a pages memory protection in the debugee's address space, optionally setting a page guard.
Args:
addr: Address to operate on
page_rights: New memory protection configuration
guard: page guard toggle
Returns:
Success
"""
rights_str = str(page_rights)
if guard:
rights_str = f'G{rights_str}'
if not self.cmd_sync(f"setpagerights 0x{addr:x}, {rights_str}"):
raise ValueError("Failed to set memory protection")
return True
virt_query(addr)
Retrieves information about a memory region.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
Address to query |
required |
Returns:
Type | Description |
---|---|
MemPage | None
|
MemPage on success, None on failure |
Source code in x64dbg_automate/hla_xauto.py
def virt_query(self, addr: int) -> MemPage | None:
"""
Retrieves information about a memory region.
Args:
addr: Address to query
Returns:
MemPage on success, None on failure
"""
map = self.memmap()
for m in map:
if m.base_address <= addr < m.base_address + m.region_size:
return m
return None
virt_free(addr)
Frees memory in the debugee's address space
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
Address to free memory at |
required |
Returns:
Type | Description |
---|---|
bool
|
Success |
Source code in x64dbg_automate/hla_xauto.py
def virt_free(self, addr: int) -> bool:
"""
Frees memory in the debugee's address space
Args:
addr: Address to free memory at
Returns:
Success
"""
if not self.cmd_sync(f"free 0x{addr:x}"):
raise ValueError("Failed to free memory")
return True
memset(addr, byte_val, size)
Sets memory in the debugee's address space to the specified value
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
Address to set memory at |
required |
byte_val
|
int
|
Value to set memory to |
required |
size
|
int
|
Number of bytes to set |
required |
Returns:
Type | Description |
---|---|
bool
|
Success |
Source code in x64dbg_automate/hla_xauto.py
def memset(self, addr: int, byte_val: int, size: int) -> bool:
"""
Sets memory in the debugee's address space to the specified value
Args:
addr: Address to set memory at
byte_val: Value to set memory to
size: Number of bytes to set
Returns:
Success
"""
if not self.cmd_sync(f"memset 0x{addr:x}, 0x{byte_val:x}, 0x{size:x}"):
raise ValueError("Failed to set memory")
return True
check_valid_read_ptr(addr)
Checks if the specified address is accessible read memory in the debugee
Parameters:
Name | Type | Description | Default |
---|---|---|---|
addr
|
int
|
The address to check |
required |
Returns:
Type | Description |
---|---|
bool
|
True if the address is valid, False otherwise |
Source code in x64dbg_automate/commands_xauto.py
def check_valid_read_ptr(self, addr: int) -> bool:
"""
Checks if the specified address is accessible read memory in the debugee
Args:
addr: The address to check
Returns:
True if the address is valid, False otherwise
"""
return self._send_request(XAutoCommand.XAUTO_REQ_DBG_IS_VALID_READ_PTR, addr)
API Model Reference
MemPage
Source code in x64dbg_automate/models.py
class MemPage(BaseModel):
base_address: int
allocation_base: int
allocation_protect: int
partition_id: int
region_size: int
state: int
protect: int
type: int
info: str
PageRightsConfiguration
Source code in x64dbg_automate/models.py
class PageRightsConfiguration(StrEnum):
Execute = "Execute"
ExecuteRead = "ExecuteRead"
ExecuteReadWrite = "ExecuteReadWrite"
ExecuteWriteCopy = "ExecuteWriteCopy"
NoAccess = "NoAccess"
ReadOnly = "ReadOnly"
ReadWrite = "ReadWrite"
WriteCopy = "WriteCopy"