|
| 1 | +# Iterators |
| 2 | + |
| 3 | +`foreach` idiom adds iteration capabilities to the rule language. Under the hood, `foreach` is implemented as a function that accepts three required and multiple optional arguments. The first argument is the `iterable` value typically yielded by the pseudo field. |
| 4 | + |
| 5 | +The function recognizes process internal state collections such as modules, threads, memory mappings, or thread stack frames. Obviously, it is also possible to iterate over simple string slices. The second argument represents the `bound variable` which is an item associated with every element in the slice. The bound variable is accessed in the third argument, the `predicate`. It is usually followed by the `segment` that denotes the accessed value. Unsurprisingly, the predicate is commonly a binary expression that can be formed of `not/paren` expressions, other functions, and so on. The predicate is executed on every item in the slice. If the predicate evaluates to true, the function also returns the true value. |
| 6 | + |
| 7 | +Lastly, foreach function can receive an optional `list of fields` from the outer context, i.e. outside predicate loop. Therefore, for the predicate to access the field not defined within the scope of the iterable, it must capture the field first. |
| 8 | + |
| 9 | +Some examples of the `foreach` usage: |
| 10 | + |
| 11 | +- Traverses process modules and return true if the module path matches the pattern |
| 12 | + |
| 13 | +``` |
| 14 | +foreach(ps._modules, $mod, $mod.path imatches '?:\\Windows\\System32\\us?r32.dll') |
| 15 | +``` |
| 16 | + |
| 17 | +- For each process ancestor, check if the ancestor is `services.exe` and the current process is protected. In this example, the `ps.is_protected` field is captured before its usage in the predicate |
| 18 | + |
| 19 | +``` |
| 20 | +foreach(ps._ancestors, $proc, $proc.name = 'services.exe' and ps.is_protected, ps.is_protected) |
| 21 | +``` |
| 22 | + |
| 23 | +## Process iterators {docsify-ignore} |
| 24 | + |
| 25 | +The `ps.ancestor` returns all ancestor names of the process generating the event. Alternatively, the filter field can accept an argument. In case of the `ps.ancestor` field, the argument indicates the ancestor level. Given the process tree below and assuming the current process generating the event is `cmd.exe`, the field with an optional level argument yields the values as follows: |
| 26 | + |
| 27 | +``` |
| 28 | +├───wininit.exe |
| 29 | +│ └───services.exe |
| 30 | +│ └───svchost.exe |
| 31 | +│ └───dllhost.exe |
| 32 | +│ ├───cmd.exe |
| 33 | +│ └───winword.exe |
| 34 | +``` |
| 35 | + |
| 36 | +- `ps.ancestor[1]` returns `dllhost.exe` |
| 37 | +- `ps.ancestor[3]` returns `services.exe` |
| 38 | +- `ps.ancestor[4]` returns `wininit.exe` |
| 39 | + |
| 40 | +If the argument is omitted, the slice with all ancestor names is returned. The `ps.ancestor` field can only yield a single process attribute - process name. To build complex conditions involving different process attribute, we can use the `foreach` construct. The bound variable associated with the `ps._ancestors` pseudo field can have the any of the segments: |
| 41 | + |
| 42 | +| Segment Name | Description | |
| 43 | +| :--- | :---- | |
| 44 | +|`pid` | Process identifier | |
| 45 | +|`name` | Process name | |
| 46 | +|`args` | Process command line arguments as a list of strings | |
| 47 | +|`cmdline` | Process command line argument as a raw string | |
| 48 | +|`cwd` | Process current working directory | |
| 49 | +|`exe` | Process image path | |
| 50 | +|`sid` | Process SID (security identifier) | |
| 51 | +|`sessionid` | Process session identifier | |
| 52 | +|`username` | User name associated with the process security context | |
| 53 | +|`domain` | Domain associated with the process security context | |
| 54 | + |
| 55 | +Examples |
| 56 | + |
| 57 | +- Check if the ancestor has one of the particular process identifiers and the pid belongs to the `services.exe` process |
| 58 | + |
| 59 | +``` |
| 60 | +foreach(ps._ancestors, $proc, $proc.pid in (2034, 343) and $proc.name = 'services.exe') |
| 61 | +``` |
| 62 | + |
| 63 | +- Check if the ancestor starts with the specific security identifier and the pid belongs to the `svchost.exe` process |
| 64 | + |
| 65 | +``` |
| 66 | +foreach(ps._ancestors, $proc, $proc.sid imatches `S-1-5*` and $proc.name = 'svchost.exe') |
| 67 | +``` |
| 68 | + |
| 69 | + |
| 70 | +### Modules {docsify-ignore} |
| 71 | + |
| 72 | +The `ps._modules` pseudo field returns the process modules iterable. Available module segments are: |
| 73 | + |
| 74 | + |
| 75 | +| Segment Name | Description | |
| 76 | +| :--- | :---- | |
| 77 | +|`address` | Base address of the process in which the module is loaded| |
| 78 | +|`checksum` | Module checksum | |
| 79 | +|`size` | Module size in terms of allocated virtual address space | |
| 80 | +|`name` | Module name | |
| 81 | +|`path` | Full module path | |
| 82 | + |
| 83 | +Examples |
| 84 | + |
| 85 | +- Check the virtual memory space size of the specific module |
| 86 | + |
| 87 | +``` |
| 88 | +foreach(ps._modules, $mod, $mod.size >= 212354 and $mod.name imatches '*winhttp.dll') |
| 89 | +``` |
| 90 | + |
| 91 | +### Threads {docsify-ignore} |
| 92 | + |
| 93 | +The `ps._threads` pseudo field yields all of the process running threads. Available thread segments are: |
| 94 | + |
| 95 | + |
| 96 | +| Segment Name | Description | |
| 97 | +| :--- | :---- | |
| 98 | +|`tid` | Thread identifier | |
| 99 | +|`start_address` | The address of the function executed by the thread | |
| 100 | +|`user_stack_base` | The base address of the thread userspace stack | |
| 101 | +|`user_stack_limit` | The address denoting the thread userspace stack limit | |
| 102 | +|`kernel_stack_base` | The base address of the thread kernel stack | |
| 103 | +|`kernel_stack_limit` | he address denoting the thread kernel stack limit | |
| 104 | + |
| 105 | +### Memory mappings {docsify-ignore} |
| 106 | + |
| 107 | +Process memory mappings (also known as sections) can be accessed via the `ps._mmaps` pseudo field. Available memory mappings segments are: |
| 108 | + |
| 109 | +| Segment Name | Description | |
| 110 | +| :--- | :---- | |
| 111 | +|`address` | Address where the section is mapped within the process address space | |
| 112 | +|`type` | The type of the memory mapping. For example, `DATA`. | |
| 113 | +|`size` | Size in bytes of the memory mapping | |
| 114 | +|`protection` | Protection attributes of the mapped memory section | |
| 115 | +|`path` | If the memory mapping is backed by a physical file, indicates the path of the file | |
| 116 | + |
| 117 | +### Environment variables {docsify-ignore} |
| 118 | + |
| 119 | +You can access process environment variables by providing the name of the environment variable. Alternatively, you can provide the prefix. |
| 120 | + |
| 121 | +``` |
| 122 | +ps.envs['MOZ_CRASHREPORTER'] = 'C:\\Program Files\\Firefox' |
| 123 | +``` |
| 124 | + |
| 125 | +Or, supplying the prefix |
| 126 | + |
| 127 | +``` |
| 128 | +ps.envs['MOZ_CRASH'] = 'C:\\Program Files\\Firefox' |
| 129 | +``` |
| 130 | + |
| 131 | +It is also possible to retrieve all environment variables as a list of colon separated key/value pairs. Example using the `foreach` idiom: |
| 132 | + |
| 133 | +``` |
| 134 | +foreach(ps.envs, $env, substr($env, 0, indexof($env, ':')) = 'OS') |
| 135 | +``` |
| 136 | + |
| 137 | +## Portable Executable iterators {docsify-ignore} |
| 138 | + |
| 139 | +[Portable Executable](/pe/introduction) introspection allows for utilizing the PE metadata in filters. See other [fields](filters/fields?id=pe) that can be used to narrow down events by PE data. |
| 140 | + |
| 141 | +### Sections {docsify-ignore} |
| 142 | + |
| 143 | +The `pe._sections` pseudo field yields all of the executable image PE sections. Available section segments are: |
| 144 | + |
| 145 | +| Segment Name | Description | |
| 146 | +| :--- | :---- | |
| 147 | +|`name` | Section name. For example, `.debug$` | |
| 148 | +|`size` | Section size in bytes | |
| 149 | +|`entropy` | Section entropy | |
| 150 | +|`md5` | Section MD5 hash | |
| 151 | + |
| 152 | +### Resources {docsify-ignore} |
| 153 | + |
| 154 | +PE [resources](/pe/resources) can be accessed by the resource name. Alternatively, it is possible to obtain all the resources as a list separated by the colon delimiter: |
| 155 | + |
| 156 | +``` |
| 157 | +pe.resources iin ('FileDescription:Notepad') |
| 158 | +``` |
| 159 | + |
| 160 | + |
| 161 | +## Callstack {docsify-ignore} |
| 162 | + |
| 163 | +[Stack enrichment](/kevents/anatomy?id=callstack) attaches call frames that can be accessed by the `thread._callstack` pseudo field. Available callstsack segments are: |
| 164 | + |
| 165 | +| Segment Name | Description | |
| 166 | +| :--- | :---- | |
| 167 | +|`address` | Symbol address | |
| 168 | +|`offset` | Symbol offset | |
| 169 | +|`symbol` | Symbol name | |
| 170 | +|`module` | Module name containing the frame | |
| 171 | +|`allocation_size` | Private allocation size | |
| 172 | +|`protection` | Frame protection mask | |
| 173 | +|`is_unbacked` | Indicates if the frame is unbacked | |
| 174 | +|`callsite_leading_assembly` | Callsite leading assembly instructions | |
| 175 | +|`callsite_trailing_assembly` | Callsite trailing assembly instructions| |
| 176 | +|`module.signature.is_signed` | Indicates if the frame module is signed | |
| 177 | +|`module.signature.is_trusted` | Indicates if the frame module signature is trusted | |
| 178 | +|`module.signature.cert.subject` | Frame module signature certificate subject | |
| 179 | +|`module.signature.cert.issuer` | Frame module signature certificate issuer | |
| 180 | + |
| 181 | +Examples: |
| 182 | + |
| 183 | +- Determine if the frame protection is RWX (Read-Write-Execute) |
| 184 | + |
| 185 | +``` |
| 186 | +foreach(thread._callstack, $frame, $frame.protection = 'RWX') |
| 187 | +``` |
| 188 | + |
| 189 | +- Determine if the frame trailing assembly contain the `syscall` instruction and the frame resides in the floating memory region |
| 190 | + |
| 191 | +``` |
| 192 | +foreach(thread._callstack, $frame, $frame.callsite_trailing_assembly matches '*mov r10, rcx|mov eax, 0x*|syscall*' and $frame.module = 'unbacked') |
| 193 | +``` |
0 commit comments