- DoWithTLS 签名变更:(bool/profile) → (TLSMode, profile) - 所有调用方传入 account.GetTLSMode() 以支持 node/utls/off 三模式 - gateway_service.go、gemini_messages_compat、forward_as_* 全部更新 - claude_usage_service 的 ClaudeUsageFetchOptions 新增 TLSMode 字段 - 新增 decompressResponseBody(gzip/brotli/deflate)到 http_upstream.go - 新增 antigravity_privacy_service.go(setAntigravityPrivacy) - admin_service 新增 ForceOpenAIPrivacy/EnsureAntigravityPrivacy/ForceAntigravityPrivacy - antigravity.Client 新增 SetUserSettings/FetchUserInfo API
219 lines
7.5 KiB
Bash
Executable File
219 lines
7.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ─────────────────────────────────────────────────────────────
|
|
# capture_tls.sh - Capture TLS ClientHello fingerprints (JA3)
|
|
#
|
|
# mitmproxy terminates TLS so it can't see the real JA3 that
|
|
# Claude CLI / Antigravity sends to Anthropic. This script
|
|
# captures the REAL TLS fingerprint using tshark.
|
|
#
|
|
# Usage:
|
|
# # Run BEFORE starting claude login / claude "hello"
|
|
# # (don't use HTTPS_PROXY for this - direct connection)
|
|
#
|
|
# sudo ./capture_tls.sh # capture on default interface
|
|
# sudo ./capture_tls.sh en0 # specify interface
|
|
# sudo ./capture_tls.sh en0 30 # capture for 30 seconds
|
|
#
|
|
# Output:
|
|
# ./captures/tls_capture_<timestamp>.txt
|
|
# ./captures/tls_capture_<timestamp>.pcap
|
|
# ─────────────────────────────────────────────────────────────
|
|
|
|
set -euo pipefail
|
|
|
|
IFACE="${1:-en0}"
|
|
DURATION="${2:-60}"
|
|
OUTDIR="./captures"
|
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
PCAP_FILE="${OUTDIR}/tls_capture_${TIMESTAMP}.pcap"
|
|
TXT_FILE="${OUTDIR}/tls_capture_${TIMESTAMP}.txt"
|
|
|
|
mkdir -p "$OUTDIR"
|
|
|
|
# Resolve target IPs
|
|
echo "Resolving target domains..."
|
|
DOMAINS=(
|
|
"api.anthropic.com"
|
|
"platform.claude.com"
|
|
"claude.ai"
|
|
"cloudaicompanion.googleapis.com"
|
|
"generativelanguage.googleapis.com"
|
|
"oauth2.googleapis.com"
|
|
"accounts.google.com"
|
|
)
|
|
|
|
HOST_FILTER=""
|
|
for domain in "${DOMAINS[@]}"; do
|
|
ips=$(dig +short "$domain" 2>/dev/null | grep -E '^[0-9]+\.' | head -5)
|
|
for ip in $ips; do
|
|
if [ -n "$HOST_FILTER" ]; then
|
|
HOST_FILTER="$HOST_FILTER or host $ip"
|
|
else
|
|
HOST_FILTER="host $ip"
|
|
fi
|
|
done
|
|
echo " $domain → $ips"
|
|
done
|
|
|
|
if [ -z "$HOST_FILTER" ]; then
|
|
echo "ERROR: Could not resolve any target domains"
|
|
exit 1
|
|
fi
|
|
|
|
CAPTURE_FILTER="tcp port 443 and ($HOST_FILTER)"
|
|
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════"
|
|
echo " TLS Fingerprint Capture"
|
|
echo " Interface: $IFACE"
|
|
echo " Duration: ${DURATION}s"
|
|
echo " Filter: $CAPTURE_FILTER"
|
|
echo " PCAP: $PCAP_FILE"
|
|
echo " Report: $TXT_FILE"
|
|
echo "═══════════════════════════════════════════════════════"
|
|
echo ""
|
|
echo ">>> Now run 'claude login' or 'claude \"hello\"' in another terminal <<<"
|
|
echo ">>> Press Ctrl+C to stop early <<<"
|
|
echo ""
|
|
|
|
# Capture pcap in background
|
|
tshark -i "$IFACE" -f "$CAPTURE_FILTER" -w "$PCAP_FILE" -a "duration:$DURATION" 2>/dev/null &
|
|
TSHARK_PID=$!
|
|
|
|
# Wait for capture to complete or Ctrl+C
|
|
trap "kill $TSHARK_PID 2>/dev/null; wait $TSHARK_PID 2>/dev/null" INT TERM
|
|
wait $TSHARK_PID 2>/dev/null || true
|
|
|
|
echo ""
|
|
echo "Capture complete. Analyzing..."
|
|
echo ""
|
|
|
|
# ─── Analysis ───
|
|
|
|
{
|
|
echo "═══════════════════════════════════════════════════════"
|
|
echo " TLS ClientHello Fingerprint Report"
|
|
echo " Captured: $(date)"
|
|
echo " PCAP: $PCAP_FILE"
|
|
echo "═══════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
# Extract JA3 fingerprints
|
|
echo "─── JA3 Fingerprints (ClientHello) ───"
|
|
echo ""
|
|
tshark -r "$PCAP_FILE" \
|
|
-Y "tls.handshake.type == 1" \
|
|
-T fields \
|
|
-e frame.time \
|
|
-e ip.dst \
|
|
-e tls.handshake.extensions_server_name \
|
|
-e tls.handshake.ja3 \
|
|
-e tls.handshake.ja3_full \
|
|
2>/dev/null | while IFS=$'\t' read -r ts dst sni ja3 ja3_full; do
|
|
echo " Time: $ts"
|
|
echo " Dest IP: $dst"
|
|
echo " SNI: $sni"
|
|
echo " JA3 Hash: $ja3"
|
|
if [ -n "$ja3_full" ]; then
|
|
echo " JA3 Full: $ja3_full"
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
echo ""
|
|
echo "─── TLS Versions ───"
|
|
echo ""
|
|
tshark -r "$PCAP_FILE" \
|
|
-Y "tls.handshake.type == 1" \
|
|
-T fields \
|
|
-e tls.handshake.extensions_server_name \
|
|
-e tls.handshake.version \
|
|
-e tls.handshake.extensions.supported_version \
|
|
2>/dev/null | sort -u | while IFS=$'\t' read -r sni ver supported; do
|
|
echo " SNI: $sni"
|
|
echo " Record Version: $ver"
|
|
echo " Supported Versions: $supported"
|
|
echo ""
|
|
done
|
|
|
|
echo ""
|
|
echo "─── ALPN Protocols ───"
|
|
echo ""
|
|
tshark -r "$PCAP_FILE" \
|
|
-Y "tls.handshake.type == 1" \
|
|
-T fields \
|
|
-e tls.handshake.extensions_server_name \
|
|
-e tls.handshake.extensions_alpn_str \
|
|
2>/dev/null | sort -u | while IFS=$'\t' read -r sni alpn; do
|
|
echo " SNI: $sni → ALPN: $alpn"
|
|
done
|
|
|
|
echo ""
|
|
echo ""
|
|
echo "─── Cipher Suites (per ClientHello) ───"
|
|
echo ""
|
|
tshark -r "$PCAP_FILE" \
|
|
-Y "tls.handshake.type == 1" \
|
|
-T fields \
|
|
-e tls.handshake.extensions_server_name \
|
|
-e tls.handshake.ciphersuite \
|
|
2>/dev/null | head -5 | while IFS=$'\t' read -r sni ciphers; do
|
|
echo " SNI: $sni"
|
|
echo " Cipher Suites:"
|
|
echo " $ciphers" | tr ',' '\n' | while read -r c; do
|
|
echo " $c"
|
|
done
|
|
echo ""
|
|
done
|
|
|
|
echo ""
|
|
echo "─── Extensions (per ClientHello) ───"
|
|
echo ""
|
|
tshark -r "$PCAP_FILE" \
|
|
-Y "tls.handshake.type == 1" \
|
|
-T fields \
|
|
-e tls.handshake.extensions_server_name \
|
|
-e tls.handshake.extension.type \
|
|
2>/dev/null | head -5 | while IFS=$'\t' read -r sni exts; do
|
|
echo " SNI: $sni"
|
|
echo " Extensions: $exts"
|
|
echo ""
|
|
done
|
|
|
|
echo ""
|
|
echo "─── Unique JA3 Summary ───"
|
|
echo ""
|
|
tshark -r "$PCAP_FILE" \
|
|
-Y "tls.handshake.type == 1" \
|
|
-T fields \
|
|
-e tls.handshake.extensions_server_name \
|
|
-e tls.handshake.ja3 \
|
|
2>/dev/null | sort | uniq -c | sort -rn | while read -r count sni ja3; do
|
|
echo " ${count}x SNI: $sni JA3: $ja3"
|
|
done
|
|
|
|
echo ""
|
|
echo "─── TCP Fingerprint (Initial Window Size, TTL) ───"
|
|
echo ""
|
|
tshark -r "$PCAP_FILE" \
|
|
-Y "tcp.flags.syn == 1 && tcp.flags.ack == 0" \
|
|
-T fields \
|
|
-e ip.dst \
|
|
-e ip.ttl \
|
|
-e tcp.window_size_value \
|
|
-e tcp.options.mss_val \
|
|
-e tcp.options.wscale.shift \
|
|
2>/dev/null | sort -u | while IFS=$'\t' read -r dst ttl win mss wscale; do
|
|
echo " Dest: $dst TTL: $ttl Window: $win MSS: $mss WScale: $wscale"
|
|
done
|
|
|
|
} 2>/dev/null | tee "$TXT_FILE"
|
|
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════"
|
|
echo " Report saved to: $TXT_FILE"
|
|
echo " PCAP saved to: $PCAP_FILE"
|
|
echo ""
|
|
echo " To re-analyze: tshark -r $PCAP_FILE -Y 'tls.handshake.type==1' ..."
|
|
echo "═══════════════════════════════════════════════════════"
|