首页
论坛
课程
招聘
[逆向分析] 某抢茅台APP的reservationToken算法分析
2021-3-2 14:18 6647

[逆向分析] 某抢茅台APP的reservationToken算法分析

2021-3-2 14:18
6647

0x00 前言:

在调用下单接口(addorder.html),里面包含一个参数名称为reservationToken,它的值是一串类似md5的十六进制。这里分享下逆向追踪过程,目前已实现。

0x01 算法定位:

拖入APK到jadx,直接搜索关键字reservationToken,我们看到图中的代码

 

 

其中orderPrepareParameter2.mReservationToken = MD5Util.encode("ANDROID" + W.e() + X.b()); 是根据 3个字符串进行拼接在执行的操作,那么我们接下来就来查找这2个方法的字符串。

 

目前已知:md5("ANDROID" + ? + ?)

0x02 W.e()算法扣出:

我们追入:W.e() 内进行查看

我们可以看到,它这里是直接把结果存入的f26606b进行返回的,由于该APP它都做了缓存判断,所以如果直接下frida的话,也没办法直接HOOK到它生成的过程,但是我们发现他是调用的static方法UTDevice.getUtdid 获得的,我们可以编写frida代码主动调用,然后看看它输出的结果,以下是frida代码:

 

 

编写好代码直接运行,因为是主动调用的,无需等待hook, 那么我们看到是一串字符串,感觉有点像base64编码。

 

这部操作的意义并不是很大,只是知道了 W.e() 的返回值,但我们需要知道它的生成过程,所以继续 UTDevice.getUtdid 跟进。

 

我们继续跟进去后,发现它是调用的 b.b(context) 获取的一个class类,然后在调用 f() 返回的结果:

 

那么我们继续跟如b.b()内的实现部分

 

这里我们看到,它先判断f13187a是不是null,如果是的话,继续调用a(context)创建,这些都是它的缓存机制判断,我们继续跟进a内

 

 

这里就是它创建class类的参数赋值代码了,我们知道,刚刚它是调用 b.b(context) 然后调用 f() 来得到的,我们现在去看看 f() 它返回的是对应哪个属性,这样就知道这里对应的是哪个值了。

 

 

我们发现,f是返回的f13186g,而f13186g是由e方法赋值的,那么上一张截图,我们看到,e方法就是传入的 value 这个值。aVar.e(value) 也就是上一张截图的这行代码了,那么我们就继续跟进value的方法,也就是 String value = c.a(context).getValue(); 里面的过程

 

 

我们这里可以看到,它调用的h()方法,继续跟进,事情逐渐变得好玩了。

 

 

这里代码比较多,我们先来一下,它是调用的 this.h = i(); ,如果不是空的,就直接返回,其实这里也是获取缓存的值,我们跟进 i() 里面,由于这个方法代码反编译失败,都是一片绿色,阅读比较吃力,但是经过详细的阅读得出就是读取的缓存,所以这行忽略,我们往下看:
byte[] c2 = m5c();

 

this.h = b.encodeToString(c2, 2);

 

得出2行关键代码,这里它通过调用m5c()获得一个byteArr数组,接着进行base64编码后返回,也就是m5c里面估计就是关键生成了。

 

 

这里基本大部分都是Android的SDK代码,我们可以直接新建一个java工程,直接复制他的代码,将报错地方纠正。

 

 

我们扣出代码,发现报错的地方就是 d.getBytes 、 e.a(this.mContext) 、 g.a 、b 四处地方,我们逐一进去查看,首先查看 d.getBytes()

 

 

它就是进行的一个位移操作,我还以为是什么复杂的类,我们直接复制出来就好了,重新命名下。

 

 

直接复制出来,我重新命名成了 DgetBytes,然后把 d.getBytes 改成这个方法名,就解决了,那么我们继续查看下一个方法 e.a(this.mContext) ,跟进

 

 

这个方法也很简单,就是获取手机的deviceId,下面的就是判断如果获取不到执行别的获取方案,就不跟进了。 我们直接写死deviceId,也就是IMEI码就行了,到时候在封装成一个传参的方式即可。
最后跟进g.a()里面查看

 

 

这也是一个相加操作,很简单,也直接跟刚刚的方式一样扣出来,我重新命名成了ga,然后修改下调用的代码即可。

 

还剩最后一个 b() 方法,这个也是可以直接扣的

 

 

我们重新命名成了be,那么看下完全扣完的代码,一点报错没有了

 

后面我们在进行BASE64编码就完成了第二个参数的生成过程了!! 如果要转成其它语言代码的话,就看你的代码阅读能力了,因为都是直接扣出来的~

 

目前已知:md5("ANDROID" + 算法A(IMEI) + ?)

 

这里我就定义成它叫做算法A,他的调用过程就是 Base64.getEncoder().encodeToString(m5c()); 的返回,接下来我们解析第三个参数

0x03 X.b()算法扣出:

 

我们继续进行扣代码,继续跟进X.b()内查看

 

这个方法很简单,看着代码挺多,我们慢慢阅读解析。
首先它调用 String packageName = AppContext.getContext().getPackageName() 获取当前APP的包名,然后获取缓存,看看有没有记录device_id这个值,我们要知道他的生成过程。
如果没有,那么它继续调用Settings.Secure.getString(AppContext.getContext().getContentResolver(), "android_id"),这个就是获取系统参数的android_id
如果获取的值是一个它固定的值9774d56d682e549c它就拿来返回了。
最终我们肯定确定,它就是调用
String deviceId = ((TelephonyManager) AppContext.getContext().getSystemService("phone")).getDeviceId();
f8203a = (deviceId != null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")) : UUID.randomUUID()).toString();

 

获取手机的IMEI,然后在通过 nameUUIDFromBytes 进行格式化,自此就结束了~

 

其实我们用frida HOOK发现,它返回的值跟我们抓包看到的APPKEY是一样的值。

 

目前已知:md5("ANDROID" + 算法A(IMEI) + UUID格式化(IMEI))

0x04 算法复原:

 

最后我们根据上面已知的生成过程,成功扣出算法。


[公告] 2021 KCTF 春季赛 防守方征题火热进行中!

收藏
点赞1
打赏
分享
最新回复 (5)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
omg12 活跃值 2021-3-2 17:16
2
0
很不错,适合小白分析。
雪    币: 190
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_一口田不点 活跃值 2021-3-4 10:58
3
0
学习了,非常详细
雪    币: 279
活跃值: 活跃值 (99)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
 Kevin 活跃值 2021-3-6 09:41
4
0
学习了,感谢楼主分享
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
万里星河 活跃值 2021-3-6 10:53
5
0
支持一下
雪    币: 173
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
奋斗小菜鸟 活跃值 2021-3-26 18:05
6
0
大佬 天天手撕茅台
游客
登录 | 注册 方可回帖
返回