# Volatility
# Copyright (C) 2007-2013 Volatility Foundation
# Copyright (C) 2010,2011,2012 Michael Hale Ligh <michael.ligh@mnin.org>
#
# This file is part of Volatility.
#
# Volatility is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License Version 2 as
# published by the Free Software Foundation.  You may not use, modify or
# distribute this program under any other version of the GNU General
# Public License.
#
# Volatility is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Volatility.  If not, see <http://www.gnu.org/licenses/>.
#

import volatility.obj as obj
import volatility.utils as utils
import volatility.plugins.common as common
import volatility.win32.modules as modules
import volatility.win32.tasks as tasks

class SessionsMixin(object):
    """This is a mixin that plugins can inherit for access to the 
    main sessions APIs."""

    def session_spaces(self, kernel_space):
        """ Generators unique _MM_SESSION_SPACE objects
        referenced by active processes. 
    
        @param space: a kernel AS for process enumeration
    
        @yields _MM_SESSION_SPACE instantiated from the 
        session space native_vm. 
        """
        seen = []
        for proc in tasks.pslist(kernel_space):
            if proc.SessionId != None and proc.SessionId.v() not in seen:
                ps_ad = proc.get_process_address_space()
                if ps_ad != None:
                    seen.append(proc.SessionId.v())
                    yield obj.Object("_MM_SESSION_SPACE",
                        offset = proc.Session.v(), vm = ps_ad)

    def find_session_space(self, kernel_space, session_id):
        """ Get a session address space by its ID. 
    
        @param space: a kernel AS for process enumeration
        @param session_id: the session ID to find.
    
        @returns _MM_SESSION_SPACE instantiated from the 
        session space native_vm. 
        """
        for proc in tasks.pslist(kernel_space):
            if proc.SessionId == session_id:
                ps_ad = proc.get_process_address_space()
                if ps_ad != None:
                    return obj.Object("_MM_SESSION_SPACE",
                        offset = proc.Session.v(), vm = ps_ad)
        return obj.NoneObject("Cannot locate a session")

class Sessions(common.AbstractWindowsCommand, SessionsMixin):
    """List details on _MM_SESSION_SPACE (user logon sessions)"""

    def calculate(self):
        kernel_space = utils.load_as(self._config)

        # Once for each unique _MM_SESSION_SPACE 
        for session in self.session_spaces(kernel_space):
            yield session

    def render_text(self, outfd, data):

        # Kernel AS for looking up modules 
        kernel_space = utils.load_as(self._config)

        # Modules sorted for address lookups 
        mods = dict((kernel_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(kernel_space))
        mod_addrs = sorted(mods.keys())

        for session in data:
            outfd.write("*" * 50 + "\n")
            outfd.write("Session(V): {0:x} ID: {1} Processes: {2}\n".format(
                session.obj_offset,
                session.SessionId,
                len(list(session.processes())),
                ))
            outfd.write("PagedPoolStart: {0:x} PagedPoolEnd {1:x}\n".format(
                session.PagedPoolStart,
                session.PagedPoolEnd,
                ))
            for process in session.processes():
                outfd.write(" Process: {0} {1} {2}\n".format(
                    process.UniqueProcessId,
                    process.ImageFileName,
                    process.CreateTime,
                    ))
            for image in session.images():
                module = tasks.find_module(mods, mod_addrs, kernel_space.address_mask(image.Address))
                outfd.write(" Image: {0:#x}, Address {1:x}, Name: {2}\n".format(
                    image.obj_offset,
                    image.Address,
                    str(module and module.BaseDllName or '')
                    ))
