예제 #1
0
        /***********************/
        /*** private methods ***/
        /***********************/


        private Session CheckForConnectionAesKey(IpPacket[] tcpRemotePlayPackets)
        {
            int ipProtocolHeaderSize = 20;

            for (int i = 0; i < tcpRemotePlayPackets.Length; i++)
            {
                var tcpRemotePlayPacket = tcpRemotePlayPackets[i];
                if (tcpRemotePlayPacket.PacketData.Array != null && tcpRemotePlayPacket.PacketData.Array.Length > 0)
                {
                    int    length  = tcpRemotePlayPacket.PacketData.Count - ipProtocolHeaderSize;
                    byte[] payload = new byte[length];
                    Buffer.BlockCopy(tcpRemotePlayPacket.PacketData.Array, tcpRemotePlayPacket.PacketData.Offset + ipProtocolHeaderSize, payload, 0, payload.Length);

                    string encodedPayload = Encoding.ASCII.GetString(payload);
                    if (encodedPayload.StartsWith("GET /sce/rp/session HTTP/1.1\r\n"))
                    {
                        Dictionary <string, string> httpHeaders = ByteUtil.ByteArrayToHttpHeader(payload);
                        httpHeaders.TryGetValue("RP-Registkey", out var rpRegistKey);
                        AppendLogOutputToPcapLogTextBox("RP-Registkey: " + rpRegistKey);
                        if (i < tcpRemotePlayPackets.Length - 1)
                        {
                            var sessionResponse = tcpRemotePlayPackets[i + 1];
                            if (sessionResponse.PacketData.Array == null ||
                                sessionResponse.PacketData.Array.Length < 1)
                            {
                                return(null);
                            }

                            length  = sessionResponse.PacketData.Count - ipProtocolHeaderSize;
                            payload = new byte[length];
                            Buffer.BlockCopy(sessionResponse.PacketData.Array, sessionResponse.PacketData.Offset + ipProtocolHeaderSize, payload, 0, payload.Length);

                            encodedPayload = Encoding.ASCII.GetString(payload);
                            if (encodedPayload.StartsWith("HTTP/1.1 200 OK\r\n"))
                            {
                                httpHeaders = ByteUtil.ByteArrayToHttpHeader(payload);
                                httpHeaders.TryGetValue("RP-Nonce", out var rpNonce);
                                if (rpNonce == null)
                                {
                                    return(null);
                                }

                                byte[] rpKeyBuffer    = HexUtil.Unhexlify(_settingManager.GetRemotePlayData().RemotePlay.RpKey);
                                byte[] rpNonceDecoded = Convert.FromBase64String(rpNonce);

                                AppendLogOutputToPcapLogTextBox("RP-Nonce from \"/sce/rp/session\" response: " + HexUtil.Hexlify(rpNonceDecoded));

                                string controlAesKey = HexUtil.Hexlify(CryptoService.GetSessionAesKeyForControl(rpKeyBuffer, rpNonceDecoded));
                                string controlNonce  = HexUtil.Hexlify(CryptoService.GetSessionNonceValueForControl(rpNonceDecoded));
                                AppendLogOutputToPcapLogTextBox("!!! Control AES Key: " + controlAesKey);
                                AppendLogOutputToPcapLogTextBox("!!! Control AES Nonce: " + controlNonce + Environment.NewLine);
                                return(CryptoService.GetSessionForControl(rpKeyBuffer, rpNonceDecoded));
                            }
                        }
                    }
                }
            }

            return(null);
        }
예제 #2
0
        private void textBoxRpKey_TextChanged(object sender, EventArgs e)
        {
            string pin = textBoxRpKey.Text;

            if (pin.Length == 8 && int.TryParse(pin, out var parsedPin))
            {
                labelRegistryAesKey.Text           = HexUtil.Hexlify(CryptoService.GetRegistryAesKeyForPin(parsedPin));
                labelRegistryAesKeyHeading.Visible = true;
            }
            else
            {
                labelRegistryAesKey.Text           = "";
                labelRegistryAesKeyHeading.Visible = false;
            }
        }
예제 #3
0
        // Callback function invoked by Pcap.Net for ps4 tcp messages
        private void PacketHandlerTcp(Packet packet)
        {
            IpV4Datagram ip       = packet.Ethernet.IpV4;
            IpV4Protocol protocol = ip.Protocol;

            if (protocol == IpV4Protocol.Tcp)
            {
                TcpDatagram  tcpDatagram  = ip.Tcp;
                HttpDatagram httpDatagram = tcpDatagram.Http;
                if (httpDatagram.Length > 0 && httpDatagram.Header != null)
                {
                    string httpPacket = httpDatagram.Decode(Encoding.UTF8);
                    if (httpPacket.StartsWith("GET /sce/rp/session HTTP/1.1\r\n"))
                    {
                        Dictionary <string, string> header = HttpUtils.SplitHttpResponse(httpPacket);
                        header.TryGetValue("RP-Registkey", out var registKey);
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("RP-Registkey: " + registKey)));

                        _livePcapContext.LivePcapState = LivePcapState.SESSION_REQUEST;
                        _livePcapContext.Session       = null;
                    }
                    else if (httpDatagram.IsResponse && httpPacket.StartsWith("HTTP/1.1 200 OK\r\n") && _livePcapContext.LivePcapState == LivePcapState.SESSION_REQUEST)
                    {
                        Dictionary <string, string> header = HttpUtils.SplitHttpResponse(httpPacket);
                        header.TryGetValue("RP-Nonce", out var rpNonce);
                        if (rpNonce == null)
                        {
                            return;
                        }

                        byte[] rpKeyBuffer    = HexUtil.Unhexlify(_settingManager.GetRemotePlayData().RemotePlay.RpKey);
                        byte[] rpNonceDecoded = Convert.FromBase64String(rpNonce);

                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("RP-Nonce from \"/sce/rp/session\" response: " + HexUtil.Hexlify(rpNonceDecoded))));

                        string controlAesKey = HexUtil.Hexlify(CryptoService.GetSessionAesKeyForControl(rpKeyBuffer, rpNonceDecoded));
                        string controlNonce  = HexUtil.Hexlify(CryptoService.GetSessionNonceValueForControl(rpNonceDecoded));
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Control AES Key: " + controlAesKey)));
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Control AES Nonce: " + controlNonce + Environment.NewLine)));
                        _livePcapContext.LivePcapState = LivePcapState.SESSION_RESPONSE;
                        _livePcapContext.Session       = CryptoService.GetSessionForControl(rpKeyBuffer, rpNonceDecoded);
                    }
                }
            }
        }
        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);
        }
        /*********** Control request ***********/

        private void HandleControlRequest(string rpNonce, IPEndPoint ps4Endpoint, PS4RemotePlayData ps4RemotePlayData)
        {
            bool   connectedSuccess = false;
            Socket socket           = null;

            try
            {
                IPEndPoint ipEndPoint = new IPEndPoint(ps4Endpoint.Address, ControlPort);
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.Connect(ipEndPoint);

                byte[] rpKeyBuffer    = HexUtil.Unhexlify(ps4RemotePlayData.RemotePlay.RpKey);
                byte[] rpNonceDecoded = Convert.FromBase64String(rpNonce);
                OnPs4LogInfo?.Invoke(this,
                                     "RP-Nonce from \"/sce/rp/session\" response: " + HexUtil.Hexlify(rpNonceDecoded));

                Session session = CryptoService.GetSessionForControl(rpKeyBuffer, rpNonceDecoded);

                string controlAesKey = HexUtil.Hexlify(CryptoService.GetSessionAesKeyForControl(rpKeyBuffer, rpNonceDecoded));
                string controlNonce  = HexUtil.Hexlify(CryptoService.GetSessionNonceValueForControl(rpNonceDecoded));
                OnPs4LogInfo?.Invoke(this, "!!! Control AES Key: " + controlAesKey);
                OnPs4LogInfo?.Invoke(this, "!!! Control AES Nonce: " + controlNonce + Environment.NewLine);

                byte[] registrationKeyBuffer    = HexUtil.Unhexlify(ps4RemotePlayData.RemotePlay.RegistrationKey);
                byte[] registrationKeyPadding   = { 0, 0, 0, 0, 0, 0, 0, 0 };
                byte[] encryptedRegistrationKey =
                    session.Encrypt(ByteUtil.ConcatenateArrays(registrationKeyBuffer, registrationKeyPadding));
                string encodedRegistrationKey = Convert.ToBase64String(encryptedRegistrationKey);

                byte[] randomDid    = Guid.NewGuid().ToByteArray();
                byte[] didPrefix    = { 0x00, 0x18, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80 };
                byte[] didPadding   = { 48, 48, 48, 48, 48, 48, 48 };
                byte[] encryptedDid = session.Encrypt(ByteUtil.ConcatenateArrays(didPrefix, randomDid, didPadding));
                string encodedDid   = Convert.ToBase64String(encryptedDid);

                string osType          = "Win10.0.0";
                byte[] osTypeBuffer    = Encoding.UTF8.GetBytes(osType);
                byte[] osTypePadding   = { 0 };
                byte[] encryptedOsType = session.Encrypt(ByteUtil.ConcatenateArrays(osTypeBuffer, osTypePadding));
                string encodedOsType   = Convert.ToBase64String(encryptedOsType);

                string host = ps4Endpoint.Address + ":" + ControlPort;

                string requestData = "GET /sce/rp/session/ctrl HTTP/1.1\r\n" +
                                     $"HOST: {host}\r\n" +
                                     "User-Agent: remoteplay Windows\r\n" +
                                     "Connection: keep-alive\r\n" +
                                     "Content-Length: 0\r\n" +
                                     $"RP-Auth: {encodedRegistrationKey}\r\n" +
                                     "RP-Version: 8.0\r\n" +
                                     $"RP-Did: {encodedDid}\r\n" +
                                     "RP-ControllerType: 3\r\n" +
                                     "RP-ClientType: 11\r\n" +
                                     $"RP-OSType: {encodedOsType}\r\n" +
                                     "RP-ConPath: 1\r\n" +
                                     "\r\n";

                socket.Send(Encoding.UTF8.GetBytes(requestData));
                byte[] receiveBuffer = new byte[8192];
                int    readBytes     = socket.Receive(receiveBuffer);
                byte[] response      = new byte[readBytes];
                Buffer.BlockCopy(receiveBuffer, 0, response, 0, response.Length);
                string httpResponse = Encoding.ASCII.GetString(receiveBuffer, 0, readBytes);

                HttpStatusCode statusCode = HttpUtils.GetStatusCode(httpResponse);
                if (statusCode == HttpStatusCode.OK)
                {
                    OnPs4LogInfo?.Invoke(this, "\"/sce/rp/session/ctrl\" response: " + Environment.NewLine + httpResponse.Trim() + Environment.NewLine);
                    OnPs4LogInfo?.Invoke(this, "TCP connection to PS4 established" + Environment.NewLine);
                    _clientSocket = socket;
                    _clientSocket.ReceiveTimeout = 0;
                    PingPongAsyncResult connectionStateObject = new PingPongAsyncResult {
                        RemoteSocket = _clientSocket
                    };
                    connectionStateObject.RemoteSocket.BeginReceive(connectionStateObject.Buffer, 0,
                                                                    connectionStateObject.Buffer.Length, SocketFlags.None, PingPongHandler,
                                                                    connectionStateObject);
                    OnPs4ConnectionSuccess?.Invoke(this, EventArgs.Empty);
                    connectedSuccess = true;
                    InitializeRemotePlayChannel(session, ps4Endpoint);
                }
            }
            catch (Exception e)
            {
                OnPs4ConnectionError?.Invoke(this, "Exception occured /sce/rp/session/ctrl" + e);
            }
            finally
            {
                if (!connectedSuccess)
                {
                    socket?.Close();
                }
            }
        }
예제 #6
0
        private void HandleControlMessage(byte[] payload, Session session)
        {
            if (payload[0] == 0) // Control Packet
            {
                byte[]         message        = payload;
                ControlMessage controlMessage = new ControlMessage();
                using (MemoryStream memoryStream = new MemoryStream(message, 0, message.Length))
                    using (BinaryReader binaryWriter = new BinaryReader(memoryStream))
                    {
                        controlMessage.Deserialize(binaryWriter);
                    }

                if (controlMessage.ProtoBuffFlag == 1 && controlMessage.PLoadSize > 100)
                {
                    TakionMessage takionMessage = ProtobufUtil.Deserialize <TakionMessage>(controlMessage.UnParsedPayload);

                    if (takionMessage.bigPayload != null)
                    {
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload session key: " + takionMessage.bigPayload.sessionKey)));
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload ecdh pub key in hex: " + HexUtil.Hexlify(takionMessage.bigPayload.ecdhPubKey))));
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload ecdh sig in hex: " + HexUtil.Hexlify(takionMessage.bigPayload.ecdhSig))));
                        if (session != null)
                        {
                            byte[] launchSpecBuffer = Convert.FromBase64String(takionMessage.bigPayload.launchSpec);
                            byte[] cryptoBuffer     = new byte[launchSpecBuffer.Length];
                            cryptoBuffer = session.Encrypt(cryptoBuffer, 0);
                            byte[] newLaunchSpec = new byte[launchSpecBuffer.Length];
                            for (int j = 0; j < launchSpecBuffer.Length; j++)
                            {
                                newLaunchSpec[j] = (byte)(launchSpecBuffer[j] ^ cryptoBuffer[j]);
                            }

                            string launchSpecs = Encoding.UTF8.GetString(newLaunchSpec);
                            LaunchSpecification launchSpecJsonObject = LaunchSpecification.Deserialize(launchSpecs);
                            byte[] handshakeKey = launchSpecJsonObject.HandshakeKey;

                            if (handshakeKey != null)
                            {
                                this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload handshake key in launchSpec in hex: " + HexUtil.Hexlify(handshakeKey))));
                                var ecdhSignatureVerification = Session.CalculateHMAC(handshakeKey, takionMessage.bigPayload.ecdhPubKey);

                                if (ecdhSignatureVerification.SequenceEqual(takionMessage.bigPayload.ecdhSig))
                                {
                                    this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("ECDH Signature matches!")));
                                }
                                else
                                {
                                    this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!!! ECDH Signature mismatch")));
                                }
                            }
                            this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload full launchSpec: " + Environment.NewLine + launchSpecJsonObject.Serialize() + Environment.NewLine)));
                        }
                        else
                        {
                            this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox(Environment.NewLine)));
                        }
                    }
                    else if (takionMessage.bangPayload != null)
                    {
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Bang payload session key: " + takionMessage.bangPayload.sessionKey)));
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Bang payload ecdh pub key in hex: " + HexUtil.Hexlify(takionMessage.bangPayload.ecdhPubKey))));
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Bang payload ecdh sig in hex: " + HexUtil.Hexlify(takionMessage.bangPayload.ecdhSig) + Environment.NewLine)));
                    }
                }
                else
                {
                    if (controlMessage.Crypto != 0)
                    {
                        this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Control message with crypto value: " + HexUtil.Hexlify(ByteUtil.UIntToByteArray(controlMessage.Crypto)))));
                    }
                }
            }
        }