新版D音由于插入了过多破坏性代码,已经无法由jadx反编译成功了,太多地方都解析失败。我尝试用MT管理器换引擎反编译,也都是一样的结果。这里如果想分析,需要jadx结合smali代码硬啃。我对着smali啃了一个参数,有些蛋疼。。于是还是用豌豆荚下了历史版本,反编译看java了。
抓包
首先还是登录抓包:
首先是post请求,有query、body、cookie和另外的一些字段。换个手机号登录再抓一次作对比。参数太多,不方便看,切换到WebForms选项卡,对比参数,首先是query。第一次抓包:
第二次抓包:
可以看到变化的只有 ts 和 _rticket,很容易看出来,一个是10位时间戳,一个是13位时间戳。那么query方面可能没有我们太需要关注的地方。
然后body:
手机号与密码经过了加密。再看下其他内容:
X-Khronos与X-SS-REQ-TICKET依然是时间戳,这里需要注意的参数是X-Gorgon、X-SS-STUB和x-tt-trace-id。对比两次请求,我们发现x-tt-trace-id里中间有一部分与末尾部分是相同的,猜测与cookie或是一些本地配置参数有关。cookie重新打开了几次,也没有发生变化,暂时也不看。这个cookie的生成盲猜要比登录协议复杂得多。
那么确定下来需要找的参数:body中的password、mobile、header中的X-Gorgon、X-SS-STUB和x-tt-trace-id。
分析
还是惯例,jadx全局先搜一波url呗。
很容易的就找到这里。可以看到在将手机号与密码put进map之前,执行了StringUtils.encryptWithXor函数:
比较简单,先转成bytes数组,逐位与5进行亦或,最后再转成16进制拼接为字符串。验证一下:
相同。密码同样。
然后分析header中的字段。搜索X-Gorgon,找到这个类:
一个匿名类,我们hook一下a方法。这里夜神模拟器的firda又出问题了,换真机测试。。hook脚本与输入结果如下:
第一个参数str也就是完整的url,第二个参数map就是所有其他的headers参数,包括cookie、x-ss-req-ticket、user-agent、accept等参数。同时x-tt-trace-id与X-SS-STUB在此之前就已经赋值:
回到代码分析,我们找到X-Gorgon的来源:a3,a3是通过调用上面的函数得到的。
大概要找出来的参数有:i,a2,str4,str5,str6,那个currentTimeMillis是10位int类型的时间戳。
我们先hook最后的a.a方法,看看a2、str4、str5、str6是什么:
几乎没任何参考价值。。还是一步一步看。
a2:
由d.a(b2)得到,b2由tt.d(str)得到,str是url。看看tt.d做了什么:
对url进行了切割,获取?到#的部分,也就是query部分。再看d.a():
对query部分进行md5,解决一个。
str4:从Map中取X-SS-STUB,如果有则赋值,否则赋为32个0;
str5和str6都在这里:
首先是str5,对cookie进行md5。str6对cookie先调了tt.e():
就是从cookie里取sessionid,然后再md5。以上4个参数如果为空,就赋32个0。最后是a.a:
各种位运算。懒得看了。接下来分析i:
如果map中有“META-SHADOWMAZE”则为1,否则为-1。
然后是一个巨长无比的调用链:
最外层的a:
以及发现惊喜的leviathan,native!惊不惊喜意不意外!不往下看了🙈!
上面还漏了两个大坑:x-tt-trace-id与X-SS-STUB。
全局搜索X-SS-STUB,定位到下面:
跟进:
接口+抽象方法。现在我们掌握的线索有,真实调用的类一定继承自TypedOutput,并且实现了md5Stub()方法。全局搜索String md5Stub():
这里就不太好看了,尝试用frida打印调用栈,只要找到调用栈,我们不就知道到底走的哪条路线了吗。但是使用frida启动时遇到:
这是由于activity没有指定被调用的Intent,我在Manifest.xml中加入后依然报错,遇到了知识盲区,暂时无法解决。那么换一个思路,可以选择去hook每一个方法,或者动态调试。对于自己来说目前可能还是hook比较顺手,而且这几个方法也比较简单,直接去hook就可以,看打印出哪个那就是了。最终确定,这里真实调用的是com.bytedance.retrofit2.mime.b类中的md5Stub()。
查找md5Stub的用例却没有查找到。观察b中没有构造方法,其中的主要类变量f31162a是在a方法中初始化:
追踪a的调用,一共有两处,我们来到了这里:
继续找调用它的地方。
有点眼熟啊,貌似是从Map里取了一些参数。Map很有可能是query、body、或者header中的某些部分。再回去看上面负责初始化f31132a的a方法,就是把传过来的参数一一组合,以&和=做连接。
既然我们已经知道b类中的md5Stub一定会被调用,而a又是初始化md5Stub中参数f311332a的唯一途径,那么可以按照如上的思路,编写hook代码,尝试还原最终拼接后的参数:
这不就是post的请求体吗?再回去看调用链:
先对上面的字符串取md5,再对结果执行a方法:
又是一些位运算,结果就是最终的X-SS-STUB。
总结
java层还是属于皮毛,真正难的地方在so层,D音so层加载Map足以让人绝望。这次在找x-ss-stub的时候,迟迟定位不到关键代码,全局搜索不出来,方法剖析和打印调用栈都GG的情况下,略有小懵。不过能用的方法还是太多了,总有一款适合你。。。
声明
声明:本文分析过程仅供学习,并无任何个人以及商业或其他用途。如有不慎侵权,请联系我删除。