mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
add doc to alpc.py + improve doc now that Winstruct are documented
This commit is contained in:
+122
-15
@@ -15,8 +15,8 @@ from windows import generated_def as gdef
|
||||
# 0x1a: 0x4000000: ALPC_MESSAGE_DIRECT_ATTRIBUTE(0x4000000) size=0x8
|
||||
# 0x19: 0x2000000: ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE(0x2000000) size=0x8
|
||||
|
||||
|
||||
class AlpcMessage(object):
|
||||
"""Represent a full ALPC Message: a :class:`AlpcMessagePort` and a :class:`MessageAttribute`"""
|
||||
# PORT_MESSAGE + MessageAttribute
|
||||
def __init__(self, msg_or_size=0x1000, attributes=None):
|
||||
# Init the PORT_MESSAGE
|
||||
@@ -42,6 +42,7 @@ class AlpcMessage(object):
|
||||
# PORT_MESSAGE wrappers
|
||||
@property
|
||||
def type(self):
|
||||
"""The type of the message (``PORT_MESSAGE.u2.s2.Type``)"""
|
||||
return self.port_message.u2.s2.Type
|
||||
|
||||
def get_port_message_data(self):
|
||||
@@ -51,49 +52,79 @@ class AlpcMessage(object):
|
||||
self.port_message.data = data
|
||||
|
||||
data = property(get_port_message_data, set_port_message_data)
|
||||
"The data of the message (located after the PORT_MESSAGE header)"
|
||||
|
||||
# MessageAttributes wrappers
|
||||
|
||||
## Low level attributes access
|
||||
@property
|
||||
def security_attribute(self):
|
||||
"""The :data:`~windows.generated_def.ALPC_MESSAGE_SECURITY_ATTRIBUTE` of the message
|
||||
:
|
||||
:type: :class:`ALPC_SECURITY_ATTR`
|
||||
"""
|
||||
return self.attributes.get_attribute(gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE)
|
||||
|
||||
@property
|
||||
def view_attribute(self):
|
||||
"""The :data:`~windows.generated_def.ALPC_MESSAGE_VIEW_ATTRIBUTE` of the message:
|
||||
|
||||
:type: :class:`ALPC_DATA_VIEW_ATTR`
|
||||
"""
|
||||
return self.attributes.get_attribute(gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE)
|
||||
|
||||
@property
|
||||
def context_attribute(self):
|
||||
"""The :data:`~windows.generated_def.ALPC_MESSAGE_CONTEXT_ATTRIBUTE` of the message:
|
||||
|
||||
:type: :class:`ALPC_CONTEXT_ATTR`
|
||||
"""
|
||||
return self.attributes.get_attribute(gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE)
|
||||
|
||||
@property
|
||||
def handle_attribute(self):
|
||||
"""The :data:`~windows.generated_def.ALPC_MESSAGE_HANDLE_ATTRIBUTE` of the message:
|
||||
|
||||
:type: :class:`ALPC_HANDLE_ATTR`
|
||||
"""
|
||||
return self.attributes.get_attribute(gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE)
|
||||
|
||||
## Low level validity check (Test)
|
||||
@property
|
||||
def view_is_valid(self): # Change the name ?
|
||||
"""True if :data:`~windows.generated_def.ALPC_MESSAGE_VIEW_ATTRIBUTE` is a ValidAttributes"""
|
||||
return self.attributes.is_valid(gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE)
|
||||
|
||||
@property
|
||||
def security_is_valid(self): # Change the name ?
|
||||
"""True if :data:`~windows.generated_def.ALPC_MESSAGE_SECURITY_ATTRIBUTE` is a ValidAttributes"""
|
||||
return self.attributes.is_valid(gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE)
|
||||
|
||||
@property
|
||||
def handle_is_valid(self): # Change the name ?
|
||||
"""True if :data:`~windows.generated_def.ALPC_MESSAGE_HANDLE_ATTRIBUTE` is a ValidAttributes"""
|
||||
return self.attributes.is_valid(gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE)
|
||||
|
||||
@property
|
||||
def context_is_valid(self): # Change the name ?
|
||||
"""True if :data:`~windows.generated_def.ALPC_MESSAGE_CONTEXT_ATTRIBUTE` is a ValidAttributes"""
|
||||
return self.attributes.is_valid(gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE)
|
||||
|
||||
|
||||
@property
|
||||
def valid_attributes(self):
|
||||
"""The list of valid attributes
|
||||
|
||||
:type: [:class:`~windows.generated_def.Flag`]
|
||||
"""
|
||||
return self.attributes.valid_list
|
||||
|
||||
@property
|
||||
def allocated_attributes(self):
|
||||
"""The list of allocated attributes
|
||||
|
||||
:type: [:class:`~windows.generated_def.Flag`]
|
||||
"""
|
||||
return self.attributes.allocated_list
|
||||
|
||||
## High level setup (Test)
|
||||
@@ -103,6 +134,7 @@ class AlpcMessage(object):
|
||||
|
||||
|
||||
class AlpcMessagePort(gdef.PORT_MESSAGE):
|
||||
"""The effective ALPC Message composed of a ``PORT_MESSAGE`` structure followed by the data"""
|
||||
# Constructeur
|
||||
@classmethod
|
||||
def from_buffer(self, buffer):
|
||||
@@ -131,6 +163,7 @@ class AlpcMessagePort(gdef.PORT_MESSAGE):
|
||||
self.set_datalen(len(data))
|
||||
|
||||
data = property(read_data, write_data)
|
||||
"The data of the message (located after the header)"
|
||||
|
||||
def set_datalen(self, datalen):
|
||||
self.u1.s1.TotalLength = self.header_size + datalen
|
||||
@@ -140,6 +173,7 @@ class AlpcMessagePort(gdef.PORT_MESSAGE):
|
||||
return self.u1.s1.DataLength
|
||||
|
||||
datalen = property(get_datalen, set_datalen)
|
||||
"""The length of the data"""
|
||||
|
||||
KNOWN_ALPC_ATTRIBUTES = (gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE,
|
||||
gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE,
|
||||
@@ -153,6 +187,7 @@ KNOWN_ALPC_ATTRIBUTES_MAPPING = {x:x for x in KNOWN_ALPC_ATTRIBUTES}
|
||||
|
||||
|
||||
class MessageAttribute(gdef.ALPC_MESSAGE_ATTRIBUTES):
|
||||
"""The attributes of an ALPC message"""
|
||||
ATTRIBUTE_BY_FLAG = [(gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE, gdef.ALPC_SECURITY_ATTR),
|
||||
(gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE, gdef.ALPC_DATA_VIEW_ATTR),
|
||||
(gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE, gdef.ALPC_CONTEXT_ATTR),
|
||||
@@ -162,22 +197,34 @@ class MessageAttribute(gdef.ALPC_MESSAGE_ATTRIBUTES):
|
||||
(gdef.ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE, gdef.ALPC_WORK_ON_BEHALF_ATTR),
|
||||
]
|
||||
|
||||
# 0x1b: 0x8000000: ALPC_MESSAGE_TOKEN_ATTRIBUTE(0x8000000): size=0x18
|
||||
# 0x1a: 0x4000000: ALPC_MESSAGE_DIRECT_ATTRIBUTE(0x4000000) size=0x8
|
||||
# 0x19: 0x2000000: ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE(0x2000000) size=0x8
|
||||
|
||||
@classmethod
|
||||
def with_attributes(cls, flags):
|
||||
size = cls._get_required_buffer_size(flags)
|
||||
def with_attributes(cls, attributes):
|
||||
"""Create a new :class:`MessageAttribute` with ``attributes`` allocated
|
||||
|
||||
:returns: :class:`MessageAttribute`
|
||||
"""
|
||||
size = cls._get_required_buffer_size(attributes)
|
||||
buffer = ctypes.c_buffer(size)
|
||||
self = cls.from_buffer(buffer)
|
||||
self.raw_buffer = buffer
|
||||
res = gdef.DWORD()
|
||||
winproxy.AlpcInitializeMessageAttribute(flags, self, len(self.raw_buffer), res)
|
||||
winproxy.AlpcInitializeMessageAttribute(attributes, self, len(self.raw_buffer), res)
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def with_all_attributes(cls):
|
||||
"""Create a new :class:`MessageAttribute` with the following attributes allocated:
|
||||
|
||||
- :class:`ALPC_MESSAGE_SECURITY_ATTRIBUTE`
|
||||
- :class:`ALPC_MESSAGE_VIEW_ATTRIBUTE`
|
||||
- :class:`ALPC_MESSAGE_CONTEXT_ATTRIBUTE`
|
||||
- :class:`ALPC_MESSAGE_HANDLE_ATTRIBUTE`
|
||||
- :class:`ALPC_MESSAGE_TOKEN_ATTRIBUTE`
|
||||
- :class:`ALPC_MESSAGE_DIRECT_ATTRIBUTE`
|
||||
- :class:`ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE`
|
||||
|
||||
:returns: :class:`MessageAttribute`
|
||||
"""
|
||||
return cls.with_attributes(gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE |
|
||||
gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE |
|
||||
gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE |
|
||||
@@ -197,11 +244,13 @@ class MessageAttribute(gdef.ALPC_MESSAGE_ATTRIBUTES):
|
||||
return res.value
|
||||
return res.value
|
||||
|
||||
def is_allocated(self, value):
|
||||
return bool(self.AllocatedAttributes & value)
|
||||
def is_allocated(self, attribute):
|
||||
"""Return ``True`` if ``attribute`` is allocated"""
|
||||
return bool(self.AllocatedAttributes & attribute)
|
||||
|
||||
def is_valid(self, value):
|
||||
return bool(self.ValidAttributes & value)
|
||||
def is_valid(self, attribute):
|
||||
"""Return ``True`` if ``attribute`` is valid"""
|
||||
return bool(self.ValidAttributes & attribute)
|
||||
|
||||
def get_attribute(self, attribute):
|
||||
if not self.is_allocated(attribute):
|
||||
@@ -224,10 +273,18 @@ class MessageAttribute(gdef.ALPC_MESSAGE_ATTRIBUTES):
|
||||
|
||||
@property
|
||||
def valid_list(self):
|
||||
"""The list of valid attributes
|
||||
|
||||
:type: [:class:`~windows.generated_def.Flag`]
|
||||
"""
|
||||
return self._extract_alpc_attributes_values(self.ValidAttributes)
|
||||
|
||||
@property
|
||||
def allocated_list(self):
|
||||
"""The list of allocated attributes
|
||||
|
||||
:type: [:class:`~windows.generated_def.Flag`]
|
||||
"""
|
||||
return self._extract_alpc_attributes_values(self.AllocatedAttributes)
|
||||
|
||||
|
||||
@@ -235,6 +292,14 @@ AlpcSection = namedtuple("AlpcSection", ["handle", "size"])
|
||||
|
||||
class AlpcTransportBase(object):
|
||||
def send_receive(self, alpc_message, receive_msg=None, flags=gdef.ALPC_MSGFLG_SYNC_REQUEST):
|
||||
"""Send and receive a message with ``flags``.
|
||||
|
||||
:param alpc_message: The message to send. If ``alpc_message`` is a :class:`str` it build an AlpcMessage with the message as data.
|
||||
:type alpc_message: AlpcMessage or str
|
||||
:param receive_msg: The message to send. If ``receive_msg`` is a ``None`` it create and return a simple :class:`AlpcMessage`
|
||||
:type receive_msg: AlpcMessage or None
|
||||
:param int flags: The flags for :func:`NtAlpcSendWaitReceivePort`
|
||||
"""
|
||||
if isinstance(alpc_message, basestring):
|
||||
raw_alpc_message = alpc_message
|
||||
alpc_message = AlpcMessage(max(0x1000, len(alpc_message)))
|
||||
@@ -247,6 +312,12 @@ class AlpcTransportBase(object):
|
||||
return receive_msg
|
||||
|
||||
def send(self, alpc_message, flags=0):
|
||||
"""Send the ``alpc_message`` with ``flags``
|
||||
|
||||
:param alpc_message: The message to send. If ``alpc_message`` is a :class:`str` it build an AlpcMessage with the message as data.
|
||||
:type alpc_message: AlpcMessage or str
|
||||
:param int flags: The flags for :func:`NtAlpcSendWaitReceivePort`
|
||||
"""
|
||||
if isinstance(alpc_message, basestring):
|
||||
raw_alpc_message = alpc_message
|
||||
alpc_message = AlpcMessage(max(0x1000, len(alpc_message)))
|
||||
@@ -254,6 +325,12 @@ class AlpcTransportBase(object):
|
||||
winproxy.NtAlpcSendWaitReceivePort(self.handle, flags, alpc_message.port_message, alpc_message.attributes, None, None, None, None)
|
||||
|
||||
def recv(self, receive_msg=None, flags=0):
|
||||
"""Receive a message into ``alpc_message`` with ``flags``.
|
||||
|
||||
:param receive_msg: The message to send. If ``receive_msg`` is a ``None`` it create and return a simple :class:`AlpcMessage`
|
||||
:type receive_msg: AlpcMessage or None
|
||||
:param int flags: The flags for :func:`NtAlpcSendWaitReceivePort`
|
||||
"""
|
||||
if receive_msg is None:
|
||||
receive_msg = AlpcMessage(0x1000)
|
||||
receive_size = gdef.SIZE_T(receive_msg.port_message_buffer_size)
|
||||
@@ -263,9 +340,11 @@ class AlpcTransportBase(object):
|
||||
|
||||
|
||||
class AlpcClient(AlpcTransportBase):
|
||||
"An ALPC client able to connect to a port and send/receive messages"
|
||||
DEFAULT_MAX_MESSAGE_LENGTH = 0x1000
|
||||
|
||||
def __init__(self, port_name=None):
|
||||
"""Init the :class:`AlpcClient` automatically connect to ``port_name`` using default values if given"""
|
||||
self.handle = None
|
||||
self.portname = None
|
||||
if port_name is not None:
|
||||
@@ -275,7 +354,18 @@ class AlpcClient(AlpcTransportBase):
|
||||
utf16_len = len(name) * 2
|
||||
return gdef.UNICODE_STRING(utf16_len, utf16_len, name)
|
||||
|
||||
def connect_to_port(self, port_name, connect_message=None, receive_message=None, port_attr=None, port_attr_flags=0x10000, obj_attr=None, flags=gdef.ALPC_MSGFLG_SYNC_REQUEST, timeout=None):
|
||||
def connect_to_port(self, port_name, connect_message=None,
|
||||
port_attr=None, port_attr_flags=0x10000, obj_attr=None,
|
||||
flags=gdef.ALPC_MSGFLG_SYNC_REQUEST, timeout=None):
|
||||
"""Connect to the ALPC port ``port_name``. Most of the parameters have defauls value is ``None`` is passed.
|
||||
|
||||
:param AlpcMessage connect_message: The message send with the connection request, if not ``None`` the function will return an :class:`AlpcMessage`
|
||||
:param ALPC_PORT_ATTRIBUTES port_attr: The port attributes, one with default value will be used if this parameter is ``None``
|
||||
:param int port_attr_flags: ``ALPC_PORT_ATTRIBUTES.Flags`` used if ``port_attr`` is ``None`` (MUTUALY EXCLUSINVE WITH ``port_attr``)
|
||||
:param OBJECT_ATTRIBUTES obj_attr: The attributes of the port (can be None)
|
||||
:param int flags: The flags for :func:`NtAlpcConnectPort`
|
||||
:param int timeout: The timeout of the request
|
||||
"""
|
||||
# TODO raise on mutual exclusive parameter
|
||||
if self.handle is not None:
|
||||
raise ValueError("Client already connected")
|
||||
@@ -340,6 +430,7 @@ class AlpcClient(AlpcTransportBase):
|
||||
|
||||
|
||||
class AlpcServer(AlpcTransportBase):
|
||||
"""An ALPC server able to create a port, accept connections and send/receive messages"""
|
||||
DEFAULT_MAX_MESSAGE_LENGTH = 0x1000
|
||||
|
||||
def __init__(self, port_name=None):
|
||||
@@ -352,6 +443,14 @@ class AlpcServer(AlpcTransportBase):
|
||||
return gdef.UNICODE_STRING(utf16_len, utf16_len, name)
|
||||
|
||||
def create_port(self, port_name, msglen=None, port_attr_flags=0, obj_attr=None, port_attr=None):
|
||||
"""Create the ALPC port ``port_name``. Most of the parameters have defauls value is ``None`` is passed.
|
||||
|
||||
:param str port_name: The port's name to create.
|
||||
:param int msglen: ``ALPC_PORT_ATTRIBUTES.MaxMessageLength`` used if ``port_attr`` is ``None`` (MUTUALY EXCLUSINVE WITH ``port_attr``)
|
||||
:param int port_attr_flags: ``ALPC_PORT_ATTRIBUTES.Flags`` used if ``port_attr`` is ``None`` (MUTUALY EXCLUSINVE WITH ``port_attr``)
|
||||
:param OBJECT_ATTRIBUTES obj_attr: The attributes of the port, one with default value will be used if this parameter is ``None``
|
||||
:param ALPC_PORT_ATTRIBUTES port_attr: The port attributes, one with default value will be used if this parameter is ``None``
|
||||
"""
|
||||
# TODO raise on mutual exclusive parameter (port_attr + port_attr_flags | obj_attr + msglen)
|
||||
handle = gdef.HANDLE()
|
||||
raw_name = port_name
|
||||
@@ -371,9 +470,9 @@ class AlpcServer(AlpcTransportBase):
|
||||
obj_attr.SecurityQualityOfService = None
|
||||
if port_attr is None:
|
||||
port_attr = gdef.ALPC_PORT_ATTRIBUTES()
|
||||
# port_attr.Flags = port_attr_flags
|
||||
port_attr.Flags = port_attr_flags
|
||||
# port_attr.Flags = 0x2080000
|
||||
port_attr.Flags = 0x90000
|
||||
# port_attr.Flags = 0x90000
|
||||
port_attr.MaxMessageLength = msglen
|
||||
port_attr.MemoryBandwidth = 0
|
||||
port_attr.MaxPoolUsage = 0xffffffff
|
||||
@@ -388,6 +487,14 @@ class AlpcServer(AlpcTransportBase):
|
||||
self.handle = handle.value
|
||||
|
||||
def accept_connection(self, msg, port_attr=None, port_context=None):
|
||||
"""Accept the connection for a ``LPC_CONNECTION_REQUEST`` message.
|
||||
``msg.MessageId`` must be the same as the connection requesting message.
|
||||
|
||||
:param AlpcMessage msg: The response message.
|
||||
:param ALPC_PORT_ATTRIBUTES port_attr: The attributes of the port, one with default value will be used if this parameter is ``None``
|
||||
:param PVOID port_context: A value that will be copied in ``ALPC_CONTEXT_ATTR.PortContext`` of every message on this connection.
|
||||
|
||||
"""
|
||||
rhandle = gdef.HANDLE()
|
||||
|
||||
if port_attr is None:
|
||||
|
||||
@@ -973,33 +973,49 @@ class Debugger(object):
|
||||
raise NotImplementedError("Debugger that explicitly single step should implement <on_single_step>")
|
||||
|
||||
def on_create_process(self, create_process):
|
||||
"""Called on create_process event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms679286(v=vs.85).aspx)"""
|
||||
"""Called on create_process event
|
||||
|
||||
:param CREATE_PROCESS_DEBUG_INFO create_process:"""
|
||||
pass
|
||||
|
||||
def on_exit_process(self, exit_process):
|
||||
"""Called on exit_process event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms679334(v=vs.85).aspx)"""
|
||||
"""Called on exit_process event
|
||||
|
||||
:param EXIT_PROCESS_DEBUG_INFO exit_process:"""
|
||||
pass
|
||||
|
||||
def on_create_thread(self, create_thread):
|
||||
"""Called on create_thread event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms679287(v=vs.85).aspx)"""
|
||||
"""Called on create_thread event
|
||||
|
||||
:param CREATE_THREAD_DEBUG_INFO create_thread:"""
|
||||
pass
|
||||
|
||||
def on_exit_thread(self, exit_thread):
|
||||
"""Called on exit_thread event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms679335(v=vs.85).aspx)"""
|
||||
"""Called on exit_thread event
|
||||
|
||||
:param EXIT_THREAD_DEBUG_INFO exit_thread:"""
|
||||
pass
|
||||
|
||||
def on_load_dll(self, load_dll):
|
||||
"""Called on load_dll event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680351(v=vs.85).aspx)"""
|
||||
"""Called on load_dll event
|
||||
|
||||
:param LOAD_DLL_DEBUG_INFO load_dll:"""
|
||||
pass
|
||||
|
||||
def on_unload_dll(self, unload_dll):
|
||||
"""Called on unload_dll event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681403(v=vs.85).aspx)"""
|
||||
"""Called on unload_dll event
|
||||
|
||||
:param UNLOAD_DLL_DEBUG_INFO unload_dll:"""
|
||||
pass
|
||||
|
||||
def on_output_debug_string(self, debug_string):
|
||||
"""Called on debug_string event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680545(v=vs.85).aspx)"""
|
||||
"""Called on debug_string event
|
||||
|
||||
:param OUTPUT_DEBUG_STRING_INFO debug_string:"""
|
||||
pass
|
||||
|
||||
def on_rip(self, rip_info):
|
||||
"""Called on rip_info event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680587(v=vs.85).aspx)"""
|
||||
"""Called on rip_info event
|
||||
|
||||
:param RIP_INFO rip_info:"""
|
||||
pass
|
||||
@@ -457,7 +457,7 @@ class Process(AutoHandle):
|
||||
def query_memory(self, addr):
|
||||
"""Query the memory informations about page at ``addr``
|
||||
|
||||
:rtype: :class:`MEMORY_BASIC_INFORMATION`
|
||||
:rtype: :class:`~windows.generated_def.MEMORY_BASIC_INFORMATION`
|
||||
"""
|
||||
if windows.current_process.bitness == 32 and self.bitness == 64:
|
||||
res = MEMORY_BASIC_INFORMATION64()
|
||||
@@ -478,7 +478,7 @@ class Process(AutoHandle):
|
||||
def memory_state(self):
|
||||
"""Yield the memory information for the whole address space of the process
|
||||
|
||||
:yield: :class:`MEMORY_BASIC_INFORMATION`
|
||||
:yield: :class:`~windows.generated_def.MEMORY_BASIC_INFORMATION`
|
||||
"""
|
||||
addr = 0
|
||||
res = []
|
||||
|
||||
Reference in New Issue
Block a user