Identificateur unique d'appareil privé dans iOS

nous travaillons sur un projet avec mes collègues qui implique l'utilisation de beaucoup de code privé et non officiel. Il s'agit de et non de destiné à une utilisation en AppStore.

la première et la seule exigence que nous avons est de et non utiliser jailbreak.

tout D'abord, UDID ou OpenUDID ou toute autre solution ne fonctionne pas ici et ils ne sont pas censés.

Nous avons fait beaucoup de recherches et de tests, en commençant par essayer d'obtenir le IMEI , ICCID, IMSI et le numéro de série programmatically. Aucune des méthodes ci-dessus ne fonctionne avec iOS 7 et plus sans jailbreak.

nous avons également passé un couple de mois pour jouer avec IOKit cadre en utilisant le célèbre IOKitBrowser et le dumping de la contenu entier de iOS internes. Malheureusement, nous avons découvert qu'avec iOS 8.3 il a cessé de fonctionner.

nous ne parlons pas ici d'obtenir le UDID ou toute autre chose" mainstream", mais de façon générale nous avons besoin d'un moyen d'obtenir

tout identificateur matériel permanent suffisamment unique pour permettre d'identifier un appareil qui persisterait malgré les essuie-glaces et parmi les différentes versions de iOS

cette question ne fait pas double emploi avec d'autres ( aucune solution n'est trouvée ici , par exemple) et vise uniquement APIs privés .

toute aide serait grandement appréciée.

30
demandé sur Community 2015-04-21 02:53:02

8 réponses

après quelques recherches, j'ai découvert que tous les APIs privés utilisent libMobileGestalt pour obtenir n'importe quel identifiant matériel, qui à son tour utilise IOKit . MobileGestalt vérifie les règles du bac à sable pour les pid courants et cherche l'admissibilité com.apple.private.MobileGestalt.AllowedProtectedKeys .

voir le code ci-dessous:

signed int __fastcall sub_2EB8803C(int a1, int a2, int a3, int a4)
{
  int v4; // r5@1
  int v5; // r4@1
  int v6; // r10@1
  int v7; // r2@1
  int v8; // r0@3
  int v9; // r6@3
  int v10; // r11@4
  int v11; // r4@4
  int v12; // r0@5
  signed int v13; // r6@6
  int v14; // r6@7
  char *v15; // r0@7
  int v16; // r1@7
  int v17; // r1@14
  int v18; // r3@16
  int v19; // r5@16
  signed int v20; // r1@17
  int v21; // r0@17
  __CFString *v22; // r2@19
  int v23; // r4@27
  __CFString *v24; // r2@27
  int v26; // [sp+8h] [bp-428h]@1
  char v27; // [sp+10h] [bp-420h]@1
  int v28; // [sp+414h] [bp-1Ch]@1

  v26 = a2;
  v4 = a1;
  v5 = a3;
  v6 = a4;
  v28 = __stack_chk_guard;
  memset(&v27, 0, 0x401u);
  v7 = *(_DWORD *)(dword_32260254 + 260);
  if ( !v7 )
    v7 = sub_2EB8047C(65, 2);
  v8 = ((int (__fastcall *)(int, _DWORD))v7)(v4, "com.apple.private.MobileGestalt.AllowedProtectedKeys");
  v9 = v8;
  if ( !v8 )
    goto LABEL_12;
  v10 = v5;
  v11 = CFGetTypeID(v8);
  if ( v11 != CFArrayGetTypeID() )
  {
    v14 = (int)"/SourceCache/MobileGestalt/MobileGestalt-297.1.14/MobileGestalt.c";
    v15 = rindex("/SourceCache/MobileGestalt/MobileGestalt-297.1.14/MobileGestalt.c", 47);
    v16 = *(_DWORD *)(dword_32260254 + 288);
    if ( v15 )
      v14 = (int)(v15 + 1);
    if ( !v16 )
      v16 = sub_2EB8047C(72, 2);
    ((void (__fastcall *)(int))v16)(v4);
    _MGLog(3, v14);
LABEL_12:
    v13 = 0;
    goto LABEL_13;
  }
  v12 = CFArrayGetCount(v9);
  if ( CFArrayContainsValue(v9, 0, v12, v26) )
    v13 = 1;
  else
    v13 = sub_2EB7F948(v9, v26, v10, "MGCopyAnswer");
LABEL_13:
  if ( !v6 )
    goto LABEL_30;
  v17 = *(_DWORD *)(dword_32260254 + 288);
  if ( !v17 )
    v17 = sub_2EB8047C(72, 2);
  v19 = ((int (__fastcall *)(int))v17)(v4);
  if ( v13 != 1 )
  {
    v21 = *(_DWORD *)v6;
    if ( *(_DWORD *)v6 )
    {
      v22 = CFSTR(" and IS NOT appropriately entitled");
      goto LABEL_22;
    }
    v23 = CFStringCreateMutable(0, 0);
    *(_DWORD *)v6 = v23;
    sub_2EB7F644(v19, &v27);
    v24 = CFSTR("pid %d (%s) IS NOT appropriately entitled to fetch %@");
    goto LABEL_29;
  }
  v20 = MGGetBoolAnswer((int)CFSTR("LBJfwOEzExRxzlAnSuI7eg"));
  v21 = *(_DWORD *)v6;
  if ( v20 == 1 )
  {
    if ( v21 )
    {
      v22 = CFSTR(" but IS appropriately entitled; all is good in the world");
LABEL_22:
      CFStringAppendFormat(v21, 0, v22, v18);
      goto LABEL_30;
    }
    v23 = CFStringCreateMutable(0, 0);
    *(_DWORD *)v6 = v23;
    sub_2EB7F644(v19, &v27);
    v24 = CFSTR("pid %d (%s) IS appropriately entitled to fetch %@; all is good in the world");
LABEL_29:
    CFStringAppendFormat(v23, 0, v24, v19);
    goto LABEL_30;
  }
  if ( v21 )
  {
    CFRelease(v21);
    *(_DWORD *)v6 = 0;
  }
  *(_DWORD *)v6 = 0;
LABEL_30:
  if ( __stack_chk_guard != v28 )
    __stack_chk_fail(__stack_chk_guard - v28);
  return v13;
}

signed int __fastcall sub_2EB88228(int a1, int a2, int a3)
{
  int v3; // r4@1
  int v4; // r10@1
  int v5; // r0@1
  int v6; // r6@1
  int v7; // r5@5
  signed int result; // r0@6
  char v9; // [sp+8h] [bp-420h]@5
  int v10; // [sp+40Ch] [bp-1Ch]@1

  v3 = a1;
  v4 = a3;
  v10 = __stack_chk_guard;
  v5 = sandbox_check();
  v6 = v5;
  if ( v5 )
    v5 = 1;
  if ( v4 && v5 == 1 )
  {
    memset(&v9, 0, 0x401u);
    v7 = CFStringCreateMutable(0, 0);
    *(_DWORD *)v4 = v7;
    sub_2EB7F644(v3, &v9);
    CFStringAppendFormat(v7, 0, CFSTR("pid %d (%s) does not have sandbox access for %@"), v3);
  }
  result = 0;
  if ( !v6 )
    result = 1;
  if ( __stack_chk_guard != v10 )
    __stack_chk_fail(result);
  return result;
}

comme décrit ici , UDID est calculé comme ceci:

UDID = SHA1(serial + ECID + wifiMac + bluetoothMac)

MobileGestalt obtient ces valeurs via IOKit comme ceci:

CFMutableDictionaryRef service = IOServiceMatching("IOPlatformExpertDevice");
    io_service_t ioservice = IOServiceGetMatchingService(kIOMasterPortDefault, service);
    CFTypeRef entry = IORegistryEntryCreateCFProperty(ioservice, CFSTR("IOPlatformSerialNumber"), kCFAllocatorDefault, 0);

    const UInt8 * data = CFDataGetBytePtr(entry);
    CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, data, kCFStringEncodingUTF8);

si vous essayez de le faire vous-même, il échouera parce que les nouvelles règles sandbox dans iOS 8.3 sont très strictes et refusent l'accès à tous les identificateurs matériels comme ceci:

deny iokit-get-properties IOPlatformSerialNumber

Solution Possible

il semble que le seul moyen que vous pouvez obtenir UDID est le suivant:

  1. lancer un serveur web à l'intérieur de l'application avec deux pages: l'un devrait retourner le profil de configuration MobileConfiguration spécialement conçu et l'autre devrait recueillir UDID. Plus d'infos ici , ici et ici .
  2. vous ouvrez la première page de Mobile Safari à l'intérieur de l'application et il vous redirige vers les paramètres.application demandant d'installer le profil de configuration. Après avoir installé le profil, UDID est envoyé à la deuxième page web et vous pouvez y accéder à partir de l'intérieur de l'application. (Paramètre.app a tous les droits nécessaires et différentes règles de bac à sable).

solution de travail confirmée

voici un exemple basé sur RoutingHTTPServer :

import UIKit
import RoutingHTTPServer

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var bgTask = UIBackgroundTaskInvalid
    let server = HTTPServer()

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        application.openURL(NSURL(string: "http://localhost:55555")!)
        return true
    }

    func applicationDidEnterBackground(application: UIApplication) {
        bgTask = application.beginBackgroundTaskWithExpirationHandler() {
            dispatch_async(dispatch_get_main_queue()) {[unowned self] in
                application.endBackgroundTask(self.bgTask)
                self.bgTask = UIBackgroundTaskInvalid
            }
        }
    }
}

class HTTPServer: RoutingHTTPServer {
    override init() {
        super.init()
        setPort(55555)
        handleMethod("GET", withPath: "/") {
            .setHeader("Content-Type", value: "application/x-apple-aspen-config")
            .respondWithData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("udid", ofType: "mobileconfig")!)!)
        }
        handleMethod("POST", withPath: "/") {
            let raw = NSString(data:"151940920".body(), encoding:NSISOLatin1StringEncoding) as! String
            let plistString = raw.substringWithRange(Range(start: raw.rangeOfString("<?xml")!.startIndex,end: raw.rangeOfString("</plist>")!.endIndex))
            let plist = NSPropertyListSerialization.propertyListWithData(plistString.dataUsingEncoding(NSISOLatin1StringEncoding)!, options: .allZeros, format: nil, error: nil) as! [String:String]

            let udid = plist["UDID"]! 
            println(udid) // Here is your UDID!

            .statusCode = 200
            .respondWithString("see https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/ConfigurationProfileExamples/ConfigurationProfileExamples.html")
        }
        start(nil)
    }
}

voici le contenu de udid.mobileconfig :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>PayloadContent</key>
        <dict>
            <key>URL</key>
            <string>http://localhost:55555</string>
            <key>DeviceAttributes</key>
            <array>
                <string>IMEI</string>
                <string>UDID</string>
                <string>PRODUCT</string>
                <string>VERSION</string>
                <string>SERIAL</string>
            </array>
        </dict>
        <key>PayloadOrganization</key>
        <string>udid</string>
        <key>PayloadDisplayName</key>
        <string>Get Your UDID</string>
        <key>PayloadVersion</key>
        <integer>1</integer>
        <key>PayloadUUID</key>
        <string>9CF421B3-9853-9999-BC8A-982CBD3C907C</string>
        <key>PayloadIdentifier</key>
        <string>udid</string>
        <key>PayloadDescription</key>
        <string>Install this temporary profile to find and display your current device's UDID. It is automatically removed from device right after you get your UDID.</string>
        <key>PayloadType</key>
        <string>Profile Service</string>
    </dict>
</plist>

l'installation du profil va échouer (je n'ai pas pris la peine de mettre en œuvre une réponse attendue, voir documentation ), mais l'application obtiendra un UDID correct. Et vous devez également signer le mobileconfig .

14
répondu bzz 2017-05-23 12:10:08

je suis désolé de dire que apparemment à partir de iOS 8.3, Pour obtenir un identifiant unique, vous avez besoin d'un niveau d'accès plus élevé que l'utilisateur normal.

sans exploiter quoi que ce soit, juste avec des cadres privés, des bibliothèques et des requêtes du noyau, toute requête à des identificateurs uniques renvoie null.

illustrant:

Essaie d'utiliser IOKit:

void *IOKit = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW);
if (IOKit)
{
    mach_port_t *kIOMasterPortDefault = dlsym(IOKit, "kIOMasterPortDefault");
    CFMutableDictionaryRef (*IOServiceMatching)(const char *name) = dlsym(IOKit, "IOServiceMatching");
    mach_port_t (*IOServiceGetMatchingService)(mach_port_t masterPort, CFDictionaryRef matching) = dlsym(IOKit, "IOServiceGetMatchingService");
    CFTypeRef (*IORegistryEntryCreateCFProperty)(mach_port_t entry, CFStringRef key, CFAllocatorRef allocator, uint32_t options) = dlsym(IOKit, "IORegistryEntryCreateCFProperty");
    kern_return_t (*IOObjectRelease)(mach_port_t object) = dlsym(IOKit, "IOObjectRelease");

    if (kIOMasterPortDefault && IOServiceGetMatchingService && IORegistryEntryCreateCFProperty && IOObjectRelease)
    {
        mach_port_t platformExpertDevice = IOServiceGetMatchingService(*kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
        if (platformExpertDevice)
        {
            CFTypeRef platformSerialNumber = IORegistryEntryCreateCFProperty(platformExpertDevice, CFSTR("IOPlatformSerialNumber"), kCFAllocatorDefault, 0);
            if (platformSerialNumber && CFGetTypeID(platformSerialNumber) == CFStringGetTypeID())
            {
                serialNumber = [NSString stringWithString:(__bridge NSString *)platformSerialNumber];
                CFRelease(platformSerialNumber);
            }
            IOObjectRelease(platformExpertDevice);
        }
    }
    dlclose(IOKit);
}

échoue. Raison: IOPlatformSerialNumber n'est pas accessible. De nombreux autres les demandes de travail de l'amende.

essayer d'utiliser les appels Mach pour obtenir des adaptateurs réseau HW IDs:

int         mib[6], 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) {
    perror("if_nametoindex error");
    exit(2);
}

if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
    perror("sysctl 1 error");
    exit(3);
}

if ((buf = malloc(len)) == NULL) {
    perror("malloc error");
    exit(4);
}

if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
    perror("sysctl 2 error");
    exit(5);
}

ifm = (struct if_msghdr *)buf;
sdl = (struct sockaddr_dl *)(ifm + 1);
ptr = (unsigned char *)LLADDR(sdl);
printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *ptr, *(ptr+1), *(ptr+2),
       *(ptr+3), *(ptr+4), *(ptr+5));

échoue. Motif: renvoie 02:00:00:00:00:00 pour tout adaptateur réseau.

essayer de se connecter à lockdownd:

void *libHandle = dlopen("/usr/lib/liblockdown.dylib", RTLD_LAZY);
if (libHandle)
{
    lockdown_connect = dlsym(libHandle, "lockdown_connect");
    lockdown_copy_value = dlsym(libHandle, "lockdown_copy_value");

    id connection = lockdown_connect();
    NSString *kLockdownDeviceColorKey
    NSString *color = lockdown_copy_value(connection, nil, kLockdownDeviceColorKey);
    NSLog(@"color = %@", color);
    lockdown_disconnect(connection);

    dlclose(libHandle);
}
else {
    printf("[%s] Unable to open liblockdown.dylib: %s\n",
           __FILE__, dlerror());
}

échoue. La raison: lockdown_connect() échoue, le retour null .

Essaie d'utiliser libMobileGestalt:

void *libHandle = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_LAZY);
if (libHandle)
{
    MGCopyAnswer = dlsym(libHandle, "MGCopyAnswer");

    NSString* value = MGCopyAnswer(CFSTR("SerialNumber"));
    NSLog(@"Value: %@", value);
    CFRelease(value);
}

échoue. Raison: demandes d'identificateurs uniques retourner null . Toute autre demande fonctionne très bien.

Ma suggestion est d'utiliser un certain privilège d'escalade technique pour obtenir l'accès super-utilisateur, exécutez ensuite l'une des méthodes énumérées ici pour obtenir la propriété.

aussi, étendre l'étude sur liblockdown. Si elle est accessible au niveau de l'utilisateur (avec quelque chose d'autre que lockdown_connect), il pourrait être possible de lire ces choses.

15
répondu gbuzogany 2015-04-27 08:29:04

vous pouvez essayer d'accéder à lockdownd API directement, via libMobileGestalt.dylib .

en-Tête ici . Le code de base pour accéder à L'UDID devrait être: (vous avez encore besoin de charger le dylib)

CFStringRef udid = (CFStringRef)MGCopyAnswer(kMGUniqueDeviceID);

pris (et légèrement modifié) de ici .

pour plus d'informations sur libMobileGestalt , regardez ici .

si ce Échecs vous pouvez toujours essayer de communiquer avec lockdownd via une Socket SSL( voir ici ), aucune idée de comment cela fonctionne cependant, mais vous pourriez le comprendre.

Comme vous l'avez remarqué, tous les trucs sur cette est ans. Encore vaut le coup d'essayer je pense.

3
répondu Malte 2015-04-26 22:05:36

Je ne suis pas sûr de vos intentions, mais serait-il suffisant que l'application génère et stocke votre propre ID unique dans l'application lors de l'installation? Alors peut-être que l'application envoie cet ID à un serveur et stocke L'IP d'où il vient. Peut-être aussi avoir une certaine logique d'avoir l'app Téléphone maison de temps en temps ainsi vous pouvez stocker des IP supplémentaires si elles changent. Évidemment, ce n'est pas infaillible, mais il est peut-être le début d'une solution de contournement.

2
répondu Rel 2015-04-23 01:32:05

Qu'en est-il: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDevice_Class/#//apple_ref/occ/instp/UIDevice/identifierForVendor

chaîne alphanumérique qui identifie de façon unique un périphérique au vendeur de l'application. (en lecture seule)

La valeur de cette propriété est la même pour les applications qui viennent de la même fournisseur tournant sur le même appareil. Une valeur différente est retournée pour les applications sur le même appareil qui viennent de différents fournisseurs, et pour applications sur différents appareils, quel que soit le fournisseur.

depuis iOS 6 et courant dans iOS 8

cela répond à votre exigence de:

tout identificateur matériel permanent suffisamment unique pour identifier un appareil qui persisterait malgré les essuie-glaces de l'appareil et parmi les différentes versions d'iOS

C'est documenté pour être unique par appareil et persistante que ce soit de app store ou d'entreprise livré.

Normalement, le vendeur est déterminé par les données fournies par l'App Store. Si l'application n'a pas été installée à partir de l'app store (comme les applications d'entreprise et les applications encore en développement), alors un identifiant de fournisseur est calculé sur la base de l'identifiant de groupe de l'application. L'ID de faisceau est supposé être dans le format inverse-DNS.

2
répondu Rel 2015-04-23 04:53:31

Je ne sais pas si les solutions commerciales vous intéressent, mais regardez http://www.appsflyer.com

Je ne suis pas affilié à eux mais nous avons utilisé leur SDK chez mon employeur précédent. Ils ont une technologie d'empreintes digitales qui fonctionne.

Note: Si L'utilisateur réinitialise L'IDFA, AppsFlyer le verra comme un nouveau périphérique. Cependant, et ça fait longtemps donc je ne me souviens pas, je pense que vous pouvez utiliser leur SDK, pas utiliser AdSupport.et puis ils n'auront pas L'IDFA à leur disposition. Donc je suppose que leurs empreintes digitales peuvent marcher.

ils ont également des concurrents, la recherche d'empreintes digitales de l'appareil. Regarde Yozio et branch.io, ils prétendent tous les deux faire ça. Je n'ai pas utilisé leur produit, juste vu leurs sites web.

2
répondu Paul Cezanne 2015-04-23 23:20:39

en fait, je ne sais pas si cette solution est utile ou non. mais après le soutien retiré de UDID. j'ai gérer l'identité unique de l'appareil de la façon suivante. avec l'aide de Vendor ID . Voici ce que l'on fait.

comme initiale pendant que l'application va courir, je vais vérifier que le temps vendor ID pour l'application spécifique est stored in key chain or not . s'il n'est pas stocké, alors je stockerai ce vendor ID dans key chain . donc la deuxième fois une fois mon application va vérifier à nouveau que le vendeur de la météo ID pour l'application spécifique il stocké de pas dans la chaîne de clés. si stocké puis l'apporter de la chaîne de clés et de faire action sur la même selon l'exigence. so here alway vendor ID is unique for device.

Voici les étapes que nous gardons unicité de l'appareil avec l'aide de vendor ID .

Étape 1 : Intégrez Lockbox dans votre projet. cela vous aidera à stored/retrived vendeur ID in/from chaîne de clés.

Step 2 : voici le code qui exécute l'action de checking vendor ID et retrieving vendor ID de la chaîne à clé.

-(NSString*)getidentifierForVendor{
    NSString *aStrExisting = [Lockbox stringForKey:[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleIdentifierKey]];

    if (aStrExisting == Nil) {
        NSString *aVendorID = [[[UIDevice currentDevice]identifierForVendor]UUIDString];
        aStrExisting=aVendorID;
        [Lockbox setString:aVendorID forKey:[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleIdentifierKey]];
        return aVendorID;
    }else{
        return aStrExisting;
    }

avec l'aide des étapes ci-dessus, vous obtenez toujours unicité de l'appareil. parce que la chaîne à clé n'est jamais effacée. il a toujours mis à jour.

J'espère que cela vous aidera...

1
répondu Jatin Patel 2015-04-29 11:04:32

Êtes-vous autorisé à demander à l'utilisateur leurs MSISDN? (numéros de téléphone internationaux) comme ce que fait whatsApp lorsque vous vous connectez avec eux en confirmant avec un code SMS envoyé à l'utilisateur msisdn. Si vous connaissez les MSISDN de vos utilisateurs, vous pouvez les conserver dans la base de données de vos serveurs et n'autoriser qu'un msisdn de liste blanche à s'inscrire et à utiliser vos services. Si vous voulez être plus sûr, vous pouvez envoyer des SMS plus souvent, mais il ya un moyen de comprendre le changement de carte SIM (c'est pour t-mobile et l'europe utilisent je suppose) à partir de l'application de sorte que l'utilisateur ne peut pas vous tromper en créant SMS pour un MSISDN différent et ensuite changer à son/sa carte sim réelle MSISDN.

MSISDNs sont uniques dans le monde entier et ils sont sécurisés par les opérateurs télécom donc je suppose que cette solution est strictement sécurisé. Qu'en dites-vous? bonne chance

mise à jour: en fait, en lisant votre question attentivement à nouveau, je pense que vous ne voulez pas que l'utilisateur de se connecter n'importe quelle information droite? si c'est le cas désolé pour la mauvaise réponse:/

note: pourquoi ne pouvez-vous pas utiliser

[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]

l'identificateur publicitaire est unique à l'appareil et permanent je suppose. Je ne sais pas si vous pouvez l'utiliser en privé..

1
répondu aytunch 2015-04-29 19:43:56