@@ -27,6 +27,7 @@ import (
2727 "os"
2828 "path/filepath"
2929 "strings"
30+ "sync"
3031 "sync/atomic"
3132 "time"
3233
@@ -41,11 +42,20 @@ var (
4142 return fmt .Errorf ("unable to read value %s : %v" , filepath .Join (key , subkey ), err )
4243 }
4344
45+ // valueTTL specifies the maximum allowed period for RegSetValueInternal events
46+ // to remain in the queue
47+ valueTTL = time .Minute * 2
48+ // valuePurgerInterval specifies the purge interval for stale values
49+ valuePurgerInterval = time .Minute
50+
4451 // kcbCount counts the total KCBs found during the duration of the kernel session
4552 kcbCount = expvar .NewInt ("registry.kcb.count" )
4653 kcbMissCount = expvar .NewInt ("registry.kcb.misses" )
4754 keyHandleHits = expvar .NewInt ("registry.key.handle.hits" )
4855
56+ readValueOps = expvar .NewInt ("registry.read.value.ops" )
57+ capturedDataHits = expvar .NewInt ("registry.data.hits" )
58+
4959 handleThrottleCount uint32
5060)
5161
@@ -57,6 +67,13 @@ type registryProcessor struct {
5767 // keys stores the mapping between the KCB (Key Control Block) and the key name.
5868 keys map [uint64 ]string
5969 hsnap handle.Snapshotter
70+
71+ values map [uint32 ][]* event.Event
72+ mu sync.Mutex
73+
74+ purger * time.Ticker
75+
76+ quit chan struct {}
6077}
6178
6279func newRegistryProcessor (hsnap handle.Snapshotter ) Processor {
@@ -68,10 +85,18 @@ func newRegistryProcessor(hsnap handle.Snapshotter) Processor {
6885 atomic .StoreUint32 (& handleThrottleCount , 0 )
6986 }
7087 }()
71- return & registryProcessor {
72- keys : make (map [uint64 ]string ),
73- hsnap : hsnap ,
88+
89+ r := & registryProcessor {
90+ keys : make (map [uint64 ]string ),
91+ hsnap : hsnap ,
92+ values : make (map [uint32 ][]* event.Event ),
93+ purger : time .NewTicker (valuePurgerInterval ),
94+ quit : make (chan struct {}, 1 ),
7495 }
96+
97+ go r .housekeep ()
98+
99+ return r
75100}
76101
77102func (r * registryProcessor ) ProcessEvent (e * event.Event ) (* event.Event , bool , error ) {
@@ -93,6 +118,12 @@ func (r *registryProcessor) processEvent(e *event.Event) (*event.Event, error) {
93118 delete (r .keys , khandle )
94119 kcbCount .Add (- 1 )
95120 default :
121+ if e .IsRegSetValueInternal () {
122+ // store the event in temporary queue
123+ r .pushSetValue (e )
124+ return e , nil
125+ }
126+
96127 khandle := e .Params .MustGetUint64 (params .RegKeyHandle )
97128 // we have to obey a straightforward algorithm to connect relative
98129 // key names to their root keys. If key handle is equal to zero we
@@ -116,7 +147,32 @@ func (r *registryProcessor) processEvent(e *event.Event) (*event.Event, error) {
116147 }
117148 }
118149
119- // get the type/value of the registry key and append to parameters
150+ if e .IsRegSetValue () {
151+ // previously stored RegSetValueInternal event
152+ // is popped from the queue. RegSetValue can
153+ // be enriched with registry value type/data
154+ v := r .popSetValue (e )
155+ if v == nil {
156+ // try to read captured data from userspace
157+ goto readValue
158+ }
159+
160+ capturedDataHits .Add (1 )
161+
162+ // enrich the event with value data/type parameters
163+ typ , err := v .Params .GetUint32 (params .RegValueType )
164+ if err == nil {
165+ e .AppendEnum (params .RegValueType , typ , key .RegistryValueTypes )
166+ }
167+ data , err := v .Params .Get (params .RegData )
168+ if err == nil {
169+ e .AppendParam (params .RegData , data .Type , data .Value )
170+ }
171+
172+ return e , nil
173+ }
174+
175+ readValue:
120176 if ! e .IsRegSetValue () || ! e .IsSuccess () {
121177 return e , nil
122178 }
@@ -126,36 +182,42 @@ func (r *registryProcessor) processEvent(e *event.Event) (*event.Event, error) {
126182 return e , nil
127183 }
128184
185+ // get the type/value of the registry key and append to parameters
129186 rootkey , subkey := key .Format (keyName )
130- if rootkey != key .Invalid {
131- typ , val , err := rootkey .ReadValue (subkey )
132- if err != nil {
133- errno , ok := err .(windows.Errno )
134- if ok && (errno .Is (os .ErrNotExist ) || err == windows .ERROR_ACCESS_DENIED ) {
135- return e , nil
136- }
137- return e , ErrReadValue (rootkey .String (), keyName , err )
138- }
139- e .AppendEnum (params .RegValueType , typ , key .RegistryValueTypes )
140- switch typ {
141- case registry .SZ , registry .EXPAND_SZ :
142- e .AppendParam (params .RegValue , params .UnicodeString , val )
143- case registry .MULTI_SZ :
144- e .AppendParam (params .RegValue , params .Slice , val )
145- case registry .BINARY :
146- e .AppendParam (params .RegValue , params .Binary , val )
147- case registry .QWORD :
148- e .AppendParam (params .RegValue , params .Uint64 , val )
149- case registry .DWORD :
150- e .AppendParam (params .RegValue , params .Uint32 , uint32 (val .(uint64 )))
187+ if rootkey == key .Invalid {
188+ return e , nil
189+ }
190+
191+ readValueOps .Add (1 )
192+ typ , val , err := rootkey .ReadValue (subkey )
193+ if err != nil {
194+ errno , ok := err .(windows.Errno )
195+ if ok && (errno .Is (os .ErrNotExist ) || err == windows .ERROR_ACCESS_DENIED ) {
196+ return e , nil
151197 }
198+ return e , ErrReadValue (rootkey .String (), keyName , err )
199+ }
200+ e .AppendEnum (params .RegValueType , typ , key .RegistryValueTypes )
201+
202+ switch typ {
203+ case registry .SZ , registry .EXPAND_SZ :
204+ e .AppendParam (params .RegData , params .UnicodeString , val )
205+ case registry .MULTI_SZ :
206+ e .AppendParam (params .RegData , params .Slice , val )
207+ case registry .BINARY :
208+ e .AppendParam (params .RegData , params .Binary , val )
209+ case registry .QWORD :
210+ e .AppendParam (params .RegData , params .Uint64 , val )
211+ case registry .DWORD :
212+ e .AppendParam (params .RegData , params .Uint32 , uint32 (val .(uint64 )))
152213 }
153214 }
215+
154216 return e , nil
155217}
156218
157- func (registryProcessor ) Name () ProcessorType { return Registry }
158- func (registryProcessor ) Close () { }
219+ func (* registryProcessor ) Name () ProcessorType { return Registry }
220+ func (r * registryProcessor ) Close () { r . quit <- struct {}{} }
159221
160222func (r * registryProcessor ) findMatchingKey (pid uint32 , relativeKeyName string ) string {
161223 // we want to prevent too frequent queries on the process' handles
@@ -166,10 +228,12 @@ func (r *registryProcessor) findMatchingKey(pid uint32, relativeKeyName string)
166228 if atomic .LoadUint32 (& handleThrottleCount ) > maxHandleQueries {
167229 return relativeKeyName
168230 }
231+
169232 handles , err := r .hsnap .FindHandles (pid )
170233 if err != nil {
171234 return relativeKeyName
172235 }
236+
173237 for _ , h := range handles {
174238 if h .Type != handle .Key {
175239 continue
@@ -179,5 +243,71 @@ func (r *registryProcessor) findMatchingKey(pid uint32, relativeKeyName string)
179243 return h .Name
180244 }
181245 }
246+
182247 return relativeKeyName
183248}
249+
250+ // pushSetValue stores the internal RegSetValue event
251+ // into per process identifier queue.
252+ func (r * registryProcessor ) pushSetValue (e * event.Event ) {
253+ r .mu .Lock ()
254+ defer r .mu .Unlock ()
255+ vals , ok := r .values [e .PID ]
256+ if ! ok {
257+ r .values [e .PID ] = []* event.Event {e }
258+ } else {
259+ r .values [e .PID ] = append (vals , e )
260+ }
261+ }
262+
263+ // popSetValue traverses the internal RegSetValue queue
264+ // and pops the event if the suffixes match.
265+ func (r * registryProcessor ) popSetValue (e * event.Event ) * event.Event {
266+ r .mu .Lock ()
267+ defer r .mu .Unlock ()
268+ vals , ok := r .values [e .PID ]
269+ if ! ok {
270+ return nil
271+ }
272+
273+ var v * event.Event
274+ for i := len (vals ) - 1 ; i >= 0 ; i -- {
275+ val := vals [i ]
276+ if strings .HasSuffix (e .GetParamAsString (params .RegPath ), val .GetParamAsString (params .RegPath )) {
277+ v = val
278+ r .values [e .PID ] = append (vals [:i ], vals [i + 1 :]... )
279+ break
280+ }
281+ }
282+
283+ return v
284+ }
285+
286+ func (r * registryProcessor ) valuesSize (pid uint32 ) int {
287+ r .mu .Lock ()
288+ defer r .mu .Unlock ()
289+ return len (r .values [pid ])
290+ }
291+
292+ func (r * registryProcessor ) housekeep () {
293+ for {
294+ select {
295+ case <- r .purger .C :
296+ r .mu .Lock ()
297+ for pid , vals := range r .values {
298+ for i , val := range vals {
299+ if time .Since (val .Timestamp ) < valueTTL {
300+ continue
301+ }
302+ r .values [pid ] = append (vals [:i ], vals [i + 1 :]... )
303+ }
304+ if len (vals ) == 0 {
305+ delete (r .values , pid )
306+ }
307+ }
308+ r .mu .Unlock ()
309+ case <- r .quit :
310+ return
311+ }
312+ }
313+ }
0 commit comments