Implemented multi-select functionality and basic context-menu for session table.

This commit is contained in:
Jakob Friedl
2025-09-09 22:55:43 +02:00
parent 2320b705d3
commit 5f131ae916
3 changed files with 136 additions and 56 deletions

View File

@@ -1,6 +1,6 @@
[Window][Dockspace] [Window][Dockspace]
Pos=0,0 Pos=0,0
Size=1908,999 Size=2548,1359
Collapsed=0 Collapsed=0
[Window][Debug##Default] [Window][Debug##Default]
@@ -9,26 +9,26 @@ Size=449,261
Collapsed=0 Collapsed=0
[Window][Dear ImGui Demo] [Window][Dear ImGui Demo]
Pos=10,43 Pos=1852,43
Size=1453,946 Size=686,653
Collapsed=0 Collapsed=0
DockId=0x00000001,1 DockId=0x00000007,0
[Window][Agents [Table View]] [Window][Agents [Table View]]
Pos=10,43 Pos=10,43
Size=1187,946 Size=1187,946
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000006,0
[Window][Agents [Graph View]] [Window][Agents [Graph View]]
Pos=10,43 Pos=10,43
Size=1888,473 Size=1888,473
Collapsed=0 Collapsed=0
DockId=0x00000001,1 DockId=0x00000006,1
[Window][Eventlog] [Window][Eventlog]
Pos=1465,43 Pos=10,698
Size=433,946 Size=2528,651
Collapsed=0 Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
@@ -48,9 +48,10 @@ Size=1150,550
Collapsed=0 Collapsed=0
[Window][Dear ImGui Debug Log] [Window][Dear ImGui Debug Log]
Pos=60,60 Pos=10,698
Size=1088,486 Size=2528,651
Collapsed=0 Collapsed=0
DockId=0x00000004,1
[Window][Dear ImGui Metrics/Debugger] [Window][Dear ImGui Metrics/Debugger]
Pos=60,60 Pos=60,60
@@ -59,9 +60,9 @@ Collapsed=0
[Window][Sessions [Table View]] [Window][Sessions [Table View]]
Pos=10,43 Pos=10,43
Size=1453,946 Size=1840,653
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000006,0
[Window][Dear ImGui Demo/ResizableChild_478B81A3] [Window][Dear ImGui Demo/ResizableChild_478B81A3]
IsChild=1 IsChild=1
@@ -93,6 +94,10 @@ Size=1453,471
Collapsed=0 Collapsed=0
DockId=0x00000002,0 DockId=0x00000002,0
[Window][Dear ImGui Demo/##Basket_87771727]
IsChild=1
Size=614,540
[Table][0xB6880529,2] [Table][0xB6880529,2]
RefScale=27 RefScale=27
Column 0 Sort=0v Column 0 Sort=0v
@@ -100,21 +105,21 @@ Column 0 Sort=0v
[Table][0x951FCC8A,6] [Table][0x951FCC8A,6]
RefScale=27 RefScale=27
Column 0 Width=48 Column 0 Width=48
Column 1 Width=72 Sort=0v Column 1 Width=112 Sort=0v
Column 2 Width=108 Column 2 Width=108
Column 3 Weight=0.5480 Column 3 Width=136
Column 4 Weight=1.0000 Column 4 Weight=1.0000
Column 5 Weight=-1.0000 Column 5 Width=-1
[Table][0x32886A44,8] [Table][0x32886A44,8]
Column 0 Weight=0.7758 Column 0 Weight=0.7126
Column 1 Weight=1.1556 Column 1 Weight=1.0615
Column 2 Weight=0.7758 Column 2 Weight=0.7126
Column 3 Weight=0.9616 Column 3 Weight=1.2397
Column 4 Weight=0.9616 Column 4 Weight=1.6776
Column 5 Weight=1.1556 Column 5 Weight=1.2397
Column 6 Weight=0.3879 Column 6 Weight=0.3563
Column 7 Weight=1.8263 Column 7 Weight=1.5466
[Table][0x51D6F5EA,3] [Table][0x51D6F5EA,3]
Column 0 Weight=1.0000 Column 0 Weight=1.0000
@@ -168,9 +173,9 @@ Column 1 Weight=1.0000
[Table][0xAED4F161,3] [Table][0xAED4F161,3]
RefScale=27 RefScale=27
Column 0 Width=191 Visible=1 Column 0 Weight=1.0000
Column 1 Width=108 Visible=1 Sort=0v Column 1 Width=839 Sort=0v
Column 2 Width=-1 Visible=0 Column 2 Width=-1
[Table][0x3A20F3F3,3] [Table][0x3A20F3F3,3]
RefScale=27 RefScale=27
@@ -182,7 +187,7 @@ Column 2 Width=96
[Table][0x302651AD,14] [Table][0x302651AD,14]
RefScale=27 RefScale=27
Column 0 Width=96 Sort=0v Column 0 Width=96
Column 1 Width=33 Column 1 Width=33
Column 2 Width=33 Column 2 Width=33
Column 3 Width=33 Column 3 Width=33
@@ -222,13 +227,13 @@ Column 2 Weight=1.0000
[Table][0x127BEAD2,7] [Table][0x127BEAD2,7]
RefScale=27 RefScale=27
Column 0 Width=84 Column 0 Width=155
Column 1 Width=167 Column 1 Width=155
Column 2 Width=191 Column 2 Width=155
Column 3 Width=191 Column 3 Width=156
Column 4 Width=191 Column 4 Width=156
Column 5 Width=191 Column 5 Width=156
Column 6 Width=191 Column 6 Width=474
[Table][0x755BE644,3] [Table][0x755BE644,3]
RefScale=27 RefScale=27
@@ -237,9 +242,10 @@ Column 1 Width=83
Column 2 Width=84 Column 2 Width=84
[Table][0xBE0D9DE1,3] [Table][0xBE0D9DE1,3]
Column 0 Weight=1.0000 RefScale=27
Column 1 Weight=1.0000 Column 0 Width=84
Column 2 Weight=1.0000 Column 1 Width=84
Column 2 Width=217
[Table][0xDE11CCC7,8] [Table][0xDE11CCC7,8]
Column 0 Weight=0.7758 Column 0 Weight=0.7758
@@ -251,10 +257,18 @@ Column 5 Weight=1.1556
Column 6 Weight=0.3879 Column 6 Weight=0.3879
Column 7 Weight=1.8263 Column 7 Weight=1.8263
[Docking][Data] [Table][0x61294EC1,3]
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=X RefScale=27
DockNode ID=0x00000003 Parent=0x85940918 SizeRef=1453,473 Split=Y Selected=0x61E02D75 Column 0 Width=119
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1453,473 CentralNode=1 Selected=0x61E02D75 Column 1 Width=119
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1453,471 Selected=0x6D7E7D39 Column 2 Width=119
DockNode ID=0x00000004 Parent=0x85940918 SizeRef=433,473 Selected=0x0FA43D88
[Docking][Data]
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=2528,1306 Split=Y
DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1453,473 Split=Y Selected=0x61E02D75
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=2528,653 Split=X Selected=0x61E02D75
DockNode ID=0x00000006 Parent=0x00000003 SizeRef=1840,653 CentralNode=1 Selected=0x61E02D75
DockNode ID=0x00000007 Parent=0x00000003 SizeRef=686,653 Selected=0x5E5F7166
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=2528,651 Selected=0x0FA43D88
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1453,471 Selected=0x6D7E7D39

View File

@@ -19,6 +19,15 @@ proc main() =
views["Listeners"] = addr showListeners views["Listeners"] = addr showListeners
views["Eventlog"] = addr showEventlog views["Eventlog"] = addr showEventlog
# Create components
var
dockspace = Dockspace()
sessionsTable = SessionsTable("Sessions [Table View]")
sessionsGraph = SessionsTable("Sessions [Graph View]")
listenersTable = ListenersTable("Listeners")
eventlog = Eventlog("Eventlog")
let io = igGetIO() let io = igGetIO()
# main loop # main loop
@@ -30,13 +39,11 @@ proc main() =
continue continue
newFrame() newFrame()
# UI components/views # Draw/update UI components/views
Dockspace().draw(addr showConquest, views) dockspace.draw(addr showConquest, views)
if showSessionsTable: sessionsTable.draw(addr showSessionsTable)
if showSessionsTable: SessionsTable("Sessions [Table View]").draw(addr showSessionsTable) if showListeners: listenersTable.draw(addr showListeners)
if showSessionsGraph: SessionsTable("Sessions [Graph View]").draw(addr showSessionsGraph) if showEventlog: eventlog.draw(addr showEventlog)
if showListeners: ListenersTable("Listeners").draw(addr showListeners)
if showEventlog:Eventlog("Eventlog").draw(addr showEventlog)
igShowDemoWindow(nil) igShowDemoWindow(nil)

View File

@@ -1,13 +1,13 @@
import times import times
import imguin/[cimgui, glfw_opengl, simple] import imguin/[cimgui, glfw_opengl, simple]
import ../utils/appImGui import ../utils/appImGui
import ../../common/[types] import ../../common/[types, utils]
type type
SessionsTableComponent = ref object of RootObj SessionsTableComponent = ref object of RootObj
title: string title: string
agents: seq[Agent] agents: seq[Agent]
selection: ptr ImGuiSelectionBasicStorage
let exampleAgents: seq[Agent] = @[ let exampleAgents: seq[Agent] = @[
Agent( Agent(
@@ -43,6 +43,40 @@ let exampleAgents: seq[Agent] = @[
firstCheckin: now() - initDuration(hours = 1, minutes = 30), firstCheckin: now() - initDuration(hours = 1, minutes = 30),
latestCheckin: now() - initDuration(minutes = 5), latestCheckin: now() - initDuration(minutes = 5),
sessionKey: [byte 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] sessionKey: [byte 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
),
Agent(
agentId: "C9D8E7F6",
listenerId: "L2468135",
username: "charlie",
hostname: "SERVER-03",
domain: "IT",
ip: "172.16.0.20",
os: "Windows Server 2019",
process: "powershell.exe",
pid: 7890,
elevated: true,
sleep: 30,
tasks: @[],
firstCheckin: now() - initDuration(hours = 3, minutes = 15),
latestCheckin: now() - initDuration(minutes = 10),
sessionKey: [byte 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
),
Agent(
agentId: "G1H2I3J4",
listenerId: "L1357924",
username: "diana",
hostname: "WORKSTATION-04",
domain: "HR",
ip: "192.168.2.15",
os: "Windows 10",
process: "chrome.exe",
pid: 3210,
elevated: false,
sleep: 90,
tasks: @[],
firstCheckin: now() - initDuration(hours = 4),
latestCheckin: now() - initDuration(minutes = 2),
sessionKey: [byte 5, 4, 3, 2, 1, 0, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6]
) )
] ]
@@ -50,6 +84,7 @@ proc SessionsTable*(title: string): SessionsTableComponent =
result = new SessionsTableComponent result = new SessionsTableComponent
result.title = title result.title = title
result.agents = exampleAgents result.agents = exampleAgents
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) = proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
igSetNextWindowSize(vec2(800, 600), ImGuiCond_Once.int32) igSetNextWindowSize(vec2(800, 600), ImGuiCond_Once.int32)
@@ -62,13 +97,12 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
ImGuiTableFlags_Reorderable.int32 or ImGuiTableFlags_Reorderable.int32 or
ImGuiTableFlags_Hideable.int32 or ImGuiTableFlags_Hideable.int32 or
ImGuiTableFlags_HighlightHoveredColumn.int32 or ImGuiTableFlags_HighlightHoveredColumn.int32 or
ImGuiTableFlags_ContextMenuInBody.int32 or
ImGuiTableFlags_RowBg.int32 or ImGuiTableFlags_RowBg.int32 or
ImGuiTableFlags_BordersV.int32 or ImGuiTableFlags_BordersV.int32 or
ImGuiTableFlags_BordersH.int32 or ImGuiTableFlags_BordersH.int32 or
ImGuiTableFlags_ScrollY.int32 or ImGuiTableFlags_ScrollY.int32 or
ImGuiTableFlags_ScrollX.int32 or ImGuiTableFlags_ScrollX.int32 or
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
ImGui_TableFlags_SizingStretchProp.int32 ImGui_TableFlags_SizingStretchProp.int32
) )
@@ -86,12 +120,20 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
igTableSetupScrollFreeze(0, 1) igTableSetupScrollFreeze(0, 1)
igTableHeadersRow() igTableHeadersRow()
var multiSelectIO = igBeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape.int32 or ImGuiMultiSelectFlags_BoxSelect1d.int32, component.selection[].Size, int32(component.agents.len()))
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
for row in 0..< component.agents.len(): for row in 0..< component.agents.len():
igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f) igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f)
let agent = component.agents[row] let agent = component.agents[row]
if igTableSetColumnIndex(0):
igText(agent.agentId) if igTableSetColumnIndex(0):
# Enable multi-select functionality
igSetNextItemSelectionUserData(row)
var isSelected = ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](row))
discard igSelectable_Bool(agent.agentId, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32, vec2(0.0f, 0.0f))
if igTableSetColumnIndex(1): if igTableSetColumnIndex(1):
igText(agent.ip) igText(agent.ip)
if igTableSetColumnIndex(2): if igTableSetColumnIndex(2):
@@ -107,6 +149,23 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(7): if igTableSetColumnIndex(7):
igText(agent.latestCheckin.format("yyyy-MM-dd HH:mm:ss")) igText(agent.latestCheckin.format("yyyy-MM-dd HH:mm:ss"))
# Handle right-click context menu
# Right-clicking the table header to hide/show columns or reset the layout is only possible when no sessions are selected
if component.selection[].Size > 0 and igBeginPopupContextWindow("TableContextMenu", ImGui_PopupFlags_MouseButtonRight.int32):
if igMenuItem("Interact", "ENTER", false, true):
igCloseCurrentPopup()
if igMenuItem("Remove", "DELETE", false, true):
igCloseCurrentPopup()
igEndPopup()
multiSelectIO = igEndMultiSelect()
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
igEndTable() igEndTable()