33

We're working on a project with my colleagues which involves using a lot of private and non official code. This is not intended for AppStore use.

The first and only requirement we have is to not use jailbreak.

First of all, UDID or OpenUDID or any other solutions don't work here and they're not expected to.

We've done a lot of background research and tests, starting with trying to get the IMEI, ICCID, IMSI and the Serial Number programatically. None of the above methods work with iOS 7 and above without jailbreak.

We've also spent a couple of months to play with IOKit framework using the famous IOKitBrowser and dumping the whole contents of iOS internals. Unfortunately, we discovered that with iOS 8.3 it stopped working.

We're talking here not about getting the UDID or any other "mainstream" thing, but generally speaking we need a way to get

any permanent hardware identifier unique enough to identify a device that would persist in spite of device wipes and amongst different iOS versions

This question is no duplicate to others (no solutions are found here, for example) and is targeting solely private APIs.

Any help would be highly appreciated.

Community
  • 1
  • 1
Sergey Grischyov
  • 11,729
  • 18
  • 76
  • 117
  • 1
    possible duplicate of [UIDevice uniqueIdentifier Deprecated - What To Do Now?](http://stackoverflow.com/questions/6993325/uidevice-uniqueidentifier-deprecated-what-to-do-now) – Paulw11 Apr 21 '15 at 00:51
  • The only way you can get a unique device identifier is through the MDM route - you need to install a device management profile on the device and your server can then access the UDID – Paulw11 Apr 21 '15 at 00:53
  • @Paulw11 thanks for the idea, but unfortunately this wouldn't help. And our question is not a duplicate - this one is all about **private apis** and not public ones, including those deprecated. – Sergey Grischyov Apr 21 '15 at 09:06
  • I suspect that many people don't quite like the tone of your question. – dandan78 Apr 21 '15 at 09:20
  • @dandan78 What's exactly wrong with it? – Sergey Grischyov Apr 21 '15 at 09:21
  • This question sounds like something the jailbreak community could answer very easily either at IRC or twitter. Anyway, I will be posting an answer soon. – gbuzogany Apr 24 '15 at 13:30
  • @gbuzogany That would be really helpful of you, sir. – Sergey Grischyov Apr 24 '15 at 16:38
  • I'm impressed. Got much trickier on 8.3. MobileGestalt returns null for any identifier. XNU calls to get network interfaces hardware address returns bogus number. IOKit values are hidden. My guess is that it is only accessible at lockdown... I will check soon and update with my results. – gbuzogany Apr 25 '15 at 09:01

8 Answers8

16

I'm sorry to say that apparently from iOS 8.3, to get any unique identifier you need a higher access level than normal user.

Without exploiting anything, just with private frameworks, libraries and kernel requests, any request to unique identifiers returns null.

Illustrating:

Trying to use 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);
}

Fails. Reason: IOPlatformSerialNumber is not accessible. Many other requests work fine.

Trying to use Mach calls to get network adapters 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));

Fails. Reason: Returns 02:00:00:00:00:00 for any network adapter.

Trying to connect to 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());
}

Fails. Reason: lockdown_connect() fails, returning null.

Trying to use 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);
}

Fails. Reason: requests for unique identifiers return null. Any other request works fine.

My suggestion is to use some privilege escalation technique to get superuser access, then run any of the methods listed here to get the property.

Also, extend the study on liblockdown. If it's accessible at user-level (with something other than lockdown_connect), it might be possible to read these things.

gbuzogany
  • 1,892
  • 16
  • 17
  • Wow, that's certainly an amazing study! However, how about we don't seek for UDID per se, but for some other hardware IDs, mainly **any** unique hardware ID, like manufacture date - that would fit perfectly. Any idea on that? – Sergey Grischyov Apr 27 '15 at 08:19
  • I looked for any unique identifier, not just UDID. Apparently they mapped ANY unique device identifier and covered it. The iOS 8.3 brought [many](http://www.intego.com/mac-security-blog/apples-colossal-ios-8-3-update-kills-58-ios-security-bugs/) [security updates](https://support.apple.com/en-us/HT204661), so I think it was security-focused, because it didn't bring much else. I looked for network hardware identifiers, serial numbers, manufacturing dates, bluetooth minor/major ID, IMEI, baseband IDs, all that is buried. – gbuzogany Apr 27 '15 at 08:26
15

After some digging, I've found that all private APIs use libMobileGestalt for getting any hardware identifiers, which in turn uses IOKit. MobileGestalt checks sandbox rules for current pid and looks for com.apple.private.MobileGestalt.AllowedProtectedKeys entitlement.

See the code below:

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;
}

As described here, UDID is calculated like this:

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

MobileGestalt gets these values via IOKit like this:

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);

If you try to do it yourself, it will fail because new sandbox rules in iOS 8.3 are very strict and deny access to all hardware identifiers like this:

deny iokit-get-properties IOPlatformSerialNumber

Possible Solution

It looks like the only way you can get UDID is the following:

  1. Launch a web server inside the app with two pages: one should return specially crafted MobileConfiguration profile and another should collect UDID. More info here, here and here.
  2. You open the first page in Mobile Safari from inside the app and it redirects you to Settings.app asking to install configuration profile. After you install the profile, UDID is sent to the second web page and you can access it from inside the app. (Settings.app has all necessary entitlements and different sandbox rules).

Confirmed working solution

Here is an example based on 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: "/") {
            $1.setHeader("Content-Type", value: "application/x-apple-aspen-config")
            $1.respondWithData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("udid", ofType: "mobileconfig")!)!)
        }
        handleMethod("POST", withPath: "/") {
            let raw = NSString(data:$0.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!

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

Here are the contents of 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>

The profile installation will fail (I didn't bother to implement an expected response, see documentation), but the app will get a correct UDID. And you should also sign the mobileconfig.

Community
  • 1
  • 1
bzz
  • 5,368
  • 22
  • 25
  • 1
    Never knew it would be possible to set up a scheme like you described. We'll check this out, thanks – Sergey Grischyov Apr 27 '15 at 11:00
  • 1
    After weeks of findings, I'm back to confirm that all existing solutions require `php` scripts to be run to collect data. We have configured the local iOS server and the profile itself. It installs and it even redirects back to our built-in server, but it either requires a `php` script, or some `p7s` file is being returned, or it requires `HTTPS` altogether. And I'm not sure any of the server solutions know how to handle `php`. I'm ready to open a new question with a bounty of 500 if you could help. We see that probably no one has ever solved such a task before and we need any help. Thanks – Sergey Grischyov Jun 03 '15 at 21:55
  • @SergiusGee The `p7s` file is a signed plist that contains UDID. I've updated my answer with the code that contains UDID extraction technique. – bzz Jun 04 '15 at 17:43
3

You could try accessing lockdownd API directly, via libMobileGestalt.dylib.

Header here. The basic code to access the UDID should be: (you still need to load the dylib)

CFStringRef udid = (CFStringRef)MGCopyAnswer(kMGUniqueDeviceID);

taken (&slightly modified) from here.

For further information on libMobileGestalt , look here.

If this fails you could still try communicating with lockdownd via an SSL Socket( see here ), no idea how that works though, but you might figure it out.

As you may have noticed though, all stuff on this is years old. Still worth a try i guess.

Malte
  • 644
  • 1
  • 5
  • 20
  • Wow, that's a great one. I'll check this out iPad get back to you. Thanks – Sergey Grischyov Apr 26 '15 at 22:10
  • Tried it with MobileGestalt, also returns null. Trying to connect to lockdown using lockdown_connect() returns null. – gbuzogany Apr 27 '15 at 07:24
  • @gbuzogany, you also need [an entitlement to make this work](http://stackoverflow.com/a/27686125/119114), which limits you to jailbroken devices. – Nate Mar 15 '17 at 06:41
2

I am unsure of your full intentions but would it be enough to have the app generate and store your own unique ID within the app on installation? Then perhaps have the app send that ID to a server and store the IP it came from. Perhaps also have some logic to have the app phone home every so often so you can store additional IP's if they change. Obviously this is not foolproof but it may be the beginnings of a workaround.

Rel
  • 1,646
  • 10
  • 14
  • Great idea, but how do you guarantee that the dynamic IP addresses on mobile networks that are selected randomly don't intersect once in a while? And after reinstall the app would generate a new ID while I would still need the one and only it sent me before – Sergey Grischyov Apr 23 '15 at 01:34
  • I can't which is why it is not foolproof but it may be enough depending on your intentions. What are you trying to prevent / track? – Rel Apr 23 '15 at 03:52
  • If EIMI was one of your previous options, is it safe to say all your devices will be cell/mobile capable? ie not ipod touch. – Rel Apr 23 '15 at 04:16
2

What about this: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDevice_Class/#//apple_ref/occ/instp/UIDevice/identifierForVendor

An alphanumeric string that uniquely identifies a device to the app’s vendor. (read-only)

The value of this property is the same for apps that come from the same vendor running on the same device. A different value is returned for apps on the same device that come from different vendors, and for apps on different devices regardless of vendor.

Since iOS 6 and current in iOS 8

This meets your requirement of:

any permanent hardware identifier unique enough to identify a device that would persist in spite of device wipes and amongst different iOS versions

This is documented to be unique per device and persistent whether from app store or enterprise delivered.

Normally, the vendor is determined by data provided by the App Store. If the app was not installed from the app store (such as enterprise apps and apps still in development), then a vendor identifier is calculated based on the app’s bundle ID. The bundle ID is assumed to be in reverse-DNS format.

Rel
  • 1,646
  • 10
  • 14
2

I don't know if commercial solutions are of interest to you but check out http://www.appsflyer.com

I'm not affiliated with them but we used their SDK at my previous employer. They have a device fingerprint technology which does work.

Note: If the user resets the IDFA then AppsFlyer will see this as a new device. However, and its been awhile so I can't remember, I think you can use their SDK, not use AdSupport.framework, and then they won't have the IDFA available to them. So I'm guessing that their device fingerprinting may work.

They also have competitors, search for device fingerprinting. Check out Yozio and branch.io, they both claim to do this. I've not used their product, just seen their websites.

Paul Cezanne
  • 8,560
  • 7
  • 55
  • 85
  • Thanks for the great advice. We'll check it out – Sergey Grischyov Apr 23 '15 at 19:01
  • Let us know if the non IDFA trick works. If I recall their doc was a bit unclear on that. – Paul Cezanne Apr 23 '15 at 19:05
  • I worked on the Branch.io iOS SDK, and I know the other companies in the space well. Everyone just uses IDFA for privacy reasons. We don't want to put our app partners at risk with anything too risky. Apple already randomly rejects for misuse of collection of the IDFA, so be careful out there. – Alex Austin Jul 01 '15 at 23:10
2

Actually i don't know this solution is helpful or not. but after removed support of UDID. i have manage Unique device identity following way. with help of Vendor ID. Here what we did that.

As initial while application will run, i will check out that weather vendor ID for specific app is stored in key chain or not. if it not stored then i will store that vendor ID in key chain. so second time once my app is going to check again that weather vendor ID for specific app it stored of not in key chain. if stored then bring it from key chain and doing action on same according to requirement. so here alway vendor ID is unique for device.

Here are the steps that we keep uniqueness of device with help of vendor ID.

Step 1 : Integrate Lockbox in your project. that will help you to stored/retrived vendor ID in/from key chain.

Step 2 : Here are the code that perform action of checking vendor ID and retrieving vendor ID from key chain.

-(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;
    }

With help of above steps, you always get uniqueness of device. because key chain is never deleted. it always updated.

Hope this help you...

Jatin Patel - JP
  • 3,649
  • 2
  • 17
  • 42
  • And on device wipe the Vendor ID will be different. That's why we can't use it – Sergey Grischyov Apr 29 '15 at 11:08
  • @SergiusGee, ya, know that but we need to check first that if vendor ID for specific `App ID` is present in key chain or what. if it present then we don't need to used new vendor ID. just fetched initial stored vendor ID from key chain. so it will become unique for application --> device. – Jatin Patel - JP Apr 29 '15 at 11:11
  • and what happens when the keychain is wiped? It's a totally new identifier then – Sergey Grischyov Apr 29 '15 at 11:48
  • I think key chain is never wipe. There will be add or update. – Jatin Patel - JP Apr 29 '15 at 12:19
  • won't it be wiped on a full device wipe, reset to factory? – Paul Cezanne Apr 29 '15 at 12:39
  • Yes, if user restore iPhone as new iPhone then key chain is deleted. But there is option that restore from backups so all key chain contents come back. I am sure that there wan't Be case that if Iphone user is same even though he don't want to restore from backup. And also according to me any app feature is alway related to the user + iPhone. If user changed, iPhone is purchased by another user then app will ask to purchase once again. Also I am sure that 99% user never resets iPhone as new iPhone. They are always take backup and restore from backup. – Jatin Patel - JP Apr 29 '15 at 14:31
  • There is another way you will get uniqueness with help of "Device Tocken" that is used to sent push notifications. You can used it your way to find uniqueness – Jatin Patel - JP Apr 29 '15 at 14:51
  • @None sorry, but that doesn't cover 100% of use cases and your guess about the keychain irrevocably wrong. – Sergey Grischyov Apr 29 '15 at 19:13
2

Are you allowed to ask the user for their MSISDN's? (international phone numbers) Like what whatsApp is doing when you first login with them by confirming with a SMS code sent to the user msisdn. If you know the MSISDN's of your users, you can keep them in your servers DB and only allow a white-listed msisdn to register and use your services. If you want to be more secure you can send the SMS's more often however there is a way to understand SIM-card change (this is for t-mobile and europe use i guess) from within the APP so that the user cant fool you by enetering SMS for a different MSISDN and then change to his/her real MSISDN sim card.

MSISDNs are unique worldwide and they are secured by the telecom operators so i guess this solution is strictly secure. What do you say? good luck

update: actually upon reading your question carefully again, I think you dont want the user to login any info right? if thats the case sorry for the wrong answer:/

note: why cant you use

[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]

the advertising identifier is unique to the device and permanent i guess. I dont know if you can use it privately though..

aytunch
  • 1,035
  • 11
  • 24