Skip to content

Commit cdb436a

Browse files
razainaraz4ina
andauthored
Implement TTD memory access queries for esReven adapter (#1006)
Use the custom rvn:get-memory-accesses packet to retrieve the history of memory accesses (read/write/execute) for a given address range. Implement both variants: GetTTDMemoryAccessForAddress for address-only queries and GetTTDMemoryAccessForPositionRange for queries additionally filtered by a TTD position (transition) range. Co-authored-by: Tiana <tiana.razafindralambo@eshard.com>
1 parent fdfba0e commit cdb436a

2 files changed

Lines changed: 308 additions & 0 deletions

File tree

core/adapters/esrevenadapter.cpp

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,310 @@ std::vector<DebugModule> EsrevenAdapter::GetModuleList()
13071307
}
13081308

13091309

1310+
std::vector<TTDMemoryEvent> EsrevenAdapter::GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType)
1311+
{
1312+
if (m_isTargetRunning)
1313+
return {};
1314+
1315+
if (!m_rspConnector)
1316+
return {};
1317+
1318+
// Convert TTDMemoryAccessType flags to comma-separated string
1319+
std::vector<std::string> types;
1320+
if (accessType & TTDMemoryRead)
1321+
types.push_back("read");
1322+
if (accessType & TTDMemoryWrite)
1323+
types.push_back("write");
1324+
if (accessType & TTDMemoryExecute)
1325+
types.push_back("execute");
1326+
1327+
if (types.empty())
1328+
return {};
1329+
1330+
std::string typesStr;
1331+
for (size_t i = 0; i < types.size(); i++)
1332+
{
1333+
if (i > 0)
1334+
typesStr += ",";
1335+
typesStr += types[i];
1336+
}
1337+
1338+
// Send the custom REVEN packet: rvn:get-memory-accesses:<start>:<end>:<types>
1339+
auto response = m_rspConnector->TransmitAndReceive(
1340+
RspData("rvn:get-memory-accesses:{:x}:{:x}:{}", startAddress, endAddress, typesStr));
1341+
std::string jsonStr = response.AsString();
1342+
1343+
// Check if we got a valid JSON array response
1344+
if (jsonStr.empty() || jsonStr[0] != '[')
1345+
return {};
1346+
1347+
std::vector<TTDMemoryEvent> result;
1348+
1349+
// Simple JSON parser for array of memory access objects
1350+
size_t pos = 0;
1351+
while (pos < jsonStr.length())
1352+
{
1353+
// Find the start of an object
1354+
size_t objStart = jsonStr.find('{', pos);
1355+
if (objStart == std::string::npos)
1356+
break;
1357+
1358+
// Find the end of the object
1359+
size_t objEnd = jsonStr.find('}', objStart);
1360+
if (objEnd == std::string::npos)
1361+
break;
1362+
1363+
std::string objStr = jsonStr.substr(objStart, objEnd - objStart + 1);
1364+
1365+
TTDMemoryEvent event;
1366+
event.eventType = "MemoryAccess";
1367+
1368+
// Helper lambda to extract uint64_t value from JSON
1369+
auto extractUInt64 = [](const std::string& json, const std::string& key) -> uint64_t {
1370+
size_t keyPos = json.find("\"" + key + "\"");
1371+
if (keyPos == std::string::npos)
1372+
return 0;
1373+
1374+
size_t colonPos = json.find(':', keyPos);
1375+
if (colonPos == std::string::npos)
1376+
return 0;
1377+
1378+
// Skip whitespace after colon
1379+
size_t valueStart = colonPos + 1;
1380+
while (valueStart < json.length() && std::isspace(json[valueStart]))
1381+
valueStart++;
1382+
1383+
// Check if value is null
1384+
if (json.substr(valueStart, 4) == "null")
1385+
return 0;
1386+
1387+
// Find end of number (comma, closing brace, or whitespace)
1388+
size_t valueEnd = valueStart;
1389+
while (valueEnd < json.length() &&
1390+
std::isdigit(json[valueEnd]))
1391+
valueEnd++;
1392+
1393+
if (valueEnd > valueStart)
1394+
{
1395+
std::string valueStr = json.substr(valueStart, valueEnd - valueStart);
1396+
return std::stoull(valueStr);
1397+
}
1398+
return 0;
1399+
};
1400+
1401+
// Helper lambda to extract string value from JSON
1402+
auto extractString = [](const std::string& json, const std::string& key) -> std::string {
1403+
size_t keyPos = json.find("\"" + key + "\"");
1404+
if (keyPos == std::string::npos)
1405+
return "";
1406+
1407+
size_t colonPos = json.find(':', keyPos);
1408+
if (colonPos == std::string::npos)
1409+
return "";
1410+
1411+
size_t valueStart = json.find('"', colonPos);
1412+
if (valueStart == std::string::npos)
1413+
return "";
1414+
1415+
size_t valueEnd = json.find('"', valueStart + 1);
1416+
if (valueEnd == std::string::npos)
1417+
return "";
1418+
1419+
return json.substr(valueStart + 1, valueEnd - valueStart - 1);
1420+
};
1421+
1422+
// Extract fields from JSON
1423+
uint64_t transitionId = extractUInt64(objStr, "transition_id");
1424+
event.address = extractUInt64(objStr, "address");
1425+
event.memoryAddress = event.address; // Same as address
1426+
event.size = extractUInt64(objStr, "size");
1427+
event.instructionAddress = extractUInt64(objStr, "instruction_address");
1428+
event.value = extractUInt64(objStr, "value");
1429+
1430+
// Extract thread_id (may be null)
1431+
event.threadId = static_cast<uint32_t>(extractUInt64(objStr, "thread_id"));
1432+
event.uniqueThreadId = event.threadId;
1433+
1434+
// Convert transition_id to TTDPosition (use as sequence, step=0)
1435+
event.timeStart = TTDPosition(transitionId, 0);
1436+
event.timeEnd = event.timeStart;
1437+
1438+
// Extract and convert access_type string to enum
1439+
std::string accessTypeStr = extractString(objStr, "access_type");
1440+
if (accessTypeStr == "read")
1441+
event.accessType = TTDMemoryRead;
1442+
else if (accessTypeStr == "write")
1443+
event.accessType = TTDMemoryWrite;
1444+
else if (accessTypeStr == "execute")
1445+
event.accessType = TTDMemoryExecute;
1446+
else
1447+
event.accessType = TTDMemoryRead; // Default
1448+
1449+
result.push_back(event);
1450+
1451+
pos = objEnd + 1;
1452+
}
1453+
1454+
return result;
1455+
}
1456+
1457+
1458+
std::vector<TTDPositionRangeIndexedMemoryEvent> EsrevenAdapter::GetTTDMemoryAccessForPositionRange(
1459+
uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType,
1460+
const TTDPosition startTime, const TTDPosition endTime)
1461+
{
1462+
if (m_isTargetRunning)
1463+
return {};
1464+
1465+
if (!m_rspConnector)
1466+
return {};
1467+
1468+
// Convert TTDMemoryAccessType flags to comma-separated string
1469+
std::vector<std::string> types;
1470+
if (accessType & TTDMemoryRead)
1471+
types.push_back("read");
1472+
if (accessType & TTDMemoryWrite)
1473+
types.push_back("write");
1474+
if (accessType & TTDMemoryExecute)
1475+
types.push_back("execute");
1476+
1477+
if (types.empty())
1478+
return {};
1479+
1480+
std::string typesStr;
1481+
for (size_t i = 0; i < types.size(); i++)
1482+
{
1483+
if (i > 0)
1484+
typesStr += ",";
1485+
typesStr += types[i];
1486+
}
1487+
1488+
// TTDPosition.sequence maps to REVEN transition_id (step is always 0 in REVEN)
1489+
uint64_t startTransition = startTime.sequence;
1490+
uint64_t endTransition = endTime.sequence;
1491+
1492+
// Send the custom REVEN packet with time range: rvn:get-memory-accesses:<start>:<end>:<types>:<start_trans>:<end_trans>
1493+
auto response = m_rspConnector->TransmitAndReceive(
1494+
RspData("rvn:get-memory-accesses:{:x}:{:x}:{}:{}:{}",
1495+
startAddress, endAddress, typesStr, startTransition, endTransition));
1496+
std::string jsonStr = response.AsString();
1497+
1498+
// Check if we got a valid JSON array response
1499+
if (jsonStr.empty() || jsonStr[0] != '[')
1500+
return {};
1501+
1502+
std::vector<TTDPositionRangeIndexedMemoryEvent> result;
1503+
1504+
// Simple JSON parser for array of memory access objects
1505+
size_t pos = 0;
1506+
while (pos < jsonStr.length())
1507+
{
1508+
// Find the start of an object
1509+
size_t objStart = jsonStr.find('{', pos);
1510+
if (objStart == std::string::npos)
1511+
break;
1512+
1513+
// Find the end of the object
1514+
size_t objEnd = jsonStr.find('}', objStart);
1515+
if (objEnd == std::string::npos)
1516+
break;
1517+
1518+
std::string objStr = jsonStr.substr(objStart, objEnd - objStart + 1);
1519+
1520+
TTDPositionRangeIndexedMemoryEvent event;
1521+
1522+
// Helper lambda to extract uint64_t value from JSON
1523+
auto extractUInt64 = [](const std::string& json, const std::string& key) -> uint64_t {
1524+
size_t keyPos = json.find("\"" + key + "\"");
1525+
if (keyPos == std::string::npos)
1526+
return 0;
1527+
1528+
size_t colonPos = json.find(':', keyPos);
1529+
if (colonPos == std::string::npos)
1530+
return 0;
1531+
1532+
// Skip whitespace after colon
1533+
size_t valueStart = colonPos + 1;
1534+
while (valueStart < json.length() && std::isspace(json[valueStart]))
1535+
valueStart++;
1536+
1537+
// Check if value is null
1538+
if (json.substr(valueStart, 4) == "null")
1539+
return 0;
1540+
1541+
// Find end of number
1542+
size_t valueEnd = valueStart;
1543+
while (valueEnd < json.length() && std::isdigit(json[valueEnd]))
1544+
valueEnd++;
1545+
1546+
if (valueEnd > valueStart)
1547+
{
1548+
std::string valueStr = json.substr(valueStart, valueEnd - valueStart);
1549+
return std::stoull(valueStr);
1550+
}
1551+
return 0;
1552+
};
1553+
1554+
// Helper lambda to extract string value from JSON
1555+
auto extractString = [](const std::string& json, const std::string& key) -> std::string {
1556+
size_t keyPos = json.find("\"" + key + "\"");
1557+
if (keyPos == std::string::npos)
1558+
return "";
1559+
1560+
size_t colonPos = json.find(':', keyPos);
1561+
if (colonPos == std::string::npos)
1562+
return "";
1563+
1564+
size_t valueStart = json.find('"', colonPos);
1565+
if (valueStart == std::string::npos)
1566+
return "";
1567+
1568+
size_t valueEnd = json.find('"', valueStart + 1);
1569+
if (valueEnd == std::string::npos)
1570+
return "";
1571+
1572+
return json.substr(valueStart + 1, valueEnd - valueStart - 1);
1573+
};
1574+
1575+
// Extract fields from JSON
1576+
uint64_t transitionId = extractUInt64(objStr, "transition_id");
1577+
event.address = extractUInt64(objStr, "address");
1578+
event.size = extractUInt64(objStr, "size");
1579+
event.instructionAddress = extractUInt64(objStr, "instruction_address");
1580+
event.value = extractUInt64(objStr, "value");
1581+
1582+
// Extract thread_id (may be null)
1583+
event.threadId = static_cast<uint32_t>(extractUInt64(objStr, "thread_id"));
1584+
event.uniqueThreadId = event.threadId;
1585+
1586+
// Convert transition_id to TTDPosition (use as sequence, step=0)
1587+
event.position = TTDPosition(transitionId, 0);
1588+
1589+
// Extract and convert access_type string to enum
1590+
std::string accessTypeStr = extractString(objStr, "access_type");
1591+
if (accessTypeStr == "read")
1592+
event.accessType = TTDMemoryRead;
1593+
else if (accessTypeStr == "write")
1594+
event.accessType = TTDMemoryWrite;
1595+
else if (accessTypeStr == "execute")
1596+
event.accessType = TTDMemoryExecute;
1597+
else
1598+
event.accessType = TTDMemoryRead; // Default
1599+
1600+
// Initialize data array (first 8 bytes at memory address)
1601+
// REVEN doesn't provide this currently, so zero it out
1602+
for (int i = 0; i < 8; i++)
1603+
event.data[i] = 0;
1604+
1605+
result.push_back(event);
1606+
1607+
pos = objEnd + 1;
1608+
}
1609+
1610+
return result;
1611+
}
1612+
1613+
13101614
std::string EsrevenAdapter::GetTargetArchitecture()
13111615
{
13121616
return m_remoteArch;

core/adapters/esrevenadapter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ namespace BinaryNinjaDebugger
140140
std::string GetRemoteFile(const std::string& path);
141141
std::vector<DebugModule> GetModuleList() override;
142142

143+
// TTD Memory Access support
144+
std::vector<TTDMemoryEvent> GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead) override;
145+
std::vector<TTDPositionRangeIndexedMemoryEvent> GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime) override;
146+
143147
std::string GetTargetArchitecture() override;
144148

145149
DebugStopReason StopReason() override;

0 commit comments

Comments
 (0)