2025-09-07 17:18:50 +02:00
|
|
|
import times
|
|
|
|
|
import imguin/[cimgui, glfw_opengl, simple]
|
|
|
|
|
import ../utils/appImGui
|
2025-09-09 22:55:43 +02:00
|
|
|
import ../../common/[types, utils]
|
2025-09-07 17:18:50 +02:00
|
|
|
|
|
|
|
|
type
|
|
|
|
|
SessionsTableComponent = ref object of RootObj
|
|
|
|
|
title: string
|
|
|
|
|
agents: seq[Agent]
|
2025-09-09 22:55:43 +02:00
|
|
|
selection: ptr ImGuiSelectionBasicStorage
|
2025-09-07 17:18:50 +02:00
|
|
|
|
|
|
|
|
let exampleAgents: seq[Agent] = @[
|
|
|
|
|
Agent(
|
|
|
|
|
agentId: "DEADBEEF",
|
|
|
|
|
listenerId: "L1234567",
|
|
|
|
|
username: "alice",
|
|
|
|
|
hostname: "DESKTOP-01",
|
|
|
|
|
domain: "CORP",
|
|
|
|
|
ip: "192.168.1.10",
|
|
|
|
|
os: "Windows 10",
|
|
|
|
|
process: "explorer.exe",
|
|
|
|
|
pid: 2340,
|
|
|
|
|
elevated: true,
|
|
|
|
|
sleep: 60,
|
|
|
|
|
tasks: @[],
|
|
|
|
|
firstCheckin: now() - initDuration(hours = 2),
|
|
|
|
|
latestCheckin: now(),
|
|
|
|
|
sessionKey: [byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
|
|
|
|
|
),
|
|
|
|
|
Agent(
|
|
|
|
|
agentId: "FACEDEAD",
|
|
|
|
|
listenerId: "L7654321",
|
|
|
|
|
username: "bob",
|
|
|
|
|
hostname: "LAPTOP-02",
|
|
|
|
|
domain: "SALES",
|
|
|
|
|
ip: "10.0.0.5",
|
|
|
|
|
os: "Windows 11",
|
|
|
|
|
process: "cmd.exe",
|
|
|
|
|
pid: 4567,
|
|
|
|
|
elevated: false,
|
|
|
|
|
sleep: 120,
|
|
|
|
|
tasks: @[],
|
|
|
|
|
firstCheckin: now() - initDuration(hours = 1, minutes = 30),
|
|
|
|
|
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]
|
2025-09-09 22:55:43 +02:00
|
|
|
),
|
|
|
|
|
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]
|
2025-09-07 17:18:50 +02:00
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
proc SessionsTable*(title: string): SessionsTableComponent =
|
|
|
|
|
result = new SessionsTableComponent
|
|
|
|
|
result.title = title
|
|
|
|
|
result.agents = exampleAgents
|
2025-09-09 22:55:43 +02:00
|
|
|
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
|
2025-09-07 17:18:50 +02:00
|
|
|
|
|
|
|
|
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
|
|
|
|
igSetNextWindowSize(vec2(800, 600), ImGuiCond_Once.int32)
|
|
|
|
|
|
|
|
|
|
igBegin(component.title, showComponent, 0)
|
|
|
|
|
defer: igEnd()
|
|
|
|
|
|
|
|
|
|
let tableFlags = (
|
|
|
|
|
ImGuiTableFlags_Resizable.int32 or
|
|
|
|
|
ImGuiTableFlags_Reorderable.int32 or
|
|
|
|
|
ImGuiTableFlags_Hideable.int32 or
|
|
|
|
|
ImGuiTableFlags_HighlightHoveredColumn.int32 or
|
|
|
|
|
ImGuiTableFlags_RowBg.int32 or
|
|
|
|
|
ImGuiTableFlags_BordersV.int32 or
|
|
|
|
|
ImGuiTableFlags_BordersH.int32 or
|
|
|
|
|
ImGuiTableFlags_ScrollY.int32 or
|
|
|
|
|
ImGuiTableFlags_ScrollX.int32 or
|
2025-09-09 22:55:43 +02:00
|
|
|
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
|
2025-09-07 17:18:50 +02:00
|
|
|
ImGui_TableFlags_SizingStretchProp.int32
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
let cols: int32 = 8
|
|
|
|
|
if igBeginTable("Sessions", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f):
|
|
|
|
|
|
|
|
|
|
igTableSetupColumn("AgentID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0)
|
|
|
|
|
igTableSetupColumn("Address", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
|
|
|
igTableSetupColumn("Username", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
|
|
|
igTableSetupColumn("Hostname", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
|
|
|
igTableSetupColumn("OS", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
|
|
|
igTableSetupColumn("Process", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
|
|
|
igTableSetupColumn("PID", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
|
|
|
igTableSetupColumn("Activity", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
|
|
|
|
|
|
|
|
igTableSetupScrollFreeze(0, 1)
|
|
|
|
|
igTableHeadersRow()
|
2025-09-09 22:55:43 +02:00
|
|
|
|
|
|
|
|
var multiSelectIO = igBeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape.int32 or ImGuiMultiSelectFlags_BoxSelect1d.int32, component.selection[].Size, int32(component.agents.len()))
|
|
|
|
|
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
|
2025-09-07 17:18:50 +02:00
|
|
|
|
|
|
|
|
for row in 0..< component.agents.len():
|
2025-09-09 22:55:43 +02:00
|
|
|
|
2025-09-07 17:18:50 +02:00
|
|
|
igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f)
|
|
|
|
|
let agent = component.agents[row]
|
2025-09-09 22:55:43 +02:00
|
|
|
|
|
|
|
|
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))
|
2025-09-07 17:18:50 +02:00
|
|
|
if igTableSetColumnIndex(1):
|
|
|
|
|
igText(agent.ip)
|
|
|
|
|
if igTableSetColumnIndex(2):
|
|
|
|
|
igText(agent.username)
|
|
|
|
|
if igTableSetColumnIndex(3):
|
|
|
|
|
igText(agent.hostname)
|
|
|
|
|
if igTableSetColumnIndex(4):
|
|
|
|
|
igText(agent.os)
|
|
|
|
|
if igTableSetColumnIndex(5):
|
|
|
|
|
igText(agent.process)
|
|
|
|
|
if igTableSetColumnIndex(6):
|
|
|
|
|
igText($agent.pid)
|
|
|
|
|
if igTableSetColumnIndex(7):
|
|
|
|
|
igText(agent.latestCheckin.format("yyyy-MM-dd HH:mm:ss"))
|
|
|
|
|
|
2025-09-09 22:55:43 +02:00
|
|
|
# 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)
|
|
|
|
|
|
2025-09-07 17:18:50 +02:00
|
|
|
igEndTable()
|
|
|
|
|
|
2025-09-09 22:55:43 +02:00
|
|
|
|
2025-09-07 17:18:50 +02:00
|
|
|
|