Skip to content

Commit bfdceb7

Browse files
committed
feat(proc): Augment process state with creation flags
The process state is enriched with three new attributes designating if the process is a 32-bit process executed in Windows 64 bit subsystem, if the process is packaged, and if the process is protected, respectively.
1 parent be05bab commit bfdceb7

File tree

6 files changed

+116
-25
lines changed

6 files changed

+116
-25
lines changed

pkg/ps/snapshotter_windows.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ func (s *snapshotter) newProcState(pid, ppid uint32, e *kevent.Kevent) (*pstypes
320320
)
321321
proc.Parent = s.procs[ppid]
322322
proc.StartTime, _ = e.Kparams.GetTime(kparams.StartTime)
323+
proc.IsWOW64 = (e.Kparams.MustGetUint32(kparams.ProcessFlags) & kevent.PsWOW64) != 0
324+
proc.IsPackaged = (e.Kparams.MustGetUint32(kparams.ProcessFlags) & kevent.PsPackaged) != 0
325+
proc.IsProtected = (e.Kparams.MustGetUint32(kparams.ProcessFlags) & kevent.PsProtected) != 0
323326

324327
if !s.capture {
325328
if proc.Username != "" {
@@ -544,6 +547,16 @@ func (s *snapshotter) Find(pid uint32) (bool, *pstypes.PS) {
544547
proc.SessionID = peb.GetSessionID()
545548
proc.Cwd = peb.GetCurrentWorkingDirectory()
546549

550+
// get process creation attributes
551+
var isWOW64 bool
552+
if err := windows.IsWow64Process(process, &isWOW64); err != nil && isWOW64 {
553+
proc.IsWOW64 = true
554+
}
555+
if p, err := sys.QueryInformationProcess[sys.PsProtection](process, sys.ProcessProtectionInformation); err != nil && p != nil {
556+
proc.IsProtected = p.IsProtected()
557+
}
558+
proc.IsPackaged = sys.IsProcessPackaged(process)
559+
547560
return false, proc
548561
}
549562

pkg/ps/snapshotter_windows_test.go

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,23 @@ func TestWrite(t *testing.T) {
6262
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
6363
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
6464
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
65+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
6566
},
6667
},
6768
&pstypes.PS{
68-
PID: uint32(os.Getpid()),
69-
Ppid: uint32(os.Getppid()),
70-
Name: "spotify.exe",
71-
Cmdline: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --type=crashpad-handler /prefetch:7 --max-uploads=5 --max-db-size=20 --max-db-age=5 --monitor-self-annotation=ptype=crashpad-handler "--metrics-dir=C:\Users\admin\AppData\Local\Spotify\User Data" --url=https://crashdump.spotify.com:443/ --annotation=platform=win32 --annotation=product=spotify --annotation=version=1.1.4.197 --initial-client-data=0x5a4,0x5a0,0x5a8,0x59c,0x5ac,0x6edcbf60,0x6edcbf70,0x6edcbf7c`,
72-
Exe: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --parent`,
73-
Cwd: "C:\\fibratus\\pkg\\ps",
74-
SessionID: 1,
75-
SID: "S-1-5-18",
76-
Username: "SYSTEM",
77-
Domain: "NT AUTHORITY",
69+
PID: uint32(os.Getpid()),
70+
Ppid: uint32(os.Getppid()),
71+
Name: "spotify.exe",
72+
Cmdline: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --type=crashpad-handler /prefetch:7 --max-uploads=5 --max-db-size=20 --max-db-age=5 --monitor-self-annotation=ptype=crashpad-handler "--metrics-dir=C:\Users\admin\AppData\Local\Spotify\User Data" --url=https://crashdump.spotify.com:443/ --annotation=platform=win32 --annotation=product=spotify --annotation=version=1.1.4.197 --initial-client-data=0x5a4,0x5a0,0x5a8,0x59c,0x5ac,0x6edcbf60,0x6edcbf70,0x6edcbf7c`,
73+
Exe: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --parent`,
74+
Cwd: "C:\\fibratus\\pkg\\ps",
75+
SessionID: 1,
76+
SID: "S-1-5-18",
77+
Username: "SYSTEM",
78+
Domain: "NT AUTHORITY",
79+
IsWOW64: true,
80+
IsPackaged: true,
81+
IsProtected: false,
7882
},
7983
},
8084
{"write state from spawned process with parent",
@@ -89,6 +93,7 @@ func TestWrite(t *testing.T) {
8993
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
9094
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
9195
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
96+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
9297
},
9398
PID: uint32(os.Getpid()),
9499
},
@@ -102,10 +107,13 @@ func TestWrite(t *testing.T) {
102107
Parent: &pstypes.PS{
103108
PID: uint32(os.Getpid()),
104109
},
105-
SessionID: 1,
106-
SID: "S-1-5-18",
107-
Username: "SYSTEM",
108-
Domain: "NT AUTHORITY",
110+
SessionID: 1,
111+
SID: "S-1-5-18",
112+
Username: "SYSTEM",
113+
Domain: "NT AUTHORITY",
114+
IsWOW64: true,
115+
IsPackaged: true,
116+
IsProtected: false,
109117
},
110118
},
111119
{"write state from rundown event",
@@ -120,19 +128,23 @@ func TestWrite(t *testing.T) {
120128
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
121129
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
122130
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
131+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
123132
},
124133
},
125134
&pstypes.PS{
126-
PID: uint32(os.Getpid()),
127-
Ppid: 8390,
128-
Name: "spotify.exe",
129-
Cmdline: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --type=crashpad-handler /prefetch:7 --max-uploads=5 --max-db-size=20 --max-db-age=5 --monitor-self-annotation=ptype=crashpad-handler "--metrics-dir=C:\Users\admin\AppData\Local\Spotify\User Data" --url=https://crashdump.spotify.com:443/ --annotation=platform=win32 --annotation=product=spotify --annotation=version=1.1.4.197 --initial-client-data=0x5a4,0x5a0,0x5a8,0x59c,0x5ac,0x6edcbf60,0x6edcbf70,0x6edcbf7c`,
130-
Exe: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --parent`,
131-
Cwd: "C:\\fibratus\\pkg\\ps",
132-
SessionID: 1,
133-
SID: "S-1-5-18",
134-
Username: "SYSTEM",
135-
Domain: "NT AUTHORITY",
135+
PID: uint32(os.Getpid()),
136+
Ppid: 8390,
137+
Name: "spotify.exe",
138+
Cmdline: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --type=crashpad-handler /prefetch:7 --max-uploads=5 --max-db-size=20 --max-db-age=5 --monitor-self-annotation=ptype=crashpad-handler "--metrics-dir=C:\Users\admin\AppData\Local\Spotify\User Data" --url=https://crashdump.spotify.com:443/ --annotation=platform=win32 --annotation=product=spotify --annotation=version=1.1.4.197 --initial-client-data=0x5a4,0x5a0,0x5a8,0x59c,0x5ac,0x6edcbf60,0x6edcbf70,0x6edcbf7c`,
139+
Exe: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --parent`,
140+
Cwd: "C:\\fibratus\\pkg\\ps",
141+
SessionID: 1,
142+
SID: "S-1-5-18",
143+
Username: "SYSTEM",
144+
Domain: "NT AUTHORITY",
145+
IsWOW64: true,
146+
IsPackaged: true,
147+
IsProtected: false,
136148
},
137149
},
138150
}
@@ -197,6 +209,7 @@ func TestRemove(t *testing.T) {
197209
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
198210
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
199211
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
212+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
200213
},
201214
},
202215
false,
@@ -236,6 +249,7 @@ func TestAddThread(t *testing.T) {
236249
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
237250
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
238251
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
252+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
239253
},
240254
}
241255
require.NoError(t, psnap.Write(evt))
@@ -316,6 +330,7 @@ func TestRemoveThread(t *testing.T) {
316330
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
317331
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
318332
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
333+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
319334
},
320335
}
321336
require.NoError(t, psnap.Write(pevt))
@@ -363,6 +378,7 @@ func TestAddModule(t *testing.T) {
363378
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
364379
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
365380
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
381+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
366382
},
367383
}
368384
require.NoError(t, psnap.Write(evt))
@@ -427,6 +443,7 @@ func TestRemoveModule(t *testing.T) {
427443
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
428444
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
429445
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
446+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
430447
},
431448
}
432449
require.NoError(t, psnap.Write(pevt))
@@ -476,6 +493,7 @@ func TestReapDeadProcesses(t *testing.T) {
476493
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
477494
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
478495
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
496+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
479497
},
480498
},
481499
{
@@ -489,6 +507,7 @@ func TestReapDeadProcesses(t *testing.T) {
489507
kparams.UserSID: {Name: kparams.UserSID, Type: kparams.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}},
490508
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
491509
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
510+
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
492511
},
493512
},
494513
}
@@ -520,8 +539,11 @@ func TestFindQueryOS(t *testing.T) {
520539
require.NotNil(t, proc)
521540

522541
assert.Equal(t, notepadPID, proc.PID)
523-
assert.Equal(t, "notepad.exe", proc.Name)
542+
assert.Equal(t, "notepad.exe", strings.ToLower(proc.Name))
524543
assert.Equal(t, uint32(os.Getpid()), proc.Ppid)
544+
assert.True(t, proc.IsPackaged)
545+
assert.False(t, proc.IsWOW64)
546+
assert.False(t, proc.IsProtected)
525547
assert.Equal(t, strings.ToLower(filepath.Join(os.Getenv("windir"), "notepad.exe")), strings.ToLower(proc.Exe))
526548
assert.Equal(t, filepath.Join(os.Getenv("windir"), "notepad.exe"), proc.Cmdline)
527549
assert.True(t, len(proc.Envs) > 0)

pkg/ps/types/types_windows.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ type PS struct {
8383
Username string `json:"username"`
8484
// Domain represents the domain under which the process is run. (e.g. NT AUTHORITY)
8585
Domain string `json:"domain"`
86+
// IsWOW64 indicates if this is 32-bit process created in 64-bit Windows system (Windows on Windows)
87+
IsWOW64 bool `json:"is_wow_64"`
88+
// IsPackaged denotes that the process is packaged with the MSIX technology and thus has
89+
// associated package identity.
90+
IsPackaged bool `json:"is_packaged"`
91+
// IsProtected denotes a protected process. The system restricts access to protected
92+
// processes and the threads of protected processes.
93+
IsProtected bool `json:"is_protected"`
8694
}
8795

8896
// UUID is meant to offer a more robust version of process ID that

pkg/sys/process.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,32 @@ const (
3131
ProcessStatusStillActive uint32 = 259
3232
)
3333

34+
// ProcessProtectionInformation is the information class that returns a
35+
// value indicating the type of protected process and the protected process
36+
// signer.
37+
const ProcessProtectionInformation = 61
38+
39+
// PsProtection describes the process protection attributes.
40+
type PsProtection struct {
41+
// S is the C union field describing protection attributes.
42+
// union {
43+
// struct {
44+
// PS_PROTECTED_TYPE Type : 3;
45+
// BOOLEAN Audit : 1;
46+
// PS_PROTECTED_SIGNER Signer : 4;
47+
// } s;
48+
// }
49+
S byte
50+
Level byte
51+
}
52+
53+
// IsProtected determines if the process has the protected flag.
54+
// The protected mask is stored in the bit field comprising the
55+
// bits 1 to 3.
56+
func (pp PsProtection) IsProtected() bool {
57+
return int((pp.S>>1)&((1<<3)-1)) != 0
58+
}
59+
3460
// QueryInformationProcess consults the specified process information class and returns
3561
// a pointer to the structure containing process information.
3662
func QueryInformationProcess[C any](proc windows.Handle, class int32) (*C, error) {
@@ -73,6 +99,18 @@ func IsProcessRunning(proc windows.Handle) bool {
7399
return exitcode == ProcessStatusStillActive
74100
}
75101

102+
// IsProcessPackaged determines if the process is packaged by trying
103+
// to resolve the package identifier.
104+
func IsProcessPackaged(proc windows.Handle) bool {
105+
var n uint32
106+
err := GetPackageID(proc, &n, 0)
107+
if err == windows.ERROR_INSUFFICIENT_BUFFER {
108+
b := make([]byte, n)
109+
err = GetPackageID(proc, &n, uintptr(unsafe.Pointer(&b[0])))
110+
}
111+
return err == nil
112+
}
113+
76114
// IsWindowsService reports whether the process is currently executing
77115
// as a Windows service.
78116
func IsWindowsService() bool {

pkg/sys/syscall.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ package sys
5656

5757
// Process Status API Functions
5858
//sys GetMappedFileName(handle windows.Handle, addr uintptr, filename *uint16, size uint32) (n uint32) = psapi.GetMappedFileNameW
59+
//sys GetPackageID(handle windows.Handle, length *uint32, buf uintptr) (err error) = kernel32.GetPackageId
5960

6061
// Debug Helper API Functions
6162
//sys SymInitialize(handle windows.Handle, searchPath *uint16, invadeProcess bool) (b bool) = dbghelp.SymInitialize

pkg/sys/zsyscall_windows.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)