首页 > 其他分享 >某运动app的登陆协议分析

某运动app的登陆协议分析

时间:2023-10-10 16:22:55浏览次数:29  
标签:协议 Java app gotokeep keep 登陆 com utils common

sign分析

首先对https进行抓包,密码验证接口的http头部有一个x-sign字段的值为3bb776e25f1f4f7334f706d723adae79e26a6a56

hook libc.so的字符串和内存相关处理函数,然后进行栈回溯。可以hookmemcpy, memmove, memcmp, strlen, strstr, strncpy, strncmp, strncat,通过栈回溯可以看到libcryp.so+0x1e14位置会调用NewStringUTF生成sign字符串3bb776e25f1f4f7334f706d723adae79e26a6a56

libcryp.so+0x1e14位于导出函数Java_com_gotokeep_keep_common_utils_CrypLib_getEncryptDeviceId,此函数首先通过jni接口反射获取PackageManagerService中的apk签名信息并进行验证是否为0x5E38A0E8

然后获取从java层传进来的一个0x20长度字符串参数,对这个字符串调用自定义的算法生成8个字节的数据并放置在原字符串尾部,最后调用NewStringUTF并返回。

因为最后生成的sign是3bb776e25f1f4f7334f706d723adae79e26a6a56,所以前0x20个字节是3bb776e25f1f4f7334f706d723adae79,经过上述算法后生成了最后8个字节e26a6a56。此算法是将原0x20个字符串分成4组,每次倒着取每一组的一个字符参与计算最后生成一个字符,共循环8次,共生成8个字符e26a6a56。重写算法验证如下。

void getTail(const char* input, char* output) {
    int sum = 0;
    int value = 0;
    int index = 0;
    int v1 = 0, v2 = 0, v3 = 0, v4 = 0, v5 = 0, v6 = 0;

    for (index = 0; index + 8 >= 0; index--) {
        sum = 0;
        for (int i = 0; i < 4; i++) {
            value = input[index + 7 + i * 8];
            v1 = value - '0';
            if (v1 > 9) {
                if (value - 'a' > 5) {
                    if (value - 'A' >= 6) {
                        v1 = 0;
                    }
                    else {
                        v1 = value - '7';
                    }
                }
                else {
                    v1 = value - 'W';
                }
            }
            sum = sum + v1;
        }

        sum = sum + v2;
        v3 = sum + 0x3A1;
        v4 = sum + 0x3B0;
        if (v3 >= 0)
            v5 = v3;
        else
            v5 = v4;

        v2 = v5 >> 4;
        v6 = v3 - (v5 & 0xFFFFFFF0);

        if (v6 >= 10)
            output[index + 7] = 0x57 + v6;
        else
            output[index + 7] = 0x30 + v6;
    }
}

那么java层传入的0x20大小的字符串参数是什么呢!继续分析java层代码,因为Java_com_gotokeep_keep_common_utils_CrypLib_getEncryptDeviceId是静态注册,直接找到所有getEncryptDeviceId函数的引用(可以hook此函数查看栈回溯寻找),看到一个intercept函数。

查看intercept函数,看到sb4字符串经过四次拼接后调用MessageDigest.getInstance(EvpMdRef.MD5.JCA_NAME)进行MD5得到hash值,用hash值作为参数调用getEncryptDeviceId函数。

通过hook四次拼接中的相关函数得到每一次拼接的字符串。

Java.perform(function(){
    //com.gotokeep.keep.common.utils.n h()
    Java.use("com.gotokeep.keep.common.utils.n").h.implementation = function (str, num) {
        var result = this.h(str, num)
        console.log("string4 : ", str, " >> ", num, " to ", result)
        return result
    };
    
    //js.b.intercept(okhttp3.j.a aVar)
    Java.use("js.b").intercept.overload('okhttp3.j$a').implementation = function(aVar){
        //vk3.c.B0() String
        Java.use("vk3.c").B0.implementation = function(){
            var ret = this.B0()
            console.log("string1 : ", ret)
            return ret;
        }

        //js.b.b(hashMap) String
        Java.use("js.b").b.implementation = function(hashMap){
            var ret = this.b(hashMap)
            console.log("string2 : ", ret)
            return ret;
        }

        //hk3.m.d() String
        Java.use("hk3.m").d.implementation = function(){
            var ret = this.d()
            console.log("string3 : ", ret)
            return ret;
        }

        //com.gotokeep.keep.common.utils.h0.e(str)
        Java.use("com.gotokeep.keep.common.utils.h0").e.implementation = function(str){
            var ret = this.e(str)
            console.log("target string : ", ret)
            return ret;
        }
        return this.intercept(aVar)
    }
})

最后的拼接过程如下:第一次拼接的数据是body,第二次拼接的数据为空,第三次拼接的数据是url,第四次拼接的数据是字符串vQiLcJhbGcioijIUzI1NiJ9T/OgJAHfEag每个字符加2 + KeFEQvE-JeF5`每个字符加4)。最后经过md5 hash后生成0x20个字节就是sign的前0x20个字节。

sign的算法就是MD5(body + url + vQiLcJhbGcioijIUzI1NiJ9) + getTail(MD5(body + url + vQiLcJhbGcioijIUzI1NiJ9))

body分析

分析sign的时候看到第一次拼接的数据就是body,body是通过调用hk3.b.a()函数得到的。

hk3.b.a()获取的值是在hk3.q.$init构造函数中初始化的。

hook hk3.q.$init构造函数并进行栈回溯,可以看到其使用的是okhttp网络框架,定位到关键函数com.gotokeep.keep.fd.business.account.login.mvp.pnresenter.LoginNainActionPresenter.e()

com.gotokeep.keep.fd.business.account.login.mvp.pnresenter.LoginNainActionPresenter.e()会设置mobile, countryCode, countryName, password, oaid, android_id这些LoginParams登陆参数,然后调用com.gotokeep.keep.common.utils.n.i进行加密,加密之后调用retrofit2.g$b.enqueue()进行异步网络请求。

查看com.gotokeep.keep.common.utils.n.i是如何对LoginParams登陆参数进行加密的,首先将LoginParams登陆参数格式化为js格式,然后调用com.gotokeep.keep.common.utils.n.g对js数据进行加密

查看com.gotokeep.keep.common.utils.n.g发现是一个AES CBC加密。调用Java_com_gotokeep_keep_common_utils_CrypLib_getCrypKeyPl*Rxe76fx'fWWqR生成一个hash值,然后调用com.gotokeep.keep.common.utils.n.h(str, 2)将hash值的每一个字符都加2得到key,iv为2346892432920300,最后调用AES CBC加密后进行base64编码。

看一下Java_com_gotokeep_keep_common_utils_CrypLib_getCrypKey函数发现是一个hash算法。

hook整个加密过程中的相关函数

Java.perform(function(){
    //com.gotokeep.keep.common.utils.CrypLib getCrypKey()
    Java.use("com.gotokeep.keep.common.utils.CrypLib").getCrypKey.implementation = function(str){
        var result = this.getCrypKey(str)
        console.log(str, " to ", result)
        return result
    }

    //com.gotokeep.keep.common.utils.n h()
    Java.use("com.gotokeep.keep.common.utils.n").h.implementation = function (str, num) {
        var result = this.h(str, num)
        console.log(str, " >> ", num, " to ", result)
        return result
    };

    //javax.crypto.spec.SecretKeySpec
    Java.use("javax.crypto.spec.SecretKeySpec").$init.overload('[B', 'java.lang.String').implementation = function(key, str){
        console.log("AES key : ", byteToString(key))
        return this.$init(key, str)
    }
    //javax.crypto.spec.IvParameterSpec
    Java.use("javax.crypto.spec.IvParameterSpec").$init.overload('[B').implementation = function(iv){
        console.log("AES iv : ", byteToString(iv))
        return this.$init(iv)
    }
    //javax.crypto.Cipher  doFinal(bytes3)
    Java.use("javax.crypto.Cipher").doFinal.overload('[B').implementation = function(data){
        console.log("data : ", byteToString(data))
        var result = this.doFinal(data)
        console.log("AES encrypy data : ", result)
        return result
    }

    //android.util.Base64 encode()
    Java.use("android.util.Base64").encode.overload('[B', 'int').implementation = function(data, flag){
        var result = this.encode(data, flag)
        console.log("BASE64(AES encrypt data) : ", byteToString(result))
        return result
    }
})

得到body的加密过程如下。
key:调用Java_com_gotokeep_keep_common_utils_CrypLib_getCrypKeyPl*Rxe76fx'fWWqR生成的hash值为34dc37960e8b651a34dc37960e8b651a的每个字符加2得到Key为56fe59;82g:d873c
iv:2346892432920300
data:{"androidId":"14c5efodfe4e773f","countrycode":"86","countrywame":"CHN","mobile":"19137031751", "password":"qqq123456789"}
BASE64(AES encrypt data):/6jBElbU5ddJH9AiLtUWf370Drsp+BVsNaLsHbO3Twk7uu86z4a8T+vavaL99SXObrBGJI94g4MSOFPb05KQ6MrwTX0xQyNJpZwMmHgmXvRCGgI5hQLYNsO+XVTIZypSApnIMqT5qRgI/ed1I21XfV2SB2VkHOn8EB/3Le4Pbh8=

标签:协议,Java,app,gotokeep,keep,登陆,com,utils,common
From: https://www.cnblogs.com/revercc/p/17754995.html

相关文章

  • KdMapper扩展实现之REALiX(hwinfo64a.sys)
    1.背景  KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。 2.驱动信息 驱动名称hwinfo64a.sys 时间戳5472......
  • 国标GB28181协议接入平台中,如何单独对某路监控视频流进行控制操作?
    GB/T28181协议从本质上说和ONVIF都是一样的,目的都是为了降低视频监控设备互联的难度。该协议都是基于IP网络,如果要对接,需要有相关的协议转换模块。协议之间也并不矛盾,可以实现接入上的互补。GB/T28181不仅包括设备间的级联,也包含系统的级联,故并不矛盾。如网络摄像机通过ONVIF协议......
  • APP内如何实现小程序直播技术?
    如今,越来越“卷”的直播逐渐迈向精细化运营的阶段。抖音小程序作为商家经营的利器,可通过短视频、直播、主页挂载等场景中,以商家自播、达人直播等方式,让商家与消费者建立密切的互动关系,帮助实现内容的种草、商品的销售、或服务的供给等不同目标,紧密结合内容价值最终变现。小程序......
  • app直播源代码,android中几种常用的弹框
    app直播源代码,android中几种常用的弹框一、SweetAlertDialog弹框使用该控件需要添加依赖: implementation'com.github.f0ris.sweetalert:library:1.5.1'​下面是具体用法:  newSweetAlertDialog(this,SweetAlertDialog.WARNING_TYPE)        .setTitl......
  • Error: Failed to download metadata for repo 'appstream': Cannot prepare internal
    一背景跑了一份centos容器,想装一下net-tools,报如下错误Error:Failedtodownloadmetadataforrepo'appstream':Cannotprepareinternalmirrorlist:NoURLsinmirrorlist 二解决参考帖子:https://developer.aliyun.com/article/1165954  CentOS已经停止......
  • ASP .Net Core: AutoMapper与DTO的使用(自定类型转换)
    建立DTO物件创建名为Dtos文件夹,并添加TodoListSelectDto文件publicclassTodoListSelectDto{publicGuidTodoId{get;set;}publicstringName{get;set;}=null!;publicDateTimeInsertTime{get;set;}publicDateTimeUpdateTime{get......
  • 蓝牙MAP协议
    蓝牙MAP协议(MessageAccessProfile)是蓝牙技术联盟(BluetoothSIG)制定的一种蓝牙协议,用于手机和车载设备之间的短信同步。MAP协议可以让手机将短信发送到车载设备,也可以让车载设备将短信发送到手机。MAP协议的基本原理是使用OBEX协议传输短信数据。OBEX协议是一种无线文件传输协议......
  • 国标GB28181协议视频平台LntonGBS内网访问正常,公网无法访问是什么原因?
    国标视频云服务平台LntonGBS可支持通过国标GB28181协议,接入多路视频源设备,实现视频流的接入、转码、处理与分发等功能,对外输出的视频流格式包括RTSP、RTMP、FLV、HLS、WebRTC等。平台视频能力丰富灵活,包括监控直播、视频分发、录像、回看与检索、云存储、语音对讲、告警上报、云台......
  • 国标GB28181协议平台LntonGBS如何批量删除通道?
    国标视频云服务平台LntonGBS可支持通过国标GB28181协议,接入多路视频源设备,实现视频流的接入、转码、处理与分发等功能,对外输出的视频流格式包括RTSP、RTMP、FLV、HLS、WebRTC等。平台视频能力丰富灵活,包括监控直播、视频分发、录像、回看与检索、云存储、语音对讲、告警上报、云台......
  • 国标GB28181协议视频分析EasyGBS新版中iframe地址不能用如何解决
    iframe是HTML标签,在TSINGSEE青犀视频云边端架构全线视频智能分析平台中,分享页面内生成的iframe地址,可直接嵌入到前端页面中,调取播放画面。但是近期我们接到的用户咨询当中,发现EasyGBS1.4.4新版本有不少客户反应分享页面的iframe地址不能正常使用,总是报错:登录认证过期。......