吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 384|回复: 1
上一主题 下一主题
收起左侧

[Web逆向] 某程token纯算-vmp插桩日志分析

[复制链接]
跳转到指定楼层
楼主
gfy1129 发表于 2026-5-8 13:08 回帖奖励
本帖最后由 gfy1129 于 2026-5-8 13:12 编辑

本文仅供学习交流,因使用本文内容而产生的任何风险及后果,作者不承担任何责任



这篇blog主要分为两种解法:第一种:“古法逆向”分析日志还原算法。第二种:使用ai解决jsvmp还原算法
·对于想要交流学习的大佬,可以看一看第一部分,欢迎斧正
·对于想要使用ai梭哈的大佬,可以只直接跳到第二部分(第二部分暂定下一篇帖子单独发
·这个phantom-token参数相对于 某Q音乐sign纯算-vmp插桩日志分析 中的sign,有个魔改的md5,分析起来难度可能高一丢丢

网址:aHR0cHM6Ly9ob3RlbHMuY3RyaXAuY29tL2hvdGVscy9saXN0P2ZsZXhUeXBlPTEmZml4ZWREYXRlPTAmY2l0eUlkPTImcHJvdmluY2VJZD0yJmNvdW50cnlJZD0xJmNpdHlOYW1lPSVFNCVCOCU4QSVFNiVCNSVCNyZkZXN0TmFtZT0lRTQlQjglOEElRTYlQjUlQjcmc2VhcmNoVHlwZT1DVCZjaGVja2luPTIwMjYtMDUtMDYmY2hlY2tvdXQ9MjAyNi0wNS0wNyZjcm49MSZsaXN0RmlsdGVycz0yOX4xKjI5KjF+MSoyJTJDODB+Mio4MCoyJTJDMTd+MSoxNyoxJmN1cnI9Q05ZJmxvY2FsZT16aC1DTiZvbGQ9MSZ2Ml9tb2Q9MzImdjJfdmVyc2lvbj1F





一、分析日志前的准备(定位加密位置,确定jsvmp,插桩,保存日志)


①请求头中的token目标参数


②全局搜索“phantom-token” 确定加密位置


③经过控制台调试,初步确定window.signature()为加密函数,跟进这个函数


④把这个vmp扣到本地,先本地补环境/直接在浏览器代码段运行这个vmp,然后输出这个加密函数,判断加密函数是否存在这个jsvmp中 (我这里采用的是本地补环境,可以直接在目标网站的代码段运行这个脚本)


⑤对jsvmp进行插桩,可以采用手动插桩/ast自动插桩/ai-skill/ai-prompt 自动插桩,插桩之后的脚本我放在附件。
tips1: 插桩插的越好,日志越详细,冗余的日志越少,分析起来可能会简单点


⑥ 运行vmp插桩之后的脚本,日志输出完毕之后运行控制台保存日志的脚本我放在附件,这个脚本在 某Q音乐sign纯算-vmp插桩日志分析 提及过,可以参考上个帖子。


此时日志已经保存在本地,可以接下来的分析




二、分析日志
tip:可以用klogg这个轻量化的工具分析日志。

① 加密值由三部分构成, 1006(固定) +  common(固定) + 加密长串(动态) 。



② 全局搜索这个 “PfNwnAIL0e9gy50jPBvhTJTnyfTW6qRqaW4Uyd3wobwgzjGcx90y...” 长串,是经过 BIN_69109 二元运算的返回结果,找到这个二元运算


往上找日志,长串的拼接,也是按照这种形式。


③日志1,经过这个匿名函数1得到两个字符
匿名函数1的入参有两个参数,数字52(取自大数组,这个大数组由fingerprint  指纹长串生成,后续会讲到以及长串SO781baBZHkzNgLF5mAtUG6qMXPQo0pc349fnTlhDds,此处先解析匿名函数1的实现逻辑
举例1:
[Python] 纯文本查看 复制代码
[CALL_69082] 调用函数 ==> [anonymous]
[CALL_69082] 参数 ==> [52, SO781baBZHkzNgLF5mAtUG6qMXPQo0pc349fnTlhDds]
[CALL_69082] this ==> [Window]
[CALL_69082] 结果 ==> OH
[BIN_69083] 操作符 ==> +
[BIN_69083] 左值 ==> PfNwnAIL0e9gy50jPBvhTJTnyfTW6qRqaW4Uyd3wobwgzjGcx90yaXv3mwb7WTlY0ork7ynURAFYHcr4bjmTwaBWlhESXjdcvfMvH8EoHEoOj4bezEbYXnK3qElpRAbRNOybNyq4y4qvctyhAYTFiUHjLcR9bv0DyLUWQ3YHswagvaYHAyAcjfOw1NKXPjGpjULEg1ysEpfi06YQEagYtPyhE5GY4syf1KnmiTDjFDjatWB8yZlIH7wz3jOkwq4eX3EAGIS6wkhin1RoEDzY5zy5PKTGiBOjsnjafW3cybEToY7GytMWUQeGPyPNjaEmqi7HypgvSnW4mjg8eQlwXHIZ1EBzjTEMmiZ5JPHRGEDNYThwqXv8NeH3epAjg3yh9JShETZj85jlESsihTJ5GWLEsaY4BwaNwkneH3j0HITPx1HymEtZi9XJdaYQEhGYkBwlaE71wmQiaOxfqe8OYslwLNj7AxqayZEftimlJcAYNEnbYgdwlqwa6ihgvzfYTQjcHwHcImNxoZyFE1qiqkJ9aRPENFY8SwoTwsgeFpjkFIgkW0hEthRTFj6EqOiOlJNESnYh7JFDwU7EmE15Y6qJTGi6sjLEZBY1OJa8jp6YcEG1YhLJMgwA4w9E38YHLJXZi8LJbEOhYgMJhTiH4ylEzkYHkwdaycMjoPymSjqmW5PEh0R3njOE37iLQJsEabYnFJhdw36EpEMQYmZJ47iNfj6EdAYMZJDljXhYNEg9YXtJNTwA1wBEQPYMLJfOiDaJFEtaYH3JoAigcy6E6nYU6wO0yNQj75EoOwbtwM5Koqw54IBXEszjpEaLi86JlnEmUwcEhGYnZwmQwdhKl5jNBwQHrLdeHTYfE0SikFJtoYqkW0DYlzwbFehlW3AJnMecXWo6J9EFbYODwg6wHDKnNj7UwdSrBpiMcvS0jGE9niU4JBUYFpW3XYNLwhOe59WdfJhzeD3eUcwbsy4YoSvBMKkXWbpRqby6hyBayskjzfw8MwsNWfPyZMEbfw03YXUwZBIptY6SJfaJ3MjSmep1JMpWmpypgjOcYSfyfMRXQvb8w0MR0YOgYpbEf3KlAvZ0E9QWD0JakE0kJOmvFXvLlemqwdhR86jnmjMteN1E8NJhLYdDjtse47vkSwlOY9owLUeOfvh8vQaEs6EGXya3JdbE3OwN6vmgEnQWkAJ6j1bKknvkYGqeoOEcDw64eHHx6XK14I5lJl1iSmxkvlDi1SvUYgLJnGi75YcDiZQYQAwUQYQDWQYLEgARzZKBQvHTeLzYkXifgY3LyM5K7SR7YtZI1EGOi3ae6LE3HjpFWMMxUfIhky7YggKzQWcoxLQrBlKnFeMDEOTWcTx9DKo4IoYfwH6EB6x9BRPDvmFYGUWg3eM1RNdWh5jMNW1MYQkjckicY6zJq1yb7RfqR3TvAXYnLWH4efoRd4WXHi71Y09xh1JMsrMYUnj98wsBr3qEBmJdFvSdy5FEadwcYbhiXEFjMpYt4igBwF4RTmE0dWDXJUDIPfxPYlpYcARDMjZMJO5yT7W8v3Y1dRZ5wZZxUTjcLwBOvQ3jqDJBSKkfydYDfipXINByQ4Yo7RLni7E0YoAjAtefEGAjBgwMAvOnjFavp3wHFK6YX5RnHWHDx3LJHLWszWHgwX6e4GeBYPcrgv7hKFqKtOwhzW8Y9wXOwDcih6RLsWOPw0AJPkWt3RApy07RkARahvc9YHbv45ehPWhYUze41RaFroDEOfim8WgXes0vNGEkmJ3vHax6Lr0YhUE1SrsQwAZRmgvlhYkZWZ4eShRGBWlniTBYfAW
[BIN_69083] 右值 ==> OH
[BIN_69083] 结果 ==> PfNwnAIL0e9gy50jPBvhTJTnyfTW6qRqaW4Uyd3wobwgzjGcx90yaXv3mwb7WTlY0ork7ynURAFYHcr4bjmTwaBWlhESXjdcvfMvH8EoHEoOj4bezEbYXnK3qElpRAbRNOybNyq4y4qvctyhAYTFiUHjLcR9bv0DyLUWQ3YHswagvaYHAyAcjfOw1NKXPjGpjULEg1ysEpfi06YQEagYtPyhE5GY4syf1KnmiTDjFDjatWB8yZlIH7wz3jOkwq4eX3EAGIS6wkhin1RoEDzY5zy5PKTGiBOjsnjafW3cybEToY7GytMWUQeGPyPNjaEmqi7HypgvSnW4mjg8eQlwXHIZ1EBzjTEMmiZ5JPHRGEDNYThwqXv8NeH3epAjg3yh9JShETZj85jlESsihTJ5GWLEsaY4BwaNwkneH3j0HITPx1HymEtZi9XJdaYQEhGYkBwlaE71wmQiaOxfqe8OYslwLNj7AxqayZEftimlJcAYNEnbYgdwlqwa6ihgvzfYTQjcHwHcImNxoZyFE1qiqkJ9aRPENFY8SwoTwsgeFpjkFIgkW0hEthRTFj6EqOiOlJNESnYh7JFDwU7EmE15Y6qJTGi6sjLEZBY1OJa8jp6YcEG1YhLJMgwA4w9E38YHLJXZi8LJbEOhYgMJhTiH4ylEzkYHkwdaycMjoPymSjqmW5PEh0R3njOE37iLQJsEabYnFJhdw36EpEMQYmZJ47iNfj6EdAYMZJDljXhYNEg9YXtJNTwA1wBEQPYMLJfOiDaJFEtaYH3JoAigcy6E6nYU6wO0yNQj75EoOwbtwM5Koqw54IBXEszjpEaLi86JlnEmUwcEhGYnZwmQwdhKl5jNBwQHrLdeHTYfE0SikFJtoYqkW0DYlzwbFehlW3AJnMecXWo6J9EFbYODwg6wHDKnNj7UwdSrBpiMcvS0jGE9niU4JBUYFpW3XYNLwhOe59WdfJhzeD3eUcwbsy4YoSvBMKkXWbpRqby6hyBayskjzfw8MwsNWfPyZMEbfw03YXUwZBIptY6SJfaJ3MjSmep1JMpWmpypgjOcYSfyfMRXQvb8w0MR0YOgYpbEf3KlAvZ0E9QWD0JakE0kJOmvFXvLlemqwdhR86jnmjMteN1E8NJhLYdDjtse47vkSwlOY9owLUeOfvh8vQaEs6EGXya3JdbE3OwN6vmgEnQWkAJ6j1bKknvkYGqeoOEcDw64eHHx6XK14I5lJl1iSmxkvlDi1SvUYgLJnGi75YcDiZQYQAwUQYQDWQYLEgARzZKBQvHTeLzYkXifgY3LyM5K7SR7YtZI1EGOi3ae6LE3HjpFWMMxUfIhky7YggKzQWcoxLQrBlKnFeMDEOTWcTx9DKo4IoYfwH6EB6x9BRPDvmFYGUWg3eM1RNdWh5jMNW1MYQkjckicY6zJq1yb7RfqR3TvAXYnLWH4efoRd4WXHi71Y09xh1JMsrMYUnj98wsBr3qEBmJdFvSdy5FEadwcYbhiXEFjMpYt4igBwF4RTmE0dWDXJUDIPfxPYlpYcARDMjZMJO5yT7W8v3Y1dRZ5wZZxUTjcLwBOvQ3jqDJBSKkfydYDfipXINByQ4Yo7RLni7E0YoAjAtefEGAjBgwMAvOnjFavp3wHFK6YX5RnHWHDx3LJHLWszWHgwX6e4GeBYPcrgv7hKFqKtOwhzW8Y9wXOwDcih6RLsWOPw0AJPkWt3RApy07RkARahvc9YHbv45ehPWhYUze41RaFroDEOfim8WgXes0vNGEkmJ3vHax6Lr0YhUE1SrsQwAZRmgvlhYkZWZ4eShRGBWlniTBYfAWOH


拆解52 ,大致是一个43进制的转换,43是入参长串“SO781baBZHkzNgLF5mAtUG6qMXPQo0pc349fnTlhDds”的长度 。
52 = 1*43  + 9, 得出的1 和 9 正是对长串的索引取值然后拼接 即为 OH
匿名函数1的伪代码:两个字符 = 长串[num/len(长串)] + 长串[num%len(长串)]

举例2:
[Python] 纯文本查看 复制代码
[CALL_69107] 调用函数 ==> [anonymous]
[CALL_69107] 参数 ==> [SO781baBZHkzNgLF5mAtUG6qMXPQo0pc349fnTlhDds, Pctrip.comSO781baBZHkzNgLF5mAtUG6qMXPQo0pc3]
[CALL_69107] this ==> [Window]
[CALL_69107] 结果 ==> 60amDPpbfLOAMh5qGXSzcZQkdBHNno3TsUl718Ftg49
[CALL_69108] 调用函数 ==> [anonymous]
[CALL_69108] 参数 ==> [103, 60amDPpbfLOAMh5qGXSzcZQkdBHNno3TsUl718Ftg49]
[CALL_69108] this ==> [Window]
[CALL_69108] 结果 ==> aX
[BIN_69109] 操作符 ==> +
[BIN_69109] 左值 ==> PfNwnAIL0e9gy50jPBvhTJTnyfTW6qRqaW4Uyd3wobwgzjGcx90yaXv3mwb7WTlY0ork7ynURAFYHcr4bjmTwaBWlhESXjdcvfMvH8EoHEoOj4bezEbYXnK3qElpRAbRNOybNyq4y4qvctyhAYTFiUHjLcR9bv0DyLUWQ3YHswagvaYHAyAcjfOw1NKXPjGpjULEg1ysEpfi06YQEagYtPyhE5GY4syf1KnmiTDjFDjatWB8yZlIH7wz3jOkwq4eX3EAGIS6wkhin1RoEDzY5zy5PKTGiBOjsnjafW3cybEToY7GytMWUQeGPyPNjaEmqi7HypgvSnW4mjg8eQlwXHIZ1EBzjTEMmiZ5JPHRGEDNYThwqXv8NeH3epAjg3yh9JShETZj85jlESsihTJ5GWLEsaY4BwaNwkneH3j0HITPx1HymEtZi9XJdaYQEhGYkBwlaE71wmQiaOxfqe8OYslwLNj7AxqayZEftimlJcAYNEnbYgdwlqwa6ihgvzfYTQjcHwHcImNxoZyFE1qiqkJ9aRPENFY8SwoTwsgeFpjkFIgkW0hEthRTFj6EqOiOlJNESnYh7JFDwU7EmE15Y6qJTGi6sjLEZBY1OJa8jp6YcEG1YhLJMgwA4w9E38YHLJXZi8LJbEOhYgMJhTiH4ylEzkYHkwdaycMjoPymSjqmW5PEh0R3njOE37iLQJsEabYnFJhdw36EpEMQYmZJ47iNfj6EdAYMZJDljXhYNEg9YXtJNTwA1wBEQPYMLJfOiDaJFEtaYH3JoAigcy6E6nYU6wO0yNQj75EoOwbtwM5Koqw54IBXEszjpEaLi86JlnEmUwcEhGYnZwmQwdhKl5jNBwQHrLdeHTYfE0SikFJtoYqkW0DYlzwbFehlW3AJnMecXWo6J9EFbYODwg6wHDKnNj7UwdSrBpiMcvS0jGE9niU4JBUYFpW3XYNLwhOe59WdfJhzeD3eUcwbsy4YoSvBMKkXWbpRqby6hyBayskjzfw8MwsNWfPyZMEbfw03YXUwZBIptY6SJfaJ3MjSmep1JMpWmpypgjOcYSfyfMRXQvb8w0MR0YOgYpbEf3KlAvZ0E9QWD0JakE0kJOmvFXvLlemqwdhR86jnmjMteN1E8NJhLYdDjtse47vkSwlOY9owLUeOfvh8vQaEs6EGXya3JdbE3OwN6vmgEnQWkAJ6j1bKknvkYGqeoOEcDw64eHHx6XK14I5lJl1iSmxkvlDi1SvUYgLJnGi75YcDiZQYQAwUQYQDWQYLEgARzZKBQvHTeLzYkXifgY3LyM5K7SR7YtZI1EGOi3ae6LE3HjpFWMMxUfIhky7YggKzQWcoxLQrBlKnFeMDEOTWcTx9DKo4IoYfwH6EB6x9BRPDvmFYGUWg3eM1RNdWh5jMNW1MYQkjckicY6zJq1yb7RfqR3TvAXYnLWH4efoRd4WXHi71Y09xh1JMsrMYUnj98wsBr3qEBmJdFvSdy5FEadwcYbhiXEFjMpYt4igBwF4RTmE0dWDXJUDIPfxPYlpYcARDMjZMJO5yT7W8v3Y1dRZ5wZZxUTjcLwBOvQ3jqDJBSKkfydYDfipXINByQ4Yo7RLni7E0YoAjAtefEGAjBgwMAvOnjFavp3wHFK6YX5RnHWHDx3LJHLWszWHgwX6e4GeBYPcrgv7hKFqKtOwhzW8Y9wXOwDcih6RLsWOPw0AJPkWt3RApy07RkARahvc9YHbv45ehPWhYUze41RaFroDEOfim8WgXes0vNGEkmJ3vHax6Lr0YhUE1SrsQwAZRmgvlhYkZWZ4eShRGBWlniTBYfAWOHE
[BIN_69109] 右值 ==> aX
[BIN_69109] 结果 ==> PfNwnAIL0e9gy50jPBvhTJTnyfTW6qRqaW4Uyd3wobwgzjGcx90yaXv3mwb7WTlY0ork7ynURAFYHcr4bjmTwaBWlhESXjdcvfMvH8EoHEoOj4bezEbYXnK3qElpRAbRNOybNyq4y4qvctyhAYTFiUHjLcR9bv0DyLUWQ3YHswagvaYHAyAcjfOw1NKXPjGpjULEg1ysEpfi06YQEagYtPyhE5GY4syf1KnmiTDjFDjatWB8yZlIH7wz3jOkwq4eX3EAGIS6wkhin1RoEDzY5zy5PKTGiBOjsnjafW3cybEToY7GytMWUQeGPyPNjaEmqi7HypgvSnW4mjg8eQlwXHIZ1EBzjTEMmiZ5JPHRGEDNYThwqXv8NeH3epAjg3yh9JShETZj85jlESsihTJ5GWLEsaY4BwaNwkneH3j0HITPx1HymEtZi9XJdaYQEhGYkBwlaE71wmQiaOxfqe8OYslwLNj7AxqayZEftimlJcAYNEnbYgdwlqwa6ihgvzfYTQjcHwHcImNxoZyFE1qiqkJ9aRPENFY8SwoTwsgeFpjkFIgkW0hEthRTFj6EqOiOlJNESnYh7JFDwU7EmE15Y6qJTGi6sjLEZBY1OJa8jp6YcEG1YhLJMgwA4w9E38YHLJXZi8LJbEOhYgMJhTiH4ylEzkYHkwdaycMjoPymSjqmW5PEh0R3njOE37iLQJsEabYnFJhdw36EpEMQYmZJ47iNfj6EdAYMZJDljXhYNEg9YXtJNTwA1wBEQPYMLJfOiDaJFEtaYH3JoAigcy6E6nYU6wO0yNQj75EoOwbtwM5Koqw54IBXEszjpEaLi86JlnEmUwcEhGYnZwmQwdhKl5jNBwQHrLdeHTYfE0SikFJtoYqkW0DYlzwbFehlW3AJnMecXWo6J9EFbYODwg6wHDKnNj7UwdSrBpiMcvS0jGE9niU4JBUYFpW3XYNLwhOe59WdfJhzeD3eUcwbsy4YoSvBMKkXWbpRqby6hyBayskjzfw8MwsNWfPyZMEbfw03YXUwZBIptY6SJfaJ3MjSmep1JMpWmpypgjOcYSfyfMRXQvb8w0MR0YOgYpbEf3KlAvZ0E9QWD0JakE0kJOmvFXvLlemqwdhR86jnmjMteN1E8NJhLYdDjtse47vkSwlOY9owLUeOfvh8vQaEs6EGXya3JdbE3OwN6vmgEnQWkAJ6j1bKknvkYGqeoOEcDw64eHHx6XK14I5lJl1iSmxkvlDi1SvUYgLJnGi75YcDiZQYQAwUQYQDWQYLEgARzZKBQvHTeLzYkXifgY3LyM5K7SR7YtZI1EGOi3ae6LE3HjpFWMMxUfIhky7YggKzQWcoxLQrBlKnFeMDEOTWcTx9DKo4IoYfwH6EB6x9BRPDvmFYGUWg3eM1RNdWh5jMNW1MYQkjckicY6zJq1yb7RfqR3TvAXYnLWH4efoRd4WXHi71Y09xh1JMsrMYUnj98wsBr3qEBmJdFvSdy5FEadwcYbhiXEFjMpYt4igBwF4RTmE0dWDXJUDIPfxPYlpYcARDMjZMJO5yT7W8v3Y1dRZ5wZZxUTjcLwBOvQ3jqDJBSKkfydYDfipXINByQ4Yo7RLni7E0YoAjAtefEGAjBgwMAvOnjFavp3wHFK6YX5RnHWHDx3LJHLWszWHgwX6e4GeBYPcrgv7hKFqKtOwhzW8Y9wXOwDcih6RLsWOPw0AJPkWt3RApy07RkARahvc9YHbv45ehPWhYUze41RaFroDEOfim8WgXes0vNGEkmJ3vHax6Lr0YhUE1SrsQwAZRmgvlhYkZWZ4eShRGBWlniTBYfAWOHEaX


拆解入参的数字103 ,103 = 2*43 + 17,对应长串的索引 a X


长串 SO781baBZHkzNgLF5mAtUG6qMXPQo0pc349fnTlhDds是由入参两个长串(这两个长串后续分析)经匿名函数2生成,此处分析长,分析匿名函数2的实现逻辑
[Python] 纯文本查看 复制代码
[CALL_69081] 调用函数 ==> [anonymous]
[CALL_69081] 参数 ==> [0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSPdsUqH9M6Lt, Pctrip.com0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSP]
[CALL_69081] this ==> [Window]
[CALL_69081] 结果 ==> SO781baBZHkzNgLF5mAtUG6qMXPQo0pc349fnTlhDds


这个匿名函数的具体实现:
[Python] 纯文本查看 复制代码
def shuffle_alphabet(alphabet: str, salt: str) -> str:
    """
    匿名函数 shuffle — 从 VMP 运行时闭包提取
    输入: 43字符字母表 + salt (lottery + "ctrip.com" + alphabet[:33])
    输出: 洗牌后的43字符字母表
    """
    if not salt:
        return alphabet
    chars = list(alphabet)
    v = 0
    acc = 0
    for i in range(len(chars) - 1, 0, -1):
        v %= len(salt)
        integer = ord(salt[v])
        acc += integer
        j = (integer + v + acc) % i
        chars[j], chars[i] = chars[i], chars[j]
        v += 1
    return "".join(chars)


if __name__ == "__main__":
    # 验证 CALL_69081 的输入输出
    alphabet = "0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSPdsUqH9M6Lt"
    salt = "Pctrip.com0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSP"

    result = shuffle_alphabet(alphabet, salt)
    print(f"result:   {result}")



⑤ 继续往上分析,这个 0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSPdsUqH9M6Lt 和 salt:Pctrip.com0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSP是怎么来的

[Python] 纯文本查看 复制代码
[CALL_69080] 调用函数 ==> bound call
[CALL_69080] 参数 ==> [Pctrip.com0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSPdsUqH9M6Lt, 0, 43]
[CALL_69080] this ==> [Window]
[CALL_69080] 结果 ==> Pctrip.com0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSP


salt是由 P + ctrip.com + 0FfTnpmA73BOcD5lkozh4ZaXQNb18gGSPdsUqH9M6Lt ,然后截取前43位长度得到的。
salt= lottery + "ctrip.com" + alphabet[:33] (共43字符)


1.lottery 的由来,其中涉及到 fingerprint  指纹长串,这个稍后会分析其生成。
[Python] 纯文本查看 复制代码
    # initial_alphabet 即 INITIAL_ALPHABET为 固定字符 S9mHXtfaMsZ6DNkgTnL5F0dzpb1lPAocQ8BGh47UqO3
    # Step 1: 计算种子
    seed = 0
    for i, ch in enumerate(fingerprint):
        seed += ord(ch) % (i + 100)

    # Step 2: 选取lottery
    lottery = initial_alphabet[seed % BASE]


2."ctrip.com"固定字符
3. alphabet 是由第一个固定长串S9mHXtfaMsZ6DNkgTnL5F0dzpb1lPAocQ8BGh47UqO3 链式生成后续的所有alphabet

⑥ 解决④和⑤中涉及的整数大数组fingerprint  指纹长串
1. 提前说结论 fingerprint --生成-->整数大数组
2.先从日志中找大数组的痕迹
数组的最后两位,即 52 103,即上述的两个举例


3. 找到把 52 103 两个整数push进数组的日志位置




调用的参数长串 ~=-7bab70a07ccecbf602e712c860aeff44ev%#J4^1777972381970}TW#F8Thttps%3A%2... 即指纹长串


4.大数组的push过程

是遍历长串每个字符,把每个字符对应的ascii码push进整形数组中


⑦指纹长串是怎么来的
先全局搜索这个指纹长串,对比发现,这个是由一段一段环境指纹拼接而来的。比如屏幕分辨率、时间戳、操作系统、字体...等 具体的参数后续分析


从头分析具体的参数都有什么,此处着重分析 噪声字符、魔改md5。其他参数跟着分析日志可以看出来,不过多赘述。
1. 噪声字符

·这份日志可以看出来,指纹长串是由一段一段的指纹环境拼接而成的。
·7bab70a07ccecbf602e712c860aeff44 这个加密值前后又拼接了三个“奇怪的”字符


分析这三个奇怪字符的生成日志




这三个字符都是由random以及一一系列操作得出的,同理,后续的每个环境前后包裹的奇怪字符都是由random生成的噪声字符。
噪声字符存在的意义:
·防重放。 噪声由 Math.random() 驱动,每次调用都会产生不同的随机序列。即使同一个浏览器、同一个页面、完全相同的请求体,两次调用
  signature() 也会因为噪声不同而导致指纹字符串不同 → 种子不同 → lottery 字符不同 → 整条字母表演化链完全发散 → 最终 token
  不同。服务端无法用"token 是否重复出现"来检测重放攻击
·防字段提取。 22 个字段的噪声和数据直接拼接,没有任何分隔符。以 MD5 字段为例:Xp@cb74974f828eab54011bb68d4ee0d5a8/Pr——Xp@
  是噪声,cb74974...d5a8 是 32 位 MD5 hex,/Pr 又是噪声。噪声字符的 charCode 范围与真实数据完全重叠(字母、数字、符号都在 37-130
  内),攻击者拿到指纹字符串后,无法通过字符特征判断哪里是数据边界、哪里是噪声填充。只有完整还原 VMP
  字节码中每个字段的噪声长度配置,才能精确提取出数据。
·不可见字符陷阱。 噪声公式的产出范围包含了 charCode 128-130——这些是不可见的 Unicode
  控制字符。在日志文本中它们不显示,在复制粘贴时会静默丢失。


[Python] 纯文本查看 复制代码
def generate_noise(length: int = 3) -> str:    """
    生成噪声字符
    公式: charCode = ceil(random() * 94) + 36  (范围 37-130)
    日志证据: ceil(0.2392*94)=23, 23+36=59, fromCharCode(59)=';'
    """
    import random
    noise = ""
    for _ in range(length):
        char_code = math.ceil(random.random() * 94) + 36
        noise += chr(char_code)
    return noise



2.魔改md5,第一段 7bab70a07ccecbf602e712c860aeff44 就是经过魔改md5生成的。具体请看以下日志


跟到CALL_233 看调用,入参是由 时间戳、ua、random-uuid 传入匿名的加密函数


看匿名函数内部的日志,调用和出结果中间的日志即为匿名函数内部的日志:
  [CALL_233] 调用函数 ==> [anonymous]           ← 进入函数
  [CALL_233] 参数 ==> ["1777972381970Mozi..."] ← 入参
  [CALL_233] this ==> [Window]
    │
    ├── [BIN_234] ...  ─┐
    ├── [CALL_235] ...   │
    ├── [UNARY_236] ...  ├── 这些都在 CALL_233 内部
    ├── ...              │   (成千上万行)
    ├── [BIN_99999] ... ─┘
    │
  [RET_xxxxx] 返回值 ==> "7bab70a07ccecbf602..." ← 函数返回
  [CALL_233] 结果 ==> "7bab70a07ccecbf602..."     ← 调用者收到结果


输出的加密值也为 32 hex

  A 寄存器被修改:+8
  标准 MD5 A = 0x67452301 = 1732584193
  日志中  A = 0x67452309 = 1732584201      差: +8
  B 寄存器被修改:-2
  标准 MD5 B = 0xEFCDAB89 = 4023233417
  日志中  B = 0xEFCDAB87 = 4023233415      差: -2
  C/D 寄存器未修改
  标准 MD5 C = 0x98BADCFE = 2562383102     日志: 2562383102  
  标准 MD5 D = 0x10325476 = 271733878      日志: 271733878   
  多了一个 H4 寄存器(非标准)
  926365489  ← 标准 MD5 只有 A/B/C/D 四个寄存器,不存在第5个

本地算法验证:
[Python] 纯文本查看 复制代码
import struct

# 魔改MD5初始向量 (仅IV被修改, 其余与标准MD5完全一致)
MD5_IV = {
    "A": 0x67452309,  # 标准: 0x67452301 (+8)
    "B": 0xEFCDAB87,  # 标准: 0xEFCDAB89 (-2)
    "C": 0x98BADCFE,  # 标准: 未修改
    "D": 0x10325476,  # 标准: 未修改
}

# MD5 T常量 & S移位表 (标准MD5, 未修改)
MD5_T = [
    0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE,
    0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
    0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE,
    0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,
    0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA,
    0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
    0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED,
    0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,
    0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C,
    0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
    0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05,
    0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,
    0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039,
    0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
    0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1,
    0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391,
]
MD5_S = [
    7,12,17,22, 7,12,17,22, 7,12,17,22, 7,12,17,22,
    5, 9,14,20, 5, 9,14,20, 5, 9,14,20, 5, 9,14,20,
    4,11,16,23, 4,11,16,23, 4,11,16,23, 4,11,16,23,
    6,10,15,21, 6,10,15,21, 6,10,15,21, 6,10,15,21,
]

def rotl(x: int, n: int) -> int:
    """32位循环左移"""
    return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF

def md5_modified(text: str) -> bytes:
    """魔改MD5 - 仅修改了初始向量, 其余与标准MD5完全一致"""
    data = text.encode("utf-8")
    msg_len = len(data) * 8

    data += b"\x80"
    while (len(data) * 8) % 512 != 448:
        data += b"\x00"
    data += struct.pack("<Q", msg_len)

    a, b, c, d = MD5_IV["A"], MD5_IV["B"], MD5_IV["C"], MD5_IV["D"]

    for offset in range(0, len(data), 64):
        chunk = data[offset:offset + 64]
        M = list(struct.unpack("<16I", chunk))
        A, B, C, D = a, b, c, d

        for i in range(64):
            if i < 16:
                F = (B & C) | (~B & D)
                g = i
            elif i < 32:
                F = (D & B) | (~D & C)
                g = (5 * i + 1) % 16
            elif i < 48:
                F = B ^ C ^ D
                g = (3 * i + 5) % 16
            else:
                F = C ^ (B | ~D)
                g = (7 * i) % 16

            F = (F + A + MD5_T[i] + M[g]) & 0xFFFFFFFF
            A, D, C, B = D, C, B, (B + rotl(F, MD5_S[i])) & 0xFFFFFFFF

        a = (a + A) & 0xFFFFFFFF
        b = (b + B) & 0xFFFFFFFF
        c = (c + C) & 0xFFFFFFFF
        d = (d + D) & 0xFFFFFFFF

    return struct.pack("<4I", a, b, c, d)


def md5_modified_hex(text: str) -> str:
    return md5_modified(text).hex()


s = '1777972381970Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.361777866074626.2b58iqNdtAdmf405a59f-61e8-452e-9c26-ff447ba6f405'
print(md5_modified_hex(s))





一致,验证成功

3.其他字符以及对应的环境参数



以上这些对应日志分析,能够逐一找到以及一些硬编码,然后用噪声字符包裹即可,不过多赘述。





三、“古法分析”总结

最终的大致流程


至此,以上的所有主要参数已经分析完毕,纯算代码和依赖的环境数据、日志我统一放置附件压缩包中。




all.zip (1.11 MB, 下载次数: 10)



免费评分

参与人数 3吾爱币 +4 热心值 +3 收起 理由
helian147 + 1 + 1 谢谢@Thanks!
李玉风我爱你 + 2 + 1 我很赞同!
zmolli775 + 1 + 1 脑细胞阵亡一大片,感谢分享。佩服大佬...

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
PhoenixOe 发表于 2026-5-8 17:27
66666大佬学习学习
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-9 11:36

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表