Skip to content

Commit

Permalink
feat(proc): Augment process state with creation flags
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rabbitstack committed Oct 9, 2024
1 parent be05bab commit bfdceb7
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 25 deletions.
13 changes: 13 additions & 0 deletions pkg/ps/snapshotter_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ func (s *snapshotter) newProcState(pid, ppid uint32, e *kevent.Kevent) (*pstypes
)
proc.Parent = s.procs[ppid]
proc.StartTime, _ = e.Kparams.GetTime(kparams.StartTime)
proc.IsWOW64 = (e.Kparams.MustGetUint32(kparams.ProcessFlags) & kevent.PsWOW64) != 0
proc.IsPackaged = (e.Kparams.MustGetUint32(kparams.ProcessFlags) & kevent.PsPackaged) != 0
proc.IsProtected = (e.Kparams.MustGetUint32(kparams.ProcessFlags) & kevent.PsProtected) != 0

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

// get process creation attributes
var isWOW64 bool
if err := windows.IsWow64Process(process, &isWOW64); err != nil && isWOW64 {
proc.IsWOW64 = true
}
if p, err := sys.QueryInformationProcess[sys.PsProtection](process, sys.ProcessProtectionInformation); err != nil && p != nil {
proc.IsProtected = p.IsProtected()
}
proc.IsPackaged = sys.IsProcessPackaged(process)

return false, proc
}

Expand Down
72 changes: 47 additions & 25 deletions pkg/ps/snapshotter_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,23 @@ func TestWrite(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
},
&pstypes.PS{
PID: uint32(os.Getpid()),
Ppid: uint32(os.Getppid()),
Name: "spotify.exe",
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`,
Exe: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --parent`,
Cwd: "C:\\fibratus\\pkg\\ps",
SessionID: 1,
SID: "S-1-5-18",
Username: "SYSTEM",
Domain: "NT AUTHORITY",
PID: uint32(os.Getpid()),
Ppid: uint32(os.Getppid()),
Name: "spotify.exe",
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`,
Exe: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --parent`,
Cwd: "C:\\fibratus\\pkg\\ps",
SessionID: 1,
SID: "S-1-5-18",
Username: "SYSTEM",
Domain: "NT AUTHORITY",
IsWOW64: true,
IsPackaged: true,
IsProtected: false,
},
},
{"write state from spawned process with parent",
Expand All @@ -89,6 +93,7 @@ func TestWrite(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
PID: uint32(os.Getpid()),
},
Expand All @@ -102,10 +107,13 @@ func TestWrite(t *testing.T) {
Parent: &pstypes.PS{
PID: uint32(os.Getpid()),
},
SessionID: 1,
SID: "S-1-5-18",
Username: "SYSTEM",
Domain: "NT AUTHORITY",
SessionID: 1,
SID: "S-1-5-18",
Username: "SYSTEM",
Domain: "NT AUTHORITY",
IsWOW64: true,
IsPackaged: true,
IsProtected: false,
},
},
{"write state from rundown event",
Expand All @@ -120,19 +128,23 @@ func TestWrite(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
},
&pstypes.PS{
PID: uint32(os.Getpid()),
Ppid: 8390,
Name: "spotify.exe",
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`,
Exe: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --parent`,
Cwd: "C:\\fibratus\\pkg\\ps",
SessionID: 1,
SID: "S-1-5-18",
Username: "SYSTEM",
Domain: "NT AUTHORITY",
PID: uint32(os.Getpid()),
Ppid: 8390,
Name: "spotify.exe",
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`,
Exe: `C:\Users\admin\AppData\Roaming\Spotify\Spotify.exe --parent`,
Cwd: "C:\\fibratus\\pkg\\ps",
SessionID: 1,
SID: "S-1-5-18",
Username: "SYSTEM",
Domain: "NT AUTHORITY",
IsWOW64: true,
IsPackaged: true,
IsProtected: false,
},
},
}
Expand Down Expand Up @@ -197,6 +209,7 @@ func TestRemove(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
},
false,
Expand Down Expand Up @@ -236,6 +249,7 @@ func TestAddThread(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
}
require.NoError(t, psnap.Write(evt))
Expand Down Expand Up @@ -316,6 +330,7 @@ func TestRemoveThread(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
}
require.NoError(t, psnap.Write(pevt))
Expand Down Expand Up @@ -363,6 +378,7 @@ func TestAddModule(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
}
require.NoError(t, psnap.Write(evt))
Expand Down Expand Up @@ -427,6 +443,7 @@ func TestRemoveModule(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
}
require.NoError(t, psnap.Write(pevt))
Expand Down Expand Up @@ -476,6 +493,7 @@ func TestReapDeadProcesses(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
},
{
Expand All @@ -489,6 +507,7 @@ func TestReapDeadProcesses(t *testing.T) {
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}},
kparams.StartTime: {Name: kparams.StartTime, Type: kparams.Time, Value: time.Now()},
kparams.SessionID: {Name: kparams.SessionID, Type: kparams.Uint32, Value: uint32(1)},
kparams.ProcessFlags: {Name: kparams.ProcessFlags, Type: kparams.Flags, Value: uint32(0x00000010)},
},
},
}
Expand Down Expand Up @@ -520,8 +539,11 @@ func TestFindQueryOS(t *testing.T) {
require.NotNil(t, proc)

assert.Equal(t, notepadPID, proc.PID)
assert.Equal(t, "notepad.exe", proc.Name)
assert.Equal(t, "notepad.exe", strings.ToLower(proc.Name))
assert.Equal(t, uint32(os.Getpid()), proc.Ppid)
assert.True(t, proc.IsPackaged)
assert.False(t, proc.IsWOW64)
assert.False(t, proc.IsProtected)
assert.Equal(t, strings.ToLower(filepath.Join(os.Getenv("windir"), "notepad.exe")), strings.ToLower(proc.Exe))
assert.Equal(t, filepath.Join(os.Getenv("windir"), "notepad.exe"), proc.Cmdline)
assert.True(t, len(proc.Envs) > 0)
Expand Down
8 changes: 8 additions & 0 deletions pkg/ps/types/types_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ type PS struct {
Username string `json:"username"`
// Domain represents the domain under which the process is run. (e.g. NT AUTHORITY)
Domain string `json:"domain"`
// IsWOW64 indicates if this is 32-bit process created in 64-bit Windows system (Windows on Windows)
IsWOW64 bool `json:"is_wow_64"`
// IsPackaged denotes that the process is packaged with the MSIX technology and thus has
// associated package identity.
IsPackaged bool `json:"is_packaged"`
// IsProtected denotes a protected process. The system restricts access to protected
// processes and the threads of protected processes.
IsProtected bool `json:"is_protected"`
}

// UUID is meant to offer a more robust version of process ID that
Expand Down
38 changes: 38 additions & 0 deletions pkg/sys/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,32 @@ const (
ProcessStatusStillActive uint32 = 259
)

// ProcessProtectionInformation is the information class that returns a
// value indicating the type of protected process and the protected process
// signer.
const ProcessProtectionInformation = 61

// PsProtection describes the process protection attributes.
type PsProtection struct {
// S is the C union field describing protection attributes.
// union {
// struct {
// PS_PROTECTED_TYPE Type : 3;
// BOOLEAN Audit : 1;
// PS_PROTECTED_SIGNER Signer : 4;
// } s;
// }
S byte
Level byte
}

// IsProtected determines if the process has the protected flag.
// The protected mask is stored in the bit field comprising the
// bits 1 to 3.
func (pp PsProtection) IsProtected() bool {
return int((pp.S>>1)&((1<<3)-1)) != 0
}

// QueryInformationProcess consults the specified process information class and returns
// a pointer to the structure containing process information.
func QueryInformationProcess[C any](proc windows.Handle, class int32) (*C, error) {
Expand Down Expand Up @@ -73,6 +99,18 @@ func IsProcessRunning(proc windows.Handle) bool {
return exitcode == ProcessStatusStillActive
}

// IsProcessPackaged determines if the process is packaged by trying
// to resolve the package identifier.
func IsProcessPackaged(proc windows.Handle) bool {
var n uint32
err := GetPackageID(proc, &n, 0)
if err == windows.ERROR_INSUFFICIENT_BUFFER {
b := make([]byte, n)
err = GetPackageID(proc, &n, uintptr(unsafe.Pointer(&b[0])))
}
return err == nil
}

// IsWindowsService reports whether the process is currently executing
// as a Windows service.
func IsWindowsService() bool {
Expand Down
1 change: 1 addition & 0 deletions pkg/sys/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ package sys

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

// Debug Helper API Functions
//sys SymInitialize(handle windows.Handle, searchPath *uint16, invadeProcess bool) (b bool) = dbghelp.SymInitialize
Expand Down
9 changes: 9 additions & 0 deletions pkg/sys/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit bfdceb7

Please sign in to comment.