做互联网开发的同学可能对流量这个词很熟悉,
在互联网行业中对一个产品的质量有一些关键指标,比如日活DAU,比如次日留存,点击率。
往往评估一个产品的变现能力会通过日活来计算。
因此也就诞生了一些灰色产业,专门在日活上做文章。
而作为金主(广告主)来说,如何判断一个产品的真实日活就显得非常重要了。
行业潜规则里,你自己家给的日活数据都要打个折。。除非能提供一些第三方的统计数据。
今天要说的是一个设计不完善的DAU系统有多容易伪造数据。
刷量手段
现在市面上做Android灰色产业的技术手段离不开这几种
· 协议破解
· 伪造用户操作
· 伪造用户数据
我们以其中"伪造用户数据"这一点为例子来展示一下如何通过技术手段实现用一个手机让一个app日活过百的。
一个正常的日活
首先我挑选了一个国外的小众SDK,类似于友盟这种有日活统计功能的。
下面是在我开始刷量之前的数据,
记住哦,从这里开始我都只用了两台手机。
可以看到DAU在3/6之前不超过5(当然这里面超过2的那些是google play上的其他真实用户)。
开始破解
首先需要确定的是它用哪个指标来进行用户唯一性的判断,
这里我通过抓包分析它的接口数据如下,
{
"advertiser_id": "966694c4-05fa-4020-a39c-ad3bced26b62",
"carrier": "",
"custom_id": "",
"screen_height": 1920,
"screen_width": 1080,
"limit_tracking": false,
"ln": "zh",
"locale": "CN",
"device_brand": "Xiaomi",
"device_model": "MI MAX",
"device_type": "tablet",
//省略无关数据
"device_time": 1526006628156,
"controller_version": "1.0.9.16",
"user_metadata": {},
"device_audio": false,
"test_mode": false,
"guid": "395fce37-55c3-410e-bdb8-7d20fad3c14e",
"guid_key": "395fce37-55c3-410e-bdb8-7d20fad3c14eDUBu6wJ27y6xs7VWmNDw67DD"
}
乍看下来没发现什么特别的字段,起码没有我想象中应该出现的 device_id这种字段。
但是我发现了一个熟悉的数据模式
"advertiser_id": "966694c4-05fa-4020-a39c-ad3bced26b62",
做过SDK相关的同学会知道,这个字段跟UUID一模一样,
那么这个UUID是从哪里来的呢?
进一步猜测
到这里开始陷入僵局,接下来有两种可能性,
· sdk在app第一次启动的时候生成了UUID
· sdk在上报数据的时候从GP取的UUID
做过海外的同学可能了解,GP本身也有一个AdvertisingIdClient.Info,可以获取设备唯一性识别码,而这个编码跟上面接口中的数据长的也是一样的。
如果是第二种可能的话,要破解就比较麻烦了。
那么我们从简单的第一个可能下手。
如果把gms service删了会怎样?这样就能排除 gp的干扰了。
在这个思路下,我把手机的 GMS框架删掉后重新请求了数据,
这时候的接口数据变成下这样了,
{
"advertiser_id": "",
"device_id": "97b4cf89a32c3ddb08c1ee0be43d827b129a134b",
}
上面的接口数据只列出了前后的差异,并不是只有这两条哦。
可以看到原本的跟UUID一模一样的字段变空了,取而代之的是多了个 device_id字段,
也就是说,
这个SDK 在国内环境下(没有谷歌的环境)用了另一套机制用来确定设备唯一性,
然而这里再次陷入僵局。
这一长串字符是什么玩意?
山穷水尽疑无路
这个时候开始基本就是靠猜了,
一般用来确定设备唯一性的数据有这个几个
· UUID
· android_id(通过 Setttings获取)
· IMEI/MEID等移动设备唯一编号
对于上面这几种可能来说,比较可能的是第二第三个数据,
第一个为什么被我排除了呢,
因为考虑到 device_id 应该是经过了加密,而UUID加密不出来这样的字段,所以我们可以先从第二个可能性开始。
接下来就是确定它的加密方式了!
一般常见的加密方式,不外乎 md5/sha1这些,可以先暴力猜测一下,顶多20分钟就可以知道结果。
5分钟后。。。
运气不错,当尝试到 android_id + sha1 组合的时候就得到了接口中的 device_id数据。
数据有了,接下来的思路就是用 Xposed框架来 hook获取 android_id的接口了,
相对于上面的破解过程,Xposed的代码非常简单,
private void hookAndroidID(XC_LoadPackage.LoadPackageParam lpparam, final DeviceInfo info) {
XposedBridge.log("[methodhook] hookAndroidID()");
XposedHelpers.findAndHookMethod(Settings.Secure.class.getName(), lpparam.classLoader, "getString", ContentResolver.class, String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("[afterHookedMethod] hookAndroidID: " + (String)info.getAndroidId());
param.setResult((String)"whatever you want");
super.afterHookedMethod(param);
}
});
}
在hook了这个方法之后,每次sdk要去拿 android_id,都会被替换成我们设置进去的假数据。
伪造后的日活
下面来看看在经过破解之后的数据吧,
看看这暴涨100倍的日活。
如何防御刷量
其实关于破解和加密一直都是魔高一丈道高一尺的博弈,
并没有能够完全无解的加密逻辑,有的只是无限提高破解代价的逻辑。
对于上面给出的例子来说,其实只要在接口和唯一性数据的选择上给出更好的方案就可以避免被刷量了。
比如接口数据加密,不要用 device_id这种显眼的字段,
比如用下发秘钥的方式去和 android_id一起加密,这样即使拿到其中一个,也猜测不出来加密算法。
总而言之,Android 的刷量思路基本就跟上面所说的这样,
但我不鼓励大家去恶意刷量,希望在平时开发中在敏感数据的设计上多绕几个弯,这样能避免被其他人利用。
喜欢的亲们请点个关注吧!欢迎转发。
更多Android进阶技术,面试资料系统整理分享,职业生涯规划,产品,思维,行业观察,谈天说地。可以加Android架构师群;701740775。