Implemented console input field.

This commit is contained in:
Jakob Friedl
2025-09-16 20:17:48 +02:00
parent ce417db941
commit ee397c4fb5
3 changed files with 112 additions and 74 deletions

View File

@@ -1,43 +1,43 @@
[Window][Sessions [Table View]] [Window][Sessions [Table View]]
Pos=10,43 Pos=10,43
Size=2117,381 Size=2117,223
Collapsed=0 Collapsed=0
DockId=0x00000003,0 DockId=0x00000003,0
[Window][Listeners] [Window][Listeners]
Pos=10,426 Pos=10,268
Size=2528,971 Size=2528,1081
Collapsed=0 Collapsed=0
DockId=0x00000002,0 DockId=0x00000002,0
[Window][Eventlog] [Window][Eventlog]
Pos=2129,43 Pos=2129,43
Size=409,381 Size=409,223
Collapsed=0 Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
[Window][Dear ImGui Demo] [Window][Dear ImGui Demo]
Pos=2129,43 Pos=2129,43
Size=409,381 Size=409,223
Collapsed=0 Collapsed=0
DockId=0x00000004,1 DockId=0x00000004,1
[Window][Dockspace] [Window][Dockspace]
Pos=0,0 Pos=0,0
Size=2548,1407 Size=2548,1359
Collapsed=0 Collapsed=0
[Window][[FACEDEAD] bob@LAPTOP-02] [Window][[FACEDEAD] bob@LAPTOP-02]
Pos=10,426 Pos=10,268
Size=2528,971 Size=2528,1081
Collapsed=0 Collapsed=0
DockId=0x00000002,3 DockId=0x00000002,1
[Window][[C9D8E7F6] charlie@SERVER-03] [Window][[C9D8E7F6] charlie@SERVER-03]
Pos=10,426 Pos=10,268
Size=2528,971 Size=2528,1081
Collapsed=0 Collapsed=0
DockId=0x00000002,2 DockId=0x00000002,1
[Window][Debug##Default] [Window][Debug##Default]
Pos=60,60 Pos=60,60
@@ -45,16 +45,16 @@ Size=400,400
Collapsed=0 Collapsed=0
[Window][[G1H2I3J5] diana@WORKSTATION-04] [Window][[G1H2I3J5] diana@WORKSTATION-04]
Pos=10,426 Pos=10,268
Size=2528,971 Size=2528,1081
Collapsed=0 Collapsed=0
DockId=0x00000002,1 DockId=0x00000002,1
[Window][[DEADBEEF] alice@DESKTOP-01] [Window][[DEADBEEF] alice@DESKTOP-01]
Pos=10,429 Pos=10,604
Size=1888,560 Size=2848,1081
Collapsed=0 Collapsed=0
DockId=0x00000002,1 DockId=0x00000002,2
[Window][Example: Console] [Window][Example: Console]
Pos=10,466 Pos=10,466
@@ -80,8 +80,8 @@ Collapsed=0
[Table][0x32886A44,8] [Table][0x32886A44,8]
Column 0 Weight=0.6513 Column 0 Weight=0.6513
Column 1 Weight=0.9753 Column 1 Weight=0.9753
Column 2 Weight=0.5524 Column 2 Weight=0.3274
Column 3 Weight=1.0605 Column 3 Weight=1.2856
Column 4 Weight=1.7323 Column 4 Weight=1.7323
Column 5 Weight=1.1492 Column 5 Weight=1.1492
Column 6 Weight=0.4263 Column 6 Weight=0.4263
@@ -92,9 +92,9 @@ RefScale=27
Column 0 Sort=0v Column 0 Sort=0v
[Docking][Data] [Docking][Data]
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=2528,1354 Split=Y DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=2528,1306 Split=Y
DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,381 Split=X DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,559 Split=X
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=613,159 CentralNode=1 Selected=0x61E02D75 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=613,159 CentralNode=1 Selected=0x61E02D75
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=409,159 Selected=0x5E5F7166 DockNode ID=0x00000004 Parent=0x00000001 SizeRef=409,159 Selected=0x5E5F7166
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,971 Selected=0x8D780333 DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,1081 Selected=0x65D642C0

View File

@@ -3,33 +3,30 @@ import imguin/[cimgui, glfw_opengl, simple]
import ../utils/appImGui import ../utils/appImGui
import ../../common/[types] import ../../common/[types]
const MAX_INPUT_LENGTH = 512
type type
ConsoleItem = ref object
timestamp: DateTime
logType: LogType
text: string
ConsoleItems = ref object
items: seq[string]
ConsoleComponent* = ref object of RootObj ConsoleComponent* = ref object of RootObj
agent: Agent agent: Agent
showConsole*: bool showConsole*: bool
inputBuffer: string inputBuffer: array[MAX_INPUT_LENGTH, char]
consoleItems: ConsoleItems console: ConsoleItems
textSelect: ptr TextSelect textSelect: ptr TextSelect
proc getItemText(item: ConsoleItem): cstring =
let timestamp = item.timestamp.format("dd-MM-yyyy HH:mm:ss")
return fmt"[{timestamp}] {$item.itemType} {item.text}".string
proc getNumLines(data: pointer): csize_t {.cdecl.} = proc getNumLines(data: pointer): csize_t {.cdecl.} =
if data.isNil: if data.isNil:
return 0 return 0
let consoleItems = cast[ConsoleItems](data) let console = cast[ConsoleItems](data)
return consoleItems.items.len().csize_t return console.items.len().csize_t
proc getLineAtIndex(i: csize_t, data: pointer, outLen: ptr csize_t): cstring {.cdecl.} = proc getLineAtIndex(i: csize_t, data: pointer, outLen: ptr csize_t): cstring {.cdecl.} =
if data.isNil: if data.isNil:
return nil return nil
let consoleItems = cast[ConsoleItems](data) let console = cast[ConsoleItems](data)
let line = consoleItems.items[i].cstring let line = getItemText(console.items[i])
if not outLen.isNil: if not outLen.isNil:
outLen[] = line.len.csize_t outLen[] = line.len.csize_t
return line return line
@@ -38,16 +35,18 @@ proc Console*(agent: Agent): ConsoleComponent =
result = new ConsoleComponent result = new ConsoleComponent
result.agent = agent result.agent = agent
result.showConsole = true result.showConsole = true
result.inputBuffer = "" zeroMem(addr result.inputBuffer[0], MAX_INPUT_LENGTH)
result.consoleItems = new ConsoleItems result.console = new ConsoleItems
result.consoleItems.items = @[] result.console.items = @[]
result.textSelect = textselect_create(getLineAtIndex, getNumLines, cast[pointer](result.consoleItems), 0) result.textSelect = textselect_create(getLineAtIndex, getNumLines, cast[pointer](result.console), 0)
proc draw*(component: ConsoleComponent) = proc draw*(component: ConsoleComponent) =
igBegin(fmt"[{component.agent.agentId}] {component.agent.username}@{component.agent.hostname}", addr component.showConsole, 0) igBegin(fmt"[{component.agent.agentId}] {component.agent.username}@{component.agent.hostname}", addr component.showConsole, 0)
defer: igEnd() defer: igEnd()
var focusInput = false
#[ #[
Console items/text section using ImGuiTextSelect in a child window Console items/text section using ImGuiTextSelect in a child window
Supports: Supports:
@@ -61,51 +60,74 @@ proc draw*(component: ConsoleComponent) =
- https://github.com/ocornut/imgui/issues/950 - https://github.com/ocornut/imgui/issues/950
Huge thanks to @dinau for implementing ImGuiTextSelect into imguin very rapidly after I requested it. Huge thanks to @dinau for implementing ImGuiTextSelect into imguin very rapidly after I requested it.
]# ]#
let footerHeight = igGetStyle().ItemSpacing.y + igGetFrameHeightWithSpacing() * 2 let consolePadding: float = 10.0f
let footerHeight = (consolePadding * 2) + (igGetStyle().ItemSpacing.y + igGetFrameHeightWithSpacing())
let textSpacing = igGetStyle().ItemSpacing.x
if igBeginChild_Str("##Console", vec2(-1.0f, -footerHeight), ImGuiChildFlags_NavFlattened.int32, ImGuiWindowFlags_HorizontalScrollbar.int32): # Padding
igDummy(vec2(0.0f, consolePadding))
try:
# Set styles of the console window
igPushStyleColor_Vec4(ImGui_Col_FrameBg.int32, vec4(0.1f, 0.1f, 0.1f, 1.0f))
igPushStyleColor_Vec4(ImGui_Col_ScrollbarBg.int32, vec4(0.1f, 0.1f, 0.1f, 1.0f))
igPushStyleColor_Vec4(ImGui_Col_Border.int32, vec4(0.2f, 0.2f, 0.2f, 1.0f))
igPushStyleVar_Float(ImGui_StyleVar_FrameBorderSize .int32, 1.0f)
let childWindowFlags = ImGuiChildFlags_NavFlattened.int32 or ImGui_ChildFlags_Borders.int32 or ImGui_ChildFlags_AlwaysUseWindowPadding.int32 or ImGuiChildFlags_FrameStyle.int32
if igBeginChild_Str("##Console", vec2(-1.0f, -footerHeight), childWindowFlags, ImGuiWindowFlags_HorizontalScrollbar.int32):
# Display console items # Display console items
for entry in component.consoleItems.items: for entry in component.console.items:
igTextColored(vec4(0.0f, 1.0f, 1.0f, 1.0f), entry.cstring) let timestamp = entry.timestamp.format("dd-MM-yyyy HH:mm:ss")
igTextColored(vec4(0.6f, 0.6f, 0.6f, 1.0f), fmt"[{timestamp}]".cstring)
igSameLine(0.0f, textSpacing)
igTextColored(vec4(0.0f, 1.0f, 1.0f, 1.0f), $entry.itemType)
igSameLine(0.0f, textSpacing)
igTextUnformatted(entry.text.cstring, nil)
component.textSelect.textselect_update() component.textSelect.textselect_update()
# Auto-scroll to bottom if we're already at the bottom # Auto-scroll to bottom
if igGetScrollY() >= igGetScrollMaxY(): if igGetScrollY() >= igGetScrollMaxY():
igSetScrollHereY(1.0f) igSetScrollHereY(1.0f)
except IndexDefect:
# CTRL+A crashes when no items are in the console
discard
finally:
igPopStyleColor(3)
igPopStyleVar(1)
igEndChild() igEndChild()
# Buttons for testing the console # Padding
if igButton("Add Items", vec2(0.0f, 0.0f)): igDummy(vec2(0.0f, consolePadding))
for i in 1..10:
component.consoleItems.items.add("Hello world!")
igSameLine(0.0f, 5.0f)
if igButton("Add Long Items", vec2(0.0f, 0.0f)):
for i in 1..3:
component.consoleItems.items.add("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.")
igSameLine(0.0f, 5.0f)
if igButton("Clear", vec2(0.0f, 0.0f)):
component.consoleItems.items.setLen(0)
#[ #[
Input field with prompt indicator Input field with prompt indicator
]# ]#
igText(fmt"[{component.agent.agentId}]") igText(fmt"[{component.agent.agentId}]")
let spacing = igGetStyle().ItemSpacing.x igSameLine(0.0f, textSpacing)
igSameLine(0.0f, spacing)
# Calculate available width for input # Calculate available width for input
var availableWidth: ImVec2 var availableWidth: ImVec2
igGetContentRegionAvail(addr availableWidth) igGetContentRegionAvail(addr availableWidth)
igSetNextItemWidth(availableWidth.x) igSetNextItemWidth(availableWidth.x)
let inputFlags = ImGuiInputTextFlags_EnterReturnsTrue.int32 or ImGuiInputTextFlags_EscapeClearsAll.int32 or ImGuiInputTextFlags_CallbackCompletion.int32 or ImGuiInputTextFlags_CallbackHistory.int32 let inputFlags = ImGuiInputTextFlags_EnterReturnsTrue.int32 or ImGuiInputTextFlags_EscapeClearsAll.int32 # or ImGuiInputTextFlags_CallbackCompletion.int32 or ImGuiInputTextFlags_CallbackHistory.int32
if igInputText("##Input", component.inputBuffer, 256, inputFlags, nil, nil): if igInputText("##Input", addr component.inputBuffer[0], MAX_INPUT_LENGTH, inputFlags, nil, nil):
discard
let command = $(addr component.inputBuffer[0]).cstring
let commandItem = ConsoleItem(
timestamp: now(),
itemType: LOG_COMMAND,
text: command
)
component.console.items.add(commandItem)
# TODO: Handle command execution
zeroMem(addr component.inputBuffer[0], MAX_INPUT_LENGTH)
focusInput = true
#[ #[
Session information (optional footer) Session information (optional footer)
@@ -115,3 +137,5 @@ proc draw*(component: ConsoleComponent) =
# igText(sessionInfo) # igText(sessionInfo)
igSetItemDefaultFocus() igSetItemDefaultFocus()
if focusInput:
igSetKeyboardFocusHere(-1)

View File

@@ -73,10 +73,14 @@ type
# LOG_ERROR = "[ - ] " # LOG_ERROR = "[ - ] "
# LOG_SUCCESS = "[ + ] " # LOG_SUCCESS = "[ + ] "
# LOG_WARNING = "[ ! ] " # LOG_WARNING = "[ ! ] "
# LOG_COMMAND = "[ > ] "
# LOG_OUTPUT = ""
LOG_INFO = "[INFO] " LOG_INFO = "[INFO] "
LOG_ERROR = "[FAIL] " LOG_ERROR = "[FAIL] "
LOG_SUCCESS = "[DONE] " LOG_SUCCESS = "[DONE] "
LOG_WARNING = "[WARN] " LOG_WARNING = "[WARN] "
LOG_COMMAND = "[>>>>] "
LOG_OUTPUT = ""
SleepObfuscationTechnique* = enum SleepObfuscationTechnique* = enum
NONE = 0'u8 NONE = 0'u8
@@ -233,3 +237,13 @@ type
arguments*: seq[Argument] arguments*: seq[Argument]
dispatchMessage*: string dispatchMessage*: string
execute*: proc(config: AgentCtx, task: Task): TaskResult {.nimcall.} execute*: proc(config: AgentCtx, task: Task): TaskResult {.nimcall.}
# Definitions for ImGui User interface
type
ConsoleItem* = ref object
timestamp*: DateTime
itemType*: LogType
text*: string
ConsoleItems* = ref object
items*: seq[ConsoleItem]