add doc to alpc.py + improve doc now that Winstruct are documented

This commit is contained in:
Clement Rouault
2017-08-16 17:38:18 +02:00
parent bd6a3d35d5
commit c76a50c045
3 changed files with 148 additions and 25 deletions
+122 -15
View File
@@ -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:
+24 -8
View File
@@ -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
+2 -2
View File
@@ -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 = []