QStringList MemoryMap::loadedKernelModules() { QStringList ret; const MemoryDump* mem = _symbols->memDumps().at(_vmem->memDumpIndex()); Instance modules = mem->queryInstance("modules", ksNone); const Structured* module_t = dynamic_cast<const Structured*>( factory()->findBaseTypeByName("module")); if (modules.isValid() && module_t) { // First module in the list Instance m = modules.member("next", BaseType::trAny, 1, ksNone); qint64 offset = -module_t->memberOffset("list"); // Go through linked list and add all module names while (m.isValid() && m.address() != modules.address()) { m.setType(module_t); m.addToAddress(offset); QString name = m.member("name").toString(); if (!name.isEmpty()) { // Get rid of quotes if (name.startsWith('"')) name = name.mid(1, name.size() - 2); // Module binaries use dashes, the names use underscores name.replace('_', "-"); ret += name + ".ko"; } m = m.member("list").member("next", BaseType::trAny, 1, ksNone); } } return ret; }
void MemoryMapBuilderCS::processInstance(const Instance &inst, MemoryMapNode *node, VariableTypeContainerList *path) { // Ignore user-land objects if (inst.address() < _map->_vmem->memSpecs().pageOffset) return; // Ignore instances which we cannot access if (!inst.isValid() || !inst.isAccessible()) return; try { if (MemoryMapHeuristics::isFunctionPointer(inst)) processFunctionPointer(inst, node, path); else if (inst.type()->type() & rtPointer) processPointer(inst, node); else if (inst.type()->type() & rtArray) processArray(inst, node, path); else if (inst.type()->type() & StructOrUnion) processStructured(inst, node, path); } catch (GenericException&) { // Do nothing } }
QString MemoryDump::query(const int queryId, const ColorPalette& col, KnowledgeSources src) const { QString ret; Instance instance = queryInstance(queryId, src); QString s = QString("%1%2%3%4: ") .arg(col.color(ctTypeId)) .arg("0x") .arg(queryId, 0, 16) .arg(col.color(ctReset)); if (instance.isValid()) { s += QString("%1%2%3 (ID%4 0x%5%6)") .arg(col.color(ctType)) .arg(instance.typeName()) .arg(col.color(ctReset)) .arg(col.color(ctTypeId)) .arg((uint)instance.type()->id(), 0, 16) .arg(col.color(ctReset)); ret = instance.toString(&col); } else s += "(unresolved type)"; s += QString(" @%1 0x%2%3\n") .arg(col.color(ctAddress)) .arg(instance.address(), _specs.sizeofPointer << 1, 16, QChar('0')) .arg(col.color(ctReset)); ret = s + ret; return ret; }
QString MemoryDump::query(const QString& queryString, const ColorPalette& col, KnowledgeSources src) const { QString ret; if (queryString.isEmpty()) { // Generate a list of all global variables for (int i = 0; i < _factory->vars().size(); i++) { if (i > 0) ret += "\n"; Variable* var = _factory->vars().at(i); ret += var->prettyName(); } } else { Instance instance = queryInstance(queryString, src); QString s = QString("%1%2%3: ") .arg(col.color(ctBold)) .arg(queryString) .arg(col.color(ctReset)); if (instance.isValid()) { s += QString("%1%2%3 (ID%4 0x%5%6)") .arg(col.color(ctType)) .arg(instance.typeName()) .arg(col.color(ctReset)) .arg(col.color(ctTypeId)) .arg((uint)instance.type()->id(), 0, 16) .arg(col.color(ctReset)); if (instance.isNull()) ret = QString(col.color(ctAddress)) + "NULL" + QString(col.color(ctReset)); else { ret = instance.toString(&col); } } else s += "(unresolved type)"; s += QString(" @%1 0x%2%3") .arg(col.color(ctAddress)) .arg(instance.address(), _specs.sizeofPointer << 1, 16, QChar('0')) .arg(col.color(ctReset)); if (instance.bitSize() >= 0) { s += QString("[%1%3%2:%1%4%2]") .arg(col.color(ctOffset)) .arg(col.color(ctReset)) .arg((instance.size() << 3) - instance.bitOffset() - 1) .arg((instance.size() << 3) - instance.bitOffset() - instance.bitSize()); } ret = s + "\n" + ret; } return ret; }
void MemoryMapBuilderCS::processStructured(const Instance& inst, MemoryMapNode *node, VariableTypeContainerList *path) { assert(inst.type()->type() & StructOrUnion); // Ignore non-nested structs/unions that are not aligned at a four-byte // boundary if (!path && (inst.address() & 0x3ULL)) return; addMembers(inst, node, path); }
MemMapList MemoryMap::findAllNodes(const Instance& origInst, const InstanceList &candidates) const { // Find all nodes in the affected memory ranges MemMapList nodes; quint64 addrStart = origInst.address(), addrEnd = addrStart ? origInst.endAddress() : 0; for (int i = 0; i < candidates.size(); ++i) { quint64 c_start = candidates[i].address(); quint64 c_end = candidates[i].endAddress(); // If the interval is invalid, initialize it if (!addrStart && !addrEnd) { if ( (addrStart = c_start) ) addrEnd = c_end; else continue; } // If the memory regions overlap, we enlarge the interval bool overlap = false; if (c_start < addrStart) { if (addrStart <= c_end) { overlap = true; addrStart = c_start; addrEnd = qMax(addrEnd, c_end); } } else if (c_start <= addrEnd) { overlap = true; addrEnd = qMax(c_end, addrEnd); } // If we have a valid interval, append all nodes and reset it to zero if (!overlap && (addrStart || addrEnd)) { _shared->vmemMapLock.lockForRead(); nodes += _vmemMap.objectsInRangeFast(addrStart, addrEnd); _shared->vmemMapLock.unlock(); addrStart = c_start; addrEnd = c_end; } } // Request nodes from the final interval if (addrStart || addrEnd) { _shared->vmemMapLock.lockForRead(); nodes += _vmemMap.objectsInRangeFast(addrStart, addrEnd); _shared->vmemMapLock.unlock(); } return nodes; }
float MemoryMapBuilderCS::calculateNodeProb(const Instance &inst, float parentProbability, MemoryMap *map) { // if (inst.type()->name() == "sysfs_dirent") // debugmsg("Calculating prob. of " << inst.typeName()); // Degradation of 0.1% per parent-child relation. static const float degPerGeneration = 0.00001; // static const float degPerGeneration = 0.0; // Degradation of 20% for objects not matching the magic numbers static const float degForInvalidMagicNumbers = 0.3; // Degradation of 5% for address begin in userland // static const float degForUserlandAddr = 0.05; // Degradation of 90% for an invalid address of this node static const float degForInvalidAddr = 0.9; // // Max. degradation of 30% for non-aligned pointer childen the type of this // // node has // static const float degForNonAlignedChildAddr = 0.3; // Max. degradation of 80% for invalid pointer childen the type of this node // has static const float degForInvalidChildAddr = 0.8; // Invalid Instance degradation - 99% static const float degInvalidInstance = 0.99; // // Invalid Pointer degradation - 90% // static const float degInvalidPointer = 0.1; float prob = parentProbability < 0 ? 1.0 : parentProbability * (1.0 - degPerGeneration); if (map && parentProbability >= 0) map->_shared->degPerGenerationCnt++; // Is the instance valid? if (!MemoryMapHeuristics::isValidInstance(inst)) { // Instance is invalid, so do not check futher return (prob * (1.0 - degInvalidInstance)); } // Find the BaseType of this instance, dereference any lexical type(s) const BaseType* instType = inst.type()->dereferencedBaseType(BaseType::trLexical); // Function Pointer ? if (MemoryMapHeuristics::isFunctionPointer(inst)) { if (!MemoryMapHeuristics::isValidFunctionPointer(inst)) // Invalid function pointer that has no default value return (prob * (1.0 - degForInvalidAddr)); return prob; } // Pointer ? else if (instType && instType->type() & rtPointer) { // Verify. Default values are fine. if (!MemoryMapHeuristics::isValidPointer(inst)) // Invalid pointer that has no default value return (prob * (1.0 - degForInvalidAddr)); return prob; } // If this a union or struct, we have to consider the pointer members // For unions, the largest prob. of all children is taken else if ( instType && (instType->type() & StructOrUnion) ) { if (!inst.isValidConcerningMagicNumbers()) prob *= (1.0 - degForInvalidMagicNumbers); int testedChildren = 0; int invalidChildren = countInvalidChildren( inst.dereference(BaseType::trLexical), &testedChildren); // A count < 0 results from an invalid list_head if (invalidChildren < 0) { return (prob * (1.0 - degInvalidInstance)); } else if (invalidChildren > 0) { float invalidPct = invalidChildren / (float) testedChildren; prob *= invalidPct * (1.0 - degForInvalidChildAddr) + (1.0 - invalidPct); if (map) map->_shared->degForInvalidChildAddrCnt++; } } if (prob < 0 || prob > 1) { debugerr("Probability for of type '" << inst.typeName() << "' at 0x" << QString::number(inst.address(), 16) << " is " << prob << ", should be between 0 and 1."); prob = 0; // Make it safe } return prob; }
QString MemoryDump::dump(const QString& type, quint64 address, int length, const ColorPalette& col) const { if (type == "char") { char c; if (_vmem->readAtomic(address, &c, sizeof(char)) != sizeof(char)) queryError(QString("Cannot read memory from address 0x%1") .arg(address, (_specs.sizeofPointer << 1), 16, QChar('0'))); return QString("%1 (0x%2)").arg(c).arg(c, (sizeof(c) << 1), 16, QChar('0')); } if (type == "int") { qint32 i; if (_vmem->readAtomic(address, (char*)&i, sizeof(qint32)) != sizeof(qint32)) queryError(QString("Cannot read memory from address 0x%1") .arg(address, (_specs.sizeofPointer << 1), 16, QChar('0'))); return QString("%1 (0x%2)").arg(i).arg((quint32)i, (sizeof(i) << 1), 16, QChar('0')); } if (type == "long") { qint64 l; if (_vmem->readAtomic(address, (char*)&l, sizeof(qint64)) != sizeof(qint64)) queryError(QString("Cannot read memory from address 0x%1") .arg(address, (_specs.sizeofPointer << 1), 16, QChar('0'))); return QString("%1 (0x%2)").arg(l).arg((quint64)l, (sizeof(l) << 1), 16, QChar('0')); } if (type == "raw" || type == "hex") { QString ret; const int buflen = 256, linelen = 16; char buf[buflen]; char bufstr[linelen + 1] = {0}; // Make sure we got a valid length if (length < 0) queryError(QString("No valid length given for dumping raw memory")); int totalBytesRead = 0, col = 0; while (length > 0) { int bytesRead = _vmem->readAtomic(address, buf, qMin(buflen, length)); length -= bytesRead; int i = 0; while (i < bytesRead) { // New line every 16 bytes begins with address if (totalBytesRead % 16 == 0) { if (totalBytesRead > 0) { ret += QString(" |%0|\n").arg(bufstr, -linelen); memset(bufstr, 0, linelen + 1); col = 0; } ret += QString("%1 ").arg(address, _specs.sizeofPointer << 1, 16, QChar('0')); } // Wider column after 8 bytes if (totalBytesRead % 8 == 0) ret += ' '; // Write the character as hex string if ((unsigned char)buf[i] < 16) ret += '0'; ret += QString::number((unsigned char)buf[i], 16) + ' '; // Add character to string buffer, if it's an ASCII char if ( ((unsigned char)buf[i] >= 32) && ((unsigned char)buf[i] < 127) ) bufstr[col] = buf[i]; else bufstr[col] = '.'; ++col; ++totalBytesRead; ++address; ++i; } } // Finish it up if (col > 0) { while (col < linelen) { ret += " "; if (col % 8 == 0) ret += ' '; ++col; } ret += QString(" |%0|").arg(bufstr, -linelen); } return ret; } QStringList components = type.split('.', QString::SkipEmptyParts); Instance result; if (!components.isEmpty()) { // Get first instance result = getInstanceAt(components.first(), address, QStringList("user")); components.pop_front(); while (!components.isEmpty()) { result = getNextInstance(components.first(), result, ksNone); components.pop_front(); } return QString("%1%2%3 (ID%4 0x%5%6) @%7 0x%8%9\n") .arg(col.color(ctType)) .arg(result.typeName()) .arg(col.color(ctReset)) .arg(col.color(ctTypeId)) .arg((uint)result.type()->id(), 0, 16) .arg(col.color(ctReset)) .arg(col.color(ctAddress)) .arg(result.address(), 0, 16) .arg(col.color(ctReset)) + result.toString(&col); } queryError3("Unknown type: " + type, QueryException::ecUnresolvedType, type); return QString(); }
Instance MemoryDump::getNextInstance(const QString& component, const Instance& instance, KnowledgeSources src) const { Instance result; QString typeString, symbol, offsetString, candidate, arrayIndexString; bool okay; // quint32 compatibleCnt = 0; // A component should have the form (symbol(-offset)?)?symbol(<candidate>)?([index])? #define SYMBOL "[A-Za-z0-9_]+" #define NUMBER "\\d+" QRegExp re( "^\\s*(?:" "\\(\\s*" "(" SYMBOL ")" "(?:" "\\s*-\\s*(" SYMBOL ")" ")?" "\\s*\\)" ")?" "\\s*(" SYMBOL ")\\s*" "(?:<\\s*(" NUMBER ")\\s*>\\s*)?" "((?:\\[\\s*" NUMBER "\\s*\\]\\s*)*)\\s*"); if (!re.exactMatch(component)) { queryError(QString("Could not parse a part of the query string: %1") .arg(component)); } // Set variables according to the matching typeString = re.cap(1); offsetString = re.cap(2).trimmed(); symbol = re.cap(3); candidate = re.cap(4); arrayIndexString = re.cap(5).trimmed(); int candidateIndex = candidate.isEmpty() ? -1 : candidate.toInt(); // debugmsg(QString("1: %1, 2: %2, 3: %3, 4: %4, 5: %5") // .arg(re.cap(1)) // .arg(re.cap(2)) // .arg(re.cap(3)) // .arg(re.cap(4)) // .arg(re.cap(5))); // A candidate index of 0 means to ignore the alternative types if (candidateIndex == 0) src = static_cast<KnowledgeSources>(src|ksNoAltTypes); // If the given instance is Null, we interpret this as the first component // in the query string and will therefore try to resolve the variable. if (!instance.isValid()) { Variable* v = _factory->findVarByName(symbol); if (!v) queryError(QString("Variable does not exist: %1").arg(symbol)); if (candidateIndex > 0) { if (v->altRefTypeCount() < candidateIndex) queryError(QString("Variable \"%1\" does not have a candidate " "with index %2") .arg(symbol) .arg(candidateIndex)); result = v->altRefTypeInstance(_vmem, candidateIndex - 1); } else { result = v->toInstance(_vmem, BaseType::trLexical, src); } } else { // Dereference any pointers/arrays first result = instance.dereference(BaseType::trAnyNonNull); // Did we get a null instance? if (!(result.type()->type() & StructOrUnion) && (result.isNull() || !result.toPointer())) queryError(QString("Member \"%1\" is null") .arg(result.fullName())); // We have a instance therefore we resolve the member if (!(result.type()->type() & StructOrUnion)) queryError(QString("Member \"%1\" is not a struct or union") .arg(result.fullName())); if (!result.memberExists(symbol)) queryError(QString("Struct \"%1\" has no member named \"%2\"") .arg(result.typeName()) .arg(symbol)); // Do we have a candidate index? if (candidateIndex > 0) { if (result.memberCandidatesCount(symbol) < candidateIndex) queryError(QString("Member \"%1\" does not have a candidate " "with index %2") .arg(symbol) .arg(candidateIndex)); result = result.memberCandidate(symbol, candidateIndex - 1); } else { result = result.member(symbol, BaseType::trLexical, 0, src); } } if (!result.isValid()) return result; // Cast the instance if necessary if (!typeString.isEmpty()) { quint32 offset = 0; // Is a offset given? if (!offsetString.isEmpty()) { // Is the offset given as string or as int? offset = offsetString.toUInt(&okay, 10); if (!okay) { // String. BaseType* type = getType(typeString); if (!type || !(type->type() & StructOrUnion)) queryError(QString("The given type \"%1\" is not a struct " "or union and therefore has no offset") .arg(typeString)); Structured* structd = dynamic_cast<Structured *>(type); if (!structd->memberExists(offsetString)) { queryError(QString("Struct of type \"%1\" has no member " "named \"%2\"") .arg(typeString) .arg(offsetString)); } else { StructuredMember* structdMember = structd->member(offsetString); offset = structdMember->offset(); } } } // Get address size_t address; if (result.type()->type() & (rtPointer)) address = (size_t)result.toPointer() - offset; else address = result.address() - offset; result = getInstanceAt(typeString, address, result.fullNameComponents()); } // Add array index if (!arrayIndexString.isEmpty()) { QRegExp reArrayIndex("\\[\\s*(" NUMBER ")\\s*\\]\\s*"); QStringList matches; int strpos = 0; while (strpos < arrayIndexString.size() && (strpos = arrayIndexString.indexOf(reArrayIndex, strpos)) >= 0) { matches.append(reArrayIndex.cap(1)); strpos += reArrayIndex.cap(0).size(); } for (int i = 0; i < matches.count(); ++i) { quint32 arrayIndex = matches[i].toUInt(&okay, 10); if (okay) { // Is the result already an instance list? if (result.isList()) { InstanceList list(result.toList()); if (arrayIndex < (quint32)list.size()) result = list[arrayIndex]; else queryError(QString("Given array index %1 is out of bounds.") .arg(arrayIndex)); } else { // Is this a pointer or an array type? Instance tmp = result.arrayElem(arrayIndex); if (!tmp.isNull()) result = tmp.dereference(BaseType::trLexical); // Manually update the address else { result.addToAddress(arrayIndex * result.type()->size()); result.setName(QString("%1[%2]").arg(result.name()).arg(arrayIndex)); } } } else { queryError(QString("Given array index %1 could not be converted " "to a number.") .arg(matches[i])); } } } // Try to dereference this instance as deep as possible return result.dereference(BaseType::trLexicalAndPointers); }