summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Miller <brandon@vector35.com>2024-03-21 10:52:21 -0400
committerBrandon Miller <bkmiller89@icloud.com>2024-04-25 10:56:18 -0400
commit4b2b3d561b92a01f4d29b86d5510d39e4d1accf3 (patch)
tree2d51e8c9c1fd539f27ffc52bcfe464ab6bd73ef0
parent6ba7605eafb5419b82e2721026e9dc922de95347 (diff)
Base address detection widget in Triage view
Initial implementation of base address detection UI widget in triage summary
-rw-r--r--basedetection.cpp77
-rw-r--r--basedetection.h0
-rw-r--r--binaryninjaapi.h55
-rw-r--r--binaryninjacore.h66
-rw-r--r--examples/triage/baseaddress.cpp355
-rw-r--r--examples/triage/baseaddress.h81
-rw-r--r--examples/triage/view.cpp13
7 files changed, 647 insertions, 0 deletions
diff --git a/basedetection.cpp b/basedetection.cpp
new file mode 100644
index 00000000..4389f1b7
--- /dev/null
+++ b/basedetection.cpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2015-2024 Vector 35 Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+BaseAddressDetection::BaseAddressDetection(Ref<BinaryView> bv)
+{
+ m_object = BNCreateBaseAddressDetection(bv->GetObject());
+}
+
+
+BaseAddressDetection::~BaseAddressDetection()
+{
+ BNFreeBaseAddressDetection(m_object);
+}
+
+
+bool BaseAddressDetection::DetectBaseAddress(BaseAddressDetectionSettings& settings)
+{
+ BNBaseAddressDetectionSettings bnSettings = {
+ settings.Architecture.c_str(),
+ settings.Analysis.c_str(),
+ settings.MinStrlen,
+ settings.Alignment,
+ settings.LowerBoundary,
+ settings.UpperBoundary,
+ settings.POIAnalysis,
+ settings.MaxPointersPerCluster,
+ };
+
+ return BNDetectBaseAddress(m_object, bnSettings);
+}
+
+
+void BaseAddressDetection::Abort()
+{
+ return BNAbortBaseAddressDetection(m_object);
+}
+
+
+bool BaseAddressDetection::IsAborted()
+{
+ return BNIsBaseAddressDetectionAborted(m_object);
+}
+
+
+std::set<std::pair<size_t, uint64_t>> BaseAddressDetection::GetScores(BaseAddressDetectionConfidence* confidence)
+{
+ std::set<std::pair<size_t, uint64_t>> result;
+ BNBaseAddressDetectionScore scores[10];
+ size_t numCandidates = BNGetBaseAddressDetectionScores(m_object, scores, 10,
+ (BNBaseAddressDetectionConfidence *)confidence);
+ for (size_t i = 0; i < numCandidates; i++)
+ result.insert(std::make_pair(scores[i].Score, scores[i].BaseAddress));
+ return result;
+}
diff --git a/basedetection.h b/basedetection.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/basedetection.h
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 6bd63a4b..6bd33c80 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -17378,6 +17378,61 @@ namespace BinaryNinja {
const std::function<void(Symbol*, Type*)>& add);
void Process();
};
+
+ struct BaseAddressDetectionSettings
+ {
+ std::string Architecture;
+ std::string Analysis;
+ uint32_t MinStrlen;
+ uint32_t Alignment;
+ uint64_t LowerBoundary;
+ uint64_t UpperBoundary;
+ BNBaseAddressDetectionPOISetting POIAnalysis;
+ uint32_t MaxPointersPerCluster;
+ };
+
+ enum BaseAddressDetectionConfidence
+ {
+ NoConfidence = 0,
+ LowConfidence = 1,
+ HighConfidence = 2,
+ };
+
+ /*!
+ \ingroup baseaddressdetection
+ */
+ class BaseAddressDetection
+ {
+ BNBaseAddressDetection* m_object;
+
+ public:
+ BaseAddressDetection(Ref<BinaryView> view);
+ ~BaseAddressDetection();
+
+ /*! Analyze program, identify pointers and points-of-interest, and detect candidate base addresses
+
+ \param settings Base address detection settings
+ \return true on success, false otherwise
+ */
+ bool DetectBaseAddress(BaseAddressDetectionSettings& settings);
+
+ /*! Get the top 10 candidate base addresses and thier scores
+
+ \param confidence Confidence level that the top base address candidate is correct
+ \return Set of pairs containing candidate base addresses and their scores
+ */
+ std::set<std::pair<size_t, uint64_t>> GetScores(BaseAddressDetectionConfidence* confidence);
+
+ /*! Abort base address detection
+ */
+ void Abort();
+
+ /*! Determine if base address detection is aborted
+
+ \return true if aborted by user, false otherwise
+ */
+ bool IsAborted();
+ };
} // namespace BinaryNinja
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 68828ce8..25d7618e 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -279,6 +279,7 @@ extern "C"
typedef struct BNExternalLibrary BNExternalLibrary;
typedef struct BNExternalLocation BNExternalLocation;
typedef struct BNProjectFolder BNProjectFolder;
+ typedef struct BNBaseAddressDetection BNBaseAddressDetection;
//! Console log levels
typedef enum BNLogLevel
@@ -3157,6 +3158,63 @@ extern "C"
ConflictSyncStatus
} BNSyncStatus;
+ typedef enum BNBaseAddressDetectionPOISetting
+ {
+ POI_ANALYSIS_STRINGS_ONLY,
+ POI_ANALYSIS_FUNCTIONS_ONLY,
+ POI_ANALYSIS_ALL,
+ } BNBaseAddressDetectionPOISetting;
+
+ typedef enum BNBaseAddressDetectionPOIType
+ {
+ POI_STRING,
+ POI_FUNCTION,
+ POI_DATA_VARIABLE,
+ POI_FILE_START,
+ POI_FILE_END,
+ } BNBaseAddressDetectionPOIType;
+
+ typedef enum BNBaseAddressDetectionConfidence
+ {
+ CONFIDENCE_UNASSIGNED,
+ CONFIDENCE_LOW,
+ CONFIDENCE_HIGH,
+ } BNBaseAddressDetectionConfidence;
+
+ typedef struct BNBaseAddressDetectionSettings
+ {
+ const char* Architecture;
+ const char* Analysis;
+ uint32_t MinStrlen;
+ uint32_t Alignment;
+ uint64_t LowerBoundary;
+ uint64_t UpperBoundary;
+ BNBaseAddressDetectionPOISetting POIAnalysis;
+ uint32_t MaxPointersPerCluster;
+ } BNBaseAddressDetectionSettings;
+
+ typedef struct BNBaseAddressDetectionReason
+ {
+ uint64_t Pointer;
+ uint64_t POIOffset;
+ BNBaseAddressDetectionPOIType BaseAddressDetectionPOIType;
+ } BNBaseAddressDetectionReason;
+
+ typedef struct BNBaseAddressDetectionScore
+ {
+ size_t Score;
+ uint64_t BaseAddress;
+ } BNBaseAddressDetectionScore;
+
+ typedef struct BNBaseAddressDetectionResults
+ {
+ BNBaseAddressDetectionConfidence Confidence;
+ BNBaseAddressDetectionScore** Scores;
+ BNBaseAddressDetectionReason** Reasons;
+ char* ErrorStr;
+ uint64_t LastTestedBaseAddress;
+ } BNBaseAddressDetectionResults;
+
BINARYNINJACOREAPI char* BNAllocString(const char* contents);
BINARYNINJACOREAPI void BNFreeString(char* str);
BINARYNINJACOREAPI char** BNAllocStringList(const char** contents, size_t size);
@@ -6988,6 +7046,14 @@ extern "C"
BINARYNINJACOREAPI bool BNBinaryViewPullTypeArchiveTypes(BNBinaryView* view, const char* archiveId, const char* const* archiveTypeIds, size_t archiveTypeIdCount, char*** updatedArchiveTypeIds, char*** updatedAnalysisTypeIds, size_t* updatedTypeCount);
BINARYNINJACOREAPI bool BNBinaryViewPushTypeArchiveTypes(BNBinaryView* view, const char* archiveId, const char* const* typeIds, size_t typeIdCount, char*** updatedAnalysisTypeIds, char*** updatedArchiveTypeIds, size_t* updatedTypeCount);
+ // Base Address Detection
+ BINARYNINJACOREAPI BNBaseAddressDetection* BNCreateBaseAddressDetection(BNBinaryView *view);
+ BINARYNINJACOREAPI bool BNDetectBaseAddress(BNBaseAddressDetection* bad, BNBaseAddressDetectionSettings& settings);
+ BINARYNINJACOREAPI size_t BNGetBaseAddressDetectionScores(BNBaseAddressDetection* bad,
+ BNBaseAddressDetectionScore* scores, size_t count, BNBaseAddressDetectionConfidence* confidence);
+ BINARYNINJACOREAPI void BNAbortBaseAddressDetection(BNBaseAddressDetection* bad);
+ BINARYNINJACOREAPI bool BNIsBaseAddressDetectionAborted(BNBaseAddressDetection* bad);
+ BINARYNINJACOREAPI void BNFreeBaseAddressDetection(BNBaseAddressDetection* bad);
#ifdef __cplusplus
}
#endif
diff --git a/examples/triage/baseaddress.cpp b/examples/triage/baseaddress.cpp
new file mode 100644
index 00000000..41068cfb
--- /dev/null
+++ b/examples/triage/baseaddress.cpp
@@ -0,0 +1,355 @@
+#include "baseaddress.h"
+
+using namespace std;
+
+
+BNBaseAddressDetectionPOISetting BaseAddressDetectionPOISettingFromString(const std::string& setting)
+{
+ if (setting == "Strings only")
+ return POI_ANALYSIS_STRINGS_ONLY;
+ if (setting == "Functions only")
+ return POI_ANALYSIS_FUNCTIONS_ONLY;
+ return POI_ANALYSIS_ALL; // Default to All
+}
+
+
+std::string BaseAddressDetectionPOITypeToString(BNBaseAddressDetectionPOIType type)
+{
+ switch (type)
+ {
+ case POI_STRING:
+ return "String";
+ case POI_FUNCTION:
+ return "Function";
+ case POI_DATA_VARIABLE:
+ return "Data variable";
+ case POI_FILE_END:
+ return "File end";
+ case POI_FILE_START:
+ return "File start";
+ default:
+ return "Unknown";
+ }
+}
+
+
+std::string BaseAddressDetectionConfidenceToString(BinaryNinja::BaseAddressDetectionConfidence level)
+{
+ switch (level)
+ {
+ case BinaryNinja::NoConfidence:
+ return "Unassigned";
+ case BinaryNinja::HighConfidence:
+ return "High";
+ case BinaryNinja::LowConfidence:
+ return "Low";
+ default:
+ return "Unknown";
+ }
+}
+
+
+uint32_t HexOrDecimalQStringToUint32(const QString& str)
+{
+ if (str.startsWith("0x"))
+ return str.mid(2).toUInt(nullptr, 16);
+ return str.toUInt();
+}
+
+
+uint64_t HexOrDecimalQStringToUint64(const QString& str)
+{
+ if (str.startsWith("0x"))
+ return str.mid(2).toULongLong(nullptr, 16);
+ return str.toULongLong();
+}
+
+
+void BaseAddressDetectionThread::run()
+{
+ BaseAddressDetectionQtResults results;
+ uint32_t alignment = HexOrDecimalQStringToUint32(m_inputs->AlignmentLineEdit->text());
+ if (alignment == 0)
+ {
+ results.Status = "Invalid alignment value";
+ emit ResultReady(results);
+ return;
+ }
+
+ uint32_t minStrlen = HexOrDecimalQStringToUint32(m_inputs->StrlenLineEdit->text());
+ if (minStrlen == 0)
+ {
+ results.Status = "Invalid minimum string length";
+ emit ResultReady(results);
+ return;
+ }
+
+ uint64_t upperBoundary = HexOrDecimalQStringToUint64(m_inputs->UpperBoundary->text());
+ if (upperBoundary == 0)
+ {
+ results.Status = "Invalid upper boundary address";
+ emit ResultReady(results);
+ return;
+ }
+
+ uint64_t lowerBoundary = HexOrDecimalQStringToUint64(m_inputs->LowerBoundary->text());
+ if (lowerBoundary >= upperBoundary)
+ {
+ results.Status = "Upper boundary address is less than lower";
+ emit ResultReady(results);
+ return;
+ }
+
+ uint32_t maxPointersPerCluster = HexOrDecimalQStringToUint32(m_inputs->MaxPointersPerCluster->text());
+ if (maxPointersPerCluster < 2)
+ {
+ results.Status = "Invalid max pointers (must be >= 2)";
+ emit ResultReady(results);
+ return;
+ }
+
+ BNBaseAddressDetectionPOISetting poiSetting = BaseAddressDetectionPOISettingFromString(
+ m_inputs->POIBox->currentText().toStdString());
+ BinaryNinja::BaseAddressDetectionSettings settings = {
+ m_inputs->ArchitectureBox->currentText().toStdString(),
+ m_inputs->AnalysisBox->currentText().toStdString(),
+ minStrlen,
+ alignment,
+ lowerBoundary,
+ upperBoundary,
+ poiSetting,
+ maxPointersPerCluster,
+ };
+
+ if (!m_baseDetection->DetectBaseAddress(settings))
+ emit ResultReady(results);
+
+ auto scores = m_baseDetection->GetScores(&results.Confidence);
+ results.Scores = scores;
+ emit ResultReady(results);
+}
+
+
+void BaseAddressDetectionWidget::HandleResults(const BaseAddressDetectionQtResults& results)
+{
+ if (!results.Status.empty())
+ m_status->setText(QString::fromStdString(results.Status));
+
+ /* TODO
+ if (results.Status.empty() && m_worker->IsAborted())
+ m_status->setText("Aborted by user (Last Base: 0x" + QString::number(results.Results.LastTestedBaseAddress, 16) + ")");
+ */
+
+ if (results.Scores.empty())
+ {
+ if (!m_worker->IsAborted())
+ m_status->setText("Completed with no results");
+ m_preferredBase->setText("Not available");
+ m_confidence->setText("Not available");
+ }
+ else
+ {
+ m_rebaseButton->setEnabled(true);
+ if (results.Status.empty() && !m_worker->IsAborted())
+ m_status->setText("Completed with results");
+ m_preferredBase->setText("0x" + QString::number(results.Scores.rbegin()->second, 16));
+ m_confidence->setText(QString::fromStdString(BaseAddressDetectionConfidenceToString(results.Confidence)) +
+ " (Score: " + QString::number(results.Scores.rbegin()->first) + ")");
+ m_reloadBase->setText("0x" + QString::number(results.Scores.rbegin()->second, 16));
+ }
+
+ m_resultsTableWidget->clearContents();
+ /* TODO
+ size_t numRows = 0;
+ for (auto rit = results.Results.Scores.rbegin(); rit != results.Results.Scores.rend(); rit++)
+ numRows += results.Results.Reasons.at(rit->second).size();
+
+ m_resultsTableWidget->setRowCount(numRows);
+ size_t row = 0;
+ for (auto rit = results.Results.Scores.rbegin(); rit != results.Results.Scores.rend(); rit++)
+ {
+ auto [score, baseaddr] = *rit;
+ for (const auto& reason : results.Results.Reasons.at(baseaddr))
+ {
+ m_resultsTableWidget->setItem(row, 0, new QTableWidgetItem("0x" + QString::number(baseaddr, 16)));
+ m_resultsTableWidget->setItem(row, 1, new QTableWidgetItem("0x" + QString::number(reason.Pointer, 16)));
+ m_resultsTableWidget->setItem(row, 2, new QTableWidgetItem("0x" + QString::number(reason.POIOffset, 16)));
+ m_resultsTableWidget->setItem(row, 3, new QTableWidgetItem(
+ QString::fromStdString(BaseAddressDetectionPOITypeToString(reason.BaseAddressDetectionPOIType))));
+ row++;
+ }
+ }
+ */
+
+ m_detectBaseAddressButton->setEnabled(true);
+ m_abortButton->setHidden(true);
+}
+
+
+void BaseAddressDetectionWidget::DetectBaseAddress()
+{
+ m_status->setText("Running...");
+ m_resultsTableWidget->clearContents();
+ m_preferredBase->setText("Not available");
+ m_confidence->setText("Not available");
+ m_detectBaseAddressButton->setEnabled(false);
+ m_worker = new BaseAddressDetectionThread(&m_inputs, m_view);
+ connect(m_worker, &BaseAddressDetectionThread::ResultReady, this, &BaseAddressDetectionWidget::HandleResults);
+ connect(m_worker, &BaseAddressDetectionThread::finished, m_worker, &QObject::deleteLater);
+ m_worker->start();
+ m_abortButton->setHidden(false);
+}
+
+
+void BaseAddressDetectionWidget::Abort()
+{
+ m_worker->Abort();
+ m_abortButton->setHidden(true);
+}
+
+
+void BaseAddressDetectionWidget::RebaseWithFullAnalysis()
+{
+ auto mappedView = m_view->GetFile()->GetViewOfType("Mapped");
+ if (!mappedView)
+ return;
+
+ auto fileMetadata = m_view->GetFile();
+ if (!fileMetadata)
+ return;
+
+ uint64_t address = HexOrDecimalQStringToUint64(m_reloadBase->text());
+ if (!fileMetadata->Rebase(mappedView, address))
+ return;
+
+ BinaryNinja::Settings::Instance()->Set("analysis.mode", "full", mappedView);
+ mappedView->Reanalyze();
+
+ auto frame = ViewFrame::viewFrameForWidget(this);
+ if (!frame)
+ return;
+
+ auto fileContext = frame->getFileContext();
+ if (!fileContext)
+ return;
+
+ auto uiContext = UIContext::contextForWidget(this);
+ if (!uiContext)
+ return;
+
+ uiContext->recreateViewFrames(fileContext);
+ fileContext->refreshDataViewCache();
+ auto view = frame->getCurrentViewInterface();
+ if (!view)
+ return;
+
+ if (!view->navigate(address))
+ m_view->Navigate(std::string("Linear:" + frame->getCurrentDataType().toStdString()), address);
+}
+
+
+BaseAddressDetectionWidget::BaseAddressDetectionWidget(QWidget* parent, BinaryNinja::Ref<BinaryNinja::BinaryView> bv)
+{
+ m_view = bv->GetParentView() ? bv->GetParentView() : bv;
+ m_layout = new QGridLayout();
+ int32_t row = 0;
+ int32_t column = 0;
+
+ m_layout->addWidget(new QLabel("Architecture:"), row, column, Qt::AlignLeft);
+ m_inputs.ArchitectureBox = new QComboBox(this);
+ auto architectures = BinaryNinja::Architecture::GetList();
+ auto archItemList = QStringList();
+ archItemList << "auto detect";
+ for (const auto& arch : architectures)
+ archItemList << QString::fromStdString(arch->GetName());
+ m_inputs.ArchitectureBox->addItems(archItemList);
+ m_layout->addWidget(m_inputs.ArchitectureBox, row, column + 1, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Analysis Level:"), row, column + 2, Qt::AlignLeft);
+ m_inputs.AnalysisBox = new QComboBox(this);
+ auto analysisItemList = QStringList() << "basic" << "controlFlow" << "full";
+ m_inputs.AnalysisBox->addItems(analysisItemList);
+ m_layout->addWidget(m_inputs.AnalysisBox, row++, column + 3, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Min. String Length:"), row, column, Qt::AlignLeft);
+ m_inputs.StrlenLineEdit = new QLineEdit("10");
+ m_layout->addWidget(m_inputs.StrlenLineEdit, row, column + 1, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Alignment:"), row, column + 2, Qt::AlignLeft);
+ m_inputs.AlignmentLineEdit = new QLineEdit("1024");
+ m_layout->addWidget(m_inputs.AlignmentLineEdit, row++, column + 3, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Lower Boundary:"), row, column, Qt::AlignLeft);
+ m_inputs.LowerBoundary = new QLineEdit("0x0");
+ m_layout->addWidget(m_inputs.LowerBoundary, row, column + 1, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Upper Boundary:"), row, column + 2, Qt::AlignLeft);
+ m_inputs.UpperBoundary = new QLineEdit("0xffffffffffffffff");
+ m_layout->addWidget(m_inputs.UpperBoundary, row++, column + 3, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Points Of Interest:"), row, column, Qt::AlignLeft);
+ auto poiList = QStringList() << "All" << "Strings only" << "Functions only";
+ m_inputs.POIBox = new QComboBox(this);
+ m_inputs.POIBox->addItems(poiList);
+ m_layout->addWidget(m_inputs.POIBox, row, column + 1, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Max Pointers:"), row, column + 2, Qt::AlignLeft);
+ m_inputs.MaxPointersPerCluster = new QLineEdit("128");
+ m_layout->addWidget(m_inputs.MaxPointersPerCluster, row++, column + 3, Qt::AlignLeft);
+
+ m_detectBaseAddressButton = new QPushButton("Start");
+ connect(m_detectBaseAddressButton, &QPushButton::clicked, this, &BaseAddressDetectionWidget::DetectBaseAddress);
+ m_layout->addWidget(m_detectBaseAddressButton, row, column, Qt::AlignLeft);
+
+ m_abortButton = new QPushButton("Abort");
+ connect(m_abortButton, &QPushButton::clicked, this, &BaseAddressDetectionWidget::Abort);
+ m_abortButton->setHidden(true);
+ m_layout->addWidget(m_abortButton, row++, column + 1, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Status:"), row, column, Qt::AlignLeft);
+ m_status = new QLabel("Not running");
+ auto palette = m_status->palette();
+ palette.setColor(QPalette::WindowText, getThemeColor(AlphanumericHighlightColor));
+ m_status->setPalette(palette);
+ m_status->setFont(getMonospaceFont(this));
+ m_layout->addWidget(m_status, row++, column + 1, 1, 2, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Preferred Base:"), row, column, Qt::AlignLeft);
+ m_preferredBase = new QLabel("Not available");
+ m_preferredBase->setTextInteractionFlags(Qt::TextSelectableByMouse);
+ m_preferredBase->setFont(getMonospaceFont(this));
+ m_preferredBase->setPalette(palette);
+ m_layout->addWidget(m_preferredBase, row, column + 1, Qt::AlignLeft);
+
+ m_layout->addWidget(new QLabel("Confidence:"), row, column + 2, Qt::AlignLeft);
+ m_confidence = new QLabel("Not available");
+ m_confidence->setFont(getMonospaceFont(this));
+ m_confidence->setPalette(palette);
+ m_layout->addWidget(m_confidence, row++, column + 3, Qt::AlignLeft);
+
+ m_resultsTableWidget = new QTableWidget(this);
+ m_resultsTableWidget->setColumnCount(4);
+ QStringList header;
+ header << "Base Address" << "Pointer" << "POI Offset" << "POI Type";
+ m_resultsTableWidget->setHorizontalHeaderLabels(header);
+ m_resultsTableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
+ m_resultsTableWidget->horizontalHeader()->setStretchLastSection(true);
+ m_resultsTableWidget->verticalHeader()->setVisible(false);
+ m_resultsTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ m_resultsTableWidget->setSelectionBehavior(QAbstractItemView::SelectItems);
+ m_resultsTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_resultsTableWidget->setMinimumHeight(150);
+ m_layout->addWidget(m_resultsTableWidget, row++, column, 1, 4);
+
+ m_layout->addWidget(new QLabel("Rebase At:"), row, column, Qt::AlignLeft);
+ m_reloadBase = new QLineEdit("0x0");
+ m_layout->addWidget(m_reloadBase, row++, column + 1, Qt::AlignLeft);
+
+ m_rebaseButton = new QPushButton("Start Full Analysis");
+ m_rebaseButton->setEnabled(false);
+ connect(m_rebaseButton, &QPushButton::clicked, this, &BaseAddressDetectionWidget::RebaseWithFullAnalysis);
+ m_layout->addWidget(m_rebaseButton, row, column, Qt::AlignLeft);
+
+ m_layout->setColumnStretch(3, 1);
+ setLayout(m_layout);
+} \ No newline at end of file
diff --git a/examples/triage/baseaddress.h b/examples/triage/baseaddress.h
new file mode 100644
index 00000000..aa5e70ab
--- /dev/null
+++ b/examples/triage/baseaddress.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <QThread>
+#include <QtWidgets/QPushButton>
+#include <QtWidgets/QLineEdit>
+#include <QtWidgets/QComboBox>
+#include <QtWidgets/QTableWidget>
+#include <QHeaderView>
+#include "theme.h"
+#include "fontsettings.h"
+#include "viewframe.h"
+#include "binaryninjaapi.h"
+#include "binaryninjacore.h"
+
+struct BaseAddressDetectionQtInputs
+{
+ QComboBox* ArchitectureBox;
+ QComboBox* AnalysisBox;
+ QLineEdit* StrlenLineEdit;
+ QLineEdit* AlignmentLineEdit;
+ QLineEdit* LowerBoundary;
+ QLineEdit* UpperBoundary;
+ QComboBox* POIBox;
+ QLineEdit* MaxPointersPerCluster;
+};
+
+struct BaseAddressDetectionQtResults
+{
+ std::string Status;
+ std::set<std::pair<size_t, uint64_t>> Scores;
+ BinaryNinja::BaseAddressDetectionConfidence Confidence;
+};
+
+class BaseAddressDetectionThread : public QThread
+{
+ Q_OBJECT
+ BinaryNinja::Ref<BinaryNinja::BinaryView> m_view;
+ BinaryNinja::BaseAddressDetection* m_baseDetection;
+ BaseAddressDetectionQtInputs* m_inputs {};
+ void run() override;
+
+public:
+ BaseAddressDetectionThread(BaseAddressDetectionQtInputs* widgetInputs, BinaryNinja::Ref<BinaryNinja::BinaryView> bv)
+ {
+ m_inputs = widgetInputs;
+ m_view = bv;
+ m_baseDetection = new BinaryNinja::BaseAddressDetection(m_view);
+ }
+
+ void Abort() { m_baseDetection->Abort(); }
+ bool IsAborted() { return m_baseDetection->IsAborted(); }
+
+signals:
+ void ResultReady(const BaseAddressDetectionQtResults& result);
+};
+
+class BaseAddressDetectionWidget : public QWidget
+{
+ BaseAddressDetectionThread* m_worker;
+ BinaryNinja::Ref<BinaryNinja::BinaryView> m_view;
+ QGridLayout* m_layout {};
+
+ QPushButton* m_detectBaseAddressButton = nullptr;
+ QPushButton* m_abortButton = nullptr;
+
+ BaseAddressDetectionQtInputs m_inputs;
+ QLabel* m_preferredBase;
+ QLabel* m_confidence;
+ QLabel* m_status;
+ QLineEdit* m_reloadBase;
+ QPushButton* m_rebaseButton;
+ QTableWidget* m_resultsTableWidget;
+
+ void DetectBaseAddress();
+ void RebaseWithFullAnalysis();
+ void Abort();
+ void HandleResults(const BaseAddressDetectionQtResults& results);
+
+public:
+ BaseAddressDetectionWidget(QWidget* parent, BinaryNinja::Ref<BinaryNinja::BinaryView> bv);
+}; \ No newline at end of file
diff --git a/examples/triage/view.cpp b/examples/triage/view.cpp
index 87cdb61a..579f8917 100644
--- a/examples/triage/view.cpp
+++ b/examples/triage/view.cpp
@@ -10,6 +10,7 @@
#include "librariesinfo.h"
#include "headers.h"
#include "strings.h"
+#include "baseaddress.h"
#include "fontsettings.h"
#include <binaryninjacore.h>
@@ -52,6 +53,18 @@ TriageView::TriageView(QWidget* parent, BinaryViewRef data) : QScrollArea(parent
delete hdr;
}
+ auto fileMetadata = m_data->GetFile();
+ auto existingViews = fileMetadata->GetExistingViews();
+ if ((existingViews.size() == 2 && fileMetadata->GetViewOfType("Mapped")) || existingViews.size() == 1)
+ {
+ // Binary either only has raw view (Open for triage mode) or raw and mapped view
+ QGroupBox* baseDetectionGroup = new QGroupBox("Base Address Detection", container);
+ QVBoxLayout* baseDetectionLayout = new QVBoxLayout();
+ baseDetectionLayout->addWidget(new BaseAddressDetectionWidget(this, data));
+ baseDetectionGroup->setLayout(baseDetectionLayout);
+ layout->addWidget(baseDetectionGroup);
+ }
+
QGroupBox* librariesGroup = new QGroupBox("Libraries", container);
QVBoxLayout* librariesLayout = new QVBoxLayout();
librariesLayout->addWidget(new LibrariesWidget(this, data));