¿Cómo puedo obtener programáticamente la dirección MAC de un iPhone?

144

¿Cómo obtener mediante programación la dirección MAC y la dirección IP de un iPhone?

Coeur
fuente
Quiero obtener la dirección MAC y la dirección IP mediante programación en una aplicación de iPhone.
¿Dirección MAC wifi? o BlueTooth? Hay muchas direcciones Mac.
mskw
11
A partir de iOS 7, el sistema siempre devuelve el valor 02:00:00:00:00:00cuando solicita la dirección MAC en cualquier dispositivo. Mira mi respuesta a continuación.
Hejazi
Para
iOS

Respuestas:

114

NOTA A partir de iOS7, ya no puede recuperar las direcciones MAC del dispositivo. Se devolverá un valor fijo en lugar del MAC real


Algo con lo que me topé hace un tiempo. Originalmente desde aquí lo modifiqué un poco y limpié las cosas.

IPAddress.h
IPAddress.c

Y para usarlo

InitAddresses();
GetIPAddresses();
GetHWAddresses();

int i;
NSString *deviceIP = nil;
for (i=0; i<MAXADDRS; ++i)
{
    static unsigned long localHost = 0x7F000001;        // 127.0.0.1
    unsigned long theAddr;

    theAddr = ip_addrs[i];

    if (theAddr == 0) break;
    if (theAddr == localHost) continue;

    NSLog(@"Name: %s MAC: %s IP: %s\n", if_names[i], hw_addrs[i], ip_names[i]);

        //decided what adapter you want details for
    if (strncmp(if_names[i], "en", 2) == 0)
    {
        NSLog(@"Adapter en has a IP of %s", ip_names[i]);
    }
}

Los nombres de los adaptadores varían según el simulador / dispositivo, así como el wifi o la celda del dispositivo.

PyjamaSam
fuente
Me pregunto si esto es diferente de un enfoque genérico OS X (Lo digo porque yo soy un n00b mac)
Tamas Czinege
2
@abc: te recomiendo que publiques otra pregunta al respecto. Será mejor que enterrarlo en los comentarios para una pregunta diferente.
PyjamaSam
1
pero ¿qué pasa con la llamada a ether_ntoa? esta es una API privada, ¿no?
stigi el
1
@NicTesla No puedo comentar con certeza, pero como solo está haciendo llamadas de red básicas, no puedo ver ningún problema. Aunque dicho esto con la eliminación del UDID de iOS5, obtener la dirección mac y usarla para desarrollar algún tipo de identificador único alternativo podría ser objeto de escrutinio por parte de Apple en algún momento en el futuro.
PyjamaSam
1
Con respecto a la ether_ntoaadvertencia, consulte: stackoverflow.com/a/13412265/88597
ohho
45

Actualización: esto no funcionará en iOS 7. Debería usar ASIdentifierManager .


Solución más limpia en el sitio web MobileDeveloperTips:

#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>

...

- (NSString *)getMacAddress
{
  int                 mgmtInfoBase[6];
  char                *msgBuffer = NULL;
  size_t              length;
  unsigned char       macAddress[6];
  struct if_msghdr    *interfaceMsgStruct;
  struct sockaddr_dl  *socketStruct;
  NSString            *errorFlag = NULL;

  // Setup the management Information Base (mib)
  mgmtInfoBase[0] = CTL_NET;        // Request network subsystem
  mgmtInfoBase[1] = AF_ROUTE;       // Routing table info
  mgmtInfoBase[2] = 0;              
  mgmtInfoBase[3] = AF_LINK;        // Request link layer information
  mgmtInfoBase[4] = NET_RT_IFLIST;  // Request all configured interfaces

  // With all configured interfaces requested, get handle index
  if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0) 
    errorFlag = @"if_nametoindex failure";
  else
  {
    // Get the size of the data available (store in len)
    if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0) 
      errorFlag = @"sysctl mgmtInfoBase failure";
    else
    {
      // Alloc memory based on above call
      if ((msgBuffer = malloc(length)) == NULL)
        errorFlag = @"buffer allocation failure";
      else
      {
        // Get system information, store in buffer
        if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
          errorFlag = @"sysctl msgBuffer failure";
      }
    }
  }

  // Befor going any further...
  if (errorFlag != NULL)
  {
    NSLog(@"Error: %@", errorFlag);
    return errorFlag;
  }

  // Map msgbuffer to interface message structure
  interfaceMsgStruct = (struct if_msghdr *) msgBuffer;

  // Map to link-level socket structure
  socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);

  // Copy link layer address data in socket structure to an array
  memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);

  // Read from char array into a string object, into traditional Mac address format
  NSString *macAddressString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", 
                                macAddress[0], macAddress[1], macAddress[2], 
                                macAddress[3], macAddress[4], macAddress[5]];
  NSLog(@"Mac Address: %@", macAddressString);

  // Release the buffer memory
  free(msgBuffer);

  return macAddressString;
}
ArtFeel
fuente
cambiar el enlace a mobiledevelopertips en lugar de iphonedevelopertips
SEG
Es muy probable que Apple rompa esto en iOS7 ... y no podríamos comentarlo públicamente hasta que se envíe, excepto en los foros oficiales de desarrollo de Apple.
Gabe
3
Esto no funcionó en mi iPhone5 con iOS7. Me da: 02: 00: 00: 00: 00: 00 que no está bien.
Sjoerd realiza
3
@Brenden Ya no puede obtener la dirección MAC de la red utilizando PublicAPI en iOS7. Entonces, si necesita una identificación única, debe usarIdentifierManager
ArtFeel
2
@SjoerdPerfors En iOS 7, siempre obtendrá la misma dirección 02:00:00:00:00:00que una precaución de privacidad de Apple.
Basil Bourque
31

Quería que algo devolviera la dirección independientemente de si el wifi estaba habilitado o no, por lo que la solución elegida no funcionó para mí. Utilicé otra llamada que encontré en algún foro después de algunos ajustes. Terminé con lo siguiente (disculpe mi oxidada C):

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <net/if_dl.h>
#include <ifaddrs.h>


char*  getMacAddress(char* macAddress, char* ifName) {

int  success;
struct ifaddrs * addrs;
struct ifaddrs * cursor;
const struct sockaddr_dl * dlAddr;
const unsigned char* base;
int i;

success = getifaddrs(&addrs) == 0;
if (success) {
    cursor = addrs;
    while (cursor != 0) {
        if ( (cursor->ifa_addr->sa_family == AF_LINK)
            && (((const struct sockaddr_dl *) cursor->ifa_addr)->sdl_type == IFT_ETHER) && strcmp(ifName,  cursor->ifa_name)==0 ) {
            dlAddr = (const struct sockaddr_dl *) cursor->ifa_addr;
            base = (const unsigned char*) &dlAddr->sdl_data[dlAddr->sdl_nlen];
            strcpy(macAddress, ""); 
            for (i = 0; i < dlAddr->sdl_alen; i++) {
                if (i != 0) {
                    strcat(macAddress, ":");
                }
                char partialAddr[3];
                sprintf(partialAddr, "%02X", base[i]);
                strcat(macAddress, partialAddr);

            }
        }
        cursor = cursor->ifa_next;
    }

    freeifaddrs(addrs);
}    
return macAddress;
}

Y luego lo llamaría pidiendo en0 , de la siguiente manera:

char* macAddressString= (char*)malloc(18);
NSString* macAddress= [[NSString alloc] initWithCString:getMacAddress(macAddressString, "en0")
                                              encoding:NSMacOSRomanStringEncoding];
free(macAddressString);
patrón
fuente
9
Tuve que agregar #define IFT_ETHER 0x6
teedyay
Este no funciona para mí ... dice ifName no se declara tan bien como la variable macaddress.
bugfixr
El código de llamada anterior se escapará. libre (macAddressString); antes de regresar del método de llamada anterior. Además, macAddress se debe volver a publicar automáticamente si el código de llamada anterior se encuentra en otro método.
44
Para ser claros, el código en sí no se filtrará, el comentarista estaba hablando sobre el fragmento de llamada al final, donde asigno dos cadenas sin liberarlas.
capitán de barco el
1
Puse esto en un buen método de Objective
wsidell
23

A partir de iOS 7, el sistema siempre devuelve el valor 02:00:00:00:00:00cuando solicita la dirección MAC en cualquier dispositivo.

En iOS 7 y versiones posteriores, si solicita la dirección MAC de un dispositivo iOS, el sistema devuelve el valor 02: 00: 00: 00: 00: 00. Si necesita identificar el dispositivo, use la propiedad identifierForVendor de UIDevice en su lugar. (Las aplicaciones que necesitan un identificador para sus propios fines publicitarios deberían considerar usar la propiedad advertisingIdentifier de ASIdentifierManager). "

Referencia: releasenotes

Hejazi
fuente
Lo realmente bueno de esta depreciación en la API es que todavía funciona correctamente en el simulador, y solo en el hardware en este momento.
John Nye
12

Hay varias soluciones sobre esto, pero no pude encontrar todo. Entonces hice mi propia solución para:

nicinfo

Cómo utilizar :

NICInfoSummary* summary = [[[NICInfoSummary alloc] init] autorelease];

// en0 is for WiFi 
NICInfo* wifi_info = [summary findNICInfo:@"en0"];

// you can get mac address in 'XX-XX-XX-XX-XX-XX' form
NSString* mac_address = [wifi_info getMacAddressWithSeparator:@"-"];

// ip can be multiple
if(wifi_info.nicIPInfos.count > 0)
{
    NICIPInfo* ip_info = [wifi_info.nicIPInfos objectAtIndex:0];
    NSString* ip = ip_info.ip;
    NSString* netmask = ip_info.netmask;
    NSString* broadcast_ip = ip_info.broadcastIP;
}
else
{
    NSLog(@"WiFi not connected!");
}
Kenial
fuente
Muy bonita y útil clase! ¡Buen trabajo! Sin embargo, se perdió [super dealloc] en ambas clases y olvidó incluir una biblioteca, lo que genera advertencias en xCode. La versión parcheada
Raptor
obteniendo este resultado Dirección Mac: 02: 00: 00: 00: 00: 00
Stalin Pusparaj
1
Si. Apple bloqueó esto desde iOS 7 :( ... developer.apple.com/library/content/releasenotes/General/…
Kenial
5

Esto parece una solución bastante limpia: UIDevice BIdentifier

// Return the local MAC addy
// Courtesy of FreeBSD hackers email list
// Accidentally munged during previous update. Fixed thanks to erica sadun & mlamb.
- (NSString *) macaddress{

    int                 mib[6];
    size_t              len;
    char                *buf;
    unsigned char       *ptr;
    struct if_msghdr    *ifm;
    struct sockaddr_dl  *sdl;

    mib[0] = CTL_NET;
    mib[1] = AF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_LINK;
    mib[4] = NET_RT_IFLIST;

    if ((mib[5] = if_nametoindex("en0")) == 0) {
        printf("Error: if_nametoindex error\n");
        return NULL;
    }

    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 1\n");
        return NULL;
    }

    if ((buf = malloc(len)) == NULL) {
        printf("Could not allocate memory. error!\n");
        return NULL;
    }

    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 2");
        free(buf);
        return NULL;
    }

    ifm = (struct if_msghdr *)buf;
    sdl = (struct sockaddr_dl *)(ifm + 1);
    ptr = (unsigned char *)LLADDR(sdl);
    NSString *outstring = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", 
                           *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    free(buf);

    return outstring;
}
Grantland Chew
fuente
1
Utilizo su solución, el 99% de los casos funcionan bien como se esperaba, pero la tasa del 1% no puede obtener la dirección mac, devuelve NULL, informada por los usuarios en AppStore. Aún no obtienes pasos reproducibles, ¿lo tienes? Gracias.
jianhua
Todavía no he notado ningún problema, ¡pero estaré atento!
Grantland Chew
Continuando con el comentario de @jianhua: acabamos de recibir informes de un usuario que básicamente no puede usar la aplicación porque este código devuelve nulo para su dispositivo (usamos el identificador para autenticar cada llamada al servidor).
samvermette
El caso fallido está en iPhone 4S con IOS versión 5.0.1, por supuesto, solo en algunas ocasiones especiales.
jianhua
5

Ahora dispositivos iOS 7: siempre devuelven una dirección MAC de 02: 00: 00: 00: 00: 00.

Así que mejor use [UIDevice identifierForVendor].

así que es mejor llamar a este método para obtener una clave única específica de la aplicación

Categoría será más adecuada

importar "UIDevice + Identifier.h"

- (NSString *) identifierForVendor1
{
    if ([[UIDevice currentDevice] respondsToSelector:@selector(identifierForVendor)]) {
        return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    }
    return @"";
}

Ahora llame al método anterior para obtener una dirección única

NSString *like_UDID=[NSString stringWithFormat:@"%@",
                [[UIDevice currentDevice] identifierForVendor1]];

NSLog(@"%@",like_UDID);
Nagendra Tripathi
fuente
¿sería este identificador consistente por dispositivo en múltiples instalaciones de la misma aplicación?
samsam
4

@Grantland Esta "solución bastante limpia" se parece a mi propia mejora con respecto a la solución iPhoneDeveloperTips.

Puedes ver mi paso aquí: https://gist.github.com/1409855/

/* Original source code courtesy John from iOSDeveloperTips.com */

#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>

+ (NSString *)getMacAddress
{
    int                 mgmtInfoBase[6];
    char                *msgBuffer = NULL;
    NSString            *errorFlag = NULL;
    size_t              length;

    // Setup the management Information Base (mib)
    mgmtInfoBase[0] = CTL_NET;        // Request network subsystem
    mgmtInfoBase[1] = AF_ROUTE;       // Routing table info
    mgmtInfoBase[2] = 0;              
    mgmtInfoBase[3] = AF_LINK;        // Request link layer information
    mgmtInfoBase[4] = NET_RT_IFLIST;  // Request all configured interfaces

    // With all configured interfaces requested, get handle index
    if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0) 
        errorFlag = @"if_nametoindex failure";
    // Get the size of the data available (store in len)
    else if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0) 
        errorFlag = @"sysctl mgmtInfoBase failure";
    // Alloc memory based on above call
    else if ((msgBuffer = malloc(length)) == NULL)
        errorFlag = @"buffer allocation failure";
    // Get system information, store in buffer
    else if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
    {
        free(msgBuffer);
        errorFlag = @"sysctl msgBuffer failure";
    }
    else
    {
        // Map msgbuffer to interface message structure
        struct if_msghdr *interfaceMsgStruct = (struct if_msghdr *) msgBuffer;

        // Map to link-level socket structure
        struct sockaddr_dl *socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);

        // Copy link layer address data in socket structure to an array
        unsigned char macAddress[6];
        memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);

        // Read from char array into a string object, into traditional Mac address format
        NSString *macAddressString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
                                      macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]];
        NSLog(@"Mac Address: %@", macAddressString);

        // Release the buffer memory
        free(msgBuffer);

        return macAddressString;
    }

    // Error...
    NSLog(@"Error: %@", errorFlag);

    return nil;
}
Coeur
fuente
y no entiendo por qué puedo agregar comentarios para mis propias respuestas pero no para las respuestas de otros? : /
Cœur
1
Porque tu reputación no es lo suficientemente alta.
honus
Gracias, parece que algunas personas han presentado sus propias variantes de esta solución: ios.biomsoft.com/2011/11/07/determine-mac-address
josh-fuggle
1

Ya no es posible en dispositivos con iOS 7.0 o posterior, por lo tanto, no está disponible para obtener la dirección MAC en Swift.

Como dijo Apple:

En iOS 7 y versiones posteriores, si solicita la dirección MAC de un dispositivo iOS, el sistema devuelve el valor 02: 00: 00: 00: 00: 00. Si necesita identificar el dispositivo, use la propiedad identifierForVendor de UIDevice en su lugar. (Las aplicaciones que necesitan un identificador para sus propios fines publicitarios deberían considerar el uso de la propiedad advertisingIdentifier de ASIdentifierManager).

pedrouan
fuente
0
#import <sys/socket.h>
#import <net/if_dl.h>
#import <ifaddrs.h>
#import <sys/xattr.h>
#define IFT_ETHER 0x6

...

- (NSString*)macAddress
{
    NSString* result = nil;

    char* macAddressString = (char*)malloc(18);
    if (macAddressString != NULL)
    {
        strcpy(macAddressString, "");

        struct ifaddrs* addrs = NULL;
        struct ifaddrs* cursor;

        if (getifaddrs(&addrs) == 0)
        {
            cursor = addrs;

            while (cursor != NULL)
            {
                if ((cursor->ifa_addr->sa_family == AF_LINK) && (((const struct sockaddr_dl*)cursor->ifa_addr)->sdl_type == IFT_ETHER) && strcmp("en0", cursor->ifa_name) == 0)
                {
                    const struct sockaddr_dl* dlAddr = (const struct sockaddr_dl*) cursor->ifa_addr;
                    const unsigned char* base = (const unsigned char*)&dlAddr->sdl_data[dlAddr->sdl_nlen];

                    for (NSInteger index = 0; index < dlAddr->sdl_alen; index++)
                    {
                        char partialAddr[3];

                        sprintf(partialAddr, "%02X", base[index]);
                        strcat(macAddressString, partialAddr);
                    }
                }

                cursor = cursor->ifa_next;
            }

        }

        result = [[[NSString alloc] initWithUTF8String:macAddressString] autorelease];

        free(macAddressString);
    }

    return result;
}
Valentin Shamardin
fuente
0

Para crear una cadena única basada en un identificador único del dispositivo en iOS 6:

#import <AdSupport/ASIdentifierManager.h>

NSString *uniqueString = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
NSLog(@"uniqueString: %@", uniqueString);
Denis Kutlubaev
fuente
1
¿Cómo se relaciona esto con la pregunta formulada?
Valeriy Van
1
La dirección Mac es necesaria para identificar de manera única el dispositivo en la mayoría de los casos. En cambio, puede usar esta solución.
Denis Kutlubaev
0

Muchas de estas preguntas solo abordan la dirección Mac. Si también necesita la dirección IP que acabo de escribir, es posible que necesite algo de trabajo pero parece funcionar bien en mi máquina ...

- (NSString *)getLocalIPAddress
{
    NSArray *ipAddresses = [[NSHost currentHost] addresses];
    NSArray *sortedIPAddresses = [ipAddresses sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    numberFormatter.allowsFloats = NO;

    for (NSString *potentialIPAddress in sortedIPAddresses)
    {
        if ([potentialIPAddress isEqualToString:@"127.0.0.1"]) {
            continue;
        }

        NSArray *ipParts = [potentialIPAddress componentsSeparatedByString:@"."];

        BOOL isMatch = YES;

        for (NSString *ipPart in ipParts) {
            if (![numberFormatter numberFromString:ipPart]) {
                isMatch = NO;
                break;
            }
        }
        if (isMatch) {
            return potentialIPAddress;
        }
    }

    // No IP found
    return @"?.?.?.?";
}
Oliver Pearmain
fuente