add rpc.rst doc + rename endpoint_map_alpc + rename ndr EptMapAuth

This commit is contained in:
Clement Rouault
2017-09-14 17:24:18 +02:00
parent 5301f91927
commit b9a00b2ed7
5 changed files with 296 additions and 14 deletions
+230
View File
@@ -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=<RPC_IF_ID "201EF99A-7FA0-444C-9399-19BA84F12A1A" (1, 0)>,
syntax=<RPC_IF_ID "8A885D04-1CEB-11C9-9FE8-08002B104860" (2, 0)>)]
: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
<windows.rpc.client.RPCClient object at 0x046A1470>
>>> client.alpc_client.portname
'\\RPC Control\\LRPC-c30c67fef2afa1612b'
>>> iid = client.bind(UAC_UIID)
>>> iid
<IID "201EF99A-7FA0-444C-9399-19BA84F12A1A">
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'
+2 -2
View File
@@ -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):
+1 -1
View File
@@ -1,4 +1,4 @@
import ndr
from client import RPCClient
from epmapper import find_alpc_endpoint_and_connect, endpoint_map_alpc, construct_alpc_tower
from epmapper import find_alpc_endpoint_and_connect, find_alpc_endpoints, construct_alpc_tower
+15 -10
View File
@@ -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("<H", 4) + floor_0 + floor_1 + floor_2 + floor_3
return len(towerarray), bytearray(towerarray)
def endpoint_map_alpc(targetiid, version=(1,0), nb_response=1, sid=gdef.WinLocalSystemSid):
def find_alpc_endpoints(targetiid, version=(1,0), nb_response=1, sid=gdef.WinLocalSystemSid):
"""Ask the EPMapper for ALPC endpoints of ``targetiid:version`` (maximum of ``nb_response``)
:param str targetiid: The IID of the requested interface
@@ -149,16 +154,16 @@ def endpoint_map_alpc(targetiid, version=(1,0), nb_response=1, sid=gdef.WinLocal
context = (0, 0, 0, 0, 0)
# Pack request
fullreq = EPMapperFunc8Parameters.pack([bytearray(targetiid),
fullreq = EptMapAuthParameters.pack([bytearray(targetiid),
(tower_array_size, towerarray),
local_system_psid,
context,
nb_response])
# RPC Call
response = client.call(epmapperiid, 8, fullreq)
response = client.call(epmapperiid, 7, fullreq)
# Unpack response
stream = ndr.NdrStream(response)
unpacked = EPMapperFunc8Results.unpack(stream)
unpacked = EptMapAuthResults.unpack(stream)
# Looks like there is a memory leak here (in stream.data) if nb_response > 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")
+48 -1
View File
@@ -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("<B")[0]
class NdrContextHandle(object):
@classmethod
def pack(cls, data):
if not isinstance(data, gdef.IID):
data = gdef.IID.from_string(data)
return struct.pack("<I", 0) + str(bytearray(data))
@classmethod
def unpack(self, stream):
attributes, rawguid = stream.partial_unpack("<I16s")
return gdef.IID.from_buffer_copy(rawguid)
class NdrStructure(object):
"""a NDR structure that tries to respect the rules of pointer packing, this class should be subclassed with
an attribute ``MEMBERS`` describing the members of the class
@@ -210,11 +230,23 @@ class NdrStructure(object):
data = []
if is_conformant:
conformant_size = NdrLong.unpack(stream)
post_subcls = []
for i, member in enumerate(cls.MEMBERS):
if conformant_members[i]:
data.append(member.unpack_conformant(stream, conformant_size))
else:
data.append(member.unpack(stream))
if hasattr(member, "unpack_in_struct"):
ptr, subcls = member.unpack_in_struct(stream)
if not ptr:
data.append(None)
else:
data.append(ptr)
post_subcls.append((i, subcls))
print(post_subcls)
else:
data.append(member.unpack(stream))
for i, entry in post_subcls:
data[i] = entry.unpack(stream)
return cls.post_unpack(data)
@classmethod
@@ -312,6 +344,7 @@ class NdrConformantVaryingArrays(object):
def _post_unpack(cls, result):
return result
class NdrWcharConformantVaryingArrays(NdrConformantVaryingArrays):
MEMBER_TYPE = NdrShort
@@ -319,13 +352,16 @@ class NdrWcharConformantVaryingArrays(NdrConformantVaryingArrays):
def _post_unpack(self, result):
return u"".join(unichr(c) for c in result)
class NdrLongConformantArray(NdrConformantArray):
MEMBER_TYPE = NdrLong
class NdrByteConformantArray(NdrConformantArray):
MEMBER_TYPE = NdrByte
class NdrStream(object):
"""A stream of bytes used for NDR unpacking"""
def __init__(self, data):
@@ -360,3 +396,14 @@ class NdrStream(object):
# Realign
size_to_align = (size - (already_read % size))
self.data = self.data[size_to_align:]
def make_parameters(types, name=None):
class NdrCustomParameters(NdrParameters):
MEMBERS = types
return NdrCustomParameters
def make_structure(types, name=None):
class NdrCustomStructure(NdrStructure):
MEMBERS = types
return NdrCustomStructure