Aller au contenu

Nouveau membre ?! Pense à te présenter pour accéder au contenu du forum !

New member ?! Introduce yourself to get access to the forum !

Takuma

 tutoriel / partage [Partie 2] Développons un anti-cheat en Python

Messages recommandés

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.

Partager ce message


Lien à poster
Partager sur d’autres sites

Wallah.

Partager ce message


Lien à poster
Partager sur d’autres sites

gg une protection aussi efficace que durex

Partager ce message


Lien à poster
Partager sur d’autres sites

Ouais mais des fois ça craque....

Partager ce message


Lien à poster
Partager sur d’autres sites

Tutoriel fort utile, cependant je déconseille l'utilisation du md5 qui est déprécié, utilisez plus le sha512 ou un autre algorithme de cyrpt qui est à jour est non déprécié.

Partager ce message


Lien à poster
Partager sur d’autres sites

Tu as raison, mais bon... Le risque de collision avec un cheat en plus est quasi nul....

 

sha512 on n'a pour l'instant pas trouvé de collision, rien ne prouve qu'il n'est n'existe pas non plus :3

Partager ce message


Lien à poster
Partager sur d’autres sites

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 !

Partager ce message


Lien à poster
Partager sur d’autres sites

Je compte faire du cs plus tard je publierais des launchers. Enfin si le forum est toujours debout....

Partager ce message


Lien à poster
Partager sur d’autres sites

Je valide & Déplace.

Merci à toi ! :)

Partager ce message


Lien à poster
Partager sur d’autres sites

Vas falloir que tu répares mes 150 tutos @Calypso Je t'aurais bien aidé, mais je peux pas. Là ils sont totalement inutile. Soit tu les répares, soit je les répare, soit tu les archives, mais ils servent clairement à rien là.

Partager ce message


Lien à poster
Partager sur d’autres sites

Propose une version corrigé ici et on les éditera nous.

Partager ce message


Lien à poster
Partager sur d’autres sites

J'ai pas la motivation. Faudra les refaire ;) Ça va y'en a que 150 courage ;) Là ça pollue le forum.

Partager ce message


Lien à poster
Partager sur d’autres sites

×

Information importante

By using this site, you agree to our Conditions d’utilisation.