某运动APP的登录协议分析

news2025/1/11 20:04:35

前言

最近在尝试逆向方向相关的探索,针对某款运动APP的登录协议进行了分析,此文记录一下分析过程与结果,仅供学习研究,使用的工具较少,内容也比较简单,新手项,大佬请跳过。针对密码登录模块进行分析,随便输入一个手机号与密码,后续使用抓包工具分析,针对登录协议的几个字段从学习角度还是值得看下实现逻辑的。

抓包

  1. 抓包使用 Charles,请自行安装并配置证书
  2. 抓取登陆接口,点击密码登陆。使用假账密测试抓包,能够抓包成功

    图片描述

Sign分析

首先能看到请求头里面有sign字段,针对该字段进行分析:

sign: b61df9a8bce7a8641c5ca986b55670e633a7ab29

整体长度为40,常用的MD5长度为32,第一反应不太像,但是也有可能md5以后再拼接其它字段,sha1散列函数的长度是40,正好吻合。那我们就一一验证,先看下是否有MD5的痕迹,直接写脚本frida试着跑下。 脚本内容比较明确,针对MD5的Init、Update、Final分别hook打印看下输入与输出,下面给到关键代码:

// hook CC_MD5

// unsigned char * CC_MD5(const void *data, CC_LONG len, unsigned char *md);

Interceptor.attach(Module.findExportByName("libcommonCrypto.dylib", g_funcName), {

    onEnter: function(args) {

        console.log(g_funcName + " begin");

        var len = args[1].toInt32();

        console.log("input:");

        dumpBytes(args[0], len);

        this.md = args[2];

    },

    onLeave: function(retval) {

        console.log(g_funcName + " return value");

        dumpBytes(this.md, g_funcRetvalLength);

        console.log(g_funcName + ' called from:\n' +

            Thread.backtrace(this.context, Backtracer.ACCURATE)

            .map(DebugSymbol.fromAddress).join('\n') + '\n');

    }

});

// hook CC_MD5_Update

// int CC_MD5_Update(CC_MD5_CTX *c, const void *data, CC_LONG len);

Interceptor.attach(Module.findExportByName("libcommonCrypto.dylib", g_updateFuncName), {

    onEnter: function(args) {

        console.log(g_updateFuncName + " begin");

        var len = args[2].toInt32();

        console.log("input:");

        dumpBytes(args[1], len);

    },

    onLeave: function(retval) {

        console.log(g_updateFuncName + ' called from:\n' +

            Thread.backtrace(this.context, Backtracer.ACCURATE)

            .map(DebugSymbol.fromAddress).join('\n') + '\n');

    }

});

// hook CC_MD5_Final

// int CC_MD5_Final(unsigned char *md, CC_MD5_CTX *c);

Interceptor.attach(Module.findExportByName("libcommonCrypto.dylib", g_finalFuncName), {

    onEnter: function(args) {

        //console.log(func.name + " begin");

        finalArgs_md = args[0];

    },

    onLeave: function(retval) {

        console.log(g_finalFuncName + " return value");

        dumpBytes(finalArgs_md, g_funcRetvalLength);

        console.log(g_finalFuncName + ' called from:\n' +

            Thread.backtrace(this.context, Backtracer.ACCURATE)

            .map(DebugSymbol.fromAddress).join('\n') + '\n');

    }

});

 

很幸运,在打印中明显看到了sign相关的内容打印,但是缺少sign的后面一部分,那就明确sign值的构成为32(md5)+8,先看下md5的数据构造过程。

b61df9a8bce7a8641c5ca986b55670e6 33a7ab29

图片描述

 

 

通过打印可以明确的看到,sign的MD5由三部分数据组成,分别为:bodyData+Url+Str,body数据也可从Charles获取到。

  • {"body":"5gJEXtLqe3tzRsP8a/bSwehe0ta3zQx6wG7K74sOeXQ6Auz1NI1bg68wNLmj1e5Xl7CIwWelukC445W7HXxJY6nQ0v0SUg1tVyWS5L8E2oaCgoSeC6ypFNXV2xVm8hHV"}

  • /account/v4/login/password

  • V1QiLCJhbGciOiJIUzI1NiJ9

  • 图片描述

     

    图片描述

     

    Sign尾部分析

    接下来我们针对Sign的尾部数据进行分析,单纯盲猜或者挂frida脚本已经解决不了问题了,我们用IDA看下具体的实现逻辑,当然上面的MD5分析也可以直接从IDA反编译入手,通过搜索sign关键字进行定位,只是我习惯先钩一下脚本,万一直接命中就不用费时间去分析了...

    通过MD5的脚本打印,我们也能看到相关的函数调用栈,这对于我们快速定位也提供了很大的方便。我们直接搜索[KEPPostSecuritySign kep_signWithURL: body:]方法,可以看到明显的字符串拼接的痕迹,IDA还是比较智能的,已经识别出了MD5的salt值。

  •  

    图片描述

     

通过分析,定位到[NSString kep_networkStringOffsetSecurity]函数,在内部进行了字符串的处理,在循环里面进行了各种判断以及移位操作,不嫌麻烦的话可以分析一下逻辑,重写一下处理流程。 

 

图片描述

 

针对 AES128、DES、3DES、CAST、RC4、RC2、Blowfish等加密算法进行hook,脚本的关键代码如下: 

var handlers = {

    CCCrypt: {

        onEnter: function(args) {

            var operation = CCOperation[args[0].toInt32()];

            var alg = CCAlgorithm[args[1].toInt32()].name;

            this.options = CCoptions[args[2].toInt32()];

            var keyBytes = args[3];

            var keyLength = args[4].toInt32();

            var ivBuffer = args[5];

            var inBuffer = args[6];

            this.inLength = args[7].toInt32();

            this.outBuffer = args[8];

            var outLength = args[9].toInt32();

            this.outCountPtr = args[10];

            if (this.inLength < MIN_LENGTH || this.inLength > MAX_LENGTH){

                return;

            }

            if (operation === "kCCEncrypt") {

                this.operation = "encrypt"

                console.log("***************** encrypt begin **********************");

            else {

                this.operation = "decrypt"

                console.log("***************** decrypt begin **********************");

            }

            console.log("CCCrypt(" +

                "operation: " this.operation + ", " +

                "CCAlgorithm: " + alg + ", " +

                "CCOptions: " this.options + ", " +

                "keyBytes: " + keyBytes + ", " +

                "keyLength: " + keyLength + ", " +

                "ivBuffer: " + ivBuffer + ", " +

                "inBuffer: " + inBuffer + ", " +

                "inLength: " this.inLength + ", " +

                "outBuffer: " this.outBuffer + ", " +

                "outLength: " + outLength + ", " +

                "outCountPtr: " this.outCountPtr + ")"

            );

            //console.log("Key: utf-8 string:" + ptr(keyBytes).readUtf8String())

            //console.log("Key: utf-16 string:" + ptr(keyBytes).readUtf16String())

            console.log("key: ");

            dumpBytes(keyBytes, keyLength);

            console.log("IV: ");

            // ECB模式不需要iv,所以iv是null

            dumpBytes(ivBuffer, keyLength);

            var isOutput = true;

            if (!SHOW_PLAIN_AND_CIPHER && this.operation == "decrypt") {

                isOutput = false;

            }

            if (isOutput){

                // Show the buffers here if this an encryption operation

                console.log("In buffer:");

                dumpBytes(inBuffer, this.inLength);

            }

             

        },

        onLeave: function(retVal) {

            // 长度过长和长度太短的都不要输出

            if (this.inLength < MIN_LENGTH || this.inLength > MAX_LENGTH){

                return;

            }

            var isOutput = true;

            if (!SHOW_PLAIN_AND_CIPHER && this.operation == "encrypt") {

                isOutput = false;

            }

            if (isOutput) {

                // Show the buffers here if this a decryption operation

                console.log("Out buffer:");

                dumpBytes(this.outBuffer, Memory.readUInt(this.outCountPtr));

            }

            // 输出调用堆栈,会识别类名函数名,非常好用

            console.log('CCCrypt called from:\n' +

                Thread.backtrace(this.context, Backtracer.ACCURATE)

                .map(DebugSymbol.fromAddress).join('\n') + '\n');

        }

    },

};

if (ObjC.available) {

    console.log("frida attach");

    for (var func in handlers) {

        console.log("hook " + func);

        Interceptor.attach(Module.findExportByName("libcommonCrypto.dylib", func), handlers[func]);

    }

else {

    console.log("Objective-C Runtime is not available!");

}

 

查看脚本的输出日志,直接命中了AES128的加密算法,并且输出的Base64数据完全匹配,只能说运气爆棚。 

 

 

简单的变形以后,即使脚本能hook到对应的函数,但是想直接脱机调用结果还是不可以的,此时就要不得不进行反编译分析或者动态调试,此时配合代码混淆、VMP等静态防护手段,再加上反调试等安全手段,对于攻击的门槛也相应的提高。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/859088.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot整合WebSocket实现定时任务消息推送

在平时项目开发中&#xff0c;肯定有很多小伙伴会需要实现定时向某个页面推送消息的功能&#xff0c;为了解决大家无从下手的问题&#xff0c;加哥今天展示一套简单的代码解决方案。 1.创建WebSocketConfig配置类 在这个类中注入ServerEndpointExporter&#xff0c;这个bean会…

vmwera中安装的centos8出现ifconfig不可用

刚刚在虚拟机中装好centos结果发现自己的ifconfig命令不可用。 看一下环境变量里有没有ifconfig命令的路径&#xff0c;因为ifconfig是在/sbin路径下的&#xff0c;root用户登录进去才可以运行&#xff0c;先看一下root用户的环境变量。 root用户的环境变量里是有/sbin路径的&a…

数据链路层 -帧结构

帧结构 802.3 有线网卡的帧格式802.11 无线网卡的帧格式 802是指一个国际标准委员会 IEEE 802 帧头主要包括三个部分&#xff1a; 目标MAC&#xff08;6个字节&#xff09;源MAC &#xff08;6个字节&#xff09;类型 &#xff08;包含两种类型&#xff1a;0x0800【IP协议】 …

【生成式AI】ProlificDreamer论文阅读

ProlificDreamer 论文阅读 Project指路&#xff1a;https://ml.cs.tsinghua.edu.cn/prolificdreamer/ 论文简介&#xff1a;截止2023/8/10&#xff0c;text-to-3D的baseline SOTA&#xff0c;提出了VSD优化方法 前置芝士:text-to-3D任务简介 text-to-3D Problem text-to-3D…

关于Postman如何配置随请求携带token

文章目录 一些吐槽实际应用 一些吐槽 首先吐槽一下 postman官网的文档说明&#xff0c;真是乱七八糟&#xff0c;一点都不清晰&#xff0c;能不能好好写用户手册啊&#xff08;比如把用户都当作初始小白&#xff09; 然后吐槽一下网上铺天盖地让我写js脚本应用全局access toke…

leetcode - 75. 颜色分类(java)

颜色分类 leetcode - 75. 颜色分类题目描述双指针代码演示 双指针算法专题 leetcode - 75. 颜色分类 难度 - 中等 原题链接 - 颜色分类 题目描述 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&…

Ansys Lumerical | 米氏散射 FDTD

计算平面波激发的纳米粒子的散射和吸收截面、局部场增强和远场散射分布&#xff08;Mie 散射&#xff09;。将截面和远场结果与解析解进行比较&#xff0c;以验证仿真的准确性。&#xff08;联系我们获取文章附件&#xff09; 概述 纳米粒子的散射特性通常用场增强、横截面和远…

AtcoderABC223场

A - Exact PriceA - Exact Price 题目大意 高橋的钱包里只有一个或多个100日元硬币&#xff0c;没有其他的东西。现在给定一个金额X&#xff0c;需要判断是否可能存在这样的情况&#xff0c;使得钱包中的总金额是X日元。 思路分析 根据题目要求&#xff0c;钱包中只有100日元…

【Yolov5+Deepsort】训练自己的数据集(2)| 目标检测追踪 | 轨迹绘制

&#x1f4e2;前言&#xff1a;本篇是关于如何使用YoloV5Deepsort训练自己的数据集&#xff0c;从而实现目标检测与目标追踪&#xff0c;并绘制出物体的运动轨迹。本章讲解的为第二部分内容&#xff1a;训练集的采集与划分&#xff0c;Yolov5模型的训练。本文中用到的数据集均为…

【C语言学习——————文件处理操作的简单介绍与讲解】

欢迎阅读新一期的c语言学习模块————文件处理操作 ✒️个人主页&#xff1a;-_Joker_- &#x1f3f7;️专栏&#xff1a;C语言 &#x1f4dc;代码仓库&#xff1a;c_code &#x1f339;&#x1f339;欢迎大佬们的阅读和三连关注&#xff0c;顺着评论回访&#x1f339;&#…

【工作记录】mysql中实现分组统计的三种方式

前言 实际工作中对范围分组统计的需求还是相对普遍的&#xff0c;本文记录下在mysql中通过函数和sql完成分组统计的实现过程。 数据及期望 比如我们获取到了豆瓣电影top250&#xff0c;现在想知道各个分数段的电影总数. 表数据如下: 期望结果: 实现方案 主要思路是根据s…

国产芯力特Mini LIN SBC SIT1028Q应用方案,可替代TJA1028

SIT1028Q是一款内部集成高压LDO稳压源的本地互联网络&#xff08;LIN&#xff09;物理层收发器&#xff0c;可为外部ECU&#xff08;Electronic Control Unit&#xff09;微控制器或相关外设提供稳定的5V/3.3V电源&#xff0c;该LIN收发器符合LIN2.0、LIN2.1、LIN2.2、LIN2.2A、…

学习嵌入式系统的推荐步骤:

C语言&#xff1a;作为基础中的基础&#xff0c;选择一本常用的C语言教材&#xff0c;并注意通过实践编写习题、编译运行代码来加深理解。动手实践是非常重要的。 微机原理与接口技术&#xff1a;这本教材将帮助你了解CPU的基本结构、工作原理以及与外设的交互。虽然开始可能有…

手把手教你如何从零开始搭建自己的鞋店商城

对于不懂技术的新手来说&#xff0c;建立一个鞋店商城可能会显得有些困难。然而&#xff0c;现在有一些方便易用的网站建设平台可以帮助您快速搭建一个鞋店商城。本文将介绍乔拓云网的建站教程&#xff0c;让您轻松完成建站。 步骤1&#xff1a;注册乔拓云网账号并登录 首先&a…

苹果Mac像Windows一样使用

一、将磁盘访问设置的像Windows一样&#xff1a; 1.1、点击任务栏第一个按钮打开“访达”&#xff0c;点击菜单栏上的访达-偏好设置&#xff1a; 1.2、勾选“硬盘”&#xff0c;这样macOS的桌面上就会显示一个本地磁盘&#xff0c;之后重命名为磁盘根&#xff0c;相当于window…

Token 失效退出至登录页面

1. 在登录页面&#xff0c;调用登录的接口后&#xff0c;直接写上当前时间&#xff0c;保存在本地 代码&#xff1a; // 点击登录login(form) {this.$refs[form].validate((valid) > {if (valid) {this.$API.Login(this.form).then((res) > {// console.log(res, "1…

专注于创意设计,为您的小程序和网站建设带来更多的可能性

随着移动互联网的快速发展&#xff0c;越来越多的企业开始关注小程序和网站建设&#xff0c;以此来拓展业务和提升品牌形象。 在这个领域中&#xff0c;创意设计扮演着关键的角色。它不仅可以帮助企业打造独特的形象和品牌&#xff0c;还能够提高用户体验和购买决策的效率。 因…

C语言每日一题:15:寻找峰值。

题目链接 思路一&#xff1a; 思路二&#xff1a; int findPeakElement(int* nums, int numsLen ) {// write code hereint left0;int rightnumsLen-1;int* curnums;int mid0;//特殊情况判断两个值&#xff0c;单增和单减if(cur[0]>cur[1]){return 0;}if(cur[numsLen-1]>…

PROFINET转DeviceNet网关普通网线能代替profinet吗

捷米JM-DNT-PN这款神器&#xff0c;连接PROFINET和DeviceNet网络&#xff0c;让两边数据轻松传输。 这个网关不仅从ETHERNET/IP和DEVICENET一侧读写数据&#xff0c;还可以将缓冲区数据交换&#xff0c;这样就可以在两个网络之间愉快地传递数据了&#xff01;而且&#xff0c;…

找不到msvcr120.dll,无法继续执行代码,怎么修复?

当msvcp120.dll文件丢失或找不到时&#xff0c;会导致无法运行使用C编写的程序。这可能是由于以下原因导致的&#xff1a; 1.删除或移动文件&#xff1a;如果你不小心删除了或移动了msvcp120.dll文件&#xff0c;你将无法找到它并加载它&#xff0c;从而导致程序无法正常运行。…