JS逆向寻找生成bid变量的加密算法,一顿操作猛如虎,结果发现原来是混淆代码

分享一下最近我JS逆向的心得。

我最近使用Python爬取某个网站某个链接,用requests的get发送请求得到的status_code不是200,请求失败。在浏览器的开发者工具里查看该链接的详细信息,看到cookie必须加入qgqp_b_id参数以及其它由随机数产生的参数才能获取数据,如下图。

这个参数是一个32位字符串,通过开发者工具分析网页源代码,了解到这个qgqp_b_id变量不是服务器返回给客户端的,而是前端JS的某段代码运算出来的。经过一段时间设断点、单步跟踪、反复调试(在此省略一万字),发现这个变量源自bid变量,bid又是哪里算出来的?

起初以为32位字符串是MD5的算法,但随着深入逆向分析JS代码,在jQuery.js、usercollect.min.js、require.min.js、main.js等多个JS代码里跳来跳去,终于在usercollect.min.js找到了一段x64hash128的加密算法:

 然后在这个JS代码里再查找x64hash128可以找到是把变量 r 进行加密运算:

再往上找变量r,可见r = e, e是一大堆JS函数,在获取当前操作环境的参数,如user-agent、系统语言、显示色深、屏幕像素比、屏幕分辨率等,这些信息都以~~~符号串联起来。

得出的字符串很长很长,其中占比最多的是canvasKey(e)和webglKey(e)这两个函数生成的字符串。查找canvasKey(e),发现里面有个笑脸符号,嘿嘿,非常有意思:

这段canvasKey(e)函数生成的是base64加密的字符串。我把它进行base64解密,得出的是一个PNG图片。

再看另一个函数webglKey(e),也是生成一个图片然后转base64密文。图片如下:

哈哈,为了层层加密,居然还要代码画个图然后转成密文再来进一步加密,作为JS小白的我真是活久见了。

这还没完,上面说了 r = e,e的最后一个元素是通过fontskey (e)函数获取字体名称列表,函数里的u是设定一长串的字体名称列表,函数会从当前系统查找已安装的字体名称,然后从u字体名称列表里筛选出系统存在的字体名称,组成一串字体名称。

bid变量正是由系统环境信息、加上两个实时生成的图片转base64密文、再加上字体名称列表,撮合起来,然后进行x64hash128()加密运算。

x64hash128()加密运算还调用x64Add、x64Multiply、x64Xor、x64Rotl等子函数综合计算……问你怕未!

看罢,令我倒吸一口凉气。如果要把它们这么多代码转换成Python代码,相信已劝退了99%的人。反正我就没有对它转换。

在后来的调试中,我尝试把qgqp_b_id参数随便改了个字符,然后加入headers和cookie里,再试试requests.get()能否请求成功。嘿!返回的status_code是200!可以连接成功!

我又尝试把qgqp_b_id的值乱改一通,比如:qgqp_b_id = '9527-3547-709394'(广东人你懂的)。也连接成功!后续的获取信息也就水到渠成了。

这说明什么?

1、逆向JS分析了一大轮,一顿操作猛如虎,发现这些加密代码原来是混淆代码,就是用来吓唬小白的。所谓加密算出来的qgqp_b_id变量,带着它发送请求到服务器,可服务器那边根本不校验,只是要求后续的每次请求都要带着这个变量就行,所以随便编造一个“9527”的字符串也能蒙混过关!真系混吉!

2、狗屎运。这次真的纯粹碰上了好运气,一连串的加密代码最终证明是纸老虎,下次JS逆向别的网站可能就没那么好彩了,必须认真分析、调试和判断,不然分分钟给你返回400、403、500等错误代码折磨死人。

3、为什么某些网站加载需要那么长时间。当然现在网站内容比以前丰富很多,高像素的图片和视频消耗了不少时间,除此之外还要运算大量的JS代码。即使是高配置电脑,加载含有大量JS代码的网页所花的时间,跟十几年前低配电脑加载少量JS代码的时间相比,好像差不了多少。现在的网站为了提高安全性,JS代码设计得越来越复杂,相当一部分的加载时间都耗在了运行JS代码上。

4、本文提及的这波信息采集叫做fingerprint2用户指纹,可以参考下面文章了解一下:

fingerprint2生成的用户指纹重复踩坑 - 掘金

### 分布式事务管理的概念 分布式事务是指涉及多个独立资源(如数据库、消息队列等)的操作,这些操作需要作为一个整体来执行,即要么全部成功完成,要么全部失败并回退。为了确保数据的一致性和完整性,在分布式环境中通常采用特定的协议和技术方案。 #### XA 协议 XA 是一种两阶段提交协议,用于支持分布式事务中的多资源协调。它允许多个资源管理器参与到同一个全局事务中,并确保它们能够同步提交或回滚事务[^1]。具体来说: - **准备阶段**:所有参与者被询问是否可以提交其本地事务。 - **提交阶段**:如果所有参与者都同意,则由协调者发起正式提交;如果有任何一个参与者拒绝,则整个事务会被回滚。 这种模式适用于强一致性的场景需求,但由于其实现复杂度较高以及性能开销较大,因此可能不适合所有的应用场景。 #### Spring 的分布式事务支持 在基于 Java 的微服务开发框架中,Spring 提供了一些便捷的方式来开启和配置分布式事务功能。例如,在项目初始化时可以通过简单的注解形式启用此特性[@EnableDistributedTransaction][^2]。这使得开发者能够在不深入底层细节的情况下快速集成基本的功能模块。 然而需要注意的是,默认情况下这样的设置仅限于单体应用程序内的多数据源环境下的事务管理,并未真正解决跨不同进程间通信所引发的问题[^3]。 #### 跨服务边界的扩展方法 当业务逻辑不仅局限于单一节点内部还需要与其他远程组件交互时,就必须考虑更加复杂的策略了。此时除了继续沿用传统的集中式控制外还可以探索其他替代选项比如借助第三方中间件或者自定义协议等方式达成目标。 ##### 使用消息驱动架构配合补偿机制(TCC) TCC (Try, Confirm, Cancel) 方法提供了一种灵活的选择路径去构建松耦合程度较高的大型生态系统。它的核心理念在于预先尝试执行某些预定动作(try),随后依据实际情况决定确认(confirm)还是撤销(cancel)[^4]。这种方法虽然牺牲了一定程度上的即时准确性但却换取到了更高的灵活性与可维护性尤其适合那些对于实时反馈要求相对较低的任务流程。 以下是利用 TCC 思想设计的一个简单伪代码例子展示如何处理订单创建过程中的库存扣减环节: ```java @Service public class OrderService { private final InventoryClient inventoryClient; public void createOrder(String productId, int quantity){ // Try phase: Reserve stock without actually reducing it yet. boolean reserved = this.inventoryClient.reserveStock(productId,quantity); if(!reserved){ throw new RuntimeException("Failed to reserve stock."); } try{ saveNewOrderToDatabase(); // Confirm phase: Actually reduce the stock after successful order creation. this.inventoryClient.confirmReservation(productId,quantity); }catch(Exception e){ // Cancel phase: Release reservation when something goes wrong during confirmation. this.inventoryClient.cancelReservation(productId,quantity); throw e; } } } ``` 上述片段展示了在一个典型电商平台上新增一笔销售记录的同时调整对应商品可用数量的过程。其中涉及到两次网络请求分别代表不同的状态转换步骤——预留存货空间 vs 执行实际变更操作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值