/***********************/ /*** 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); }
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; } }
// 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(); } } }
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))))); } } } }