AdaptixC2-Mod0/Source/UI/Widgets/BrowserProcessWidget.cpp
2026-04-06 00:20:51 -05:00

520 lines
20 KiB
C++

#include <Agent/Agent.h>
#include <Utils/CustomElements.h>
#include <UI/Widgets/BrowserProcessWidget.h>
#include <UI/Widgets/ConsoleWidget.h>
#include <UI/Widgets/AdaptixWidget.h>
#include <UI/Widgets/DockWidgetRegister.h>
#include <Client/AuthProfile.h>
#include <Client/AxScript/AxScriptManager.h>
REGISTER_DOCK_WIDGET(BrowserProcessWidget, "Browser Process", false)
BrowserProcessWidget::BrowserProcessWidget(const AdaptixWidget* w, Agent* a) : DockTab(QString("Processes [%1]").arg(a->data.Id), w->GetProfile()->GetProject())
{
agent = a;
this->createUI();
connect(buttonReload, &QPushButton::clicked, this, &BrowserProcessWidget::onReload);
connect(inputFilter, &QLineEdit::textChanged, this, &BrowserProcessWidget::onFilter);
connect(tableView, &QTableView::customContextMenuRequested, this, &BrowserProcessWidget::handleTableMenu );
connect(tableView, &QTableView::clicked, this, &BrowserProcessWidget::onTableSelect );
connect(treeBrowserWidget, &QTreeWidget::clicked, this, &BrowserProcessWidget::onTreeSelect );
connect(tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this](const QItemSelection &selected, const QItemSelection &deselected){
Q_UNUSED(selected)
Q_UNUSED(deselected)
if (!inputFilter->hasFocus())
tableView->setFocus();
});
this->dockWidget->setWidget(this);
}
BrowserProcessWidget::~BrowserProcessWidget() = default;
void BrowserProcessWidget::createUI()
{
buttonReload = new QPushButton(QIcon(":/icons/reload"), "", this);
buttonReload->setIconSize( QSize( 24,24 ));
buttonReload->setFixedSize(37, 28);
buttonReload->setToolTip("Reload");
inputFilter = new QLineEdit(this);
line_1 = new QFrame(this);
line_1->setFrameShape(QFrame::VLine);
line_1->setMinimumHeight(25);
statusLabel = new QLabel(this);
statusLabel->setText("Status: ");
tableModel = new QStandardItemModel(this);
tableView = new QTableView(this );
tableView->setModel(tableModel);
tableView->setHorizontalHeader(new BoldHeaderView(Qt::Horizontal, tableView));
tableView->setContextMenuPolicy( Qt::CustomContextMenu );
tableView->setAutoFillBackground( false );
tableView->setShowGrid( false );
tableView->setSortingEnabled( true );
tableView->setWordWrap( true );
tableView->setCornerButtonEnabled( true );
tableView->setSelectionBehavior( QAbstractItemView::SelectRows );
tableView->setFocusPolicy( Qt::NoFocus );
tableView->setAlternatingRowColors( true );
tableView->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
tableView->horizontalHeader()->setCascadingSectionResizes( true );
tableView->horizontalHeader()->setHighlightSections( false );
tableView->verticalHeader()->setVisible( false );
tableView->setItemDelegate(new PaddingDelegate(tableView));
if (this->agent->data.Os == OS_WINDOWS) {
tableModel->setColumnCount(6);
tableModel->setHorizontalHeaderItem(0, new QStandardItem("PID"));
tableModel->setHorizontalHeaderItem(1, new QStandardItem("PPID"));
tableModel->setHorizontalHeaderItem(2, new QStandardItem("Arch"));
tableModel->setHorizontalHeaderItem(3, new QStandardItem("Session"));
tableModel->setHorizontalHeaderItem(4, new QStandardItem("Context"));
tableModel->setHorizontalHeaderItem(5, new QStandardItem("Process"));
}
else {
tableModel->setColumnCount(5);
tableModel->setHorizontalHeaderItem(0, new QStandardItem("PID"));
tableModel->setHorizontalHeaderItem(1, new QStandardItem("PPID"));
tableModel->setHorizontalHeaderItem(2, new QStandardItem("TTY"));
tableModel->setHorizontalHeaderItem(3, new QStandardItem("Context"));
tableModel->setHorizontalHeaderItem(4, new QStandardItem("Process"));
}
listGridLayout = new QGridLayout(this);
listGridLayout->setContentsMargins(5, 4, 1, 1);
listGridLayout->setVerticalSpacing(4);
listGridLayout->setHorizontalSpacing(8);
listGridLayout->addWidget( buttonReload, 0, 0, 1, 1 );
listGridLayout->addWidget( inputFilter, 0, 1, 1, 5 );
listGridLayout->addWidget( line_1, 0, 6, 1, 1 );
listGridLayout->addWidget( statusLabel, 0, 7, 1, 1 );
listGridLayout->addWidget( tableView, 1, 0, 1, 8 );
listBrowserWidget = new QWidget(this);
listBrowserWidget->setLayout(listGridLayout);
treeBrowserWidget = new QTreeWidget();
treeBrowserWidget->setSortingEnabled(false);
treeBrowserWidget->headerItem()->setText( 0, "Process Tree" );
treeBrowserWidget->setIconSize(QSize(25, 25));
splitter = new QSplitter( this );
splitter->setOrientation( Qt::Horizontal );
splitter->addWidget( treeBrowserWidget );
splitter->addWidget( listBrowserWidget );
splitter->setSizes( QList<int>() << 110 << 200 );
mainGridLayout = new QGridLayout(this );
mainGridLayout->setContentsMargins(0, 0, 0, 0 );
mainGridLayout->addWidget(splitter, 0, 0, 1, 1 );
this->setLayout(mainGridLayout);
}
void BrowserProcessWidget::SetStatus(qint64 time, int msgType, const QString &message) const
{
QString sTime = UnixTimestampGlobalToStringLocal(time);
QString status;
if( msgType == CONSOLE_OUT_LOCAL_ERROR || msgType == CONSOLE_OUT_ERROR ) {
status = TextColorHtml(message, COLOR_ChiliPepper) + " >> " + sTime;
statusLabel->setText(status);
}
else {
status = TextColorHtml(message, COLOR_NeonGreen) + " >> " + sTime;
statusLabel->setText(status);
}
}
void BrowserProcessWidget::SetProcess(int msgType, const QString &data) const
{
if( msgType == CONSOLE_OUT_LOCAL_ERROR || msgType == CONSOLE_OUT_ERROR )
return;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data.toUtf8());
if ( !jsonDoc.isArray() )
return;
QJsonArray jsonArray = jsonDoc.array();
if (agent->data.Os == OS_WINDOWS) {
QMap<int, BrowserProcessDataWin> processMap;
for ( const QJsonValue& value : jsonArray ) {
QJsonObject jsonObject = value.toObject();
BrowserProcessDataWin processData = {0};
processData.pid = jsonObject["b_pid"].toDouble();
processData.ppid = jsonObject["b_ppid"].toDouble();
processData.sessId = jsonObject["b_session_id"].toDouble();
processData.arch = jsonObject["b_arch"].toString();
processData.context = jsonObject["b_context"].toString();
processData.process = jsonObject["b_process_name"].toString();
processMap[processData.pid] = processData;
}
this->setTableProcessDataWin(processMap);
this->setTreeProcessDataWin(processMap);
}
else {
QMap<int, BrowserProcessDataUnix> processMap;
for ( const QJsonValue& value : jsonArray ) {
QJsonObject jsonObject = value.toObject();
BrowserProcessDataUnix processData = {0};
processData.pid = jsonObject["b_pid"].toDouble();
processData.ppid = jsonObject["b_ppid"].toDouble();
processData.tty = jsonObject["b_tty"].toString();
processData.context = jsonObject["b_context"].toString();
processData.process = jsonObject["b_process_name"].toString();
processMap[processData.pid] = processData;
}
this->setTableProcessDataUnix(processMap);
this->setTreeProcessDataUnix(processMap);
}
this->onFilter(inputFilter->text());
}
/// PRIVATE
void BrowserProcessWidget::setTableProcessDataWin(const QMap<int, BrowserProcessDataWin>& processMap) const
{
tableModel->removeRows(0, tableModel->rowCount());
tableModel->setRowCount(processMap.size());
tableView->setSortingEnabled( false );
int row = 0;
for (const auto& item : processMap) {
auto item_Pid = new QStandardItem( QString::number(item.pid) );
item_Pid->setTextAlignment( Qt::AlignCenter );
item_Pid->setFlags(item_Pid->flags() & ~Qt::ItemIsEditable);
auto item_Ppid = new QStandardItem( QString::number(item.ppid) );
item_Ppid->setTextAlignment( Qt::AlignCenter );
item_Ppid->setFlags(item_Ppid->flags() & ~Qt::ItemIsEditable);
auto item_Arch = new QStandardItem( item.arch );
item_Arch->setTextAlignment( Qt::AlignCenter );
item_Arch->setFlags(item_Arch->flags() & ~Qt::ItemIsEditable);
auto item_Session = new QStandardItem( QString::number(item.sessId) );
item_Session->setTextAlignment( Qt::AlignCenter );
item_Session->setFlags(item_Session->flags() & ~Qt::ItemIsEditable);
auto item_Context = new QStandardItem( item.context );
item_Context->setFlags(item_Context->flags() & ~Qt::ItemIsEditable);
auto item_Process = new QStandardItem( item.process );
item_Process->setFlags(item_Process->flags() & ~Qt::ItemIsEditable);
if ( agent->data.Pid == QString::number(item.pid) ) {
item_Pid->setForeground(QColor(COLOR_ChiliPepper));
item_Ppid->setForeground(QColor(COLOR_ChiliPepper));
item_Arch->setForeground(QColor(COLOR_ChiliPepper));
item_Session->setForeground(QColor(COLOR_ChiliPepper));
item_Context->setForeground(QColor(COLOR_ChiliPepper));
item_Process->setForeground(QColor(COLOR_ChiliPepper));
}
tableModel->setItem(row, 0, item_Pid);
tableModel->setItem(row, 1, item_Ppid);
tableModel->setItem(row, 2, item_Arch);
tableModel->setItem(row, 3, item_Session);
tableModel->setItem(row, 4, item_Context);
tableModel->setItem(row, 5, item_Process);
row++;
}
tableView->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
tableView->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::ResizeToContents );
tableView->horizontalHeader()->setSectionResizeMode( 2, QHeaderView::ResizeToContents );
tableView->horizontalHeader()->setSectionResizeMode( 3, QHeaderView::ResizeToContents );
tableView->horizontalHeader()->setSectionResizeMode( 4, QHeaderView::ResizeToContents );
tableView->setSortingEnabled( true );
}
void BrowserProcessWidget::setTableProcessDataUnix(const QMap<int, BrowserProcessDataUnix>& processMap) const
{
tableModel->removeRows(0, tableModel->rowCount());
tableModel->setRowCount(processMap.size());
tableView->setSortingEnabled( false );
int row = 0;
for (const auto& item : processMap) {
auto item_Pid = new QStandardItem( QString::number(item.pid) );
item_Pid->setTextAlignment( Qt::AlignCenter );
item_Pid->setFlags(item_Pid->flags() & ~Qt::ItemIsEditable);
auto item_Ppid = new QStandardItem( QString::number(item.ppid) );
item_Ppid->setTextAlignment( Qt::AlignCenter );
item_Ppid->setFlags(item_Ppid->flags() & ~Qt::ItemIsEditable);
auto item_Tty = new QStandardItem( item.tty );
item_Tty->setTextAlignment( Qt::AlignCenter );
item_Tty->setFlags(item_Tty->flags() & ~Qt::ItemIsEditable);
auto item_Context = new QStandardItem( item.context );
item_Context->setFlags(item_Context->flags() & ~Qt::ItemIsEditable);
auto item_Process = new QStandardItem( item.process );
item_Process->setFlags(item_Process->flags() & ~Qt::ItemIsEditable);
if ( agent->data.Pid == QString::number(item.pid) ) {
item_Pid->setForeground(QColor(COLOR_ChiliPepper));
item_Ppid->setForeground(QColor(COLOR_ChiliPepper));
item_Tty->setForeground(QColor(COLOR_ChiliPepper));
item_Context->setForeground(QColor(COLOR_ChiliPepper));
item_Process->setForeground(QColor(COLOR_ChiliPepper));
}
tableModel->setItem(row, 0, item_Pid);
tableModel->setItem(row, 1, item_Ppid);
tableModel->setItem(row, 2, item_Tty);
tableModel->setItem(row, 3, item_Context);
tableModel->setItem(row, 4, item_Process);
row++;
}
tableView->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
tableView->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::ResizeToContents );
tableView->horizontalHeader()->setSectionResizeMode( 2, QHeaderView::ResizeToContents );
tableView->horizontalHeader()->setSectionResizeMode( 3, QHeaderView::ResizeToContents );
tableView->setSortingEnabled( true );
}
void BrowserProcessWidget::setTreeProcessDataWin(QMap<int, BrowserProcessDataWin> processMap) const
{
treeBrowserWidget->clear();
treeBrowserWidget->setColumnCount(3);
treeBrowserWidget->setHeaderLabels({"Process", "Process ID", "Context"});
QMap<int, QTreeWidgetItem*> nodeMap;
auto pids = processMap.keys();
for (int pid : pids) {
BrowserProcessDataWin processData = processMap[pid];
if (processData.ppid == 0 || !pids.contains(processData.ppid)) {
QTreeWidgetItem* rootItem = new QTreeWidgetItem(treeBrowserWidget);
rootItem->setText(0, processData.process);
rootItem->setText(1, QString::number(processData.pid));
rootItem->setText(2, processData.context);
processMap.remove(processData.pid);
nodeMap[processData.pid] = rootItem;
addProcessToTreeWin(rootItem, processData.pid, processMap, &nodeMap);
}
}
treeBrowserWidget->header()->setStretchLastSection(false);
treeBrowserWidget->header()->setSectionResizeMode( 0, QHeaderView::Stretch );
treeBrowserWidget->header()->setSectionResizeMode( 1, QHeaderView::ResizeToContents );
treeBrowserWidget->header()->setSectionResizeMode( 2, QHeaderView::ResizeToContents );
treeBrowserWidget->expandAll();
}
void BrowserProcessWidget::addProcessToTreeWin(QTreeWidgetItem* parent, const int parentPID, QMap<int, BrowserProcessDataWin> processMap, QMap<int, QTreeWidgetItem*> *nodeMap)
{
auto pids = processMap.keys();
for (int pid : pids) {
BrowserProcessDataWin processData = processMap[pid];
if (processData.ppid == parentPID) {
QTreeWidgetItem* childItem = new QTreeWidgetItem(parent);
childItem->setText(0, processData.process);
childItem->setText(1, QString::number(processData.pid));
childItem->setText(2, processData.context);
processMap.remove(processData.pid);
(*nodeMap)[processData.pid] = childItem;
addProcessToTreeWin(childItem, processData.pid, processMap, nodeMap);
}
}
}
void BrowserProcessWidget::setTreeProcessDataUnix(QMap<int, BrowserProcessDataUnix> processMap) const
{
treeBrowserWidget->clear();
treeBrowserWidget->setColumnCount(3);
treeBrowserWidget->setHeaderLabels({"Process", "Process ID", "Context"});
QMap<int, QTreeWidgetItem*> nodeMap;
auto pids = processMap.keys();
for (int pid : pids) {
BrowserProcessDataUnix processData = processMap[pid];
if (processData.ppid == 0 || !pids.contains(processData.ppid)) {
QTreeWidgetItem* rootItem = new QTreeWidgetItem(treeBrowserWidget);
rootItem->setText(0, processData.process);
rootItem->setText(1, QString::number(processData.pid));
rootItem->setText(2, processData.context);
processMap.remove(processData.pid);
nodeMap[processData.pid] = rootItem;
addProcessToTreeUnix(rootItem, processData.pid, processMap, &nodeMap);
}
}
treeBrowserWidget->header()->setStretchLastSection(false);
treeBrowserWidget->header()->setSectionResizeMode( 0, QHeaderView::Stretch );
treeBrowserWidget->header()->setSectionResizeMode( 1, QHeaderView::ResizeToContents );
treeBrowserWidget->header()->setSectionResizeMode( 2, QHeaderView::ResizeToContents );
treeBrowserWidget->expandAll();
}
void BrowserProcessWidget::addProcessToTreeUnix(QTreeWidgetItem* parent, const int parentPID, QMap<int, BrowserProcessDataUnix> processMap, QMap<int, QTreeWidgetItem*> *nodeMap)
{
auto pids = processMap.keys();
for (int pid : pids) {
BrowserProcessDataUnix processData = processMap[pid];
if (processData.ppid == parentPID) {
QTreeWidgetItem* childItem = new QTreeWidgetItem(parent);
childItem->setText(0, processData.process);
childItem->setText(1, QString::number(processData.pid));
childItem->setText(2, processData.context);
processMap.remove(processData.pid);
(*nodeMap)[processData.pid] = childItem;
addProcessToTreeUnix(childItem, processData.pid, processMap, nodeMap);
}
}
}
void BrowserProcessWidget::filterTreeWidget(const QString &filterText) const
{
std::function<bool(QTreeWidgetItem*)> filterItem = [&](QTreeWidgetItem* item) -> bool {
bool match = item->text(0).contains(filterText, Qt::CaseInsensitive);
for (int i = 0; i < item->childCount(); ++i) {
match |= filterItem(item->child(i));
}
item->setHidden(!match);
return match;
};
for (int i = 0; i < treeBrowserWidget->topLevelItemCount(); ++i) {
filterItem(treeBrowserWidget->topLevelItem(i));
}
}
void BrowserProcessWidget::filterTableWidget(const QString &filterText) const
{
for (int row = 0; row < tableModel->rowCount(); ++row) {
bool match = false;
for (int col = 0; col < tableModel->columnCount(); ++col) {
if (tableModel->item(row, col) && tableModel->item(row, col)->text().contains(filterText, Qt::CaseInsensitive)) {
match = true;
break;
}
}
tableView->setRowHidden(row, !match);
}
}
/// SLOTS
void BrowserProcessWidget::onReload() const
{
statusLabel->setText("");
Q_EMIT agent->adaptixWidget->eventProcessBrowserList(agent->data.Id);
}
void BrowserProcessWidget::onFilter(const QString &text) const
{
this->filterTreeWidget(text);
this->filterTableWidget(text);
}
void BrowserProcessWidget::handleTableMenu(const QPoint &pos)
{
if ( !tableView->indexAt(pos).isValid() )
return;
QVector<DataMenuProcessBrowser> items;
for( int rowIndex = 0 ; rowIndex < tableModel->rowCount() ; rowIndex++ ) {
if ( tableView->selectionModel()->isSelected(tableModel->index(rowIndex, 0)) ) {
DataMenuProcessBrowser data;
data.agentId = this->agent->data.Id;
if (this->agent->data.Os == OS_WINDOWS) {
data.pid = tableModel->item(rowIndex, 0)->text();
data.ppid = tableModel->item(rowIndex, 1)->text();
data.arch = tableModel->item(rowIndex, 2)->text();
data.session_id = tableModel->item(rowIndex, 3)->text();
data.context = tableModel->item(rowIndex, 4)->text();
data.process = tableModel->item(rowIndex, 5)->text();
}
else {
data.pid = tableModel->item(rowIndex, 0)->text();
data.ppid = tableModel->item(rowIndex, 1)->text();
data.session_id = tableModel->item(rowIndex, 2)->text();
data.context = tableModel->item(rowIndex, 3)->text();
data.process = tableModel->item(rowIndex, 4)->text();
}
items.append(data);
}
}
auto ctxMenu = QMenu();
int count = agent->adaptixWidget->ScriptManager->AddMenuProcessBrowser(&ctxMenu, items);
if (count) {
ctxMenu.addSeparator();
}
ctxMenu.addAction( "Copy PID", this, &BrowserProcessWidget::actionCopyPid);
ctxMenu.exec(tableView->horizontalHeader()->viewport()->mapToGlobal(pos));
}
void BrowserProcessWidget::actionCopyPid() const
{
int row = tableView->currentIndex().row();
if( row >= 0) {
QString pid = tableModel->item( row, 0 )->text();
QApplication::clipboard()->setText( pid );
}
}
void BrowserProcessWidget::onTableSelect() const
{
QString pid = tableModel->item( tableView->currentIndex().row(), 0 )->text();
auto item = QTreeWidgetItemIterator ( treeBrowserWidget );
while ( *item ) {
if ( ( *item )->text( 1 ) == pid ) {
treeBrowserWidget->setCurrentItem( *item );
return;
}
++item;
}
}
void BrowserProcessWidget::onTreeSelect() const
{
QString pid = treeBrowserWidget->currentItem()->text( 1 );
for ( int i = 0; i < tableModel->rowCount(); i++ ) {
if ( tableModel->item( i, 0 )->text().compare( pid ) == 0 )
tableView->setCurrentIndex( tableModel->index(i, 0) );
}
}