a_bogus、msToken、fp、verifyFp | bdms_1.0.1.19 签名算法分析记录

【作者主页】:小鱼神1024

【擅长领域】:JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!若有侵权,请联系作者立即删除!

前言

最近听小伙伴说,a_bogus 签名算法又变了,出于学习目的,于是乎,我决定重新分析记录一下,希望能帮助到有需要的小伙伴。

前置分析

我们在请求header中发现,有很多请求都带有a_bogusmsTokenfpverifyFp这四个参数的值是动态变化的,所以我们猜测这四个参数应该是加密参数。

a_bogus、msToken、fp、verifyFp

逆向分析

fp、verifyFp

从上图可以发现,fpverifyFp这两个参数的值是一样的。

全局搜索 verifyFp,打上断点,重新请求,发现断住了。如下图:

a_bogus、msToken、fp、verifyFp

其中,fpverifyFp的值来自 r, 往上翻可以看到,var r = n.getFp(),打上断点,重新请求。进入getFp 函数,如下图:

a_bogus、msToken、fp、verifyFp

提取函数为:

function get_fp() {
    var e = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("")
      , t = e.length
      , n = Date.now().toString(36)
      , r = [];
    r[8] = r[13] = r[18] = r[23] = "_",
    r[14] = "4";
    for (var o = 0, i = void 0; o < 36; o++)
        r[o] || (i = 0 | Math.random() * t,
        r[o] = e[19 == o ? 3 & i | 8 : i]);
    return "verify_" + n + "_" + r.join("")
}
msToken

经过测试发现,它不是前端js生成的,是服务端生成之后,保存到cookie中的,如下图:

a_bogus、msToken、fp、verifyFp

经过多次对比发现,它的生成方式和 base64 算法生成相似,所以我们可以尝试模拟生成,代码如下:

function get_ms_token(length = 182) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
  let result = '';
  for (let i = 0; i < length; i++) {
      result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return encodeURIComponent(result);
}
a_bogus

重头戏来喽。打起精神来,这部分插桩分析可能有点枯燥,耐心看完,相信你会有收获的。

我也把自己当成小白,从头开始分析,一步步来。

由于平台大部分接口暂时没有对a_bogus 参数做强校验,但二级评论接口是个例外,如果不加a_bogus 参数,会返回错误。为了验证a_bogus 参数生成的是否正确,我们决定先从二级评论接口入手。

通过跟栈分析,发现a_bogus 参数是在 bdms.js 文件中生成的,点击进去,发现是 vmp 代码,如下图:

a_bogus、msToken、fp、verifyFp

有些小伙伴第一时间可能想补环节,但是,尝试过之后的结果会让你失望的,因为你都很难找到生成入口。。

所以,我们换个思路,直接插桩纯算。

说到插桩,你可以尝试一下我之前写的插桩日志框架,可能会让你事半功倍。传送门:终极逆向插桩日志框架,让浏览器崩溃成为历史!

使用日志框架在bdms.js 文件中插桩,如下图:

a_bogus、msToken、fp、verifyFp

a_bogus、msToken、fp、verifyFp

由于运算符比较多,我就不一一截图展示了。自己把常见的运算符都插桩下即可。

打开日志文件,开始分析。

a_bogus、msToken、fp、verifyFp
抖音a_bogus、msToken、fp、verifyFp
首先,我们发现请求参数通过加盐 dhzx, 得到新的值:device_platform=webapp&aid=6383&channel=channel_pc_web&item_id=7429636428608425267&comment_id=7431107242646258473&cut_version=1&cursor=0&count=3&item_type=0&update_version_code=170400&pc_client_type=1&pc_libra_divert=Mac&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1280&screen_height=800&browser_language=zh-CN&browser_platform=MacIntel&browser_name=Chrome&browser_version=131.0.0.0&browser_online=true&engine_name=Blink&engine_version=131.0.0.0&os_name=Mac+OS&os_version=10.15.7&cpu_core_num=8&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=0&webid=7427765608665040399&uifid=63dd93172fb5fdaa8078f33ae0ba1106c8e3f1f870eecbc4b54b497d452c3517e3b7a5ab3d4ec186d7be006b7a6d53846c6734c86b69acdaa2eb531c0fa92958a8cb940dd76c55017b279b6778ad806977b04a00d235639ba1505bb8c942fc1f1ffdb6b5bed6e26fc7db5b9400d1d9d20450dca0257efb46047e349b49ef1ed79c5ceaff02e8b12e4efffc9114f06bb77651d820b35a2638844fee7a6ac6db85&msToken=ahDrxe9ibTXPfbP25qOkt6QDX0xXfaehFEV-e5XofFV3eQQBqtpPVa_SYmy-Gdi2wSRdz4JN_U1I_fghOF2VNxh_B0A1MlFqT0vsgUqOvv5WRg-W_yIuqJrYt_e-76afo7T6nlzQIiSNZF04A82HjbCnogmaut96WjCkWSJivKn1TdYy04jsXw%3D%3D , + , dhzx , ====> , device_platform=webapp&aid=6383&channel=channel_pc_web&item_id=7429636428608425267&comment_id=7431107242646258473&cut_version=1&cursor=0&count=3&item_type=0&update_version_code=170400&pc_client_type=1&pc_libra_divert=Mac&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1280&screen_height=800&browser_language=zh-CN&browser_platform=MacIntel&browser_name=Chrome&browser_version=131.0.0.0&browser_online=true&engine_name=Blink&engine_version=131.0.0.0&os_name=Mac+OS&os_version=10.15.7&cpu_core_num=8&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=0&webid=7427765608665040399&uifid=63dd93172fb5fdaa8078f33ae0ba1106c8e3f1f870eecbc4b54b497d452c3517e3b7a5ab3d4ec186d7be006b7a6d53846c6734c86b69acdaa2eb531c0fa92958a8cb940dd76c55017b279b6778ad806977b04a00d235639ba1505bb8c942fc1f1ffdb6b5bed6e26fc7db5b9400d1d9d20450dca0257efb46047e349b49ef1ed79c5ceaff02e8b12e4efffc9114f06bb77651d820b35a2638844fee7a6ac6db85&msToken=ahDrxe9ibTXPfbP25qOkt6QDX0xXfaehFEV-e5XofFV3eQQBqtpPVa_SYmy-Gdi2wSRdz4JN_U1I_fghOF2VNxh_B0A1MlFqT0vsgUqOvv5WRg-W_yIuqJrYt_e-76afo7T6nlzQIiSNZF04A82HjbCnogmaut96WjCkWSJivKn1TdYy04jsXw%3D%3Ddhzx,后续就进入进行两次某种算法加密。

经过测试发现,这个是标准的 sm3 算法,即:sm3(sm3(搜索参数+dhzx))。当然了,你可以扣 sum 函数,也可以计算出值。

日志继续往下翻,发现盐值 dhzx 也进行两次 sm3 算法加密,即:sm3(sm3(dhzx))

日志继续发下翻,发现 user-agent 进入我们的视野,同时发现一个长度为3的数组,即:[0.00390625,1,12],经过测试,Windows 系统下,这个数组为 [0.00390625,1,8],都是固定的。

a_bogus、msToken、fp、verifyFp

后续就进入了一个循环计算,一开始我也不清楚它是什么算法,但是它有一个特点,就是先初始化一个从 255 - 0 的数组,然后又是一个 0 - 255 的循环,而且每次还进行相同的运算,如下:

a_bogus、msToken、fp、verifyFp

看它的运算规则,很像 rc4 算法,如下:

function rc4(key, str) {
    var s = [], i = 0, j = 0, x, res = '';
    for (var k = 0; k < 256; k++) {
        s[k] = k;
    }
    for (k = 0; k < 256; k++) {
        j = (j + s[k] + key.charCodeAt(k % key.length)) % 256;
        x = s[k];
        s[k] = s[j];
        s[j] = x;
    }
    for (var k = 0; k < str.length; k++) {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        x = s[i];
        s[i] = s[j];
        s[j] = x;
        res += String.fromCharCode(str.charCodeAt(k) ^ s[(s[i] + s[j]) % 256]);
    }
    return res;
}

所以,我们可以大胆猜测,它就是 rc4 算法,只不过它的 key 是一个数组,如下:

var key = [0.00390625, 1, 12];

经过代码验证,它不是标准的 rc4 算法,而是 rc4 算法的变种,即魔改的 rc4 算法。

遇到类似的运算加密,尽量不要根据日志去还原代码,很麻烦的,亲身经历过的。。。,而是尽量能找到类似结构的标准算法上去改造,这样对于插桩纯算,很节约大量时间。

所以根据日志,很容易就得到魔改后的 rc4 算法,如下:

function rc4_magic_encrypt(plaintext, key) {
  var s = [];
  for (var i = 0; i < 256; i++) {
    s[i] = 255 - i;
  }
  var j = 0;
  for (var i = 0; i < 256; i++) {
    j = (j * s[i] + j + key.charCodeAt(i % key.length)) % 256;
    var temp = s[i];
    s[i] = s[j];
    s[j] = temp;
  }

  var i = 0;
  var j = 0;
  var cipher = [];
  for (var k = 0; k < plaintext.length; k++) {
    i = (i + 1) % 256;
    j = (j + s[i]) % 256;
    var plaintext_charCodeAt = plaintext.charCodeAt(k);
    var temp = s[i];
    var t = (temp + s[j]) % 256;
    s[i] = s[j];
    s[j] = temp;
    cipher.push(String.fromCharCode(plaintext_charCodeAt ^ s[t]));
  }
  return cipher.join("");
}

a_bogus、msToken、fp、verifyFp

继续往下翻,可以看到,上述魔改后的 rc4 算法加密后,再进行三个字符一组进行循环,而且还看到:a << 16a << 8 等关键运算。此时,我暗自窃喜,因为这种运算,很容易想到 base64 算法。标准如下:

function standardBase64Encode(inputString) {
    // 标准Base64字符集
    var base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    // 处理一个3字节组的函数,将其转换为4个Base64字符
    function processGroup(a, b, c) {
        // 组合三个字节为一个24位整数
        var combined = (a << 16) | (b << 8) | c;

        // 从24位整数中提取4个6位部分
        var part1 = (combined >> 18) & 63; // 提取最高的6位
        var part2 = (combined >> 12) & 63; // 提取次高的6位
        var part3 = (combined >> 6) & 63;  // 提取次低的6位
        var part4 = combined & 63;         // 提取最低的6位

        // 将每部分映射到标准Base64字符集
        return [
            base64Chars.charAt(part1),
            base64Chars.charAt(part2),
            base64Chars.charAt(part3),
            base64Chars.charAt(part4)
        ];
    }

    // 将输入字符串转为字节数组
    var inputArray = [];
    for (var i = 0; i < inputString.length; i++) {
        inputArray.push(inputString.charCodeAt(i));
    }

    var encodedStr = ""; // 存储最终编码结果
    for (var i = 0; i < inputArray.length; i += 3) {
        // 每次处理三个字节
        var group = inputArray.slice(i, i + 3); // 获取3字节组
        var paddedGroup = group.slice(0); // 复制数组
        while (paddedGroup.length < 3) {
            // 不足3字节时用0填充
            paddedGroup.push(0);
        }
        // 处理组并将结果拼接到最终字符串
        var encodedGroup = processGroup(paddedGroup[0], paddedGroup[1], paddedGroup[2]);
        encodedStr += encodedGroup.join(""); // 将编码的字符数组拼接为字符串
    }

    // 处理末尾填充(如果有的话)
    var paddingLength = (3 - inputArray.length % 3) % 3;
    if (paddingLength > 0) {
        encodedStr = encodedStr.slice(0, -paddingLength) + Array(paddingLength + 1).join("=");
    }

    return encodedStr; // 返回编码后的字符串
}

// 使用示例:
var encoded = standardBase64Encode("Hello, World!");
console.log(encoded); // 输出:SGVsbG8sIFdvcmxkIQ==

经过验证,它又不是标准的 base64 算法,而是魔改后的 base64 算法。此时,我内心是崩溃的。。

没办法,只能再继续分析日志,在原有的 base64 算法基础上,再进行魔改。经过一番努力,终于找到了规律,并还原。

上述有了 rc4 魔改算法的经验,这个我就不贴出来了,毕竟我们学习插桩技巧的。不难的,多尝试,努力提升自己。

继续往下翻日志,发现魔改 base64 加密后的结果,又进行了 sm3 加密。日志如下:

a_bogus、msToken、fp、verifyFp

继续往下翻日志,很难找到规律了。此时,我内心又是崩溃的。

当时,我想着既然正向找不到规律,那我就逆向分析,看看能不能找到规律。于是,我就找到生成 a_bogus 的地方,进行逆向分析。

全局搜索日志中的 a_bogus,找到生成 a_bogus 的地方,如下:

a_bogus、msToken、fp、verifyFp

从下往上翻日志,发现 a_bogus 是从长度为 134 位数组得到的,如下:

a_bogus、msToken、fp、verifyFp

a_bogus、msToken、fp、verifyFp

当然了,不同浏览器,环境也不一样,加密参数也不一样,所以,你的浏览器未必是 134 位数组。这是为啥有的人得到结果是:184188192 长度 a_bogus 的原因。

经过日志分析得到,a_bogus 是从长度为 134 位数组通过魔改的 rc4 算法得到的。

那问题又来了。这个长度为 134 位数组又是从哪里来的呢?

134位数组分析,和相关js文件,会分享到知识星球当中,需要的小伙伴自取,仅供学习交流。

至此,a_bogus 的生成逻辑就分析完了。

参数验证

写个小例子,验证下生成的参数是否正确,如下:

a_bogus、msToken、fp、verifyFp

搞定!!

如果还有什么疑问,欢迎留言讨论!

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值