Skip to content

Instantly share code, notes, and snippets.

@ceramicwhite
Last active February 25, 2024 13:58
Show Gist options
  • Select an option

  • Save ceramicwhite/84997e386bb40d063aee680fa6440c21 to your computer and use it in GitHub Desktop.

Select an option

Save ceramicwhite/84997e386bb40d063aee680fa6440c21 to your computer and use it in GitHub Desktop.
Steam Family Share Patch
// For Rust 2021
use std::fs;
use std::iter::zip;
static STEAMCLIENT_FILE_PATH: &str = "./steamclient.so";
static FUNCTION_OFFSETS_STRING: &str = "SharedLibraryLockStatus:
00244530: 55 89 e5 57 56 8d .. .. .. .. .. 53 e8 .. .. ..
00244540: .. 81 c3 .. .. .. .. 81 ec c4 00 00 00 89 bd 48
SharedLibraryStopPlaying:
002485d0: 55 89 e5 57 56 e8 .. .. .. .. 81 c6 .. .. .. ..
002485e0: 53 81 ec e4 00 00 00 8b 45 08 8b 7d 0c 89 85 50
IsSubscribedApp:
0096b9d0: 57 b8 01 00 00 00 53 e8 .. .. .. .. 81 c3 .. ..
0096b9e0: .. .. 83 ec 44 8b 54 24 54 85 d2 78 30 8d 7c 24";
fn hex_char_to_byte(character: char) -> u8 {
let byte = character as u8;
match byte {
b'0'..=b'9' => byte - b'0',
b'a'..=b'f' => byte - b'a' + 10,
b'A'..=b'F' => byte - b'A' + 10,
_ => unreachable!(),
}
}
fn hex_string_to_byte(hex_string: &str) -> Option<u8> {
assert!(hex_string.len() == 2);
if hex_string == ".." {
return None;
}
let upper_nibble = hex_char_to_byte(hex_string.chars().next().unwrap());
let lower_nibble = hex_char_to_byte(hex_string.chars().nth(1).unwrap());
Some(upper_nibble << 4 | lower_nibble)
}
fn parse_pattern_string() -> Vec<Vec<Option<u8>>> {
let mut byte_patterns: Vec<Vec<Option<u8>>> = Vec::new();
for line in FUNCTION_OFFSETS_STRING.split('\n') {
if line.ends_with(':') {
byte_patterns.push(Vec::new());
} else if line == " " {
continue;
} else {
let line = &line[10..];
for hex_byte in line.split_whitespace() {
let last_index = byte_patterns.len() - 1;
byte_patterns[last_index].push(hex_string_to_byte(hex_byte));
}
}
}
byte_patterns
}
fn main() {
let patterns = parse_pattern_string();
let file = fs::read(STEAMCLIENT_FILE_PATH)
.unwrap_or_else(|_| panic!("A '{}' file should exist", STEAMCLIENT_FILE_PATH));
let mut locations_to_patch: Vec<usize> = Vec::new();
let mut iter = file.windows(32);
let mut current_index: usize = 0;
while let Some(word) = iter.next() {
for pattern in &patterns {
let mut matches = true;
for (actual, pattern_byte) in zip(word, pattern) {
if pattern_byte.is_none() {
continue;
}
if pattern_byte.unwrap() != *actual {
matches = false;
}
}
if matches {
locations_to_patch.push(current_index);
}
}
current_index += 16;
for _ in 0..15 {
iter.next();
}
}
if locations_to_patch.is_empty() {
print!("echo \"No matches found\"");
return;
}
let mut command: String = "echo -e \"".to_string();
let mut iter = locations_to_patch.iter().peekable();
while let Some(location) = iter.next() {
if iter.peek().is_some() {
command += &format!("00{location:x}: 31c040c3\\n");
} else {
command += &format!("00{location:x}: 31c040c3\"");
}
}
command += &format!(" | xxd -r - {STEAMCLIENT_FILE_PATH}");
print!("{}", command);
}
#!/bin/bash
# Check for Rust installation and install it if not present
if ! command -v cargo &> /dev/null
then
echo "Rust is not installed. Installing Rust..."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
else
echo "Rust is installed."
fi
# Check for 'cc' (C Compiler)
if ! command -v cc &> /dev/null
then
echo "'cc' (C Compiler) is not installed. Installing 'base-devel'."
sudo pacman -Sy base-devel
fi
# Check for 'xxd'
if ! command -v xxd &> /dev/null
then
echo "'xxd' is not installed. Installing 'vim'."
sudo pacman -Sy vim
fi
# Define the steamclient.so file path
steamclient_path="/home/$USER/.steam/bin32/steamclient.so"
# Backup the original steamclient.so file
if [ -f "$steamclient_path" ]; then
echo "Backing up steamclient.so to steamclient.so.bk"
cp "$steamclient_path" "${steamclient_path}.bk"
else
echo "Error: steamclient.so does not exist at the specified path."
exit 1
fi
steamclient_sum=$(sha256sum "$steamclient_path" | awk '{print $1}')
# Ensure the Rust project directory exists and navigate to it
rust_project_dir="/home/$USER/steam-patch"
if [ ! -d "$rust_project_dir" ]; then
mkdir "$rust_project_dir"
fi
cd "$rust_project_dir"
# Ensure main.rs exists in the project directory
if [ ! -f "main.rs" ]; then
curl https://gist.githubusercontent.com/ceramicwhite/84997e386bb40d063aee680fa6440c21/raw/a28de8c328fcc029735dc2dd4786f5ce71c3af71/main.rs > main.rs
fi
# Initialize a new Cargo project if Cargo.toml does not exist
if [ ! -f "Cargo.toml" ]; then
cargo init --name steam-patch .
fi
# Copy steamclient.so to the project directory
cp "$steamclient_path" .
# Run the Rust program and capture the output
patch_command=$(cargo run | tail -n 1)
# Check if cargo run was successful and patch_command is not empty
if [ $? -eq 0 ] && [ ! -z "$patch_command" ]; then
patch_command=${patch_command//.\//$rust_project_dir\/}
# Execute the patch command
echo "Command to patch steamclient.so:"
echo ""
echo $patch_command
echo ""
echo "Patching steamclient.so..."
eval $patch_command
if [[ "$steamclient_sum" == $(sha256sum "steamclient.so" | awk '{print $1}') ]]; then
echo ""
echo "steamclient.so has not been patched."
echo ""
exit 1
fi
# Move the patched steamclient.so back to its original location
mv "steamclient.so" "$steamclient_path"
echo ""
echo "Original sha256:$steamclient_sum"
echo ""
echo "New sha256:$(sha256sum "$steamclient_path" | awk '{print $1}')"
echo ""
echo "steamclient.so has been patched and moved back."
else
echo "Failed to run the Rust program or no patch command was generated."
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment