mirror of
https://github.com/hotyue/IP-Sentinel.git
synced 2026-07-23 05:26:57 +08:00
271 lines
11 KiB
Bash
Executable File
271 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# ==========================================================
|
||
# 脚本名称: mod_google.sh
|
||
# 核心功能: 区域网络模拟、行为轨迹拉伸、地理定位锚定
|
||
# ==========================================================
|
||
|
||
MODULE_NAME="Google"
|
||
CONFIG_FILE="/opt/ip_sentinel/config.conf"
|
||
|
||
# --- [环境预载] ---
|
||
if [ -f "$CONFIG_FILE" ]; then
|
||
source "$CONFIG_FILE"
|
||
else
|
||
echo "配置文件丢失!退出执行。"
|
||
exit 1
|
||
fi
|
||
|
||
# [容灾机制] 若宿主环境未注入日志函数,则启动 Fallback 接管
|
||
if ! type log >/dev/null 2>&1; then
|
||
log() {
|
||
# [版本锚定] 提取运行时动态版本标识
|
||
local local_ver="${AGENT_VERSION:-未知}"
|
||
|
||
mkdir -p "${INSTALL_DIR}/logs"
|
||
|
||
# [时区对齐] 强制采用绝对 UTC 时间消除跨域日志偏移
|
||
local core_msg=$(printf "[v%-5s] [%-5s] [%-7s] [%s] %s" "$local_ver" "$2" "$1" "$REGION_CODE" "$3")
|
||
echo "[$(date -u '+%Y-%m-%d %H:%M:%S UTC')] $core_msg" >> "${INSTALL_DIR}/logs/sentinel.log"
|
||
|
||
# [系统挂载] 桥接至 Systemd Journal 守护日志
|
||
if command -v logger >/dev/null 2>&1; then
|
||
logger -t ip-sentinel "$core_msg"
|
||
else
|
||
echo "$core_msg"
|
||
fi
|
||
}
|
||
fi
|
||
|
||
log "$MODULE_NAME" "START" "========== 唤醒网络模拟器 [区域: $REGION_NAME] =========="
|
||
|
||
# --- [数据装配] ---
|
||
UA_FILE="${INSTALL_DIR}/data/user_agents.txt"
|
||
KW_FILE="${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt"
|
||
|
||
if [ ! -f "$UA_FILE" ] || [ ! -f "$KW_FILE" ]; then
|
||
log "$MODULE_NAME" "ERROR" "热数据缺失,请检查 data 目录。放弃本次执行。"
|
||
exit 1
|
||
fi
|
||
|
||
mapfile -t UA_POOL < <(grep -v '^$' "$UA_FILE")
|
||
mapfile -t KEYWORDS < <(grep -v '^$' "$KW_FILE")
|
||
|
||
# --- [辅助运算] ---
|
||
get_random_coord() {
|
||
local base=$1
|
||
local range=$2
|
||
local offset=$(awk "BEGIN {print ( ( ($RANDOM % ($range * 2)) - $range ) / 10000 )}")
|
||
awk "BEGIN {print ($base + $offset)}"
|
||
}
|
||
|
||
# --- [身份画像构建] ---
|
||
# [防线提取] 优先捕获固化的公网面孔作为种子
|
||
CURRENT_IP="${PUBLIC_IP:-${BIND_IP:-Unknown}}"
|
||
|
||
# -----------------------------------------------------------
|
||
# [指纹固化] 哈希锚定法 (Hash-Seeded Persona)
|
||
# 基于 IP 算力固定会话指纹池,彻底破除僵尸网络同质化特征
|
||
# -----------------------------------------------------------
|
||
TOTAL_UA=${#UA_POOL[@]}
|
||
if [ "$TOTAL_UA" -gt 0 ]; then
|
||
SEED=$(echo -n "$CURRENT_IP" | cksum | awk '{print $1}')
|
||
|
||
IDX1=$(( SEED % TOTAL_UA ))
|
||
IDX2=$(( (SEED * 17) % TOTAL_UA ))
|
||
IDX3=$(( (SEED * 31) % TOTAL_UA ))
|
||
|
||
MY_UA_POOL=("${UA_POOL[$IDX1]}" "${UA_POOL[$IDX2]}" "${UA_POOL[$IDX3]}")
|
||
SESSION_UA=${MY_UA_POOL[$RANDOM % 3]}
|
||
else
|
||
# [极简兜底] 降维指纹防御崩溃
|
||
SESSION_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
||
fi
|
||
|
||
# [LBS 锚定] 在基准战区内生成固定范围内的微抖动咖啡馆坐标
|
||
SESSION_BASE_LAT=$(get_random_coord $BASE_LAT 270)
|
||
SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
|
||
|
||
# [行为学控制] 随机指派本次会话的动作深度
|
||
TOTAL_ACTIONS=$((5 + RANDOM % 4))
|
||
|
||
log "$MODULE_NAME" "INFO " "当前出网 IP: $CURRENT_IP"
|
||
log "$MODULE_NAME" "INFO " "设备指纹锁定: ${SESSION_UA:0:45}..."
|
||
log "$MODULE_NAME" "INFO " "虚拟驻留坐标: $SESSION_BASE_LAT, $SESSION_BASE_LON"
|
||
|
||
# -----------------------------------------------------------
|
||
# [网络栈探底] 协议自适应与出站死锁
|
||
# -----------------------------------------------------------
|
||
CURL_BIND_OPT=""
|
||
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
|
||
|
||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||
# [防线校验] 探测物理网卡存活状态,防 IP 漂移引发通信雪崩
|
||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||
log "$MODULE_NAME" "WARN " "检测到配置的出口 IP ($RAW_BIND_IP) 已丢失,自动降级为系统默认路由出网!"
|
||
CURL_BIND_OPT=""
|
||
else
|
||
CURL_BIND_OPT="--interface $BIND_IP"
|
||
if [[ "$BIND_IP" == *":"* ]]; then
|
||
DYNAMIC_IP_PREF="-6"
|
||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||
DYNAMIC_IP_PREF="-4"
|
||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# --- [会话漫游模拟] ---
|
||
for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||
# [LBS 微抖动] 模拟人类手持设备时的 GPS 细微漂移
|
||
ACTION_LAT=$(get_random_coord $SESSION_BASE_LAT 1)
|
||
ACTION_LON=$(get_random_coord $SESSION_BASE_LON 1)
|
||
|
||
RAND_KEY=${KEYWORDS[$RANDOM % ${#KEYWORDS[@]}]}
|
||
ENCODED_KEY=$(echo "$RAND_KEY" | jq -sRr @uri)
|
||
|
||
# [动作轮盘] 随机指派单次行为类型
|
||
ACTION_TYPE=$((1 + RANDOM % 4))
|
||
|
||
# [协议挂载] 注入双栈与网卡死锁参数
|
||
case $ACTION_TYPE in
|
||
1) # 搜索引擎交互
|
||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||
"https://www.google.com/search?q=${ENCODED_KEY}&${LANG_PARAMS}")
|
||
;;
|
||
2) # 区域新闻阅读
|
||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||
"https://news.google.com/home?${LANG_PARAMS}")
|
||
;;
|
||
3) # 坐标系 LBS 查询
|
||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||
"https://www.google.com/maps/search/$${ENCODED_KEY}/@${ACTION_LAT},${ACTION_LON},17z?${LANG_PARAMS}")
|
||
;;
|
||
4) # 底层系统级网络探测连通性握手
|
||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||
"https://connectivitycheck.gstatic.com/generate_204")
|
||
;;
|
||
esac
|
||
|
||
log "$MODULE_NAME" "EXEC " "动作[$i/$TOTAL_ACTIONS]完成 | HTTP状态: $CODE | 抖动坐标: $ACTION_LAT, $ACTION_LON"
|
||
|
||
# [行为拉伸] 动作间隔注入泊松长尾睡眠,模拟人类真实阅读思考时间
|
||
if [ $i -lt $TOTAL_ACTIONS ]; then
|
||
SLEEP_TIME=$((45 + RANDOM % 31))
|
||
log "$MODULE_NAME" "WAIT " "阅读当前页面内容,模拟停留 $SLEEP_TIME 秒..."
|
||
sleep $SLEEP_TIME
|
||
fi
|
||
done
|
||
|
||
# -----------------------------------------------------------
|
||
# [态势感知] 三核探测雷达 (跳转 / Premium / Music)
|
||
# -----------------------------------------------------------
|
||
log "$MODULE_NAME" "INFO " "启动三核交叉验证 (URL跳转 + YT Premium + YT Music) 穿透获取 GeoIP..."
|
||
|
||
# 探针 1: URL 区域重定向嗅探
|
||
JUMP_HDR=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -sI "http://www.google.com/")
|
||
JUMP_LOC=$(echo "$JUMP_HDR" | grep -i "^location:" | tr -d '\r\n')
|
||
JUMP_GL=""
|
||
|
||
if [ -z "$JUMP_LOC" ]; then
|
||
JUMP_GL="US"
|
||
elif [[ "$JUMP_LOC" == *".google.cn"* ]] || [[ "$JUMP_LOC" == *"gl=CN"* ]]; then
|
||
JUMP_GL="CN"
|
||
elif [[ "$JUMP_LOC" == *"gl="* ]]; then
|
||
JUMP_GL=$(echo "$JUMP_LOC" | grep -o 'gl=[A-Za-z]\{2\}' | head -n 1 | cut -d'=' -f2 | tr 'a-z' 'A-Z')
|
||
else
|
||
JUMP_DOMAIN=$(echo "$JUMP_LOC" | grep -o 'google\.[a-z\.]*' | head -n 1 | sed 's/google\.//')
|
||
case "$JUMP_DOMAIN" in
|
||
"com") JUMP_GL="US" ;;
|
||
"com.hk") JUMP_GL="HK" ;;
|
||
"com.tw") JUMP_GL="TW" ;;
|
||
"co.jp") JUMP_GL="JP" ;;
|
||
"co.uk") JUMP_GL="GB" ;;
|
||
"co.kr") JUMP_GL="KR" ;;
|
||
"co.in") JUMP_GL="IN" ;;
|
||
"co.id") JUMP_GL="ID" ;;
|
||
"co.th") JUMP_GL="TH" ;;
|
||
"com.sg") JUMP_GL="SG" ;;
|
||
"com.my") JUMP_GL="MY" ;;
|
||
"com.au") JUMP_GL="AU" ;;
|
||
"com.br") JUMP_GL="BR" ;;
|
||
"com.mx") JUMP_GL="MX" ;;
|
||
"com.ar") JUMP_GL="AR" ;;
|
||
"co.za") JUMP_GL="ZA" ;;
|
||
"cn") JUMP_GL="CN" ;;
|
||
"") JUMP_GL="" ;;
|
||
*)
|
||
LAST_EXT=$(echo "$JUMP_DOMAIN" | awk -F'.' '{print $NF}' | tr 'a-z' 'A-Z')
|
||
if [ ${#LAST_EXT} -eq 2 ]; then
|
||
JUMP_GL="$LAST_EXT"
|
||
else
|
||
JUMP_GL="US"
|
||
fi
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
# 探针 2: YouTube Premium 区域锁嗅探
|
||
YT_PR_GL=""
|
||
YT_PR_HTML=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -s -L -A "$SESSION_UA" "https://www.youtube.com/premium")
|
||
if [[ "$YT_PR_HTML" == *"www.google.cn"* ]]; then
|
||
YT_PR_GL="CN"
|
||
else
|
||
YT_PR_GL=$(echo "$YT_PR_HTML" | grep -o '"contentRegion":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||
[ -z "$YT_PR_GL" ] && YT_PR_GL=$(echo "$YT_PR_HTML" | grep -o '"countryCode":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||
[ -z "$YT_PR_GL" ] && YT_PR_GL=$(echo "$YT_PR_HTML" | grep -o '"INNERTUBE_CONTEXT_GL":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||
fi
|
||
|
||
# 探针 3: YouTube Music 区域锁嗅探
|
||
YT_MU_GL=""
|
||
YT_MU_HTML=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -s -L -A "$SESSION_UA" "https://music.youtube.com/")
|
||
if [[ "$YT_MU_HTML" == *"www.google.cn"* ]]; then
|
||
YT_MU_GL="CN"
|
||
else
|
||
YT_MU_GL=$(echo "$YT_MU_HTML" | grep -o '"INNERTUBE_CONTEXT_GL":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||
[ -z "$YT_MU_GL" ] && YT_MU_GL=$(echo "$YT_MU_HTML" | grep -o '"countryCode":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||
[ -z "$YT_MU_GL" ] && YT_MU_GL=$(echo "$YT_MU_HTML" | grep -o '"GL":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||
fi
|
||
|
||
# [坐标规整] 兼容横杠分割体系,并修正英区缩写
|
||
TARGET_CC="${REGION_CODE%%-*}"
|
||
[ "$TARGET_CC" == "UK" ] && TARGET_CC="GB"
|
||
|
||
# -----------------------------------------------------------
|
||
# [终极审判] 异常过滤与一致性裁决机制
|
||
# -----------------------------------------------------------
|
||
IS_CN=0
|
||
VALID_PROBES=0
|
||
|
||
for val in "$JUMP_GL" "$YT_PR_GL" "$YT_MU_GL"; do
|
||
if [ -n "$val" ]; then
|
||
((VALID_PROBES++))
|
||
[ "$val" == "CN" ] && IS_CN=1
|
||
fi
|
||
done
|
||
|
||
if [ $VALID_PROBES -eq 0 ]; then
|
||
STATUS="🚨 探针失效 (三核全部熔断,可能遭严重风控拦截)"
|
||
elif [ $IS_CN -eq 1 ]; then
|
||
STATUS="❌ 严重高危!三核雷达判定 IP 已被中国大陆锁定 (送中)!"
|
||
else
|
||
# [权重仲裁] 以流媒体核心解锁状态为主导,允许基础网段跨国漂移
|
||
YT_MATCH=0
|
||
[ "$YT_PR_GL" == "$TARGET_CC" ] && YT_MATCH=1
|
||
[ "$YT_MU_GL" == "$TARGET_CC" ] && YT_MATCH=1
|
||
|
||
if [ $YT_MATCH -eq 1 ]; then
|
||
if [ -n "$JUMP_GL" ] && [ "$JUMP_GL" != "$TARGET_CC" ]; then
|
||
STATUS="✅ 目标区域达成 (YT主导成功, Jump副雷达漂移至 ${JUMP_GL}) | Prem: ${YT_PR_GL:-无} | Music: ${YT_MU_GL:-无}"
|
||
else
|
||
STATUS="✅ 目标区域达成 (Jump: ${JUMP_GL:-无} | Prem: ${YT_PR_GL:-无} | Music: ${YT_MU_GL:-无})"
|
||
fi
|
||
else
|
||
STATUS="⚠️ 区域发生漂移!目标 $TARGET_CC,实际 (Jump: ${JUMP_GL:-无} | Prem: ${YT_PR_GL:-无} | Music: ${YT_MU_GL:-无})"
|
||
fi
|
||
fi
|
||
|
||
log "$MODULE_NAME" "SCORE" "自检结论: $STATUS"
|
||
log "$MODULE_NAME" "END " "========== 会话结束,释放进程 ==========" |