github.com/iDigitalFlame/xmt@v0.5.4/cmd/filter/filter.go (about)

     1  // Copyright (C) 2020 - 2023 iDigitalFlame
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  //
    16  
    17  // Package filter is a separate container for the 'Filter' struct that can be used
    18  // to target a specific process or one that matches an attribute set.
    19  package filter
    20  
    21  import (
    22  	"github.com/iDigitalFlame/xmt/data"
    23  	"github.com/iDigitalFlame/xmt/util/xerr"
    24  )
    25  
    26  const (
    27  	// True is the 'true' bool value.
    28  	True = boolean(2)
    29  	// False is the 'false' bool value.
    30  	False = boolean(1)
    31  	// Empty represents the absence of a value.
    32  	Empty = boolean(0)
    33  )
    34  
    35  // ErrNoProcessFound is returned by the SetParent* functions on Windows
    36  // devices when a specified parent could not be found.
    37  var ErrNoProcessFound = xerr.Sub("could not find a suitable process", 0x3E)
    38  
    39  var (
    40  	// Any will attempt to locate a parent process that may be elevated
    41  	// based on the current process permissions.
    42  	//
    43  	// This one will fall back to non-elevated if all checks fail.
    44  	Any = (&Filter{Fallback: true}).SetElevated(true)
    45  	// Random is a Filter that can be used by default to select ANY random
    46  	// process on the target device to be used as the parent process without
    47  	// creating a new Filter struct.
    48  	Random = &Filter{Fallback: false}
    49  )
    50  
    51  // Filter is a struct that can be used to set the Parent process for many types
    52  // of 'Runnable' compatible interfaces.
    53  //
    54  // Each option can be set directly or chained using the function calls which all
    55  // return the struct for chain usage.
    56  //
    57  // This struct can be serialized into JSON or written using a Stream Marshaler.
    58  type Filter struct {
    59  	// Exclude and Include determine the processes that can be included or omitted
    60  	// during process listing. 'Exclude' always takes precedence over 'Include'.
    61  	//
    62  	// Either one being nil or empty means no processes are included/excluded.
    63  	// All matches are case-insensitive.
    64  	Exclude []string
    65  	Include []string
    66  	// PID will attempt to select the PID to be used for the parent.
    67  	// If set to zero, it will be ignored. Values less than 5 are not valid!
    68  	PID uint32
    69  	// Fallback specifies if the opts routine should try again with less constraints
    70  	// than the previous attempt. All attempts will still respect the 'Exclude'
    71  	// and 'Ignore' directives.
    72  	Fallback bool
    73  	// Session can be set to 'True' or 'False' to attempt to target processes that
    74  	// are either in or not in a DWM session environment (ie: in a user desktop
    75  	// [True] or a service context [False]). This value is ignored if set to 'Empty'.
    76  	Session boolean
    77  	// Elevated can be set 'True' or 'False' to attempt to target processes that
    78  	// are in a High/System or Lower integrity context. 'True' will attempt to
    79  	// select elevated processes, while 'False' will select lower integrity or
    80  	// non-elevated processes. If set to 'Empty' or omitted, this will choose
    81  	// any process, regardless of integrity level.
    82  	Elevated boolean
    83  }
    84  type boolean uint8
    85  type filter func(uint32, bool, string, uintptr) bool
    86  
    87  // F is a shortcut for 'new(Filter)'
    88  func F() *Filter {
    89  	return new(Filter)
    90  }
    91  
    92  // B is a shortcut for '&Filter{Fallback: f}'
    93  func B(f bool) *Filter {
    94  	return &Filter{Fallback: f}
    95  }
    96  
    97  // I is a shortcut for '&Filter{Include: s}'
    98  func I(s ...string) *Filter {
    99  	return &Filter{Include: s}
   100  }
   101  
   102  // E is a shortcut for '&Filter{Exclude: s}'
   103  func E(s ...string) *Filter {
   104  	return &Filter{Exclude: s}
   105  }
   106  
   107  // Empty will return true if this Filter is nil or unset.
   108  func (f *Filter) Empty() bool {
   109  	return f == nil || f.isEmpty()
   110  }
   111  func (f Filter) isEmpty() bool {
   112  	return f.PID == 0 && f.Session == Empty && f.Elevated == Empty && len(f.Exclude) == 0 && len(f.Include) == 0
   113  }
   114  
   115  // Clear clears the Filter settings, except for 'Fallback' and returns itself.
   116  func (f *Filter) Clear() *Filter {
   117  	f.PID, f.Session, f.Elevated, f.Exclude, f.Include = 0, Empty, Empty, nil, nil
   118  	return f
   119  }
   120  
   121  // SetPID sets the target PID and returns the Filter struct.
   122  func (f *Filter) SetPID(p uint32) *Filter {
   123  	f.PID = p
   124  	return f
   125  }
   126  
   127  // SetSession sets the Session setting to 'True' or 'False' and returns itself.
   128  func (f *Filter) SetSession(s bool) *Filter {
   129  	if s {
   130  		f.Session = True
   131  	} else {
   132  		f.Session = False
   133  	}
   134  	return f
   135  }
   136  
   137  // SetElevated sets the Elevated setting to 'True' or 'False' and returns itself.
   138  func (f *Filter) SetElevated(e bool) *Filter {
   139  	if e {
   140  		f.Elevated = True
   141  	} else {
   142  		f.Elevated = False
   143  	}
   144  	return f
   145  }
   146  
   147  // SetFallback sets the Fallback setting and returns itself.
   148  func (f *Filter) SetFallback(i bool) *Filter {
   149  	f.Fallback = i
   150  	return f
   151  }
   152  
   153  // SetInclude sets the Inclusion list and returns itself.
   154  func (f *Filter) SetInclude(n ...string) *Filter {
   155  	f.Include = n
   156  	return f
   157  }
   158  
   159  // SetExclude sets the Exclusion list and returns itself.
   160  func (f *Filter) SetExclude(n ...string) *Filter {
   161  	f.Exclude = n
   162  	return f
   163  }
   164  
   165  // MarshalStream will attempt to write the Filter data to the supplied Writer
   166  // and return any errors that may occur.
   167  func (f *Filter) MarshalStream(w data.Writer) error {
   168  	if f == nil || f.isEmpty() {
   169  		return w.WriteBool(false)
   170  	}
   171  	if err := w.WriteBool(true); err != nil {
   172  		return err
   173  	}
   174  	if err := w.WriteUint32(f.PID); err != nil {
   175  		return err
   176  	}
   177  	if err := w.WriteBool(f.Fallback); err != nil {
   178  		return err
   179  	}
   180  	if err := w.WriteUint8(uint8(f.Session)); err != nil {
   181  		return err
   182  	}
   183  	if err := w.WriteUint8(uint8(f.Elevated)); err != nil {
   184  		return err
   185  	}
   186  	if err := data.WriteStringList(w, f.Exclude); err != nil {
   187  		return err
   188  	}
   189  	return data.WriteStringList(w, f.Include)
   190  }
   191  
   192  // UnmarshalStream will attempt to read the Filter data from the supplied Reader
   193  // and return any errors that may occur.
   194  func (f *Filter) UnmarshalStream(r data.Reader) error {
   195  	v, err := r.Bool()
   196  	if err != nil {
   197  		return err
   198  	}
   199  	if !v {
   200  		return nil
   201  	}
   202  	if f == nil {
   203  		f = new(Filter)
   204  	}
   205  	return f.unmarshalStream(r)
   206  }
   207  func (f *Filter) unmarshalStream(r data.Reader) error {
   208  	if err := r.ReadUint32(&f.PID); err != nil {
   209  		return err
   210  	}
   211  	if err := r.ReadBool(&f.Fallback); err != nil {
   212  		return err
   213  	}
   214  	if err := r.ReadUint8((*uint8)(&f.Session)); err != nil {
   215  		return err
   216  	}
   217  	if err := r.ReadUint8((*uint8)(&f.Elevated)); err != nil {
   218  		return err
   219  	}
   220  	if err := data.ReadStringList(r, &f.Exclude); err != nil {
   221  		return err
   222  	}
   223  	return data.ReadStringList(r, &f.Include)
   224  }
   225  
   226  // UnmarshalStream will attempt to read the Filter data from the supplied Reader
   227  // and return any errors that may occur.
   228  //
   229  // This function takes a pointer of the Filter pointer so it can fill in any
   230  // nil or empty Filters with data for a new Filter struct.
   231  func UnmarshalStream(r data.Reader, f **Filter) error {
   232  	v, err := r.Bool()
   233  	if err != nil {
   234  		return err
   235  	}
   236  	if !v {
   237  		return nil
   238  	}
   239  	if *f == nil {
   240  		*f = new(Filter)
   241  	}
   242  	return (*f).unmarshalStream(r)
   243  }