Lot of small py3 compat fix

This commit is contained in:
hakril
2020-02-13 22:23:20 +01:00
parent b27d161a6a
commit 4d3b3e18ad
24 changed files with 283 additions and 110 deletions
+9 -6
View File
@@ -5,6 +5,7 @@ from collections import namedtuple
import windows
from windows import winproxy
from windows import generated_def as gdef
import windows.pycompat
## For 64b python
@@ -21,7 +22,7 @@ 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)):
if isinstance(msg_or_size, windows.pycompat.int_types):
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)
@@ -301,9 +302,11 @@ class AlpcTransportBase(object):
:type receive_msg: AlpcMessage or None
:param int flags: The flags for :func:`NtAlpcSendWaitReceivePort`
"""
if isinstance(alpc_message, basestring):
if isinstance(alpc_message, windows.pycompat.anybuff):
raw_alpc_message = alpc_message
alpc_message = AlpcMessage(max(0x1000, len(alpc_message)))
if len(alpc_message) > 0x1000:
import pdb;pdb.set_trace()
alpc_message = AlpcMessage(max(0x1000, len(alpc_message) + 0x200))
alpc_message.port_message.data = raw_alpc_message
if receive_msg is None:
@@ -319,7 +322,7 @@ class AlpcTransportBase(object):
:type alpc_message: AlpcMessage or str
:param int flags: The flags for :func:`NtAlpcSendWaitReceivePort`
"""
if isinstance(alpc_message, basestring):
if isinstance(alpc_message, windows.pycompat.anybuff):
raw_alpc_message = alpc_message
alpc_message = AlpcMessage(max(0x1000, len(alpc_message)))
alpc_message.port_message.data = raw_alpc_message
@@ -346,7 +349,7 @@ class AlpcTransportBase(object):
class AlpcClient(AlpcTransportBase):
"An ALPC client able to connect to a port and send/receive messages"
DEFAULT_MAX_MESSAGE_LENGTH = 0x1000
DEFAULT_MAX_MESSAGE_LENGTH = 0x9000
def __init__(self, port_name=None):
"""Init the :class:`AlpcClient` automatically connect to ``port_name`` using default values if given"""
@@ -396,7 +399,7 @@ class AlpcClient(AlpcTransportBase):
send_msg = None
send_msg_attr = None
buffersize = None
elif isinstance(connect_message, basestring):
elif isinstance(connect_message, windows.pycompat.anybuff):
buffersize = gdef.DWORD(len(connect_message) + 0x1000)
send_msg = AlpcMessagePort.from_buffer_size(buffersize.value)
send_msg.data = connect_message
+6 -3
View File
@@ -1,3 +1,4 @@
import sys
import struct
import ctypes
import functools
@@ -12,6 +13,8 @@ from windows.generated_def import RPC_C_IMP_LEVEL_IMPERSONATE, CLSCTX_INPROC_SER
from windows.generated_def import interfaces
from windows.generated_def.interfaces import generate_IID, IID
from windows.pycompat import int_types, basestring
# We have windows.com.COMImplementation
# So we need windows.com.COMInterface
COMInterface = interfaces.COMInterface
@@ -109,11 +112,11 @@ def check_type_null(value):
def check_type_i4(value):
# 31 ? as we may want to keep sign :)
return isinstance(value, (int, long)) and (value).bit_length() <= 32
return isinstance(value, int_types) and (value).bit_length() <= 32
def check_type_i8(value):
# 63 ? as we may want to keep sign :)
return isinstance(value, (int, long)) and (value).bit_length() <= 64
return isinstance(value, int_types) and (value).bit_length() <= 64
def check_type_bstr(value):
return isinstance(value, basestring)
@@ -299,7 +302,7 @@ class COMImplementation(object):
def QueryInterface(self, this, piid, result):
"""Default ``QueryInterface`` implementation that returns ``self`` if piid is the implemented interface"""
if piid[0] in (IUnknown.IID, self.IMPLEMENT.IID):
if piid[0] in (gdef.IUnknown.IID, self.IMPLEMENT.IID):
result[0] = this
return 1
return E_NOINTERFACE
+8 -2
View File
@@ -98,7 +98,7 @@ class CryptObject(object):
return list(self._signers_and_certs_generator())
def __repr__(self):
return '<{0} "{1}" content_type={2}>'.format(type(self).__name__, self.filename, self.content_type)
return '<{0} "{1}" content_type={2!r}>'.format(type(self).__name__, self.filename, self.content_type)
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa382037(v=vs.85).aspx
@@ -176,6 +176,9 @@ class CertificateStore(gdef.HCERTSTORE):
return None
return Certificate.from_pointer(rawcertcontext)
def __del__(self):
return winproxy.CertCloseStore(self, 0)
# PKCS12_NO_PERSIST_KEY -> do not save it in a key container on disk
# Without it, a key container is created at 'C:\Users\USERNAME\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-3241049326-165485355-1070449050-1001'
@@ -189,6 +192,7 @@ class CertificateStore(gdef.HCERTSTORE):
# By forcing PKCS12_ALWAYS_CNG_KSP we remove this as the key are directly linked to the correct CNG_KSP in the CertStore
# Look like it's based on this part of the PFX:
# Microsoft CSP Name: Microsoft Enhanced Cryptographic Provider v1.0
# BUT this will not allow to decrypt RSA_RC4 ?
def import_pfx(pfx, password=None, flags=gdef.CRYPT_USER_KEYSET | gdef.PKCS12_NO_PERSIST_KEY | gdef.PKCS12_ALWAYS_CNG_KSP):
"""Import the file ``pfx`` with the ``password``.
@@ -199,7 +203,7 @@ def import_pfx(pfx, password=None, flags=gdef.CRYPT_USER_KEYSET | gdef.PKCS12_NO
:return: :class:`CertificateStore`
"""
if isinstance(pfx, (basestring, bytearray)):
if isinstance(pfx, windows.pycompat.anybuff) or isinstance(pfx, bytearray):
pfx = gdef.CRYPT_DATA_BLOB.from_string(pfx)
cert_store = winproxy.PFXImportCertStore(pfx, password, flags)
return CertificateStore(cert_store)
@@ -483,6 +487,8 @@ class Certificate(gdef.CERT_CONTEXT):
# class CertficateChain(object):
# def __init__(self, pc_chain_context):
# self.chain = pc_chain_context[0]
+3
View File
@@ -127,3 +127,6 @@ class CryptMessage(gdef.HCRYPTMSG):
newmsg = CryptMessage(hmsg)
newmsg.update(data, final=True)
return newmsg
def __del__(self):
return winproxy.CryptMsgClose(self)
+3 -3
View File
@@ -48,7 +48,7 @@ def encrypt(cert_or_certlist, msg, algo=szOID_NIST_AES256_CBC, initvector=genini
:return: :class:`bytearray`: The encrypted message
"""
alg_ident = CRYPT_ALGORITHM_IDENTIFIER()
alg_ident.pszObjId = algo
alg_ident.pszObjId = algo.encode("ascii")
# We want to have automatique translation of Certificate -> PCERT_CONTEXT
# In order to simple create the 'PCERT_CONTEXT[] certs'
# For that we need a tuple of X * 1-item-tuple
@@ -114,6 +114,6 @@ def decrypt(cert_store, encrypted):
dcryptsize = DWORD()
winproxy.CryptDecryptMessage(dparam, buf, ctypes.sizeof(buf), None, dcryptsize, None)
#Decrypt the msg
dcryptbuff = (BYTE * dcryptsize.value)()
dcryptbuff = (BYTE * (dcryptsize.value + 0x1000))()
winproxy.CryptDecryptMessage(dparam, buf, ctypes.sizeof(buf), dcryptbuff, dcryptsize, None)
return str(bytearray(dcryptbuff[:dcryptsize.value]))
return bytes(bytearray(dcryptbuff[:dcryptsize.value]))
+3 -1
View File
@@ -208,6 +208,7 @@ def generate_python_exec_shellcode_32(target, PyDll):
code += x86.Jnz(":DO_ENSURE")
code += x86.Mov('EAX', Py_Initialize)
code += x86.Call('EAX')
# https://docs.python.org/3/c-api/init.html#c.PyEval_InitThreads
code += x86.Mov('EAX', PyEval_InitThreads)
code += x86.Call('EAX')
code += x86.Label(":DO_ENSURE")
@@ -272,6 +273,7 @@ def generate_python_exec_shellcode_64(target, PyDll):
code += x64.Jnz(":DO_ENSURE")
code += x64.Mov('RAX', Py_Initialize)
code += x64.Call('RAX')
# https://docs.python.org/3/c-api/init.html#c.PyEval_InitThreads
code += x64.Mov('RAX', PyEval_InitThreads)
code += x64.Call('RAX')
code += x64.Label(":DO_ENSURE")
@@ -398,7 +400,7 @@ import ctypes
size = ctypes.c_uint.from_address(addr)
size.value = len(txt)
buff = (ctypes.c_char * len(txt)).from_address(addr + ctypes.sizeof(ctypes.c_uint))
buff[:] = txt
buff[:] = txt.encode()
"""
def retrieve_last_exception_data(process):
+12 -5
View File
@@ -1,17 +1,24 @@
import windows
from windows import winproxy
import windows.generated_def as gdef
import _multiprocessing
import ctypes
from windows.pycompat import is_py3
if is_py3:
from multiprocessing.connection import PipeConnection as native_PipeConnection
else:
from _multiprocessing import PipeConnection as native_PipeConnection
# Inspired from 'multiprocessing\connection.py'
def full_pipe_address(addr):
"""Return the full address of the pipe `addr`"""
if addr.startswith("\\\\"):
if not isinstance(addr, bytes):
addr = addr.encode("ascii")
if addr.startswith(b"\\\\"):
return addr
return r"\\.\pipe\{addr}".format(addr=addr)
return br"\\.\pipe" + "\\".encode() + addr
class PipeConnection(object): # Cannot inherit: crash the interpreter
"""A wrapper arround :class:`_multiprocessing.PipeConnection` able to work as a ContextManager"""
@@ -26,7 +33,7 @@ class PipeConnection(object): # Cannot inherit: crash the interpreter
@classmethod
def from_handle(cls, phandle, *args, **kwargs):
"""Create a :class:`PipeConnection` from pipe handle `phandle`"""
connection = _multiprocessing.PipeConnection(phandle)
connection = native_PipeConnection(phandle)
return cls(connection, *args, **kwargs)
@classmethod
+19
View File
@@ -10,6 +10,16 @@ if is_py3:
basestring = str
anybuff = (str, bytes)
def raw_encode(s):
if isinstance(s, str):
return s.encode("latin1")
return s
def raw_decode(s):
if isinstance(s, bytes):
return s.decode("latin1")
return s
else: # py2.7
def str_from_ascii_function(s):
return s
@@ -17,3 +27,12 @@ else: # py2.7
int_types = (int, long)
basestring = basestring
anybuff = basestring
def raw_encode(s):
if isinstance(s, unicode):
return s.encode("latin1")
return s
def raw_decode(s):
# No unicode for now on py2
return s
+9 -1
View File
@@ -7,6 +7,14 @@ import ctypes.wintypes
import itertools
from _ctypes import _SimpleCData
# No PFW deps in this file
import sys
is_py3 = (sys.version_info.major >= 3)
if is_py3:
int_types = int
else:
int_types = (int, long)
# ## Utils ### #
def is_pointer(x):
@@ -168,7 +176,7 @@ def create_remote_array(subtype, len):
def __getitem__(self, slice):
# import pdb;pdb.set_trace()
if not isinstance(slice, (int, long)):
if not isinstance(slice, int_types):
raise NotImplementedError("RemoteArray slice __getitem__")
if slice >= len:
raise IndexError("Access to {0} for a RemoteArray of size {1}".format(slice, len))
+3 -4
View File
@@ -1,4 +1,3 @@
import ndr
from client import RPCClient
from epmapper import find_alpc_endpoint_and_connect, find_alpc_endpoints, construct_alpc_tower
from . import ndr
from .client import RPCClient
from .epmapper import find_alpc_endpoint_and_connect, find_alpc_endpoints, construct_alpc_tower
+44 -12
View File
@@ -5,6 +5,9 @@ import windows.alpc as alpc
import windows.com
import windows.generated_def as gdef
if windows.pycompat.is_py3:
buffer = bytes
KNOW_REQUEST_TYPE = gdef.FlagMapper(gdef.RPC_REQUEST_TYPE_CALL, gdef.RPC_REQUEST_TYPE_BIND)
KNOW_RESPONSE_TYPE = gdef.FlagMapper(gdef.RPC_RESPONSE_TYPE_FAIL, gdef.RPC_RESPONSE_TYPE_SUCCESS, gdef.RPC_RESPONSE_TYPE_BIND_OK)
@@ -54,10 +57,7 @@ class ALPC_RPC_CALL(ctypes.Structure):
("UNK5", gdef.DWORD),
("UNK6", gdef.DWORD),
("UNK7", gdef.DWORD),
("UNK8", gdef.DWORD),
("UNK9", gdef.DWORD),
("UNK10", gdef.DWORD),
("UNK11", gdef.DWORD),
("ORPC_IPID", gdef.GUID)
]
class RPCClient(object):
@@ -86,7 +86,20 @@ class RPCClient(object):
#TODO: attach version information to IID
return IID
def call(self, IID, method_offset, params):
def forge_alpc_request(self, IID, method_offset, params, ipid=None):
"""Craft an ALPC message containing an RPC request to call ``method_offset`` of interface ``IID`
with ``params``.
Can be used to craft request without directly sending it
"""
iid_hash = hash(buffer(IID)[:])
interface_nb = self.if_bind_number[iid_hash] # TODO: add __hash__ to IID
if len(params) > 0x900: # 0x1000 - size of meta-data
request = self._forge_call_request_in_view(interface_nb, method_offset, params, ipid=ipid)
else:
request = self._forge_call_request(interface_nb, method_offset, params, ipid=ipid)
return request
def call(self, IID, method_offset, params, ipid=None):
"""Call method number ``method_offset`` of interface ``IID`` with mashalled ``params``
:param IID IID: An IID previously returned by :func:`bind`
@@ -94,15 +107,14 @@ class RPCClient(object):
: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)
request = self.forge_alpc_request(IID, method_offset, params, ipid=ipid)
response = self._send_request(request)
# Parse reponse
request_type = self._get_request_type(response)
if request_type != gdef.RPC_RESPONSE_TYPE_SUCCESS:
raise ValueError("Unexpected reponse type. Expected RESPONSE_SUCCESS got {0}".format(KNOW_RESPONSE_TYPE[request_type]))
# windows.utils.sprint(ALPC_RPC_CALL.from_buffer_copy(response + "\x00" * 12))
data = struct.unpack("<6I", response[:6 * 4])
assert data[3] == self.REQUEST_IDENTIFIER
return response[4 * 6:] # Should be the return value (not completly verified)
@@ -111,7 +123,7 @@ class RPCClient(object):
response = self.alpc_client.send_receive(request)
return response.data
def _forge_call_request(self, interface_nb, method_offset, params):
def _forge_call_request(self, interface_nb, method_offset, params, ipid=None):
# TODO: differents REQUEST_IDENTIFIER for each req ?
# TODO: what is this '0' ? (1 is also accepted) (flags ?)
# request = struct.pack("<16I", gdef.RPC_REQUEST_TYPE_CALL, NOT_USED, 1, self.REQUEST_IDENTIFIER, interface_nb, method_offset, *[NOT_USED] * 10)
@@ -121,19 +133,39 @@ class RPCClient(object):
req.request_id = self.REQUEST_IDENTIFIER
req.if_nb = interface_nb
req.method_offset = method_offset
if ipid:
req.ORPC_IPID = ipid
this = gdef.ORPCTHIS()
this.version = (5,7)
this.flags = 1
lthis = gdef.LOCALTHIS()
return buffer(req)[:] + buffer(this)[:] + buffer(lthis)[:] + params
return buffer(req)[:] + params
def _forge_call_request_in_view(self, interface_nb, method_offset, params, ipid=None):
# import pdb;pdb.set_trace()
# Version crade qui clean rien pour POC. GROS DOUTES :D
raw_request = self._forge_call_request(interface_nb, method_offset, "")
p = windows.alpc.AlpcMessage(0x2000)
section = self.alpc_client.create_port_section(0x40000, 0, len(params))
view = self.alpc_client.map_section(section[0], len(params))
p.port_message.data = raw_request + windows.rpc.ndr.NdrLong.pack(len(params) + 0x200) + "\x00" * 40
p.attributes.ValidAttributes |= gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE
p.view_attribute.Flags = 0x40000
p.view_attribute.ViewBase = view.ViewBase
p.view_attribute.SectionHandle = view.SectionHandle
p.view_attribute.ViewSize = len(params)
windows.current_process.write_memory(view.ViewBase, params) # Write NDR to view
return p
def _forge_bind_request(self, uuid, syntaxversion, requested_if_nb):
version_major, version_minor = syntaxversion
req = ALPC_RPC_BIND()
req.request_type = gdef.RPC_REQUEST_TYPE_BIND
req.target = gdef.RPC_IF_ID(uuid, *syntaxversion)
req.flags = gdef.BIND_IF_SYNTAX_NDR32
# req.flags = gdef.BIND_IF_SYNTAX_NDR64
req.if_nb_ndr32 = requested_if_nb
req.if_nb_ndr64 = 0
req.if_nb_ndr64 = requested_if_nb
req.if_nb_unkn = 0
req.register_multiple_syntax = False
req.some_context_id = 0xB00B00B
+11 -9
View File
@@ -5,6 +5,7 @@ import windows
import windows.generated_def as gdef
from windows.rpc import ndr
from windows.dbgprint import dbgprint
from windows.pycompat import basestring
@@ -85,14 +86,15 @@ def explode_alpc_tower(tower):
lhs, rhs = parse_floor(stream)
if not (rhs[-1] == 0):
raise ValueError("ALPC Port name doest not end by \\x00")
return UnpackTower("ncalrpc", rhs[:-1], None, object, syntax)
rhs = rhs[:rhs.find("\x00")]
# raise ValueError("ALPC Port name doest not end by \\x00")
return UnpackTower("ncalrpc", bytes(rhs[:-1]), None, object, syntax)
# http://pubs.opengroup.org/onlinepubs/9629399/apdxi.htm#tagcjh_28
# Octet 0 contains the hexadecimal value 0d. This is a reserved protocol identifier prefix that indicates that the protocol ID is UUID derived
TOWER_PROTOCOL_IS_UUID = "\x0d"
TOWER_EMPTY_RHS = "\x00\x00"
TOWER_PROTOCOL_ID_ALPC = "\x0c" # From RE
TOWER_PROTOCOL_IS_UUID = b"\x0d"
TOWER_EMPTY_RHS = b"\x00\x00"
TOWER_PROTOCOL_ID_ALPC = b"\x0c" # From RE
def construct_alpc_tower(object, syntax, protseq, endpoint, address):
if address is not None:
@@ -113,11 +115,11 @@ def construct_alpc_tower(object, syntax, protseq, endpoint, address):
floor_2 = craft_floor(floor_2_lsh, floor_2_rsh)
# Floor 3
if endpoint is None:
floor_3_lsh = "\xff"
floor_3_lsh = b"\xff"
floor_3_rsh = TOWER_EMPTY_RHS
floor_3 = craft_floor(floor_3_lsh, floor_3_rsh)
else:
floor_3_lsh = "\x10"
floor_3_lsh = b"\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
@@ -147,7 +149,7 @@ def find_alpc_endpoints(targetiid, version=(1,0), nb_response=1, sid=gdef.WinLoc
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)
tower_array_size, towerarray = construct_alpc_tower(rpc_object, rpc_syntax, "ncalrpc", b"", None)
# parameters
local_system_psid = windows.utils.get_known_sid(sid)
@@ -183,7 +185,7 @@ def find_alpc_endpoint_and_connect(targetiid, version=(1,0), sid=gdef.WinLocalSy
dbgprint("ALPC endpoints list: <{0}>".format(alpctowers), "RPC")
for tower in alpctowers:
dbgprint("Trying to connect to endpoint <{0}>".format(tower.endpoint), "RPC")
alpc_port = r"\RPC Control\{0}".format(tower.endpoint)
alpc_port = r"\RPC Control\{0}".format(tower.endpoint.decode())
try:
client = windows.rpc.RPCClient(alpc_port)
except Exception as e:
+52 -11
View File
@@ -24,7 +24,7 @@ def pack_dword(x):
def dword_pad(s):
if (len(s) % 4) == 0:
return s
return s + ("P" * (4 - len(s) % 4))
return s + (b"P" * (4 - len(s) % 4))
class NdrUniquePTR(object):
@@ -55,7 +55,7 @@ class NdrUniquePTR(object):
def unpack_in_struct(self, stream):
ptr = NdrLong.unpack(stream)
if not ptr:
return 0, None
return 0, NdrUnpackNone
return ptr, self.subcls
def parse(self, stream):
@@ -64,6 +64,22 @@ class NdrUniquePTR(object):
return None
return self.subcls.parse(stream)
class NdrUnpackNone(object):
@classmethod
def unpack(cls, stream):
return None
class NdrRef(object):
# TESTING
def __init__(self, subcls):
self.subcls = subcls
def unpack(self, stream):
ptr = NdrLong.unpack(stream)
if not ptr:
raise ValueError("Ndr REF cannot be NULL")
return self.subcls.unpack(stream)
class NdrFixedArray(object):
def __init__(self, subcls, size):
self.subcls = subcls
@@ -72,7 +88,7 @@ class NdrFixedArray(object):
def pack(self, data):
data = list(data)
assert len(data) == self.size
return dword_pad("".join([self.subcls.pack(elt) for elt in data]))
return dword_pad(b"".join([self.subcls.pack(elt) for elt in data]))
def unpack(self, stream):
@@ -119,7 +135,7 @@ class NdrWString(object):
if not data.endswith('\x00'):
data += '\x00'
data = data.encode("utf-16-le")
l = (len(data) / 2)
l = (len(data) // 2)
result = struct.pack("<3I", l, 0, l)
result += data
return dword_pad(result)
@@ -249,7 +265,7 @@ class NdrStructure(object):
else:
packed_member = member.pack(memberdata)
res.append(packed_member)
return dword_pad("".join(conformant_size)) + dword_pad("".join(res)) + dword_pad("".join(pointed))
return dword_pad(b"".join(conformant_size)) + dword_pad(b"".join(res)) + dword_pad(b"".join(pointed))
@classmethod
def unpack(cls, stream):
@@ -257,7 +273,6 @@ class NdrStructure(object):
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"
data = []
if is_conformant:
conformant_size = NdrLong.unpack(stream)
@@ -279,7 +294,11 @@ class NdrStructure(object):
data.append(member.unpack(stream))
# print("Applying deref unpack")
for i, entry in post_subcls:
data[i] = entry.unpack(stream)
new_data = entry.unpack(stream)
if getattr(entry, "post_unpack", None):
new_data = entry.post_unpack(new_data)
data[i] = new_data
return cls.post_unpack(data)
@classmethod
@@ -305,7 +324,7 @@ class NdrParameters(object):
for (member, memberdata) in zip(cls.MEMBERS, data):
packed_member = member.pack(memberdata)
res.append(packed_member)
return "".join(dword_pad(elt) for elt in res)
return b"".join(dword_pad(elt) for elt in res)
@classmethod
def unpack(cls, stream):
@@ -321,14 +340,24 @@ class NdrConformantArray(object):
@classmethod
def pack(cls, data):
ndrsize = NdrLong.pack(len(data))
return dword_pad(ndrsize + "".join([cls.MEMBER_TYPE.pack(memberdata) for memberdata in data]))
return dword_pad(ndrsize + b"".join([cls.MEMBER_TYPE.pack(memberdata) for memberdata in data]))
@classmethod
def pack_conformant(cls, data):
ndrsize = NdrLong.pack(len(data))
ndrdata = dword_pad("".join([cls.MEMBER_TYPE.pack(memberdata) for memberdata in data]))
ndrdata = dword_pad(b"".join([cls.MEMBER_TYPE.pack(memberdata) for memberdata in data]))
return ndrsize, ndrdata
@classmethod
def unpack(cls, stream):
nbelt = NdrLong.unpack(stream)
result = cls.unpack_conformant(stream, nbelt)
return cls._post_unpack(result)
@classmethod
def _post_unpack(cls, result):
return result
@classmethod
def unpack_conformant(cls, stream, size):
res = [cls.MEMBER_TYPE.unpack(stream) for i in range(size)]
@@ -342,7 +371,7 @@ class NdrConformantVaryingArrays(object):
def pack(cls, data):
ndrsize = NdrLong.pack(len(data))
offset = NdrLong.pack(0)
return dword_pad(ndrsize + offset + ndrsize + "".join([cls.MEMBER_TYPE.pack(memberdata) for memberdata in data]))
return dword_pad(ndrsize + offset + ndrsize + b"".join([cls.MEMBER_TYPE.pack(memberdata) for memberdata in data]))
@classmethod
def unpack(cls, stream):
@@ -385,6 +414,8 @@ class NdrWcharConformantVaryingArrays(NdrConformantVaryingArrays):
def _post_unpack(self, result):
return u"".join(unichr(c) for c in result)
class NdrCharConformantVaryingArrays(NdrConformantVaryingArrays):
MEMBER_TYPE = NdrByte
class NdrHyperConformantVaryingArrays(NdrConformantVaryingArrays):
MEMBER_TYPE = NdrHyper
@@ -405,6 +436,13 @@ class NdrByteConformantArray(NdrConformantArray):
def _post_unpack(self, result):
return bytearray(result)
class NdrWcharConformantArray(NdrConformantArray):
MEMBER_TYPE = NdrShort
@classmethod
def _post_unpack(self, result):
return bytearray(result)
class NdrGuidConformantArray(NdrConformantArray):
MEMBER_TYPE = NdrGuid
@@ -438,12 +476,15 @@ class NdrStream(object):
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
size_to_align = (size - (already_read % size))
self.data = self.data[size_to_align:]
# print("align {0}: {1}".format(size, size_to_align))
return size_to_align
# print("align {0}: 0".format(size))
return 0
+4 -4
View File
@@ -8,18 +8,18 @@ DEFAULT_CREATION_FLAGS = gdef.CREATE_NEW_CONSOLE
if windows.system.bitness == 32:
def pop_proc_32(dwCreationFlags=DEFAULT_CREATION_FLAGS):
return create_process(r"C:\Windows\system32\{0}".format(test_binary_name), dwCreationFlags=dwCreationFlags, show_windows=True)
return create_process(r"C:\Windows\system32\{0}".format(test_binary_name).encode(), dwCreationFlags=dwCreationFlags, show_windows=True)
def pop_proc_64(dwCreationFlags=DEFAULT_CREATION_FLAGS):
raise WindowsError("Cannot create calc64 in 32bits system")
else:
def pop_proc_32(dwCreationFlags=DEFAULT_CREATION_FLAGS):
return create_process(r"C:\Windows\syswow64\{0}".format(test_binary_name), dwCreationFlags=dwCreationFlags, show_windows=True)
return create_process(r"C:\Windows\syswow64\{0}".format(test_binary_name).encode(), dwCreationFlags=dwCreationFlags, show_windows=True)
if windows.current_process.bitness == 32:
def pop_proc_64(dwCreationFlags=DEFAULT_CREATION_FLAGS):
with DisableWow64FsRedirection():
return create_process(r"C:\Windows\system32\{0}".format(test_binary_name), dwCreationFlags=dwCreationFlags, show_windows=True)
return create_process(r"C:\Windows\system32\{0}".format(test_binary_name).encode(), dwCreationFlags=dwCreationFlags, show_windows=True)
else:
def pop_proc_64(dwCreationFlags=DEFAULT_CREATION_FLAGS):
return create_process(r"C:\Windows\system32\{0}".format(test_binary_name), dwCreationFlags=dwCreationFlags, show_windows=True)
return create_process(r"C:\Windows\system32\{0}".format(test_binary_name).encode(), dwCreationFlags=dwCreationFlags, show_windows=True)
+2 -2
View File
@@ -80,13 +80,13 @@ class PartialBufferType(object):
def from_buffer(self, buffer): # size as kwargs ?
if len(buffer) % ctypes.sizeof(self.type):
raise NotImplementedError("Buffer size of not a multiple of sizeof({0})".format(self.type.__name__))
nbelt = len(buffer) / ctypes.sizeof(self.type)
nbelt = int(len(buffer) / ctypes.sizeof(self.type))
return self.create_real_implem(self.type, nbelt).from_buffer(buffer)
def from_buffer_copy(self, buffer): # size as kwargs ?
if len(buffer) % ctypes.sizeof(self.type):
raise NotImplementedError("Buffer size of not a multiple of sizeof({0})".format(self.type.__name__))
nbelt = len(buffer) / ctypes.sizeof(self.type)
nbelt = int(len(buffer) / ctypes.sizeof(self.type))
return self.create_real_implem(self.type, nbelt).from_buffer_copy(buffer)
def create(self, nbelt):
+35 -20
View File
@@ -51,7 +51,15 @@ def create_file_from_handle(handle, mode="r"):
"""Return a Python :class:`file` around a ``Windows`` HANDLE"""
flags = os.O_BINARY if "b" in mode else os.O_TEXT
fd = msvcrt.open_osfhandle(handle, flags)
return os.fdopen(fd, mode, 0)
kwargs = {}
if windows.pycompat.is_py3 and flags == os.O_TEXT:
# Buffering, encoding
args = (100, "ascii")
else:
# Buffering
args = (0,)
# In py2 os.fdopen do not accept kwargs
return os.fdopen(fd, mode, *args)
def get_handle_from_file(f):
@@ -68,7 +76,7 @@ def create_console():
sys.stdout = console_stdout
stdin_handle = winproxy.GetStdHandle(gdef.STD_INPUT_HANDLE)
console_stdin = create_file_from_handle(stdin_handle, "r+")
console_stdin = create_file_from_handle(stdin_handle, "r")
sys.stdin = console_stdin
stderr_handle = winproxy.GetStdHandle(gdef.STD_ERROR_HANDLE)
@@ -87,7 +95,7 @@ def create_process(path, args=None, dwCreationFlags=0, show_windows=True):
lpStartupInfo = ctypes.byref(StartupInfo)
lpCommandLine = None
if args:
lpCommandLine = (" ".join([str(a) for a in args]))
lpCommandLine = (b" ".join([a for a in args]))
windows.winproxy.CreateProcessA(path, lpCommandLine=lpCommandLine, dwCreationFlags=dwCreationFlags, lpProcessInformation=ctypes.byref(proc_info), lpStartupInfo=lpStartupInfo)
dbgprint("CreateProcessA new process handle {:#x}".format(proc_info.hProcess), "HANDLE")
dbgprint("CreateProcessA new thread handle {:#x}".format(proc_info.hThread), "HANDLE")
@@ -213,13 +221,29 @@ WIN_TO_UNIX_EPOCH_WIN_TICKS = WIN_TO_UNIX_EPOCH_SECOND * WIN_TICK_PER_SECOND_INT
def unix_timestamp_from_filetime(filetime):
# Round the filetime
round_win_ticks = ((filetime / 10) + int(round((filetime % 10) / 10.0))) * 10
last_number = (filetime % 10)
# We do some sort of "manual rounding cause of py2 vs py3
# PY2: round(0.5) == 1
# PY3: round(0.5) == 0
if last_number == 5:
rounding = 1
else:
rounding = round(last_number / 10.0)
round_win_ticks = ((filetime // 10) + int(rounding)) * 10
return round((round_win_ticks - WIN_TO_UNIX_EPOCH_WIN_TICKS) / WIN_TICK_PER_SECOND_FLOAT, 7)
def datetime_from_filetime(filetime):
"""return a :class:`datetime.datetime` from a ``windows`` FILETIME int"""
# Manual non-approx rounding as filetime will not have a perfect representation as Python float
round_microsecond = (filetime / 10) + int(round((filetime % 10) / 10.0))
# We do some sort of "manual rounding cause of py2 vs py3
# PY2: round(0.5) == 1
# PY3: round(0.5) == 0
last_number = (filetime % 10)
if last_number == 5:
rounding = 1
else:
rounding = round(last_number / 10.0)
round_microsecond = (filetime // 10) + int(rounding)
return WINDOWS_EPOCH + datetime.timedelta(microseconds=round_microsecond)
def filetime_from_datetime(dtime):
@@ -303,7 +327,7 @@ ntqueryinformationfile_info_structs = {
}
def query_file_information(file_or_handle, file_info_class):
if isinstance(file_or_handle, file):
if not isinstance(file_or_handle, windows.pycompat.int_types):
file_or_handle = windows.utils.get_handle_from_file(file_or_handle)
handle = file_or_handle
io_status = gdef.IO_STATUS_BLOCK()
@@ -313,7 +337,6 @@ def query_file_information(file_or_handle, file_info_class):
try:
windows.winproxy.NtQueryInformationFile(handle, io_status, pinfo, ctypes.sizeof(info), FileInformationClass=file_info_class)
except Exception as e:
# import pdb;pdb.set_trace()
if not (e.winerror & 0xffffffff) == gdef.STATUS_BUFFER_OVERFLOW:
raise
# STATUS_BUFFER_OVERFLOW -> Guess we have a FILE_NAME_INFORMATION somewhere that need a bigger buffer
@@ -407,7 +430,7 @@ ntqueryvolumeinformationfile_info_structs = {
# TODO: FileFsDriverPathInformation
# TODO: Extended FILE_FS_VOLUME_INFORMATION that can read the real value of 'VolumeLabel'
def query_volume_information(file_or_handle, volume_info_class):
if isinstance(file_or_handle, file):
if not isinstance(file_or_handle, windows.pycompat.int_types):
file_or_handle = get_handle_from_file(file_or_handle)
handle = file_or_handle
io_status = gdef.IO_STATUS_BLOCK()
@@ -460,12 +483,8 @@ def get_long_path(path):
:returns: :class:`str` | :obj:`unicode` -- same type as ``path`` parameter
"""
size = 0x1000
if isinstance(path, unicode):
buffer = ctypes.create_unicode_buffer(size)
rsize = winproxy.GetLongPathNameW(path, buffer, size)
else:
buffer = ctypes.c_buffer(size)
rsize = winproxy.GetLongPathNameA(path, buffer, size)
buffer = ctypes.create_unicode_buffer(size)
rsize = winproxy.GetLongPathNameW(path, buffer, size)
return buffer[:rsize]
@@ -478,12 +497,8 @@ def get_short_path(path):
:returns: :class:`str` | :obj:`unicode` -- same type as ``path`` parameter
"""
size = 0x1000
if isinstance(path, unicode):
buffer = ctypes.create_unicode_buffer(size)
rsize = winproxy.GetShortPathNameW(path, buffer, size)
else:
buffer = ctypes.c_buffer(size)
rsize = winproxy.GetShortPathNameA(path, buffer, size)
buffer = ctypes.create_unicode_buffer(size)
rsize = winproxy.GetShortPathNameW(path, buffer, size)
return buffer[:rsize]
def dospath_to_ntpath(dospath):
+1 -1
View File
@@ -191,7 +191,7 @@ class BitsCopyJob(IBackgroundCopyJob):
def __repr__(self):
return '<{0} iid="{1}" at {2:#08x}>'.format(type(self).__name__, self.iid.to_string(), id(self))
return '<{0} iid="{1}" at {2:#08x}>'.format(type(self).__name__, self.iid, id(self))
class BitsCopyJob2(gdef.IBackgroundCopyJob2, BitsCopyJob):
+1 -1
View File
@@ -18,7 +18,7 @@ def query_link(linkpath):
obj_attr.SecurityQualityOfService = 0
res = gdef.HANDLE()
x = winproxy.NtOpenSymbolicLinkObject(res, gdef.DIRECTORY_QUERY | gdef.READ_CONTROL , obj_attr)
v = gdef.LSA_UNICODE_STRING.from_string("\x00" * 1000)
v = gdef.LSA_UNICODE_STRING.from_size(1000)
s = gdef.ULONG()
winproxy.NtQuerySymbolicLinkObject(res, v, s) # Handle Buffer-too-small ?
return v.str
+1 -1
View File
@@ -378,7 +378,7 @@ class Process(utils.AutoHandle):
# handle read_wstring at end of page
# Of read failed: read only the half of size
# read_size must remain a multiple of 2
read_size = read_size / 2
read_size = int(read_size / 2)
continue
readden += read_size
# Bytearray will work on py2 & py3
+15 -9
View File
@@ -8,6 +8,7 @@ import windows
from windows.dbgprint import dbgprint
import windows.generated_def as gdef
from windows import winproxy
from windows.pycompat import basestring, int_types, is_py3
WENCODING = "utf-16-le"
@@ -61,9 +62,10 @@ def Py2Reg_DWORD_BIG_ENDIAN(obj):
def Reg2Py_BINARY(buffer, size):
return str(bytearray(buffer[:size]))
return bytes(bytearray(buffer[:size]))
def Py2Reg_BINARY(obj):
# latin-1 encoding if py3 & type is str ?
return obj
@@ -75,7 +77,8 @@ def Reg2Py_SZ(buffer, size):
# NULL TERMINATED: EASY
return buffer.as_wstring()
# Not null terminated: keep last byte
return (gdef.WCHAR * (size / 2)).from_buffer(buffer)[:]
assert not size % 2
return (gdef.WCHAR * (size // 2)).from_buffer(buffer)[:]
def Py2Reg_SZ(obj):
return obj.encode(WENCODING)
@@ -84,7 +87,10 @@ def Reg2Py_Multi_SZ(buffer, size):
if not size:
return []
# Simple path
rawstr = "".join([chr(c) for c in buffer[:size]])
if is_py3:
rawstr = bytes(buffer)
else:
rawstr = "".join([chr(c) for c in buffer[:size]])
try:
unistr = rawstr.decode(WENCODING)
return unistr.rstrip(u"\x00").split(u"\x00")
@@ -93,16 +99,16 @@ def Reg2Py_Multi_SZ(buffer, size):
# Complexe-path
# This is not some valide UTF-16
# Try our best to extract some stuff from raw
return rawstr.rstrip("\x00").split("\x00")
return rawstr.rstrip(b"\x00").split(b"\x00")
def Py2Reg_Multi_SZ(obj):
# Work on encoded values (to prevent str/unicode errors)
uni_list = [s.encode(WENCODING) for s in obj]
# Separate by UTF-16 NULL BYTE (2 \x00)
uni_str = "\x00\x00".join(uni_list)
uni_str = b"\x00\x00".join(uni_list)
# Add UTF-16 NULL byte for final string + final UTF-16 \x00 (4 \x00)
return uni_str + "\x00\x00\x00\x00"
return uni_str + b"\x00\x00\x00\x00"
DECODE_METHOD = 0
ENCODE_METHOD = 1
@@ -293,7 +299,7 @@ class PyHKey(object):
def _guess_value_type(self, value):
if isinstance(value, basestring):
return gdef.REG_SZ
elif isinstance(value, (int, long)):
elif isinstance(value, int_types):
return gdef.REG_DWORD
# elif isinstance(value, (list, tuple)):
# if all(isinstance(v, basestring) in value):
@@ -308,7 +314,7 @@ class PyHKey(object):
buffer = ENCODE_DECODE_METHODS[type][ENCODE_METHOD](value)
if isinstance(buffer, str): # Should not be unicode at this point
if isinstance(buffer, bytes): # Should not be unicode at this point
buffer = windows.utils.BUFFER(gdef.BYTE).from_buffer_copy(buffer)
return winproxy.RegSetValueExW(self.phkey, name, 0, type, buffer, len(buffer))
@@ -357,7 +363,7 @@ class PyHKey(object):
def __setitem__(self, name, value):
rtype = None
if not isinstance(value, (int, long, basestring)):
if not (isinstance(value, basestring) or isinstance(value, int_types)):
value, rtype = value
return self.set(name, value, rtype)
+31 -8
View File
@@ -147,8 +147,13 @@ class System(object):
:type: :class:`str`
"""
size = gdef.DWORD(0x1000)
buf = ctypes.c_buffer(size.value)
winproxy.GetComputerNameA(buf, ctypes.byref(size))
# For now I don't know what is best as A vs W APIs...
if windows.pycompat.is_py3:
buf = ctypes.create_unicode_buffer(size.value)
winproxy.GetComputerNameW(buf, ctypes.byref(size))
else:
buf = ctypes.create_string_buffer(size.value)
winproxy.GetComputerNameA(buf, ctypes.byref(size))
return buf[:size.value]
@utils.fixedpropety
@@ -372,12 +377,30 @@ class System(object):
@utils.fixedpropety
def build_number(self):
# This returns the last version where ntdll was updated
# Should look at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
# values: CurrentBuild + UBR
# windows.system.registry(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion")["CurrentBuild"].value
# windows.system.registry(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion")["UBR"].value
return self.get_file_version("comctl32")
# Best effort. use get_file_version if registry code fails
try:
# Does not works on Win7..
# Missing CurrentMajorVersionNumber/CurrentMinorVersionNumber/UBR
# We have CurrentVersion instead
# Use this code and get_file_version as a backup ?
curver_key = windows.system.registry(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion")
try:
major = curver_key["CurrentMajorVersionNumber"].value
minor = curver_key["CurrentMinorVersionNumber"].value
except WindowsError as e:
version = curver_key["CurrentVersion"].value
# May raise ValueError if no "."
major, minor = version.split(".")
build = curver_key["CurrentBuildNumber"].value
# Update Build Revision
try:
ubr = curver_key["UBR"].value
except WindowsError as e:
ubr = 0 # Not present on Win7
return "{0}.{1}.{2}.{3}".format(major, minor, build, ubr)
except (WindowsError, ValueError):
return self.get_file_version("ntdll")
@staticmethod
def enumerate_processes():
+3 -2
View File
@@ -1,12 +1,12 @@
import ctypes
import functools
from __builtin__ import type as bltn_type
import windows
from windows import utils
from windows import winproxy
import windows.generated_def as gdef
bltn_type = type
KNOW_INTEGRITY_LEVEL = gdef.FlagMapper(
gdef.SECURITY_MANDATORY_UNTRUSTED_RID,
@@ -465,7 +465,8 @@ class Token(utils.AutoHandle):
"""
mandatory_label = gdef.TOKEN_MANDATORY_LABEL()
mandatory_label.Label.Attributes = 0x60
mandatory_label.Label.Sid = gdef.PSID.from_string("S-1-16-{0}".format(integrity))
# cast integrity to int to accept SECURITY_MANDATORY_LOW_RID & other Flags
mandatory_label.Label.Sid = gdef.PSID.from_string("S-1-16-{0}".format(int(integrity)))
return self.set_informations(gdef.TokenIntegrityLevel, mandatory_label)
_INTEGRITY_PROPERTY_DOC = """The integrity of the token as an int (extracted from integrity PSID)
+2 -2
View File
@@ -71,7 +71,7 @@ def get_logical_drive_names():
size = 0x100
buffer = ctypes.c_buffer(size)
rsize = winproxy.GetLogicalDriveStringsA(0x1000, buffer)
return buffer[:rsize].rstrip("\x00").split("\x00")
return buffer[:rsize].rstrip(b"\x00").split(b"\x00")
def get_info(drivename):
size = 0x1000
@@ -85,4 +85,4 @@ def query_dos_device(name):
size = 0x1000
buffer = ctypes.c_buffer(size)
rsize = winproxy.QueryDosDeviceA(name, buffer, size)
return buffer[:rsize].rstrip("\x00").split("\x00")
return buffer[:rsize].rstrip(b"\x00").split(b"\x00")
+6 -3
View File
@@ -4,6 +4,9 @@ import windows.generated_def as gdef
from ..apiproxy import ApiProxy, NeededParameter
from ..error import no_error_check, fail_on_zero
import windows.pycompat
from windows.pycompat import int_types
class Crypt32Proxy(ApiProxy):
APIDLL = "crypt32"
default_error_check = staticmethod(fail_on_zero)
@@ -74,7 +77,7 @@ def CryptHashCertificate(hCryptProv, Algid, dwFlags, pbEncoded, cbEncoded, pbCom
@Crypt32Proxy()
def CertOpenStore(lpszStoreProvider, dwMsgAndCertEncodingType, hCryptProv, dwFlags, pvPara):
if isinstance(lpszStoreProvider, (long, int)):
if isinstance(lpszStoreProvider, int_types):
lpszStoreProvider = gdef.LPCSTR(lpszStoreProvider)
return CertOpenStore.ctypes_function(lpszStoreProvider, dwMsgAndCertEncodingType, hCryptProv, dwFlags, pvPara)
@@ -119,7 +122,7 @@ def CryptAcquireCertificatePrivateKey(pCert, dwFlags, pvParameters, phCryptProvO
@Crypt32Proxy()
def CryptEncryptMessage(pEncryptPara, cRecipientCert, rgpRecipientCert, pbToBeEncrypted, cbToBeEncrypted, pbEncryptedBlob, pcbEncryptedBlob):
if isinstance(pbToBeEncrypted, basestring):
if isinstance(pbToBeEncrypted, windows.pycompat.anybuff):
# Transform string to array of byte
pbToBeEncrypted = (gdef.BYTE * len(pbToBeEncrypted))(*bytearray(pbToBeEncrypted))
if cbToBeEncrypted is None and pbToBeEncrypted is not None:
@@ -161,7 +164,7 @@ def CryptVerifyMessageHash(pHashPara, pbHashedBlob, cbHashedBlob, pbToBeHashed,
@Crypt32Proxy()
def CryptEncodeObjectEx(dwCertEncodingType, lpszStructType, pvStructInfo, dwFlags, pEncodePara, pvEncoded, pcbEncoded):
lpszStructType = gdef.LPCSTR(lpszStructType) if isinstance(lpszStructType, (int, long)) else lpszStructType
lpszStructType = gdef.LPCSTR(lpszStructType) if isinstance(lpszStructType, int_types) else lpszStructType
return CryptEncodeObjectEx.ctypes_function(dwCertEncodingType, lpszStructType, pvStructInfo, dwFlags, pEncodePara, pvEncoded, pcbEncoded)
@Crypt32Proxy()