From 830e9cd947ffdf2f975c94006f46cf6daa1bf426 Mon Sep 17 00:00:00 2001 From: yossizap Date: Fri, 10 Jan 2020 20:46:49 +0000 Subject: [PATCH] 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 --- src/core/Cutter.cpp | 139 +++++++++++++++++++++++++++++++++++- src/core/Cutter.h | 14 +++- src/widgets/StackWidget.cpp | 69 ++++++++++++------ 3 files changed, 199 insertions(+), 23 deletions(-) diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 8c23443a..1523b0ce 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1139,9 +1139,144 @@ QJsonDocument CutterCore::getSignatureInfo() return cmdj("iCj"); } -QJsonDocument CutterCore::getStack(int size) +QList CutterCore::getStack(int size, int depth) { - return cmdj("pxrj " + QString::number(size) + " @ r:SP"); + QList 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) diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 0660f374..cb30a056 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -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 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 diff --git a/src/widgets/StackWidget.cpp b/src/widgets/StackWidget.cpp index d4a09f17..93e5a44b 100644 --- a/src/widgets/StackWidget.cpp +++ b/src/widgets/StackWidget.cpp @@ -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 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);