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
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,19 @@ api/python/__pycache__
*.bndb

/.claude/

# visual studio related build files
*.vcxproj*
*.cmake
*.recipe
*.tlog
*.stamp
*.depend
**/CMakeFiles
/out
/ui/debuggerui_autogen
**/RelWithDebInfo
CMakeCache.txt
*.sln
api/python/binaryninjacore.dll
/core/api/vendor
3 changes: 2 additions & 1 deletion api/debuggerapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ namespace BinaryNinjaDebuggerAPI {

// TTD Memory Analysis Methods
std::vector<TTDMemoryEvent> GetTTDMemoryAccessForAddress(uint64_t address, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead);
std::vector<TTDMemoryEvent> GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime);
std::vector<TTDCallEvent> GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress = 0, uint64_t endReturnAddress = 0);
std::vector<TTDEvent> GetTTDEvents(TTDEventType eventType);
std::vector<TTDEvent> GetAllTTDEvents();
Expand All @@ -807,7 +808,7 @@ namespace BinaryNinjaDebuggerAPI {

// TTD Code Coverage Analysis Methods
bool IsInstructionExecuted(uint64_t address);
bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress);
bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime);
size_t GetExecutedInstructionCount() const;
bool SaveCodeCoverageToFile(const std::string& filePath) const;
bool LoadCodeCoverageFromFile(const std::string& filePath);
Expand Down
46 changes: 44 additions & 2 deletions api/debuggercontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,43 @@ bool DebuggerController::IsTTD()
return BNDebuggerIsTTD(m_object);
}

std::vector<TTDMemoryEvent> DebuggerController::GetTTDMemoryAccessForPositionRange(uint64_t address, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime)
{
std::vector<TTDMemoryEvent> result;

BNDebuggerTTDMemoryAccessType type = static_cast<BNDebuggerTTDMemoryAccessType>(accessType);
BNDebuggerTTDPosition bnStartTime = {startTime.sequence, startTime.step};
BNDebuggerTTDPosition bnEndTime = {endTime.sequence, endTime.step};

size_t count = 0;
BNDebuggerTTDMemoryEvent* events = BNDebuggerGetTTDMemoryAccessForPositionRange(m_object, address, endAddress, type, bnStartTime, bnEndTime, &count);

if (events && count > 0)
{
result.reserve(count);
for (size_t i = 0; i < count; i++)
{
TTDMemoryEvent event;
event.eventType = events[i].eventType ? std::string(events[i].eventType) : "";
event.threadId = events[i].threadId;
event.uniqueThreadId = events[i].uniqueThreadId;
event.timeStart.sequence = events[i].timeStart.sequence;
event.timeStart.step = events[i].timeStart.step;
event.timeEnd.sequence = events[i].timeEnd.sequence;
event.timeEnd.step = events[i].timeEnd.step;
event.accessType = static_cast<TTDMemoryAccessType>(events[i].accessType);
event.address = events[i].address;
event.size = events[i].size;
event.memoryAddress = events[i].memoryAddress;
event.instructionAddress = events[i].instructionAddress;
event.value = events[i].value;
result.push_back(event);
}
BNDebuggerFreeTTDMemoryEvents(events, count);
}

return result;
}

std::vector<TTDMemoryEvent> DebuggerController::GetTTDMemoryAccessForAddress(uint64_t address, uint64_t endAddress, TTDMemoryAccessType accessType)
{
Expand Down Expand Up @@ -1325,9 +1362,14 @@ bool DebuggerController::IsInstructionExecuted(uint64_t address)
}


bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress)
bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime)
{
return BNDebuggerRunCodeCoverageAnalysisRange(m_object, startAddress, endAddress);
BNDebuggerTTDPosition startPos, endPos;
startPos.sequence = startTime.sequence;
startPos.step = startTime.step;
endPos.sequence = endTime.sequence;
endPos.step = endTime.step;
return BNDebuggerRunCodeCoverageAnalysisRange(m_object, startAddress, endAddress, startPos, endPos);
}


Expand Down
5 changes: 4 additions & 1 deletion api/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,9 @@ extern "C"
// TTD Memory Analysis Functions
DEBUGGER_FFI_API BNDebuggerTTDMemoryEvent* BNDebuggerGetTTDMemoryAccessForAddress(BNDebuggerController* controller,
uint64_t address, uint64_t endAddress, BNDebuggerTTDMemoryAccessType accessType, size_t* count);
DEBUGGER_FFI_API BNDebuggerTTDMemoryEvent* BNDebuggerGetTTDMemoryAccessForPositionRange(BNDebuggerController* controller,
uint64_t address, uint64_t endAddress, BNDebuggerTTDMemoryAccessType accessType ,BNDebuggerTTDPosition startPosition, BNDebuggerTTDPosition endPosition,
size_t* count);
DEBUGGER_FFI_API BNDebuggerTTDCallEvent* BNDebuggerGetTTDCallsForSymbols(BNDebuggerController* controller,
const char* symbols, uint64_t startReturnAddress, uint64_t endReturnAddress, size_t* count);
DEBUGGER_FFI_API BNDebuggerTTDEvent* BNDebuggerGetTTDEvents(BNDebuggerController* controller,
Expand All @@ -678,7 +681,7 @@ extern "C"

// TTD Code Coverage Analysis Functions
DEBUGGER_FFI_API bool BNDebuggerIsInstructionExecuted(BNDebuggerController* controller, uint64_t address);
DEBUGGER_FFI_API bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress);
DEBUGGER_FFI_API bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress, BNDebuggerTTDPosition startTime, BNDebuggerTTDPosition endTime);
DEBUGGER_FFI_API size_t BNDebuggerGetExecutedInstructionCount(BNDebuggerController* controller);
DEBUGGER_FFI_API bool BNDebuggerSaveCodeCoverageToFile(BNDebuggerController* controller, const char* filePath);
DEBUGGER_FFI_API bool BNDebuggerLoadCodeCoverageFromFile(BNDebuggerController* controller, const char* filePath);
Expand Down
56 changes: 56 additions & 0 deletions core/adapters/dbgengttdadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,18 @@ std::vector<TTDMemoryEvent> DbgEngTTDAdapter::GetTTDMemoryAccessForAddress(uint6
return events;
}

std::vector<TTDMemoryEvent> DbgEngTTDAdapter::GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime)
{
std::vector<TTDMemoryEvent> events;

if (!QueryMemoryAccessByAddressAndPositionRange(startAddress, endAddress, accessType, startTime, endTime, events))
{
LogError("Failed to query TTD memory access events for address range 0x%llx-0x%llx", startAddress, endAddress);
}

return events;
}

TTDPosition DbgEngTTDAdapter::GetCurrentTTDPosition()
{
TTDPosition position;
Expand Down Expand Up @@ -563,6 +575,50 @@ bool DbgEngTTDAdapter::QueryMemoryAccessByAddress(uint64_t startAddress, uint64_
}
}

bool DbgEngTTDAdapter::QueryMemoryAccessByAddressAndPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, TTDPosition startTime, TTDPosition endTime, std::vector<TTDMemoryEvent>& events)
{
if (!m_debugControl)
{
LogError("Debug control interface not available");
return false;
}

try
{
// Build the access type string for TTD memory queries - combine flags as needed
std::string accessTypeStr;
if (accessType & TTDMemoryRead) accessTypeStr += "r";
if (accessType & TTDMemoryWrite) accessTypeStr += "w";
if (accessType & TTDMemoryExecute) accessTypeStr += "e";

if (accessTypeStr.empty())
{
LogError("Invalid access type specified");
return false;
}

// Create the actual TTD memory query expression
std::string expression = fmt::format("@$cursession.TTD.MemoryForPositionRange(0x{:x},0x{:x},\"{}\",\"{:x}:{:x}\",\"{:x}:{:x}\")", startAddress, endAddress, accessTypeStr, startTime.sequence,startTime.step, endTime.sequence, endTime.step);

LogInfo("Executing TTD memory query: %s", expression.c_str());

// Execute the query and parse results
if (!ParseTTDMemoryObjects(expression, accessType, events))
{
LogError("Failed to parse TTD memory objects from query");
return false;
}

LogInfo("Successfully retrieved %zu TTD memory events", events.size());
return true;
}
catch (const std::exception& e)
{
LogError("Exception in QueryMemoryAccessByAddress: %s", e.what());
return false;
}
}


void DbgEngTTDAdapter::GenerateDefaultAdapterSettings(BinaryView* data)
{
Expand Down
4 changes: 4 additions & 0 deletions core/adapters/dbgengttdadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ namespace BinaryNinjaDebugger {

// TTD Memory Analysis Methods
std::vector<TTDMemoryEvent> GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead) override;
std::vector<TTDMemoryEvent> GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, TTDPosition startTime, TTDPosition endTime) override;

// TTD Position Methods
TTDPosition GetCurrentTTDPosition() override;
bool SetTTDPosition(const TTDPosition& position) override;

Expand All @@ -64,6 +67,7 @@ namespace BinaryNinjaDebugger {
private:
// Helper methods for TTD memory analysis
bool QueryMemoryAccessByAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, std::vector<TTDMemoryEvent>& events);
bool QueryMemoryAccessByAddressAndPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, TTDPosition startTime, TTDPosition endTime, std::vector<TTDMemoryEvent>& events);

// Helper methods for TTD calls analysis
bool QueryCallsForSymbols(const std::vector<std::string>& symbols, uint64_t startReturnAddress, uint64_t endReturnAddress, std::vector<TTDCallEvent>& events);
Expand Down
6 changes: 6 additions & 0 deletions core/debugadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ std::vector<TTDMemoryEvent> DebugAdapter::GetTTDMemoryAccessForAddress(uint64_t
return {};
}

std::vector<TTDMemoryEvent> DebugAdapter::GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime)
{
// Default implementation returns empty results for adapters that don't support TTD
return {};
}


std::vector<TTDCallEvent> DebugAdapter::GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress, uint64_t endReturnAddress)
{
Expand Down
1 change: 1 addition & 0 deletions core/debugadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ namespace BinaryNinjaDebugger {

// TTD (Time Travel Debugging) methods - default implementations return empty results
virtual std::vector<TTDMemoryEvent> GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead);
virtual std::vector<TTDMemoryEvent> GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime);
virtual std::vector<TTDCallEvent> GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress = 0, uint64_t endReturnAddress = 0);
virtual std::vector<TTDEvent> GetTTDEvents(TTDEventType eventType);
virtual std::vector<TTDEvent> GetAllTTDEvents();
Expand Down
42 changes: 36 additions & 6 deletions core/debuggercontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3134,6 +3134,24 @@ std::vector<TTDMemoryEvent> DebuggerController::GetTTDMemoryAccessForAddress(uin
return events;
}

std::vector<TTDMemoryEvent> DebuggerController::GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime)
{
std::vector<TTDMemoryEvent> events;

if (!m_state->IsConnected() || !IsTTD())
{
LogError("Current adapter does not support TTD");
return events;
}

if (m_adapter)
{
events = m_adapter->GetTTDMemoryAccessForPositionRange(startAddress, endAddress, accessType, startTime, endTime);
}

return events;
}

std::vector<TTDCallEvent> DebuggerController::GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress, uint64_t endReturnAddress)
{
std::vector<TTDCallEvent> events;
Expand Down Expand Up @@ -3227,7 +3245,7 @@ bool DebuggerController::IsInstructionExecuted(uint64_t address)
}


bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress)
bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime)
{
if (!m_state->IsConnected() || !IsTTD())
{
Expand All @@ -3245,25 +3263,37 @@ bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t
m_executedInstructions.clear();
m_codeCoverageAnalysisRun = false;

LogInfo("Starting TTD code coverage analysis for range 0x%" PRIX64 " - 0x%" PRIX64 "...", startAddress, endAddress);
LogInfo("Starting TTD code coverage analysis.");
LogInfo("\tAddress range: 0x%" PRIX64 " - 0x%" PRIX64, startAddress, endAddress);
//log time range
bool endTimeIsMax = endTime.sequence== std::numeric_limits<uint64_t>::max() && endTime.step == std::numeric_limits<uint64_t>::max();
if(endTimeIsMax)
{
LogInfo("\tTime range: %" PRIX64 ":%" PRIX64 " - end of trace", startTime.sequence, startTime.step);
}
else{
LogInfo("\tTime range: %" PRIX64 ":%" PRIX64 " - %" PRIX64 ":%" PRIX64, startTime.sequence, startTime.step,
endTime.sequence, endTime.step);
}

// Query TTD for execute access covering the specified range
auto events = GetTTDMemoryAccessForAddress(startAddress, endAddress, TTDMemoryExecute);
auto events = GetTTDMemoryAccessForPositionRange(startAddress, endAddress, TTDMemoryExecute, startTime, endTime);

for (const auto& event : events)
{
if (event.accessType == TTDMemoryExecute)
{
// Add all executed instruction addresses within the range
if (event.instructionAddress >= startAddress && event.instructionAddress <= endAddress)
{
// Check if the event is within the specified time range
m_executedInstructions.insert(event.instructionAddress);
}
}
}

m_codeCoverageAnalysisRun = true;
LogInfo("TTD code coverage analysis completed for range. Found 0x%" PRIu64 "executed instructions.",
LogInfo("TTD code coverage analysis completed for ranges. Found %" PRIu64 " executed instructions.",
(uint64_t)m_executedInstructions.size());

return true;
Expand Down Expand Up @@ -3309,7 +3339,7 @@ bool DebuggerController::SaveCodeCoverageToFile(const std::string& filePath) con
}

file.close();
LogError("%s", fmt::format("Saved {} executed instruction addresses to {}", count, filePath.c_str()).c_str());
LogInfo("%s", fmt::format("Saved {} executed instruction addresses to {}", count, filePath.c_str()).c_str());

return true;
}
Expand Down
3 changes: 2 additions & 1 deletion core/debuggercontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ namespace BinaryNinjaDebugger {

// TTD Memory Analysis Methods
std::vector<TTDMemoryEvent> GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead);
std::vector<TTDMemoryEvent> GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime);
std::vector<TTDCallEvent> GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress = 0, uint64_t endReturnAddress = 0);
std::vector<TTDEvent> GetTTDEvents(TTDEventType eventType);
std::vector<TTDEvent> GetAllTTDEvents();
Expand All @@ -397,7 +398,7 @@ namespace BinaryNinjaDebugger {

// TTD Code Coverage Analysis Methods
bool IsInstructionExecuted(uint64_t address);
bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress);
bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime);
size_t GetExecutedInstructionCount() const;
bool SaveCodeCoverageToFile(const std::string& filePath) const;
bool LoadCodeCoverageFromFile(const std::string& filePath);
Expand Down
44 changes: 42 additions & 2 deletions core/ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,44 @@ BNDebuggerTTDMemoryEvent* BNDebuggerGetTTDMemoryAccessForAddress(BNDebuggerContr
return result;
}

BNDebuggerTTDMemoryEvent* BNDebuggerGetTTDMemoryAccessForPositionRange(BNDebuggerController* controller,
uint64_t address, uint64_t endAddress, BNDebuggerTTDMemoryAccessType accessType, BNDebuggerTTDPosition startTime, BNDebuggerTTDPosition endTime, size_t* count)
{
if (!count)
return nullptr;

*count = 0;

TTDMemoryAccessType type = static_cast<TTDMemoryAccessType>(accessType);
TTDPosition startPos(startTime.sequence, startTime.step);
TTDPosition endPos(endTime.sequence, endTime.step);
auto events = controller->object->GetTTDMemoryAccessForPositionRange(address, endAddress, type, startPos, endPos);
if (events.empty())
return nullptr;

*count = events.size();
auto result = new BNDebuggerTTDMemoryEvent[events.size()];

for (size_t i = 0; i < events.size(); i++)
{
result[i].eventType = BNAllocString(events[i].eventType.c_str());
result[i].threadId = events[i].threadId;
result[i].uniqueThreadId = events[i].uniqueThreadId;
result[i].timeStart.sequence = events[i].timeStart.sequence;
result[i].timeStart.step = events[i].timeStart.step;
result[i].timeEnd.sequence = events[i].timeEnd.sequence;
result[i].timeEnd.step = events[i].timeEnd.step;
result[i].address = events[i].address;
result[i].size = events[i].size;
result[i].memoryAddress = events[i].memoryAddress;
result[i].instructionAddress = events[i].instructionAddress;
result[i].value = events[i].value;
result[i].accessType = static_cast<BNDebuggerTTDMemoryAccessType>(events[i].accessType);
}

return result;
}

BNDebuggerTTDPosition BNDebuggerGetCurrentTTDPosition(BNDebuggerController* controller)
{
auto position = controller->object->GetCurrentTTDPosition();
Expand All @@ -1246,9 +1284,11 @@ bool BNDebuggerIsInstructionExecuted(BNDebuggerController* controller, uint64_t
return controller->object->IsInstructionExecuted(address);
}

bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress)
bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress, BNDebuggerTTDPosition startTime, BNDebuggerTTDPosition endTime)
{
return controller->object->RunCodeCoverageAnalysis(startAddress, endAddress);
TTDPosition startPos(startTime.sequence, startTime.step);
TTDPosition endPos(endTime.sequence, endTime.step);
return controller->object->RunCodeCoverageAnalysis(startAddress, endAddress, startPos, endPos);
}

size_t BNDebuggerGetExecutedInstructionCount(BNDebuggerController* controller)
Expand Down
Loading