mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Add doc to RPC + RPC_IF_ID generated-extended + lsass sample
This commit is contained in:
@@ -261,3 +261,9 @@ typedef struct _ALPC_WORK_ON_BEHALF_ATTR
|
||||
{
|
||||
ULONGLONG Ticket;
|
||||
} ALPC_WORK_ON_BEHALF_ATTR, *PALPC_WORK_ON_BEHALF_ATTR;
|
||||
|
||||
typedef struct _RPC_IF_ID {
|
||||
IID Uuid;
|
||||
USHORT VersMajor;
|
||||
USHORT VersMinor;
|
||||
} RPC_IF_ID;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
INITIAL_RPC_IF_ID = RPC_IF_ID
|
||||
|
||||
class _RPC_IF_ID(INITIAL_RPC_IF_ID):
|
||||
def __repr__(self):
|
||||
return '<RPC_IF_ID "{0}" ({1}, {2})>'.format(self.Uuid.to_string(), self.VersMajor, self.VersMinor)
|
||||
@@ -258,7 +258,7 @@ class InitialDefGenerator(CtypesGenerator):
|
||||
with open(target_file, "w") as f:
|
||||
f.writelines(all_lines)
|
||||
|
||||
EXTENDED_STRUCT = ["_GUID"] # TODO: check auto the dir
|
||||
EXTENDED_STRUCT = ["_GUID", "_RPC_IF_ID"] # TODO: check auto the dir
|
||||
|
||||
class StructGenerator(CtypesGenerator):
|
||||
PARSER = struct_parser.WinStructParser
|
||||
|
||||
@@ -22,6 +22,7 @@ Contents:
|
||||
com.rst
|
||||
crypto.rst
|
||||
alpc.rst
|
||||
rpc.rst
|
||||
generated.rst
|
||||
iat_hook.rst
|
||||
wip.rst
|
||||
|
||||
@@ -10555,6 +10555,28 @@ _ALPC_WORK_ON_BEHALF_ATTR
|
||||
|
||||
:class:`ULONGLONG`
|
||||
|
||||
_RPC_IF_ID
|
||||
''''''''''
|
||||
.. class:: RPC_IF_ID
|
||||
|
||||
Alias for :class:`_RPC_IF_ID`
|
||||
|
||||
.. class:: _RPC_IF_ID
|
||||
|
||||
.. attribute:: Uuid
|
||||
|
||||
:class:`IID`
|
||||
|
||||
|
||||
.. attribute:: VersMajor
|
||||
|
||||
:class:`USHORT`
|
||||
|
||||
|
||||
.. attribute:: VersMinor
|
||||
|
||||
:class:`USHORT`
|
||||
|
||||
WinEnums
|
||||
--------
|
||||
_SYSTEM_INFORMATION_CLASS
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import windows.rpc
|
||||
from windows.rpc import ndr
|
||||
|
||||
|
||||
# Ndr stuff
|
||||
class NdrContext(ndr.NdrStructure):
|
||||
MEMBERS = [ndr.NdrLong, ndr.NdrLong, ndr.NdrLong, ndr.NdrLong, ndr.NdrLong]
|
||||
|
||||
|
||||
class PLSAPR_OBJECT_ATTRIBUTES(ndr.NdrStructure):
|
||||
MEMBERS = [ndr.NdrLong,
|
||||
ndr.NdrUniquePTR(ndr.NdrWString),
|
||||
ndr.NdrUniquePTR(ndr.NdrLong), # We dont care if the subtype as we will pass None
|
||||
ndr.NdrLong,
|
||||
ndr.NdrUniquePTR(ndr.NdrLong), # We dont care if the subtype as we will pass None
|
||||
ndr.NdrUniquePTR(ndr.NdrLong)] # We dont care if the subtype as we will pass None
|
||||
|
||||
|
||||
class LsarOpenPolicy2Parameter(ndr.NdrParameters):
|
||||
MEMBERS = [ndr.NdrUniquePTR(ndr.NdrWString),
|
||||
PLSAPR_OBJECT_ATTRIBUTES,
|
||||
ndr.NdrLong]
|
||||
|
||||
|
||||
class LsarEnumeratePrivilegesParameter(ndr.NdrParameters):
|
||||
MEMBERS = [NdrContext,
|
||||
ndr.NdrLong,
|
||||
ndr.NdrLong]
|
||||
|
||||
|
||||
class LSAPR_POLICY_PRIVILEGE_DEF(object):
|
||||
@classmethod
|
||||
def unpack(cls, stream):
|
||||
size1 = ndr.NdrShort.unpack(stream)
|
||||
ptr = ndr.NdrShort.unpack(stream)
|
||||
size2 = ndr.NdrLong.unpack(stream)
|
||||
luid = ndr.NdrHyper.unpack(stream)
|
||||
return ptr, luid
|
||||
|
||||
|
||||
class LSAPR_PRIVILEGE_ENUM_BUFFER(object):
|
||||
@classmethod
|
||||
def unpack(cls, stream):
|
||||
entries = ndr.NdrLong.unpack(stream)
|
||||
array_size = ndr.NdrLong.unpack(stream)
|
||||
array_ptr = ndr.NdrLong.unpack(stream)
|
||||
# Unpack pointed array
|
||||
array_size2 = ndr.NdrLong.unpack(stream)
|
||||
assert array_size == array_size2
|
||||
x = []
|
||||
# unpack each elements LSAPR_POLICY_PRIVILEGE_DEF
|
||||
for i in range(array_size2):
|
||||
ptr, luid = LSAPR_POLICY_PRIVILEGE_DEF.unpack(stream)
|
||||
if ptr:
|
||||
x.append(luid)
|
||||
# unpack pointed strings
|
||||
result = []
|
||||
for luid in x:
|
||||
name = ndr.NdrWcharConformantVaryingArrays.unpack(stream)
|
||||
result.append((luid, name))
|
||||
return result
|
||||
|
||||
|
||||
# Actual code
|
||||
|
||||
## LSASS alpc endpoints is fixed, no need for the epmapper
|
||||
client = windows.rpc.RPCClient(r"\RPC Control\lsasspirpc")
|
||||
## Bind to the desired interface
|
||||
iid = client.bind('12345778-1234-abcd-ef00-0123456789ab', version=(0,0))
|
||||
|
||||
## Craft parameters and call 'LsarOpenPolicy2'
|
||||
params = LsarOpenPolicy2Parameter.pack([None, (0, None, None, 0, None, None), 0x20000000])
|
||||
res = client.call(iid, 44, params)
|
||||
## Unpack the resulting handle
|
||||
handle = NdrContext.unpack(ndr.NdrStream(res))
|
||||
|
||||
## Craft parameters and call 'LsarEnumeratePrivileges'
|
||||
x = LsarEnumeratePrivilegesParameter.pack([handle, 0, 10000]);
|
||||
res = client.call(iid, 2, x)
|
||||
|
||||
## Unpack the resulting 'LSAPR_PRIVILEGE_ENUM_BUFFER'
|
||||
priviledges = LSAPR_PRIVILEGE_ENUM_BUFFER.unpack(ndr.NdrStream(res))
|
||||
for priv in priviledges:
|
||||
print priv
|
||||
@@ -4262,3 +4262,18 @@ class _ALPC_WORK_ON_BEHALF_ATTR(Structure):
|
||||
]
|
||||
PALPC_WORK_ON_BEHALF_ATTR = POINTER(_ALPC_WORK_ON_BEHALF_ATTR)
|
||||
ALPC_WORK_ON_BEHALF_ATTR = _ALPC_WORK_ON_BEHALF_ATTR
|
||||
|
||||
class _RPC_IF_ID(Structure):
|
||||
_fields_ = [
|
||||
("Uuid", IID),
|
||||
("VersMajor", USHORT),
|
||||
("VersMinor", USHORT),
|
||||
]
|
||||
RPC_IF_ID = _RPC_IF_ID
|
||||
|
||||
INITIAL_RPC_IF_ID = RPC_IF_ID
|
||||
|
||||
class _RPC_IF_ID(INITIAL_RPC_IF_ID):
|
||||
def __repr__(self):
|
||||
return '<RPC_IF_ID "{0}" ({1}, {2})>'.format(self.Uuid.to_string(), self.VersMajor, self.VersMinor)
|
||||
RPC_IF_ID = _RPC_IF_ID
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ndr
|
||||
from client import RPCClient, RPC_IF_ID
|
||||
from client import RPCClient
|
||||
|
||||
from epmapper import find_alpc_endpoint_and_connect, endpoint_map_alpc, construct_alpc_tower
|
||||
+15
-18
@@ -6,23 +6,10 @@ import windows.com
|
||||
import windows.generated_def as gdef
|
||||
|
||||
|
||||
class _RPC_IF_ID(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Uuid", gdef.IID),
|
||||
("MajorVersion", gdef.USHORT),
|
||||
("MinorVersion", gdef.USHORT),
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
return '<RPC_IF_ID "{0}" ({1}, {2})>'.format(self.Uuid.to_string(), self.MajorVersion, self.MinorVersion)
|
||||
|
||||
RPC_IF_ID = _RPC_IF_ID
|
||||
|
||||
|
||||
KNOW_REQUEST_TYPE = {x:x for x in [gdef.RPC_REQUEST_TYPE_CALL, gdef.RPC_REQUEST_TYPE_BIND]}
|
||||
|
||||
|
||||
KNOW_RESPONSE_TYPE = {x:x for x in [gdef.RPC_RESPONSE_TYPE_FAIL, gdef.RPC_RESPONSE_TYPE_SUCESS, gdef.RPC_RESPONSE_TYPE_SUCESS]}
|
||||
KNOW_RESPONSE_TYPE = {x:x for x in [gdef.RPC_RESPONSE_TYPE_FAIL, gdef.RPC_RESPONSE_TYPE_SUCESS, gdef.RPC_RESPONSE_TYPE_BIND_OK]}
|
||||
|
||||
|
||||
KNOWN_RPC_ERROR_CODE = {x:x for x in [
|
||||
@@ -34,17 +21,15 @@ KNOWN_RPC_ERROR_CODE = {x:x for x in [
|
||||
gdef.RPC_S_PROCNUM_OUT_OF_RANGE,
|
||||
]}
|
||||
|
||||
|
||||
NOT_USED = 0xBAADF00D
|
||||
|
||||
|
||||
class ALPC_RPC_BIND(ctypes.Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("request_type", gdef.DWORD),
|
||||
("UNK1", gdef.DWORD),
|
||||
("UNK2", gdef.DWORD),
|
||||
("target", RPC_IF_ID),
|
||||
("target", gdef.RPC_IF_ID),
|
||||
("flags", gdef.DWORD),
|
||||
("if_nb_ndr32", gdef.USHORT),
|
||||
("if_nb_ndr64", gdef.USHORT),
|
||||
@@ -61,6 +46,7 @@ class ALPC_RPC_BIND(ctypes.Structure):
|
||||
|
||||
|
||||
class RPCClient(object):
|
||||
"""A client for RPC-over-ALPC able to bind to interface and perform calls using NDR32 marshalling"""
|
||||
REQUEST_IDENTIFIER = 0x11223344
|
||||
def __init__(self, port):
|
||||
self.alpc_client = alpc.AlpcClient(port)
|
||||
@@ -68,6 +54,10 @@ class RPCClient(object):
|
||||
self.if_bind_number = {}
|
||||
|
||||
def bind(self, IID_str, version=(1,0)):
|
||||
"""Bind to the ``IID_str`` with the given ``version``
|
||||
|
||||
:returns: :class:`windows.generated_def.IID`
|
||||
"""
|
||||
IID = windows.com.IID.from_string(IID_str)
|
||||
request = self._forge_bind_request(IID, version, self.number_of_bind_if)
|
||||
response = self._send_request(request)
|
||||
@@ -82,6 +72,13 @@ class RPCClient(object):
|
||||
return IID
|
||||
|
||||
def call(self, IID, method_offset, params):
|
||||
"""Call method number ``method_offset`` of interface ``IID`` with mashalled ``params``
|
||||
|
||||
:param IID IID: An IID previously returned by :func:`bind`
|
||||
:param int method_offset:
|
||||
:param str params: The mashalled parameters (NDR32)
|
||||
:returns: :class:`str`
|
||||
"""
|
||||
iid_hash = hash(buffer(IID)[:])
|
||||
interface_nb = self.if_bind_number[iid_hash] # TODO: add __hash__ to IID
|
||||
request = self._forge_call_request(interface_nb, method_offset, params)
|
||||
@@ -110,7 +107,7 @@ class RPCClient(object):
|
||||
version_major, version_minor = syntaxversion
|
||||
req = ALPC_RPC_BIND()
|
||||
req.request_type = gdef.RPC_REQUEST_TYPE_BIND
|
||||
req.target = RPC_IF_ID(uuid, *syntaxversion)
|
||||
req.target = gdef.RPC_IF_ID(uuid, *syntaxversion)
|
||||
req.flags = gdef.BIND_IF_SYNTAX_NDR32
|
||||
req.if_nb_ndr32 = requested_if_nb
|
||||
req.if_nb_ndr64 = 0
|
||||
|
||||
+28
-10
@@ -68,15 +68,15 @@ def explode_alpc_tower(tower):
|
||||
lhs, rhs = parse_floor(stream)
|
||||
if not (lhs[0] == 0xd):
|
||||
raise ValueError("Floor 0: IID expected")
|
||||
iid = windows.com.IID.from_buffer_copy(lhs[1:17])
|
||||
object = windows.rpc.RPC_IF_ID(iid, lhs[17], lhs[18])
|
||||
iid = gdef.IID.from_buffer_copy(lhs[1:17])
|
||||
object = gdef.RPC_IF_ID(iid, lhs[17], lhs[18])
|
||||
|
||||
# Floor 1
|
||||
lhs, rhs = parse_floor(stream)
|
||||
if not (lhs[0] == 0xd):
|
||||
raise ValueError("Floor 0: IID expected")
|
||||
iid = windows.com.IID.from_buffer_copy(lhs[1:17])
|
||||
syntax = windows.rpc.RPC_IF_ID(iid, lhs[17], lhs[18])
|
||||
iid = gdef.IID.from_buffer_copy(lhs[1:17])
|
||||
syntax = gdef.RPC_IF_ID(iid, lhs[17], lhs[18])
|
||||
|
||||
# Floor 2
|
||||
lhs, rhs = parse_floor(stream)
|
||||
@@ -100,11 +100,11 @@ def construct_alpc_tower(object, syntax, protseq, endpoint, address):
|
||||
if protseq != "ncalrpc":
|
||||
raise NotImplementedError("Construct ALPC Tower with protseq != 'ncalrpc'")
|
||||
# Floor 0
|
||||
floor_0_lsh = TOWER_PROTOCOL_IS_UUID + bytearray(object.Uuid) + struct.pack("<BB", object.MajorVersion, object.MinorVersion)
|
||||
floor_0_lsh = TOWER_PROTOCOL_IS_UUID + bytearray(object.Uuid) + struct.pack("<BB", object.VersMajor, object.VersMinor)
|
||||
floor_0_rsh = TOWER_EMPTY_RHS
|
||||
floor_0 = craft_floor(floor_0_lsh, floor_0_rsh)
|
||||
# Floor 1
|
||||
floor_1_lsh = TOWER_PROTOCOL_IS_UUID + bytearray(object.Uuid) + struct.pack("<BB", object.MajorVersion, object.MinorVersion)
|
||||
floor_1_lsh = TOWER_PROTOCOL_IS_UUID + bytearray(object.Uuid) + struct.pack("<BB", object.VersMajor, object.VersMinor)
|
||||
floor_1_rsh = TOWER_EMPTY_RHS
|
||||
floor_1 = craft_floor(floor_1_lsh, floor_1_rsh)
|
||||
# Floor 2
|
||||
@@ -119,18 +119,28 @@ def construct_alpc_tower(object, syntax, protseq, endpoint, address):
|
||||
return len(towerarray), bytearray(towerarray)
|
||||
|
||||
def endpoint_map_alpc(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
|
||||
:param (int,int) version: The version requested interface
|
||||
:param int nb_response: The maximum number of response
|
||||
:param WELL_KNOWN_SID_TYPE sid: The SID used to request the EPMapper
|
||||
|
||||
:returns: [:class:`~windows.rpc.epmapper.UnpackTower`] -- A list of :class:`~windows.rpc.epmapper.UnpackTower`
|
||||
"""
|
||||
|
||||
if isinstance(targetiid, basestring):
|
||||
targetiid = windows.com.IID.from_string(targetiid)
|
||||
targetiid = gdef.IID.from_string(targetiid)
|
||||
# Connect to epmapper
|
||||
client = windows.rpc.RPCClient(r"\RPC Control\epmapper")
|
||||
epmapperiid = client.bind("e1af8308-5d1f-11c9-91a4-08002b14a0fa", version=(3,0))
|
||||
|
||||
# Compute request tower
|
||||
## object
|
||||
rpc_object = windows.rpc.RPC_IF_ID(targetiid, *version)
|
||||
rpc_object = gdef.RPC_IF_ID(targetiid, *version)
|
||||
## Syntax
|
||||
syntax_iid = windows.com.IID.from_string("8a885d04-1ceb-11c9-9fe8-08002b104860")
|
||||
rpc_syntax = windows.rpc.RPC_IF_ID(syntax_iid, 2, 0)
|
||||
syntax_iid = gdef.IID.from_string("8a885d04-1ceb-11c9-9fe8-08002b104860")
|
||||
rpc_syntax = gdef.RPC_IF_ID(syntax_iid, 2, 0)
|
||||
## Forge tower
|
||||
tower_array_size, towerarray = construct_alpc_tower(rpc_object, rpc_syntax, "ncalrpc", "", None)
|
||||
|
||||
@@ -155,6 +165,14 @@ def endpoint_map_alpc(targetiid, version=(1,0), nb_response=1, sid=gdef.WinLocal
|
||||
|
||||
|
||||
def find_alpc_endpoint_and_connect(targetiid, version=(1,0), sid=gdef.WinLocalSystemSid):
|
||||
"""Ask the EPMapper for ALPC endpoints of ``targetiid:version`` and connect to one of them.
|
||||
|
||||
:param str targetiid: The IID of the requested interface
|
||||
:param (int,int) version: The version requested interface
|
||||
:param WELL_KNOWN_SID_TYPE sid: The SID used to request the EPMapper
|
||||
|
||||
: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)
|
||||
dbgprint("ALPC endpoints list: <{0}>".format(alpctowers), "RPC")
|
||||
|
||||
@@ -28,6 +28,7 @@ def dword_pad(s):
|
||||
|
||||
|
||||
class NdrUniquePTR(object):
|
||||
"""Create a UNIQUE PTR around a given Ndr type"""
|
||||
def __init__(self, subcls):
|
||||
self.subcls = subcls
|
||||
|
||||
@@ -77,6 +78,10 @@ class NdrFixedArray(object):
|
||||
class NdrSID(object):
|
||||
@classmethod
|
||||
def pack(cls, psid):
|
||||
"""Pack a PSID
|
||||
|
||||
:param PSID psid:
|
||||
"""
|
||||
subcount = windows.winproxy.GetSidSubAuthorityCount(psid)
|
||||
size = windows.winproxy.GetLengthSid(psid)
|
||||
sid_data = windows.current_process.read_memory(psid.value, size)
|
||||
@@ -84,6 +89,7 @@ class NdrSID(object):
|
||||
|
||||
@classmethod
|
||||
def unpack(cls, stream):
|
||||
"""Unpack a PSID, partial implementation that returns a :class:`str` and not a PSID"""
|
||||
subcount = NdrLong.unpack(stream)
|
||||
return stream.read(8 + (subcount * 4))
|
||||
|
||||
@@ -163,8 +169,12 @@ class NdrByte(object):
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
@classmethod
|
||||
def pack(cls, data):
|
||||
"""Pack data into the struct, ``data`` size must equals the number of members in the structure"""
|
||||
if not (len(data) == len(cls.MEMBERS)):
|
||||
print("Size mistach:")
|
||||
print(" * data size = {0}".format(len(data)))
|
||||
@@ -192,6 +202,7 @@ class NdrStructure(object):
|
||||
|
||||
@classmethod
|
||||
def unpack(cls, stream):
|
||||
"""Unpack the structure from the stream"""
|
||||
conformant_members = [hasattr(m, "pack_conformant") for m in cls.MEMBERS]
|
||||
is_conformant = any(conformant_members)
|
||||
assert(conformant_members.count(True) <= 1), "Unpack conformant struct with more that one conformant MEMBER not implem"
|
||||
@@ -213,6 +224,9 @@ class NdrStructure(object):
|
||||
|
||||
|
||||
class NdrParameters(object):
|
||||
"""a class to pack NDR parameters together to performs RPC call, this class should be subclassed with
|
||||
an attribute ``MEMBERS`` describing the members of the class
|
||||
"""
|
||||
@classmethod
|
||||
def pack(cls, data):
|
||||
if not (len(data) == len(cls.MEMBERS)):
|
||||
@@ -291,9 +305,19 @@ class NdrConformantVaryingArrays(object):
|
||||
for i, entry in post_subcls:
|
||||
data = entry.unpack(stream)
|
||||
result[i] = data
|
||||
|
||||
return cls._post_unpack(result)
|
||||
|
||||
@classmethod
|
||||
def _post_unpack(cls, result):
|
||||
return result
|
||||
|
||||
class NdrWcharConformantVaryingArrays(NdrConformantVaryingArrays):
|
||||
MEMBER_TYPE = NdrShort
|
||||
|
||||
@classmethod
|
||||
def _post_unpack(self, result):
|
||||
return u"".join(unichr(c) for c in result)
|
||||
|
||||
class NdrLongConformantArray(NdrConformantArray):
|
||||
MEMBER_TYPE = NdrLong
|
||||
@@ -303,6 +327,7 @@ class NdrByteConformantArray(NdrConformantArray):
|
||||
|
||||
|
||||
class NdrStream(object):
|
||||
"""A stream of bytes used for NDR unpacking"""
|
||||
def __init__(self, data):
|
||||
self.fulldata = data
|
||||
self.data = data
|
||||
@@ -329,6 +354,7 @@ class NdrStream(object):
|
||||
return data
|
||||
|
||||
def align(self, size):
|
||||
"""Discard some bytes to align the remaining stream on ``size``"""
|
||||
already_read = len(self.fulldata) - len(self.data)
|
||||
if already_read % size:
|
||||
# Realign
|
||||
|
||||
Reference in New Issue
Block a user