吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4132|回复: 28
收起左侧

[Android 原创] 简单的分析:某点评 TCP协议 算法-登录(二)

[复制链接]
西枫游戏 发表于 2024-2-15 22:29
本帖最后由 西枫游戏 于 2024-2-15 22:34 编辑

前言

简单分析{某点评}TCP私信协议,本帖只负责分析交流切勿他用。若侵犯了权益,麻烦管理帮忙删除,谢谢!

TCP交互流程

TCP协议交互理解:

  1. 请求服务器共享密钥,或本地按规则生成一组key【篇文比较长分段开始】
  2. RSA公钥加密并且组包发送到服务器统一加密密钥({某小破站登陆协议一样})【分析初始化并且登陆服务器】
  3. 登陆服务器:组包数据包括 1. 登陆的设备id  2. 附带APP版本号   3. 附带该账号的uid   4. 附带该账号的令牌{token,CK}之类

工具

Tools Version
样本 11.12.14
Frida 16.1.11
objection 1.11.0
jadx-gui 1.2.0
调试工具 Redmi Note 7

分析流程

1.前篇文章已经分析了统一密钥,接着突破口继续来初始化并且登陆服务器。frida脚本 hook  java 层的系统加解密并且打印堆栈。观察到统一密钥之后的一个初始化token令牌堆栈。

  AES/CTR/NoPadding: AAABwQADAHEAFQAA9Lz1gwAAAAIAAAAAOHf6P+g7xAc75dCBq9r2QGVivjy0MhT32lDB/Za6FZweGbqI0UA8W//3pEbkvFKkplHuLkyAhWgFgjXN1jI4E82dRjYtoQlEBQzWJZyHgfmQgIaWMuKHyDg/6BEzypM/b+ZX5QMZMYwS+Q+wM+LWLi1RWPIj07a/UdbeFSaU5Bq1YXLDcLEnSGvWDrrZZBv+c6tx95zSeh17rFnYEYjA7d9j4yGQ1mBUsYG5xbjGFNtszW/ov5lq+0/9pJ+A87c/gUVXAs3xtDPaJs/RfwNENDE2qCHOwFImwOqhDRgzAz6AtgouY7LLmzpcQSLvV0YlJ4YTfDxJ4TgGpALHKujkw5q+W+ZKK/UEYPYOqr7v+VqE477BtXOZx6zwSxZKMVCAFkOx21AYPTNo/02J9oy3koCwYGdpgxgs9hxIXNnk9OioJPDVzEYHF9ckvyHnClvdnhNXS5b17RDZlb+L55uCE0BjYJ1wmO+00SRff9MXiH5j30fJPNB3TAvN2Ev+FpgK9R2SlN5YfHoiBhHyKY4PrYd69KaUH2WZ7mTAO+aeF0o0leb8J81NMNY=
  java.lang.Exception
        at com.sankuai.xm.protobase.utils.a.c(Native Method)
        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.channel.b.F(Unknown Source:30)
        at com.sankuai.xm.login.manager.channel.b.x(ConnectionChannel.java:69)
        at com.sankuai.xm.login.manager.channel.b.w(ConnectionChannel.java:8)
        at com.sankuai.xm.login.manager.channel.b$a.a(ConnectionChannel.java:29)
        at com.sankuai.xm.login.manager.channel.b.a(ConnectionChannel.java:5)
        at com.sankuai.xm.login.manager.channel.e.a(Connector.java:3)
        at com.sankuai.xm.login.net.e.d(NetTcpLink.java:13)
        at com.sankuai.xm.login.net.f.f(SocketPump.java:6)
        at com.sankuai.xm.login.net.f.d(SocketPump.java:9)
        at com.sankuai.xm.login.net.taskqueue.e.b(TaskPump.java:1)
        at com.sankuai.xm.login.net.taskqueue.b$a.run(AbstractQueue.java:17)
        at com.sankuai.android.jarvis.i.run(JarvisRunnableProxy.java:13)
        at java.lang.Thread.run(Thread.java:919)
        at com.sankuai.android.jarvis.p.run(JarvisThreadProxy.java:3)
        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.模糊判定是初始化数据包,跟进堆栈com.sankuai.xm.login.manager.channel.b.x(ConnectionChannel.java:69) 方法太长判断很多 贴出主要代码部分

                     if (i3 == 0) {
                            com.sankuai.xm.login.beans.b bVar = (com.sankuai.xm.login.beans.b) this.f89418e;
                            com.sankuai.xm.base.proto.protosingal.h hVar = new com.sankuai.xm.base.proto.protosingal.h();// new 了一个类然后传参数进去
                            hVar.L(bVar.d);
                            hVar.f = bVar.f89407e;  //TCP数据参数
                            hVar.g = bVar.f;  //TCP数据参数
                            hVar.h = bVar.g;  //TCP数据参数
                            hVar.i = bVar.h;   //TCP数据参数
                            o.o().h();
                            hVar.j = 1;   //TCP数据参数
                            hVar.k = o.o().a(); //TCP数据参数
                            o.o().k();
                            hVar.l = 1;   //TCP数据参数
                            hVar.m = bVar.i   //TCP数据参数
                            hVar.n = bVar.j;   //TCP数据参数
                            hVar.o = h2   //TCP数据参数
                            hVar.p = q();     //TCP数据包参数 以上都是传参数进去
                            if (F.d(hVar.f) || F.d(hVar.h)) {
                                d.c("ConnectionChannel::doAuth:: PLoginByPassport, passport or device==null", new Object[0]);
                                u(21, 0, "", "", "", null);
                            } else {
                                byte[] marshall = hVar.marshall();  // 这里是取出原数据的方法
                                StringBuilder m2 = android.arch.core.internal.b.m("ConnectionChannel::doAuth:: PLoginByPassport, passport = ");
                                m2.append(bVar.f89407e);
                                m2.append(", device = ");
                                m2.append(bVar.g);
                                m2.append(",deviceData = ");
                                m2.append(hVar.n);
                                m2.append(", crc ");
                                if (marshall != null) {
                                    str = CommonUtil.a(marshall);
                                }
                                m2.append(str);
                                d.f(m2.toString());      
                                F(marshall);    //这里是  x(ConnectionChannel.java:69)  最后TCP协议发送前处理数据的地方  包含加密
                            }

4.跟进 byte[] marshall = hVar.marshall();

public final class h extends g {
    public static ChangeQuickRedirect changeQuickRedirect;
    public String f;
    public String g;
    public String h;
    public String i;
    public short j;
    public int k;
    public short l;
    public String m;
    public String n;
    public boolean o;
    public long p;
    @Override // com.sankuai.xm.base.proto.protobase.g, com.sankuai.xm.base.proto.protobase.b
    public final byte[] marshall() {   //返回原数据的marshall 方法
        O(196721);   //消息标签
        D(this.f);
        D(this.g);
        D(this.h);
        D(this.i);
        C(this.j);
        z(this.k);
        C(this.l);
        D(this.m);
        D(this.n);
        v(Boolean.valueOf(this.o));
        A(this.p);
        D(null);    //  以上就是new的一个类 然后把参数全部都传进处理完毕后存放在 public ByteBuffer f87924b;
        return super.marshall(); //返回的是他的父类的marshall方法 跟进去
    }
}

5.简单列举一个D方法 传进的 D(this.f);

    public final void D(String str) {
        Object[] objArr = {str};
        ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
        if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, 444570)) {
            PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, 444570);
        } else if (str == null) {    //str 不为空
            b(2);
            this.f87924b.putShort(0);
        } else {
            try {
                int d = d(str);
                if (d <= 32767) {
                    short s = (short) d;
                    b(s + 2);
                    this.f87924b.putShort(s);   //传进数据的长度
                    this.f87924b.put(str.getBytes());  //传进数据的内容
                    return;
                }
                throw new RuntimeException("string too long");
            } catch (UnsupportedEncodingException e2) {
                throw new RuntimeException(e2);
            }
        }
    }

6.跟进父类super.marshall() 的marshall方法

    public byte[] marshall() {
        int i;
        int i2;
        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 一个byte[]
        this.f87924b.position(0);           
        this.f87924b.get(bArr);                //把数据存放进bArr 里面
        int F = F(bArr);                            //CRC32校验
        this.f87924b.putInt(12, F);          //把CRC32校验数据存放进第12 位置里面
        this.c.f87922e = F;
        this.f87924b.position(0);
        this.f87924b.get(bArr);              //把this.f87924b数据包存进bArr 里面
        this.f87924b = null;
        return bArr;                             //返回最终原数据包
    }

7.原始数据包已经分析完毕,现在堆栈处at com.sankuai.xm.protobase.utils.a.c(Native Method)  最后调用该方法进行AES/CTR/NoPadding 加密之后再发送到服务器。

 public final byte[] c(byte[] bArr) {
        byte[] bArr2;
        if (bArr != null && bArr.length >= 24) {
            try {
                int i = this.c ? 16 : 0;
                byte[] bArr3 = new byte[(bArr.length + i)];
                SecretKeySpec secretKeySpec = new SecretKeySpec(this.f89761b, "AES");
                Cipher instance = Cipher.getInstance("AES/CTR/NoPadding");
                if (i > 0) {
                    bArr2 = new byte[i];
                    new SecureRandom().nextBytes(bArr2);
                } else {
                    bArr2 = new byte[instance.getBlockSize()];
                }
                instance.init(1, secretKeySpec, new IvParameterSpec(bArr2));
                byte[] doFinal = instance.doFinal(bArr, 24, bArr.length - 24);// 取整个TCP数据包的第25位到末尾进行doFinal加密
                System.arraycopy(bArr, 0, bArr3, 0, 24);
                if (i > 0) {
                    ByteBuffer allocate = ByteBuffer.allocate(4);
                    allocate.position(0);
                    allocate.put(bArr3, 0, 4);
                    allocate.flip();
                    int i2 = allocate.getInt();
                    allocate.flip();
                    allocate.putInt(i2 + i);
                    allocate.flip();
                    byte[] bArr4 = new byte[4];
                    allocate.get(bArr4);
                    System.arraycopy(bArr4, 0, bArr3, 0, 4);
                    System.arraycopy(bArr2, 0, bArr3, 24, i);
                }
                System.arraycopy(doFinal, 0, bArr3, i + 24, doFinal.length);//取原数据包的前25位  +  加密后的数据组成最终TCP服务器认可的数据包
                return bArr3;
            } catch (Exception e2) {
                com.sankuai.xm.log.a.e(e2);
            }
        }
        return bArr;
    }

8.至此 ,初始化数据包并登陆服务器分析完毕,代码没多少就是需要花费点时间调试。

二进制数据包解析:

原始数据包:{ 0, 0, 1, 193, 0, 3, 0, 113, 0, 21, 0, 0, 244, 188, 245, 131, 0, 0, 0, 2, 0, 0, 0, 0, 0, 10, 49, 57, 49, 53, 57, 56, 52, 52, 52, 53, 0, 228, 48, 50, 48, 50, 50, 55, 50, 51, 48, 98, 99, 97, 52, 49, 50, 48, 54, 99, 100, 48, 97, 52, 99, 100, 48, 55, 48, 54, 52, 55, 50, 56, 100, 52, 97, 100, 52, 98, 49, 97, 48, 53, 97, 48, 54, 50, 101, 53, 52, 98, 101, 97, 55, 97, 50, 97, 98, 57, 54, 98, 102, 50, 55, 97, 97, 97, 99, 100, 98, 53, 98, 101, 50, 52, 54, 99, 55, 51, 57, 102, 48, 52, 51, 50, 55, 51, 57, 99, 51, 101, 50, 52, 54, 52, 48, 97, 101, 102, 51, 51, 56, 48, 57, 50, 48, 55, 98, 50, 48, 57, 54, 56, 102, 102, 102, 56, 48, 48, 48, 48, 48, 48, 48, 48, 102, 99, 49, 100, 48, 48, 48, 48, 48, 102, 49, 53, 100, 99, 102, 52, 52, 99, 98, 54, 102, 54, 101, 48, 102, 54, 50, 52, 54, 97, 53, 98, 101, 101, 51, 100, 97, 48, 53, 102, 56, 54, 50, 101, 97, 53, 102, 51, 51, 51, 52, 57, 53, 101, 57, 57, 49, 50, 52, 101, 99, 99, 51, 50, 98, 49, 54, 54, 48, 102, 100, 49, 97, 54, 102, 51, 102, 99, 50, 49, 56, 51, 55, 54, 54, 56, 102, 48, 101, 98, 52, 48, 53, 54, 56, 54, 97, 55, 100, 54, 100, 102, 51, 101, 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, 8, 49, 49, 46, 49, 50, 46, 49, 52, 0, 1, 0, 61, 193, 102, 0, 1, 0, 51, 50, 97, 102, 100, 100, 54, 51, 48, 54, 98, 57, 100, 52, 50, 52, 49, 56, 49, 52, 101, 50, 50, 52, 52, 50, 99, 98, 49, 53, 101, 99, 55, 97, 49, 54, 55, 49, 51, 57, 52, 49, 51, 50, 54, 53, 51, 49, 57, 55, 48, 50, 0, 56, 123, 34, 100, 101, 118, 105, 99, 101, 73, 100, 34, 58, 34, 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, 34, 125, 0, 3, 69, 29, 101, 57, 67, 207, 186, 0, 0 }

1. { 0, 0, 1, 193 }   //数据包总长度 449
2. { 0, 3, 0, 113 }    //消息标签【196721】
3. { 0, 21, 0, 0 }      //未知  暂时固定
4. { 244, 188, 245, 131 }   //CRC32 数据包校验
5. { 0, 0, 0, 2, 0, 0, 0, 0, 0 }  //调用次数  固定
6. {10, 49, 57, 49, 53, 57, 56, 52, 52, 52, 53 }  //长度为10位的 userid  由于是文章 此处设为随机
7. { 228, 48, 50, 48, 50, 50, 55, 50, 51, 48, 98, 99, 97, 52, 49, 50, 48, 54, 99, 100, 48, 97, 52, 99, 100, 48, 55, 48, 54, 52, 55, 50, 56, 100, 52, 97, 100, 52, 98, 49, 97, 48, 53, 97, 48, 54, 50, 101, 53, 52, 98, 101, 97, 55, 97, 50, 97, 98, 57, 54, 98, 102, 50, 55, 97, 97, 97, 99, 100, 98, 53, 98, 101, 50, 52, 54, 99, 55, 51, 57, 102, 48, 52, 51, 50, 55, 51, 57, 99, 51, 101, 50, 52, 54, 52, 48, 97, 101, 102, 51, 51, 56, 48, 57, 50, 48, 55, 98, 50, 48, 57, 54, 56, 102, 102, 102, 56, 48, 48, 48, 48, 48, 48, 48, 48, 102, 99, 49, 100, 48, 48, 48, 48, 48, 102, 49, 53, 100, 99, 102, 52, 52, 99, 98, 54, 102, 54, 101, 48, 102, 54, 50, 52, 54, 97, 53, 98, 101, 101, 51, 100, 97, 48, 53, 102, 56, 54, 50, 101, 97, 53, 102, 51, 51, 51, 52, 57, 53, 101, 57, 57, 49, 50, 52, 101, 99, 99, 51, 50, 98, 49, 54, 54, 48, 102, 100, 49, 97, 54, 102, 51, 102, 99, 50, 49, 56, 51, 55, 54, 54, 56, 102, 48, 101, 98, 52, 48, 53, 54, 56, 54, 97, 55, 100, 54, 100, 102, 51, 101 }    //长度为228位的 token令牌  由于是文章 此处设为随机
8. { 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 }   //长度为41 的 devicesid
9. { 8, 49, 49, 46, 49, 50, 46, 49, 52 }  // 长度为8 的 APP Vsersion
10. { 1, 0, 61, 193, 102, 0, 1 }  //未知  固定
11. { 51, 50, 97, 102, 100, 100, 54, 51, 48, 54, 98, 57, 100, 52, 50, 52, 49, 56, 49, 52, 101, 50, 50, 52, 52, 50, 99, 98, 49, 53, 101, 99, 55, 97, 49, 54, 55, 49, 51, 57, 52, 49, 51, 50, 54, 53, 51, 49, 57, 55, 48, 50 }   //长度为51的dpid 设备id
12. { 56, 123, 34, 100, 101, 118, 105, 99, 101, 73, 100, 34, 58, 34, 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, 34, 125 }  //长度为56的 devicesid
13. { 0, 3, 69, 29, 101, 57, 67, 207, 186, 0, 0 }    // 未知 固定

组包发送登陆成功,小号发送消息解密

2024年2月15日22时20分45秒 更新通讯密钥成功!

登陆聊天服务器:103.X.X.X:8500 | 连接是否成功: 真
===================================================================
当前心跳包1发送:真
当前心跳包2发送:真
------------------------------------------------
对方回复消息时间: 2024年2月15日22时21分3秒

Content:【 1 】
------------------------------------------------------------------
对方回复消息时间: 2024年2月15日22时21分9秒

Content:【 登录服务器的流程已经完成仅供学习交流 若侵犯了权益,麻烦管理帮忙删除,谢谢 】
------------------------------------------------------------------

【至此 {登陆服务器}二进制数据包解析完毕!再次声明仅供学习交流 若侵犯了权益,麻烦管理帮忙删除,谢谢】

免费评分

参与人数 6威望 +1 吾爱币 +25 热心值 +6 收起 理由
offerking + 1 + 1 我很赞同!
孺子韫 + 1 + 1 我很赞同!
timeslover + 1 + 1 用心讨论,共获提升!
allspark + 1 + 1 用心讨论,共获提升!
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
T4DNA + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 西枫游戏 发表于 2024-2-16 21:30
ak472pj 发表于 2024-2-16 13:30
谢谢分享,二进制包解析是手动分析的吗?

是的哦  看帖子可能觉得比较简单,手动分析记录了好多数据
yixinyixi 发表于 2024-2-15 23:38
Y0uD1 发表于 2024-2-15 23:44
lj98 发表于 2024-2-16 00:06
谢谢分享,有点深度
52pojieplayer 发表于 2024-2-16 01:37
谢谢分享,收藏学习!
Heybo. 发表于 2024-2-16 04:54

谢谢分享,收藏学习!
kkkkkkkkn 发表于 2024-2-16 08:12
现在网络分析也可以用java了吗(纯小白望解惑)
ltgb 发表于 2024-2-16 08:16
对我们来说不简单
ztqddj007 发表于 2024-2-16 08:16
受益匪浅 谢谢
tzblue 发表于 2024-2-16 09:11
如果获取了本地解密方法,就可以在中途抓数据包解密了,是吧?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 12:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表