Add a custom telescoping function and improve StackWidget's output (#1990)

* Add a custom telescoping function and improve StackWidget's output

* Use colors from the color configuration for stackswidget

* Improve telescoping output
This commit is contained in:
yossizap 2020-01-10 20:46:49 +00:00 committed by Itay Cohen
parent f1f64b0f55
commit 830e9cd947
3 changed files with 199 additions and 23 deletions

View File

@ -1139,9 +1139,144 @@ QJsonDocument CutterCore::getSignatureInfo()
return cmdj("iCj");
}
QJsonDocument CutterCore::getStack(int size)
QList<QJsonObject> CutterCore::getStack(int size, int depth)
{
return cmdj("pxrj " + QString::number(size) + " @ r:SP");
QList<QJsonObject> stack;
if (!currentlyDebugging) {
return stack;
}
CORE_LOCK();
bool ret;
RVA addr = cmd("dr SP").toULongLong(&ret, 16);
if (!ret) {
return stack;
}
int base = core->anal->bits;
for (int i = 0; i < size; i += base / 8) {
if ((base == 32 && addr + i >= UT32_MAX) || (base == 16 && addr + i >= UT16_MAX)) {
break;
}
stack.append(getAddrRefs(addr + i, depth));
}
return stack;
}
QJsonObject CutterCore::getAddrRefs(RVA addr, int depth) {
QJsonObject json;
if (depth < 1 || addr == UT64_MAX) {
return json;
}
CORE_LOCK();
int bits = core->assembler->bits;
QByteArray buf = QByteArray();
ut64 type = r_core_anal_address(core, addr);
json["addr"] = QString::number(addr);
// Search for the section the addr is in, avoid duplication for heap/stack with type
if(!(type & R_ANAL_ADDR_TYPE_HEAP || type & R_ANAL_ADDR_TYPE_STACK)) {
// Attempt to find the address within a map
RDebugMap *map = r_debug_map_get(core->dbg, addr);
if (map && map->name && map->name[0]) {
json["mapname"] = map->name;
}
RBinSection *sect = r_bin_get_section_at(r_bin_cur_object (core->bin), addr, true);
if (sect && sect->name[0]) {
json["section"] = sect->name;
}
}
// Check if the address points to a register
RFlagItem *fi = r_flag_get_i(core->flags, addr);
if (fi) {
RRegItem *r = r_reg_get(core->dbg->reg, fi->name, -1);
if (r) {
json["reg"] = r->name;
}
}
// Attempt to find the address within a function
RAnalFunction *fcn = r_anal_get_fcn_in(core->anal, addr, 0);
if (fcn) {
json["fcn"] = fcn->name;
}
// Update type and permission information
if (type != 0) {
if (type & R_ANAL_ADDR_TYPE_HEAP) {
json["type"] = "heap";
} else if (type & R_ANAL_ADDR_TYPE_STACK) {
json["type"] = "stack";
} else if (type & R_ANAL_ADDR_TYPE_PROGRAM) {
json["type"] = "program";
} else if (type & R_ANAL_ADDR_TYPE_LIBRARY) {
json["type"] = "library";
} else if (type & R_ANAL_ADDR_TYPE_ASCII) {
json["type"] = "ascii";
} else if (type & R_ANAL_ADDR_TYPE_SEQUENCE) {
json["type"] = "sequence";
}
QString perms = "";
if (type & R_ANAL_ADDR_TYPE_READ) {
perms += "r";
}
if (type & R_ANAL_ADDR_TYPE_WRITE) {
perms += "w";
}
if (type & R_ANAL_ADDR_TYPE_EXEC) {
RAsmOp op;
buf.resize(32);
perms += "x";
// Instruction disassembly
r_io_read_at(core->io, addr, (unsigned char*)buf.data(), buf.size());
r_asm_set_pc(core->assembler, addr);
r_asm_disassemble(core->assembler, &op, (unsigned char*)buf.data(), buf.size());
json["asm"] = r_asm_op_get_asm(&op);
}
if (!perms.isEmpty()) {
json["perms"] = perms;
}
}
// Try to telescope further if depth permits it
if ((type & R_ANAL_ADDR_TYPE_READ) && !(type & R_ANAL_ADDR_TYPE_EXEC)) {
buf.resize(64);
ut32 *n32 = (ut32 *)buf.data();
ut64 *n64 = (ut64 *)buf.data();
r_io_read_at(core->io, addr, (unsigned char*)buf.data(), buf.size());
ut64 n = (bits == 64)? *n64: *n32;
// The value of the next address will serve as an indication that there's more to
// telescope if we have reached the depth limit
json["value"] = QString::number(n);
if (depth && n != addr) {
// Make sure we aren't telescoping the same address
QJsonObject ref = getAddrRefs(n, depth - 1);
if (!ref.empty() && !ref["type"].isNull()) {
// If the dereference of the current pointer is an ascii character we
// might have a string in this address
if (ref["type"].toString().contains("ascii")) {
buf.resize(128);
r_io_read_at(core->io, addr, (unsigned char*)buf.data(), buf.size());
QString strVal = QString(buf);
// Indicate that the string is longer than the printed value
if (strVal.size() == buf.size()) {
strVal += "...";
}
json["string"] = strVal;
}
json["ref"] = ref;
}
}
}
return json;
}
QJsonDocument CutterCore::getProcessThreads(int pid)

View File

@ -286,7 +286,19 @@ public:
* @brief Attach to a given pid from a debug session
*/
void setCurrentDebugProcess(int pid);
QJsonDocument getStack(int size = 0x100);
/**
* @brief Returns a list of stack address and their telescoped references
* @param size number of bytes to scan
* @param depth telescoping depth
*/
QList<QJsonObject> getStack(int size = 0x100, int depth = 6);
/**
* @brief Recursively dereferences pointers starting at the specified address
* up to a given depth
* @param addr telescoping addr
* @param depth telescoping depth
*/
QJsonObject getAddrRefs(RVA addr, int depth);
/**
* @brief Get a list of a given process's threads
* @param pid The pid of the process, -1 for the currently debugged process

View File

@ -144,37 +144,66 @@ StackModel::StackModel(QObject *parent)
{
}
// Utility function to check if a telescoped item exists and add it with prefixes to the desc
static inline const QString append_var(QString &dst, const QString val, const QString prepend_val,
const QString append_val)
{
if (!val.isEmpty()) {
dst += prepend_val + val + append_val;
}
return val;
}
void StackModel::reload()
{
QJsonArray stackValues = Core()->getStack().array();
QList<QJsonObject> stackItems = Core()->getStack();
beginResetModel();
values.clear();
for (const QJsonValue &value : stackValues) {
QJsonObject stackItem = value.toObject();
for (const QJsonObject &stackItem : stackItems) {
Item item;
item.offset = stackItem["addr"].toVariant().toULongLong();
item.value = RAddressString(stackItem["value"].toVariant().toULongLong());
QJsonObject refItem = stackItem["ref"].toObject();
if (!refItem.empty()) {
QString str = refItem["string"].toVariant().toString();
if (!str.isEmpty()) {
item.description = str;
item.descriptionColor = ConfigColor("comment");
} else {
QString type, string;
do {
item.description += " ->";
append_var(item.description, refItem["reg"].toVariant().toString(), " @", "");
append_var(item.description, refItem["mapname"].toVariant().toString(), " (", ")");
append_var(item.description, refItem["section"].toVariant().toString(), " (", ")");
append_var(item.description, refItem["func"].toVariant().toString(), " ", "");
type = append_var(item.description, refItem["type"].toVariant().toString(), " ", "");
append_var(item.description, refItem["perms"].toVariant().toString(), " ", "");
append_var(item.description, refItem["asm"].toVariant().toString(), " \"", "\"");
string = append_var(item.description, refItem["string"].toVariant().toString(), " ", "");
if (!string.isNull()) {
// There is no point in adding ascii and addr info after a string
break;
}
if (!refItem["value"].isNull()) {
append_var(item.description, RAddressString(refItem["value"].toVariant().toULongLong()), " ", "");
}
refItem = refItem["ref"].toObject();
} while (!refItem.empty());
QJsonValue refObject = stackItem["ref"];
if (!refObject.isUndefined()) { // check that the key exists
QString ref = refObject.toString();
if (ref.contains("ascii") && ref.count("-->") == 1) {
ref = Core()->cmdj(QString("pszj @ [%1]").arg(item.offset)).object().value("string").toString();
}
item.description = ref;
if (refObject.toString().contains("ascii") && refObject.toString().count("-->") == 1) {
item.descriptionColor = QVariant(QColor(243, 156, 17));
} else if (ref.contains("program R X") && ref.count("-->") == 0) {
item.descriptionColor = QVariant(QColor(Qt::red));
} else if (ref.contains("stack") && ref.count("-->") == 0) {
item.descriptionColor = QVariant(QColor(Qt::cyan));
} else if (ref.contains("library") && ref.count("-->") == 0) {
item.descriptionColor = QVariant(QColor(Qt::green));
// Set the description's color according to the last item type
if (type == "ascii" || !string.isEmpty()) {
item.descriptionColor = ConfigColor("comment");
} else if (type == "program") {
item.descriptionColor = ConfigColor("fname");
} else if (type == "library") {
item.descriptionColor = ConfigColor("floc");
} else if (type == "stack") {
item.descriptionColor = ConfigColor("offset");
}
}
}
values.push_back(item);