예제 #1
0
    def parseGeneralCapability(self, version: int, payload: bytes) -> DeviceRedirectionGeneralCapability:
        stream = BytesIO(payload)
        osType = Uint32LE.unpack(stream)
        osVersion = Uint32LE.unpack(stream)
        protocolMajorVersion = Uint16LE.unpack(stream)
        protocolMinorVersion = Uint16LE.unpack(stream)
        ioCode1 = Uint32LE.unpack(stream)
        ioCode2 = Uint32LE.unpack(stream)
        extendedPDU = Uint32LE.unpack(stream)
        extraFlags1 = Uint32LE.unpack(stream)
        extraFlags2 = Uint32LE.unpack(stream)
        specialTypeDeviceCap = None

        if version == GeneralCapabilityVersion.GENERAL_CAPABILITY_VERSION_02:
            specialTypeDeviceCap = Uint32LE.unpack(stream)

        return DeviceRedirectionGeneralCapability(
            version,
            osType,
            osVersion,
            protocolMajorVersion,
            protocolMinorVersion,
            ioCode1,
            ioCode2,
            extendedPDU,
            extraFlags1,
            extraFlags2,
            specialTypeDeviceCap
        )
예제 #2
0
    def parse(self, data: bytes) -> NegotiationRequestPDU:
        """
        Parse a negotiation request.
        :param data: the request data.
        """

        cookie = None

        if b"\r\n" in data:
            cookie = data[: data.index(b"\r\n")]
            data = data[data.index(b"\r\n") + 2 :]

        stream = BytesIO(data)

        if len(data) >= 8:
            type = Uint8.unpack(stream)
            requestFlags = Uint8.unpack(stream)
            requestLength = Uint16LE.unpack(stream)
            requestedProtocols = Uint32LE.unpack(stream)

            correlationFlags = None
            correlationID = None

            if requestFlags & NegotiationRequestFlags.CORRELATION_INFO_PRESENT != 0 and len(data) >= 36:
                type = Uint8.unpack(stream)
                correlationFlags = Uint8.unpack(stream)
                correlationLength = Uint16LE.unpack(stream)
                correlationID = stream.read(16)
                stream.read(16)

            return NegotiationRequestPDU(cookie, requestFlags, requestedProtocols, correlationFlags, correlationID)
        else:
            return NegotiationRequestPDU(cookie, None, None, None, None)
예제 #3
0
파일: fastpath.py 프로젝트: xianlimei/pyrdp
    def getEventLength(self, event: FastPathOutputUpdateEvent):

        if isinstance(event, bytes):
            header = Uint8.unpack(event[0])
            if self.isCompressed(header):
                return Uint16LE.unpack(event[2: 4]) + 4
            else:
                return Uint16LE.unpack(event[1: 3]) + 3

        size = 3

        if self.isCompressed(event.header):
            size += 1

        if isinstance(event, FastPathOrdersEvent):
            size += 2 + len(event.orderData)
        elif isinstance(event, FastPathBitmapEvent):
            size += len(event.payload)
        elif isinstance(event, FastPathOutputUpdateEvent):
            length = len(event.payload) + 3
            if event.compressionFlags is not None:
                length += 1
            return length

        return size
예제 #4
0
파일: fastpath.py 프로젝트: xianlimei/pyrdp
 def parseSecondaryDrawingOrder(self, orderData):
     stream = BytesIO(orderData)
     controlFlags = Uint8.unpack(stream.read(1))
     orderLength = Uint16LE.unpack(stream.read(2))
     extraFlags = Uint16LE.unpack(stream.read(2))
     orderType = Uint8.unpack(stream.read(1))
     return SecondaryDrawingOrder(controlFlags, orderLength, extraFlags, orderType)
예제 #5
0
 def parseSuppressOutput(self, stream: BytesIO, header):
     allowDisplayUpdates = Uint8.unpack(stream)
     stream.read(3)
     left = Uint16LE.unpack(stream)
     top = Uint16LE.unpack(stream)
     right = Uint16LE.unpack(stream)
     bottom = Uint16LE.unpack(stream)
     return SuppressOutputPDU(header, allowDisplayUpdates, left, top, right, bottom)
예제 #6
0
파일: slowpath.py 프로젝트: wjcxk21/pyrdp
 def parseShareDataHeader(self, stream: BytesIO, controlHeader: ShareControlHeader):
     shareID = Uint32LE.unpack(stream)
     stream.read(1)
     streamID = Uint8.unpack(stream)
     uncompressedLength = Uint16LE.unpack(stream)
     pduSubtype = Uint8.unpack(stream)
     compressedType = Uint8.unpack(stream)
     compressedLength = Uint16LE.unpack(stream)
     return ShareDataHeader(controlHeader.pduType, controlHeader.version, controlHeader.source, shareID, streamID, uncompressedLength, SlowPathDataType(pduSubtype), compressedType, compressedLength)
예제 #7
0
 def readChannelId(self, stream: BytesIO, cbid: int):
     if cbid == CbId.ONE_BYTE:
         return Uint8.unpack(stream)
     elif cbid == CbId.TWO_BYTE:
         return Uint16LE.unpack(stream)
     elif cbid == CbId.FOUR_BYTES:
         return Uint16LE.unpack(stream)
     else:
         raise ValueError(f"Invalid channel id length: {cbid}")
예제 #8
0
    def doParse(self, data: bytes) -> DeviceRedirectionPDU:
        stream = BytesIO(data)
        component = DeviceRedirectionComponent(Uint16LE.unpack(stream))
        packetID = DeviceRedirectionPacketID(Uint16LE.unpack(stream))

        if component == DeviceRedirectionComponent.RDPDR_CTYP_CORE and packetID in self.parsers.keys():
            return self.parsers[packetID](stream)
        else:
            return DeviceRedirectionPDU(component, packetID, payload=stream.read())
예제 #9
0
    def parse(s: BytesIO) -> 'StreamBitmapNext':
        self = StreamBitmapNext()

        self.flags = Uint8.unpack(s)
        self.bitmapType = Uint16LE.unpack(s)

        blockSize = Uint16LE.unpack(s)
        self.data = s.read(blockSize)

        return self
예제 #10
0
 def parseLicenseBlob(self, stream):
     """
     Parse the provided byte stream and return the corresponding RDPLicenseBinaryBlob
     :type stream: BytesIO
     :return: RDPLicenseBinaryBlob
     """
     type = LicenseBinaryBlobType(Uint16LE.unpack(stream))
     length = Uint16LE.unpack(stream)
     data = stream.read(length)
     return LicenseBinaryBlob(type, data)
예제 #11
0
    def parseCapability(self, stream: BytesIO) -> DeviceRedirectionCapability:
        capabilityType = RDPDRCapabilityType(Uint16LE.unpack(stream))
        capabilityLength = Uint16LE.unpack(stream)
        version = Uint32LE.unpack(stream)
        payload = stream.read(capabilityLength - 8)

        if capabilityType in self.capabilityParsers:
            return self.capabilityParsers[capabilityType](version, payload)
        else:
            return DeviceRedirectionCapability(capabilityType, version, payload)
예제 #12
0
    def parseBitmapCapability(self, data: bytes) -> BitmapCapability:
        """
        https://msdn.microsoft.com/en-us/library/cc240554.aspx
        :param data: Raw data starting after lengthCapability
        """
        stream = BytesIO(data)
        preferredBitsPerPixel = Uint16LE.unpack(stream.read(2))
        receive1bitPerPixel = Uint16LE.unpack(stream.read(2))
        receive4bitPerPixel = Uint16LE.unpack(stream.read(2))
        receive8bitPerPixel = Uint16LE.unpack(stream.read(2))
        desktopWidth = Uint16LE.unpack(stream.read(2))
        desktopHeight = Uint16LE.unpack(stream.read(2))
        stream.read(2)  # pad2octets
        desktopResizeFlag = Uint16LE.unpack(stream.read(2))
        bitmapCompressionFlag = Uint16LE.unpack(stream.read(2))
        highColorFlags = Uint8.unpack(stream.read(1))
        drawingFlags = Uint8.unpack(stream.read(1))
        multipleRectangleSupport = Uint16LE.unpack(stream.read(2))
        # ignoring pad2octetsB

        capability = BitmapCapability(preferredBitsPerPixel,
                                      receive1bitPerPixel, receive4bitPerPixel,
                                      receive8bitPerPixel, desktopWidth,
                                      desktopHeight, desktopResizeFlag,
                                      bitmapCompressionFlag, highColorFlags,
                                      drawingFlags, multipleRectangleSupport)
        capability.rawData = data
        return capability
예제 #13
0
    def parseCapabilitySets(self, capabilitySetsRaw, numberCapabilities):
        stream = BytesIO(capabilitySetsRaw)
        capabilitySets = {}
        # Do minimum parsing for every capability
        for i in range(numberCapabilities):
            capabilitySetType = Uint16LE.unpack(stream.read(2))
            lengthCapability = Uint16LE.unpack(stream.read(2))
            capabilityData = stream.read(lengthCapability - 4)
            capability = Capability(capabilitySetType, capabilityData)
            capabilitySets[CapabilityType(capabilitySetType)] = capability

        # Fully parse the General capability set
        capabilitySets[CapabilityType.CAPSTYPE_GENERAL] = \
            self.parseGeneralCapability(capabilitySets[CapabilityType.CAPSTYPE_GENERAL].rawData)

        # Fully parse the Glyph cache capability set
        if CapabilityType.CAPSTYPE_GLYPHCACHE in capabilitySets:
            capabilitySets[CapabilityType.CAPSTYPE_GLYPHCACHE] = \
                self.parseGlyphCacheCapability(capabilitySets[CapabilityType.CAPSTYPE_GLYPHCACHE].rawData)

        # If present, fully parse the offscreen cache capability set
        if CapabilityType.CAPSTYPE_OFFSCREENCACHE in capabilitySets:
            capabilitySets[CapabilityType.CAPSTYPE_OFFSCREENCACHE] = \
                self.parseOffscreenCacheCapability(capabilitySets[CapabilityType.CAPSTYPE_OFFSCREENCACHE].rawData)

        # If present, fully parse the surface commands cache capability set
        if CapabilityType.CAPSETTYPE_SURFACE_COMMANDS in capabilitySets:
            capabilitySets[CapabilityType.CAPSETTYPE_SURFACE_COMMANDS] = \
                self.parseSurfaceCommandsCapability(capabilitySets[CapabilityType.CAPSETTYPE_SURFACE_COMMANDS].rawData)

        # Fully parse the Bitmap capability set
        capabilitySets[CapabilityType.CAPSTYPE_BITMAP] = \
            self.parseBitmapCapability(capabilitySets[CapabilityType.CAPSTYPE_BITMAP].rawData)

        # Fully parse the Order capability set
        capabilitySets[
            CapabilityType.CAPSTYPE_ORDER] = self.parseOrderCapability(
                capabilitySets[CapabilityType.CAPSTYPE_ORDER].rawData)

        # Fully parse the VirtualChannel capability set
        if CapabilityType.CAPSTYPE_VIRTUALCHANNEL in capabilitySets:
            capabilitySets[
                CapabilityType.
                CAPSTYPE_VIRTUALCHANNEL] = self.parseVirtualChannelCapability(
                    capabilitySets[
                        CapabilityType.CAPSTYPE_VIRTUALCHANNEL].rawData)

        # Fully parse the Pointer capability set
        if CapabilityType.CAPSTYPE_POINTER in capabilitySets:
            capabilitySets[
                CapabilityType.CAPSTYPE_POINTER] = self.parsePointerCapability(
                    capabilitySets[CapabilityType.CAPSTYPE_POINTER].rawData)

        return capabilitySets
예제 #14
0
    def parse(s: BytesIO) -> 'GdiPlusCacheNext':
        self = GdiPlusCacheNext()

        self.flags = Uint8.unpack(s)
        self.cacheType = Uint16LE.unpack(s)
        self.cacheIdx = Uint16LE.unpack(s)

        cbSize = Uint16LE.unpack(s)
        self.data = s.read(cbSize)

        return self
예제 #15
0
파일: slowpath.py 프로젝트: wjcxk21/pyrdp
    def parsePointerCapability(self, data: bytes) -> PointerCapability:
        """
        https://msdn.microsoft.com/en-us/library/cc240562.aspx
        :param data: Raw data starting after lengthCapability
        """
        stream = BytesIO(data)
        colorPointerFlag = Uint16LE.unpack(stream)
        colorPointerCacheSize = Uint16LE.unpack(stream)
        pointerCacheSize = Uint16LE.unpack(stream)

        return PointerCapability(colorPointerFlag, colorPointerCacheSize, pointerCacheSize)
예제 #16
0
    def parse(s: BytesIO) -> 'GdiPlusCacheEnd':
        self = GdiPlusCacheEnd()

        self.flags = Uint8.unpack(s)
        self.cacheType = Uint16LE.unpack(s)
        self.cacheIndex = Uint16LE.unpack(s)
        cbSize = Uint16LE.unpack(s)
        self.totalSize = Uint32LE.unpack(s)
        self.data = s.read(cbSize)

        return self
예제 #17
0
    def parseBitmapEvent(
            self,
            fastPathBitmapEvent: FastPathOutputEvent) -> FastPathBitmapEvent:
        rawBitmapUpdateData = fastPathBitmapEvent.payload
        stream = BytesIO(rawBitmapUpdateData)
        Uint16LE.unpack(stream.read(2))  # updateType (unused)
        bitmapData = self.bitmapParser.parseBitmapUpdateData(stream.read())

        return FastPathBitmapEvent(fastPathBitmapEvent.header,
                                   fastPathBitmapEvent.compressionFlags,
                                   bitmapData, rawBitmapUpdateData)
예제 #18
0
    def parseColorEvent(self, stream):
        cacheIndex = Uint16LE.unpack(stream)
        hotSpot = Uint32LE.unpack(stream)
        width = Uint16LE.unpack(stream)
        height = Uint16LE.unpack(stream)
        andMaskLength = Uint16LE.unpack(stream)
        xorMaskLength = Uint16LE.unpack(stream)
        xorMask = stream.read(xorMaskLength)
        andMask = stream.read(andMaskLength)
        stream.read(1)

        return PointerColorEvent(cacheIndex, hotSpot, width, height, andMask, xorMask)
예제 #19
0
파일: slowpath.py 프로젝트: wjcxk21/pyrdp
    def parseDemandActive(self, stream: BytesIO, header):
        shareID = Uint32LE.unpack(stream)
        lengthSourceDescriptor = Uint16LE.unpack(stream)
        lengthCombinedCapabilities = Uint16LE.unpack(stream)
        sourceDescriptor = stream.read(lengthSourceDescriptor)
        numberCapabilities = Uint16LE.unpack(stream)
        pad2Octets = stream.read(2)
        capabilitySets = stream.read(lengthCombinedCapabilities - 4)
        sessionID = Uint32LE.unpack(stream)
        parsedCapabilitySets = self.parseCapabilitySets(capabilitySets, numberCapabilities)

        return DemandActivePDU(header, shareID, sourceDescriptor, numberCapabilities, capabilitySets, sessionID, parsedCapabilitySets)
예제 #20
0
 def parseSingleCapability(self, stream: BytesIO) -> Tuple[CapabilityType, DeviceRedirectionCapability]:
     """
     https://msdn.microsoft.com/en-us/library/cc241325.aspx
     """
     capabilityType = CapabilityType(Uint16LE.unpack(stream))
     capabilityLength = Uint16LE.unpack(stream)
     version = Uint32LE.unpack(stream)
     payload = stream.read(capabilityLength - 8)
     if capabilityType == CapabilityType.CAP_GENERAL_TYPE:
         return capabilityType, self.parseGeneralCapability(version, payload)
     else:
         return capabilityType, DeviceRedirectionCapability(capabilityType, version, payload=payload)
예제 #21
0
파일: clipboard.py 프로젝트: robeving/pyrdp
    def parse(self, data):
        stream = BytesIO(data)
        msgType = Uint16LE.unpack(stream)
        msgFlags = Uint16LE.unpack(stream)
        dataLen = Uint32LE.unpack(stream)
        payload = stream.read(dataLen)

        if msgType in self.dispatch:
            clipboardPDU = self.dispatch[msgType](payload, msgFlags)
        else:
            clipboardPDU = ClipboardPDU(ClipboardMessageType(msgType), msgFlags, payload)

        return clipboardPDU
예제 #22
0
파일: slowpath.py 프로젝트: wjcxk21/pyrdp
    def parseOffscreenCacheCapability(self, data) -> OffscreenBitmapCacheCapability:
        """
        https://msdn.microsoft.com/en-us/library/cc240550.aspx
        :param data: Raw data starting after lengthCapability
        """
        stream = BytesIO(data)
        offscreenSupportLevel = Uint32LE.unpack(stream.read(4))
        offscreenCacheSize = Uint16LE.unpack(stream.read(2))
        offscreenCacheEntries = Uint16LE.unpack(stream.read(2))

        capability = OffscreenBitmapCacheCapability(offscreenSupportLevel, offscreenCacheSize, offscreenCacheEntries)
        capability.rawData = data
        return capability
예제 #23
0
    def parse(self, data: bytes) -> DeviceRedirectionPDU:
        stream = BytesIO(data)
        unpack = Uint16LE.unpack(stream)
        component = DeviceRedirectionComponent(unpack)
        packetId = DeviceRedirectionPacketId(Uint16LE.unpack(stream))

        if component == DeviceRedirectionComponent.RDPDR_CTYP_PRN:
            log.warning("Received Printing component packets, which are not handled. Might cause a crash.")

        if packetId in self.parsers.keys():
            return self.parsers[packetId](stream)
        else:
            return DeviceRedirectionPDU(component, packetId, payload=stream.read())
예제 #24
0
파일: slowpath.py 프로젝트: wjcxk21/pyrdp
    def parseConfirmActive(self, stream: BytesIO, header):
        shareID = Uint32LE.unpack(stream)
        originatorID = Uint16LE.unpack(stream)
        lengthSourceDescriptor = Uint16LE.unpack(stream)
        lengthCombinedCapabilities = Uint16LE.unpack(stream)
        sourceDescriptor = stream.read(lengthSourceDescriptor)
        numberCapabilities = Uint16LE.unpack(stream)
        stream.read(2)
        capabilitySetsRaw = stream.read(lengthCombinedCapabilities - 4)
        capabilitySets = self.parseCapabilitySets(capabilitySetsRaw, numberCapabilities)

        return ConfirmActivePDU(header, shareID, originatorID, sourceDescriptor,
                                numberCapabilities, capabilitySets, capabilitySetsRaw)
예제 #25
0
 def parse(self, data):
     stream = BytesIO(data)
     msgType = Uint16LE.unpack(stream)
     msgFlags = Uint16LE.unpack(stream)
     dataLen = Uint32LE.unpack(stream)
     payload = stream.read(dataLen)
     if msgType == ClipboardMessageType.CB_FORMAT_DATA_RESPONSE:
         clipboardPDU = self.parseFormatDataResponse(payload, msgFlags)
     elif msgType == ClipboardMessageType.CB_FORMAT_LIST:
         clipboardPDU = self.parseFormatList(payload, msgFlags)
     else:
         clipboardPDU = ClipboardPDU(ClipboardMessageType(msgType), msgFlags, payload)
     return clipboardPDU
예제 #26
0
    def parseStructure(self, stream: BytesIO) -> typing.Union[ClientCoreData, ClientNetworkData, ClientSecurityData, ClientClusterData]:
        header = Uint16LE.unpack(stream)
        length = Uint16LE.unpack(stream) - 4
        data = stream.read(length)

        if len(data) != length:
            raise ParsingError("Client Data length field does not match actual size")

        substream = BytesIO(data)

        if header not in self.parsers:
            raise UnknownPDUTypeError("Trying to parse unknown client data structure %s" % header, header)

        return self.parsers[header](substream)
예제 #27
0
    def parseProprietaryCertificate(self, stream: BytesIO) -> ProprietaryCertificate:
        signatureAlgorithmID = Uint32LE.unpack(stream)
        keyAlgorithmID = Uint32LE.unpack(stream)
        publicKeyType = Uint16LE.unpack(stream)
        keyLength = Uint16LE.unpack(stream)
        publicKey = stream.read(keyLength)
        signatureType = Uint16LE.unpack(stream)
        signatureLength = Uint16LE.unpack(stream)
        signature = stream.read(signatureLength - 8)
        padding = stream.read()

        publicKey = self.parsePublicKey(publicKey)

        return ProprietaryCertificate(signatureAlgorithmID, keyAlgorithmID, publicKeyType, publicKey, signatureType, signature, padding)
예제 #28
0
    def parseStructure(self, stream):
        header = Uint16LE.unpack(stream)
        length = Uint16LE.unpack(stream) - 4
        data = stream.read(length)

        if len(data) != length:
            raise ParsingError("Client Data length field does not match actual size")

        substream = BytesIO(data)

        if header not in self.parsers:
            raise UnknownPDUTypeError("Trying to parse unknown client data structure %s" % header, header)

        return self.parsers[header](substream)
예제 #29
0
    def parseOrderCapability(self, data) -> OrderCapability:
        """
        https://msdn.microsoft.com/en-us/library/cc240556.aspx
        :param data: Raw data starting after lengthCapability
        """
        stream = BytesIO(data)
        terminalDescriptor = stream.read(16)
        stream.read(4)  # pad4octetsA
        desktopSaveXGranularity = Uint16LE.unpack(stream.read(2))
        desktopSaveYGranularity = Uint16LE.unpack(stream.read(2))
        stream.read(2)  # pad2octetsA
        maximumOrderLevel = Uint16LE.unpack(stream.read(2))
        numberFonts = Uint16LE.unpack(stream.read(2))
        orderFlags = Uint16LE.unpack(stream.read(2))
        orderSupport = stream.read(32)
        textFlags = Uint16LE.unpack(stream.read(2))
        orderSupportExFlags = Uint16LE.unpack(stream.read(2))
        stream.read(4)  # pad4octetsB
        desktopSaveSize = Uint32LE.unpack(stream.read(4))
        stream.read(4)  # pad2octetsC, pad2octetsD
        textANSICodePage = Uint16LE.unpack(stream.read(2))
        # ignoring pad2octetsE

        capability = OrderCapability(
            terminalDescriptor, desktopSaveXGranularity,
            desktopSaveYGranularity, maximumOrderLevel, numberFonts,
            orderFlags, orderSupport, textFlags, orderSupportExFlags,
            desktopSaveSize, textANSICodePage)
        capability.rawData = data
        return capability
예제 #30
0
    def parseClientCoreData(self, stream: BytesIO) -> ClientCoreData:
        stream = StrictStream(stream)

        # 128 bytes minimum (excluding header)
        version = RDPVersion(Uint32LE.unpack(stream))
        desktopWidth = Uint16LE.unpack(stream)
        desktopHeight = Uint16LE.unpack(stream)
        colorDepth = ColorDepth(Uint16LE.unpack(stream))
        sasSequence = Uint16LE.unpack(stream)
        keyboardLayout = Uint32LE.unpack(stream)
        clientBuild = Uint32LE.unpack(stream)
        clientName = decodeUTF16LE(stream.read(32))
        keyboardType = Uint32LE.unpack(stream)
        keyboardSubType = Uint32LE.unpack(stream)
        keyboardFunctionKey = Uint32LE.unpack(stream)
        imeFileName = stream.read(64)

        core = ClientCoreData(version, desktopWidth, desktopHeight, colorDepth,
                              sasSequence, keyboardLayout, clientBuild,
                              clientName, keyboardType, keyboardSubType,
                              keyboardFunctionKey, imeFileName)

        # Optional data
        # The optional fields are read in order. If one of them is not present, then all subsequent fields are also not present.
        try:
            core.postBeta2ColorDepth = Uint16LE.unpack(stream)
            core.clientProductId = Uint16LE.unpack(stream)
            core.serialNumber = Uint32LE.unpack(stream)

            # Should match HighColorDepth enum most of the time, but in order to support scanners and we script, we have to loosely accept this one
            # Anyway, the server will reject it and enforce another one
            core.highColorDepth = Uint16LE.unpack(stream)
            core.supportedColorDepths = Uint16LE.unpack(stream)
            core.earlyCapabilityFlags = Uint16LE.unpack(stream)
            core.clientDigProductId = decodeUTF16LE(stream.read(64))
            core.connectionType = ConnectionType(Uint8.unpack(stream))
            stream.read(1)
            core.serverSelectedProtocol = Uint32LE.unpack(stream)
            core.desktopPhysicalWidth = Uint32LE.unpack(stream)
            core.desktopPhysicalHeight = Uint32LE.unpack(stream)
            core.desktopOrientation = DesktopOrientation(
                Uint16LE.unpack(stream))
            core.desktopScaleFactor = Uint32LE.unpack(stream)
            core.deviceScaleFactor = Uint32LE.unpack(stream)
        except EOFError:
            # The stream has reached the end, we don't have any more optional fields. This exception can be ignored.
            pass

        return core