From b9a00b2ed7b37e8bdb28276089a8ea4fd102e573 Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Thu, 14 Sep 2017 17:24:18 +0200 Subject: [PATCH] add rpc.rst doc + rename endpoint_map_alpc + rename ndr EptMapAuth --- docs/source/rpc.rst | 230 ++++++++++++++++++++++++++++++++++++++++ windows/alpc.py | 4 +- windows/rpc/__init__.py | 2 +- windows/rpc/epmapper.py | 25 +++-- windows/rpc/ndr.py | 49 ++++++++- 5 files changed, 296 insertions(+), 14 deletions(-) create mode 100644 docs/source/rpc.rst diff --git a/docs/source/rpc.rst b/docs/source/rpc.rst new file mode 100644 index 0000000..cebd434 --- /dev/null +++ b/docs/source/rpc.rst @@ -0,0 +1,230 @@ +``windows.rpc`` -- ALPC-based Windows RPC +***************************************** + +.. module:: windows.rpc + +The :mod:`windows.rpc` allows to perform the basic for MS-RPC: + + * find interface endpoints + * connect to it + * bind to interfaces + * perform call + * Marshall/Unmarshall NDR + +.. note:: + + See UAC POC + + +RPCClient +--------- + +.. autoclass:: RPCClient + + +Epmapper +-------- + +.. autoclass:: windows.rpc.epmapper.UnpackTower + :exclude-members: count, index + +:func:`find_alpc_endpoints` +''''''''''''''''''''''''' + +.. autofunction:: find_alpc_endpoints + +Example: + +.. code-block:: python + + >>> import windows.rpc + >>> UAC_UIID = "201ef99a-7fa0-444c-9399-19ba84f12a1a" + >>> windows.rpc.find_alpc_endpoints(UAC_UIID) + [UnpackTower(protseq='ncalrpc', + endpoint=bytearray(b'LRPC-c30c67fef2afa1612b'), + address=None, + object=, + syntax=)] + + +:func:`find_alpc_endpoint_and_connect` +'''''''''''''''''''''''''''''''''''''' + +.. autofunction:: find_alpc_endpoint_and_connect + +Example: + +.. code-block:: python + + >>> import windows.rpc + >>> UAC_UIID = "201ef99a-7fa0-444c-9399-19ba84f12a1a" + >>> client = windows.rpc.find_alpc_endpoint_and_connect(UAC_UIID) + >>> client + + >>> client.alpc_client.portname + '\\RPC Control\\LRPC-c30c67fef2afa1612b' + >>> iid = client.bind(UAC_UIID) + >>> iid + + +Ndr +--- + +.. module:: windows.rpc.ndr + +The :mod:`windows.rpc.ndr` module offers some construction to help marshalling types and structures to NDR. + +.. note:: + + The NDR supported for now is ``8a885d04-1ceb-11c9-9fe8-08002b104860`` version ``2.0`` + +Each NDR class has a function :func:`pack`. + + +.. autoclass:: NdrSID + +.. code-block:: python + + >>> import windows.generated_def as gdef + >>> sid = windows.utils.get_known_sid(gdef.WinLocalSystemSid) + >>> sid + c_void_p(78304040) + >>> psidstr = windows.rpc.ndr.NdrSID.pack(sid) + >>> psidstr + '\x01\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x05\x12\x00\x00\x00' + >>> windows.rpc.ndr.NdrSID.unpack(windows.rpc.ndr.NdrStream(psidstr)) + # Implementation is partial for now and does not return a PSID but a string + '\x01\x01\x00\x00\x00\x00\x00\x05\x12\x00\x00\x00' + +.. autoclass:: NdrWString + +.. code-block:: python + + >>> from windows.rpc import ndr + >>> x = ndr.NdrWString.pack("Test-String\x00") + >>> x + '\x0c\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00T\x00e\x00s\x00t\x00-\x00S\x00t\x00r\x00i\x00n\x00g\x00\x00\x00' + >>> ndr.NdrWString.unpack(ndr.NdrStream(x)) + u'Test-String\x00' + +.. autoclass:: NdrCString + +.. code-block:: python + + >>> from windows.rpc import ndr + >>> x = ndr.NdrCString.pack("Test-String\x00") + >>> x + '\x0c\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00Test-String\x00' + # TODO: implem unpack + +.. autoclass:: NdrLong + +.. code-block:: python + + >>> from windows.rpc import ndr + >>> x = ndr.NdrLong.pack(0x01020304) + >>> x + '\x04\x03\x02\x01' + >>> hex(ndr.NdrLong.unpack(ndr.NdrStream(x))) + '0x1020304' + +.. autoclass:: NdrHyper + +.. code-block:: python + + >>> from windows.rpc import ndr + >>> x = ndr.NdrHyper.pack(0x0102030405060708) + >>> x + '\x08\x07\x06\x05\x04\x03\x02\x01' + >>> hex(ndr.NdrHyper.unpack(ndr.NdrStream(x))) + '0x102030405060708L' + +.. autoclass:: NdrShort + + >>> from windows.rpc import ndr + >>> x = ndr.NdrShort.pack(0x0102) + >>> x + '\x02\x01' + >>> hex(ndr.NdrShort.unpack(ndr.NdrStream(x))) + '0x102' + +.. autoclass:: NdrByte + +.. code-block:: python + + >>> from windows.rpc import ndr + >>> x = ndr.NdrByte.pack(0x42) + >>> x + 'B' + >>> hex(ndr.NdrByte.unpack(ndr.NdrStream(x))) + '0x42' + + +.. autoclass:: NdrUniquePTR + +.. code-block:: python + + >>> from windows.rpc import ndr + >>> ndr.NdrLong.pack(0x11111111) + '\x11\x11\x11\x11' + >>> ndr.NdrUniquePTR(ndr.NdrLong).pack(0x11111111) + '\x02\x02\x02\x02\x11\x11\x11\x11' + + +.. autoclass:: NdrConformantArray +.. autoclass:: NdrConformantVaryingArrays +.. autoclass:: NdrLongConformantArray + +.. code-block:: python + + >>> windows.rpc.ndr.NdrLongConformantArray.pack([1,2,3,4]) + '\x04\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00' + +.. autoclass:: NdrByteConformantArray + +.. code-block:: python + + >>> windows.rpc.ndr.NdrByteConformantArray.pack([1,2,3,4]) + '\x04\x00\x00\x00\x01\x02\x03\x04' + + +.. autoclass:: NdrStructure + +.. code-block:: python + + >>> from windows.rpc import ndr + >>> class NDRTest(ndr.NdrStructure): + ... MEMBERS = [ndr.NdrLong, ndr.NdrLong, ndr.NdrWString] + ... + >>> x = NDRTest.pack([1, 2, "Test\x00"]) + >>> x + '\x01\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00T\x00e\x00s\x00t\x00\x00\x00PP' + >>> NDRTest.unpack(ndr.NdrStream(x)) + [1, 2, u'Test\x00'] + + +.. autoclass:: NdrParameters + + +NDR STREAM +^^^^^^^^^^ + +.. autoclass:: NdrStream + +.. code-block:: python + + >>> from windows.rpc import ndr + >>> x = ndr.NdrStream("AAAABBBBCCCC") + >>> hex(ndr.NdrLong.unpack(x)) + '0x41414141' + >>> x.data + 'BBBBCCCC' + >>> hex(ndr.NdrShort.unpack(x)) + '0x4242' + >>> x.data + 'BBCCCC' + >>> x.align(4) + >>> x.data + 'CCCC' + >>> hex(ndr.NdrLong.unpack(x)) + '0x43434343' \ No newline at end of file diff --git a/windows/alpc.py b/windows/alpc.py index dd23650..4dd9e8d 100644 --- a/windows/alpc.py +++ b/windows/alpc.py @@ -346,7 +346,7 @@ class AlpcClient(AlpcTransportBase): 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 + self.port_name = None if port_name is not None: x = self.connect_to_port(port_name, "") @@ -409,7 +409,7 @@ class AlpcClient(AlpcTransportBase): 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 + self.port_name = port_name return AlpcMessage(send_msg, receive_attr) if send_msg is not None else None def create_port_section(self, Flags, SectionHandle, SectionSize): diff --git a/windows/rpc/__init__.py b/windows/rpc/__init__.py index b76fd5d..6dbd6ec 100644 --- a/windows/rpc/__init__.py +++ b/windows/rpc/__init__.py @@ -1,4 +1,4 @@ import ndr from client import RPCClient -from epmapper import find_alpc_endpoint_and_connect, endpoint_map_alpc, construct_alpc_tower \ No newline at end of file +from epmapper import find_alpc_endpoint_and_connect, find_alpc_endpoints, construct_alpc_tower \ No newline at end of file diff --git a/windows/rpc/epmapper.py b/windows/rpc/epmapper.py index 6036b75..7552a66 100644 --- a/windows/rpc/epmapper.py +++ b/windows/rpc/epmapper.py @@ -26,7 +26,7 @@ class NDRIID(ndr.NdrStructure): MEMBERS = [ndr.NdrByte] * 16 -class EPMapperFunc8Parameters(ndr.NdrParameters): +class EptMapAuthParameters(ndr.NdrParameters): MEMBERS = [NDRIID, NdrTower, ndr.NdrUniquePTR(ndr.NdrSID), @@ -38,7 +38,7 @@ class Towers(ndr.NdrConformantVaryingArrays): MEMBER_TYPE = ndr.NdrUniquePTR(NdrTower) -class EPMapperFunc8Results(ndr.NdrParameters): +class EptMapAuthResults(ndr.NdrParameters): MEMBERS = [NdrContext, ndr.NdrLong, Towers] @@ -112,13 +112,18 @@ def construct_alpc_tower(object, syntax, protseq, endpoint, address): floor_2_rsh = TOWER_EMPTY_RHS floor_2 = craft_floor(floor_2_lsh, floor_2_rsh) # Floor 3 - floor_3_lsh = "\xff" - floor_3_rsh = TOWER_EMPTY_RHS - floor_3 = craft_floor(floor_3_lsh, floor_3_rsh) + if endpoint is None: + floor_3_lsh = "\xff" + floor_3_rsh = TOWER_EMPTY_RHS + floor_3 = craft_floor(floor_3_lsh, floor_3_rsh) + else: + floor_3_lsh = "\x10" + floor_3_rsh = endpoint + floor_3 = craft_floor(floor_3_lsh, floor_3_rsh) towerarray = struct.pack(" len(unpacked[2]) # Parse towers return [explode_alpc_tower(obj) for obj in unpacked[2]] @@ -174,7 +179,7 @@ def find_alpc_endpoint_and_connect(targetiid, version=(1,0), sid=gdef.WinLocalSy :returns: A connected :class:`~windows.rpc.RPCClient` """ dbgprint("Finding ALPC endpoints for <{0}>".format(targetiid), "RPC") - alpctowers = endpoint_map_alpc(targetiid, version, nb_response=50, sid=sid) + alpctowers = find_alpc_endpoints(targetiid, version, nb_response=50, sid=sid) dbgprint("ALPC endpoints list: <{0}>".format(alpctowers), "RPC") for tower in alpctowers: dbgprint("Trying to connect to endpoint <{0}>".format(tower.endpoint), "RPC") diff --git a/windows/rpc/ndr.py b/windows/rpc/ndr.py index 0a98492..4373e36 100644 --- a/windows/rpc/ndr.py +++ b/windows/rpc/ndr.py @@ -75,6 +75,10 @@ class NdrFixedArray(object): return dword_pad("".join([self.subcls.pack(elt) for elt in data])) + def unpack(self, stream): + return [self.subcls.unpack(stream) for i in range(self.size)] + + class NdrSID(object): @classmethod def pack(cls, psid): @@ -128,6 +132,9 @@ class NdrCString(object): # maxcount, offset, count = stream.partial_unpack("<3I") # return maxcount, offset, count +NdrUniqueCString = NdrUniquePTR(NdrCString) +NdrUniqueWString = NdrUniquePTR(NdrWString) + class NdrLong(object): @classmethod def pack(cls, data): @@ -168,6 +175,19 @@ class NdrByte(object): return stream.partial_unpack("