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

打印 上一主题 下一主题

主题 969|帖子 969|积分 2907

【作者主页】:小鱼神1024
  【擅长范畴】:JS逆向、小步伐逆向、AST还原、验证码突防、Python开辟、欣赏器插件开辟、React前端开辟、NestJS后端开辟等等
    本文章中所有内容仅供学习交换利用,不消于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切结果均与作者无关!若有侵权,请联系作者立刻删除!
  前言

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

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

逆向分析

fp、verifyFp

从上图可以发现,fp、verifyFp这两个参数的值是一样的。
全局搜索 verifyFp,打上断点,重新请求,发现断住了。如下图:

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

提取函数为:
  1. function get_fp() {
  2.     var e = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("")
  3.       , t = e.length
  4.       , n = Date.now().toString(36)
  5.       , r = [];
  6.     r[8] = r[13] = r[18] = r[23] = "_",
  7.     r[14] = "4";
  8.     for (var o = 0, i = void 0; o < 36; o++)
  9.         r[o] || (i = 0 | Math.random() * t,
  10.         r[o] = e[19 == o ? 3 & i | 8 : i]);
  11.     return "verify_" + n + "_" + r.join("")
  12. }
复制代码
msToken

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

经过多次对比发现,它的天生方式和 base64 算法天生相似,所以我们可以尝试模拟天生,代码如下:
  1. function get_ms_token(length = 182) {
  2.   const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
  3.   let result = '';
  4.   for (let i = 0; i < length; i++) {
  5.       result += chars.charAt(Math.floor(Math.random() * chars.length));
  6.   }
  7.   return encodeURIComponent(result);
  8. }
复制代码
a_bogus

重头戏来喽。打起精力来,这部分插桩分析可能有点枯燥,耐心看完,信任你会有收获的。
我也把自己当成小白,从头开始分析,一步步来。
由于平台大部分接口临时没有对a_bogus 参数做强校验,但二级品评接口是个例外,如果不加a_bogus 参数,会返回错误。为了验证a_bogus 参数天生的是否正确,我们决定先从二级品评接口入手。
通过跟栈分析,发现a_bogus 参数是在 bdms.js 文件中天生的,点击进去,发现是 vmp 代码,如下图:

有些小伙伴第一时间可能想补环节,但是,尝试过之后的结果会让你失望的,因为你都很难找到天生入口。。
所以,我们换个思路,直接插桩纯算。
说到插桩,你可以尝试一下我之前写的插桩日记框架,可能会让你事半功倍。传送门:终极逆向插桩日记框架,让欣赏器瓦解成为历史!
利用日记框架在bdms.js 文件中插桩,如下图:


由于运算符比较多,我就不一一截图展示了。自己把常见的运算符都插桩下即可。
打开日记文件,开始分析。


首先,我们发现请求参数通过加盐 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],都是固定的。

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

看它的运算规则,很像 rc4 算法,如下:
  1. function rc4(key, str) {
  2.     var s = [], i = 0, j = 0, x, res = '';
  3.     for (var k = 0; k < 256; k++) {
  4.         s[k] = k;
  5.     }
  6.     for (k = 0; k < 256; k++) {
  7.         j = (j + s[k] + key.charCodeAt(k % key.length)) % 256;
  8.         x = s[k];
  9.         s[k] = s[j];
  10.         s[j] = x;
  11.     }
  12.     for (var k = 0; k < str.length; k++) {
  13.         i = (i + 1) % 256;
  14.         j = (j + s[i]) % 256;
  15.         x = s[i];
  16.         s[i] = s[j];
  17.         s[j] = x;
  18.         res += String.fromCharCode(str.charCodeAt(k) ^ s[(s[i] + s[j]) % 256]);
  19.     }
  20.     return res;
  21. }
复制代码
所以,我们可以大胆猜测,它就是 rc4 算法,只不外它的 key 是一个数组,如下:
  1. var key = [0.00390625, 1, 12];
复制代码
经过代码验证,它不是标准的 rc4 算法,而是 rc4 算法的变种,即魔改的 rc4 算法。
遇到类似的运算加密,只管不要根据日记去还原代码,很贫苦的,亲身经历过的。。。,而是只管能找到类似结构的标准算法上去改造,如许对于插桩纯算,很节省大量时间。
所以根据日记,很容易就得到魔改后的 rc4 算法,如下:
  1. function rc4_magic_encrypt(plaintext, key) {
  2.   var s = [];
  3.   for (var i = 0; i < 256; i++) {
  4.     s[i] = 255 - i;
  5.   }
  6.   var j = 0;
  7.   for (var i = 0; i < 256; i++) {
  8.     j = (j * s[i] + j + key.charCodeAt(i % key.length)) % 256;
  9.     var temp = s[i];
  10.     s[i] = s[j];
  11.     s[j] = temp;
  12.   }
  13.   var i = 0;
  14.   var j = 0;
  15.   var cipher = [];
  16.   for (var k = 0; k < plaintext.length; k++) {
  17.     i = (i + 1) % 256;
  18.     j = (j + s[i]) % 256;
  19.     var plaintext_charCodeAt = plaintext.charCodeAt(k);
  20.     var temp = s[i];
  21.     var t = (temp + s[j]) % 256;
  22.     s[i] = s[j];
  23.     s[j] = temp;
  24.     cipher.push(String.fromCharCode(plaintext_charCodeAt ^ s[t]));
  25.   }
  26.   return cipher.join("");
  27. }
复制代码

继续往下翻,可以看到,上述魔改后的 rc4 算法加密后,再进行三个字符一组进行循环,而且还看到:a << 16 、a << 8 等关键运算。此时,我暗自窃喜,因为这种运算,很容易想到 base64 算法。标准如下:
  1. function standardBase64Encode(inputString) {
  2.     // 标准Base64字符集
  3.     var base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  4.     // 处理一个3字节组的函数,将其转换为4个Base64字符
  5.     function processGroup(a, b, c) {
  6.         // 组合三个字节为一个24位整数
  7.         var combined = (a << 16) | (b << 8) | c;
  8.         // 从24位整数中提取4个6位部分
  9.         var part1 = (combined >> 18) & 63; // 提取最高的6位
  10.         var part2 = (combined >> 12) & 63; // 提取次高的6位
  11.         var part3 = (combined >> 6) & 63;  // 提取次低的6位
  12.         var part4 = combined & 63;         // 提取最低的6位
  13.         // 将每部分映射到标准Base64字符集
  14.         return [
  15.             base64Chars.charAt(part1),
  16.             base64Chars.charAt(part2),
  17.             base64Chars.charAt(part3),
  18.             base64Chars.charAt(part4)
  19.         ];
  20.     }
  21.     // 将输入字符串转为字节数组
  22.     var inputArray = [];
  23.     for (var i = 0; i < inputString.length; i++) {
  24.         inputArray.push(inputString.charCodeAt(i));
  25.     }
  26.     var encodedStr = ""; // 存储最终编码结果
  27.     for (var i = 0; i < inputArray.length; i += 3) {
  28.         // 每次处理三个字节
  29.         var group = inputArray.slice(i, i + 3); // 获取3字节组
  30.         var paddedGroup = group.slice(0); // 复制数组
  31.         while (paddedGroup.length < 3) {
  32.             // 不足3字节时用0填充
  33.             paddedGroup.push(0);
  34.         }
  35.         // 处理组并将结果拼接到最终字符串
  36.         var encodedGroup = processGroup(paddedGroup[0], paddedGroup[1], paddedGroup[2]);
  37.         encodedStr += encodedGroup.join(""); // 将编码的字符数组拼接为字符串
  38.     }
  39.     // 处理末尾填充(如果有的话)
  40.     var paddingLength = (3 - inputArray.length % 3) % 3;
  41.     if (paddingLength > 0) {
  42.         encodedStr = encodedStr.slice(0, -paddingLength) + Array(paddingLength + 1).join("=");
  43.     }
  44.     return encodedStr; // 返回编码后的字符串
  45. }
  46. // 使用示例:
  47. var encoded = standardBase64Encode("Hello, World!");
  48. console.log(encoded); // 输出:SGVsbG8sIFdvcmxkIQ==
复制代码
经过验证,它又不是标准的 base64 算法,而是魔改后的 base64 算法。此时,我内心是瓦解的。。
没办法,只能再继续分析日记,在原有的 base64 算法基础上,再进行魔改。经过一番努力,终于找到了规律,并还原。
上述有了 rc4 魔改算法的履历,这个我就不贴出来了,毕竟我们学习插桩本领的。不难的,多尝试,努力提拔自己。
继续往下翻日记,发现魔改 base64 加密后的结果,又进行了 sm3 加密。日记如下:

继续往下翻日记,很难找到规律了。此时,我内心又是瓦解的。
当时,我想着既然正向找不到规律,那我就逆向分析,看看能不能找到规律。于是,我就找到天生 a_bogus 的地方,进行逆向分析。
全局搜索日记中的 a_bogus,找到天生 a_bogus 的地方,如下:

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


当然了,差别欣赏器,环境也不一样,加密参数也不一样,所以,你的欣赏器未必是 134 位数组。这是为啥有的人得到结果是:184、188、192 长度 a_bogus 的缘故起因。
经过日记分析得到,a_bogus 是从长度为 134 位数组通过魔改的 rc4 算法得到的。
那问题又来了。这个长度为 134 位数组又是从哪里来的呢?
134位数组分析,和相干js文件,会分享到知识星球当中,需要的小伙伴自取,仅供学习交换。
至此,a_bogus 的天生逻辑就分析完了。
参数验证

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

搞定!!
如果另有什么疑问,欢迎留言讨论!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

飞不高

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表