Files
Aluer/src/main/java/com/aluer/security/AntiPacketFlyService.java
T
Peijun Zhao 1b66197804 V5.0 安全模块大扩展:Meteor Client 全功能反作弊 + AI行为分析 + 服务器保护
新增 45 个安全模块,覆盖 Meteor Client 全部 hack 类别:

【反作弊基础扩展 6 模块】
- AntiTimerService: 游戏加速检测(移动tick间隔分析)
- AntiVelocityService: 击退修改检测(伤害后速度分析)
- AntiPhaseService: 穿墙检测(固体方块射线追踪)
- AntiBlinkService: 闪烁检测(伤害-断连-重连时序)
- AntiFastBreakService: 快速破坏检测(方块标准破坏时间对比)
- AntiElytraFlyService: 鞘翅飞行检测(速度/高度操控分析)

【Meteor Combat 10 模块】
- AntiCriticalsService: 自动暴击检测(跳跃/地面状态验证)
- AntiAutoCrystalService: 水晶Aura检测(放置/爆炸时序分析)
- AntiAutoTotemService: 自动图腾检测(副手装备速度分析)
- AntiSurroundService: 自动包围检测(脚下方块精确放置模式)
- AntiAutoTrapService: 自动陷阱检测(目标周围方块网格分析)
- AntiAutoArmorService: 自动装备检测(多槽位同tick交换)
- AntiChestSwapService: 自动胸甲交换检测(鞘翅-胸甲乒乓切换)
- AntiAutoLogService: 自动断线检测(伤害-断连500ms时序)
- AntiHitboxesService: 扩展碰撞箱检测(射线-边界边缘命中率)
- AntiBowAimBotService: 弓箭自瞄检测(移动目标精度分析)

【Meteor Movement 10 模块】
- AntiNoSlowService: 无减速检测(物品使用+移动速度组合)
- AntiSpiderService: 爬墙检测(无攀爬方块垂直上升)
- AntiStepService: 自动跨步检测(非跳跃1格上升)
- AntiPacketFlyService: 数据包飞行检测(连续悬浮/弹跳模式)
- AntiAirJumpService: 空中跳跃检测(离地跳跃包)
- AntiLongJumpService: 远跳检测(跳跃后水平速度衰减分析)
- AntiAntiHungerService: 免饥饿检测(活动量vs饥饿消耗率)
- AntiFastFallService: 快速下落检测(超终端速度持续下落)
- AntiVClipService: 垂直穿墙检测(固体方块间瞬移)
- AntiNoFallService 增强: 新增GroundSpoof/数据包地面/坠落累积/瞬移坠落检测

【Meteor World+Player 10 模块】
- AntiSpeedMineService: 加速挖掘检测(方块标准破坏时间对比)
- AntiFastUseService: 快速使用检测(物品冷却时间分析)
- AntiNoInteractService: 交互绕过检测(持剑开箱等异常操作)
- AntiAutoMineService: 自动挖掘检测(挖掘间隔方差/视角追踪)
- AntiVeinMinerService: 连锁挖矿检测(相邻矿石快速序列)
- AntiAutoToolService: 自动工具检测(同tick切工具+开挖)
- AntiFakePlayerService: 假人检测(心跳响应/交互缺失/移动模式)
- AntiPistonAuraService: 活塞光环检测(战斗上下文活塞激活率)
- AntiAnchorService: 锚定检测(受击零位移+封闭空间)
- AntiStashFinderService: 仓库扫描检测(网格区块加载+零交互)

【网络协议安全 5 模块】
- ProtocolStateValidator: 协议状态机验证(四态转换违规检测)
- TokenBucketRateLimiter: 令牌桶速率限制器(可复用速率控制基元)
- BotFingerprintDetector: 机器人指纹检测(五维行为指纹分析)
- NBTExploitPrevention: NBT漏洞防护(深度/大小/书页/告示牌验证)
- ConnectionHandshakeValidator: 握手完整性验证(协议版本/状态/主机名)

【服务器保护 4 模块】
- ChunkLoadRateLimiter: 区块加载限制器(WARN/LIMIT/BLOCK三级响应)
- EntityCountEnforcer: 实体数量执行器(分块限制+优先级清理)
- RedstoneUpdateLimiter: 红石更新限制器(三级渐进+观察者钟检测)
- CrashExploitSignatureDB: 崩溃漏洞签名库(12种已知漏洞+4级自动响应)

【AI/ML 行为分析 4 模块】
- BehavioralProfilingEngine: 行为分析引擎(5维特征+香农熵+Z分数)
- ThreatScoreAggregator: 威胁分数聚合器(指数衰减+4级上报)
- MovementPatternAnalyzer: 移动模式分析器(FFT频谱+旋转分析)
- CombatPatternRecognizer: 战斗模式识别器(CPS方差+多目标熵+扫视分析)

配置更新:AlertType新增39个枚举值,SuperEvolutionConfig新增39个开关,
application.yml新增39个配置项。323项全量测试通过,0失败。
2026-05-16 18:02:49 +08:00

379 lines
15 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.aluer.security;
import com.aluer.config.ServerGuardConfig;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* 数据包飞行(PacketFly)检测服务 — V5.2 反作弊移动模块
*
* 检测原理:
* 1. 无辅助持续上升检测 — PacketFly是最先进的飞行hack之一,通过操控移动数据包序列
* 欺骗服务端,实现无鞘翅、无缓降、无创造模式的持续飞行。
* 检测玩家在生存模式下没有任何飞行辅助物品(鞘翅/烟花/浮空效果)的持续垂直上升。
* 2. 震荡模式检测 — PacketFly最典型的特征是"上下震荡"模式:
* 快速的上下来回移动(约±0.5-2.0方块/tick的振荡),目的是重置服务端的摔落距离计算,
* 从而避免被反飞行检测。正常玩家不会产生这种高频的垂直振荡模式。
* 3. 永不落地检测 — PacketFly用户可以无限期保持在空中不接触地面,
* 远超正常玩家通过跳跃、鞘翅滑翔等方式能维持的最大空中时间。
* 4. 总垂直位移异常检测 — 在生存模式下,累计的总上升高度不应超过物理极限。
* PacketFly用户可以在几分钟内上升数百方块而没有任何合法的上升手段。
*
* 配置开关:serverguard.security.super-evolution.anti-packet-fly
*/
@Service
public class AntiPacketFlyService {
private final ServerGuardConfig config;
/**
* 追踪每个玩家的移动历史(playerName -> 移动记录列表)
*/
private final Map<String, List<PacketFlyRecord>> playerMoveHistory = new ConcurrentHashMap<>();
/**
* 追踪每个玩家的连续空中tick(playerName -> 连续不落地tick计数)
*/
private final Map<String, Integer> playerAirTicks = new ConcurrentHashMap<>();
/**
* 追踪每个玩家的累计上升高度(playerName -> 累计上升方块数)
*/
private final Map<String, Double> playerTotalRise = new ConcurrentHashMap<>();
/**
* 追踪每个玩家最近一次接触地面的时间(playerName -> 时间戳)
*/
private final Map<String, Instant> playerLastGroundContact = new ConcurrentHashMap<>();
/**
* 追踪每个玩家的振荡检测数据(playerName -> Y方向变化列表)
*/
private final Map<String, List<Double>> playerDyHistory = new ConcurrentHashMap<>();
private final AtomicLong totalChecks = new AtomicLong(0);
private final AtomicLong packetFlyViolations = new AtomicLong(0);
/**
* 单tick最大合法垂直上升(生存模式无飞行辅助)— 约为0.42 blocks/tick(跳跃峰值)
*/
private static final double MAX_SURVIVAL_VERTICAL_RISE = 0.5;
/**
* 持续空中tick阈值 — 超过此值且持续上升则标记
* 正常玩家从最高处跳下也最多在空中停留约12 ticks
*/
private static final int MAX_AIR_TICKS_NORMAL = 20;
/**
* 振荡检测窗口大小 — 在此窗口内检测方向反转次数
*/
private static final int OSCILLATION_WINDOW = 10;
/**
* 振荡检测:方向反转次数阈值 — 在窗口内反转超过此值即为振荡模式
*/
private static final int MAX_DIRECTION_REVERSALS = 4;
/**
* 累计上升高度异常阈值 — 生存模式无辅助累计上升超过此值可疑
*/
private static final double MAX_CUMULATIVE_RISE = 50.0;
/**
* 永不落地时间阈值(毫秒)— 超过30秒不落地即为异常
*/
private static final long MAX_AIR_TIME_MS = 30_000;
/**
* 每个玩家保留的最大记录数
*/
private static final int MAX_RECORDS_PER_PLAYER = 60;
/**
* 无参构造函数 — 测试/默认配置使用
*/
public AntiPacketFlyService() {
this(new ServerGuardConfig());
}
/**
* Spring 注入构造函数
* @param config 全局配置,提供模块开关控制
*/
public AntiPacketFlyService(ServerGuardConfig config) {
this.config = config;
}
/**
* 检测玩家是否使用PacketFly数据包操控飞行
*
* @param playerName 玩家名称
* @param playerUUID 玩家UUID
* @param fromX 移动起始X坐标
* @param fromY 移动起始Y坐标
* @param fromZ 移动起始Z坐标
* @param toX 移动结束X坐标
* @param toY 移动结束Y坐标
* @param toZ 移动结束Z坐标
* @param onGround 玩家是否在地面
* @param gameMode 游戏模式(survival/creative/spectator)
* @param hasElytra 是否装备鞘翅
* @param hasLevitation 是否有浮空效果
* @param isUsingFirework 是否正在使用烟花(鞘翅助推)
* @param timestamp 时间戳
* @return 检测结果
*/
public PacketFlyCheckResult detect(String playerName, String playerUUID,
double fromX, double fromY, double fromZ,
double toX, double toY, double toZ,
boolean onGround, String gameMode,
boolean hasElytra, boolean hasLevitation,
boolean isUsingFirework, Instant timestamp) {
// 模块开关检查 — 关闭时跳过所有检测
if (!config.getSecurity().getSuperEvolution().isAntiPacketFly()) {
return PacketFlyCheckResult.clean();
}
totalChecks.incrementAndGet();
// 创造/旁观模式允许飞行,跳过
if ("creative".equalsIgnoreCase(gameMode) || "spectator".equalsIgnoreCase(gameMode)) {
return PacketFlyCheckResult.clean();
}
double dy = toY - fromY;
double dx = toX - fromX;
double dz = toZ - fromZ;
double horizontalDist = Math.sqrt(dx * dx + dz * dz);
List<PacketFlyRecord> history = playerMoveHistory.computeIfAbsent(
playerName, k -> new ArrayList<>());
List<Double> dyHistory = playerDyHistory.computeIfAbsent(
playerName, k -> new ArrayList<>());
PacketFlyRecord record = new PacketFlyRecord(timestamp, fromX, fromY, fromZ,
toX, toY, toZ, dy, horizontalDist, onGround, hasElytra,
hasLevitation, isUsingFirework);
history.add(record);
dyHistory.add(dy);
while (history.size() > MAX_RECORDS_PER_PLAYER) {
history.remove(0);
}
while (dyHistory.size() > 30) {
dyHistory.remove(0);
}
// 更新空中tick计数
if (!onGround) {
int airTicks = playerAirTicks.getOrDefault(playerName, 0) + 1;
playerAirTicks.put(playerName, airTicks);
} else {
playerAirTicks.remove(playerName);
playerLastGroundContact.put(playerName, timestamp);
// 落地时重置累计上升
playerTotalRise.remove(playerName);
}
// 累计上升高度(只计正方向)
if (dy > 0) {
double totalRise = playerTotalRise.getOrDefault(playerName, 0.0) + dy;
playerTotalRise.put(playerName, totalRise);
}
// 确定玩家是否有合法飞行手段
boolean canFlyLegitimately = hasElytra || hasLevitation || isUsingFirework;
List<String> reasons = new ArrayList<>();
// 1. 核心检测:无合法手段的持续上升
if (!onGround && dy > MAX_SURVIVAL_VERTICAL_RISE && !canFlyLegitimately) {
reasons.add("IMPOSSIBLE_RISE: dy=" + String.format("%.3f", dy)
+ " blocks/tick in survival with no flight items"
+ " (max normal: " + String.format("%.2f", MAX_SURVIVAL_VERTICAL_RISE) + ")");
}
// 2. 振荡模式检测 — PacketFly的典型特征
if (dyHistory.size() >= OSCILLATION_WINDOW) {
int reversals = 0;
double prevSign = Math.signum(dyHistory.get(dyHistory.size() - OSCILLATION_WINDOW));
for (int i = dyHistory.size() - OSCILLATION_WINDOW + 1; i < dyHistory.size(); i++) {
double currentSign = Math.signum(dyHistory.get(i));
if (currentSign != 0 && prevSign != 0 && currentSign != prevSign) {
reversals++;
}
if (currentSign != 0) {
prevSign = currentSign;
}
}
// 高频振荡且振幅明显 — PacketFly特征
if (reversals >= MAX_DIRECTION_REVERSALS) {
// 检查振荡幅度是否显著
double maxDy = 0;
int start = Math.max(0, dyHistory.size() - OSCILLATION_WINDOW);
for (int i = start; i < dyHistory.size(); i++) {
maxDy = Math.max(maxDy, Math.abs(dyHistory.get(i)));
}
if (maxDy > 0.3) {
reasons.add("PACKET_FLY_OSCILLATION: " + reversals
+ " direction reversals in " + OSCILLATION_WINDOW
+ " ticks (amplitude: " + String.format("%.2f", maxDy)
+ ", typical PacketFly bounce pattern)");
}
}
}
// 3. 永不落地检测
int airTicks = playerAirTicks.getOrDefault(playerName, 0);
if (airTicks >= MAX_AIR_TICKS_NORMAL && !canFlyLegitimately) {
// 检查是否持续上升(而非下落)
long risingTicks = history.stream()
.filter(r -> r.dy > 0.05)
.count();
if (risingTicks >= MAX_AIR_TICKS_NORMAL / 2) {
reasons.add("NEVER_LANDS: " + airTicks
+ " ticks in air without touching ground"
+ " (" + risingTicks + " ticks rising, no flight items)");
}
}
// 4. 累计上升高度异常
double totalRise = playerTotalRise.getOrDefault(playerName, 0.0);
if (totalRise > MAX_CUMULATIVE_RISE && !canFlyLegitimately) {
packetFlyViolations.incrementAndGet();
reasons.add("EXCESSIVE_CUMULATIVE_RISE: " + String.format("%.1f", totalRise)
+ " blocks total rise without flight items"
+ " (threshold: " + String.format("%.0f", MAX_CUMULATIVE_RISE) + ")");
}
// 5. 空中持续时间过长(基于时间戳)
Instant lastGround = playerLastGroundContact.get(playerName);
if (lastGround != null && !canFlyLegitimately) {
long airTimeMs = timestamp.toEpochMilli() - lastGround.toEpochMilli();
if (airTimeMs > MAX_AIR_TIME_MS) {
reasons.add("PROLONGED_AIR_TIME: " + (airTimeMs / 1000)
+ " seconds without ground contact (no flight items)");
}
}
if (reasons.size() >= 2) {
return PacketFlyCheckResult.flagged(reasons);
} else if (reasons.size() == 1) {
return PacketFlyCheckResult.suspicious(reasons);
}
return PacketFlyCheckResult.clean();
}
/**
* 玩家离线时清理追踪数据
* @param playerName 离线玩家名称
*/
public void clearPlayer(String playerName) {
playerMoveHistory.remove(playerName);
playerAirTicks.remove(playerName);
playerTotalRise.remove(playerName);
playerLastGroundContact.remove(playerName);
playerDyHistory.remove(playerName);
}
/**
* 获取模块运行状态
* @return 包含计数器和违规数量的Map
*/
public Map<String, Object> getStatus() {
Map<String, Object> status = new LinkedHashMap<>();
status.put("totalChecks", totalChecks.get());
status.put("packetFlyViolations", packetFlyViolations.get());
status.put("activeTrackedPlayers", playerMoveHistory.size());
// 列出空中玩家
List<Map<String, Object>> airbornePlayers = new ArrayList<>();
for (Map.Entry<String, Integer> entry : playerAirTicks.entrySet()) {
if (entry.getValue() > MAX_AIR_TICKS_NORMAL) {
Map<String, Object> info = new LinkedHashMap<>();
info.put("player", entry.getKey());
info.put("airTicks", entry.getValue());
info.put("totalRise", String.format("%.1f",
playerTotalRise.getOrDefault(entry.getKey(), 0.0)));
airbornePlayers.add(info);
}
}
status.put("extendedAirbornePlayers", airbornePlayers);
return status;
}
/**
* 内部PacketFly移动记录 — 记录玩家单次移动的状态数据
*/
private static class PacketFlyRecord {
final Instant timestamp;
final double fromX, fromY, fromZ;
final double toX, toY, toZ;
final double dy;
final double horizontalDist;
final boolean onGround;
final boolean hasElytra;
final boolean hasLevitation;
final boolean isUsingFirework;
PacketFlyRecord(Instant timestamp, double fromX, double fromY, double fromZ,
double toX, double toY, double toZ, double dy,
double horizontalDist, boolean onGround,
boolean hasElytra, boolean hasLevitation,
boolean isUsingFirework) {
this.timestamp = timestamp;
this.fromX = fromX; this.fromY = fromY; this.fromZ = fromZ;
this.toX = toX; this.toY = toY; this.toZ = toZ;
this.dy = dy;
this.horizontalDist = horizontalDist;
this.onGround = onGround;
this.hasElytra = hasElytra;
this.hasLevitation = hasLevitation;
this.isUsingFirework = isUsingFirework;
}
}
/**
* PacketFly检测结果 — 不可变结果类
*/
public static class PacketFlyCheckResult {
private final boolean flagged;
private final boolean suspicious;
private final List<String> reasons;
private PacketFlyCheckResult(boolean flagged, boolean suspicious, List<String> reasons) {
this.flagged = flagged;
this.suspicious = suspicious;
this.reasons = reasons;
}
/** 无异常 — 正常的地面/合法飞行移动 */
public static PacketFlyCheckResult clean() {
return new PacketFlyCheckResult(false, false, List.of());
}
/** 可疑 — 短暂的异常但不满足所有检测条件 */
public static PacketFlyCheckResult suspicious(List<String> reasons) {
return new PacketFlyCheckResult(false, true, reasons);
}
/** 已标记 — 确认PacketFly hack,持续无合法手段的飞行 */
public static PacketFlyCheckResult flagged(List<String> reasons) {
return new PacketFlyCheckResult(true, false, reasons);
}
public boolean isFlagged() { return flagged; }
public boolean isSuspicious() { return suspicious; }
public boolean isClean() { return !flagged && !suspicious; }
public List<String> getReasons() { return reasons; }
}
}