Comment créer un changeur d'adresse MAC en Python

Par : Géry Menye, le 10 Juillet 2022

L'adresse MAC est un identifiant unique attribué à chaque interface réseau de tout appareil qui se connecte à un réseau. La modification de cette adresse présente de nombreux avantages, notamment la prévention du blocage des adresses MAC. Si votre adresse MAC est bloquée sur un point d'accès, il vous suffit de la modifier pour continuer à utiliser ce réseau.

Dans ce tutoriel, vous apprendrez à modifier votre adresse MAC dans les environnements Windows et Linux à l'aide de Python.

Nous n'avons pas besoin d'installer quoi que ce soit, car nous utiliserons le module subprocess de Python en interaction avec la commande ifconfig sous Linux et les commandes getmac, reg et wmic sous Windows.

 

 

 Modification de l'adresse MAC sous Linux

Pour commencer, ouvrez un nouveau fichier Python et importez les bibliothèques suivantes :

import re
import string
import random
import subprocess

Dans notre programme, nous aurons le choix de générer une nouvelle adresse MAC de façon aléatoire ou de la remplacer par une adresse spécifiée. Par conséquent, créons une fonction pour générer et retourner une adresse MAC :

def get_random_mac_address():
    """Generate and return a MAC address in the format of Linux"""
    # get the hexdigits uppercased
    uppercased_hexdigits = ''.join(set(string.hexdigits.upper()))
    # 2nd character must be 0, 2, 4, 6, 8, A, C, or E
    mac = ""
    for i in range(6):
        for j in range(2):
            if i == 0:
                mac += random.choice("02468ACE")
            else:
                mac += random.choice(uppercased_hexdigits)
        mac += ":"
    return mac.strip(":")

Nous utilisons le module string pour obtenir les caractères hexadécimaux utilisés dans les adresses MAC ; nous supprimons les caractères minuscules et utilisons le module random pour extraire ces caractères.

Ensuite, créons une autre fonction qui utilise la commande ifconfig pour obtenir l'adresse MAC actuelle de notre machine :

def get_current_mac_address(iface):
    # use the ifconfig command to get the interface details, including the MAC address
    output = subprocess.check_output(f"ifconfig {iface}", shell=True).decode()
    return re.search("ether (.+) ", output).group().split()[1].strip()

Nous utilisons la fonction check_output() du module subprocess qui exécute la commande sur le shell par défaut et renvoie le résultat de la commande.

L'adresse MAC est située juste après le mot "ether", nous utilisons la méthode re.search() pour la récupérer.

Maintenant que nous avons nos utilitaires, créons la fonction principale pour changer l'adresse MAC :

def change_mac_address(iface, new_mac_address):
    # disable the network interface
    subprocess.check_output(f"ifconfig {iface} down", shell=True)
    # change the MAC
    subprocess.check_output(f"ifconfig {iface} hw ether {new_mac_address}", shell=True)
    # enable the network interface again
    subprocess.check_output(f"ifconfig {iface} up", shell=True)

Assez simple, la fonction change_mac_address() accepte l'interface et la nouvelle adresse MAC comme paramètres. Elle désactive l'interface, change l'adresse MAC, et la réactive.

Maintenant que nous avons tout, utilisons le module argparse pour terminer notre script :

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Python Mac Changer on Linux")
    parser.add_argument("interface", help="The network interface name on Linux")
    parser.add_argument("-r", "--random", action="store_true", help="Whether to generate a random MAC address")
    parser.add_argument("-m", "--mac", help="The new MAC you want to change to")
    args = parser.parse_args()
    iface = args.interface
    if args.random:
        # if random parameter is set, generate a random MAC
        new_mac_address = get_random_mac_address()
    elif args.mac:
        # if mac is set, use it instead
        new_mac_address = args.mac
    # get the current MAC address
    old_mac_address = get_current_mac_address(iface)
    print("[*] Old MAC address:", old_mac_address)
    # change the MAC address
    change_mac_address(iface, new_mac_address)
    # check if it's really changed
    new_mac_address = get_current_mac_address(iface)
    print("[+] New MAC address:", new_mac_address)

Nous avons un total de trois paramètres à passer à ce script :

  • interface : Le nom de l'interface réseau dont vous voulez changer l'adresse MAC, vous pouvez l'obtenir en utilisant les commandes ifconfig ou ip sous Linux.
  • -r ou --random : Si nous générons une adresse MAC aléatoire au lieu d'une adresse spécifiée.
  • -m ou --mac : La nouvelle adresse MAC que nous voulons changer, ne l'utilisez pas avec le paramètre -r.

Dans le code principal, nous utilisons la fonction get_current_mac_address() pour obtenir l'ancienne adresse MAC. Nous changeons l'adresse MAC, puis nous exécutons à nouveau get_current_mac_address() pour vérifier si elle a changé. Voici une exécution :

$ python mac_address_changer_linux.py wlan0 -r

Mon nom d'interface est wlan0, et j'ai choisi -r pour générer une adresse MAC de façon aléatoire. Voici le résultat :

[*] Old MAC address: 84:76:04:07:40:59
[+] New MAC address: ee:52:93:6e:1c:f2

Attribuons maintenant une adresse MAC spécifique :

$ python mac_address_changer_linux.py wlan0 -m 00:FA:CE:DE:AD:00

Résultat

[*] Old MAC address: ee:52:93:6e:1c:f2
[+] New MAC address: 00:fa:ce:de:ad:00

La modification est répercutée sur la machine et les autres machines du même réseau et du routeur.

 

Voici le code source complet pour les environnements Linux.

mac_address_changer_linux.py

import re
import string
import random
import subprocess


def get_random_mac_address():
    """Generate and return a MAC address in the format of Linux"""
    # get the hexdigits uppercased
    uppercased_hexdigits = ''.join(set(string.hexdigits.upper()))
    # 2nd character must be 0, 2, 4, 6, 8, A, C, or E
    mac = ""
    for i in range(6):
        for j in range(2):
            if i == 0:
                mac += random.choice("02468ACE")
            else:
                mac += random.choice(uppercased_hexdigits)
        mac += ":"
    return mac.strip(":")


def get_current_mac_address(iface):
    # use the ifconfig command to get the interface details, including the MAC address
    output = subprocess.check_output(f"ifconfig {iface}", shell=True).decode()
    return re.search("ether (.+) ", output).group().split()[1].strip()
    


def change_mac_address(iface, new_mac_address):
    # disable the network interface
    subprocess.check_output(f"ifconfig {iface} down", shell=True)
    # change the MAC
    subprocess.check_output(f"ifconfig {iface} hw ether {new_mac_address}", shell=True)
    # enable the network interface again
    subprocess.check_output(f"ifconfig {iface} up", shell=True)
    

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Python Mac Changer on Linux")
    parser.add_argument("interface", help="The network interface name on Linux")
    parser.add_argument("-r", "--random", action="store_true", help="Whether to generate a random MAC address")
    parser.add_argument("-m", "--mac", help="The new MAC you want to change to")
    args = parser.parse_args()
    iface = args.interface
    if args.random:
        # if random parameter is set, generate a random MAC
        new_mac_address = get_random_mac_address()
    elif args.mac:
        # if mac is set, use it instead
        new_mac_address = args.mac
    # get the current MAC address
    old_mac_address = get_current_mac_address(iface)
    print("[*] Old MAC address:", old_mac_address)
    # change the MAC address
    change_mac_address(iface, new_mac_address)
    # check if it's really changed
    new_mac_address = get_current_mac_address(iface)
    print("[+] New MAC address:", new_mac_address)

 

 

Changer l'adresse MAC sous Windows

Sous Windows, nous allons utiliser trois commandes principales, qui sont :

  • getmac : Cette commande renvoie une liste des interfaces réseau ainsi que leur adresse MAC et leur nom de transport ; ce dernier n'est pas affiché lorsqu'une interface n'est pas connectée.
  • reg : C'est la commande utilisée pour interagir avec le registre de Windows. On peut utiliser le module winreg dans le même but. Cependant, j'ai préféré utiliser la commande reg.
  • wmic : Nous utiliserons cette commande pour désactiver et activer la carte réseau, afin que le changement d'adresse MAC soit pris en compte.

Commençons :

import subprocess
import regex as re
import string
import random

# the registry path of network interfaces
network_interface_reg_path = r"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}"
# the transport name regular expression, looks like {AF1B45DB-B5D4-46D0-B4EA-3E18FA49BF5F}
transport_name_regex = re.compile("{.+}")
# the MAC address regular expression
mac_address_regex = re.compile(r"([A-Z0-9]{2}[:-]){5}([A-Z0-9]{2})")

network_interface_reg_path est le chemin dans le registre où se trouvent les détails de l'interface réseau. Nous utilisons les expressions régulières transport_name_regex et mac_address_regex pour extraire le nom de transport et l'adresse MAC de chaque adaptateur connecté, respectivement, à partir de la commande getmac.

Ensuite, créons deux fonctions simples, une pour générer des adresses MAC aléatoires (comme précédemment, mais au format Windows), et une pour nettoyer les adresses MAC lorsque l'utilisateur le spécifie :

def get_random_mac_address():
    """Generate and return a MAC address in the format of WINDOWS"""
    # get the hexdigits uppercased
    uppercased_hexdigits = ''.join(set(string.hexdigits.upper()))
    # 2nd character must be 2, 4, A, or E
    return random.choice(uppercased_hexdigits) + random.choice("24AE") + "".join(random.sample(uppercased_hexdigits, k=10))
    

def clean_mac(mac):
    """Simple function to clean non hexadecimal characters from a MAC address
    mostly used to remove '-' and ':' from MAC addresses and also uppercase it"""
    return "".join(c for c in mac if c in string.hexdigits).upper()

Pour une raison quelconque, seuls les caractères 2, 4, A et E fonctionnent comme deuxième caractère de l'adresse MAC sous Windows 10. J'ai essayé les autres caractères pairs mais sans succès.

Vous trouverez ci-dessous la fonction responsable de l'obtention des adresses MAC des adaptateurs disponibles :

def get_connected_adapters_mac_address():
    # make a list to collect connected adapter's MAC addresses along with the transport name
    connected_adapters_mac = []
    # use the getmac command to extract 
    for potential_mac in subprocess.check_output("getmac").decode().splitlines():
        # parse the MAC address from the line
        mac_address = mac_address_regex.search(potential_mac)
        # parse the transport name from the line
        transport_name = transport_name_regex.search(potential_mac)
        if mac_address and transport_name:
            # if a MAC and transport name are found, add them to our list
            connected_adapters_mac.append((mac_address.group(), transport_name.group()))
    return connected_adapters_mac

Elle utilise la commande getmac sous Windows et renvoie une liste d'adresses MAC ainsi que leur nom de transport.

Lorsque la fonction ci-dessus renvoie plus d'un adaptateur, nous devons demander à l'utilisateur de choisir l'adaptateur sur lequel modifier l'adresse MAC. C'est ce que fait la fonction ci-dessous :

def get_user_adapter_choice(connected_adapters_mac):
    # print the available adapters
    for i, option in enumerate(connected_adapters_mac):
        print(f"#{i}: {option[0]}, {option[1]}")
    if len(connected_adapters_mac) <= 1:
        # when there is only one adapter, choose it immediately
        return connected_adapters_mac[0]
    # prompt the user to choose a network adapter index
    try:
        choice = int(input("Please choose the interface you want to change the MAC address:"))
        # return the target chosen adapter's MAC and transport name that we'll use later to search for our adapter
        # using the reg QUERY command
        return connected_adapters_mac[choice]
    except:
        # if -for whatever reason- an error is raised, just quit the script
        print("Not a valid choice, quitting...")
        exit()

Maintenant, implémentons notre fonction pour changer l'adresse MAC d'un nom de transport d'adaptateur donné qui est extrait de la commande getmac :

def change_mac_address(adapter_transport_name, new_mac_address):
    # use reg QUERY command to get available adapters from the registry
    output = subprocess.check_output(f"reg QUERY " +  network_interface_reg_path.replace("\\\\", "\\")).decode()
    for interface in re.findall(rf"{network_interface_reg_path}\\\d+", output):
        # get the adapter index
        adapter_index = int(interface.split("\\")[-1])
        interface_content = subprocess.check_output(f"reg QUERY {interface.strip()}").decode()
        if adapter_transport_name in interface_content:
            # if the transport name of the adapter is found on the output of the reg QUERY command
            # then this is the adapter we're looking for
            # change the MAC address using reg ADD command
            changing_mac_output = subprocess.check_output(f"reg add {interface} /v NetworkAddress /d {new_mac_address} /f").decode()
            # print the command output
            print(changing_mac_output)
            # break out of the loop as we're done
            break
    # return the index of the changed adapter's MAC address
    return adapter_index

La fonction change_mac_address() utilise la commande reg QUERY sous Windows pour interroger le chemin d'accès network_interface_reg_path que nous avons spécifié au début du script, il retournera la liste de tous les adaptateurs disponibles, et nous distinguons l'adaptateur cible par son nom de transport.

Après avoir trouvé l'interface réseau cible, nous utilisons la commande reg add pour ajouter une nouvelle entrée NetworkAddress dans le registre en spécifiant la nouvelle adresse MAC. La fonction renvoie également l'index de l'adaptateur, dont nous aurons besoin plus tard avec la commande wmic.

Bien sûr, le changement d'adresse MAC n'est pas pris en compte immédiatement lorsque la nouvelle entrée de registre est ajoutée. Nous devons désactiver l'adaptateur et le réactiver. Les fonctions ci-dessous le font :

def disable_adapter(adapter_index):
    # use wmic command to disable our adapter so the MAC address change is reflected
    disable_output = subprocess.check_output(f"wmic path win32_networkadapter where index={adapter_index} call disable").decode()
    return disable_output


def enable_adapter(adapter_index):
    # use wmic command to enable our adapter so the MAC address change is reflected
    enable_output = subprocess.check_output(f"wmic path win32_networkadapter where index={adapter_index} call enable").decode()
    return enable_output

Le numéro de système de l'adaptateur est requis par la commande wmic, et heureusement nous l'avons obtenu de notre fonction précédente change_mac_address().

Et nous avons terminé ! Ajoutons notre code principal :

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Python Windows MAC changer")
    parser.add_argument("-r", "--random", action="store_true", help="Whether to generate a random MAC address")
    parser.add_argument("-m", "--mac", help="The new MAC you want to change to")
    args = parser.parse_args()
    if args.random:
        # if random parameter is set, generate a random MAC
        new_mac_address = get_random_mac_address()
    elif args.mac:
        # if mac is set, use it after cleaning
        new_mac_address = clean_mac(args.mac)
    
    connected_adapters_mac = get_connected_adapters_mac_address()
    old_mac_address, target_transport_name = get_user_adapter_choice(connected_adapters_mac)
    print("[*] Old MAC address:", old_mac_address)
    adapter_index = change_mac_address(target_transport_name, new_mac_address)
    print("[+] Changed to:", new_mac_address)
    disable_adapter(adapter_index)
    print("[+] Adapter is disabled")
    enable_adapter(adapter_index)
    print("[+] Adapter is enabled again")

Puisque le choix de l'interface réseau est demandé après l'exécution du script (lorsque deux interfaces ou plus sont détectées), nous n'avons pas besoin d'ajouter un argument d'interface.

Le code principal est simple :

  • Nous obtenons tous les adaptateurs connectés en utilisant la fonction get_connected_adapters_mac_address().
  • Nous recevons l'entrée de l'utilisateur indiquant l'adaptateur à cibler.
  • Nous utilisons la fonction change_mac_address() pour changer l'adresse MAC pour le nom de transport de l'adaptateur donné.
  • Nous désactivons et activons l'adaptateur en utilisant respectivement les fonctions disable_adapter() et enable_adapter(), afin que le changement d'adresse MAC soit pris en compte.

Bien, nous avons fini avec le script. Avant de le tester, vous devez vous assurer que vous l'exécutez en tant qu'administrateur. J'ai nommé le script mac_address_changer_windows.py :

$ python mac_address_changer_windows.py --help

Résultat :

usage: mac_address_changer_windows.py [-h] [-r] [-m MAC]

Python Windows MAC changer

optional arguments:
  -h, --help         show this help message and exit
  -r, --random       Whether to generate a random MAC address
  -m MAC, --mac MAC  The new MAC you want to change to

Essayons avec une adresse MAC aléatoire :

$ python mac_address_changer_windows.py --random

Résultat :

#0: EE-9C-BC-AA-AA-AA, {0104C4B7-C06C-4062-AC09-9F9B977F2A55}
#1: 02-00-4C-4F-4F-50, {DD1B45DA-B5D4-46D0-B4EA-3E07FA35BF0F}
Please choose the interface you want to change the MAC address:0
[*] Old MAC address: EE-9C-BC-AA-AA-AA
The operation completed successfully.

[+] Changed to: 5A8602E9CF3D

[+] Adapter is disabled

[+] Adapter is enabled again

Le programme me demande de choisir l'adaptateur, j'ai choisi le premier, et l'adresse MAC est changée en une adresse MAC aléatoire. Confirmons avec la commande getmac :

$ getmac

Résultat :

Physical Address    Transport Name
=================== ==========================================================
5A-86-02-E9-CF-3D   \Device\Tcpip_{0104C4B7-C06C-4062-AC09-9F9B977F2A55}
02-00-4C-4F-4F-50   \Device\Tcpip_{DD1B45DA-B5D4-46D0-B4EA-3E07FA35BF0F}

L'opération a en effet réussi ! Essayons maintenant avec une adresse MAC spécifique :

$ python mac_address_changer_windows.py -m EE:DE:AD:BE:EF:EE

Résultat :

#0: 5A-86-02-E9-CF-3D, {0104C4B7-C06C-4062-AC09-9F9B977F2A55}
#1: 02-00-4C-4F-4F-50, {DD1B45DA-B5D4-46D0-B4EA-3E07FA35BF0F}
Please choose the interface you want to change the MAC address:0
[*] Old MAC address: 5A-86-02-E9-CF-3D
The operation completed successfully.

[+] Changed to: EEDEADBEEFEE

[+] Adapter is disabled

[+] Adapter is enabled again

 

Voici le code source complet pour les systèmes Windows.

mac_address_changer_windows.py

import subprocess
import regex as re
import string
import random

# the registry path of network interfaces
network_interface_reg_path = r"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}"
# the transport name regular expression, looks like {AF1B45DB-B5D4-46D0-B4EA-3E18FA49BF5F}
transport_name_regex = re.compile("{.+}")
# the MAC address regular expression
mac_address_regex = re.compile(r"([A-Z0-9]{2}[:-]){5}([A-Z0-9]{2})")

def get_random_mac_address():
    """Generate and return a MAC address in the format of WINDOWS"""
    # get the hexdigits uppercased
    uppercased_hexdigits = ''.join(set(string.hexdigits.upper()))
    # 2nd character must be 2, 4, A, or E
    return random.choice(uppercased_hexdigits) + random.choice("24AE") + "".join(random.sample(uppercased_hexdigits, k=10))
    

def clean_mac(mac):
    """Simple function to clean non hexadecimal characters from a MAC address
    mostly used to remove '-' and ':' from MAC addresses and also uppercase it"""
    return "".join(c for c in mac if c in string.hexdigits).upper()    


def get_connected_adapters_mac_address():
    # make a list to collect connected adapter's MAC addresses along with the transport name
    connected_adapters_mac = []
    # use the getmac command to extract 
    for potential_mac in subprocess.check_output("getmac").decode().splitlines():
        # parse the MAC address from the line
        mac_address = mac_address_regex.search(potential_mac)
        # parse the transport name from the line
        transport_name = transport_name_regex.search(potential_mac)
        if mac_address and transport_name:
            # if a MAC and transport name are found, add them to our list
            connected_adapters_mac.append((mac_address.group(), transport_name.group()))
    return connected_adapters_mac


def get_user_adapter_choice(connected_adapters_mac):
    # print the available adapters
    for i, option in enumerate(connected_adapters_mac):
        print(f"#{i}: {option[0]}, {option[1]}")
    if len(connected_adapters_mac) <= 1:
        # when there is only one adapter, choose it immediately
        return connected_adapters_mac[0]
    # prompt the user to choose a network adapter index
    try:
        choice = int(input("Please choose the interface you want to change the MAC address:"))
        # return the target chosen adapter's MAC and transport name that we'll use later to search for our adapter
        # using the reg QUERY command
        return connected_adapters_mac[choice]
    except:
        # if -for whatever reason- an error is raised, just quit the script
        print("Not a valid choice, quitting...")
        exit()
    

def change_mac_address(adapter_transport_name, new_mac_address):
    # use reg QUERY command to get available adapters from the registry
    output = subprocess.check_output(f"reg QUERY " +  network_interface_reg_path.replace("\\\\", "\\")).decode()
    for interface in re.findall(rf"{network_interface_reg_path}\\\d+", output):
        # get the adapter index
        adapter_index = int(interface.split("\\")[-1])
        interface_content = subprocess.check_output(f"reg QUERY {interface.strip()}").decode()
        if adapter_transport_name in interface_content:
            # if the transport name of the adapter is found on the output of the reg QUERY command
            # then this is the adapter we're looking for
            # change the MAC address using reg ADD command
            changing_mac_output = subprocess.check_output(f"reg add {interface} /v NetworkAddress /d {new_mac_address} /f").decode()
            # print the command output
            print(changing_mac_output)
            # break out of the loop as we're done
            break
    # return the index of the changed adapter's MAC address
    return adapter_index
      

def disable_adapter(adapter_index):
    # use wmic command to disable our adapter so the MAC address change is reflected
    disable_output = subprocess.check_output(f"wmic path win32_networkadapter where index={adapter_index} call disable").decode()
    return disable_output


def enable_adapter(adapter_index):
    # use wmic command to enable our adapter so the MAC address change is reflected
    enable_output = subprocess.check_output(f"wmic path win32_networkadapter where index={adapter_index} call enable").decode()
    return enable_output
        

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Python Windows MAC changer")
    parser.add_argument("-r", "--random", action="store_true", help="Whether to generate a random MAC address")
    parser.add_argument("-m", "--mac", help="The new MAC you want to change to")
    args = parser.parse_args()
    if args.random:
        # if random parameter is set, generate a random MAC
        new_mac_address = get_random_mac_address()
    elif args.mac:
        # if mac is set, use it after cleaning
        new_mac_address = clean_mac(args.mac)
    
    connected_adapters_mac = get_connected_adapters_mac_address()
    old_mac_address, target_transport_name = get_user_adapter_choice(connected_adapters_mac)
    print("[*] Old MAC address:", old_mac_address)
    adapter_index = change_mac_address(target_transport_name, new_mac_address)
    print("[+] Changed to:", new_mac_address)
    disable_adapter(adapter_index)
    print("[+] Adapter is disabled")
    enable_adapter(adapter_index)
    print("[+] Adapter is enabled again")

 

 

Conclusion

Bien ! Dans ce tutoriel, vous avez appris comment créer un changeur d'adresse MAC sur n'importe quelle machine Linux ou Windows.

Si vous n'avez pas la commande ifconfig installée, vous devez l'installer via apt install net-tools sur Debian/Ubuntu ou yum install net-tools sur Fedora/CentOS.

 

VEUILLEZ NOTER QUE NOUS NE PRENONS AUCUNE RESPONSABILITÉ SI VOUS UTILISEZ MAL LE SCRIPT FOURNI DANS CE TUTORIEL ; RESTEZ ÉTHIQUE !