mirror of
https://github.com/naksyn/PythonMemoryModule
synced 2026-06-06 16:24:25 +00:00
db1893910c
This update include support to passing command line parameters to unmanaged exe via PEB stomping. This technique is not working with every executable since it depends on which functions are used to pass arguments. Generally, to get a universally working technique would be required to hook GetCommandlineA GetCommandlineW __getmainargs and __wgetmainargs since PEB stomping won't cover all cases, more details here: https://blog-30cm-tw.translate.goog/2020/08/windows-c-mainargc-argv.html?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=it&_x_tr_pto=wapp However, during my testing I found that mimikatz and several go binaries are working just by doing PEB stomping. On the other hand, cmdline passing via PEB stomping alone to mingw and VS compiled binaries won't likely work.
485 lines
16 KiB
Python
485 lines
16 KiB
Python
import windows.com
|
|
import windows.generated_def as gdef
|
|
|
|
|
|
def generate_simple_getter(function, restype, extract_value=True, doc=None):
|
|
def value_getter(self):
|
|
res = restype()
|
|
getattr(self, function)(res)
|
|
if extract_value:
|
|
return res.value
|
|
return res
|
|
return property(value_getter, doc=doc)
|
|
|
|
|
|
def add_simple_setter(getter, function, restype):
|
|
@getter.setter
|
|
def value_setter(self, value):
|
|
resvalue = restype(value)
|
|
return getattr(self, function)(resvalue)
|
|
return value_setter
|
|
|
|
|
|
class TaskCollectionType(object):
|
|
ITEM_TYPE = None
|
|
|
|
count = generate_simple_getter("get_Count", gdef.LONG)
|
|
|
|
def get_item_type(self):
|
|
return self.ITEM_TYPE
|
|
|
|
def get_item(self, index):
|
|
"""Return elements nb ``index``. Collection index starts at 1"""
|
|
if index == 0:
|
|
raise IndexError("<{0}> Index start as 1".format(type(self).__name__))
|
|
index = self.get_index(index)
|
|
res = self.get_item_type()()
|
|
self.get_Item(index, res)
|
|
return res
|
|
|
|
def get_index(self, index):
|
|
return index
|
|
|
|
def items_generator(self):
|
|
for i in range(self.count):
|
|
# Start index is 1
|
|
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa446901(v=vs.85).aspx
|
|
yield self.get_item(1 + i)
|
|
|
|
@property
|
|
def items(self):
|
|
"""Return the list of item in the collection
|
|
|
|
:type: :class:`list`
|
|
"""
|
|
return list(self.items_generator())
|
|
|
|
def __iter__(self):
|
|
return self.items_generator()
|
|
|
|
def __getitem__(self, index): # Allow subclasses to only overwrite 'get_item' to rewrite both behavior
|
|
return self.get_item(index)
|
|
|
|
# Need to-do the doc=xx tricks to have the documentation in the 'AbstractAction' subclasses
|
|
|
|
class AbstractAction(object):
|
|
type_doc = """The type of action
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.TASK_ACTION_TYPE`
|
|
"""
|
|
type = generate_simple_getter("get_Type", gdef.TASK_ACTION_TYPE, doc=type_doc)
|
|
|
|
id_doc = """The action id
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.BSTR`
|
|
"""
|
|
id = generate_simple_getter("get_Id", gdef.BSTR, doc=id_doc)
|
|
|
|
|
|
class Action(gdef.IAction, AbstractAction):
|
|
"""Describe an action performed by a task"""
|
|
ACTION_SUBTYPE = {}
|
|
|
|
|
|
@property
|
|
def subtype(self):
|
|
"""Return the :class:`Action`-subtype according to :data:`AbstractAction.type`"""
|
|
subinterface = self.ACTION_SUBTYPE[self.type] # KeyError ?
|
|
return self.query(subinterface)
|
|
|
|
|
|
class ExecAction(gdef.IExecAction, AbstractAction):
|
|
"""Represent an action of type
|
|
:data:`~windows.generated_def.winstructs._TASK_ACTION_TYPE.TASK_ACTION_EXEC`"""
|
|
path = generate_simple_getter("get_Path", gdef.BSTR)
|
|
path = add_simple_setter(path, "put_Path", gdef.BSTR)
|
|
"""[R-W] The path of the programm to execute"""
|
|
|
|
arguments = generate_simple_getter("get_Arguments", gdef.BSTR)
|
|
arguments = add_simple_setter(arguments, "put_Arguments", gdef.BSTR)
|
|
"""[R-W] The arguments for the command to execute"""
|
|
|
|
working_directory = generate_simple_getter("get_WorkingDirectory", gdef.BSTR)
|
|
"""The working directory for the command to execute"""
|
|
|
|
# Register action subtype
|
|
Action.ACTION_SUBTYPE[gdef.TASK_ACTION_EXEC] = ExecAction
|
|
|
|
class ComHandlerAction(gdef.IComHandlerAction, AbstractAction):
|
|
"""Represent an action of type
|
|
:data:`~windows.generated_def.winstructs._TASK_ACTION_TYPE.TASK_ACTION_COM_HANDLER`"""
|
|
|
|
classid = generate_simple_getter("get_ClassId", gdef.BSTR)
|
|
classid = add_simple_setter(classid, "put_ClassId", gdef.BSTR)
|
|
"""The CLSID of the COM server executed
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.BSTR`
|
|
"""
|
|
data = generate_simple_getter("get_Data", gdef.BSTR)
|
|
data = add_simple_setter(data, "put_Data", gdef.BSTR)
|
|
"""The DATA for the COM class
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.BSTR`
|
|
"""
|
|
|
|
# Register action subtype
|
|
Action.ACTION_SUBTYPE[gdef.TASK_ACTION_COM_HANDLER] = ComHandlerAction
|
|
|
|
|
|
class EmailAction(gdef.IEmailAction, AbstractAction):
|
|
pass
|
|
|
|
|
|
Action.ACTION_SUBTYPE[gdef.TASK_ACTION_SEND_EMAIL] = EmailAction
|
|
|
|
class ShowMessageAction(gdef.IShowMessageAction, AbstractAction):
|
|
pass
|
|
|
|
Action.ACTION_SUBTYPE[gdef.TASK_ACTION_SHOW_MESSAGE] = ShowMessageAction
|
|
|
|
class Trigger(gdef.ITrigger):
|
|
"""A task trigger"""
|
|
type = generate_simple_getter("get_Type", gdef.TASK_TRIGGER_TYPE2)
|
|
"""The type of trigger
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.TASK_TRIGGER_TYPE2`
|
|
"""
|
|
|
|
|
|
class ActionCollection(gdef.IActionCollection, TaskCollectionType):
|
|
ITEM_TYPE = Action
|
|
|
|
def create(self, action_type):
|
|
"""Create a new action of type ``action_type``
|
|
|
|
:rtype: A subclass of :class:`Action`
|
|
"""
|
|
res = self.ITEM_TYPE()
|
|
self.Create(action_type, res)
|
|
return res.subtype
|
|
|
|
def get_item(self, index):
|
|
item = super(ActionCollection, self).get_item(index)
|
|
# Need to Release() item ?
|
|
return item.subtype
|
|
|
|
class TriggerCollection(gdef.ITriggerCollection, TaskCollectionType):
|
|
ITEM_TYPE = Trigger
|
|
|
|
|
|
class TaskRegistrationInfo(gdef.IRegistrationInfo):
|
|
"""Provides the administrative information that can be used to describe the task.
|
|
|
|
This information includes details such as a description of the task,
|
|
the author of the task, the date the task is registered,
|
|
and the security descriptor of the task.
|
|
"""
|
|
author = generate_simple_getter("get_Author", gdef.BSTR)
|
|
"""The author of the task"""
|
|
description = generate_simple_getter("get_Description", gdef.BSTR)
|
|
"""The description of the task"""
|
|
date = generate_simple_getter("get_Date", gdef.BSTR)
|
|
"""The registration date of the task"""
|
|
source = generate_simple_getter("get_Source", gdef.BSTR)
|
|
"""Where the task originated from.
|
|
|
|
For example, a task may originate from a component, service, application, or user.
|
|
"""
|
|
documentation = generate_simple_getter("get_Documentation", gdef.BSTR)
|
|
"""Any additional documentation for the task"""
|
|
uri = generate_simple_getter("get_URI", gdef.BSTR)
|
|
"""the URI of the task."""
|
|
version = generate_simple_getter("get_Version", gdef.BSTR)
|
|
"""The version number of the task."""
|
|
|
|
# Return WindowsError: [Error -2147467263] Not implemented
|
|
# xml = generate_simple_getter("get_XmlText", gdef.BSTR)
|
|
|
|
sddl = generate_simple_getter("get_SecurityDescriptor", windows.com.Variant)
|
|
|
|
@property
|
|
def security_descriptor(self):
|
|
sddl = self.sddl
|
|
if not sddl:
|
|
return None
|
|
return windows.security.SecurityDescriptor.from_string(sddl)
|
|
|
|
|
|
|
|
class TaskPrincipal(gdef.IPrincipal):
|
|
"""Provides the security credentials for a principal.
|
|
These security credentials define the security context for the tasks that are associated with the principal.
|
|
"""
|
|
|
|
name = generate_simple_getter("get_DisplayName", gdef.BSTR)
|
|
name = add_simple_setter(name, "put_DisplayName", gdef.BSTR)
|
|
"""The name of the principal"""
|
|
id = generate_simple_getter("get_Id", gdef.BSTR)
|
|
id = add_simple_setter(id, "put_Id", gdef.BSTR)
|
|
"""the identifier of the principal."""
|
|
user_id = generate_simple_getter("get_UserId", gdef.BSTR)
|
|
user_id = add_simple_setter(user_id, "put_UserId", gdef.BSTR)
|
|
"""the user identifier that is required to run the task"""
|
|
group_id = generate_simple_getter("get_GroupId", gdef.BSTR)
|
|
group_id = add_simple_setter(group_id, "put_GroupId", gdef.BSTR)
|
|
"""the user group that is required to run the task"""
|
|
run_level = generate_simple_getter("get_RunLevel", gdef.TASK_RUNLEVEL_TYPE)
|
|
"""the privilege level that is required to run the tasks
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.TASK_RUNLEVEL_TYPE`
|
|
"""
|
|
logon_type = generate_simple_getter("get_LogonType", gdef.TASK_LOGON_TYPE)
|
|
""" logon method that is required to run the task
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.TASK_LOGON_TYPE`
|
|
"""
|
|
|
|
class TaskDefinition(gdef.ITaskDefinition):
|
|
"""The definition of a task"""
|
|
actions = generate_simple_getter("get_Actions", ActionCollection, extract_value=False)
|
|
"""The list of actions of the task
|
|
|
|
:type: :class:`ActionCollection`
|
|
"""
|
|
triggers = generate_simple_getter("get_Triggers", TriggerCollection, extract_value=False)
|
|
"""The list of triggers of the task
|
|
|
|
:type: :class:`TriggerCollection`
|
|
"""
|
|
|
|
registration_info = generate_simple_getter("get_RegistrationInfo", TaskRegistrationInfo, extract_value=False)
|
|
"""The registration information of the task
|
|
|
|
:type: :class:`TaskRegistrationInfo`
|
|
"""
|
|
|
|
principal = generate_simple_getter("get_Principal", TaskPrincipal, extract_value=False)
|
|
"""The principal that provides the security credentials for the task.
|
|
These security credentials define the security context for the tasks that are associated with the principal.
|
|
|
|
:type: :class:`TaskPrincipal`
|
|
"""
|
|
|
|
xml = generate_simple_getter("get_XmlText", gdef.BSTR)
|
|
"""The XML representig the task definition
|
|
|
|
:type: :class:`str`
|
|
"""
|
|
|
|
|
|
|
|
class Task(gdef.IRegisteredTask):
|
|
"""A scheduled task"""
|
|
name = generate_simple_getter("get_Name", gdef.BSTR)
|
|
"""The name of the task"""
|
|
path = generate_simple_getter("get_Path", gdef.BSTR)
|
|
"""The path of the task"""
|
|
state = generate_simple_getter("get_State", gdef.TASK_STATE)
|
|
"""The state of the task
|
|
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.TASK_STATE`
|
|
"""
|
|
enabled = generate_simple_getter("get_Enabled", gdef.VARIANT_BOOL)
|
|
"""``True`` is the task is enabled"""
|
|
last_runtime = generate_simple_getter("get_LastRunTime", gdef.DATE)
|
|
"""Gets the last time the registered task was last run."""
|
|
next_runtime = generate_simple_getter("get_NextRunTime", gdef.DATE)
|
|
"""Gets the next time the registered task will be run."""
|
|
|
|
definition = generate_simple_getter("get_Definition", TaskDefinition, extract_value=False)
|
|
"""The definition of the task
|
|
|
|
:type: :class:`TaskDefinition`
|
|
"""
|
|
|
|
xml = generate_simple_getter("get_Xml", gdef.BSTR)
|
|
"""The XML representig the task
|
|
|
|
:type: :class:`str`
|
|
"""
|
|
|
|
def run(self, params=None, flags=gdef.TASK_RUN_NO_FLAGS, sessionid=0, user=None):
|
|
if params is None: params = gdef.VARIANT() # Empty variant
|
|
result = gdef.IRunningTask()
|
|
self.RunEx(params, flags, sessionid, user, result)
|
|
return result
|
|
|
|
def get_security_descriptor(self, secinfo):
|
|
res = gdef.BSTR()
|
|
self.GetSecurityDescriptor(secinfo, res)
|
|
return res.value
|
|
|
|
def __repr__(self):
|
|
return """<{0} "{1}" at {2:#x}>""".format(type(self).__name__, self.name, id(self))
|
|
|
|
|
|
class TaskCollection(gdef.IRegisteredTaskCollection, TaskCollectionType):
|
|
ITEM_TYPE = Task
|
|
def get_index(self, index):
|
|
vindex = windows.com.Variant()
|
|
vindex.vt = gdef.VT_I4
|
|
vindex._VARIANT_NAME_3.lVal = index
|
|
return vindex
|
|
|
|
|
|
class TaskService(gdef.ITaskService):
|
|
"""The task scheduler"""
|
|
def create(self, flags=0):
|
|
"""Create a new :class:`TaskDefinition` that can be used to create/register a new scheduled task
|
|
|
|
:rtype: :class:`TaskDefinition`
|
|
"""
|
|
res = TaskDefinition()
|
|
self.NewTask(flags, res)
|
|
return res
|
|
|
|
def connect(self, server=None, user=None, domain=None, password=None):
|
|
if server is None: server = gdef.VARIANT() # Empty variant
|
|
if user is None: user = gdef.VARIANT() # Empty variant
|
|
if domain is None: domain = gdef.VARIANT() # Empty variant
|
|
if password is None: password = gdef.VARIANT() # Empty variant
|
|
self.Connect(server, user, domain, password)
|
|
|
|
def folder(self, name):
|
|
"""Return the :class:`TaskFolder` with ``name``
|
|
|
|
:rtype: :class:`TaskFolder`
|
|
"""
|
|
folder = TaskFolder()
|
|
self.GetFolder(name, folder)
|
|
return folder
|
|
|
|
__call__ = folder # use the same 'API' than the registry
|
|
"""Alias for :func:`folder`"""
|
|
|
|
|
|
@property
|
|
def root(self):
|
|
r"""The root ``\`` :class:`TaskFolder`"""
|
|
return self.folder("\\")
|
|
|
|
|
|
|
|
class TaskFolder(gdef.ITaskFolder):
|
|
"""A folder of tasks"""
|
|
path = generate_simple_getter("get_Path", gdef.BSTR)
|
|
name = generate_simple_getter("get_Name", gdef.BSTR)
|
|
|
|
@property
|
|
def folders(self):
|
|
"""The list of sub-folders
|
|
|
|
:type: :class:`TaskFolderCollection`
|
|
"""
|
|
res = TaskFolderCollection()
|
|
self.GetFolders(0, res)
|
|
return res
|
|
|
|
def register(self, name, taskdef, flags=gdef.TASK_CREATE, userid=None, password=None, logonType=gdef.TASK_LOGON_NONE, ssid=None):
|
|
"""Register the task definition ``taskdef`` as a new task with ``name``
|
|
|
|
:rtype: :class:`Task`
|
|
"""
|
|
new_task = Task()
|
|
|
|
if userid is None: userid = gdef.VARIANT() # Empty variant
|
|
if password is None: password = gdef.VARIANT() # Empty variant
|
|
if ssid is None: ssid = gdef.VARIANT() # Empty variant
|
|
|
|
self.RegisterTaskDefinition(name, taskdef, flags, userid, password, logonType, ssid, new_task)
|
|
return new_task
|
|
|
|
@property
|
|
def tasks(self, flags=gdef.TASK_ENUM_HIDDEN):
|
|
"""The list of tasks in the folder
|
|
|
|
:type: :class:`TaskCollection`
|
|
"""
|
|
tasks = TaskCollection()
|
|
self.GetTasks(flags, tasks)
|
|
return tasks
|
|
|
|
def get_task(self, name):
|
|
"""Retrieve the task with ``name`` in the current folder
|
|
|
|
:rtype: :class:`Task`
|
|
"""
|
|
res = Task()
|
|
self.GetTask(name, res)
|
|
return res
|
|
|
|
def delete_task(self, name):
|
|
"""Delete the task with ``name`` in the current folder"""
|
|
return self.DeleteTask(name, 0)
|
|
|
|
def folder(self, name):
|
|
"""Return the :class:`TaskFolder` with ``name``"""
|
|
folder = TaskFolder()
|
|
self.GetFolder(name, folder)
|
|
return folder
|
|
|
|
def create_folder(self, name):
|
|
"""Create a new sub-:class:`TaskFolder` with ``name``"""
|
|
folder = TaskFolder()
|
|
self.CreateFolder(name, gdef.VARIANT(), folder)
|
|
return folder
|
|
|
|
def delete_folder(self, name):
|
|
"""Delete the sub-folder with ``name`` in the current folder"""
|
|
return self.DeleteFolder(name, 0)
|
|
|
|
__getitem__ = get_task
|
|
""" Alias for :func:`get_task`"""
|
|
__delitem__ = delete_task
|
|
""" Alias for :func:`delete_task`"""
|
|
__call__ = folder # use the same 'API' than the registry
|
|
""" Alias for :func:`folder`"""
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
return """<{0} "{1}" at {2:#x}>""".format(type(self).__name__, self.path, id(self))
|
|
|
|
|
|
class TaskFolderCollection(gdef.ITaskFolderCollection, TaskCollectionType):
|
|
ITEM_TYPE = TaskFolder
|
|
|
|
def get_index(self, index):
|
|
vindex = windows.com.Variant()
|
|
vindex.vt = gdef.VT_I4
|
|
vindex._VARIANT_NAME_3.lVal = index
|
|
return vindex
|
|
|
|
# windows.com.init()
|
|
# clsid_task_scheduler = gdef.IID.from_string("0f87369f-a4e5-4cfc-bd3e-73e6154572dd")
|
|
# x = TaskService()
|
|
# emptvar = gdef.VARIANT()
|
|
# windows.com.create_instance(clsid_task_scheduler, x)
|
|
# x.connect()
|
|
# folder = x.folder("\\")
|
|
# assert folder.value
|
|
# tasks = folder.tasks
|
|
|
|
# for task in tasks.items:
|
|
# print(task.name)
|
|
# for action in task.definition.actions.items:
|
|
# print(" * {0}".format(action.type))
|
|
# subtype = action.subtype
|
|
# print(" * Path: {0}".format(subtype.path))
|
|
# print(" * Args: {0}".format(subtype.arguments))
|
|
# print(" * WDir: {0}".format(subtype.working_directory))
|
|
|
|
# Test creation
|
|
|
|
# ntd = x.create()
|
|
# actions = ntd.actions
|
|
# nea = actions.create(gdef.TASK_ACTION_EXEC).subtype
|
|
# nea.path = "MY_BINARY"
|
|
# nea.arguments = "MY_ARGUMENTS"
|
|
|
|
# folder.register("PROUT", ntd)
|
|
|
|
# path = gdef.BSTR()
|
|
# e.get_Path(path)
|
|
# print(path) |