From 454e1c8c115ea90091516253d819ec870a14f92d Mon Sep 17 00:00:00 2001 From: Connor James Date: Sat, 10 Sep 2016 22:01:13 -0500 Subject: [PATCH] AntiCheat 1.2 (#200) * Implement Anticheat 1.2 hooks * Add default check thresholds for unknown check types * Add violation level support for new checks * Add ban waves for KillAura type D * Amend DELETE_PENDING now that it's not hardcoded * Add instant bans for high VL Glide/Speed * Don't specify hack type in banwave ban message --- Plugins/Mineplex.Core/pom.xml | 2 +- .../src/mineplex/core/antihack/AntiHack.java | 50 +++++++++++--- .../core/antihack/CheckThresholds.java | 9 ++- .../core/antihack/actions/AntiHackAction.java | 6 ++ .../core/antihack/actions/BanwaveAction.java | 15 ++-- .../core/antihack/banwave/BanWaveInfo.java | 56 +++------------ .../core/antihack/banwave/BanWaveManager.java | 24 ++----- .../antihack/banwave/BanWaveRepository.java | 68 ++++++++++--------- 8 files changed, 113 insertions(+), 117 deletions(-) diff --git a/Plugins/Mineplex.Core/pom.xml b/Plugins/Mineplex.Core/pom.xml index 81a567966..45dbf6cfb 100644 --- a/Plugins/Mineplex.Core/pom.xml +++ b/Plugins/Mineplex.Core/pom.xml @@ -47,7 +47,7 @@ com.mineplex anticheat - 1.1 + 1.2 diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHack.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHack.java index 91a57dd97..650362224 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHack.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/AntiHack.java @@ -62,13 +62,16 @@ import java.util.function.Predicate; @ReflectivelyCreateMiniPlugin public class AntiHack extends MiniPlugin { - public static final Map CHECKS = ImmutableMap.of( - "Killaura (Type A)", new CheckThresholds("Kill Aura", 25, 50), - "Killaura (Type B)", new CheckThresholds("High CPS", 0, Integer.MAX_VALUE), - "Killaura (Type C)", new CheckThresholds("Reach", Integer.MAX_VALUE, Integer.MAX_VALUE), - "Killaura (Type D)", new CheckThresholds("Kill Aura", 1000, 1500), - "BadPackets", new CheckThresholds("Regen", 1000, 2000) - ); + public static final Map CHECKS = ImmutableMap.builder() + .put("Killaura (Type A)", new CheckThresholds("Kill Aura", 0, 25, 50)) + .put("Killaura (Type B)", new CheckThresholds("High CPS", 0, 0, Integer.MAX_VALUE)) + .put("Killaura (Type C)", new CheckThresholds("Reach", 0, Integer.MAX_VALUE, Integer.MAX_VALUE)) + .put("Killaura (Type D)", new CheckThresholds("Kill Aura", 500, 1000, 1500)) + .put("BadPackets", new CheckThresholds("Regen", 500, 1000, 2000)) + .put("Glide", new CheckThresholds("Flying", 50, 100, 200)) // TODO: specific VL levels + .put("Speed", new CheckThresholds("Speed", 50, 100, 200)) // TODO: specific VL levels + .put("HeadRoll", new CheckThresholds("Illegal Movement", 0, 0, 0)) + .build(); public static final String NAME = "Chiss"; public static final String USER_HAS_BEEN_BANNED = F.main("GWEN", "%s has been banned. I am always watching"); @@ -176,6 +179,24 @@ public class AntiHack extends MiniPlugin { return UtilPlayer.isSpectator(player); } + + @Override + public double getTPS() + { + return MinecraftServer.getServer().recentTps[0]; // Return the average TPS from the last minute + } + + @Override + public int getPing(Player player) + { + return Math.min(((CraftPlayer)player).getHandle().ping, 1000); + } + + @Override + public boolean isUsingItem(Player player) + { + return ((CraftPlayer)player).getHandle().bS(); // See Anticheat javadoc + } }, this._plugin, ServicePriority.Normal); ServerCommandManager.getInstance().registerCommandType(MajorViolationCommand.class, violation -> @@ -973,7 +994,20 @@ public class AntiHack extends MiniPlugin { if (event.shouldTellStaff()) { - String key = event.getPlayer().getName() + "." + event.getHackType() + "." + CHECKS.get(event.getHackType()).getSeverity(event.getViolations()).toString(); + CheckThresholds thresholds = CHECKS.get(event.getHackType()); + if (thresholds == null) + { + thresholds = new CheckThresholds(event.getHackType(), 0, Integer.MAX_VALUE, Integer.MAX_VALUE); + } + CheckThresholds.Severity severity = thresholds.getSeverity(event.getViolations()); + + if (severity == CheckThresholds.Severity.NONE) + { + return; + } + + String key = event.getPlayer().getName() + "." + event.getHackType() + "." + severity.toString(); + Integer pastVl = this._cooldown.getIfPresent(key); if (pastVl != null) { diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/CheckThresholds.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/CheckThresholds.java index b00207db6..f64783e55 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/CheckThresholds.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/CheckThresholds.java @@ -8,12 +8,14 @@ import net.minecraft.server.v1_8_R3.IChatBaseComponent; public class CheckThresholds { private final String _friendlyName; + private final int _low; private final int _med; private final int _high; - public CheckThresholds(String friendlyName, int med, int high) + public CheckThresholds(String friendlyName, int low, int med, int high) { _friendlyName = friendlyName; + _low = low; _med = med; _high = high; } @@ -39,14 +41,17 @@ public class CheckThresholds { return Severity.MEDIUM; - } else + } else if (violationLevel >= _low) { return Severity.LOW; } + + return Severity.NONE; } public enum Severity { + NONE(EnumChatFormat.GREEN), LOW(EnumChatFormat.GREEN), MEDIUM(EnumChatFormat.GOLD), HIGH(EnumChatFormat.RED), diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/AntiHackAction.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/AntiHackAction.java index 689f57f7b..ddcc5e909 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/AntiHackAction.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/AntiHackAction.java @@ -2,6 +2,9 @@ package mineplex.core.antihack.actions; import com.mineplex.anticheat.api.PlayerViolationEvent; import com.mineplex.anticheat.checks.combat.KillauraTypeA; +import com.mineplex.anticheat.checks.combat.KillauraTypeD; +import com.mineplex.anticheat.checks.move.Glide; +import com.mineplex.anticheat.checks.move.Speed; import mineplex.core.common.util.UtilServer; import org.bukkit.event.Listener; @@ -20,6 +23,9 @@ public abstract class AntiHackAction implements Listener static { ACTIONS.put(KillauraTypeA.class, new ImmediateBanAction(200)); + ACTIONS.put(KillauraTypeD.class, new BanwaveAction(2000)); + ACTIONS.put(Glide.class, new ImmediateBanAction(10000)); + ACTIONS.put(Speed.class, new ImmediateBanAction(10000)); } private int _vl; diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/BanwaveAction.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/BanwaveAction.java index 0a28ffdd7..876671029 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/BanwaveAction.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/actions/BanwaveAction.java @@ -3,18 +3,17 @@ package mineplex.core.antihack.actions; import com.mineplex.anticheat.api.PlayerViolationEvent; import mineplex.core.Managers; import mineplex.core.antihack.banwave.BanWaveManager; +import mineplex.core.common.util.UtilMath; import mineplex.core.common.util.UtilServer; -import java.util.Date; - class BanwaveAction extends AntiHackAction { - private Date nextBanWave; + private static final int BAN_DELAY_AVERAGE = 6 * 60 * 60 * 1000; // 6 hours + private static final int BAN_DELAY_VARIANCE_SPAN = 4 * 60 * 60 * 1000; // 4 hours total; 2 on either side - BanwaveAction(Date nextBanWave, int vl) + BanwaveAction(int vl) { super(vl); - this.nextBanWave = nextBanWave; } @Override @@ -22,11 +21,13 @@ class BanwaveAction extends AntiHackAction { if (event.getViolations() >= this.getMinVl()) { + // Delay bans by 6 hours +/- 2 hours for fuzzing + long banTime = System.currentTimeMillis() + BAN_DELAY_AVERAGE + (UtilMath.r(BAN_DELAY_VARIANCE_SPAN) - (BAN_DELAY_VARIANCE_SPAN / 2)); Managers.get(BanWaveManager.class).insertBanWaveInfo( event.getPlayer(), - nextBanWave.getTime(), + banTime, event.getCheckClass(), - event.getMessage(), + "[GWEN] Hacking [BanWave]", event.getViolations(), UtilServer.getServerName() ); diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveInfo.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveInfo.java index c73b544a6..2c471cd07 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveInfo.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveInfo.java @@ -1,12 +1,9 @@ package mineplex.core.antihack.banwave; +import java.util.Objects; + public class BanWaveInfo { - /** - * The unique id for this BanWaveInfo - */ - private int _id; - /** * The account id for this BanWaveInfo */ @@ -17,11 +14,6 @@ public class BanWaveInfo */ private long _timeToBan; - /** - * Whether this BanWaveInfo has been executed - */ - private boolean _banned; - /** * The hack type */ @@ -42,16 +34,6 @@ public class BanWaveInfo */ private String _server; - public int getId() - { - return _id; - } - - public void setId(int id) - { - _id = id; - } - public int getAccountId() { return _accountId; @@ -72,16 +54,6 @@ public class BanWaveInfo _timeToBan = timeToBan; } - public boolean isBanned() - { - return _banned; - } - - public void setBanned(boolean banned) - { - _banned = banned; - } - public String getHackType() { return _hackType; @@ -130,28 +102,18 @@ public class BanWaveInfo BanWaveInfo that = (BanWaveInfo) o; - if (_id != that._id) return false; - if (_accountId != that._accountId) return false; - if (_timeToBan != that._timeToBan) return false; - if (_banned != that._banned) return false; - if (_vl != that._vl) return false; - if (_hackType != null ? !_hackType.equals(that._hackType) : that._hackType != null) return false; - if (_message != null ? !_message.equals(that._message) : that._message != null) return false; - return _server != null ? _server.equals(that._server) : that._server == null; + return Objects.equals(_accountId, that._accountId) + && Objects.equals(_timeToBan, that._timeToBan) + && Objects.equals(_vl, that._vl) + && Objects.equals(_hackType, that._hackType) + && Objects.equals(_message, that._message) + && Objects.equals(_server, that._server); } @Override public int hashCode() { - int result = _id; - result = 31 * result + _accountId; - result = 31 * result + (int) (_timeToBan ^ (_timeToBan >>> 32)); - result = 31 * result + (_banned ? 1 : 0); - result = 31 * result + (_hackType != null ? _hackType.hashCode() : 0); - result = 31 * result + (_message != null ? _message.hashCode() : 0); - result = 31 * result + _vl; - result = 31 * result + (_server != null ? _server.hashCode() : 0); - return result; + return Objects.hash(_accountId, _timeToBan, _hackType, _message, _vl, _server); } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveManager.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveManager.java index 8b39d9ac0..dd2bebe96 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveManager.java @@ -9,8 +9,6 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerJoinEvent; -import java.util.List; - @ReflectivelyCreateMiniPlugin public class BanWaveManager extends MiniPlugin { @@ -28,28 +26,16 @@ public class BanWaveManager extends MiniPlugin { CoreClient client = require(CoreClientManager.class).Get(event.getPlayer()); - List infos = _repository.getBanWaveInfo(client.getAccountId()); - - long now = System.currentTimeMillis(); - - boolean banned = false; - for (BanWaveInfo info : infos) + _repository.getPendingBanWaveInfo(client.getAccountId(), info -> { - if (info.getTimeToBan() < now && !info.isBanned()) + long now = System.currentTimeMillis(); + + if (info.getTimeToBan() < now) { - banned = true; require(AntiHack.class).doBanWave(event.getPlayer(), info.getMessage()); - break; - } - } - - if (banned) - { - for (BanWaveInfo info : infos) - { _repository.flagDone(info); } - } + }); }); } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveRepository.java b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveRepository.java index 7b8650d35..ff41ab647 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveRepository.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/antihack/banwave/BanWaveRepository.java @@ -1,5 +1,6 @@ package mineplex.core.antihack.banwave; +import mineplex.core.common.util.Callback; import mineplex.core.common.util.UtilServer; import mineplex.core.database.MinecraftRepository; import mineplex.serverdata.database.DBPool; @@ -7,27 +8,33 @@ import mineplex.serverdata.database.column.ColumnInt; import mineplex.serverdata.database.column.ColumnLong; import mineplex.serverdata.database.column.ColumnVarChar; -import java.util.ArrayList; -import java.util.List; - public class BanWaveRepository extends MinecraftRepository { - private static final String TABLE_NAME = "banwave"; - - private static final String INITIALIZE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + - "id INT AUTO_INCREMENT, " + - "accountId INT NOT NULL, " + + private static final String INITIALIZE_PENDING_TABLE = "CREATE TABLE IF NOT EXISTS banwavePending (" + + "accountId INT(11) NOT NULL, " + "timeToBan BIGINT UNSIGNED NOT NULL, " + - "banned TINYINT DEFAULT '0', " + "hacktype VARCHAR(64), " + "message VARCHAR(255), " + "vl INT, " + "server VARCHAR(32), " + - "PRIMARY KEY (id), " + - "INDEX (accountId))"; - private static final String QUERY_BY_ACCOUNT = "SELECT * FROM " + TABLE_NAME + " WHERE accountId = ?"; - private static final String INSERT_INTO_TABLE = "INSERT INTO " + TABLE_NAME + " (accountId, timeToBan, hacktype, message, vl, server) VALUES (?, ?, ?, ?, ?, ?)"; - private static final String FLAG_DONE = "UPDATE " + TABLE_NAME + " SET banned = 1 WHERE id = ?"; + "PRIMARY KEY (accountId)," + + "FOREIGN KEY (accountId) REFERENCES accounts(id))"; + + private static final String INITIALIZED_PROCESSED_TABLE = "CREATE TABLE IF NOT EXISTS banwaveProcessed (" + + "id INT NOT NULL AUTO_INCREMENT, " + + "accountId INT(11) NOT NULL, " + + "timeToBan BIGINT UNSIGNED NOT NULL, " + + "hacktype VARCHAR(64), " + + "message VARCHAR(255), " + + "vl INT, " + + "server VARCHAR(32), " + + "PRIMARY KEY (id)," + + "FOREIGN KEY (accountId) REFERENCES accounts(id))"; + private static final String QUERY_PENDING = "SELECT * FROM banwavePending WHERE accountId = ?"; + private static final String INSERT_PENDING = "INSERT IGNORE INTO banwavePending (accountId, timeToBan, hacktype, message, vl, server) VALUES (?, ?, ?, ?, ?, ?)"; + + private static final String PROCESS_WAVE_FOR_ACCOUNT = "INSERT INTO banwaveProcessed SELECT 0, accountId, timeToBan, hacktype, message, vl, server FROM banwavePending WHERE accountId = ?"; + private static final String DELETE_PENDING = "DELETE FROM banwavePending WHERE accountId = ?"; BanWaveRepository() { @@ -37,7 +44,7 @@ public class BanWaveRepository extends MinecraftRepository @Override protected void initialize() { - executeUpdate(INITIALIZE_TABLE); + //executeUpdate(INITIALIZE_TABLE); } @Override @@ -46,34 +53,28 @@ public class BanWaveRepository extends MinecraftRepository } - List getBanWaveInfo(int accountId) + void getPendingBanWaveInfo(int accountId, Callback callback) { - List banWaveInfo = new ArrayList<>(); - - executeQuery(QUERY_BY_ACCOUNT, resultSet -> + executeQuery(QUERY_PENDING, resultSet -> { - while (resultSet.next()) + if (resultSet.next()) { BanWaveInfo info = new BanWaveInfo(); - info.setId(resultSet.getInt(1)); - info.setAccountId(resultSet.getInt(2)); - info.setTimeToBan(resultSet.getLong(3)); - info.setBanned(resultSet.getInt(4) == 1); - info.setHackType(resultSet.getString(5)); - info.setMessage(resultSet.getString(6)); - info.setVl(resultSet.getInt(7)); - info.setServer(resultSet.getString(8)); + info.setAccountId(resultSet.getInt(1)); + info.setTimeToBan(resultSet.getLong(2)); + info.setHackType(resultSet.getString(3)); + info.setMessage(resultSet.getString(4)); + info.setVl(resultSet.getInt(5)); + info.setServer(resultSet.getString(6)); - banWaveInfo.add(info); + callback.run(info); } }, new ColumnInt("accountId", accountId)); - - return banWaveInfo; } void insertBanWaveInfo(int accountId, long timeToBan, String hackType, String message, int vl, String server) { - executeInsert(INSERT_INTO_TABLE, null, + executeInsert(INSERT_PENDING, null, new ColumnInt("accountId", accountId), new ColumnLong("timeToBan", timeToBan), new ColumnVarChar("hacktype", 64, hackType), @@ -85,6 +86,7 @@ public class BanWaveRepository extends MinecraftRepository void flagDone(BanWaveInfo info) { - executeUpdate(FLAG_DONE, new ColumnInt("id", info.getId())); + executeUpdate(PROCESS_WAVE_FOR_ACCOUNT, new ColumnInt("id", info.getAccountId())); + executeUpdate(DELETE_PENDING, new ColumnInt("id", info.getAccountId())); } }