Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ test/__pycache__
/test/Pipfile

.DS_Store
__pycache__/
4 changes: 4 additions & 0 deletions api/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ extern "C"
BNDebuggerController* controller, uint64_t address, size_t size);
DEBUGGER_FFI_API bool BNDebuggerWriteMemory(
BNDebuggerController* controller, uint64_t address, BNDataBuffer* buffer);
DEBUGGER_FFI_API uint64_t BNDebuggerAllocateMemory(
BNDebuggerController* controller, size_t size, uint32_t permissions);
DEBUGGER_FFI_API bool BNDebuggerFreeMemory(
BNDebuggerController* controller, uint64_t address);

DEBUGGER_FFI_API BNDebugProcess* BNDebuggerGetProcessList(BNDebuggerController* controller, size_t* count);
DEBUGGER_FFI_API void BNDebuggerFreeProcessList(BNDebugProcess* processes, size_t count);
Expand Down
27 changes: 27 additions & 0 deletions api/python/debuggercontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,14 @@ class DebuggerController:
>>> dbg.data.write(dbg.stack_pointer, b'a' * 0x10)
16

To allocate and free memory in the target process, use ``allocate_memory``/``free_memory``:

>>> addr = dbg.allocate_memory(1024) # Allocate 1KB with default permissions
>>> if addr != 0:
... dbg.write_memory(addr, b'Hello, World!')
... dbg.free_memory(addr)
True

``modules`` returns the list of modules, `threads` returns the list of threads.

Breakpoints can be added via `add_breakpoint`:
Expand Down Expand Up @@ -674,6 +682,25 @@ def write_memory(self, address: int, buffer) -> bool:
buffer_obj = ctypes.cast(buffer.handle, ctypes.POINTER(dbgcore.BNDataBuffer))
return dbgcore.BNDebuggerWriteMemory(self.handle, address, buffer_obj)

def allocate_memory(self, size: int, permissions: int = 0x7) -> int:
"""
Allocate memory in the target process.

:param size: number of bytes to allocate
:param permissions: memory permissions (default 0x7 for read/write/execute)
:return: address of allocated memory, or 0 on failure
"""
return dbgcore.BNDebuggerAllocateMemory(self.handle, size, permissions)

def free_memory(self, address: int) -> bool:
"""
Free previously allocated memory in the target process.

:param address: address of memory to free (returned by allocate_memory)
:return: True on success, False on failure
"""
return dbgcore.BNDebuggerFreeMemory(self.handle, address)

@property
def processes(self) -> List[DebugProcess]:
"""
Expand Down
103 changes: 103 additions & 0 deletions core/adapters/corelliumadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,109 @@ bool CorelliumAdapter::WriteMemory(std::uintptr_t address, const DataBuffer& buf
}


std::uintptr_t CorelliumAdapter::AllocateMemory(std::size_t size, std::uint32_t permissions)
{
if (m_isTargetRunning)
return 0;

// Use Corellium's memory allocation command via monitor/maintenance commands
// Similar to GDB approach but tailored for Corellium's capabilities

// Try using the 'monitor' command for memory allocation
std::string allocCommand = fmt::format("monitor memory allocate {}", size);
auto reply = this->m_rspConnector->TransmitAndReceive(RspData("qRcmd,{}",
[&allocCommand]() {
std::string hex;
for (char c : allocCommand) {
hex += fmt::format("{:02X}", static_cast<unsigned char>(c));
}
return hex;
}()));

// Parse the response to extract the allocated address
std::string response = reply.AsString();
if (response.substr(0, 2) == "OK" || response.substr(0, 1) == "E") {
// If monitor command is not supported, return 0 to indicate failure
return 0;
}

// Try to parse hex address from response
if (response.length() >= 2) {
try {
// Remove any "O" prefixes (GDB console output) and decode hex
if (response.substr(0, 1) == "O") {
// Decode hex-encoded console output
std::string decoded;
for (size_t i = 1; i < response.length(); i += 2) {
if (i + 1 < response.length()) {
int byte = std::stoi(response.substr(i, 2), nullptr, 16);
decoded += static_cast<char>(byte);
}
}

// Try to extract address from decoded string
size_t pos = decoded.find("0x");
if (pos != std::string::npos) {
std::string addrStr = decoded.substr(pos + 2);
// Find end of hex address
size_t endPos = 0;
while (endPos < addrStr.length() &&
std::isxdigit(addrStr[endPos])) {
endPos++;
}
if (endPos > 0) {
return std::stoull(addrStr.substr(0, endPos), nullptr, 16);
}
}
}
} catch (const std::exception&) {
// Failed to parse address
}
}

return 0; // Allocation failed
}


bool CorelliumAdapter::FreeMemory(std::uintptr_t address)
{
if (m_isTargetRunning)
return false;

// Use Corellium's memory deallocation command via monitor commands
std::string freeCommand = fmt::format("monitor memory free 0x{:x}", address);
auto reply = this->m_rspConnector->TransmitAndReceive(RspData("qRcmd,{}",
[&freeCommand]() {
std::string hex;
for (char c : freeCommand) {
hex += fmt::format("{:02X}", static_cast<unsigned char>(c));
}
return hex;
}()));

// Check if the operation was successful
std::string response = reply.AsString();

// Success is typically indicated by "OK" response
if (response == "OK") {
return true;
}

// Also consider hex-encoded "OK" response
if (response == "4F4B") { // "OK" in hex
return true;
}

// If we get a console output response starting with "O"
if (response.substr(0, 1) == "O") {
// For simplicity, assume success if no error message is detected
return true;
}

return false; // Deallocation failed
}


std::string CorelliumAdapter::GetRemoteFile(const std::string& path)
{
if (m_isTargetRunning)
Expand Down
2 changes: 2 additions & 0 deletions core/adapters/corelliumadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ namespace BinaryNinjaDebugger

DataBuffer ReadMemory(std::uintptr_t address, std::size_t size) override;
bool WriteMemory(std::uintptr_t address, const DataBuffer& buffer) override;
std::uintptr_t AllocateMemory(std::size_t size, std::uint32_t permissions = 0x7) override;
bool FreeMemory(std::uintptr_t address) override;
std::string GetRemoteFile(const std::string& path);
std::vector<DebugModule> GetModuleList() override;

Expand Down
18 changes: 18 additions & 0 deletions core/adapters/dbgengadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,24 @@ bool DbgEngAdapter::WriteMemory(std::uintptr_t address, const DataBuffer& buffer
}


std::uintptr_t DbgEngAdapter::AllocateMemory(std::size_t size, std::uint32_t permissions)
{
// DbgEng doesn't have a direct memory allocation API
// We would need to use VirtualAllocEx or similar Win32 APIs
// For now, return 0 to indicate allocation is not supported
return 0;
}


bool DbgEngAdapter::FreeMemory(std::uintptr_t address)
{
// DbgEng doesn't have a direct memory deallocation API
// We would need to use VirtualFreeEx or similar Win32 APIs
// For now, return false to indicate deallocation is not supported
return false;
}


std::vector<DebugFrame> DbgEngAdapter::GetFramesOfThread(uint32_t tid)
{
std::vector<DebugFrame> result;
Expand Down
2 changes: 2 additions & 0 deletions core/adapters/dbgengadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ namespace BinaryNinjaDebugger {

DataBuffer ReadMemory(std::uintptr_t address, std::size_t size) override;
bool WriteMemory(std::uintptr_t address, const DataBuffer& buffer) override;
std::uintptr_t AllocateMemory(std::size_t size, std::uint32_t permissions = 0x7) override;
bool FreeMemory(std::uintptr_t address) override;

// bool ReadMemory(std::uintptr_t address, void* out, std::size_t size) override;
// bool WriteMemory(std::uintptr_t address, const void* out, std::size_t size) override;
Expand Down
14 changes: 14 additions & 0 deletions core/adapters/dbgengttdadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ bool DbgEngTTDAdapter::WriteMemory(std::uintptr_t address, const BinaryNinja::Da
}


std::uintptr_t DbgEngTTDAdapter::AllocateMemory(std::size_t size, std::uint32_t permissions)
{
// TTD (Time Travel Debugging) traces are read-only, memory allocation is not supported
return 0;
}


bool DbgEngTTDAdapter::FreeMemory(std::uintptr_t address)
{
// TTD (Time Travel Debugging) traces are read-only, memory deallocation is not supported
return false;
}


bool DbgEngTTDAdapter::WriteRegister(const std::string& reg, intx::uint512 value)
{
return false;
Expand Down
2 changes: 2 additions & 0 deletions core/adapters/dbgengttdadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace BinaryNinjaDebugger {
[[nodiscard]] bool ExecuteWithArgsInternal(const std::string& path, const std::string& args,
const std::string& workingDir, const LaunchConfigurations& configs = {}) override;
bool WriteMemory(std::uintptr_t address, const DataBuffer& buffer) override;
std::uintptr_t AllocateMemory(std::size_t size, std::uint32_t permissions = 0x7) override;
bool FreeMemory(std::uintptr_t address) override;
bool WriteRegister(const std::string& reg, intx::uint512 value) override;

bool Start() override;
Expand Down
91 changes: 91 additions & 0 deletions core/adapters/esrevenadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,97 @@ bool EsrevenAdapter::WriteMemory(std::uintptr_t address, const DataBuffer& buffe
}


std::uintptr_t EsrevenAdapter::AllocateMemory(std::size_t size, std::uint32_t permissions)
{
if (m_isTargetRunning)
return 0;

// Use ESReven's memory allocation command via monitor/maintenance commands
// Similar to other RSP-based adapters

std::string allocCommand = fmt::format("monitor memory allocate {}", size);
auto reply = this->m_rspConnector->TransmitAndReceive(RspData("qRcmd,{}",
[&allocCommand]() {
std::string hex;
for (char c : allocCommand) {
hex += fmt::format("{:02X}", static_cast<unsigned char>(c));
}
return hex;
}()));

// Parse the response to extract the allocated address
std::string response = reply.AsString();
if (response.substr(0, 2) == "OK" || response.substr(0, 1) == "E") {
return 0;
}

// Try to parse hex address from response
if (response.length() >= 2) {
try {
if (response.substr(0, 1) == "O") {
// Decode hex-encoded console output
std::string decoded;
for (size_t i = 1; i < response.length(); i += 2) {
if (i + 1 < response.length()) {
int byte = std::stoi(response.substr(i, 2), nullptr, 16);
decoded += static_cast<char>(byte);
}
}

// Try to extract address from decoded string
size_t pos = decoded.find("0x");
if (pos != std::string::npos) {
std::string addrStr = decoded.substr(pos + 2);
size_t endPos = 0;
while (endPos < addrStr.length() &&
std::isxdigit(addrStr[endPos])) {
endPos++;
}
if (endPos > 0) {
return std::stoull(addrStr.substr(0, endPos), nullptr, 16);
}
}
}
} catch (const std::exception&) {
// Failed to parse address
}
}

return 0; // Allocation failed
}


bool EsrevenAdapter::FreeMemory(std::uintptr_t address)
{
if (m_isTargetRunning)
return false;

// Use ESReven's memory deallocation command via monitor commands
std::string freeCommand = fmt::format("monitor memory free 0x{:x}", address);
auto reply = this->m_rspConnector->TransmitAndReceive(RspData("qRcmd,{}",
[&freeCommand]() {
std::string hex;
for (char c : freeCommand) {
hex += fmt::format("{:02X}", static_cast<unsigned char>(c));
}
return hex;
}()));

// Check if the operation was successful
std::string response = reply.AsString();

if (response == "OK" || response == "4F4B") { // "OK" in hex
return true;
}

if (response.substr(0, 1) == "O") {
return true;
}

return false; // Deallocation failed
}


std::string EsrevenAdapter::GetRemoteFile(const std::string& path)
{
if (m_isTargetRunning || !m_rspConnector)
Expand Down
2 changes: 2 additions & 0 deletions core/adapters/esrevenadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ namespace BinaryNinjaDebugger

DataBuffer ReadMemory(std::uintptr_t address, std::size_t size) override;
bool WriteMemory(std::uintptr_t address, const DataBuffer& buffer) override;
std::uintptr_t AllocateMemory(std::size_t size, std::uint32_t permissions = 0x7) override;
bool FreeMemory(std::uintptr_t address) override;
std::string GetRemoteFile(const std::string& path);
std::vector<DebugModule> GetModuleList() override;

Expand Down
Loading