百度知道app有个心跳包检测机制,百度看下这个接口
POST:https://kstj.baidu.com/ctj/iknow?知道字段v=737&i=0E046D49CA01C9B2177F109C170A6C60%7CVDWE3WI7K&s=1440*2392&fr=android&c=baiduzhushou&mc=Pixel+XL&ie=&op=&aid=9&av=9.1.5.2&sv=7.1.2
我们在模拟请求的时候发现headers有个参数进行加密。
MD5,跳包需要解决该字段。分析
md5,百度猜测盲猜一波,知道字段真的跳包是MD5???
md5长度一般为32位或者16位,我们看这个10位的分析长度是啥情况?
难道是截取前十位?
算了直接反编译看吧,反编译搜关键词?
我们今天换种快速的百度方法,直接用算法助手hook吧。知道字段
直接hook了该 请求url,跳包并将堆栈打印出来了。分析
调用堆栈: at com.baidu.common.klog.KBaseStatWorker.getUploadUrl() at com.baidu.common.klog.core.KBaseWorker.getSignUrl(SourceFile:1) at com.baidu.common.klog.core.i$a.a(SourceFile:9) at com.baidu.common.klog.core.i$a.a(SourceFile:28) at com.baidu.common.klog.core.i$a.a(SourceFile:1) at com.baidu.asynctask.CommonAsyncTask$a.call(SourceFile:2) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at com.baidu.asynctask.d.k(SourceFile:1) at com.baidu.asynctask.e$c.run(SourceFile:8) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761)
直接找 com.baidu.common.klog.core.i$a.a 方法看下
定位到了。百度
直接扣:
byte[] processItem = this.f7349o.processItem(list); byte[] a = m8627a(processItem); CRC32 crc32 = new CRC32(); StringBuilder sb = new StringBuilder(); sb.append(processItem.length); sb.append("%"); sb.append(a.length); crc32.update(sb.toString().getBytes()); String valueOf = String.valueOf(crc32.getValue());
frida 勾一下
# -*- coding: utf-8 -*-# @Author : Codeooo# @Time : 2022/9/21import frida,知道字段 sys# JSON.stringify(baidu.common.klog.KBaseStatWorker$a */# public class C2693a implements Callable{ #Response解密Response1 = """Java.perform(function () { var utils = Java.use('com.baidu.common.klog.KBaseStatWorker'); //var utils = Java.use('com.baidu.common.klog.KDebugWorker'); utils.processHeader.implementation = function (arg0,arg1,arg2) { console.log("arg0 ->" + arg0); //console.log("arg1 ->" + arg1); console.log("result ->" + this.processHeader(arg0,arg1,arg2)); return this.processHeader(arg0,arg1,arg2); }});"""Response = """Java.perform(function () { var utils = Java.use('com.baidu.common.klog.core.i$a'); utils.a.overload().implementation = function (arg0) { console.log("arg0 ->" + arg0); console.log("result ->" + this.a(arg0)); return this.a(arg0); }});"""process = frida.get_remote_device().attach('com.baidu.iknow')script = process.create_script(Response)script.load()sys.stdin.read()
整体加密思路:
将请求体转换为字节集, 然后把字节集进行gzip压缩,跳包
字节集取一次长度为:a,压碎后的为:b,然后把这俩长度进行CRC32算法编码输出就出来md5了
888341776 没错
1050: 则是bArr.length 也就是 length | Content-Length 字段 也是请求体的长度
CRC(Cyclic Redundancy Check)校验实用程序库在数据存储和数据通讯领域,为了保证数据的正确,就不得不采用检错的手段。在诸多检错手段中,CRC是最著名的一种。CRC的全称是循环冗余校验。
所以看来此MD5非MD5算法哈哈哈哈~
直接贴代码:
import java.io.IOException;import java.util.zip.CRC32;import java.util.zip.GZIPOutputStream;import java.io.ByteArrayOutputStream;public class MD5 { public static void main(String[] args) { /*请求体 每一小节后面跟\n换行*/ String str = "act=view&l=wifi&seq=1&ts=30f55&sid=l8bc21246qqx&t=1663752141189&sampleToken=941&name=logTodayAnswerCardShow\n" + "seq=1&l=wifi&act=view&ts=30f5s&sid=l8bc21246qqx&t=1663752141212&sampleToken=941&name=logVoteCardContainerShow&qid=1375810357052180219\n" + "seq=1&l=wifi&act=view&ts=30f6i&sid=l8bc21246qqx&t=1663752141238&sampleToken=941&name=logHomeBannerShow\n" + "seq=1&l=wifi&act=view&ts=30f7a&sid=l8bc21246qqx&t=1663752141266&sampleToken=941&name=logContentOfFeedAllShow\n" + "seq=1&l=wifi&act=view&ts=30fgs&sid=l8bc21246qqx&t=1663752141608&sampleToken=941&name=logTimeLimitAnswerCardShow&qid=1455695397450803940\n" + "seq=1&l=wifi&act=view&ts=30fhs&sid=l8bc21246qqx&t=1663752141644&sampleToken=941&name=logQuestionItemShow&statId=33&qid=1869336249901253347\n" + "seq=1&l=wifi&act=view&ts=30fie&display_time=1663752141&sid=l8bc21246qqx&t=1663752141666&sampleToken=941&name=logQuestionFeedCellShow&request_page=na_answertab_tag&product=qengine_question&qid=1869336249901253347\n" + "seq=1&l=wifi&act=view&ts=30fip&sid=l8bc21246qqx&t=1663752141677&sampleToken=941&name=logContentOfFeedAllShow\n"; //将请求体字符串转化为byte byte[] bArr = str.getBytes(); System.out.println(bArr.length); //将转化后byte再传入gzip压缩算法里 byte[] bArr2 = m8627a(bArr); System.out.println(bArr2.length); //将两次的byte长度传入StringBuilder (bArr.length + "%" + bArr2.length) CRC32 crc32 = new CRC32(); StringBuilder sb = new StringBuilder(); sb.append(bArr.length); sb.append("%"); sb.append(bArr2.length); //转Str后进行CRC32算法编码输出结果 crc32.update(sb.toString().getBytes()); System.out.println(crc32.getValue()); } //gzip压缩 public static byte[] m8627a(byte[] bArr) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { GZIPOutputStream gZIPOutputStream = new GZIPOutputStream(byteArrayOutputStream); gZIPOutputStream.write(bArr); gZIPOutputStream.flush(); gZIPOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } return byteArrayOutputStream.toByteArray(); }}
其实有个地方也可以这样写哈,都一样~