xianyucoder 发表于 2021-9-15 08:48

Js 逆向之补环境到底是在补什么?

### 序言

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

![](https://tc.xianyucoder.cn/blog20210913144159.png)

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

今天写的 Js 逆向之补环境,就可以理解是在 Js 环境下精进我们的 " 骗术 "

### 正文

大家在看文章之前应该都清楚,Node 环境和浏览器环境是完全不同的,平台有很多的检测点可以发现我们是在浏览器运行 Js 还是在 Node 环境下运行 Js

补环境做的就是尽可能根据网页上的 Js 完善本地的 Node 环境,让 Js 运行在 Node 中像浏览器一样不报错就可以了。

#### window

最基本的补环境是早期的文章,大家经常遇到的是

```bash
'window' is not defined
```

那像这样的报错提示应该如何处理?

Node 环境下一般如下定义

```js
window = global;
```

如果只是单单缺少了`window`这一个变量的定义,像上面这样报错自然就消失了。

#### document

除了`window`之外,我们经常还遇到类似下面这些文章中的情况

https://www.52pojie.cn/thread-1513028-1-1.html

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

那像这样的`document`应该怎么补?

在没有检测只是为了能让 js 运行不报错的情况下,我是这样写的

```js
var document = {
    cookie:"xxxxxx"
}
```

或者像下面这样写

![](https://tc.xianyucoder.cn/blog20210913161432.png)

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

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

那么检测的 Js 长什么样?

#### Object.getOwnPropertyDescriptor

这里可以参考之前写的关于某乎的分析文章

https://www.52pojie.cn/thread-1495847-1-1.html

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

![](https://tc.xianyucoder.cn/blog20210913164013.png)

这个东西是个啥?

官方定义是这样的

> **`Object.getOwnPropertyDescriptor()`** 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

通过描述有点晦涩,你可以这样理解,如果是自己构造的对象,例如

```bash
var navigator = {
    platform:"win32"
}
```

就没办法通过这样的检测

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

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

先是我们自己写的例子

```bash
var navigator1 = {
    platform:"win32"
}
```

![](https://tc.xianyucoder.cn/blog20210913175141.png)

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

![](https://tc.xianyucoder.cn/blog20210913175203.png)


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

这里站在前人的肩膀上,写一下

```bash
var Navigator = function() {};
Navigator.prototype = {   "platform": "win32"
};
navigator = new Navigator();
# 只针对检测 Navigator 原型链的写法
```

这样就可以通过上面的原型链检测了

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

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

那么又回到上面的代码,这里用到的`prototype`又是个啥?

为什么像上面那样用到`prototype`去定义对象就可以通过的检测?

#### prototype 与 _ _ _proto_ _ _

首先先上一张图

![](https://tc.xianyucoder.cn/blog20210913170515.png)

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

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

按照官方的说法

> 在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。
> 即:对象具有属性_ _ _proto_ _ _,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。
>
> 方法(Function)这个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。

理解大概的意思(别被搞晕了)可以得出下面这行代码

```bash
xxx.__proto__ == yyy.prototype
```

这里我们需要用到谷歌浏览器验证一下我们的想法

![](https://tc.xianyucoder.cn/blog20210913172224.png)

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

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

![](https://tc.xianyucoder.cn/blog20210913165843.png)

![](https://tc.xianyucoder.cn/blog20210913165905.png)

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

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

例如

```bash
var window1 = {
    bbb:"xxxx"
};
# 在浏览器里没法定义 window 这里用 window1 做个样子
```

![](https://tc.xianyucoder.cn/blog20210913172821.png)

这里`window1`的结果和我们上面浏览器中`window`的输出结果大相径庭。

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

这里就又涉及了关于上述各类的继承关系,这里分享我找到的一张`DOM`中各类的继承关系图,希望对大家补环境有所帮助

![](https://tc.xianyucoder.cn/blog20210913180556.png)

### 总结

这篇文章我的定义并不是关于补环境的总纲或者是总述,只能说是掀起了补环境神秘面纱的一角,希望大家多多交流,不断完善自己的环境框架,做到一键通杀~

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

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

Peace and Love

xianyucoder 发表于 2021-9-15 09:36

艾莉希雅 发表于 2021-9-15 09:32
有没有一种可能,我是说可能
我们直接把浏览器的抠出来用呢

那可以直接编译 chromium

艾莉希雅 发表于 2021-9-15 09:32

有没有一种可能,我是说可能
我们直接把浏览器的抠出来用呢

逝去的初夏c 发表于 2021-9-15 08:56

分析的很独特!

yufd1986 发表于 2021-9-15 09:07

这个原理说的挺好的,确实没有好好研究过。

yyb414 发表于 2021-9-15 09:08

虽然看不懂,但是还得顶一下

SysEntry 发表于 2021-9-15 10:07

学习了,之前只会单纯从js中扣算法.

Anekys 发表于 2021-9-15 10:16

之前爬BOSS的时候就遇到了JS里面存在浏览器对象而Python的execjs是node.js无法直接运行的情况
学习了,感谢分享

罗通晓 发表于 2021-9-15 10:41

本帖最后由 罗通晓 于 2021-9-15 10:43 编辑

xianyucoder 发表于 2021-9-15 09:36
那可以直接编译 chromium
其实用puppeteer就可以了,里面用的好像官方提供无头浏览器,puppeteer封装了一下,所以感觉补环境的唯一优点就是可能运行得更快一些

apie 发表于 2021-9-15 10:50

能不能直接把浏览器的这个window对象序列化下来,然后反序列化加载到我们的node环境中用,这样就不用逐一字段去补了
页: [1] 2 3 4 5 6 7
查看完整版本: Js 逆向之补环境到底是在补什么?