Подтвердить что ты не робот

Частный уникальный идентификатор устройства в iOS

Мы работаем над проектом с моими коллегами, который предполагает использование большого количества частного и неофициального кода. Это не, предназначенный для использования AppStore.

Первое и единственное требование - не использовать джейлбрейк.

Прежде всего, UDID или OpenUDID или любые другие решения не работают здесь, и они не ожидаются.

Мы провели много фоновых исследований и тестов, начиная с попытки получить IMEI, ICCID, IMSI и Серийный номер программно. Ни один из вышеперечисленных методов не работает с iOS 7 и выше без джейлбрейка.

Мы также потратили пару месяцев, чтобы играть в рамки IOKit, используя знаменитый IOKitBrowser и сбрасывая все содержимое из iOS внутренних элементов. К сожалению, мы обнаружили, что с iOS 8.3 он перестает работать.

Мы говорим здесь не о получении UDID или любой другой "основной" вещи, но вообще нам нужен способ получить

любой постоянный идентификатор оборудования, достаточно уникальный, чтобы идентифицировать устройство, которое будет сохраняться, несмотря на стирание устройства и среди разных версий iOS

Этот вопрос не дублируется другим (здесь не найдены решения) и нацеливается исключительно на частные API.

Любая помощь будет высоко оценена.

4b9b3361

Ответ 1

После некоторого копания я обнаружил, что все частные API используют libMobileGestalt для получения любых идентификаторов оборудования, которые, в свою очередь, используют IOKit. MobileGestalt проверяет правила песочницы для текущего pid и ищет права com.apple.private.MobileGestalt.AllowedProtectedKeys.

Смотрите код ниже:

signed int __fastcall sub_2EB8803C(int a1, int a2, int a3, int a4)
{
  int v4; // [email protected]
  int v5; // [email protected]
  int v6; // [email protected]
  int v7; // [email protected]
  int v8; // [email protected]
  int v9; // [email protected]
  int v10; // [email protected]
  int v11; // [email protected]
  int v12; // [email protected]
  signed int v13; // [email protected]
  int v14; // [email protected]
  char *v15; // [email protected]
  int v16; // [email protected]
  int v17; // [email protected]
  int v18; // [email protected]
  int v19; // [email protected]
  signed int v20; // [email protected]
  int v21; // [email protected]
  __CFString *v22; // [email protected]
  int v23; // [email protected]
  __CFString *v24; // [email protected]
  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; // [email protected]
  int v4; // [email protected]
  int v5; // [email protected]
  int v6; // [email protected]
  int v7; // [email protected]
  signed int result; // [email protected]
  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;
}

Как описано здесь, UDID рассчитывается следующим образом:

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

MobileGestalt получает эти значения через IOKit следующим образом:

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

Если вы попытаетесь сделать это самостоятельно, это будет терпеть неудачу, потому что новые правила песочницы в iOS 8.3 очень строги и запрещают доступ ко всем идентификаторам оборудования, например:

deny iokit-get-properties IOPlatformSerialNumber

Возможное решение

Похоже, что единственный способ получить UDID - это следующее:

  • Запустите веб-сервер внутри приложения с двумя страницами: нужно вернуть специально созданный профиль MobileConfiguration, а другой должен собирать UDID. Подробнее здесь, здесь и здесь.
  • Вы открываете первую страницу в Mobile Safari изнутри приложения и перенаправляете на Settings.app с просьбой установить профиль конфигурации. После установки профиля UDID отправляется на вторую веб-страницу, и вы можете получить доступ к нему изнутри приложения. (Settings.app имеет все необходимые права и различные правила для песочницы).

Подтвержденное рабочее решение

Вот пример, основанный на 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)
    }
}

Вот содержимое 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 UDID. It is automatically removed from device right after you get your UDID.</string>
        <key>PayloadType</key>
        <string>Profile Service</string>
    </dict>
</plist>

Установка профиля завершится неудачно (я не потрудился реализовать ожидаемый ответ, см. documentation), но приложение будет получить правильный UDID. И вы также должны подписать mobileconfig.

Ответ 2

Мне жаль говорить, что, по-видимому, из iOS 8.3, чтобы получить уникальный идентификатор, вам нужен более высокий уровень доступа, чем обычный пользователь.

Без использования чего-либо, только с помощью частных фреймворков, библиотек и запросов ядра любой запрос к уникальным идентификаторам возвращает null.

Иллюстрируя:

Попытка использовать 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);
}

Не удается. Причина: IOPlatformSerialNumber недоступен. Многие другие запросы работают нормально.

Попытка использовать вызовы Mach для получения сетевых адаптеров HW ID:

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

Не удается. Причина. Возвращает 02:00:00:00:00:00 для любого сетевого адаптера.

Попытка подключения к 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());
}

Не удается. Причина: lockdown_connect() не удается, возвращая null.

Попытка использования 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);
}

Не удается. Причина: запросы для уникальных идентификаторов возвращают null. Любой другой запрос работает нормально.

Мое предложение состоит в том, чтобы использовать некоторую технику эскалации привилегий для получения доступа суперпользователя, а затем запустить любой из перечисленных здесь методов, чтобы получить свойство.

Также распространите исследование на liblockdown. Если он доступен на уровне пользователя (с чем-то отличным от lockdown_connect), возможно, будет возможно прочитать эти вещи.

Ответ 3

Вы можете напрямую обратиться к lockdownd API через libMobileGestalt.dylib.

Заголовок здесь. Основной код для доступа к UDID должен быть: (вам все равно нужно загрузить dylib)

CFStringRef udid = (CFStringRef)MGCopyAnswer(kMGUniqueDeviceID);

взято (слегка изменено) из здесь.

Для получения дополнительной информации о libMobileGestalt, посмотрите здесь.

Если это не удается, вы все равно можете попытаться связаться с lockdownd через SSL Socket (см. здесь), не знаю, как это работает, но вы можете понять это.

Как вы, возможно, заметили, все это в этом возрасте. Еще стоит попробовать, я думаю.

Ответ 4

Я не уверен в ваших полных намерениях, но будет ли достаточно, чтобы приложение создало и сохранила ваш собственный уникальный идентификатор в приложении при установке? Тогда, возможно, приложение отправит этот идентификатор на сервер и сохранит IP-адрес, из которого он пришел. Возможно, также есть некоторая логика, чтобы иметь телефон приложения домой так часто, чтобы вы могли хранить дополнительный IP-адрес, если они меняются. Очевидно, что это не является надежным, но это может быть началом обхода.

Ответ 5

Как насчет этого: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDevice_Class/#//apple_ref/occ/instp/UIDevice/identifierForVendor

Буквенно-цифровая строка, которая однозначно идентифицирует устройство для поставщика приложений. (только для чтения)

Значение этого свойства одинаково для приложений, которые поступают из тот же самый поставщик работает на одном устройстве. Возвращается другое значение для приложений на том же устройстве, что и у разных поставщиков, и для приложений на разных устройствах независимо от поставщика.

Так как iOS 6 и ток в iOS 8

Это соответствует вашим требованиям:

любой постоянный идентификатор оборудования, достаточно уникальный, чтобы идентифицировать устройство, которое будет сохраняться, несмотря на стирание устройства и среди разных версий iOS

Это задокументировано как уникальное для каждого устройства и постоянное, будь то в магазине приложений или на предприятии.

Обычно поставщик определяется данными, предоставленными в App Store. Если приложение не было установлено из магазина приложений (например, корпоративные приложения и приложения, все еще находящиеся в разработке), то идентификатор поставщика рассчитывается на основе идентификатора пакета приложений. Предполагается, что идентификатор пакета имеет формат обратного DNS.

Ответ 6

Я не знаю, интересуют ли вас коммерческие решения, но посмотрите http://www.appsflyer.com

Я не связан с ними, но мы использовали SDK у моего предыдущего работодателя. У них есть технология отпечатков пальцев устройства, которая работает.

Примечание. Если пользователь сбрасывает IDFA, то приложение AppsFlyer увидит это как новое устройство. Тем не менее, и это было некоторое время, поэтому я не могу вспомнить, я думаю, вы можете использовать их SDK, а не использовать AdSupport.framework, и тогда у них не будет доступной для них IDFA. Поэтому я предполагаю, что их отпечатки пальцев на устройстве могут работать.

У них также есть конкуренты, поиск отпечатков пальцев устройства. Отъезд Yozio и branch.io, они оба утверждают, что делают это. Я не использовал их продукт, просто видел их веб-сайты.

Ответ 7

На самом деле я не знаю, что это решение полезно или нет. но после удаления поддержки UDID. Я управляю уникальной идентификацией устройства следующим образом. с помощью Vendor ID. Вот что мы сделали.

Как начальное, пока приложение будет работать, я проверю, что погода Vendor ID для конкретного приложения stored in key chain or not. если он не сохранен, тогда я сохраню это Vendor ID в key chain. так что второй раз, когда мое приложение снова проверит, что идентификатор поставщика погоды для конкретного приложения хранится не в цепочке ключей. если они сохранены, то принесите его из цепочки ключей и выполните действие на том же уровне в соответствии с требованием. so here alway vendor ID is unique for device.

Вот шаги, которые мы сохраняем уникальность устройства с помощью Vendor ID.

Шаг 1: интегрируйте Lockbox в свой проект. которые помогут вам в цепочке stored/retrived идентификатор поставщика in/from.

Шаг 2. Вот код, который выполняет действие checking vendor ID и retrieving vendor ID из цепочки ключей.

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

С помощью вышеуказанных шагов вы всегда получаете уникальность устройства. потому что цепочка ключей никогда не удаляется. он всегда обновляется.

Надеюсь, это поможет вам...

Ответ 8

Можно ли спросить пользователя о своих MSISDN? (международные номера телефонов) Как то, что делает whatsApp, когда вы впервые входите в систему с их помощью, с помощью SMS-кода, отправленного пользователю msisdn. Если вы знаете MSISDN своих пользователей, вы можете сохранить их в своей БД серверов и разрешить зарегистрировать и использовать ваши услуги msisdn, перечисленные в белом списке. Если вы хотите быть более безопасным, вы можете отправлять SMS чаще, но есть способ понять изменение SIM-карты (это для использования t-mobile и europe, я думаю) из APP, чтобы пользователь не мог вас обмануть ввод SMS для другого MSISDN, а затем переход на его/ее реальную SIM-карту MSISDN.

MSISDN уникальны во всем мире, и они обеспечены операторами связи, поэтому я думаю, что это решение строго безопасно. Что ты говоришь? удачи

update: на самом деле, внимательно прочитав свой вопрос, я думаю, вы не хотите, чтобы пользователь входил в систему с любой информацией? если это извиняется за неправильный ответ:/

Примечание: почему вы не можете использовать

[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]

рекламный идентификатор уникален для устройства, и я предполагаю, что он прав. Я не знаю, можете ли вы использовать его конфиденциально, хотя..