1fb664710a
Adds the app-centric FLE bridge direction (direction.md) replacing the Blaze-backend route as primary plan, plus a corrected foundational test procedure and snapshot/diff/apply tooling for reverse-engineering FLE's Freeze Lineup write mechanism. Also picks up prior untracked research docs (status-review, fut-integration-options, fifa23-startup-flow, track-c) and existing capture/export tooling that hadn't been committed yet.
136 lines
4.1 KiB
Bash
Executable File
136 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# network-metadata-logger/netlog.sh
|
|
# Logs FIFA 23 network connection metadata: host, port, protocol, timing.
|
|
# Does NOT capture payload, does NOT decrypt TLS.
|
|
#
|
|
# Usage: netlog.sh [--interval 2] [--duration 120]
|
|
# Polls /proc/net/tcp* and resolves FIFA23 PIDs every N seconds.
|
|
# Output: CSV to stdout and netlog-TIMESTAMP.csv in this directory.
|
|
|
|
set -euo pipefail
|
|
|
|
INTERVAL="${INTERVAL:-2}"
|
|
DURATION="${DURATION:-300}"
|
|
OUT_DIR="$(cd "$(dirname "$0")" && pwd)/logs"
|
|
mkdir -p "$OUT_DIR"
|
|
OUT_FILE="$OUT_DIR/netlog-$(date +%Y%m%d-%H%M%S).csv"
|
|
|
|
log() { echo "$*" | tee -a "$OUT_FILE"; }
|
|
|
|
find_fifa_pids() {
|
|
pgrep -x FIFA23.exe 2>/dev/null || \
|
|
pgrep -f FIFA23.exe 2>/dev/null || \
|
|
true
|
|
}
|
|
|
|
# Decode a hex IP:port pair from /proc/net/tcp
|
|
# e.g. "0F02000A:01BB" → "10.0.2.15:443"
|
|
decode_addr() {
|
|
local hex_ip="${1%%:*}"
|
|
local hex_port="${1##*:}"
|
|
local port=$((16#$hex_port))
|
|
# IPv4 in /proc/net/tcp is little-endian
|
|
local b1=$((16#${hex_ip:6:2}))
|
|
local b2=$((16#${hex_ip:4:2}))
|
|
local b3=$((16#${hex_ip:2:2}))
|
|
local b4=$((16#${hex_ip:0:2}))
|
|
echo "${b1}.${b2}.${b3}.${b4}:${port}"
|
|
}
|
|
|
|
# TCP state codes
|
|
state_name() {
|
|
case "$1" in
|
|
01) echo ESTABLISHED ;; 02) echo SYN_SENT ;;
|
|
03) echo SYN_RECV ;; 04) echo FIN_WAIT1 ;;
|
|
05) echo FIN_WAIT2 ;; 06) echo TIME_WAIT ;;
|
|
07) echo CLOSE ;; 08) echo CLOSE_WAIT ;;
|
|
09) echo LAST_ACK ;; 0A) echo LISTEN ;;
|
|
0B) echo CLOSING ;; *) echo "STATE_$1" ;;
|
|
esac
|
|
}
|
|
|
|
# Resolve remote IP → hostname (best-effort, non-blocking)
|
|
resolve_host() {
|
|
local ip="${1%%:*}"
|
|
# Skip local/loopback
|
|
case "$ip" in
|
|
127.*|10.*|192.168.*|172.1[6-9].*|172.2[0-9].*|172.3[0-1].*) echo "$ip"; return ;;
|
|
esac
|
|
getent hosts "$ip" 2>/dev/null | awk '{print $2}' | head -1 || echo "$ip"
|
|
}
|
|
|
|
# Categorise destination by port/host heuristic
|
|
categorise() {
|
|
local host="$1" port="$2"
|
|
case "$port" in
|
|
443|8443) echo "HTTPS/TLS" ;;
|
|
80) echo "HTTP" ;;
|
|
10853) echo "EA-LSX-auth" ;;
|
|
3216|3478|3479|3480) echo "EA-UDP-STUN-or-relay" ;;
|
|
*)
|
|
case "$host" in
|
|
*ea.com*|*easports.com*|*footapi.com*|*ugc.*) echo "EA-service" ;;
|
|
*akamai*|*amazonaws*|*cloudfront*) echo "CDN" ;;
|
|
*) echo "other" ;;
|
|
esac
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Build inode→pid map from /proc
|
|
build_inode_pid_map() {
|
|
declare -gA INODE_PID
|
|
INODE_PID=()
|
|
for pid_dir in /proc/[0-9]*/fd; do
|
|
local pid="${pid_dir%%/fd}"
|
|
pid="${pid##/proc/}"
|
|
for link in "$pid_dir"/[0-9]* 2>/dev/null; do
|
|
local target
|
|
target=$(readlink "$link" 2>/dev/null) || continue
|
|
if [[ "$target" == "socket:["* ]]; then
|
|
local inode="${target//[^0-9]/}"
|
|
INODE_PID["$inode"]="$pid"
|
|
fi
|
|
done
|
|
done 2>/dev/null || true
|
|
}
|
|
|
|
log "timestamp,pid,proc,local_addr,remote_host,remote_addr,port,state,category"
|
|
|
|
declare -A SEEN_CONNS
|
|
END_TIME=$(( $(date +%s) + DURATION ))
|
|
|
|
echo "Logging FIFA 23 network metadata to $OUT_FILE (${DURATION}s, Ctrl-C to stop)..."
|
|
|
|
while [ "$(date +%s)" -lt "$END_TIME" ]; do
|
|
TS=$(date +%H:%M:%S)
|
|
build_inode_pid_map
|
|
|
|
# Read /proc/net/tcp (IPv4 ESTABLISHED and SYN_SENT)
|
|
while read -r _ local_hex remote_hex state _ _ _ _ _ inode _rest; do
|
|
[ "$state" = "01" ] || [ "$state" = "02" ] || continue
|
|
local_addr=$(decode_addr "$local_hex")
|
|
remote_addr=$(decode_addr "$remote_hex")
|
|
remote_ip="${remote_addr%%:*}"
|
|
remote_port="${remote_addr##*:}"
|
|
pid="${INODE_PID[$inode]:-}"
|
|
[ -z "$pid" ] && continue
|
|
|
|
# Filter to FIFA-related PIDs (or log all if no FIFA found)
|
|
proc=$(cat "/proc/$pid/comm" 2>/dev/null) || continue
|
|
|
|
state_str=$(state_name "$state")
|
|
key="${pid}:${local_addr}:${remote_addr}"
|
|
if [ -z "${SEEN_CONNS[$key]:-}" ] || [ "$state_str" != "${SEEN_CONNS[$key]}" ]; then
|
|
SEEN_CONNS["$key"]="$state_str"
|
|
host=$(resolve_host "$remote_ip")
|
|
cat=$(categorise "$host" "$remote_port")
|
|
log "$TS,$pid,$proc,$local_addr,$host,$remote_addr,$remote_port,$state_str,$cat"
|
|
fi
|
|
done < /proc/net/tcp 2>/dev/null || true
|
|
|
|
sleep "$INTERVAL"
|
|
done
|
|
|
|
echo "Done. Output: $OUT_FILE"
|