Update analysis method handling for name/realname (#2843)

This also fixes a crash when adding a new class method.
This commit is contained in:
Anton Angelov 2021-12-01 05:11:29 -08:00 committed by GitHub
parent 78a9a11209
commit 6438cc4d50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 14 deletions

View File

@ -3152,7 +3152,7 @@ QList<SectionDescription> CutterCore::getAllSections()
if (!digests) { if (!digests) {
continue; continue;
} }
const char *entropy = (const char*)ht_pp_find(digests, "entropy", NULL); const char *entropy = (const char *)ht_pp_find(digests, "entropy", NULL);
section.entropy = rz_str_get(entropy); section.entropy = rz_str_get(entropy);
ht_pp_free(digests); ht_pp_free(digests);
@ -3376,6 +3376,7 @@ QList<AnalysisMethodDescription> CutterCore::getAnalysisClassMethods(const QStri
{ {
AnalysisMethodDescription desc; AnalysisMethodDescription desc;
desc.name = QString::fromUtf8(meth->name); desc.name = QString::fromUtf8(meth->name);
desc.realName = QString::fromUtf8(meth->real_name);
desc.addr = meth->addr; desc.addr = meth->addr;
desc.vtableOffset = meth->vtable_offset; desc.vtableOffset = meth->vtable_offset;
ret.append(desc); ret.append(desc);
@ -3465,6 +3466,7 @@ bool CutterCore::getAnalysisMethod(const QString &cls, const QString &meth,
return false; return false;
} }
desc->name = QString::fromUtf8(analysisMeth.name); desc->name = QString::fromUtf8(analysisMeth.name);
desc->realName = QString::fromUtf8(analysisMeth.real_name);
desc->addr = analysisMeth.addr; desc->addr = analysisMeth.addr;
desc->vtableOffset = analysisMeth.vtable_offset; desc->vtableOffset = analysisMeth.vtable_offset;
rz_analysis_class_method_fini(&analysisMeth); rz_analysis_class_method_fini(&analysisMeth);
@ -3475,7 +3477,8 @@ void CutterCore::setAnalysisMethod(const QString &className, const AnalysisMetho
{ {
CORE_LOCK(); CORE_LOCK();
RzAnalysisMethod analysisMeth; RzAnalysisMethod analysisMeth;
analysisMeth.name = strdup(meth.name.toUtf8().constData()); analysisMeth.name = rz_str_new(meth.name.toUtf8().constData());
analysisMeth.real_name = rz_str_new(meth.realName.toUtf8().constData());
analysisMeth.addr = meth.addr; analysisMeth.addr = meth.addr;
analysisMeth.vtable_offset = meth.vtableOffset; analysisMeth.vtable_offset = meth.vtableOffset;
rz_analysis_class_method_set(core->analysis, className.toUtf8().constData(), &analysisMeth); rz_analysis_class_method_set(core->analysis, className.toUtf8().constData(), &analysisMeth);

View File

@ -248,6 +248,7 @@ struct BinClassDescription
struct AnalysisMethodDescription struct AnalysisMethodDescription
{ {
QString name; QString name;
QString realName;
RVA addr; RVA addr;
st64 vtableOffset; st64 vtableOffset;
}; };

View File

@ -26,6 +26,9 @@ EditMethodDialog::EditMethodDialog(bool classFixed, QWidget *parent)
connect(ui->virtualCheckBox, &QCheckBox::stateChanged, this, connect(ui->virtualCheckBox, &QCheckBox::stateChanged, this,
&EditMethodDialog::updateVirtualUI); &EditMethodDialog::updateVirtualUI);
connect(ui->nameEdit, &QLineEdit::textChanged, this, &EditMethodDialog::validateInput); connect(ui->nameEdit, &QLineEdit::textChanged, this, &EditMethodDialog::validateInput);
connect(ui->realNameEdit, &QLineEdit::textChanged, this, &EditMethodDialog::updateName);
connect(ui->autoRenameCheckBox, &QCheckBox::stateChanged, this,
&EditMethodDialog::updateAutoRenameEnabled);
} }
EditMethodDialog::~EditMethodDialog() {} EditMethodDialog::~EditMethodDialog() {}
@ -54,15 +57,42 @@ void EditMethodDialog::validateInput()
} }
} }
void EditMethodDialog::updateName()
{
if (ui->autoRenameCheckBox->isChecked()) {
ui->nameEdit->setText(convertRealNameToName(ui->realNameEdit->text()));
}
validateInput();
}
void EditMethodDialog::updateAutoRenameEnabled()
{
ui->nameEdit->setEnabled(!ui->autoRenameCheckBox->isChecked());
if (ui->autoRenameCheckBox->isChecked()) {
ui->nameEdit->setText(convertRealNameToName(ui->realNameEdit->text()));
}
}
bool EditMethodDialog::inputValid() bool EditMethodDialog::inputValid()
{ {
if (ui->nameEdit->text().isEmpty()) { if (ui->nameEdit->text().isEmpty() || ui->realNameEdit->text().isEmpty()) {
return false; return false;
} }
// TODO: do more checks here, for example for name clashes // TODO: do more checks here, for example for name clashes
return true; return true;
} }
QString EditMethodDialog::convertRealNameToName(const QString &realName)
{
std::unique_ptr<const char, void (*)(const char *)> sanitizedCString(
rz_str_sanitize_sdb_key(realName.toUtf8().constData()),
[](const char *s) { rz_mem_free((void*)s); });
return QString(sanitizedCString.get());
}
void EditMethodDialog::setClass(const QString &className) void EditMethodDialog::setClass(const QString &className)
{ {
if (classComboBox) { if (classComboBox) {
@ -89,6 +119,7 @@ void EditMethodDialog::setClass(const QString &className)
void EditMethodDialog::setMethod(const AnalysisMethodDescription &desc) void EditMethodDialog::setMethod(const AnalysisMethodDescription &desc)
{ {
ui->nameEdit->setText(desc.name); ui->nameEdit->setText(desc.name);
ui->realNameEdit->setText(desc.realName);
ui->addressEdit->setText(desc.addr != RVA_INVALID ? RzAddressString(desc.addr) : nullptr); ui->addressEdit->setText(desc.addr != RVA_INVALID ? RzAddressString(desc.addr) : nullptr);
if (desc.vtableOffset >= 0) { if (desc.vtableOffset >= 0) {
@ -99,6 +130,16 @@ void EditMethodDialog::setMethod(const AnalysisMethodDescription &desc)
ui->vtableOffsetEdit->setText(nullptr); ui->vtableOffsetEdit->setText(nullptr);
} }
// Check if auto-rename should be enabled
bool enableAutoRename = ui->nameEdit->text().isEmpty()
|| ui->nameEdit->text() == convertRealNameToName(ui->realNameEdit->text());
ui->autoRenameCheckBox->setChecked(enableAutoRename);
// Set focus to real name edit widget if auto-rename is enabled
if (enableAutoRename) {
ui->realNameEdit->setFocus();
}
updateVirtualUI(); updateVirtualUI();
validateInput(); validateInput();
} }
@ -120,6 +161,7 @@ AnalysisMethodDescription EditMethodDialog::getMethod() const
{ {
AnalysisMethodDescription ret; AnalysisMethodDescription ret;
ret.name = ui->nameEdit->text(); ret.name = ui->nameEdit->text();
ret.realName = ui->realNameEdit->text();
ret.addr = Core()->num(ui->addressEdit->text()); ret.addr = Core()->num(ui->addressEdit->text());
if (!ui->virtualCheckBox->isChecked()) { if (!ui->virtualCheckBox->isChecked()) {
ret.vtableOffset = -1; ret.vtableOffset = -1;
@ -145,7 +187,8 @@ bool EditMethodDialog::showDialog(const QString &title, bool classFixed, QString
void EditMethodDialog::newMethod(QString className, const QString &meth, QWidget *parent) void EditMethodDialog::newMethod(QString className, const QString &meth, QWidget *parent)
{ {
AnalysisMethodDescription desc; AnalysisMethodDescription desc;
desc.name = meth; desc.name = convertRealNameToName(meth);
desc.realName = meth;
desc.vtableOffset = -1; desc.vtableOffset = -1;
desc.addr = Core()->getOffset(); desc.addr = Core()->getOffset();

View File

@ -59,6 +59,8 @@ private slots:
void updateVirtualUI(); void updateVirtualUI();
void validateInput(); void validateInput();
void updateName();
void updateAutoRenameEnabled();
private: private:
std::unique_ptr<Ui::EditMethodDialog> ui; std::unique_ptr<Ui::EditMethodDialog> ui;
@ -72,6 +74,7 @@ private:
QString fixedClass; QString fixedClass;
bool inputValid(); bool inputValid();
static QString convertRealNameToName(const QString& realName);
}; };
#endif // EDITMETHODDIALOG_H #endif // EDITMETHODDIALOG_H

View File

@ -29,45 +29,69 @@
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="nameLabel"> <widget class="QLabel" name="nameLabel">
<property name="text"> <property name="text">
<string>Name:</string> <string>Unique Identifier (name):</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="nameEdit"/> <layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="nameEdit"/>
</item>
<item>
<widget class="QCheckBox" name="autoRenameCheckBox">
<property name="text">
<string>Auto-Rename</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="realNameLabel">
<property name="text">
<string>Display Name (realname):</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="realNameEdit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="addressLabel"> <widget class="QLabel" name="addressLabel">
<property name="text"> <property name="text">
<string>Address:</string> <string>Address:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="3" column="1">
<widget class="QLineEdit" name="addressEdit"/> <widget class="QLineEdit" name="addressEdit"/>
</item> </item>
<item row="3" column="0"> <item row="4" column="0">
<widget class="QLabel" name="virtualLabel"> <widget class="QLabel" name="virtualLabel">
<property name="text"> <property name="text">
<string>Virtual:</string> <string>Virtual:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="4" column="1">
<widget class="QCheckBox" name="virtualCheckBox"> <widget class="QCheckBox" name="virtualCheckBox">
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QLabel" name="vtableOffsetLabel"> <widget class="QLabel" name="vtableOffsetLabel">
<property name="text"> <property name="text">
<string>Offset in VTable:</string> <string>Offset in VTable:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="5" column="1">
<widget class="QLineEdit" name="vtableOffsetEdit"/> <widget class="QLineEdit" name="vtableOffsetEdit"/>
</item> </item>
</layout> </layout>
@ -84,6 +108,14 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<tabstops>
<tabstop>nameEdit</tabstop>
<tabstop>autoRenameCheckBox</tabstop>
<tabstop>realNameEdit</tabstop>
<tabstop>addressEdit</tabstop>
<tabstop>virtualCheckBox</tabstop>
<tabstop>vtableOffsetEdit</tabstop>
</tabstops>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>

View File

@ -17,6 +17,8 @@ QVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const
switch (section) { switch (section) {
case NAME: case NAME:
return tr("Name"); return tr("Name");
case REAL_NAME:
return tr("Real Name");
case TYPE: case TYPE:
return tr("Type"); return tr("Type");
case OFFSET: case OFFSET:
@ -188,7 +190,8 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const
case OFFSET: case OFFSET:
return cls->addr == RVA_INVALID ? QString() : RzAddressString(cls->addr); return cls->addr == RVA_INVALID ? QString() : RzAddressString(cls->addr);
case VTABLE: case VTABLE:
return cls->vtableAddr == RVA_INVALID ? QString() : RzAddressString(cls->vtableAddr); return cls->vtableAddr == RVA_INVALID ? QString()
: RzAddressString(cls->vtableAddr);
default: default:
return QVariant(); return QVariant();
} }
@ -312,7 +315,8 @@ void AnalysisClassesModel::classAttrsChanged(const QString &cls)
layoutChanged({ persistentIndex }); layoutChanged({ persistentIndex });
} }
const QVector<AnalysisClassesModel::Attribute> &AnalysisClassesModel::getAttrs(const QString &cls) const const QVector<AnalysisClassesModel::Attribute> &
AnalysisClassesModel::getAttrs(const QString &cls) const
{ {
auto it = attrs->find(cls); auto it = attrs->find(cls);
if (it != attrs->end()) { if (it != attrs->end()) {
@ -454,6 +458,8 @@ QVariant AnalysisClassesModel::data(const QModelIndex &index, int role) const
switch (index.column()) { switch (index.column()) {
case NAME: case NAME:
return meth.name; return meth.name;
case REAL_NAME:
return meth.realName;
case TYPE: case TYPE:
return tr("method"); return tr("method");
case OFFSET: case OFFSET:
@ -476,6 +482,8 @@ QVariant AnalysisClassesModel::data(const QModelIndex &index, int role) const
return QVariant::fromValue(meth.addr); return QVariant::fromValue(meth.addr);
case NameRole: case NameRole:
return meth.name; return meth.name;
case RealNameRole:
return meth.realName;
case TypeRole: case TypeRole:
return QVariant::fromValue(RowType::Method); return QVariant::fromValue(RowType::Method);
default: default:

View File

@ -24,7 +24,7 @@ class ClassesWidget;
class ClassesModel : public QAbstractItemModel class ClassesModel : public QAbstractItemModel
{ {
public: public:
enum Columns { NAME = 0, TYPE, OFFSET, VTABLE, COUNT }; enum Columns { NAME = 0, REAL_NAME, TYPE, OFFSET, VTABLE, COUNT };
/** /**
* @brief values for TypeRole data * @brief values for TypeRole data
@ -61,6 +61,14 @@ public:
*/ */
static const int VTableRole = Qt::UserRole + 3; static const int VTableRole = Qt::UserRole + 3;
/**
* @brief Real Name role of data for QModelIndex
*
* will contain values of QString, used for sorting,
* as well as identifying classes and methods
*/
static const int RealNameRole = Qt::UserRole + 4;
explicit ClassesModel(QObject *parent = nullptr) : QAbstractItemModel(parent) {} explicit ClassesModel(QObject *parent = nullptr) : QAbstractItemModel(parent) {}
QVariant headerData(int section, Qt::Orientation orientation, QVariant headerData(int section, Qt::Orientation orientation,