上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
12下一页
返回列表 发新帖

[油猴脚本开发指南]script标签初始化对象劫持

[复制链接]

214

主题

1755

帖子

2287

积分

管理员

非物质文化遗产社会摇传承人

Rank: 9Rank: 9Rank: 9

积分
2287
发表于 2021-9-12 18:28:15 | 显示全部楼层 | 阅读模式

开篇

在我们写脚本的时候,经常碰到一种情况,就是网页调用了外部的库,而我们想要去修改内容或者使用一些功能,这时候通常会束手无策,所以我在这里简单的提供一些我自己的想法,可能存在一些问题,可以评论下写出。

通常分为两种情况,一种情况是网页的功能数据暴露在了windows的内部,这时候我们可以考虑直接对其进行劫持。另外一种是在封闭作用域内,这种情况我们可以考虑对其内部的一些函数进行劫持。

这里我们以https://www.acwing.com/problem/content/1/为例,我想修改这个编辑器的内容

图片.png

通过这里的domclass类可以知道,这个是一个ace编辑器,手册在https://ace.c9.io/#nav=embedding

顺便一提,目前常见的有两种编辑器,一种是ace编辑器,一种是codemirror编辑器,目前两种并无优劣,ace个人认为开发更舒服一点

并且查看windows下挂载了ace,我们可以直接使用

ace.edit("code_editor").setValue('8888')

(备注,初始化编辑器通常使用ace.edit函数进行初始化)

来进行设置内容,因为ace编辑器允许反复初始化,并返回相同的内容,这里可以用相等来进行判断

图片.png

但是这么简单就没有挑战了!所以我们搞的可以更复杂一点。

我们现在document-start的时候对脚本注入,然后debugger

图片.png

可以看到这时候body里的script函数很少的

这时候我们查看一下ace对象是否有初始化

图片.png

证明我们注入的时候ace还没初始化。我们再看一下加载完毕的body内的内容,可以看到差距非常大

图片.png

那这时候我们就可以考虑一个想法,在监控body内的dom变化的时候,检测是否有ace,如果存在就开始劫持ace.edit,直接把对象挂载到window上,这样就把编辑器的对象直接暴露到了外部出去,这里我直接提供一下代码

window.oldace=null
document.querySelector('head').addEventListener("DOMNodeInserted", function(event) {
if(window.ace!==undefined&&window.oldace===null&&window.ace.edit!==undefined){
window.oldace=window.ace.edit
window.ace.edit=function(...args){
let ret=window.oldace.call(this,...args)
window.setace=ret
return ret
}
}
});

if判断window.ace不为undefine的时候检测存储编辑器对象是否为空,如果为空则检测ace.edit是否存在,因为根据我的观察ace的各种函数是逐步初始化的,所以必须进行检测。

如果检测到了edit函数,则对其包装一层,并将返回的内容暴露到window下。

这时候我们直接控制window下的oldace就可以操作编辑器了!

附加

我们也可以使用数据响应来对其进行劫持完成目的,这时候可以对edit再进行一下处理

let _ace;
Object.defineProperty(unsafeWindow, 'ace', {
  configurable: true,
  get: () => _ace,
  set: value => {
    console.log('ace被赋值了');
    _ace = value;
  }
});

结语

撒花~

混的人。

9

主题

219

帖子

554

积分

开发者

Rank: 6Rank: 6

积分
554

活跃会员热心会员突出贡献三好学生猫咪币纪念章中秋纪念章国庆纪念章

发表于 2021-9-12 19:39:45 | 显示全部楼层
李恒道 发表于 2021-9-12 18:28
@cxxjackie 大佬,监控某个js文件彻底加载完毕有什么好一点的办法么....这样感觉其实还是不太对劲 ...

那种完全封闭的应该监控不了,除非他自己对外发了消息,监听页面节点插入是比较常见的做法,如果是这种暴露全局对象的,用Object.defineProperty劫持set更好,像这样:
  1. let _ace;
  2. Object.defineProperty(unsafeWindow, 'ace', {
  3.   configurable: true,
  4.   get: () => _ace,
  5.   set: value => {
  6.     console.log('ace被赋值了');
  7.     _ace = value;
  8.   }
  9. });
复制代码

监听ace.edit的话,再嵌套一层Object.defineProperty就好了(可以封装成Promise写起来好看点)。
回复

使用道具 举报

9

主题

219

帖子

554

积分

开发者

Rank: 6Rank: 6

积分
554

活跃会员热心会员突出贡献三好学生猫咪币纪念章中秋纪念章国庆纪念章

发表于 2021-9-13 21:11:12 | 显示全部楼层

[quote][size=2][color=#999999]李恒道 发表于 2021-9-13 19:49[/color][/size] 我怀疑可能存在更核心的api用来注入,如果能找到比较优良的注入点就舒服了 然而菜的抠脚,理论可以,实际 ...[/quote]

试着写了个Promise来实现,可能不太完善:

function waitForProperty(obj, ...props) {
    let _obj = obj;
    let prop;
    const realDP = Object.defineProperty;
    const waitForValue = () => {
        return new Promise(resolve => {
            Object.defineProperty = function() {
                if (arguments[0] === _obj && arguments[1] === prop) {
                    const value = 'value' in arguments[2] ? arguments[2].value : arguments[2].get.call(_obj);
                    Object.defineProperty = realDP;
                    resolve(value);
                }
                return realDP.apply(Object, arguments);
            };
            realDP.call(Object, _obj, prop, {
                configurable: true,
                enumerable: false,
                get: () => undefined,
                set: value => {
                    realDP.call(Object, _obj, prop, {
                        configurable: true,
                        enumerable: true,
                        writable: true,
                        value: value
                    });
                    Object.defineProperty = realDP;
                    resolve(value);
                }
            });
        });
    };
    return new Promise(async (resolve, reject) => {
        while (props.length > 0) {
            prop = props.shift();
            if (prop in _obj) {
                _obj = _obj[prop];
            } else {
                _obj = await waitForValue();
            }
            if (props.length > 0 && typeof _obj !== 'function' && typeof _obj !== 'object') {
                return reject(`The property '${prop}' is not a function or object.`);
            }
        }
        resolve(_obj);
    });
}

调用方式就像这样:await waitForProperty(unsafeWindow, 'a', 'b', 'c')

前后端的话当然是前端啦,具体的就不细说了,个人隐私个人隐私~

回复

使用道具 举报

214

主题

1755

帖子

2287

积分

管理员

非物质文化遗产社会摇传承人

Rank: 9Rank: 9Rank: 9

积分
2287
发表于 2021-9-12 18:28:53 | 显示全部楼层
@cxxjackie 大佬,监控某个js文件彻底加载完毕有什么好一点的办法么....这样感觉其实还是不太对劲
混的人。
回复

使用道具 举报

7

主题

117

帖子

694

积分

版主

Rank: 7Rank: 7Rank: 7

积分
694

活跃会员猫咪币纪念章推广达人宣传达人突出贡献三好学生热心会员中秋纪念章国庆纪念章

发表于 2021-9-12 18:41:01 | 显示全部楼层
学到了!!
是晚柒载哟
回复

使用道具 举报

214

主题

1755

帖子

2287

积分

管理员

非物质文化遗产社会摇传承人

Rank: 9Rank: 9Rank: 9

积分
2287
发表于 2021-9-13 14:52:56 | 显示全部楼层
cxxjackie 发表于 2021-9-12 19:39
那种完全封闭的应该监控不了,除非他自己对外发了消息,监听页面节点插入是比较常见的做法,如果是这种暴 ...

加上去了,完全封闭的可以使用Object.defineProperty劫持之类的在基础api做手脚拿到数据,我之前阿里云盘的时候尝试过
混的人。
回复

使用道具 举报

9

主题

219

帖子

554

积分

开发者

Rank: 6Rank: 6

积分
554

活跃会员热心会员突出贡献三好学生猫咪币纪念章中秋纪念章国庆纪念章

发表于 2021-9-13 19:14:48 | 显示全部楼层
李恒道 发表于 2021-9-13 14:52
加上去了,完全封闭的可以使用Object.defineProperty劫持之类的在基础api做手脚拿到数据,我之前阿里云盘 ...

劫持基础API应该可以,这我确实没想那么多。不过Object.defineProperty只能监听到直接赋值的操作,对于同样用Object.defineProperty修改get的就没什么办法了,还得加上对defineProperty这个函数本身的劫持,这种情况可能就得祭出Proxy了,我记得Proxy是可以同时处理get、set和defineProperty的。
回复

使用道具 举报

214

主题

1755

帖子

2287

积分

管理员

非物质文化遗产社会摇传承人

Rank: 9Rank: 9Rank: 9

积分
2287
发表于 2021-9-13 19:49:34 | 显示全部楼层
cxxjackie 发表于 2021-9-13 19:14
劫持基础API应该可以,这我确实没想那么多。不过Object.defineProperty只能监听到直接赋值的操作,对于同 ...

我怀疑可能存在更核心的api用来注入,如果能找到比较优良的注入点就舒服了
然而菜的抠脚,理论可以,实际屁都不会
混的人。
回复

使用道具 举报

214

主题

1755

帖子

2287

积分

管理员

非物质文化遗产社会摇传承人

Rank: 9Rank: 9Rank: 9

积分
2287
发表于 2021-9-13 19:49:47 | 显示全部楼层
cxxjackie 发表于 2021-9-13 19:14
劫持基础API应该可以,这我确实没想那么多。不过Object.defineProperty只能监听到直接赋值的操作,对于同 ...

话说哥哥你是前端还是后端啊
混的人。
回复

使用道具 举报

214

主题

1755

帖子

2287

积分

管理员

非物质文化遗产社会摇传承人

Rank: 9Rank: 9Rank: 9

积分
2287
发表于 2021-9-13 23:07:25 | 显示全部楼层
cxxjackie 发表于 2021-9-13 21:11
[md]试着写了个Promise来实现,可能不太完善:

```javascript

硬到我了!
混的人。
回复

使用道具 举报

发表回复

本版积分规则

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