Can send first message to steam

This commit is contained in:
Artemis Tosini 2024-08-26 04:18:16 +00:00
parent 8b1ad9078a
commit 07c6320c71
Signed by: artemist
GPG key ID: EE5227935FE3FF18
4 changed files with 110 additions and 2 deletions

8
Cargo.lock generated
View file

@ -471,6 +471,12 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "home" name = "home"
version = "0.5.9" version = "0.5.9"
@ -1612,9 +1618,11 @@ dependencies = [
"color-eyre", "color-eyre",
"env_logger", "env_logger",
"futures", "futures",
"hex",
"keyvalues-serde", "keyvalues-serde",
"log", "log",
"protobuf", "protobuf",
"rand",
"reqwest", "reqwest",
"serde", "serde",
"tokio", "tokio",

View file

@ -8,9 +8,11 @@ async-tungstenite = { version = "0.27.0", features = ["tokio-rustls-native-certs
color-eyre.workspace = true color-eyre.workspace = true
env_logger.workspace = true env_logger.workspace = true
futures = "0.3.30" futures = "0.3.30"
hex = "0.4.3"
keyvalues-serde.workspace = true keyvalues-serde.workspace = true
log.workspace = true log.workspace = true
protobuf.workspace = true protobuf.workspace = true
rand = "0.8.5"
reqwest = { version = "0.12", features = ["rustls-tls-native-roots"], default-features = false} reqwest = { version = "0.12", features = ["rustls-tls-native-roots"], default-features = false}
serde = { version = "1.0.209", features = ["derive"] } serde = { version = "1.0.209", features = ["derive"] }
tokio = { version = "1.39", features = ["rt", "rt-multi-thread", "macros"]} tokio = { version = "1.39", features = ["rt", "rt-multi-thread", "macros"]}

View file

@ -41,6 +41,7 @@ pub async fn bootstrap_find_servers() -> eyre::Result<Vec<String>> {
} }
/// A message sent over the socket. Can be either sent or recieved /// A message sent over the socket. Can be either sent or recieved
#[derive(Debug, Clone)]
pub struct CMProtoBufMessage<T: protobuf::Message> { pub struct CMProtoBufMessage<T: protobuf::Message> {
pub action: enums_clientserver::EMsg, pub action: enums_clientserver::EMsg,
pub header: steammessages_base::CMsgProtoBufHeader, pub header: steammessages_base::CMsgProtoBufHeader,
@ -54,7 +55,7 @@ impl<T: protobuf::Message> CMProtoBufMessage<T> {
let length = 4 + 4 + self.header.compute_size() + self.body.compute_size(); let length = 4 + 4 + self.header.compute_size() + self.body.compute_size();
let mut out = Vec::with_capacity(length.try_into()?); let mut out = Vec::with_capacity(length.try_into()?);
out.extend_from_slice(&self.action.value().to_le_bytes()); out.extend_from_slice(&(self.action.value() as u32 | 0x80000000).to_le_bytes());
out.extend_from_slice(&self.header.cached_size().to_le_bytes()); out.extend_from_slice(&self.header.cached_size().to_le_bytes());
self.header.write_to_vec(&mut out)?; self.header.write_to_vec(&mut out)?;
self.body.write_to_vec(&mut out)?; self.body.write_to_vec(&mut out)?;
@ -74,6 +75,7 @@ impl<T: protobuf::Message> CMProtoBufMessage<T> {
} }
/// A message sent over the socket, but the body is still serialized /// A message sent over the socket, but the body is still serialized
#[derive(Debug, Clone)]
pub struct CMRawProtoBufMessage { pub struct CMRawProtoBufMessage {
pub action: enums_clientserver::EMsg, pub action: enums_clientserver::EMsg,
pub header: steammessages_base::CMsgProtoBufHeader, pub header: steammessages_base::CMsgProtoBufHeader,
@ -232,6 +234,33 @@ impl CMSession {
CMProtoBufMessage::<Response>::deserialize(response_raw) CMProtoBufMessage::<Response>::deserialize(response_raw)
} }
/// Send a message without a jobid
pub async fn send_notification<T: protobuf::Message>(
&mut self,
action: enums_clientserver::EMsg,
body: T,
) -> eyre::Result<()> {
let header = steammessages_base::CMsgProtoBufHeader {
steamid: self.steam_id,
realm: Some(self.realm),
client_sessionid: Some(self.client_session_id),
..Default::default()
};
let message = CMProtoBufMessage {
action,
header,
body,
};
let serialized = message.serialize()?;
self.socket
.send(tungstenite::protocol::Message::Binary(serialized))
.await?;
Ok(())
}
/// Whether the current session is authenticated /// Whether the current session is authenticated
pub fn is_authed(&self) -> bool { pub fn is_authed(&self) -> bool {
self.steam_id.is_some() self.steam_id.is_some()

View file

@ -1,4 +1,14 @@
use color_eyre::eyre; use color_eyre::eyre;
use rand::RngCore;
use vapore_proto::{
enums_clientserver::EMsg,
steammessages_auth_steamclient::{
CAuthentication_BeginAuthSessionViaQR_Request,
CAuthentication_BeginAuthSessionViaQR_Response, CAuthentication_DeviceDetails,
EAuthTokenPlatformType,
},
steammessages_clientserver_login::CMsgClientHello,
};
mod connection; mod connection;
@ -12,7 +22,66 @@ pub async fn main() -> eyre::Result<()> {
let servers = connection::bootstrap_find_servers().await?; let servers = connection::bootstrap_find_servers().await?;
log::debug!("Found servers: {:?}", servers); log::debug!("Found servers: {:?}", servers);
let session = connection::CMSession::connect(&servers[0]).await?; let mut session = connection::CMSession::connect(&servers[0]).await?;
session
.send_notification(
EMsg::k_EMsgClientHello,
CMsgClientHello {
protocol_version: Some(0x1002c),
..Default::default()
},
)
.await?;
log::debug!("Sent hello");
// machine_id is supposed to be a binary key/value with the SHA1 of the machine's
// BB3: Machine GUID
// FF2: MAC address
// 3B3: Disk ID
// We should probably make these consistent so Valve doesn't get suspicious,
// but for now let's make them random
// TODO: Find a more generic way to make this
let mut machine_id = Vec::with_capacity(161);
machine_id.extend_from_slice(b"\x00MessageObject\x00");
for key in [b"BB3", b"FF2", b"3B3"] {
let mut data = [0u8; 20];
rand::thread_rng().fill_bytes(&mut data);
let hex_bytes = hex::encode(data).into_bytes();
// Type is string
machine_id.push(b'\x01');
machine_id.extend_from_slice(key);
machine_id.push(b'\x00');
machine_id.extend_from_slice(&hex_bytes);
machine_id.push(b'\x00');
}
// suitable end bytes
machine_id.extend_from_slice(b"\x08\x08");
let response = session
.call_service_method::<_, CAuthentication_BeginAuthSessionViaQR_Response>(
"Authentication.BeginAuthSessionViaQR#1".to_string(),
CAuthentication_BeginAuthSessionViaQR_Request {
device_details: protobuf::MessageField::some(CAuthentication_DeviceDetails {
device_friendly_name: Some("vapore".to_string()),
platform_type: Some(protobuf::EnumOrUnknown::new(
EAuthTokenPlatformType::k_EAuthTokenPlatformType_SteamClient,
)),
// Windows 10
os_type: Some(16),
// Unknown
gaming_device_type: Some(1),
machine_id: Some(machine_id),
..Default::default()
}),
..Default::default()
},
)
.await?;
log::debug!("Got response {:#x?}", response);
Ok(()) Ok(())
} }