RpcInvestigator/Windows/Controls/SnifferListview.cs

362 lines
12 KiB
C#

//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using BrightIdeasSoftware;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using RpcInvestigator.Util;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using RpcInvestigator.TabPages;
namespace RpcInvestigator.Windows.Controls
{
internal class SnifferListview : FastObjectListView
{
private List<string> m_ChosenColumns;
private readonly SnifferCallbackTable m_Callbacks;
private Settings m_Settings;
public SnifferListview(
SnifferCallbackTable Callbacks,
Settings Settings
)
{
m_Settings = Settings;
m_Callbacks = Callbacks;
DoubleBuffered = true;
Visible = false;
BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
CellEditUseWholeCell = false;
Dock = System.Windows.Forms.DockStyle.Fill;
FullRowSelect = true;
GridLines = true;
HideSelection = false;
Location = new System.Drawing.Point(6, 130);
Margin = new System.Windows.Forms.Padding(6, 6, 6, 6);
Name = "snifferListview";
ShowGroups = false;
Size = new System.Drawing.Size(2940, 1336);
TabIndex = 0;
UseAlternatingBackColors = true;
View = View.Details;
AlternateRowBackColor = Color.LightBlue;
UseCompatibleStateImageBehavior = false;
VirtualMode = true;
UseFiltering = true;
if (m_Settings.m_SnifferColumns.Count() == 0)
{
m_ChosenColumns = new List<string>() {
"Timestamp",
"ProcessId",
"ThreadId",
"UserSid",
"Task",
"Opcode",
"InterfaceUuid",
"ProcNum",
"Protocol",
"NetworkAddress",
"Endpoint",
"Params",
"AuthenticationLevel",
"ImpersonationLevel"
};
}
else
{
m_ChosenColumns = m_Settings.m_SnifferColumns;
}
DoubleClick += new EventHandler((object obj, EventArgs e2) =>
{
if (SelectedObjects == null || SelectedObjects.Count == 0)
{
return;
}
var selectedRow = SelectedObjects.Cast<ParsedEtwEvent>().ToList()[0];
});
CellRightClick += RightClickHandler;
}
public
void
ChooseColumns(Guid EtwProviderGuid)
{
var allColumns = new List<string>();
try
{
//
// Add any member of the ParsedEtwEvent class that is marked OLVColumn
//
var props = ReflectionHelper.GetOlvAttributes(typeof(ParsedEtwEvent));
allColumns.AddRange(props);
}
catch (Exception ex)
{
MessageBox.Show("Unable to reflect ParsedEtwEvent: " + ex.Message);
return;
}
try
{
//
// Add columns for custom fields inside UserData.
//
var info = EtwProviderParser.GetDistinctProviderEventInfo(
EtwProviderGuid);
if (info.ContainsKey("UserDataProperties"))
{
allColumns.AddRange(info["UserDataProperties"]);
}
}
catch (Exception ex)
{
MessageBox.Show("Unable to load columns: " +
ex.Message);
return;
}
using (var chooser = new EtwColumnPicker(
allColumns, m_ChosenColumns, m_Settings))
{
chooser.ShowDialog();
if (chooser.DialogResult == DialogResult.OK)
{
m_ChosenColumns = chooser.m_SelectedColumns;
BuildColumns();
}
}
}
public
void
SetGroupingStrategy(bool EnableGroupingByActivity)
{
if (!EnableGroupingByActivity)
{
ShowGroups = false;
return;
}
ShowGroups = true;
AlwaysGroupByColumn = AllColumns[4]; // ActivityID
BuildGroups();
}
public
void
BuildColumns()
{
//
// Add UserData columns that were chosen by the user (or the default ones)
//
var cols = new List<OLVColumn>();
foreach (var columnName in m_ChosenColumns)
{
OLVColumn col;
if (columnName == "Params")
{
//
// This is a special-case hack for Params, which is an
// array of structs. This particular property is useful
// for RPC, so we'll manually handle it. Other arrays
// are not generally handled, because we have no idea
// how many elements are there. Columns cannot be created
// randomly on the fly as data comes in, so we have to
// choose a random number of array elements to support now.
// The column names produced below are aligned with how
// EtwEventParser handles storing array elements in the
// UserDataProperties array.
//
for (int i = 0; i < 4; i++)
{
col = new OLVColumn("Params-" + i, null);
col.Renderer = new NoEllipsisRenderer();
col.Name = "Params-" + i;
col.AspectGetter = delegate (object Row)
{
if (Row == null)
{
return "";
}
var row = (ParsedEtwEvent)Row;
if (!row.UserDataProperties.ContainsKey(col.Name))
{
return "";
}
return row.UserDataProperties[col.Name];
};
col.IsVisible = true;
cols.Add(col);
}
continue;
}
col = new OLVColumn(columnName, null);
col.Renderer = new NoEllipsisRenderer();
col.Name = columnName;
col.AspectGetter = delegate (object Row)
{
if (Row == null)
{
return "";
}
var row = (ParsedEtwEvent)Row;
//
// Use reflection to try to pull the value directly from a field
// in the structure that matches this column name.
//
object value = null;
try
{
var properties = Row.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance);
properties.ToList().ForEach(property =>
{
if (property.Name.ToLower() == col.Name.ToLower())
{
value = property.GetValue(Row, null);
}
});
}
catch (Exception) { }
//
// Next, try the UserDataProperties array.
//
if (value == null)
{
if (!row.UserDataProperties.ContainsKey(col.Name))
{
return "";
}
return row.UserDataProperties[col.Name];
}
return value;
};
col.AspectToStringConverter = delegate (object Item)
{
if (Item == null)
{
return "";
}
if (col.Name == "ProcessId" || col.Name == "ProcessID"
|| col.Name == "ThreadId")
{
return Item.ToString();
}
else if (col.Name == "UserSid")
{
}
else if (long.TryParse(Item.ToString(), out long value))
{
return "0x" + value.ToString("X");
}
return Item.ToString();
};
col.IsVisible = true;
cols.Add(col);
}
AllColumns.AddRange(cols.ToArray());
foreach (var column in AllColumns)
{
column.MaximumWidth = -1;
}
AllColumns = cols;
RebuildColumns();
}
public
void
Update(List<ParsedEtwEvent> Events)
{
SuspendLayout();
AddObjects(Events);
ResumeLayout();
}
public
new
void
Reset()
{
ClearObjects();
Groups.Clear();
}
public
void
SaveAsText()
{
if (Objects == null)
{
return;
}
var data = Objects.Cast<ParsedEtwEvent>().ToList();
if (data.Count == 0)
{
return;
}
StringBuilder sb = new StringBuilder();
data.ForEach(ev =>
{
sb.AppendLine(ev.ToString());
});
var location = Path.GetRandomFileName() + ".txt";
File.WriteAllText(location, sb.ToString());
Process.Start(location);
}
private
void
RightClickHandler(
object Obj,
CellRightClickEventArgs Args
)
{
TabPages.ContextMenu.BuildRightClickMenu(Args, new List<ToolStripMenuItem>{
new ToolStripMenuItem("Open in Library", null, ContextMenuOpenInLibrary),
});
}
private
void
ContextMenuOpenInLibrary(
object Sender,
EventArgs Args
)
{
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
var evt = args.Model as ParsedEtwEvent;
if (!evt.UserDataProperties.ContainsKey("InterfaceUuid"))
{
MessageBox.Show("Unable to show RPC server details because " +
"there is no interface UUID present in the ETW event.");
return;
}
var interfaceId = evt.UserDataProperties["InterfaceUuid"].Replace(
"{", "").Replace("}", "");
m_Callbacks.ShowRpcServerDetailsCallback(interfaceId);
}
}
}