Aller au contenu
Top-Metin2.org - Vous êtes à la recherche d'un serveur Metin 2 ? ×
×
×
  • Créer...

Python - 2: Développons un anti-cheat en Python


Messages recommandés

  • Robot

Bonjour.

 

 

Ce tutoriel est la suite de celui-ci : 

 

Vous pouvez bien sûr prendre la série à n'importe quel tutoriel, mais, je vous conseille de tout lire, ça ne peut que vous aider à mieux comprendre le code et la syntaxe.

 

Bon développement  !

 

 


 

Aujourd'hui, je voudrais que l'on voit ensemble les hash en Python.

D'abord, je voudrais vous expliquer ce qu'est un hash.

 

 

Un hash cryptographique, en gros c'est une série de chiffres et de lettres générée par une fonction faite pour ça. Ce n'est rien d'autre q'un algorithme ou une série d'opérations mathématiques. On distingue plusieurs langue de cryptographie. Dans ce tutoriel, nous allons utiliser celle nommée "md5".

 

 

Bien, commençons !

Ouvrons toujours notre fichier (takuma.py pour moi).

Je vous propose d'ajouter sur les imports ce bout de code :

__doc__ = """Module criptyage ci-dessous :"""
__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
 
algorithms_guaranteed = set(__always_supported)
algorithms_available = set(__always_supported)
 
algorithms = __always_supported
 
__all__ = __always_supported + ('new', 'algorithms_guaranteed',
                                'algorithms_available', 'algorithms',
                                'pbkdf2_hmac')
 
 
def __get_builtin_constructor(name):
    try:
        if name in ('SHA1', 'sha1'):
            import _sha
            return _sha.new
        elif name in ('MD5', 'md5'):
            import _md5
            return _md5.new
        elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'):
            import _sha256
            bs = name[3:]
            if bs == '256':
                return _sha256.sha256
            elif bs == '224':
                return _sha256.sha224
        elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'):
            import _sha512
            bs = name[3:]
            if bs == '512':
                return _sha512.sha512
            elif bs == '384':
                return _sha512.sha384
    except ImportError:
        pass
 
    raise ValueError('unsupported hash type ' + name)
 
 
def __get_openssl_constructor(name):
    try:
        f = getattr(_hashlib, 'openssl_' + name)
        f()
        return f
    except (AttributeError, ValueError):
        return __get_builtin_constructor(name)
 
 
def __py_new(name, string=''):
    return __get_builtin_constructor(name)(string)
 
 
def __hash_new(name, string=''):
    try:
        return _hashlib.new(name, string)
    except ValueError:
        return __get_builtin_constructor(name)(string)
 
 
try:
    import _hashlib
    new = __hash_new
    __get_hash = __get_openssl_constructor
    algorithms_available = algorithms_available.union(
        _hashlib.openssl_md_meth_names)
except ImportError:
    new = __py_new
    __get_hash = __get_builtin_constructor
 
for __func_name in __always_supported:
    try:
        globals()[__func_name] = __get_hash(__func_name)
    except ValueError:
        import logging
        logging.exception('code for hash %s was not found.', __func_name)
 
 
try:
    from _hashlib import pbkdf2_hmac
except ImportError:
    import binascii
    import struct
 
    _trans_5C = b"".join(chr(x ^ 0x5C) for x in range(256))
    _trans_36 = b"".join(chr(x ^ 0x36) for x in range(256))
 
    def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
        if not isinstance(hash_name, str):
            raise TypeError(hash_name)
 
        if not isinstance(password, (bytes, bytearray)):
            password = bytes(buffer(password))
        if not isinstance(salt, (bytes, bytearray)):
            salt = bytes(buffer(salt))
        inner = new(hash_name)
        outer = new(hash_name)
        blocksize = getattr(inner, 'block_size', 64)
        if len(password) > blocksize:
            password = new(hash_name, password).digest()
        password = password + b'\x00' * (blocksize - len(password))
        inner.update(password.translate(_trans_36))
        outer.update(password.translate(_trans_5C))
 
        def prf(msg, inner=inner, outer=outer):
            icpy = inner.copy()
            ocpy = outer.copy()
            icpy.update(msg)
            ocpy.update(icpy.digest())
            return ocpy.digest()
 
        if iterations < 1:
            raise ValueError(iterations)
        if dklen is None:
            dklen = outer.digest_size
        if dklen < 1:
            raise ValueError(dklen)
 
        hex_format_string = "%%0%ix" % (new(hash_name).digest_size * 2)
 
        dkey = b''
        loop = 1
        while len(dkey) < dklen:
            prev = prf(salt + struct.pack(b'>I', loop))
            rkey = int(binascii.hexlify(prev), 16)
            for i in xrange(iterations - 1):
                prev = prf(prev)
                rkey ^= int(binascii.hexlify(prev), 16)
            loop += 1
            dkey += binascii.unhexlify(hex_format_string % rkey)
 
        return dkey[:dklen]
del __always_supported, __func_name, __get_hash
del __py_new, __hash_new, __get_openssl_constructor
new = md5
blocksize = 1
digest_size = 16
def sumFileMD5(filePath):
    fichier = open(filePath, 'r')
    c = md5()
    while 1:
     	try:
            d = fichier.next()
            c.update(d)
        except: break
    fichier.close()
    return c.hexdigest()
Avec les tabulations :

 

https://pastebin.com/Ryv08UJj

 

 

Bien, je ne vais pas tout vous expliquer en gros vous venez d'ajouter le code d'une libs légèrement modifiée, je pouvais pas faire plus simple que tout vous servir comme ça !

Bien, maintenant notre but va être de "scanner" les fichiers ce trouvant dans le dossier "miles".

On va donc récupérer le hash md5 de chaque fichier.

 

Pour cela, créons une variable, par exemple VerifA qui va contenir le hash.

Pour hash, nous utilisons ici la fonction "sumFileMD5" avec le contenue à hash, ici nous allons mettre la destination de nos fichier par rapport aux client, cela donne :

 

VerifA = sumFileMD5('miles/mssa3d.m3d')
 

On fait de même pour tous ce qui donne au final :

 

VerifA = sumFileMD5('miles/mssa3d.m3d')
VerifB = sumFileMD5('miles/mssds3d.m3d')
VerifC = sumFileMD5('miles/mssdsp.flt')
VerifD = sumFileMD5('miles/mssdx7.m3d')
VerifE = sumFileMD5('miles/msseax.m3d')
VerifF = sumFileMD5('miles/mssmp3.asi')
VerifG = sumFileMD5('miles/mssrsx.m3d')
VerifH = sumFileMD5('miles/msssoft.m3d')
VerifI = sumFileMD5('miles/mssvoice.asi')
 

Bien, maintenant on va devoir vérifier que nos fichiers sont intacte, soit qu'ils ont le bon hash.

Pas besoin de launcher, de toute façon, ces fichiers n'ont jamais changé.

Nous créons donc une condition :

 

Si VerifA est différent de "HashOriginal":

 

    -Message d'erreur

    -On coupe le client.

 

Et on va faire ça pour tous, je vous montre pour un, et je vous donne après le code pour tous.

Si vous ne savez pas faire une condition, vous trouverez des tutoriels partout même ici. il vous manque peut être :

 

    -"différent de" = "!="

    -Afficher une erreur "dbg.LogBox(message)"

    -Fermer le client : "app.Abort()"

 

Cela donne pour VerifA :

if VerifA != '7fa65499747607679068ce32e57735c4':
	dbg.LogBox("Une erreur est survenue le fichier : mssa3d.m3d est corrompu !")
	app.Abort()
 

Je vous donne donc pour tous, pour pas que vous n'ayez à faire les hash vous même :

 

if VerifA != '7fa65499747607679068ce32e57735c4':
    dbg.LogBox("Une erreur est survenue le fichier : mssa3d.m3d est corrompu !")
    app.Abort()
if VerifB != 'c57b37054d90043f160e6c2fdc3a7dd7':
    dbg.LogBox("Une erreur est survenue le fichier : mssds3d.m3d est corrompu !")
    app.Abort()
if VerifC != 'c11063f4ab4dda5be827af3be724eaf0':
    dbg.LogBox("Une erreur est survenue le fichier : mssdsp.flt est corrompu !")
    app.Abort()
if VerifD != '7a76fb64014e9451b94efb1a70b40504':
    dbg.LogBox("Une erreur est survenue le fichier : mssdx7.m3d est corrompu !")
    app.Abort()
if VerifE != '09e7dd8b2a2059877dceae4bfcb7a4df':
    dbg.LogBox("Une erreur est survenue le fichier : msseax.m3d est corrompu !")
    app.Abort()
if VerifF != 'cdc0107209b266753a296046c7405c0b':
    dbg.LogBox("Une erreur est survenue le fichier : mssmp3.asi est corrompu !")
    app.Abort()
if VerifG != '15451f0091181dfd3eb3cb381fc37e0f':
    dbg.LogBox("Une erreur est survenue le fichier : mssrsx.m3d est corrompu !")
    app.Abort()
if VerifH != '605acc1aa4b0c0e731cfd764e0030d97':
    dbg.LogBox("Une erreur est survenue le fichier : msssoft.m3d est corrompu !")
    app.Abort()
if VerifI != '2c5656eebd71f8ededbda199921a918a':
    dbg.LogBox("Une erreur est survenue le fichier : mssvoice.asi est corrompu !")
    app.Abort()
 

Avec les tabulations :

https://pastebin.com/8iGVaQ0M

 

 

Voilà, c'était court, mais efficace. Votre dossier miles est maintenant entièrement protégé. Vous n'avez changer n'importe quoi avec un éditeur hexadécimal sur vos fichiers pour voir que votre client ne démarrera pas ! 

Cordialement, Takuma.

  • Metin2 Dev 1
  • Love 5

french_banner.gif

Lien vers le commentaire
Partager sur d’autres sites

  • Funkiest

J'aime bien ce genre "d'anticheat" bien qu'il ne vérifie que les fichiers, d'ailleurs, comme Manghao l'a dit, le md5 reste un peu déprécié, bien que ça ne pose pas réellement de problème.

 

Là où le bat blesse, c'est que vu que c'est en python, la possibilité de bypass est plus grande, sans parler du fait que les miles sont assez rarement tronquées, les exploits à base de .mix étant plus répandus.

 

Je précise aussi que n'importe quel patcheur de mise à jour automatique permet de faire pareil AVANT le lancement du jeu (pour les clients qui forcent l'utilisation du patcheur).

 

Mais merci de ce tutoriel, c'est très utile et ça fonctionne !

Lien vers le commentaire
Partager sur d’autres sites

  • 2 semaines après...
  • 2 mois après...


  • brilliantdiscord_widget
  • Flux d'Activité

    1. 37
    2. 21

      Metin2 en 2020 peut-on en parler?

    3. 0

      METIN2Project

    4. 3

      Ressources - UnPack - Metin2 Client - Officiel

    5. 0

      Barre des tâches d'argent étendue

    6. 16

      Redémarrage automatique des channels

    7. 16

      Multi Logo GM / SGM / GA

  • En ligne récemment

    • Aucun utilisateur enregistré regarde cette page.

Information importante

Conditions d’utilisation / Politique de confidentialité / Règles / Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer.