10赞

回答

收藏

Python 爬虫进阶必备 | Js 逆向之补环境到底是在补什么?

信息分享 信息分享 4926 人阅读 | 0 人回复 | 2021-09-14

序言
之前我就发过一篇文章,提了一嘴关于我理解的爬虫的本质

面试官问我会不会APP抓包,我..


虽然当时的主题写的是 App 爬虫,不过并不妨碍的我们理解爬虫。

今天写的 Js 逆向之补环境,就可以理解是在 Js 环境下精进我们的 " 骗术 "
正文
大家在看文章之前应该都清楚,Node 环境和浏览器环境是完全不同的,平台有很多的检测点可以发现我们是在浏览器运行 Js 还是在 Node 环境下运行 Js

补环境做的就是尽可能根据网页上的 Js 完善本地的 Node 环境,让 Js 运行在 Node 中像浏览器一样不报错就可以了。
window
最基本的补环境是公众号早期的文章,例如下面的几篇文章

实战案例浅析JS加密 - DES与Base64


实战案例浅析JS加密 - RSA与XXTEA

这些个文章中大家经常遇到的是
  1. 'window' is not defined
复制代码
那像这样的报错提示应该如何处理?

Node 环境下一般如下定义
  1. window = global;
复制代码
如果只是单单缺少了
  1. window
复制代码
这一个变量的定义,像上面这样报错自然就消失了。
document
除了
  1. window
复制代码
之外,我们经常还遇到类似下面这些文章中的情况

JS逆向 | 助力新手 , 两个JS逆向喂饭教程


JS逆向 | 助力新手 , 再来一套喂饭教程!


Python 爬虫进阶必备 | 某采购网站 cookie 加密分析(仿加速乐)


Python 爬虫进阶必备 | 某常见 cookie 加密算法逻辑分析 (加速乐 - jsl)

这些网站我们一般称之为
  1. 加速乐
复制代码
,因为标志性的 cookie 参数是以
  1. jsl
复制代码
开头,而这种类型的加密一般操纵的是
  1. document.cookie
复制代码
这个参数

那像这样的
  1. document
复制代码
应该怎么补?

在没有检测只是为了能让 js 运行不报错的情况下,我是这样写的
  1. var document = {
  2. cookie:"xxxxxx"
  3. }
复制代码
或者像下面这样写

那么这样写就一定保险吗?

不一定。开头就已经提及,要根据网页上的 Js 完善本地的 Node 环境,所以只要网页上的 Js 不检测我们这么写也没毛病

那么检测的 Js 长什么样?
Object.getOwnPropertyDescriptor
这里可以参考之前写的关于某乎的分析文章

Python 爬虫进阶必备 | 某著名人均百万问答社区 header 参数加密逻辑分析

这里
  1. Header
复制代码
中的
  1. x-zse-96
复制代码
的加密逻辑之前看过文章的,通过插桩调试应该可以看到下面这样的代码

这个东西是个啥?

官方定义是这样的

  1. Object.getOwnPropertyDescriptor()
复制代码
方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
通过描述有点晦涩,你可以这样理解,如果是自己构造的对象,例如
  1. var navigator = {
  2. platform:"win32"
  3. }
复制代码
就没办法通过这样的检测

这里是检测对象的属性是不是自己赋值给他的

这里打印下刚刚的例子看看有啥不一样的地方

先是我们自己写的例子
  1. var navigator1 = {
  2. platform:"win32"
  3. }
复制代码

再看看浏览器里面是啥样的

所以对于这样的代码检测,我们应该如何构造呢?

这里站在前人的肩膀上,写一下
  1. var Navigator = function() {};
  2. Navigator.prototype = { "platform": "win32"
  3. };
  4. navigator = new Navigator();
  5. # 只针对检测 Navigator 原型链的写法
复制代码
这样就可以通过上面的原型链检测了

这里的
  1. platform
复制代码
  1. Navigator
复制代码
上的属性,在使用 new 实例化后,navigator.platform 取到的是继承自
  1. Navigator
复制代码
的属性值,而不是直接赋予该对象的属性,所以得到的结果和浏览器是一样的。

这样看是不是很简单,但是像某乎的校验用到了很多次
  1. getOwnPropertyDescriptor
复制代码
,就需要一个个插桩调试他检测了什么对象的什么属性,相当恶心。

那么又回到上面的代码,这里用到的
  1. prototype
复制代码
又是个啥?

为什么像上面那样用到
  1. prototype
复制代码
去定义对象就可以通过的检测?
prototype 与 _ _ proto _ _
首先先上一张图

是不是很懵逼,懵逼就对了,我们是搞爬虫的,知道个大概意思就行了。

我们需要知道的是 prototype 与 _ _ proto _ _ 咋对应起来就行了

按照官方的说法

在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。
即:对象具有属性_ _ proto _ _,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。

方法(Function)这个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。
理解大概的意思(别被搞晕了)可以得出下面这行代码
  1. xxx.__proto__ == yyy.prototype
复制代码
这里我们需要用到谷歌浏览器验证一下我们的想法

有一定基础的同学知道,浏览器里 window 是由 Window 实例而来的,那么 Window 是怎么来的?

我们在浏览器的控制台里看看

可以看到原来我们之前以为简简单单就构造出来的 window 和 navigator 这么复杂

这样一看像我们上面直接使用
  1. var
复制代码
定义的方法去补环境就很容易被识别

例如
  1. var window1 = {
  2. bbb:"xxxx"
  3. };
  4. # 在浏览器里没法定义 window 这里用 window1 做个样子
复制代码

这里
  1. window1
复制代码
的结果和我们上面浏览器中
  1. window
复制代码
的输出结果大相径庭。

不仅仅是
  1. window
复制代码
,包括
  1. document
复制代码
以及
  1. navigator
复制代码
等等囊括
  1. DOM
复制代码
  1. BOM
复制代码
  1. worker
复制代码
、网络请求这些的方方面面都需要我们一个一个分析他的
  1. __proto__
复制代码
以及属性是定义在自己的
  1. prototype
复制代码
上还是继承自上一层。

这里就又涉及了关于上述各类的继承关系,这里分享我找到的一张
  1. DOM
复制代码
中各类的继承关系图,希望对大家补环境有所帮助
总结
这篇文章我的定义并不是关于补环境的总纲或者是总述,只能说是掀起了补环境神秘面纱的一角,希望大家多多交流,不断完善自己的环境框架,做到一键通杀~

还有就是关于文章中如果描述不准确的地方,欢迎在留言区指正,大家共同进步。

以上就是本次的全部内容了,咱们下次再会~

Peace and Love



本帖子中包含更多资源

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

x
分享到:
回复

使用道具 举报