Started porting over functionality to the ImGui client via websocket communication.

This commit is contained in:
Jakob Friedl
2025-09-25 19:22:17 +02:00
parent f0dbcdfc58
commit 14771a4b50
22 changed files with 455 additions and 569 deletions

View File

@@ -1,5 +1,6 @@
switch "o", "bin/client"
switch "d", "client"
switch "d", "ImGuiTextSelect"
# Select compiler

77
src/client/event/recv.nim Normal file
View File

@@ -0,0 +1,77 @@
import whisky
import times, tables
import ../views/[sessions, listeners, console, eventlog]
import ../../common/[types, utils, event]
export recvEvent
#[
Server -> Client
]#
# proc getMessageType*(message: Message): EventType =
# var unpacker = Unpacker.init(message.data)
# return cast[EventType](unpacker.getUint8())
# proc receiveAgentPayload*(message: Message): seq[byte] =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# return string.toBytes(unpacker.getDataWithLengthPrefix())
# proc receiveAgentConnection*(message: Message, sessions: ptr SessionsTableComponent) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let agent = Agent(
# agentId: Uuid.toString(unpacker.getUint32()),
# listenerId: Uuid.toString(unpacker.getUint32()),
# username: unpacker.getDataWithLengthPrefix(),
# hostname: unpacker.getDataWithLengthPrefix(),
# domain: unpacker.getDataWithLengthPrefix(),
# ip: unpacker.getDataWithLengthPrefix(),
# os: unpacker.getDataWithLengthPrefix(),
# process: unpacker.getDataWithLengthPrefix(),
# pid: int(unpacker.getUint32()),
# elevated: unpacker.getUint8() != 0,
# sleep: int(unpacker.getUint32()),
# tasks: @[],
# firstCheckin: cast[int64](unpacker.getUint32()).fromUnix().utc(),
# latestCheckin: cast[int64](unpacker.getUint32()).fromUnix().utc(),
# )
# sessions.agents.add(agent)
# proc receiveAgentCheckin*(message: Message, sessions: ptr SessionsTableComponent)=
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let agentId = Uuid.toString(unpacker.getUint32())
# let timestamp = cast[int64](unpacker.getUint32())
# # TODO: Update checkin
# proc receiveConsoleItem*(message: Message, consoles: ptr Table[string, ConsoleComponent]) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let
# agentId = Uuid.toString(unpacker.getUint32())
# logType = cast[LogType](unpacker.getUint8())
# timestamp = cast[int64](unpacker.getUint32())
# message = unpacker.getDataWithLengthPrefix()
# consoles[][agentId].addItem(logType, message, timestamp)
# proc receiveEventlogItem*(message: Message, eventlog: ptr EventlogComponent) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let
# logType = cast[LogType](unpacker.getUint8())
# timestamp = cast[int64](unpacker.getUint32())
# message = unpacker.getDataWithLengthPrefix()
# eventlog[].addItem(logType, message, timestamp)

55
src/client/event/send.nim Normal file
View File

@@ -0,0 +1,55 @@
import whisky
import times, tables
import ../views/[sessions, listeners, console, eventlog]
import ../../common/[types, utils, serialize, event]
export sendHeartbeat
#[
Client -> Server
]#
proc sendStartListener*(ws: WebSocket, listener: UIListener) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_LISTENER_START))
packer.add(string.toUUid(listener.listenerId))
packer.addDataWithLengthPrefix(string.toBytes(listener.address))
packer.add(cast[uint16](listener.port))
packer.add(cast[uint8](listener.protocol))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendStopListener*(ws: WebSocket, listenerId: string) =
discard
# var packer = Packer.init()
# packer.add(cast[uint8](CLIENT_LISTENER_STOP))
# packer.add(string.toUuid(listenerId))
# let data = packer.pack()
# ws.send(Bytes.toString(data), BinaryMessage)
# proc sendAgentCommand*(ws: WebSocket, agentId: string, command: string) =
# var packer = Packer.init()
# packer.add(cast[uint8](CLIENT_AGENT_COMMAND))
# packer.add(string.toUuid(agentId))
# packer.addDataWithLengthPrefix(string.toBytes(command))
# let data = packer.pack()
# ws.send(Bytes.toString(data), BinaryMessage)
# proc sendAgentBuild*(ws: WebSocket, listenerId: string, sleepDelay: int, sleepMask: SleepObfuscationTechnique, spoofStack: bool, modules: uint32) =
# var packer = Packer.init()
# packer.add(cast[uint8](CLIENT_AGENT_BUILD))
# packer.add(string.toUuid(listenerId))
# packer.add(cast[uint32](sleepDelay))
# packer.add(cast[uint8](sleepMask))
# packer.add(cast[uint8](spoofStack))
# packer.add(modules)
# let data = packer.pack()
# ws.send(Bytes.toString(data), BinaryMessage)

View File

@@ -1,157 +0,0 @@
[Window][Sessions [Table View]]
Pos=10,43
Size=1310,279
Collapsed=0
DockId=0x00000003,0
[Window][Listeners]
Pos=10,324
Size=1888,665
Collapsed=0
DockId=0x00000006,0
[Window][Eventlog]
Pos=1322,43
Size=576,279
Collapsed=0
DockId=0x00000004,0
[Window][Dear ImGui Demo]
Pos=1322,43
Size=576,279
Collapsed=0
DockId=0x00000004,1
[Window][Dockspace]
Pos=0,0
Size=1908,999
Collapsed=0
[Window][[FACEDEAD] bob@LAPTOP-02]
Pos=10,395
Size=1888,594
Collapsed=0
DockId=0x00000006,1
[Window][[C9D8E7F6] charlie@SERVER-03]
Pos=10,324
Size=1888,665
Collapsed=0
DockId=0x00000006,1
[Window][Debug##Default]
Pos=60,60
Size=400,400
Collapsed=0
[Window][[G1H2I3J5] diana@WORKSTATION-04]
Pos=10,125
Size=784,665
Collapsed=0
DockId=0x00000006,1
[Window][[DEADBEEF] alice@DESKTOP-01]
Pos=10,324
Size=1888,665
Collapsed=0
DockId=0x00000006,1
[Window][Example: Console]
Pos=10,661
Size=2848,1024
Collapsed=0
DockId=0x00000006,2
[Window][Example: Assets Browser]
Pos=60,60
Size=800,480
Collapsed=0
[Window][Example: Documents]
Pos=186,108
Size=997,993
Collapsed=0
[Window][Example: Log]
Pos=119,266
Size=1717,576
Collapsed=0
[Window][Same title as another window##1]
Pos=274,278
Size=754,103
Collapsed=1
[Window][Same title as another window##2]
Pos=100,200
Size=754,103
Collapsed=0
DockId=0x00000009,1
[Window][###AnimatedTitle]
Pos=100,200
Size=754,103
Collapsed=0
DockId=0x00000009,0
[Window][Delete?]
Pos=696,412
Size=516,175
Collapsed=0
[Window][Stacked 1]
Pos=588,335
Size=669,457
Collapsed=0
[Window][StartListener]
Pos=753,446
Size=76,76
Collapsed=0
[Window][Start Listener]
Pos=704,387
Size=500,225
Collapsed=0
[Window][Dear ImGui Demo/0_E1BADA21]
IsChild=1
Size=1363,540
[Window][Generate Payload]
Pos=704,161
Size=500,677
Collapsed=0
[Window][Generate Payload/0_B6B17D5F]
IsChild=1
Size=217,310
[Table][0x32886A44,8]
Column 0 Weight=0.6432
Column 1 Weight=0.9647
Column 2 Weight=0.6694
Column 3 Weight=1.0960
Column 4 Weight=1.5816
Column 5 Weight=1.1551
Column 6 Weight=0.4331
Column 7 Weight=1.4570
[Table][0xB6880529,2]
RefScale=27
Column 0 Sort=0v
[Table][0x064A67CC,4]
Column 0 Weight=1.2081
Column 1 Weight=1.3299
Column 2 Weight=0.4873
Column 3 Weight=0.9746
[Docking][Data]
DockNode ID=0x00000009 Pos=100,200 Size=754,103 Selected=0x64D005CF
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y
DockNode ID=0x00000005 Parent=0x85940918 SizeRef=1888,279 Split=X
DockNode ID=0x00000003 Parent=0x00000005 SizeRef=1310,159 CentralNode=1 Selected=0x61E02D75
DockNode ID=0x00000004 Parent=0x00000005 SizeRef=576,159 Selected=0x5E5F7166
DockNode ID=0x00000006 Parent=0x85940918 SizeRef=1888,665 Selected=0x6BE22050

View File

@@ -1,19 +1,21 @@
import whisky
import tables, strutils
import tables, strutils, json, parsetoml
import ./utils/appImGui
import ./views/[dockspace, sessions, listeners, eventlog, console]
import ../common/[types, utils]
import ./websocket
import ./event/[send, recv]
import sugar
proc main() =
var app = createApp(1024, 800, imnodes = true, title = "Conquest", docking = true)
defer: app.destroyApp()
var
profile: Profile
views: Table[string, ptr bool]
showConquest = true
showSessionsTable = true
showSessionsGraph = false
showListeners = true
showEventlog = true
consoles: Table[string, ConsoleComponent]
@@ -25,7 +27,6 @@ proc main() =
dockTopRight: ImGuiID = 0
views["Sessions [Table View]"] = addr showSessionsTable
views["Sessions [Graph View]"] = addr showSessionsGraph
views["Listeners"] = addr showListeners
views["Eventlog"] = addr showEventlog
@@ -58,12 +59,34 @@ proc main() =
ws.sendHeartbeat()
# Receive and parse websocket response message
let message = ws.receiveMessage().get()
case message.getMessageType()
of CLIENT_EVENT_LOG:
message.receiveEventlogItem(addr eventlog)
else: discard
let event = recvEvent(ws.receiveMessage().get())
case event.eventType:
of CLIENT_PROFILE:
profile = parseString(event.data["profile"].getStr())
of CLIENT_LISTENER_ADD:
let listener = event.data.to(UIListener)
dump listener.listenerId
listenersTable.listeners.add(listener)
of CLIENT_AGENT_ADD:
let agent = event.data.to(UIAgent)
dump agent.agentId
sessionsTable.agents.add(agent)
of CLIENT_AGENT_CHECKIN:
discard
of CLIENT_AGENT_PAYLOAD:
discard
of CLIENT_CONSOLE_ITEM:
consoles[event.data["agentId"].getStr()].addItem(cast[LogType](event.data["logType"].getInt()), event.data["message"].getStr(), event.timestamp)
of CLIENT_EVENTLOG_ITEM:
eventlog.addItem(cast[LogType](event.data["logType"].getInt()), event.data["message"].getStr(), event.timestamp)
else: discard
# Draw/update UI components/views
dockspace.draw(addr showConquest, views, addr dockTop, addr dockBottom, addr dockTopLeft, addr dockTopRight)

View File

@@ -7,7 +7,7 @@ import ../../common/[types]
const MAX_INPUT_LENGTH = 512
type
ConsoleComponent* = ref object of RootObj
agent*: Agent
agent*: UIAgent
showConsole*: bool
inputBuffer: array[MAX_INPUT_LENGTH, char]
console*: ConsoleItems
@@ -42,7 +42,7 @@ proc getLineAtIndex(i: csize_t, data: pointer, outLen: ptr csize_t): cstring {.c
outLen[] = line.len.csize_t
return line
proc Console*(agent: Agent): ConsoleComponent =
proc Console*(agent: UIAgent): ConsoleComponent =
result = new ConsoleComponent
result.agent = agent
result.showConsole = true

View File

@@ -3,41 +3,25 @@ import imguin/[cimgui, glfw_opengl, simple]
import ../utils/appImGui
import ../../common/[types, utils]
import ./modals/[startListener, generatePayload]
import ../websocket
import ../event/send
import whisky
type
ListenersTableComponent* = ref object of RootObj
title: string
listeners*: seq[Listener]
listeners*: seq[UIListener]
selection: ptr ImGuiSelectionBasicStorage
startListenerModal: ListenerModalComponent
generatePayloadModal: AgentModalComponent
let exampleListeners: seq[Listener] = @[
Listener(
listenerId: "L1234567",
address: "192.168.1.1",
port: 8080,
protocol: HTTP
),
Listener(
listenerId: "L7654321",
address: "10.0.0.2",
port: 443,
protocol: HTTP
)
]
proc ListenersTable*(title: string): ListenersTableComponent =
result = new ListenersTableComponent
result.title = title
result.listeners = exampleListeners
result.listeners = @[]
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
result.startListenerModal = ListenerModal()
result.generatePayloadModal = AgentModal()
proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebSocket) =
igBegin(component.title, showComponent, 0)
defer: igEnd()
@@ -73,7 +57,7 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS
ImGuiTableFlags_ScrollY.int32 or
ImGuiTableFlags_ScrollX.int32 or
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
ImGui_TableFlags_SizingStretchProp.int32
ImGui_TableFlags_SizingStretchSame.int32
)
let cols: int32 = 4
@@ -114,7 +98,7 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS
if igMenuItem("Stop", nil, false, true):
# Update agents table with only non-selected ones
var newListeners: seq[Listener] = @[]
var newListeners: seq[UIListener] = @[]
for i in 0 ..< component.listeners.len():
if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
newListeners.add(component.listeners[i])

View File

@@ -39,7 +39,7 @@ proc resetModalValues(component: AgentModalComponent) =
component.spoofStack = false
component.moduleSelection.reset()
proc draw*(component: AgentModalComponent, listeners: seq[Listener]) =
proc draw*(component: AgentModalComponent, listeners: seq[UIListener]) =
let textSpacing = igGetStyle().ItemSpacing.x

View File

@@ -25,7 +25,7 @@ proc resetModalValues(component: ListenerModalComponent) =
component.port = DEFAULT_PORT
component.protocol = 0
proc draw*(component: ListenerModalComponent): Listener =
proc draw*(component: ListenerModalComponent): UIListener =
let textSpacing = igGetStyle().ItemSpacing.x
# Center modal
@@ -76,7 +76,7 @@ proc draw*(component: ListenerModalComponent): Listener =
if igButton("Start", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
result = Listener(
result = UIListener(
listenerId: generateUUID(),
address: $(addr component.address[0]),
port: int(component.port),

View File

@@ -8,85 +8,14 @@ import ../../common/[types, utils]
type
SessionsTableComponent* = ref object of RootObj
title: string
agents*: seq[Agent]
agents*: seq[UIAgent]
selection: ptr ImGuiSelectionBasicStorage
consoles: ptr Table[string, ConsoleComponent]
let exampleAgents: seq[Agent] = @[
Agent(
agentId: "DEADBEEF",
listenerId: "L1234567",
username: "alice",
hostname: "DESKTOP-01",
domain: "corp.local",
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: "corp.local",
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]
),
Agent(
agentId: "C9D8E7F6",
listenerId: "L2468135",
username: "charlie",
hostname: "SERVER-03",
domain: "child.corp.local",
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: "G1H2I3J5",
listenerId: "L1357924",
username: "diana",
hostname: "WORKSTATION-04",
domain: "external.local",
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]
)
]
proc SessionsTable*(title: string, consoles: ptr Table[string, ConsoleComponent]): SessionsTableComponent =
result = new SessionsTableComponent
result.title = title
result.agents = exampleAgents
result.agents = @[]
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
result.consoles = consoles
@@ -97,6 +26,7 @@ proc interact(component: SessionsTableComponent) =
while ImGuiSelectionBasicStorage_GetNextSelectedItem(component.selection, addr it, addr row):
let agent = component.agents[cast[int](row)]
# Create a new console window
if not component.consoles[].hasKey(agent.agentId):
component.consoles[][agent.agentId] = Console(agent)
@@ -104,7 +34,9 @@ proc interact(component: SessionsTableComponent) =
# Focus the existing console window
else:
igSetWindowFocus_Str(fmt"[{agent.agentId}] {agent.username}@{agent.hostname}")
# TODO: Clear selection properly
ImGuiSelectionBasicStorage_Clear(component.selection)
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
igBegin(component.title, showComponent, 0)
@@ -121,7 +53,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
ImGuiTableFlags_ScrollY.int32 or
ImGuiTableFlags_ScrollX.int32 or
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
ImGui_TableFlags_SizingStretchProp.int32
ImGui_TableFlags_SizingStretchSame.int32
)
let cols: int32 = 8
@@ -134,7 +66,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
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)
igTableSetupColumn("Last seen", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupScrollFreeze(0, 1)
igTableHeadersRow()
@@ -170,7 +102,17 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(6):
igText($agent.pid)
if igTableSetColumnIndex(7):
igText(agent.latestCheckin.format("yyyy-MM-dd HH:mm:ss"))
let duration = now() - agent.latestCheckin.fromUnix().utc()
let totalSeconds = duration.inSeconds
let hours = totalSeconds div 3600
let minutes = (totalSeconds mod 3600) div 60
let seconds = totalSeconds mod 60
let dummyTime = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int)
let timeText = dummyTime.format("HH:mm:ss")
igText(fmt"{timeText} ago")
# 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
@@ -182,7 +124,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
if igMenuItem("Remove", nil, false, true):
# Update agents table with only non-selected ones
var newAgents: seq[Agent] = @[]
var newAgents: seq[UIAgent] = @[]
for i in 0 ..< component.agents.len():
if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
newAgents.add(component.agents[i])

View File

@@ -1,135 +0,0 @@
import times, tables
import ../common/[types, utils, serialize]
import views/[sessions, listeners, console, eventlog]
import whisky
#[
[ Sending Functions ]
Client -> Server
- Heartbeat
- ListenerStart
- ListenerStop
- AgentBuild
- AgentCommand
]#
proc sendHeartbeat*(ws: WebSocket) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_HEARTBEAT))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendStartListener*(ws: WebSocket, listener: Listener) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_LISTENER_START))
packer.add(string.toUUid(listener.listenerId))
packer.addDataWithLengthPrefix(string.toBytes(listener.address))
packer.add(cast[uint16](listener.port))
packer.add(cast[uint8](listener.protocol))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendStopListener*(ws: WebSocket, listenerId: string) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_LISTENER_STOP))
packer.add(string.toUuid(listenerId))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendAgentCommand*(ws: WebSocket, agentId: string, command: string) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_AGENT_COMMAND))
packer.add(string.toUuid(agentId))
packer.addDataWithLengthPrefix(string.toBytes(command))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendAgentBuild*(ws: WebSocket, listenerId: string, sleepDelay: int, sleepMask: SleepObfuscationTechnique, spoofStack: bool, modules: uint32) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_AGENT_BUILD))
packer.add(string.toUuid(listenerId))
packer.add(cast[uint32](sleepDelay))
packer.add(cast[uint8](sleepMask))
packer.add(cast[uint8](spoofStack))
packer.add(modules)
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
#[
[ Retrieval Functions ]
Server -> Client
]#
proc getMessageType*(message: Message): WsPacketType =
var unpacker = Unpacker.init(message.data)
return cast[WsPacketType](unpacker.getUint8())
proc receiveAgentPayload*(message: Message): seq[byte] =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
return string.toBytes(unpacker.getDataWithLengthPrefix())
proc receiveAgentConnection*(message: Message, sessions: ptr SessionsTableComponent) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let agent = Agent(
agentId: Uuid.toString(unpacker.getUint32()),
listenerId: Uuid.toString(unpacker.getUint32()),
username: unpacker.getDataWithLengthPrefix(),
hostname: unpacker.getDataWithLengthPrefix(),
domain: unpacker.getDataWithLengthPrefix(),
ip: unpacker.getDataWithLengthPrefix(),
os: unpacker.getDataWithLengthPrefix(),
process: unpacker.getDataWithLengthPrefix(),
pid: int(unpacker.getUint32()),
elevated: unpacker.getUint8() != 0,
sleep: int(unpacker.getUint32()),
tasks: @[],
firstCheckin: cast[int64](unpacker.getUint32()).fromUnix().utc(),
latestCheckin: cast[int64](unpacker.getUint32()).fromUnix().utc(),
)
sessions.agents.add(agent)
proc receiveAgentCheckin*(message: Message, sessions: ptr SessionsTableComponent)=
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let agentId = Uuid.toString(unpacker.getUint32())
let timestamp = cast[int64](unpacker.getUint32())
# TODO: Update checkin
proc receiveConsoleItem*(message: Message, consoles: ptr Table[string, ConsoleComponent]) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let
agentId = Uuid.toString(unpacker.getUint32())
logType = cast[LogType](unpacker.getUint8())
timestamp = cast[int64](unpacker.getUint32())
message = unpacker.getDataWithLengthPrefix()
consoles[][agentId].addItem(logType, message, timestamp)
proc receiveEventlogItem*(message: Message, eventlog: ptr EventlogComponent) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let
logType = cast[LogType](unpacker.getUint8())
timestamp = cast[int64](unpacker.getUint32())
message = unpacker.getDataWithLengthPrefix()
eventlog[].addItem(logType, message, timestamp)