前言
简单分析{某点评}TCP私信协议,本帖只负责分析交流切勿他用。若侵犯了权益,麻烦管理帮忙删除,谢谢!
TCP交互流程
TCP协议交互理解:
- 请求服务器共享密钥,或本地按规则生成一组key【完结】
- RSA公钥加密并且组包发送到服务器统一加密密钥({某小破站登陆协议一样})【分析TCP消息交互】
- 登陆服务器:组包数据包括 1. 登陆的设备id 2. 附带APP版本号 3. 附带该账号的uid 4. 附带该账号的令牌{token,CK}之类
- 理解TCP消息交互参数:
1. 带上token令牌或者CK登陆服务器
2. 发送消息带上本机token的nickname,Chatid 以及对方的Chatid 【重要参数】
3. UTF-8 编码消息文本 以及确认发送消息的字节集长度
4. 收到对方的消息并且解密打印显示出来
工具
Tools |
Vsersion |
样本 |
11.12.14 - 10.20.3 |
Frida |
16.1.11 |
objection |
1.11.0 |
jadx-gui |
1.2.0 |
调试工具 |
Redmi Note 7 |
分析流程
- 对于定位到关键函数 一般log日志 或者直接hook整个java层的系统加解密函数打印堆栈直接可跟进去。前两篇文章 服务器统一密钥 和 初始化token令牌并登陆服务器 重要的两步流程走对走完了,代表该账号已经登陆成功,可以接收任何私信信息。现在的话就可以开始继续深入的就是TCP消息交互了。对于整个APP关于这块其他的功能几乎都差不多没啥大的变化无非就是组包方式不同。延申到其他APP直播类的弹幕 私信 几乎都是差不多难就难在需要时间调试分析。分析技巧 推荐【objection 】是一个非常好用的工具[首先过检测],,主要对于批量Hook一个类下的所有方法或者批量Hook所有类下的所有方法对于调试这块简直事半功倍。
- 分析发送信息和收到信息:
1.frida 自吐算法脚本安排,运行并转发端口 打开app 发送私信“123456789” 打印出堆栈。
模式填充:AES/CTR/NoPadding
java.lang.Exception
at javax.crypto.spec.IvParameterSpec.<init>(Native Method)
at com.sankuai.xm.protobase.utils.a.c(AES.java:9)
at com.sankuai.xm.login.manager.channel.f.c(CryptProcessor.java:5)
at com.sankuai.xm.login.manager.channel.b.G(ConnectionChannel.java:8)
at com.sankuai.xm.login.manager.l.H(ConnectionManager.java:3)
at com.sankuai.xm.c.a0(IMCore.java:5)
at com.sankuai.xm.c.Z(IMCore.java:13)
at com.sankuai.xm.login.manager.b.O(Unknown Source:33)
at com.sankuai.xm.im.connection.c.x(IMProtoHandler.java:2)
at com.sankuai.xm.im.message.d.t0(MessageProcessor.java:24)
at com.sankuai.xm.im.message.d$e.run(MessageProcessor.java:8)
at com.sankuai.xm.base.trace.g.run(TraceRunnable.java:2)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at com.sankuai.android.jarvis.i.run(JarvisRunnableProxy.java:13)
at com.sankuai.android.jarvis.m.t(JarvisThreadPoolImpl.java:14)
at com.sankuai.android.jarvis.m$d.run(Unknown Source:20)
at java.lang.Thread.run(Thread.java:919)
2.跟进at com.sankuai.xm.c.a0(IMCore.java:5)
public final void a0(byte[] bArr) { //方法参数传进来 简单明了
Object[] objArr = {bArr};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, 3133362)) {
PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, 3133362);
return;
}
Object[] objArr2 = {bArr, new Byte((byte) 0)};
ChangeQuickRedirect changeQuickRedirect3 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr2, this, changeQuickRedirect3, 844226)) {
PatchProxy.accessDispatch(objArr2, this, changeQuickRedirect3, 844226);
return;
}
Object[] objArr3 = {null, bArr, new Byte((byte) 0)};
ChangeQuickRedirect changeQuickRedirect4 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr3, this, changeQuickRedirect4, 2186738)) {
PatchProxy.accessDispatch(objArr3, this, changeQuickRedirect4, 2186738);
} else if (!s()) {
com.sankuai.xm.login.d.f("IMCore::send:: is not init");
} else {
((l) z().a()).H(bArr, false);//参数是从方法里面传进来
}
}
- 继续往上跟进
at com.sankuai.xm.c.a0(IMCore.java:5)
public final void Z(short s, byte[] bArr) {
Object[] objArr = {new Short(s), bArr};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, 9317743)) {
PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, 9317743);
return;
}
v vVar = new v(); //跟前两篇文章一样 new 了一个类
vVar.L(J()); //TCP参数
vVar.f = s; //TCP参数
vVar.g = N(); //TCP参数
vVar.h = bArr; //TCP参数
vVar.j = M(); //TCP参数
com.sankuai.xm.base.trace.f j = i.j();
if (j != null) {
vVar.i = j.f87995a; //TCP参数
int c = com.sankuai.xm.base.proto.protobase.f.c(bArr);
StringBuilder m2 = android.arch.core.internal.b.m("IMCore:send: xm_trace ");
m2.append(j.f87995a);
m2.append(",uri = ");
m2.append(c);
com.sankuai.xm.login.d.f(m2.toString());
} else {
com.sankuai.xm.login.d.a("IMCore:send: info == null");
}
a0(vVar.marshall()); //这里直接把传进去的参数组包拿出来
}
4.继续跟进 public final byte[] marshall()
方法
@Override // com.sankuai.xm.base.proto.protobase.g, com.sankuai.xm.base.proto.protobase.b
public final byte[] marshall() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, 5041258)) {
return (byte[]) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, 5041258);
}
O(1966080026); //发送私信消息标签
C(this.f); //传进来的参数
A(this.g); //传进来的参数
x(this.h); //传进来的参数
D(this.d); //传进来的参数
A(this.i); //传进来的参数
D(this.j); //传进来的参数
return super.marshall(); //返回父类的值
}
5.继续跟进父类 super.marshall()
方法
@Override // com.sankuai.xm.base.proto.protobase.b
public byte[] marshall() {
int i;
int i2;
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, 767391)) {
return (byte[]) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, 767391);
}
this.c.d = 0;
b(0);
this.c.f87920a = this.f87924b.position();
this.f87924b.putInt(0, this.c.f87920a); // 整个TCP数据包的长度
this.f87924b.putInt(4, this.c.f87921b); //发送私信消息标签
this.f87924b.putShort(8, this.c.c);
this.f87924b.putShort(10, this.c.d);
this.f87924b.putInt(12, 0); //CRC32签名 空置
d dVar = this.c;
if (dVar.f <= 0) {
Object[] objArr2 = new Object[0];
ChangeQuickRedirect changeQuickRedirect3 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr2, null, changeQuickRedirect3, 2979355)) {
i = ((Integer) PatchProxy.accessDispatch(objArr2, null, changeQuickRedirect3, 2979355)).intValue();
} else {
synchronized (g.class) {
f87925e++;
if (f87925e <= 0) {
f87925e = 1;
}
i2 = f87925e;
}
i = i2;
}
dVar.f = i;
}
this.f87924b.putInt(16, this.c.f); //该类调用次数
this.f87924b.putInt(20, this.c.g); //固定
byte[] bArr = new byte[this.c.f87920a]; // new 一个字节集数组
this.f87924b.position(0);
this.f87924b.get(bArr); //把 this.f87924b 数据存放进bArr里面
int F = F(bArr); //CRC32 签名 存放进来的数据
this.f87924b.putInt(12, F); //把数据包的签名放进第12个位置
this.c.f87922e = F;
this.f87924b.position(0);
this.f87924b.get(bArr); // 把最终数据存放进bArr里面
this.f87924b = null;
return bArr; //返回最终byte[]数据
}
6.TCP 发送私信消息数据包分析完毕,其他功能也都差不多基本没啥大的变化
解析发送二进制数据包:
由于高版本有抽掉关键方法,接下来解析发送数据包和接收数据包的版本为:【10.20.3】
原始TCP私信消息数据包:{ 0, 0, 0, 211, 117, 48, 0, 26, 0, 21, 0, 0, 120, 35, 246, 110, 0, 0, 0, 0, 0, 0, 0, 0, 1, 145, 0, 0, 0, 0, 162, 43, 86, 195, 0, 175, 0, 0, 0, 175, 1, 145, 0, 1, 0, 21, 1, 0, 36, 48, 50, 102, 53, 101, 52, 48, 100, 45, 102, 49, 55, 53, 45, 55, 53, 97, 55, 45, 49, 54, 55, 57, 45, 54, 56, 99, 99, 102, 51, 56, 57, 51, 102, 48, 48, 0, 0, 0, 0, 162, 43, 86, 195, 0, 0, 0, 0, 174, 101, 177, 52, 0, 0, 0, 1, 0, 51, 0, 0, 0, 51, 1, 145, 0, 9, 0, 21, 0, 30, 228, 189, 160, 229, 165, 189, 229, 149, 138, 239, 188, 140, 230, 136, 145, 230, 152, 175, 232, 165, 191, 233, 163, 142, 230, 184, 184, 230, 136, 143, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 5, 67, 121, 100, 105, 97, 0, 0, 1, 141, 229, 167, 215, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
1. {0, 0, 0, 211} //数据包的长度
2. {117, 48, 0, 26} // 小端 数据标签 【1966080026】 私信消息标签
3. { 0, 21, 0, 0} // 固定
4. {120, 35, 246, 110} //CRC32 TCP数据包签名
5. {0, 0, 0, 0, 0, 0, 0, 0, 1, 145, 0, 0, 0, 0} //固定一整段数据
6. {162, 43, 86, 195} // 小端 该号的Chatid
7. {0, 175, 0, 0, 0, 175, 1, 145, 0, 1, 0, 21, 1, 0} // 两个字节 [175] 来源于整个【TCP数据的长度 - 36】
8. {36, 48, 50, 102, 53, 101, 52, 48, 100, 45, 102, 49, 55, 53, 45, 55, 53, 97, 55, 45, 49, 54, 55, 57, 45, 54, 56, 99, 99, 102, 51, 56, 57, 51, 102, 48, 48} //长度为36的uuid
10. {0, 0, 0, 0} //固定填充数据
11. {162, 43, 86, 195} // 小端 该号的Chatid
12. {0, 0, 0, 0} //固定填充数据
13. {174, 101, 177, 52} // 小端 对方的Chatid
13. {51, 0, 0, 0, 51, 1, 145, 0, 9, 0, 21, 0} // 两个字节 [51] 来源于 【21 + 取字节集长度 (编码_UTF8编码 (content, ))】
13. { 30, 228, 189, 160, 229, 165, 189, 229, 149, 138, 239, 188, 140, 230, 136, 145, 230, 152, 175, 232, 165, 191, 233, 163, 142, 230, 184, 184, 230, 136, 143} //长度为30的 Content【你好啊,我是西风游戏】 私信信息内容
13. {0, 0, 0, 0, 0, 13, 0, 0, 0, 0} // 固定
14. {5, 67, 121, 100, 105, 97} //长度为5的昵称 【Cydia】
15. {0, 0, 1, 141, 229, 167, 215, 138} // 小端 取13位的现行时间戳
16. {0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} //固定数据
解析接收二进制数据包:
接收消息数据包:{ 0, 0, 1, 14, 117, 48, 0, 28, 0, 21, 0, 0, 112, 217, 83, 80, 1, 212, 56, 119, 0, 0, 0, 0, 0, 0, 0, 0, 143, 244, 221, 46, 1, 20, 53, 189, 210, 27, 2, 240, 0, 0, 219, 0, 0, 0, 219, 1, 145, 0, 1, 0, 21, 1, 0, 36, 101, 53, 100, 54, 55, 50, 97, 56, 45, 52, 48, 48, 53, 45, 99, 50, 51, 49, 45, 98, 98, 56, 48, 45, 52, 55, 56, 98, 55, 51, 48, 102, 101, 101, 98, 99, 0, 0, 0, 0, 162, 43, 86, 195, 0, 0, 0, 0, 143, 244, 221, 46, 0, 0, 0, 1, 0, 51, 0, 0, 0, 51, 1, 145, 0, 9, 0, 21, 0, 30, 228, 189, 160, 229, 165, 189, 229, 149, 138, 227, 128, 130, 230, 136, 145, 230, 152, 175, 232, 165, 191, 233, 163, 142, 230, 184, 184, 230, 136, 143, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 5, 67, 121, 100, 105, 97, 0, 0, 1, 141, 229, 208, 96, 217, 20, 53, 189, 210, 27, 2, 240, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 240, 0, 0, 1, 141, 229, 61, 137, 52, 0, 0, 0, 41, 48, 49, 97, 57, 53, 53, 50, 52, 45, 101, 49, 57, 54, 45, 52, 50, 101, 55, 45, 98, 54, 54, 100, 45, 100, 102, 50, 49, 99, 98, 48, 52, 53, 97, 56, 98, 95, 49, 95, 50, 49, 0, 101, 220, 159, 215, 161, 218, 223, 55 }
目前只解析比较重要的数据:
1.{36, 101, 53, 100, 54, 55, 50, 97, 56, 45, 52, 48, 48, 53, 45, 99, 50, 51, 49, 45, 98, 98, 56, 48, 45, 52, 55, 56, 98, 55, 51, 48, 102, 101, 101, 98, 99} //36位的uuid
2.{162, 43, 86, 195} //本号的Chatid 小端
3.{143, 244, 221, 46} //对方的Chatid 小端 大概就是这样了
4.{30, 228, 189, 160, 229, 165, 189, 229, 149, 138, 227, 128, 130, 230, 136, 145, 230, 152, 175, 232, 165, 191, 233, 163, 142, 230, 184, 184, 230, 136, 143}
//长度为30的消息数据 对方回复的消息【你好啊。我是西风游戏】
重要的数据解析完毕。
至此,TCP协议三步走完了// 初始化密钥-->初始化token令牌-->私信协议
再次声明仅供学习交流 若侵犯了权益,麻烦管理帮忙删除,谢谢
下面附上协议交互:
Chatid:2720749251
2024年2月26日23时3分32秒 更新通讯密钥成功!
登陆聊天服务器:103.X.X.X:8500 | 连接是否成功: 真
========================================
当前心跳包发送:真
------------------------------------------------
本号发送消息:真 | 2024年2月26日23时4分
Content: 【你好啊,我是西风游戏,测试端嗯】
------------------------------------------------------------------
对方[2415189294]回复消息时间: 2024年2月26日23时4分14秒
Content:【我知道你是测试端,哎我也是测试端】
------------------------------------------------------------------
===================分割线=========================
Chatid:2415189294
2024年2月26日23时3分29秒 更新通讯密钥成功!
登陆聊天服务器:103.X.X.X:8500 | 连接是否成功: 真
========================================
当前心跳包发送:真
------------------------------------------------
对方[2720749251]回复消息时间: 2024年2月26日23时4分
Content:【你好啊,我是西风游戏,测试端嗯】
------------------------------------------------------------------
本号发送消息:真 | 2024年2月26日23时4分14秒
Content: 【我知道你是测试端,哎我也是测试端】
------------------------------------------------------------------