iOS中的私有唯一设备标识符 [英] Private unique device identifier in iOS
问题描述
我们正在与同事一起开展项目,涉及使用大量私有和非官方代码。这是不打算用于AppStore。
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.
首先, UDID
或 OpenUDID
或任何其他解决方案不起作用在这里,他们不应该这样做。
First of all, UDID
or OpenUDID
or any other solutions don't work here and they're not expected to.
我们做了很多背景研究和测试,开始试图获取 IMEI ,以编程方式提供ICCID,IMSI 和序列号。上述方法都不适用于iOS 7及以上没有越狱。
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.
我们还花了几个个月使用着名的 IOKitBrowser 使用 IOKit
框架a>并转储 iOS
内部的全部内容。不幸的是,我们发现了 iOS 8.3
它停止了工作。
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.
我们在这里谈论的不是获取 UDID
或任何其他主流 事情,但一般来说我们需要一种方法来获取
We're talking here not about getting the UDID
or any other "mainstream" thing, but generally speaking we need a way to get
任何永久性硬件标识符,其唯一性足以识别尽管设备擦除并且在不同的iOS版本中仍然存在的设备
any permanent hardware identifier unique enough to identify a device that would persist in spite of device wipes and amongst different iOS versions
此问题与其他人无关(,并且仅针对私有API 。
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.
推荐答案
经过一番挖掘,我发现所有私有API都在使用 libMobileGestalt
用于获取任何硬件标识符s,反过来使用 IOKit
。 MobileGestalt
检查当前pid的沙箱规则,并查找 com.apple.private.MobileGestalt.AllowedProtectedKeys
权利。
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.
请参阅下面的代码:
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;
}
如上所述这里,UDID计算如下:
As described here, UDID is calculated like this:
UDID = SHA1(serial + ECID + wifiMac + bluetoothMac)
MobileGestalt
通过 IOKit
获取这些值,如下所示:
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);
如果您尝试自己动手,它将失败,因为iOS 8.3中的新沙箱规则非常严格并拒绝访问所有硬件标识符,如下所示:
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
可能的解决方案
看起来这是唯一的方法您可以获得以下UDID:
It looks like the only way you can get UDID is the following:
- 在应用程序内部启动一个Web服务器,其中包含两个页面:一个应该返回特制的MobileConfiguration配置文件,另一个应该收集UDID。更多信息此处,这里和。
- 你可以在app里面打开Mobile Safari的第一页它会将您重定向到Settings.app,要求安装配置文件。安装配置文件后,UDID将发送到第二个网页,您可以从应用程序内部访问它。 (Settings.app具有所有必要的权利和不同的沙盒规则)。
- 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.
- 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).
确认的工作解决方案
以下是基于 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's UDID. It is automatically removed from device right after you get your UDID.</string>
<key>PayloadType</key>
<string>Profile Service</string>
</dict>
</plist>
配置文件安装将失败(我没有打算实现预期的响应,请参阅文档),但应用程序将获得正确的UDID。你还应该签署mobileconfig 一>。
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.
这篇关于iOS中的私有唯一设备标识符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!