コード例 #1
0
        private RemotePlayContext SendInitControlMessages(Socket udpClient)
        {
            /******** Initial control message 1 ********/

            byte[]         controlMessage1Payload            = HexUtil.Unhexlify("0064006400004823");
            ushort         initialControlMessage1PayloadSize = (ushort)(12 + controlMessage1Payload.Length);
            ControlMessage initialMessage1 = new ControlMessage(0, 0, 0, 0, 1, 0, initialControlMessage1PayloadSize, 0x4823, 0x19000);

            initialMessage1.UnParsedPayload = controlMessage1Payload;
            byte[]        initialControlMessage1Data = GetByteArrayForControlMessage(initialMessage1);
            ControlResult initControlResult1         = SendControlDataAndWaitForAnswer(udpClient, initialControlMessage1Data, 1, "Send Init Control message 1");

            if (!initControlResult1.WasSuccessful)
            {
                return(null);
            }

            ControlMessage initialAnswer1 = initControlResult1.ControlMessages[0];

            /******** Initial control message 2 ********/

            byte[]       initialAnswer1Payload = initialAnswer1.UnParsedPayload;
            MemoryStream memoryBuffer          = new MemoryStream(initialAnswer1Payload)
            {
                Position = 8
            };

            byte[] funcIncrBuffer = new byte[4];
            memoryBuffer.Read(funcIncrBuffer, 0, funcIncrBuffer.Length);
            uint funcIncrValue = ByteUtil.ByteArrayToUInt(funcIncrBuffer);

            memoryBuffer.Position = 28;
            byte[] lastAnswerPart = new byte[memoryBuffer.Length - memoryBuffer.Position];
            memoryBuffer.Read(lastAnswerPart, 0, lastAnswerPart.Length);
            byte[] funcIncr   = ByteUtil.UIntToByteArray(initialAnswer1.FuncIncr);
            byte[] classValue = ByteUtil.UIntToByteArray(initialAnswer1.ClassValue);

            byte[] controlMessage2Payload            = ByteUtil.ConcatenateArrays(funcIncr, classValue, funcIncr, lastAnswerPart);
            ushort initialControlMessage2PayloadSize = (ushort)(12 + controlMessage2Payload.Length);

            ControlMessage controlMessage2 = new ControlMessage(0, initialAnswer1.FuncIncr, 0, 0, 10, 0, initialControlMessage2PayloadSize, funcIncrValue, initialAnswer1.ReceiverId);

            controlMessage2.UnParsedPayload = controlMessage2Payload;
            byte[] initialControlMessage2Data = GetByteArrayForControlMessage(controlMessage2);

            ControlResult initControlResult2 = SendControlDataAndWaitForAnswer(udpClient, initialControlMessage2Data, 1, "Send Init Control message 2");

            if (!initControlResult2.WasSuccessful)
            {
                return(null);
            }

            RemotePlayContext remotePlayContext = new RemotePlayContext()
            {
                ReceiverId = initialAnswer1.FuncIncr,
                FuncIncr   = initialMessage1.FuncIncr
            };

            return(remotePlayContext);
        }
コード例 #2
0
        /*********** heartbeat handling ***********/

        private void HandleHeartBeat(Socket udpSocket, RemotePlayContext remotePlayContext)
        {
            Thread heartBeatThread = new Thread(() =>
            {
                try
                {
                    do
                    {
                        Thread.Sleep(1000);
                        byte[] heartbeatPayload     = HexUtil.Unhexlify("000803");
                        ushort heartbeatPayloadSize = (ushort)(12 + heartbeatPayload.Length);
                        ControlMessage heartbeat    = new ControlMessage((byte)0, remotePlayContext.ReceiverId, 0, 0, 0, 1, heartbeatPayloadSize, remotePlayContext.FuncIncr, 0x10000);
                        heartbeat.UnParsedPayload   = heartbeatPayload;

                        SendCryptedControlMessage(heartbeat, remotePlayContext, udpSocket);
                    } while (true);
                }
                catch (Exception)
                {
                    // ignore
                }
            });

            heartBeatThread.IsBackground = true;
            heartBeatThread.Start();
        }
コード例 #3
0
        private void HandleFeedbackData(Socket udpSocket, RemotePlayContext remotePlayContext)
        {
            Thread heartBeatThread = new Thread(() =>
            {
                try
                {
                    do
                    {
                        FeedbackMessage feedbackMessage = new FeedbackMessage(_feedbackSequenceNumber, 0, 0, 0);
                        byte[] buf = new byte[25];
                        buf[0x0]   = 0xa0; // TODO
                        buf[0x1]   = 0xff; // TODO
                        buf[0x2]   = 0x7f; // TODO
                        buf[0x3]   = 0xff; // TODO
                        buf[0x4]   = 0x7f; // TODO
                        buf[0x5]   = 0xff; // TODO
                        buf[0x6]   = 0x7f; // TODO
                        buf[0x7]   = 0xff; // TODO
                        buf[0x8]   = 0x7f; // TODO
                        buf[0x9]   = 0x99; // TODO
                        buf[0xa]   = 0x99; // TODO
                        buf[0xb]   = 0xff; // TODO
                        buf[0xc]   = 0x7f; // TODO
                        buf[0xd]   = 0xfe; // TODO
                        buf[0xe]   = 0xf7; // TODO
                        buf[0xf]   = 0xef; // TODO
                        buf[0x10]  = 0x1f; // TODO
                        buf[0x11]  = 0;    // TODO
                        buf[0x12]  = 0;    // TODO
                        buf[0x13]  = 0;    // TODO
                        buf[0x14]  = 0;    // TODO
                        buf[0x15]  = 0;    // TODO
                        buf[0x16]  = 0;    // TODO
                        buf[0x17]  = 0;    // TODO
                        buf[0x18]  = 0;    // TODO


                        SendCryptedFeedbackMessage(feedbackMessage, buf, remotePlayContext, udpSocket);
                        ++_feedbackSequenceNumber;
                        Thread.Sleep(200);
                    } while (true);
                }
                catch (Exception)
                {
                    // ignore
                }
            });

            heartBeatThread.IsBackground = true;
            heartBeatThread.Start();
        }
コード例 #4
0
        /*********** UDP request ***********/

        public void InitializeRemotePlayChannel(Session session, IPEndPoint ps4Endpoint)
        {
            const int retry = 3;

            for (int i = 0; i < retry; i++)
            {
                Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                udpClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                udpClient.ExclusiveAddressUse = false;
                udpClient.ReceiveTimeout      = 5500;
                udpClient.Connect(ps4Endpoint.Address, RemotePlayPort);

                RemotePlayContext remotePlayContext = SendInitControlMessages(udpClient);
                if (remotePlayContext == null)
                {
                    udpClient.Close();
                    udpClient.Dispose();
                    Thread.Sleep(2500);
                    continue;
                }

                remotePlayContext = SendBigBangMessages(udpClient, session, remotePlayContext);
                if (remotePlayContext == null)
                {
                    udpClient.Close();
                    udpClient.Dispose();
                    Thread.Sleep(2500);
                    continue;
                }

                remotePlayContext = SendStreamInfo(udpClient, session, remotePlayContext);
                if (remotePlayContext == null)
                {
                    udpClient.Close();
                    udpClient.Dispose();
                    Thread.Sleep(2500);
                    continue;
                }

                OnPs4LogInfo?.Invoke(this, "!!!!!!!!!!!!! Stream initialization successfully completed" + Environment.NewLine);

                _udpClient = udpClient;
                RemotePlayAsyncState remotePlayAsyncState = new RemotePlayAsyncState(remotePlayContext, session);
                //HandleFeedbackData(_udpClient, remotePlayContext);
                HandleHeartBeat(_udpClient, remotePlayContext);
                _udpClient.BeginReceive(remotePlayAsyncState.Buffer, 0, remotePlayAsyncState.Buffer.Length, SocketFlags.None, HandleRemotePlayStream, remotePlayAsyncState);

                break;
            }
        }
コード例 #5
0
        private void SendCryptedControlMessage(ControlMessage controlMessage, RemotePlayContext remotePlayContext, Socket udpClient)
        {
            controlMessage.TagPos = 0;
            byte[] controlMessageData = GetByteArrayForControlMessage(controlMessage);
            int    macOffset          = GetMacOffset(0); // crypto position

            CryptoService.AddGmacToBuffer(remotePlayContext.LocalGmacInfo, remotePlayContext.LocalKeyPosition, controlMessageData, macOffset);

            byte[] keyPosBuffer = ByteUtil.UIntToByteArray(remotePlayContext.LocalKeyPosition);
            int    position     = GetTagPosOffset(0);

            for (int i = 0; i < keyPosBuffer.Length; i++)
            {
                controlMessageData[position + i] = keyPosBuffer[i];
            }

            remotePlayContext.LocalKeyPosition += 16; //controlMessage.UnParsedPayload.Length < 16 ? 16 : controlMessage.UnParsedPayload.Length;

            SendData(udpClient, controlMessageData);
        }
コード例 #6
0
        private ControlResult SendCryptedControlMessageAndWaitForAnswer(ControlMessage controlMessage, RemotePlayContext remotePlayContext, Socket udpClient, int expectedPackets, string info)
        {
            controlMessage.TagPos = 0;
            byte[] controlMessageData = GetByteArrayForControlMessage(controlMessage);
            int    macOffset          = GetMacOffset(0); // crypto position

            CryptoService.AddGmacToBuffer(remotePlayContext.LocalGmacInfo, remotePlayContext.LocalKeyPosition, controlMessageData, macOffset);

            byte[] keyPosBuffer = ByteUtil.UIntToByteArray(remotePlayContext.LocalKeyPosition);
            int    position     = GetTagPosOffset(0);

            for (int i = 0; i < keyPosBuffer.Length; i++)
            {
                controlMessageData[position + i] = keyPosBuffer[i];
            }

            remotePlayContext.LocalKeyPosition += 16; // controlMessage.UnParsedPayload.Length < 16 ? 16 : controlMessage.UnParsedPayload.Length;

            return(SendControlDataAndWaitForAnswer(udpClient, controlMessageData, expectedPackets, info));
        }
コード例 #7
0
        private void SendCryptedFeedbackMessage(FeedbackMessage feedbackMessage, byte[] feebdackBuffer, RemotePlayContext remotePlayContext, Socket udpClient)
        {
            feedbackMessage.TagPos = (uint)remotePlayContext.LocalKeyPosition;
            byte[] feedbackMessageData = GetByteArrayForFeedbackMessage(feedbackMessage);
            int    macOffset           = GetMacOffset(6); // crypto position

            CryptoService.AddGmacToBuffer(remotePlayContext.LocalGmacInfo, feedbackMessage.TagPos, feedbackMessageData, macOffset);
            feebdackBuffer = CryptoService.EncryptGmacMessage(remotePlayContext.LocalGmacInfo, feedbackMessage.TagPos, feebdackBuffer);
            remotePlayContext.LocalKeyPosition += 16; //controlMessage.UnParsedPayload.Length < 16 ? 16 : controlMessage.UnParsedPayload.Length;

            SendData(udpClient, ByteUtil.ConcatenateArrays(feedbackMessageData, feebdackBuffer));
        }
コード例 #8
0
        private RemotePlayContext SendStreamInfo(Socket udpClient, Session session, RemotePlayContext remotePlayContext)
        {
            /******** Stream info receive ********/

            ControlResult resolutionInfoPayload = WaitForControlMessage(udpClient, 1, "Wait for stream info payload").Result;

            if (!resolutionInfoPayload.WasSuccessful)
            {
                if (remotePlayContext.LastSentMessage.Length > 0)
                {
                    resolutionInfoPayload = SendControlDataAndWaitForAnswer(udpClient, remotePlayContext.LastSentMessage, 1, "Resend bang ack control message and wait for stream info payload");
                    if (!resolutionInfoPayload.WasSuccessful)
                    {
                        return(null);
                    }
                }
                else
                {
                    return(null);
                }
            }

            ControlMessage resolutionInfoControlMessage = resolutionInfoPayload.ControlMessages[0];
            TakionMessage  resolutionPayload            = Serializer.Deserialize <TakionMessage>(new MemoryStream(resolutionInfoControlMessage.UnParsedPayload));

            if (resolutionPayload.streamInfoPayload == null)
            {
                return(null);
            }

            remotePlayContext.StreamInfoPayload = resolutionPayload.streamInfoPayload;


            byte[]         ackResolutionInfoPayload        = HexUtil.Unhexlify("00000000");
            ushort         ackResolutionInfoSize           = (ushort)(12 + ackResolutionInfoPayload.Length);
            ControlMessage ackResolutionInfoControlMessage = new ControlMessage((byte)0, remotePlayContext.ReceiverId, 0, 0, 3, 0, ackResolutionInfoSize, resolutionInfoControlMessage.FuncIncr, 0x19000);

            ackResolutionInfoControlMessage.UnParsedPayload = ackResolutionInfoPayload;

            byte[] ackResolutionInfoControlMessageData = GetByteArrayForControlMessage(ackResolutionInfoControlMessage);
            remotePlayContext.LastSentMessage = ackResolutionInfoControlMessageData;

            SendCryptedControlMessage(ackResolutionInfoControlMessage, remotePlayContext, udpClient); // ToDo handle when there is no further response and and resolution info is sent again
            //SendData(udpClient, ackResolutionInfoControlMessageData);

            /******** Stream info ack send ********/

            remotePlayContext.LastSentMessage = new byte[0];
            byte[] streamInfoAckPayload     = HexUtil.Unhexlify("00080e");
            ushort streamInfoAckPayloadSize = (ushort)(12 + streamInfoAckPayload.Length);

            ControlMessage streamInfoAckControlMessage = new ControlMessage((byte)0, remotePlayContext.ReceiverId, 0, 0, 0, 1, streamInfoAckPayloadSize, remotePlayContext.FuncIncr, 0x90000);

            streamInfoAckControlMessage.UnParsedPayload = streamInfoAckPayload;

            ControlResult controlResult = SendCryptedControlMessageAndWaitForAnswer(streamInfoAckControlMessage, remotePlayContext, udpClient, 1, "Sending stream info ack message" + Environment.NewLine);

            if (!controlResult.WasSuccessful)
            {
                const int retry = 0;
                for (int i = 0; i < retry; i++)
                {
                    SendData(udpClient, remotePlayContext.LastSentMessage);
                    controlResult = SendCryptedControlMessageAndWaitForAnswer(streamInfoAckControlMessage, remotePlayContext, udpClient, 1, "Resend stream info ack message" + Environment.NewLine);
                    if (controlResult.WasSuccessful)
                    {
                        break;
                    }
                }
            }

            if (!controlResult.WasSuccessful)
            {
                return(null);
            }

            return(remotePlayContext);
        }
コード例 #9
0
        private RemotePlayContext SendBigBangMessages(Socket udpClient, Session session, RemotePlayContext remotePlayContext)
        {
            /******** Big Payload send ********/

            // Generate random handshake key, for ECDH pubkey signature calculation
            byte[] handshakeKey = new byte[16];
            new Random().NextBytes(handshakeKey);

            // Generate ECDH keypair
            var ecdhKeyPair = CryptoService.GenerateEcdhKeyPair();
            // Get public key bytes
            var ownPublicKey = Session.GetPublicKeyBytesFromKeyPair(ecdhKeyPair);

            // Calculate ECDH pubkey signature
            var ecdhSignature = Session.CalculateHMAC(handshakeKey, ownPublicKey);

            int    unixTimestamp = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
            string timestampUnix = unixTimestamp.ToString();
            string sessionKey    = timestampUnix + CryptoService.GetUniqueKey(64);

            LaunchSpecification launchSpecs = LaunchSpecification.GetStandardSpecs("sessionId123", handshakeKey);

            byte[] launchSpecBuffer = Encoding.UTF8.GetBytes(launchSpecs.Serialize());

            byte[] cryptoBuffer = new byte[launchSpecBuffer.Length];
            cryptoBuffer = session.Encrypt(cryptoBuffer, 0);
            byte[] newLaunchSpec = new byte[launchSpecBuffer.Length];
            for (int i = 0; i < launchSpecBuffer.Length; i++)
            {
                newLaunchSpec[i] = (byte)(launchSpecBuffer[i] ^ cryptoBuffer[i]);
            }

            TakionMessage takionBigPayloadMessage = new TakionMessage
            {
                Type       = TakionMessage.PayloadType.Big,
                bigPayload = new BigPayload
                {
                    clientVersion = 9,
                    sessionKey    = sessionKey,
                    launchSpec    = Convert.ToBase64String(newLaunchSpec),
                    encryptedKey  = new byte[] { 0, 0, 0, 0 },
                    ecdhPubKey    = ownPublicKey,
                    ecdhSig       = ecdhSignature
                }
            };

            MemoryStream bigPayloadStream = new MemoryStream();

            Serializer.Serialize(bigPayloadStream, takionBigPayloadMessage);
            byte[] bigPayloadBuffer = ByteUtil.ConcatenateArrays(new byte[1], bigPayloadStream.ToArray()); // Padding byte + BigPayload
            ushort bigPayloadSize   = (ushort)(12 + bigPayloadBuffer.Length);

            ControlMessage controlMessageBigPayload = new ControlMessage(0, remotePlayContext.ReceiverId, 0, 0, 0, 1, bigPayloadSize, remotePlayContext.FuncIncr, 0x10000);

            controlMessageBigPayload.UnParsedPayload = bigPayloadBuffer;
            byte[] initialControlMessage2Data = GetByteArrayForControlMessage(controlMessageBigPayload);

            OnPs4LogInfo?.Invoke(this, Environment.NewLine + "Sending big payload:");
            OnPs4LogInfo?.Invoke(this, "ECDH pubkey: " + HexUtil.Hexlify(takionBigPayloadMessage.bigPayload.ecdhPubKey));
            OnPs4LogInfo?.Invoke(this, "ECDH sig: " + HexUtil.Hexlify(takionBigPayloadMessage.bigPayload.ecdhSig));
            OnPs4LogInfo?.Invoke(this, "Session key: " + takionBigPayloadMessage.bigPayload.sessionKey + Environment.NewLine);

            ControlResult bigPayloadResult = SendControlDataAndWaitForAnswer(udpClient, initialControlMessage2Data, 2, "Send BigPayload");

            if (!bigPayloadResult.WasSuccessful)
            {
                return(null);
            }

            /******** Bang Payload receive ********/

            ControlMessage answerPacket1 = bigPayloadResult.ControlMessages[0];
            ControlMessage answerPacket2 = bigPayloadResult.ControlMessages[1];

            if (answerPacket1.ProtoBuffFlag != 1 && answerPacket2.ProtoBuffFlag != 1)
            {
                return(null);
            }

            TakionMessage bangPayload = answerPacket1.ProtoBuffFlag == 1 ?
                                        Serializer.Deserialize <TakionMessage>(new MemoryStream(answerPacket1.UnParsedPayload)) :
                                        Serializer.Deserialize <TakionMessage>(new MemoryStream(answerPacket2.UnParsedPayload));

            if (bangPayload.bangPayload == null)
            {
                return(null);
            }

            ControlMessage bangPayloadControl = answerPacket1.ProtoBuffFlag == 1 ? answerPacket1 : answerPacket2;

            OnPs4LogInfo?.Invoke(this, Environment.NewLine + "Received bang payload:");
            OnPs4LogInfo?.Invoke(this, "ECDH pubkey: " + HexUtil.Hexlify(bangPayload.bangPayload.ecdhPubKey));
            OnPs4LogInfo?.Invoke(this, "ECDH sig: " + HexUtil.Hexlify(bangPayload.bangPayload.ecdhSig));
            OnPs4LogInfo?.Invoke(this, "Session key: " + bangPayload.bangPayload.sessionKey);

            /* Derive ECDH shared secret */
            var foreignPubkeyParams = Session.ConvertPubkeyBytesToCipherParams(bangPayload.bangPayload.ecdhPubKey);

            remotePlayContext.SharedSecret   = Session.GenerateSharedSecret(ecdhKeyPair.Private, foreignPubkeyParams);
            remotePlayContext.LocalGmacInfo  = CryptoService.SetUpGmac(2, handshakeKey, remotePlayContext.SharedSecret);
            remotePlayContext.RemoteGmacInfo = CryptoService.SetUpGmac(3, handshakeKey, remotePlayContext.SharedSecret);
            OnPs4LogInfo?.Invoke(this, "HANDSHAKE KEY: " + HexUtil.Hexlify(handshakeKey));
            OnPs4LogInfo?.Invoke(this, "SHARED SECRET: " + HexUtil.Hexlify(remotePlayContext.SharedSecret));

            byte[]         ackBangPayload        = HexUtil.Unhexlify("00000000");
            ushort         ackBangPayloadSize    = (ushort)(12 + ackBangPayload.Length);
            ControlMessage ackBangPayloadMessage = new ControlMessage(0, bangPayloadControl.FuncIncr, 0, 0, 3, 0, ackBangPayloadSize, bangPayloadControl.FuncIncr, 0x19000);

            ackBangPayloadMessage.UnParsedPayload = ackBangPayload;
            byte[] ackBangPayloadMessageData = GetByteArrayForControlMessage(ackBangPayloadMessage);
            remotePlayContext.LastSentMessage = ackBangPayloadMessageData;

            SendData(udpClient, ackBangPayloadMessageData);

            return(remotePlayContext);
        }
コード例 #10
0
 public RemotePlayAsyncState(RemotePlayContext remotePlayContext, Session session)
 {
     this.RemotePlayContext = remotePlayContext;
     this.Session           = session;
 }