Implemented initial version of logging system. Log formatting and content needs to be reworked.

This commit is contained in:
Jakob Friedl
2025-08-20 12:55:09 +02:00
parent 24208f3b4b
commit f69adc53a2
10 changed files with 139 additions and 74 deletions

3
.gitignore vendored
View File

@@ -8,6 +8,9 @@ bin/*
!bin/.gitkeep
*.exe
# Ignore log files
*.log
.vscode/
# Nim

View File

@@ -165,7 +165,7 @@ type
port*: int
protocol*: Protocol
# Server context structures
# Context structures
type
KeyPair* = object
privateKey*: Key

View File

@@ -1,5 +1,6 @@
import terminal, strformat, strutils, sequtils, tables, times, system
import ../core/logger
import ../[utils, globals]
import ../db/database
import ../protocol/packer
@@ -26,6 +27,10 @@ proc register*(registrationData: seq[byte]): bool =
cq.writeLine(fgRed, styleBright, fmt"[-] Failed to insert agent {agent.agentId} into database.", "\n")
return false
if not cq.makeAgentLogDirectory(agent.agentId):
cq.writeLine(fgRed, styleBright, "[-] Failed to create log")
return false
cq.agents[agent.agentId] = agent
let date = agent.firstCheckin.format("dd-MM-yyyy HH:mm:ss")
@@ -77,23 +82,23 @@ proc handleResult*(resultData: seq[byte]) =
listenerId = Uuid.toString(taskResult.listenerId)
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"{$resultData.len} bytes received.")
cq.info(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"{$resultData.len} bytes received.")
case cast[StatusType](taskResult.status):
of STATUS_COMPLETED:
cq.writeLine(fgBlack, styleBright, fmt"[{date}]", fgGreen, " [+] ", resetStyle, fmt"Task {taskId} completed.")
cq.success(fgBlack, styleBright, fmt"[{date}]", fgGreen, " [+] ", resetStyle, fmt"Task {taskId} completed.")
of STATUS_FAILED:
cq.writeLine(fgBlack, styleBright, fmt"[{date}]", fgRed, styleBright, " [-] ", resetStyle, fmt"Task {taskId} failed.")
cq.error(fgBlack, styleBright, fmt"[{date}]", fgRed, styleBright, " [-] ", resetStyle, fmt"Task {taskId} failed.")
of STATUS_IN_PROGRESS:
discard
case cast[ResultType](taskResult.resultType):
of RESULT_STRING:
if int(taskResult.length) > 0:
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, "Output:")
cq.info(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, "Output:")
# Split result string on newline to keep formatting
for line in Bytes.toString(taskResult.data).split("\n"):
cq.writeLine(line)
cq.output(line)
of RESULT_BINARY:
# Write binary data to a file

View File

@@ -1,6 +1,6 @@
import terminal, strformat, strutils, tables, times, system, parsetoml
import ./task
import ./[task, logger]
import ../utils
import ../db/database
import ../../common/types
@@ -119,6 +119,8 @@ proc agentInteract*(cq: Conquest, name: string) =
cq.writeLine(fgYellow, styleBright, "[+] ", resetStyle, fmt"Started interacting with agent ", fgYellow, styleBright, agent.agentId, resetStyle, ". Type 'help' to list available commands.\n")
cq.interactAgent = agent
cq.log(fmt"Started interacting with agent {agent.agentId}.")
while command.replace(" ", "") != "back":
command = cq.readLine()
cq.withOutput(handleAgentCommand, command)

View File

@@ -40,9 +40,9 @@ proc listenerList*(cq: Conquest) =
proc listenerStart*(cq: Conquest, host: string, portStr: string) =
# Validate arguments
try:
if not validatePort(portStr):
cq.writeLine(fgRed, styleBright, fmt"[-] Invalid port number: {portStr}")
return
raise newException(CatchableError,fmt"[-] Invalid port number: {portStr}")
let port = portStr.parseInt
@@ -85,13 +85,13 @@ proc listenerStart*(cq: Conquest, host: string, portStr: string) =
protocol: HTTP
)
if not cq.dbStoreListener(listenerInstance):
return
raise newException(CatchableError, "Failed to store listener in database.")
# Start serving
try:
discard listener.runAsync()
cq.add(listenerInstance)
cq.writeLine(fgGreen, "[+] ", resetStyle, "Started listener", fgGreen, fmt" {name} ", resetStyle, fmt"on {host}:{portStr}.")
except CatchableError as err:
cq.writeLine(fgRed, styleBright, "[-] Failed to start listener: ", err.msg)

View File

@@ -0,0 +1,57 @@
import terminal, times, strformat, strutils
import std/[dirs, paths]
import ../../common/[types, profile]
proc makeAgentLogDirectory*(cq: Conquest, agentId: string): bool =
try:
let cqDir = cq.profile.getString("conquest_directory")
createDir(cast[Path](fmt"{cqDir}/data/logs/{agentId}"))
return true
except OSError:
return false
proc log*(cq: Conquest, logEntry: string) =
if cq.interactAgent == nil:
return
let
date = now().format("dd-MM-yyyy")
timestamp = now().format("dd-MM-yyyy HH:mm:ss")
cqDir = cq.profile.getString("conquest_directory")
agentLogPath = fmt"{cqDir}/data/logs/{cq.interactAgent.agentId}/{date}.log"
# Write log entry to file
let file = open(agentLogPath, fmAppend)
file.writeLine(fmt"[{timestamp}] {logEntry}")
file.flushFile()
proc extractStrings(args: string): string =
if not args.startsWith("("):
return args
# Remove styling arguments, such as fgRed, styleBright, resetStyle, etc. by extracting only arguments that are quoted
var message: string
for str in args[1..^2].split(", "):
if str.startsWith("\""):
message &= str
return message.replace("\"", "")
template info*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, args)
cq.log("[*] " & extractStrings($(args)))
template error*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(fgRed, styleBright, "[-] ", resetStyle, args)
cq.log("[-] " & extractStrings($(args)))
template warn*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(fgYellow, "[!] ", resetStyle, args)
cq.log("[!] " & extractStrings($(args)))
template success*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(fgGreen, "[+] ", resetStyle, args)
cq.log("[+] " & extractStrings($(args)))
template output*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(args)
cq.log("[>] " & extractStrings($(args)))

View File

@@ -1,7 +1,7 @@
import prompt, terminal, argparse, parsetoml
import strutils, strformat, times, system, tables
import ./[agent, listener, builder]
import ./[agent, listener, builder, logger]
import ../[globals, utils]
import ../db/database
import ../../common/[types, utils, crypto, profile]
@@ -150,8 +150,8 @@ proc startServer*(profilePath: string) =
try:
# Load and parse profile
let profile = parseFile(profilePath)
styledEcho(fgGreen, styleBright, "[+] Using profile \"", profile.getString("name"), "\" (", profilePath ,").")
styledEcho(fgGreen, styleBright, "[+] ", profile.getString("private_key_file"), ": Private key found.")
styledEcho(fgBlack, styleBright, "[*] ", "Using profile \"", profile.getString("name"), "\" (", profilePath ,").")
styledEcho(fgBlack, styleBright, "[*] ", "Using private key \"", profile.getString("private_key_file"), "\".")
# Initialize framework context
cq = Conquest.init(profile)

View File

@@ -1,5 +1,6 @@
import times, strformat, terminal, tables, sequtils, strutils
import ./logger
import ../utils
import ../protocol/parser
import ../../modules/manager
@@ -56,6 +57,7 @@ proc handleAgentCommand*(cq: Conquest, input: string) =
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
cq.writeLine(fgBlue, styleBright, fmt"[{date}] ", fgYellow, fmt"[{cq.interactAgent.agentId}] ", resetStyle, styleBright, input)
cq.log(fmt"Agent command received: {input}")
# Convert user input into sequence of string arguments
let parsedArgs = parseInput(input)
@@ -78,8 +80,8 @@ proc handleAgentCommand*(cq: Conquest, input: string) =
# Add task to queue
cq.interactAgent.tasks.add(task)
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"Tasked agent to {command.description.toLowerAscii()}")
cq.info(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"Tasked agent to {command.description.toLowerAscii()}")
except CatchableError:
cq.writeLine(fgRed, styleBright, fmt"[-] {getCurrentExceptionMsg()}" & "\n")
cq.error(getCurrentExceptionMsg() & "\n")
return

View File

@@ -41,7 +41,7 @@ proc dbInit*(cq: Conquest) =
""")
cq.writeLine(fgGreen, styleBright, "[+] ", cq.dbPath, ": Database created.")
cq.writeLine(fgBlack, styleBright, "[*] Using new database: \"", cq.dbPath, "\".\n")
conquestDb.close()
except SqliteError as err:
cq.writeLine(fgGreen, styleBright, "[+] ", cq.dbPath, ": Database file found.")
cq.writeLine(fgBlack, styleBright, "[*] Using existing database: \"", cq.dbPath, "\".\n")

View File

@@ -2,12 +2,7 @@ import strutils, terminal, tables, sequtils, times, strformat, prompt
import std/wordwrap
import ../common/types
# Utility functions
proc parseOctets*(ip: string): tuple[first, second, third, fourth: int] =
# TODO: Verify that address is in correct, expected format
let octets = ip.split('.')
return (parseInt(octets[0]), parseInt(octets[1]), parseInt(octets[2]), parseInt(octets[3]))
import core/logger
proc validatePort*(portStr: string): bool =
try:
@@ -19,17 +14,18 @@ proc validatePort*(portStr: string): bool =
# Function templates and overwrites
template writeLine*(cq: Conquest, args: varargs[untyped]) =
cq.prompt.writeLine(args)
proc readLine*(cq: Conquest): string =
return cq.prompt.readLine()
template setIndicator*(cq: Conquest, indicator: string) =
proc setIndicator*(cq: Conquest, indicator: string) =
cq.prompt.setIndicator(indicator)
template showPrompt*(cq: Conquest) =
proc showPrompt*(cq: Conquest) =
cq.prompt.showPrompt()
template hidePrompt*(cq: Conquest) =
proc hidePrompt*(cq: Conquest) =
cq.prompt.hidePrompt()
template setStatusBar*(cq: Conquest, statusBar: seq[StatusBarItem]) =
proc setStatusBar*(cq: Conquest, statusBar: seq[StatusBarItem]) =
cq.prompt.setStatusBar(statusBar)
template clear*(cq: Conquest) =
proc clear*(cq: Conquest) =
cq.prompt.clear()
# Overwrite withOutput function to handle function arguments