Python Flask 实现通过 safari 获取 iOS设备的 UDID

相关名词及简介

名称 iOS描述 Android描述
UDID (Unique Device Identifier) 设备的唯一标识符,在iOS5之前,获取设备唯一标识都是采用UDID,iOS6苹果废弃了该方法,提供了IDFA 用来标示设备的唯一标识符。该UDID是获取UUID后,写入.so文件生成。即使APP重装,值也不变,除非root手机(普通用户做不到)
IDFA (Identifier For Advertising) 广告标识符,1.iOS6面世,广告标示符是由系统存储,同一个设备上的所有App都会取到相同的值,是苹果专门给各广告提供商用来追踪用户的。 2.用户可以限制广告跟踪 3.系统重置或者设置中还原广告标识符会重置
IDFV (Identifier For Vendor) 应用开发商标识符,通过CFBundleIdentifier(DNS反转格式)的前两部分生成。例如:com.text.one和com.text.two得到的IDFV是一样的。如果用户将属于此开发商的所有应用卸载,则IDFV的值会被重置。
UUID (Universally Unique Identifier) 通用唯一识别码,目前最广泛应用的 UUID,是微软公司的全局唯一标识符GUIDs。 UUID每次生成的值都是唯一且不一样的。iOS可以通过结合钥匙串来区别应用唯一性 同iOS
IMEI (International Mobile Equipment Identity) 国际移动设备身份码的缩写。是由15位数字组成的“电子串号”,它与每台手机一一对应,每个IMEI在世界上都是唯一的,写在主板上,重装APP不会改变。iOS5之后被禁止。 GSM设备返回的,并且是写在主板上的,重装APP不会改变IMEI。Android 6.0以上系统需要用户授予read_phone_state权限,如果用户拒绝就无法获得。
Mac地址(Media/Medium Access Control) 硬件标识符,包括WiFi mac地址和蓝牙mac地址。iOS7之后被禁用(同时禁用的还有OpenUDID) 硬件标识符,包括WiFi mac地址和蓝牙mac地址。Android 6.0之后被禁止,若获取则会被判定为有害应用
OpenUDID 在UDID被禁用后的一个开源方案,原理是利用剪贴板在同一设备上的不同应用间共享一个OpenUDID,iOS7对剪贴板进行了限制,功能被废掉了。

在iOS5之后,iOS为了获取设备的UDID,苹果提供了利用Safari的方式。

创建类型为 xml 的文件(ep:mobileconfig.xml)并配置以下参数

⚠️ 接收数据的接口地址 该地址用来接收并解析客户端返回的xml数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?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://172.50.10.152:5000/app/parse_udid</string>
<key>DeviceAttributes</key>
<array>
<string>UDID</string>
<string>IMEI</string>
<string>ICCID</string>
<string>VERSION</string>
<string>PRODUCT</string>
</array>
</dict>
<key>PayloadOrganization</key>
<!--组织名称-->
<string>com.dev.org</string>
<key>PayloadDisplayName</key>
<!--文件标题-->
<string>查询设备UDID</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadUUID</key>
<!--随机生成的唯一GUID字符串-->
<string>29313601-7007-474f-b916-e0bac0f94fed</string>
<key>PayloadIdentifier</key>
<string>com.dev.text</string>
<key>PayloadDescription</key>
<!--文件描述-->
<string>本描述文件仅用来获取设备的UDID</string>
<key>PayloadType</key>
<string>Profile Service</string>
</dict>
</plist>

最终显示的配置文件如下所示

图片

Python 模块

UDID xml数据解析

该接口地址对应 mobileconfig.xml 文件中服务端接收解析数据的地址,解析完成之后重定向到结果页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@main.route('/app/parse_udid', methods=['GET', 'POST'])
def parse_udid():
"""解析获取udid数据"""
b_data = request.data
data_str = str(b_data).split('<?xml')[-1].split('</plist>')[0]
data_str = '<?xml{}</plist>'.format(data_str).replace('\\t', '').replace('\\n', '')
soup = BeautifulSoup(data_str, features='xml')
xml = soup.find('dict')
keys = []
values = []
for item in xml.find_all():
if item.name == 'key':
keys.append(item.text)
else:
values.append(item.text)
print(keys)
print(values)
return redirect(url_for('main.udid', u_id=values[-2]), code=301)

⚠️需要使用301重定向,否则会提示描述文件安装失败

图片

UDID 获取和结果展示

  1. udid 不存在时显示获取udid按钮
  2. udid 存在且不为0是显示udid结果
  3. udid 为0时下载配置文件(注意Content-Type配置

python接口模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@main.route('/app/udid', methods=['GET', 'POST'])
@main.route('/app/udid/<u_id>', methods=['GET', 'POST'])
def udid(u_id=None):
"""1.udid下载页面"""
if not u_id:
return render_template('udid.html', udid='')

"""2.udid结果显示"""
if u_id != '0' and u_id != 0:
return render_template('udid.html', udid=u_id)

"""3.udid配置文件下载"""
def file_content(file_path):
with open(file_path, 'rb') as f:
return f.readlines()

file_path = basedir + '/app/static/mobileconfig.xml'
filename = os.path.basename(file_path)
response = Response(file_content(file_path))
response.headers['Content-Type'] = 'application/x-apple-aspen-config; chatset=utf-8'
response.headers['Content-Disposition'] = 'attachment;filename="{}"'.format(filename)
return response

页面配置模块

1
2
3
4
5
6
7
8
9
10
{% extends "bootstrap/base.html" %}
{% block title %} 设备信息 {% endblock %}

{% block page_content %}
{% if udid %}
<h3>UDID: {{ udid }}</h3>
{% else %}
<a class="buttons" href="/app/udid/0" target="_blank">点击获取您的UDID</a>
{% endif %}
{% endblock %}

以上内容参考以下文章:

Apple ConfigurationProfileExamples

iOS-UDID-Safari

iphone-ota-setup-with-signed-mobileconfig

Create GUIDs online