mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
406 lines
17 KiB
Python
406 lines
17 KiB
Python
import ctypes
|
|
from collections import namedtuple
|
|
|
|
import windows
|
|
from windows import winproxy
|
|
from windows import generated_def as gdef
|
|
|
|
|
|
## For 64b python
|
|
# 0x1f: 0x80000000: ALPC_MESSAGE_SECURITY_ATTRIBUTE(0x80000000) : size=0x18?
|
|
# 0x1e: 0x40000000: ALPC_MESSAGE_VIEW_ATTRIBUTE(0x40000000): size=0x20
|
|
# 0x1d: 0x20000000: ALPC_MESSAGE_CONTEXT_ATTRIBUTE(0x20000000): size=0x20
|
|
# 0x1c: 0x10000000: ALPC_MESSAGE_HANDLE_ATTRIBUTE(0x10000000): size=0x18
|
|
# 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
|
|
|
|
|
|
class AlpcMessage(object):
|
|
# PORT_MESSAGE + MessageAttribute
|
|
def __init__(self, msg_or_size=0x1000, attributes=None):
|
|
# Init the PORT_MESSAGE
|
|
if isinstance(msg_or_size, (long, int)):
|
|
self.port_message_buffer_size = msg_or_size
|
|
self.port_message_raw_buffer = ctypes.c_buffer(msg_or_size)
|
|
self.port_message = AlpcMessagePort.from_buffer(self.port_message_raw_buffer)
|
|
self.port_message.set_datalen(0)
|
|
elif isinstance(msg_or_size, AlpcMessagePort):
|
|
self.port_message = msg_or_size
|
|
self.port_message_raw_buffer = self.port_message.raw_buffer
|
|
self.port_message_buffer_size = len(self.port_message_raw_buffer)
|
|
else:
|
|
raise NotImplementedError("Uneexpected type for <msg_or_size>: {0}".format(msg_or_size))
|
|
|
|
# Init the MessageAttributes
|
|
if attributes is None:
|
|
# self.attributes = MessageAttribute.with_all_attributes()
|
|
self.attributes = MessageAttribute.with_all_attributes() ## Testing
|
|
else:
|
|
self.attributes = attributes
|
|
|
|
# PORT_MESSAGE wrappers
|
|
@property
|
|
def type(self):
|
|
return self.port_message.u2.s2.Type
|
|
|
|
def get_port_message_data(self):
|
|
return self.port_message.data
|
|
|
|
def set_port_message_data(self, data):
|
|
self.port_message.data = data
|
|
|
|
data = property(get_port_message_data, set_port_message_data)
|
|
|
|
# MessageAttributes wrappers
|
|
|
|
## Low level attributes access
|
|
@property
|
|
def security_attribute(self):
|
|
return self.attributes.get_attribute(gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE)
|
|
|
|
@property
|
|
def view_attribute(self):
|
|
return self.attributes.get_attribute(gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE)
|
|
|
|
@property
|
|
def context_attribute(self):
|
|
return self.attributes.get_attribute(gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE)
|
|
|
|
@property
|
|
def handle_attribute(self):
|
|
return self.attributes.get_attribute(gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE)
|
|
|
|
## Low level validity check (Test)
|
|
@property
|
|
def view_is_valid(self): # Change the name ?
|
|
return self.attributes.is_valid(gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE)
|
|
|
|
@property
|
|
def security_is_valid(self): # Change the name ?
|
|
return self.attributes.is_valid(gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE)
|
|
|
|
@property
|
|
def handle_is_valid(self): # Change the name ?
|
|
return self.attributes.is_valid(gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE)
|
|
|
|
@property
|
|
def context_is_valid(self): # Change the name ?
|
|
return self.attributes.is_valid(gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE)
|
|
|
|
@property
|
|
def valid_attributes(self):
|
|
return self.attributes.valid_list
|
|
|
|
@property
|
|
def allocated_attributes(self):
|
|
return self.attributes.allocated_list
|
|
|
|
## High level setup (Test)
|
|
def setup_view(self, size, section_handle=0, flags=None):
|
|
raise NotImplementedError(self.setup_view)
|
|
|
|
|
|
|
|
class AlpcMessagePort(gdef.PORT_MESSAGE):
|
|
# Constructeur
|
|
@classmethod
|
|
def from_buffer(self, buffer):
|
|
# A sort of super(AlpcMessagePort).from_buffer
|
|
# But from_buffer is from the Metaclass of AlpcMessagePort so we use 'type(AlpcMessagePort)'
|
|
# To access the standard version of from_buffer.
|
|
self = type(AlpcMessagePort).from_buffer(AlpcMessagePort, buffer)
|
|
self.buffer_size = len(buffer)
|
|
self.raw_buffer = buffer
|
|
self.header_size = ctypes.sizeof(self)
|
|
self.max_datasize = self.buffer_size - self.header_size
|
|
return self
|
|
|
|
@classmethod
|
|
def from_buffer_size(cls, buffer_size):
|
|
buffer = ctypes.c_buffer(buffer_size)
|
|
return cls.from_buffer(buffer)
|
|
|
|
def read_data(self):
|
|
return self.raw_buffer[ctypes.sizeof(self):ctypes.sizeof(self) + self.u1.s1.DataLength]
|
|
|
|
def write_data(self, data):
|
|
if len(data) > self.max_datasize:
|
|
raise ValueError("Cannot write data of len <{0}> (raw_buffer size == <{1}>)".format(len(data), self.buffer_size))
|
|
self.raw_buffer[self.header_size: self.header_size + len(data)] = data
|
|
self.set_datalen(len(data))
|
|
|
|
data = property(read_data, write_data)
|
|
|
|
def set_datalen(self, datalen):
|
|
self.u1.s1.TotalLength = self.header_size + datalen
|
|
self.u1.s1.DataLength = datalen
|
|
|
|
def get_datalen(self):
|
|
return self.u1.s1.DataLength
|
|
|
|
datalen = property(get_datalen, set_datalen)
|
|
|
|
KNOWN_ALPC_ATTRIBUTES = (gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE,
|
|
gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE,
|
|
gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE,
|
|
gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE,
|
|
gdef.ALPC_MESSAGE_TOKEN_ATTRIBUTE,
|
|
gdef.ALPC_MESSAGE_DIRECT_ATTRIBUTE,
|
|
gdef.ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE)
|
|
|
|
KNOWN_ALPC_ATTRIBUTES_MAPPING = {x:x for x in KNOWN_ALPC_ATTRIBUTES}
|
|
|
|
|
|
class MessageAttribute(gdef.ALPC_MESSAGE_ATTRIBUTES):
|
|
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),
|
|
(gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE, gdef.ALPC_HANDLE_ATTR),
|
|
(gdef.ALPC_MESSAGE_TOKEN_ATTRIBUTE, gdef.ALPC_TOKEN_ATTR),
|
|
(gdef.ALPC_MESSAGE_DIRECT_ATTRIBUTE, gdef.ALPC_DIRECT_ATTR),
|
|
(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)
|
|
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)
|
|
return self
|
|
|
|
@classmethod
|
|
def with_all_attributes(cls):
|
|
return cls.with_attributes(gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE |
|
|
gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE |
|
|
gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE |
|
|
gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE |
|
|
gdef.ALPC_MESSAGE_TOKEN_ATTRIBUTE |
|
|
gdef.ALPC_MESSAGE_DIRECT_ATTRIBUTE |
|
|
gdef.ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE)
|
|
|
|
|
|
@staticmethod
|
|
def _get_required_buffer_size(flags):
|
|
res = gdef.DWORD()
|
|
try:
|
|
windows.winproxy.AlpcInitializeMessageAttribute(flags, None, 0, res)
|
|
except windows.generated_def.ntstatus.NtStatusException as e:
|
|
# Buffer too small: osef
|
|
return res.value
|
|
return res.value
|
|
|
|
def is_allocated(self, value):
|
|
return bool(self.AllocatedAttributes & value)
|
|
|
|
def is_valid(self, value):
|
|
return bool(self.ValidAttributes & value)
|
|
|
|
def get_attribute(self, attribute):
|
|
if not self.is_allocated(attribute):
|
|
raise ValueError("Cannot get non-allocated attribute <{0}>".format(attribute))
|
|
offset = ctypes.sizeof(self)
|
|
for sflag, struct in self.ATTRIBUTE_BY_FLAG:
|
|
if sflag == attribute:
|
|
# print("Attr {0:#x} was at offet {1:#x}".format(attribute, offset))
|
|
return struct.from_address(ctypes.addressof(self) + offset)
|
|
elif self.is_allocated(sflag):
|
|
offset += ctypes.sizeof(struct)
|
|
raise ValueError("ALPC Attribute <{0}> not found :(".format(attribute))
|
|
|
|
def _extract_alpc_attributes_values(self, value):
|
|
attrs = []
|
|
for mask in (1 << i for i in range(64)):
|
|
if value & mask:
|
|
attrs.append(mask)
|
|
return [KNOWN_ALPC_ATTRIBUTES_MAPPING.get(x, x) for x in attrs]
|
|
|
|
@property
|
|
def valid_list(self):
|
|
return self._extract_alpc_attributes_values(self.ValidAttributes)
|
|
|
|
@property
|
|
def allocated_list(self):
|
|
return self._extract_alpc_attributes_values(self.AllocatedAttributes)
|
|
|
|
|
|
AlpcSection = namedtuple("AlpcSection", ["handle", "size"])
|
|
|
|
class AlpcTransportBase(object):
|
|
def send_receive(self, alpc_message, receive_msg=None, flags=gdef.ALPC_MSGFLG_SYNC_REQUEST):
|
|
if isinstance(alpc_message, basestring):
|
|
raw_alpc_message = alpc_message
|
|
alpc_message = AlpcMessage(max(0x1000, len(alpc_message)))
|
|
alpc_message.port_message.data = raw_alpc_message
|
|
|
|
if receive_msg is None:
|
|
receive_msg = AlpcMessage(0x1000)
|
|
receive_size = gdef.SIZE_T(receive_msg.port_message_buffer_size)
|
|
winproxy.NtAlpcSendWaitReceivePort(self.handle, flags, alpc_message.port_message, alpc_message.attributes, receive_msg.port_message, receive_size, receive_msg.attributes, None)
|
|
return receive_msg
|
|
|
|
def send(self, alpc_message, flags=0):
|
|
if isinstance(alpc_message, basestring):
|
|
raw_alpc_message = alpc_message
|
|
alpc_message = AlpcMessage(max(0x1000, len(alpc_message)))
|
|
alpc_message.port_message.data = raw_alpc_message
|
|
winproxy.NtAlpcSendWaitReceivePort(self.handle, flags, alpc_message.port_message, alpc_message.attributes, None, None, None, None)
|
|
|
|
def recv(self, receive_msg=None, flags=0):
|
|
if receive_msg is None:
|
|
receive_msg = AlpcMessage(0x1000)
|
|
receive_size = gdef.SIZE_T(receive_msg.port_message_buffer_size)
|
|
winproxy.NtAlpcSendWaitReceivePort(self.handle, flags, None, None, receive_msg.port_message, receive_size, receive_msg.attributes, None)
|
|
return receive_msg
|
|
|
|
|
|
|
|
class AlpcClient(AlpcTransportBase):
|
|
DEFAULT_MAX_MESSAGE_LENGTH = 0x1000
|
|
|
|
def __init__(self, port_name=None):
|
|
self.handle = None
|
|
self.portname = None
|
|
if port_name is not None:
|
|
x = self.connect_to_port(port_name, "")
|
|
|
|
def _alpc_port_to_unicode_string(self, name):
|
|
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):
|
|
# TODO raise on mutual exclusive parameter
|
|
if self.handle is not None:
|
|
raise ValueError("Client already connected")
|
|
handle = gdef.HANDLE()
|
|
port_name_unicode = self._alpc_port_to_unicode_string(port_name)
|
|
|
|
if port_attr is None:
|
|
port_attr = gdef.ALPC_PORT_ATTRIBUTES()
|
|
port_attr.Flags = port_attr_flags # Flag qui fonctionne pour l'UAC
|
|
port_attr.MaxMessageLength = self.DEFAULT_MAX_MESSAGE_LENGTH
|
|
port_attr.MemoryBandwidth = 0
|
|
port_attr.MaxPoolUsage = 0xffffffff
|
|
port_attr.MaxSectionSize = 0xffffffff
|
|
port_attr.MaxViewSize = 0xffffffff
|
|
port_attr.MaxTotalSectionSize = 0xffffffff
|
|
port_attr.DupObjectTypes = 0xffffffff
|
|
|
|
port_attr.SecurityQos.Length = ctypes.sizeof(port_attr.SecurityQos)
|
|
port_attr.SecurityQos.ImpersonationLevel = gdef.SecurityImpersonation
|
|
port_attr.SecurityQos.ContextTrackingMode = 0
|
|
port_attr.SecurityQos.EffectiveOnly = 0
|
|
|
|
if connect_message is None:
|
|
send_msg = None
|
|
send_msg_attr = None
|
|
buffersize = None
|
|
elif isinstance(connect_message, basestring):
|
|
buffersize = gdef.DWORD(len(connect_message) + 0x1000)
|
|
send_msg = AlpcMessagePort.from_buffer_size(buffersize.value)
|
|
send_msg.data = connect_message
|
|
send_msg_attr = MessageAttribute.with_all_attributes()
|
|
elif isinstance(connect_message, AlpcMessage):
|
|
send_msg = connect_message.port_message
|
|
send_msg_attr = connect_message.attributes
|
|
buffersize = gdef.DWORD(connect_message.port_message_buffer_size)
|
|
else:
|
|
raise ValueError("Don't know how to send <{0!r}> as connect message".format(connect_message))
|
|
|
|
receive_attr = MessageAttribute.with_all_attributes()
|
|
winproxy.NtAlpcConnectPort(handle, port_name_unicode, obj_attr, port_attr, flags, None, send_msg, buffersize, send_msg_attr, receive_attr, timeout)
|
|
# If send_msg is not None, it contains the ClientId.UniqueProcess : PID of the server :)
|
|
self.handle = handle.value
|
|
self.portname = port_name
|
|
return AlpcMessage(send_msg, receive_attr) if send_msg is not None else None
|
|
|
|
def create_port_section(self, Flags, SectionHandle, SectionSize):
|
|
AlpcSectionHandle = gdef.HANDLE()
|
|
ActualSectionSize = gdef.SIZE_T()
|
|
# RPCRT4 USE FLAGS 0x40000 ALPC_VIEWFLG_NOT_SECURE ?
|
|
winproxy.NtAlpcCreatePortSection(self.handle, Flags, SectionHandle, SectionSize, AlpcSectionHandle, ActualSectionSize)
|
|
return AlpcSection(AlpcSectionHandle.value, ActualSectionSize.value)
|
|
|
|
def map_section(self, section_handle, size, flags=0):
|
|
view_attributes = gdef.ALPC_DATA_VIEW_ATTR()
|
|
view_attributes.Flags = 0
|
|
view_attributes.SectionHandle = section_handle
|
|
view_attributes.ViewBase = 0
|
|
view_attributes.ViewSize = size
|
|
r = winproxy.NtAlpcCreateSectionView(self.handle, flags, view_attributes)
|
|
return view_attributes
|
|
|
|
|
|
class AlpcServer(AlpcTransportBase):
|
|
DEFAULT_MAX_MESSAGE_LENGTH = 0x1000
|
|
|
|
def __init__(self, port_name=None):
|
|
self.port_name = None
|
|
if port_name is not None:
|
|
self.create_port(port_name)
|
|
|
|
def _alpc_port_to_unicode_string(self, name):
|
|
utf16_len = len(name) * 2
|
|
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):
|
|
# TODO raise on mutual exclusive parameter (port_attr + port_attr_flags | obj_attr + msglen)
|
|
handle = gdef.HANDLE()
|
|
raw_name = port_name
|
|
if not raw_name.startswith("\\"):
|
|
raw_name = "\\" + port_name
|
|
port_name = self._alpc_port_to_unicode_string(raw_name)
|
|
|
|
if msglen is None:
|
|
msglen = self.DEFAULT_MAX_MESSAGE_LENGTH
|
|
if obj_attr is None:
|
|
obj_attr = gdef.OBJECT_ATTRIBUTES()
|
|
obj_attr.Length = ctypes.sizeof(obj_attr)
|
|
obj_attr.RootDirectory = None
|
|
obj_attr.ObjectName = ctypes.pointer(port_name)
|
|
obj_attr.Attributes = 0
|
|
obj_attr.SecurityDescriptor = None
|
|
obj_attr.SecurityQualityOfService = None
|
|
if port_attr is None:
|
|
port_attr = gdef.ALPC_PORT_ATTRIBUTES()
|
|
# port_attr.Flags = port_attr_flags
|
|
# port_attr.Flags = 0x2080000
|
|
port_attr.Flags = 0x90000
|
|
port_attr.MaxMessageLength = msglen
|
|
port_attr.MemoryBandwidth = 0
|
|
port_attr.MaxPoolUsage = 0xffffffff
|
|
port_attr.MaxSectionSize = 0xffffffff
|
|
port_attr.MaxViewSize = 0xffffffff
|
|
port_attr.MaxTotalSectionSize = 0xffffffff
|
|
port_attr.DupObjectTypes = 0xffffffff
|
|
# windows.utils.print_ctypes_struct(port_attr, " - PORT_ATTR", hexa=True)
|
|
|
|
winproxy.NtAlpcCreatePort(handle, obj_attr, port_attr)
|
|
self.port_name = raw_name
|
|
self.handle = handle.value
|
|
|
|
def accept_connection(self, msg, port_attr=None, port_context=None):
|
|
rhandle = gdef.HANDLE()
|
|
|
|
if port_attr is None:
|
|
port_attr = gdef.ALPC_PORT_ATTRIBUTES()
|
|
port_attr.Flags = 0x80000
|
|
# port_attr.Flags = 0x80000 + 0x2000000
|
|
# port_attr.Flags = 0x2000000
|
|
port_attr.MaxMessageLength = self.DEFAULT_MAX_MESSAGE_LENGTH
|
|
port_attr.MemoryBandwidth = 0
|
|
port_attr.MaxPoolUsage = 0xffffffff
|
|
port_attr.MaxSectionSize = 0xffffffff
|
|
port_attr.MaxViewSize = 0xffffffff
|
|
port_attr.MaxTotalSectionSize = 0xffffffff
|
|
port_attr.DupObjectTypes = 0xffffffff
|
|
# windows.utils.print_ctypes_struct(port_attr, " - CONN_PORT_ATTR", hexa=True)
|
|
winproxy.NtAlpcAcceptConnectPort(rhandle, self.handle, 0, None, port_attr, port_context, msg.port_message, None, True)
|
|
return rhandle.value, msg |