BBMAXI 14 Опубликовано 27 июля, 2011 Всем привет ! Долго время меня не было... Будем дополнять данный раздел далее... Поехали Сейчас вам объясню, как добавить конфигурацию в сервер для привязки аккаунтов по IP адресу. Это поможет защитится от злостного брута логина. И обезопасит аккаунты игроков. Добавляем проверку в LoginController.java PreparedStatement preparedstatement1 = con.prepareStatement((new StringBuilder()).append("SELECT * FROM accounts WHERE login='")+ user +("';").toString()); ResultSet resultset1 = preparedstatement1.executeQuery(); resultset1.next(); if(!address.getHostAddress().equalsIgnoreCase(resultset1.getString("lastIP" )) && resultset1.getBoolean("ipblock")) { ok = false; _log.warning((new StringBuilder()).append("IP Block mode: Failed attempt: account ") + user + (" IP: ") + address.getHostAddress()); Добавляем voicecommadnhandlers/ipblock.java: package com.l2jfrozen.gameserver.handler.voicedcommandhandlers; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.logging.Logger; import com.l2jfrozen.L2DatabaseFactory; import com.l2jfrozen.gameserver.handler.IVoicedCommandHandler; import com.l2jfrozen.gameserver.model.actor.instance.L2PcInstance; import com.l2jfrozen.gameserver.util.FloodProtectors; /** * by Silvein * L2Maxi.ru */ public class ipblock implements IVoicedCommandHandler { private static Logger _log = Logger.getLogger(ipblock.class.getName()); private static final String[] VOICED_COMMANDS = { "ipblock" }; public boolean useVoicedCommand(String s, L2PcInstance l2pcinstance, String s1) { if (s.startsWith("ipblock")) { Object obj = null; Object obj1 = null; try { Connection connection = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement preparedstatement = connection.prepareStatement("SELECT * FROM accounts WHERE login=?"); preparedstatement.setString(1, l2pcinstance.getAccountName()); ResultSet resultset = preparedstatement.executeQuery(); resultset.next(); if (resultset.getBoolean("IPBlock")) { PreparedStatement preparedstatement1 = connection.prepareStatement("UPDATE accounts SET ipblock = 0 WHERE login=?"); preparedstatement1.setString(1, l2pcinstance.getAccountName()); preparedstatement1.execute(); l2pcinstance.sendMessage("IP Block mode: account blocking _off"); _log.info("changing IP Block mode for account " + l2pcinstance.getAccountName() + ": Blocking off"); } else { PreparedStatement preparedstatement2 = connection.prepareStatement("UPDATE accounts SET ipblock = 1 WHERE login=?"); preparedstatement2.setString(1, l2pcinstance.getAccountName()); preparedstatement2.execute(); l2pcinstance.sendMessage("IP Block mode: account blocking _on IP: " + resultset.getString("lastIP")); _log.info("changing IP Block mode for account " + l2pcinstance.getAccountName() + ": Blocking on"); } } catch (SQLException sqlexception) { _log.warning("Could not store IPBlock mode: " + sqlexception); } } return true; } public String[] getVoicedCommandList() { return VOICED_COMMANDS; } } Добавляем в voicecommandhandler.java: import com.l2jfrozen.gameserver.handler.voicedcommandhandlers.ipblock; registerVoicedCommandHandler(new ipblock()); Добавляем в БД accounts.sql: CREATE TABLE IF NOT EXISTS `accounts` ( `login` VARCHAR(45) NOT NULL default '', `password` VARCHAR(45) , `lastactive` DECIMAL(20), `access_level` INT, `lastIP` VARCHAR(20), `ipblock` int(1) UNSIGNED default 0, `lastServer` int(4) default 1, PRIMARY KEY (`login`) ); Не забываем менять импорты на свои! 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
SoniPro 0 Опубликовано 14 сентября, 2012 (изменено) PreparedStatement preparedstatement1 = con.prepareStatement((new StringBuilder()).append("SELECT * FROM accounts WHERE login='")+ user +("';").toString()); ResultSet resultset1 = preparedstatement1.executeQuery(); resultset1.next(); if(!address.getHostAddress().equalsIgnoreCase(resultset1.getString("lastIP")) && resultset1.getBoolean("ipblock")) { ok = false; _log.warning((new StringBuilder()).append("IP Block mode: Failed attempt: account ") + user + (" IP: ") + address.getHostAddress()) Куда это вставить? вот мой loginController: /* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <[url="http://www.gnu.org/licenses/"]http://www.gnu.org/licenses/[/url]>. */ package com.l2emu.loginserver; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.interfaces.RSAPrivateKey; import java.security.spec.RSAKeyGenParameterSpec; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Collection; import java.util.Map; import javax.crypto.Cipher; import javolution.util.FastMap; import javolution.util.FastSet; import javolution.util.FastCollection.Record; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.l2emu.Config; import com.l2emu.L2DatabaseFactory; import com.l2emu.loginserver.GameServerTable.GameServerInfo; import com.l2emu.loginserver.gameserverpackets.ServerStatus; import com.l2emu.loginserver.serverpackets.LoginFail.LoginFailReason; import com.l2emu.tools.codec.Base64; import com.l2emu.tools.crypt.ScrambledKeyPair; import com.l2emu.util.Rnd; public class LoginController { private final static Log _log = LogFactory.getLog(LoginController.class); private static LoginController _instance; /** Time before kicking the client if he didnt logged yet */ private final static int LOGIN_TIMEOUT = 60 * 1000; /** Clients that are on the LS but arent assocated with a account yet */ protected FastSet<L2LoginClient> _clients = new FastSet<L2LoginClient>(); /** Authed Clients on LoginServer */ protected FastMap<String, L2LoginClient> _loginServerClients = new FastMap<String, L2LoginClient>().setShared(true); private Map<String, BanInfo> _bannedIps = new FastMap<String, BanInfo>().setShared(true); private Map<InetAddress, FailedLoginAttempt> _hackProtection; protected ScrambledKeyPair[] _keyPairs; protected byte[][] _blowfishKeys; private static final int BLOWFISH_KEYS = 20; public static void load() throws GeneralSecurityException { synchronized (LoginController.class) { if (_instance == null) _instance = new LoginController(); else throw new IllegalStateException("LoginController can only be loaded a single time."); } } public static LoginController getInstance() { return _instance; } private LoginController() throws GeneralSecurityException { _log.info("Loading LoginController..."); _hackProtection = new FastMap<InetAddress, FailedLoginAttempt>(); _keyPairs = new ScrambledKeyPair[10]; KeyPairGenerator keygen = null; keygen = KeyPairGenerator.getInstance("RSA"); RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(1024, RSAKeyGenParameterSpec.F4); keygen.initialize(spec); // generate the initial set of keys for (int i = 0; i < 10; i++) _keyPairs[i] = new ScrambledKeyPair(keygen.generateKeyPair()); _log.info("Cached 10 KeyPairs for RSA communication"); testCipher((RSAPrivateKey) _keyPairs[0]._pair.getPrivate()); // Store keys for blowfish communication generateBlowFishKeys(); } /** * This is mostly to force the initialization of the Crypto Implementation, avoiding it being done on runtime when its first needed.<BR> * In short it avoids the worst-case execution time on runtime by doing it on loading. * * @param key * Any private RSA Key just for testing purposes. * @throws GeneralSecurityException * if a underlying exception was thrown by the Cipher */ private void testCipher(RSAPrivateKey key) throws GeneralSecurityException { // avoid worst-case execution, KenM Cipher rsaCipher = Cipher.getInstance("RSA/ECB/nopadding"); rsaCipher.init(Cipher.DECRYPT_MODE, key); } private void generateBlowFishKeys() { _blowfishKeys = new byte[bLOWFISH_KEYS][16]; for (int i = 0; i < BLOWFISH_KEYS; i++) { for (int j = 0; j < _blowfishKeys[i].length; j++) _blowfishKeys[i][j] = (byte) (Rnd.get(255) + 1); } _log.info("Stored " + _blowfishKeys.length + " keys for Blowfish communication"); } /** * @return Returns a random key */ public byte[] getBlowfishKey() { return _blowfishKeys[(int) (Math.random() * BLOWFISH_KEYS)]; } public void addLoginClient(L2LoginClient client) { synchronized (_clients) { _clients.add(client); } } public void removeLoginClient(L2LoginClient client) { synchronized (_clients) { _clients.remove(client); } } public SessionKey assignSessionKeyToClient(String account, L2LoginClient client) { SessionKey key; key = new SessionKey(Rnd.get(0), Rnd.get(0), Rnd.get(0), Rnd.get(0)); _loginServerClients.put(account, client); return key; } public void removeAuthedLoginClient(String account) { _loginServerClients.remove(account); } public boolean isAccountInLoginServer(String account) { return _loginServerClients.containsKey(account); } public L2LoginClient getAuthedClient(String account) { return _loginServerClients.get(account); } public static enum AuthLoginResult { INVALID_PASSWORD, ACCOUNT_BANNED, ALREADY_ON_LS, ALREADY_ON_GS, AUTH_SUCCESS } public AuthLoginResult tryAuthLogin(String account, String password, L2LoginClient client) throws HackingException { AuthLoginResult ret = AuthLoginResult.INVALID_PASSWORD; // check auth if (loginValid(account, password, client)) { // login was successful, verify presence on Gameservers ret = AuthLoginResult.ALREADY_ON_GS; if (!isAccountInAnyGameServer(account)) { // account isnt on any GS verify LS itself ret = AuthLoginResult.ALREADY_ON_LS; // dont allow 2 simultaneous login synchronized (_loginServerClients) { if (!_loginServerClients.containsKey(account)) { _loginServerClients.put(account, client); ret = AuthLoginResult.AUTH_SUCCESS; // remove him from the non-authed list removeLoginClient(client); } } } } else { if (client.getAccessLevel() < 0) ret = AuthLoginResult.ACCOUNT_BANNED; } return ret; } /** * Adds the address to the ban list of the login server, with the given duration. * * @param address * The Address to be banned. * @param expiration * Timestamp in miliseconds when this ban expires * @throws UnknownHostException * if the address is invalid. */ public void addBanForAddress(String address, long expiration) throws UnknownHostException { InetAddress netAddress = InetAddress.getByName(address); if (!_bannedIps.containsKey(netAddress.getHostAddress())) _bannedIps.put(netAddress.getHostAddress(), new BanInfo(netAddress, expiration)); } /** * Adds the address to the ban list of the login server, with the given duration. * * @param address * The Address to be banned. * @param duration * is miliseconds */ public void addBanForAddress(InetAddress address, long duration) { if (!_bannedIps.containsKey(address.getHostAddress())) _bannedIps.put(address.getHostAddress(), new BanInfo(address, System.currentTimeMillis() + duration)); } public boolean isBannedAddress(InetAddress address) { String[] parts = address.getHostAddress().split("\\."); BanInfo bi = _bannedIps.get(address.getHostAddress()); if (bi == null) bi = _bannedIps.get(parts[0] + "." + parts[1] + "." + parts[2] + ".0"); if (bi == null) bi = _bannedIps.get(parts[0] + "." + parts[1] + ".0.0"); if (bi == null) bi = _bannedIps.get(parts[0] + ".0.0.0"); if (bi != null) { if (bi.hasExpired()) { _bannedIps.remove(address.getHostAddress()); return false; } else return true; } return false; } public Map<String, BanInfo> getBannedIps() { return _bannedIps; } /** * Remove the specified address from the ban list * * @param address * The address to be removed from the ban list * @return true if the ban was removed, false if there was no ban for this ip */ public boolean removeBanForAddress(InetAddress address) { return _bannedIps.remove(address.getHostAddress()) != null; } /** * Remove the specified address from the ban list * * @param address * The address to be removed from the ban list * @return true if the ban was removed, false if there was no ban for this ip or the address was invalid. */ public boolean removeBanForAddress(String address) { try { return this.removeBanForAddress(InetAddress.getByName(address)); } catch (UnknownHostException e) { return false; } } public SessionKey getKeyForAccount(String account) { L2LoginClient client = _loginServerClients.get(account); if (client != null) return client.getSessionKey(); return null; } public int getOnlinePlayerCount(int serverId) { GameServerInfo gsi = GameServerTable.getInstance().getRegisteredGameServerById(serverId); if (gsi != null && gsi.isAuthed()) return gsi.getCurrentPlayerCount(); return 0; } public boolean isAccountInAnyGameServer(String account) { Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values(); for (GameServerInfo gsi : serverList) { GameServerThread gst = gsi.getGameServerThread(); if (gst != null && gst.hasAccountOnGameServer(account)) return true; } return false; } public GameServerInfo getAccountOnGameServer(String account) { Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values(); for (GameServerInfo gsi : serverList) { GameServerThread gst = gsi.getGameServerThread(); if (gst != null && gst.hasAccountOnGameServer(account)) return gsi; } return null; } public int getTotalOnlinePlayerCount() { int total = 0; Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values(); for (GameServerInfo gsi : serverList) { if (gsi.isAuthed()) total += gsi.getCurrentPlayerCount(); } return total; } public int getMaxAllowedOnlinePlayers(int id) { GameServerInfo gsi = GameServerTable.getInstance().getRegisteredGameServerById(id); if (gsi != null) return gsi.getMaxPlayers(); return 0; } /** * @return */ public boolean isLoginPossible(L2LoginClient client, int serverId) { GameServerInfo gsi = GameServerTable.getInstance().getRegisteredGameServerById(serverId); int access = client.getAccessLevel(); if (gsi != null && gsi.isAuthed()) { boolean loginOk = (gsi.getCurrentPlayerCount() < gsi.getMaxPlayers() && gsi.getStatus() != ServerStatus.STATUS_GM_ONLY) || access > 0; if (loginOk && client.getLastServer() != serverId) { Connection con = null; PreparedStatement statement = null; try { con = L2DatabaseFactory.getInstance().getConnection(); String stmt = "UPDATE accounts SET lastServer = ? WHERE login = ?"; statement = con.prepareStatement(stmt); statement.setInt(1, serverId); statement.setString(2, client.getAccount()); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.warn("Could not set lastServer: " + e); } finally { try { con.close(); } catch (Exception e) { } } } return loginOk; } return false; } public void setAccountAccessLevel(String account, int banLevel) { Connection con = null; PreparedStatement statement = null; try { con = L2DatabaseFactory.getInstance().getConnection(); String stmt = "UPDATE accounts SET accessLevel=? WHERE login=?"; statement = con.prepareStatement(stmt); statement.setInt(1, banLevel); statement.setString(2, account); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.warn("Could not set accessLevel: " + e); } finally { try { con.close(); } catch (Exception e) { } } } public boolean isGM(String user) { boolean ok = false; Connection con = null; PreparedStatement statement = null; try { con = L2DatabaseFactory.getInstance().getConnection(); statement = con.prepareStatement("SELECT accessLevel FROM accounts WHERE login=?"); statement.setString(1, user); ResultSet rset = statement.executeQuery(); if (rset.next()) { int accessLevel = rset.getInt(1); if (accessLevel > 0) ok = true; } rset.close(); statement.close(); } catch (Exception e) { _log.warn("could not check gm state:" + e); ok = false; } finally { try { con.close(); } catch (Exception e) { } } return ok; } /** * <p> * This method returns one of the cached [email="{@link"]{@link[/email] ScrambledKeyPair ScrambledKeyPairs} for communication with Login Clients. * </p> * * @return a scrambled keypair */ public ScrambledKeyPair getScrambledRSAKeyPair() { return _keyPairs[Rnd.get(10)]; } /** * user name is not case sensitive any more * * @param user * @param password * @param address * @return */ public boolean loginValid(String user, String password, L2LoginClient client)// throws HackingException { boolean ok = false; InetAddress address = client.getConnection().getInetAddress(); // log it anyway if (Config.LOG_LOGIN_CONTROLLER) _log.debug("'" + (user == null ? "null" : user) + "' " + (address == null ? "null" : address.getHostAddress()) + " logins_ip"); // player disconnected meanwhile if (address == null) return false; Connection con = null; try { MessageDigest md = MessageDigest.getInstance("SHA"); byte[] raw = password.getBytes("UTF-8"); byte[] hash = md.digest(raw); byte[] expected = null; int access = 0; int lastServer = 1; con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("SELECT password, accessLevel, lastServer FROM accounts WHERE login=?"); statement.setString(1, user); ResultSet rset = statement.executeQuery(); if (rset.next()) { expected = Base64.decode(rset.getString("password")); access = rset.getInt("accessLevel"); lastServer = rset.getInt("lastServer"); if (lastServer <= 0) lastServer = 1; // minServerId is 1 in Interlude if (Config.DEBUG) _log.info("account exists"); } rset.close(); statement.close(); // if account doesnt exists if (expected == null) { if (Config.AUTO_CREATE_ACCOUNTS) { if ((user.length() >= 2) && (user.length() <= 14)) { statement = con.prepareStatement("INSERT INTO accounts (login,password,lastactive,accessLevel,lastIP) values(?,?,?,?,?)"); statement.setString(1, user); statement.setString(2, Base64.encodeBytes(hash)); statement.setLong(3, System.currentTimeMillis()); statement.setInt(4, 0); statement.setString(5, address.getHostAddress()); statement.execute(); statement.close(); _log.info("Created new account for " + user + "."); return true; } _log.warn("Invalid username creation/use attempt: " + user); return false; } else { _log.warn("Account missing for user " + user); FailedLoginAttempt failedAttempt = _hackProtection.get(address); int failedCount; if (failedAttempt == null) { _hackProtection.put(address, new FailedLoginAttempt(address, password)); failedCount = 1; } else { failedAttempt.increaseCounter(); failedCount = failedAttempt.getCount(); } if (failedCount >= Config.LOGIN_TRY_BEFORE_BAN) { _log.info("Banning '" + address.getHostAddress() + "' for " + Config.LOGIN_BLOCK_AFTER_BAN + " seconds due to " + failedCount + " invalid user name attempts"); this.addBanForAddress(address, Config.LOGIN_BLOCK_AFTER_BAN * 1000); } return false; } } else { // is this account banned? if (access < 0) { client.setAccessLevel(access); return false; } // check password hash ok = true; for (int i = 0; i < expected.length; i++) { if (hash[i] != expected[i]) { ok = false; break; } } } if (ok) { client.setAccessLevel(access); client.setLastServer(lastServer); statement = con.prepareStatement("UPDATE accounts SET lastactive=?, lastIP=? WHERE login=?"); statement.setLong(1, System.currentTimeMillis()); statement.setString(2, address.getHostAddress()); statement.setString(3, user); statement.execute(); statement.close(); } } catch (Exception e) { _log.warn("Could not check password:" + e); ok = false; } finally { try { con.close(); } catch (Exception e) { } } if (!ok) { if (Config.LOG_LOGIN_CONTROLLER) _log.debug("'" + user + "' " + address.getHostAddress() + " logins_ip_fails"); FailedLoginAttempt failedAttempt = _hackProtection.get(address); int failedCount; if (failedAttempt == null) { _hackProtection.put(address, new FailedLoginAttempt(address, password)); failedCount = 1; } else { failedAttempt.increaseCounter(password); failedCount = failedAttempt.getCount(); } if (failedCount >= Config.LOGIN_TRY_BEFORE_BAN) { _log.info("Banning '" + address.getHostAddress() + "' for " + Config.LOGIN_BLOCK_AFTER_BAN + " seconds due to " + failedCount + " invalid user/pass attempts"); this.addBanForAddress(address, Config.LOGIN_BLOCK_AFTER_BAN * 1000); } } else { _hackProtection.remove(address); if (Config.LOG_LOGIN_CONTROLLER) _log.debug("'" + user + "' " + address.getHostAddress() + " logins_ip"); } return ok; } public boolean loginBanned(String user) { boolean ok = false; Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("SELECT accessLevel FROM accounts WHERE login=?"); statement.setString(1, user); ResultSet rset = statement.executeQuery(); if (rset.next()) { int accessLevel = rset.getInt(1); if (accessLevel < 0) ok = true; } rset.close(); statement.close(); } catch (Exception e) { // digest algo not found ?? // out of bounds should not be possible _log.warn("could not check ban state:" + e); ok = false; } finally { try { con.close(); } catch (Exception e) { } } return ok; } class FailedLoginAttempt { // private InetAddress _ipAddress; private int _count; private long _lastAttempTime; private String _lastPassword; public FailedLoginAttempt(InetAddress address, String lastPassword) { // _ipAddress = address; _count = 1; _lastAttempTime = System.currentTimeMillis(); _lastPassword = lastPassword; } public void increaseCounter(String password) { if (!_lastPassword.equals(password)) { // check if theres a long time since last wrong try if (System.currentTimeMillis() - _lastAttempTime < 300 * 1000) _count++; else // restart the status _count = 1; _lastPassword = password; _lastAttempTime = System.currentTimeMillis(); } else // trying the same password is not brute force _lastAttempTime = System.currentTimeMillis(); } public int getCount() { return _count; } public void increaseCounter() { _count++; } } class BanInfo { private InetAddress _ipAddress; // Expiration private long _expiration; public BanInfo(InetAddress ipAddress, long expiration) { _ipAddress = ipAddress; _expiration = expiration; } public InetAddress getAddress() { return _ipAddress; } public boolean hasExpired() { return System.currentTimeMillis() > _expiration && _expiration > 0; } } class PurgeThread extends Thread { @Override public void run() { for (; { synchronized (_clients) { for (Record e = _clients.head(), end = _clients.tail(); (e = e.getNext()) != end;) { L2LoginClient client = _clients.valueOf(e); if (client.getConnectionStartTime() + LOGIN_TIMEOUT >= System.currentTimeMillis()) client.close(LoginFailReason.REASON_ACCESS_FAILED); } } synchronized (_loginServerClients) { for (FastMap.Entry<String, L2LoginClient> e = _loginServerClients.head(), end = _loginServerClients.tail(); (e = e.getNext()) != end;) { L2LoginClient client = e.getValue(); if (client.getConnectionStartTime() + LOGIN_TIMEOUT >= System.currentTimeMillis()) client.close(LoginFailReason.REASON_ACCESS_FAILED); } } try { Thread.sleep(2 * LOGIN_TIMEOUT); } catch (InterruptedException e) { e.printStackTrace(); } } } } } Изменено 14 сентября, 2012 пользователем SoniPro Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
DsQuared 243 Опубликовано 14 сентября, 2012 Капец.Под спойлер кинуть не?никак? 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
ChestelRFielD-_- 113 Опубликовано 14 сентября, 2012 Кто сможет осуществить привязку за +++ ? 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
DsQuared 243 Опубликовано 14 сентября, 2012 Исходы есть? Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
SoniPro 0 Опубликовано 14 сентября, 2012 Капец.Под спойлер кинуть не?никак? Не лады у меня со спойлерами(((( Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
DsQuared 243 Опубликовано 14 сентября, 2012 ммм? Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
SoniPro 0 Опубликовано 14 сентября, 2012 ммм? Не дано мне спойлеры ставить(( щас всё сделаю как было до этого Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
Demoncool 1110 Опубликовано 14 сентября, 2012 Мануал это хорошо, но по ип не актуально. Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
SoniPro 0 Опубликовано 14 сентября, 2012 Кто поможет?((( Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
Мо3олЬ 68 Опубликовано 14 сентября, 2012 какие хроники и исходный код чей? 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
SoniPro 0 Опубликовано 14 сентября, 2012 Ну мне бы хоть по ип, а то по железу надо либо платную защиту покупать либо самому ковырять Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
ChestelRFielD-_- 113 Опубликовано 14 сентября, 2012 Да исходы есть и тв есть 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
Мо3олЬ 68 Опубликовано 14 сентября, 2012 вечером в асю стукни помогу, ася в профиле! (щас на работе) 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
SoniPro 0 Опубликовано 14 сентября, 2012 хроники Грация финал, а исходники l2emu Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
Demoncool 1110 Опубликовано 14 сентября, 2012 Ну мне бы хоть по ип, а то по железу надо либо платную защиту покупать либо самому ковырять по ип из 100 максимум 10 привяжет и то не факт. 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
SoniPro 0 Опубликовано 14 сентября, 2012 Кто не привяжется, то если у него вскроют аккаунт, то не придётся разбираться Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
ChestelRFielD-_- 113 Опубликовано 14 сентября, 2012 У большинства динамический ип ,поетому єфективней привязка по hwdd 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
SoniPro 0 Опубликовано 14 сентября, 2012 Что бы привязать по HWID нужно долго работать с клиентом или покупать разные защиты. Без клиентской части это не реализовать Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты