diff --git a/windows/alpc.py b/windows/alpc.py index 430268c..f7ea80d 100644 --- a/windows/alpc.py +++ b/windows/alpc.py @@ -1,66 +1,121 @@ import ctypes +from collections import namedtuple import windows from windows import winproxy -from windows import generated_def as gn +from windows import generated_def as gdef -ALPC_MSGFLG_REPLY_MESSAGE = 0x1 -ALPC_MSGFLG_LPC_MODE = 0x2 -ALPC_MSGFLG_RELEASE_MESSAGE = 0x10000 -ALPC_MSGFLG_SYNC_REQUEST = 0x20000 -ALPC_MSGFLG_WAIT_USER_MODE = 0x100000 -ALPC_MSGFLG_WAIT_ALERTABLE = 0x200000 -ALPC_MSGFLG_WOW64_CALL = 0x80000000 -ALPC_MESSAGE_SECURITY_ATTRIBUTE = 0x80000000 -ALPC_MESSAGE_VIEW_ATTRIBUTE = 0x40000000 -ALPC_MESSAGE_CONTEXT_ATTRIBUTE = 0x20000000 -ALPC_MESSAGE_HANDLE_ATTRIBUTE = 0x10000000 +class AlpcMessage(object): + # PORT_MESSAGE + MessageAttribute + def __init__(self, msg_or_size=None, 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) + + # Init the MessageAttributes + if attributes is None: + self.attributes = MessageAttribute.with_all_attributes() + 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) + + + ## High level setup (Test) + def setup_view(self, size, section_handle=0, flags=None): + raise NotImplementedError(self.setup_view) -class AlpcMessage(gn.PORT_MESSAGE): - def __new__(cls, buffersize): - size = ctypes.sizeof(cls) + buffersize - buffer = ctypes.c_buffer(size) - self = cls.from_buffer(buffer) +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 - def __init__(self, buffersize): - self.u1.s1.TotalLength = buffersize + ctypes.sizeof(self) - self.u1.s1.DataLength = buffersize - return super(AlpcMessage, self).__init__() + @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): - self.raw_buffer[ctypes.sizeof(self): ctypes.sizeof(self) + len(data)] = 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) -class MessageAttribute(gn.ALPC_MESSAGE_ATTRIBUTES): - # def __new__(cls, flags): - # size = cls._get_required_buffer_size(flags) - # buffer = ctypes.c_buffer(size) - # self = cls.from_buffer(buffer) - # self.raw_buffer = buffer - # return self - ATTRIBUTE_BY_FLAG = [(gn.ALPC_MESSAGE_SECURITY_ATTRIBUTE, gn.ALPC_SECURITY_ATTR), - (gn.ALPC_MESSAGE_VIEW_ATTRIBUTE, gn.ALPC_DATA_VIEW_ATTR), - (gn.ALPC_MESSAGE_CONTEXT_ATTRIBUTE, gn.ALPC_CONTEXT_ATTR), - (gn.ALPC_MESSAGE_HANDLE_ATTRIBUTE, gn.ALPC_HANDLE_ATTR)] + 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) -#define ALPC_MESSAGE_SECURITY_ATTRIBUTE 0x80000000 -#define ALPC_MESSAGE_VIEW_ATTRIBUTE 0x40000000 -#define ALPC_MESSAGE_CONTEXT_ATTRIBUTE 0x20000000 -#define ALPC_MESSAGE_HANDLE_ATTRIBUTE 0x10000000 - def __init__(self, flags): - res = gn.DWORD() - winproxy.AlpcInitializeMessageAttribute(flags, self, len(self.raw_buffer), res) +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)] @classmethod def with_attributes(cls, flags): @@ -68,13 +123,20 @@ class MessageAttribute(gn.ALPC_MESSAGE_ATTRIBUTES): buffer = ctypes.c_buffer(size) self = cls.from_buffer(buffer) self.raw_buffer = buffer - res = gn.DWORD() + 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) + @staticmethod def _get_required_buffer_size(flags): - res = gn.DWORD() + res = gdef.DWORD() try: windows.winproxy.AlpcInitializeMessageAttribute(flags, None, 0, res) except windows.generated_def.ntstatus.NtStatusException as e: @@ -83,314 +145,181 @@ class MessageAttribute(gn.ALPC_MESSAGE_ATTRIBUTES): return res.value def is_allocated(self, value): - return self.AllocatedAttributes & value + return bool(self.AllocatedAttributes & value) def is_valid(self, value): - return self.ValidAttributes & value + return bool(self.ValidAttributes & value) - def get_attribute(self, flag): + 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 == flag: + if sflag == attribute: return struct.from_address(ctypes.addressof(self) + offset) elif self.is_allocated(sflag): offset += ctypes.sizeof(struct) - raise ValueError("ALPC Attribute Flag not found :(") + raise ValueError("ALPC Attribute <{0}> not found :(".format(attribute)) + +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 - -class AlpcPORT(object): - def __init__(self, port_name, msglen=0x1000): - self.port_name = port_name - self.handle = self._create_port(port_name, msglen) - - def _create_port(self, port_name, msglen=0x1000): - handle = gn.HANDLE() - raw_name = port_name - if not raw_name.startswith("\\"): - raw_name = "\\" + port_name - utf16_len = len(raw_name) * 2 - port_name = gn.UNICODE_STRING(utf16_len, utf16_len, raw_name) - - obj_attr = gn.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 - - port_attr = gn.ALPC_PORT_ATTRIBUTES() - port_attr.Flags = 0 - # port_attr.Flags = 0x2080000 # Test - port_attr.MaxMessageLength = msglen - port_attr.MemoryBandwidth = 0 - port_attr.MaxPoolUsage = 0 - - winproxy.NtAlpcCreatePort(handle, obj_attr, port_attr) - return handle.value - -#class AlpcExchange(object): -# def send_receive_data(port_handle, data): -# raw_sendmsg = ctypes.c_buffer(0x1000) -# size = gn.SIZE_T(0x1000) -# sendmsg = ctypes.cast(raw_sendmsg, gn.PPORT_MESSAGE) -# buffer = ctypes.c_buffer(0x200) -# sendmsg_attr = ctypes.cast(buffer, gn.PALPC_MESSAGE_ATTRIBUTES) -# res = gn.DWORD() -# winproxy.AlpcInitializeMessageAttribute(ALPC_MESSAGE_CONTEXT_ATTRIBUTE + ALPC_MESSAGE_HANDLE_ATTRIBUTE + 1, sendmsg_attr , 0x200, res) -# -# sendmsg = AlpcMessage(len(data)) -# sendmsg.data = data -# -# size = gn.SIZE_T(0x1000) -# receive = AlpcMessage(size.value) -# buffer = ctypes.c_buffer(0x200) -# receive_attr = ctypes.cast(buffer, gn.PALPC_MESSAGE_ATTRIBUTES) -# res = gn.DWORD() -# -# winproxy.NtAlpcSendWaitReceivePort(port_handle, ALPC_MSGFLG_SYNC_REQUEST, sendmsg, sendmsg_attr, receive, size, receive_attr, None) -# return receive, receive_attr - - -def send_receive_data(port_handle, data): - # sendmsg_attr = MessageAttribute.with_attributes(ALPC_MESSAGE_VIEW_ATTRIBUTE) - # sendmsg_attr.ValidAttributes = ALPC_MESSAGE_VIEW_ATTRIBUTE - sendmsg_attr = MessageAttribute.with_attributes(0) - sendmsg = AlpcMessage(len(data)) - sendmsg.data = data - - # import pdb;pdb.set_trace() - - size = gn.SIZE_T(0x1000) - receive = AlpcMessage(size.value) - receive_attr = MessageAttribute.with_attributes(ALPC_MESSAGE_VIEW_ATTRIBUTE) - # Its strange that this line does not always have the same effect has the one bellow - # winproxy.NtAlpcSendWaitReceivePort(port_handle, ALPC_MSGFLG_SYNC_REQUEST, sendmsg, sendmsg_attr, receive, ctypes.byref(size), receive_attr, None) - winproxy.NtAlpcSendWaitReceivePort(port_handle, ALPC_MSGFLG_SYNC_REQUEST, sendmsg, sendmsg_attr, receive, size, receive_attr, None) - # winproxy.NtAlpcSendWaitReceivePort(port_handle, 0x40020000, sendmsg, sendmsg_attr, receive, size, receive_attr, None) - return receive_attr, receive - - -class ALPC_DATA_VIEW_ATTR(ctypes.Structure): # _ALPC_DATA_VIEW_ATTR - _fields_ = [ - ("Flags", gn.ULONG), - ("SectionHandle", gn.HANDLE), - ("ViewBase", gn.ULONG), # must be zero on input - ("ViewSize", gn.ULONG) - ] - -class ALPC_DATA_VIEW_ATTR64(ctypes.Structure): # _ALPC_DATA_VIEW_ATTR - _fields_ = [ - ("Flags", gn.ULONG), - ("SectionHandle", gn.HANDLE), - ("ViewBase", gn.ULONGLONG), # must be zero on input - ("ViewSize", gn.ULONGLONG) - ] - -def send_receive_data_view(port_handle, data, view): - sendmsg_attr = MessageAttribute.with_attributes(ALPC_MESSAGE_VIEW_ATTRIBUTE) - sendmsg_attr.ValidAttributes = ALPC_MESSAGE_VIEW_ATTRIBUTE - - view_attr = ALPC_DATA_VIEW_ATTR64.from_address(ctypes.addressof(sendmsg_attr) + 8) - # view_attr.Flags = 0x60000 # 0x20000 -> Unmap la section dans le sender - view_attr.Flags = 0x40000 - # view_attr.Flags = 0x10000 - view_attr.SectionHandle = view.SectionHandle - view_attr.ViewBase = view.ViewBase - view_attr.ViewSize = view.ViewSize - - xx = windows.winproxy.AlpcGetMessageAttribute(sendmsg_attr, ALPC_MESSAGE_VIEW_ATTRIBUTE) - - - sendmsg = AlpcMessage(len(data)) - sendmsg.data = data - - - print(sendmsg_attr.ValidAttributes) - - size = gn.SIZE_T(0x1000) - receive = AlpcMessage(size.value) - receive_attr = MessageAttribute.with_attributes(ALPC_MESSAGE_VIEW_ATTRIBUTE) - receive_attr.ValidAttributes = ALPC_MESSAGE_VIEW_ATTRIBUTE - # Its strange that this line does not always have the same effect has the one bellow - # winproxy.NtAlpcSendWaitReceivePort(port_handle, ALPC_MSGFLG_SYNC_REQUEST, sendmsg, sendmsg_attr, receive, ctypes.byref(size), receive_attr, None) - # winproxy.NtAlpcSendWaitReceivePort(port_handle, 0, sendmsg, sendmsg_attr, None, size, None, None) - winproxy.NtAlpcSendWaitReceivePort(port_handle, ALPC_MSGFLG_SYNC_REQUEST , sendmsg, sendmsg_attr, receive, size, receive_attr, None) - # winproxy.NtAlpcSendWaitReceivePort(port_handle, 0x000000000410000, sendmsg, sendmsg_attr, None, None, None, None) - # 0000000000410000 # Flags ? - print(hex(windows.current_process.query_memory(view.ViewBase).State)) - print(hex(windows.current_process.query_memory(view.ViewBase).Protect)) - return receive_attr, receive - - - -class AlpcClient(object): - def __init__(self): - self.portname = None + 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 connect_to_port(self, port_name, connect_msg=None, maxmsglen=0x1000): + 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 = gn.HANDLE() + handle = gdef.HANDLE() + port_name_unicode = self._alpc_port_to_unicode_string(port_name) - #raw_name = "\\" + port_name - raw_name = port_name - utf16_len = len(raw_name) * 2 + 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 = 0 - port_name = gn.UNICODE_STRING(utf16_len, utf16_len, raw_name) - - # obj_attr = gn.OBJECT_ATTRIBUTES() - # obj_attr.Length = ctypes.sizeof(obj_attr) - # obj_attr.RootDirectory = None - # obj_attr.ObjectName = None - # obj_attr.Attributes = 0 - # obj_attr.SecurityDescriptor = None - # obj_attr.SecurityQualityOfService = None - - obj_attr = None - - - port_attr = gn.ALPC_PORT_ATTRIBUTES() - port_attr.Flags = 0 - port_attr.MaxMessageLength = maxmsglen - port_attr.MemoryBandwidth = 0 - port_attr.MaxPoolUsage = 0 - - if True: - port_attr.SecurityQos.Length = 12 - port_attr.SecurityQos.ImpersonationLevel = 2 + 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 - - #define ALPC_PORFLG_ALLOW_LPC_REQUESTS 0x20000 // rev - #define ALPC_PORFLG_WAITABLE_PORT 0x40000 // dbg - #define ALPC_PORFLG_SYSTEM_PROCESS 0x100000 // dbg - - #port_attr.MaxPoolUsage = 0 - port_attr.Flags = 0x10000 # Flag qui fonctionne pour l'UAC - port_attr.Flags = 0x2090000 # Test - port_attr.Flags = 0x2080000 # Test # Tes2 - # 0x0010000 est le flag qui permet l'impersonation (en tout cas le pop UAC) - #port_attr.MaxPoolUsage = 4294967295 - #port_attr.MaxSectionSize = 4294967295 - port_attr.MaxViewSize = 4294967295 - #port_attr.MaxTotalSectionSize = 4294967295 - #port_attr.DupObjectTypes = 4093 - - # tst.Flags -> 34144256 - # tst.SecurityQos.Length -> 12 - # tst.SecurityQos.ImpersonationLevel -> SecurityImpersonation(0x2L) - # tst.SecurityQos.ContextTrackingMode -> 0 - # tst.SecurityQos.EffectiveOnly -> 0 - # tst.MaxMessageLength -> 4096 - # tst.MemoryBandwidth -> 0 - # tst.MaxPoolUsage -> 4294967295 - # tst.MaxSectionSize -> 4294967295 - # tst.MaxViewSize -> 4294967295 - # tst.MaxTotalSectionSize -> 4294967295 - # tst.DupObjectTypes -> 4093 - - if connect_msg is not None: - size = len(connect_msg) - send_msg = AlpcMessage(size) - send_msg.data = connect_msg - sendmsg_attr = MessageAttribute.with_attributes(0) - receive_attr = MessageAttribute.with_attributes(0) - receive_attr = None - sendmsg_attr = None - buffersize = gn.DWORD(len(send_msg.raw_buffer)) - else: - size = None + if connect_message is None: send_msg = None - sendmsg_attr = None - receive_attr = 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() + else: + raise NotImplementedError("TODO: connect_to_port with type(connect_message) == AlpcMessage") - #print(hex([0].AllocatedAttributes)) - #import pdb;pdb.set_trace() - x = winproxy.NtAlpcConnectPort(handle, port_name,obj_attr, port_attr, ALPC_MSGFLG_SYNC_REQUEST, None, send_msg, buffersize, sendmsg_attr, receive_attr, None) - + 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 - if connect_msg is not None: - return send_msg + return AlpcMessage(send_msg, receive_attr) if send_msg is not None else None - def send_receive(self, data): - return send_receive_data(self.handle, data) + 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 send_receive_view(self, data, view): - return send_receive_data_view(self.handle, data, view) + 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(object): - def __init__(self, port_name): - self.port = AlpcPORT(port_name) +class AlpcServer(AlpcTransportBase): + DEFAULT_MAX_MESSAGE_LENGTH = 0x1000 - def wait_data(self): - size = gn.SIZE_T(0x1000) - receive = AlpcMessage(size.value) - # receive_attr = MessageAttribute(0) - receive_attr = MessageAttribute.with_attributes(ALPC_MESSAGE_VIEW_ATTRIBUTE) - winproxy.NtAlpcSendWaitReceivePort(self.port.handle, 0, None, None, receive, size, receive_attr, None) - return receive_attr, receive + def __init__(self, port_name=None): + self.port_name = None + if port_name is not None: + self.create_port(port_name) - def accept_connection(self, msg): - port_handle = self.port.handle - rhandle = gn.HANDLE() + def _alpc_port_to_unicode_string(self, name): + utf16_len = len(name) * 2 + return gdef.UNICODE_STRING(utf16_len, utf16_len, name) - ALPC_HANDLEFLG_DUPLICATE_INHERIT = 0x80000 - port_attr = gn.ALPC_PORT_ATTRIBUTES() - port_attr.Flags = ALPC_HANDLEFLG_DUPLICATE_INHERIT - # port_attr.Flags = ALPC_HANDLEFLG_DUPLICATE_INHERIT + 0x30000 # Testing - # port_attr.Flags = 0x2080000 # Testing - port_attr.DupObjectTypes = 4 - port_attr.MaxMessageLength = 0x578 - port_attr.MemoryBandwidth = 0 - port_attr.MaxPoolUsage = 0x15E00 + 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) - winproxy.NtAlpcAcceptConnectPort(rhandle, port_handle, 0, None, port_attr, None, msg, None, 1) + 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 = 0 + 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 = 0 + + winproxy.NtAlpcCreatePort(handle, obj_attr, port_attr) + self.port_name = raw_name + self.handle = handle.value + + def accept_connection(self, msg, port_attr=None): + rhandle = gdef.HANDLE() + + if port_attr is None: + port_attr = gdef.ALPC_PORT_ATTRIBUTES() + port_attr.Flags = gdef.ALPC_HANDLEFLG_DUPLICATE_INHERIT + port_attr.Flags = 0 # Testing + port_attr.DupObjectTypes = 4 + port_attr.MaxMessageLength = 0x1000 + port_attr.MemoryBandwidth = 0 + port_attr.MaxPoolUsage = 0xffffffff + + winproxy.NtAlpcAcceptConnectPort(rhandle, self.handle, 0, None, port_attr, None, msg.port_message, None, True) return rhandle.value, msg - - def send_receive(self, data): - return send_receive_data(self.port.handle, data) - - def reply(self, reply_to_msg, reply_msg): - port_handle = self.port.handle - sendmsg = AlpcMessage(len(reply_msg)) - sendmsg.data = reply_msg - sendmsg_attr = MessageAttribute.with_attributes(0) - sendmsg.MessageId = reply_to_msg.MessageId - winproxy.NtAlpcSendWaitReceivePort(port_handle, ALPC_MSGFLG_RELEASE_MESSAGE, sendmsg, None, None, None, None, None) - return None, None - - - def reply_with_view(self, reply_to_msg, reply_msg, view): - - sendmsg_attr = MessageAttribute.with_attributes(ALPC_MESSAGE_VIEW_ATTRIBUTE) - sendmsg_attr.ValidAttributes = ALPC_MESSAGE_VIEW_ATTRIBUTE - - view_attr = ALPC_DATA_VIEW_ATTR.from_address(ctypes.addressof(sendmsg_attr) + 8) - view_attr.Flags = 0x60000 # 0x20000 -> Unmap la section dans le sender - # view_attr.Flags = 0x40000 - # view_attr.Flags = 0x40000 - view_attr.SectionHandle = view.SectionHandle - view_attr.ViewBase = view.ViewBase - view_attr.ViewSize = view.ViewSize - print("Section jandle = {0}".format(view.SectionHandle)) - - windows.current_process.write_memory(view.ViewBase, "SERRRRVVVVVV") - - port_handle = self.port.handle - sendmsg = AlpcMessage(len(reply_msg)) - sendmsg.data = reply_msg - # sendmsg_attr = MessageAttribute.with_attributes(0) - sendmsg.MessageId = reply_to_msg.MessageId - winproxy.NtAlpcSendWaitReceivePort(port_handle, 0x410000, sendmsg, sendmsg_attr, None, None, None, None) - return None, None - diff --git a/windows/rpc/__init__.py b/windows/rpc/__init__.py index 3e0e1e9..8e832fc 100644 --- a/windows/rpc/__init__.py +++ b/windows/rpc/__init__.py @@ -1,3 +1,5 @@ import ndr -from client import RPC_SYNTAX_IDENTIFIER, RPCClient +from client import (RPC_SYNTAX_IDENTIFIER, RPCClient, + REQUEST_TYPE_CALL, REQUEST_TYPE_BIND, + RESPONSE_TYPE_BIND_OK, RESPONSE_TYPE_FAIL, RESPONSE_TYPE_SUCESS) from epmapper import find_alpc_endpoint_and_connect, endpoint_map_alpc, construct_alpc_tower \ No newline at end of file diff --git a/windows/rpc/client.py b/windows/rpc/client.py index d9c4389..892b163 100644 --- a/windows/rpc/client.py +++ b/windows/rpc/client.py @@ -1,6 +1,6 @@ -import windows.alpc as alpc +import windows.alpc2 as alpc import windows.com -from windows.generated_def import USHORT +from windows.generated_def import USHORT, DWORD, CHAR import ctypes import struct @@ -42,24 +42,49 @@ KNOW_RESPONSE_TYPE = { KNOWN_RPC_ERROR_CODE = { 1783 : "RPC_X_BAD_STUB_DATA", 1717 : "RPC_S_UNKNOWN_IF", - 1745 : "RPC_S_PROCNUM_OUT_OF_RANGE" - + 1728 : "RPC_S_PROTOCOL_ERROR", + 1730 : "RPC_S_UNSUPPORTED_TRANS_SYN", + 1745 : "RPC_S_PROCNUM_OUT_OF_RANGE", } +BIND_IF_SYNTAX_NDR32 = 1 +BIND_IF_SYNTAX_NDR64 = 2 +BIND_IF_SYNTAX_UNKNOWN = 4 + NOT_USED = 0xBAADF00D +class ALPC_RPC_BIND(ctypes.Structure): + _pack_ = 1 + _fields_ = [ + ("request_type", DWORD), + ("UNK1", DWORD), + ("UNK2", DWORD), + ("target", RPC_SYNTAX_IDENTIFIER), + ("flags", DWORD), + ("if_nb_ndr32", USHORT), + ("if_nb_ndr64", USHORT), + ("if_nb_unkn", USHORT), + ("PAD", USHORT), + ("register_multiple_syntax", DWORD), + ("use_flow", DWORD), + ("UNK5", DWORD), + ("maybe_flow_id", DWORD), + ("UNK7", DWORD), + ("some_context_id", DWORD), + ("UNK9", DWORD), + ] + class RPCClient(object): REQUEST_IDENTIFIER = 0x11223344 def __init__(self, port): - self.aplc_client = alpc.AlpcClient() - self.aplc_client.connect_to_port(port) + self.alpc_client = alpc.AlpcClient(port) self.number_of_bind_if = 0 # if -> interface self.if_bind_number = {} def bind(self, IID_str, version=(1,0)): IID = windows.com.IID.from_string(IID_str) - request = self._forge_bind_request(buffer(IID)[:], version, self.number_of_bind_if) + request = self._forge_bind_request(IID, version, self.number_of_bind_if) response = self._send_request(request) # Parse reponse request_type = self._get_request_type(response) @@ -86,21 +111,31 @@ class RPCClient(object): return response[4 * 6:] # Should be the return value (not completly verified) def _send_request(self, request): - resp_attr, resp = self.aplc_client.send_receive(request) - return resp.data - - def _forge_bind_request(self, rawuuid, syntaxversion, requested_if_nb): - version_major, version_minor = syntaxversion - data = struct.pack("III16sHHII8I", REQUEST_TYPE_BIND, NOT_USED, NOT_USED, rawuuid, version_major, version_minor, NOT_USED, requested_if_nb, *[NOT_USED] * 8) - return data + response = self.alpc_client.send_receive(request) + return response.data def _forge_call_request(self, interface_nb, method_offset, params): # TODO: differents REQUEST_IDENTIFIER for each req ? # TODO: what is this '0' ? (1 is also accepted) (flags ?) - request = struct.pack("<16I", REQUEST_TYPE_CALL, NOT_USED, 0, self.REQUEST_IDENTIFIER, interface_nb, method_offset, *[NOT_USED] * 10) + request = struct.pack("<16I", REQUEST_TYPE_CALL, NOT_USED, 1, self.REQUEST_IDENTIFIER, interface_nb, method_offset, *[NOT_USED] * 10) request += params return request + def _forge_bind_request(self, uuid, syntaxversion, requested_if_nb): + # if syntaxversion == (1, 0): + # requested_if_nb = 0x10000 + version_major, version_minor = syntaxversion + req = ALPC_RPC_BIND() + req.request_type = REQUEST_TYPE_BIND + req.target = RPC_SYNTAX_IDENTIFIER(uuid, *syntaxversion) + req.flags = BIND_IF_SYNTAX_NDR32 + req.if_nb_ndr32 = requested_if_nb + req.if_nb_ndr64 = 0 + req.if_nb_unkn = 0 + req.register_multiple_syntax = False + req.some_context_id = 0xB00B00B + return buffer(req)[:] + def _get_request_type(self, response): "raise if request_type == RESPONSE_TYPE_FAIL" request_type = struct.unpack("