Android逆向入门6——定位到小红书Sign算法

在上一节中,我们猜测小红书自定义了一个拦截器,给每条请求添加了DeviceId、Timestamp等等字段,以及我们心心念念的sign字段。
这一节我们顺着这个思路去尝试定位sign,看看猜测是否有误。

一、浅谈Okhttp拦截器

添加一个自定义拦截器很简单,只需要实现Okhttp的Interceptor接口,重写其中的intercept方法,最后在OkHttpClient.Builder链式代码中注册和添加这个拦截器,为什么要称作“自定义“拦截器呢?因为Okhttp核心实现就是基于其内部五大拦截器,我们下文再说。
需要注意的是,Retrofit是基于Okhttp的封装,所以当我们谈论“Retrofit的拦截器”,其实就是OKhttp的拦截器。

先看一下上一节,我们用Okhttp实现网络请求的demo
在这里插入图片描述对照着看一下Okhttp发送请求的执行流程
在这里插入图片描述我们分析大红框里的内容,这张图有些繁琐,也可以转化成下图。
在这里插入图片描述
还是直接上图,就像流水线工厂一样,一个一无所有的Request,依照顺序依次经过五大拦截器的前置拦截,最后拿到response后,再倒序经过五大拦截器的后置拦截。

我们引用@yuashuai 所举的例子,他表述的既生动又贴切。

老张有很多干面条,但是他想吃汤面,可是自己又不会做,但是碰巧村里大郎会做,于是老张拿一包干面条让大郎做成了汤面。但是老张发现他做面不好吃,盐都没放,连个青菜叶子都没有。

这时候老张正好碰到隔壁老王,老王说了这东西我也会做,比他做的好吃多了。于是老张又拿着一包干面条给了老王,老王说老张你等着,我马上回家给你做,做好了就给你送过去。但是老王回家并没有做,而是去家里拿了一包盐,然后去找隔壁老李了,原来老王并不会做面,但是他知道隔壁老李会做,而且做得比较好吃。于是他把干面条和盐都交给了老李。老李对老王说你回去等着吧,做好了马上给你送过去。可是老李同样不会做,但是他知道村里的大郎会做,这时老李首先回厨房拿了两个生菜叶子,然后带着老王给的干面条和盐去找大郎了,对大郎说,生菜叶子,盐,面条都给你了,你快给我做一碗面。大郎对老李说好嘞,3分钟就好了,3分钟后,老李拿着做好了的放了盐和生菜叶子的一碗面回去了。本来打算直接给老王,但是一想,自己放了两个生菜叶子,不吃点这个面吃不是有点亏,于是老李偷偷了吃了几根面。然后老李去找老王说你的面做好了并把面交给了老王。老王一看这面只有两个青菜叶子,营养是不是不够呀!于是老王又买了半斤熟牛肉,切切放了进去。然后老王去找老张说你的面做好了,还说道这么大一碗你也吃不完吧,让小张也吃点。最后老张吃着老王送来的红烧牛肉面感动的肉牛满面。

这里的干面条就可以看做一个最原始的request,到老王哪里被加了点盐,到老李哪里被加了生菜叶子,于是大郎才能把这个request做成放了盐和生菜叶子的response,这个response回到老李哪里又被啃了几口,到老王哪里又被放了点牛肉。于是最后回到老张哪里收到的response就是被扣了几口并且加了牛肉的response。这样整个链条是不是就清楚了!

原文链接:https://blog.csdn.net/qq_16445551/article/details/79008433

@Wanghao花了一张非常全面的图。在这里插入图片描述
在第三讲的末尾我们讨论过BridgeInterceptor拦截器,它会对我们我们配置的request请求补充头信息,比如请求类型、UA,如果你自己不加ua,Bridge拦截器就会添加“okhttp/版本号”这样的ua。
在这里插入图片描述
这是BridgeIntercetpor的源码
在这里插入图片描述
我们也可以自定义拦截器,分为应用级别拦截器和网络级别拦截器,在@Wanghao的图上我们可以看到它们分别作用的时机。

应用级别拦截器:只会调用一次,获取到最终的response结果
网络级别拦截器:可以感知到网络的请求的重定向,以及重试会被执行多次

这两种拦截器在注册方式各不同,分别调用addInterceptor()以及addNetworkInterceptor方法进行注册,但实现拦截器的步骤没有不同,所以我们不做过多笔墨。
在这里插入图片描述
在这里插入图片描述

大家可以看下面这些博客,写的非常好,文中的图和例子就是来源于这些博客,我的笔力不及这些作者十分之一。

@Wanghao OkHttp源码分析 初识结构篇 http://www.whdreamblog.cn/2019/04/19/OkHttp%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E5%88%9D%E8%AF%86%E7%BB%93%E6%9E%84%E7%AF%87/
OkHttp Interceptor工作原理 https://blog.csdn.net/qq_16445551/article/details/79008433
@Kyrie_Wangyz OkHttp源码彻底解析系列 https://blog.csdn.net/qq_39037047/article/category/9282071

二、分析一个简单的拦截器

我们随便找一个拦截器demo分析一下,我们需要熟悉拦截器的具体操作,找到它代码流程上的一些特征,然后根据这些特征在Jadx中搜索反编译后的Java代码,去寻找小红书实现添加sign等等11个参数的拦截器。

除此之外还有个思路,上文说到,自定义的拦截器分为Application拦截器和Network拦截器,但不管哪一种,都需要在OkHttpClient.Builder链式代码中通过addInterceptor/addNetworkInterceptor方法添加和注册后才能生效,那我们可以全局搜索addInterceptor以及addNetworkInterceptor,然后得到拦截器的线索,这样应该也是可以的。我们先看看当前的这个思路。
在这里插入图片描述
我们可以看到三个明显的特征
一是实现Interceptor接口 “public class xxx(自定义的拦截器名) implements Interceptor {”
二是重写intercept方法“public (可能有修饰符) Response intercept(Chain Chain)……”
三是添加字段时调用的“.addQueryParameter”方法(这儿我们加了个“.”,可以缩小检索范围),如果是添加Header头信息,则会调用”.header“这个方法

如果App没有对Okhttp类进行混淆,那我们就可以根据这三个特征找到Retrofit的拦截器实现。

我们这儿再多说两句,看图中第九行,Request originalRequest = chain.request();这个demo作者为什么要将这个Request命名为originalRequest(original:原先的,原始的)呢?再回想一下拦截器的工作原理。想象一下一个流水线,request是材料,response是产品,从头到尾经历了许多次加工(拦截器)。每个拦截器先通过chain.request()得到这个reqeust,经过一系列操作后的request,再放回chain.proceed方法,最后return回去,request经过了一次洗礼,等待它的是不断的新的洗礼。
在这里插入图片描述
在这里插入图片描述
chain.proceed 这个方法名非常精准和有神,Okhttp采用了设计模式中的责任链模式,感兴趣的可以看一下这篇关于责任链的文章https://www.cnblogs.com/aeolian/p/8888958.html。
因此,对于每一个拦截器来说,取到的request就是原始和过去的,命名就可能会用”old“、”original“,返回到reqeust就可能用”final“、”new“修饰,解释到这儿,我想大家对拦截器应该有了一个具象的了解了。

上面我们找到了三个特征,那么接下来我们通过Jadx的全局搜索功能,开始寻找小红书Retrofit的拦截器,进而找到sign等参数的实现。如果你的电脑内存只有8G,那么接下来的这一步操作对你十分有用,除此之外,它对我们搜索关键代码点也有奇效。

反编译一个App,你会看到成千上万的类和方法,我们可以简单将这些类和方法分成“具体业务逻辑的代码”和“App架构和工具的代码”,前者的类名一般是包名.xxx,比如小红书App包名为com.xingin.xhs,它的业务代码就是com.xingin.xxxx;而第二类往往五花八门,比如Android本身的一些方法类“android.xx”,腾讯的sdk“com.tencent.xxx”,微博的登录接入sdk“com.weibo.xxx”。我们想要定位到的内容/加解密逻辑等等,基本都在前者的类里,而后者既缭乱人眼,烦人心神,又占用内存,Jadx给我们提供了屏蔽这些类的方法,屏蔽后Jadx将不再反编译这些类,你也无法再跳转到该方法里,或者在全局检索时看到这些类中扰人的代码。

方法很简单,只需要在类列表中选中某个类,右键exclude即可屏蔽这个类。
在这里插入图片描述
但我们总不可能点几百几千下,这样实在太麻烦了。Jadx的设计者给我们提供了一种类似正则表达式的匹配方法,可以屏蔽这些讨人厌的第三方类,操作也很简单。打开文件——首选项,如下图,(建议)不要勾选“自动后台反编译”,因为它可能占用你电脑更多的内存。
在这里插入图片描述
点击编辑,像我这样输入,类名以baidu或android开头的类就会被Jadx屏蔽掉,点击确定后保存设置,Jadx会重新加载这个Apk,你会发现已经生效,这些类变成了灰色。
在这里插入图片描述

我们浏览一下列表,将比较明显的一些刺头频闭掉。

  1. android
  2. com.alipay
  3. com.baidu
  4. com.facebook
  5. com.google
  6. com.huawei
  7. com.meizu
  8. com.networkbench
  9. com.qiniu
  10. com.tencent
  11. com.vivo
  12. kotlin

你可能会难以把控,怕误伤,比如com.networkbench.xxx,它似乎很像App中用于处理网络请求的模块,怎么避免误伤呢?百度一下就行,如果消息模棱两可,就搁在那儿不排除即可。
在这里插入图片描述
在这里插入图片描述

android com.alipay com.baidu com.facebook com.google com.huawei com.meizu com.networkbench com.qiniu com.tencent com.vivo kotlin

在这里插入图片描述
一路保存设置,最好重启一下JADX,这样可以释放掉由于反复操作Jadx的设置而导致的内存占用。
在这里插入图片描述
Jadx默认在使用搜索功能时,才开始反编译,点击Jadx左上角魔法棒,然后稍等片刻。
在这里插入图片描述原先反编译小红书时占用6G左右内存,现在少了几乎一半。
在这里插入图片描述
经过这一顿调教,我们正式开始搜索拦截器相关的特征。

二、定位关键代码

Jadx并不支持正则表达式的方式进行检索,所以我们这儿搜索“implements Interceptor {”
在这里插入图片描述
再试试第二个关键词
在这里插入图片描述
再试试第三个关键词
在这里插入图片描述
真是让人惊喜,只有三处适宜的代码,而且其实就在两个类里,先看一下第一个类
在这里插入图片描述
传入了一个map集合,不停for循环,按照键和值的方式将内容传进addQueryParameter方法中,妙啊,比如map为{“sign:12345678”},这不就传进去了吗,赶紧右键查找一下用例,只有一处,点进去。
在这里插入图片描述
代码越来越乱了,这个时候冷静一下,要避免一叶障目,不要先钻进去研究逻辑,保持怀疑,然后直接看下一个符合条件的类,要知道这儿可是出现多处.addQueryParameter方法,也很像添加多个参数的操作。

点进去看一下,有些代码会让你觉得很demo很像,比如什么request.url().newBuilder(),除此之外,我们看到了大量类似oldRequest的提示,这也只能说明这个地方很可疑,就像第一个检索内容一样,但不足以让我们认定它就是我们要找到拦截器。
在这里插入图片描述
从上往下看一下这个类
在这里插入图片描述

在这里插入图片描述
这儿似乎就是我们需要的那个拦截器类,我们看一下sign的用例,j上右键——查找用例
在这里插入图片描述
两处方法点开后,仔细瞅瞅,不管是map的put方法,还是newBuilder的add方法,其实都是往集合里塞东西,里面都是两个参数,迷糊的同学可以看一下前面的demo,参数一是键,也就是参数名,在这儿也就是this.j,也就是”sign“,参数二就是具体的sign值,也就是我们要破解的sign。我们可以发现,两处的sign值都是由a方法生成,a方法的传入是一个map集合(linkedhashmap是map的一种),返回一个String。

在https://blog.csdn.net/qq_38851536/article/details/93919613这篇文章中,我浅谈了Java中的md5加密,一个很常见的特征就是传入一个map,在代码内排序和加密后,返回加密后的String提供给别的地方调用。

多半这个a方法就是sign的生成处了,在下一讲中,我们会验证一下是否属实,如果没找错的话,就可以分析这个sign,进行sign的破解。这篇文章就到此为止了。
在这里插入图片描述

看我教程的人不多,但读者一直支撑着我继续写这些拙劣的文字和教程,非常非常感谢大家。在这儿做一下更新的说明,我每一节的内容其实量挺大的,再加上我需要反复琢磨表达和思路,所以更新比较慢,以后会减少单篇的内容,在保证质量的前提下尝试一天一更,谢谢三五读者的支持和厚爱。

发布了27 篇原创文章 · 获赞 120 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_38851536/article/details/100146115