Mac: Consolidate methods of listing processes

This also updates all other plugins that rely on process listing
and theoretically allows them to choose their preferred method of
process listing.  At the moment, the default (first in the method list)
is chosen.  An optional pslist_method StringRequirement can be added to
each plugin, but using the list in the requirements could break if the
pslist plugin is too old (ie, using the list would happen before the
PluginRequirement gets checked).

If this is a feature we want, it should be easy to add to all but
netstat, which does not parameterize the list of processes.
This commit is contained in:
Mike Auty
2020-07-18 16:38:49 +01:00
committed by ikelos
parent 0ccb1f82e6
commit 7673dd8d2d
10 changed files with 214 additions and 210 deletions
+12 -14
View File
@@ -12,10 +12,9 @@ from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.layers import scanners
from volatility.framework.objects import utility
from volatility.plugins import timeliner
from volatility.plugins.mac import tasks
from volatility.framework.symbols.linux.bash import BashIntermedSymbols
from volatility.plugins import timeliner
from volatility.plugins.mac import pslist
class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
@@ -28,7 +27,7 @@ class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0))
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0))
]
def _generator(self, tasks):
@@ -84,24 +83,23 @@ class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
yield (0, (int(task.p_pid), task_name, hist.get_time_object(), hist.get_command()))
def run(self):
filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)])
filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)])
list_tasks = pslist.PsList.get_list_tasks(self.config.get('method', 'tasks'))
return renderers.TreeGrid([("PID", int), ("Process", str), ("CommandTime", datetime.datetime),
("Command", str)],
self._generator(
tasks.Tasks.list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
def generate_timeline(self):
filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)])
filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)])
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
for row in self._generator(
tasks.Tasks.list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)):
list_tasks(self.context, self.config['primary'], self.config['darwin'], filter_func = filter_func)):
_depth, row_data = row
description = "{} ({}): \"{}\"".format(row_data[0], row_data[1], row_data[3])
yield (description, timeliner.TimeLinerType.CREATED, row_data[2])
+8 -7
View File
@@ -8,7 +8,7 @@ from volatility.framework import renderers
from volatility.framework.automagic import mac
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.plugins.mac import tasks
from volatility.plugins.mac import pslist
vollog = logging.getLogger(__name__)
@@ -23,7 +23,7 @@ class Lsof(plugins.PluginInterface):
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Mac Kernel"),
requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0))
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0))
]
def _generator(self, tasks):
@@ -36,11 +36,12 @@ class Lsof(plugins.PluginInterface):
yield (0, (pid, fd, filepath))
def run(self):
filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)])
filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)])
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
return renderers.TreeGrid([("PID", int), ("File Descriptor", int), ("File Path", str)],
self._generator(
tasks.Tasks.list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
+8 -7
View File
@@ -8,7 +8,7 @@ from volatility.framework import renderers
from volatility.framework.configuration import requirements
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
from volatility.plugins.mac import tasks
from volatility.plugins.mac import pslist
class Malfind(interfaces.plugins.PluginInterface):
@@ -21,7 +21,7 @@ class Malfind(interfaces.plugins.PluginInterface):
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Linux kernel symbols"),
requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0)),
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
]
def _list_injections(self, task):
@@ -61,13 +61,14 @@ class Malfind(interfaces.plugins.PluginInterface):
vma.get_perms(), format_hints.HexBytes(data), disasm))
def run(self):
filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)])
filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)])
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
return renderers.TreeGrid([("PID", int), ("Process", str), ("Start", format_hints.Hex),
("End", format_hints.Hex), ("Protection", str), ("Hexdump", format_hints.HexBytes),
("Disasm", interfaces.renderers.Disassembly)],
self._generator(
tasks.Tasks.list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
+6 -4
View File
@@ -11,7 +11,7 @@ from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
from volatility.plugins.mac import tasks
from volatility.plugins.mac import pslist
vollog = logging.getLogger(__name__)
@@ -26,7 +26,7 @@ class Netstat(plugins.PluginInterface):
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Mac Kernel"),
requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0))
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0))
]
@classmethod
@@ -45,7 +45,9 @@ class Netstat(plugins.PluginInterface):
2) The process ID of the processed that opened the socket
3) The address of the associated socket structure
"""
for task in tasks.Tasks.list_tasks(context, layer_name, darwin_symbols, filter_func):
# This is hardcoded, since a change in method
list_tasks = pslist.PsList.get_list_tasks(pslist.PsList.pslist_methods[0])
for task in list_tasks(context, layer_name, darwin_symbols, filter_func):
task_name = utility.array_to_string(task.p_comm)
pid = task.p_pid
@@ -67,7 +69,7 @@ class Netstat(plugins.PluginInterface):
yield task_name, pid, socket
def _generator(self):
filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)])
filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)])
for task_name, pid, socket in self.list_sockets(self.context,
self.config['primary'],
@@ -6,7 +6,7 @@ from volatility.framework import renderers, interfaces
from volatility.framework.configuration import requirements
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
from volatility.plugins.mac import tasks
from volatility.plugins.mac import pslist
class Maps(interfaces.plugins.PluginInterface):
@@ -19,7 +19,7 @@ class Maps(interfaces.plugins.PluginInterface):
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Linux kernel symbols"),
requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0))
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0))
]
def _generator(self, tasks):
@@ -36,12 +36,13 @@ class Maps(interfaces.plugins.PluginInterface):
format_hints.Hex(vma.links.end), vma.get_perms(), path))
def run(self):
filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)])
filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)])
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
return renderers.TreeGrid([("PID", int), ("Process", str), ("Start", format_hints.Hex),
("End", format_hints.Hex), ("Protection", str), ("Map Name", str)],
self._generator(
tasks.Tasks.list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
+8 -7
View File
@@ -8,7 +8,7 @@ from volatility.framework import exceptions, renderers, interfaces
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.plugins.mac import tasks
from volatility.plugins.mac import pslist
class Psaux(plugins.PluginInterface):
@@ -21,7 +21,7 @@ class Psaux(plugins.PluginInterface):
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0))
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0))
]
def _generator(self, tasks: Iterator[Any]) -> Generator[Tuple[int, Tuple[int, str, int, str]], None, None]:
@@ -86,11 +86,12 @@ class Psaux(plugins.PluginInterface):
yield (0, (task.p_pid, task_name, task.p_argc, args_str))
def run(self) -> renderers.TreeGrid:
filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)])
filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)])
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
return renderers.TreeGrid([("PID", int), ("Process", str), ("Argc", int), ("Arguments", str)],
self._generator(
tasks.Tasks.list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func)))
+158 -15
View File
@@ -6,7 +6,6 @@ import logging
from typing import Callable, Iterable, List, Dict
from volatility.framework import renderers, interfaces, contexts, exceptions
from volatility.framework.automagic import mac
from volatility.framework.configuration import requirements
from volatility.framework.objects import utility
@@ -16,7 +15,8 @@ vollog = logging.getLogger(__name__)
class PsList(interfaces.plugins.PluginInterface):
"""Lists the processes present in a particular mac memory image."""
_version = (1, 0, 0)
_version = (2, 0, 0)
pslist_methods = ['tasks', 'allproc', 'process_group']
@classmethod
def get_requirements(cls):
@@ -24,9 +24,43 @@ class PsList(interfaces.plugins.PluginInterface):
requirements.TranslationLayerRequirement(name = 'primary',
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols")
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
requirements.ChoiceRequirement(name = 'pslist_method',
description = 'Method to determine for processes',
choices = cls.pslist_methods,
default = cls.pslist_methods[0],
optional = True)
]
@classmethod
def get_list_tasks(
cls, method: str
) -> Callable[[interfaces.context.ContextInterface, str, str, Callable[[int], bool]],
Iterable[interfaces.objects.ObjectInterface]]:
"""Returns the list_tasks method based on the selector
Args:
method: Must be one fo the available methods in get_task_choices
Returns:
list_tasks method for listing tasks
"""
# Ensure method is one of the suitable choices
if method not in cls.pslist_methods:
method = cls.pslist_methods[0]
if method == 'allproc':
list_tasks = cls.list_tasks_allproc
elif method == 'tasks':
list_tasks = cls.list_tasks_tasks
elif method == 'process_group':
list_tasks = cls.list_tasks_process_group
else:
raise ValueError("Impossible method choice chosen")
vollog.debug("Using method {}".format(method))
return list_tasks
@classmethod
def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[int], bool]:
@@ -43,23 +77,25 @@ class PsList(interfaces.plugins.PluginInterface):
return filter_func
def _generator(self):
for task in self.list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = self.create_pid_filter([self.config.get('pid', None)])):
list_tasks = self.get_list_tasks(self.config.get('method', self.pslist_methods[0]))
for task in list_tasks(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = self.create_pid_filter([self.config.get('pid', None)])):
pid = task.p_pid
ppid = task.p_ppid
name = utility.array_to_string(task.p_comm)
yield (0, (pid, ppid, name))
@classmethod
def list_tasks(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
darwin_symbols: str,
filter_func: Callable[[int], bool] = lambda _: False) -> \
def list_tasks_allproc(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
darwin_symbols: str,
filter_func: Callable[[int], bool] = lambda _: False) -> \
Iterable[interfaces.objects.ObjectInterface]:
"""Lists all the processes in the primary layer.
"""Lists all the processes in the primary layer based on the allproc method
Args:
context: The context to retrieve required elements (layers, symbol tables) from
@@ -73,7 +109,7 @@ class PsList(interfaces.plugins.PluginInterface):
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
kernel_as = context.layers[layer_name]
kernel_layer = context.layers[layer_name]
proc = kernel.object_from_symbol(symbol_name = "allproc").lh_first
@@ -85,7 +121,7 @@ class PsList(interfaces.plugins.PluginInterface):
else:
seen[proc.vol.offset] = 1
if not filter_func(proc) and kernel_as.is_valid(proc.vol.offset, proc.vol.size):
if not filter_func(proc) and kernel_layer.is_valid(proc.vol.offset, proc.vol.size):
yield proc
try:
@@ -93,5 +129,112 @@ class PsList(interfaces.plugins.PluginInterface):
except exceptions.InvalidAddressException:
break
@classmethod
def list_tasks_tasks(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
darwin_symbols: str,
filter_func: Callable[[int], bool] = lambda _: False) -> \
Iterable[interfaces.objects.ObjectInterface]:
"""Lists all the tasks in the primary layer based on the tasks queue
Args:
context: The context to retrieve required elements (layers, symbol tables) from
layer_name: The name of the layer on which to operate
darwin_symbols: The name of the table containing the kernel symbols
filter_func: A function which takes a task object and returns True if the task should be ignored/filtered
Returns:
The list of task objects from the `layer_name` layer's `tasks` list after filtering
"""
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
kernel_layer = context.layers[layer_name]
queue_entry = kernel.object_from_symbol(symbol_name = "tasks")
seen = {} # type: Dict[int, int]
for task in queue_entry.walk_list(queue_entry, "tasks", "task"):
if task.vol.offset in seen:
vollog.log(logging.INFO, "Recursive process list detected (a result of non-atomic acquisition).")
break
else:
seen[task.vol.offset] = 1
try:
proc = task.bsd_info.dereference().cast("proc")
except exceptions.PagedInvalidAddressException:
continue
if kernel_layer.is_valid(proc.vol.offset, proc.vol.size) and not filter_func(proc):
yield proc
@classmethod
def list_tasks_process_group(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
darwin_symbols: str,
filter_func: Callable[[int], bool] = lambda _: False) -> \
Iterable[interfaces.objects.ObjectInterface]:
"""Lists all the tasks in the primary layer using process groups
Args:
context: The context to retrieve required elements (layers, symbol tables) from
layer_name: The name of the layer on which to operate
darwin_symbols: The name of the table containing the kernel symbols
filter_func: A function which takes a task object and returns True if the task should be ignored/filtered
Returns:
The list of task objects from the `layer_name` layer's `tasks` list after filtering
"""
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
table_size = kernel.object_from_symbol(symbol_name = "pgrphash")
pgrphashtbl = kernel.object_from_symbol(symbol_name = "pgrphashtbl")
proc_array = kernel.object(object_type = "array",
offset = pgrphashtbl,
count = table_size + 1,
subtype = kernel.get_type("pgrphashhead"))
for proc_list in proc_array:
# test the validity of the current element
# it is expected that many won't be initialized
try:
pgrp = proc_list.lh_first
except exceptions.PagedInvalidAddressException:
continue
seen_pgrps = set()
# this walks the particular process group
while pgrp and pgrp.vol.offset not in seen_pgrps:
seen_pgrps.add(pgrp.vol.offset)
# nothing can be done if this list pointer is invalid, so move on
try:
p = pgrp.pg_members.lh_first
except exceptions.PagedInvalidAddressException:
break
seen_pg = set()
while p and p.vol.offset not in seen_pg:
seen_pg.add(p.vol.offset)
if p.is_readable() and not filter_func(p):
yield p
try:
p = p.p_pglist.le_next
except exceptions.PagedInvalidAddressException:
break
try:
pgrp = pgrp.pg_hash.le_next
except exceptions.PagedInvalidAddressException:
break
def run(self):
return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str)], self._generator())
@@ -1,89 +0,0 @@
# This file is Copyright 2020 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import logging
from typing import Callable, Dict, Iterable
from volatility.framework import constants, interfaces, objects, contexts, exceptions
from volatility.plugins.mac import pslist
vollog = logging.getLogger(__name__)
class Pslist_Process_Groups(pslist.PsList):
"""Lists the processes present in a particular mac memory image by enumerating the process group hash table."""
@classmethod
def list_tasks(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
darwin_symbols: str,
filter_func: Callable[[int], bool] = lambda _: False) -> \
Iterable[interfaces.objects.ObjectInterface]:
"""Lists all the tasks in the primary layer.
Args:
context: The context to retrieve required elements (layers, symbol tables) from
layer_name: The name of the layer on which to operate
darwin_symbols: The name of the table containing the kernel symbols
filter_func: A function which takes a task object and returns True if the task should be ignored/filtered
Returns:
The list of task objects from the `layer_name` layer's `tasks` list after filtering
"""
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
kernel_as = context.layers[layer_name]
table_size = kernel.object_from_symbol(symbol_name = "pgrphash")
pgrphashtbl = kernel.object_from_symbol(symbol_name = "pgrphashtbl")
proc_array = kernel.object(object_type = "array",
offset = pgrphashtbl,
count = table_size + 1,
subtype = kernel.get_type("pgrphashhead"))
for proc_list in proc_array:
# test the validity of the current element
# it is expected that many won't be initialized
try:
pgrp = proc_list.lh_first
except exceptions.PagedInvalidAddressException:
continue
seen_pgrps = set()
# this walks the particular process group
while pgrp and pgrp.vol.offset not in seen_pgrps:
seen_pgrps.add(pgrp.vol.offset)
# nothing can be done if this list pointer is invalid, so move on
try:
p = pgrp.pg_members.lh_first
except exceptions.PagedInvalidAddressException:
break
seen_pg = set()
while p and p.vol.offset not in seen_pg:
seen_pg.add(p.vol.offset)
if p.is_readable():
yield p
try:
p = p.p_pglist.le_next
except exceptions.PagedInvalidAddressException:
break
try:
pgrp = pgrp.pg_hash.le_next
except exceptions.PagedInvalidAddressException:
break
+6 -4
View File
@@ -6,7 +6,7 @@ from volatility.framework import renderers
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.plugins.mac import tasks
from volatility.plugins.mac import pslist
class PsTree(plugins.PluginInterface):
@@ -26,7 +26,7 @@ class PsTree(plugins.PluginInterface):
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0))
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0))
]
def _find_level(self, pid):
@@ -45,8 +45,10 @@ class PsTree(plugins.PluginInterface):
self._levels[pid] = level
def _generator(self):
"""Generates the."""
for proc in tasks.Tasks.list_tasks(self.context, self.config['primary'], self.config['darwin']):
"""Generates the tree list of processes"""
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
for proc in list_tasks(self.context, self.config['primary'], self.config['darwin']):
self._processes[proc.p_pid] = proc
# Build the child/level maps
-56
View File
@@ -1,56 +0,0 @@
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import logging
from typing import Callable, Dict, Iterable
from volatility.framework import interfaces, contexts, exceptions
from volatility.plugins.mac import pslist
vollog = logging.getLogger(__name__)
class Tasks(pslist.PsList):
"""Lists the processes present in a particular mac memory image."""
@classmethod
def list_tasks(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
darwin_symbols: str,
filter_func: Callable[[int], bool] = lambda _: False) -> \
Iterable[interfaces.objects.ObjectInterface]:
"""Lists all the tasks in the primary layer.
Args:
context: The context to retrieve required elements (layers, symbol tables) from
layer_name: The name of the layer on which to operate
darwin_symbols: The name of the table containing the kernel symbols
filter_func: A function which takes a task object and returns True if the task should be ignored/filtered
Returns:
The list of task objects from the `layer_name` layer's `tasks` list after filtering
"""
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
kernel_as = context.layers[layer_name]
queue_entry = kernel.object_from_symbol(symbol_name = "tasks")
seen = {} # type: Dict[int, int]
for task in queue_entry.walk_list(queue_entry, "tasks", "task"):
if task.vol.offset in seen:
vollog.log(logging.INFO, "Recursive process list detected (a result of non-atomic acquisition).")
break
else:
seen[task.vol.offset] = 1
try:
proc = task.bsd_info.dereference().cast("proc")
except exceptions.PagedInvalidAddressException:
continue
if kernel_as.is_valid(proc.vol.offset, proc.vol.size) and not filter_func(proc):
yield proc