开篇
之前我们曾经聊过一点点的东西
这次决定开专篇聊这个问题
但是因为我也了解不深
加上油猴闭源
说实话调的很费劲
如果cxxjackie讲这些可能效果更好
斗胆今天试一下
算作抛砖引cxxjackie了
开始
如果我们创建一个脚本
对脚本进行debugger
可能很多人就发蒙
因为显示的代码跟我们在管理器内写的代码不一致

我们可以细心读一下这里的代码
window["p7356196.837629042"]上赋值了一个自执行的函数
传入了一个proxy的windows叫context,powers是一些信息数据,fapply根据名字是改变this指针用的

我们可以看一下context

我们可以看一下powers

传入之后首先创建了一个with context,设定了一下作用域
然后将我们脚本作为一个函数,传入到上方的module中
这里可以看到我们的脚本本质上就是一个js文件
而所谓的脚本头在经过油猴管理器进行解析出来数据后
实际执行只是一个简单的注释

传入函数之后将我们的脚本作为module变量
然后调用fapply

然后我们追到fapply里看
可以发现fapply有三个参数,e,t,r
其中e是我们传入的的第一个参数,t是第二个参数,即context,而r则是参数

我们看看r的参数
[undefined, undefined, powers.CDATA, powers.uneval, powers.define, powers.module, powers.exports, context, context, powers.unsafeWindow, powers.cloneInto, powers.exportFunction, powers.createObjectIn, powers.GM, powers.GM_info]
截图如下

可以看到是传入了一堆数据以及一些gm_info等信息
具体我也不清楚是啥,哈哈哈哈哈
然后调用了
(e,t,r)=>n(o, e, t, r)
n为call函数
o为apply函数
e为脚本函数
t为context
r为powers等数组信息
这里的语法为
call(apply,脚本函数,context,arguments参数信息)
但是实际不存在这种语法的,这里我也不太清楚什么愿意
但是根据实际的调试结果
可以知道
context设置为了脚本函数的this,而arguments函数则作为了参数
在这里我们可以看到
this已经变成了经过proxy代理的window

而且函数内window也变成了包装后的window
这时候unsafewindow没有被包装
这就是unsafeWindow与window之间的区别

那包装后的window实际都做了什么呢?
在
https://bbs.tampermonkey.net.cn/forum.php?mod=viewthread&tid=2461&page=1#pid27703
以及
https://bbs.tampermonkey.net.cn/thread-1080-1-1.html
都曾经涉及到过document.addeventListener的问题
我们可以研究一下到底在执行过程中发生了什么?
document之谜
当我们在脚本写上了
console.log(document.addEventListener)
就会导致
let oldadd = EventTarget.prototype.addEventListener
的劫持不生效
那么到底发生了什么呢?
我们先思考一下document到底从哪里来的
因为这里是在脚本的作用域内
所以通常会从with内优先查找,也就是context中

context是一个proxy的windows对象
如果读取属性会触发get函数
我们可以跳进去看看

跳进去发现了一个属性读取的处理的函数,然后我们打上断点,等待触发document

多次的执行终于执行到了document的读取

我们查看e可以发现是一个函数列表

然后可以看到首先获取e的列表内是否存在
如果存在,则查看是否有vlaue,有value则执行返回
有get则调用get
如果都没有则继续执行其他逻辑
document刚好是有get函数的
所以我们直接看get的部分

可以看到走到了get的处理部分
这里可以看到获取了document对象,然后执行ee函数

而ee函数为一个箭头函数
获取到了document分别传给了N、M、I

可以知道油猴的执行在我们脚本内读取了document进行了一定的处理~
这与我们在正常js内的执行行为是不一样的
结语
太长,分两篇
撒一半花