某工宝协议逆向-视频快进

查看 171|回复 10
作者:3800104045   
某工宝协议逆向-视频快进
1. 介绍
最近一个在建筑公司上班的朋友给我说,他们现在每天都要用手机在某工宝上面看视频学习建筑知识,不能一起愉快的游戏了,让我帮忙看看能不能快进一丢丢。本着积极学习的态度,顺便看看那些并不那么出名的APP的在数据传输的时候用的协议算法,见见世面。
2.抓包
为了简便起见,这里直接使用HTTPCanary开始抓包,定位到记录视频进度的关键部分。
[ol]

  • 登录。
    登录部分比想象中简单,竟然是直接明文传输


    image-20221119223753657.png (59.06 KB, 下载次数: 0)
    下载附件
    2022-11-19 23:42 上传

    这就很有意思了,说明重点不在这里。

  • 播放视频
    播放视频这里果然才是重点。目前市面上大概有以下两种记录学习进度方法:

  • 每隔固定时间上传一次记录

  • 自由上传记录,时间搭配在参数中
    不过,第一种方式,服务端可能存在主动校验,将服务器经过的时间与获得的请求经过的时间做一个比较,这种方式只能通过快速提交的方法实现快进。第二种方式,可能会存在其他与时间有关的东西做签名函数做重复校验,这种方式可以通过伪造观看位置的方法实现快进。
    根据抓包内容,可以看到,这里的记录进度的方式显示是通过将时间放在参数中记录。


    image-20221119224650605.png (118.21 KB, 下载次数: 0)
    下载附件
    2022-11-19 23:42 上传

    在提交的参数中,总共有以下几种:
    [
    {
         "classStuId": "6aa4b9df-fc16-4dc5-96b2-a9c7e8c68cc6",
         "id": 1,
         "lastPlayPotion": 0,
         "logType": "1",
         "memberId": "66ad20b2-f8e5-4576-a267-fdbf22189bb2",
         "packId": "fcf228cc-1ee4-11ed-a2ee-00ff07067ec4",
         "playEndPoint": 11,
         "playEndTime": "2022-11-19 18:02:21",
         "playStartPoint": 1,
         "playStartTime": "2022-11-19 18:02:11",
         "playType": 1,
         "sign": "803227E1CA186200B97DDEA748033BA1",
         "stuHourDetailId": "1ed655dc-9688-6ed7-9fca-39c8584a299d"
    }
    ]
    大胆猜测,其中的playEndPoint就是记录视频观看到那个位置的一个关键变量,因此我们只需要伪造这个变量,改成这个视频的总时间,应该就能实现快进的原理。
    其中有个关键参数sign应该就是我们这里最难的一部分了,这类跟随其余参数一起上传的sign一般都是某个散列函数,用来在服务端校验数据有没有被修改过,粗略猜测是个md5算法。

    [/ol]
    3. 反编译
    为了能够确定sign是如何得到的,这里对原有app进行反编译,这里使用jadx快速看一眼,没有加壳,感谢作者。根据关键发包链接检索代码,我们这里就直接搜索sign关键字


    image-20221119225811137.png (327.81 KB, 下载次数: 0)
    下载附件
    2022-11-19 23:42 上传

    包含了太多的sign,其实并不好找,我们可以根据发包链接videoLogSign去寻找


    image-20221119230005353.png (40.38 KB, 下载次数: 0)
    下载附件
    2022-11-19 23:42 上传

    最后可以找到这是返回Observable的函数注解函数实现的发包,继续深入检索postCommonVideoLog


    image-20221119230429161.png (70.05 KB, 下载次数: 0)
    下载附件
    2022-11-19 23:42 上传

    其中这一个吸引了注意,其中的List猜测可能是日志记录的实体列表。点进去查看得到VideoPlayLogEntity,这就是我们对应的视频记录的实体类。


    image-20221119230717435.png (164.61 KB, 下载次数: 0)
    下载附件
    2022-11-19 23:42 上传

    继续搜索VideoPlayLogEntity看看他的sign参数是何时被set进去的,真相已经很接近了。


    image-20221119231145034.png (86.98 KB, 下载次数: 0)
    下载附件
    2022-11-19 23:42 上传

    根据反编译的代码变量名,我们基本可以确定刚才的sign肯定是跟MD5有关系,至于怎么做的散列,需要在看看源码,点进去查看源码。其代码如下:
        public static void sgin(List list) {
            for (VideoPlayLogEntity videoPlayLogEntity : list) {
                LinkedHashMap linkedHashMap = new LinkedHashMap();
                linkedHashMap.put("playStartPoint", Integer.valueOf(videoPlayLogEntity.getPlayStartPoint()));
                linkedHashMap.put("playEndPoint", Integer.valueOf(videoPlayLogEntity.getPlayEndPoint()));
                linkedHashMap.put("playStartTime", videoPlayLogEntity.getPlayStartTime());
                linkedHashMap.put("playEndTime", videoPlayLogEntity.getPlayEndTime());
                linkedHashMap.put("playType", Integer.valueOf(videoPlayLogEntity.getPlayType()));
                String sgin = sgin(linkedHashMap, Constants.LogKey);
                Log.d("=====result:", "sgin=" + sgin);
                videoPlayLogEntity.sign = sgin;
            }
        }
    在这个方法中是将VideoPlayLogEntity类中的playStartPoint、playEndPoint、playStartTime、playEndTime、playType这五个参数放入LinkedHashMap中,然后传入另外一个重载的sgin方法中。这个重载的sgin还有一个参数是Constants.LogKey,点击得到定义的salt:
      public static final String LogKey = "d^*A%8^43YsrYZ$9";
    继续进入到重载的sgin方法中,代码如下:
        public static String sgin(Map map, String str) {
            Set keySet = map.keySet();
            String[] strArr = (String[]) keySet.toArray(new String[keySet.size()]);
            StringBuilder sb = new StringBuilder();
            for (String str2 : strArr) {
                if (String.valueOf(map.get(str2)).trim().length() > 0) {
                    sb.append(str2);
                    sb.append("=");
                    sb.append(String.valueOf(map.get(str2)).trim());
                    sb.append("&");
                }
            }
            sb.append("key=" + str);
            System.out.print("=====sgin:" + sb.toString());
            return md5(str, sb.toString()).toUpperCase();
        }
    这部分代码就是将接收到的Map中各个参数取出来并拼接成如下所示字符串:
    playStartPoint=3&playEndPoint=2297&playStartTime=2022-11-19 17:48:00&playEndTime=2022-11-19 17:48:33&playType=1&key=d^*A%8^43YsrYZ$9
    随后将这部分字符串放入定义的md5函数
        public static String md5(String str, String str2) {
            try {
                MessageDigest instance = MessageDigest.getInstance("MD5");
                instance.update(str.getBytes());
                instance.update(str2.getBytes(InternalZipConstants.CHARSET_UTF8));
                byte[] digest = instance.digest();
                String str3 = "";
                for (int i = 0; i
    很明显,这是对经典md5算法的一种魔改,将经典md5算法得到的结果与255做与操作然后与InputDeviceCompat.SOURCE_ANY(-256)做或操作,最后将得到的整数转成十六进制,取十六进制最后两位。
    4. 协议复现
    有了上述原理就很好复现出快进协议了,整理python的代码如下:
    def get_sign(sp, ep, st='2022-11-19 17:48:00', et='2022-11-19 17:48:33'):
        salt = 'd^*A%8^43YsrYZ$9'
        sign_input = f'playStartPoint={sp}&playEndPoint={ep}&playStartTime={st}&playEndTime={et}&playType=1&key=d^*A%8^43YsrYZ$9'
        m = hashlib.md5()
        m.update(salt.encode())
        m.update(bytes(sign_input, 'UTF-8'))
        digest = m.digest()
        result = ''
        for b in digest:
            h = hex(((b & 255) | -256) & 0xffffffff)
            result += h[-2:]
        return result.upper()
    这里有个坑,python的hex()函数转十六进制是不包括负号的,也就是不会把二进制第一个1看成是负号,而java的Integer.toHexString()这个函数会将二进制第一个1看作是负号。因此,python需要在与上0xffffffff才能得到与java一致的结果。
    最终复现结果得到sign为:


    image-20221119233441950.png (15.55 KB, 下载次数: 0)
    下载附件
    2022-11-19 23:42 上传

    5. 总结
    [ol]
  • 现有许多app基本混淆都不加,很容易被反汇编获取源代码,甚至对服务器发起攻击。
  • 就算不加混淆,也应该将加密函数放在so层,增加逆向难度。
    [/ol]

    下载次数, 快进

  • tomhex   

    谢谢大神分享,学习了
    我与秋风皆过客   


    latanghu 发表于 2022-12-6 20:59
    这个应该是典工宝,楼主这个分析给平时做APP的朋友提了醒,还是要注意该加密的地方要加密,要有安全意识!

    我们一群搞工程的,除了用电脑玩玩WPS和游戏,你还指望我们这群大老粗攻击服务器嘛
    xzhtx   

    谢谢分享
    孤寡   

    路过 学习一下啊
    lvlxl1   

    大佬牛逼
    wmsj666   

    感谢分享
    Ironhatking   

    牛牛牛牛
    ankh04   

    赞!思路清晰,自己写app的时候也应该多加小心了!
    Alan1020   

    牛逼啊大佬
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部